diff --git a/src/Symfony/Component/VarDumper/Caster/ArgsStub.php b/src/Symfony/Component/VarDumper/Caster/ArgsStub.php new file mode 100644 index 0000000000000..20e3f92791f55 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Caster/ArgsStub.php @@ -0,0 +1,81 @@ + + * + * 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 list of function arguments. + * + * @author Nicolas Grekas + */ +class ArgsStub extends EnumStub +{ + private static $parameters = array(); + + public function __construct(array $values, $function, $class) + { + list($variadic, $params) = self::getParameters($function, $class); + + foreach ($values as $k => $v) { + if (!is_scalar($v) && !$v instanceof Stub) { + $values[$k] = new CutStub($v); + } + } + if (null === $params) { + parent::__construct($values, false); + + return; + } + if (count($values) < count($params)) { + $params = array_slice($params, 0, count($values)); + } elseif (count($values) > count($params)) { + $values[] = new EnumStub(array_splice($values, count($params)), false); + $params[] = $variadic; + } + if (array('...') === $params) { + $this->dumpKeys = false; + $this->value = $values[0]->value; + } else { + $this->value = array_combine($params, $values); + } + } + + private static function getParameters($function, $class) + { + if (isset(self::$parameters[$k = $class.'::'.$function])) { + return self::$parameters[$k]; + } + + try { + $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); + } catch (\ReflectionException $e) { + return array(null, null); + } + + $variadic = '...'; + $params = array(); + foreach ($r->getParameters() as $v) { + $k = '$'.$v->name; + if ($v->isPassedByReference()) { + $k = '&'.$k; + } + if (method_exists($v, 'isVariadic') && $v->isVariadic()) { + $variadic .= $k; + } else { + $params[] = $k; + } + } + + return self::$parameters[$k] = array($variadic, $params); + } +} diff --git a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php index 4765fbf6db907..eaa007322a6cd 100644 --- a/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php +++ b/src/Symfony/Component/VarDumper/Caster/ExceptionCaster.php @@ -67,11 +67,7 @@ public static function castThrowingCasterException(ThrowingCasterException $e, a if (isset($a[$xPrefix.'previous'], $a[$xPrefix.'trace'])) { $b = (array) $a[$xPrefix.'previous']; - array_unshift($b[$xPrefix.'trace'], array( - 'function' => 'new '.get_class($a[$xPrefix.'previous']), - 'file' => $b[$prefix.'file'], - 'line' => $b[$prefix.'line'], - )); + self::traceUnshift($b[$xPrefix.'trace'], get_class($a[$xPrefix.'previous']), $b[$prefix.'file'], $b[$prefix.'line']); $a[$xPrefix.'trace'] = new TraceStub($b[$xPrefix.'trace'], false, 0, -1 - count($a[$xPrefix.'trace']->value)); } @@ -121,8 +117,8 @@ public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $is foreach ($f[$prefix.'src']->value as $label => $frame) { $label = substr_replace($label, "title=Stack level $j.&", 2, 0); } - if (isset($f[$prefix.'args']) && $frame instanceof EnumStub) { - $frame->value['args'] = $f[$prefix.'args']; + if (isset($f[$prefix.'arguments']) && $frame instanceof EnumStub) { + $frame->value['arguments'] = $f[$prefix.'arguments']; } } $a[$label] = $frame; @@ -192,8 +188,8 @@ public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, $is unset($a[$k]); } } - if ($frame->keepArgs && isset($f['args'])) { - $a[$prefix.'args'] = new EnumStub($f['args'], false); + if ($frame->keepArgs && !empty($f['args'])) { + $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']); } return $a; @@ -209,11 +205,7 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte } if (!($filter & Caster::EXCLUDE_VERBOSE)) { - array_unshift($trace, array( - 'function' => $xClass ? 'new '.$xClass : null, - 'file' => $a[Caster::PREFIX_PROTECTED.'file'], - 'line' => $a[Caster::PREFIX_PROTECTED.'line'], - )); + self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); $a[$xPrefix.'trace'] = new TraceStub($trace, self::$traceArgs); } if (empty($a[$xPrefix.'previous'])) { @@ -224,6 +216,18 @@ private static function filterExceptionArray($xClass, array $a, $xPrefix, $filte return $a; } + private static function traceUnshift(&$trace, $class, $file, $line) + { + if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { + return; + } + array_unshift($trace, array( + 'function' => $class ? 'new '.$class : null, + 'file' => $file, + 'line' => $line, + )); + } + private static function extractSource(array $srcArray, $line, $srcContext, $title, $lang, $file = null) { $src = array(); diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php index 72f2fa8223a6e..6a41910dcf022 100644 --- a/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php +++ b/src/Symfony/Component/VarDumper/Tests/Caster/ExceptionCasterTest.php @@ -33,7 +33,7 @@ protected function tearDown() public function testDefaultSettings() { - $e = $this->getTestException(1); + $e = $this->getTestException($this); $expectedDump = <<<'EODUMP' Exception { @@ -49,10 +49,10 @@ public function testDefaultSettings() } %sExceptionCasterTest.php:%d: { : { - : $e = $this->getTestException(1); + : $e = $this->getTestException($this); : - args: { - 1 + arguments: { + Symfony\Component\VarDumper\Tests\Caster\ExceptionCasterTest {#1 …} } } %A @@ -76,7 +76,7 @@ public function testSeek() : { : $e = $this->getTestException(2); : - args: { + arguments: { 2 } } diff --git a/src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php b/src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php new file mode 100644 index 0000000000000..88aa419de8497 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Caster/StubCasterTest.php @@ -0,0 +1,83 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\VarDumper\Tests\Caster; + +use Symfony\Component\VarDumper\Caster\ArgsStub; +use Symfony\Component\VarDumper\Test\VarDumperTestTrait; + +class StubCasterTest extends \PHPUnit_Framework_TestCase +{ + use VarDumperTestTrait; + + public function testArgsStubWithDefaults($foo = 234, $bar = 456) + { + $args = array(new ArgsStub(array(123), __FUNCTION__, __CLASS__)); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + $foo: 123 + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + public function testArgsStubWithExtraArgs($foo = 234) + { + $args = array(new ArgsStub(array(123, 456), __FUNCTION__, __CLASS__)); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + $foo: 123 + ...: { + 456 + } + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + public function testArgsStubNoParamWithExtraArgs() + { + $args = array(new ArgsStub(array(123), __FUNCTION__, __CLASS__)); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + 123 + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } + + public function testArgsStubWithClosure() + { + $args = array(new ArgsStub(array(123), '{closure}', null)); + + $expectedDump = <<<'EODUMP' +array:1 [ + 0 => { + 123 + } +] +EODUMP; + + $this->assertDumpMatchesFormat($expectedDump, $args); + } +}