Skip to content

Commit

Permalink
[DoctrineBridge] Refactored the query sanitization in the collector
Browse files Browse the repository at this point in the history
The original parameters are kept whenever possible to allow using them
again to explain the query.
  • Loading branch information
stof committed Jan 23, 2012
1 parent 3b260d2 commit e37783f
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 34 deletions.
72 changes: 49 additions & 23 deletions src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php
Expand Up @@ -13,6 +13,7 @@

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;
Expand Down Expand Up @@ -54,7 +55,7 @@ public function collect(Request $request, Response $response, \Exception $except
{
$queries = array();
foreach ($this->loggers as $name => $logger) {
$queries[$name] = $this->sanitizeQueries($logger->queries);
$queries[$name] = $this->sanitizeQueries($name, $logger->queries);
}

$this->data = array(
Expand Down Expand Up @@ -104,48 +105,73 @@ public function getName()
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));
}

if (null === $var) {
return 'null';
}

if (false === $var) {
return 'false';
}

if (true === $var) {
return 'true';
return array(sprintf('Resource(%s)', get_resource_type($var)), false);
}

return (string) $var;
return array($var, true);
}
}
Expand Up @@ -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;
Expand Down Expand Up @@ -70,49 +71,58 @@ public function testCollectTime()
/**
* @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());
$c = unserialize(serialize($c));

$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())
Expand All @@ -122,6 +132,9 @@ private function createCollector($queries)
->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;
Expand Down

0 comments on commit e37783f

Please sign in to comment.