From c5402b4804ba9d61ca54b82ae4289b206ebea62d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 4 Oct 2017 16:36:09 +0200 Subject: [PATCH] Adding SQL dumper --- composer.json | 2 +- .../Patcher/AbstractDatabasePatch.php | 9 +- .../Database/Patcher/DatabasePatchLogger.php | 184 ++++++++++++++++++ src/Mouf/Database/Patcher/PatchConnection.php | 9 +- 4 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 src/Mouf/Database/Patcher/DatabasePatchLogger.php diff --git a/composer.json b/composer.json index f6fd144..609ef26 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ "require": { "php": ">=7.1", "mouf/database.doctrine-dbal-wrapper": "^1.0", - "mouf/utils.patcher": "^2.1", + "mouf/utils.patcher": "^2.2", "mouf/classname-mapper": "^1.0", "symfony/filesystem": "^2.0 || ^3.0", "thecodingmachine/dbal-fluid-schema-builder": "^1.0" diff --git a/src/Mouf/Database/Patcher/AbstractDatabasePatch.php b/src/Mouf/Database/Patcher/AbstractDatabasePatch.php index 0021c63..0911c7f 100644 --- a/src/Mouf/Database/Patcher/AbstractDatabasePatch.php +++ b/src/Mouf/Database/Patcher/AbstractDatabasePatch.php @@ -22,6 +22,8 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Schema\Schema; +use Mouf\Utils\Patcher\Dumper\DumpableInterface; +use Mouf\Utils\Patcher\Dumper\DumperInterface; use Mouf\Utils\Patcher\PatchInterface; use Mouf\Utils\Patcher\PatchException; use Mouf\MoufManager; @@ -34,7 +36,7 @@ * * @author David Negrier */ -abstract class AbstractDatabasePatch implements PatchInterface +abstract class AbstractDatabasePatch implements PatchInterface, DumpableInterface { /** * @var PatchConnection @@ -171,4 +173,9 @@ protected function getConnection(): Connection { return $this->patchConnection->getConnection(); } + + public function setDumper(DumperInterface $dumper) + { + $this->patchConnection->setDumper($dumper); + } } diff --git a/src/Mouf/Database/Patcher/DatabasePatchLogger.php b/src/Mouf/Database/Patcher/DatabasePatchLogger.php new file mode 100644 index 0000000..2dd92dd --- /dev/null +++ b/src/Mouf/Database/Patcher/DatabasePatchLogger.php @@ -0,0 +1,184 @@ +dumper = $dumper; + $this->platform = $platform; + } + + /** + * Code borrowed from Zend Framework 2. Thanks guys. => https://github.com/tburschka/zf2-doctrine-sql-logger/blob/1.0.0/src/ZF2DoctrineSQLLogger/ZF2DoctrineSQLLogger.php + * + * @param string $sql + * @param array $params + * @param array $types + */ + public function startQuery($sql, array $params = null, array $types = null) + { + if (strpos($sql, 'SELECT ') === 0) { + return; + } + if (strpos($sql, 'SHOW ') === 0) { + return; + } + + $errors = ''; + $assembled = $sql; + if(!empty($params)) { + foreach ($params as $key => $param) { + if ($param === null) { + $assembled = implode('NULL', explode('?', $assembled, 2)); + } else { + $type = $this->mapType($types, $key, $param); + if (null === $type) { + $errors .= 'Param could not be prepared: key: "' . $key + . '", value "' . var_export($param, true) . '"!'."\n"; + $assembled = implode('?', explode('?', $assembled, 2)); + } else { + $value = $type->convertToDatabaseValue($param, $this->platform); + $assembled = implode($this->prepareValue($type, $value), explode('?', $assembled, 2)); + } + } + } + } + $this->dumper->dumpPatch($errors.$assembled.';'); + } + + /** + * @param $type Type + * @param $value mixed + * @return mixed + */ + protected function prepareValue($type, $value) + { + if (is_object($type)) { + switch(get_class($type)) { + case 'Doctrine\DBAL\Types\SimpleArrayType': + break; + default: + $value = var_export($value, true); + break; + } + } else { + $value = var_export($value, true); + } + return $value; + } + + /** + * @param $types mixed + * @param $key mixed + * @param $param mixed + * @return Type|null + */ + protected function mapType($types, $key, $param) + { + // map type name by doctrine types map + $name = $this->mapByTypesMap($types, $key); + // map type name for known numbers + if (is_null($name)) { + $name = $this->mapByKeyNumber($key); + } + // map type name for known param type + if (is_null($name)) { + $name = $this->mapByParamType($param); + } + // if type could not be mapped, return null + if (is_null($name)) { + return null; + } + return Type::getType($name); + } + + /** + * Map by Doctrine DBAL types map + * @param $types + * @param $key + * @return null|string + */ + protected function mapByTypesMap($types, $key) + { + $typesMap = Type::getTypesMap(); + if (array_key_exists($key, $types) && array_key_exists($types[$key], $typesMap)) { + $name = $types[$key]; + } else { + $name = null; + } + return $name; + } + + /** + * @param $key + * @return null|string + */ + protected function mapByKeyNumber($key) + { + switch($key) { + case 2: + $name = Type::STRING; + break; + case 102: + $name = Type::SIMPLE_ARRAY; + break; + default: + $name = null; + break; + } + return $name; + } + + /** + * @param $param + * @return null|string + */ + protected function mapByParamType($param) + { + switch(gettype($param)) { + case 'array': + $name = Type::SIMPLE_ARRAY; + break; + case 'string': + $name = Type::STRING; + break; + case 'integer': + $name = Type::INTEGER; + break; + default: + $name = null; + break; + } + return $name; + } + + /** + * Marks the last started query as stopped. This can be used for timing of queries. + * + * @return void + */ + public function stopQuery() + { + } +} diff --git a/src/Mouf/Database/Patcher/PatchConnection.php b/src/Mouf/Database/Patcher/PatchConnection.php index 29008a4..1b4a945 100644 --- a/src/Mouf/Database/Patcher/PatchConnection.php +++ b/src/Mouf/Database/Patcher/PatchConnection.php @@ -22,6 +22,8 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\AbstractMySQLDriver; +use Mouf\Utils\Patcher\Dumper\DumpableInterface; +use Mouf\Utils\Patcher\Dumper\DumperInterface; use Mouf\Utils\Patcher\PatchListenerInterface; @@ -30,7 +32,7 @@ * * @author Pierre Vaidie */ -class PatchConnection implements PatchListenerInterface +class PatchConnection implements PatchListenerInterface, DumpableInterface { /** * @var string @@ -108,4 +110,9 @@ public function onReset(): void $this->dbalRootConnection->exec('USE '.$dbName); } } + + public function setDumper(DumperInterface $dumper) + { + $this->dbalConnection->getConfiguration()->setSQLLogger(new DatabasePatchLogger($dumper, $this->dbalConnection->getDatabasePlatform())); + } }