diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index 81bf186e5593a..51c70dd5db9a8 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -21,6 +21,7 @@ */ class ExceptionCaster { + public static $srcContext = 1; public static $traceArgs = true; public static $errorTypes = array( E_DEPRECATED => 'E_DEPRECATED', @@ -71,7 +72,7 @@ public static function castThrowingCasterException(ThrowingCasterException $e, a 'file' => $b[$prefix.'file'], 'line' => $b[$prefix.'line'], )); - $a[$xPrefix.'trace'] = new TraceStub($b[$xPrefix.'trace'], 1, false, 0, -1 - count($a[$xPrefix.'trace']->value)); + $a[$xPrefix.'trace'] = new TraceStub($b[$xPrefix.'trace'], false, 0, -1 - count($a[$xPrefix.'trace']->value)); } unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); @@ -90,7 +91,7 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is $a = array(); $j = count($frames); - if (0 > $i = $trace->offset) { + if (0 > $i = $trace->sliceOffset) { $i = max(0, $j + $i); } if (!isset($trace->value[$i])) { @@ -98,7 +99,7 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is } $lastCall = isset($frames[$i]['function']) ? ' ==> '.(isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; - for ($j -= $i++; isset($frames[$i]); ++$i, --$j) { + for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { $call = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[$i]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '???'; $a[Caster::PREFIX_VIRTUAL.$j.'. '.$call.$lastCall] = new FrameStub( @@ -108,7 +109,6 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is 'type' => isset($frames[$i]['type']) ? $frames[$i]['type'] : null, 'function' => isset($frames[$i]['function']) ? $frames[$i]['function'] : null, ) + $frames[$i - 1], - $trace->srcContext, $trace->keepArgs, true ); @@ -122,12 +122,11 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is 'type' => null, 'function' => '{main}', ) + $frames[$i - 1], - $trace->srcContext, $trace->keepArgs, true ); - if (null !== $trace->length) { - $a = array_slice($a, 0, $trace->length, true); + if (null !== $trace->sliceLength) { + $a = array_slice($a, 0, $trace->sliceLength, true); } return $a; @@ -146,8 +145,8 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is $f['file'] = substr($f['file'], 0, -strlen($match[0])); $f['line'] = (int) $match[1]; } - if (file_exists($f['file']) && 0 <= $frame->srcContext) { - $src[$f['file'].':'.$f['line']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], $frame->srcContext); + if (file_exists($f['file']) && 0 <= self::$srcContext) { + $src[$f['file'].':'.$f['line']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext); if (!empty($f['class']) && is_subclass_of($f['class'], 'Twig_Template') && method_exists($f['class'], 'getDebugInfo')) { $template = isset($f['object']) ? $f['object'] : new $f['class'](new \Twig_Environment(new \Twig_Loader_Filesystem())); @@ -157,7 +156,7 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is $templateSrc = explode("\n", method_exists($template, 'getSource') ? $template->getSource() : $template->getEnvironment()->getLoader()->getSource($templateName)); $templateInfo = $template->getDebugInfo(); if (isset($templateInfo[$f['line']])) { - $src[$templateName.':'.$templateInfo[$f['line']]] = self::extractSource($templateSrc, $templateInfo[$f['line']], $frame->srcContext); + $src[$templateName.':'.$templateInfo[$f['line']]] = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext); } } catch (\Twig_Error_Loader $e) { } @@ -247,15 +246,29 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte private static function extractSource(array $srcArray, $line, $srcContext) { - $src = ''; + $src = array(); for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { - $src .= (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n"; + $src[] = (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n"; } - if (!$srcContext) { - $src = trim($src); + + $ltrim = 0; + while (' ' === $src[0][$ltrim] || "\t" === $src[0][$ltrim]) { + $i = $srcContext << 1; + while ($i > 0 && $src[0][$ltrim] === $src[$i][$ltrim]) { + --$i; + } + if ($i) { + break; + } + ++$ltrim; + } + if ($ltrim) { + foreach ($src as $i => $line) { + $src[$i] = substr($line, $ltrim); + } } - return $src; + return implode('', $src); } } diff --git a/src/Symfony/Component/VarDumper/Caster/FrameStub.php b/src/Symfony/Component/VarDumper/Caster/FrameStub.php index 58686f9dbe135..1e1194dc85b89 100644 --- a/src/Symfony/Component/VarDumper/Caster/FrameStub.php +++ b/src/Symfony/Component/VarDumper/Caster/FrameStub.php @@ -18,14 +18,12 @@ */ class FrameStub extends EnumStub { - public $srcContext; public $keepArgs; public $inTraceStub; - public function __construct(array $trace, $srcContext = 1, $keepArgs = true, $inTraceStub = false) + public function __construct(array $frame, $keepArgs = true, $inTraceStub = false) { - $this->value = $trace; - $this->srcContext = $srcContext; + $this->value = $frame; $this->keepArgs = $keepArgs; $this->inTraceStub = $inTraceStub; } diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php index 1d83c335445c4..3352c87b7f1f6 100644 --- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php @@ -74,6 +74,59 @@ public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested) return $a; } + public static function castGenerator(\Generator $c, array $a, Stub $stub, $isNested) + { + return class_exists('ReflectionGenerator', false) ? self::castReflectionGenerator(new \ReflectionGenerator($c), $a, $stub, $isNested) : $a; + } + + public static function castType(\ReflectionType $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + $a += array( + $prefix.'type' => $c->__toString(), + $prefix.'allowsNull' => $c->allowsNull(), + $prefix.'isBuiltin' => $c->isBuiltin(), + ); + + return $a; + } + + public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, $isNested) + { + $prefix = Caster::PREFIX_VIRTUAL; + + if ($c->getThis()) { + $a[$prefix.'this'] = new CutStub($c->getThis()); + } + $x = $c->getFunction(); + $frame = array( + 'class' => isset($x->class) ? $x->class : null, + 'type' => isset($x->class) ? ($x->isStatic() ? '::' : '->') : null, + 'function' => $x->name, + 'file' => $c->getExecutingFile(), + 'line' => $c->getExecutingLine(), + ); + if ($trace = $c->getTrace(DEBUG_BACKTRACE_IGNORE_ARGS)) { + $x = new \ReflectionGenerator($c->getExecutingGenerator()); + array_unshift($trace, array( + 'function' => 'yield', + 'file' => $x->getExecutingFile(), + 'line' => $x->getExecutingLine() - 1, + )); + $trace[] = $frame; + $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); + } else { + $x = new FrameStub($frame, false, true); + $x = ExceptionCaster::castFrameStub($x, array(), $x, true); + $a[$prefix.'executing'] = new EnumStub(array( + $frame['class'].$frame['type'].$frame['function'].'()' => $x[$prefix.'src'], + )); + } + + return $a; + } + public static function castClass(\ReflectionClass $c, array $a, Stub $stub, $isNested, $filter = 0) { $prefix = Caster::PREFIX_VIRTUAL; diff --git a/src/Symfony/Component/VarDumper/Caster/StubCaster.php b/src/Symfony/Component/VarDumper/Caster/StubCaster.php index 0b90358726a3a..ebad5ba9844e4 100644 --- a/src/Symfony/Component/VarDumper/Caster/StubCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/StubCaster.php @@ -54,6 +54,7 @@ public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested) if ($isNested) { $stub->class = ''; $stub->handle = 0; + $stub->value = null; $a = array(); diff --git a/src/Symfony/Component/VarDumper/Caster/TraceStub.php b/src/Symfony/Component/VarDumper/Caster/TraceStub.php index 98d6d892b9324..59548acaee61c 100644 --- a/src/Symfony/Component/VarDumper/Caster/TraceStub.php +++ b/src/Symfony/Component/VarDumper/Caster/TraceStub.php @@ -20,17 +20,17 @@ */ class TraceStub extends Stub { - public $srcContext; public $keepArgs; - public $offset; - public $length; + public $sliceOffset; + public $sliceLength; + public $numberingOffset; - public function __construct(array $trace, $srcContext = 1, $keepArgs = true, $offset = 0, $length = null) + public function __construct(array $trace, $keepArgs = true, $sliceOffset = 0, $sliceLength = null, $numberingOffset = 0) { $this->value = $trace; - $this->srcContext = $srcContext; $this->keepArgs = $keepArgs; - $this->offset = $offset; - $this->length = $length; + $this->sliceOffset = $sliceOffset; + $this->sliceLength = $sliceLength; + $this->numberingOffset = $numberingOffset; } } diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php index 08bf7d33b3743..afb7094a32b65 100644 --- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php @@ -28,6 +28,9 @@ abstract class AbstractCloner implements ClonerInterface 'Symfony\Component\VarDumper\Caster\EnumStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castEnum', 'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure', + 'Generator' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castGenerator', + 'ReflectionType' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castType', + 'ReflectionGenerator' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflectionGenerator', 'ReflectionClass' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClass', 'ReflectionFunctionAbstract' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castFunctionAbstract', 'ReflectionMethod' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castMethod', diff --git a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php index 51309c662b500..139f59eae643e 100644 --- a/src/Symfony/Component/VarDumper/Cloner/VarCloner.php +++ b/src/Symfony/Component/VarDumper/Cloner/VarCloner.php @@ -149,7 +149,7 @@ protected function doClone($var) $stub->handle = $h; $a = $this->castObject($stub, 0 < $i); if ($v !== $stub->value) { - if (Stub::TYPE_OBJECT !== $stub->type) { + if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { break; } if ($useExt) { diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php index e304e15e05b28..dcbbb17898837 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ReflectionCasterTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\VarDumper\Tests\Caster; use Symfony\Component\VarDumper\Test\VarDumperTestCase; +use Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo; /** * @author Nicolas Grekas
@@ -72,7 +73,7 @@ public function testClosureCaster() \$b: & 123 } file: "%sReflectionCasterTest.php" - line: "62 to 62" + line: "63 to 63" } EOTXT , $var @@ -92,11 +93,92 @@ public function testReturnType() returnType: "int" class: "Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest" this: Symfony\Component\VarDumper\Tests\Caster\ReflectionCasterTest { …} - file: "%sReflectionCasterTest.php(87) : eval()'d code" + file: "%sReflectionCasterTest.php(88) : eval()'d code" line: "1 to 1" } EOTXT , $f ); } + + /** + * @requires PHP 7.0 + */ + public function testGenerator() + { + $g = new GeneratorDemo(); + $g = $g->baz(); + $r = new \ReflectionGenerator($g); + + $xDump = <<<'EODUMP' +Generator { + this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …} + executing: { + Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz(): { + %sGeneratorDemo.php:14: """ + {\n + yield from bar();\n + }\n + """ + } + } +} +EODUMP; + + $this->assertDumpMatchesFormat($xDump, $g); + + foreach ($g as $v) { + break; + } + + $xDump = <<<'EODUMP' +array:2 [ + 0 => ReflectionGenerator { + this: Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo { …} + trace: { + 3. Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo() ==> yield(): { + src: { + %sGeneratorDemo.php:9: """ + {\n + yield 1;\n + }\n + """ + } + } + 2. Symfony\Component\VarDumper\Tests\Fixtures\bar() ==> Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo(): { + src: { + %sGeneratorDemo.php:20: """ + {\n + yield from GeneratorDemo::foo();\n + }\n + """ + } + } + 1. Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo->baz() ==> Symfony\Component\VarDumper\Tests\Fixtures\bar(): { + src: { + %sGeneratorDemo.php:14: """ + {\n + yield from bar();\n + }\n + """ + } + } + } + } + 1 => Generator { + executing: { + Symfony\Component\VarDumper\Tests\Fixtures\GeneratorDemo::foo(): { + %sGeneratorDemo.php:10: """ + yield 1;\n + }\n + \n + """ + } + } + } +] +EODUMP; + + $this->assertDumpMatchesFormat($xDump, array($r, $r->getExecutingGenerator())); + } } diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index 66144165bb8bb..9cb606fadcb78 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -232,45 +232,45 @@ public function testThrowingCaster() %d. __TwigTemplate_VarDumperFixture_u75a09->doDisplay() ==> new Exception(): { src: { %sTwig.php:19: """ - // line 2\\n - throw new \Exception('Foobar');\\n - }\\n + // line 2\\n + throw new \Exception('Foobar');\\n + }\\n """ {$twig} } } %d. Twig_Template->displayWithErrorHandling() ==> __TwigTemplate_VarDumperFixture_u75a09->doDisplay(): { src: { %sTemplate.php:%d: """ - try {\\n - \$this->doDisplay(\$context, \$blocks);\\n - } catch (Twig_Error \$e) {\\n + try {\\n + \$this->doDisplay(\$context, \$blocks);\\n + } catch (Twig_Error \$e) {\\n """ } } %d. Twig_Template->display() ==> Twig_Template->displayWithErrorHandling(): { src: { %sTemplate.php:%d: """ - {\\n - \$this->displayWithErrorHandling(\$this->env->mergeGlobals(\$context), array_merge(\$this->blocks, \$blocks));\\n - }\\n + {\\n + \$this->displayWithErrorHandling(\$this->env->mergeGlobals(\$context), array_merge(\$this->blocks, \$blocks));\\n + }\\n """ } } %d. Twig_Template->render() ==> Twig_Template->display(): { src: { %sTemplate.php:%d: """ - try {\\n - \$this->display(\$context);\\n - } catch (Exception \$e) {\\n + try {\\n + \$this->display(\$context);\\n + } catch (Exception \$e) {\\n """ } } %d. %slosure%s() ==> Twig_Template->render(): { src: { %sCliDumperTest.php:{$line}: """ - }\\n - };'),\\n - ));\\n + }\\n + };'),\\n + ));\\n """ } } diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/GeneratorDemo.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/GeneratorDemo.php new file mode 100644 index 0000000000000..e2d24c8c0eac6 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/GeneratorDemo.php @@ -0,0 +1,21 @@ +
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: