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

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