Skip to content

Commit e37783f

Browse files
committed
[DoctrineBridge] Refactored the query sanitization in the collector
The original parameters are kept whenever possible to allow using them again to explain the query.
1 parent 3b260d2 commit e37783f

File tree

2 files changed

+73
-34
lines changed

2 files changed

+73
-34
lines changed

src/Symfony/Bridge/Doctrine/DataCollector/DoctrineDataCollector.php

Lines changed: 49 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\Common\Persistence\ManagerRegistry;
1515
use Doctrine\DBAL\Logging\DebugStack;
16+
use Doctrine\DBAL\Types\Type;
1617
use Symfony\Component\HttpKernel\DataCollector\DataCollector;
1718
use Symfony\Component\HttpFoundation\Request;
1819
use Symfony\Component\HttpFoundation\Response;
@@ -54,7 +55,7 @@ public function collect(Request $request, Response $response, \Exception $except
5455
{
5556
$queries = array();
5657
foreach ($this->loggers as $name => $logger) {
57-
$queries[$name] = $this->sanitizeQueries($logger->queries);
58+
$queries[$name] = $this->sanitizeQueries($name, $logger->queries);
5859
}
5960

6061
$this->data = array(
@@ -104,48 +105,73 @@ public function getName()
104105
return 'db';
105106
}
106107

107-
private function sanitizeQueries($queries)
108+
private function sanitizeQueries($connectionName, $queries)
108109
{
109110
foreach ($queries as $i => $query) {
110-
foreach ((array) $query['params'] as $j => $param) {
111-
$queries[$i]['params'][$j] = $this->varToString($param);
112-
}
111+
$queries[$i] = $this->sanitizeQuery($connectionName, $query);
113112
}
114113

115114
return $queries;
116115
}
117116

118-
private function varToString($var)
117+
private function sanitizeQuery($connectionName, $query)
118+
{
119+
$query['explainable'] = true;
120+
$query['params'] = (array) $query['params'];
121+
foreach ($query['params'] as $j => &$param) {
122+
if (isset($query['types'][$j])) {
123+
// Transform the param according to the type
124+
$type = $query['types'][$j];
125+
if (is_string($type)) {
126+
$type = Type::getType($type);
127+
}
128+
if ($type instanceof Type) {
129+
$query['types'][$j] = $type->getBindingType();
130+
$param = $type->convertToDatabaseValue($param, $this->registry->getConnection($connectionName)->getDatabasePlatform());
131+
}
132+
}
133+
134+
list($param, $explainable) = $this->sanitizeParam($param);
135+
if (!$explainable) {
136+
$query['explainable'] = false;
137+
}
138+
}
139+
140+
return $query;
141+
}
142+
143+
/**
144+
* Sanitizes a param.
145+
*
146+
* The return value is an array with the sanitized value and a boolean
147+
* indicating if the original value was kept (allowing to use the sanitized
148+
* value to explain the query).
149+
*
150+
* @param mixed $var
151+
* @return array
152+
*/
153+
private function sanitizeParam($var)
119154
{
120155
if (is_object($var)) {
121-
return sprintf('Object(%s)', get_class($var));
156+
return array(sprintf('Object(%s)', get_class($var)), false);
122157
}
123158

124159
if (is_array($var)) {
125160
$a = array();
161+
$original = true;
126162
foreach ($var as $k => $v) {
127-
$a[] = sprintf('%s => %s', $k, $this->varToString($v));
163+
list($value, $orig) = $this->sanitizeParam($v);
164+
$original = $original && $orig;
165+
$a[$k] = $value;
128166
}
129167

130-
return sprintf("Array(%s)", implode(', ', $a));
168+
return array($a, $original);
131169
}
132170

133171
if (is_resource($var)) {
134-
return sprintf('Resource(%s)', get_resource_type($var));
135-
}
136-
137-
if (null === $var) {
138-
return 'null';
139-
}
140-
141-
if (false === $var) {
142-
return 'false';
143-
}
144-
145-
if (true === $var) {
146-
return 'true';
172+
return array(sprintf('Resource(%s)', get_resource_type($var)), false);
147173
}
148174

149-
return (string) $var;
175+
return array($var, true);
150176
}
151177
}

tests/Symfony/Tests/Bridge/Doctrine/DataCollector/DoctrineDataCollectorTest.php

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Tests\Bridge\Doctrine\DataCollector;
1313

14+
use Doctrine\DBAL\Platforms\MySqlPlatform;
1415
use Symfony\Bridge\Doctrine\DataCollector\DoctrineDataCollector;
1516
use Symfony\Component\HttpFoundation\Request;
1617
use Symfony\Component\HttpFoundation\Response;
@@ -70,49 +71,58 @@ public function testCollectTime()
7071
/**
7172
* @dataProvider paramProvider
7273
*/
73-
public function testCollectQueries($param, $expected)
74+
public function testCollectQueries($param, $types, $expected, $explainable)
7475
{
7576
$queries = array(
76-
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => array(), 'executionMS' => 1)
77+
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => $types, 'executionMS' => 1)
7778
);
7879
$c = $this->createCollector($queries);
7980
$c->collect(new Request(), new Response());
8081

8182
$collected_queries = $c->getQueries();
8283
$this->assertEquals($expected, $collected_queries['default'][0]['params'][0]);
84+
$this->assertEquals($explainable, $collected_queries['default'][0]['explainable']);
8385
}
8486

8587
/**
8688
* @dataProvider paramProvider
8789
*/
88-
public function testSerialization($param, $expected)
90+
public function testSerialization($param, $types, $expected, $explainable)
8991
{
9092
$queries = array(
91-
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => array(), 'executionMS' => 1)
93+
array('sql' => "SELECT * FROM table1 WHERE field1 = ?1", 'params' => array($param), 'types' => $types, 'executionMS' => 1)
9294
);
9395
$c = $this->createCollector($queries);
9496
$c->collect(new Request(), new Response());
9597
$c = unserialize(serialize($c));
9698

9799
$collected_queries = $c->getQueries();
98100
$this->assertEquals($expected, $collected_queries['default'][0]['params'][0]);
101+
$this->assertEquals($explainable, $collected_queries['default'][0]['explainable']);
99102
}
100103

101104
public function paramProvider()
102105
{
103106
return array(
104-
array('some value', 'some value'),
105-
array(1, '1'),
106-
array(true, 'true'),
107-
array(null, 'null'),
108-
array(new \stdClass(), 'Object(stdClass)'),
109-
array(fopen(__FILE__, 'r'), 'Resource(stream)'),
110-
array(new \SplFileInfo(__FILE__), 'Object(SplFileInfo)'),
107+
array('some value', array(), 'some value', true),
108+
array(1, array(), 1, true),
109+
array(true, array(), true, true),
110+
array(null, array(), null, true),
111+
array(new \DateTime('2011-09-11'), array('date'), '2011-09-11', true),
112+
array(fopen(__FILE__, 'r'), array(), 'Resource(stream)', false),
113+
array(new \SplFileInfo(__FILE__), array(), 'Object(SplFileInfo)', false),
111114
);
112115
}
113116

114117
private function createCollector($queries)
115118
{
119+
$connection = $this->getMockBuilder('Doctrine\DBAL\Connection')
120+
->disableOriginalConstructor()
121+
->getMock();
122+
$connection->expects($this->any())
123+
->method('getDatabasePlatform')
124+
->will($this->returnValue(new MySqlPlatform()));
125+
116126
$registry = $this->getMock('Doctrine\Common\Persistence\ManagerRegistry');
117127
$registry
118128
->expects($this->any())
@@ -122,6 +132,9 @@ private function createCollector($queries)
122132
->expects($this->any())
123133
->method('getManagerNames')
124134
->will($this->returnValue(array('default' => 'doctrine.orm.default_entity_manager')));
135+
$registry->expects($this->any())
136+
->method('getConnection')
137+
->will($this->returnValue($connection));
125138

126139
$logger = $this->getMock('Doctrine\DBAL\Logging\DebugStack');
127140
$logger->queries = $queries;

0 commit comments

Comments
 (0)