From b95926b40175efd5588954b7356b03725fb7713e Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 09:56:53 -0400 Subject: [PATCH 01/23] Create fork - prepare to add SQLite driver --- .gitignore | 1 + composer.json | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 27a3df6..041cd14 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea /vendor/ composer.lock /tests/coverage.html diff --git a/composer.json b/composer.json index 191e5e4..b485479 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,10 @@ "classmap": ["src/"] }, "require-dev": { - "nette/tester": "^2.5" - } + "nette/tester": "^2.5", + "phpstan/phpstan": "^2.1" + }, + "suggest": { + "ext-sqlite3": "For SQLite driver" + } } From 1bf4cac69c980d224a2e5976fc27bbb2ecd36c94 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 10:00:29 -0400 Subject: [PATCH 02/23] Create SQLite driver --- src/Drivers/SqliteDriver.php | 58 ++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/Drivers/SqliteDriver.php diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php new file mode 100644 index 0000000..86c7570 --- /dev/null +++ b/src/Drivers/SqliteDriver.php @@ -0,0 +1,58 @@ +escapeText($value->format('Y-m-d')); + } + + /** + * @param DateTimeInterface|string $value + * @throws Exception + */ + public function escapeDateTime($value): string + { + if (!($value instanceof DateTimeInterface)) { + $value = new DateTimeImmutable($value); + } + + return $this->escapeText($value->format('Y-m-d H:i:s')); + } + } From bd7163847a91cfce77ebe1617980e932276092b3 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 10:06:02 -0400 Subject: [PATCH 03/23] Improve types for drivers --- src/Drivers/MysqlDriver.php | 39 +++++++++++++++++++-------------- src/Drivers/SqliteDriver.php | 30 +++++++++++++------------- src/IDriver.php | 41 ++++++++++------------------------- tests/libs/DummyDriver.php | 42 +++++++++++++++++++++--------------- 4 files changed, 74 insertions(+), 78 deletions(-) diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index 5e21111..60c0b7f 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -5,19 +5,19 @@ namespace CzProject\SqlGenerator\Drivers; use CzProject\SqlGenerator\IDriver; + use DateTimeInterface; + use DateTimeImmutable; + use Exception; - - class MysqlDriver implements IDriver + class MysqlDriver implements IDriver { - public function escapeIdentifier($value) - { + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html // @see http://api.dibiphp.com/2.3.2/source-drivers.DibiMySqlDriver.php.html#307 return '`' . str_replace('`', '``', $value) . '`'; } - - public function escapeText($value) + public function escapeText(string $value): string { // https://dev.mysql.com/doc/refman/5.5/en/string-literals.html // http://us3.php.net/manual/en/function.mysql-real-escape-string.php#101248 @@ -28,27 +28,34 @@ public function escapeText($value) ) . '\''; } - - public function escapeBool($value) + public function escapeBool(bool $value): string { return $value ? '1' : '0'; } - - public function escapeDate($value) + public function escapeDate(DateTimeInterface|string $value): string { - if (!($value instanceof \DateTime) && !($value instanceof \DateTimeInterface)) { - $value = new \DateTime($value); + if (!($value instanceof \DateTimeInterface)) { + try { + $value = new DateTimeImmutable($value); + } catch (Exception $e) { + return ''; + } } + return $value->format("'Y-m-d'"); } - - public function escapeDateTime($value) + public function escapeDateTime(DateTimeInterface|string $value): string { - if (!($value instanceof \DateTime) && !($value instanceof \DateTimeInterface)) { - $value = new \DateTime($value); + if (!($value instanceof DateTimeInterface)) { + try { + $value = new DateTimeImmutable($value); + } catch (Exception $e) { + return ''; + } } + return $value->format("'Y-m-d H:i:s'"); } } diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index 86c7570..3badb58 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -15,42 +15,42 @@ */ class SqliteDriver implements IDriver { - public function escapeIdentifier($value): string + public function escapeIdentifier(string $value): string { return '"'.str_replace('"', '""', $value).'"'; } - public function escapeText($value): string + public function escapeText(string $value): string { return "'".SQLite3::escapeString($value)."'"; } - public function escapeBool($value): string + public function escapeBool(bool $value): string { return strval($value ? 1 : 0); } - /** - * @param DateTimeInterface|string $value - * @throws Exception - */ - public function escapeDate($value): string + public function escapeDate(DateTimeInterface|string $value): string { if (!($value instanceof DateTimeInterface)) { - $value = new DateTimeImmutable($value); + try { + $value = new DateTimeImmutable($value); + } catch (Exception $e) { + return ''; + } } return $this->escapeText($value->format('Y-m-d')); } - /** - * @param DateTimeInterface|string $value - * @throws Exception - */ - public function escapeDateTime($value): string + public function escapeDateTime(DateTimeInterface|string $value): string { if (!($value instanceof DateTimeInterface)) { - $value = new DateTimeImmutable($value); + try { + $value = new DateTimeImmutable($value); + } catch (Exception $e) { + return ''; + } } return $this->escapeText($value->format('Y-m-d H:i:s')); diff --git a/src/IDriver.php b/src/IDriver.php index d257d64..de456fb 100644 --- a/src/IDriver.php +++ b/src/IDriver.php @@ -4,36 +4,17 @@ namespace CzProject\SqlGenerator; + use DateTimeInterface; - interface IDriver + interface IDriver { - /** - * @param string $value - * @return string - */ - function escapeIdentifier($value); - - /** - * @param string $value - * @return string - */ - function escapeText($value); - - /** - * @param bool $value - * @return string - */ - function escapeBool($value); - - /** - * @param string|\DateTime|\DateTimeInterface $value - * @return string - */ - function escapeDate($value); - - /** - * @param string|\DateTime|\DateTimeInterface $value - * @return string - */ - function escapeDateTime($value); + public function escapeIdentifier(string $value): string; + + public function escapeText(string $value): string; + + public function escapeBool(bool $value): string; + + public function escapeDate(DateTimeInterface|string $value): string; + + public function escapeDateTime(DateTimeInterface|string $value): string; } diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index 636a6a9..8b92cda 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -5,19 +5,20 @@ namespace Tests; use CzProject\SqlGenerator\IDriver; + use DateTimeImmutable; + use DateTimeInterface; + use Exception; - - class DummyDriver implements IDriver + class DummyDriver implements IDriver { - public function escapeIdentifier($value) + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html // @see http://api.dibiphp.com/2.3.2/source-drivers.DibiMySqlDriver.php.html#307 return '`' . str_replace('`', '``', $value) . '`'; } - - public function escapeText($value) + public function escapeText(string $value): string { // https://dev.mysql.com/doc/refman/5.5/en/string-literals.html // http://us3.php.net/manual/en/function.mysql-real-escape-string.php#101248 @@ -28,27 +29,34 @@ public function escapeText($value) ) . '\''; } - - public function escapeBool($value) + public function escapeBool(bool $value): string { return $value ? 'TRUE' : 'FALSE'; } - - public function escapeDate($value) + public function escapeDate(DateTimeInterface|string $value): string { - if (!($value instanceof \DateTime) && !($value instanceof \DateTimeInterface)) { - $value = new \DateTime($value); - } + if (!($value instanceof DateTimeInterface)) { + try { + $value = new DateTimeImmutable($value); + } catch (Exception $e) { + return ''; + } + } + return $value->format("'Y-m-d'"); } - - public function escapeDateTime($value) + public function escapeDateTime(DateTimeInterface|string $value): string { - if (!($value instanceof \DateTime) && !($value instanceof \DateTimeInterface)) { - $value = new \DateTime($value); - } + if (!($value instanceof DateTimeInterface)) { + try { + $value = new DateTimeImmutable($value); + } catch (Exception $e) { + return ''; + } + } + return $value->format("'Y-m-d H:i:s'"); } } From b6da70ca768a1a124523425baabe98818fbc5e42 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 10:15:23 -0400 Subject: [PATCH 04/23] Move duplicate date parsing code into a trait --- src/Drivers/DateParserTrait.php | 20 ++++++++++++++++++++ src/Drivers/MysqlDriver.php | 24 ++++-------------------- src/Drivers/SqliteDriver.php | 24 ++++-------------------- tests/libs/DummyDriver.php | 25 +++++-------------------- 4 files changed, 33 insertions(+), 60 deletions(-) create mode 100644 src/Drivers/DateParserTrait.php diff --git a/src/Drivers/DateParserTrait.php b/src/Drivers/DateParserTrait.php new file mode 100644 index 0000000..5c4f7dc --- /dev/null +++ b/src/Drivers/DateParserTrait.php @@ -0,0 +1,20 @@ +format($format); + } catch (Exception $e) { + return ''; + } + } + } \ No newline at end of file diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index 60c0b7f..0427e5f 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -6,11 +6,11 @@ use CzProject\SqlGenerator\IDriver; use DateTimeInterface; - use DateTimeImmutable; - use Exception; class MysqlDriver implements IDriver { + use DateParserTrait; + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html // @see http://api.dibiphp.com/2.3.2/source-drivers.DibiMySqlDriver.php.html#307 @@ -35,27 +35,11 @@ public function escapeBool(bool $value): string public function escapeDate(DateTimeInterface|string $value): string { - if (!($value instanceof \DateTimeInterface)) { - try { - $value = new DateTimeImmutable($value); - } catch (Exception $e) { - return ''; - } - } - - return $value->format("'Y-m-d'"); + return $this->dateFormat($value, "'Y-m-d'"); } public function escapeDateTime(DateTimeInterface|string $value): string { - if (!($value instanceof DateTimeInterface)) { - try { - $value = new DateTimeImmutable($value); - } catch (Exception $e) { - return ''; - } - } - - return $value->format("'Y-m-d H:i:s'"); + return $this->dateFormat($value, "'Y-m-d H:i:s'"); } } diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index 3badb58..545d6b0 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -5,9 +5,7 @@ namespace CzProject\SqlGenerator\Drivers; use CzProject\SqlGenerator\IDriver; - use DateTimeImmutable; use DateTimeInterface; - use Exception; use SQLite3; /** @@ -15,6 +13,8 @@ */ class SqliteDriver implements IDriver { + use DateParserTrait; + public function escapeIdentifier(string $value): string { return '"'.str_replace('"', '""', $value).'"'; @@ -32,27 +32,11 @@ public function escapeBool(bool $value): string public function escapeDate(DateTimeInterface|string $value): string { - if (!($value instanceof DateTimeInterface)) { - try { - $value = new DateTimeImmutable($value); - } catch (Exception $e) { - return ''; - } - } - - return $this->escapeText($value->format('Y-m-d')); + return $this->escapeText($this->dateFormat($value, 'Y-m-d')); } public function escapeDateTime(DateTimeInterface|string $value): string { - if (!($value instanceof DateTimeInterface)) { - try { - $value = new DateTimeImmutable($value); - } catch (Exception $e) { - return ''; - } - } - - return $this->escapeText($value->format('Y-m-d H:i:s')); + return $this->escapeText($this->dateFormat($value, 'Y-m-d H:i:s')); } } diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index 8b92cda..95c28c7 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -4,13 +4,14 @@ namespace Tests; + use CzProject\SqlGenerator\Drivers\DateParserTrait; use CzProject\SqlGenerator\IDriver; - use DateTimeImmutable; use DateTimeInterface; - use Exception; class DummyDriver implements IDriver { + use DateParserTrait; + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html @@ -36,27 +37,11 @@ public function escapeBool(bool $value): string public function escapeDate(DateTimeInterface|string $value): string { - if (!($value instanceof DateTimeInterface)) { - try { - $value = new DateTimeImmutable($value); - } catch (Exception $e) { - return ''; - } - } - - return $value->format("'Y-m-d'"); + return $this->dateFormat($value, "'Y-m-d'"); } public function escapeDateTime(DateTimeInterface|string $value): string { - if (!($value instanceof DateTimeInterface)) { - try { - $value = new DateTimeImmutable($value); - } catch (Exception $e) { - return ''; - } - } - - return $value->format("'Y-m-d H:i:s'"); + return $this->dateFormat($value, "'Y-m-d H:i:s'"); } } From 40f72864197143764ee21deb825542148501e92c Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 10:40:43 -0400 Subject: [PATCH 05/23] Update more types in interfaces and classes --- src/Helpers.php | 49 ++++++++---------------- src/IStatement.php | 6 +-- src/SqlDocument.php | 91 +++++++-------------------------------------- src/TableName.php | 19 ++-------- src/Value.php | 25 ++++++------- 5 files changed, 46 insertions(+), 144 deletions(-) diff --git a/src/Helpers.php b/src/Helpers.php index 29db07d..c586611 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -4,28 +4,28 @@ namespace CzProject\SqlGenerator; + use DateTimeInterface; class Helpers { - public function __construct() + /** + * @throws StaticClassException + */ + public function __construct() { throw new StaticClassException('This is static class.'); } - - /** - * @param mixed $value - * @return string - * @throws InvalidArgumentException - * @see https://api.dibiphp.com/3.0/source-Dibi.Translator.php.html#174 - */ - public static function formatValue($value, IDriver $driver) - { + /** + * @throws InvalidArgumentException + * @see https://api.dibiphp.com/3.0/source-Dibi.Translator.php.html#174 + */ + public static function formatValue(mixed $value, IDriver $driver): string { if (is_string($value)) { return $driver->escapeText($value); } elseif (is_int($value)) { - return (string) $value; + return (string)$value; } elseif (is_float($value)) { return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); @@ -36,20 +36,15 @@ public static function formatValue($value, IDriver $driver) } elseif ($value === NULL) { return 'NULL'; - } elseif ($value instanceof \DateTime || $value instanceof \DateTimeInterface) { + } elseif ($value instanceof DateTimeInterface) { return $driver->escapeDateTime($value); + } throw new InvalidArgumentException("Unsupported value type."); } - - /** - * @param string|TableName $tableName - * @return string - */ - public static function escapeTableName($tableName, IDriver $driver) - { + public static function escapeTableName(TableName|string $tableName, IDriver $driver): string { if ($tableName instanceof TableName) { return $tableName->toString($driver); } @@ -57,13 +52,7 @@ public static function escapeTableName($tableName, IDriver $driver) return $driver->escapeIdentifier($tableName); } - - /** - * @param string|TableName $tableName - * @return string|TableName - */ - public static function createTableName($tableName) - { + public static function createTableName(TableName|string $tableName): TableName|string { if (is_string($tableName) && strpos($tableName, '.')) { return TableName::create($tableName); } @@ -71,13 +60,7 @@ public static function createTableName($tableName) return $tableName; } - - /** - * @param string $s - * @return string - */ - public static function normalizeNewLines($s) - { + public static function normalizeNewLines(string $s): string { return str_replace(["\r\n", "\r"], "\n", $s); } } diff --git a/src/IStatement.php b/src/IStatement.php index 900a413..7c23c87 100644 --- a/src/IStatement.php +++ b/src/IStatement.php @@ -4,11 +4,7 @@ namespace CzProject\SqlGenerator; - interface IStatement { - /** - * @return string - */ - function toSql(IDriver $driver); + public function toSql(IDriver $driver): string; } diff --git a/src/SqlDocument.php b/src/SqlDocument.php index 9df7352..afc6485 100644 --- a/src/SqlDocument.php +++ b/src/SqlDocument.php @@ -4,37 +4,24 @@ namespace CzProject\SqlGenerator; - class SqlDocument { /** @var IStatement[] */ - private $statements = []; - + private array $statements = []; - /** - * @return static - */ - public function addStatement(IStatement $statement) - { + public function addStatement(IStatement $statement): static { $this->statements[] = $statement; return $this; } - - /** - * @return bool - */ - public function isEmpty() - { + public function isEmpty(): bool { return empty($this->statements); } - /** * @return string[] */ - public function getSqlQueries(IDriver $driver) - { + public function getSqlQueries(IDriver $driver): array { $output = []; foreach ($this->statements as $statement) { @@ -44,12 +31,7 @@ public function getSqlQueries(IDriver $driver) return $output; } - - /** - * @return string - */ - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string { $output = ''; $first = TRUE; @@ -68,14 +50,10 @@ public function toSql(IDriver $driver) return $output; } - /** - * @param string $file - * @return void * @throws IOException */ - public function save($file, IDriver $driver) - { + public function save(string $file, IDriver $driver): void { // create directory $dir = dirname($file); @@ -91,87 +69,46 @@ public function save($file, IDriver $driver) } } - /** - * @param string|TableName $tableName - * @param array $data - * @return Statements\Insert + * @param array $data */ - public function insert($tableName, array $data) - { + public function insert(string|TableName $tableName, array $data): Statements\Insert { $statement = new Statements\Insert($tableName, $data); $this->addStatement($statement); return $statement; } - - /** - * @param string|TableName $tableName - * @return Statements\CreateTable - */ - public function createTable($tableName) - { + public function createTable(string|TableName $tableName): Statements\CreateTable { $statement = new Statements\CreateTable($tableName); $this->addStatement($statement); return $statement; } - - /** - * @param string|TableName $tableName - * @return Statements\DropTable - */ - public function dropTable($tableName) - { + public function dropTable(string|TableName $tableName): Statements\DropTable { $statement = new Statements\DropTable($tableName); $this->addStatement($statement); return $statement; } - - /** - * @param string|TableName $oldTable - * @param string|TableName $newTable - * @return Statements\RenameTable - */ - public function renameTable($oldTable, $newTable) - { + public function renameTable(string|TableName $oldTable, string|TableName $newTable): Statements\RenameTable { $statement = new Statements\RenameTable($oldTable, $newTable); $this->addStatement($statement); return $statement; } - - /** - * @param string|TableName $tableName - * @return Statements\AlterTable - */ - public function alterTable($tableName) - { + public function alterTable(string|TableName $tableName): Statements\AlterTable { $statement = new Statements\AlterTable($tableName); $this->addStatement($statement); return $statement; } - - /** - * @param string $command - * @return Statements\SqlCommand - */ - public function command($command) - { + public function command(string $command): Statements\SqlCommand { $statement = new Statements\SqlCommand($command); $this->addStatement($statement); return $statement; } - - /** - * @param string $comment - * @return Statements\Comment - */ - public function comment($comment) - { + public function comment(string $comment): Statements\Comment { $statement = new Statements\Comment($comment); $this->addStatement($statement); return $statement; diff --git a/src/TableName.php b/src/TableName.php index 4559c7d..4c092a8 100644 --- a/src/TableName.php +++ b/src/TableName.php @@ -4,12 +4,10 @@ namespace CzProject\SqlGenerator; - class TableName { /** @var string[] */ - private $parts; - + private array $parts; /** * @param string ...$parts @@ -19,12 +17,7 @@ public function __construct(...$parts) $this->parts = $parts; } - - /** - * @return string - */ - public function toString(IDriver $driver) - { + public function toString(IDriver $driver): string { $res = []; foreach ($this->parts as $part) { @@ -34,13 +27,7 @@ public function toString(IDriver $driver) return implode('.', $res); } - - /** - * @param string $name - * @return self - */ - public static function create($name) - { + public static function create(string $name): self { $parts = explode('.', $name); return new self(...$parts); } diff --git a/src/Value.php b/src/Value.php index 5fc523b..f394f3a 100644 --- a/src/Value.php +++ b/src/Value.php @@ -4,37 +4,36 @@ namespace CzProject\SqlGenerator; + use DateTimeInterface; + use Stringable; class Value { - /** @var scalar|\Stringable|\DateTimeInterface */ - private $value; + /** @var scalar|Stringable|DateTimeInterface */ + private int|float|bool|string|Stringable|DateTimeInterface $value; /** - * @param scalar|\Stringable|\DateTimeInterface $value + * @param scalar|Stringable|DateTimeInterface $value */ - public function __construct($value) + public function __construct(int|float|bool|string|Stringable|DateTimeInterface $value) { $this->value = $value; } - /** - * @return string - */ - public function toString(IDriver $driver) - { + /** + * @throws InvalidArgumentException + */ + public function toString(IDriver $driver): string { return Helpers::formatValue($this->value, $driver); } /** - * @param scalar|\Stringable|\DateTimeInterface $value - * @return self + * @param scalar|Stringable|DateTimeInterface $value */ - public static function create($value) - { + public static function create(int|float|bool|string|Stringable|DateTimeInterface $value): self { return new self($value); } } From 2d14fd3734da908a705e6df7b00e6452a8cbed08 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 11:21:42 -0400 Subject: [PATCH 06/23] Add transactions, the statements need to be in the drivers since they are different between MySQL and SQLite --- src/Drivers/MysqlDriver.php | 14 ++++++++++++++ src/Drivers/SqliteDriver.php | 14 ++++++++++++++ src/IDriver.php | 5 +++++ src/SqlDocument.php | 17 +++++++++++++---- src/Statements/Transaction.php | 26 ++++++++++++++++++++++++++ tests/libs/DummyDriver.php | 14 ++++++++++++++ 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 src/Statements/Transaction.php diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index 0427e5f..9fb6d4d 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -11,6 +11,15 @@ class MysqlDriver implements IDriver { use DateParserTrait; + /** + * @var string[] + */ + private const TRANSACTION = [ + 'start' => 'BEGIN;', + 'commit' => 'COMMIT;', + 'rollback' => 'ROLLBACK', + ]; + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html // @see http://api.dibiphp.com/2.3.2/source-drivers.DibiMySqlDriver.php.html#307 @@ -42,4 +51,9 @@ public function escapeDateTime(DateTimeInterface|string $value): string { return $this->dateFormat($value, "'Y-m-d H:i:s'"); } + + public function transaction(string $action): string + { + return self::TRANSACTION[$action]; + } } diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index 545d6b0..326dc30 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -15,6 +15,15 @@ class SqliteDriver implements IDriver { use DateParserTrait; + /** + * @var string[] + */ + private const TRANSACTION = [ + 'start' => 'BEGIN TRANSACTION;', + 'commit' => 'COMMIT TRANSACTION;', + 'rollback' => 'ROLLBACK TRANSACTION;', + ]; + public function escapeIdentifier(string $value): string { return '"'.str_replace('"', '""', $value).'"'; @@ -39,4 +48,9 @@ public function escapeDateTime(DateTimeInterface|string $value): string { return $this->escapeText($this->dateFormat($value, 'Y-m-d H:i:s')); } + + public function transaction(string $action): string + { + return self::TRANSACTION[$action]; + } } diff --git a/src/IDriver.php b/src/IDriver.php index de456fb..bc272a5 100644 --- a/src/IDriver.php +++ b/src/IDriver.php @@ -17,4 +17,9 @@ public function escapeBool(bool $value): string; public function escapeDate(DateTimeInterface|string $value): string; public function escapeDateTime(DateTimeInterface|string $value): string; + + /** + * @param 'start'|'commit'|'rollback' $action + */ + public function transaction(string $action): string; } diff --git a/src/SqlDocument.php b/src/SqlDocument.php index afc6485..53efc28 100644 --- a/src/SqlDocument.php +++ b/src/SqlDocument.php @@ -4,7 +4,7 @@ namespace CzProject\SqlGenerator; - class SqlDocument + class SqlDocument { /** @var IStatement[] */ private array $statements = []; @@ -69,9 +69,18 @@ public function save(string $file, IDriver $driver): void { } } - /** - * @param array $data - */ + /** + * @param 'start'|'commit'|'rollback' $action + */ + public function transaction(string $action): Statements\Transaction { + $statement = new Statements\Transaction($action); + $this->addStatement($statement); + return $statement; + } + + /** + * @param array $data + */ public function insert(string|TableName $tableName, array $data): Statements\Insert { $statement = new Statements\Insert($tableName, $data); $this->addStatement($statement); diff --git a/src/Statements/Transaction.php b/src/Statements/Transaction.php new file mode 100644 index 0000000..a2256b7 --- /dev/null +++ b/src/Statements/Transaction.php @@ -0,0 +1,26 @@ +action = $action; + } + + public function toSql(IDriver $driver): string { + return $driver->transaction($this->action); + } + } \ No newline at end of file diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index 95c28c7..bdd08f4 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -12,6 +12,15 @@ class DummyDriver implements IDriver { use DateParserTrait; + /** + * @var string[] + */ + private array $transaction = [ + 'start' => 'BEGIN;', + 'commit' => 'COMMIT;', + 'rollback' => 'ROLLBACK', + ]; + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html @@ -44,4 +53,9 @@ public function escapeDateTime(DateTimeInterface|string $value): string { return $this->dateFormat($value, "'Y-m-d H:i:s'"); } + + public function transaction(string $action): string + { + return $this->transaction[$action]; + } } From 6ec0a74c80f3d2b43fafd277ba900ea9e139710b Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 11:56:41 -0400 Subject: [PATCH 07/23] Add `UPDATE` statement --- src/SqlDocument.php | 10 ++++++ src/Statements/Update.php | 67 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/Statements/Update.php diff --git a/src/SqlDocument.php b/src/SqlDocument.php index 53efc28..b5c0d88 100644 --- a/src/SqlDocument.php +++ b/src/SqlDocument.php @@ -87,6 +87,16 @@ public function insert(string|TableName $tableName, array $data): Statements\Ins return $statement; } + /** + * @param array $data + * @param array $where + */ + public function update(string|TableName $tableName, array $data, array $where): Statements\Update { + $statement = new Statements\Update($tableName, $data, $where); + $this->addStatement($statement); + return $statement; + } + public function createTable(string|TableName $tableName): Statements\CreateTable { $statement = new Statements\CreateTable($tableName); $this->addStatement($statement); diff --git a/src/Statements/Update.php b/src/Statements/Update.php new file mode 100644 index 0000000..555825c --- /dev/null +++ b/src/Statements/Update.php @@ -0,0 +1,67 @@ + */ + private array $data; + + /** @var array */ + private array $where; + + /** + * @param array $data + * @param array $where + */ + public function __construct(string|TableName $tableName, array $data, array $where) + { + $this->tableName = Helpers::createTableName($tableName); + $this->data = $data; + $this->where = $where; + } + + /** + * @throws InvalidArgumentException + */ + public function toSql(IDriver $driver): string + { + try { + $tableName = Helpers::escapeTableName($this->tableName, $driver); + + $set = implode(',', array_map( + static fn (string $field, mixed $value): string => sprintf( + '%s=%s', + $driver->escapeIdentifier($field), + Helpers::formatValue($value, $driver), + ), + array_keys($this->data), + array_values($this->data), + )); + + $where = implode(' AND ', array_map( + static fn (string $field, string $value): string => sprintf( + '%s=%s', + $driver->escapeIdentifier($field), + Helpers::formatValue($value, $driver), + ), + array_keys($this->where), + array_values($this->where), + )); + + return "UPDATE $tableName SET $set WHERE $where;"; + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException('Error creating UPDATE query.', previous: $e); + } + } + } From b480aac5fd694bdc9b14029593d141aae9b827a7 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 12:00:49 -0400 Subject: [PATCH 08/23] Clean up `INSERT` statement --- src/Statements/Insert.php | 52 ++++++++++++++------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/src/Statements/Insert.php b/src/Statements/Insert.php index 8114e5f..d8f4584 100644 --- a/src/Statements/Insert.php +++ b/src/Statements/Insert.php @@ -6,52 +6,38 @@ use CzProject\SqlGenerator\Helpers; use CzProject\SqlGenerator\IDriver; - use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\InvalidArgumentException; + use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\TableName; - class Insert implements IStatement { - /** @var string|TableName */ - private $tableName; + private TableName|string $tableName; /** @var array */ - private $data; - + private array $data; /** - * @param string|TableName $tableName - * @param array $data + * @param array $data */ - public function __construct($tableName, array $data) + public function __construct(string|TableName $tableName, array $data) { $this->tableName = Helpers::createTableName($tableName); $this->data = $data; } - - public function toSql(IDriver $driver) - { - $output = 'INSERT INTO ' . Helpers::escapeTableName($this->tableName, $driver); - - // columns - $output .= ' ('; - $output .= implode(', ', array_map([$driver, 'escapeIdentifier'], array_keys($this->data))); - $output .= ")\nVALUES ("; - - // data - $fields = count($this->data); - - foreach ($this->data as $value) { - $output .= Helpers::formatValue($value, $driver); - $fields--; - - if ($fields > 0) { - $output .= ', '; - } - } - - $output .= ');'; - return $output; + /** + * @throws InvalidArgumentException + */ + public function toSql(IDriver $driver): string { + try { + $tableName = Helpers::escapeTableName($this->tableName, $driver); + $fields = implode(',', array_map(static fn (string $field) => $driver->escapeIdentifier($field), array_keys($this->data))); + $values = implode(',', array_map(static fn (mixed $value) => Helpers::formatValue($value, $driver), array_values($this->data))); + + return "INSERT INTO $tableName($fields) VALUES ($values);"; + } catch (InvalidArgumentException $e) { + throw new InvalidArgumentException('Error creating UPDATE query.', previous: $e); + } } } From afdd174d225e5ecf6105139ee58c65dcd5e62d2a Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 12:04:50 -0400 Subject: [PATCH 09/23] Add "last inserted ID" for each driver --- src/Drivers/MysqlDriver.php | 6 ++++++ src/Drivers/SqliteDriver.php | 6 ++++++ src/IDriver.php | 2 ++ tests/libs/DummyDriver.php | 10 ++++++++-- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index 9fb6d4d..f9827ed 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -20,6 +20,8 @@ class MysqlDriver implements IDriver 'rollback' => 'ROLLBACK', ]; + private const LAST_ID = 'LAST_INSERT_ID()'; + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html // @see http://api.dibiphp.com/2.3.2/source-drivers.DibiMySqlDriver.php.html#307 @@ -56,4 +58,8 @@ public function transaction(string $action): string { return self::TRANSACTION[$action]; } + + public function lastId(): string { + return self::LAST_ID; + } } diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index 326dc30..8d88ea4 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -24,6 +24,8 @@ class SqliteDriver implements IDriver 'rollback' => 'ROLLBACK TRANSACTION;', ]; + private const LAST_ID = 'LAST_INSERT_ROWID()'; + public function escapeIdentifier(string $value): string { return '"'.str_replace('"', '""', $value).'"'; @@ -53,4 +55,8 @@ public function transaction(string $action): string { return self::TRANSACTION[$action]; } + + public function lastId(): string { + return self::LAST_ID; + } } diff --git a/src/IDriver.php b/src/IDriver.php index bc272a5..5478ac6 100644 --- a/src/IDriver.php +++ b/src/IDriver.php @@ -22,4 +22,6 @@ public function escapeDateTime(DateTimeInterface|string $value): string; * @param 'start'|'commit'|'rollback' $action */ public function transaction(string $action): string; + + public function lastId(): string; } diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index bdd08f4..323fa59 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -15,12 +15,14 @@ class DummyDriver implements IDriver /** * @var string[] */ - private array $transaction = [ + private const TRANSACTION = [ 'start' => 'BEGIN;', 'commit' => 'COMMIT;', 'rollback' => 'ROLLBACK', ]; + private const LAST_ID = 'LAST_INSERT_ID()'; + public function escapeIdentifier(string $value): string { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html @@ -56,6 +58,10 @@ public function escapeDateTime(DateTimeInterface|string $value): string public function transaction(string $action): string { - return $this->transaction[$action]; + return self::TRANSACTION[$action]; + } + + public function lastId(): string { + return self::LAST_ID; } } From f297b4c2df8504dd92cefdcd97b09198fc0111d8 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 12:07:27 -0400 Subject: [PATCH 10/23] Fix semicolons in transaction commands --- src/Drivers/MysqlDriver.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index f9827ed..4c56884 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -17,7 +17,7 @@ class MysqlDriver implements IDriver private const TRANSACTION = [ 'start' => 'BEGIN;', 'commit' => 'COMMIT;', - 'rollback' => 'ROLLBACK', + 'rollback' => 'ROLLBACK;', ]; private const LAST_ID = 'LAST_INSERT_ID()'; From d5879b703b198130b0a67855e927f9918dcae9d3 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 12:25:55 -0400 Subject: [PATCH 11/23] Rename tables for SQLite --- src/Statements/RenameTable.php | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/Statements/RenameTable.php b/src/Statements/RenameTable.php index bbc5d7d..5560bce 100644 --- a/src/Statements/RenameTable.php +++ b/src/Statements/RenameTable.php @@ -11,37 +11,33 @@ use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\TableName; - class RenameTable implements IStatement { - /** @var string|TableName */ - private $oldTable; - - /** @var string|TableName */ - private $newTable; + private TableName|string $oldTable; + private TableName|string $newTable; - /** - * @param string|TableName $oldTable - * @param string|TableName $newTable - */ - public function __construct($oldTable, $newTable) + public function __construct(TableName|string $oldTable, TableName|string $newTable) { $this->oldTable = Helpers::createTableName($oldTable); $this->newTable = Helpers::createTableName($newTable); } + /** + * @throws NotImplementedException + */ + public function toSql(IDriver $driver): string { + $oldTable = Helpers::escapeTableName($this->oldTable, $driver); + $newTable = Helpers::escapeTableName($this->newTable, $driver); - public function toSql(IDriver $driver) - { if ($driver instanceof Drivers\MysqlDriver) { - return 'RENAME TABLE ' . Helpers::escapeTableName($this->oldTable, $driver) - . ' TO ' - . Helpers::escapeTableName($this->newTable, $driver) - . ';'; + return "RENAME TABLE $oldTable TO $newTable;"; } + elseif ($driver instanceof Drivers\SqliteDriver) { + return "ALTER TABLE $oldTable RENAME TO $newTable;"; + } - // see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query + // @see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query throw new NotImplementedException('Table rename is not implemented for driver ' . get_class($driver) . '.'); } } From 969f554c858463f1dc7617ddfdc43d0be9e26d33 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 13:29:38 -0400 Subject: [PATCH 12/23] Move "rename table" implementation into driver --- src/Drivers/MysqlDriver.php | 5 +++++ src/Drivers/SqliteDriver.php | 5 +++++ src/IDriver.php | 5 +++++ src/Statements/RenameTable.php | 19 ++++++++----------- src/Statements/SqlCommand.php | 14 +++----------- tests/libs/DummyDriver.php | 6 ++++++ 6 files changed, 32 insertions(+), 22 deletions(-) diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index 4c56884..5dce029 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -62,4 +62,9 @@ public function transaction(string $action): string public function lastId(): string { return self::LAST_ID; } + + public function renameTable(string $oldTable, string $newTable): string + { + return "RENAME TABLE $oldTable TO $newTable;"; + } } diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index 8d88ea4..ef97e67 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -59,4 +59,9 @@ public function transaction(string $action): string public function lastId(): string { return self::LAST_ID; } + + public function renameTable(string $oldTable, string $newTable): string + { + return "ALTER TABLE $oldTable RENAME TO $newTable;"; + } } diff --git a/src/IDriver.php b/src/IDriver.php index 5478ac6..a2cb10d 100644 --- a/src/IDriver.php +++ b/src/IDriver.php @@ -24,4 +24,9 @@ public function escapeDateTime(DateTimeInterface|string $value): string; public function transaction(string $action): string; public function lastId(): string; + + /** + * @throws NotImplementedException + */ + public function renameTable(string $oldTable, string $newTable): string; } diff --git a/src/Statements/RenameTable.php b/src/Statements/RenameTable.php index 5560bce..7af46a4 100644 --- a/src/Statements/RenameTable.php +++ b/src/Statements/RenameTable.php @@ -27,17 +27,14 @@ public function __construct(TableName|string $oldTable, TableName|string $newTab * @throws NotImplementedException */ public function toSql(IDriver $driver): string { - $oldTable = Helpers::escapeTableName($this->oldTable, $driver); - $newTable = Helpers::escapeTableName($this->newTable, $driver); - - if ($driver instanceof Drivers\MysqlDriver) { - return "RENAME TABLE $oldTable TO $newTable;"; - } - elseif ($driver instanceof Drivers\SqliteDriver) { - return "ALTER TABLE $oldTable RENAME TO $newTable;"; + try { + $oldTable = Helpers::escapeTableName($this->oldTable, $driver); + $newTable = Helpers::escapeTableName($this->newTable, $driver); + + return $driver->renameTable($oldTable, $newTable); + } catch (NotImplementedException $e) { + // @see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query + throw new NotImplementedException('Table rename is not implemented for driver ' . get_class($driver) . '.', previous: $e); } - - // @see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query - throw new NotImplementedException('Table rename is not implemented for driver ' . get_class($driver) . '.'); } } diff --git a/src/Statements/SqlCommand.php b/src/Statements/SqlCommand.php index 87ca0c2..d8e3070 100644 --- a/src/Statements/SqlCommand.php +++ b/src/Statements/SqlCommand.php @@ -7,24 +7,16 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; - class SqlCommand implements IStatement { - /** @var string */ - private $command; - + private string $command; - /** - * @param string $command - */ - public function __construct($command) + public function __construct(string $command) { $this->command = $command; } - - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string { return rtrim($this->command, ';') . ';'; } } diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index 323fa59..1c8a548 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -6,6 +6,7 @@ use CzProject\SqlGenerator\Drivers\DateParserTrait; use CzProject\SqlGenerator\IDriver; + use CzProject\SqlGenerator\NotImplementedException; use DateTimeInterface; class DummyDriver implements IDriver @@ -64,4 +65,9 @@ public function transaction(string $action): string public function lastId(): string { return self::LAST_ID; } + + public function renameTable(string $oldTable, string $newTable): string + { + throw new NotImplementedException('Dummy driver cannot rename tables.'); + } } From 324ad634f135a4b30e6ba8d6f468410a75838f33 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 13:51:46 -0400 Subject: [PATCH 13/23] We can use the same syntax for renaming for both drivers --- src/Drivers/MysqlDriver.php | 5 ----- src/Drivers/SqliteDriver.php | 5 ----- src/IDriver.php | 5 ----- src/Statements/RenameTable.php | 10 ++++++---- tests/libs/DummyDriver.php | 7 ++----- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index 5dce029..4c56884 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -62,9 +62,4 @@ public function transaction(string $action): string public function lastId(): string { return self::LAST_ID; } - - public function renameTable(string $oldTable, string $newTable): string - { - return "RENAME TABLE $oldTable TO $newTable;"; - } } diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index ef97e67..8d88ea4 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -59,9 +59,4 @@ public function transaction(string $action): string public function lastId(): string { return self::LAST_ID; } - - public function renameTable(string $oldTable, string $newTable): string - { - return "ALTER TABLE $oldTable RENAME TO $newTable;"; - } } diff --git a/src/IDriver.php b/src/IDriver.php index a2cb10d..5478ac6 100644 --- a/src/IDriver.php +++ b/src/IDriver.php @@ -24,9 +24,4 @@ public function escapeDateTime(DateTimeInterface|string $value): string; public function transaction(string $action): string; public function lastId(): string; - - /** - * @throws NotImplementedException - */ - public function renameTable(string $oldTable, string $newTable): string; } diff --git a/src/Statements/RenameTable.php b/src/Statements/RenameTable.php index 7af46a4..a8d643b 100644 --- a/src/Statements/RenameTable.php +++ b/src/Statements/RenameTable.php @@ -27,14 +27,16 @@ public function __construct(TableName|string $oldTable, TableName|string $newTab * @throws NotImplementedException */ public function toSql(IDriver $driver): string { - try { + if ($driver->renameTable ?? true) { $oldTable = Helpers::escapeTableName($this->oldTable, $driver); $newTable = Helpers::escapeTableName($this->newTable, $driver); - return $driver->renameTable($oldTable, $newTable); - } catch (NotImplementedException $e) { + // Works with both SQLite and MySQL/MariaDB + return "ALTER TABLE $oldTable RENAME TO $newTable;"; + } + else { // @see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query - throw new NotImplementedException('Table rename is not implemented for driver ' . get_class($driver) . '.', previous: $e); + throw new NotImplementedException('Table rename is not implemented for driver ' . get_class($driver) . '.'); } } } diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index 1c8a548..6683e69 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -13,6 +13,8 @@ class DummyDriver implements IDriver { use DateParserTrait; + public bool $renameTable = false; + /** * @var string[] */ @@ -65,9 +67,4 @@ public function transaction(string $action): string public function lastId(): string { return self::LAST_ID; } - - public function renameTable(string $oldTable, string $newTable): string - { - throw new NotImplementedException('Dummy driver cannot rename tables.'); - } } From dffb2795720e727be88a78b70db7bf974efa9946 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 13:57:22 -0400 Subject: [PATCH 14/23] Add `RENAME COLUMN` alongside `MODIFY COLUMN` --- src/Statements/RenameColumn.php | 46 +++++++++++++++++++++++++++++++++ tests/libs/DummyDriver.php | 2 ++ 2 files changed, 48 insertions(+) create mode 100644 src/Statements/RenameColumn.php diff --git a/src/Statements/RenameColumn.php b/src/Statements/RenameColumn.php new file mode 100644 index 0000000..87f997d --- /dev/null +++ b/src/Statements/RenameColumn.php @@ -0,0 +1,46 @@ +tableName = Helpers::createTableName($tableName); + $this->oldColumn = $oldColumn; + $this->newColumn = $newColumn; + } + + /** + * @throws NotImplementedException + */ + public function toSql(IDriver $driver): string { + if ($driver->renameColumn ?? true) { + $tableName = Helpers::escapeTableName($this->tableName, $driver); + $oldColumn = $driver->escapeIdentifier($this->oldColumn); + $newColumn = $driver->escapeIdentifier($this->newColumn); + + // Works with both SQLite and MySQL/MariaDB + return "ALTER TABLE $tableName RENAME COLUMN $oldColumn TO $newColumn;"; + } + else { + // @see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query + throw new NotImplementedException('Column rename is not implemented for driver ' . get_class($driver) . '.'); + } + } + } diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index 6683e69..a41668b 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -15,6 +15,8 @@ class DummyDriver implements IDriver public bool $renameTable = false; + public bool $renameColumn = false; + /** * @var string[] */ From 43dffdc38c49b5fb38d4af9746b9e673a4826c76 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 14:04:12 -0400 Subject: [PATCH 15/23] SQLite doesn't support `MODIFY COLUMN` --- src/Drivers/SqliteDriver.php | 2 + src/Statements/ColumnDefinition.php | 2 +- src/Statements/ModifyColumn.php | 98 +++++++++-------------------- src/Statements/RenameColumn.php | 1 - tests/libs/DummyDriver.php | 2 + 5 files changed, 35 insertions(+), 70 deletions(-) diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index 8d88ea4..5a06ea5 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -15,6 +15,8 @@ class SqliteDriver implements IDriver { use DateParserTrait; + public bool $modifyColumn = false; + /** * @var string[] */ diff --git a/src/Statements/ColumnDefinition.php b/src/Statements/ColumnDefinition.php index cd7e1d8..e3ce0bd 100644 --- a/src/Statements/ColumnDefinition.php +++ b/src/Statements/ColumnDefinition.php @@ -97,7 +97,7 @@ public function setComment($comment) } - public function toSql(IDriver $driver) + public function toSql(IDriver $driver): string { $output = $driver->escapeIdentifier($this->name) . ' ' . $this->type; diff --git a/src/Statements/ModifyColumn.php b/src/Statements/ModifyColumn.php index 2268f41..8ce3519 100644 --- a/src/Statements/ModifyColumn.php +++ b/src/Statements/ModifyColumn.php @@ -6,119 +6,81 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; - use CzProject\SqlGenerator\Value; - + use CzProject\SqlGenerator\NotImplementedException; + use CzProject\SqlGenerator\Value; class ModifyColumn implements IStatement { const POSITION_FIRST = TRUE; const POSITION_LAST = FALSE; - /** @var ColumnDefinition */ - private $definition; - - /** @var string|bool */ - private $position = self::POSITION_LAST; + private ColumnDefinition $definition; + private string|bool $position = self::POSITION_LAST; /** - * @param string $name - * @param string $type - * @param array $parameters - * @param array $options [name => value] + * @param array $parameters + * @param array $options [name => value] */ - public function __construct($name, $type, ?array $parameters = NULL, array $options = []) + public function __construct(string $name, string $type, ?array $parameters = NULL, array $options = []) { $this->definition = new ColumnDefinition($name, $type, $parameters, $options); } - - /** - * @return static - */ - public function moveToFirstPosition() - { + public function moveToFirstPosition(): static { $this->position = self::POSITION_FIRST; return $this; } - - /** - * @param string $column - * @return static - */ - public function moveAfterColumn($column) - { + public function moveAfterColumn(string $column): static { $this->position = $column; return $this; } - - /** - * @return static - */ - public function moveToLastPosition() - { + public function moveToLastPosition(): static { $this->position = self::POSITION_LAST; return $this; } - - /** - * @param bool $nullable - * @return static - */ - public function setNullable($nullable = TRUE) - { + public function setNullable(bool $nullable = TRUE): static { $this->definition->setNullable($nullable); return $this; } - - /** - * @param mixed|NULL $defaultValue - * @return static - */ - public function setDefaultValue($defaultValue) - { + public function setDefaultValue(mixed $defaultValue): static { $this->definition->setDefaultValue($defaultValue); return $this; } - - /** - * @param bool $autoIncrement - * @return static - */ - public function setAutoIncrement($autoIncrement = TRUE) - { + public function setAutoIncrement(bool $autoIncrement = TRUE): static { $this->definition->setAutoIncrement($autoIncrement); return $this; } - - /** - * @param string|NULL $comment - * @return static - */ - public function setComment($comment) - { + public function setComment(?string $comment): static { $this->definition->setComment($comment); return $this; } - - public function toSql(IDriver $driver) + /** + * @throws NotImplementedException + */ + public function toSql(IDriver $driver): string { - $output = 'MODIFY COLUMN ' . $this->definition->toSql($driver); + if ($driver->modifyColumn ?? true) { + $output = 'MODIFY COLUMN ' . $this->definition->toSql($driver); - if ($this->position === self::POSITION_FIRST) { - $output .= ' FIRST'; + if ($this->position === self::POSITION_FIRST) { + $output .= ' FIRST'; - } elseif ($this->position !== self::POSITION_LAST) { - $output .= ' AFTER ' . $driver->escapeIdentifier($this->position); - } + } elseif ($this->position !== self::POSITION_LAST) { + $output .= ' AFTER ' . $driver->escapeIdentifier($this->position); + } - return $output; + return $output . ';'; + } + else { + throw new NotImplementedException('Modify column is not implemented for driver ' . get_class($driver) . '.'); + } } } diff --git a/src/Statements/RenameColumn.php b/src/Statements/RenameColumn.php index 87f997d..af82d4a 100644 --- a/src/Statements/RenameColumn.php +++ b/src/Statements/RenameColumn.php @@ -39,7 +39,6 @@ public function toSql(IDriver $driver): string { return "ALTER TABLE $tableName RENAME COLUMN $oldColumn TO $newColumn;"; } else { - // @see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query throw new NotImplementedException('Column rename is not implemented for driver ' . get_class($driver) . '.'); } } diff --git a/tests/libs/DummyDriver.php b/tests/libs/DummyDriver.php index a41668b..a9d9d14 100644 --- a/tests/libs/DummyDriver.php +++ b/tests/libs/DummyDriver.php @@ -17,6 +17,8 @@ class DummyDriver implements IDriver public bool $renameColumn = false; + public bool $modifyColumn = false; + /** * @var string[] */ From f4c440eddd2f67de3d97349715f8e23d95b4c544 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 14:30:39 -0400 Subject: [PATCH 16/23] Move all "statements" for `ALTER TABLE` into their own folder. Use `ALTER TABLE` for renaming a table or column. --- src/Statements/{ => Alter}/AddColumn.php | 0 src/Statements/{ => Alter}/AddForeignKey.php | 0 src/Statements/{ => Alter}/AddIndex.php | 0 src/Statements/{ => Alter}/DropColumn.php | 0 src/Statements/{ => Alter}/DropForeignKey.php | 0 src/Statements/{ => Alter}/DropIndex.php | 0 src/Statements/{ => Alter}/ModifyColumn.php | 0 src/Statements/Alter/RenameColumnTo.php | 31 +++++ src/Statements/Alter/RenameTo.php | 25 ++++ src/Statements/AlterTable.php | 119 ++++++------------ src/Statements/RenameColumn.php | 14 +-- src/Statements/RenameTable.php | 13 +- 12 files changed, 101 insertions(+), 101 deletions(-) rename src/Statements/{ => Alter}/AddColumn.php (100%) rename src/Statements/{ => Alter}/AddForeignKey.php (100%) rename src/Statements/{ => Alter}/AddIndex.php (100%) rename src/Statements/{ => Alter}/DropColumn.php (100%) rename src/Statements/{ => Alter}/DropForeignKey.php (100%) rename src/Statements/{ => Alter}/DropIndex.php (100%) rename src/Statements/{ => Alter}/ModifyColumn.php (100%) create mode 100644 src/Statements/Alter/RenameColumnTo.php create mode 100644 src/Statements/Alter/RenameTo.php diff --git a/src/Statements/AddColumn.php b/src/Statements/Alter/AddColumn.php similarity index 100% rename from src/Statements/AddColumn.php rename to src/Statements/Alter/AddColumn.php diff --git a/src/Statements/AddForeignKey.php b/src/Statements/Alter/AddForeignKey.php similarity index 100% rename from src/Statements/AddForeignKey.php rename to src/Statements/Alter/AddForeignKey.php diff --git a/src/Statements/AddIndex.php b/src/Statements/Alter/AddIndex.php similarity index 100% rename from src/Statements/AddIndex.php rename to src/Statements/Alter/AddIndex.php diff --git a/src/Statements/DropColumn.php b/src/Statements/Alter/DropColumn.php similarity index 100% rename from src/Statements/DropColumn.php rename to src/Statements/Alter/DropColumn.php diff --git a/src/Statements/DropForeignKey.php b/src/Statements/Alter/DropForeignKey.php similarity index 100% rename from src/Statements/DropForeignKey.php rename to src/Statements/Alter/DropForeignKey.php diff --git a/src/Statements/DropIndex.php b/src/Statements/Alter/DropIndex.php similarity index 100% rename from src/Statements/DropIndex.php rename to src/Statements/Alter/DropIndex.php diff --git a/src/Statements/ModifyColumn.php b/src/Statements/Alter/ModifyColumn.php similarity index 100% rename from src/Statements/ModifyColumn.php rename to src/Statements/Alter/ModifyColumn.php diff --git a/src/Statements/Alter/RenameColumnTo.php b/src/Statements/Alter/RenameColumnTo.php new file mode 100644 index 0000000..4d8058c --- /dev/null +++ b/src/Statements/Alter/RenameColumnTo.php @@ -0,0 +1,31 @@ +oldName = $oldName; + $this->newName = $newName; + } + + public function toSql(IDriver $driver): string + { + $oldName = $driver->escapeIdentifier($this->oldName); + $newName = $driver->escapeIdentifier($this->newName); + + return "RENAME COLUMN $oldName TO $newName"; + } + } diff --git a/src/Statements/Alter/RenameTo.php b/src/Statements/Alter/RenameTo.php new file mode 100644 index 0000000..140d348 --- /dev/null +++ b/src/Statements/Alter/RenameTo.php @@ -0,0 +1,25 @@ +tableName = Helpers::createTableName($tableName); + } + + public function toSql(IDriver $driver): string + { + return 'RENAME TO ' . Helpers::escapeTableName($this->tableName, $driver); + } + } diff --git a/src/Statements/AlterTable.php b/src/Statements/AlterTable.php index 3e4fca3..5f35e81 100644 --- a/src/Statements/AlterTable.php +++ b/src/Statements/AlterTable.php @@ -6,140 +6,91 @@ use CzProject\SqlGenerator\Helpers; use CzProject\SqlGenerator\IDriver; - use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\InvalidArgumentException; + use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\TableName; use CzProject\SqlGenerator\Value; - class AlterTable implements IStatement { - /** @var string|TableName */ - private $tableName; + private TableName|string $tableName; /** @var IStatement[] */ - private $statements = []; - - /** @var string|NULL */ - private $comment; + private array $statements = []; - /** @var array [name => value] */ - private $options = []; + private ?string $comment; + /** @var array [name => value] */ + private array $options = []; - /** - * @param string|TableName $tableName - */ - public function __construct($tableName) + public function __construct(string|TableName $tableName) { $this->tableName = Helpers::createTableName($tableName); } + public function rename(string|TableName $newName): RenameTo { + return $this->statements[] = new RenameTo($newName); + } + + public function renameColumn(string $oldName, string $newName): RenameColumnTo + { + return $this->statements[] = new RenameColumnTo($oldName, $newName); + } /** - * @param string $name - * @param string $type - * @param array $parameters - * @param array $options [name => value] - * @return AddColumn + * @param array $parameters + * @param array $options [name => value] */ - public function addColumn($name, $type, ?array $parameters = NULL, array $options = []) - { + public function addColumn(string $name, string $type, ?array $parameters = NULL, array $options = []): AddColumn { return $this->statements[] = new AddColumn($name, $type, $parameters, $options); } - - /** - * @param string $column - * @return DropColumn - */ - public function dropColumn($column) - { + public function dropColumn(string $column): DropColumn { return $this->statements[] = new DropColumn($column); } - /** - * @param string $name - * @param string $type - * @param array $parameters - * @param array $options [name => value] - * @return ModifyColumn + * @param array $parameters + * @param array $options [name => value] */ - public function modifyColumn($name, $type, ?array $parameters = NULL, array $options = []) - { + public function modifyColumn(string $name, string $type, ?array $parameters = NULL, array $options = []): ModifyColumn { return $this->statements[] = new ModifyColumn($name, $type, $parameters, $options); } - - /** - * @param string|NULL $name - * @param string $type - * @return AddIndex - */ - public function addIndex($name, $type) - { + public function addIndex(?string $name, string $type): AddIndex { return $this->statements[] = new AddIndex($name, $type); } - - /** - * @param string|NULL $index - * @return DropIndex - */ - public function dropIndex($index) - { + public function dropIndex(?string $index): DropIndex { return $this->statements[] = new DropIndex($index); } - /** - * @param string $name - * @param string[]|string $columns - * @param string|TableName $targetTable - * @param string[]|string $targetColumns - * @return AddForeignKey + * @param string|string[] $columns + * @param string|string[] $targetColumns */ - public function addForeignKey($name, $columns, $targetTable, $targetColumns) - { + public function addForeignKey(string $name, array|string $columns, string|TableName $targetTable, array|string $targetColumns): AddForeignKey { return $this->statements[] = new AddForeignKey($name, $columns, $targetTable, $targetColumns); } - - /** - * @param string $foreignKey - * @return DropForeignKey - */ - public function dropForeignKey($foreignKey) - { + public function dropForeignKey(string $foreignKey): DropForeignKey { return $this->statements[] = new DropForeignKey($foreignKey); } - - /** - * @param string|NULL $comment - * @return static - */ - public function setComment($comment) - { + public function setComment(?string $comment): static { $this->comment = $comment; return $this; } - - /** - * @param string $name - * @param string|Value $value - * @return static - */ - public function setOption($name, $value) - { + public function setOption(string $name, string|Value $value): static { $this->options[$name] = $value; return $this; } - - public function toSql(IDriver $driver) - { + /** + * @throws InvalidArgumentException + */ + public function toSql(IDriver $driver): string { if (empty($this->statements) && empty($this->options) && !isset($this->comment)) { return ''; } diff --git a/src/Statements/RenameColumn.php b/src/Statements/RenameColumn.php index af82d4a..781d0ba 100644 --- a/src/Statements/RenameColumn.php +++ b/src/Statements/RenameColumn.php @@ -11,17 +11,16 @@ use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\TableName; - class RenameColumn implements IStatement + class RenameColumn extends AlterTable { - private TableName|string $tableName; - private string $oldColumn; private string $newColumn; public function __construct(TableName|string $tableName, string $oldColumn, string $newColumn) { - $this->tableName = Helpers::createTableName($tableName); + parent::__construct($tableName); + $this->oldColumn = $oldColumn; $this->newColumn = $newColumn; } @@ -31,12 +30,9 @@ public function __construct(TableName|string $tableName, string $oldColumn, stri */ public function toSql(IDriver $driver): string { if ($driver->renameColumn ?? true) { - $tableName = Helpers::escapeTableName($this->tableName, $driver); - $oldColumn = $driver->escapeIdentifier($this->oldColumn); - $newColumn = $driver->escapeIdentifier($this->newColumn); + $this->renameColumn($this->oldColumn, $this->newColumn); - // Works with both SQLite and MySQL/MariaDB - return "ALTER TABLE $tableName RENAME COLUMN $oldColumn TO $newColumn;"; + return parent::toSql($driver); } else { throw new NotImplementedException('Column rename is not implemented for driver ' . get_class($driver) . '.'); diff --git a/src/Statements/RenameTable.php b/src/Statements/RenameTable.php index a8d643b..a5bde18 100644 --- a/src/Statements/RenameTable.php +++ b/src/Statements/RenameTable.php @@ -11,15 +11,14 @@ use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\TableName; - class RenameTable implements IStatement + class RenameTable extends AlterTable { - private TableName|string $oldTable; - private TableName|string $newTable; public function __construct(TableName|string $oldTable, TableName|string $newTable) { - $this->oldTable = Helpers::createTableName($oldTable); + parent::__construct($oldTable); + $this->newTable = Helpers::createTableName($newTable); } @@ -28,11 +27,9 @@ public function __construct(TableName|string $oldTable, TableName|string $newTab */ public function toSql(IDriver $driver): string { if ($driver->renameTable ?? true) { - $oldTable = Helpers::escapeTableName($this->oldTable, $driver); - $newTable = Helpers::escapeTableName($this->newTable, $driver); + $this->rename($this->newTable); - // Works with both SQLite and MySQL/MariaDB - return "ALTER TABLE $oldTable RENAME TO $newTable;"; + return parent::toSql($driver); } else { // @see http://stackoverflow.com/questions/886786/how-do-i-rename-the-table-name-using-sql-query From b85324da6e1c26d413cc18eb4c3d1c9dd37b06ea Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 14:32:41 -0400 Subject: [PATCH 17/23] Fix exceptions/imports on rename table/column --- src/Statements/RenameColumn.php | 8 +++----- src/Statements/RenameTable.php | 5 ++--- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/Statements/RenameColumn.php b/src/Statements/RenameColumn.php index 781d0ba..72e32c0 100644 --- a/src/Statements/RenameColumn.php +++ b/src/Statements/RenameColumn.php @@ -4,11 +4,9 @@ namespace CzProject\SqlGenerator\Statements; - use CzProject\SqlGenerator\Drivers; use CzProject\SqlGenerator\IDriver; - use CzProject\SqlGenerator\Helpers; - use CzProject\SqlGenerator\NotImplementedException; - use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\InvalidArgumentException; + use CzProject\SqlGenerator\NotImplementedException; use CzProject\SqlGenerator\TableName; class RenameColumn extends AlterTable @@ -26,7 +24,7 @@ public function __construct(TableName|string $tableName, string $oldColumn, stri } /** - * @throws NotImplementedException + * @throws NotImplementedException|InvalidArgumentException */ public function toSql(IDriver $driver): string { if ($driver->renameColumn ?? true) { diff --git a/src/Statements/RenameTable.php b/src/Statements/RenameTable.php index a5bde18..add503d 100644 --- a/src/Statements/RenameTable.php +++ b/src/Statements/RenameTable.php @@ -4,11 +4,10 @@ namespace CzProject\SqlGenerator\Statements; - use CzProject\SqlGenerator\Drivers; use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\Helpers; use CzProject\SqlGenerator\NotImplementedException; - use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\InvalidArgumentException; use CzProject\SqlGenerator\TableName; class RenameTable extends AlterTable @@ -23,7 +22,7 @@ public function __construct(TableName|string $oldTable, TableName|string $newTab } /** - * @throws NotImplementedException + * @throws NotImplementedException|InvalidArgumentException */ public function toSql(IDriver $driver): string { if ($driver->renameTable ?? true) { From 062bfdea25712540b092824b1fe826f9667f6e5c Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 14:35:53 -0400 Subject: [PATCH 18/23] Update types on `DROP TABLE` and `-- Comments` --- src/Statements/Comment.php | 14 +++----------- src/Statements/DropTable.php | 14 +++----------- 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/src/Statements/Comment.php b/src/Statements/Comment.php index 9ef2528..cad6633 100644 --- a/src/Statements/Comment.php +++ b/src/Statements/Comment.php @@ -8,24 +8,16 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; - class Comment implements IStatement { - /** @var string */ - private $comment; - + private string $comment; - /** - * @param string $comment - */ - public function __construct($comment) + public function __construct(string $comment) { $this->comment = $comment; } - - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string { return '-- ' . str_replace("\n", "\n-- ", Helpers::normalizeNewLines(trim($this->comment))); } } diff --git a/src/Statements/DropTable.php b/src/Statements/DropTable.php index 13ba3a5..22608b7 100644 --- a/src/Statements/DropTable.php +++ b/src/Statements/DropTable.php @@ -9,24 +9,16 @@ use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\TableName; - class DropTable implements IStatement { - /** @var string|TableName */ - private $tableName; - + private TableName|string $tableName; - /** - * @param string|TableName $tableName - */ - public function __construct($tableName) + public function __construct(string|TableName $tableName) { $this->tableName = Helpers::createTableName($tableName); } - - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string { return 'DROP TABLE ' . Helpers::escapeTableName($this->tableName, $driver) . ';'; } } From 4ae5ca8d2ec9ff8464f467c1dec69ffd92274d8f Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 14:55:01 -0400 Subject: [PATCH 19/23] Move definitions for `CREATE TABLE` to their own folder --- .../{ => Defs}/ColumnDefinition.php | 90 +++++++------------ .../{ => Defs}/ForeignKeyDefinition.php | 60 +++++-------- .../{ => Defs}/IndexColumnDefinition.php | 41 ++++----- src/Statements/{ => Defs}/IndexDefinition.php | 50 ++++------- 4 files changed, 87 insertions(+), 154 deletions(-) rename src/Statements/{ => Defs}/ColumnDefinition.php (61%) rename src/Statements/{ => Defs}/ForeignKeyDefinition.php (69%) rename src/Statements/{ => Defs}/IndexColumnDefinition.php (62%) rename src/Statements/{ => Defs}/IndexDefinition.php (68%) diff --git a/src/Statements/ColumnDefinition.php b/src/Statements/Defs/ColumnDefinition.php similarity index 61% rename from src/Statements/ColumnDefinition.php rename to src/Statements/Defs/ColumnDefinition.php index e3ce0bd..dc4c968 100644 --- a/src/Statements/ColumnDefinition.php +++ b/src/Statements/Defs/ColumnDefinition.php @@ -7,44 +7,35 @@ use CzProject\SqlGenerator\Drivers; use CzProject\SqlGenerator\Helpers; use CzProject\SqlGenerator\IDriver; - use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\InvalidArgumentException; + use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\Value; - class ColumnDefinition implements IStatement { - /** @var string */ - private $name; + private string $name; - /** @var string */ - private $type; + private string $type; /** @var array */ - private $parameters = []; + private array $parameters = []; /** @var array [name => value] */ - private $options = []; - - /** @var bool */ - private $nullable = FALSE; + private array $options = []; - /** @var mixed|NULL */ - private $defaultValue; + private bool $nullable = FALSE; - /** @var bool */ - private $autoIncrement = FALSE; + private mixed $defaultValue; - /** @var string|NULL */ - private $comment; + private bool $autoIncrement = FALSE; + private ?string $comment; /** - * @param string $name - * @param string $type - * @param array|NULL $parameters - * @param array $options [name => value] + * @param array|NULL $parameters + * @param array $options [name => value] */ - public function __construct($name, $type, ?array $parameters = NULL, array $options = []) + public function __construct(string $name, string $type, ?array $parameters = NULL, array $options = []) { $this->name = $name; $this->type = $type; @@ -52,52 +43,34 @@ public function __construct($name, $type, ?array $parameters = NULL, array $opti $this->options = $options; } - - /** - * @param bool $nullable - * @return static - */ - public function setNullable($nullable = TRUE) - { + public function setNullable(bool $nullable = TRUE): static + { $this->nullable = $nullable; return $this; } - - /** - * @param mixed|NULL $defaultValue - * @return static - */ - public function setDefaultValue($defaultValue) - { + public function setDefaultValue(mixed $defaultValue): static + { $this->defaultValue = $defaultValue; return $this; } - - /** - * @param bool $autoIncrement - * @return static - */ - public function setAutoIncrement($autoIncrement = TRUE) - { + public function setAutoIncrement(bool $autoIncrement = TRUE): static + { $this->autoIncrement = $autoIncrement; return $this; } - - /** - * @param string|NULL $comment - * @return static - */ - public function setComment($comment) - { + public function setComment(?string $comment): static + { $this->comment = $comment; return $this; } - - public function toSql(IDriver $driver): string + /** + * @throws InvalidArgumentException + */ + public function toSql(IDriver $driver): string { $output = $driver->escapeIdentifier($this->name) . ' ' . $this->type; @@ -147,14 +120,11 @@ public function toSql(IDriver $driver): string return $output; } - - /** - * @param string $name - * @param string|Value|NULL $value - * @return string - */ - private static function formatOption($name, $value, IDriver $driver) - { + /** + * @throws InvalidArgumentException + */ + private static function formatOption(string $name, string|Value|null $value, IDriver $driver): string + { if ($value instanceof Value) { $value = $value->toString($driver); } diff --git a/src/Statements/ForeignKeyDefinition.php b/src/Statements/Defs/ForeignKeyDefinition.php similarity index 69% rename from src/Statements/ForeignKeyDefinition.php rename to src/Statements/Defs/ForeignKeyDefinition.php index 7db5355..f9d46f0 100644 --- a/src/Statements/ForeignKeyDefinition.php +++ b/src/Statements/Defs/ForeignKeyDefinition.php @@ -18,32 +18,25 @@ class ForeignKeyDefinition implements IStatement const ACTION_CASCADE = 'CASCADE'; const ACTION_SET_NULL = 'SET NULL'; - /** @var string */ - private $name; + private string $name; /** @var string[] */ - private $columns; + private array $columns; - /** @var string|TableName */ - private $targetTable; + private TableName|string $targetTable; /** @var string[] */ - private $targetColumns; + private array $targetColumns; - /** @var string */ - private $onUpdateAction = self::ACTION_RESTRICT; - - /** @var string */ - private $onDeleteAction = self::ACTION_RESTRICT; + private string $onUpdateAction = self::ACTION_RESTRICT; + private string $onDeleteAction = self::ACTION_RESTRICT; /** - * @param string $name - * @param string[]|string $columns - * @param string|TableName $targetTable - * @param string[]|string $targetColumns + * @param string|string[] $columns + * @param string|string[] $targetColumns */ - public function __construct($name, $columns, $targetTable, $targetColumns) + public function __construct(string $name, array|string $columns, string|TableName $targetTable, array|string $targetColumns) { $this->name = $name; $this->targetTable = Helpers::createTableName($targetTable); @@ -66,12 +59,11 @@ public function __construct($name, $columns, $targetTable, $targetColumns) } - /** - * @param string $onUpdateAction - * @return static - */ - public function setOnUpdateAction($onUpdateAction) - { + /** + * @throws OutOfRangeException + */ + public function setOnUpdateAction(string $onUpdateAction): static + { if (!$this->validateAction($onUpdateAction)) { throw new OutOfRangeException("Action '$onUpdateAction' is invalid."); } @@ -81,12 +73,11 @@ public function setOnUpdateAction($onUpdateAction) } - /** - * @param string $onDeleteAction - * @return static - */ - public function setOnDeleteAction($onDeleteAction) - { + /** + * @throws OutOfRangeException + */ + public function setOnDeleteAction(string $onDeleteAction): static + { if (!$this->validateAction($onDeleteAction)) { throw new OutOfRangeException("Action '$onDeleteAction' is invalid."); } @@ -96,8 +87,8 @@ public function setOnDeleteAction($onDeleteAction) } - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string + { $output = 'CONSTRAINT ' . $driver->escapeIdentifier($this->name); $output .= ' FOREIGN KEY ('; $output .= implode(', ', array_map([$driver, 'escapeIdentifier'], $this->columns)); @@ -109,13 +100,8 @@ public function toSql(IDriver $driver) return $output; } - - /** - * @param string $action - * @return bool - */ - private function validateAction($action) - { + private function validateAction(string $action): bool + { return $action === self::ACTION_RESTRICT || $action === self::ACTION_NO_ACTION || $action === self::ACTION_CASCADE diff --git a/src/Statements/IndexColumnDefinition.php b/src/Statements/Defs/IndexColumnDefinition.php similarity index 62% rename from src/Statements/IndexColumnDefinition.php rename to src/Statements/Defs/IndexColumnDefinition.php index a8448e0..9ee93c5 100644 --- a/src/Statements/IndexColumnDefinition.php +++ b/src/Statements/Defs/IndexColumnDefinition.php @@ -14,48 +14,37 @@ class IndexColumnDefinition implements IStatement const ASC = 'ASC'; const DESC = 'DESC'; - /** @var string */ - private $name; + private string $name; - /** @var string */ - private $order; + private string $order; - /** @var int|NULL */ - private $length; + private ?int $length; - /** - * @param string $name - * @param string $order - * @param int|NULL $length - */ - public function __construct($name, $order = self::ASC, $length = NULL) + /** + * @throws OutOfRangeException + */ + public function __construct(string $name, string $order = self::ASC, ?int $length = NULL) { $this->name = $name; $this->setOrder($order); $this->length = $length; } - - /** - * @param string $order - * @return static - */ - private function setOrder($order) - { - $order = (string) $order; - + /** + * @throws OutOfRangeException + */ + private function setOrder(string $order): void + { if ($order !== self::ASC && $order !== self::DESC) { throw new OutOfRangeException("Order type '$order' not found."); } $this->order = $order; - return $this; - } - + } - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string + { $output = $driver->escapeIdentifier($this->name); if ($this->length !== NULL) { diff --git a/src/Statements/IndexDefinition.php b/src/Statements/Defs/IndexDefinition.php similarity index 68% rename from src/Statements/IndexDefinition.php rename to src/Statements/Defs/IndexDefinition.php index 989693c..662475a 100644 --- a/src/Statements/IndexDefinition.php +++ b/src/Statements/Defs/IndexDefinition.php @@ -16,47 +16,36 @@ class IndexDefinition implements IStatement const TYPE_UNIQUE = 'UNIQUE'; const TYPE_FULLTEXT = 'FULLTEXT'; - /** @var string|NULL */ - private $name; + private ?string $name; - /** @var string */ - private $type; + private string $type; /** @var IndexColumnDefinition[] */ - private $columns = []; + private array $columns = []; - - /** - * @param string|NULL $name - * @param string $type - */ - public function __construct($name, $type) + /** + * @throws OutOfRangeException + */ + public function __construct(?string $name, string $type) { $this->name = $name; $this->setType($type); } - - /** - * @param string $column - * @param string $order - * @param int|NULL $length - * @return static - */ - public function addColumn($column, $order = IndexColumnDefinition::ASC, $length = NULL) - { + /** + * @throws OutOfRangeException + */ + public function addColumn(string $column, string $order = IndexColumnDefinition::ASC, ?int $length = NULL): static + { $this->columns[] = new IndexColumnDefinition($column, $order, $length); return $this; } - - /** - * @param string $type - * @return void - */ - private function setType($type) - { - $type = (string) $type; + /** + * @throws OutOfRangeException + */ + private function setType(string $type): void + { $exists = $type === self::TYPE_INDEX || $type === self::TYPE_PRIMARY || $type === self::TYPE_UNIQUE @@ -69,9 +58,8 @@ private function setType($type) $this->type = $type; } - - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string + { $output = $this->type !== self::TYPE_INDEX ? ($this->type . ' ') : ''; $output .= 'KEY'; From 3712d433f602fb0d5971bb32d350288ff29ebba6 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 14:55:10 -0400 Subject: [PATCH 20/23] Reformat/fix types in various files --- src/Drivers/MysqlDriver.php | 6 +- src/Drivers/SqliteDriver.php | 3 +- src/Statements/Alter/AddColumn.php | 86 +++++++--------------- src/Statements/Alter/AddForeignKey.php | 41 +++++------ src/Statements/Alter/AddIndex.php | 33 +++------ src/Statements/Alter/DropColumn.php | 15 +--- src/Statements/Alter/DropForeignKey.php | 14 +--- src/Statements/Alter/DropIndex.php | 22 +++--- src/Statements/Alter/ModifyColumn.php | 26 ++++--- src/Statements/AlterTable.php | 39 +++++++--- src/Statements/Comment.php | 3 +- src/Statements/CreateTable.php | 98 ++++++++++--------------- src/Statements/DropTable.php | 3 +- src/Statements/Insert.php | 3 +- src/Statements/RenameColumn.php | 3 +- src/Statements/RenameTable.php | 3 +- src/Statements/SqlCommand.php | 3 +- src/Statements/Transaction.php | 9 ++- 18 files changed, 181 insertions(+), 229 deletions(-) diff --git a/src/Drivers/MysqlDriver.php b/src/Drivers/MysqlDriver.php index 4c56884..729763d 100644 --- a/src/Drivers/MysqlDriver.php +++ b/src/Drivers/MysqlDriver.php @@ -22,7 +22,8 @@ class MysqlDriver implements IDriver private const LAST_ID = 'LAST_INSERT_ID()'; - public function escapeIdentifier(string $value): string { + public function escapeIdentifier(string $value): string + { // @see http://dev.mysql.com/doc/refman/5.0/en/identifiers.html // @see http://api.dibiphp.com/2.3.2/source-drivers.DibiMySqlDriver.php.html#307 return '`' . str_replace('`', '``', $value) . '`'; @@ -59,7 +60,8 @@ public function transaction(string $action): string return self::TRANSACTION[$action]; } - public function lastId(): string { + public function lastId(): string + { return self::LAST_ID; } } diff --git a/src/Drivers/SqliteDriver.php b/src/Drivers/SqliteDriver.php index 5a06ea5..47b8b82 100644 --- a/src/Drivers/SqliteDriver.php +++ b/src/Drivers/SqliteDriver.php @@ -58,7 +58,8 @@ public function transaction(string $action): string return self::TRANSACTION[$action]; } - public function lastId(): string { + public function lastId(): string + { return self::LAST_ID; } } diff --git a/src/Statements/Alter/AddColumn.php b/src/Statements/Alter/AddColumn.php index 89d33da..5d3e21f 100644 --- a/src/Statements/Alter/AddColumn.php +++ b/src/Statements/Alter/AddColumn.php @@ -5,111 +5,77 @@ namespace CzProject\SqlGenerator\Statements; use CzProject\SqlGenerator\IDriver; - use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\InvalidArgumentException; + use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\Value; - class AddColumn implements IStatement { const POSITION_FIRST = TRUE; const POSITION_LAST = FALSE; - /** @var ColumnDefinition */ - private $definition; + private ColumnDefinition $definition; - /** @var string|bool */ - private $position = self::POSITION_LAST; + private string|bool $position = self::POSITION_LAST; /** - * @param string $name - * @param string $type - * @param array $parameters - * @param array $options [name => value] + * @param array $parameters + * @param array $options [name => value] */ - public function __construct($name, $type, ?array $parameters = NULL, array $options = []) + public function __construct(string $name, string $type, ?array $parameters = NULL, array $options = []) { $this->definition = new ColumnDefinition($name, $type, $parameters, $options); } - - /** - * @return static - */ - public function moveToFirstPosition() - { + public function moveToFirstPosition(): static + { $this->position = self::POSITION_FIRST; return $this; } - - /** - * @param string $column - * @return static - */ - public function moveAfterColumn($column) - { + public function moveAfterColumn(string $column): static + { $this->position = $column; return $this; } - - /** - * @return static - */ - public function moveToLastPosition() - { + public function moveToLastPosition(): static + { $this->position = self::POSITION_LAST; return $this; } - - /** - * @param bool $nullable - * @return static - */ - public function setNullable($nullable = TRUE) - { + public function setNullable(bool $nullable = TRUE): static + { $this->definition->setNullable($nullable); return $this; } - - /** - * @param mixed|NULL $defaultValue - * @return static - */ - public function setDefaultValue($defaultValue) - { + public function setDefaultValue(mixed $defaultValue): static + { $this->definition->setDefaultValue($defaultValue); return $this; } - - /** - * @param bool $autoIncrement - * @return static - */ - public function setAutoIncrement($autoIncrement = TRUE) - { + public function setAutoIncrement(bool $autoIncrement = TRUE): static + { $this->definition->setAutoIncrement($autoIncrement); return $this; } - - /** - * @param string|NULL $comment - * @return static - */ - public function setComment($comment) - { + public function setComment(?string $comment): static + { $this->definition->setComment($comment); return $this; } - public function toSql(IDriver $driver) - { + /** + * @throws InvalidArgumentException + */ + public function toSql(IDriver $driver): string + { $output = 'ADD COLUMN ' . $this->definition->toSql($driver); if ($this->position === self::POSITION_FIRST) { diff --git a/src/Statements/Alter/AddForeignKey.php b/src/Statements/Alter/AddForeignKey.php index fddda28..c2846a0 100644 --- a/src/Statements/Alter/AddForeignKey.php +++ b/src/Statements/Alter/AddForeignKey.php @@ -6,51 +6,46 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; - use CzProject\SqlGenerator\TableName; - + use CzProject\SqlGenerator\OutOfRangeException; + use CzProject\SqlGenerator\TableName; class AddForeignKey implements IStatement { - /** @var ForeignKeyDefinition */ - private $definition; + private ForeignKeyDefinition $definition; /** - * @param string $name - * @param string[]|string $columns - * @param string|TableName $targetTable - * @param string[]|string $targetColumns + * @param string|string[] $columns + * @param string|string[] $targetColumns */ - public function __construct($name, $columns, $targetTable, $targetColumns) + public function __construct(string $name, array|string $columns, string|TableName $targetTable, array|string $targetColumns) { $this->definition = new ForeignKeyDefinition($name, $columns, $targetTable, $targetColumns); } - /** - * @param string $onUpdateAction - * @return static - */ - public function setOnUpdateAction($onUpdateAction) - { + /** + * @throws OutOfRangeException + */ + public function setOnUpdateAction(string $onUpdateAction): static + { $this->definition->setOnUpdateAction($onUpdateAction); return $this; } - /** - * @param string $onDeleteAction - * @return static - */ - public function setOnDeleteAction($onDeleteAction) - { + /** + * @throws OutOfRangeException + */ + public function setOnDeleteAction(string $onDeleteAction): static + { $this->definition->setOnDeleteAction($onDeleteAction); return $this; } - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string + { return 'ADD ' . $this->definition->toSql($driver); } } diff --git a/src/Statements/Alter/AddIndex.php b/src/Statements/Alter/AddIndex.php index a7480bc..4fb8d68 100644 --- a/src/Statements/Alter/AddIndex.php +++ b/src/Statements/Alter/AddIndex.php @@ -6,39 +6,28 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\OutOfRangeException; - - class AddIndex implements IStatement + class AddIndex implements IStatement { - /** @var IndexDefinition */ - private $definition; - + private IndexDefinition $definition; - /** - * @param string|NULL $name - * @param string $type - */ - public function __construct($name, $type) + /** + * @throws OutOfRangeException + */ + public function __construct(?string $name, string $type) { $this->definition = new IndexDefinition($name, $type); } - - /** - * @param string $column - * @param string $order - * @param int|NULL $length - * @return static - */ - public function addColumn($column, $order = IndexColumnDefinition::ASC, $length = NULL) - { + public function addColumn(string $column, string $order = IndexColumnDefinition::ASC, ?int $length = NULL): static + { $this->definition->addColumn($column, $order, $length); return $this; } - - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string + { return 'ADD ' . $this->definition->toSql($driver); } } diff --git a/src/Statements/Alter/DropColumn.php b/src/Statements/Alter/DropColumn.php index 4146a22..3479c5e 100644 --- a/src/Statements/Alter/DropColumn.php +++ b/src/Statements/Alter/DropColumn.php @@ -7,24 +7,17 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; - class DropColumn implements IStatement { - /** @var string */ - private $column; - + private string $column; - /** - * @param string $column - */ - public function __construct($column) + public function __construct(string $column) { $this->column = $column; } - - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string + { return 'DROP COLUMN ' . $driver->escapeIdentifier($this->column); } } diff --git a/src/Statements/Alter/DropForeignKey.php b/src/Statements/Alter/DropForeignKey.php index f69040e..b20430a 100644 --- a/src/Statements/Alter/DropForeignKey.php +++ b/src/Statements/Alter/DropForeignKey.php @@ -7,24 +7,18 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; - class DropForeignKey implements IStatement { - /** @var string */ - private $foreignKey; - + private string $foreignKey; - /** - * @param string $foreignKey - */ - public function __construct($foreignKey) + public function __construct(string $foreignKey) { $this->foreignKey = $foreignKey; } - public function toSql(IDriver $driver) - { + public function toSql(IDriver $driver): string + { return 'DROP FOREIGN KEY ' . $driver->escapeIdentifier($this->foreignKey); } } diff --git a/src/Statements/Alter/DropIndex.php b/src/Statements/Alter/DropIndex.php index fe5388e..9a02a9a 100644 --- a/src/Statements/Alter/DropIndex.php +++ b/src/Statements/Alter/DropIndex.php @@ -7,31 +7,29 @@ use CzProject\SqlGenerator\Drivers; use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\NotImplementedException; - - class DropIndex implements IStatement + class DropIndex implements IStatement { - /** @var string|NULL */ - private $index; - + private ?string $index; - /** - * @param string|NULL $index - */ - public function __construct($index) + public function __construct(?string $index) { $this->index = $index; } - public function toSql(IDriver $driver) - { + /** + * @throws NotImplementedException + */ + public function toSql(IDriver $driver): string + { if ($this->index === NULL) { // PRIMARY KEY if ($driver instanceof Drivers\MysqlDriver) { return 'DROP PRIMARY KEY'; } else { - throw new \CzProject\SqlGenerator\NotImplementedException('Drop of primary key is not implemented for driver ' . get_class($driver) . '.'); + throw new NotImplementedException('Drop of primary key is not implemented for driver ' . get_class($driver) . '.'); } } diff --git a/src/Statements/Alter/ModifyColumn.php b/src/Statements/Alter/ModifyColumn.php index 8ce3519..1c6a8fd 100644 --- a/src/Statements/Alter/ModifyColumn.php +++ b/src/Statements/Alter/ModifyColumn.php @@ -5,7 +5,8 @@ namespace CzProject\SqlGenerator\Statements; use CzProject\SqlGenerator\IDriver; - use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\InvalidArgumentException; + use CzProject\SqlGenerator\IStatement; use CzProject\SqlGenerator\NotImplementedException; use CzProject\SqlGenerator\Value; @@ -27,43 +28,50 @@ public function __construct(string $name, string $type, ?array $parameters = NUL $this->definition = new ColumnDefinition($name, $type, $parameters, $options); } - public function moveToFirstPosition(): static { + public function moveToFirstPosition(): static + { $this->position = self::POSITION_FIRST; return $this; } - public function moveAfterColumn(string $column): static { + public function moveAfterColumn(string $column): static + { $this->position = $column; return $this; } - public function moveToLastPosition(): static { + public function moveToLastPosition(): static + { $this->position = self::POSITION_LAST; return $this; } - public function setNullable(bool $nullable = TRUE): static { + public function setNullable(bool $nullable = TRUE): static + { $this->definition->setNullable($nullable); return $this; } - public function setDefaultValue(mixed $defaultValue): static { + public function setDefaultValue(mixed $defaultValue): static + { $this->definition->setDefaultValue($defaultValue); return $this; } - public function setAutoIncrement(bool $autoIncrement = TRUE): static { + public function setAutoIncrement(bool $autoIncrement = TRUE): static + { $this->definition->setAutoIncrement($autoIncrement); return $this; } - public function setComment(?string $comment): static { + public function setComment(?string $comment): static + { $this->definition->setComment($comment); return $this; } /** - * @throws NotImplementedException + * @throws NotImplementedException|InvalidArgumentException */ public function toSql(IDriver $driver): string { diff --git a/src/Statements/AlterTable.php b/src/Statements/AlterTable.php index 5f35e81..d60e719 100644 --- a/src/Statements/AlterTable.php +++ b/src/Statements/AlterTable.php @@ -8,7 +8,8 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\InvalidArgumentException; use CzProject\SqlGenerator\IStatement; - use CzProject\SqlGenerator\TableName; + use CzProject\SqlGenerator\OutOfRangeException; + use CzProject\SqlGenerator\TableName; use CzProject\SqlGenerator\Value; class AlterTable implements IStatement @@ -28,7 +29,8 @@ public function __construct(string|TableName $tableName) $this->tableName = Helpers::createTableName($tableName); } - public function rename(string|TableName $newName): RenameTo { + public function rename(string|TableName $newName): RenameTo + { return $this->statements[] = new RenameTo($newName); } @@ -41,11 +43,13 @@ public function renameColumn(string $oldName, string $newName): RenameColumnTo * @param array $parameters * @param array $options [name => value] */ - public function addColumn(string $name, string $type, ?array $parameters = NULL, array $options = []): AddColumn { + public function addColumn(string $name, string $type, ?array $parameters = NULL, array $options = []): AddColumn + { return $this->statements[] = new AddColumn($name, $type, $parameters, $options); } - public function dropColumn(string $column): DropColumn { + public function dropColumn(string $column): DropColumn + { return $this->statements[] = new DropColumn($column); } @@ -53,15 +57,21 @@ public function dropColumn(string $column): DropColumn { * @param array $parameters * @param array $options [name => value] */ - public function modifyColumn(string $name, string $type, ?array $parameters = NULL, array $options = []): ModifyColumn { + public function modifyColumn(string $name, string $type, ?array $parameters = NULL, array $options = []): ModifyColumn + { return $this->statements[] = new ModifyColumn($name, $type, $parameters, $options); } - public function addIndex(?string $name, string $type): AddIndex { + /** + * @throws OutOfRangeException + */ + public function addIndex(?string $name, string $type): AddIndex + { return $this->statements[] = new AddIndex($name, $type); } - public function dropIndex(?string $index): DropIndex { + public function dropIndex(?string $index): DropIndex + { return $this->statements[] = new DropIndex($index); } @@ -69,20 +79,24 @@ public function dropIndex(?string $index): DropIndex { * @param string|string[] $columns * @param string|string[] $targetColumns */ - public function addForeignKey(string $name, array|string $columns, string|TableName $targetTable, array|string $targetColumns): AddForeignKey { + public function addForeignKey(string $name, array|string $columns, string|TableName $targetTable, array|string $targetColumns): AddForeignKey + { return $this->statements[] = new AddForeignKey($name, $columns, $targetTable, $targetColumns); } - public function dropForeignKey(string $foreignKey): DropForeignKey { + public function dropForeignKey(string $foreignKey): DropForeignKey + { return $this->statements[] = new DropForeignKey($foreignKey); } - public function setComment(?string $comment): static { + public function setComment(?string $comment): static + { $this->comment = $comment; return $this; } - public function setOption(string $name, string|Value $value): static { + public function setOption(string $name, string|Value $value): static + { $this->options[$name] = $value; return $this; } @@ -90,7 +104,8 @@ public function setOption(string $name, string|Value $value): static { /** * @throws InvalidArgumentException */ - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { if (empty($this->statements) && empty($this->options) && !isset($this->comment)) { return ''; } diff --git a/src/Statements/Comment.php b/src/Statements/Comment.php index cad6633..d5cd712 100644 --- a/src/Statements/Comment.php +++ b/src/Statements/Comment.php @@ -17,7 +17,8 @@ public function __construct(string $comment) $this->comment = $comment; } - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { return '-- ' . str_replace("\n", "\n-- ", Helpers::normalizeNewLines(trim($this->comment))); } } diff --git a/src/Statements/CreateTable.php b/src/Statements/CreateTable.php index d7b2c61..2a262f0 100644 --- a/src/Statements/CreateTable.php +++ b/src/Statements/CreateTable.php @@ -7,50 +7,46 @@ use CzProject\SqlGenerator\DuplicateException; use CzProject\SqlGenerator\Helpers; use CzProject\SqlGenerator\IDriver; - use CzProject\SqlGenerator\IStatement; - use CzProject\SqlGenerator\TableName; + use CzProject\SqlGenerator\InvalidArgumentException; + use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\OutOfRangeException; + use CzProject\SqlGenerator\TableName; use CzProject\SqlGenerator\Value; class CreateTable implements IStatement { - /** @var string|TableName */ - private $tableName; + private TableName|string $tableName; /** @var array [name => ColumnDefinition] */ - private $columns = []; + private array $columns = []; /** @var array [name => IndexDefinition] */ - private $indexes = []; + private array $indexes = []; /** @var array [name => ForeignKeyDefinition] */ - private $foreignKeys = []; + private array $foreignKeys = []; - /** @var string|NULL */ - private $comment; + private ?string $comment; /** @var array [name => value] */ - private $options = []; - + private array $options = []; /** - * @param string|TableName $tableName + * @param string|TableName $tableName */ - public function __construct($tableName) + public function __construct(string|TableName $tableName) { $this->tableName = Helpers::createTableName($tableName); } - - /** - * @param string $name - * @param string $type - * @param array|NULL $parameters - * @param array $options - * @return ColumnDefinition - */ - public function addColumn($name, $type, ?array $parameters = NULL, array $options = []) - { + /** + * @param array|NULL $parameters + * @param array $options + * @throws DuplicateException + */ + public function addColumn(string $name, string $type, ?array $parameters = NULL, array $options = []): ColumnDefinition + { if (isset($this->columns[$name])) { throw new DuplicateException("Column '$name' already exists."); } @@ -58,14 +54,11 @@ public function addColumn($name, $type, ?array $parameters = NULL, array $option return $this->columns[$name] = new ColumnDefinition($name, $type, $parameters, $options); } - - /** - * @param string|NULL $name - * @param string $type - * @return IndexDefinition - */ - public function addIndex($name, $type) - { + /** + * @throws DuplicateException|OutOfRangeException + */ + public function addIndex(?string $name, string $type): IndexDefinition + { if (isset($this->indexes[$name])) { throw new DuplicateException("Index '$name' already exists."); } @@ -73,16 +66,13 @@ public function addIndex($name, $type) return $this->indexes[$name] = new IndexDefinition($name, $type); } - - /** - * @param string $name - * @param string[]|string $columns - * @param string|TableName $targetTable - * @param string[]|string $targetColumns - * @return ForeignKeyDefinition - */ - public function addForeignKey($name, $columns, $targetTable, $targetColumns) - { + /** + * @param string|string[] $columns + * @param string|string[] $targetColumns + * @throws DuplicateException + */ + public function addForeignKey(string $name, array|string $columns, string|TableName $targetTable, array|string $targetColumns): ForeignKeyDefinition + { if (isset($this->foreignKeys[$name])) { throw new DuplicateException("Foreign key '$name' already exists."); } @@ -90,32 +80,24 @@ public function addForeignKey($name, $columns, $targetTable, $targetColumns) return $this->foreignKeys[$name] = new ForeignKeyDefinition($name, $columns, $targetTable, $targetColumns); } - - /** - * @param string|NULL $comment - * @return static - */ - public function setComment($comment) - { + public function setComment(?string $comment): static + { $this->comment = $comment; return $this; } - - /** - * @param string $name - * @param string|Value $value - * @return static - */ - public function setOption($name, $value) - { + public function setOption(string $name, string|Value $value): static + { $this->options[$name] = $value; return $this; } - public function toSql(IDriver $driver) - { + /** + * @throws InvalidArgumentException + */ + public function toSql(IDriver $driver): string + { $output = 'CREATE TABLE ' . Helpers::escapeTableName($this->tableName, $driver) . " (\n"; // columns diff --git a/src/Statements/DropTable.php b/src/Statements/DropTable.php index 22608b7..591197d 100644 --- a/src/Statements/DropTable.php +++ b/src/Statements/DropTable.php @@ -18,7 +18,8 @@ public function __construct(string|TableName $tableName) $this->tableName = Helpers::createTableName($tableName); } - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { return 'DROP TABLE ' . Helpers::escapeTableName($this->tableName, $driver) . ';'; } } diff --git a/src/Statements/Insert.php b/src/Statements/Insert.php index d8f4584..ed60562 100644 --- a/src/Statements/Insert.php +++ b/src/Statements/Insert.php @@ -29,7 +29,8 @@ public function __construct(string|TableName $tableName, array $data) /** * @throws InvalidArgumentException */ - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { try { $tableName = Helpers::escapeTableName($this->tableName, $driver); $fields = implode(',', array_map(static fn (string $field) => $driver->escapeIdentifier($field), array_keys($this->data))); diff --git a/src/Statements/RenameColumn.php b/src/Statements/RenameColumn.php index 72e32c0..40bc18b 100644 --- a/src/Statements/RenameColumn.php +++ b/src/Statements/RenameColumn.php @@ -26,7 +26,8 @@ public function __construct(TableName|string $tableName, string $oldColumn, stri /** * @throws NotImplementedException|InvalidArgumentException */ - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { if ($driver->renameColumn ?? true) { $this->renameColumn($this->oldColumn, $this->newColumn); diff --git a/src/Statements/RenameTable.php b/src/Statements/RenameTable.php index add503d..61f1390 100644 --- a/src/Statements/RenameTable.php +++ b/src/Statements/RenameTable.php @@ -24,7 +24,8 @@ public function __construct(TableName|string $oldTable, TableName|string $newTab /** * @throws NotImplementedException|InvalidArgumentException */ - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { if ($driver->renameTable ?? true) { $this->rename($this->newTable); diff --git a/src/Statements/SqlCommand.php b/src/Statements/SqlCommand.php index d8e3070..2fb8a1a 100644 --- a/src/Statements/SqlCommand.php +++ b/src/Statements/SqlCommand.php @@ -16,7 +16,8 @@ public function __construct(string $command) $this->command = $command; } - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { return rtrim($this->command, ';') . ';'; } } diff --git a/src/Statements/Transaction.php b/src/Statements/Transaction.php index a2256b7..a42b949 100644 --- a/src/Statements/Transaction.php +++ b/src/Statements/Transaction.php @@ -7,7 +7,8 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; - class Transaction implements IStatement { + class Transaction implements IStatement + { /** * @var 'start'|'commit'|'rollback' */ @@ -16,11 +17,13 @@ class Transaction implements IStatement { /** * @param 'start'|'commit'|'rollback' $action */ - public function __construct(string $action) { + public function __construct(string $action) + { $this->action = $action; } - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { return $driver->transaction($this->action); } } \ No newline at end of file From e0a30235711a9a8c24a90a72719648be6fe55817 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 15:00:02 -0400 Subject: [PATCH 21/23] SQLite only supports a few `ALTER TABLE` commands --- src/Statements/Alter/AddColumn.php | 14 ++++++++------ src/Statements/Alter/AddForeignKey.php | 13 +++++++++++-- src/Statements/Alter/AddIndex.php | 13 +++++++++++-- src/Statements/Alter/DropForeignKey.php | 15 ++++++++++++--- src/Statements/Alter/DropIndex.php | 25 +++++++++++++++---------- 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/src/Statements/Alter/AddColumn.php b/src/Statements/Alter/AddColumn.php index 5d3e21f..f5f05c9 100644 --- a/src/Statements/Alter/AddColumn.php +++ b/src/Statements/Alter/AddColumn.php @@ -78,12 +78,14 @@ public function toSql(IDriver $driver): string { $output = 'ADD COLUMN ' . $this->definition->toSql($driver); - if ($this->position === self::POSITION_FIRST) { - $output .= ' FIRST'; - - } elseif ($this->position !== self::POSITION_LAST) { - $output .= ' AFTER ' . $driver->escapeIdentifier($this->position); - } + if ($driver->modifyColumn ?? true) { + if ($this->position === self::POSITION_FIRST) { + $output .= ' FIRST'; + } + elseif ($this->position !== self::POSITION_LAST) { + $output .= ' AFTER ' . $driver->escapeIdentifier($this->position); + } + } return $output; } diff --git a/src/Statements/Alter/AddForeignKey.php b/src/Statements/Alter/AddForeignKey.php index c2846a0..2dae0fe 100644 --- a/src/Statements/Alter/AddForeignKey.php +++ b/src/Statements/Alter/AddForeignKey.php @@ -6,6 +6,7 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\NotImplementedException; use CzProject\SqlGenerator\OutOfRangeException; use CzProject\SqlGenerator\TableName; @@ -44,8 +45,16 @@ public function setOnDeleteAction(string $onDeleteAction): static } - public function toSql(IDriver $driver): string + /** + * @throws NotImplementedException + */ + public function toSql(IDriver $driver): string { - return 'ADD ' . $this->definition->toSql($driver); + if ($driver->modifyColumn ?? true) { + return 'ADD ' . $this->definition->toSql($driver); + } + else { + throw new NotImplementedException('Add key is not implemented for driver ' . get_class($driver) . '.'); + } } } diff --git a/src/Statements/Alter/AddIndex.php b/src/Statements/Alter/AddIndex.php index 4fb8d68..bc348ef 100644 --- a/src/Statements/Alter/AddIndex.php +++ b/src/Statements/Alter/AddIndex.php @@ -6,6 +6,7 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\NotImplementedException; use CzProject\SqlGenerator\OutOfRangeException; class AddIndex implements IStatement @@ -26,8 +27,16 @@ public function addColumn(string $column, string $order = IndexColumnDefinition: return $this; } - public function toSql(IDriver $driver): string + /** + * @throws NotImplementedException + */ + public function toSql(IDriver $driver): string { - return 'ADD ' . $this->definition->toSql($driver); + if ($driver->modifyColumn ?? true) { + return 'ADD ' . $this->definition->toSql($driver); + } + else { + throw new NotImplementedException('Add key is not implemented for driver ' . get_class($driver) . '.'); + } } } diff --git a/src/Statements/Alter/DropForeignKey.php b/src/Statements/Alter/DropForeignKey.php index b20430a..e14674d 100644 --- a/src/Statements/Alter/DropForeignKey.php +++ b/src/Statements/Alter/DropForeignKey.php @@ -6,8 +6,9 @@ use CzProject\SqlGenerator\IDriver; use CzProject\SqlGenerator\IStatement; + use CzProject\SqlGenerator\NotImplementedException; - class DropForeignKey implements IStatement + class DropForeignKey implements IStatement { private string $foreignKey; @@ -17,8 +18,16 @@ public function __construct(string $foreignKey) } - public function toSql(IDriver $driver): string + /** + * @throws NotImplementedException + */ + public function toSql(IDriver $driver): string { - return 'DROP FOREIGN KEY ' . $driver->escapeIdentifier($this->foreignKey); + if ($driver->modifyColumn ?? true) { + return 'DROP FOREIGN KEY ' . $driver->escapeIdentifier($this->foreignKey); + } + else { + throw new NotImplementedException('Drop key is not implemented for driver ' . get_class($driver) . '.'); + } } } diff --git a/src/Statements/Alter/DropIndex.php b/src/Statements/Alter/DropIndex.php index 9a02a9a..b80acd0 100644 --- a/src/Statements/Alter/DropIndex.php +++ b/src/Statements/Alter/DropIndex.php @@ -24,15 +24,20 @@ public function __construct(?string $index) */ public function toSql(IDriver $driver): string { - if ($this->index === NULL) { // PRIMARY KEY - if ($driver instanceof Drivers\MysqlDriver) { - return 'DROP PRIMARY KEY'; - - } else { - throw new NotImplementedException('Drop of primary key is not implemented for driver ' . get_class($driver) . '.'); - } - } - - return 'DROP INDEX ' . $driver->escapeIdentifier($this->index); + if ($driver->modifyColumn ?? true) { + if ($this->index === NULL) { // PRIMARY KEY + if ($driver instanceof Drivers\MysqlDriver) { + return 'DROP PRIMARY KEY'; + + } else { + throw new NotImplementedException('Drop of primary key is not implemented for driver ' . get_class($driver) . '.'); + } + } + + return 'DROP INDEX ' . $driver->escapeIdentifier($this->index); + } + else { + throw new NotImplementedException('Drop key is not implemented for driver ' . get_class($driver) . '.'); + } } } From 9fa1f6ae5663d52a4aa2ff8bde9c2954cc1cd49a Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 15:07:27 -0400 Subject: [PATCH 22/23] Additional formatting --- src/Helpers.php | 33 ++++++++++++++++++--------------- src/SqlDocument.php | 45 +++++++++++++++++++++++++++++++-------------- src/TableName.php | 6 ++++-- src/Value.php | 6 ++++-- 4 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/Helpers.php b/src/Helpers.php index c586611..09f03c5 100644 --- a/src/Helpers.php +++ b/src/Helpers.php @@ -20,31 +20,32 @@ public function __construct() * @throws InvalidArgumentException * @see https://api.dibiphp.com/3.0/source-Dibi.Translator.php.html#174 */ - public static function formatValue(mixed $value, IDriver $driver): string { + public static function formatValue(mixed $value, IDriver $driver): string + { if (is_string($value)) { return $driver->escapeText($value); - - } elseif (is_int($value)) { + } + elseif (is_int($value)) { return (string)$value; - - } elseif (is_float($value)) { + } + elseif (is_float($value)) { return rtrim(rtrim(number_format($value, 10, '.', ''), '0'), '.'); - - } elseif (is_bool($value)) { + } + elseif (is_bool($value)) { return $driver->escapeBool($value); - - } elseif ($value === NULL) { + } + elseif ($value === NULL) { return 'NULL'; - - } elseif ($value instanceof DateTimeInterface) { + } + elseif ($value instanceof DateTimeInterface) { return $driver->escapeDateTime($value); - } throw new InvalidArgumentException("Unsupported value type."); } - public static function escapeTableName(TableName|string $tableName, IDriver $driver): string { + public static function escapeTableName(TableName|string $tableName, IDriver $driver): string + { if ($tableName instanceof TableName) { return $tableName->toString($driver); } @@ -52,7 +53,8 @@ public static function escapeTableName(TableName|string $tableName, IDriver $dri return $driver->escapeIdentifier($tableName); } - public static function createTableName(TableName|string $tableName): TableName|string { + public static function createTableName(TableName|string $tableName): TableName|string + { if (is_string($tableName) && strpos($tableName, '.')) { return TableName::create($tableName); } @@ -60,7 +62,8 @@ public static function createTableName(TableName|string $tableName): TableName|s return $tableName; } - public static function normalizeNewLines(string $s): string { + public static function normalizeNewLines(string $s): string + { return str_replace(["\r\n", "\r"], "\n", $s); } } diff --git a/src/SqlDocument.php b/src/SqlDocument.php index b5c0d88..d80ef69 100644 --- a/src/SqlDocument.php +++ b/src/SqlDocument.php @@ -4,24 +4,29 @@ namespace CzProject\SqlGenerator; + use CzProject\SqlGenerator\Statements\Transaction; + class SqlDocument { /** @var IStatement[] */ private array $statements = []; - public function addStatement(IStatement $statement): static { + public function addStatement(IStatement $statement): static + { $this->statements[] = $statement; return $this; } - public function isEmpty(): bool { + public function isEmpty(): bool + { return empty($this->statements); } /** * @return string[] */ - public function getSqlQueries(IDriver $driver): array { + public function getSqlQueries(IDriver $driver): array + { $output = []; foreach ($this->statements as $statement) { @@ -31,7 +36,8 @@ public function getSqlQueries(IDriver $driver): array { return $output; } - public function toSql(IDriver $driver): string { + public function toSql(IDriver $driver): string + { $output = ''; $first = TRUE; @@ -53,7 +59,8 @@ public function toSql(IDriver $driver): string { /** * @throws IOException */ - public function save(string $file, IDriver $driver): void { + public function save(string $file, IDriver $driver): void + { // create directory $dir = dirname($file); @@ -71,8 +78,10 @@ public function save(string $file, IDriver $driver): void { /** * @param 'start'|'commit'|'rollback' $action + * @return Transaction */ - public function transaction(string $action): Statements\Transaction { + public function transaction(string $action): Statements\Transaction + { $statement = new Statements\Transaction($action); $this->addStatement($statement); return $statement; @@ -81,7 +90,8 @@ public function transaction(string $action): Statements\Transaction { /** * @param array $data */ - public function insert(string|TableName $tableName, array $data): Statements\Insert { + public function insert(string|TableName $tableName, array $data): Statements\Insert + { $statement = new Statements\Insert($tableName, $data); $this->addStatement($statement); return $statement; @@ -91,43 +101,50 @@ public function insert(string|TableName $tableName, array $data): Statements\Ins * @param array $data * @param array $where */ - public function update(string|TableName $tableName, array $data, array $where): Statements\Update { + public function update(string|TableName $tableName, array $data, array $where): Statements\Update + { $statement = new Statements\Update($tableName, $data, $where); $this->addStatement($statement); return $statement; } - public function createTable(string|TableName $tableName): Statements\CreateTable { + public function createTable(string|TableName $tableName): Statements\CreateTable + { $statement = new Statements\CreateTable($tableName); $this->addStatement($statement); return $statement; } - public function dropTable(string|TableName $tableName): Statements\DropTable { + public function dropTable(string|TableName $tableName): Statements\DropTable + { $statement = new Statements\DropTable($tableName); $this->addStatement($statement); return $statement; } - public function renameTable(string|TableName $oldTable, string|TableName $newTable): Statements\RenameTable { + public function renameTable(string|TableName $oldTable, string|TableName $newTable): Statements\RenameTable + { $statement = new Statements\RenameTable($oldTable, $newTable); $this->addStatement($statement); return $statement; } - public function alterTable(string|TableName $tableName): Statements\AlterTable { + public function alterTable(string|TableName $tableName): Statements\AlterTable + { $statement = new Statements\AlterTable($tableName); $this->addStatement($statement); return $statement; } - public function command(string $command): Statements\SqlCommand { + public function command(string $command): Statements\SqlCommand + { $statement = new Statements\SqlCommand($command); $this->addStatement($statement); return $statement; } - public function comment(string $comment): Statements\Comment { + public function comment(string $comment): Statements\Comment + { $statement = new Statements\Comment($comment); $this->addStatement($statement); return $statement; diff --git a/src/TableName.php b/src/TableName.php index 4c092a8..b02b1b3 100644 --- a/src/TableName.php +++ b/src/TableName.php @@ -17,7 +17,8 @@ public function __construct(...$parts) $this->parts = $parts; } - public function toString(IDriver $driver): string { + public function toString(IDriver $driver): string + { $res = []; foreach ($this->parts as $part) { @@ -27,7 +28,8 @@ public function toString(IDriver $driver): string { return implode('.', $res); } - public static function create(string $name): self { + public static function create(string $name): self + { $parts = explode('.', $name); return new self(...$parts); } diff --git a/src/Value.php b/src/Value.php index f394f3a..c012fc5 100644 --- a/src/Value.php +++ b/src/Value.php @@ -25,7 +25,8 @@ public function __construct(int|float|bool|string|Stringable|DateTimeInterface $ /** * @throws InvalidArgumentException */ - public function toString(IDriver $driver): string { + public function toString(IDriver $driver): string + { return Helpers::formatValue($this->value, $driver); } @@ -33,7 +34,8 @@ public function toString(IDriver $driver): string { /** * @param scalar|Stringable|DateTimeInterface $value */ - public static function create(int|float|bool|string|Stringable|DateTimeInterface $value): self { + public static function create(int|float|bool|string|Stringable|DateTimeInterface $value): self + { return new self($value); } } From aa08011a1fc018c7da4406455dec7e64178c42d8 Mon Sep 17 00:00:00 2001 From: Eric Siegel Date: Tue, 9 Sep 2025 15:09:44 -0400 Subject: [PATCH 23/23] Document the extra `IDriver` properties --- src/IDriver.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/IDriver.php b/src/IDriver.php index 5478ac6..b304882 100644 --- a/src/IDriver.php +++ b/src/IDriver.php @@ -6,6 +6,12 @@ use DateTimeInterface; + /** + * A few additional properties can be set to control the driver's capabilities: + * - `public bool $renameTable` + * - `public bool $renameColumn` + * - `public bool $modifyColumn` + */ interface IDriver { public function escapeIdentifier(string $value): string;