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

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