[VarDumper] add Stub objects for cutting cleanly and dumping consts

This commit is contained in:
Nicolas Grekas 2014-09-08 20:39:52 +02:00
parent c8746a43c9
commit 0d8a942cfa
19 changed files with 482 additions and 271 deletions

View File

@ -218,7 +218,7 @@ class DumpDataCollector extends DataCollector implements DataDumperInterface
$h = headers_list(); $h = headers_list();
$i = count($h); $i = count($h);
array_unshift($h, 'Content-Type: ' . ini_get('default_mimetype')); array_unshift($h, 'Content-Type: '.ini_get('default_mimetype'));
while (0 !== stripos($h[$i], 'Content-Type:')) { while (0 !== stripos($h[$i], 'Content-Type:')) {
--$i; --$i;
} }

View File

@ -0,0 +1,62 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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 the main properties of a PHP variable, pre-casted by a caster.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class CasterStub extends Stub
{
public function __construct($value, $class = '')
{
switch (gettype($value)) {
case 'object':
$this->type = self::TYPE_OBJECT;
$this->class = get_class($value);
$this->value = spl_object_hash($value);
$this->cut = -1;
break;
case 'array':
$this->type = self::TYPE_ARRAY;
$this->class = self::ARRAY_ASSOC;
$this->cut = $this->value = count($value);
break;
case 'resource':
case 'unknown type':
$this->type = self::TYPE_RESOURCE;
$this->class = @get_resource_type($value);
$this->value = (int) $value;
$this->cut = -1;
break;
case 'string':
if ('' === $class) {
$this->type = self::TYPE_STRING;
$this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY;
$this->cut = self::STRING_BINARY === $this->class ? strlen($value) : (function_exists('iconv_strlen') ? iconv_strlen($value, 'UTF-8') : -1);
break;
}
// No break;
default:
$this->class = $class;
$this->value = $value;
break;
}
}
}

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\VarDumper\Caster; namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Casts DOM related classes to array representation. * Casts DOM related classes to array representation.
* *
@ -38,16 +40,37 @@ class DOMCaster
DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR',
); );
public static function castException(\DOMException $e, array $a, $isNested, &$cut) private static $nodeTypes = array(
XML_ELEMENT_NODE => 'XML_ELEMENT_NODE',
XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE',
XML_TEXT_NODE => 'XML_TEXT_NODE',
XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE',
XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE',
XML_ENTITY_NODE => 'XML_ENTITY_NODE',
XML_PI_NODE => 'XML_PI_NODE',
XML_COMMENT_NODE => 'XML_COMMENT_NODE',
XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE',
XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE',
XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE',
XML_NOTATION_NODE => 'XML_NOTATION_NODE',
XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE',
XML_DTD_NODE => 'XML_DTD_NODE',
XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE',
XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE',
XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE',
XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE',
);
public static function castException(\DOMException $e, array $a, Stub $stub, $isNested)
{ {
if (isset($a["\0*\0code"], self::$errorCodes[$a["\0*\0code"]])) { if (isset($a["\0*\0code"], self::$errorCodes[$a["\0*\0code"]])) {
$a["\0*\0code"] .= ' ('.self::$errorCodes[$a["\0*\0code"]].')'; $a["\0*\0code"] = new CasterStub(self::$errorCodes[$a["\0*\0code"]], 'const');
} }
return $a; return $a;
} }
public static function castLength($dom, array $a, $isNested, &$cut) public static function castLength($dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'length' => $dom->length, 'length' => $dom->length,
@ -56,7 +79,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castImplementation($dom, array $a, $isNested, &$cut) public static function castImplementation($dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
"\0~\0Core" => '1.0', "\0~\0Core" => '1.0',
@ -66,53 +89,49 @@ class DOMCaster
return $a; return $a;
} }
public static function castNode(\DOMNode $dom, array $a, $isNested, &$cut) public static function castNode(\DOMNode $dom, array $a, Stub $stub, $isNested)
{ {
// Commented lines denote properties that exist but are better not dumped for clarity.
$a += array( $a += array(
'nodeName' => $dom->nodeName, 'nodeName' => $dom->nodeName,
//'nodeValue' => $dom->nodeValue, 'nodeValue' => new CasterStub($dom->nodeValue),
'nodeType' => $dom->nodeType, 'nodeType' => new CasterStub(self::$nodeTypes[$dom->nodeType], 'const'),
//'parentNode' => $dom->parentNode, 'parentNode' => new CasterStub($dom->parentNode),
'childNodes' => $dom->childNodes, 'childNodes' => $dom->childNodes,
//'firstChild' => $dom->firstChild, 'firstChild' => new CasterStub($dom->firstChild),
//'lastChild' => $dom->lastChild, 'lastChild' => new CasterStub($dom->lastChild),
//'previousSibling' => $dom->previousSibling, 'previousSibling' => new CasterStub($dom->previousSibling),
//'nextSibling' => $dom->nextSibling, 'nextSibling' => new CasterStub($dom->nextSibling),
'attributes' => $dom->attributes, 'attributes' => $dom->attributes,
//'ownerDocument' => $dom->ownerDocument, 'ownerDocument' => new CasterStub($dom->ownerDocument),
'namespaceURI' => $dom->namespaceURI, 'namespaceURI' => $dom->namespaceURI,
'prefix' => $dom->prefix, 'prefix' => $dom->prefix,
'localName' => $dom->localName, 'localName' => $dom->localName,
'baseURI' => $dom->baseURI, 'baseURI' => $dom->baseURI,
//'textContent' => $dom->textContent, 'textContent' => new CasterStub($dom->textContent),
); );
$cut += 8;
return $a; return $a;
} }
public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, $isNested, &$cut) public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, $isNested)
{ {
// Commented lines denote properties that exist but are better not dumped for clarity. // Commented lines denote properties that exist but are better not dumped for clarity.
$a += array( $a += array(
'nodeName' => $dom->nodeName, 'nodeName' => $dom->nodeName,
//'nodeValue' => $dom->nodeValue, 'nodeValue' => new CasterStub($dom->nodeValue),
'nodeType' => $dom->nodeType, 'nodeType' => new CasterStub(self::$nodeTypes[$dom->nodeType], 'const'),
'prefix' => $dom->prefix, 'prefix' => $dom->prefix,
'localName' => $dom->localName, 'localName' => $dom->localName,
'namespaceURI' => $dom->namespaceURI, 'namespaceURI' => $dom->namespaceURI,
//'ownerDocument' => $dom->ownerDocument, 'ownerDocument' => new CasterStub($dom->ownerDocument),
//'parentNode' => $dom->parentNode, 'parentNode' => new CasterStub($dom->parentNode),
); );
$cut += 3;
return $a; return $a;
} }
public static function castDocument(\DOMDocument $dom, array $a, $isNested, &$cut) public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, $isNested)
{ {
$formatOutput = $dom->formatOutput; $formatOutput = $dom->formatOutput;
$dom->formatOutput = true; $dom->formatOutput = true;
@ -120,7 +139,7 @@ class DOMCaster
$a += array( $a += array(
'doctype' => $dom->doctype, 'doctype' => $dom->doctype,
'implementation' => $dom->implementation, 'implementation' => $dom->implementation,
'documentElement' => $dom->documentElement, 'documentElement' => new CasterStub($dom->documentElement),
'actualEncoding' => $dom->actualEncoding, 'actualEncoding' => $dom->actualEncoding,
'encoding' => $dom->encoding, 'encoding' => $dom->encoding,
'xmlEncoding' => $dom->xmlEncoding, 'xmlEncoding' => $dom->xmlEncoding,
@ -145,7 +164,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castCharacterData(\DOMCharacterData $dom, array $a, $isNested, &$cut) public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'data' => $dom->data, 'data' => $dom->data,
@ -155,7 +174,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castAttr(\DOMAttr $dom, array $a, $isNested, &$cut) public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'name' => $dom->name, 'name' => $dom->name,
@ -168,7 +187,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castElement(\DOMElement $dom, array $a, $isNested, &$cut) public static function castElement(\DOMElement $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'tagName' => $dom->tagName, 'tagName' => $dom->tagName,
@ -178,7 +197,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castText(\DOMText $dom, array $a, $isNested, &$cut) public static function castText(\DOMText $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'wholeText' => $dom->wholeText, 'wholeText' => $dom->wholeText,
@ -187,7 +206,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castTypeinfo(\DOMTypeinfo $dom, array $a, $isNested, &$cut) public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'typeName' => $dom->typeName, 'typeName' => $dom->typeName,
@ -197,7 +216,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castDomError(\DOMDomError $dom, array $a, $isNested, &$cut) public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'severity' => $dom->severity, 'severity' => $dom->severity,
@ -211,7 +230,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castLocator(\DOMLocator $dom, array $a, $isNested, &$cut) public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'lineNumber' => $dom->lineNumber, 'lineNumber' => $dom->lineNumber,
@ -224,7 +243,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castDocumentType(\DOMDocumentType $dom, array $a, $isNested, &$cut) public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'name' => $dom->name, 'name' => $dom->name,
@ -238,7 +257,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castNotation(\DOMNotation $dom, array $a, $isNested, &$cut) public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'publicId' => $dom->publicId, 'publicId' => $dom->publicId,
@ -248,7 +267,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castEntity(\DOMEntity $dom, array $a, $isNested, &$cut) public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'publicId' => $dom->publicId, 'publicId' => $dom->publicId,
@ -262,7 +281,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, $isNested, &$cut) public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'target' => $dom->target, 'target' => $dom->target,
@ -272,7 +291,7 @@ class DOMCaster
return $a; return $a;
} }
public static function castXPath(\DOMXPath $dom, array $a, $isNested, &$cut) public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
'document' => $dom->document, 'document' => $dom->document,

View File

@ -11,10 +11,10 @@
namespace Symfony\Component\VarDumper\Caster; namespace Symfony\Component\VarDumper\Caster;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Proxy\Proxy as CommonProxy; use Doctrine\Common\Proxy\Proxy as CommonProxy;
use Doctrine\ORM\Proxy\Proxy as OrmProxy; use Doctrine\ORM\Proxy\Proxy as OrmProxy;
use Doctrine\ORM\PersistentCollection; use Doctrine\ORM\PersistentCollection;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Casts Doctrine related classes to array representation. * Casts Doctrine related classes to array representation.
@ -23,50 +23,36 @@ use Doctrine\ORM\PersistentCollection;
*/ */
class DoctrineCaster class DoctrineCaster
{ {
public static function castCommonProxy(CommonProxy $proxy, array $a, $isNested, &$cut) public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, $isNested)
{ {
unset( unset(
$a['__cloner__'], $a['__cloner__'],
$a['__initializer__'] $a['__initializer__']
); );
$cut += 2; $stub->cut += 2;
return $a; return $a;
} }
public static function castOrmProxy(OrmProxy $proxy, array $a, $isNested, &$cut) public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, $isNested)
{ {
$prefix = "\0Doctrine\\ORM\\Proxy\\Proxy\0"; $prefix = "\0Doctrine\\ORM\\Proxy\\Proxy\0";
unset( unset(
$a[$prefix.'_entityPersister'], $a[$prefix.'_entityPersister'],
$a[$prefix.'_identifier'] $a[$prefix.'_identifier']
); );
$cut += 2; $stub->cut += 2;
return $a; return $a;
} }
public static function castObjectManager(ObjectManager $manager, array $a, $isNested, &$cut) public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, $isNested)
{
if ($isNested) {
$cut += count($a);
return array();
}
return $a;
}
public static function castPersistentCollection(PersistentCollection $coll, array $a, $isNested, &$cut)
{ {
$prefix = "\0Doctrine\\ORM\\PersistentCollection\0"; $prefix = "\0Doctrine\\ORM\\PersistentCollection\0";
unset(
$a[$prefix.'snapshot'], $a[$prefix.'snapshot'] = new CasterStub($a[$prefix.'snapshot']);
$a[$prefix.'association'], $a[$prefix.'association'] = new CasterStub($a[$prefix.'association']);
$a[$prefix.'em'], $a[$prefix.'typeClass'] = new CasterStub($a[$prefix.'typeClass']);
$a[$prefix.'typeClass']
);
$cut += 4;
return $a; return $a;
} }

View File

@ -12,6 +12,7 @@
namespace Symfony\Component\VarDumper\Caster; namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Exception\ThrowingCasterException; use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Casts common Exception classes to array representation. * Casts common Exception classes to array representation.
@ -39,7 +40,7 @@ class ExceptionCaster
E_STRICT => 'E_STRICT', E_STRICT => 'E_STRICT',
); );
public static function castException(\Exception $e, array $a) public static function castException(\Exception $e, array $a, Stub $stub, $isNested)
{ {
$trace = $a["\0Exception\0trace"]; $trace = $a["\0Exception\0trace"];
unset($a["\0Exception\0trace"]); // Ensures the trace is always last unset($a["\0Exception\0trace"]); // Ensures the trace is always last
@ -57,16 +58,16 @@ class ExceptionCaster
return $a; return $a;
} }
public static function castErrorException(\ErrorException $e, array $a) public static function castErrorException(\ErrorException $e, array $a, Stub $stub, $isNested)
{ {
if (isset($a[$s = "\0*\0severity"], self::$errorTypes[$a[$s]])) { if (isset($a[$s = "\0*\0severity"], self::$errorTypes[$a[$s]])) {
$a[$s] = self::$errorTypes[$a[$s]]; $a[$s] = new CasterStub(self::$errorTypes[$a[$s]], 'const');
} }
return $a; return $a;
} }
public static function castThrowingCasterException(ThrowingCasterException $e, array $a) public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, $isNested)
{ {
$b = (array) $a["\0Exception\0previous"]; $b = (array) $a["\0Exception\0previous"];
@ -74,7 +75,7 @@ class ExceptionCaster
$t = static::$traceArgs; $t = static::$traceArgs;
static::$traceArgs = false; static::$traceArgs = false;
$b = static::castException($a["\0Exception\0previous"], $b); $b = static::castException($a["\0Exception\0previous"], $b, $stub, $isNested);
static::$traceArgs = $t; static::$traceArgs = $t;
if (empty($a["\0*\0message"])) { if (empty($a["\0*\0message"])) {

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\VarDumper\Caster; namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Casts PDO related classes to array representation. * Casts PDO related classes to array representation.
* *
@ -55,7 +57,7 @@ class PdoCaster
), ),
); );
public static function castPdo(\PDO $c, array $a) public static function castPdo(\PDO $c, array $a, Stub $stub, $isNested)
{ {
$a = array(); $a = array();
$errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE);
@ -70,7 +72,7 @@ class PdoCaster
try { try {
$a[$attr] = 'ERRMODE' === $attr ? $errmode : $c->getAttribute(constant("PDO::ATTR_{$attr}")); $a[$attr] = 'ERRMODE' === $attr ? $errmode : $c->getAttribute(constant("PDO::ATTR_{$attr}"));
if (isset($values[$a[$attr]])) { if (isset($values[$a[$attr]])) {
$a[$attr] = $values[$a[$attr]]; $a[$attr] = new CasterStub($values[$a[$attr]], 'const');
} }
} catch (\Exception $m) { } catch (\Exception $m) {
} }
@ -98,7 +100,7 @@ class PdoCaster
return $a; return $a;
} }
public static function castPdoStatement(\PDOStatement $c, array $a) public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, $isNested)
{ {
$m = "\0~\0"; $m = "\0~\0";
$a[$m.'errorInfo'] = $c->errorInfo(); $a[$m.'errorInfo'] = $c->errorInfo();

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\VarDumper\Caster; namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Casts Reflector related classes to array representation. * Casts Reflector related classes to array representation.
* *
@ -18,16 +20,16 @@ namespace Symfony\Component\VarDumper\Caster;
*/ */
class ReflectionCaster class ReflectionCaster
{ {
public static function castReflector(\Reflector $c, array $a) public static function castReflector(\Reflector $c, array $a, Stub $stub, $isNested)
{ {
$a["\0~\0reflection"] = $c->__toString(); $a["\0~\0reflection"] = $c->__toString();
return $a; return $a;
} }
public static function castClosure(\Closure $c, array $a) public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested)
{ {
$a = static::castReflector(new \ReflectionFunction($c), $a); $a = static::castReflector(new \ReflectionFunction($c), $a, $stub, $isNested);
unset($a["\0+\0000"], $a['name']); unset($a["\0+\0000"], $a['name']);
return $a; return $a;

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\VarDumper\Caster; namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Casts common resource types to array representation. * Casts common resource types to array representation.
* *
@ -18,12 +20,12 @@ namespace Symfony\Component\VarDumper\Caster;
*/ */
class ResourceCaster class ResourceCaster
{ {
public static function castCurl($h, array $a) public static function castCurl($h, array $a, Stub $stub, $isNested)
{ {
return curl_getinfo($h); return curl_getinfo($h);
} }
public static function castDba($dba, array $a) public static function castDba($dba, array $a, Stub $stub, $isNested)
{ {
$list = dba_list(); $list = dba_list();
$a['file'] = $list[substr((string) $dba, 13)]; $a['file'] = $list[substr((string) $dba, 13)];
@ -31,22 +33,22 @@ class ResourceCaster
return $a; return $a;
} }
public static function castProcess($process, array $a) public static function castProcess($process, array $a, Stub $stub, $isNested)
{ {
return proc_get_status($process); return proc_get_status($process);
} }
public static function castStream($stream, array $a) public static function castStream($stream, array $a, Stub $stub, $isNested)
{ {
return stream_get_meta_data($stream) + static::castStreamContext($stream, $a); return stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested);
} }
public static function castStreamContext($stream, array $a) public static function castStreamContext($stream, array $a, Stub $stub, $isNested)
{ {
return stream_context_get_params($stream); return stream_context_get_params($stream);
} }
public static function castGd($gd, array $a) public static function castGd($gd, array $a, Stub $stub, $isNested)
{ {
$a['size'] = imagesx($gd).'x'.imagesy($gd); $a['size'] = imagesx($gd).'x'.imagesy($gd);
$a['trueColor'] = imageistruecolor($gd); $a['trueColor'] = imageistruecolor($gd);
@ -54,7 +56,7 @@ class ResourceCaster
return $a; return $a;
} }
public static function castMysqlLink($h, array $a) public static function castMysqlLink($h, array $a, Stub $stub, $isNested)
{ {
$a['host'] = mysql_get_host_info($h); $a['host'] = mysql_get_host_info($h);
$a['protocol'] = mysql_get_proto_info($h); $a['protocol'] = mysql_get_proto_info($h);

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\VarDumper\Caster; namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Casts SPL related classes to array representation. * Casts SPL related classes to array representation.
* *
@ -18,9 +20,9 @@ namespace Symfony\Component\VarDumper\Caster;
*/ */
class SplCaster class SplCaster
{ {
public static function castArrayObject(\ArrayObject $c, array $a) public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, $isNested)
{ {
$class = get_class($c); $class = $stub->class;
$flags = $c->getFlags(); $flags = $c->getFlags();
$b = array( $b = array(
@ -55,7 +57,7 @@ class SplCaster
return $a; return $a;
} }
public static function castHeap(\Iterator $c, array $a) public static function castHeap(\Iterator $c, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
"\0~\0heap" => iterator_to_array(clone $c), "\0~\0heap" => iterator_to_array(clone $c),
@ -64,13 +66,13 @@ class SplCaster
return $a; return $a;
} }
public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a) public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, $isNested)
{ {
$mode = $c->getIteratorMode(); $mode = $c->getIteratorMode();
$c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE);
$a += array( $a += array(
"\0~\0mode" => (($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_KEEP) ? 'IT_MODE_KEEP' : 'IT_MODE_DELETE'), "\0~\0mode" => new CasterStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_KEEP) ? 'IT_MODE_KEEP' : 'IT_MODE_DELETE'), 'const'),
"\0~\0dllist" => iterator_to_array($c), "\0~\0dllist" => iterator_to_array($c),
); );
$c->setIteratorMode($mode); $c->setIteratorMode($mode);
@ -78,7 +80,7 @@ class SplCaster
return $a; return $a;
} }
public static function castFixedArray(\SplFixedArray $c, array $a) public static function castFixedArray(\SplFixedArray $c, array $a, Stub $stub, $isNested)
{ {
$a += array( $a += array(
"\0~\0storage" => $c->toArray(), "\0~\0storage" => $c->toArray(),
@ -87,7 +89,7 @@ class SplCaster
return $a; return $a;
} }
public static function castObjectStorage(\SplObjectStorage $c, array $a) public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, $isNested)
{ {
$storage = array(); $storage = array();
unset($a["\0+\0\0gcdata"]); // Don't hit https://bugs.php.net/65967 unset($a["\0+\0\0gcdata"]); // Don't hit https://bugs.php.net/65967

View File

@ -0,0 +1,45 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* 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;
/**
* Casts a CasterStub.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class StubCaster
{
public static function castStub(CasterStub $c, array $a, Stub $stub, $isNested)
{
if ($isNested) {
$stub->type = $c->type;
$stub->class = $c->class;
$stub->value = $c->value;
$stub->cut = $c->cut;
return array();
}
}
public static function castNestedFat($obj, array $a, Stub $stub, $isNested)
{
if ($isNested) {
$stub->cut += count($a);
return array();
}
return $a;
}
}

View File

@ -21,10 +21,12 @@ use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
abstract class AbstractCloner implements ClonerInterface abstract class AbstractCloner implements ClonerInterface
{ {
public static $defaultCasters = array( public static $defaultCasters = array(
'o:Symfony\Component\VarDumper\Caster\CasterStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub',
'o:Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure', 'o:Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure',
'o:Reflector' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflector', 'o:Reflector' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflector',
'o:Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castObjectManager', 'o:Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\StubCaster::castNestedFat',
'o:Doctrine\Common\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castCommonProxy', '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\Proxy\Proxy' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castOrmProxy',
'o:Doctrine\ORM\PersistentCollection' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castPersistentCollection', 'o:Doctrine\ORM\PersistentCollection' => 'Symfony\Component\VarDumper\Caster\DoctrineCaster::castPersistentCollection',
@ -54,6 +56,8 @@ abstract class AbstractCloner implements ClonerInterface
'o:ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException', 'o:ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException',
'o:Exception' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException', 'o:Exception' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException',
'o:Symfony\Component\DependencyInjection\ContainerInterface'
=> 'Symfony\Component\VarDumper\Caster\StubCaster::castNestedFat',
'o:Symfony\Component\VarDumper\Exception\ThrowingCasterException' 'o:Symfony\Component\VarDumper\Exception\ThrowingCasterException'
=> 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException', => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException',
@ -171,19 +175,22 @@ abstract class AbstractCloner implements ClonerInterface
/** /**
* Casts an object to an array representation. * Casts an object to an array representation.
* *
* @param string $class The class of the object.
* @param object $obj The object itself. * @param object $obj The object itself.
* @param Stub $stub The Stub for the casted object.
* @param bool $isNested True if the object is nested in the dumped structure. * @param bool $isNested True if the object is nested in the dumped structure.
* @param int &$cut After the cast, number of items removed from $obj.
* *
* @return array The object casted as array. * @return array The object casted as array.
*/ */
protected function castObject($class, $obj, $isNested, &$cut) protected function castObject($obj, Stub $stub, $isNested)
{ {
$class = $stub->class;
if (isset($this->classInfo[$class])) { if (isset($this->classInfo[$class])) {
$classInfo = $this->classInfo[$class]; $classInfo = $this->classInfo[$class];
$stub->class = $classInfo[0];
} else { } else {
$classInfo = array( $classInfo = array(
$class,
method_exists($class, '__debugInfo'), method_exists($class, '__debugInfo'),
new \ReflectionClass($class), 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) + array('*' => '*')),
@ -192,24 +199,23 @@ abstract class AbstractCloner implements ClonerInterface
$this->classInfo[$class] = $classInfo; $this->classInfo[$class] = $classInfo;
} }
if ($classInfo[0]) { if ($classInfo[1]) {
$a = $this->callCaster(array($obj, '__debugInfo'), $obj, array(), $isNested); $a = $this->callCaster(array($obj, '__debugInfo'), $obj, array(), null, $isNested);
} else { } else {
$a = (array) $obj; $a = (array) $obj;
} }
$cut = 0;
foreach ($a as $k => $p) { foreach ($a as $k => $p) {
if (!isset($k[0]) || ("\0" !== $k[0] && !$classInfo[1]->hasProperty($k))) { if (!isset($k[0]) || ("\0" !== $k[0] && !$classInfo[2]->hasProperty($k))) {
unset($a[$k]); unset($a[$k]);
$a["\0+\0".$k] = $p; $a["\0+\0".$k] = $p;
} }
} }
foreach ($classInfo[2] as $p) { foreach ($classInfo[3] as $p) {
if (!empty($this->casters[$p = 'o:'.strtolower($p)])) { if (!empty($this->casters[$p = 'o:'.strtolower($p)])) {
foreach ($this->casters[$p] as $p) { foreach ($this->casters[$p] as $p) {
$a = $this->callCaster($p, $obj, $a, $isNested, $cut); $a = $this->callCaster($p, $obj, $a, $stub, $isNested);
} }
} }
} }
@ -220,19 +226,20 @@ abstract class AbstractCloner implements ClonerInterface
/** /**
* Casts a resource to an array representation. * Casts a resource to an array representation.
* *
* @param string $type The type of the resource.
* @param resource $res The resource. * @param resource $res The resource.
* @param Stub $stub The Stub for the casted resource.
* @param bool $isNested True if the object is nested in the dumped structure. * @param bool $isNested True if the object is nested in the dumped structure.
* *
* @return array The resource casted as array. * @return array The resource casted as array.
*/ */
protected function castResource($type, $res, $isNested) protected function castResource($res, Stub $stub, $isNested)
{ {
$a = array(); $a = array();
$type = $stub->class;
if (!empty($this->casters['r:'.$type])) { if (!empty($this->casters['r:'.$type])) {
foreach ($this->casters['r:'.$type] as $c) { foreach ($this->casters['r:'.$type] as $c) {
$a = $this->callCaster($c, $res, $a, $isNested); $a = $this->callCaster($c, $res, $a, $stub, $isNested);
} }
} }
@ -245,15 +252,15 @@ abstract class AbstractCloner implements ClonerInterface
* @param callable $callback The caster. * @param callable $callback The caster.
* @param object|resource $obj The object/resource being casted. * @param object|resource $obj The object/resource being casted.
* @param array $a The result of the previous cast for chained casters. * @param array $a The result of the previous cast for chained casters.
* @param Stub $stub The Stub for the casted object/resource.
* @param bool $isNested True if $obj is nested in the dumped structure. * @param bool $isNested True if $obj is nested in the dumped structure.
* @param int &$cut After the cast, number of items removed from $obj.
* *
* @return array The casted object/resource. * @return array The casted object/resource.
*/ */
private function callCaster($callback, $obj, $a, $isNested, &$cut = 0) private function callCaster($callback, $obj, $a, $stub, $isNested)
{ {
try { try {
$cast = call_user_func_array($callback, array($obj, $a, $isNested, &$cut)); $cast = call_user_func($callback, $obj, $a, $stub, $isNested);
if (is_array($cast)) { if (is_array($cast)) {
$a = $cast; $a = $cast;

View File

@ -49,29 +49,34 @@ class Data
* @param DumperInternalsInterface $dumper The dumper being used for dumping. * @param DumperInternalsInterface $dumper The dumper being used for dumping.
* @param Cursor $cursor A cursor used for tracking dumper state position. * @param Cursor $cursor A cursor used for tracking dumper state position.
* @param array &$refs A map of all references discovered while dumping. * @param array &$refs A map of all references discovered while dumping.
* @param mixed $item A stub stdClass or the original value being dumped. * @param mixed $item A Stub object or the original value being dumped.
*/ */
private function dumpItem($dumper, $cursor, &$refs, $item) private function dumpItem($dumper, $cursor, &$refs, $item)
{ {
$cursor->refIndex = $cursor->refTo = $cursor->refIsHard = false; $cursor->refIndex = $cursor->softRefTo = $cursor->hardRefTo = false;
if ($item instanceof \stdClass) { if (!$item instanceof Stub) {
if (property_exists($item, 'val')) { $type = gettype($item);
if (isset($item->ref)) { } elseif (Stub::TYPE_REF === $item->type) {
if ($item->ref) {
if (isset($refs[$r = $item->ref])) { if (isset($refs[$r = $item->ref])) {
$cursor->refTo = $refs[$r]; $cursor->hardRefTo = $refs[$r];
$cursor->refIsHard = true;
} else { } else {
$cursor->refIndex = $refs[$r] = ++$refs[0]; $cursor->refIndex = $refs[$r] = ++$refs[0];
} }
} }
$item = $item->val; $type = $item->class ?: gettype($item->value);
$item = $item->value;
} }
if (isset($item->ref)) { if ($item instanceof Stub) {
if ($item->ref) {
if (isset($refs[$r = $item->ref])) { if (isset($refs[$r = $item->ref])) {
if (false === $cursor->refTo) { if (Stub::TYPE_ARRAY === $item->type) {
$cursor->refTo = $refs[$r]; if (false === $cursor->hardRefTo) {
$cursor->refIsHard = isset($item->count); $cursor->hardRefTo = $refs[$r];
}
} elseif (false === $cursor->softRefTo) {
$cursor->softRefTo = $refs[$r];
} }
} elseif (false !== $cursor->refIndex) { } elseif (false !== $cursor->refIndex) {
$refs[$r] = $cursor->refIndex; $refs[$r] = $cursor->refIndex;
@ -79,10 +84,10 @@ class Data
$cursor->refIndex = $refs[$r] = ++$refs[0]; $cursor->refIndex = $refs[$r] = ++$refs[0];
} }
} }
$cut = isset($item->cut) ? $item->cut : 0; $cut = $item->cut;
if (isset($item->pos) && false === $cursor->refTo) { if ($item->position && false === $cursor->softRefTo && false === $cursor->hardRefTo) {
$children = $this->data[$item->pos]; $children = $this->data[$item->position];
if ($cursor->stop) { if ($cursor->stop) {
if ($cut >= 0) { if ($cut >= 0) {
@ -93,41 +98,33 @@ class Data
} else { } else {
$children = array(); $children = array();
} }
switch (true) { switch ($item->type) {
case isset($item->bin): case Stub::TYPE_STRING:
$dumper->dumpString($cursor, $item->bin, true, $cut); $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut);
break;
return; case Stub::TYPE_ARRAY:
$dumper->enterArray($cursor, $item->value, Stub::ARRAY_INDEXED === $item->class, (bool) $children);
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->class);
$dumper->leaveArray($cursor, $item->value, Stub::ARRAY_INDEXED === $item->class, (bool) $children, $cut);
break;
case isset($item->str): case Stub::TYPE_OBJECT:
$dumper->dumpString($cursor, $item->str, false, $cut);
return;
case isset($item->count):
$dumper->enterArray($cursor, $item->count, !empty($item->indexed), (bool) $children);
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, empty($item->indexed) ? $cursor::HASH_ASSOC : $cursor::HASH_INDEXED);
$dumper->leaveArray($cursor, $item->count, !empty($item->indexed), (bool) $children, $cut);
return;
case isset($item->class):
$dumper->enterObject($cursor, $item->class, (bool) $children); $dumper->enterObject($cursor, $item->class, (bool) $children);
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $cursor::HASH_OBJECT); $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, Cursor::HASH_OBJECT);
$dumper->leaveObject($cursor, $item->class, (bool) $children, $cut); $dumper->leaveObject($cursor, $item->class, (bool) $children, $cut);
break;
return; case Stub::TYPE_RESOURCE:
$dumper->enterResource($cursor, $item->class, (bool) $children);
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, Cursor::HASH_RESOURCE);
$dumper->leaveResource($cursor, $item->class, (bool) $children, $cut);
break;
case isset($item->res): default:
$dumper->enterResource($cursor, $item->res, (bool) $children); throw new \RuntimeException(sprintf('Unexpected Stub type: %s', $item->type));
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $cursor::HASH_RESOURCE);
$dumper->leaveResource($cursor, $item->res, (bool) $children, $cut);
return;
} }
} } elseif ('array' === $type) {
if ('array' === $type = gettype($item)) {
$dumper->enterArray($cursor, 0, true, 0, 0); $dumper->enterArray($cursor, 0, true, 0, 0);
$dumper->leaveArray($cursor, 0, true, 0, 0); $dumper->leaveArray($cursor, 0, true, 0, 0);
} else { } else {
@ -143,7 +140,7 @@ class Data
* @param array &$refs A map of all references discovered while dumping. * @param array &$refs A map of all references discovered while dumping.
* @param array $children The children to dump. * @param array $children The children to dump.
* @param int $hashCut The number of items removed from the original hash. * @param int $hashCut The number of items removed from the original hash.
* @param int $hashType A Cursor::HASH_* const. * @param string $hashType A Cursor::HASH_* const.
* *
* @return int The final number of removed items. * @return int The final number of removed items.
*/ */

View File

@ -32,7 +32,7 @@ class ExtCloner extends AbstractCloner
$maxItems = $this->maxItems; $maxItems = $this->maxItems;
$maxString = $this->maxString; $maxString = $this->maxString;
$a = null; // Array cast for nested structures $a = null; // Array cast for nested structures
$stub = null; // stdClass capturing the main properties of an original item value, $stub = null; // Stub capturing the main properties of an original item value,
// or null if the original value is used directly // or null if the original value is used directly
for ($i = 0; $i < $len; ++$i) { for ($i = 0; $i < $len; ++$i) {
@ -60,15 +60,20 @@ class ExtCloner extends AbstractCloner
switch ($zval['type']) { switch ($zval['type']) {
case 'string': case 'string':
if (isset($v[0]) && !preg_match('//u', $v)) { if (isset($v[0]) && !preg_match('//u', $v)) {
$stub = new Stub();
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_BINARY;
if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) { if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) {
$stub = substr_replace($v, '', -$cut); $stub->cut = $cut;
$stub = (object) array('cut' => $cut, 'bin' => Data::utf8Encode($stub)); $v = substr_replace($v, '', -$cut);
} else {
$stub = (object) array('bin' => Data::utf8Encode($v));
} }
$stub->value = Data::utf8Encode($v);
} elseif (0 <= $maxString && isset($v[1+($maxString>>2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) { } elseif (0 <= $maxString && isset($v[1+($maxString>>2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) {
$stub = iconv_substr($v, 0, $maxString, 'UTF-8'); $stub = new Stub();
$stub = (object) array('cut' => $cut, 'str' => $stub); $stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_UTF8;
$stub->cut = $cut;
$stub->value = iconv_substr($v, 0, $maxString, 'UTF-8');
} }
break; break;
@ -77,23 +82,33 @@ class ExtCloner extends AbstractCloner
case 'array': case 'array':
if ($v) { if ($v) {
$stub = (object) array('count' => $zval['array_count']); $stub = $arrayRefs[$len] = new Stub();
$arrayRefs[$len] = $stub; $stub->type = Stub::TYPE_ARRAY;
$stub->class = Stub::ARRAY_ASSOC;
$stub->value = $zval['array_count'];
$a = $v; $a = $v;
} }
break; break;
case 'object': case 'object':
if (empty($softRefs[$h = $zval['object_hash']])) { if (empty($softRefs[$h = $zval['object_hash']])) {
$stub = $softRefs[$h] = (object) array('class' => $zval['object_class']); $stub = new Stub();
if (0 > $maxItems || $pos < $maxItems) { $stub->type = Stub::TYPE_OBJECT;
$a = $this->castObject($stub->class, $v, 0 < $i, $cut); $stub->class = $zval['object_class'];
if ($cut) { $stub->value = $h;
$stub->cut = $cut; $a = $this->castObject($v, $stub, 0 < $i);
if (Stub::TYPE_OBJECT !== $stub->type) {
break;
} }
} else { $h = $stub->value;
$stub->cut = -1; $stub->value = '';
if (0 <= $maxItems && $maxItems <= $pos) {
$stub->cut = count($a);
$a = array();
} }
}
if (empty($softRefs[$h])) {
$softRefs[$h] = $stub;
} else { } else {
$stub = $softRefs[$h]; $stub = $softRefs[$h];
$stub->ref = ++$refs; $stub->ref = ++$refs;
@ -101,13 +116,24 @@ class ExtCloner extends AbstractCloner
break; break;
case 'resource': case 'resource':
if (empty($softRefs[$h = $zval['resource_id']])) { if (empty($softRefs[$h = (int) $v])) {
$stub = $softRefs[$h] = (object) array('res' => $zval['resource_type']); $stub = new Stub();
if (0 > $maxItems || $pos < $maxItems) { $stub->type = Stub::TYPE_RESOURCE;
$a = $this->castResource($stub->res, $v, 0 < $i); $stub->class = $zval['resource_type'];
} else { $stub->value = $h;
$stub->cut = -1; $a = $this->castResource($v, $stub, 0 < $i);
if (Stub::TYPE_RESOURCE !== $stub->type) {
break;
} }
$h = $stub->value;
$stub->value = '';
if (0 <= $maxItems && $maxItems <= $pos) {
$stub->cut = count($a);
$a = array();
}
}
if (empty($softRefs[$h])) {
$softRefs[$h] = $stub;
} else { } else {
$stub = $softRefs[$h]; $stub = $softRefs[$h];
$stub->ref = ++$refs; $stub->ref = ++$refs;
@ -117,10 +143,11 @@ class ExtCloner extends AbstractCloner
if (isset($stub)) { if (isset($stub)) {
if ($zval['zval_isref']) { if ($zval['zval_isref']) {
if (isset($stub->count)) { if (Stub::TYPE_ARRAY === $stub->type) {
$queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $stub; $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $stub;
} else { } else {
$queue[$i][$k] = $hardRefs[$zval['zval_hash']] = (object) array('val' => $stub); $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub();
$v->value = $stub;
} }
} else { } else {
$queue[$i][$k] = $stub; $queue[$i][$k] = $stub;
@ -132,16 +159,12 @@ class ExtCloner extends AbstractCloner
if ($pos < $maxItems) { if ($pos < $maxItems) {
if ($maxItems < $pos += $k) { if ($maxItems < $pos += $k) {
$a = array_slice($a, 0, $maxItems - $pos); $a = array_slice($a, 0, $maxItems - $pos);
if (empty($stub->cut)) { if ($stub->cut >= 0) {
$stub->cut = $pos - $maxItems;
} elseif ($stub->cut > 0) {
$stub->cut += $pos - $maxItems; $stub->cut += $pos - $maxItems;
} }
} }
} else { } else {
if (empty($stub->cut)) { if ($stub->cut >= 0) {
$stub->cut = $k;
} elseif ($stub->cut > 0) {
$stub->cut += $k; $stub->cut += $k;
} }
$stub = $a = null; $stub = $a = null;
@ -150,17 +173,18 @@ class ExtCloner extends AbstractCloner
} }
} }
$queue[$len] = $a; $queue[$len] = $a;
$stub->pos = $len++; $stub->position = $len++;
} }
$stub = $a = null; $stub = $a = null;
} elseif ($zval['zval_isref']) { } elseif ($zval['zval_isref']) {
$queue[$i][$k] = $hardRefs[$zval['zval_hash']] = (object) array('val' => $v); $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = new Stub();
$queue[$i][$k]->value = $v;
} }
} }
if (isset($arrayRefs[$i])) { if (isset($arrayRefs[$i])) {
if ($indexed) { if ($indexed) {
$arrayRefs[$i]->indexed = 1; $arrayRefs[$i]->class = Stub::ARRAY_INDEXED;
} }
unset($arrayRefs[$i]); unset($arrayRefs[$i]);
} }

View File

@ -35,7 +35,7 @@ class PhpCloner extends AbstractCloner
$cookie = (object) array(); // Unique object used to detect hard references $cookie = (object) array(); // Unique object used to detect hard references
$isRef = false; $isRef = false;
$a = null; // Array cast for nested structures $a = null; // Array cast for nested structures
$stub = null; // stdClass capturing the main properties of an original item value, $stub = null; // Stub capturing the main properties of an original item value,
// or null if the original value is used directly // or null if the original value is used directly
for ($i = 0; $i < $len; ++$i) { for ($i = 0; $i < $len; ++$i) {
@ -52,7 +52,7 @@ class PhpCloner extends AbstractCloner
if ($queue[$i][$k] === $cookie) { if ($queue[$i][$k] === $cookie) {
$queue[$i][$k] =& $stub; // Break hard references to make $queue completely $queue[$i][$k] =& $stub; // Break hard references to make $queue completely
unset($stub); // independent from the original structure unset($stub); // independent from the original structure
if ($v instanceof \stdClass && isset($hardRefs[spl_object_hash($v)])) { if ($v instanceof Stub && isset($hardRefs[spl_object_hash($v)])) {
$v->ref = ++$refs; $v->ref = ++$refs;
$step[$k] = $queue[$i][$k] = $v; $step[$k] = $queue[$i][$k] = $v;
continue; continue;
@ -64,15 +64,22 @@ class PhpCloner extends AbstractCloner
switch (gettype($v)) { switch (gettype($v)) {
case 'string': case 'string':
if (isset($v[0]) && !preg_match('//u', $v)) { if (isset($v[0]) && !preg_match('//u', $v)) {
$stub = new Stub();
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_BINARY;
if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) { if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) {
$stub = substr_replace($v, '', -$cut); $stub->cut = $cut;
$stub = (object) array('cut' => $cut, 'bin' => Data::utf8Encode($stub)); $cut = substr_replace($v, '', -$cut);
} else { } else {
$stub = (object) array('bin' => Data::utf8Encode($v)); $cut = $v;
} }
$stub->value = Data::utf8Encode($cut);
} elseif (0 <= $maxString && isset($v[1+($maxString>>2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) { } elseif (0 <= $maxString && isset($v[1+($maxString>>2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) {
$stub = iconv_substr($v, 0, $maxString, 'UTF-8'); $stub = new Stub();
$stub = (object) array('cut' => $cut, 'str' => $stub); $stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_UTF8;
$stub->cut = $cut;
$stub->value = iconv_substr($v, 0, $maxString, 'UTF-8');
} }
break; break;
@ -81,23 +88,33 @@ class PhpCloner extends AbstractCloner
case 'array': case 'array':
if ($v) { if ($v) {
$stub = (object) array('count' => count($v)); $stub = $arrayRefs[$len] = new Stub();
$arrayRefs[$len] = $stub; $stub->type = Stub::TYPE_ARRAY;
$stub->class = Stub::ARRAY_ASSOC;
$stub->value = count($v);
$a = $v; $a = $v;
} }
break; break;
case 'object': case 'object':
if (empty($softRefs[$h = spl_object_hash($v)])) { if (empty($softRefs[$h = spl_object_hash($v)])) {
$stub = $softRefs[$h] = (object) array('class' => get_class($v)); $stub = new Stub();
if (0 > $maxItems || $pos < $maxItems) { $stub->type = Stub::TYPE_OBJECT;
$a = $this->castObject($stub->class, $v, 0 < $i, $cut); $stub->class = get_class($v);
if ($cut) { $stub->value = $h;
$stub->cut = $cut; $a = $this->castObject($v, $stub, 0 < $i);
if (Stub::TYPE_OBJECT !== $stub->type) {
break;
} }
} else { $h = $stub->value;
$stub->cut = -1; $stub->value = '';
if (0 <= $maxItems && $maxItems <= $pos) {
$stub->cut = count($a);
$a = array();
} }
}
if (empty($softRefs[$h])) {
$softRefs[$h] = $stub;
} else { } else {
$stub = $softRefs[$h]; $stub = $softRefs[$h];
$stub->ref = ++$refs; $stub->ref = ++$refs;
@ -106,13 +123,24 @@ class PhpCloner extends AbstractCloner
case 'resource': case 'resource':
case 'unknown type': case 'unknown type':
if (empty($softRefs[$h = (int) substr_replace($v, '', 0, 13)])) { if (empty($softRefs[$h = (int) $v])) {
$stub = $softRefs[$h] = (object) array('res' => @get_resource_type($v)); $stub = new Stub();
if (0 > $maxItems || $pos < $maxItems) { $stub->type = Stub::TYPE_RESOURCE;
$a = $this->castResource($stub->res, $v, 0 < $i); $stub->class = get_resource_type($v);
} else { $stub->value = $h;
$stub->cut = -1; $a = $this->castResource($v, $stub, 0 < $i);
if (Stub::TYPE_RESOURCE !== $stub->type) {
break;
} }
$h = $stub->value;
$stub->value = '';
if (0 <= $maxItems && $maxItems <= $pos) {
$stub->cut = count($a);
$a = array();
}
}
if (empty($softRefs[$h])) {
$softRefs[$h] = $stub;
} else { } else {
$stub = $softRefs[$h]; $stub = $softRefs[$h];
$stub->ref = ++$refs; $stub->ref = ++$refs;
@ -122,10 +150,11 @@ class PhpCloner extends AbstractCloner
if (isset($stub)) { if (isset($stub)) {
if ($isRef) { if ($isRef) {
if (isset($stub->count)) { if (Stub::TYPE_ARRAY === $stub->type) {
$step[$k] = $stub; $step[$k] = $stub;
} else { } else {
$step[$k] = (object) array('val' => $stub); $step[$k] = new Stub();
$step[$k]->value = $stub;
} }
$h = spl_object_hash($step[$k]); $h = spl_object_hash($step[$k]);
$queue[$i][$k] = $hardRefs[$h] =& $step[$k]; $queue[$i][$k] = $hardRefs[$h] =& $step[$k];
@ -141,16 +170,12 @@ class PhpCloner extends AbstractCloner
if ($pos < $maxItems) { if ($pos < $maxItems) {
if ($maxItems < $pos += $k) { if ($maxItems < $pos += $k) {
$a = array_slice($a, 0, $maxItems - $pos); $a = array_slice($a, 0, $maxItems - $pos);
if (empty($stub->cut)) { if ($stub->cut >= 0) {
$stub->cut = $pos - $maxItems;
} elseif ($stub->cut > 0) {
$stub->cut += $pos - $maxItems; $stub->cut += $pos - $maxItems;
} }
} }
} else { } else {
if (empty($stub->cut)) { if ($stub->cut >= 0) {
$stub->cut = $k;
} elseif ($stub->cut > 0) {
$stub->cut += $k; $stub->cut += $k;
} }
$stub = $a = null; $stub = $a = null;
@ -159,21 +184,22 @@ class PhpCloner extends AbstractCloner
} }
} }
$queue[$len] = $a; $queue[$len] = $a;
$stub->pos = $len++; $stub->position = $len++;
} }
$stub = $a = null; $stub = $a = null;
} elseif ($isRef) { } elseif ($isRef) {
$step[$k] = $queue[$i][$k] = $v = (object) array('val' => $v); $step[$k] = $queue[$i][$k] = new Stub();
$h = spl_object_hash($v); $step[$k]->value = $v;
$h = spl_object_hash($step[$k]);
$hardRefs[$h] =& $step[$k]; $hardRefs[$h] =& $step[$k];
$values[$h] = $v->val; $values[$h] = $v;
$isRef = false; $isRef = false;
} }
} }
if (isset($arrayRefs[$i])) { if (isset($arrayRefs[$i])) {
if ($indexed) { if ($indexed) {
$arrayRefs[$i]->indexed = 1; $arrayRefs[$i]->class = Stub::ARRAY_INDEXED;
} }
unset($arrayRefs[$i]); unset($arrayRefs[$i]);
} }

View File

@ -0,0 +1,39 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\VarDumper\Cloner;
/**
* Represents the main properties of a PHP variable.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class Stub
{
const TYPE_REF = 'ref';
const TYPE_STRING = 'string';
const TYPE_ARRAY = 'array';
const TYPE_OBJECT = 'object';
const TYPE_RESOURCE = 'resource';
const STRING_BINARY = 'bin';
const STRING_UTF8 = 'utf8';
const ARRAY_ASSOC = 'assoc';
const ARRAY_INDEXED = 'indexed';
public $type = self::TYPE_REF;
public $class = '';
public $value;
public $cut = 0;
public $ref = 0;
public $position = 0;
}

View File

@ -93,7 +93,7 @@ class CliDumper extends AbstractDumper
$style = 'const'; $style = 'const';
switch ($type) { switch ($type) {
case 'int': case 'integer':
$style = 'num'; $style = 'num';
break; break;
@ -124,10 +124,6 @@ class CliDumper extends AbstractDumper
$this->line .= $this->style($style, $val); $this->line .= $this->style($style, $val);
if (false !== $cursor->refTo) {
$this->line .= ' '.$this->style('ref', '&'.$cursor->refTo);
}
$this->endLine($cursor); $this->endLine($cursor);
} }
@ -140,14 +136,11 @@ class CliDumper extends AbstractDumper
if ('' === $str) { if ('' === $str) {
$this->line .= '""'; $this->line .= '""';
if (false !== $cursor->refTo) {
$this->line .= ' '.$this->style('ref', '&'.$cursor->refTo);
}
$this->endLine($cursor); $this->endLine($cursor);
} else { } else {
$str = explode("\n", $str); $str = explode("\n", $str);
$m = count($str) - 1; $m = count($str) - 1;
$i = 0; $i = $lineCut = 0;
if ($bin) { if ($bin) {
$this->line .= 'b'; $this->line .= 'b';
@ -155,9 +148,6 @@ class CliDumper extends AbstractDumper
if ($m) { if ($m) {
$this->line .= '"""'; $this->line .= '"""';
if (false !== $cursor->refTo) {
$this->line .= $this->style('ref', '&'.$cursor->refTo);
}
$this->endLine($cursor); $this->endLine($cursor);
} else { } else {
$this->line .= '"'; $this->line .= '"';
@ -165,32 +155,31 @@ class CliDumper extends AbstractDumper
foreach ($str as $str) { foreach ($str as $str) {
if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = iconv_strlen($str, 'UTF-8')) { if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = iconv_strlen($str, 'UTF-8')) {
$str = iconv_substr($str, 0, $this->maxStringWidth - 1, 'UTF-8'); $str = iconv_substr($str, 0, $this->maxStringWidth, 'UTF-8');
$str = $this->style('str', $str).'…'; $lineCut = $len - $this->maxStringWidth;
} else {
$str = $this->style('str', $str);
} }
if ($m) { if ($m) {
$this->line .= $this->indentPad; $this->line .= $this->indentPad;
} }
$this->line .= $str; $this->line .= $this->style('str', $str);
if ($i++ == $m) { if ($i++ == $m) {
if ($cut) {
if (0 >= $this->maxStringWidth || $this->maxStringWidth >= $len) {
$this->line .= '…';
}
$this->line .= $cut;
} elseif ($m) {
$this->line .= '"""';
} else {
$this->line .= '"'; $this->line .= '"';
if ($m) {
$this->line .= '""';
} }
if (!$m && false !== $cursor->refTo) { if ($cut < 0) {
$this->line .= $this->style('ref', '&'.$cursor->refTo); $this->line .= '…';
$lineCut = 0;
} elseif ($cut) {
$lineCut += $cut;
} }
} }
if ($lineCut) {
$this->line .= '…'.$lineCut;
$lineCut = 0;
}
$this->endLine($cursor, !$m); $this->endLine($cursor, !$m);
} }
@ -257,8 +246,10 @@ class CliDumper extends AbstractDumper
$this->dumpKey($cursor); $this->dumpKey($cursor);
$this->line .= $prefix; $this->line .= $prefix;
if (false !== $cursor->refTo) { if (false !== $cursor->softRefTo) {
$this->line .= $this->style('ref', ($cursor->refIsHard ? '&' : '@').$cursor->refTo); $this->line .= $this->style('ref', '@'.$cursor->softRefTo);
} elseif (false !== $cursor->hardRefTo) {
$this->line .= $this->style('ref', '@'.$cursor->hardRefTo);
} elseif ($hasChild) { } elseif ($hasChild) {
$this->endLine($cursor); $this->endLine($cursor);
} }
@ -274,7 +265,7 @@ class CliDumper extends AbstractDumper
*/ */
protected function leaveHash(Cursor $cursor, $suffix, $hasChild, $cut) protected function leaveHash(Cursor $cursor, $suffix, $hasChild, $cut)
{ {
if ($cut && false === $cursor->refTo) { if ($cut && false === $cursor->softRefTo && false === $cursor->hardRefTo) {
$this->line .= '…'; $this->line .= '…';
if (0 < $cut) { if (0 < $cut) {
$this->line .= $cut; $this->line .= $cut;
@ -336,6 +327,10 @@ class CliDumper extends AbstractDumper
} }
break; break;
} }
if (false !== $cursor->hardRefTo) {
$this->line .= $this->style('ref', '&'.$cursor->hardRefTo).' ';
}
} }
} }

View File

@ -11,6 +11,8 @@
namespace Symfony\Component\VarDumper\Dumper; namespace Symfony\Component\VarDumper\Dumper;
use Symfony\Component\VarDumper\Cloner\Stub;
/** /**
* Represents the current state of a dumper while dumping. * Represents the current state of a dumper while dumping.
* *
@ -18,15 +20,15 @@ namespace Symfony\Component\VarDumper\Dumper;
*/ */
class Cursor class Cursor
{ {
const HASH_INDEXED = 'indexed-array'; const HASH_INDEXED = Stub::ARRAY_INDEXED;
const HASH_ASSOC = 'associative-array'; const HASH_ASSOC = Stub::ARRAY_ASSOC;
const HASH_OBJECT = 'object'; const HASH_OBJECT = Stub::TYPE_OBJECT;
const HASH_RESOURCE = 'resource'; const HASH_RESOURCE = Stub::TYPE_RESOURCE;
public $depth = 0; public $depth = 0;
public $refIndex = false; public $refIndex = false;
public $refTo = false; public $softRefTo = false;
public $refIsHard = false; public $hardRefTo = false;
public $hashType; public $hashType;
public $hashKey; public $hashKey;
public $hashIndex = 0; public $hashIndex = 0;

View File

@ -82,11 +82,11 @@ array:25 [
0 => {} #3 0 => {} #3
] ]
"recurs" => array:1 [ #4 "recurs" => array:1 [ #4
0 => array:1 [&4] 0 => &4 array:1 [@4]
] ]
9 => null &1 9 => &1 null
"sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {@2} "sobj" => Symfony\Component\VarDumper\Tests\Fixture\DumbFoo {@2}
"snobj" => {&3} "snobj" => &3 {@3}
"snobj2" => {@3} "snobj2" => {@3}
"file" => "{$var['file']}" "file" => "{$var['file']}"
b"bin-key-é" => "" b"bin-key-é" => ""

View File

@ -52,7 +52,7 @@ span.sf-dump-meta {color:#005FFF}
</style> </style>
<pre class=sf-dump style=white-space:pre><span class=sf-dump-0><span class=sf-dump-note>array:25</span> [ <pre class=sf-dump style=white-space:pre><span class=sf-dump-0><span class=sf-dump-note>array:25</span> [
<span class=sf-dump-1>"<span class=sf-dump-meta>number</span>" => <span class=sf-dump-const>1</span> <span class=sf-dump-1>"<span class=sf-dump-meta>number</span>" => <span class=sf-dump-num>1</span>
<span class=sf-dump-meta>0</span> => <span class=sf-dump-const>null</span> <a class=sf-dump-ref name="sf-dump-ref1">#1</a> <span class=sf-dump-meta>0</span> => <span class=sf-dump-const>null</span> <a class=sf-dump-ref name="sf-dump-ref1">#1</a>
"<span class=sf-dump-meta>const</span>" => <span class=sf-dump-num>1.1</span> "<span class=sf-dump-meta>const</span>" => <span class=sf-dump-num>1.1</span>
<span class=sf-dump-meta>1</span> => <span class=sf-dump-const>true</span> <span class=sf-dump-meta>1</span> => <span class=sf-dump-const>true</span>
@ -60,7 +60,7 @@ span.sf-dump-meta {color:#005FFF}
<span class=sf-dump-meta>3</span> => <span class=sf-dump-num>NAN</span> <span class=sf-dump-meta>3</span> => <span class=sf-dump-num>NAN</span>
<span class=sf-dump-meta>4</span> => <span class=sf-dump-num>INF</span> <span class=sf-dump-meta>4</span> => <span class=sf-dump-num>INF</span>
<span class=sf-dump-meta>5</span> => <span class=sf-dump-num>-INF</span> <span class=sf-dump-meta>5</span> => <span class=sf-dump-num>-INF</span>
<span class=sf-dump-meta>6</span> => <span class=sf-dump-const>9223372036854775807</span> <span class=sf-dump-meta>6</span> => <span class=sf-dump-num>9223372036854775807</span>
"<span class=sf-dump-meta>str</span>" => "<span class=sf-dump-str>déjà</span>" "<span class=sf-dump-meta>str</span>" => "<span class=sf-dump-str>déjà</span>"
<span class=sf-dump-meta>7</span> => b"<span class=sf-dump-str>é</span>" <span class=sf-dump-meta>7</span> => b"<span class=sf-dump-str>é</span>"
"<span class=sf-dump-meta>[]</span>" => [] "<span class=sf-dump-meta>[]</span>" => []
@ -68,7 +68,7 @@ span.sf-dump-meta {color:#005FFF}
<span class=sf-dump-2><span class=sf-dump-meta>wrapper_type</span>: "<span class=sf-dump-str>plainfile</span>" <span class=sf-dump-2><span class=sf-dump-meta>wrapper_type</span>: "<span class=sf-dump-str>plainfile</span>"
<span class=sf-dump-meta>stream_type</span>: "<span class=sf-dump-str>dir</span>" <span class=sf-dump-meta>stream_type</span>: "<span class=sf-dump-str>dir</span>"
<span class=sf-dump-meta>mode</span>: "<span class=sf-dump-str>r</span>" <span class=sf-dump-meta>mode</span>: "<span class=sf-dump-str>r</span>"
<span class=sf-dump-meta>unread_bytes</span>: <span class=sf-dump-const>0</span> <span class=sf-dump-meta>unread_bytes</span>: <span class=sf-dump-num>0</span>
<span class=sf-dump-meta>seekable</span>: <span class=sf-dump-const>true</span> <span class=sf-dump-meta>seekable</span>: <span class=sf-dump-const>true</span>
<span class=sf-dump-meta>timed_out</span>: <span class=sf-dump-const>false</span> <span class=sf-dump-meta>timed_out</span>: <span class=sf-dump-const>false</span>
<span class=sf-dump-meta>blocked</span>: <span class=sf-dump-const>true</span> <span class=sf-dump-meta>blocked</span>: <span class=sf-dump-const>true</span>
@ -92,16 +92,16 @@ span.sf-dump-meta {color:#005FFF}
<span class=sf-dump-str>}</span> <span class=sf-dump-str>}</span>
""" """
</span>} </span>}
"<span class=sf-dump-meta>line</span>" => <span class=sf-dump-const>{$var['line']}</span> "<span class=sf-dump-meta>line</span>" => <span class=sf-dump-num>{$var['line']}</span>
"<span class=sf-dump-meta>nobj</span>" => <span class=sf-dump-note>array:1</span> [ "<span class=sf-dump-meta>nobj</span>" => <span class=sf-dump-note>array:1</span> [
<span class=sf-dump-2><span class=sf-dump-meta>0</span> => {} <a class=sf-dump-ref name="sf-dump-ref3">#3</a> <span class=sf-dump-2><span class=sf-dump-meta>0</span> => {} <a class=sf-dump-ref name="sf-dump-ref3">#3</a>
</span>] </span>]
"<span class=sf-dump-meta>recurs</span>" => <span class=sf-dump-note>array:1</span> [ <a class=sf-dump-ref name="sf-dump-ref4">#4</a> "<span class=sf-dump-meta>recurs</span>" => <span class=sf-dump-note>array:1</span> [ <a class=sf-dump-ref name="sf-dump-ref4">#4</a>
<span class=sf-dump-2><span class=sf-dump-meta>0</span> => <span class=sf-dump-note>array:1</span> [<a class=sf-dump-ref href="#sf-dump-ref4">&4</a>] <span class=sf-dump-2><span class=sf-dump-meta>0</span> => <a class=sf-dump-ref href="#sf-dump-ref4">&4</a> <span class=sf-dump-note>array:1</span> [<a class=sf-dump-ref href="#sf-dump-ref4">@4</a>]
</span>] </span>]
<span class=sf-dump-meta>9</span> => <span class=sf-dump-const>null</span> <a class=sf-dump-ref href="#sf-dump-ref1">&1</a> <span class=sf-dump-meta>9</span> => <a class=sf-dump-ref href="#sf-dump-ref1">&1</a> <span class=sf-dump-const>null</span>
"<span class=sf-dump-meta>sobj</span>" => <span class=sf-dump-note>Symfony\Component\VarDumper\Tests\Fixture\DumbFoo</span> {<a class=sf-dump-ref href="#sf-dump-ref2">@2</a>} "<span class=sf-dump-meta>sobj</span>" => <span class=sf-dump-note>Symfony\Component\VarDumper\Tests\Fixture\DumbFoo</span> {<a class=sf-dump-ref href="#sf-dump-ref2">@2</a>}
"<span class=sf-dump-meta>snobj</span>" => {<a class=sf-dump-ref href="#sf-dump-ref3">&3</a>} "<span class=sf-dump-meta>snobj</span>" => <a class=sf-dump-ref href="#sf-dump-ref3">&3</a> {<a class=sf-dump-ref href="#sf-dump-ref3">@3</a>}
"<span class=sf-dump-meta>snobj2</span>" => {<a class=sf-dump-ref href="#sf-dump-ref3">@3</a>} "<span class=sf-dump-meta>snobj2</span>" => {<a class=sf-dump-ref href="#sf-dump-ref3">@3</a>}
"<span class=sf-dump-meta>file</span>" => "<span class=sf-dump-str>{$var['file']}</span>" "<span class=sf-dump-meta>file</span>" => "<span class=sf-dump-str>{$var['file']}</span>"
b"<span class=sf-dump-meta>bin-key-é</span>" => "" b"<span class=sf-dump-meta>bin-key-é</span>" => ""