annotate vendor/symfony/var-dumper/Cloner/Data.php @ 19:fa3358dc1485 tip

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