diff --git a/composer.json b/composer.json index 97a5dd5b73..07aae60f97 100644 --- a/composer.json +++ b/composer.json @@ -85,10 +85,7 @@ "src/Symfony/Component/HttpFoundation/Resources/stubs", "src/Symfony/Component/Intl/Resources/stubs" ], - "files": [ - "src/Symfony/Component/Intl/Resources/stubs/functions.php", - "src/Symfony/Component/VarDumper/Resources/functions/dump.php" - ] + "files": [ "src/Symfony/Component/Intl/Resources/stubs/functions.php" ] }, "minimum-stability": "dev", "extra": { diff --git a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php index a0e895dd87..2fc02f3302 100644 --- a/src/Symfony/Bridge/Twig/Extension/DumpExtension.php +++ b/src/Symfony/Bridge/Twig/Extension/DumpExtension.php @@ -12,6 +12,8 @@ namespace Symfony\Bridge\Twig\Extension; use Symfony\Bridge\Twig\TokenParser\DumpTokenParser; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Dumper\HtmlDumper; /** * Provides integration of the dump() function with Twig. @@ -20,6 +22,18 @@ use Symfony\Bridge\Twig\TokenParser\DumpTokenParser; */ class DumpExtension extends \Twig_Extension { + public function __construct(ClonerInterface $cloner = null) + { + $this->cloner = $cloner; + } + + public function getFunctions() + { + return array( + new \Twig_SimpleFunction('dump', array($this, 'dump'), array('is_safe' => array('html'), 'needs_context' => true, 'needs_environment' => true)), + ); + } + public function getTokenParsers() { return array(new DumpTokenParser()); @@ -29,4 +43,38 @@ class DumpExtension extends \Twig_Extension { return 'dump'; } + + public function dump(\Twig_Environment $env, $context) + { + if (!$env->isDebug() || !$this->cloner) { + return; + } + + if (2 === func_num_args()) { + $vars = array(); + foreach ($context as $key => $value) { + if (!$value instanceof \Twig_Template) { + $vars[$key] = $value; + } + } + + $vars = array($vars); + } else { + $vars = func_get_args(); + unset($vars[0], $vars[1]); + } + + $html = ''; + $dumper = new HtmlDumper(function ($line, $depth) use (&$html) { + if (-1 !== $depth) { + $html .= str_repeat(' ', $depth).$line."\n"; + } + }); + + foreach ($vars as $value) { + $dumper->dump($this->cloner->cloneVar($value)); + } + + return $html; + } } diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php index 6f2d44aa51..3de9a0e91e 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/DumpExtensionTest.php @@ -13,20 +13,22 @@ namespace Symfony\Bridge\Twig\Tests\Extension; use Symfony\Bridge\Twig\Extension\DumpExtension; use Symfony\Component\VarDumper\VarDumper; +use Symfony\Component\VarDumper\Cloner\PhpCloner; class DumpExtensionTest extends \PHPUnit_Framework_TestCase { /** - * @dataProvider getDumpParams + * @dataProvider getDumpTags */ - public function testDebugDump($template, $debug, $expectedOutput, $expectedDumped) + public function testDumpTag($template, $debug, $expectedOutput, $expectedDumped) { + $extension = new DumpExtension(new PhpCloner()); $twig = new \Twig_Environment(new \Twig_Loader_String(), array( 'debug' => $debug, 'cache' => false, 'optimizations' => 0, )); - $twig->addExtension(new DumpExtension()); + $twig->addExtension($extension); $dumped = null; $exception = null; @@ -46,7 +48,7 @@ class DumpExtensionTest extends \PHPUnit_Framework_TestCase $this->assertSame($expectedDumped, $dumped); } - public function getDumpParams() + public function getDumpTags() { return array( array('A{% dump %}B', true, 'AB', array()), @@ -54,4 +56,50 @@ class DumpExtensionTest extends \PHPUnit_Framework_TestCase array('A{% dump %}B', false, 'AB', null), ); } + + /** + * @dataProvider getDumpArgs + */ + public function testDump($context, $args, $expectedOutput, $debug = true) + { + $extension = new DumpExtension(new PhpCloner()); + $twig = new \Twig_Environment(new \Twig_Loader_String(), array( + 'debug' => $debug, + 'cache' => false, + 'optimizations' => 0, + )); + + array_unshift($args, $context); + array_unshift($args, $twig); + + $dump = call_user_func_array(array($extension, 'dump'), $args); + + if ($debug) { + $this->assertStringStartsWith('\n"), + array( + array(), + array(123, 456), + "
123\n
\n" + ."
456\n
\n", + ), + array( + array('foo' => 'bar'), + array(), + "
array:1 [\n"
+                ."  \"foo\" => \"bar\"\n"
+                ."]\n"
+                ."
\n", + ), + ); + } } diff --git a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml b/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml index 2d12710bb8..36ddc2a33d 100644 --- a/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml +++ b/src/Symfony/Bundle/TwigBundle/Resources/config/debug.xml @@ -17,12 +17,9 @@ - - - - + diff --git a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php index 3f8f2ef63f..add239269c 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/DumpDataCollector.php @@ -169,7 +169,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface $dumper->dump( $dump['data']->getLimitedClone($maxDepthLimit, $maxItemsPerDepth), function ($line, $depth) use (&$data) { - if (false !==$depth) { + if (-1 !== $depth) { $data .= str_repeat(' ', $depth).$line."\n"; } } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php index a6d9669f6f..1218ef67e3 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/DumpDataCollectorTest.php @@ -34,14 +34,17 @@ class DumpDataCollectorTest extends \PHPUnit_Framework_TestCase $xDump = array( array( - 'data' => "
123\n
\n", + 'data' => "
123\n
\n", 'name' => 'DumpDataCollectorTest.php', 'file' => __FILE__, 'line' => $line, 'fileExcerpt' => false, ), ); - $this->assertSame($xDump, $collector->getDumps('html')); + $dump = $collector->getDumps('html'); + $this->assertTrue(isset($dump[0]['data'])); + $dump[0]['data'] = preg_replace('/^.*?
assertSame($xDump, $dump);
 
         $this->assertStringStartsWith(
             'a:1:{i:0;a:5:{s:4:"data";O:39:"Symfony\Component\VarDumper\Cloner\Data":3:{s:45:"Symfony\Component\VarDumper\Cloner\Datadata";a:1:{i:0;a:1:{i:0;i:123;}}s:49:"Symfony\Component\VarDumper\Cloner\DatamaxDepth";i:-1;s:57:"Symfony\Component\VarDumper\Cloner\DatamaxItemsPerDepth";i:-1;}s:4:"name";s:25:"DumpDataCollectorTest.php";s:4:"file";s:',
diff --git a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
index a3acb93670..94a25a89e4 100644
--- a/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
+++ b/src/Symfony/Component/VarDumper/Caster/ReflectionCaster.php
@@ -29,8 +29,9 @@ class ReflectionCaster
 
     public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested)
     {
+        $stub->class = 'Closure'; // HHVM generates unique class names for closures
         $a = static::castReflector(new \ReflectionFunction($c), $a, $stub, $isNested);
-        unset($a["\0+\0000"], $a['name']);
+        unset($a["\0+\0000"], $a['name'], $a["\0+\0this"], $a["\0+\0parameter"]);
 
         return $a;
     }
diff --git a/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php b/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php
index 419280ee4b..903641f69c 100644
--- a/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php
+++ b/src/Symfony/Component/VarDumper/Caster/ResourceCaster.php
@@ -28,7 +28,7 @@ class ResourceCaster
     public static function castDba($dba, array $a, Stub $stub, $isNested)
     {
         $list = dba_list();
-        $a['file'] = $list[substr((string) $dba, 13)];
+        $a['file'] = $list[(int) $dba];
 
         return $a;
     }
diff --git a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
index 8622311d6f..ef94af979e 100644
--- a/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
+++ b/src/Symfony/Component/VarDumper/Cloner/AbstractCloner.php
@@ -21,64 +21,64 @@ use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
 abstract class AbstractCloner implements ClonerInterface
 {
     public static $defaultCasters = array(
-        'o:Symfony\Component\VarDumper\Caster\CasterStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub',
+        'Symfony\Component\VarDumper\Caster\CasterStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub',
 
-        'o:Closure'        => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure',
-        'o:Reflector'      => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflector',
+        'Closure'        => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure',
+        'Reflector'      => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflector',
 
-        'o:Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\StubCaster::castNestedFat',
-        'o:Doctrine\Common\Proxy\Proxy'               => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castCommonProxy',
-        'o:Doctrine\ORM\Proxy\Proxy'                  => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castOrmProxy',
-        'o:Doctrine\ORM\PersistentCollection'         => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castPersistentCollection',
+        'Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\StubCaster::castNestedFat',
+        'Doctrine\Common\Proxy\Proxy'               => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castCommonProxy',
+        'Doctrine\ORM\Proxy\Proxy'                  => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castOrmProxy',
+        'Doctrine\ORM\PersistentCollection'         => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castPersistentCollection',
 
-        'o:DOMException'             => 'Symfony\Component\VarDumper\Caster\DOMCaster::castException',
-        'o:DOMStringList'            => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
-        'o:DOMNameList'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
-        'o:DOMImplementation'        => 'Symfony\Component\VarDumper\Caster\DOMCaster::castImplementation',
-        'o:DOMImplementationList'    => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
-        'o:DOMNode'                  => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNode',
-        'o:DOMNameSpaceNode'         => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNameSpaceNode',
-        'o:DOMDocument'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocument',
-        'o:DOMNodeList'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
-        'o:DOMNamedNodeMap'          => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
-        'o:DOMCharacterData'         => 'Symfony\Component\VarDumper\Caster\DOMCaster::castCharacterData',
-        'o:DOMAttr'                  => 'Symfony\Component\VarDumper\Caster\DOMCaster::castAttr',
-        'o:DOMElement'               => 'Symfony\Component\VarDumper\Caster\DOMCaster::castElement',
-        'o:DOMText'                  => 'Symfony\Component\VarDumper\Caster\DOMCaster::castText',
-        'o:DOMTypeinfo'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castTypeinfo',
-        'o:DOMDomError'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDomError',
-        'o:DOMLocator'               => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLocator',
-        'o:DOMDocumentType'          => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocumentType',
-        'o:DOMNotation'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNotation',
-        'o:DOMEntity'                => 'Symfony\Component\VarDumper\Caster\DOMCaster::castEntity',
-        'o:DOMProcessingInstruction' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castProcessingInstruction',
-        'o:DOMXPath'                 => 'Symfony\Component\VarDumper\Caster\DOMCaster::castXPath',
+        'DOMException'             => 'Symfony\Component\VarDumper\Caster\DOMCaster::castException',
+        'DOMStringList'            => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
+        'DOMNameList'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
+        'DOMImplementation'        => 'Symfony\Component\VarDumper\Caster\DOMCaster::castImplementation',
+        'DOMImplementationList'    => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
+        'DOMNode'                  => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNode',
+        'DOMNameSpaceNode'         => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNameSpaceNode',
+        'DOMDocument'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocument',
+        'DOMNodeList'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
+        'DOMNamedNodeMap'          => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLength',
+        'DOMCharacterData'         => 'Symfony\Component\VarDumper\Caster\DOMCaster::castCharacterData',
+        'DOMAttr'                  => 'Symfony\Component\VarDumper\Caster\DOMCaster::castAttr',
+        'DOMElement'               => 'Symfony\Component\VarDumper\Caster\DOMCaster::castElement',
+        'DOMText'                  => 'Symfony\Component\VarDumper\Caster\DOMCaster::castText',
+        'DOMTypeinfo'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castTypeinfo',
+        'DOMDomError'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDomError',
+        'DOMLocator'               => 'Symfony\Component\VarDumper\Caster\DOMCaster::castLocator',
+        'DOMDocumentType'          => 'Symfony\Component\VarDumper\Caster\DOMCaster::castDocumentType',
+        'DOMNotation'              => 'Symfony\Component\VarDumper\Caster\DOMCaster::castNotation',
+        'DOMEntity'                => 'Symfony\Component\VarDumper\Caster\DOMCaster::castEntity',
+        'DOMProcessingInstruction' => 'Symfony\Component\VarDumper\Caster\DOMCaster::castProcessingInstruction',
+        'DOMXPath'                 => 'Symfony\Component\VarDumper\Caster\DOMCaster::castXPath',
 
-        'o:ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException',
-        'o:Exception'      => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException',
-        'o:Symfony\Component\DependencyInjection\ContainerInterface'
+        'ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException',
+        'Exception'      => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException',
+        'Symfony\Component\DependencyInjection\ContainerInterface'
                            => 'Symfony\Component\VarDumper\Caster\StubCaster::castNestedFat',
-        'o:Symfony\Component\VarDumper\Exception\ThrowingCasterException'
+        'Symfony\Component\VarDumper\Exception\ThrowingCasterException'
                            => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException',
 
-        'o:PDO'            => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdo',
-        'o:PDOStatement'   => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdoStatement',
+        'PDO'            => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdo',
+        'PDOStatement'   => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdoStatement',
 
-        'o:ArrayObject'         => 'Symfony\Component\VarDumper\Caster\SplCaster::castArrayObject',
-        'o:SplDoublyLinkedList' => 'Symfony\Component\VarDumper\Caster\SplCaster::castDoublyLinkedList',
-        'o:SplFixedArray'       => 'Symfony\Component\VarDumper\Caster\SplCaster::castFixedArray',
-        'o:SplHeap'             => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap',
-        'o:SplObjectStorage'    => 'Symfony\Component\VarDumper\Caster\SplCaster::castObjectStorage',
-        'o:SplPriorityQueue'    => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap',
+        'ArrayObject'         => 'Symfony\Component\VarDumper\Caster\SplCaster::castArrayObject',
+        'SplDoublyLinkedList' => 'Symfony\Component\VarDumper\Caster\SplCaster::castDoublyLinkedList',
+        'SplFixedArray'       => 'Symfony\Component\VarDumper\Caster\SplCaster::castFixedArray',
+        'SplHeap'             => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap',
+        'SplObjectStorage'    => 'Symfony\Component\VarDumper\Caster\SplCaster::castObjectStorage',
+        'SplPriorityQueue'    => 'Symfony\Component\VarDumper\Caster\SplCaster::castHeap',
 
-        'r:curl'           => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castCurl',
-        'r:dba'            => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castDba',
-        'r:dba persistent' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castDba',
-        'r:gd'             => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castGd',
-        'r:mysql link'     => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castMysqlLink',
-        'r:process'        => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castProcess',
-        'r:stream'         => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStream',
-        'r:stream-context' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStreamContext',
+        ':curl'           => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castCurl',
+        ':dba'            => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castDba',
+        ':dba persistent' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castDba',
+        ':gd'             => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castGd',
+        ':mysql link'     => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castMysqlLink',
+        ':process'        => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castProcess',
+        ':stream'         => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStream',
+        ':stream-context' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStreamContext',
     );
 
     protected $maxItems = 250;
@@ -107,8 +107,7 @@ abstract class AbstractCloner implements ClonerInterface
      *
      * Maps resources or objects types to a callback.
      * Types are in the key, with a callable caster for value.
-     * Objects class are to be prefixed with a `o:`,
-     * resources type are to be prefixed with a `r:`,
+     * Resource types are to be prefixed with a `:`,
      * see e.g. static::$defaultCasters.
      *
      * @param callable[] $casters A map of casters.
@@ -193,7 +192,7 @@ abstract class AbstractCloner implements ClonerInterface
                 $class,
                 method_exists($class, '__debugInfo'),
                 new \ReflectionClass($class),
-                array_reverse(array($class => $class) + class_parents($class) + class_implements($class) + array('*' => '*')),
+                array_reverse(array($class => $class) + class_parents($class) + class_implements($class)),
             );
 
             $this->classInfo[$class] = $classInfo;
@@ -213,7 +212,7 @@ abstract class AbstractCloner implements ClonerInterface
         }
 
         foreach ($classInfo[3] as $p) {
-            if (!empty($this->casters[$p = 'o:'.strtolower($p)])) {
+            if (!empty($this->casters[$p = strtolower($p)])) {
                 foreach ($this->casters[$p] as $p) {
                     $a = $this->callCaster($p, $obj, $a, $stub, $isNested);
                 }
@@ -237,8 +236,8 @@ abstract class AbstractCloner implements ClonerInterface
         $a = array();
         $type = $stub->class;
 
-        if (!empty($this->casters['r:'.$type])) {
-            foreach ($this->casters['r:'.$type] as $c) {
+        if (!empty($this->casters[':'.$type])) {
+            foreach ($this->casters[':'.$type] as $c) {
                 $a = $this->callCaster($c, $res, $a, $stub, $isNested);
             }
         }
diff --git a/src/Symfony/Component/VarDumper/Dumper/Cursor.php b/src/Symfony/Component/VarDumper/Cloner/Cursor.php
similarity index 89%
rename from src/Symfony/Component/VarDumper/Dumper/Cursor.php
rename to src/Symfony/Component/VarDumper/Cloner/Cursor.php
index 82aa13532f..50266ea52a 100644
--- a/src/Symfony/Component/VarDumper/Dumper/Cursor.php
+++ b/src/Symfony/Component/VarDumper/Cloner/Cursor.php
@@ -9,9 +9,7 @@
  * file that was distributed with this source code.
  */
 
-namespace Symfony\Component\VarDumper\Dumper;
-
-use Symfony\Component\VarDumper\Cloner\Stub;
+namespace Symfony\Component\VarDumper\Cloner;
 
 /**
  * Represents the current state of a dumper while dumping.
diff --git a/src/Symfony/Component/VarDumper/Cloner/Data.php b/src/Symfony/Component/VarDumper/Cloner/Data.php
index ad2a2a2b06..3394cf796f 100644
--- a/src/Symfony/Component/VarDumper/Cloner/Data.php
+++ b/src/Symfony/Component/VarDumper/Cloner/Data.php
@@ -11,9 +11,6 @@
 
 namespace Symfony\Component\VarDumper\Cloner;
 
-use Symfony\Component\VarDumper\Dumper\DumperInternalsInterface;
-use Symfony\Component\VarDumper\Dumper\Cursor;
-
 /**
  * @author Nicolas Grekas 
  */
@@ -57,21 +54,21 @@ class Data
     }
 
     /**
-     * Dumps data with a DumperInternalsInterface dumper.
+     * Dumps data with a DumperInterface dumper.
      */
-    public function dump(DumperInternalsInterface $dumper)
+    public function dump(DumperInterface $dumper)
     {
         $refs = array(0);
         $this->dumpItem($dumper, new Cursor, $refs, $this->data[0][0]);
     }
 
     /**
-     * Breadth-first dumping of items.
+     * Depth-first dumping of items.
      *
-     * @param DumperInternalsInterface $dumper The dumper being used for dumping.
-     * @param Cursor                   $cursor A cursor used for tracking dumper state position.
-     * @param array                    &$refs  A map of all references discovered while dumping.
-     * @param mixed                    $item   A Stub object or the original value being dumped.
+     * @param DumperInterface $dumper The dumper being used for dumping.
+     * @param Cursor          $cursor A cursor used for tracking dumper state position.
+     * @param array           &$refs  A map of all references discovered while dumping.
+     * @param mixed           $item   A Stub object or the original value being dumped.
      */
     private function dumpItem($dumper, $cursor, &$refs, $item)
     {
@@ -157,12 +154,12 @@ class Data
     /**
      * Dumps children of hash structures.
      *
-     * @param DumperInternalsInterface $dumper
-     * @param Cursor                   $parentCursor The cursor of the parent hash.
-     * @param array                    &$refs        A map of all references discovered while dumping.
-     * @param array                    $children     The children to dump.
-     * @param int                      $hashCut      The number of items removed from the original hash.
-     * @param string                   $hashType     A Cursor::HASH_* const.
+     * @param DumperInterface $dumper
+     * @param Cursor          $parentCursor The cursor of the parent hash.
+     * @param array           &$refs        A map of all references discovered while dumping.
+     * @param array           $children     The children to dump.
+     * @param int             $hashCut      The number of items removed from the original hash.
+     * @param string          $hashType     A Cursor::HASH_* const.
      *
      * @return int The final number of removed items.
      */
diff --git a/src/Symfony/Component/VarDumper/Dumper/DumperInternalsInterface.php b/src/Symfony/Component/VarDumper/Cloner/DumperInterface.php
similarity index 97%
rename from src/Symfony/Component/VarDumper/Dumper/DumperInternalsInterface.php
rename to src/Symfony/Component/VarDumper/Cloner/DumperInterface.php
index d60974efdd..d910834ddf 100644
--- a/src/Symfony/Component/VarDumper/Dumper/DumperInternalsInterface.php
+++ b/src/Symfony/Component/VarDumper/Cloner/DumperInterface.php
@@ -9,14 +9,14 @@
  * file that was distributed with this source code.
  */
 
-namespace Symfony\Component\VarDumper\Dumper;
+namespace Symfony\Component\VarDumper\Cloner;
 
 /**
  * DumperInterface used by Data objects.
  *
  * @author Nicolas Grekas 
  */
-interface DumperInternalsInterface
+interface DumperInterface
 {
     /**
      * Dumps a scalar value.
diff --git a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php
index 708dc493ec..4b99c1f71b 100644
--- a/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php
+++ b/src/Symfony/Component/VarDumper/Dumper/AbstractDumper.php
@@ -12,13 +12,14 @@
 namespace Symfony\Component\VarDumper\Dumper;
 
 use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Cloner\DumperInterface;
 
 /**
  * Abstract mechanism for dumping a Data object.
  *
  * @author Nicolas Grekas 
  */
-abstract class AbstractDumper implements DataDumperInterface, DumperInternalsInterface
+abstract class AbstractDumper implements DataDumperInterface, DumperInterface
 {
     public static $defaultOutputStream = 'php://output';
 
@@ -87,14 +88,22 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInternalsInt
      */
     public function dump(Data $data, $lineDumper = null)
     {
-        $this->decimalPoint = (string) 0.5;
-        $this->decimalPoint = $this->decimalPoint[1];
-        $dumper = clone $this;
+        $exception = null;
         if ($lineDumper) {
-            $dumper->setLineDumper($lineDumper);
+            $prevLineDumper = $this->setLineDumper($lineDumper);
+        }
+        try {
+            $data->dump($this);
+            $this->dumpLine(-1);
+        } catch (\Exception $exception) {
+            // Re-thrown below
+        }
+        if ($lineDumper) {
+            $this->setLineDumper($prevLineDumper);
+        }
+        if (null !== $exception) {
+            throw $exception;
         }
-        $data->dump($dumper);
-        $dumper->dumpLine(false);
     }
 
     /**
@@ -116,7 +125,7 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInternalsInt
      */
     protected function echoLine($line, $depth)
     {
-        if (false !== $depth) {
+        if (-1 !== $depth) {
             fwrite($this->outputStream, str_repeat($this->indentPad, $depth).$line."\n");
         }
     }
diff --git a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
index fc4f7ae51b..87680de598 100644
--- a/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
+++ b/src/Symfony/Component/VarDumper/Dumper/CliDumper.php
@@ -12,6 +12,7 @@
 namespace Symfony\Component\VarDumper\Dumper;
 
 use Symfony\Component\VarDumper\Cloner\Data;
+use Symfony\Component\VarDumper\Cloner\Cursor;
 
 /**
  * CliDumper dumps variables for command line output.
diff --git a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
index 39e15f95e8..6e740c9dcd 100644
--- a/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
+++ b/src/Symfony/Component/VarDumper/Dumper/HtmlDumper.php
@@ -11,6 +11,8 @@
 
 namespace Symfony\Component\VarDumper\Dumper;
 
+use Symfony\Component\VarDumper\Cloner\Cursor;
+
 /**
  * HtmlDumper dumps variables as HTML.
  *
@@ -21,8 +23,8 @@ class HtmlDumper extends CliDumper
     public static $defaultOutputStream = 'php://output';
 
     protected $dumpHeader;
-    protected $dumpPrefix = '
';
-    protected $dumpSuffix = '
'; + protected $dumpPrefix = '
';
+    protected $dumpSuffix = '
'; protected $colors = true; protected $headerIsDumped = false; protected $lastDepth = -1; @@ -74,7 +76,7 @@ class HtmlDumper extends CliDumper * @param string $prefix The prepended HTML string. * @param string $suffix The appended HTML string. */ - public function setDumpBoudaries($prefix, $suffix) + public function setDumpBoundaries($prefix, $suffix) { $this->dumpPrefix = $prefix; $this->dumpSuffix = $suffix; @@ -87,28 +89,104 @@ class HtmlDumper extends CliDumper { $this->headerIsDumped = true; - $p = 'sf-dump'; - $line = <<'.$this->dumpHeader; + return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).''.$this->dumpHeader; + } + + /** + * {@inheritdoc} + */ + protected function enterHash(Cursor $cursor, $prefix, $hasChild) + { + if ($hasChild) { + $prefix .= ''; + } + + return parent::enterHash($cursor, $prefix, $hasChild); + } + + /** + * {@inheritdoc} + */ + protected function leaveHash(Cursor $cursor, $suffix, $hasChild, $cut) + { + if ($hasChild) { + $suffix = ''.$suffix; + } + + return parent::leaveHash($cursor, $suffix, $hasChild, $cut); } /** @@ -138,6 +216,10 @@ EOHTML; $val = str_replace($c, "$r", $val); } } + } elseif ('note' === $style) { + if (false !== $c = strrpos($val, '\\')) { + $val = sprintf('%s', $val, $style, substr($val, $c+1)); + } } return "$val"; @@ -148,7 +230,6 @@ EOHTML; */ protected function dumpLine($depth) { - switch ($this->lastDepth - $depth) { case +1: $this->line = ''.$this->line; break; case -1: $this->line = "$this->line"; break; @@ -161,13 +242,11 @@ EOHTML; $this->line = $this->getDumpHeader().$this->line; } - if (false === $depth) { - $this->lastDepth = -1; + if (-1 === $depth) { $this->line .= $this->dumpSuffix; parent::dumpLine(0); - } else { - $this->lastDepth = $depth; } + $this->lastDepth = $depth; parent::dumpLine($depth); } diff --git a/src/Symfony/Component/VarDumper/README.md b/src/Symfony/Component/VarDumper/README.md index b38d82f9d0..3eb3ef2405 100644 --- a/src/Symfony/Component/VarDumper/README.md +++ b/src/Symfony/Component/VarDumper/README.md @@ -1,10 +1,10 @@ -Symfony mechanim for exploring and dumping PHP variables -======================================================== +Symfony mechanism for exploring and dumping PHP variables +========================================================= This component provides a mechanism that allows exploring then dumping any PHP variable. -It handles scalar, objects and resources properly, taking hard and soft +It handles scalars, objects and resources properly, taking hard and soft references into account. More than being immune to inifinite recursion problems, it allows dumping where references link to each other. It explores recursive structures using a breadth-first algorithm. diff --git a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php index 1bc32bda7c..6a4e9c3f88 100644 --- a/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/CliDumperTest.php @@ -26,6 +26,13 @@ class CliDumperTest extends \PHPUnit_Framework_TestCase $dumper = new CliDumper('php://output'); $dumper->setColors(false); $cloner = new PhpCloner(); + $cloner->addCasters(array( + ':stream' => function ($res, $a) { + unset($a['uri']); + + return $a; + } + )); $data = $cloner->cloneVar($var); ob_start(); @@ -51,7 +58,7 @@ array:25 [ "[]" => [] "res" => resource:stream { wrapper_type: "plainfile" - stream_type: "dir" + stream_type: "STDIO" mode: "r" unread_bytes: 0 seekable: true diff --git a/src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php b/src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php new file mode 100644 index 0000000000..6ffc8dd214 --- /dev/null +++ b/src/Symfony/Component/VarDumper/Tests/Fixtures/dumb-var.php @@ -0,0 +1,43 @@ +bar = 'bar'; + +$g = fopen(__FILE__, 'r'); +$h = fopen(__FILE__, 'r'); +fclose($h); + +$var = array( + 'number' => 1, null, + 'const' => 1.1, true, false, NAN, INF, -INF, PHP_INT_MAX, + 'str' => "déjà", "\xE9", + '[]' => array(), + 'res' => $g, + $h, + 'obj' => $foo, + 'closure' => function ($a, \PDO &$b = null) {}, + 'line' => __LINE__ - 1, + 'nobj' => array((object) array()), +); + +$r = array(); +$r[] =& $r; + +$var['recurs'] =& $r; +$var[] =& $var[0]; +$var['sobj'] = $var['obj']; +$var['snobj'] =& $var['nobj'][0]; +$var['snobj2'] = $var['nobj'][0]; +$var['file'] = __FILE__; +$var["bin-key-\xE9"] = ""; + +unset($g, $h, $r); diff --git a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php index d866aa83f9..25b027d62e 100644 --- a/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php +++ b/src/Symfony/Component/VarDumper/Tests/HtmlDumperTest.php @@ -25,7 +25,16 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase $dumper = new HtmlDumper('php://output'); $dumper->setColors(false); + $dumper->setDumpHeader(''); + $dumper->setDumpBoundaries('', ''); $cloner = new PhpCloner(); + $cloner->addCasters(array( + ':stream' => function ($res, $a) { + unset($a['uri']); + + return $a; + } + )); $data = $cloner->cloneVar($var); ob_start(); @@ -37,7 +46,7 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase $this->assertSame( <<
array:25 [
+array:25 [
   "number" => 1
   0 => null #1
   "const" => 1.1
@@ -50,9 +59,9 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
   "str" => "déjà"
   7 => b"é"
   "[]" => []
-  "res" => resource:stream {
+  "res" => resource:stream {
     wrapper_type: "plainfile"
-    stream_type: "dir"
+    stream_type: "STDIO"
     mode: "r"
     unread_bytes: 0
     seekable: true
@@ -60,13 +69,13 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
     blocked: true
     eof: false
     options: []
-  }
+  }
   8 => resource:Unknown {}
-  "obj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo { #2
+  "obj" => DumbFoo { #2
     foo: "foo"
     "bar": "bar"
-  }
-  "closure" => Closure {
+  }
+  "closure" => Closure {
     reflection: """
       Closure [ <user> {$closureLabel} Symfony\Component\VarDumper\Tests\Fixture\{closure} ] {
         @@ {$var['file']} {$var['line']} - {$var['line']}
@@ -77,22 +86,22 @@ class HtmlDumperTest extends \PHPUnit_Framework_TestCase
         }
       }
       """
-  }
+  }
   "line" => {$var['line']}
-  "nobj" => array:1 [
+  "nobj" => array:1 [
     0 => {} #3
-  ]
-  "recurs" => array:1 [ #4
+  ]
+  "recurs" => array:1 [ #4
     0 => &4 array:1 [@4]
-  ]
+  ]
   9 => &1 null
-  "sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {@2}
+  "sobj" => DumbFoo {@2}
   "snobj" => &3 {@3}
   "snobj2" => {@3}
   "file" => "{$var['file']}"
   b"bin-key-é" => ""
-]
-
+
] + EOTXT ,