Data.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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. /**
  12. * @author Nicolas Grekas <p@tchwork.com>
  13. */
  14. class Data
  15. {
  16. private $data;
  17. private $maxDepth = 20;
  18. private $maxItemsPerDepth = -1;
  19. private $useRefHandles = -1;
  20. /**
  21. * @param array $data An array as returned by ClonerInterface::cloneVar()
  22. */
  23. public function __construct(array $data)
  24. {
  25. $this->data = $data;
  26. }
  27. /**
  28. * @return array The raw data structure
  29. */
  30. public function getRawData()
  31. {
  32. return $this->data;
  33. }
  34. /**
  35. * Returns a depth limited clone of $this.
  36. *
  37. * @param int $maxDepth The max dumped depth level
  38. *
  39. * @return self A clone of $this
  40. */
  41. public function withMaxDepth($maxDepth)
  42. {
  43. $data = clone $this;
  44. $data->maxDepth = (int) $maxDepth;
  45. return $data;
  46. }
  47. /**
  48. * Limits the number of elements per depth level.
  49. *
  50. * @param int $maxItemsPerDepth The max number of items dumped per depth level
  51. *
  52. * @return self A clone of $this
  53. */
  54. public function withMaxItemsPerDepth($maxItemsPerDepth)
  55. {
  56. $data = clone $this;
  57. $data->maxItemsPerDepth = (int) $maxItemsPerDepth;
  58. return $data;
  59. }
  60. /**
  61. * Enables/disables objects' identifiers tracking.
  62. *
  63. * @param bool $useRefHandles False to hide global ref. handles
  64. *
  65. * @return self A clone of $this
  66. */
  67. public function withRefHandles($useRefHandles)
  68. {
  69. $data = clone $this;
  70. $data->useRefHandles = $useRefHandles ? -1 : 0;
  71. return $data;
  72. }
  73. /**
  74. * Returns a depth limited clone of $this.
  75. *
  76. * @param int $maxDepth The max dumped depth level
  77. * @param int $maxItemsPerDepth The max number of items dumped per depth level
  78. * @param bool $useRefHandles False to hide ref. handles
  79. *
  80. * @return self A depth limited clone of $this
  81. *
  82. * @deprecated since Symfony 2.7, to be removed in 3.0. Use withMaxDepth, withMaxItemsPerDepth or withRefHandles instead.
  83. */
  84. public function getLimitedClone($maxDepth, $maxItemsPerDepth, $useRefHandles = true)
  85. {
  86. @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.7 and will be removed in 3.0. Use withMaxDepth, withMaxItemsPerDepth or withRefHandles methods instead.', E_USER_DEPRECATED);
  87. $data = clone $this;
  88. $data->maxDepth = (int) $maxDepth;
  89. $data->maxItemsPerDepth = (int) $maxItemsPerDepth;
  90. $data->useRefHandles = $useRefHandles ? -1 : 0;
  91. return $data;
  92. }
  93. /**
  94. * Dumps data with a DumperInterface dumper.
  95. */
  96. public function dump(DumperInterface $dumper)
  97. {
  98. $refs = array(0);
  99. $this->dumpItem($dumper, new Cursor(), $refs, $this->data[0][0]);
  100. }
  101. /**
  102. * Depth-first dumping of items.
  103. *
  104. * @param DumperInterface $dumper The dumper being used for dumping
  105. * @param Cursor $cursor A cursor used for tracking dumper state position
  106. * @param array &$refs A map of all references discovered while dumping
  107. * @param mixed $item A Stub object or the original value being dumped
  108. */
  109. private function dumpItem($dumper, $cursor, &$refs, $item)
  110. {
  111. $cursor->refIndex = 0;
  112. $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0;
  113. $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0;
  114. $firstSeen = true;
  115. if (!$item instanceof Stub) {
  116. $type = gettype($item);
  117. } elseif (Stub::TYPE_REF === $item->type) {
  118. if ($item->handle) {
  119. if (!isset($refs[$r = $item->handle - (PHP_INT_MAX >> 1)])) {
  120. $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0];
  121. } else {
  122. $firstSeen = false;
  123. }
  124. $cursor->hardRefTo = $refs[$r];
  125. $cursor->hardRefHandle = $this->useRefHandles & $item->handle;
  126. $cursor->hardRefCount = $item->refCount;
  127. }
  128. $type = $item->class ?: gettype($item->value);
  129. $item = $item->value;
  130. }
  131. if ($item instanceof Stub) {
  132. if ($item->refCount) {
  133. if (!isset($refs[$r = $item->handle])) {
  134. $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0];
  135. } else {
  136. $firstSeen = false;
  137. }
  138. $cursor->softRefTo = $refs[$r];
  139. }
  140. $cursor->softRefHandle = $this->useRefHandles & $item->handle;
  141. $cursor->softRefCount = $item->refCount;
  142. $cut = $item->cut;
  143. if ($item->position && $firstSeen) {
  144. $children = $this->data[$item->position];
  145. if ($cursor->stop) {
  146. if ($cut >= 0) {
  147. $cut += count($children);
  148. }
  149. $children = array();
  150. }
  151. } else {
  152. $children = array();
  153. }
  154. switch ($item->type) {
  155. case Stub::TYPE_STRING:
  156. $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut);
  157. break;
  158. case Stub::TYPE_ARRAY:
  159. $item = clone $item;
  160. $item->type = $item->class;
  161. $item->class = $item->value;
  162. // no break
  163. case Stub::TYPE_OBJECT:
  164. case Stub::TYPE_RESOURCE:
  165. $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth;
  166. $dumper->enterHash($cursor, $item->type, $item->class, $withChildren);
  167. if ($withChildren) {
  168. $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type);
  169. } elseif ($children && 0 <= $cut) {
  170. $cut += count($children);
  171. }
  172. $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut);
  173. break;
  174. default:
  175. throw new \RuntimeException(sprintf('Unexpected Stub type: %s', $item->type));
  176. }
  177. } elseif ('array' === $type) {
  178. $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false);
  179. $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0);
  180. } elseif ('string' === $type) {
  181. $dumper->dumpString($cursor, $item, false, 0);
  182. } else {
  183. $dumper->dumpScalar($cursor, $type, $item);
  184. }
  185. }
  186. /**
  187. * Dumps children of hash structures.
  188. *
  189. * @param DumperInterface $dumper
  190. * @param Cursor $parentCursor The cursor of the parent hash
  191. * @param array &$refs A map of all references discovered while dumping
  192. * @param array $children The children to dump
  193. * @param int $hashCut The number of items removed from the original hash
  194. * @param string $hashType A Cursor::HASH_* const
  195. *
  196. * @return int The final number of removed items
  197. */
  198. private function dumpChildren($dumper, $parentCursor, &$refs, $children, $hashCut, $hashType)
  199. {
  200. $cursor = clone $parentCursor;
  201. ++$cursor->depth;
  202. $cursor->hashType = $hashType;
  203. $cursor->hashIndex = 0;
  204. $cursor->hashLength = count($children);
  205. $cursor->hashCut = $hashCut;
  206. foreach ($children as $key => $child) {
  207. $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key);
  208. $cursor->hashKey = $key;
  209. $this->dumpItem($dumper, $cursor, $refs, $child);
  210. if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) {
  211. $parentCursor->stop = true;
  212. return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut;
  213. }
  214. }
  215. return $hashCut;
  216. }
  217. }