vendor/symfony/var-dumper/Cloner/Data.php line 122

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\VarDumper\Cloner;
  11. use Symfony\Component\VarDumper\Caster\Caster;
  12. use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider;
  13. /**
  14.  * @author Nicolas Grekas <p@tchwork.com>
  15.  */
  16. class Data implements \ArrayAccess\Countable\IteratorAggregate
  17. {
  18.     private $data;
  19.     private $position 0;
  20.     private $key 0;
  21.     private $maxDepth 20;
  22.     private $maxItemsPerDepth = -1;
  23.     private $useRefHandles = -1;
  24.     private $context = [];
  25.     /**
  26.      * @param array $data An array as returned by ClonerInterface::cloneVar()
  27.      */
  28.     public function __construct(array $data)
  29.     {
  30.         $this->data $data;
  31.     }
  32.     /**
  33.      * @return string|null The type of the value
  34.      */
  35.     public function getType()
  36.     {
  37.         $item $this->data[$this->position][$this->key];
  38.         if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
  39.             $item $item->value;
  40.         }
  41.         if (!$item instanceof Stub) {
  42.             return \gettype($item);
  43.         }
  44.         if (Stub::TYPE_STRING === $item->type) {
  45.             return 'string';
  46.         }
  47.         if (Stub::TYPE_ARRAY === $item->type) {
  48.             return 'array';
  49.         }
  50.         if (Stub::TYPE_OBJECT === $item->type) {
  51.             return $item->class;
  52.         }
  53.         if (Stub::TYPE_RESOURCE === $item->type) {
  54.             return $item->class.' resource';
  55.         }
  56.         return null;
  57.     }
  58.     /**
  59.      * @param array|bool $recursive Whether values should be resolved recursively or not
  60.      *
  61.      * @return string|int|float|bool|array|Data[]|null A native representation of the original value
  62.      */
  63.     public function getValue($recursive false)
  64.     {
  65.         $item $this->data[$this->position][$this->key];
  66.         if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
  67.             $item $item->value;
  68.         }
  69.         if (!($item $this->getStub($item)) instanceof Stub) {
  70.             return $item;
  71.         }
  72.         if (Stub::TYPE_STRING === $item->type) {
  73.             return $item->value;
  74.         }
  75.         $children $item->position $this->data[$item->position] : [];
  76.         foreach ($children as $k => $v) {
  77.             if ($recursive && !($v $this->getStub($v)) instanceof Stub) {
  78.                 continue;
  79.             }
  80.             $children[$k] = clone $this;
  81.             $children[$k]->key $k;
  82.             $children[$k]->position $item->position;
  83.             if ($recursive) {
  84.                 if (Stub::TYPE_REF === $v->type && ($v $this->getStub($v->value)) instanceof Stub) {
  85.                     $recursive = (array) $recursive;
  86.                     if (isset($recursive[$v->position])) {
  87.                         continue;
  88.                     }
  89.                     $recursive[$v->position] = true;
  90.                 }
  91.                 $children[$k] = $children[$k]->getValue($recursive);
  92.             }
  93.         }
  94.         return $children;
  95.     }
  96.     /**
  97.      * @return int
  98.      */
  99.     public function count()
  100.     {
  101.         return \count($this->getValue());
  102.     }
  103.     /**
  104.      * @return \Traversable
  105.      */
  106.     public function getIterator()
  107.     {
  108.         if (!\is_array($value $this->getValue())) {
  109.             throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".'self::class, \gettype($value)));
  110.         }
  111.         yield from $value;
  112.     }
  113.     public function __get($key)
  114.     {
  115.         if (null !== $data $this->seek($key)) {
  116.             $item $this->getStub($data->data[$data->position][$data->key]);
  117.             return $item instanceof Stub || [] === $item $data $item;
  118.         }
  119.         return null;
  120.     }
  121.     /**
  122.      * @return bool
  123.      */
  124.     public function __isset($key)
  125.     {
  126.         return null !== $this->seek($key);
  127.     }
  128.     /**
  129.      * @return bool
  130.      */
  131.     public function offsetExists($key)
  132.     {
  133.         return $this->__isset($key);
  134.     }
  135.     public function offsetGet($key)
  136.     {
  137.         return $this->__get($key);
  138.     }
  139.     public function offsetSet($key$value)
  140.     {
  141.         throw new \BadMethodCallException(self::class.' objects are immutable.');
  142.     }
  143.     public function offsetUnset($key)
  144.     {
  145.         throw new \BadMethodCallException(self::class.' objects are immutable.');
  146.     }
  147.     /**
  148.      * @return string
  149.      */
  150.     public function __toString()
  151.     {
  152.         $value $this->getValue();
  153.         if (!\is_array($value)) {
  154.             return (string) $value;
  155.         }
  156.         return sprintf('%s (count=%d)'$this->getType(), \count($value));
  157.     }
  158.     /**
  159.      * Returns a depth limited clone of $this.
  160.      *
  161.      * @param int $maxDepth The max dumped depth level
  162.      *
  163.      * @return static
  164.      */
  165.     public function withMaxDepth($maxDepth)
  166.     {
  167.         $data = clone $this;
  168.         $data->maxDepth = (int) $maxDepth;
  169.         return $data;
  170.     }
  171.     /**
  172.      * Limits the number of elements per depth level.
  173.      *
  174.      * @param int $maxItemsPerDepth The max number of items dumped per depth level
  175.      *
  176.      * @return static
  177.      */
  178.     public function withMaxItemsPerDepth($maxItemsPerDepth)
  179.     {
  180.         $data = clone $this;
  181.         $data->maxItemsPerDepth = (int) $maxItemsPerDepth;
  182.         return $data;
  183.     }
  184.     /**
  185.      * Enables/disables objects' identifiers tracking.
  186.      *
  187.      * @param bool $useRefHandles False to hide global ref. handles
  188.      *
  189.      * @return static
  190.      */
  191.     public function withRefHandles($useRefHandles)
  192.     {
  193.         $data = clone $this;
  194.         $data->useRefHandles $useRefHandles ? -0;
  195.         return $data;
  196.     }
  197.     /**
  198.      * @return static
  199.      */
  200.     public function withContext(array $context)
  201.     {
  202.         $data = clone $this;
  203.         $data->context $context;
  204.         return $data;
  205.     }
  206.     /**
  207.      * Seeks to a specific key in nested data structures.
  208.      *
  209.      * @param string|int $key The key to seek to
  210.      *
  211.      * @return static|null Null if the key is not set
  212.      */
  213.     public function seek($key)
  214.     {
  215.         $item $this->data[$this->position][$this->key];
  216.         if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
  217.             $item $item->value;
  218.         }
  219.         if (!($item $this->getStub($item)) instanceof Stub || !$item->position) {
  220.             return null;
  221.         }
  222.         $keys = [$key];
  223.         switch ($item->type) {
  224.             case Stub::TYPE_OBJECT:
  225.                 $keys[] = Caster::PREFIX_DYNAMIC.$key;
  226.                 $keys[] = Caster::PREFIX_PROTECTED.$key;
  227.                 $keys[] = Caster::PREFIX_VIRTUAL.$key;
  228.                 $keys[] = "\0$item->class\0$key";
  229.                 // no break
  230.             case Stub::TYPE_ARRAY:
  231.             case Stub::TYPE_RESOURCE:
  232.                 break;
  233.             default:
  234.                 return null;
  235.         }
  236.         $data null;
  237.         $children $this->data[$item->position];
  238.         foreach ($keys as $key) {
  239.             if (isset($children[$key]) || \array_key_exists($key$children)) {
  240.                 $data = clone $this;
  241.                 $data->key $key;
  242.                 $data->position $item->position;
  243.                 break;
  244.             }
  245.         }
  246.         return $data;
  247.     }
  248.     /**
  249.      * Dumps data with a DumperInterface dumper.
  250.      */
  251.     public function dump(DumperInterface $dumper)
  252.     {
  253.         $refs = [0];
  254.         $cursor = new Cursor();
  255.         if ($cursor->attr $this->context[SourceContextProvider::class] ?? []) {
  256.             $cursor->attr['if_links'] = true;
  257.             $cursor->hashType = -1;
  258.             $dumper->dumpScalar($cursor'default''^');
  259.             $cursor->attr = ['if_links' => true];
  260.             $dumper->dumpScalar($cursor'default'' ');
  261.             $cursor->hashType 0;
  262.         }
  263.         $this->dumpItem($dumper$cursor$refs$this->data[$this->position][$this->key]);
  264.     }
  265.     /**
  266.      * Depth-first dumping of items.
  267.      *
  268.      * @param mixed $item A Stub object or the original value being dumped
  269.      */
  270.     private function dumpItem(DumperInterface $dumperCursor $cursor, array &$refs$item)
  271.     {
  272.         $cursor->refIndex 0;
  273.         $cursor->softRefTo $cursor->softRefHandle $cursor->softRefCount 0;
  274.         $cursor->hardRefTo $cursor->hardRefHandle $cursor->hardRefCount 0;
  275.         $firstSeen true;
  276.         if (!$item instanceof Stub) {
  277.             $cursor->attr = [];
  278.             $type \gettype($item);
  279.             if ($item && 'array' === $type) {
  280.                 $item $this->getStub($item);
  281.             }
  282.         } elseif (Stub::TYPE_REF === $item->type) {
  283.             if ($item->handle) {
  284.                 if (!isset($refs[$r $item->handle - (\PHP_INT_MAX >> 1)])) {
  285.                     $cursor->refIndex $refs[$r] = $cursor->refIndex ?: ++$refs[0];
  286.                 } else {
  287.                     $firstSeen false;
  288.                 }
  289.                 $cursor->hardRefTo $refs[$r];
  290.                 $cursor->hardRefHandle $this->useRefHandles $item->handle;
  291.                 $cursor->hardRefCount $item->refCount;
  292.             }
  293.             $cursor->attr $item->attr;
  294.             $type $item->class ?: \gettype($item->value);
  295.             $item $this->getStub($item->value);
  296.         }
  297.         if ($item instanceof Stub) {
  298.             if ($item->refCount) {
  299.                 if (!isset($refs[$r $item->handle])) {
  300.                     $cursor->refIndex $refs[$r] = $cursor->refIndex ?: ++$refs[0];
  301.                 } else {
  302.                     $firstSeen false;
  303.                 }
  304.                 $cursor->softRefTo $refs[$r];
  305.             }
  306.             $cursor->softRefHandle $this->useRefHandles $item->handle;
  307.             $cursor->softRefCount $item->refCount;
  308.             $cursor->attr $item->attr;
  309.             $cut $item->cut;
  310.             if ($item->position && $firstSeen) {
  311.                 $children $this->data[$item->position];
  312.                 if ($cursor->stop) {
  313.                     if ($cut >= 0) {
  314.                         $cut += \count($children);
  315.                     }
  316.                     $children = [];
  317.                 }
  318.             } else {
  319.                 $children = [];
  320.             }
  321.             switch ($item->type) {
  322.                 case Stub::TYPE_STRING:
  323.                     $dumper->dumpString($cursor$item->valueStub::STRING_BINARY === $item->class$cut);
  324.                     break;
  325.                 case Stub::TYPE_ARRAY:
  326.                     $item = clone $item;
  327.                     $item->type $item->class;
  328.                     $item->class $item->value;
  329.                     // no break
  330.                 case Stub::TYPE_OBJECT:
  331.                 case Stub::TYPE_RESOURCE:
  332.                     $withChildren $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
  333.                     $dumper->enterHash($cursor$item->type$item->class$withChildren);
  334.                     if ($withChildren) {
  335.                         if ($cursor->skipChildren) {
  336.                             $withChildren false;
  337.                             $cut = -1;
  338.                         } else {
  339.                             $cut $this->dumpChildren($dumper$cursor$refs$children$cut$item->typenull !== $item->class);
  340.                         }
  341.                     } elseif ($children && <= $cut) {
  342.                         $cut += \count($children);
  343.                     }
  344.                     $cursor->skipChildren false;
  345.                     $dumper->leaveHash($cursor$item->type$item->class$withChildren$cut);
  346.                     break;
  347.                 default:
  348.                     throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".'$item->type));
  349.             }
  350.         } elseif ('array' === $type) {
  351.             $dumper->enterHash($cursorCursor::HASH_INDEXED0false);
  352.             $dumper->leaveHash($cursorCursor::HASH_INDEXED0false0);
  353.         } elseif ('string' === $type) {
  354.             $dumper->dumpString($cursor$itemfalse0);
  355.         } else {
  356.             $dumper->dumpScalar($cursor$type$item);
  357.         }
  358.     }
  359.     /**
  360.      * Dumps children of hash structures.
  361.      *
  362.      * @return int The final number of removed items
  363.      */
  364.     private function dumpChildren(DumperInterface $dumperCursor $parentCursor, array &$refs, array $childrenint $hashCutint $hashTypebool $dumpKeys): int
  365.     {
  366.         $cursor = clone $parentCursor;
  367.         ++$cursor->depth;
  368.         $cursor->hashType $hashType;
  369.         $cursor->hashIndex 0;
  370.         $cursor->hashLength \count($children);
  371.         $cursor->hashCut $hashCut;
  372.         foreach ($children as $key => $child) {
  373.             $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u'$key);
  374.             $cursor->hashKey $dumpKeys $key null;
  375.             $this->dumpItem($dumper$cursor$refs$child);
  376.             if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
  377.                 $parentCursor->stop true;
  378.                 return $hashCut >= $hashCut $cursor->hashLength $cursor->hashIndex $hashCut;
  379.             }
  380.         }
  381.         return $hashCut;
  382.     }
  383.     private function getStub($item)
  384.     {
  385.         if (!$item || !\is_array($item)) {
  386.             return $item;
  387.         }
  388.         $stub = new Stub();
  389.         $stub->type Stub::TYPE_ARRAY;
  390.         foreach ($item as $stub->class => $stub->position) {
  391.         }
  392.         if (isset($item[0])) {
  393.             $stub->cut $item[0];
  394.         }
  395.         $stub->value $stub->cut + ($stub->position \count($this->data[$stub->position]) : 0);
  396.         return $stub;
  397.     }
  398. }