diff --git a/analyzer-baseline.toml b/analyzer-baseline.toml index cca21de4..d8284b60 100644 --- a/analyzer-baseline.toml +++ b/analyzer-baseline.toml @@ -1908,330 +1908,24 @@ code = "possibly-undefined-string-array-index" message = "Possibly undefined array key `string('combine')` accessed on `array|null>>|string>`." count = 1 -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\AlterTable` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "imprecise-type" -message = "Type `array` in return type of `getRawState` is imprecise, equivalent to `array`." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "invalid-return-statement" -message = '''Invalid return type for function `PhpDb\Sql\Ddl\AlterTable::getRawState`: expected `array|string`, but found `PhpDb\Sql\TableIdentifier|array|array{'addColumns': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|bool|int|list|string, 'addConstraints': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|bool|int|list|string, 'changeColumns': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|array|bool|int|string, 'dropColumns': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|bool|int|list|string, 'dropConstraints': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|bool|int|list|string, 'dropIndexes': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|bool|int|list|string, 'table': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|PhpDb\Sql\TableIdentifier|bool|int|string, 'tableOptions': PhpDb\Sql\Ddl\Column\ColumnInterface|PhpDb\Sql\Literal|array|bool|int|string}|list|string`.''' -count = 1 - [[issues]] file = "src/Sql/Ddl/AlterTable.php" code = "invalid-return-statement" message = 'Invalid return type for function `PhpDb\Sql\Ddl\AlterTable::processTable`: expected `array`, but found `list{array|null|string}`.' count = 1 -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 5 - -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "possibly-null-argument" -message = 'Argument #2 of method `PhpDb\Sql\AbstractSql::processExpression` is possibly `null`, but parameter type `PhpDb\Adapter\Platform\PlatformInterface` does not accept it.' -count = 3 - -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "possibly-null-argument" -message = 'Argument #2 of method `PhpDb\Sql\AbstractSql::resolveTable` is possibly `null`, but parameter type `PhpDb\Adapter\Platform\PlatformInterface` does not accept it.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/AbstractLengthColumn.php" -code = "missing-api-or-internal" -message = 'Abstract class `PhpDb\Sql\Ddl\Column\AbstractLengthColumn` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/AbstractPrecisionColumn.php" -code = "missing-api-or-internal" -message = 'Abstract class `PhpDb\Sql\Ddl\Column\AbstractPrecisionColumn` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/AbstractTimestampColumn.php" -code = "missing-api-or-internal" -message = 'Abstract class `PhpDb\Sql\Ddl\Column\AbstractTimestampColumn` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/BigInteger.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\BigInteger` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Binary.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Binary` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Blob.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Blob` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Boolean.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Boolean` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Char.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Char` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Column.php" -code = "mixed-argument" -message = 'Invalid argument type for argument #1 of `PhpDb\Sql\Ddl\Column\Column::setDefault`: expected `PhpDb\Sql\Argument\Literal|PhpDb\Sql\Argument\Value|null|scalar`, but found `mixed`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Column.php" -code = "uninitialized-property" -message = 'Property `$default` is not initialized in the constructor of class `PhpDb\Sql\Ddl\Column\Column`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/ColumnInterface.php" -code = "missing-api-or-internal" -message = 'Interface `PhpDb\Sql\Ddl\Column\ColumnInterface` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Date.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Date` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Datetime.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Datetime` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Decimal.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Decimal` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Double.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Double` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Floating.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Floating` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Json.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Json` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/SmallInteger.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\SmallInteger` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Text.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Text` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Time.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Time` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Timestamp.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Timestamp` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Varbinary.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Varbinary` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/Varchar.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Column\Varchar` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/AbstractConstraint.php" -code = "missing-api-or-internal" -message = 'Abstract class `PhpDb\Sql\Ddl\Constraint\AbstractConstraint` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/Check.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Constraint\Check` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/ConstraintInterface.php" -code = "missing-api-or-internal" -message = 'Interface `PhpDb\Sql\Ddl\Constraint\ConstraintInterface` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/ForeignKey.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Constraint\ForeignKey` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/ForeignKey.php" -code = "possibly-undefined-int-array-index" -message = "Possibly undefined array key `int(0)` accessed on `array`." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/ForeignKey.php" -code = "possibly-undefined-int-array-index" -message = "Possibly undefined array key `int(1)` accessed on `array`." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/PrimaryKey.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Constraint\PrimaryKey` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/UniqueKey.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Constraint\UniqueKey` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\CreateTable` should be declared `final`.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "invalid-operand" -message = "Left operand in `&&` operation is an `array`." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "invalid-operand" -message = "Right operand in `&&` operation is an `array`." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "invalid-return-statement" -message = '''Invalid return type for function `PhpDb\Sql\Ddl\CreateTable::getRawState`: expected `array|string>|string`, but found `PhpDb\Sql\TableIdentifier|array|array{'columns': PhpDb\Sql\Literal|bool|int|list|string, 'constraints': PhpDb\Sql\Literal|bool|int|list|string, 'table': PhpDb\Sql\Literal|PhpDb\Sql\TableIdentifier|bool|int|string, 'tableOptions': PhpDb\Sql\Literal|array|bool|int|string}|list|string`.''' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "invalid-return-statement" -message = 'Invalid return type for function `PhpDb\Sql\Ddl\CreateTable::processCombinedby`: expected `null|string`, but found `array|null>>|null|string`.' -count = 1 - [[issues]] file = "src/Sql/Ddl/CreateTable.php" code = "invalid-return-statement" message = '''Invalid return type for function `PhpDb\Sql\Ddl\CreateTable::processTable`: expected `array`, but found `list{string('')|string('TEMPORARY '), string('')|string('IF NOT EXISTS '), array|null|string}`.''' count = 1 -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "possible-method-access-on-null" -message = "Attempting to call a method on `null`." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "possibly-null-argument" -message = 'Argument #2 of method `PhpDb\Sql\AbstractSql::processExpression` is possibly `null`, but parameter type `PhpDb\Adapter\Platform\PlatformInterface` does not accept it.' -count = 2 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "possibly-null-argument" -message = 'Argument #2 of method `PhpDb\Sql\AbstractSql::resolveTable` is possibly `null`, but parameter type `PhpDb\Adapter\Platform\PlatformInterface` does not accept it.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "possibly-undefined-string-array-index" -message = "Possibly undefined array key `string('combinedBy')` accessed on `array|null>>|string>`." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "unused-parameter" -message = "Parameter `$adapterPlatform` is never used." -count = 2 - -[[issues]] -file = "src/Sql/Ddl/DropTable.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\DropTable` should be declared `final`.' -count = 1 - [[issues]] file = "src/Sql/Ddl/DropTable.php" code = "invalid-return-statement" message = '''Invalid return type for function `PhpDb\Sql\Ddl\DropTable::processTable`: expected `array`, but found `list{string('')|string('IF EXISTS '), array|null|string}`.''' count = 1 -[[issues]] -file = "src/Sql/Ddl/DropTable.php" -code = "possibly-null-argument" -message = 'Argument #2 of method `PhpDb\Sql\AbstractSql::resolveTable` is possibly `null`, but parameter type `PhpDb\Adapter\Platform\PlatformInterface` does not accept it.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Index/AbstractIndex.php" -code = "missing-api-or-internal" -message = 'Abstract class `PhpDb\Sql\Ddl\Index\AbstractIndex` is missing an `@api` or `@internal` annotation.' -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Index/Index.php" -code = "class-must-be-final" -message = 'Class `PhpDb\Sql\Ddl\Index\Index` should be declared `final`.' -count = 1 - [[issues]] file = "src/Sql/Delete.php" code = "class-must-be-final" diff --git a/lint-baseline.toml b/lint-baseline.toml index c92c2258..0bebf452 100644 --- a/lint-baseline.toml +++ b/lint-baseline.toml @@ -378,42 +378,6 @@ code = "no-array-accumulation-in-loop" message = "Array spread in reassignment inside a loop causes O(n²) array copying." count = 1 -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "kan-defect" -message = "Class has a high kan defect score." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/AlterTable.php" -code = "too-many-methods" -message = "Class has too many methods." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/AbstractPrecisionColumn.php" -code = "excessive-parameter-list" -message = "Parameter list is too long." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Column/AbstractPrecisionColumn.php" -code = "halstead" -message = "Method has a high halstead difficulty" -count = 1 - -[[issues]] -file = "src/Sql/Ddl/Constraint/ForeignKey.php" -code = "excessive-parameter-list" -message = "Parameter list is too long." -count = 1 - -[[issues]] -file = "src/Sql/Ddl/CreateTable.php" -code = "too-many-methods" -message = "Class has too many methods." -count = 1 - [[issues]] file = "src/Sql/Insert.php" code = "cyclomatic-complexity" diff --git a/mago.toml b/mago.toml index 4780bd50..8aab97ce 100644 --- a/mago.toml +++ b/mago.toml @@ -15,9 +15,9 @@ baseline = "lint-baseline.toml" # Exception classes are message catalogues: one template and one named constructor per # condition, per the convention in docs/book/exceptions.md. The method count tracks the # number of distinct failures a component can report, so the maintainability signal does -# not apply. Predicate is the fluent WHERE/HAVING builder: one public method per predicate -# type, so its method count is the size of its public API. "test/" repeats the -# phpdb-qa-tools default, which this key replaces. +# not apply. Predicate, AlterTable and CreateTable are fluent builders: one public method per +# clause they can emit, so their method count is the size of their public API. "test/" +# repeats the phpdb-qa-tools default, which this key replaces. too-many-methods = { exclude = [ "test/", "src/Sql/Exception/", @@ -25,6 +25,17 @@ too-many-methods = { exclude = [ "src/TableGateway/Exception/", "src/Adapter/Exception/", "src/Sql/Predicate/Predicate.php", + "src/Sql/Ddl/AlterTable.php", + "src/Sql/Ddl/CreateTable.php", +] } + +# These constructors take one argument per part of the SQL definition they model +# (a foreign key's name, columns, reference table, reference columns and two rules; a +# column's name, digits, scale, nullability, default and options). They are public API, so +# the list cannot be shortened without breaking callers. +excessive-parameter-list = { exclude = [ + "src/Sql/Ddl/Constraint/ForeignKey.php", + "src/Sql/Ddl/Column/AbstractPrecisionColumn.php", ] } # The capability interfaces return `ResultSetInterface&`, and PHP rejects diff --git a/src/Sql/Ddl/AlterTable.php b/src/Sql/Ddl/AlterTable.php index 47fa6ecd..1113b771 100644 --- a/src/Sql/Ddl/AlterTable.php +++ b/src/Sql/Ddl/AlterTable.php @@ -6,16 +6,30 @@ use PhpDb\Adapter\Platform\PlatformInterface; use PhpDb\Sql\AbstractSql; +use PhpDb\Sql\ExpressionInterface; use PhpDb\Sql\Literal; use PhpDb\Sql\TableIdentifier; use function array_key_exists; +use function array_map; use function is_bool; use function is_int; use function strtoupper; /** + * @api + * * @psalm-import-type Specification from AbstractSql + * @psalm-type RawState = array{ + * table: string|TableIdentifier, + * addColumns: list, + * dropColumns: list, + * changeColumns: array, + * addConstraints: list, + * dropConstraints: list, + * dropIndexes: list, + * tableOptions: array, + * } */ class AlterTable extends AbstractSql { @@ -160,7 +174,10 @@ public function getOptions(): array return $this->options; } - public function getRawState(?string $key = null): array|string + /** + * @return RawState|value-of + */ + public function getRawState(?string $key = null): array|string|TableIdentifier { $rawState = [ self::TABLE => $this->table, @@ -199,33 +216,29 @@ public function setTable(string|TableIdentifier $name): static /** * @return array{0: list} */ - protected function processAddColumns(?PlatformInterface $adapterPlatform = null): array + protected function processAddColumns(PlatformInterface $adapterPlatform): array { - $sqls = []; - foreach ($this->addColumns as $column) { - $sqls[] = $this->processExpression($column, $adapterPlatform); - } - - return [$sqls]; + return [array_map( + fn(ExpressionInterface $column): string => $this->processExpression($column, $adapterPlatform), + $this->addColumns, + )]; } /** * @return array{0: list} */ - protected function processAddConstraints(?PlatformInterface $adapterPlatform = null): array + protected function processAddConstraints(PlatformInterface $adapterPlatform): array { - $sqls = []; - foreach ($this->addConstraints as $constraint) { - $sqls[] = $this->processExpression($constraint, $adapterPlatform); - } - - return [$sqls]; + return [array_map( + fn(ExpressionInterface $constraint): string => $this->processExpression($constraint, $adapterPlatform), + $this->addConstraints, + )]; } /** * @return array{0: list} */ - protected function processChangeColumns(?PlatformInterface $adapterPlatform = null): array + protected function processChangeColumns(PlatformInterface $adapterPlatform): array { $sqls = []; foreach ($this->changeColumns as $name => $column) { @@ -241,44 +254,29 @@ protected function processChangeColumns(?PlatformInterface $adapterPlatform = nu /** * @return array{0: list} */ - protected function processDropColumns(?PlatformInterface $adapterPlatform = null): array + protected function processDropColumns(PlatformInterface $adapterPlatform): array { - $sqls = []; - foreach ($this->dropColumns as $column) { - $sqls[] = $adapterPlatform->quoteIdentifier($column); - } - - return [$sqls]; + return [array_map($adapterPlatform->quoteIdentifier(...), $this->dropColumns)]; } /** * @return array{0: list} */ - protected function processDropConstraints(?PlatformInterface $adapterPlatform = null): array + protected function processDropConstraints(PlatformInterface $adapterPlatform): array { - $sqls = []; - foreach ($this->dropConstraints as $constraint) { - $sqls[] = $adapterPlatform->quoteIdentifier($constraint); - } - - return [$sqls]; + return [array_map($adapterPlatform->quoteIdentifier(...), $this->dropConstraints)]; } /** * @return array{0: list} */ - protected function processDropIndexes(?PlatformInterface $adapterPlatform = null): array + protected function processDropIndexes(PlatformInterface $adapterPlatform): array { - $sqls = []; - foreach ($this->dropIndexes as $index) { - $sqls[] = $adapterPlatform->quoteIdentifier($index); - } - - return [$sqls]; + return [array_map($adapterPlatform->quoteIdentifier(...), $this->dropIndexes)]; } /** @return string[] */ - protected function processTable(?PlatformInterface $adapterPlatform = null): array + protected function processTable(PlatformInterface $adapterPlatform): array { return [$this->resolveTable($this->table, $adapterPlatform)]; } @@ -286,7 +284,7 @@ protected function processTable(?PlatformInterface $adapterPlatform = null): arr /** * @return array{0: list}|null */ - protected function processTableOptions(?PlatformInterface $adapterPlatform = null): ?array + protected function processTableOptions(PlatformInterface $adapterPlatform): ?array { if (! $this->options) { return null; diff --git a/src/Sql/Ddl/Column/AbstractLengthColumn.php b/src/Sql/Ddl/Column/AbstractLengthColumn.php index 0f99759e..8140aa00 100644 --- a/src/Sql/Ddl/Column/AbstractLengthColumn.php +++ b/src/Sql/Ddl/Column/AbstractLengthColumn.php @@ -6,12 +6,16 @@ use Override; use PhpDb\Sql\Argument\Literal; +use PhpDb\Sql\Argument\Value; use PhpDb\Sql\Exception\InvalidArgumentException; use function array_splice; use function strlen; use function substr; +/** + * @api + */ abstract class AbstractLengthColumn extends Column { /** @@ -26,7 +30,7 @@ public function __construct( string $name, ?int $length = null, bool $nullable = false, - mixed $default = null, + string|int|float|bool|Literal|Value|null $default = null, array $options = [], ) { $this->setLength($length); diff --git a/src/Sql/Ddl/Column/AbstractPrecisionColumn.php b/src/Sql/Ddl/Column/AbstractPrecisionColumn.php index 48bdd39e..e8f0c150 100644 --- a/src/Sql/Ddl/Column/AbstractPrecisionColumn.php +++ b/src/Sql/Ddl/Column/AbstractPrecisionColumn.php @@ -5,8 +5,13 @@ namespace PhpDb\Sql\Ddl\Column; use Override; +use PhpDb\Sql\Argument\Literal; +use PhpDb\Sql\Argument\Value; use PhpDb\Sql\Exception\InvalidArgumentException; +/** + * @api + */ abstract class AbstractPrecisionColumn extends AbstractLengthColumn { protected ?int $decimal; @@ -17,7 +22,7 @@ public function __construct( ?int $digits = null, ?int $decimal = null, bool $nullable = false, - mixed $default = null, + string|int|float|bool|Literal|Value|null $default = null, array $options = [], ) { $this->setDecimal($decimal); @@ -53,14 +58,10 @@ public function setDigits(?int $digits): static #[Override] protected function getLengthExpression(): string { - if (null === $this->decimal) { - return (string) $this->length; - } - - if (null === $this->length) { - throw InvalidArgumentException::forMissingDecimalDigits($this->name, $this->type); - } - - return "{$this->length},{$this->decimal}"; + return match (true) { + null === $this->decimal => (string) $this->length, + null === $this->length => throw InvalidArgumentException::forMissingDecimalDigits($this->name, $this->type), + default => "{$this->length},{$this->decimal}", + }; } } diff --git a/src/Sql/Ddl/Column/AbstractTimestampColumn.php b/src/Sql/Ddl/Column/AbstractTimestampColumn.php index e34b25fd..fa6293c7 100644 --- a/src/Sql/Ddl/Column/AbstractTimestampColumn.php +++ b/src/Sql/Ddl/Column/AbstractTimestampColumn.php @@ -8,6 +8,8 @@ use PhpDb\Sql\Argument\Literal; /** + * @api + * * @see doc section http://dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html */ abstract class AbstractTimestampColumn extends Column diff --git a/src/Sql/Ddl/Column/BigInteger.php b/src/Sql/Ddl/Column/BigInteger.php index b0ad7f3b..bfb94ad4 100644 --- a/src/Sql/Ddl/Column/BigInteger.php +++ b/src/Sql/Ddl/Column/BigInteger.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class BigInteger extends Integer { protected string $type = 'BIGINT'; diff --git a/src/Sql/Ddl/Column/Binary.php b/src/Sql/Ddl/Column/Binary.php index 936f1fac..a87bd0a5 100644 --- a/src/Sql/Ddl/Column/Binary.php +++ b/src/Sql/Ddl/Column/Binary.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Binary extends AbstractLengthColumn { protected string $type = 'BINARY'; diff --git a/src/Sql/Ddl/Column/Blob.php b/src/Sql/Ddl/Column/Blob.php index 929d6cbe..c5f281b9 100644 --- a/src/Sql/Ddl/Column/Blob.php +++ b/src/Sql/Ddl/Column/Blob.php @@ -6,6 +6,9 @@ use Override; +/** + * @api + */ class Blob extends AbstractLengthColumn { /** @var string Change type to blob */ diff --git a/src/Sql/Ddl/Column/Boolean.php b/src/Sql/Ddl/Column/Boolean.php index 0c62dd1e..4ca7c90d 100644 --- a/src/Sql/Ddl/Column/Boolean.php +++ b/src/Sql/Ddl/Column/Boolean.php @@ -6,6 +6,9 @@ use Override; +/** + * @api + */ class Boolean extends Column { protected string $type = 'BOOLEAN'; diff --git a/src/Sql/Ddl/Column/Char.php b/src/Sql/Ddl/Column/Char.php index 08fb3345..f5e4bb55 100644 --- a/src/Sql/Ddl/Column/Char.php +++ b/src/Sql/Ddl/Column/Char.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Char extends AbstractLengthColumn { protected string $type = 'CHAR'; diff --git a/src/Sql/Ddl/Column/Column.php b/src/Sql/Ddl/Column/Column.php index c921ce9a..02a86928 100644 --- a/src/Sql/Ddl/Column/Column.php +++ b/src/Sql/Ddl/Column/Column.php @@ -13,9 +13,12 @@ use function implode; +/** + * @api + */ class Column implements ColumnInterface { - protected string|int|float|bool|Literal|Value|null $default; + protected string|int|float|bool|Literal|Value|null $default = null; protected bool $isNullable = false; @@ -35,7 +38,7 @@ class Column implements ColumnInterface public function __construct( string $name = '', bool $nullable = false, - mixed $default = null, + string|int|float|bool|Literal|Value|null $default = null, array $options = [], ) { $this->setName($name); diff --git a/src/Sql/Ddl/Column/ColumnInterface.php b/src/Sql/Ddl/Column/ColumnInterface.php index ebf4ad23..09ea0865 100644 --- a/src/Sql/Ddl/Column/ColumnInterface.php +++ b/src/Sql/Ddl/Column/ColumnInterface.php @@ -9,6 +9,8 @@ use PhpDb\Sql\ExpressionInterface; /** + * @api + * * Interface ColumnInterface describes the protocol on how Column objects interact */ interface ColumnInterface extends ExpressionInterface diff --git a/src/Sql/Ddl/Column/Date.php b/src/Sql/Ddl/Column/Date.php index b8e4a59e..b8fab22a 100644 --- a/src/Sql/Ddl/Column/Date.php +++ b/src/Sql/Ddl/Column/Date.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Date extends Column { protected string $type = 'DATE'; diff --git a/src/Sql/Ddl/Column/Datetime.php b/src/Sql/Ddl/Column/Datetime.php index 590c0a31..3c24a376 100644 --- a/src/Sql/Ddl/Column/Datetime.php +++ b/src/Sql/Ddl/Column/Datetime.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Datetime extends Column { protected string $type = 'DATETIME'; diff --git a/src/Sql/Ddl/Column/Decimal.php b/src/Sql/Ddl/Column/Decimal.php index ae85bdba..e9eaca61 100644 --- a/src/Sql/Ddl/Column/Decimal.php +++ b/src/Sql/Ddl/Column/Decimal.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Decimal extends AbstractPrecisionColumn { protected string $type = 'DECIMAL'; diff --git a/src/Sql/Ddl/Column/Double.php b/src/Sql/Ddl/Column/Double.php index ad9a6085..d12f4af2 100644 --- a/src/Sql/Ddl/Column/Double.php +++ b/src/Sql/Ddl/Column/Double.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Double extends AbstractPrecisionColumn { protected string $type = 'DOUBLE'; diff --git a/src/Sql/Ddl/Column/Floating.php b/src/Sql/Ddl/Column/Floating.php index 2b02a471..17fbd01e 100644 --- a/src/Sql/Ddl/Column/Floating.php +++ b/src/Sql/Ddl/Column/Floating.php @@ -5,6 +5,8 @@ namespace PhpDb\Sql\Ddl\Column; /** + * @api + * * Column representing a FLOAT type. * * Cannot name a class "float" starting in PHP 7, as it's a reserved keyword; diff --git a/src/Sql/Ddl/Column/Integer.php b/src/Sql/Ddl/Column/Integer.php index e2328019..4abd5f3b 100644 --- a/src/Sql/Ddl/Column/Integer.php +++ b/src/Sql/Ddl/Column/Integer.php @@ -15,6 +15,9 @@ use function strlen; use function substr; +/** + * @api + */ class Integer extends Column { /** diff --git a/src/Sql/Ddl/Column/Json.php b/src/Sql/Ddl/Column/Json.php index 4e2d4073..cd7fa462 100644 --- a/src/Sql/Ddl/Column/Json.php +++ b/src/Sql/Ddl/Column/Json.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Json extends Column { protected string $type = 'JSON'; diff --git a/src/Sql/Ddl/Column/SmallInteger.php b/src/Sql/Ddl/Column/SmallInteger.php index a75c5d43..5a921682 100644 --- a/src/Sql/Ddl/Column/SmallInteger.php +++ b/src/Sql/Ddl/Column/SmallInteger.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class SmallInteger extends Integer { protected string $type = 'SMALLINT'; diff --git a/src/Sql/Ddl/Column/Text.php b/src/Sql/Ddl/Column/Text.php index ec989744..1f887038 100644 --- a/src/Sql/Ddl/Column/Text.php +++ b/src/Sql/Ddl/Column/Text.php @@ -6,6 +6,9 @@ use Override; +/** + * @api + */ class Text extends AbstractLengthColumn { protected string $type = 'TEXT'; diff --git a/src/Sql/Ddl/Column/Time.php b/src/Sql/Ddl/Column/Time.php index 7b637636..c9daa8ce 100644 --- a/src/Sql/Ddl/Column/Time.php +++ b/src/Sql/Ddl/Column/Time.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Time extends Column { protected string $type = 'TIME'; diff --git a/src/Sql/Ddl/Column/Timestamp.php b/src/Sql/Ddl/Column/Timestamp.php index 7c8273ac..02850bef 100644 --- a/src/Sql/Ddl/Column/Timestamp.php +++ b/src/Sql/Ddl/Column/Timestamp.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Timestamp extends AbstractTimestampColumn { protected string $type = 'TIMESTAMP'; diff --git a/src/Sql/Ddl/Column/Varbinary.php b/src/Sql/Ddl/Column/Varbinary.php index 8d98f08d..05fd8651 100644 --- a/src/Sql/Ddl/Column/Varbinary.php +++ b/src/Sql/Ddl/Column/Varbinary.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Varbinary extends AbstractLengthColumn { protected bool $lengthRequired = true; diff --git a/src/Sql/Ddl/Column/Varchar.php b/src/Sql/Ddl/Column/Varchar.php index 62681e7a..e8d9dd87 100644 --- a/src/Sql/Ddl/Column/Varchar.php +++ b/src/Sql/Ddl/Column/Varchar.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Column; +/** + * @api + */ class Varchar extends AbstractLengthColumn { protected bool $lengthRequired = true; diff --git a/src/Sql/Ddl/Constraint/AbstractConstraint.php b/src/Sql/Ddl/Constraint/AbstractConstraint.php index 687bba42..72749012 100644 --- a/src/Sql/Ddl/Constraint/AbstractConstraint.php +++ b/src/Sql/Ddl/Constraint/AbstractConstraint.php @@ -12,6 +12,9 @@ use function implode; use function str_replace; +/** + * @api + */ abstract class AbstractConstraint implements ConstraintInterface { protected string $columnSpecification = '(%s)'; diff --git a/src/Sql/Ddl/Constraint/Check.php b/src/Sql/Ddl/Constraint/Check.php index 31a9d962..68552145 100644 --- a/src/Sql/Ddl/Constraint/Check.php +++ b/src/Sql/Ddl/Constraint/Check.php @@ -13,6 +13,9 @@ use function implode; use function sprintf; +/** + * @api + */ class Check extends AbstractConstraint { protected string|ExpressionInterface $expression; diff --git a/src/Sql/Ddl/Constraint/ConstraintInterface.php b/src/Sql/Ddl/Constraint/ConstraintInterface.php index d61be959..82904da1 100644 --- a/src/Sql/Ddl/Constraint/ConstraintInterface.php +++ b/src/Sql/Ddl/Constraint/ConstraintInterface.php @@ -6,6 +6,9 @@ use PhpDb\Sql\ExpressionInterface; +/** + * @api + */ interface ConstraintInterface extends ExpressionInterface { /** @return list */ diff --git a/src/Sql/Ddl/Constraint/ForeignKey.php b/src/Sql/Ddl/Constraint/ForeignKey.php index d06b8eec..4e18762a 100644 --- a/src/Sql/Ddl/Constraint/ForeignKey.php +++ b/src/Sql/Ddl/Constraint/ForeignKey.php @@ -12,6 +12,9 @@ use function count; use function implode; +/** + * @api + */ class ForeignKey extends AbstractConstraint { protected string $onDeleteRule = 'NO ACTION'; @@ -25,7 +28,7 @@ class ForeignKey extends AbstractConstraint /** @var list */ protected array $referenceColumn = []; - /** @var string[] */ + /** @var array{0: string, 1: string} */ protected array $referenceSpecification = [ 'REFERENCES %s', 'ON DELETE %s ON UPDATE %s', diff --git a/src/Sql/Ddl/Constraint/PrimaryKey.php b/src/Sql/Ddl/Constraint/PrimaryKey.php index 98917745..b30ab54c 100644 --- a/src/Sql/Ddl/Constraint/PrimaryKey.php +++ b/src/Sql/Ddl/Constraint/PrimaryKey.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Constraint; +/** + * @api + */ class PrimaryKey extends AbstractConstraint { protected string $specification = 'PRIMARY KEY'; diff --git a/src/Sql/Ddl/Constraint/UniqueKey.php b/src/Sql/Ddl/Constraint/UniqueKey.php index 39fad3d4..dfd0bbb4 100644 --- a/src/Sql/Ddl/Constraint/UniqueKey.php +++ b/src/Sql/Ddl/Constraint/UniqueKey.php @@ -4,6 +4,9 @@ namespace PhpDb\Sql\Ddl\Constraint; +/** + * @api + */ class UniqueKey extends AbstractConstraint { protected string $specification = 'UNIQUE'; diff --git a/src/Sql/Ddl/CreateTable.php b/src/Sql/Ddl/CreateTable.php index 28b519a4..656030f2 100644 --- a/src/Sql/Ddl/CreateTable.php +++ b/src/Sql/Ddl/CreateTable.php @@ -16,7 +16,15 @@ use function strtoupper; /** + * @api + * * @psalm-import-type Specification from AbstractSql + * @psalm-type RawState = array{ + * columns: list, + * constraints: list, + * table: string|TableIdentifier, + * tableOptions: array, + * } */ class CreateTable extends AbstractSql { @@ -91,10 +99,9 @@ public function getOptions(): array } /** - * @return ((Column\ColumnInterface|string)[]|Column\ColumnInterface|string)[]|string - * @psalm-return array|string>|string + * @return RawState|value-of */ - public function getRawState(?string $key = null): array|string + public function getRawState(?string $key = null): array|string|TableIdentifier { $rawState = [ self::COLUMNS => $this->columns, @@ -145,7 +152,7 @@ public function setTemporary(string|int|bool $temporary): static /** * @return string[][]|null */ - protected function processColumns(?PlatformInterface $adapterPlatform = null): ?array + protected function processColumns(PlatformInterface $adapterPlatform): ?array { if (! $this->columns) { return null; @@ -160,19 +167,18 @@ protected function processColumns(?PlatformInterface $adapterPlatform = null): ? return [$sqls]; } - protected function processCombinedby(?PlatformInterface $adapterPlatform = null): ?string + /** + * @return array{}|null + */ + protected function processCombinedby(): ?array { - if ($this->constraints && $this->columns) { - return $this->specifications['combinedBy']; - } - - return null; + return [] !== $this->constraints && [] !== $this->columns ? [] : null; } /** * @return string[][]|null */ - protected function processConstraints(?PlatformInterface $adapterPlatform = null): ?array + protected function processConstraints(PlatformInterface $adapterPlatform): ?array { if (! $this->constraints) { return null; @@ -190,7 +196,7 @@ protected function processConstraints(?PlatformInterface $adapterPlatform = null /** * @return string[] */ - protected function processStatementEnd(?PlatformInterface $adapterPlatform = null): array + protected function processStatementEnd(): array { return ["\n)"]; } @@ -198,7 +204,7 @@ protected function processStatementEnd(?PlatformInterface $adapterPlatform = nul /** * @return string[] */ - protected function processTable(?PlatformInterface $adapterPlatform = null): array + protected function processTable(PlatformInterface $adapterPlatform): array { return [ $this->isTemporary ? 'TEMPORARY ' : '', @@ -210,7 +216,7 @@ protected function processTable(?PlatformInterface $adapterPlatform = null): arr /** * @return string[]|null */ - protected function processTableOptions(?PlatformInterface $adapterPlatform = null): ?array + protected function processTableOptions(PlatformInterface $adapterPlatform): ?array { if (! $this->options) { return null; diff --git a/src/Sql/Ddl/DropTable.php b/src/Sql/Ddl/DropTable.php index 9bdd5bfa..3a9bb62b 100644 --- a/src/Sql/Ddl/DropTable.php +++ b/src/Sql/Ddl/DropTable.php @@ -9,6 +9,8 @@ use PhpDb\Sql\TableIdentifier; /** + * @api + * * @psalm-import-type Specification from AbstractSql */ class DropTable extends AbstractSql @@ -41,7 +43,7 @@ public function ifExists(bool $ifExists = true): static } /** @return string[] */ - protected function processTable(?PlatformInterface $adapterPlatform = null): array + protected function processTable(PlatformInterface $adapterPlatform): array { return [ $this->ifExists ? 'IF EXISTS ' : '', diff --git a/src/Sql/Ddl/Index/AbstractIndex.php b/src/Sql/Ddl/Index/AbstractIndex.php index bf6cedec..dff4e666 100644 --- a/src/Sql/Ddl/Index/AbstractIndex.php +++ b/src/Sql/Ddl/Index/AbstractIndex.php @@ -6,4 +6,7 @@ use PhpDb\Sql\Ddl\Constraint\AbstractConstraint; +/** + * @api + */ abstract class AbstractIndex extends AbstractConstraint {} diff --git a/src/Sql/Ddl/Index/Index.php b/src/Sql/Ddl/Index/Index.php index 431b5f5a..1e142aba 100644 --- a/src/Sql/Ddl/Index/Index.php +++ b/src/Sql/Ddl/Index/Index.php @@ -11,6 +11,9 @@ use function implode; use function str_replace; +/** + * @api + */ class Index extends AbstractIndex { protected string $specification = 'INDEX %s(...)'; diff --git a/test/unit/Sql/Ddl/AlterTableTest.php b/test/unit/Sql/Ddl/AlterTableTest.php index f3625fb8..1200b365 100644 --- a/test/unit/Sql/Ddl/AlterTableTest.php +++ b/test/unit/Sql/Ddl/AlterTableTest.php @@ -225,6 +225,14 @@ public function getRawStateReturnsAllState(): void static::assertEquals(['drop_idx'], $rawState[AlterTable::DROP_INDEXES]); } + #[Test] + public function getRawStateReturnsTableIdentifierForTable(): void + { + $table = new TableIdentifier('foo', 'bar'); + + static::assertSame($table, (new AlterTable($table))->getRawState(AlterTable::TABLE)); + } + #[Test] public function getRawStateWithInvalidKey(): void { diff --git a/test/unit/Sql/Ddl/CreateTableTest.php b/test/unit/Sql/Ddl/CreateTableTest.php index 69d7dcac..3fa9bb99 100644 --- a/test/unit/Sql/Ddl/CreateTableTest.php +++ b/test/unit/Sql/Ddl/CreateTableTest.php @@ -185,6 +185,14 @@ public function getRawStateReturnsAllState(): void static::assertEquals([$con], $rawState[CreateTable::CONSTRAINTS]); } + #[Test] + public function getRawStateReturnsTableIdentifierForTable(): void + { + $table = new TableIdentifier('foo', 'bar'); + + static::assertSame($table, (new CreateTable($table))->getRawState(CreateTable::TABLE)); + } + #[Test] public function getRawStateWithInvalidKey(): void {