Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 12 additions & 42 deletions analyzer-baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1173,7 +1173,7 @@ count = 1
[[issues]]
file = "src/Adapter/Profiler/Profiler.php"
code = "invalid-property-assignment-value"
message = '''Invalid type for property `$profiles`: expected `array<array-key, array{'elapse': float|null, 'end': float|null, 'parameters': PhpDb\Adapter\ParameterContainer|null, 'sql': string, 'start': float}>`, but got `array<array-key, array{'elapse': float|null, 'end': float|null, 'parameters': PhpDb\Adapter\ParameterContainer|null, 'sql': null|string, 'start': float}>`.'''
message = '''Invalid type for property `$profiles`: expected `array<array-key, array{'elapse': float|null, 'end': float|null, 'parameters': PhpDb\Adapter\ParameterContainer|null, 'sql': string, 'start': float}>`, but got `non-empty-array<array-key, array{'elapse': float|null, 'end': float|null, 'parameters': PhpDb\Adapter\ParameterContainer|null, 'sql': null|string, 'start': float}>`.'''
count = 1

[[issues]]
Expand All @@ -1194,24 +1194,6 @@ code = "missing-property-type"
message = "Property `$profiles` is missing a type hint."
count = 1

[[issues]]
file = "src/Adapter/Profiler/Profiler.php"
code = "possibly-null-operand"
message = "Right operand in arithmetic operation might be `null` (type `float|null`)."
count = 1

[[issues]]
file = "src/Adapter/Profiler/Profiler.php"
code = "possibly-undefined-int-array-index"
message = '''Possibly undefined array key `int` accessed on `array<array-key, array{'elapse': float|null, 'end': float|null, 'parameters': PhpDb\Adapter\ParameterContainer|null, 'sql': string, 'start': float}>`.'''
count = 1

[[issues]]
file = "src/Adapter/Profiler/Profiler.php"
code = "possibly-undefined-string-array-index"
message = '''Possibly undefined array key accessed on `array{'elapse': float|null, 'end': float, 'parameters': PhpDb\Adapter\ParameterContainer|null, 'sql': string, 'start': float}|array{'end': float}`.'''
count = 1

[[issues]]
file = "src/Adapter/Profiler/Profiler.php"
code = "unhandled-thrown-type"
Expand Down Expand Up @@ -2289,7 +2271,7 @@ count = 1
[[issues]]
file = "src/Sql/AbstractSql.php"
code = "less-specific-nested-argument-type"
message = "Argument type mismatch for argument #2 of `vsprintf`: expected `array<array-key, Stringable|null|scalar>`, but provided type `array{}|non-empty-list<mixed>` is less specific."
message = "Argument type mismatch for argument #2 of `vsprintf`: expected `array<array-key, Stringable|null|scalar>`, but provided type `list<mixed>` is less specific."
count = 1

[[issues]]
Expand Down Expand Up @@ -3321,7 +3303,13 @@ 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<array-key, array<array-key, mixed>|string>`."
message = "Possibly undefined array key `string('combinedBy')` accessed on `array<array-key, array<array-key, mixed>>`."
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<array-key, string>`."
count = 1

[[issues]]
Expand Down Expand Up @@ -4353,7 +4341,7 @@ count = 1
[[issues]]
file = "src/Sql/Predicate/PredicateSet.php"
code = "less-specific-nested-argument-type"
message = "Argument type mismatch for argument #2 of `implode`: expected `array<array-key, Stringable|null|scalar>|null`, but provided type `array{}|non-empty-list<mixed>` is less specific."
message = "Argument type mismatch for argument #2 of `implode`: expected `array<array-key, Stringable|null|scalar>|null`, but provided type `list<mixed>` is less specific."
count = 1

[[issues]]
Expand All @@ -4365,7 +4353,7 @@ count = 1
[[issues]]
file = "src/Sql/Predicate/PredicateSet.php"
code = "less-specific-nested-return-statement"
message = '''Returned type `array{'spec': string, 'values': array{}|list<mixed>}` is less specific than the declared return type `array{'spec': string, 'values': array<array-key, PhpDb\Sql\ArgumentInterface>}` for function `PhpDb\Sql\Predicate\PredicateSet::getExpressionData` due to nested 'mixed'.'''
message = '''Returned type `array{'spec': string, 'values': list<mixed>}` is less specific than the declared return type `array{'spec': string, 'values': array<array-key, PhpDb\Sql\ArgumentInterface>}` for function `PhpDb\Sql\Predicate\PredicateSet::getExpressionData` due to nested 'mixed'.'''
count = 1

[[issues]]
Expand Down Expand Up @@ -4623,7 +4611,7 @@ count = 1
[[issues]]
file = "src/Sql/Select.php"
code = "invalid-property-assignment-value"
message = "Invalid type for property `$specifications`: expected `array<array-key, array<array-key, mixed>>|array<array-key, string>`, but got `array<array-key, array<array-key, mixed>|string>`."
message = "Invalid type for property `$specifications`: expected `array<array-key, array<array-key, mixed>>|array<array-key, string>`, but got `non-empty-array<array-key, array<array-key, mixed>|string>`."
count = 1

[[issues]]
Expand Down Expand Up @@ -5898,30 +5886,12 @@ code = "class-must-be-final"
message = 'Class `PhpDb\TableGateway\Feature\GlobalAdapterFeature` should be declared `final`.'
count = 1

[[issues]]
file = "src/TableGateway/Feature/GlobalAdapterFeature.php"
code = "invalid-return-statement"
message = 'Invalid return type for function `PhpDb\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter`: expected `PhpDb\Adapter\AdapterInterface`, but found `PhpDb\Adapter\AdapterInterface|null`.'
count = 1

[[issues]]
file = "src/TableGateway/Feature/GlobalAdapterFeature.php"
code = "missing-constructor"
message = 'Class `PhpDb\TableGateway\Feature\GlobalAdapterFeature` has typed properties without default values but no constructor to initialize them.'
count = 1

[[issues]]
file = "src/TableGateway/Feature/GlobalAdapterFeature.php"
code = "nullable-return-statement"
message = 'Function `PhpDb\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter` is declared to return `PhpDb\Adapter\AdapterInterface` but possibly returns a nullable value (inferred as `PhpDb\Adapter\AdapterInterface|null`).'
count = 1

[[issues]]
file = "src/TableGateway/Feature/GlobalAdapterFeature.php"
code = "possibly-undefined-string-array-index"
message = '''Possibly undefined array key `class-string('PhpDb\TableGateway\Feature\GlobalAdapterFeature')` accessed on `array<array-key, PhpDb\Adapter\AdapterInterface>`.'''
count = 1

[[issues]]
file = "src/TableGateway/Feature/MasterSlaveFeature.php"
code = "class-must-be-final"
Expand Down
9 changes: 7 additions & 2 deletions docs/book/sql-ddl/columns.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ use PhpDb\Sql\Ddl\Column\Integer;
$column = new Integer('user_id');
$column = new Integer('count', false, 0); // NOT NULL with default 0

// With display length (platform-specific)
// With display width (platform-specific)
$column = new Integer('user_id');
$column->setOption('length', 11);
$column->setOption('length', 11); // INTEGER(11)
```

The `length` option is a display width rendered directly after the type, as `INTEGER(11)`. It
accepts an `int` or a string of digits. MySQL deprecated integer display widths in 8.0.17, and the
MySQL platform decorator drops the attribute (see phpdb-mysql#81); other platforms render it as
given.

**Constructor:**

```php
Expand Down
10 changes: 6 additions & 4 deletions src/Metadata/Source/AbstractSource.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
$schema = $this->defaultSchema;
}

$this->loadColumnData($table, $schema);

Check warning on line 127 in src/Metadata/Source/AbstractSource.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $schema = $this->defaultSchema; } - $this->loadColumnData($table, $schema); + $info = $this->data['columns'][$schema][$table][$columnName] ?? null; if (null === $info) {

$info = $this->data['columns'][$schema][$table][$columnName] ?? null;
if (null === $info) {
Expand All @@ -142,10 +142,10 @@
$column->setCharacterOctetLength(
$info['character_octet_length'] ? (int) $info['character_octet_length'] : null,
);
$column->setNumericPrecision(

Check warning on line 145 in src/Metadata/Source/AbstractSource.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $column->setCharacterOctetLength( $info['character_octet_length'] ? (int) $info['character_octet_length'] : null, ); - $column->setNumericPrecision( - $info['numeric_precision'] ? (int) $info['numeric_precision'] : null, - ); + $column->setNumericScale( $info['numeric_scale'] ? (int) $info['numeric_scale'] : null, );
$info['numeric_precision'] ? (int) $info['numeric_precision'] : null,
);
$column->setNumericScale(

Check warning on line 148 in src/Metadata/Source/AbstractSource.php

View workflow job for this annotation

GitHub Actions / qa / Mutation testing

Escaped Mutant for Mutator "MethodCallRemoval": @@ @@ $column->setNumericPrecision( $info['numeric_precision'] ? (int) $info['numeric_precision'] : null, ); - $column->setNumericScale( - $info['numeric_scale'] ? (int) $info['numeric_scale'] : null, - ); + $column->setNumericUnsigned($info['numeric_unsigned']); $column->setErratas($info['erratas']);
$info['numeric_scale'] ? (int) $info['numeric_scale'] : null,
);
$column->setNumericUnsigned($info['numeric_unsigned']);
Expand Down Expand Up @@ -693,15 +693,17 @@
* Prepare data hierarchy
*
* The by-reference walk builds arbitrary depths of the hierarchy, which
* cannot be expressed against the MetadataData shape.
* cannot be expressed against the MetadataData shape. The reference is
* re-typed as a plain array so the analyzer does not re-derive the shape
* on every iteration, which does not terminate in useful time on Mago
* 1.47.5 and later.
*
* @mago-expect analysis:possibly-undefined-string-array-index
* @mago-expect analysis:possibly-undefined-int-array-index
* @mago-expect analysis:possibly-null-array-access
* @mago-expect analysis:mixed-assignment
* @mago-expect lint:no-isset
*/
protected function prepareDataHierarchy(string $type, string ...$keys): void
{
/** @var array<array-key, mixed> $data */
$data = &$this->data;
foreach ([$type, ...$keys] as $key) {
if (! isset($data[$key])) {
Expand Down
2 changes: 1 addition & 1 deletion src/Sql/Ddl/Column/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public function setNullable(bool $nullable): static
return $this;
}

public function setOption(string $name, bool|string $value): static
public function setOption(string $name, bool|int|string $value): static
{
$this->options[$name] = $value;
return $this;
Expand Down
47 changes: 44 additions & 3 deletions src/Sql/Ddl/Column/Integer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,61 @@
namespace PhpDb\Sql\Ddl\Column;

use Override;
use PhpDb\Sql\Argument\Literal;
use PhpDb\Sql\Exception\InvalidArgumentException;

use function array_splice;
use function ctype_digit;
use function is_int;
use function is_string;
use function sprintf;
use function strlen;
use function substr;

class Integer extends Column
{
/** @inheritDoc */
/**
* Renders the display width from the "length" option in parentheses directly after the type,
* ahead of the nullability and default clauses.
*
* @inheritDoc
* @throws InvalidArgumentException When the "length" option is not a non-negative integer.
*/
#[Override]
public function getExpressionData(): array
{
$expressionData = parent::getExpressionData();
$options = $this->getOptions();

if (isset($options['length'])) {
$expressionData['spec'] .= " ({$options['length']})";
if (! isset($options['length'])) {
return $expressionData;
}

$displayWidth = $this->normaliseDisplayWidth($options['length']);
$attributes = substr($expressionData['spec'], strlen($this->specification));

$expressionData['spec'] = "{$this->specification}(%s){$attributes}";
array_splice($expressionData['values'], offset: 2, length: 0, replacement: [new Literal($displayWidth)]);

return $expressionData;
}

/**
* @throws InvalidArgumentException When the value is not a non-negative integer.
*/
private function normaliseDisplayWidth(mixed $length): string
{
if (is_int($length) && $length >= 0) {
return (string) $length;
}

if (is_string($length) && ctype_digit($length)) {
return $length;
}

throw new InvalidArgumentException(sprintf(
'Column "%s" length option must be a non-negative integer',
$this->name,
));
}
}
8 changes: 4 additions & 4 deletions src/TableGateway/AbstractTableGateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,8 @@ protected function executeSelect(Select $select): ResultSetInterface

if (
isset($selectState['columns'])
&& [Select::SQL_STAR] === $selectState['columns']
&& [] !== $this->columns
&& [Select::SQL_STAR] === $selectState['columns']
&& [] !== $this->columns
) {
$select->columns($this->columns);
}
Expand Down Expand Up @@ -430,8 +430,8 @@ public function __clone(): void
$this->table = clone $this->table;
} elseif (
is_array($this->table)
&& count($this->table) === 1
&& is_object(reset($this->table))
&& count($this->table) === 1
&& is_object(reset($this->table))
) {
foreach ($this->table as &$tableObject) {
$tableObject = clone $tableObject;
Expand Down
15 changes: 15 additions & 0 deletions test/unit/Sql/Ddl/Column/BigIntegerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,28 @@
use PhpDb\Sql\Argument;
use PhpDb\Sql\Ddl\Column\BigInteger;
use PhpDb\Sql\Ddl\Column\Column;
use PhpDb\Sql\Ddl\Column\Integer;
use PhpDb\Sql\Ddl\CreateTable;
use PHPUnit\Framework\Attributes\CoversMethod;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversMethod(BigInteger::class, '__construct')]
#[CoversMethod(Column::class, 'getExpressionData')]
#[CoversMethod(Integer::class, 'getExpressionData')]
#[Group('unit')]
final class BigIntegerTest extends TestCase
{
#[Test]
public function rendersLengthDirectlyAfterType(): void
{
$createTable = new CreateTable('t');
$createTable->addColumn(new BigInteger('i', false, null, ['length' => 20]));

static::assertSame("CREATE TABLE \"t\" ( \n \"i\" BIGINT(20) NOT NULL \n)", $createTable->getSqlString());
}

public function testGetExpressionData(): void
{
$column = new BigInteger('foo');
Expand Down
12 changes: 12 additions & 0 deletions test/unit/Sql/Ddl/Column/ColumnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PhpDb\Sql\Ddl\Constraint\PrimaryKey;
use PHPUnit\Framework\Attributes\CoversMethod;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

#[CoversMethod(Column::class, '__construct')]
Expand All @@ -28,6 +29,17 @@
#[Group('unit')]
final class ColumnTest extends TestCase
{
#[Test]
public function setOptionAcceptsIntegerValue(): void
{
$column = new Column();

$result = $column->setOption('length', 11);

static::assertSame($column, $result);
static::assertSame(['length' => 11], $column->getOptions());
}

public function testAddConstraintAppendsConstraintToColumn(): void
{
$column = new Column('id');
Expand Down
Loading
Loading