[DoctrineBridge] Refactored the query sanitization in the collector

The original parameters are kept whenever possible to allow using them
again to explain the query.
This commit is contained in:
Christophe Coevoet 2012-01-23 10:57:46 +01:00
parent 3b260d268b
commit e37783f4f9
2 changed files with 73 additions and 34 deletions

View File

@ -13,6 +13,7 @@ namespace Symfony\Bridge\Doctrine\DataCollector;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\DBAL\Logging\DebugStack;
use Doctrine\DBAL\Types\Type;
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@ -54,7 +55,7 @@ class DoctrineDataCollector extends DataCollector
{
$queries = array();
foreach ($this->loggers as $name => $logger) {
$queries[$name] = $this->sanitizeQueries($logger->queries);
$queries[$name] = $this->sanitizeQueries($name, $logger->queries);
}
$this->data = array(
@ -104,48 +105,73 @@ class DoctrineDataCollector extends DataCollector
return 'db';
}
private function sanitizeQueries($queries)
private function sanitizeQueries($connectionName, $queries)
{
foreach ($queries as $i => $query) {
foreach ((array) $query['params'] as $j => $param) {
$queries[$i]['params'][$j] = $this->varToString($param);
}
$queries[$i] = $this->sanitizeQuery($connectionName, $query);
}
return $queries;
}
private function varToString($var)
private function sanitizeQuery($connectionName, $query)
{
$query['explainable'] = true;
$query['params'] = (array) $query['params'];
foreach ($query['params'] as $j => &$param) {
if (isset($query['types'][$j])) {
// Transform the param according to the type
$type = $query['types'][$j];
if (is_string($type)) {
$type = Type::getType($type);
}
if ($type instanceof Type) {
$query['types'][$j] = $type->getBindingType();
$param = $type->convertToDatabaseValue($param, $this->registry->getConnection($connectionName)->getDatabasePlatform());
}
}
list($param, $explainable) = $this->sanitizeParam($param);
if (!$explainable) {
$query['explainable'] = false;
}
}
return $query;
}
/**
* Sanitizes a param.
*
* The return value is an array with the sanitized value and a boolean
* indicating if the original value was kept (allowing to use the sanitized
* value to explain the query).
*
* @param mixed $var
* @return array
*/
private function sanitizeParam($var)
{
if (is_object($var)) {
return sprintf('Object(%s)', get_class($var));
return array(sprintf('Object(%s)', get_class($var)), false);
}
if (is_array($var)) {
$a = array();
$original = true;
foreach ($var as $k => $v) {
$a[] = sprintf('%s => %s', $k, $this->varToString($v));
list($value, $orig) = $this->sanitizeParam($v);
$original = $original && $orig;
$a[$k] = $value;
}
return sprintf("Array(%s)", implode(', ', $a));
return array($a, $original);
}
if (is_resource($var)) {
return sprintf('Resource(%s)', get_resource_type($var));
return array(sprintf('Resource(%s)', get_resource_type($var)), false);
}
if (null === $var) {
return 'null';
}
if (false === $var) {
return 'false';
}
if (true === $var) {
return 'true';
}
return (string) $var;
return array($var, true);
}
}

View File

@ -11,6 +11,7 @@
namespace Symfony\Tests\Bridge\Doctrine\DataCollector;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
@ -70,25 +71,26 @@ class DoctrineDataCollectorTest extends \PHPUnit_Framework_TestCase
/**
* @dataProvider paramProvider
*/
public function testCollectQueries($param, $expected)
public function testCollectQueries($param, $types, $expected, $explainable)
{
$queries = array(
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => array(), 'executionMS' => 1)
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => $types, 'executionMS' => 1)
);
$c = $this->createCollector($queries);
$c->collect(new Request(), new Response());
$collected_queries = $c->getQueries();
$this->assertEquals($expected, $collected_queries['default'][0]['params'][0]);
$this->assertEquals($explainable, $collected_queries['default'][0]['explainable']);
}
/**
* @dataProvider paramProvider
*/
public function testSerialization($param, $expected)
public function testSerialization($param, $types, $expected, $explainable)
{
$queries = array(
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => array(), 'executionMS' => 1)
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => $types, 'executionMS' => 1)
);
$c = $this->createCollector($queries);
$c->collect(new Request(), new Response());
@ -96,23 +98,31 @@ class DoctrineDataCollectorTest extends \PHPUnit_Framework_TestCase
$collected_queries = $c->getQueries();
$this->assertEquals($expected, $collected_queries['default'][0]['params'][0]);
$this->assertEquals($explainable, $collected_queries['default'][0]['explainable']);
}
public function paramProvider()
{
return array(
array('some value', 'some value'),
array(1, '1'),
array(true, 'true'),
array(null, 'null'),
array(new \stdClass(), 'Object(stdClass)'),
array(fopen(__FILE__, 'r'), 'Resource(stream)'),
array(new \SplFileInfo(__FILE__), 'Object(SplFileInfo)'),
array('some value', array(), 'some value', true),
array(1, array(), 1, true),
array(true, array(), true, true),
array(null, array(), null, true),
array(new \DateTime('2011-09-11'), array('date'), '2011-09-11', true),
array(fopen(__FILE__, 'r'), array(), 'Resource(stream)', false),
array(new \SplFileInfo(__FILE__), array(), 'Object(SplFileInfo)', false),
);
}
private function createCollector($queries)
{
$connection = $this->getMockBuilder('Doctrine\DBAL\Connection')
->disableOriginalConstructor()
->getMock();
$connection->expects($this->any())
->method('getDatabasePlatform')
->will($this->returnValue(new MySqlPlatform()));
$registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
$registry
->expects($this->any())
@ -122,6 +132,9 @@ class DoctrineDataCollectorTest extends \PHPUnit_Framework_TestCase
->expects($this->any())
->method('getManagerNames')
->will($this->returnValue(array('default' => 'doctrine.orm.default_entity_manager')));
$registry->expects($this->any())
->method('getConnection')
->will($this->returnValue($connection));
$logger = $this->getMock('Doctrine\DBAL\Logging\DebugStack');
$logger->queries = $queries;