From e4c8021569121df40918c258ef9447b67f2c97fc Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 8 Sep 2021 18:05:38 +0300 Subject: [PATCH 1/5] Sync with spiral/database/pull/80 --- .github/workflows/main.yml | 1 - .gitignore | 3 +- phpunit.xml | 2 +- src/Driver/Driver.php | 19 +- src/Driver/DriverInterface.php | 10 + .../Postgres/Query/PostgresInsertQuery.php | 5 + src/Exception/ReadonlyConnectionException.php | 27 ++ src/Query/InsertQuery.php | 1 - tests/Database/BaseTest.php | 26 +- tests/Database/Driver/MySQL/ReadonlyTest.php | 21 ++ .../Database/Driver/Postgres/ReadonlyTest.php | 21 ++ .../Driver/SQLServer/ReadonlyTest.php | 21 ++ tests/Database/Driver/SQLite/ReadonlyTest.php | 21 ++ tests/Database/ReadonlyTest.php | 286 ++++++++++++++++++ tests/bootstrap.php | 2 +- 15 files changed, 447 insertions(+), 19 deletions(-) create mode 100644 src/Exception/ReadonlyConnectionException.php create mode 100644 tests/Database/Driver/MySQL/ReadonlyTest.php create mode 100644 tests/Database/Driver/Postgres/ReadonlyTest.php create mode 100644 tests/Database/Driver/SQLServer/ReadonlyTest.php create mode 100644 tests/Database/Driver/SQLite/ReadonlyTest.php create mode 100644 tests/Database/ReadonlyTest.php diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2896f219..bbab423a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,7 +66,6 @@ jobs: file: ./coverage.xml sqlite: - needs: lint name: SQLite PHP ${{ matrix.php-versions }} runs-on: ubuntu-latest strategy: diff --git a/.gitignore b/.gitignore index 36ee7b85..d89a76d9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ build/ *.db clover.xml clover.json -.php_cs.cache \ No newline at end of file +.php_cs.cache +.phpunit.result.cache diff --git a/phpunit.xml b/phpunit.xml index 1d822c27..c7cd12cf 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -22,4 +22,4 @@ src/ - \ No newline at end of file + diff --git a/src/Driver/Driver.php b/src/Driver/Driver.php index 690955e2..fffea70e 100644 --- a/src/Driver/Driver.php +++ b/src/Driver/Driver.php @@ -19,6 +19,7 @@ use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Cycle\Database\Exception\DriverException; +use Cycle\Database\Exception\ReadonlyConnectionException; use Cycle\Database\Exception\StatementException; use Cycle\Database\Injection\ParameterInterface; use Cycle\Database\Query\BuilderInterface; @@ -69,7 +70,10 @@ abstract class Driver implements DriverInterface, LoggerAwareInterface 'queryCache' => true, // disable schema modifications - 'readonlySchema' => false + 'readonlySchema' => false, + + // disable write expressions + 'readonly' => false, ]; /** @var PDO|null */ @@ -125,6 +129,14 @@ public function __construct( } } + /** + * {@inheritDoc} + */ + public function isReadonly(): bool + { + return (bool)($this->options['readonly'] ?? false); + } + /** * Disconnect and destruct. */ @@ -316,9 +328,14 @@ public function query(string $statement, array $parameters = []): StatementInter * @return int * * @throws StatementException + * @throws ReadonlyConnectionException */ public function execute(string $query, array $parameters = []): int { + if ($this->isReadonly()) { + throw ReadonlyConnectionException::onWriteStatementExecution(); + } + return $this->statement($query, $parameters)->rowCount(); } diff --git a/src/Driver/DriverInterface.php b/src/Driver/DriverInterface.php index a6bb9493..fa1dd013 100644 --- a/src/Driver/DriverInterface.php +++ b/src/Driver/DriverInterface.php @@ -14,6 +14,7 @@ use DateTimeZone; use PDO; use Cycle\Database\Exception\DriverException; +use Cycle\Database\Exception\ReadonlyConnectionException; use Cycle\Database\Exception\StatementException; use Cycle\Database\Query\BuilderInterface; use Cycle\Database\StatementInterface; @@ -86,6 +87,14 @@ interface DriverInterface */ public const ISOLATION_READ_UNCOMMITTED = 'READ UNCOMMITTED'; + /** + * Returns {@see true} in the case that the connection is available only + * for reading or {@see false} instead. + * + * @return bool + */ + public function isReadonly(): bool; + /** * Driver type (name). * @@ -169,6 +178,7 @@ public function query(string $statement, array $parameters = []): StatementInter * @return int * * @throws StatementException + * @throws ReadonlyConnectionException */ public function execute(string $query, array $parameters = []): int; diff --git a/src/Driver/Postgres/Query/PostgresInsertQuery.php b/src/Driver/Postgres/Query/PostgresInsertQuery.php index 2bce8532..69564177 100644 --- a/src/Driver/Postgres/Query/PostgresInsertQuery.php +++ b/src/Driver/Postgres/Query/PostgresInsertQuery.php @@ -14,6 +14,7 @@ use Cycle\Database\Driver\DriverInterface; use Cycle\Database\Driver\Postgres\PostgresDriver; use Cycle\Database\Exception\BuilderException; +use Cycle\Database\Exception\ReadonlyConnectionException; use Cycle\Database\Query\InsertQuery; use Cycle\Database\Query\QueryInterface; use Cycle\Database\Query\QueryParameters; @@ -67,6 +68,10 @@ public function run() $params = new QueryParameters(); $queryString = $this->sqlStatement($params); + if ($this->driver->isReadonly()) { + throw ReadonlyConnectionException::onWriteStatementExecution(); + } + $result = $this->driver->query($queryString, $params->getParameters()); try { diff --git a/src/Exception/ReadonlyConnectionException.php b/src/Exception/ReadonlyConnectionException.php new file mode 100644 index 00000000..e9d77630 --- /dev/null +++ b/src/Exception/ReadonlyConnectionException.php @@ -0,0 +1,27 @@ +driver)) { $class = $config['driver']; - $this->driver = new $class( - [ - 'connection' => $config['conn'], - 'username' => $config['user'], - 'password' => $config['pass'], - 'options' => [], - 'queryCache' => true - ] - ); + $this->driver = new $class(\array_merge($options, [ + 'connection' => $config['conn'], + 'username' => $config['user'], + 'password' => $config['pass'], + 'options' => [], + 'queryCache' => true + ])); } static::$logger = static::$logger ?? new TestLogger(); @@ -83,15 +83,15 @@ public function getDriver(): Driver /** * @param string $name * @param string $prefix - * + * @param array $config * @return Database|null When non empty null will be given, for safety, for science. */ - protected function db(string $name = 'default', string $prefix = '') + protected function db(string $name = 'default', string $prefix = '', array $config = []): ?Database { if (isset(static::$driverCache[static::DRIVER])) { $driver = static::$driverCache[static::DRIVER]; } else { - static::$driverCache[static::DRIVER] = $driver = $this->getDriver(); + static::$driverCache[static::DRIVER] = $driver = $this->getDriver($config); } return new Database($name, $prefix, $driver); diff --git a/tests/Database/Driver/MySQL/ReadonlyTest.php b/tests/Database/Driver/MySQL/ReadonlyTest.php new file mode 100644 index 00000000..faaa7528 --- /dev/null +++ b/tests/Database/Driver/MySQL/ReadonlyTest.php @@ -0,0 +1,21 @@ +database = new Database('default', '', $this->getDriver(['readonly' => true])); + + $this->allowWrite(function () { + $table = $this->database->table($this->table); + $schema = $table->getSchema(); + $schema->primary('id'); + $schema->string('value')->nullable(); + $schema->save(); + }); + } + + private function allowWrite(\Closure $then): void + { + /** @var Driver $driver */ + $driver = $this->database->getDriver(); + + (function (\Closure $then): void { + $this->options['readonly'] = false; + try { + $then(); + } finally { + $this->options['readonly'] = true; + } + })->call($driver, $then); + } + + public function tearDown(): void + { + $this->allowWrite(function () { + $schema = $this->database->table($this->table) + ->getSchema(); + + $schema->declareDropped(); + $schema->save(); + }); + } + + protected function table(): Table + { + return $this->database->table($this->table); + } + + public function testTableAllowSelection(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->select() + ->run() + ; + } + + public function testTableAllowCount(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->count() + ; + } + + public function testTableAllowExists(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->exists() + ; + } + + public function testTableAllowGetPrimaryKeys(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->getPrimaryKeys() + ; + } + + public function testTableAllowHasColumn(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->hasColumn('column') + ; + } + + public function testTableAllowGetColumns(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->getColumns() + ; + } + + public function testTableAllowHasIndex(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->hasIndex(['column']) + ; + } + + public function testTableAllowGetIndexes(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->getIndexes() + ; + } + + public function testTableAllowHasForeignKey(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->hasForeignKey(['column']) + ; + } + + public function testTableAllowGetForeignKeys(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->getForeignKeys() + ; + } + + public function testTableAllowGetDependencies(): void + { + $this->expectNotToPerformAssertions(); + + $this->table() + ->getDependencies() + ; + } + + public function testTableRejectInsertOne(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->table() + ->insertOne(['value' => 'example']) + ; + } + + public function testTableRejectInsertMultiple(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->table() + ->insertMultiple(['value'], ['example']) + ; + } + + public function testTableRejectInsert(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->table() + ->insert() + ->columns('value') + ->values('example') + ->run(); + } + + public function testTableRejectUpdate(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->table() + ->update(['value' => 'updated']) + ->run() + ; + } + + public function testTableRejectDelete(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->table() + ->delete() + ->run() + ; + } + + public function testTableRejectEraseData(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->table() + ->eraseData() + ; + } + + public function testSchemaRejectSaving(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $table = $this->database + ->table('not_allowed_to_creation'); + + $schema = $table->getSchema(); + $schema->primary('id'); + $schema->string('value')->nullable(); + $schema->save(); + } + + public function testDatabaseAllowSelection(): void + { + $this->expectNotToPerformAssertions(); + + $this->database->select() + ->from($this->table) + ->run() + ; + } + + public function testDatabaseRejectUpdate(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->database->update($this->table, ['value' => 'example']) + ->run() + ; + } + + public function testDatabaseRejectInsert(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->database->insert($this->table) + ->columns('value') + ->values('example') + ->run() + ; + } + + public function testDatabaseRejectDelete(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->database->delete($this->table) + ->run() + ; + } + + public function testDatabaseAllowRawQuery(): void + { + $this->expectNotToPerformAssertions(); + + $this->database->query('SELECT 1'); + } + + public function testDatabaseRejectRawExecution(): void + { + $this->expectException(ReadonlyConnectionException::class); + + $this->database->execute("DROP TABLE {$this->table}"); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 506d98d4..22b762c4 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -53,7 +53,7 @@ $db = getenv('DB') ?: null; Database\Tests\BaseTest::$config = [ - 'debug' => false, + 'debug' => getenv('DB_DEBUG') ?: false, ] + ($db === null ? $drivers : array_intersect_key($drivers, array_flip((array)$db)) From a95b7863c661c78e13e83e898161bd27abc987b2 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 8 Sep 2021 18:23:32 +0300 Subject: [PATCH 2/5] Sync with spiral/database/pull/78: add supporting of general URI DSN --- src/Driver/Driver.php | 73 ++++++++++++++++++++++++++++++++++--- tests/Database/BaseTest.php | 4 +- tests/bootstrap.php | 4 +- 3 files changed, 70 insertions(+), 11 deletions(-) diff --git a/src/Driver/Driver.php b/src/Driver/Driver.php index fffea70e..4c67924d 100644 --- a/src/Driver/Driver.php +++ b/src/Driver/Driver.php @@ -18,6 +18,7 @@ use PDOStatement; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; +use Cycle\Database\Exception\ConfigException; use Cycle\Database\Exception\DriverException; use Cycle\Database\Exception\ReadonlyConnectionException; use Cycle\Database\Exception\StatementException; @@ -127,6 +128,33 @@ public function __construct( if ($this->options['readonlySchema']) { $this->schemaHandler = new ReadonlyHandler($this->schemaHandler); } + + // Actualize DSN + $this->updateDSN(); + } + + /** + * Updates an internal options + * + * @return void + */ + private function updateDSN(): void + { + [$connection, $this->options['username'], $this->options['password']] = $this->parseDSN(); + + // Update connection. The DSN field can be located in one of the + // following keys of the configuration array. + switch (true) { + case \array_key_exists('dsn', $this->options): + $this->options['dsn'] = $connection; + break; + case \array_key_exists('addr', $this->options): + $this->options['addr'] = $connection; + break; + default: + $this->options['connection'] = $connection; + break; + } } /** @@ -706,6 +734,42 @@ protected function rollbackSavepoint(int $level): void $this->execute('ROLLBACK TO SAVEPOINT ' . $this->identifier("SVP{$level}")); } + /** + * @return array{string, string, string} + */ + private function parseDSN(): array + { + $dsn = $this->getDSN(); + + $user = (string)($this->options['username'] ?? ''); + $pass = (string)($this->options['password'] ?? ''); + + if (\strpos($dsn, '://') > 0) { + $parts = \parse_url($dsn); + + if (!isset($parts['scheme'])) { + throw new ConfigException('Configuration database scheme must be defined'); + } + + // Update username and password from DSN if not defined. + $user = $user ?: $parts['user'] ?? ''; + $pass = $pass ?: $parts['pass'] ?? ''; + + // Build new DSN + $dsn = \sprintf('%s:host=%s', $parts['scheme'], $parts['host'] ?? 'localhost'); + + if (isset($parts['port'])) { + $dsn .= ';port=' . $parts['port']; + } + + if (isset($parts['path']) && \trim($parts['path'], '/')) { + $dsn .= ';dbname=' . \trim($parts['path'], '/'); + } + } + + return [$dsn, $user, $pass]; + } + /** * Create instance of configured PDO class. * @@ -713,12 +777,9 @@ protected function rollbackSavepoint(int $level): void */ protected function createPDO(): PDO { - return new PDO( - $this->getDSN(), - $this->options['username'], - $this->options['password'], - $this->options['options'] - ); + [$dsn, $user, $pass] = $this->parseDSN(); + + return new PDO($dsn, $user, $pass, $this->options['options']); } /** diff --git a/tests/Database/BaseTest.php b/tests/Database/BaseTest.php index 2f7a7dbb..f9898307 100644 --- a/tests/Database/BaseTest.php +++ b/tests/Database/BaseTest.php @@ -63,8 +63,8 @@ public function getDriver(array $options = []): Driver $this->driver = new $class(\array_merge($options, [ 'connection' => $config['conn'], - 'username' => $config['user'], - 'password' => $config['pass'], + 'username' => $config['user'] ?? '', + 'password' => $config['pass'] ?? '', 'options' => [], 'queryCache' => true ])); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 22b762c4..ec5fd432 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -30,9 +30,7 @@ ], 'mysql' => [ 'driver' => Database\Driver\MySQL\MySQLDriver::class, - 'conn' => 'mysql:host=127.0.0.1:13306;dbname=spiral', - 'user' => 'root', - 'pass' => 'root', + 'conn' => 'mysql://root:root@127.0.0.1:13306/spiral', 'queryCache' => 100 ], 'postgres' => [ From 6b136a51a99a09e938e78a8b74183908de3a2b40 Mon Sep 17 00:00:00 2001 From: roxblnfk Date: Wed, 15 Sep 2021 23:19:51 +0300 Subject: [PATCH 3/5] Update StatementInterface: add IteratorAggregate --- src/Driver/Statement.php | 2 +- src/StatementInterface.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Driver/Statement.php b/src/Driver/Statement.php index a19c1acf..deabfb65 100644 --- a/src/Driver/Statement.php +++ b/src/Driver/Statement.php @@ -21,7 +21,7 @@ * * @internal Do not use this class directly. */ -final class Statement implements StatementInterface, \IteratorAggregate +final class Statement implements StatementInterface { /** @var PDOStatement */ private $pdoStatement; diff --git a/src/StatementInterface.php b/src/StatementInterface.php index 75c1363e..75ba4223 100644 --- a/src/StatementInterface.php +++ b/src/StatementInterface.php @@ -15,7 +15,7 @@ * Must implement Traversable as IteratorAggregate or Iterator. You can access underlying PDOStatement * using getPDOStatement() method of `Cycle\Database\Driver\Statement` object. */ -interface StatementInterface +interface StatementInterface extends \IteratorAggregate { // Fetch rows as assoc array. Default. public const FETCH_ASSOC = 2; From 5f216a9072c97dcdcf06555971a760cbca0e641d Mon Sep 17 00:00:00 2001 From: Kirill Nesmeyanov Date: Mon, 4 Oct 2021 11:19:40 +0300 Subject: [PATCH 4/5] Add spiral/database polyfill (#7) --- .editorconfig | 20 + .styleci.yml | 75 ++ LICENSE | 2 +- composer.json | 92 ++- phpcs.xml | 45 ++ psalm.xml | 26 + resources/.phpstorm.meta.php | 754 ++++++++++++++++++ src/ColumnInterface.php | 6 +- src/Config/DatabaseConfig.php | 6 +- src/Config/DatabasePartial.php | 6 +- src/Database.php | 16 +- src/DatabaseInterface.php | 6 +- src/DatabaseManager.php | 9 +- src/DatabaseProviderInterface.php | 6 +- src/Driver/CachingCompilerInterface.php | 6 +- src/Driver/Compiler.php | 7 +- src/Driver/CompilerCache.php | 6 +- src/Driver/CompilerInterface.php | 6 +- src/Driver/Driver.php | 6 +- src/Driver/DriverInterface.php | 6 +- src/Driver/Handler.php | 6 +- src/Driver/HandlerInterface.php | 6 +- src/Driver/MySQL/Exception/MySQLException.php | 6 +- src/Driver/MySQL/MySQLCompiler.php | 6 +- src/Driver/MySQL/MySQLDriver.php | 6 +- src/Driver/MySQL/MySQLHandler.php | 10 +- src/Driver/MySQL/Schema/MySQLColumn.php | 6 +- src/Driver/MySQL/Schema/MySQLForeignKey.php | 6 +- src/Driver/MySQL/Schema/MySQLIndex.php | 6 +- src/Driver/MySQL/Schema/MySQLTable.php | 6 +- src/Driver/Postgres/PostgresCompiler.php | 6 +- src/Driver/Postgres/PostgresDriver.php | 11 +- src/Driver/Postgres/PostgresHandler.php | 6 +- .../Postgres/Query/PostgresInsertQuery.php | 6 +- .../Postgres/Query/PostgresSelectQuery.php | 6 +- src/Driver/Postgres/Schema/PostgresColumn.php | 10 +- .../Postgres/Schema/PostgresForeignKey.php | 6 +- src/Driver/Postgres/Schema/PostgresIndex.php | 6 +- src/Driver/Postgres/Schema/PostgresTable.php | 6 +- src/Driver/Quoter.php | 6 +- src/Driver/ReadonlyHandler.php | 6 +- src/Driver/SQLServer/SQLServerCompiler.php | 6 +- src/Driver/SQLServer/SQLServerDriver.php | 7 +- src/Driver/SQLServer/SQLServerHandler.php | 10 +- .../SQLServer/Schema/SQLServerColumn.php | 6 +- ...ForeignKey.php => SQLServerForeignKey.php} | 10 +- .../SQLServer/Schema/SQLServerIndex.php | 6 +- .../SQLServer/Schema/SQLServerTable.php | 12 +- src/Driver/SQLite/SQLiteCompiler.php | 6 +- src/Driver/SQLite/SQLiteDriver.php | 6 +- src/Driver/SQLite/SQLiteHandler.php | 6 +- src/Driver/SQLite/Schema/SQLiteColumn.php | 6 +- src/Driver/SQLite/Schema/SQLiteForeignKey.php | 6 +- src/Driver/SQLite/Schema/SQLiteIndex.php | 6 +- src/Driver/SQLite/Schema/SQLiteTable.php | 15 +- src/Driver/Statement.php | 6 +- src/Exception/BuilderException.php | 6 +- src/Exception/CompilerException.php | 6 +- src/Exception/ConfigException.php | 6 +- src/Exception/DBALException.php | 6 +- src/Exception/DatabaseException.php | 6 +- src/Exception/DefaultValueException.php | 6 +- src/Exception/DriverException.php | 6 +- src/Exception/HandlerException.php | 6 +- src/Exception/InterpolatorException.php | 6 +- src/Exception/SchemaException.php | 6 +- src/Exception/StatementException.php | 6 +- .../ConnectionException.php | 6 +- .../StatementException/ConstrainException.php | 6 +- src/Exception/StatementExceptionInterface.php | 6 +- src/ForeignKeyInterface.php | 6 +- src/IndexInterface.php | 6 +- src/Injection/Expression.php | 6 +- src/Injection/Fragment.php | 6 +- src/Injection/FragmentInterface.php | 6 +- src/Injection/Parameter.php | 6 +- src/Injection/ParameterInterface.php | 6 +- src/Injection/ValueInterface.php | 6 +- src/Query/ActiveQuery.php | 6 +- src/Query/BuilderInterface.php | 6 +- src/Query/DeleteQuery.php | 6 +- src/Query/InsertQuery.php | 6 +- src/Query/Interpolator.php | 6 +- src/Query/QueryBuilder.php | 6 +- src/Query/QueryInterface.php | 6 +- src/Query/QueryParameters.php | 6 +- src/Query/SelectQuery.php | 6 +- src/Query/Traits/HavingTrait.php | 6 +- src/Query/Traits/JoinTrait.php | 6 +- src/Query/Traits/TokenTrait.php | 6 +- src/Query/Traits/WhereTrait.php | 6 +- src/Query/UpdateQuery.php | 6 +- src/Schema/AbstractColumn.php | 6 +- src/Schema/AbstractForeignKey.php | 8 +- src/Schema/AbstractIndex.php | 6 +- src/Schema/AbstractTable.php | 6 +- src/Schema/Comparator.php | 18 +- src/Schema/ComparatorInterface.php | 6 +- src/Schema/ElementInterface.php | 6 +- src/Schema/Reflector.php | 9 +- src/Schema/State.php | 6 +- src/Schema/Traits/ElementTrait.php | 6 +- src/StatementInterface.php | 7 +- src/Table.php | 6 +- src/TableInterface.php | 6 +- src/polyfill.php | 31 + 106 files changed, 1338 insertions(+), 364 deletions(-) create mode 100644 .editorconfig create mode 100644 .styleci.yml create mode 100644 phpcs.xml create mode 100644 psalm.xml create mode 100644 resources/.phpstorm.meta.php rename src/Driver/SQLServer/Schema/{SQlServerForeignKey.php => SQLServerForeignKey.php} (75%) create mode 100644 src/polyfill.php diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..f02a64bf --- /dev/null +++ b/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.yml] +indent_style = space +indent_size = 2 + +[*.json] +indent_style = space +indent_size = 4 diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 00000000..24f3954e --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,75 @@ +preset: psr12 +risky: true + +version: 7 + +enabled: + - alpha_ordered_traits + - array_indentation + - array_push + - combine_consecutive_issets + - combine_consecutive_unsets + - combine_nested_dirname + - declare_strict_types + - dir_constant + - fully_qualified_strict_types + - function_to_constant + - hash_to_slash_comment + - is_null + - logical_operators + - magic_constant_casing + - magic_method_casing + - method_separation + - modernize_types_casting + - native_function_casing + - native_function_type_declaration_casing + - no_alias_functions + - no_empty_comment + - no_empty_phpdoc + - no_empty_statement + - no_extra_block_blank_lines + - no_short_bool_cast + - no_superfluous_elseif + - no_unneeded_control_parentheses + - no_unneeded_curly_braces + - no_unneeded_final_method + - no_unset_cast + - no_unused_imports + - no_unused_lambda_imports + - no_useless_else + - no_useless_return + - normalize_index_brace + - php_unit_dedicate_assert + - php_unit_dedicate_assert_internal_type + - php_unit_expectation + - php_unit_mock + - php_unit_mock_short_will_return + - php_unit_namespaced + - php_unit_no_expectation_annotation + - phpdoc_no_empty_return + - phpdoc_no_useless_inheritdoc + - phpdoc_order + - phpdoc_property + - phpdoc_scalar + - phpdoc_separation + - phpdoc_singular_inheritdoc + - phpdoc_trim + - phpdoc_trim_consecutive_blank_line_separation + - phpdoc_type_to_var + - phpdoc_types + - phpdoc_types_order + - print_to_echo + - regular_callable_call + - return_assignment + - self_accessor + - self_static_accessor + - set_type_to_cast + - short_array_syntax + - short_list_syntax + - simplified_if_return + - single_quote + - standardize_not_equals + - ternary_to_null_coalescing + - trailing_comma_in_multiline_array + - unalign_double_arrow + - unalign_equals diff --git a/LICENSE b/LICENSE index be7d3b94..586cdb02 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2020 Spiral Scout +Copyright (c) 2021 Spiral Scout Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/composer.json b/composer.json index 6eaabea9..907e2e20 100644 --- a/composer.json +++ b/composer.json @@ -1,36 +1,60 @@ { - "name": "cycle/database", - "type": "library", - "description": "DBAL, schema introspection, migration and pagination", - "license": "MIT", - "authors": [ - { - "name": "Anton Titov / Wolfy-J", - "email": "wolfy.jd@gmail.com" - } - ], - "require": { - "php": ">=7.2", - "ext-pdo": "*", - "spiral/core": "^2.7", - "spiral/logger": "^2.7", - "spiral/pagination": "^2.7" - }, - "require-dev": { - "phpunit/phpunit": "~8.0", - "mockery/mockery": "^1.1", - "spiral/dumper": "^2.7", - "spiral/code-style": "^1.0", - "spiral/tokenizer": "^2.7" - }, - "autoload": { - "psr-4": { - "Cycle\\Database\\": "src/" - } - }, - "autoload-dev": { - "psr-4": { - "Cycle\\Database\\Tests\\": "tests/Database/" - } - } + "name": "cycle/database", + "type": "library", + "description": "DBAL, schema introspection, migration and pagination", + "license": "MIT", + "authors": [ + { + "name": "Anton Titov / Wolfy-J", + "email": "wolfy.jd@gmail.com" + } + ], + "replace": { + "spiral/database": "^2.0" + }, + "require": { + "php": ">=7.2", + "ext-pdo": "*", + "spiral/core": "^2.8", + "spiral/logger": "^2.8", + "spiral/pagination": "^2.8" + }, + "autoload": { + "files": [ + "src/polyfill.php" + ], + "psr-4": { + "Cycle\\Database\\": "src" + } + }, + "require-dev": { + "vimeo/psalm": "^4.10", + "phpunit/phpunit": "^8.5|^9.0", + "mockery/mockery": "^1.3", + "spiral/dumper": "^2.8", + "spiral/code-style": "^1.0", + "spiral/tokenizer": "^2.8" + }, + "autoload-dev": { + "psr-4": { + "Cycle\\Database\\Tests\\": "tests/Database" + } + }, + "scripts": { + "test": [ + "phpcs --standard=phpcs.xml", + "psalm --no-cache", + "phpunit" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true } diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 00000000..a81e1731 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ./src + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 00000000..b82c72f3 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/resources/.phpstorm.meta.php b/resources/.phpstorm.meta.php new file mode 100644 index 00000000..55f0c22e --- /dev/null +++ b/resources/.phpstorm.meta.php @@ -0,0 +1,754 @@ +select(), stupid bug. - $columns = $columns[0]; + $arguments = $arguments[0]; } return $this->getDriver(self::READ) ->getQueryBuilder() - ->selectQuery($this->prefix, [], $columns); + ->selectQuery($this->prefix, [], $arguments); } /** diff --git a/src/DatabaseInterface.php b/src/DatabaseInterface.php index 2789d768..42fcd9d6 100644 --- a/src/DatabaseInterface.php +++ b/src/DatabaseInterface.php @@ -1,10 +1,10 @@ config->getDefaultDatabase(); } - //Spiral support ability to link multiple virtual databases together using aliases + // Cycle support ability to link multiple virtual databases together + // using aliases. $database = $this->config->resolveAlias($database); if (isset($this->databases[$database])) { diff --git a/src/DatabaseProviderInterface.php b/src/DatabaseProviderInterface.php index 2492d243..ada66cbc 100644 --- a/src/DatabaseProviderInterface.php +++ b/src/DatabaseProviderInterface.php @@ -1,10 +1,10 @@ driver->query( $query, - [$this->driver->getSource(), $name] + [$this->driver->getSource(), $table] )->fetchColumn(); } diff --git a/src/Driver/MySQL/Schema/MySQLColumn.php b/src/Driver/MySQL/Schema/MySQLColumn.php index a8f20884..596f38b8 100644 --- a/src/Driver/MySQL/Schema/MySQLColumn.php +++ b/src/Driver/MySQL/Schema/MySQLColumn.php @@ -1,10 +1,10 @@ exec("SET NAMES 'UTF-8'"); diff --git a/src/Driver/Postgres/PostgresHandler.php b/src/Driver/Postgres/PostgresHandler.php index 0422f499..6c096fbf 100644 --- a/src/Driver/Postgres/PostgresHandler.php +++ b/src/Driver/Postgres/PostgresHandler.php @@ -1,10 +1,10 @@ type = $schema['typname']; /** - * Attention, this is not default spiral enum type emulated via CHECK. This is real - * Postgres enum type. + * Attention, this is not default enum type emulated via CHECK. + * This is real Postgres enum type. */ self::resolveEnum($driver, $column); } diff --git a/src/Driver/Postgres/Schema/PostgresForeignKey.php b/src/Driver/Postgres/Schema/PostgresForeignKey.php index 6ff46bab..9fd24f3a 100644 --- a/src/Driver/Postgres/Schema/PostgresForeignKey.php +++ b/src/Driver/Postgres/Schema/PostgresForeignKey.php @@ -1,10 +1,10 @@ bindParam( $index, $parameter, diff --git a/src/Driver/SQLServer/SQLServerHandler.php b/src/Driver/SQLServer/SQLServerHandler.php index cbe97bca..61bac289 100644 --- a/src/Driver/SQLServer/SQLServerHandler.php +++ b/src/Driver/SQLServer/SQLServerHandler.php @@ -1,10 +1,10 @@ driver->query($query, [$name])->fetchColumn(); + return (bool)$this->driver->query($query, [$table])->fetchColumn(); } /** diff --git a/src/Driver/SQLServer/Schema/SQLServerColumn.php b/src/Driver/SQLServer/Schema/SQLServerColumn.php index 08112510..ecf86e0f 100644 --- a/src/Driver/SQLServer/Schema/SQLServerColumn.php +++ b/src/Driver/SQLServer/Schema/SQLServerColumn.php @@ -1,10 +1,10 @@ $schema) { + foreach ($indexes as $_ => $schema) { //Once all columns are aggregated we can finally create an index $result[] = SQLServerIndex::createInstance($this->getName(), $schema); } @@ -116,7 +116,7 @@ protected function fetchReferences(): array $result = []; foreach ($fks as $schema) { - $result[] = SQlServerForeignKey::createInstance( + $result[] = SQLServerForeignKey::createInstance( $this->getName(), $this->getPrefix(), $schema @@ -171,6 +171,6 @@ protected function createIndex(string $name): AbstractIndex */ protected function createForeign(string $name): AbstractForeignKey { - return new SQlServerForeignKey($this->getName(), $this->getPrefix(), $name); + return new SQLServerForeignKey($this->getName(), $this->getPrefix(), $name); } } diff --git a/src/Driver/SQLite/SQLiteCompiler.php b/src/Driver/SQLite/SQLiteCompiler.php index cf344969..882d3b80 100644 --- a/src/Driver/SQLite/SQLiteCompiler.php +++ b/src/Driver/SQLite/SQLiteCompiler.php @@ -1,10 +1,10 @@ fetchColumn(); /* - * There is not really many ways to get extra information about column in SQLite, let's parse - * table schema. As mention, spiral SQLite schema reader will support fully only tables created - * by spiral as we expecting every column definition be on new line. - */ + * There is not really many ways to get extra information about column + * in SQLite, let's parse table schema. As mention, Cycle SQLite + * schema reader will support fully only tables created by Cycle as we + * expecting every column definition be on new line. + */ $definition = explode("\n", $definition); $result = []; diff --git a/src/Driver/Statement.php b/src/Driver/Statement.php index deabfb65..319ead4b 100644 --- a/src/Driver/Statement.php +++ b/src/Driver/Statement.php @@ -1,10 +1,10 @@ current->getIndexes() as $name => $index) { + foreach ($this->current->getIndexes() as $_ => $index) { if (!$this->initial->hasIndex($index->getColumnsWithSort())) { $difference[] = $index; } @@ -155,7 +155,7 @@ public function addedIndexes(): array public function droppedIndexes(): array { $difference = []; - foreach ($this->initial->getIndexes() as $name => $index) { + foreach ($this->initial->getIndexes() as $_ => $index) { if (!$this->current->hasIndex($index->getColumnsWithSort())) { $difference[] = $index; } @@ -173,7 +173,7 @@ public function alteredIndexes(): array { $difference = []; - foreach ($this->current->getIndexes() as $name => $index) { + foreach ($this->current->getIndexes() as $_ => $index) { if (!$this->initial->hasIndex($index->getColumnsWithSort())) { //Added into schema continue; @@ -194,7 +194,7 @@ public function alteredIndexes(): array public function addedForeignKeys(): array { $difference = []; - foreach ($this->current->getForeignKeys() as $name => $foreignKey) { + foreach ($this->current->getForeignKeys() as $_ => $foreignKey) { if (!$this->initial->hasForeignKey($foreignKey->getColumns())) { $difference[] = $foreignKey; } @@ -209,7 +209,7 @@ public function addedForeignKeys(): array public function droppedForeignKeys(): array { $difference = []; - foreach ($this->initial->getForeignKeys() as $name => $foreignKey) { + foreach ($this->initial->getForeignKeys() as $_ => $foreignKey) { if (!$this->current->hasForeignKey($foreignKey->getColumns())) { $difference[] = $foreignKey; } @@ -227,7 +227,7 @@ public function alteredForeignKeys(): array { $difference = []; - foreach ($this->current->getForeignKeys() as $name => $foreignKey) { + foreach ($this->current->getForeignKeys() as $_ => $foreignKey) { if (!$this->initial->hasForeignKey($foreignKey->getColumns())) { //Added into schema continue; diff --git a/src/Schema/ComparatorInterface.php b/src/Schema/ComparatorInterface.php index e3be69ba..09e92f24 100644 --- a/src/Schema/ComparatorInterface.php +++ b/src/Schema/ComparatorInterface.php @@ -1,10 +1,10 @@ beginTransaction(null, false); } else { - /** @var DriverInterface $driver */ $driver->beginTransaction(null); } } @@ -192,7 +191,6 @@ protected function beginTransaction(): void protected function commitTransaction(): void { foreach ($this->drivers as $driver) { - /** @var DriverInterface $driver */ $driver->commitTransaction(); } } @@ -203,7 +201,6 @@ protected function commitTransaction(): void protected function rollbackTransaction(): void { foreach (array_reverse($this->drivers) as $driver) { - /** @var DriverInterface $driver */ $driver->rollbackTransaction(); } } diff --git a/src/Schema/State.php b/src/Schema/State.php index 1f760588..38067df3 100644 --- a/src/Schema/State.php +++ b/src/Schema/State.php @@ -1,10 +1,10 @@ Date: Mon, 4 Oct 2021 19:36:22 +0300 Subject: [PATCH 5/5] Fix statement impl --- src/Driver/Statement.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Driver/Statement.php b/src/Driver/Statement.php index 319ead4b..62e7159e 100644 --- a/src/Driver/Statement.php +++ b/src/Driver/Statement.php @@ -21,7 +21,7 @@ * * @internal Do not use this class directly. */ -final class Statement implements StatementInterface +final class Statement implements StatementInterface, \IteratorAggregate { /** @var PDOStatement */ private $pdoStatement;