From a2ce7b0817c9d3a72ceb02dacb17aa8419a226b3 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Nov 2025 22:56:55 +0300 Subject: [PATCH 1/8] implement --- src/Schema/Column/AbstractColumnFactory.php | 7 ++---- ...php => AbstractColumnDefinitionParser.php} | 22 ++++++------------- .../CommonColumnDefinitionParserTest.php | 11 +++------- tests/Common/CommonColumnTest.php | 5 ++++- tests/Db/Schema/Column/ColumnFactoryTest.php | 22 +++++++++++++++++-- .../Stub/StubColumnDefinitionParser.php | 15 +++++++++++++ tests/Support/Stub/StubColumnFactory.php | 6 +++++ 7 files changed, 57 insertions(+), 31 deletions(-) rename src/Syntax/{ColumnDefinitionParser.php => AbstractColumnDefinitionParser.php} (89%) create mode 100644 tests/Support/Stub/StubColumnDefinitionParser.php diff --git a/src/Schema/Column/AbstractColumnFactory.php b/src/Schema/Column/AbstractColumnFactory.php index 5f2c217d3..73c07adb1 100644 --- a/src/Schema/Column/AbstractColumnFactory.php +++ b/src/Schema/Column/AbstractColumnFactory.php @@ -8,7 +8,7 @@ use Yiisoft\Db\Constant\ColumnType; use Yiisoft\Db\Constant\PseudoType; use Yiisoft\Db\Expression\Expression; -use Yiisoft\Db\Syntax\ColumnDefinitionParser; +use Yiisoft\Db\Syntax\ColumnDefinitionParserInterface; use function array_diff_key; use function array_key_exists; @@ -221,10 +221,7 @@ public function fromType(string $type, array $info = []): ColumnInterface /** * Returns the column definition parser. */ - protected function columnDefinitionParser(): ColumnDefinitionParser - { - return new ColumnDefinitionParser(); - } + abstract protected function columnDefinitionParser(): ColumnDefinitionParserInterface; /** * @psalm-param ColumnInfo $info diff --git a/src/Syntax/ColumnDefinitionParser.php b/src/Syntax/AbstractColumnDefinitionParser.php similarity index 89% rename from src/Syntax/ColumnDefinitionParser.php rename to src/Syntax/AbstractColumnDefinitionParser.php index 0fcb775be..99029e541 100644 --- a/src/Syntax/ColumnDefinitionParser.php +++ b/src/Syntax/AbstractColumnDefinitionParser.php @@ -29,7 +29,7 @@ * unsigned?: bool * } */ -class ColumnDefinitionParser +abstract class AbstractColumnDefinitionParser implements ColumnDefinitionParserInterface { /** * Parses column definition string. @@ -62,11 +62,7 @@ public function parse(string $definition): array $info = ['type' => $type]; if (isset($matches[2]) && $matches[2] !== '') { - if ($type === 'enum') { - $info += $this->enumInfo($matches[2]); - } else { - $info += $this->sizeInfo($matches[2]); - } + $info += $this->parseTypeParams($type, $matches[2]); } if (isset($matches[3])) { @@ -80,14 +76,10 @@ public function parse(string $definition): array } /** - * @psalm-return array{enumValues: list} + * @psalm-param non-empty-string $params + * @psalm-return array{enumValues?: list, size?: int, scale?: int} */ - protected function enumInfo(string $values): array - { - preg_match_all("/'([^']*)'/", $values, $matches); - - return ['enumValues' => $matches[1]]; - } + abstract protected function parseTypeParams(string $type, string $params): array; /** * @psalm-return ExtraInfo @@ -169,9 +161,9 @@ protected function parseBoolValue(string $extra, string $pattern, string $name, /** * @psalm-return array{size: int, scale?: int} */ - protected function sizeInfo(string $size): array + protected function parseSizeInfo(string $params): array { - $values = explode(',', $size); + $values = explode(',', $params); $info = [ 'size' => (int) $values[0], diff --git a/tests/Common/CommonColumnDefinitionParserTest.php b/tests/Common/CommonColumnDefinitionParserTest.php index 89b2b9c7b..878d83ee9 100644 --- a/tests/Common/CommonColumnDefinitionParserTest.php +++ b/tests/Common/CommonColumnDefinitionParserTest.php @@ -6,12 +6,10 @@ use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\TestCase; -use Yiisoft\Db\Syntax\ColumnDefinitionParser; +use Yiisoft\Db\Syntax\AbstractColumnDefinitionParser; +use Yiisoft\Db\Syntax\ColumnDefinitionParserInterface; use Yiisoft\Db\Tests\Provider\ColumnDefinitionParserProvider; -/** - * @group db - */ abstract class CommonColumnDefinitionParserTest extends TestCase { #[DataProviderExternal(ColumnDefinitionParserProvider::class, 'parse')] @@ -22,8 +20,5 @@ public function testParse(string $definition, array $expected): void $this->assertSame($expected, $parser->parse($definition)); } - protected function createColumnDefinitionParser(): ColumnDefinitionParser - { - return new ColumnDefinitionParser(); - } + abstract protected function createColumnDefinitionParser(): ColumnDefinitionParserInterface; } diff --git a/tests/Common/CommonColumnTest.php b/tests/Common/CommonColumnTest.php index f69e087eb..3b74b2393 100644 --- a/tests/Common/CommonColumnTest.php +++ b/tests/Common/CommonColumnTest.php @@ -215,7 +215,10 @@ public function testDateTimeColumn(float|int|string $value, array $expected): vo $values = array_fill_keys(array_keys($expected), $value); - $expected = array_map(static fn(string $value) => new DateTimeImmutable($value, new DateTimeZone('UTC')), $expected); + $expected = array_map( + static fn(string $value) => new DateTimeImmutable($value, new DateTimeZone('UTC')), + $expected, + ); $command->insert(static::DATETIME_COLUMN_TABLE, $values)->execute(); diff --git a/tests/Db/Schema/Column/ColumnFactoryTest.php b/tests/Db/Schema/Column/ColumnFactoryTest.php index c3cb57e84..2b19f6cc6 100644 --- a/tests/Db/Schema/Column/ColumnFactoryTest.php +++ b/tests/Db/Schema/Column/ColumnFactoryTest.php @@ -4,13 +4,17 @@ namespace Yiisoft\Db\Tests\Db\Schema\Column; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\TestCase; use Yiisoft\Db\Constant\ColumnType; use Yiisoft\Db\Schema\Column\AbstractArrayColumn; +use Yiisoft\Db\Schema\Column\ArrayColumn; use Yiisoft\Db\Schema\Column\ArrayLazyColumn; +use Yiisoft\Db\Schema\Column\BigIntColumn; use Yiisoft\Db\Schema\Column\ColumnInterface; use Yiisoft\Db\Schema\Column\DateTimeColumn; +use Yiisoft\Db\Schema\Column\DoubleColumn; use Yiisoft\Db\Schema\Column\IntegerColumn; use Yiisoft\Db\Schema\Column\JsonLazyColumn; use Yiisoft\Db\Schema\Column\StringColumn; @@ -76,7 +80,22 @@ public function testFromDbType(string $dbType, string $expectedType, string $exp $this->assertSame($dbType, $column->getDbType()); } - #[DataProviderExternal(ColumnFactoryProvider::class, 'definitions')] + public static function dataFromDefinition(): array + { + return [ + // definition, expected type, expected instance of, expected column method results + '' => ['', new StringColumn(dbType: '')], + 'text' => ['text', new StringColumn(ColumnType::TEXT, dbType: 'text')], + 'text NOT NULL' => ['text NOT NULL', new StringColumn(ColumnType::TEXT, dbType: 'text', notNull: true)], + 'char(1)' => ['char(1)', new StringColumn(ColumnType::CHAR, dbType: 'char')], + 'decimal(10,2)' => ['decimal(10,2)', new DoubleColumn(ColumnType::DECIMAL, dbType: 'decimal')], + 'bigint UNSIGNED' => ['bigint UNSIGNED', new BigIntColumn(dbType: 'bigint', unsigned: true)], + 'integer[]' => ['integer[]', new ArrayColumn(dbType: 'integer', column: new IntegerColumn(dbType: 'integer'))], + 'string(126)[][]' => ['string(126)[][]', new ArrayColumn(dimension: 2, column: new StringColumn())], + ]; + } + + #[DataProvider('dataFromDefinition')] public function testFromDefinition(string $definition, ColumnInterface $expected): void { $columnFactory = new StubColumnFactory(); @@ -115,7 +134,6 @@ public function testFromDefinitionWithExtra(): void $this->assertInstanceOf(StringColumn::class, $column); $this->assertSame('char', $column->getType()); - $this->assertSame(1, $column->getSize()); $this->assertSame('INVISIBLE COLLATE utf8mb4', $column->getExtra()); } diff --git a/tests/Support/Stub/StubColumnDefinitionParser.php b/tests/Support/Stub/StubColumnDefinitionParser.php new file mode 100644 index 000000000..64ccfa817 --- /dev/null +++ b/tests/Support/Stub/StubColumnDefinitionParser.php @@ -0,0 +1,15 @@ + $this->isType($dbType), }; } + + protected function columnDefinitionParser(): ColumnDefinitionParserInterface + { + return new StubColumnDefinitionParser(); + } } From eda27b0d94bf021b9e10bee20a932ad9bceded87 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Nov 2025 23:47:33 +0300 Subject: [PATCH 2/8] improve --- src/Syntax/AbstractColumnDefinitionParser.php | 35 ++++++++++++++----- .../ColumnDefinitionParserProvider.php | 2 -- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/Syntax/AbstractColumnDefinitionParser.php b/src/Syntax/AbstractColumnDefinitionParser.php index 99029e541..438b09d73 100644 --- a/src/Syntax/AbstractColumnDefinitionParser.php +++ b/src/Syntax/AbstractColumnDefinitionParser.php @@ -56,23 +56,42 @@ abstract class AbstractColumnDefinitionParser implements ColumnDefinitionParserI */ public function parse(string $definition): array { - preg_match('/^(\w*)(?:\(([^)]+)\))?(\[[\d\[\]]*\])?\s*/', $definition, $matches); + [$type, $typeParams, $dimension, $extraInfo] = $this->parseDefinition($definition); - $type = strtolower($matches[1]); + $type = strtolower($type); $info = ['type' => $type]; - if (isset($matches[2]) && $matches[2] !== '') { - $info += $this->parseTypeParams($type, $matches[2]); + if ($typeParams !== '' && $typeParams !== null) { + $info += $this->parseTypeParams($type, $typeParams); } - if (isset($matches[3])) { + if (isset($dimension)) { /** @psalm-var positive-int */ - $info['dimension'] = substr_count($matches[3], '['); + $info['dimension'] = substr_count($dimension, '['); } - $extra = substr($definition, strlen($matches[0])); + return $info + $this->extraInfo($extraInfo); + } - return $info + $this->extraInfo($extra); + /** + * Parse the column definition into its components: + * - type + * - type parameters + * - dimension + * - extra information + * + * @psalm-return list{string, string|null, string|null, string|null} + */ + protected function parseDefinition(string $definition): array + { + preg_match('/^(\w*)(?:\(([^)]+)\))?(\[[\d\[\]]*\])?\s*/', $definition, $matches); + + return [ + $matches[1], + $matches[2] ?? null, + $matches[3] ?? null, + substr($definition, strlen($matches[0])), + ]; } /** diff --git a/tests/Provider/ColumnDefinitionParserProvider.php b/tests/Provider/ColumnDefinitionParserProvider.php index 91ce5a7fe..6fe563200 100644 --- a/tests/Provider/ColumnDefinitionParserProvider.php +++ b/tests/Provider/ColumnDefinitionParserProvider.php @@ -28,8 +28,6 @@ public static function parse(): array ['int DEFAULT (1 + 2)', ['type' => 'int', 'defaultValueRaw' => '(1 + 2)']], ["int COMMENT '''Quoted'' comment'", ['type' => 'int', 'comment' => "'Quoted' comment"]], ['int CHECK (value > (1 + 5))', ['type' => 'int', 'check' => 'value > (1 + 5)']], - ["enum('a','b','c')", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c']]], - ["enum('a','b','c') NOT NULL", ['type' => 'enum', 'enumValues' => ['a', 'b', 'c'], 'notNull' => true]], ['int[]', ['type' => 'int', 'dimension' => 1]], ['string(126)[][]', ['type' => 'string', 'size' => 126, 'dimension' => 2]], ]; From 11690f83fc7439a68b4288d6af5aedc443d7b813 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Nov 2025 23:48:44 +0300 Subject: [PATCH 3/8] interface --- .../ColumnDefinitionParserInterface.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/Syntax/ColumnDefinitionParserInterface.php diff --git a/src/Syntax/ColumnDefinitionParserInterface.php b/src/Syntax/ColumnDefinitionParserInterface.php new file mode 100644 index 000000000..f9040acdb --- /dev/null +++ b/src/Syntax/ColumnDefinitionParserInterface.php @@ -0,0 +1,36 @@ +, + * extra?: string, + * notNull?: bool, + * scale?: int, + * size?: int, + * type: lowercase-string, + * unique?: bool, + * unsigned?: bool, + * } + */ + public function parse(string $definition): array; +} From 56367f0a510fc1d6d7bbd65849297b74fc78ad81 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Tue, 25 Nov 2025 23:57:33 +0300 Subject: [PATCH 4/8] info --- CHANGELOG.md | 2 ++ UPGRADE.md | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9dcc03227..688984e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -177,6 +177,8 @@ - Bug #1103: Fix view names' cache refreshing after "drop table" command execution (@vjik) - Chg #1103: Remove `AbstractCommand::refreshTableSchema()` method (@vjik) - Chg #1106: Remove parameters from `PdoConnectionInterface::getActivePdo()` method (@vjik) +- Chg #1108: Refactor `ColumnDefinitionParser` and rename to `AbstractColumnDefinitionParser`, + add `ColumnDefinitionParserInterface` (@vjik) ## 1.3.0 March 21, 2024 diff --git a/UPGRADE.md b/UPGRADE.md index 9a48f2862..45d28e21a 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -97,6 +97,12 @@ and the following changes were made: - added `DEFAULT_TYPE` constant to `AbstractColumn` class; - added method chaining. +### `ColumnDefinitionParser` changes + +- Rename to `AbstractColumnDefinitionParser`; +- Make class abstract and add abstract method `parseTypeParams()`; +- Rename method `sizeInfo()` to `parseSizeInfo()`. + ### Changes in constraint classes - Remove `Constraint` class; @@ -293,3 +299,4 @@ Each table column has its own class in the `Yiisoft\Db\Schema\Column` namespace - Change `QueryPartsInterface::withQueries()` parameter to variadic with type `WithQuery`; - In `DQLQueryBuilderInterface::buildWithQueries()` method change first parameter type form `array[]` to `WithQuery[]`; - Remove `AbstractCommand::refreshTableSchema()` method; +- Make method `AbstractColumnFactory::columnDefinitionParser()` abstract; From d9a9cfd682418f45205be02b90441ed57055a1d5 Mon Sep 17 00:00:00 2001 From: vjik <525501+vjik@users.noreply.github.com> Date: Tue, 25 Nov 2025 21:10:02 +0000 Subject: [PATCH 5/8] Apply PHP CS Fixer and Rector changes (CI) --- src/Syntax/AbstractColumnDefinitionParser.php | 1 - tests/Common/CommonColumnDefinitionParserTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Syntax/AbstractColumnDefinitionParser.php b/src/Syntax/AbstractColumnDefinitionParser.php index 438b09d73..41ed7e651 100644 --- a/src/Syntax/AbstractColumnDefinitionParser.php +++ b/src/Syntax/AbstractColumnDefinitionParser.php @@ -6,7 +6,6 @@ use function explode; use function preg_match; -use function preg_match_all; use function preg_replace; use function str_replace; use function strlen; diff --git a/tests/Common/CommonColumnDefinitionParserTest.php b/tests/Common/CommonColumnDefinitionParserTest.php index 878d83ee9..93c784025 100644 --- a/tests/Common/CommonColumnDefinitionParserTest.php +++ b/tests/Common/CommonColumnDefinitionParserTest.php @@ -6,7 +6,6 @@ use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\TestCase; -use Yiisoft\Db\Syntax\AbstractColumnDefinitionParser; use Yiisoft\Db\Syntax\ColumnDefinitionParserInterface; use Yiisoft\Db\Tests\Provider\ColumnDefinitionParserProvider; From 28bc7dc6a623e7ec5723569b02d789529378418a Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 26 Nov 2025 00:17:48 +0300 Subject: [PATCH 6/8] improve --- src/Syntax/AbstractColumnDefinitionParser.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Syntax/AbstractColumnDefinitionParser.php b/src/Syntax/AbstractColumnDefinitionParser.php index 438b09d73..7332340a5 100644 --- a/src/Syntax/AbstractColumnDefinitionParser.php +++ b/src/Syntax/AbstractColumnDefinitionParser.php @@ -70,7 +70,11 @@ public function parse(string $definition): array $info['dimension'] = substr_count($dimension, '['); } - return $info + $this->extraInfo($extraInfo); + if ($extraInfo !== '' && $extraInfo !== null) { + $info += $this->extraInfo($extraInfo); + } + + return $info; } /** @@ -101,14 +105,11 @@ protected function parseDefinition(string $definition): array abstract protected function parseTypeParams(string $type, string $params): array; /** + * @psalm-param non-empty-string $extra * @psalm-return ExtraInfo */ protected function extraInfo(string $extra): array { - if (empty($extra)) { - return []; - } - $info = []; $bracketsPattern = '(\(((?>[^()]+)|(?-2))*\))'; $defaultPattern = "/\\s*\\bDEFAULT\\s+('(?:[^']|'')*'|\"(?:[^\"]|\"\")*\"|[^(\\s]*$bracketsPattern?\\S*)/i"; From 9bab05e79a853423587b19c83297015a6d668691 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 26 Nov 2025 00:19:56 +0300 Subject: [PATCH 7/8] fix ci --- .github/workflows/db-mariadb.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/db-mariadb.yml b/.github/workflows/db-mariadb.yml index 134faef92..14495ce77 100644 --- a/.github/workflows/db-mariadb.yml +++ b/.github/workflows/db-mariadb.yml @@ -29,6 +29,7 @@ jobs: env: COMPOSER_ROOT_VERSION: dev-master EXTENSIONS: pdo, pdo_mysql + YII_MYSQL_TYPE: 'mariadb' runs-on: ubuntu-latest From 3d8f2790712b3271dbea4133946ce051d8f48005 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Wed, 26 Nov 2025 00:22:47 +0300 Subject: [PATCH 8/8] Update CHANGELOG.md Co-authored-by: Alexander Makarov --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 688984e7c..bb1cfa7d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -177,7 +177,7 @@ - Bug #1103: Fix view names' cache refreshing after "drop table" command execution (@vjik) - Chg #1103: Remove `AbstractCommand::refreshTableSchema()` method (@vjik) - Chg #1106: Remove parameters from `PdoConnectionInterface::getActivePdo()` method (@vjik) -- Chg #1108: Refactor `ColumnDefinitionParser` and rename to `AbstractColumnDefinitionParser`, +- Chg #1108: Refactor `ColumnDefinitionParser` and rename it to `AbstractColumnDefinitionParser`, add `ColumnDefinitionParserInterface` (@vjik) ## 1.3.0 March 21, 2024