diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index 8452590d63dad..81bf186e5593a 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -42,12 +42,12 @@ class ExceptionCaster public static function castError(\Error $e, array $a, Stub $stub, $isNested, $filter = 0) { - return self::filterExceptionArray($a, "\0Error\0", $filter); + return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); } public static function castException(\Exception $e, array $a, Stub $stub, $isNested, $filter = 0) { - return self::filterExceptionArray($a, "\0Exception\0", $filter); + return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); } public static function castErrorException(\ErrorException $e, array $a, Stub $stub, $isNested) @@ -64,24 +64,133 @@ public static function castThrowingCasterException(ThrowingCasterException $e, a $prefix = Caster::PREFIX_PROTECTED; $xPrefix = "\0Exception\0"; - if (isset($a[$xPrefix.'previous'], $a[$xPrefix.'trace'][0])) { + if (isset($a[$xPrefix.'previous'], $a[$xPrefix.'trace'])) { $b = (array) $a[$xPrefix.'previous']; - $b[$xPrefix.'trace'][0] += array( + array_unshift($b[$xPrefix.'trace'], array( + 'function' => 'new '.get_class($a[$xPrefix.'previous']), '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)); + } + + unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); + + return $a; + } + + public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $isNested) + { + if (!$isNested) { + return $a; + } + $stub->class = ''; + $stub->handle = 0; + $frames = $trace->value; + + $a = array(); + $j = count($frames); + if (0 > $i = $trace->offset) { + $i = max(0, $j + $i); + } + if (!isset($trace->value[$i])) { + return array(); + } + $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) { + $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( + array( + 'object' => isset($frames[$i]['object']) ? $frames[$i]['object'] : null, + 'class' => isset($frames[$i]['class']) ? $frames[$i]['class'] : null, + '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 ); - array_splice($b[$xPrefix.'trace'], -1 - count($a[$xPrefix.'trace'])); - static::filterTrace($b[$xPrefix.'trace'], false); - $a[Caster::PREFIX_VIRTUAL.'trace'] = $b[$xPrefix.'trace']; + + $lastCall = ' ==> '.$call; } + $a[Caster::PREFIX_VIRTUAL.$j.'. {main}'.$lastCall] = new FrameStub( + array( + 'object' => null, + 'class' => null, + 'type' => null, + 'function' => '{main}', + ) + $frames[$i - 1], + $trace->srcContext, + $trace->keepArgs, + true + ); + if (null !== $trace->length) { + $a = array_slice($a, 0, $trace->length, true); + } + + return $a; + } - unset($a[$xPrefix.'trace'], $a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); + public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $isNested) + { + if (!$isNested) { + return $a; + } + $f = $frame->value; + $prefix = Caster::PREFIX_VIRTUAL; + + if (isset($f['file'], $f['line'])) { + if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { + $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 (!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())); + + try { + $templateName = $template->getTemplateName(); + $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); + } + } catch (\Twig_Error_Loader $e) { + } + } + } else { + $src[$f['file']] = $f['line']; + } + $a[$prefix.'src'] = new EnumStub($src); + } + + unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); + if ($frame->inTraceStub) { + unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']); + } + foreach ($a as $k => $v) { + if (!$v) { + unset($a[$k]); + } + } + if ($frame->keepArgs && isset($f['args'])) { + $a[$prefix.'args'] = $f['args']; + } return $a; } + /** + * @deprecated since 2.8, to be removed in 3.0. Use the castTraceStub method instead. + */ public static function filterTrace(&$trace, $dumpArgs, $offset = 0) { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use the castTraceStub method instead.', E_USER_DEPRECATED); + if (0 > $offset || empty($trace[$offset])) { return $trace = null; } @@ -111,7 +220,7 @@ public static function filterTrace(&$trace, $dumpArgs, $offset = 0) } } - private static function filterExceptionArray(array $a, $xPrefix, $filter) + private static function filterExceptionArray($xClass, array $a, $xPrefix, $filter) { if (isset($a[$xPrefix.'trace'])) { $trace = $a[$xPrefix.'trace']; @@ -121,11 +230,12 @@ private static function filterExceptionArray(array $a, $xPrefix, $filter) } if (!($filter & Caster::EXCLUDE_VERBOSE)) { - static::filterTrace($trace, static::$traceArgs); - - if (null !== $trace) { - $a[$xPrefix.'trace'] = $trace; - } + array_unshift($trace, array( + 'function' => $xClass ? 'new '.$xClass : null, + 'file' => $a[Caster::PREFIX_PROTECTED.'file'], + 'line' => $a[Caster::PREFIX_PROTECTED.'line'], + )); + $a[$xPrefix.'trace'] = new TraceStub($trace); } if (empty($a[$xPrefix.'previous'])) { unset($a[$xPrefix.'previous']); @@ -134,4 +244,18 @@ private static function filterExceptionArray(array $a, $xPrefix, $filter) return $a; } + + private static function extractSource(array $srcArray, $line, $srcContext) + { + $src = ''; + + for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { + $src .= (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n"; + } + if (!$srcContext) { + $src = trim($src); + } + + return $src; + } } diff --git a/src/Symfony/Component/VarDumper/Caster/FrameStub.php b/src/Symfony/Component/VarDumper/Caster/FrameStub.php new file mode 100644 index 0000000000000..58686f9dbe135 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/FrameStub.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +/** + * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace(). + * + * @author Nicolas Grekas
+ */ +class FrameStub extends EnumStub +{ + public $srcContext; + public $keepArgs; + public $inTraceStub; + + public function __construct(array $trace, $srcContext = 1, $keepArgs = true, $inTraceStub = false) + { + $this->value = $trace; + $this->srcContext = $srcContext; + $this->keepArgs = $keepArgs; + $this->inTraceStub = $inTraceStub; + } +} diff --git a/src/Symfony/Component/VarDumper/Caster/TraceStub.php b/src/Symfony/Component/VarDumper/Caster/TraceStub.php new file mode 100644 index 0000000000000..98d6d892b9324 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/TraceStub.php @@ -0,0 +1,36 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Caster; + +use Symfony\Component\VarDumper\Cloner\Stub; + +/** + * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace(). + * + * @author Nicolas Grekas
+ */
+class TraceStub extends Stub
+{
+ public $srcContext;
+ public $keepArgs;
+ public $offset;
+ public $length;
+
+ public function __construct(array $trace, $srcContext = 1, $keepArgs = true, $offset = 0, $length = null)
+ {
+ $this->value = $trace;
+ $this->srcContext = $srcContext;
+ $this->keepArgs = $keepArgs;
+ $this->offset = $offset;
+ $this->length = $length;
+ }
+}
diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
index c8f9eb6258498..c4fc2e0119e23 100644
--- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
+++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
@@ -69,6 +69,8 @@ abstract class AbstractCloner implements ClonerInterface
'Error' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castError',
'Symfony\Component\DependencyInjection\ContainerInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException',
+ 'Symfony\Component\VarDumper\Caster\TraceStub' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castTraceStub',
+ 'Symfony\Component\VarDumper\Caster\FrameStub' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castFrameStub',
'PHPUnit_Framework_MockObject_MockObject' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
'Prophecy\Prophecy\ProphecySubjectInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php
index cfb2c6c464192..5f208d87d9ab7 100644
--- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php
+++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php
@@ -170,6 +170,9 @@ public function testThrowingCaster()
{
$out = fopen('php://memory', 'r+b');
+ require_once __DIR__.'/Fixtures/Twig.php';
+ $twig = new \__TwigTemplate_VarDumperFixture_u75a09(new \Twig_Environment(new \Twig_Loader_Filesystem()));
+
$dumper = new CliDumper();
$dumper->setColors(false);
$cloner = new VarCloner();
@@ -181,12 +184,15 @@ public function testThrowingCaster()
},
));
$cloner->addCasters(array(
- ':stream' => function () {
- throw new \Exception('Foobar');
- },
+ ':stream' => eval('return function () use ($twig) {
+ try {
+ $twig->render(array());
+ } catch (\Twig_Error_Runtime $e) {
+ throw $e->getPrevious();
+ }
+ };'),
));
- $line = __LINE__ - 3;
- $file = __FILE__;
+ $line = __LINE__ - 2;
$ref = (int) $out;
$data = $cloner->cloneVar($out);
@@ -194,6 +200,19 @@ public function testThrowingCaster()
rewind($out);
$out = stream_get_contents($out);
+ if (method_exists($twig, 'getSource')) {
+ $twig = << 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:Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.