maj+ header

This commit is contained in:
2018-08-22 14:46:33 +02:00
parent 28c9e9d76f
commit 3fc62ab1f1
443 changed files with 24198 additions and 6201 deletions
+6
View File
@@ -1,6 +1,12 @@
CHANGELOG
=========
3.4.0
-----
* added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth
* deprecated `MongoCaster`
2.7.0
-----
+80
View File
@@ -0,0 +1,80 @@
<?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 a list of function arguments.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class ArgsStub extends EnumStub
{
private static $parameters = array();
public function __construct(array $args, $function, $class)
{
list($variadic, $params) = self::getParameters($function, $class);
$values = array();
foreach ($args as $k => $v) {
$values[$k] = !is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v;
}
if (null === $params) {
parent::__construct($values, false);
return;
}
if (\count($values) < \count($params)) {
$params = \array_slice($params, 0, \count($values));
} elseif (\count($values) > \count($params)) {
$values[] = new EnumStub(array_splice($values, \count($params)), false);
$params[] = $variadic;
}
if (array('...') === $params) {
$this->dumpKeys = false;
$this->value = $values[0]->value;
} else {
$this->value = array_combine($params, $values);
}
}
private static function getParameters($function, $class)
{
if (isset(self::$parameters[$k = $class.'::'.$function])) {
return self::$parameters[$k];
}
try {
$r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function);
} catch (\ReflectionException $e) {
return array(null, null);
}
$variadic = '...';
$params = array();
foreach ($r->getParameters() as $v) {
$k = '$'.$v->name;
if ($v->isPassedByReference()) {
$k = '&'.$k;
}
if (method_exists($v, 'isVariadic') && $v->isVariadic()) {
$variadic .= $k;
} else {
$params[] = $k;
}
}
return self::$parameters[$k] = array($variadic, $params);
}
}
+59 -15
View File
@@ -11,10 +11,14 @@
namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
/**
* Helper for filtering out properties in casters.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @final
*/
class Caster
{
@@ -36,31 +40,57 @@ class Caster
/**
* Casts objects to arrays and adds the dynamic property prefix.
*
* @param object $obj The object to cast
* @param \ReflectionClass $reflector The class reflector to use for inspecting the object definition
* @param object $obj The object to cast
* @param string $class The class of the object
* @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not
*
* @return array The array-cast of the object, with prefixed dynamic properties
*/
public static function castObject($obj, \ReflectionClass $reflector)
public static function castObject($obj, $class, $hasDebugInfo = false)
{
if ($reflector->hasMethod('__debugInfo')) {
if ($class instanceof \ReflectionClass) {
@trigger_error(sprintf('Passing a ReflectionClass to "%s()" is deprecated since Symfony 3.3 and will be unsupported in 4.0. Pass the class name as string instead.', __METHOD__), E_USER_DEPRECATED);
$hasDebugInfo = $class->hasMethod('__debugInfo');
$class = $class->name;
}
if ($hasDebugInfo) {
$a = $obj->__debugInfo();
} elseif ($obj instanceof \Closure) {
$a = array();
} else {
$a = (array) $obj;
}
if ($obj instanceof \__PHP_Incomplete_Class) {
return $a;
}
if ($a) {
$p = array_keys($a);
foreach ($p as $i => $k) {
if (isset($k[0]) ? "\0" !== $k[0] && !$reflector->hasProperty($k) : \PHP_VERSION_ID >= 70200) {
$p[$i] = self::PREFIX_DYNAMIC.$k;
static $publicProperties = array();
$i = 0;
$prefixedKeys = array();
foreach ($a as $k => $v) {
if (isset($k[0]) ? "\0" !== $k[0] : \PHP_VERSION_ID >= 70200) {
if (!isset($publicProperties[$class])) {
foreach (get_class_vars($class) as $prop => $v) {
$publicProperties[$class][$prop] = true;
}
}
if (!isset($publicProperties[$class][$k])) {
$prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k;
}
} elseif (isset($k[16]) && "\0" === $k[16] && 0 === strpos($k, "\0class@anonymous\0")) {
$p[$i] = "\0".$reflector->getParentClass().'@anonymous'.strrchr($k, "\0");
$prefixedKeys[$i] = "\0".get_parent_class($class).'@anonymous'.strrchr($k, "\0");
}
++$i;
}
if ($prefixedKeys) {
$keys = array_keys($a);
foreach ($prefixedKeys as $i => $k) {
$keys[$i] = $k;
}
$a = array_combine($keys, $a);
}
$a = array_combine($p, $a);
}
return $a;
@@ -75,24 +105,27 @@ class Caster
* @param array $a The array containing the properties to filter
* @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out
* @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set
* @param int &$count Set to the number of removed properties
*
* @return array The filtered array
*/
public static function filter(array $a, $filter, array $listedProperties = array())
public static function filter(array $a, $filter, array $listedProperties = array(), &$count = 0)
{
$count = 0;
foreach ($a as $k => $v) {
$type = self::EXCLUDE_STRICT & $filter;
if (null === $v) {
$type |= self::EXCLUDE_NULL & $filter;
}
if (empty($v)) {
$type |= self::EXCLUDE_EMPTY & $filter;
} elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || array() === $v) {
$type |= self::EXCLUDE_EMPTY & $filter;
}
if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !in_array($k, $listedProperties, true)) {
if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) {
$type |= self::EXCLUDE_NOT_IMPORTANT;
}
if ((self::EXCLUDE_VERBOSE & $filter) && in_array($k, $listedProperties, true)) {
if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) {
$type |= self::EXCLUDE_VERBOSE;
}
@@ -110,9 +143,20 @@ class Caster
if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) {
unset($a[$k]);
++$count;
}
}
return $a;
}
public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, $isNested)
{
if (isset($a['__PHP_Incomplete_Class_Name'])) {
$stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')';
unset($a['__PHP_Incomplete_Class_Name']);
}
return $a;
}
}
+87
View File
@@ -0,0 +1,87 @@
<?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;
/**
* Represents a PHP class identifier.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class ClassStub extends ConstStub
{
/**
* @param string A PHP identifier, e.g. a class, method, interface, etc. name
* @param callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier
*/
public function __construct($identifier, $callable = null)
{
$this->value = $identifier;
if (0 < $i = strrpos($identifier, '\\')) {
$this->attr['ellipsis'] = \strlen($identifier) - $i;
$this->attr['ellipsis-type'] = 'class';
$this->attr['ellipsis-tail'] = 1;
}
try {
if (null !== $callable) {
if ($callable instanceof \Closure) {
$r = new \ReflectionFunction($callable);
} elseif (\is_object($callable)) {
$r = array($callable, '__invoke');
} elseif (\is_array($callable)) {
$r = $callable;
} elseif (false !== $i = strpos($callable, '::')) {
$r = array(substr($callable, 0, $i), substr($callable, 2 + $i));
} else {
$r = new \ReflectionFunction($callable);
}
} elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) {
$r = array(substr($identifier, 0, $i), substr($identifier, 2 + $i));
} else {
$r = new \ReflectionClass($identifier);
}
if (\is_array($r)) {
try {
$r = new \ReflectionMethod($r[0], $r[1]);
} catch (\ReflectionException $e) {
$r = new \ReflectionClass($r[0]);
}
}
} catch (\ReflectionException $e) {
return;
}
if ($f = $r->getFileName()) {
$this->attr['file'] = $f;
$this->attr['line'] = $r->getStartLine();
}
}
public static function wrapCallable($callable)
{
if (\is_object($callable) || !\is_callable($callable)) {
return $callable;
}
if (!\is_array($callable)) {
$callable = new static($callable);
} elseif (\is_string($callable[0])) {
$callable[0] = new static($callable[0]);
} else {
$callable[1] = new static($callable[1], $callable);
}
return $callable;
}
}
+5
View File
@@ -25,4 +25,9 @@ class ConstStub extends Stub
$this->class = $name;
$this->value = $value;
}
public function __toString()
{
return (string) $this->value;
}
}
+1 -1
View File
@@ -25,6 +25,6 @@ class CutArrayStub extends CutStub
parent::__construct($value);
$this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys));
$this->cut -= count($this->preservedSubset);
$this->cut -= \count($this->preservedSubset);
}
}
+4 -4
View File
@@ -24,17 +24,17 @@ class CutStub extends Stub
{
$this->value = $value;
switch (gettype($value)) {
switch (\gettype($value)) {
case 'object':
$this->type = self::TYPE_OBJECT;
$this->class = get_class($value);
$this->class = \get_class($value);
$this->cut = -1;
break;
case 'array':
$this->type = self::TYPE_ARRAY;
$this->class = self::ARRAY_ASSOC;
$this->cut = $this->value = count($value);
$this->cut = $this->value = \count($value);
break;
case 'resource':
@@ -51,7 +51,7 @@ class CutStub extends Stub
case 'string':
$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) : mb_strlen($value, 'UTF-8');
$this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8');
$this->value = '';
break;
}
+3 -3
View File
@@ -107,7 +107,7 @@ class DOMCaster
'namespaceURI' => $dom->namespaceURI,
'prefix' => $dom->prefix,
'localName' => $dom->localName,
'baseURI' => $dom->baseURI,
'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI,
'textContent' => new CutStub($dom->textContent),
);
@@ -144,7 +144,7 @@ class DOMCaster
'version' => $dom->version,
'xmlVersion' => $dom->xmlVersion,
'strictErrorChecking' => $dom->strictErrorChecking,
'documentURI' => $dom->documentURI,
'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI,
'config' => $dom->config,
'formatOutput' => $dom->formatOutput,
'validateOnParse' => $dom->validateOnParse,
@@ -237,7 +237,7 @@ class DOMCaster
'columnNumber' => $dom->columnNumber,
'offset' => $dom->offset,
'relatedNode' => $dom->relatedNode,
'uri' => $dom->uri,
'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri,
);
return $a;
+129
View File
@@ -0,0 +1,129 @@
<?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 DateTimeInterface related classes to array representation.
*
* @author Dany Maillard <danymaillard93b@gmail.com>
*/
class DateCaster
{
public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, $isNested, $filter)
{
$prefix = Caster::PREFIX_VIRTUAL;
$location = $d->getTimezone()->getLocation();
$fromNow = (new \DateTime())->diff($d);
$title = $d->format('l, F j, Y')
."\n".self::formatInterval($fromNow).' from now'
.($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '')
;
$a = array();
$a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title);
$stub->class .= $d->format(' @U');
return $a;
}
public static function castInterval(\DateInterval $interval, array $a, Stub $stub, $isNested, $filter)
{
$now = new \DateTimeImmutable();
$numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp();
$title = number_format($numberOfSeconds, 0, '.', ' ').'s';
$i = array(Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title));
return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a;
}
private static function formatInterval(\DateInterval $i)
{
$format = '%R ';
if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) {
$i = date_diff($d = new \DateTime(), date_add(clone $d, $i)); // recalculate carry over points
$format .= 0 < $i->days ? '%ad ' : '';
} else {
$format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : '');
}
if (\PHP_VERSION_ID >= 70100 && isset($i->f)) {
$format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : '';
} else {
$format .= $i->h || $i->i || $i->s ? '%H:%I:%S' : '';
}
$format = '%R ' === $format ? '0s' : $format;
return $i->format(rtrim($format));
}
public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, $isNested, $filter)
{
$location = $timeZone->getLocation();
$formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P');
$title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code'], \Locale::getDefault()) : '';
$z = array(Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title));
return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a;
}
public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, $isNested, $filter)
{
if (\defined('HHVM_VERSION_ID') || \PHP_VERSION_ID < 50620 || (\PHP_VERSION_ID >= 70000 && \PHP_VERSION_ID < 70005)) { // see https://bugs.php.net/bug.php?id=71635
return $a;
}
$dates = array();
if (\PHP_VERSION_ID >= 70107) { // see https://bugs.php.net/bug.php?id=74639
foreach (clone $p as $i => $d) {
if (3 === $i) {
$now = new \DateTimeImmutable();
$dates[] = sprintf('%s more', ($end = $p->getEndDate())
? ceil(($end->format('U.u') - $d->format('U.u')) / ($now->add($p->getDateInterval())->format('U.u') - $now->format('U.u')))
: $p->recurrences - $i
);
break;
}
$dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d));
}
}
$period = sprintf(
'every %s, from %s (%s) %s',
self::formatInterval($p->getDateInterval()),
self::formatDateTime($p->getStartDate()),
$p->include_start_date ? 'included' : 'excluded',
($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end) : 'recurring '.$p->recurrences.' time/s'
);
$p = array(Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates)));
return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a;
}
private static function formatDateTime(\DateTimeInterface $d, $extra = '')
{
return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra);
}
private static function formatSeconds($s, $us)
{
return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us));
}
}
+1 -1
View File
@@ -12,8 +12,8 @@
namespace Symfony\Component\VarDumper\Caster;
use Doctrine\Common\Proxy\Proxy as CommonProxy;
use Doctrine\ORM\Proxy\Proxy as OrmProxy;
use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\Proxy as OrmProxy;
use Symfony\Component\VarDumper\Cloner\Stub;
/**
+4 -1
View File
@@ -20,8 +20,11 @@ use Symfony\Component\VarDumper\Cloner\Stub;
*/
class EnumStub extends Stub
{
public function __construct(array $values)
public $dumpKeys = true;
public function __construct(array $values, $dumpKeys = true)
{
$this->value = $values;
$this->dumpKeys = $dumpKeys;
}
}
+166 -103
View File
@@ -11,8 +11,9 @@
namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
use Symfony\Component\Debug\Exception\SilencedErrorContext;
use Symfony\Component\VarDumper\Cloner\Stub;
use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
/**
* Casts common Exception classes to array representation.
@@ -41,6 +42,8 @@ class ExceptionCaster
E_STRICT => 'E_STRICT',
);
private static $framesCache = array();
public static function castError(\Error $e, array $a, Stub $stub, $isNested, $filter = 0)
{
return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter);
@@ -62,17 +65,14 @@ class ExceptionCaster
public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, $isNested)
{
$trace = Caster::PREFIX_VIRTUAL.'trace';
$prefix = Caster::PREFIX_PROTECTED;
$xPrefix = "\0Exception\0";
if (isset($a[$xPrefix.'previous'], $a[$xPrefix.'trace']) && $a[$xPrefix.'previous'] instanceof \Exception) {
if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) {
$b = (array) $a[$xPrefix.'previous'];
array_unshift($b[$xPrefix.'trace'], array(
'function' => 'new '.get_class($a[$xPrefix.'previous']),
'file' => $b[$prefix.'file'],
'line' => $b[$prefix.'line'],
));
$a[$xPrefix.'trace'] = new TraceStub($b[$xPrefix.'trace'], false, 0, -1 - count($a[$xPrefix.'trace']->value));
self::traceUnshift($b[$xPrefix.'trace'], \get_class($a[$xPrefix.'previous']), $b[$prefix.'file'], $b[$prefix.'line']);
$a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value));
}
unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']);
@@ -80,6 +80,33 @@ class ExceptionCaster
return $a;
}
public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, $isNested)
{
$sPrefix = "\0".SilencedErrorContext::class."\0";
if (!isset($a[$s = $sPrefix.'severity'])) {
return $a;
}
if (isset(self::$errorTypes[$a[$s]])) {
$a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]);
}
$trace = array(array(
'file' => $a[$sPrefix.'file'],
'line' => $a[$sPrefix.'line'],
));
if (isset($a[$sPrefix.'trace'])) {
$trace = array_merge($trace, $a[$sPrefix.'trace']);
}
unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']);
$a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs);
return $a;
}
public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, $isNested)
{
if (!$isNested) {
@@ -88,45 +115,67 @@ class ExceptionCaster
$stub->class = '';
$stub->handle = 0;
$frames = $trace->value;
$prefix = Caster::PREFIX_VIRTUAL;
$a = array();
$j = count($frames);
$j = \count($frames);
if (0 > $i = $trace->sliceOffset) {
$i = max(0, $j + $i);
}
if (!isset($trace->value[$i])) {
return array();
}
$lastCall = isset($frames[$i]['function']) ? ' ==> '.(isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
$lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '';
$frames[] = array('function' => '');
$collapse = false;
for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) {
$call = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[$i]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : '???';
$f = $frames[$i];
$call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???';
$a[Caster::PREFIX_VIRTUAL.$j.'. '.$call.$lastCall] = new FrameStub(
$frame = new FrameStub(
array(
'object' => isset($frames[$i]['object']) ? $frames[$i]['object'] : null,
'class' => isset($frames[$i]['class']) ? $frames[$i]['class'] : null,
'type' => isset($frames[$i]['type']) ? $frames[$i]['type'] : null,
'function' => isset($frames[$i]['function']) ? $frames[$i]['function'] : null,
'object' => isset($f['object']) ? $f['object'] : null,
'class' => isset($f['class']) ? $f['class'] : null,
'type' => isset($f['type']) ? $f['type'] : null,
'function' => isset($f['function']) ? $f['function'] : null,
) + $frames[$i - 1],
$trace->keepArgs,
false,
true
);
$f = self::castFrameStub($frame, array(), $frame, true);
if (isset($f[$prefix.'src'])) {
foreach ($f[$prefix.'src']->value as $label => $frame) {
if (0 === strpos($label, "\0~collapse=0")) {
if ($collapse) {
$label = substr_replace($label, '1', 11, 1);
} else {
$collapse = true;
}
}
$label = substr_replace($label, "title=Stack level $j.&", 2, 0);
}
$f = $frames[$i - 1];
if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) {
$frame->value['arguments'] = new ArgsStub($f['args'], isset($f['function']) ? $f['function'] : null, isset($f['class']) ? $f['class'] : null);
}
} elseif ('???' !== $lastCall) {
$label = new ClassStub($lastCall);
if (isset($label->attr['ellipsis'])) {
$label->attr['ellipsis'] += 2;
$label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()';
} else {
$label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()';
}
} else {
$label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall;
}
$a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame;
$lastCall = ' ==> '.$call;
$lastCall = $call;
}
$a[Caster::PREFIX_VIRTUAL.$j.'. {main}'.$lastCall] = new FrameStub(
array(
'object' => null,
'class' => null,
'type' => null,
'function' => '{main}',
) + $frames[$i - 1],
$trace->keepArgs,
true
);
if (null !== $trace->sliceLength) {
$a = array_slice($a, 0, $trace->sliceLength, true);
$a = \array_slice($a, 0, $trace->sliceLength, true);
}
return $a;
@@ -141,35 +190,57 @@ class ExceptionCaster
$prefix = Caster::PREFIX_VIRTUAL;
if (isset($f['file'], $f['line'])) {
if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) {
$f['file'] = substr($f['file'], 0, -strlen($match[0]));
$f['line'] = (int) $match[1];
}
if (file_exists($f['file']) && 0 <= self::$srcContext) {
$src[$f['file'].':'.$f['line']] = self::extractSource(explode("\n", file_get_contents($f['file'])), $f['line'], self::$srcContext);
$cacheKey = $f;
unset($cacheKey['object'], $cacheKey['args']);
$cacheKey[] = self::$srcContext;
$cacheKey = implode('-', $cacheKey);
if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) {
$template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', strlen($f['class']), $f['class']));
if (isset(self::$framesCache[$cacheKey])) {
$a[$prefix.'src'] = self::$framesCache[$cacheKey];
} else {
if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) {
$f['file'] = substr($f['file'], 0, -\strlen($match[0]));
$f['line'] = (int) $match[1];
}
$caller = isset($f['function']) ? sprintf('in %s() on line %d', (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'], $f['line']) : null;
$src = $f['line'];
$srcKey = $f['file'];
$ellipsis = new LinkStub($srcKey, 0);
$srcAttr = 'collapse='.(int) $ellipsis->inVendor;
$ellipsisTail = isset($ellipsis->attr['ellipsis-tail']) ? $ellipsis->attr['ellipsis-tail'] : 0;
$ellipsis = isset($ellipsis->attr['ellipsis']) ? $ellipsis->attr['ellipsis'] : 0;
$templateName = $template->getTemplateName();
$templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
$templateInfo = $template->getDebugInfo();
if (isset($templateInfo[$f['line']])) {
if (method_exists($template, 'getSourceContext')) {
$templateName = $template->getSourceContext()->getPath() ?: $templateName;
}
if ($templateSrc) {
$templateSrc = explode("\n", $templateSrc);
$src[$templateName.':'.$templateInfo[$f['line']]] = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext);
} else {
$src[$templateName] = $templateInfo[$f['line']];
if (file_exists($f['file']) && 0 <= self::$srcContext) {
if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) {
$template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class']));
$ellipsis = 0;
$templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : '');
$templateInfo = $template->getDebugInfo();
if (isset($templateInfo[$f['line']])) {
if (!method_exists($template, 'getSourceContext') || !file_exists($templatePath = $template->getSourceContext()->getPath())) {
$templatePath = null;
}
if ($templateSrc) {
$src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, $caller, 'twig', $templatePath);
$srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']];
}
}
}
if ($srcKey == $f['file']) {
$src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, $caller, 'php', $f['file']);
$srcKey .= ':'.$f['line'];
if ($ellipsis) {
$ellipsis += 1 + \strlen($f['line']);
}
}
$srcAttr .= '&separator= ';
} else {
$srcAttr .= '&separator=:';
}
} else {
$src[$f['file']] = $f['line'];
$srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : '';
self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(array("\0~$srcAttr\0$srcKey" => $src));
}
$a[$prefix.'src'] = new EnumStub($src);
}
unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']);
@@ -181,49 +252,13 @@ class ExceptionCaster
unset($a[$k]);
}
}
if ($frame->keepArgs && isset($f['args'])) {
$a[$prefix.'args'] = $f['args'];
if ($frame->keepArgs && !empty($f['args'])) {
$a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']);
}
return $a;
}
/**
* @deprecated since 2.8, to be removed in 3.0. Use the castTraceStub method instead.
*/
public static function filterTrace(&$trace, $dumpArgs, $offset = 0)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0. Use the castTraceStub method instead.', E_USER_DEPRECATED);
if (0 > $offset || empty($trace[$offset])) {
return $trace = null;
}
$t = $trace[$offset];
if (empty($t['class']) && isset($t['function'])) {
if ('user_error' === $t['function'] || 'trigger_error' === $t['function']) {
++$offset;
}
}
if ($offset) {
array_splice($trace, 0, $offset);
}
foreach ($trace as &$t) {
$t = array(
'call' => (isset($t['class']) ? $t['class'].$t['type'] : '').$t['function'].'()',
'file' => isset($t['line']) ? "{$t['file']}:{$t['line']}" : '',
'args' => &$t['args'],
);
if (!isset($t['args']) || !$dumpArgs) {
unset($t['args']);
}
}
}
private static function filterExceptionArray($xClass, array $a, $xPrefix, $filter)
{
if (isset($a[$xPrefix.'trace'])) {
@@ -233,32 +268,46 @@ class ExceptionCaster
$trace = array();
}
if (!($filter & Caster::EXCLUDE_VERBOSE)) {
if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) {
if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) {
array_unshift($trace, array(
'function' => $xClass ? 'new '.$xClass : null,
'file' => $a[Caster::PREFIX_PROTECTED.'file'],
'line' => $a[Caster::PREFIX_PROTECTED.'line'],
));
self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']);
}
$a[$xPrefix.'trace'] = new TraceStub($trace, self::$traceArgs);
$a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs);
}
if (empty($a[$xPrefix.'previous'])) {
unset($a[$xPrefix.'previous']);
}
unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']);
if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) {
$a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']);
}
return $a;
}
private static function extractSource(array $srcArray, $line, $srcContext)
private static function traceUnshift(&$trace, $class, $file, $line)
{
if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) {
return;
}
array_unshift($trace, array(
'function' => $class ? 'new '.$class : null,
'file' => $file,
'line' => $line,
));
}
private static function extractSource($srcLines, $line, $srcContext, $title, $lang, $file = null)
{
$srcLines = explode("\n", $srcLines);
$src = array();
for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) {
$src[] = (isset($srcArray[$i]) ? $srcArray[$i] : '')."\n";
$src[] = (isset($srcLines[$i]) ? $srcLines[$i] : '')."\n";
}
$srcLines = array();
$ltrim = 0;
do {
$pad = null;
@@ -275,12 +324,26 @@ class ExceptionCaster
++$ltrim;
} while (0 > $i && null !== $pad);
if (--$ltrim) {
foreach ($src as $i => $line) {
$src[$i] = isset($line[$ltrim]) && "\r" !== $line[$ltrim] ? substr($line, $ltrim) : ltrim($line, " \t");
--$ltrim;
foreach ($src as $i => $c) {
if ($ltrim) {
$c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t");
}
$c = substr($c, 0, -1);
if ($i !== $srcContext) {
$c = new ConstStub('default', $c);
} else {
$c = new ConstStub($c, $title);
if (null !== $file) {
$c->attr['file'] = $file;
$c->attr['line'] = $line;
}
}
$c->attr['lang'] = $lang;
$srcLines[sprintf("\0~separator= &%d\0", $i + $line - $srcContext)] = $c;
}
return implode('', $src);
return new EnumStub($srcLines);
}
}
+108
View File
@@ -0,0 +1,108 @@
<?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;
/**
* Represents a file or a URL.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class LinkStub extends ConstStub
{
public $inVendor = false;
private static $vendorRoots;
private static $composerRoots;
public function __construct($label, $line = 0, $href = null)
{
$this->value = $label;
if (null === $href) {
$href = $label;
}
if (!\is_string($href)) {
return;
}
if (0 === strpos($href, 'file://')) {
if ($href === $label) {
$label = substr($label, 7);
}
$href = substr($href, 7);
} elseif (false !== strpos($href, '://')) {
$this->attr['href'] = $href;
return;
}
if (!file_exists($href)) {
return;
}
if ($line) {
$this->attr['line'] = $line;
}
if ($label !== $this->attr['file'] = realpath($href) ?: $href) {
return;
}
if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) {
$this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1;
$this->attr['ellipsis-type'] = 'path';
$this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode(\array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0);
} elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) {
$this->attr['ellipsis'] = 2 + \strlen(implode(\array_slice($ellipsis, -2)));
$this->attr['ellipsis-type'] = 'path';
$this->attr['ellipsis-tail'] = 1;
}
}
private function getComposerRoot($file, &$inVendor)
{
if (null === self::$vendorRoots) {
self::$vendorRoots = array();
foreach (get_declared_classes() as $class) {
if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) {
$r = new \ReflectionClass($class);
$v = \dirname(\dirname($r->getFileName()));
if (file_exists($v.'/composer/installed.json')) {
self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR;
}
}
}
}
$inVendor = false;
if (isset(self::$composerRoots[$dir = \dirname($file)])) {
return self::$composerRoots[$dir];
}
foreach (self::$vendorRoots as $root) {
if ($inVendor = 0 === strpos($file, $root)) {
return $root;
}
}
$parent = $dir;
while (!@file_exists($parent.'/composer.json')) {
if (!@file_exists($parent)) {
// open_basedir restriction in effect
break;
}
if ($parent === \dirname($parent)) {
return self::$composerRoots[$dir] = false;
}
$parent = \dirname($parent);
}
return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR;
}
}
+4
View File
@@ -13,10 +13,14 @@ namespace Symfony\Component\VarDumper\Caster;
use Symfony\Component\VarDumper\Cloner\Stub;
@trigger_error('The '.__NAMESPACE__.'\MongoCaster class is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
/**
* Casts classes from the MongoDb extension to array representation.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @deprecated since version 3.4, to be removed in 4.0.
*/
class MongoCaster
{
+7 -1
View File
@@ -70,13 +70,19 @@ class PdoCaster
}
try {
$attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(constant('PDO::ATTR_'.$k));
$attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k));
if ($v && isset($v[$attr[$k]])) {
$attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]);
}
} catch (\Exception $e) {
}
}
if (isset($attr[$k = 'STATEMENT_CLASS'][1])) {
if ($attr[$k][1]) {
$attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]);
}
$attr[$k][0] = new ClassStub($attr[$k][0]);
}
$prefix = Caster::PREFIX_VIRTUAL;
$a += array(
+77
View File
@@ -0,0 +1,77 @@
<?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 Redis class from ext-redis to array representation.
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class RedisCaster
{
private static $serializer = array(
\Redis::SERIALIZER_NONE => 'NONE',
\Redis::SERIALIZER_PHP => 'PHP',
2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY
);
public static function castRedis(\Redis $c, array $a, Stub $stub, $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;
if (\defined('HHVM_VERSION_ID')) {
if (isset($a[Caster::PREFIX_PROTECTED.'serializer'])) {
$ser = $a[Caster::PREFIX_PROTECTED.'serializer'];
$a[Caster::PREFIX_PROTECTED.'serializer'] = isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser;
}
return $a;
}
if (!$connected = $c->isConnected()) {
return $a + array(
$prefix.'isConnected' => $connected,
);
}
$ser = $c->getOption(\Redis::OPT_SERIALIZER);
$retry = \defined('Redis::OPT_SCAN') ? $c->getOption(\Redis::OPT_SCAN) : 0;
return $a + array(
$prefix.'isConnected' => $connected,
$prefix.'host' => $c->getHost(),
$prefix.'port' => $c->getPort(),
$prefix.'auth' => $c->getAuth(),
$prefix.'dbNum' => $c->getDbNum(),
$prefix.'timeout' => $c->getTimeout(),
$prefix.'persistentId' => $c->getPersistentID(),
$prefix.'options' => new EnumStub(array(
'READ_TIMEOUT' => $c->getOption(\Redis::OPT_READ_TIMEOUT),
'SERIALIZER' => isset(self::$serializer[$ser]) ? new ConstStub(self::$serializer[$ser], $ser) : $ser,
'PREFIX' => $c->getOption(\Redis::OPT_PREFIX),
'SCAN' => new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry),
)),
);
}
public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, $isNested)
{
$prefix = Caster::PREFIX_VIRTUAL;
return $a + array(
$prefix.'hosts' => $c->_hosts(),
$prefix.'function' => ClassStub::wrapCallable($c->_function()),
);
}
}
+20 -20
View File
@@ -31,24 +31,13 @@ class ReflectionCaster
'isVariadic' => 'isVariadic',
);
/**
* @deprecated since Symfony 2.7, to be removed in 3.0.
*/
public static function castReflector(\Reflector $c, array $a, Stub $stub, $isNested)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0.', E_USER_DEPRECATED);
$a[Caster::PREFIX_VIRTUAL.'reflection'] = $c->__toString();
return $a;
}
public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested)
public static function castClosure(\Closure $c, array $a, Stub $stub, $isNested, $filter = 0)
{
$prefix = Caster::PREFIX_VIRTUAL;
$c = new \ReflectionFunction($c);
$stub->class = 'Closure'; // HHVM generates unique class names for closures
$a = static::castFunctionAbstract($c, $a, $stub, $isNested);
$a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter);
if (isset($a[$prefix.'parameters'])) {
foreach ($a[$prefix.'parameters']->value as &$v) {
@@ -63,8 +52,8 @@ class ReflectionCaster
}
}
if ($f = $c->getFileName()) {
$a[$prefix.'file'] = $f;
if (!($filter & Caster::EXCLUDE_VERBOSE) && $f = $c->getFileName()) {
$a[$prefix.'file'] = new LinkStub($f, $c->getStartLine());
$a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine();
}
@@ -133,7 +122,7 @@ class ReflectionCaster
$function = new FrameStub($frame, false, true);
$function = ExceptionCaster::castFrameStub($function, array(), $function, true);
$a[$prefix.'executing'] = new EnumStub(array(
$frame['class'].$frame['type'].$frame['function'].'()' => $function[$prefix.'src'],
"\0~separator= \0".$frame['class'].$frame['type'].$frame['function'].'()' => $function[$prefix.'src'],
));
}
@@ -185,7 +174,10 @@ class ReflectionCaster
if (isset($a[$prefix.'returnType'])) {
$v = $a[$prefix.'returnType'];
$v = $v instanceof \ReflectionNamedType ? $v->getName() : $v->__toString();
$a[$prefix.'returnType'] = $a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v;
$a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, array(class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', ''));
}
if (isset($a[$prefix.'class'])) {
$a[$prefix.'class'] = new ClassStub($a[$prefix.'class']);
}
if (isset($a[$prefix.'this'])) {
$a[$prefix.'this'] = new CutStub($a[$prefix.'this']);
@@ -207,7 +199,11 @@ class ReflectionCaster
if ($v = $c->getStaticVariables()) {
foreach ($v as $k => &$v) {
$a[$prefix.'use']['$'.$k] = &$v;
if (\is_object($v)) {
$a[$prefix.'use']['$'.$k] = new CutStub($v);
} else {
$a[$prefix.'use']['$'.$k] = &$v;
}
}
unset($v);
$a[$prefix.'use'] = new EnumStub($a[$prefix.'use']);
@@ -251,7 +247,11 @@ class ReflectionCaster
} elseif (preg_match('/^(?:[^ ]++ ){4}([a-zA-Z_\x7F-\xFF][^ ]++)/', $c, $v)) {
$a[$prefix.'typeHint'] = $v[1];
}
if (!isset($a[$prefix.'typeHint'])) {
if (isset($a[$prefix.'typeHint'])) {
$v = $a[$prefix.'typeHint'];
$a[$prefix.'typeHint'] = new ClassStub($v, array(class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', ''));
} else {
unset($a[$prefix.'allowsNull']);
}
@@ -314,7 +314,7 @@ class ReflectionCaster
$x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : array();
if (method_exists($c, 'getFileName') && $m = $c->getFileName()) {
$x['file'] = $m;
$x['file'] = new LinkStub($m, $c->getStartLine());
$x['line'] = $c->getStartLine().' to '.$c->getEndLine();
}
+6 -1
View File
@@ -40,7 +40,12 @@ class ResourceCaster
public static function castStream($stream, array $a, Stub $stub, $isNested)
{
return stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested);
$a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested);
if (isset($a['uri'])) {
$a['uri'] = new LinkStub($a['uri']);
}
return $a;
}
public static function castStreamContext($stream, array $a, Stub $stub, $isNested)
+6 -2
View File
@@ -97,6 +97,10 @@ class SplCaster
}
}
if (isset($a[$prefix.'realPath'])) {
$a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']);
}
if (isset($a[$prefix.'perms'])) {
$a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']);
}
@@ -192,7 +196,7 @@ class SplCaster
if (!($flags & \ArrayObject::STD_PROP_LIST)) {
$c->setFlags(\ArrayObject::STD_PROP_LIST);
$a = Caster::castObject($c, new \ReflectionClass($class));
$a = Caster::castObject($c, $class);
$c->setFlags($flags);
}
$a += array(
@@ -200,7 +204,7 @@ class SplCaster
$prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS),
);
if ($c instanceof \ArrayObject) {
$a[$prefix.'iteratorClass'] = $c->getIteratorClass();
$a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass());
}
$a[$prefix.'storage'] = $c->getArrayCopy();
+10 -2
View File
@@ -28,6 +28,12 @@ class StubCaster
$stub->value = $c->value;
$stub->handle = $c->handle;
$stub->cut = $c->cut;
$stub->attr = $c->attr;
if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) {
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_BINARY;
}
$a = array();
}
@@ -43,7 +49,7 @@ class StubCaster
public static function cutInternals($obj, array $a, Stub $stub, $isNested)
{
if ($isNested) {
$stub->cut += count($a);
$stub->cut += \count($a);
return array();
}
@@ -54,9 +60,11 @@ class StubCaster
public static function castEnum(EnumStub $c, array $a, Stub $stub, $isNested)
{
if ($isNested) {
$stub->class = '';
$stub->class = $c->dumpKeys ? '' : null;
$stub->handle = 0;
$stub->value = null;
$stub->cut = $c->cut;
$stub->attr = $c->attr;
$a = array();
+43
View File
@@ -0,0 +1,43 @@
<?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\HttpFoundation\Request;
use Symfony\Component\VarDumper\Cloner\Stub;
class SymfonyCaster
{
private static $requestGetters = array(
'pathInfo' => 'getPathInfo',
'requestUri' => 'getRequestUri',
'baseUrl' => 'getBaseUrl',
'basePath' => 'getBasePath',
'method' => 'getMethod',
'format' => 'getRequestFormat',
);
public static function castRequest(Request $request, array $a, Stub $stub, $isNested)
{
$clone = null;
foreach (self::$requestGetters as $prop => $getter) {
if (null === $a[Caster::PREFIX_PROTECTED.$prop]) {
if (null === $clone) {
$clone = clone $request;
}
$a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}();
}
}
return $a;
}
}
+77
View File
@@ -0,0 +1,77 @@
<?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 XmlReader class to array representation.
*
* @author Baptiste Clavié <clavie.b@gmail.com>
*/
class XmlReaderCaster
{
private static $nodeTypes = array(
\XMLReader::NONE => 'NONE',
\XMLReader::ELEMENT => 'ELEMENT',
\XMLReader::ATTRIBUTE => 'ATTRIBUTE',
\XMLReader::TEXT => 'TEXT',
\XMLReader::CDATA => 'CDATA',
\XMLReader::ENTITY_REF => 'ENTITY_REF',
\XMLReader::ENTITY => 'ENTITY',
\XMLReader::PI => 'PI (Processing Instruction)',
\XMLReader::COMMENT => 'COMMENT',
\XMLReader::DOC => 'DOC',
\XMLReader::DOC_TYPE => 'DOC_TYPE',
\XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT',
\XMLReader::NOTATION => 'NOTATION',
\XMLReader::WHITESPACE => 'WHITESPACE',
\XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE',
\XMLReader::END_ELEMENT => 'END_ELEMENT',
\XMLReader::END_ENTITY => 'END_ENTITY',
\XMLReader::XML_DECLARATION => 'XML_DECLARATION',
);
public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, $isNested)
{
$props = Caster::PREFIX_VIRTUAL.'parserProperties';
$info = array(
'localName' => $reader->localName,
'prefix' => $reader->prefix,
'nodeType' => new ConstStub(self::$nodeTypes[$reader->nodeType], $reader->nodeType),
'depth' => $reader->depth,
'isDefault' => $reader->isDefault,
'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement,
'xmlLang' => $reader->xmlLang,
'attributeCount' => $reader->attributeCount,
'value' => $reader->value,
'namespaceURI' => $reader->namespaceURI,
'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI,
$props => array(
'LOADDTD' => $reader->getParserProperty(\XMLReader::LOADDTD),
'DEFAULTATTRS' => $reader->getParserProperty(\XMLReader::DEFAULTATTRS),
'VALIDATE' => $reader->getParserProperty(\XMLReader::VALIDATE),
'SUBST_ENTITIES' => $reader->getParserProperty(\XMLReader::SUBST_ENTITIES),
),
);
if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, array(), $count)) {
$info[$props] = new EnumStub($info[$props]);
$info[$props]->cut = $count;
}
$info = Caster::filter($info, Caster::EXCLUDE_EMPTY, array(), $count);
// +2 because hasValue and hasAttributes are always filtered
$stub->cut += $count + 2;
return $a + $info;
}
}
+164 -154
View File
@@ -22,103 +22,118 @@ use Symfony\Component\VarDumper\Exception\ThrowingCasterException;
abstract class AbstractCloner implements ClonerInterface
{
public static $defaultCasters = array(
'Symfony\Component\VarDumper\Caster\CutStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub',
'Symfony\Component\VarDumper\Caster\CutArrayStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castCutArray',
'Symfony\Component\VarDumper\Caster\ConstStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castStub',
'Symfony\Component\VarDumper\Caster\EnumStub' => 'Symfony\Component\VarDumper\Caster\StubCaster::castEnum',
'__PHP_Incomplete_Class' => array('Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'),
'Closure' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClosure',
'Generator' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castGenerator',
'ReflectionType' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castType',
'ReflectionGenerator' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castReflectionGenerator',
'ReflectionClass' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castClass',
'ReflectionFunctionAbstract' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castFunctionAbstract',
'ReflectionMethod' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castMethod',
'ReflectionParameter' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castParameter',
'ReflectionProperty' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castProperty',
'ReflectionExtension' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castExtension',
'ReflectionZendExtension' => 'Symfony\Component\VarDumper\Caster\ReflectionCaster::castZendExtension',
'Symfony\Component\VarDumper\Caster\CutStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'),
'Symfony\Component\VarDumper\Caster\CutArrayStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'),
'Symfony\Component\VarDumper\Caster\ConstStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'),
'Symfony\Component\VarDumper\Caster\EnumStub' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'),
'Doctrine\Common\Persistence\ObjectManager' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
'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',
'Closure' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'),
'Generator' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'),
'ReflectionType' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'),
'ReflectionGenerator' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'),
'ReflectionClass' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'),
'ReflectionFunctionAbstract' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'),
'ReflectionMethod' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'),
'ReflectionParameter' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'),
'ReflectionProperty' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'),
'ReflectionExtension' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'),
'ReflectionZendExtension' => array('Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'),
'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',
'Doctrine\Common\Persistence\ObjectManager' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'),
'Doctrine\Common\Proxy\Proxy' => array('Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'),
'Doctrine\ORM\Proxy\Proxy' => array('Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'),
'Doctrine\ORM\PersistentCollection' => array('Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'),
'ErrorException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castErrorException',
'Exception' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castException',
'Error' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castError',
'Symfony\Component\DependencyInjection\ContainerInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castThrowingCasterException',
'Symfony\Component\VarDumper\Caster\TraceStub' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castTraceStub',
'Symfony\Component\VarDumper\Caster\FrameStub' => 'Symfony\Component\VarDumper\Caster\ExceptionCaster::castFrameStub',
'DOMException' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'),
'DOMStringList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'),
'DOMNameList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'),
'DOMImplementation' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'),
'DOMImplementationList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'),
'DOMNode' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'),
'DOMNameSpaceNode' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'),
'DOMDocument' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'),
'DOMNodeList' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'),
'DOMNamedNodeMap' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'),
'DOMCharacterData' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'),
'DOMAttr' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'),
'DOMElement' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'),
'DOMText' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'),
'DOMTypeinfo' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'),
'DOMDomError' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'),
'DOMLocator' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'),
'DOMDocumentType' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'),
'DOMNotation' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'),
'DOMEntity' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'),
'DOMProcessingInstruction' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'),
'DOMXPath' => array('Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'),
'PHPUnit_Framework_MockObject_MockObject' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
'Prophecy\Prophecy\ProphecySubjectInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
'Mockery\MockInterface' => 'Symfony\Component\VarDumper\Caster\StubCaster::cutInternals',
'XmlReader' => array('Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'),
'PDO' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdo',
'PDOStatement' => 'Symfony\Component\VarDumper\Caster\PdoCaster::castPdoStatement',
'ErrorException' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'),
'Exception' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'),
'Error' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'),
'Symfony\Component\DependencyInjection\ContainerInterface' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'),
'Symfony\Component\HttpFoundation\Request' => array('Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'),
'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'),
'Symfony\Component\VarDumper\Caster\TraceStub' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'),
'Symfony\Component\VarDumper\Caster\FrameStub' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'),
'Symfony\Component\Debug\Exception\SilencedErrorContext' => array('Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'),
'AMQPConnection' => 'Symfony\Component\VarDumper\Caster\AmqpCaster::castConnection',
'AMQPChannel' => 'Symfony\Component\VarDumper\Caster\AmqpCaster::castChannel',
'AMQPQueue' => 'Symfony\Component\VarDumper\Caster\AmqpCaster::castQueue',
'AMQPExchange' => 'Symfony\Component\VarDumper\Caster\AmqpCaster::castExchange',
'AMQPEnvelope' => 'Symfony\Component\VarDumper\Caster\AmqpCaster::castEnvelope',
'PHPUnit_Framework_MockObject_MockObject' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'),
'Prophecy\Prophecy\ProphecySubjectInterface' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'),
'Mockery\MockInterface' => array('Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'),
'ArrayObject' => 'Symfony\Component\VarDumper\Caster\SplCaster::castArrayObject',
'ArrayIterator' => 'Symfony\Component\VarDumper\Caster\SplCaster::castArrayIterator',
'SplDoublyLinkedList' => 'Symfony\Component\VarDumper\Caster\SplCaster::castDoublyLinkedList',
'SplFileInfo' => 'Symfony\Component\VarDumper\Caster\SplCaster::castFileInfo',
'SplFileObject' => 'Symfony\Component\VarDumper\Caster\SplCaster::castFileObject',
'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',
'OuterIterator' => 'Symfony\Component\VarDumper\Caster\SplCaster::castOuterIterator',
'PDO' => array('Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'),
'PDOStatement' => array('Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'),
'MongoCursorInterface' => 'Symfony\Component\VarDumper\Caster\MongoCaster::castCursor',
'AMQPConnection' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'),
'AMQPChannel' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'),
'AMQPQueue' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'),
'AMQPExchange' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'),
'AMQPEnvelope' => array('Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'),
':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',
':pgsql large object' => 'Symfony\Component\VarDumper\Caster\PgSqlCaster::castLargeObject',
':pgsql link' => 'Symfony\Component\VarDumper\Caster\PgSqlCaster::castLink',
':pgsql link persistent' => 'Symfony\Component\VarDumper\Caster\PgSqlCaster::castLink',
':pgsql result' => 'Symfony\Component\VarDumper\Caster\PgSqlCaster::castResult',
':process' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castProcess',
':stream' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStream',
':persistent stream' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStream',
':stream-context' => 'Symfony\Component\VarDumper\Caster\ResourceCaster::castStreamContext',
':xml' => 'Symfony\Component\VarDumper\Caster\XmlResourceCaster::castXml',
'ArrayObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'),
'ArrayIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'),
'SplDoublyLinkedList' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'),
'SplFileInfo' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'),
'SplFileObject' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'),
'SplFixedArray' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castFixedArray'),
'SplHeap' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'),
'SplObjectStorage' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'),
'SplPriorityQueue' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'),
'OuterIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'),
'MongoCursorInterface' => array('Symfony\Component\VarDumper\Caster\MongoCaster', 'castCursor'),
'Redis' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'),
'RedisArray' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'),
'DateTimeInterface' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'),
'DateInterval' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'),
'DateTimeZone' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'),
'DatePeriod' => array('Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'),
':curl' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'),
':dba' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'),
':dba persistent' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'),
':gd' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'),
':mysql link' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'),
':pgsql large object' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'),
':pgsql link' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'),
':pgsql link persistent' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'),
':pgsql result' => array('Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'),
':process' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'),
':stream' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'),
':persistent stream' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'),
':stream-context' => array('Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'),
':xml' => array('Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'),
);
protected $maxItems = 2500;
protected $maxString = -1;
protected $minDepth = 1;
protected $useExt;
private $casters = array();
@@ -137,7 +152,7 @@ abstract class AbstractCloner implements ClonerInterface
$casters = static::$defaultCasters;
}
$this->addCasters($casters);
$this->useExt = extension_loaded('symfony_debug');
$this->useExt = \extension_loaded('symfony_debug');
}
/**
@@ -153,12 +168,12 @@ abstract class AbstractCloner implements ClonerInterface
public function addCasters(array $casters)
{
foreach ($casters as $type => $callback) {
$this->casters[strtolower($type)][] = $callback;
$this->casters[strtolower($type)][] = \is_string($callback) && false !== strpos($callback, '::') ? explode('::', $callback, 2) : $callback;
}
}
/**
* Sets the maximum number of items to clone past the first level in nested structures.
* Sets the maximum number of items to clone past the minimum depth in nested structures.
*
* @param int $maxItems
*/
@@ -177,6 +192,17 @@ abstract class AbstractCloner implements ClonerInterface
$this->maxString = (int) $maxString;
}
/**
* Sets the minimum tree depth where we are guaranteed to clone all the items. After this
* depth is reached, only setMaxItems items will be cloned.
*
* @param int $minDepth
*/
public function setMinDepth($minDepth)
{
$this->minDepth = (int) $minDepth;
}
/**
* Clones a PHP variable.
*
@@ -187,20 +213,32 @@ abstract class AbstractCloner implements ClonerInterface
*/
public function cloneVar($var, $filter = 0)
{
$this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) {
if (E_RECOVERABLE_ERROR === $type || E_USER_ERROR === $type) {
// Cloner never dies
throw new \ErrorException($msg, 0, $type, $file, $line);
}
if ($this->prevErrorHandler) {
return \call_user_func($this->prevErrorHandler, $type, $msg, $file, $line, $context);
}
return false;
});
$this->filter = $filter;
$this->prevErrorHandler = set_error_handler(array($this, 'handleError'));
if ($gc = gc_enabled()) {
gc_disable();
}
try {
$data = $this->doClone($var);
} catch (\Exception $e) {
return new Data($this->doClone($var));
} finally {
if ($gc) {
gc_enable();
}
restore_error_handler();
$this->prevErrorHandler = null;
}
restore_error_handler();
$this->prevErrorHandler = null;
if (isset($e)) {
throw $e;
}
return new Data($data);
}
/**
@@ -229,24 +267,37 @@ abstract class AbstractCloner implements ClonerInterface
$stub->class = get_parent_class($class).'@anonymous';
}
if (isset($this->classInfo[$class])) {
$classInfo = $this->classInfo[$class];
list($i, $parents, $hasDebugInfo) = $this->classInfo[$class];
} else {
$classInfo = array(
new \ReflectionClass($class),
array_reverse(array($class => $class) + class_parents($class) + class_implements($class) + array('*' => '*')),
);
$i = 2;
$parents = array(strtolower($class));
$hasDebugInfo = method_exists($class, '__debugInfo');
$this->classInfo[$class] = $classInfo;
foreach (class_parents($class) as $p) {
$parents[] = strtolower($p);
++$i;
}
foreach (class_implements($class) as $p) {
$parents[] = strtolower($p);
++$i;
}
$parents[] = '*';
$this->classInfo[$class] = array($i, $parents, $hasDebugInfo);
}
$a = $this->callCaster('Symfony\Component\VarDumper\Caster\Caster::castObject', $obj, $classInfo[0], null, $isNested);
$a = Caster::castObject($obj, $class, $hasDebugInfo);
foreach ($classInfo[1] as $p) {
if (!empty($this->casters[$p = strtolower($p)])) {
foreach ($this->casters[$p] as $p) {
$a = $this->callCaster($p, $obj, $a, $stub, $isNested);
try {
while ($i--) {
if (!empty($this->casters[$p = $parents[$i]])) {
foreach ($this->casters[$p] as $callback) {
$a = $callback($obj, $a, $stub, $isNested, $this->filter);
}
}
}
} catch (\Exception $e) {
$a = array((Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)) + $a;
}
return $a;
@@ -266,57 +317,16 @@ abstract class AbstractCloner implements ClonerInterface
$res = $stub->value;
$type = $stub->class;
if (!empty($this->casters[':'.$type])) {
foreach ($this->casters[':'.$type] as $c) {
$a = $this->callCaster($c, $res, $a, $stub, $isNested);
}
}
return $a;
}
/**
* Calls a custom caster.
*
* @param callable $callback The caster
* @param object|resource $obj The object/resource being casted
* @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
*
* @return array The casted object/resource
*/
private function callCaster($callback, $obj, $a, $stub, $isNested)
{
try {
$cast = call_user_func($callback, $obj, $a, $stub, $isNested, $this->filter);
if (is_array($cast)) {
$a = $cast;
if (!empty($this->casters[':'.$type])) {
foreach ($this->casters[':'.$type] as $callback) {
$a = $callback($res, $a, $stub, $isNested, $this->filter);
}
}
} catch (\Exception $e) {
$a[(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠'] = new ThrowingCasterException($e);
$a = array((Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)) + $a;
}
return $a;
}
/**
* Special handling for errors: cloning must be fail-safe.
*
* @internal
*/
public function handleError($type, $msg, $file, $line, $context)
{
if (E_RECOVERABLE_ERROR === $type || E_USER_ERROR === $type) {
// Cloner never dies
throw new \ErrorException($msg, 0, $type, $file, $line);
}
if ($this->prevErrorHandler) {
return call_user_func($this->prevErrorHandler, $type, $msg, $file, $line, $context);
}
return false;
}
}
+2
View File
@@ -38,4 +38,6 @@ class Cursor
public $hashLength = 0;
public $hashCut = 0;
public $stop = false;
public $attr = array();
public $skipChildren = false;
}
+220 -24
View File
@@ -11,12 +11,16 @@
namespace Symfony\Component\VarDumper\Cloner;
use Symfony\Component\VarDumper\Caster\Caster;
/**
* @author Nicolas Grekas <p@tchwork.com>
*/
class Data
class Data implements \ArrayAccess, \Countable, \IteratorAggregate
{
private $data;
private $position = 0;
private $key = 0;
private $maxDepth = 20;
private $maxItemsPerDepth = -1;
private $useRefHandles = -1;
@@ -29,11 +33,147 @@ class Data
$this->data = $data;
}
/**
* @return string The type of the value
*/
public function getType()
{
$item = $this->data[$this->position][$this->key];
if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
$item = $item->value;
}
if (!$item instanceof Stub) {
return \gettype($item);
}
if (Stub::TYPE_STRING === $item->type) {
return 'string';
}
if (Stub::TYPE_ARRAY === $item->type) {
return 'array';
}
if (Stub::TYPE_OBJECT === $item->type) {
return $item->class;
}
if (Stub::TYPE_RESOURCE === $item->type) {
return $item->class.' resource';
}
}
/**
* @param bool $recursive Whether values should be resolved recursively or not
*
* @return string|int|float|bool|array|null|Data[] A native representation of the original value
*/
public function getValue($recursive = false)
{
$item = $this->data[$this->position][$this->key];
if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
$item = $item->value;
}
if (!($item = $this->getStub($item)) instanceof Stub) {
return $item;
}
if (Stub::TYPE_STRING === $item->type) {
return $item->value;
}
$children = $item->position ? $this->data[$item->position] : array();
foreach ($children as $k => $v) {
if ($recursive && !($v = $this->getStub($v)) instanceof Stub) {
continue;
}
$children[$k] = clone $this;
$children[$k]->key = $k;
$children[$k]->position = $item->position;
if ($recursive) {
if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) {
$recursive = (array) $recursive;
if (isset($recursive[$v->position])) {
continue;
}
$recursive[$v->position] = true;
}
$children[$k] = $children[$k]->getValue($recursive);
}
}
return $children;
}
public function count()
{
return \count($this->getValue());
}
public function getIterator()
{
if (!\is_array($value = $this->getValue())) {
throw new \LogicException(sprintf('%s object holds non-iterable type "%s".', self::class, \gettype($value)));
}
foreach ($value as $k => $v) {
yield $k => $v;
}
}
public function __get($key)
{
if (null !== $data = $this->seek($key)) {
$item = $this->getStub($data->data[$data->position][$data->key]);
return $item instanceof Stub || array() === $item ? $data : $item;
}
}
public function __isset($key)
{
return null !== $this->seek($key);
}
public function offsetExists($key)
{
return $this->__isset($key);
}
public function offsetGet($key)
{
return $this->__get($key);
}
public function offsetSet($key, $value)
{
throw new \BadMethodCallException(self::class.' objects are immutable.');
}
public function offsetUnset($key)
{
throw new \BadMethodCallException(self::class.' objects are immutable.');
}
public function __toString()
{
$value = $this->getValue();
if (!\is_array($value)) {
return (string) $value;
}
return sprintf('%s (count=%d)', $this->getType(), \count($value));
}
/**
* @return array The raw data structure
*
* @deprecated since version 3.3. Use array or object access instead.
*/
public function getRawData()
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the array or object access instead.', __METHOD__));
return $this->data;
}
@@ -83,24 +223,49 @@ class Data
}
/**
* Returns a depth limited clone of $this.
* Seeks to a specific key in nested data structures.
*
* @param int $maxDepth The max dumped depth level
* @param int $maxItemsPerDepth The max number of items dumped per depth level
* @param bool $useRefHandles False to hide ref. handles
* @param string|int $key The key to seek to
*
* @return self A depth limited clone of $this
*
* @deprecated since Symfony 2.7, to be removed in 3.0. Use withMaxDepth, withMaxItemsPerDepth or withRefHandles instead.
* @return self|null A clone of $this or null if the key is not set
*/
public function getLimitedClone($maxDepth, $maxItemsPerDepth, $useRefHandles = true)
public function seek($key)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0. Use withMaxDepth, withMaxItemsPerDepth or withRefHandles methods instead.', E_USER_DEPRECATED);
$item = $this->data[$this->position][$this->key];
$data = clone $this;
$data->maxDepth = (int) $maxDepth;
$data->maxItemsPerDepth = (int) $maxItemsPerDepth;
$data->useRefHandles = $useRefHandles ? -1 : 0;
if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
$item = $item->value;
}
if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) {
return;
}
$keys = array($key);
switch ($item->type) {
case Stub::TYPE_OBJECT:
$keys[] = Caster::PREFIX_DYNAMIC.$key;
$keys[] = Caster::PREFIX_PROTECTED.$key;
$keys[] = Caster::PREFIX_VIRTUAL.$key;
$keys[] = "\0$item->class\0$key";
// no break
case Stub::TYPE_ARRAY:
case Stub::TYPE_RESOURCE:
break;
default:
return;
}
$data = null;
$children = $this->data[$item->position];
foreach ($keys as $key) {
if (isset($children[$key]) || array_key_exists($key, $children)) {
$data = clone $this;
$data->key = $key;
$data->position = $item->position;
break;
}
}
return $data;
}
@@ -111,7 +276,7 @@ class Data
public function dump(DumperInterface $dumper)
{
$refs = array(0);
$this->dumpItem($dumper, new Cursor(), $refs, $this->data[0][0]);
$this->dumpItem($dumper, new Cursor(), $refs, $this->data[$this->position][$this->key]);
}
/**
@@ -130,7 +295,11 @@ class Data
$firstSeen = true;
if (!$item instanceof Stub) {
$type = gettype($item);
$cursor->attr = array();
$type = \gettype($item);
if ($item && 'array' === $type) {
$item = $this->getStub($item);
}
} elseif (Stub::TYPE_REF === $item->type) {
if ($item->handle) {
if (!isset($refs[$r = $item->handle - (PHP_INT_MAX >> 1)])) {
@@ -142,8 +311,9 @@ class Data
$cursor->hardRefHandle = $this->useRefHandles & $item->handle;
$cursor->hardRefCount = $item->refCount;
}
$type = $item->class ?: gettype($item->value);
$item = $item->value;
$cursor->attr = $item->attr;
$type = $item->class ?: \gettype($item->value);
$item = $this->getStub($item->value);
}
if ($item instanceof Stub) {
if ($item->refCount) {
@@ -156,6 +326,7 @@ class Data
}
$cursor->softRefHandle = $this->useRefHandles & $item->handle;
$cursor->softRefCount = $item->refCount;
$cursor->attr = $item->attr;
$cut = $item->cut;
if ($item->position && $firstSeen) {
@@ -163,7 +334,7 @@ class Data
if ($cursor->stop) {
if ($cut >= 0) {
$cut += count($children);
$cut += \count($children);
}
$children = array();
}
@@ -185,10 +356,16 @@ class Data
$withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
$dumper->enterHash($cursor, $item->type, $item->class, $withChildren);
if ($withChildren) {
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type);
if ($cursor->skipChildren) {
$withChildren = false;
$cut = -1;
} else {
$cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class);
}
} elseif ($children && 0 <= $cut) {
$cut += count($children);
$cut += \count($children);
}
$cursor->skipChildren = false;
$dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut);
break;
@@ -214,20 +391,21 @@ class Data
* @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 bool $dumpKeys Whether keys should be dumped or not
*
* @return int The final number of removed items
*/
private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType)
private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType, $dumpKeys)
{
$cursor = clone $parentCursor;
++$cursor->depth;
$cursor->hashType = $hashType;
$cursor->hashIndex = 0;
$cursor->hashLength = count($children);
$cursor->hashLength = \count($children);
$cursor->hashCut = $hashCut;
foreach ($children as $key => $child) {
$cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key);
$cursor->hashKey = $key;
$cursor->hashKey = $dumpKeys ? $key : null;
$this->dumpItem($dumper, $cursor, $refs, $child);
if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
$parentCursor->stop = true;
@@ -238,4 +416,22 @@ class Data
return $hashCut;
}
private function getStub($item)
{
if (!$item || !\is_array($item)) {
return $item;
}
$stub = new Stub();
$stub->type = Stub::TYPE_ARRAY;
foreach ($item as $stub->class => $stub->position) {
}
if (isset($item[0])) {
$stub->cut = $item[0];
}
$stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0);
return $stub;
}
}
+3 -3
View File
@@ -21,9 +21,9 @@ interface DumperInterface
/**
* Dumps a scalar value.
*
* @param Cursor $cursor The Cursor position in the dump
* @param string $type The PHP type of the value being dumped
* @param scalar $value The scalar value being dumped
* @param Cursor $cursor The Cursor position in the dump
* @param string $type The PHP type of the value being dumped
* @param string|int|float|bool $value The scalar value being dumped
*/
public function dumpScalar(Cursor $cursor, $type, $value);
+27 -10
View File
@@ -16,19 +16,19 @@ namespace Symfony\Component\VarDumper\Cloner;
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class Stub
class Stub implements \Serializable
{
const TYPE_REF = 'ref';
const TYPE_STRING = 'string';
const TYPE_ARRAY = 'array';
const TYPE_OBJECT = 'object';
const TYPE_RESOURCE = 'resource';
const TYPE_REF = 1;
const TYPE_STRING = 2;
const TYPE_ARRAY = 3;
const TYPE_OBJECT = 4;
const TYPE_RESOURCE = 5;
const STRING_BINARY = 'bin';
const STRING_UTF8 = 'utf8';
const STRING_BINARY = 1;
const STRING_UTF8 = 2;
const ARRAY_ASSOC = 'assoc';
const ARRAY_INDEXED = 'indexed';
const ARRAY_ASSOC = 1;
const ARRAY_INDEXED = 2;
public $type = self::TYPE_REF;
public $class = '';
@@ -37,4 +37,21 @@ class Stub
public $handle = 0;
public $refCount = 0;
public $position = 0;
public $attr = array();
/**
* @internal
*/
public function serialize()
{
return \serialize(array($this->class, $this->position, $this->cut, $this->type, $this->value, $this->handle, $this->refCount, $this->attr));
}
/**
* @internal
*/
public function unserialize($serialized)
{
list($this->class, $this->position, $this->cut, $this->type, $this->value, $this->handle, $this->refCount, $this->attr) = \unserialize($serialized);
}
}
+129 -128
View File
@@ -16,117 +16,148 @@ namespace Symfony\Component\VarDumper\Cloner;
*/
class VarCloner extends AbstractCloner
{
private static $gid;
private static $hashMask = 0;
private static $hashOffset = 0;
private static $arrayCache = array();
/**
* {@inheritdoc}
*/
protected function doClone($var)
{
$useExt = $this->useExt;
$len = 1; // Length of $queue
$pos = 0; // Number of cloned items past the first level
$pos = 0; // Number of cloned items past the minimum depth
$refsCounter = 0; // Hard references counter
$queue = array(array($var)); // This breadth-first queue is the return value
$arrayRefs = array(); // Map of queue indexes to stub array objects
$indexedArrays = array(); // Map of queue indexes that hold numerically indexed arrays
$hardRefs = array(); // Map of original zval hashes to stub objects
$objRefs = array(); // Map of original object handles to their stub object couterpart
$resRefs = array(); // Map of original resource handles to their stub object couterpart
$values = array(); // Map of stub objects' hashes to original values
$maxItems = $this->maxItems;
$maxString = $this->maxString;
$minDepth = $this->minDepth;
$currentDepth = 0; // Current tree depth
$currentDepthFinalIndex = 0; // Final $queue index for current tree depth
$minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached
$cookie = (object) array(); // Unique object used to detect hard references
$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
$a = null; // Array cast for nested structures
$stub = null; // Stub capturing the main properties of an original item value
// or null if the original value is used directly
$zval = array( // Main properties of the current value
'type' => null,
'zval_isref' => null,
'zval_hash' => null,
'array_count' => null,
'object_class' => null,
'object_handle' => null,
'resource_type' => null,
);
if (!self::$hashMask) {
self::$gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
self::initHashMask();
}
$gid = self::$gid;
$hashMask = self::$hashMask;
$hashOffset = self::$hashOffset;
$arrayStub = new Stub();
$arrayStub->type = Stub::TYPE_ARRAY;
$fromObjCast = false;
for ($i = 0; $i < $len; ++$i) {
$indexed = true; // Whether the currently iterated array is numerically indexed or not
$j = -1; // Position in the currently iterated array
$fromObjCast = array_keys($queue[$i]);
$fromObjCast = array_keys(array_flip($fromObjCast)) !== $fromObjCast;
$refs = $vals = $fromObjCast ? array_values($queue[$i]) : $queue[$i];
foreach ($queue[$i] as $k => $v) {
// $k is the original key
// $v is the original value or a stub object in case of hard references
if ($k !== ++$j) {
$indexed = false;
// Detect when we move on to the next tree depth
if ($i > $currentDepthFinalIndex) {
++$currentDepth;
$currentDepthFinalIndex = $len - 1;
if ($currentDepth >= $minDepth) {
$minimumDepthReached = true;
}
if ($fromObjCast) {
$k = $j;
}
if ($useExt) {
$zval = symfony_zval_info($k, $refs);
} else {
$refs[$k] = $cookie;
if ($zval['zval_isref'] = $vals[$k] === $cookie) {
$zval['zval_hash'] = $v instanceof Stub ? spl_object_hash($v) : null;
}
$refs = $vals = $queue[$i];
if (\PHP_VERSION_ID < 70200 && empty($indexedArrays[$i])) {
// see https://wiki.php.net/rfc/convert_numeric_keys_in_object_array_casts
foreach ($vals as $k => $v) {
if (\is_int($k)) {
continue;
}
foreach (array($k => true) as $gk => $gv) {
}
if ($gk !== $k) {
$fromObjCast = true;
$refs = $vals = \array_values($queue[$i]);
break;
}
$zval['type'] = gettype($v);
}
if ($zval['zval_isref']) {
}
foreach ($vals as $k => $v) {
// $v is the original value or a stub object in case of hard references
$refs[$k] = $cookie;
if ($zvalIsRef = $vals[$k] === $cookie) {
$vals[$k] = &$stub; // Break hard references to make $queue completely
unset($stub); // independent from the original structure
if (isset($hardRefs[$zval['zval_hash']])) {
$vals[$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($refs[$k] = $v);
if ($v instanceof Stub && isset($hardRefs[\spl_object_hash($v)])) {
$vals[$k] = $refs[$k] = $v;
if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) {
++$v->value->refCount;
}
++$v->refCount;
continue;
}
$refs[$k] = $vals[$k] = new Stub();
$refs[$k]->value = $v;
$h = \spl_object_hash($refs[$k]);
$hardRefs[$h] = &$refs[$k];
$values[$h] = $v;
$vals[$k]->handle = ++$refsCounter;
}
// Create $stub when the original value $v can not be used directly
// If $v is a nested structure, put that structure in array $a
switch ($zval['type']) {
case 'string':
if (isset($v[0]) && !preg_match('//u', $v)) {
switch (true) {
case null === $v:
case \is_bool($v):
case \is_int($v):
case \is_float($v):
continue 2;
case \is_string($v):
if ('' === $v) {
continue 2;
}
if (!\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->cut = $cut;
$stub->value = substr($v, 0, -$cut);
$stub->value = \substr($v, 0, -$cut);
} else {
$stub->value = $v;
}
} elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) {
} elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = \mb_strlen($v, 'UTF-8') - $maxString) {
$stub = new Stub();
$stub->type = Stub::TYPE_STRING;
$stub->class = Stub::STRING_UTF8;
$stub->cut = $cut;
$stub->value = mb_substr($v, 0, $maxString, 'UTF-8');
$stub->value = \mb_substr($v, 0, $maxString, 'UTF-8');
} else {
continue 2;
}
$a = null;
break;
case 'integer':
break;
case \is_array($v):
if (!$v) {
continue 2;
}
$stub = $arrayStub;
$stub->class = Stub::ARRAY_INDEXED;
case 'array':
if ($v) {
$stub = $arrayRefs[$len] = new Stub();
$stub->type = Stub::TYPE_ARRAY;
$stub->class = Stub::ARRAY_ASSOC;
$j = -1;
foreach ($v as $gk => $gv) {
if ($gk !== ++$j) {
$stub->class = Stub::ARRAY_ASSOC;
break;
}
}
$a = $v;
if (Stub::ARRAY_ASSOC === $stub->class) {
// Copies of $GLOBALS have very strange behavior,
// let's detect them with some black magic
$a = $v;
$a[$gid] = true;
// Happens with copies of $GLOBALS
@@ -136,19 +167,21 @@ class VarCloner extends AbstractCloner
foreach ($v as $gk => &$gv) {
$a[$gk] = &$gv;
}
unset($gv);
} else {
$a = $v;
}
$stub->value = $zval['array_count'] ?: count($a);
} elseif (\PHP_VERSION_ID < 70200) {
$indexedArrays[$len] = true;
}
break;
case 'object':
if (empty($objRefs[$h = $zval['object_handle'] ?: ($hashMask ^ hexdec(substr(spl_object_hash($v), $hashOffset, PHP_INT_SIZE)))])) {
case \is_object($v):
case $v instanceof \__PHP_Incomplete_Class:
if (empty($objRefs[$h = $hashMask ^ \hexdec(\substr(\spl_object_hash($v), $hashOffset, \PHP_INT_SIZE))])) {
$stub = new Stub();
$stub->type = Stub::TYPE_OBJECT;
$stub->class = $zval['object_class'] ?: get_class($v);
$stub->class = \get_class($v);
$stub->value = $v;
$stub->handle = $h;
$a = $this->castObject($stub, 0 < $i);
@@ -156,18 +189,12 @@ class VarCloner extends AbstractCloner
if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) {
break;
}
if ($useExt) {
$zval['type'] = $stub->value;
$zval = symfony_zval_info('type', $zval);
$h = $zval['object_handle'];
} else {
$h = $hashMask ^ hexdec(substr(spl_object_hash($stub->value), $hashOffset, PHP_INT_SIZE));
}
$h = $hashMask ^ \hexdec(\substr(\spl_object_hash($stub->value), $hashOffset, \PHP_INT_SIZE));
$stub->handle = $h;
}
$stub->value = null;
if (0 <= $maxItems && $maxItems <= $pos) {
$stub->cut = count($a);
if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
$stub->cut = \count($a);
$a = null;
}
}
@@ -180,21 +207,19 @@ class VarCloner extends AbstractCloner
}
break;
case 'resource':
case 'unknown type':
case 'resource (closed)':
default: // resource
if (empty($resRefs[$h = (int) $v])) {
$stub = new Stub();
$stub->type = Stub::TYPE_RESOURCE;
if ('Unknown' === $stub->class = $zval['resource_type'] ?: @get_resource_type($v)) {
if ('Unknown' === $stub->class = @\get_resource_type($v)) {
$stub->class = 'Closed';
}
$stub->value = $v;
$stub->handle = $h;
$a = $this->castResource($stub, 0 < $i);
$stub->value = null;
if (0 <= $maxItems && $maxItems <= $pos) {
$stub->cut = count($a);
if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) {
$stub->cut = \count($a);
$a = null;
}
}
@@ -208,69 +233,52 @@ class VarCloner extends AbstractCloner
break;
}
if (isset($stub)) {
if ($zval['zval_isref']) {
if ($useExt) {
$vals[$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub();
$v->value = $stub;
} else {
$refs[$k] = new Stub();
$refs[$k]->value = $stub;
$h = spl_object_hash($refs[$k]);
$vals[$k] = $hardRefs[$h] = &$refs[$k];
$values[$h] = $v;
}
$vals[$k]->handle = ++$refsCounter;
} else {
$vals[$k] = $stub;
}
if ($a) {
if ($i && 0 <= $maxItems) {
$k = count($a);
if ($pos < $maxItems) {
if ($maxItems < $pos += $k) {
$a = array_slice($a, 0, $maxItems - $pos);
if ($stub->cut >= 0) {
$stub->cut += $pos - $maxItems;
}
}
} else {
if ($stub->cut >= 0) {
$stub->cut += $k;
}
$stub = $a = null;
unset($arrayRefs[$len]);
continue;
if ($a) {
if (!$minimumDepthReached || 0 > $maxItems) {
$queue[$len] = $a;
$stub->position = $len++;
} elseif ($pos < $maxItems) {
if ($maxItems < $pos += \count($a)) {
$a = \array_slice($a, 0, $maxItems - $pos);
if ($stub->cut >= 0) {
$stub->cut += $pos - $maxItems;
}
}
$queue[$len] = $a;
$stub->position = $len++;
} elseif ($stub->cut >= 0) {
$stub->cut += \count($a);
$stub->position = 0;
}
$stub = $a = null;
} elseif ($zval['zval_isref']) {
if ($useExt) {
$vals[$k] = $hardRefs[$zval['zval_hash']] = new Stub();
$vals[$k]->value = $v;
}
if ($arrayStub === $stub) {
if ($arrayStub->cut) {
$stub = array($arrayStub->cut, $arrayStub->class => $arrayStub->position);
$arrayStub->cut = 0;
} elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) {
$stub = self::$arrayCache[$arrayStub->class][$arrayStub->position];
} else {
$refs[$k] = $vals[$k] = new Stub();
$refs[$k]->value = $v;
$h = spl_object_hash($refs[$k]);
$hardRefs[$h] = &$refs[$k];
$values[$h] = $v;
self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = array($arrayStub->class => $arrayStub->position);
}
$vals[$k]->handle = ++$refsCounter;
}
if ($zvalIsRef) {
$refs[$k]->value = $stub;
} else {
$vals[$k] = $stub;
}
}
if ($fromObjCast) {
$fromObjCast = false;
$refs = $vals;
$vals = array();
$j = -1;
foreach ($queue[$i] as $k => $v) {
foreach (array($k => $v) as $a => $v) {
foreach (array($k => true) as $gk => $gv) {
}
if ($a !== $k) {
if ($gk !== $k) {
$vals = (object) $vals;
$vals->{$k} = $refs[++$j];
$vals = (array) $vals;
@@ -281,13 +289,6 @@ class VarCloner extends AbstractCloner
}
$queue[$i] = $vals;
if (isset($arrayRefs[$i])) {
if ($indexed) {
$arrayRefs[$i]->class = Stub::ARRAY_INDEXED;
}
unset($arrayRefs[$i]);
}
}
foreach ($values as $h => $v) {
@@ -303,13 +304,13 @@ class VarCloner extends AbstractCloner
self::$hashOffset = 16 - PHP_INT_SIZE;
self::$hashMask = -1;
if (defined('HHVM_VERSION')) {
if (\defined('HHVM_VERSION')) {
self::$hashOffset += 16;
} else {
// check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below
$obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush');
foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) {
if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && in_array($frame['function'], $obFuncs)) {
foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) as $frame) {
if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && \in_array($frame['function'], $obFuncs)) {
$frame['line'] = 0;
break;
}
+44 -19
View File
@@ -21,6 +21,11 @@ use Symfony\Component\VarDumper\Cloner\DumperInterface;
*/
abstract class AbstractDumper implements DataDumperInterface, DumperInterface
{
const DUMP_LIGHT_ARRAY = 1;
const DUMP_STRING_LENGTH = 2;
const DUMP_COMMA_SEPARATOR = 4;
const DUMP_TRAILING_COMMA = 8;
public static $defaultOutput = 'php://output';
protected $line = '';
@@ -28,20 +33,23 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
protected $outputStream;
protected $decimalPoint; // This is locale dependent
protected $indentPad = ' ';
protected $flags;
private $charset;
/**
* @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput
* @param string $charset The default character encoding to use for non-UTF8 strings
* @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation
*/
public function __construct($output = null, $charset = null)
public function __construct($output = null, $charset = null, $flags = 0)
{
$this->flags = (int) $flags;
$this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8');
$this->decimalPoint = localeconv();
$this->decimalPoint = $this->decimalPoint['decimal_point'];
$this->setOutput($output ?: static::$defaultOutput);
if (!$output && is_string(static::$defaultOutput)) {
if (!$output && \is_string(static::$defaultOutput)) {
static::$defaultOutput = $this->outputStream;
}
}
@@ -57,11 +65,11 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
{
$prev = null !== $this->outputStream ? $this->outputStream : $this->lineDumper;
if (is_callable($output)) {
if (\is_callable($output)) {
$this->outputStream = null;
$this->lineDumper = $output;
} else {
if (is_string($output)) {
if (\is_string($output)) {
$output = fopen($output, 'wb');
}
$this->outputStream = $output;
@@ -108,31 +116,43 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
/**
* Dumps a Data object.
*
* @param Data $data A Data object
* @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path
* @param Data $data A Data object
* @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump
*
* @return string|null The dump as string when $output is true
*/
public function dump(Data $data, $output = null)
{
$this->decimalPoint = localeconv();
$this->decimalPoint = $this->decimalPoint['decimal_point'];
$exception = null;
if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(LC_NUMERIC, 0) : null) {
setlocale(LC_NUMERIC, 'C');
}
if ($returnDump = true === $output) {
$output = fopen('php://memory', 'r+b');
}
if ($output) {
$prevOutput = $this->setOutput($output);
}
try {
$data->dump($this);
$this->dumpLine(-1);
} catch (\Exception $exception) {
// Re-thrown below
} catch (\Throwable $exception) {
// Re-thrown below
}
if ($output) {
$this->setOutput($prevOutput);
}
if (null !== $exception) {
throw $exception;
if ($returnDump) {
$result = stream_get_contents($output, -1, 0);
fclose($output);
return $result;
}
} finally {
if ($output) {
$this->setOutput($prevOutput);
}
if ($locale) {
setlocale(LC_NUMERIC, $locale);
}
}
}
@@ -144,7 +164,7 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
*/
protected function dumpLine($depth)
{
call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad);
\call_user_func($this->lineDumper, $this->line, $depth, $this->indentPad);
$this->line = '';
}
@@ -171,9 +191,14 @@ abstract class AbstractDumper implements DataDumperInterface, DumperInterface
*/
protected function utf8Encode($s)
{
if (!function_exists('iconv')) {
if (preg_match('//u', $s)) {
return $s;
}
if (!\function_exists('iconv')) {
throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.');
}
if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) {
return $c;
}
+91 -29
View File
@@ -12,6 +12,7 @@
namespace Symfony\Component\VarDumper\Dumper;
use Symfony\Component\VarDumper\Cloner\Cursor;
use Symfony\Component\VarDumper\Cloner\Stub;
/**
* CliDumper dumps variables for command line output.
@@ -51,14 +52,17 @@ class CliDumper extends AbstractDumper
"\033" => '\e',
);
protected $collapseNextHash = false;
protected $expandNextHash = false;
/**
* {@inheritdoc}
*/
public function __construct($output = null, $charset = null)
public function __construct($output = null, $charset = null, $flags = 0)
{
parent::__construct($output, $charset);
parent::__construct($output, $charset, $flags);
if ('\\' === DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) {
if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) {
// Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI
$this->setStyles(array(
'default' => '31',
@@ -112,9 +116,13 @@ class CliDumper extends AbstractDumper
$this->dumpKey($cursor);
$style = 'const';
$attr = array();
$attr = $cursor->attr;
switch ($type) {
case 'default':
$style = 'default';
break;
case 'integer':
$style = 'num';
break;
@@ -144,14 +152,14 @@ class CliDumper extends AbstractDumper
break;
default:
$attr['value'] = isset($value[0]) && !preg_match('//u', $value) ? $this->utf8Encode($value) : $value;
$value = isset($type[0]) && !preg_match('//u', $type) ? $this->utf8Encode($type) : $type;
$attr += array('value' => $this->utf8Encode($value));
$value = $this->utf8Encode($type);
break;
}
$this->line .= $this->style($style, $value, $attr);
$this->dumpLine($cursor->depth, true);
$this->endValue($cursor);
}
/**
@@ -160,15 +168,16 @@ class CliDumper extends AbstractDumper
public function dumpString(Cursor $cursor, $str, $bin, $cut)
{
$this->dumpKey($cursor);
$attr = $cursor->attr;
if ($bin) {
$str = $this->utf8Encode($str);
}
if ('' === $str) {
$this->line .= '""';
$this->dumpLine($cursor->depth, true);
$this->endValue($cursor);
} else {
$attr = array(
$attr += array(
'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0,
'binary' => $bin,
);
@@ -177,9 +186,12 @@ class CliDumper extends AbstractDumper
unset($str[1]);
$str[0] .= "\n";
}
$m = count($str) - 1;
$m = \count($str) - 1;
$i = $lineCut = 0;
if (self::DUMP_STRING_LENGTH & $this->flags) {
$this->line .= '('.$attr['length'].') ';
}
if ($bin) {
$this->line .= 'b';
}
@@ -229,7 +241,11 @@ class CliDumper extends AbstractDumper
$lineCut = 0;
}
$this->dumpLine($cursor->depth, $i > $m);
if ($i > $m) {
$this->endValue($cursor);
} else {
$this->dumpLine($cursor->depth);
}
}
}
}
@@ -241,15 +257,18 @@ class CliDumper extends AbstractDumper
{
$this->dumpKey($cursor);
if (!preg_match('//u', $class)) {
$class = $this->utf8Encode($class);
if ($this->collapseNextHash) {
$cursor->skipChildren = true;
$this->collapseNextHash = $hasChild = false;
}
$class = $this->utf8Encode($class);
if (Cursor::HASH_OBJECT === $type) {
$prefix = $class && 'stdClass' !== $class ? $this->style('note', $class).' {' : '{';
} elseif (Cursor::HASH_RESOURCE === $type) {
$prefix = $this->style('note', $class.' resource').($hasChild ? ' {' : ' ');
} else {
$prefix = $class ? $this->style('note', 'array:'.$class).' [' : '[';
$prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class).' [' : '[';
}
if ($cursor->softRefCount || 0 < $cursor->softRefHandle) {
@@ -274,7 +293,7 @@ class CliDumper extends AbstractDumper
{
$this->dumpEllipsis($cursor, $hasChild, $cut);
$this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : ''));
$this->dumpLine($cursor->depth, true);
$this->endValue($cursor);
}
/**
@@ -314,10 +333,13 @@ class CliDumper extends AbstractDumper
switch ($cursor->hashType) {
default:
case Cursor::HASH_INDEXED:
if (self::DUMP_LIGHT_ARRAY & $this->flags) {
break;
}
$style = 'index';
// no break
case Cursor::HASH_ASSOC:
if (is_int($key)) {
if (\is_int($key)) {
$this->line .= $this->style($style, $key).' => ';
} else {
$this->line .= $bin.'"'.$this->style($style, $key).'" => ';
@@ -333,13 +355,17 @@ class CliDumper extends AbstractDumper
} elseif (0 < strpos($key, "\0", 1)) {
$key = explode("\0", substr($key, 1), 2);
switch ($key[0]) {
switch ($key[0][0]) {
case '+': // User inserted keys
$attr['dynamic'] = true;
$this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": ';
break 2;
case '~':
$style = 'meta';
if (isset($key[0][1])) {
parse_str(substr($key[0], 1), $attr);
$attr += array('binary' => $cursor->hashKeyIsBinary);
}
break;
case '*':
$style = 'protected';
@@ -352,7 +378,15 @@ class CliDumper extends AbstractDumper
break;
}
$this->line .= $bin.$this->style($style, $key[1], $attr).': ';
if (isset($attr['collapse'])) {
if ($attr['collapse']) {
$this->collapseNextHash = true;
} else {
$this->expandNextHash = true;
}
}
$this->line .= $bin.$this->style($style, $key[1], $attr).(isset($attr['separator']) ? $attr['separator'] : ': ');
} else {
// This case should not happen
$this->line .= '-'.$bin.'"'.$this->style('private', $key, array('class' => '')).'": ';
@@ -381,6 +415,21 @@ class CliDumper extends AbstractDumper
$this->colors = $this->supportsColors();
}
if (isset($attr['ellipsis'], $attr['ellipsis-type'])) {
$prefix = substr($value, 0, -$attr['ellipsis']);
if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && 0 === strpos($prefix, $_SERVER[$pwd])) {
$prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd]));
}
if (!empty($attr['ellipsis-tail'])) {
$prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']);
$value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']);
} else {
$value = substr($value, -$attr['ellipsis']);
}
return $this->style('default', $prefix).$this->style($style, $value);
}
$style = $this->styles[$style];
$map = static::$controlCharsMap;
@@ -390,7 +439,7 @@ class CliDumper extends AbstractDumper
$s = $startCchr;
$c = $c[$i = 0];
do {
$s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', ord($c[$i]));
$s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i]));
} while (isset($c[++$i]));
return $s.$endCchr;
@@ -398,12 +447,12 @@ class CliDumper extends AbstractDumper
if ($this->colors) {
if ($cchrCount && "\033" === $value[0]) {
$value = substr($value, strlen($startCchr));
$value = substr($value, \strlen($startCchr));
} else {
$value = "\033[{$style}m".$value;
}
if ($cchrCount && $endCchr === substr($value, -strlen($endCchr))) {
$value = substr($value, 0, -strlen($endCchr));
if ($cchrCount && $endCchr === substr($value, -\strlen($endCchr))) {
$value = substr($value, 0, -\strlen($endCchr));
} else {
$value .= "\033[{$this->styles['default']}m";
}
@@ -425,7 +474,7 @@ class CliDumper extends AbstractDumper
}
if (isset($_SERVER['argv'][1])) {
$colors = $_SERVER['argv'];
$i = count($colors);
$i = \count($colors);
while (--$i > 0) {
if (isset($colors[$i][5])) {
switch ($colors[$i]) {
@@ -463,6 +512,19 @@ class CliDumper extends AbstractDumper
parent::dumpLine($depth);
}
protected function endValue(Cursor $cursor)
{
if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) {
if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) {
$this->line .= ',';
} elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) {
$this->line .= ',';
}
}
$this->dumpLine($cursor->depth, true);
}
/**
* Returns true if the stream supports colorization.
*
@@ -475,7 +537,7 @@ class CliDumper extends AbstractDumper
*/
private function hasColorSupport($stream)
{
if (!is_resource($stream) || 'stream' !== get_resource_type($stream)) {
if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) {
return false;
}
@@ -483,19 +545,19 @@ class CliDumper extends AbstractDumper
return true;
}
if (DIRECTORY_SEPARATOR === '\\') {
return (function_exists('sapi_windows_vt100_support')
if (\DIRECTORY_SEPARATOR === '\\') {
return (\function_exists('sapi_windows_vt100_support')
&& @sapi_windows_vt100_support($stream))
|| false !== getenv('ANSICON')
|| 'ON' === getenv('ConEmuANSI')
|| 'xterm' === getenv('TERM');
}
if (function_exists('stream_isatty')) {
if (\function_exists('stream_isatty')) {
return @stream_isatty($stream);
}
if (function_exists('posix_isatty')) {
if (\function_exists('posix_isatty')) {
return @posix_isatty($stream);
}
@@ -520,7 +582,7 @@ class CliDumper extends AbstractDumper
|| 'xterm' === getenv('TERM')
|| 'Hyper' === getenv('TERM_PROGRAM');
if (!$result && PHP_VERSION_ID >= 70200) {
if (!$result && \PHP_VERSION_ID >= 70200) {
$version = sprintf(
'%s.%s.%s',
PHP_WINDOWS_VERSION_MAJOR,
+510 -53
View File
@@ -25,13 +25,13 @@ class HtmlDumper extends CliDumper
protected $dumpHeader;
protected $dumpPrefix = '<pre class=sf-dump id=%s data-indent-pad="%s">';
protected $dumpSuffix = '</pre><script>Sfdump("%s")</script>';
protected $dumpSuffix = '</pre><script>Sfdump(%s)</script>';
protected $dumpId = 'sf-dump';
protected $colors = true;
protected $headerIsDumped = false;
protected $lastDepth = -1;
protected $styles = array(
'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: normal',
'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all',
'num' => 'font-weight:bold; color:#1299DA',
'const' => 'font-weight:bold',
'str' => 'font-weight:bold; color:#56DB3A',
@@ -43,15 +43,24 @@ class HtmlDumper extends CliDumper
'meta' => 'color:#B729D9',
'key' => 'color:#56DB3A',
'index' => 'color:#1299DA',
'ellipsis' => 'color:#FF8400',
);
private $displayOptions = array(
'maxDepth' => 1,
'maxStringLength' => 160,
'fileLinkFormat' => null,
);
private $extraDisplayOptions = array();
/**
* {@inheritdoc}
*/
public function __construct($output = null, $charset = null)
public function __construct($output = null, $charset = null, $flags = 0)
{
AbstractDumper::__construct($output, $charset);
AbstractDumper::__construct($output, $charset, $flags);
$this->dumpId = 'sf-dump-'.mt_rand();
$this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
}
/**
@@ -63,6 +72,17 @@ class HtmlDumper extends CliDumper
$this->styles = $styles + $this->styles;
}
/**
* Configures display options.
*
* @param array $displayOptions A map of display options to customize the behavior
*/
public function setDisplayOptions(array $displayOptions)
{
$this->headerIsDumped = false;
$this->displayOptions = $displayOptions + $this->displayOptions;
}
/**
* Sets an HTML header that will be dumped once in the output stream.
*
@@ -88,10 +108,13 @@ class HtmlDumper extends CliDumper
/**
* {@inheritdoc}
*/
public function dump(Data $data, $output = null)
public function dump(Data $data, $output = null, array $extraDisplayOptions = array())
{
parent::dump($data, $output);
$this->extraDisplayOptions = $extraDisplayOptions;
$result = parent::dump($data, $output);
$this->dumpId = 'sf-dump-'.mt_rand();
return $result;
}
/**
@@ -105,7 +128,7 @@ class HtmlDumper extends CliDumper
return $this->dumpHeader;
}
$line = <<<'EOHTML'
$line = str_replace('{$options}', json_encode($this->displayOptions, JSON_FORCE_OBJECT), <<<'EOHTML'
<script>
Sfdump = window.Sfdump || (function (doc) {
@@ -168,35 +191,144 @@ function toggle(a, recursive) {
return true;
};
return function (root) {
function collapse(a, recursive) {
var s = a.nextSibling || {}, oldClass = s.className;
if (/\bsf-dump-expanded\b/.test(oldClass)) {
toggle(a, recursive);
return true;
}
return false;
};
function expand(a, recursive) {
var s = a.nextSibling || {}, oldClass = s.className;
if (/\bsf-dump-compact\b/.test(oldClass)) {
toggle(a, recursive);
return true;
}
return false;
};
function collapseAll(root) {
var a = root.querySelector('a.sf-dump-toggle');
if (a) {
collapse(a, true);
expand(a);
return true;
}
return false;
}
function reveal(node) {
var previous, parents = [];
while ((node = node.parentNode || {}) && (previous = node.previousSibling) && 'A' === previous.tagName) {
parents.push(previous);
}
if (0 !== parents.length) {
parents.forEach(function (parent) {
expand(parent);
});
return true;
}
return false;
}
function highlight(root, activeNode, nodes) {
resetHighlightedNodes(root);
Array.from(nodes||[]).forEach(function (node) {
if (!/\bsf-dump-highlight\b/.test(node.className)) {
node.className = node.className + ' sf-dump-highlight';
}
});
if (!/\bsf-dump-highlight-active\b/.test(activeNode.className)) {
activeNode.className = activeNode.className + ' sf-dump-highlight-active';
}
}
function resetHighlightedNodes(root) {
Array.from(root.querySelectorAll('.sf-dump-str, .sf-dump-key, .sf-dump-public, .sf-dump-protected, .sf-dump-private')).forEach(function (strNode) {
strNode.className = strNode.className.replace(/\bsf-dump-highlight\b/, '');
strNode.className = strNode.className.replace(/\bsf-dump-highlight-active\b/, '');
});
}
return function (root, x) {
root = doc.getElementById(root);
var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'),
options = {$options},
elt = root.getElementsByTagName('A'),
len = elt.length,
i = 0, s, h,
t = [];
while (i < len) t.push(elt[i++]);
for (i in x) {
options[i] = x[i];
}
function a(e, f) {
addEventListener(root, e, function (e) {
if ('A' == e.target.tagName) {
f(e.target, e);
} else if ('A' == e.target.parentNode.tagName) {
f(e.target.parentNode, e);
} else if (e.target.nextElementSibling && 'A' == e.target.nextElementSibling.tagName) {
f(e.target.nextElementSibling, e, true);
}
});
};
function isCtrlKey(e) {
return e.ctrlKey || e.metaKey;
}
function xpathString(str) {
var parts = str.match(/[^'"]+|['"]/g).map(function (part) {
if ("'" == part) {
return '"\'"';
}
if ('"' == part) {
return "'\"'";
}
return "'" + part + "'";
});
return "concat(" + parts.join(",") + ", '')";
}
function xpathHasClass(className) {
return "contains(concat(' ', normalize-space(@class), ' '), ' " + className +" ')";
}
addEventListener(root, 'mouseover', function (e) {
if ('' != refStyle.innerHTML) {
refStyle.innerHTML = '';
}
});
a('mouseover', function (a) {
if (a = idRx.exec(a.className)) {
a('mouseover', function (a, e, c) {
if (c) {
e.target.style.cursor = "pointer";
} else if (a = idRx.exec(a.className)) {
try {
refStyle.innerHTML = 'pre.sf-dump .'+a[0]+'{background-color: #B729D9; color: #FFF !important; border-radius: 2px}';
} catch (e) {
}
}
});
a('click', function (a, e) {
a('click', function (a, e, c) {
if (/\bsf-dump-toggle\b/.test(a.className)) {
e.preventDefault();
if (!toggle(a, isCtrlKey(e))) {
@@ -217,7 +349,8 @@ return function (root) {
}
}
if (doc.getSelection) {
if (c) {
} else if (doc.getSelection) {
try {
doc.getSelection().removeAllRanges();
} catch (e) {
@@ -226,31 +359,23 @@ return function (root) {
} else {
doc.selection.empty();
}
} else if (/\bsf-dump-str-toggle\b/.test(a.className)) {
e.preventDefault();
e = a.parentNode.parentNode;
e.className = e.className.replace(/\bsf-dump-str-(expand|collapse)\b/, a.parentNode.className);
}
});
var indentRx = new RegExp('^('+(root.getAttribute('data-indent-pad') || ' ').replace(rxEsc, '\\$1')+')+', 'm'),
elt = root.getElementsByTagName('A'),
len = elt.length,
i = 0,
t = [];
while (i < len) t.push(elt[i++]);
elt = root.getElementsByTagName('SAMP');
len = elt.length;
i = 0;
while (i < len) t.push(elt[i++]);
root = t;
len = t.length;
i = t = 0;
while (i < len) {
elt = root[i];
if ("SAMP" == elt.tagName) {
elt.className = "sf-dump-expanded";
for (i = 0; i < len; ++i) {
elt = t[i];
if ('SAMP' == elt.tagName) {
a = elt.previousSibling || {};
if ('A' != a.tagName) {
a = doc.createElement('A');
@@ -262,7 +387,15 @@ return function (root) {
a.title = (a.title ? a.title+'\n[' : '[')+keyHint+'+click] Expand all children';
a.innerHTML += '<span>▼</span>';
a.className += ' sf-dump-toggle';
if (!/\bsf-dump\b/.test(elt.parentNode.className)) {
x = 1;
if ('sf-dump' != elt.parentNode.className) {
x += elt.parentNode.getAttribute('data-depth')/1;
}
elt.setAttribute('data-depth', x);
var className = elt.className;
elt.className = 'sf-dump-expanded';
if (className ? 'sf-dump-expanded' !== className : (x > options.maxDepth)) {
toggle(a);
}
} else if (/\bsf-dump-ref\b/.test(elt.className) && (a = elt.getAttribute('href'))) {
@@ -272,9 +405,9 @@ return function (root) {
if (/[\[{]$/.test(elt.previousSibling.nodeValue)) {
a = a != elt.nextSibling.id && doc.getElementById(a);
try {
t = a.nextSibling;
s = a.nextSibling;
elt.appendChild(a);
t.parentNode.insertBefore(a, t);
s.parentNode.insertBefore(a, s);
if (/^[@#]/.test(elt.innerHTML)) {
elt.innerHTML += ' <span>▶</span>';
} else {
@@ -290,7 +423,181 @@ return function (root) {
}
}
}
++i;
}
if (doc.evaluate && Array.from && root.children.length > 1) {
root.setAttribute('tabindex', 0);
SearchState = function () {
this.nodes = [];
this.idx = 0;
};
SearchState.prototype = {
next: function () {
if (this.isEmpty()) {
return this.current();
}
this.idx = this.idx < (this.nodes.length - 1) ? this.idx + 1 : 0;
return this.current();
},
previous: function () {
if (this.isEmpty()) {
return this.current();
}
this.idx = this.idx > 0 ? this.idx - 1 : (this.nodes.length - 1);
return this.current();
},
isEmpty: function () {
return 0 === this.count();
},
current: function () {
if (this.isEmpty()) {
return null;
}
return this.nodes[this.idx];
},
reset: function () {
this.nodes = [];
this.idx = 0;
},
count: function () {
return this.nodes.length;
},
};
function showCurrent(state)
{
var currentNode = state.current();
if (currentNode) {
reveal(currentNode);
highlight(root, currentNode, state.nodes);
}
counter.textContent = (state.isEmpty() ? 0 : state.idx + 1) + ' of ' + state.count();
}
var search = doc.createElement('div');
search.className = 'sf-dump-search-wrapper sf-dump-search-hidden';
search.innerHTML = '
<input type="text" class="sf-dump-search-input">
<span class="sf-dump-search-count">0 of 0<\/span>
<button type="button" class="sf-dump-search-input-previous" tabindex="-1">
<svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path d="M1683 1331l-166 165q-19 19-45 19t-45-19l-531-531-531 531q-19 19-45 19t-45-19l-166-165q-19-19-19-45.5t19-45.5l742-741q19-19 45-19t45 19l742 741q19 19 19 45.5t-19 45.5z"\/>
<\/svg>
<\/button>
<button type="button" class="sf-dump-search-input-next" tabindex="-1">
<svg viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg">
<path d="M1683 808l-742 741q-19 19-45 19t-45-19l-742-741q-19-19-19-45.5t19-45.5l166-165q19-19 45-19t45 19l531 531 531-531q19-19 45-19t45 19l166 165q19 19 19 45.5t-19 45.5z"\/>
<\/svg>
<\/button>
';
root.insertBefore(search, root.firstChild);
var state = new SearchState();
var searchInput = search.querySelector('.sf-dump-search-input');
var counter = search.querySelector('.sf-dump-search-count');
var searchInputTimer = 0;
var previousSearchQuery = '';
addEventListener(searchInput, 'keyup', function (e) {
var searchQuery = e.target.value;
/* Don't perform anything if the pressed key didn't change the query */
if (searchQuery === previousSearchQuery) {
return;
}
previousSearchQuery = searchQuery;
clearTimeout(searchInputTimer);
searchInputTimer = setTimeout(function () {
state.reset();
collapseAll(root);
resetHighlightedNodes(root);
if ('' === searchQuery) {
counter.textContent = '0 of 0';
return;
}
var classMatches = [
"sf-dump-str",
"sf-dump-key",
"sf-dump-public",
"sf-dump-protected",
"sf-dump-private",
].map(xpathHasClass).join(' or ');
var xpathResult = doc.evaluate('.//span[' + classMatches + '][contains(translate(child::text(), ' + xpathString(searchQuery.toUpperCase()) + ', ' + xpathString(searchQuery.toLowerCase()) + '), ' + xpathString(searchQuery.toLowerCase()) + ')]', root, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE, null);
while (node = xpathResult.iterateNext()) state.nodes.push(node);
showCurrent(state);
}, 400);
});
Array.from(search.querySelectorAll('.sf-dump-search-input-next, .sf-dump-search-input-previous')).forEach(function (btn) {
addEventListener(btn, 'click', function (e) {
e.preventDefault();
-1 !== e.target.className.indexOf('next') ? state.next() : state.previous();
searchInput.focus();
collapseAll(root);
showCurrent(state);
})
});
addEventListener(root, 'keydown', function (e) {
var isSearchActive = !/\bsf-dump-search-hidden\b/.test(search.className);
if ((114 === e.keyCode && !isSearchActive) || (isCtrlKey(e) && 70 === e.keyCode)) {
/* F3 or CMD/CTRL + F */
e.preventDefault();
search.className = search.className.replace(/\bsf-dump-search-hidden\b/, '');
searchInput.focus();
} else if (isSearchActive) {
if (27 === e.keyCode) {
/* ESC key */
search.className += ' sf-dump-search-hidden';
e.preventDefault();
resetHighlightedNodes(root);
searchInput.value = '';
} else if (
(isCtrlKey(e) && 71 === e.keyCode) /* CMD/CTRL + G */
|| 13 === e.keyCode /* Enter */
|| 114 === e.keyCode /* F3 */
) {
e.preventDefault();
e.shiftKey ? state.previous() : state.next();
collapseAll(root);
showCurrent(state);
}
}
});
}
if (0 >= options.maxStringLength) {
return;
}
try {
elt = root.querySelectorAll('.sf-dump-str');
len = elt.length;
i = 0;
t = [];
while (i < len) t.push(elt[i++]);
len = t.length;
for (i = 0; i < len; ++i) {
elt = t[i];
s = elt.innerText || elt.textContent;
x = s.length - options.maxStringLength;
if (0 < x) {
h = elt.innerHTML;
elt[elt.innerText ? 'innerText' : 'textContent'] = s.substring(0, options.maxStringLength);
elt.className += ' sf-dump-str-collapse';
elt.innerHTML = '<span class=sf-dump-str-collapse>'+h+'<a class="sf-dump-ref sf-dump-str-toggle" title="Collapse"> ◀</a></span>'+
'<span class=sf-dump-str-expand>'+elt.innerHTML+'<a class="sf-dump-ref sf-dump-str-toggle" title="'+x+' remaining characters"> ▶</a></span>';
}
}
} catch (e) {
}
};
@@ -301,6 +608,13 @@ pre.sf-dump {
white-space: pre;
padding: 5px;
}
pre.sf-dump:after {
content: "";
visibility: hidden;
display: block;
height: 0;
clear: both;
}
pre.sf-dump span {
display: inline;
}
@@ -317,11 +631,110 @@ pre.sf-dump a {
cursor: pointer;
border: 0;
outline: none;
color: inherit;
}
EOHTML;
pre.sf-dump .sf-dump-ellipsis {
display: inline-block;
overflow: visible;
text-overflow: ellipsis;
max-width: 5em;
white-space: nowrap;
overflow: hidden;
vertical-align: top;
}
pre.sf-dump .sf-dump-ellipsis+.sf-dump-ellipsis {
max-width: none;
}
pre.sf-dump code {
display:inline;
padding:0;
background:none;
}
.sf-dump-str-collapse .sf-dump-str-collapse {
display: none;
}
.sf-dump-str-expand .sf-dump-str-expand {
display: none;
}
.sf-dump-public.sf-dump-highlight,
.sf-dump-protected.sf-dump-highlight,
.sf-dump-private.sf-dump-highlight,
.sf-dump-str.sf-dump-highlight,
.sf-dump-key.sf-dump-highlight {
background: rgba(111, 172, 204, 0.3);
border: 1px solid #7DA0B1;
border-radius: 3px;
}
.sf-dump-public.sf-dump-highlight-active,
.sf-dump-protected.sf-dump-highlight-active,
.sf-dump-private.sf-dump-highlight-active,
.sf-dump-str.sf-dump-highlight-active,
.sf-dump-key.sf-dump-highlight-active {
background: rgba(253, 175, 0, 0.4);
border: 1px solid #ffa500;
border-radius: 3px;
}
pre.sf-dump .sf-dump-search-hidden {
display: none;
}
pre.sf-dump .sf-dump-search-wrapper {
float: right;
font-size: 0;
white-space: nowrap;
max-width: 100%;
text-align: right;
}
pre.sf-dump .sf-dump-search-wrapper > * {
vertical-align: top;
box-sizing: border-box;
height: 21px;
font-weight: normal;
border-radius: 0;
background: #FFF;
color: #757575;
border: 1px solid #BBB;
}
pre.sf-dump .sf-dump-search-wrapper > input.sf-dump-search-input {
padding: 3px;
height: 21px;
font-size: 12px;
border-right: none;
width: 140px;
border-top-left-radius: 3px;
border-bottom-left-radius: 3px;
color: #000;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next,
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous {
background: #F2F2F2;
outline: none;
border-left: none;
font-size: 0;
line-height: 0;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next {
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-next > svg,
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-input-previous > svg {
pointer-events: none;
width: 12px;
height: 12px;
}
pre.sf-dump .sf-dump-search-wrapper > .sf-dump-search-count {
display: inline-block;
padding: 0 5px;
margin: 0;
border-left: none;
line-height: 21px;
font-size: 12px;
}
EOHTML
);
foreach ($this->styles as $class => $style) {
$line .= 'pre.sf-dump'.('default' !== $class ? ' .sf-dump-'.$class : '').'{'.$style.'}';
$line .= 'pre.sf-dump'.('default' === $class ? ', pre.sf-dump' : '').' .sf-dump-'.$class.'{'.$style.'}';
}
return $this->dumpHeader = preg_replace('/\s+/', ' ', $line).'</style>'.$this->dumpHeader;
@@ -334,15 +747,25 @@ EOHTML;
{
parent::enterHash($cursor, $type, $class, false);
if ($cursor->skipChildren) {
$cursor->skipChildren = false;
$eol = ' class=sf-dump-compact>';
} elseif ($this->expandNextHash) {
$this->expandNextHash = false;
$eol = ' class=sf-dump-expanded>';
} else {
$eol = '>';
}
if ($hasChild) {
$this->line .= '<samp';
if ($cursor->refIndex) {
$r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2;
$r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex;
$this->line .= sprintf('<samp id=%s-ref%s>', $this->dumpId, $r);
} else {
$this->line .= '<samp>';
$this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r);
}
$this->line .= $eol;
$this->dumpLine($cursor->depth);
}
}
@@ -389,31 +812,49 @@ EOHTML;
return sprintf('<abbr title="%s" class=sf-dump-%s>%s</abbr>', $v, $style, substr($v, $c + 1));
} elseif ('protected' === $style) {
$style .= ' title="Protected property"';
} elseif ('meta' === $style && isset($attr['title'])) {
$style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title'])));
} elseif ('private' === $style) {
$style .= sprintf(' title="Private property defined in class:&#10;`%s`"', esc($attr['class']));
$style .= sprintf(' title="Private property defined in class:&#10;`%s`"', esc($this->utf8Encode($attr['class'])));
}
$map = static::$controlCharsMap;
if (isset($attr['ellipsis'])) {
$class = 'sf-dump-ellipsis';
if (isset($attr['ellipsis-type'])) {
$class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']);
}
$label = esc(substr($value, -$attr['ellipsis']));
$style = str_replace(' title="', " title=\"$v\n", $style);
$v = sprintf('<span class=%s>%s</span>', $class, substr($v, 0, -\strlen($label)));
if (!empty($attr['ellipsis-tail'])) {
$tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail'])));
$v .= sprintf('<span class=sf-dump-ellipsis>%s</span>%s', substr($label, 0, $tail), substr($label, $tail));
} else {
$v .= $label;
}
}
$map = static::$controlCharsMap;
$style = "<span class=sf-dump-{$style}>";
$v = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $style) {
$s = '</span>';
$v = "<span class=sf-dump-{$style}>".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) {
$s = '<span class=sf-dump-default>';
$c = $c[$i = 0];
do {
$s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', ord($c[$i]));
$s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i]));
} while (isset($c[++$i]));
return $s.$style;
}, $v, -1, $cchrCount);
return $s.'</span>';
}, $v).'</span>';
if ($cchrCount && '<' === $v[0]) {
$v = substr($v, 7);
} else {
$v = $style.$v;
if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) {
$attr['href'] = $href;
}
if ($cchrCount && '>' === substr($v, -1)) {
$v = substr($v, 0, -strlen($style));
} else {
$v .= '</span>';
if (isset($attr['href'])) {
$target = isset($attr['file']) ? '' : ' target="_blank"';
$v = sprintf('<a href="%s"%s rel="noopener noreferrer">%s</a>', esc($this->utf8Encode($attr['href'])), $target, $v);
}
if (isset($attr['lang'])) {
$v = sprintf('<code class="%s">%s</code>', esc($attr['lang']), $v);
}
return $v;
@@ -432,7 +873,12 @@ EOHTML;
}
if (-1 === $depth) {
$this->line .= sprintf($this->dumpSuffix, $this->dumpId);
$args = array('"'.$this->dumpId.'"');
if ($this->extraDisplayOptions) {
$args[] = json_encode($this->extraDisplayOptions, JSON_FORCE_OBJECT);
}
// Replace is for BC
$this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args));
}
$this->lastDepth = $depth;
@@ -443,6 +889,17 @@ EOHTML;
}
AbstractDumper::dumpLine($depth);
}
private function getSourceLink($file, $line)
{
$options = $this->extraDisplayOptions + $this->displayOptions;
if ($fmt = $options['fileLinkFormat']) {
return \is_string($fmt) ? strtr($fmt, array('%f' => $file, '%l' => $line)) : $fmt->format($file, $line);
}
return false;
}
}
function esc($str)
@@ -19,13 +19,8 @@ class ThrowingCasterException extends \Exception
/**
* @param \Exception $prev The exception thrown from the caster
*/
public function __construct($prev, \Exception $e = null)
public function __construct(\Exception $prev)
{
if (!$prev instanceof \Exception) {
@trigger_error('Providing $caster as the first argument of the '.__METHOD__.' method is deprecated since Symfony 2.8 and will be removed in 3.0. Provide directly the $prev exception instead.', E_USER_DEPRECATED);
$prev = $e;
}
parent::__construct('Unexpected '.get_class($prev).' thrown from a caster: '.$prev->getMessage(), 0, $prev);
parent::__construct('Unexpected '.\get_class($prev).' thrown from a caster: '.$prev->getMessage(), 0, $prev);
}
}
@@ -20,5 +20,11 @@ if (!function_exists('dump')) {
foreach (func_get_args() as $var) {
VarDumper::dump($var);
}
if (1 < func_num_args()) {
return func_get_args();
}
return $var;
}
}
+3 -7
View File
@@ -29,21 +29,17 @@ class VarDumper
{
if (null === self::$handler) {
$cloner = new VarCloner();
$dumper = \in_array(PHP_SAPI, array('cli', 'phpdbg'), true) ? new CliDumper() : new HtmlDumper();
$dumper = \in_array(\PHP_SAPI, array('cli', 'phpdbg'), true) ? new CliDumper() : new HtmlDumper();
self::$handler = function ($var) use ($cloner, $dumper) {
$dumper->dump($cloner->cloneVar($var));
};
}
return call_user_func(self::$handler, $var);
return \call_user_func(self::$handler, $var);
}
public static function setHandler($callable)
public static function setHandler(callable $callable = null)
{
if (null !== $callable && !is_callable($callable, true)) {
throw new \InvalidArgumentException('Invalid PHP callback.');
}
$prevHandler = self::$handler;
self::$handler = $callable;