PageRenderTime 38ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/symfony/symfony/src/Symfony/Component/VarDumper/Cloner/VarCloner.php

https://gitlab.com/TouirMohamedMarwen/Symfony2
PHP | 284 lines | 247 code | 17 blank | 20 comment | 59 complexity | c86dd2fcfea655fccb97e3d18f0db3cf MD5 | raw file
  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 VarCloner extends AbstractCloner
  15. {
  16. private static $hashMask = 0;
  17. private static $hashOffset = 0;
  18. /**
  19. * {@inheritdoc}
  20. */
  21. protected function doClone($var)
  22. {
  23. $useExt = extension_loaded('symfony_debug');
  24. $i = 0; // Current iteration position in $queue
  25. $len = 1; // Length of $queue
  26. $pos = 0; // Number of cloned items past the first level
  27. $refs = 0; // Hard references counter
  28. $queue = array(array($var)); // This breadth-first queue is the return value
  29. $arrayRefs = array(); // Map of queue indexes to stub array objects
  30. $hardRefs = array(); // Map of original zval hashes to stub objects
  31. $objRefs = array(); // Map of original object handles to their stub object couterpart
  32. $resRefs = array(); // Map of original resource handles to their stub object couterpart
  33. $values = array(); // Map of stub objects' hashes to original values
  34. $maxItems = $this->maxItems;
  35. $maxString = $this->maxString;
  36. $cookie = (object) array(); // Unique object used to detect hard references
  37. $a = null; // Array cast for nested structures
  38. $stub = null; // Stub capturing the main properties of an original item value,
  39. // or null if the original value is used directly
  40. $zval = array( // Main properties of the current value
  41. 'type' => null,
  42. 'zval_isref' => null,
  43. 'zval_hash' => null,
  44. 'array_count' => null,
  45. 'object_class' => null,
  46. 'object_handle' => null,
  47. 'resource_type' => null,
  48. );
  49. if (!self::$hashMask) {
  50. self::initHashMask();
  51. }
  52. $hashMask = self::$hashMask;
  53. $hashOffset = self::$hashOffset;
  54. for ($i = 0; $i < $len; ++$i) {
  55. $indexed = true; // Whether the currently iterated array is numerically indexed or not
  56. $j = -1; // Position in the currently iterated array
  57. $step = $queue[$i]; // Copy of the currently iterated array used for hard references detection
  58. foreach ($step as $k => $v) {
  59. // $k is the original key
  60. // $v is the original value or a stub object in case of hard references
  61. if ($indexed && $k !== ++$j) {
  62. $indexed = false;
  63. }
  64. if ($useExt) {
  65. $zval = symfony_zval_info($k, $step);
  66. } else {
  67. $step[$k] = $cookie;
  68. if ($zval['zval_isref'] = $queue[$i][$k] === $cookie) {
  69. $zval['zval_hash'] = $v instanceof Stub ? spl_object_hash($v) : null;
  70. }
  71. $zval['type'] = gettype($v);
  72. }
  73. if ($zval['zval_isref']) {
  74. $queue[$i][$k] =& $stub; // Break hard references to make $queue completely
  75. unset($stub); // independent from the original structure
  76. if (isset($hardRefs[$zval['zval_hash']])) {
  77. $queue[$i][$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($step[$k] = $v);
  78. if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) {
  79. ++$v->value->refCount;
  80. }
  81. ++$v->refCount;
  82. continue;
  83. }
  84. }
  85. // Create $stub when the original value $v can not be used directly
  86. // If $v is a nested structure, put that structure in array $a
  87. switch ($zval['type']) {
  88. case 'string':
  89. if (isset($v[0]) && !preg_match('//u', $v)) {
  90. $stub = new Stub();
  91. $stub->type = Stub::TYPE_STRING;
  92. $stub->class = Stub::STRING_BINARY;
  93. if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) {
  94. $stub->cut = $cut;
  95. $cut = substr_replace($v, '', -$cut);
  96. } else {
  97. $cut = $v;
  98. }
  99. $stub->value = Data::utf8Encode($cut);
  100. } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = iconv_strlen($v, 'UTF-8') - $maxString) {
  101. $stub = new Stub();
  102. $stub->type = Stub::TYPE_STRING;
  103. $stub->class = Stub::STRING_UTF8;
  104. $stub->cut = $cut;
  105. $stub->value = iconv_substr($v, 0, $maxString, 'UTF-8');
  106. }
  107. break;
  108. case 'integer':
  109. break;
  110. case 'array':
  111. if ($v) {
  112. $stub = $arrayRefs[$len] = new Stub();
  113. $stub->type = Stub::TYPE_ARRAY;
  114. $stub->class = Stub::ARRAY_ASSOC;
  115. $stub->value = $zval['array_count'] ?: count($v);
  116. $a = $v;
  117. }
  118. break;
  119. case 'object':
  120. if (empty($objRefs[$h = $zval['object_handle'] ?: ($hashMask ^ hexdec(substr(spl_object_hash($v), $hashOffset, PHP_INT_SIZE)))])) {
  121. $stub = new Stub();
  122. $stub->type = Stub::TYPE_OBJECT;
  123. $stub->class = $zval['object_class'] ?: get_class($v);
  124. $stub->value = $v;
  125. $stub->handle = $h;
  126. $a = $this->castObject($stub, 0 < $i);
  127. if ($v !== $stub->value) {
  128. if (Stub::TYPE_OBJECT !== $stub->type) {
  129. break;
  130. }
  131. if ($useExt) {
  132. $zval['type'] = $stub->value;
  133. $zval = symfony_zval_info('type', $zval);
  134. $h = $zval['object_handle'];
  135. } else {
  136. $h = $hashMask ^ hexdec(substr(spl_object_hash($stub->value), $hashOffset, PHP_INT_SIZE));
  137. }
  138. $stub->handle = $h;
  139. }
  140. $stub->value = null;
  141. if (0 <= $maxItems && $maxItems <= $pos) {
  142. $stub->cut = count($a);
  143. $a = null;
  144. }
  145. }
  146. if (empty($objRefs[$h])) {
  147. $objRefs[$h] = $stub;
  148. } else {
  149. $stub = $objRefs[$h];
  150. ++$stub->refCount;
  151. $a = null;
  152. }
  153. break;
  154. case 'resource':
  155. case 'unknown type':
  156. if (empty($resRefs[$h = (int) $v])) {
  157. $stub = new Stub();
  158. $stub->type = Stub::TYPE_RESOURCE;
  159. $stub->class = $zval['resource_type'] ?: get_resource_type($v);
  160. $stub->value = $v;
  161. $stub->handle = $h;
  162. $a = $this->castResource($stub, 0 < $i);
  163. $stub->value = null;
  164. if (0 <= $maxItems && $maxItems <= $pos) {
  165. $stub->cut = count($a);
  166. $a = null;
  167. }
  168. }
  169. if (empty($resRefs[$h])) {
  170. $resRefs[$h] = $stub;
  171. } else {
  172. $stub = $resRefs[$h];
  173. ++$stub->refCount;
  174. $a = null;
  175. }
  176. break;
  177. }
  178. if (isset($stub)) {
  179. if ($zval['zval_isref']) {
  180. if ($useExt) {
  181. $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub();
  182. $v->value = $stub;
  183. } else {
  184. $step[$k] = new Stub();
  185. $step[$k]->value = $stub;
  186. $h = spl_object_hash($step[$k]);
  187. $queue[$i][$k] = $hardRefs[$h] =& $step[$k];
  188. $values[$h] = $v;
  189. }
  190. $queue[$i][$k]->handle = ++$refs;
  191. } else {
  192. $queue[$i][$k] = $stub;
  193. }
  194. if ($a) {
  195. if ($i && 0 <= $maxItems) {
  196. $k = count($a);
  197. if ($pos < $maxItems) {
  198. if ($maxItems < $pos += $k) {
  199. $a = array_slice($a, 0, $maxItems - $pos);
  200. if ($stub->cut >= 0) {
  201. $stub->cut += $pos - $maxItems;
  202. }
  203. }
  204. } else {
  205. if ($stub->cut >= 0) {
  206. $stub->cut += $k;
  207. }
  208. $stub = $a = null;
  209. unset($arrayRefs[$len]);
  210. continue;
  211. }
  212. }
  213. $queue[$len] = $a;
  214. $stub->position = $len++;
  215. }
  216. $stub = $a = null;
  217. } elseif ($zval['zval_isref']) {
  218. if ($useExt) {
  219. $queue[$i][$k] = $hardRefs[$zval['zval_hash']] = new Stub();
  220. $queue[$i][$k]->value = $v;
  221. } else {
  222. $step[$k] = $queue[$i][$k] = new Stub();
  223. $step[$k]->value = $v;
  224. $h = spl_object_hash($step[$k]);
  225. $hardRefs[$h] =& $step[$k];
  226. $values[$h] = $v;
  227. }
  228. $queue[$i][$k]->handle = ++$refs;
  229. }
  230. }
  231. if (isset($arrayRefs[$i])) {
  232. if ($indexed) {
  233. $arrayRefs[$i]->class = Stub::ARRAY_INDEXED;
  234. }
  235. unset($arrayRefs[$i]);
  236. }
  237. }
  238. foreach ($values as $h => $v) {
  239. $hardRefs[$h] = $v;
  240. }
  241. return $queue;
  242. }
  243. private static function initHashMask()
  244. {
  245. $obj = (object) array();
  246. self::$hashOffset = 16 - PHP_INT_SIZE;
  247. self::$hashMask = -1;
  248. if (defined('HHVM_VERSION')) {
  249. self::$hashOffset += 16;
  250. } else {
  251. // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below
  252. $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush');
  253. foreach (debug_backtrace(PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) {
  254. if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && in_array($frame['function'], $obFuncs)) {
  255. $frame['line'] = 0;
  256. break;
  257. }
  258. }
  259. if (!empty($frame['line'])) {
  260. ob_start();
  261. debug_zval_dump($obj);
  262. self::$hashMask = substr(ob_get_clean(), 17);
  263. }
  264. }
  265. self::$hashMask ^= hexdec(substr(spl_object_hash($obj), self::$hashOffset, PHP_INT_SIZE));
  266. }
  267. }