8000 minor #20675 [VarDumper][HttpKernel] Enhance perf of ExceptionCaster … · symfony/symfony@ee4ae55 · GitHub
[go: up one dir, main page]

Skip to content

Commit ee4ae55

Browse files
committed
minor #20675 [VarDumper][HttpKernel] Enhance perf of ExceptionCaster & DataCollector (nicolas-grekas)
This PR was merged into the 3.2 branch. Discussion ---------- [VarDumper][HttpKernel] Enhance perf of ExceptionCaster & DataCollector | Q | A | ------------- | --- | Branch? | 3.2 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - In **dev** on 3.2 , serializing collectors' data is slow because VarDumper is called so many times. Here is a PR to make it a bit faster, with its Blackfire profile: https://blackfire.io/profiles/compare/6f0fdc7a-9157-4dad-bee4-4c98a96184b2/graph Note that it is possible to make things fast again by replacing these multiple calls by a single one juste before serializing the data. Yet, it's not trivial and VarDumper misses a few features to allow dealing with an unserialized Data object and make it look like a real data structure to other code. I'll look at it for 3.3. Commits ------- be2b7df [VarDumper][HttpKernel] Enhance perf of ExceptionCaster & DataCollector
2 parents cb03103 + be2b7df commit ee4ae55

File tree

7 files changed

+76
-53
lines changed

7 files changed

+76
-53
lines changed

src/Symfony/Component/HttpKernel/DataCollector/DataCollector.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ abstract class DataCollector implements DataCollectorInterface, \Serializable
4242
*/
4343
private $cloner;
4444

45+
private static $stubsCache = array();
46+
4547
public function serialize()
4648
{
4749
return serialize($this->data);
@@ -124,14 +126,17 @@ private function decorateVar($var)
124126
return $var;
125127
}
126128
if (is_string($var)) {
129+
if (isset(self::$stubsCache[$var])) {
130+
return self::$stubsCache[$var];
131+
}
127132
if (false !== strpos($var, '\\')) {
128133
$c = (false !== $i = strpos($var, '::')) ? substr($var, 0, $i) : $var;
129134
if (class_exists($c, false) || interface_exists($c, false) || trait_exists($c, false)) {
130-
return new ClassStub($var);
135+
return self::$stubsCache[$var] = new ClassStub($var);
131136
}
132137
}
133138
if (false !== strpos($var, DIRECTORY_SEPARATOR) && false === strpos($var, '://') && false === strpos($var, "\0") && is_file($var)) {
134-
return new LinkStub($var);
139+
return self::$stubsCache[$var] = new LinkStub($var);
135140
}
136141
}
137142

src/Symfony/Component/VarDumper/Caster/Caster.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,20 @@ public static function castObject($obj, \ReflectionClass $reflector)
5757
}
5858

5959
if ($a) {
60+
$combine = false;
6061
$p = array_keys($a);
6162
foreach ($p as $i => $k) {
6263
if (isset($k[0]) && "\0" !== $k[0] && !$reflector->hasProperty($k)) {
64+
$combine = true;
6365
$p[$i] = self::PREFIX_DYNAMIC.$k;
6466
} elseif (isset($k[16]) && "\0" === $k[16] && 0 === strpos($k, "\0class@anonymous\0")) {
67+
$combine = true;
6568
$p[$i] = "\0".$reflector->getParentClass().'@anonymous'.strrchr($k, "\0");
6669
}
6770
}
68-
$a = array_combine($p, $a);
71+
if ($combine) {
72+
$a = array_combine($p, $a);
73+
}
6974
}
7075

7176
return $a;

src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class ExceptionCaster
4141
E_STRICT => 'E_STRICT',
4242
);
4343

44+
private static $framesCache = array();
45+
4446
public static function castError(\Error $e, array $a, Stub $stub, $isNested, $filter = 0)
4547
{
4648
return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter);
@@ -142,43 +144,52 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is
142144
$prefix = Caster::PREFIX_VIRTUAL;
143145

144146
if (isset($f['file'], $f['line'])) {
145-
if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) {
146-
$f['file'] = substr($f['file'], 0, -strlen($match[0]));
147-
$f['line'] = (int) $match[1];
148-
}
149-
$caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null;
150-
$src = $f['line'];
151-
$srcKey = $f['file'];
152-
$ellipsis = explode(DIRECTORY_SEPARATOR, $srcKey);
153-
$ellipsis = 3 < count($ellipsis) ? 2 + strlen(implode(array_slice($ellipsis, -2))) : 0;
154-
155-
if (file_exists($f['file']) && 0 <= self::$srcContext) {
156-
if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) {
157-
$template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', strlen($f['class']), $f['class']));
158-
159-
$ellipsis = 0;
160-
$templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
161-
$templateInfo = $template->getDebugInfo();
162-
if (isset($templateInfo[$f['line']])) {
163-
$templatePath = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null;
164-
165-
if ($templateSrc) {
166-
$templateSrc = explode("\n", $templateSrc);
167-
$src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath);
168-
$srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']];
147+
$cacheKey = $f;
148+
unset($cacheKey['object'], $cacheKey['args']);
149+
$cacheKey[] = self::$srcContext;
150+
$cacheKey = implode('-', $cacheKey);
151+
152+
if (isset(self::$framesCache[$cacheKey])) {
153+
$a[$prefix.'src'] = self::$framesCache[$cacheKey];
154+
} else {
155+
if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) {
156+
$f['file'] = substr($f['file'], 0, -strlen($match[0]));
157+
$f['line'] = (int) $match[1];
158+
}
159+
$caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null;
160+
$src = $f['line'];
161+
$srcKey = $f['file'];
162+
$ellipsis = explode(DIRECTORY_SEPARATOR, $srcKey);
163+
$ellipsis = 3 < count($ellipsis) ? 2 + strlen(implode(array_slice($ellipsis, -2))) : 0;
164+
165+
if (file_exists($f['file']) && 0 <= self::$srcContext) {
166+
if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) {
167+
$template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', strlen($f['class']), $f['class']));
168+
169+
$ellipsis = 0;
170+
$templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
171+
$templateInfo = $template->getDebugInfo();
172+
if (isset($templateInfo[$f['line']])) {
173+
if (!method_exists($template, 'getSourceContext') || !file_exists($templatePath = $template->getSourceContext()->getPath())) {
174+
$templatePath = null;
175+
}
176+
if ($templateSrc) {
177+
$src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath);
178+
$srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']];
179+
}
169180
}
170181
}
171-
}
172-
if ($srcKey == $f['file']) {
173-
$src = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext, $caller, 'php', $f['file']);
174-
$srcKey .= ':'.$f['line'];
175-
if ($ellipsis) {
176-
$ellipsis += 1 + strlen($f['line']);
182+
if ($srcKey == $f['file']) {
183+
$src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, $caller, 'php', $f['file']);
184+
$srcKey .= ':'.$f['line'];
185+
if ($ellipsis) {
186+
$ellipsis += 1 + strlen($f['line']);
187+
179B }
177188
}
178189
}
190+
$srcAttr = $ellipsis ? 'ellipsis='.$ellipsis : '';
191+
self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src));
179192
}
180-
$srcAttr = $ellipsis ? 'ellipsis='.$ellipsis : '';
181-
$a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src));
182193
}
183194

184195
unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']);
@@ -232,14 +243,16 @@ private static function traceUnshift(&$trace, $class, $file, $line)
232243
));
233244
}
234245

235-
private static function extractSource(array $srcArray, $line, $srcContext, $title, $lang, $file = null)
246+
private static function extractSource($srcLines, $line, $srcContext, $title, $lang, $file = null)
236247
{
248+
$srcLines = explode("\n", $srcLines);
237249
$src = array();
238250

239251
for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) {
240-
$src[] = (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n";
252+
$src[] = (isset($srcLines[$i]) ? $srcLines[$i] : '')."\n";
241253
}
242254

255+
$srcLines = array();
243256
$ltrim = 0;
244257
do {
245258
$pad = null;
@@ -257,7 +270,6 @@ private static function extractSource(array $srcArray, $line, $srcContext, $titl
257270
} while (0 > $i && null !== $pad);
258271

259272
--$ltrim;
260-
$srcArray = array();
261273

262274
foreach ($src as $i => $c) {
263275
if ($ltrim) {
@@ -274,9 +286,9 @@ private static function extractSource(array $srcArray, $line, $srcContext, $titl
274286
}
275287
}
276288
$c->attr['lang'] = $lang;
277-
$srcArray[sprintf("\0~%d\0", $i + $line - $srcContext)] = $c;
289+
$srcLines[sprintf("\0~%d\0", $i + $line - $srcContext)] = $c;
278290
}
279291

280-
return new EnumStub($srcArray);
292+
return new EnumStub($srcLines);
281293
}
282294
}

src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,14 +252,15 @@ protected function castObject(Stub $stub, $isNested)
252252
new \ReflectionClass($class),
253253
array_reverse(array($class => $class) + class_parents($class) + class_implements($class) + array('*' => '*')),
254254
);
255+
$classInfo[1] = array_map('strtolower', $classInfo[1]);
255256

256257
$this->classInfo[$class] = $classInfo;
257258
}
258259

259-
$a = $this->callCaster('Symfony\Component\VarDumper\Caster\Caster::castObject', $obj, $classInfo[0], null, $isNested);
260+
$a = Caster::castObject($obj, $classInfo[0]);
260261

261262
foreach ($classInfo[1] as $p) {
262-
if (!empty($this->casters[$p = strtolower($p)])) {
263+
if (!empty($this->casters[$p])) {
263264
foreach ($this->casters[$p] as $p) {
264265
$a = $this->callCaster($p, $obj, $a, $stub, $isNested);
265266
}

src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,14 +179,14 @@ public function testFrameWithTwig()
179179
$f = array(
180180
new FrameStub(array(
181181
'file' => dirname(__DIR__).'/Fixtures/Twig.php',
182-
'line' => 21,
182+
'line' => 20,
183183
'class' => '__TwigTemplate_VarDumperFixture_u75a09',
184184
)),
185185
new FrameStub(array(
186186
'file' => dirname(__DIR__).'/Fixtures/Twig.php',
187187
'line' => 21,
188188
'class' => '__TwigTemplate_VarDumperFixture_u75a09',
189-
'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, false),
189+
'object' => new \__TwigTemplate_VarDumperFixture_u75a09(null, __FILE__),
190190
)),
191191
);
192192

@@ -195,10 +195,10 @@ public function testFrameWithTwig()
195195
0 => {
196196
class: "__TwigTemplate_VarDumperFixture_u75a09"
197197
src: {
198-
bar.twig:2: {
198+
%sTwig.php:1: {
199+
:
199200
: foo bar
200201
: twig source
201-
:
202202
}
203203
}
204204
}
@@ -208,7 +208,7 @@ class: "__TwigTemplate_VarDumperFixture_u75a09"
208208
%A
209209
}
210210
src: {
211-
foo.twig:2: {
211+
%sExceptionCasterTest.php:2: {
212212
: foo bar
213213
: twig source
214214
:

src/Symfony/Component/VarDumper/Tests/CliDumperTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ public function testThrowingCaster()
271271
⚠: Symfony\Component\VarDumper\Exception\ThrowingCasterException {{$r}
272272
#message: "Unexpected Exception thrown from a caster: Foobar"
273273
-trace: {
274-
bar.twig:%d: {
274+
%sTwig.php:2: {
275275
: foo bar
276276
: twig source
277277
:

src/Symfony/Component/VarDumper/Tests/Fixtures/Twig.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
/* foo.twig */
44
class __TwigTemplate_VarDumperFixture_u75a09 extends Twig_Template
55
{
6-
private $filename;
6+
private $path;
77

8-
public function __construct(Twig_Environment $env = null, $filename = null)
8+
public function __construct(Twig_Environment $env = null, $path = null)
99
{
1010
if (null !== $env) {
1111
parent::__construct($env);
1212
}
1313
$this->parent = false;
1414
$this->blocks = array();
15-
$this->filename = $filename;
15+
$this->path = $path;
1616
}
1717

1818
protected function doDisplay(array $context, array $blocks = array())
@@ -28,11 +28,11 @@ public function getTemplateName()
2828

2929
public function getDebugInfo()
3030
{
31-
return array(21 => 2);
31+
return array(20 => 1, 21 => 2);
3232
}
3333

3434
public function getSourceContext()
3535
{
36-
return new Twig_Source(" foo bar\n twig source\n\n", 'foo.twig', false === $this->filename ? null : ($this->filename ?: 'bar.twig'));
36+
return new Twig_Source(" foo bar\n twig source\n\n", 'foo.twig', $this->path ?: __FILE__);
3737
}
3838
}

0 commit comments

Comments
 (0)
0