From 0dc93ec25d5ab5032c0ecb66d5f8f9978131915e Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Tue, 16 Jan 2018 11:53:16 +0100 Subject: [PATCH 01/17] Do not add data dependent filter for self referencing tables --- src/Dumper/Sql/TableDependencyResolver.php | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Dumper/Sql/TableDependencyResolver.php b/src/Dumper/Sql/TableDependencyResolver.php index b228d3c..c33d3f7 100644 --- a/src/Dumper/Sql/TableDependencyResolver.php +++ b/src/Dumper/Sql/TableDependencyResolver.php @@ -91,15 +91,21 @@ private function findDependencies(array $foreignKeys, TableConfiguration $tableC || !empty($referencedTableConfig->getFilters()) || $referencedTableConfig->getQuery() != null; - if ($hasDependency) { - $tableConfig->addFilter( - new DataDependentFilter( - $foreignKey->getColumns()[0], - $referencedTable, - $foreignKey->getForeignColumns()[0] - ) - ); + if (!$hasDependency) { + continue; + } + if ($foreignKey->getForeignTableName() === $tableConfig->getName()) { + // TODO foreign keys pointing to own table not supported yet + continue; } + + $tableConfig->addFilter( + new DataDependentFilter( + $foreignKey->getColumns()[0], + $referencedTable, + $foreignKey->getForeignColumns()[0] + ) + ); } } } From 00df57ba6e3c0522eefe46c833e4d73fc5641633 Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Tue, 16 Jan 2018 12:02:17 +0100 Subject: [PATCH 02/17] Add further logging output --- src/Dumper/Context/AbstractDumperContext.php | 7 ++++++- src/Dumper/Sql/DataLoader.php | 10 +++++++++- src/Dumper/Sql/Dumper/TableContentsDumper.php | 3 ++- src/Dumper/Sql/Tests/DataLoaderTest.php | 3 ++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Dumper/Context/AbstractDumperContext.php b/src/Dumper/Context/AbstractDumperContext.php index 5726ecd..de3b7d8 100644 --- a/src/Dumper/Context/AbstractDumperContext.php +++ b/src/Dumper/Context/AbstractDumperContext.php @@ -156,8 +156,13 @@ private function createLogger(OutputInterface $applicationOutput) return new NullLogger(); } + $minLevel = Logger::INFO; + if ($applicationOutput->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) { + $minLevel = Logger::DEBUG; + } + $logger = new Logger('snakedumper'); - $logger->pushHandler(new StreamHandler(fopen('php://stderr', 'w'))); + $logger->pushHandler(new StreamHandler(fopen('php://stderr', 'w'), $minLevel)); return $logger; } diff --git a/src/Dumper/Sql/DataLoader.php b/src/Dumper/Sql/DataLoader.php index a7e1e2f..a7850f0 100644 --- a/src/Dumper/Sql/DataLoader.php +++ b/src/Dumper/Sql/DataLoader.php @@ -9,6 +9,7 @@ use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Schema\Table; use InvalidArgumentException; +use Psr\Log\LoggerInterface; /** * This class helps to query the appropriate data that should be dumped. @@ -20,13 +21,19 @@ class DataLoader * @var ConnectionHandler */ private $connectionHandler; + /** + * @var LoggerInterface + */ + private $logger; /** * @param ConnectionHandler $connectionHandler + * @param LoggerInterface $logger */ - public function __construct(ConnectionHandler $connectionHandler) + public function __construct(ConnectionHandler $connectionHandler, LoggerInterface $logger) { $this->connectionHandler = $connectionHandler; + $this->logger = $logger; } /** @@ -43,6 +50,7 @@ public function executeSelectQuery(TableConfiguration $tableConfig, Table $table { list($query, $parameters) = $this->buildSelectQuery($tableConfig, $table, $harvestedValues); + $this->logger->debug('Executing select query' . $query); $result = $this->connectionHandler->getConnection()->prepare($query); $result->execute($parameters); diff --git a/src/Dumper/Sql/Dumper/TableContentsDumper.php b/src/Dumper/Sql/Dumper/TableContentsDumper.php index a6ad849..c1740ea 100644 --- a/src/Dumper/Sql/Dumper/TableContentsDumper.php +++ b/src/Dumper/Sql/Dumper/TableContentsDumper.php @@ -61,7 +61,7 @@ public function __construct(SqlDumperContext $context) { $this->dumpOutput = $context->getDumpOutput(); $this->logger = $context->getLogger(); - $this->dataLoader = new DataLoader($this->connectionHandler); + $this->dataLoader = new DataLoader($this->connectionHandler, $context->getLogger()); } /** @@ -108,6 +108,7 @@ private function dumpTableContent(Table $table, TableConfiguration $tableConfig) $bufferCount = 0; // number of rows in buffer $buffer = array(); // array to buffer rows + $this->logger->info('Querying table ' . $table->getName()); $rowCount = $this->dataLoader->countRows($tableConfig, $table, $this->harvestedValues); $result = $this->dataLoader->executeSelectQuery($tableConfig, $table, $this->harvestedValues); diff --git a/src/Dumper/Sql/Tests/DataLoaderTest.php b/src/Dumper/Sql/Tests/DataLoaderTest.php index 6451281..5055e14 100644 --- a/src/Dumper/Sql/Tests/DataLoaderTest.php +++ b/src/Dumper/Sql/Tests/DataLoaderTest.php @@ -8,6 +8,7 @@ use Digilist\SnakeDumper\Dumper\Sql\DataLoader; use Doctrine\DBAL\Query\QueryBuilder; use Doctrine\DBAL\Schema\Table; +use Psr\Log\NullLogger; class DataLoaderTest extends AbstractSqlTest { @@ -30,7 +31,7 @@ public function setUp() parent::setUp(); $dbConfig = new DatabaseConfiguration(['connection' => $this->connection]); - $this->dataLoader = new DataLoader(new ConnectionHandler($dbConfig)); + $this->dataLoader = new DataLoader(new ConnectionHandler($dbConfig), new NullLogger()); $refl = new \ReflectionObject($this->dataLoader); $createSelectQueryBuilder = $refl->getMethod('createSelectQueryBuilder'); From 16beddfe2c8d65bde86b4e4df243675f4ba60292 Mon Sep 17 00:00:00 2001 From: Frank Giesecke Date: Tue, 5 Jun 2018 14:27:39 +0200 Subject: [PATCH 03/17] Provide more option Add options to overwrite the configuration values for: - `host` - `port` - `user` Add an option to avoid structure information in the resulted dump. --- src/Command/DumpCommand.php | 18 +++++++++++++- src/Configuration/SqlDumperConfiguration.php | 25 ++++++++++++++++++++ src/Dumper/SqlDumper.php | 4 +++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Command/DumpCommand.php b/src/Command/DumpCommand.php index 1709ed6..f3c10a4 100644 --- a/src/Command/DumpCommand.php +++ b/src/Command/DumpCommand.php @@ -25,8 +25,12 @@ protected function configure() ->addArgument('config', InputArgument::REQUIRED, 'The path to your config file.') ->addOption('progress', null, InputOption::VALUE_NONE, 'Show a progress bar') ->addOption('disable-limits', null, InputOption::VALUE_NONE, 'Create a full database dump') - ->addOption('dbname', null, InputOption::VALUE_REQUIRED, 'Override the name of database that should be dumped') + ->addOption('disable-structure', null, InputOption::VALUE_NONE, 'Create a dump containing data only') + ->addOption('host', 'H', InputOption::VALUE_REQUIRED, 'Override the configured host') + ->addOption('port', 'P', InputOption::VALUE_REQUIRED, 'Override the configured port') + ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Override the configured user') ->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Override the configured password') + ->addOption('dbname', null, InputOption::VALUE_REQUIRED, 'Override the name of database that should be dumped') ; } @@ -92,13 +96,25 @@ private function overrideConfigs(DumperConfigurationInterface $config, InputInte if ($input->getOption('dbname') !== null) { $config->getDatabaseConfig()->setDatabaseName($input->getOption('dbname')); } + if ($input->getOption('user') !== null) { + $config->getDatabaseConfig()->setUser($input->getOption('user')); + } if ($input->getOption('password') !== null) { $config->getDatabaseConfig()->setPassword($input->getOption('password')); } + if ($input->getOption('host') !== null) { + $config->getDatabaseConfig()->setHost($input->getOption('host')); + } + if ($input->getOption('port') !== null) { + $config->getDatabaseConfig()->setPort($input->getOption('port')); + } if ($input->getOption('disable-limits')) { foreach ($config->getTableConfigs() as $tableConfig) { $tableConfig->setLimit(null); } } + if ($input->getOption('disable-structure')) { + $config->setIgnoreStructure(true); + } } } diff --git a/src/Configuration/SqlDumperConfiguration.php b/src/Configuration/SqlDumperConfiguration.php index daeb6a6..ca94b3f 100644 --- a/src/Configuration/SqlDumperConfiguration.php +++ b/src/Configuration/SqlDumperConfiguration.php @@ -23,6 +23,11 @@ class SqlDumperConfiguration extends AbstractConfiguration implements DumperConf */ private $tableConfigs = array(); + /** + * @var bool + */ + private $ignoreStructure = false; + /** * @return DatabaseConfiguration */ @@ -163,4 +168,24 @@ protected function parseConfig(array $dumperConfig) $this->parseDependencies(); } + + /** + * Get ignore structure. + * + * @return bool + */ + public function isIgnoreStructure() + { + return $this->ignoreStructure; + } + + /** + * Set ignore structure. + * + * @param bool $ignoreStructure + */ + public function setIgnoreStructure($ignoreStructure) + { + $this->ignoreStructure = $ignoreStructure; + } } diff --git a/src/Dumper/SqlDumper.php b/src/Dumper/SqlDumper.php index c13b4c5..03974c3 100644 --- a/src/Dumper/SqlDumper.php +++ b/src/Dumper/SqlDumper.php @@ -40,7 +40,9 @@ public function dump(DumperContextInterface $context) ); $this->dumpPreamble($context); - $structureDumper->dumpTableStructure($tables); + if (!$context->getConfig()->isIgnoreStructure()) { + $structureDumper->dumpTableStructure($tables); + } $this->dumpTables($context, $tables); $structureDumper->dumpConstraints($tables); } From 887a489e5f7c6226d89fa2917ca115d73cd056a7 Mon Sep 17 00:00:00 2001 From: Oliver Gassner Date: Wed, 13 Jun 2018 17:22:02 +0200 Subject: [PATCH 04/17] remove dead link to missing docs. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2afbbf2..18964aa 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ php snakedumper.phar dump ./demo.yml ### Example Configuration -There are a lot of configuration options for the SnakeDumper available. The best way to get started is by looking at the example configuration file ([demo.yml](demo.yml)). For other available options, please take a look at the [docs/](docs). +There are a lot of configuration options for the SnakeDumper available. The best way to get started is by looking at the example configuration file ([demo.yml](demo.yml)). ## Testing From 4c37cde0984069c483cbc55784f00bbe1507aab0 Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Wed, 20 Jun 2018 20:17:03 +0200 Subject: [PATCH 05/17] Add docker-compose.yml --- docker-compose.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..59a5f67 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +version: "3" + +services: + mysql: + image: mysql:5.7 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: 'yes' + MYSQL_DATABASE: snakedumper + TZ: Europe/Berlin + command: "--character-set-server=utf8 --collation-server=utf8_unicode_ci" + ports: + - "3306:3306" From 4bb16bdf96a2fc67795aa57dd0bdceddf310b097 Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Wed, 20 Jun 2018 20:27:14 +0200 Subject: [PATCH 06/17] Allow to override the output path --- src/Command/DumpCommand.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Command/DumpCommand.php b/src/Command/DumpCommand.php index f3c10a4..252df7d 100644 --- a/src/Command/DumpCommand.php +++ b/src/Command/DumpCommand.php @@ -30,6 +30,7 @@ protected function configure() ->addOption('port', 'P', InputOption::VALUE_REQUIRED, 'Override the configured port') ->addOption('user', 'u', InputOption::VALUE_REQUIRED, 'Override the configured user') ->addOption('password', 'p', InputOption::VALUE_REQUIRED, 'Override the configured password') + ->addOption('output', 'o', InputOption::VALUE_REQUIRED, 'Override the configured output path') ->addOption('dbname', null, InputOption::VALUE_REQUIRED, 'Override the name of database that should be dumped') ; } @@ -91,7 +92,7 @@ private function parseConfig(InputInterface $input) * @param DumperConfigurationInterface $config * @param InputInterface $input */ - private function overrideConfigs(DumperConfigurationInterface $config, InputInterface $input) + private function overrideConfigs(SqlDumperConfiguration $config, InputInterface $input) { if ($input->getOption('dbname') !== null) { $config->getDatabaseConfig()->setDatabaseName($input->getOption('dbname')); @@ -116,5 +117,10 @@ private function overrideConfigs(DumperConfigurationInterface $config, InputInte if ($input->getOption('disable-structure')) { $config->setIgnoreStructure(true); } + if ($input->getOption('output') !== null) { + $path = $input->getOption('output'); + $config->getOutputConfig()->setFile($path); + $config->getOutputConfig()->setGzip(strrpos($path, '.gz') === strlen($path) - 3); + } } } From 00ba95366c129dff397317d0b665bef318426ed4 Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Wed, 20 Jun 2018 21:11:21 +0200 Subject: [PATCH 07/17] Add support for databases with custom Doctrine DBAL types --- .../Bridge/Doctrine/DBAL/Connection.php | 58 +++++++++++++++++++ src/Dumper/Sql/ConnectionHandler.php | 15 +++-- src/Dumper/Sql/Tests/AbstractSqlTest.php | 9 ++- src/Dumper/Sql/Tests/TableSelectorTest.php | 17 ++++++ 4 files changed, 90 insertions(+), 9 deletions(-) create mode 100644 src/Dumper/Bridge/Doctrine/DBAL/Connection.php diff --git a/src/Dumper/Bridge/Doctrine/DBAL/Connection.php b/src/Dumper/Bridge/Doctrine/DBAL/Connection.php new file mode 100644 index 0000000..8a339d5 --- /dev/null +++ b/src/Dumper/Bridge/Doctrine/DBAL/Connection.php @@ -0,0 +1,58 @@ +_schemaManager) { + $this->_schemaManager = $this->createSchemaManager(); + } + + return $this->_schemaManager; + } + + private function createSchemaManager() + { + $originalSchemaManager = $this->_driver->getSchemaManager($this); + $originalSchemaManagerClass = get_class($originalSchemaManager); + + $schemaManagerName = 'SnakeDumperSchemaManager' . uniqid(); + $schemaManagerCode = << $this->config->getDriver(), - 'host' => $this->config->getHost(), - 'port' => $this->config->getPort(), - 'user' => $this->config->getUser(), + 'driver' => $this->config->getDriver(), + 'host' => $this->config->getHost(), + 'port' => $this->config->getPort(), + 'user' => $this->config->getUser(), 'password' => $this->config->getPassword(), - 'dbname' => $this->config->getDatabaseName(), - 'charset' => $this->config->getCharset(), + 'dbname' => $this->config->getDatabaseName(), + 'charset' => $this->config->getCharset(), + 'wrapperClass' => \Digilist\SnakeDumper\Dumper\Bridge\Doctrine\DBAL\Connection::class, ); $dbalConfig = new Configuration(); $this->connection = DriverManager::getConnection($connectionParams, $dbalConfig); $this->connection->connect(); + $this->connection->getSchemaManager(); + $this->initPlatformAdjustment($this->connection); $this->platformAdjustment->initConnection(); $this->platformAdjustment->registerCustomTypeMappings(); diff --git a/src/Dumper/Sql/Tests/AbstractSqlTest.php b/src/Dumper/Sql/Tests/AbstractSqlTest.php index d7f12d5..923db5b 100644 --- a/src/Dumper/Sql/Tests/AbstractSqlTest.php +++ b/src/Dumper/Sql/Tests/AbstractSqlTest.php @@ -2,6 +2,8 @@ namespace Digilist\SnakeDumper\Dumper\Sql\Tests; +use Digilist\SnakeDumper\Configuration\DatabaseConfiguration; +use Digilist\SnakeDumper\Dumper\Sql\ConnectionHandler; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Platforms\AbstractPlatform; @@ -34,10 +36,11 @@ public function setUp() // // Use MySQL instead. $this->connection = DriverManager::getConnection(array( - 'user' => 'root', + 'user' => 'root', 'password' => '', - 'host' => '127.0.0.1', - 'driver' => 'pdo_mysql', + 'host' => '127.0.0.1', + 'driver' => 'pdo_mysql', + 'wrapperClass' => \Digilist\SnakeDumper\Dumper\Bridge\Doctrine\DBAL\Connection::class, )); $this->connection->exec('DROP DATABASE IF EXISTS ' . self::DBNAME); diff --git a/src/Dumper/Sql/Tests/TableSelectorTest.php b/src/Dumper/Sql/Tests/TableSelectorTest.php index c140e35..aa79975 100644 --- a/src/Dumper/Sql/Tests/TableSelectorTest.php +++ b/src/Dumper/Sql/Tests/TableSelectorTest.php @@ -63,4 +63,21 @@ public function testSelectTables() $this->assertEquals('customer_id', $billingTable->getForeignKey('customer_id')->getName()); $this->assertEquals('`customer_id`', $billingTable->getForeignKey('customer_id')->getQuotedName($this->platform)); } + + /** + * Tests that the dumper does not get confused by column comments that indicate custom doctrine types (which are + * not registered). + * + * @test + */ + public function testDoctrineCustomTypes() + { + $pdo = $this->connection->getWrappedConnection(); + $pdo->query('CREATE TABLE custom_doctrine_type ( + foobar VARCHAR(255) NOT NULL COMMENT \'(DC2Type:example)\' + )'); + + $tableSelector = new TableSelector($this->connection); + $tableSelector->findTablesToDump(new SqlDumperConfiguration()); + } } From 8ac667946bea805f762fb949529082b9fa0a26c6 Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Wed, 20 Jun 2018 21:13:48 +0200 Subject: [PATCH 08/17] Update dependencies (primarly to support MySQL JSON type with Doctrine) --- composer.lock | 449 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 269 insertions(+), 180 deletions(-) diff --git a/composer.lock b/composer.lock index 2977cd5..675e9d2 100644 --- a/composer.lock +++ b/composer.lock @@ -1,7 +1,7 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], "content-hash": "9ce13389805be21e18808bbb25705af4", @@ -47,30 +47,30 @@ }, { "name": "doctrine/annotations", - "version": "v1.4.0", + "version": "v1.6.0", "source": { "type": "git", "url": "https://github.com/doctrine/annotations.git", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97" + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/54cacc9b81758b14e3ce750f205a393d52339e97", - "reference": "54cacc9b81758b14e3ce750f205a393d52339e97", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", + "reference": "c7f2050c68a9ab0bdb0f98567ec08d80ea7d24d5", "shasum": "" }, "require": { "doctrine/lexer": "1.*", - "php": "^5.6 || ^7.0" + "php": "^7.1" }, "require-dev": { "doctrine/cache": "1.*", - "phpunit/phpunit": "^5.7" + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -111,37 +111,41 @@ "docblock", "parser" ], - "time": "2017-02-24T16:22:25+00:00" + "time": "2017-12-06T07:11:42+00:00" }, { "name": "doctrine/cache", - "version": "v1.6.1", + "version": "v1.7.1", "source": { "type": "git", "url": "https://github.com/doctrine/cache.git", - "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3" + "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/b6f544a20f4807e81f7044d31e679ccbb1866dc3", - "reference": "b6f544a20f4807e81f7044d31e679ccbb1866dc3", + "url": "https://api.github.com/repos/doctrine/cache/zipball/b3217d58609e9c8e661cd41357a54d926c4a2a1a", + "reference": "b3217d58609e9c8e661cd41357a54d926c4a2a1a", "shasum": "" }, "require": { - "php": "~5.5|~7.0" + "php": "~7.1" }, "conflict": { "doctrine/common": ">2.2,<2.4" }, "require-dev": { - "phpunit/phpunit": "~4.8|~5.0", - "predis/predis": "~1.0", - "satooshi/php-coveralls": "~0.6" + "alcaeus/mongo-php-adapter": "^1.1", + "mongodb/mongodb": "^1.1", + "phpunit/phpunit": "^5.7", + "predis/predis": "~1.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "1.7.x-dev" } }, "autoload": { @@ -181,24 +185,24 @@ "cache", "caching" ], - "time": "2016-10-29T11:16:17+00:00" + "time": "2017-08-25T07:02:50+00:00" }, { "name": "doctrine/collections", - "version": "v1.4.0", + "version": "v1.5.0", "source": { "type": "git", "url": "https://github.com/doctrine/collections.git", - "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba" + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/collections/zipball/1a4fb7e902202c33cce8c55989b945612943c2ba", - "reference": "1a4fb7e902202c33cce8c55989b945612943c2ba", + "url": "https://api.github.com/repos/doctrine/collections/zipball/a01ee38fcd999f34d9bfbcee59dbda5105449cbf", + "reference": "a01ee38fcd999f34d9bfbcee59dbda5105449cbf", "shasum": "" }, "require": { - "php": "^5.6 || ^7.0" + "php": "^7.1" }, "require-dev": { "doctrine/coding-standard": "~0.1@dev", @@ -248,20 +252,20 @@ "collections", "iterator" ], - "time": "2017-01-03T10:49:41+00:00" + "time": "2017-07-22T10:37:32+00:00" }, { "name": "doctrine/common", - "version": "v2.7.2", + "version": "v2.8.1", "source": { "type": "git", "url": "https://github.com/doctrine/common.git", - "reference": "930297026c8009a567ac051fd545bf6124150347" + "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/common/zipball/930297026c8009a567ac051fd545bf6124150347", - "reference": "930297026c8009a567ac051fd545bf6124150347", + "url": "https://api.github.com/repos/doctrine/common/zipball/f68c297ce6455e8fd794aa8ffaf9fa458f6ade66", + "reference": "f68c297ce6455e8fd794aa8ffaf9fa458f6ade66", "shasum": "" }, "require": { @@ -270,15 +274,15 @@ "doctrine/collections": "1.*", "doctrine/inflector": "1.*", "doctrine/lexer": "1.*", - "php": "~5.6|~7.0" + "php": "~7.1" }, "require-dev": { - "phpunit/phpunit": "^5.4.6" + "phpunit/phpunit": "^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7.x-dev" + "dev-master": "2.8.x-dev" } }, "autoload": { @@ -321,29 +325,33 @@ "persistence", "spl" ], - "time": "2017-01-13T14:02:13+00:00" + "time": "2017-08-31T08:43:38+00:00" }, { "name": "doctrine/dbal", - "version": "v2.5.12", + "version": "v2.7.1", "source": { "type": "git", "url": "https://github.com/doctrine/dbal.git", - "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44" + "reference": "11037b4352c008373561dc6fc836834eed80c3b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/dbal/zipball/7b9e911f9d8b30d43b96853dab26898c710d8f44", - "reference": "7b9e911f9d8b30d43b96853dab26898c710d8f44", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/11037b4352c008373561dc6fc836834eed80c3b5", + "reference": "11037b4352c008373561dc6fc836834eed80c3b5", "shasum": "" }, "require": { - "doctrine/common": ">=2.4,<2.8-dev", - "php": ">=5.3.2" + "doctrine/common": "^2.7.1", + "ext-pdo": "*", + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "4.*", - "symfony/console": "2.*||^3.0" + "doctrine/coding-standard": "^4.0", + "phpunit/phpunit": "^7.0", + "phpunit/phpunit-mock-objects": "!=3.2.4,!=3.2.5", + "symfony/console": "^2.0.5||^3.0", + "symfony/phpunit-bridge": "^3.4.5|^4.0.5" }, "suggest": { "symfony/console": "For helpful console commands such as SQL execution and import of files." @@ -354,7 +362,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5.x-dev" + "dev-master": "2.7.x-dev" } }, "autoload": { @@ -392,37 +400,37 @@ "persistence", "queryobject" ], - "time": "2017-02-08T12:53:47+00:00" + "time": "2018-04-07T18:44:18+00:00" }, { "name": "doctrine/inflector", - "version": "v1.1.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/doctrine/inflector.git", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + "reference": "5527a48b7313d15261292c149e55e26eae771b0a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", - "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a", + "reference": "5527a48b7313d15261292c149e55e26eae771b0a", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": "^7.1" }, "require-dev": { - "phpunit/phpunit": "4.*" + "phpunit/phpunit": "^6.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Inflector\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector" } }, "notification-url": "https://packagist.org/downloads/", @@ -459,7 +467,7 @@ "singularize", "string" ], - "time": "2015-11-06T14:35:42+00:00" + "time": "2018-01-09T20:05:19+00:00" }, { "name": "doctrine/lexer", @@ -517,29 +525,31 @@ }, { "name": "fzaninotto/faker", - "version": "v1.6.0", + "version": "v1.7.1", "source": { "type": "git", "url": "https://github.com/fzaninotto/Faker.git", - "reference": "44f9a286a04b80c76a4e5fb7aad8bb539b920123" + "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/44f9a286a04b80c76a4e5fb7aad8bb539b920123", - "reference": "44f9a286a04b80c76a4e5fb7aad8bb539b920123", + "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", + "reference": "d3ed4cc37051c1ca52d22d76b437d14809fc7e0d", "shasum": "" }, "require": { - "php": "^5.3.3|^7.0" + "php": "^5.3.3 || ^7.0" }, "require-dev": { "ext-intl": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~1.5" + "phpunit/phpunit": "^4.0 || ^5.0", + "squizlabs/php_codesniffer": "^1.5" }, "type": "library", "extra": { - "branch-alias": [] + "branch-alias": { + "dev-master": "1.8-dev" + } }, "autoload": { "psr-4": { @@ -561,20 +571,20 @@ "faker", "fixtures" ], - "time": "2016-04-29T12:21:54+00:00" + "time": "2017-08-15T16:48:10+00:00" }, { "name": "monolog/monolog", - "version": "1.22.1", + "version": "1.23.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0" + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1e044bc4b34e91743943479f1be7a1d5eb93add0", - "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", "shasum": "" }, "require": { @@ -595,7 +605,7 @@ "phpunit/phpunit-mock-objects": "2.3.0", "ruflin/elastica": ">=0.90 <3.0", "sentry/sentry": "^0.13", - "swiftmailer/swiftmailer": "~5.3" + "swiftmailer/swiftmailer": "^5.3|^6.0" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", @@ -639,7 +649,7 @@ "logging", "psr-3" ], - "time": "2017-03-13T07:08:03+00:00" + "time": "2017-06-19T01:22:40+00:00" }, { "name": "psr/log", @@ -690,24 +700,32 @@ }, { "name": "symfony/config", - "version": "v3.2.8", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "e5533fcc0b3dd377626153b2852707878f363728" + "reference": "73e055cf2e6467715f187724a0347ea32079967c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/e5533fcc0b3dd377626153b2852707878f363728", - "reference": "e5533fcc0b3dd377626153b2852707878f363728", + "url": "https://api.github.com/repos/symfony/config/zipball/73e055cf2e6467715f187724a0347ea32079967c", + "reference": "73e055cf2e6467715f187724a0347ea32079967c", "shasum": "" }, "require": { - "php": ">=5.5.9", - "symfony/filesystem": "~2.8|~3.0" + "php": "^5.5.9|>=7.0.8", + "symfony/filesystem": "~2.8|~3.0|~4.0", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/dependency-injection": "<3.3", + "symfony/finder": "<3.3" }, "require-dev": { - "symfony/yaml": "~3.0" + "symfony/dependency-injection": "~3.3|~4.0", + "symfony/event-dispatcher": "~3.3|~4.0", + "symfony/finder": "~3.3|~4.0", + "symfony/yaml": "~3.0|~4.0" }, "suggest": { "symfony/yaml": "To use the yaml reference dumper" @@ -715,7 +733,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -742,43 +760,49 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2017-04-12T14:13:17+00:00" + "time": "2018-05-14T16:49:53+00:00" }, { "name": "symfony/console", - "version": "v3.2.8", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38" + "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38", - "reference": "a7a17e0c6c3c4d70a211f80782e4b90ddadeaa38", + "url": "https://api.github.com/repos/symfony/console/zipball/36f83f642443c46f3cf751d4d2ee5d047d757a27", + "reference": "36f83f642443c46f3cf751d4d2ee5d047d757a27", "shasum": "" }, "require": { - "php": ">=5.5.9", - "symfony/debug": "~2.8|~3.0", + "php": "^5.5.9|>=7.0.8", + "symfony/debug": "~2.8|~3.0|~4.0", "symfony/polyfill-mbstring": "~1.0" }, + "conflict": { + "symfony/dependency-injection": "<3.4", + "symfony/process": "<3.3" + }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.8|~3.0", - "symfony/filesystem": "~2.8|~3.0", - "symfony/process": "~2.8|~3.0" + "symfony/config": "~3.3|~4.0", + "symfony/dependency-injection": "~3.4|~4.0", + "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/lock": "~3.4|~4.0", + "symfony/process": "~3.3|~4.0" }, "suggest": { - "psr/log": "For using the console logger", + "psr/log-implementation": "For using the console logger", "symfony/event-dispatcher": "", - "symfony/filesystem": "", + "symfony/lock": "", "symfony/process": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -805,37 +829,36 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-04-26T01:39:17+00:00" + "time": "2018-05-16T08:49:21+00:00" }, { "name": "symfony/debug", - "version": "v3.2.8", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686" + "reference": "449f8b00b28ab6e6912c3e6b920406143b27193b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/fd6eeee656a5a7b384d56f1072243fe1c0e81686", - "reference": "fd6eeee656a5a7b384d56f1072243fe1c0e81686", + "url": "https://api.github.com/repos/symfony/debug/zipball/449f8b00b28ab6e6912c3e6b920406143b27193b", + "reference": "449f8b00b28ab6e6912c3e6b920406143b27193b", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^7.1.3", "psr/log": "~1.0" }, "conflict": { - "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" + "symfony/http-kernel": "<3.4" }, "require-dev": { - "symfony/class-loader": "~2.8|~3.0", - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~3.4|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -862,29 +885,30 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-04-19T20:17:50+00:00" + "time": "2018-05-16T14:33:22+00:00" }, { "name": "symfony/filesystem", - "version": "v3.2.8", + "version": "v4.1.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "040651db13cf061827a460cc10f6e36a445c45b4" + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/040651db13cf061827a460cc10f6e36a445c45b4", - "reference": "040651db13cf061827a460cc10f6e36a445c45b4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", + "reference": "562bf7005b55fd80d26b582d28e3e10f2dd5ae9c", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^7.1.3", + "symfony/polyfill-ctype": "~1.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "4.1-dev" } }, "autoload": { @@ -911,20 +935,75 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2017-04-12T14:13:17+00:00" + "time": "2018-05-30T07:26:09+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "reference": "7cc359f1b7b80fc25ed7796be7d96adc9b354bae", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + }, + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "time": "2018-04-30T19:57:29+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.3.0", + "version": "v1.8.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + "reference": "3296adf6a6454a050679cde90f95350ad604b171" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/3296adf6a6454a050679cde90f95350ad604b171", + "reference": "3296adf6a6454a050679cde90f95350ad604b171", "shasum": "" }, "require": { @@ -936,7 +1015,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -970,27 +1049,31 @@ "portable", "shim" ], - "time": "2016-11-14T01:06:16+00:00" + "time": "2018-04-26T10:06:28+00:00" }, { "name": "symfony/yaml", - "version": "v3.2.8", + "version": "v3.4.11", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6" + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/acec26fcf7f3031e094e910b94b002fa53d4e4d6", - "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6", + "url": "https://api.github.com/repos/symfony/yaml/zipball/c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", + "reference": "c5010cc1692ce1fa328b1fb666961eb3d4a85bb0", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8", + "symfony/polyfill-ctype": "~1.8" + }, + "conflict": { + "symfony/console": "<3.4" }, "require-dev": { - "symfony/console": "~2.8|~3.0" + "symfony/console": "~3.4|~4.0" }, "suggest": { "symfony/console": "For validating YAML files using the lint command" @@ -998,7 +1081,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -1025,38 +1108,38 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-05-01T14:55:58+00:00" + "time": "2018-05-03T23:18:14+00:00" } ], "packages-dev": [ { "name": "doctrine/instantiator", - "version": "1.0.5", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/doctrine/instantiator.git", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", - "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", + "reference": "185b8868aa9bf7159f5f953ed5afb2d7fcdc3bda", "shasum": "" }, "require": { - "php": ">=5.3,<8.0-DEV" + "php": "^7.1" }, "require-dev": { "athletic/athletic": "~0.1.8", "ext-pdo": "*", "ext-phar": "*", - "phpunit/phpunit": "~4.0", - "squizlabs/php_codesniffer": "~2.0" + "phpunit/phpunit": "^6.2.3", + "squizlabs/php_codesniffer": "^3.0.2" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.2.x-dev" } }, "autoload": { @@ -1081,20 +1164,20 @@ "constructor", "instantiate" ], - "time": "2015-06-14T21:17:01+00:00" + "time": "2017-07-22T11:58:36+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { @@ -1135,33 +1218,39 @@ "reflection", "static analysis" ], - "time": "2015-12-27T11:43:31+00:00" + "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.1.1", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", + "php": "^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.4" + "doctrine/instantiator": "~1.0.5", + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.x-dev" + } + }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": [ @@ -1180,24 +1269,24 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30T07:12:33+00:00" + "time": "2017-11-30T07:14:17+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.2.1", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { - "php": ">=5.5", + "php": "^5.5 || ^7.0", "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { @@ -1227,37 +1316,37 @@ "email": "me@mikevanriel.com" } ], - "time": "2016-11-25T06:54:22+00:00" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.7.0", + "version": "1.7.6", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", - "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/33a7e3c4fda54e912ff6338c48823bd5c0f0b712", + "reference": "33a7e3c4fda54e912ff6338c48823bd5c0f0b712", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1|^2.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", + "sebastian/comparator": "^1.1|^2.0|^3.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "1.7.x-dev" } }, "autoload": { @@ -1290,7 +1379,7 @@ "spy", "stub" ], - "time": "2017-03-02T20:05:34+00:00" + "time": "2018-04-18T13:57:24+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1356,16 +1445,16 @@ }, { "name": "phpunit/php-file-iterator", - "version": "1.4.2", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", "shasum": "" }, "require": { @@ -1399,7 +1488,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03T07:40:28+00:00" + "time": "2017-11-27T13:52:08+00:00" }, { "name": "phpunit/php-text-template", @@ -1493,16 +1582,16 @@ }, { "name": "phpunit/php-token-stream", - "version": "1.4.11", + "version": "1.4.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", "shasum": "" }, "require": { @@ -1538,20 +1627,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-02-27T10:12:30+00:00" + "time": "2017-12-04T08:55:13+00:00" }, { "name": "phpunit/phpunit", - "version": "4.8.35", + "version": "4.8.36", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87" + "reference": "46023de9a91eec7dfb06cc56cb4e260017298517" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/791b1a67c25af50e230f841ee7a9c6eba507dc87", - "reference": "791b1a67c25af50e230f841ee7a9c6eba507dc87", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/46023de9a91eec7dfb06cc56cb4e260017298517", + "reference": "46023de9a91eec7dfb06cc56cb4e260017298517", "shasum": "" }, "require": { @@ -1610,7 +1699,7 @@ "testing", "xunit" ], - "time": "2017-02-06T05:18:07+00:00" + "time": "2017-06-21T08:07:12+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -2042,16 +2131,16 @@ }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { @@ -2088,7 +2177,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:58+00:00" + "time": "2018-01-29T19:49:41+00:00" } ], "aliases": [], From 9c892d2134128e22b727995cfd8fd6c76e510516 Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Wed, 20 Jun 2018 21:19:25 +0200 Subject: [PATCH 09/17] Improve unit test for custom doctrine types --- src/Dumper/Sql/Tests/TableSelectorTest.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Dumper/Sql/Tests/TableSelectorTest.php b/src/Dumper/Sql/Tests/TableSelectorTest.php index aa79975..db9b2a9 100644 --- a/src/Dumper/Sql/Tests/TableSelectorTest.php +++ b/src/Dumper/Sql/Tests/TableSelectorTest.php @@ -4,6 +4,7 @@ use Digilist\SnakeDumper\Configuration\SqlDumperConfiguration; use Digilist\SnakeDumper\Configuration\Table\TableConfiguration; +use Digilist\SnakeDumper\Dumper\Sql\Dumper\StructureDumper; use Digilist\SnakeDumper\Dumper\Sql\IdentifierQuoter; use Digilist\SnakeDumper\Dumper\Sql\TableSelector; @@ -78,6 +79,9 @@ public function testDoctrineCustomTypes() )'); $tableSelector = new TableSelector($this->connection); - $tableSelector->findTablesToDump(new SqlDumperConfiguration()); + $tables = $tableSelector->findTablesToDump(new SqlDumperConfiguration()); + + $this->assertEquals('string', $tables[0]->getColumn('foobar')->getType()->getName()); + $this->assertEquals('(DC2Type:example)', $tables[0]->getColumn('foobar')->getComment()); } } From 8491d9e8ad8d7570d23061bdd1d42a7d760bd9a7 Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Wed, 20 Jun 2018 22:29:09 +0200 Subject: [PATCH 10/17] Start documentation --- README.md | 11 ++- demo.yml | 3 - docs/index.md | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 5 deletions(-) create mode 100644 docs/index.md diff --git a/README.md b/README.md index 18964aa..cd4b44c 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # SnakeDumper -SnakeDumper is a tool to create a reasonable development dump of your production database **which does not contain any personal data**. It works similar to mysqldumper (or related tools), but applies a set of data converters and filters to your data. (If there aren't any filters and converteres configured it works exactly like any other dumper.) +SnakeDumper is a tool to create a reasonable development dump of your production database **which does not contain any sensitive data**. It works similar to mysqldumper (or related tools), but applies a set of data converters and filters to your data. (If there aren't any filters and converteres configured it works exactly like any other dumper.) _Please note that the SnakeDumper is currently in an early preview phase. It is written to work with any SQL-compatible database, but is at the moment only tested against MySQL and SQLite._ The vision is to support a wide range of database systems later (including NoSQL). @@ -20,7 +20,7 @@ Furthermore, you can configure SnakeDumper to skip whole tables or the contents ### Data Converter -The main objective of the SnakeDumper is to build reasonable development dumps that do not contain any personal data. Therefore, there are are various data converts that allow to alter the dumped data and replace all personal information with other random (or static) data. +The main objective of the SnakeDumper is to build reasonable development dumps that do not contain any sensitive data. Therefore, there are are various data converts that allow to alter the dumped data and replace all sensitive information with other random (or static) data. There are already a lot of converters. Here are just a few examples: - Random first and last name @@ -44,8 +44,10 @@ php bin/snakedumper dump ./demo.yml php snakedumper.phar dump ./demo.yml ``` +To get started please take a look at the [docs](docs/index.md). ### Example Configuration + There are a lot of configuration options for the SnakeDumper available. The best way to get started is by looking at the example configuration file ([demo.yml](demo.yml)). ## Testing @@ -56,9 +58,14 @@ The test suite needs a running MySQL server at the moment. You can use the follo docker run -it -p 3306:3306 --rm -e MYSQL_ALLOW_EMPTY_PASSWORD=1 mysql:5.7 --character-set-server=utf8 --collation-server=utf8_unicode_ci ``` +Alternatively, there is a [docker-compose.yml](docker-compose.yml) which you can use to start a Docker container for testing. + ### Security / Caution! + Please note that some configuration parameters are passed directly to the database server. Although this tool does not perform any changes on your data, it is still possible to alter your data with invalid configuration parameters (e.g. by defining a custom query which performs updates). So please do not configure this tool with any kind of user provided data! We do not perform any security checks at the moment! Use it at your own risk, we give absolutely no warranty. +Furthermore, SnakeDumper does not guarantee that there is no sensitive data left in your dump. You - as a user of SnakeDumper - are responsible for the correct configuration and usage. + ## How to contribute We are open for every type of contribution. You can test the SnakeDumper, report bugs, propose new features or help us with the development. Feel free to create a new issue or open a pull request :smiley: diff --git a/demo.yml b/demo.yml index 236ec65..0d2f11c 100644 --- a/demo.yml +++ b/demo.yml @@ -46,6 +46,3 @@ tables: converters: name: - JohnDoe - foo_depends: - filters: - - [depends, foo_id, foo.id] # Column foo_id has a foreign key on foo.id diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..b34a3ff --- /dev/null +++ b/docs/index.md @@ -0,0 +1,184 @@ +# SnakeDumper Documentation + +## Introduction + +SnakeDumper is a tool that was designed to create reasonable development database dumps for from your production database. At the moment it supports only SQL (and was actually only tested with MySQL, but should be supported by any other database - or require only a small set of changes). However, the vision is that it should support NoSQL databases as well - one day. + +The goal of SnakeDumper is to provide developers with a development dump of a production database that does not contain any sensitive data. + +## A little warning + +Please note that some configuration parameters are passed directly to the database server. Although this tool does not perform any changes on your data, it is still possible to alter your data with invalid configuration parameters (e.g. by defining a custom query which performs updates). So please do not configure this tool with any kind of user provided data! We do not perform any security checks at the moment! Use it at your own risk, we give absolutely no warranty. + +Furthermore, SnakeDumper does not guarantee that there is no sensitive data left in your dump. You - as a user of SnakeDumper - are responsible for the correct configuration and usage. + +## Usage + +You can use SnakeDumper by defining a configuration file for your dump (see next section). You can then pass the configuration file as argument to `snakedumper`. + +You can create a dump like this: + +``` +php bin/snakedumper dump ./demo.yml +``` + +``` +php snakedumper.phar dump ./demo.yml +``` + +You can override most of the connection and output settings via command line arguments. Please execute `snakedumper dump --help` to see the available options. + +## Configuration File + +SnakeDumper can be configured with a YAML file. You can find a demo here [here](../demo.yml). + +The configuration file consists of the following options: +- `dumper`: Which dumper to use (required) +- `output`: The output configuration (required) +- `database`: Database connection configuration (required) +- `table_white_list`: List of tables that should be dumped (optional) +- `tables`: Table specific dump configuration (optional, but when you define nothing it's a simple database dump without data conversion) + +In the following we will give a little explanation to each option section. + +### dumper + +As SnakeDumper supports only SQL at the moment, the only available option at the moment is `Sql`. + +### output + +Available options: +- `rows_per_statement` (int): How many rows should be grouped into a single insert statement +- `file` (string): The filepath where the generated dump should be stored +- `gzip` (bool): indicates whether the generated dump should be gzipped (true/false) + +**Hint**: If you want to test/debug your configure, just use php://stdout as filepath to see the generated dump in your terminal. + +### database + +Available options (all string): +- `driver`: Database driver to use (depending on your database platform). You can find a list of available drivers [here](https://www.doctrine-project.org/projects/doctrine-dbal/en/2.7/reference/configuration.html#connecting-using-a-url). +- `host` +- `user` +- `password` +- `dbname` +- `charset` + +### table_white_list + +By default SnakeDumper dumps all available tables. By defining a white list you can configure explicitly which tables you want to include in your dump. + +### tables + +Here you can define table specific configuration. The following options are available: + +- `ignore_table` (bool): Set to true to ignore this table in the dump +- `ignore_content` (bool): Set to true to dump the table's structure, but ignore its contents +- `order_by` (string, e.g `id ASC`): Define the ordering of the queried data, e.g. to select only the most recent records +- `limit` (int): Number of rows that should be selected from this table +- `converters`: Rules that should be applied to the dumped data. See below for more details +- `filters`: Conditions that must apply to the selected rows to be included in the dump (e.g. select only customers from a specific country). See below for more details. +- `query` (string): If the available options are not sufficient, you can also define a custom query to select the data that should be dumped + +## Filters / Conditions + +There are a number of filters that you can use to reduce the amount of dumped data. Most filters are based on standard SQL conditions. + +A filter is an array with three items: [operator, column, value]. + +Your filters can then look like this: +```yaml + filters: + - [in, 'id', [1, 2, 3]] # only select rows which id is 1, 2 or 3 + - [like, 'name', 'Markus %'] # only select rows which column "name" starts with Markus +``` + +Currently, the following operators are available: +- eq +- neq +- lt +- lte +- gt +- gte +- isNull +- isNotNull +- like +- notLike +- in +- notIn + +## Converters + +There are lots of data converts available that help to convert any sensitive information into arbitrary generated data. You can define converters for a table under the `converters` key. Each column can have multiple converters which are applied in sequence. + +The configuration looks similar to the following: + +```yaml + converters: + name: + - JohnDoe + street: + - StreetName + company_name: + - Faker: + formatter: company + +``` + +You can find all available Converters [here](https://github.com/digilist/SnakeDumper/tree/master/src/Converter). In the future I might provide a list with examples for each converter, but currently there is none. So please take a look at the converters to see if they accept parameters. + +By using the faker converter you can use any available data generator that is part of the famous [Faker library](https://github.com/fzaninotto/Faker). + +## Relations between tables & referential integrity + +It is typical for a relational database to have relations between tables. For example, you can have customers and billings. If you know exclude some customers you do not want to have the billings of those customers included in your dump. + +Consider the following example: + +Table `customers`: + + id | first_name | last_name +---: | ---: | ---: +1 | Markus | Fasselt +2 | Linus | Torvalds +3 | Rasmus | Lerdorf + +Table `billings`: + +id | customer_id | amount +---: | ---: | ---: +1 | 1 | 20 +2 | 1 | 30 +3 | 2 | 10 +4 | 3 | 10 +5 | 3 | 42 +6 | 3 | 20 + +When you now define, that you only want to have the first to customers (id 1 and 2), the result should be the following: + +Table `customers`: + +id | first_name | last_name +---: | ---: | ---: +1 | Markus | Fasselt +2 | Linus | Torvalds + +Table `billings`: + +id | customer_id | amount +---: | ---: | ---: +1 | 1 | 20 +2 | 1 | 30 +3 | 2 | 10 + +As you can see, customer 3 is not included in the customers table. Furthermore, the billings do not contain the billings of customer 3. + +This feature is a part of SnakeDumper and allows you to create small development dumps that keep the referential integrity in the database. + +Note: SnakeDumper cannot handle cyclic references between tables at the moment. This only works in acyclic databases. + +## Custom queries + +There isn't much to say about custom queries. You can write any `SELECT` query you like and all dumped rows will be included into the dump. + +You can include the magic `$autoConditions` string into your query to consider also conditions defined under the `filters` configuration key and also automatic relational filters to keep the data integrity. From ccb5e7d08de65127c6c422770a4afe3c7fb8eaad Mon Sep 17 00:00:00 2001 From: Hamza Amrouche Date: Wed, 25 Jul 2018 18:13:56 +0200 Subject: [PATCH 11/17] minor: avoid dupplication of get option --- src/Command/DumpCommand.php | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/Command/DumpCommand.php b/src/Command/DumpCommand.php index 252df7d..827fd23 100644 --- a/src/Command/DumpCommand.php +++ b/src/Command/DumpCommand.php @@ -94,20 +94,20 @@ private function parseConfig(InputInterface $input) */ private function overrideConfigs(SqlDumperConfiguration $config, InputInterface $input) { - if ($input->getOption('dbname') !== null) { - $config->getDatabaseConfig()->setDatabaseName($input->getOption('dbname')); + if (($dbname = $input->getOption('dbname')) !== null) { + $config->getDatabaseConfig()->setDatabaseName($dbname); } - if ($input->getOption('user') !== null) { - $config->getDatabaseConfig()->setUser($input->getOption('user')); + if (($user = $input->getOption('user')) !== null) { + $config->getDatabaseConfig()->setUser($user); } - if ($input->getOption('password') !== null) { - $config->getDatabaseConfig()->setPassword($input->getOption('password')); + if (($password = $input->getOption('password')) !== null) { + $config->getDatabaseConfig()->setPassword($password); } - if ($input->getOption('host') !== null) { - $config->getDatabaseConfig()->setHost($input->getOption('host')); + if (($host = $input->getOption('host')) !== null) { + $config->getDatabaseConfig()->setHost($host); } - if ($input->getOption('port') !== null) { - $config->getDatabaseConfig()->setPort($input->getOption('port')); + if (($port = $input->getOption('port')) !== null) { + $config->getDatabaseConfig()->setPort($port); } if ($input->getOption('disable-limits')) { foreach ($config->getTableConfigs() as $tableConfig) { @@ -117,8 +117,7 @@ private function overrideConfigs(SqlDumperConfiguration $config, InputInterface if ($input->getOption('disable-structure')) { $config->setIgnoreStructure(true); } - if ($input->getOption('output') !== null) { - $path = $input->getOption('output'); + if (($path = $input->getOption('output')) !== null) { $config->getOutputConfig()->setFile($path); $config->getOutputConfig()->setGzip(strrpos($path, '.gz') === strlen($path) - 3); } From e6f84bb7887baaad33900c7367233b73097b8a0a Mon Sep 17 00:00:00 2001 From: Julien Turby Date: Tue, 7 Aug 2018 00:03:44 +0200 Subject: [PATCH 12/17] add safe email converter --- src/Converter/EmailConverter.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/Converter/EmailConverter.php diff --git a/src/Converter/EmailConverter.php b/src/Converter/EmailConverter.php new file mode 100644 index 0000000..8ef9931 --- /dev/null +++ b/src/Converter/EmailConverter.php @@ -0,0 +1,21 @@ + Date: Thu, 9 Aug 2018 18:28:37 +0200 Subject: [PATCH 13/17] add on delete/update options on foreignKeys query --- src/Dumper/Sql/Dumper/StructureDumper.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Dumper/Sql/Dumper/StructureDumper.php b/src/Dumper/Sql/Dumper/StructureDumper.php index 1d1a19c..3d63650 100644 --- a/src/Dumper/Sql/Dumper/StructureDumper.php +++ b/src/Dumper/Sql/Dumper/StructureDumper.php @@ -60,9 +60,15 @@ public function dumpTableStructure(array $tables) public function dumpConstraints(array $tables) { foreach ($tables as $table) { foreach ($table->getForeignKeys() as $constraint) { + $options = ''; + if ($onUpdate = $constraint->onUpdate()) { + $options .= ' ON UPDATE' . $onUpdate; + } + if ($onDelete = $constraint->onDelete()) { + $options .= ' ON DELETE ' . $onDelete; + } $constraint = $this->platform->getCreateConstraintSQL($constraint, $table); - - $this->dumpOutput->writeln($constraint . ';'); + $this->dumpOutput->writeln($constraint . $options . ';'); } } } From fddc691d879d7d27cc4557d0ca1d35f58bd02392 Mon Sep 17 00:00:00 2001 From: Julien Turby Date: Tue, 21 Aug 2018 11:11:13 +0200 Subject: [PATCH 14/17] add test for foreign key delete/update options --- src/Converter/Tests/ConditionalConverterTest.php | 4 ++-- src/Converter/Tests/RandomConverterTest.php | 3 ++- src/Dumper/Sql/Dumper/StructureDumper.php | 2 +- src/Dumper/Sql/Tests/AbstractSqlTest.php | 7 +++---- src/Dumper/Sql/Tests/TableFilterTest.php | 3 ++- src/Dumper/Sql/Tests/test_dump.sql | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Converter/Tests/ConditionalConverterTest.php b/src/Converter/Tests/ConditionalConverterTest.php index e6e0e51..3665e0c 100644 --- a/src/Converter/Tests/ConditionalConverterTest.php +++ b/src/Converter/Tests/ConditionalConverterTest.php @@ -3,9 +3,9 @@ namespace Digilist\SnakeDumper\Converter\Tests; use Digilist\SnakeDumper\Converter\ConditionalConverter; -use Digilist\SnakeDumper\Converter\Service\DataConverter; +use PHPUnit\Framework\TestCase; -class ConditionalConverterTest extends \PHPUnit_Framework_TestCase +class ConditionalConverterTest extends TestCase { public function testConditionalIfTrue() diff --git a/src/Converter/Tests/RandomConverterTest.php b/src/Converter/Tests/RandomConverterTest.php index 907e15f..2a57c21 100644 --- a/src/Converter/Tests/RandomConverterTest.php +++ b/src/Converter/Tests/RandomConverterTest.php @@ -4,12 +4,13 @@ use Digilist\SnakeDumper\Converter\RandomConverter; use Digilist\SnakeDumper\Exception\InvalidArgumentException; +use PHPUnit\Framework\TestCase; /** * @package Digilist\SnakeDumper\Converter\Tests * @author moellers */ -class RandomConverterTest extends \PHPUnit_Framework_TestCase +class RandomConverterTest extends TestCase { public function testEqualBounds() diff --git a/src/Dumper/Sql/Dumper/StructureDumper.php b/src/Dumper/Sql/Dumper/StructureDumper.php index 3d63650..50d9bc7 100644 --- a/src/Dumper/Sql/Dumper/StructureDumper.php +++ b/src/Dumper/Sql/Dumper/StructureDumper.php @@ -62,7 +62,7 @@ public function dumpConstraints(array $tables) { foreach ($table->getForeignKeys() as $constraint) { $options = ''; if ($onUpdate = $constraint->onUpdate()) { - $options .= ' ON UPDATE' . $onUpdate; + $options .= ' ON UPDATE ' . $onUpdate; } if ($onDelete = $constraint->onDelete()) { $options .= ' ON DELETE ' . $onDelete; diff --git a/src/Dumper/Sql/Tests/AbstractSqlTest.php b/src/Dumper/Sql/Tests/AbstractSqlTest.php index 923db5b..1324b04 100644 --- a/src/Dumper/Sql/Tests/AbstractSqlTest.php +++ b/src/Dumper/Sql/Tests/AbstractSqlTest.php @@ -2,13 +2,12 @@ namespace Digilist\SnakeDumper\Dumper\Sql\Tests; -use Digilist\SnakeDumper\Configuration\DatabaseConfiguration; -use Digilist\SnakeDumper\Dumper\Sql\ConnectionHandler; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Platforms\AbstractPlatform; +use PHPUnit\Framework\TestCase; -abstract class AbstractSqlTest extends \PHPUnit_Framework_TestCase +abstract class AbstractSqlTest extends TestCase { const DBNAME = 'snakedumper'; @@ -75,7 +74,7 @@ protected function createTestSchema($randomTables = false) customer_id INTEGER, product VARCHAR(100), amount REAL, - CONSTRAINT customer_id FOREIGN KEY (customer_id) REFERENCES Customer(id) + CONSTRAINT customer_id FOREIGN KEY (customer_id) REFERENCES Customer(id) ON UPDATE CASCADE ON DELETE SET NULL )'); $pdo->query('CREATE INDEX billing_product ON Billing (product)'); diff --git a/src/Dumper/Sql/Tests/TableFilterTest.php b/src/Dumper/Sql/Tests/TableFilterTest.php index 8633597..39eb773 100644 --- a/src/Dumper/Sql/Tests/TableFilterTest.php +++ b/src/Dumper/Sql/Tests/TableFilterTest.php @@ -6,8 +6,9 @@ use Digilist\SnakeDumper\Configuration\Table\TableConfiguration; use Digilist\SnakeDumper\Dumper\Sql\TableFilter; use Doctrine\DBAL\Schema\Table; +use PHPUnit\Framework\TestCase; -class TableFilterTest extends \PHPUnit_Framework_TestCase +class TableFilterTest extends TestCase { /** diff --git a/src/Dumper/Sql/Tests/test_dump.sql b/src/Dumper/Sql/Tests/test_dump.sql index 9a19c0e..3ebcbfc 100644 --- a/src/Dumper/Sql/Tests/test_dump.sql +++ b/src/Dumper/Sql/Tests/test_dump.sql @@ -10,4 +10,4 @@ CREATE TABLE `RandomTable` (`id` INT AUTO_INCREMENT NOT NULL, `name` VARCHAR(10) INSERT INTO `Customer` (`id`, `name`) VALUES ('1', 'Foobar'), ('2', 'Foobar'), ('3', 'Foobar'); INSERT INTO `Customer` (`id`, `name`) VALUES ('4', 'Foobar'); INSERT INTO `Billing` (`id`, `customer_id`, `product`, `amount`) VALUES ('1', '1', 'IT', '42'), ('2', '1', NULL, '1337'), ('3', '2', 'Some stuff', '1337'); -ALTER TABLE `Billing` ADD CONSTRAINT `customer_id` FOREIGN KEY (`customer_id`) REFERENCES `Customer` (`id`); +ALTER TABLE `Billing` ADD CONSTRAINT `customer_id` FOREIGN KEY (`customer_id`) REFERENCES `Customer` (`id`) ON UPDATE CASCADE ON DELETE SET NULL; From 5c119fa53ee61599d59239932682bcbe7b9284fa Mon Sep 17 00:00:00 2001 From: Markus Fasselt Date: Fri, 24 Aug 2018 15:08:48 +0200 Subject: [PATCH 15/17] Allow to specify pdo options --- demo.yml | 5 ++++- src/Command/DumpCommand.php | 2 +- src/Configuration/DatabaseConfiguration.php | 18 ++++++++++++++++++ src/Configuration/SnakeConfigurationTree.php | 4 ++++ src/Dumper/Sql/ConnectionHandler.php | 1 + 5 files changed, 28 insertions(+), 2 deletions(-) diff --git a/demo.yml b/demo.yml index 0d2f11c..c19a87b 100644 --- a/demo.yml +++ b/demo.yml @@ -5,11 +5,14 @@ output: gzip: false database: driver: pdo_mysql - host: localhost + host: 127.0.0.1 user: root password: '' dbname: demo charset: UTF8 + pdo_options: + !php/const PDO::MYSQL_ATTR_SSL_CA: './path-to-ca.pem' + !php/const PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT: true table_white_list: - foo diff --git a/src/Command/DumpCommand.php b/src/Command/DumpCommand.php index 827fd23..ee721da 100644 --- a/src/Command/DumpCommand.php +++ b/src/Command/DumpCommand.php @@ -74,7 +74,7 @@ private function parseConfig(InputInterface $input) throw new \InvalidArgumentException('Cannot find configuration file: ' . $configArg); } - $config = Yaml::parse(file_get_contents($configFile)); + $config = Yaml::parse(file_get_contents($configFile), Yaml::PARSE_CONSTANT); $processor = new Processor(); $configuration = new SnakeConfigurationTree(); diff --git a/src/Configuration/DatabaseConfiguration.php b/src/Configuration/DatabaseConfiguration.php index 7997c75..478ba67 100644 --- a/src/Configuration/DatabaseConfiguration.php +++ b/src/Configuration/DatabaseConfiguration.php @@ -134,6 +134,24 @@ public function getConnection() return $this->get('connection'); } + /** + * @return array + */ + public function getPdoOptions() + { + return $this->get('pdo_options'); + } + + /** + * @param array $options + * + * @return DatabaseConfiguration + */ + public function setPdoOptions(array $options) + { + return $this->set('pdo_options', $options); + } + protected function parseConfig(array $config) { if (!isset($config['connection'])) { diff --git a/src/Configuration/SnakeConfigurationTree.php b/src/Configuration/SnakeConfigurationTree.php index 2138e9c..603aab3 100644 --- a/src/Configuration/SnakeConfigurationTree.php +++ b/src/Configuration/SnakeConfigurationTree.php @@ -43,6 +43,10 @@ public function getConfigTreeBuilder() ->scalarNode('password')->end() ->scalarNode('dbname')->end() ->scalarNode('charset')->end() + ->arrayNode('pdo_options') + ->prototype('scalar') + ->end() + ->end() ->end() ->end() ; diff --git a/src/Dumper/Sql/ConnectionHandler.php b/src/Dumper/Sql/ConnectionHandler.php index 946dcd9..e2e6d31 100644 --- a/src/Dumper/Sql/ConnectionHandler.php +++ b/src/Dumper/Sql/ConnectionHandler.php @@ -119,6 +119,7 @@ private function connect() 'dbname' => $this->config->getDatabaseName(), 'charset' => $this->config->getCharset(), 'wrapperClass' => \Digilist\SnakeDumper\Dumper\Bridge\Doctrine\DBAL\Connection::class, + 'driverOptions' => $this->config->getPdoOptions(), ); $dbalConfig = new Configuration(); From c0a3f8b9643edda5af479ea44e7d5043ba4d12b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Simon=20Maria=20M=C3=B6llers?= Date: Wed, 2 Aug 2017 17:44:44 +0200 Subject: [PATCH 16/17] Add support for custom types in Doctrine --- src/Configuration/DatabaseConfiguration.php | 19 +++++++ src/Configuration/SnakeConfigurationTree.php | 7 +++ src/Dumper/Sql/ConnectionHandler.php | 13 +++++ src/Dumper/Sql/Tests/AbstractSqlTest.php | 20 ++++--- src/Dumper/Sql/Tests/TestType.php | 55 ++++++++++++++++++++ src/Dumper/Sql/Tests/test_dump.sql | 6 +-- 6 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 src/Dumper/Sql/Tests/TestType.php diff --git a/src/Configuration/DatabaseConfiguration.php b/src/Configuration/DatabaseConfiguration.php index 478ba67..aabe875 100644 --- a/src/Configuration/DatabaseConfiguration.php +++ b/src/Configuration/DatabaseConfiguration.php @@ -2,6 +2,7 @@ namespace Digilist\SnakeDumper\Configuration; +use Digilist\SnakeDumper\Exception\ConfigurationException; use Doctrine\DBAL\Connection; class DatabaseConfiguration extends AbstractConfiguration @@ -134,6 +135,14 @@ public function getConnection() return $this->get('connection'); } + /** + * @return array + */ + public function getCustomTypes() + { + return $this->get('custom_types'); + } + /** * @return array */ @@ -161,6 +170,16 @@ protected function parseConfig(array $config) $this->ensureHas('password'); $this->ensureHas('user'); $this->ensureHas('charset'); + + if (!isset($config['custom_types'])) { + $config['custom_types'] = array(); + } + + foreach ($config['custom_types'] as $customType) { + if (!isset($customType['class'])) { + throw ConfigurationException::createEnsureHasException('class'); + } + } } } } diff --git a/src/Configuration/SnakeConfigurationTree.php b/src/Configuration/SnakeConfigurationTree.php index 603aab3..3ba1b7c 100644 --- a/src/Configuration/SnakeConfigurationTree.php +++ b/src/Configuration/SnakeConfigurationTree.php @@ -43,6 +43,13 @@ public function getConfigTreeBuilder() ->scalarNode('password')->end() ->scalarNode('dbname')->end() ->scalarNode('charset')->end() + ->arrayNode('custom_types') + ->prototype('array') + ->children() + ->scalarNode('class')->end() + ->end() + ->end() + ->end() ->arrayNode('pdo_options') ->prototype('scalar') ->end() diff --git a/src/Dumper/Sql/ConnectionHandler.php b/src/Dumper/Sql/ConnectionHandler.php index e2e6d31..aa15ea8 100644 --- a/src/Dumper/Sql/ConnectionHandler.php +++ b/src/Dumper/Sql/ConnectionHandler.php @@ -10,6 +10,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Platforms\MySqlPlatform; +use Doctrine\DBAL\Types\Type; use PDO; class ConnectionHandler @@ -126,6 +127,8 @@ private function connect() $this->connection = DriverManager::getConnection($connectionParams, $dbalConfig); $this->connection->connect(); + $this->registerCustomTypes(); + $this->connection->getSchemaManager(); $this->initPlatformAdjustment($this->connection); @@ -154,4 +157,14 @@ private function initPlatformAdjustment(Connection $connection) $this->platformAdjustment = new DummyAdjustment($this); } + + private function registerCustomTypes() { + foreach ($this->config->getCustomTypes() as $typeName => $config) { + $this->registerCustomType($typeName, $config); + } + } + + private function registerCustomType($typeName, array $config) { + Type::addType($typeName, $config['class']); + } } diff --git a/src/Dumper/Sql/Tests/AbstractSqlTest.php b/src/Dumper/Sql/Tests/AbstractSqlTest.php index 1324b04..8adbc74 100644 --- a/src/Dumper/Sql/Tests/AbstractSqlTest.php +++ b/src/Dumper/Sql/Tests/AbstractSqlTest.php @@ -5,6 +5,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\DriverManager; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Types\Type; use PHPUnit\Framework\TestCase; abstract class AbstractSqlTest extends TestCase @@ -33,6 +34,12 @@ public function setUp() // $pdo = new \PDO('sqlite::memory:'); // $pdo->query('PRAGMA foreign_keys=ON;'); // + + // Register custom type + if (!Type::hasType('test')) { + Type::addType('test', TestType::class); + } + // Use MySQL instead. $this->connection = DriverManager::getConnection(array( 'user' => 'root', @@ -67,7 +74,8 @@ protected function createTestSchema($randomTables = false) $pdo->query('CREATE TABLE Customer ( id INTEGER PRIMARY KEY AUTO_INCREMENT, - name VARCHAR(10) + name VARCHAR(10), + `test` VARCHAR(10) NOT NULL COLLATE utf8_unicode_ci COMMENT \'(DC2Type:test)\' )'); $pdo->query('CREATE TABLE Billing ( id INTEGER PRIMARY KEY AUTO_INCREMENT, @@ -90,11 +98,11 @@ protected function createTestSchema($randomTables = false) } // insert data - $pdo->query('INSERT INTO Customer VALUES (1, "Markus")'); - $pdo->query('INSERT INTO Customer VALUES (2, "Konstantin")'); - $pdo->query('INSERT INTO Customer VALUES (3, "John")'); - $pdo->query('INSERT INTO Customer VALUES (4, "Konrad")'); - $pdo->query('INSERT INTO Customer VALUES (5, "Mark")'); + $pdo->query('INSERT INTO Customer VALUES (1, "Markus", "today")'); + $pdo->query('INSERT INTO Customer VALUES (2, "Konstantin", "yesterday")'); + $pdo->query('INSERT INTO Customer VALUES (3, "John", "tomorrow")'); + $pdo->query('INSERT INTO Customer VALUES (4, "Konrad", "always")'); + $pdo->query('INSERT INTO Customer VALUES (5, "Mark", "today")'); $pdo->query('INSERT INTO Billing VALUES (1, 1, "IT", 42)'); $pdo->query('INSERT INTO Billing VALUES (2, 1, NULL, 1337)'); $pdo->query('INSERT INTO Billing VALUES (3, 2, "Some stuff", 1337)'); diff --git a/src/Dumper/Sql/Tests/TestType.php b/src/Dumper/Sql/Tests/TestType.php new file mode 100644 index 0000000..9e5828a --- /dev/null +++ b/src/Dumper/Sql/Tests/TestType.php @@ -0,0 +1,55 @@ + Date: Fri, 4 Aug 2017 23:04:20 +0200 Subject: [PATCH 17/17] Enable empty replacement values. --- src/Converter/ReplaceConverter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Converter/ReplaceConverter.php b/src/Converter/ReplaceConverter.php index 2892363..ef64578 100644 --- a/src/Converter/ReplaceConverter.php +++ b/src/Converter/ReplaceConverter.php @@ -23,7 +23,7 @@ class ReplaceConverter implements ConverterInterface */ public function __construct(array $parameters) { - if (empty($parameters['search']) || empty($parameters['replace'])) { + if (empty($parameters['search']) || is_null($parameters['replace'])) { throw new InvalidArgumentException('You have to pass the search and value to replace.'); }