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, get_debug_type($value)));
  110.         }
  111.         yield from $value;
  112.     }
  113.     public function __get(string $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(string $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.      * @return static
  162.      */
  163.     public function withMaxDepth(int $maxDepth)
  164.     {
  165.         $data = clone $this;
  166.         $data->maxDepth = (int) $maxDepth;
  167.         return $data;
  168.     }
  169.     /**
  170.      * Limits the number of elements per depth level.
  171.      *
  172.      * @return static
  173.      */
  174.     public function withMaxItemsPerDepth(int $maxItemsPerDepth)
  175.     {
  176.         $data = clone $this;
  177.         $data->maxItemsPerDepth = (int) $maxItemsPerDepth;
  178.         return $data;
  179.     }
  180.     /**
  181.      * Enables/disables objects' identifiers tracking.
  182.      *
  183.      * @param bool $useRefHandles False to hide global ref. handles
  184.      *
  185.      * @return static
  186.      */
  187.     public function withRefHandles(bool $useRefHandles)
  188.     {
  189.         $data = clone $this;
  190.         $data->useRefHandles $useRefHandles ? -0;
  191.         return $data;
  192.     }
  193.     /**
  194.      * @return static
  195.      */
  196.     public function withContext(array $context)
  197.     {
  198.         $data = clone $this;
  199.         $data->context $context;
  200.         return $data;
  201.     }
  202.     /**
  203.      * Seeks to a specific key in nested data structures.
  204.      *
  205.      * @param string|int $key The key to seek to
  206.      *
  207.      * @return static|null Null if the key is not set
  208.      */
  209.     public function seek($key)
  210.     {
  211.         $item $this->data[$this->position][$this->key];
  212.         if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) {
  213.             $item $item->value;
  214.         }
  215.         if (!($item $this->getStub($item)) instanceof Stub || !$item->position) {
  216.             return null;
  217.         }
  218.         $keys = [$key];
  219.         switch ($item->type) {
  220.             case Stub::TYPE_OBJECT:
  221.                 $keys[] = Caster::PREFIX_DYNAMIC.$key;
  222.                 $keys[] = Caster::PREFIX_PROTECTED.$key;
  223.                 $keys[] = Caster::PREFIX_VIRTUAL.$key;
  224.                 $keys[] = "\0$item->class\0$key";
  225.                 // no break
  226.             case Stub::TYPE_ARRAY:
  227.             case Stub::TYPE_RESOURCE:
  228.                 break;
  229.             default:
  230.                 return null;
  231.         }
  232.         $data null;
  233.         $children $this->data[$item->position];
  234.         foreach ($keys as $key) {
  235.             if (isset($children[$key]) || \array_key_exists($key$children)) {
  236.                 $data = clone $this;
  237.                 $data->key $key;
  238.                 $data->position $item->position;
  239.                 break;
  240.             }
  241.         }
  242.         return $data;
  243.     }
  244.     /**
  245.      * Dumps data with a DumperInterface dumper.
  246.      */
  247.     public function dump(DumperInterface $dumper)
  248.     {
  249.         $refs = [0];
  250.         $cursor = new Cursor();
  251.         if ($cursor->attr $this->context[SourceContextProvider::class] ?? []) {
  252.             $cursor->attr['if_links'] = true;
  253.             $cursor->hashType = -1;
  254.             $dumper->dumpScalar($cursor'default''^');
  255.             $cursor->attr = ['if_links' => true];
  256.             $dumper->dumpScalar($cursor'default'' ');
  257.             $cursor->hashType 0;
  258.         }
  259.         $this->dumpItem($dumper$cursor$refs$this->data[$this->position][$this->key]);
  260.     }
  261.     /**
  262.      * Depth-first dumping of items.
  263.      *
  264.      * @param mixed $item A Stub object or the original value being dumped
  265.      */
  266.     private function dumpItem(DumperInterface $dumperCursor $cursor, array &$refs$item)
  267.     {
  268.         $cursor->refIndex 0;
  269.         $cursor->softRefTo $cursor->softRefHandle $cursor->softRefCount 0;
  270.         $cursor->hardRefTo $cursor->hardRefHandle $cursor->hardRefCount 0;
  271.         $firstSeen true;
  272.         if (!$item instanceof Stub) {
  273.             $cursor->attr = [];
  274.             $type \gettype($item);
  275.             if ($item && 'array' === $type) {
  276.                 $item $this->getStub($item);
  277.             }
  278.         } elseif (Stub::TYPE_REF === $item->type) {
  279.             if ($item->handle) {
  280.                 if (!isset($refs[$r $item->handle - (\PHP_INT_MAX >> 1)])) {
  281.                     $cursor->refIndex $refs[$r] = $cursor->refIndex ?: ++$refs[0];
  282.                 } else {
  283.                     $firstSeen false;
  284.                 }
  285.                 $cursor->hardRefTo $refs[$r];
  286.                 $cursor->hardRefHandle $this->useRefHandles $item->handle;
  287.                 $cursor->hardRefCount $item->handle $item->refCount 0;
  288.             }
  289.             $cursor->attr $item->attr;
  290.             $type $item->class ?: \gettype($item->value);
  291.             $item $this->getStub($item->value);
  292.         }
  293.         if ($item instanceof Stub) {
  294.             if ($item->refCount) {
  295.                 if (!isset($refs[$r $item->handle])) {
  296.                     $cursor->refIndex $refs[$r] = $cursor->refIndex ?: ++$refs[0];
  297.                 } else {
  298.                     $firstSeen false;
  299.                 }
  300.                 $cursor->softRefTo $refs[$r];
  301.             }
  302.             $cursor->softRefHandle $this->useRefHandles $item->handle;
  303.             $cursor->softRefCount $item->refCount;
  304.             $cursor->attr $item->attr;
  305.             $cut $item->cut;
  306.             if ($item->position && $firstSeen) {
  307.                 $children $this->data[$item->position];
  308.                 if ($cursor->stop) {
  309.                     if ($cut >= 0) {
  310.                         $cut += \count($children);
  311.                     }
  312.                     $children = [];
  313.                 }
  314.             } else {
  315.                 $children = [];
  316.             }
  317.             switch ($item->type) {
  318.                 case Stub::TYPE_STRING:
  319.                     $dumper->dumpString($cursor$item->valueStub::STRING_BINARY === $item->class$cut);
  320.                     break;
  321.                 case Stub::TYPE_ARRAY:
  322.                     $item = clone $item;
  323.                     $item->type $item->class;
  324.                     $item->class $item->value;
  325.                     // no break
  326.                 case Stub::TYPE_OBJECT:
  327.                 case Stub::TYPE_RESOURCE:
  328.                     $withChildren $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
  329.                     $dumper->enterHash($cursor$item->type$item->class$withChildren);
  330.                     if ($withChildren) {
  331.                         if ($cursor->skipChildren) {
  332.                             $withChildren false;
  333.                             $cut = -1;
  334.                         } else {
  335.                             $cut $this->dumpChildren($dumper$cursor$refs$children$cut$item->typenull !== $item->class);
  336.                         }
  337.                     } elseif ($children && <= $cut) {
  338.                         $cut += \count($children);
  339.                     }
  340.                     $cursor->skipChildren false;
  341.                     $dumper->leaveHash($cursor$item->type$item->class$withChildren$cut);
  342.                     break;
  343.                 default:
  344.                     throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".'$item->type));
  345.             }
  346.         } elseif ('array' === $type) {
  347.             $dumper->enterHash($cursorCursor::HASH_INDEXED0false);
  348.             $dumper->leaveHash($cursorCursor::HASH_INDEXED0false0);
  349.         } elseif ('string' === $type) {
  350.             $dumper->dumpString($cursor$itemfalse0);
  351.         } else {
  352.             $dumper->dumpScalar($cursor$type$item);
  353.         }
  354.     }
  355.     /**
  356.      * Dumps children of hash structures.
  357.      *
  358.      * @return int The final number of removed items
  359.      */
  360.     private function dumpChildren(DumperInterface $dumperCursor $parentCursor, array &$refs, array $childrenint $hashCutint $hashTypebool $dumpKeys): int
  361.     {
  362.         $cursor = clone $parentCursor;
  363.         ++$cursor->depth;
  364.         $cursor->hashType $hashType;
  365.         $cursor->hashIndex 0;
  366.         $cursor->hashLength \count($children);
  367.         $cursor->hashCut $hashCut;
  368.         foreach ($children as $key => $child) {
  369.             $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u'$key);
  370.             $cursor->hashKey $dumpKeys $key null;
  371.             $this->dumpItem($dumper$cursor$refs$child);
  372.             if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
  373.                 $parentCursor->stop true;
  374.                 return $hashCut >= $hashCut $cursor->hashLength $cursor->hashIndex $hashCut;
  375.             }
  376.         }
  377.         return $hashCut;
  378.     }
  379.     private function getStub($item)
  380.     {
  381.         if (!$item || !\is_array($item)) {
  382.             return $item;
  383.         }
  384.         $stub = new Stub();
  385.         $stub->type Stub::TYPE_ARRAY;
  386.         foreach ($item as $stub->class => $stub->position) {
  387.         }
  388.         if (isset($item[0])) {
  389.             $stub->cut $item[0];
  390.         }
  391.         $stub->value $stub->cut + ($stub->position \count($this->data[$stub->position]) : 0);
  392.         return $stub;
  393.     }
  394. }