9c9717ce155356188c7155f4a3af747d0b27e5c3
[yaffs-website] / vendor / symfony / var-dumper / Cloner / VarCloner.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\VarDumper\Cloner;
13
14 /**
15  * @author Nicolas Grekas <p@tchwork.com>
16  */
17 class VarCloner extends AbstractCloner
18 {
19     private static $hashMask = 0;
20     private static $hashOffset = 0;
21
22     /**
23      * {@inheritdoc}
24      */
25     protected function doClone($var)
26     {
27         $useExt = $this->useExt;
28         $len = 1;                       // Length of $queue
29         $pos = 0;                       // Number of cloned items past the first level
30         $refsCounter = 0;               // Hard references counter
31         $queue = array(array($var));    // This breadth-first queue is the return value
32         $arrayRefs = array();           // Map of queue indexes to stub array objects
33         $hardRefs = array();            // Map of original zval hashes to stub objects
34         $objRefs = array();             // Map of original object handles to their stub object couterpart
35         $resRefs = array();             // Map of original resource handles to their stub object couterpart
36         $values = array();              // Map of stub objects' hashes to original values
37         $maxItems = $this->maxItems;
38         $maxString = $this->maxString;
39         $cookie = (object) array();     // Unique object used to detect hard references
40         $gid = uniqid(mt_rand(), true); // Unique string used to detect the special $GLOBALS variable
41         $a = null;                      // Array cast for nested structures
42         $stub = null;                   // Stub capturing the main properties of an original item value
43                                         // or null if the original value is used directly
44         $zval = array(                  // Main properties of the current value
45             'type' => null,
46             'zval_isref' => null,
47             'zval_hash' => null,
48             'array_count' => null,
49             'object_class' => null,
50             'object_handle' => null,
51             'resource_type' => null,
52         );
53         if (!self::$hashMask) {
54             self::initHashMask();
55         }
56         $hashMask = self::$hashMask;
57         $hashOffset = self::$hashOffset;
58
59         for ($i = 0; $i < $len; ++$i) {
60             $indexed = true;            // Whether the currently iterated array is numerically indexed or not
61             $j = -1;                    // Position in the currently iterated array
62             $fromObjCast = array_keys($queue[$i]);
63             $fromObjCast = array_keys(array_flip($fromObjCast)) !== $fromObjCast;
64             $refs = $vals = $fromObjCast ? array_values($queue[$i]) : $queue[$i];
65             foreach ($queue[$i] as $k => $v) {
66                 // $k is the original key
67                 // $v is the original value or a stub object in case of hard references
68                 if ($k !== ++$j) {
69                     $indexed = false;
70                 }
71                 if ($fromObjCast) {
72                     $k = $j;
73                 }
74                 if ($useExt) {
75                     $zval = symfony_zval_info($k, $refs);
76                 } else {
77                     $refs[$k] = $cookie;
78                     if ($zval['zval_isref'] = $vals[$k] === $cookie) {
79                         $zval['zval_hash'] = $v instanceof Stub ? spl_object_hash($v) : null;
80                     }
81                     $zval['type'] = gettype($v);
82                 }
83                 if ($zval['zval_isref']) {
84                     $vals[$k] = &$stub;         // Break hard references to make $queue completely
85                     unset($stub);               // independent from the original structure
86                     if (isset($hardRefs[$zval['zval_hash']])) {
87                         $vals[$k] = $useExt ? ($v = $hardRefs[$zval['zval_hash']]) : ($refs[$k] = $v);
88                         if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) {
89                             ++$v->value->refCount;
90                         }
91                         ++$v->refCount;
92                         continue;
93                     }
94                 }
95                 // Create $stub when the original value $v can not be used directly
96                 // If $v is a nested structure, put that structure in array $a
97                 switch ($zval['type']) {
98                     case 'string':
99                         if (isset($v[0]) && !preg_match('//u', $v)) {
100                             $stub = new Stub();
101                             $stub->type = Stub::TYPE_STRING;
102                             $stub->class = Stub::STRING_BINARY;
103                             if (0 <= $maxString && 0 < $cut = strlen($v) - $maxString) {
104                                 $stub->cut = $cut;
105                                 $stub->value = substr($v, 0, -$cut);
106                             } else {
107                                 $stub->value = $v;
108                             }
109                         } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) {
110                             $stub = new Stub();
111                             $stub->type = Stub::TYPE_STRING;
112                             $stub->class = Stub::STRING_UTF8;
113                             $stub->cut = $cut;
114                             $stub->value = mb_substr($v, 0, $maxString, 'UTF-8');
115                         }
116                         break;
117
118                     case 'integer':
119                         break;
120
121                     case 'array':
122                         if ($v) {
123                             $stub = $arrayRefs[$len] = new Stub();
124                             $stub->type = Stub::TYPE_ARRAY;
125                             $stub->class = Stub::ARRAY_ASSOC;
126
127                             // Copies of $GLOBALS have very strange behavior,
128                             // let's detect them with some black magic
129                             $a = $v;
130                             $a[$gid] = true;
131
132                             // Happens with copies of $GLOBALS
133                             if (isset($v[$gid])) {
134                                 unset($v[$gid]);
135                                 $a = array();
136                                 foreach ($v as $gk => &$gv) {
137                                     $a[$gk] = &$gv;
138                                 }
139                             } else {
140                                 $a = $v;
141                             }
142
143                             $stub->value = $zval['array_count'] ?: count($a);
144                         }
145                         break;
146
147                     case 'object':
148                         if (empty($objRefs[$h = $zval['object_handle'] ?: ($hashMask ^ hexdec(substr(spl_object_hash($v), $hashOffset, PHP_INT_SIZE)))])) {
149                             $stub = new Stub();
150                             $stub->type = Stub::TYPE_OBJECT;
151                             $stub->class = $zval['object_class'] ?: get_class($v);
152                             $stub->value = $v;
153                             $stub->handle = $h;
154                             $a = $this->castObject($stub, 0 < $i);
155                             if ($v !== $stub->value) {
156                                 if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) {
157                                     break;
158                                 }
159                                 if ($useExt) {
160                                     $zval['type'] = $stub->value;
161                                     $zval = symfony_zval_info('type', $zval);
162                                     $h = $zval['object_handle'];
163                                 } else {
164                                     $h = $hashMask ^ hexdec(substr(spl_object_hash($stub->value), $hashOffset, PHP_INT_SIZE));
165                                 }
166                                 $stub->handle = $h;
167                             }
168                             $stub->value = null;
169                             if (0 <= $maxItems && $maxItems <= $pos) {
170                                 $stub->cut = count($a);
171                                 $a = null;
172                             }
173                         }
174                         if (empty($objRefs[$h])) {
175                             $objRefs[$h] = $stub;
176                         } else {
177                             $stub = $objRefs[$h];
178                             ++$stub->refCount;
179                             $a = null;
180                         }
181                         break;
182
183                     case 'resource':
184                     case 'unknown type':
185                     case 'resource (closed)':
186                         if (empty($resRefs[$h = (int) $v])) {
187                             $stub = new Stub();
188                             $stub->type = Stub::TYPE_RESOURCE;
189                             if ('Unknown' === $stub->class = $zval['resource_type'] ?: @get_resource_type($v)) {
190                                 $stub->class = 'Closed';
191                             }
192                             $stub->value = $v;
193                             $stub->handle = $h;
194                             $a = $this->castResource($stub, 0 < $i);
195                             $stub->value = null;
196                             if (0 <= $maxItems && $maxItems <= $pos) {
197                                 $stub->cut = count($a);
198                                 $a = null;
199                             }
200                         }
201                         if (empty($resRefs[$h])) {
202                             $resRefs[$h] = $stub;
203                         } else {
204                             $stub = $resRefs[$h];
205                             ++$stub->refCount;
206                             $a = null;
207                         }
208                         break;
209                 }
210
211                 if (isset($stub)) {
212                     if ($zval['zval_isref']) {
213                         if ($useExt) {
214                             $vals[$k] = $hardRefs[$zval['zval_hash']] = $v = new Stub();
215                             $v->value = $stub;
216                         } else {
217                             $refs[$k] = new Stub();
218                             $refs[$k]->value = $stub;
219                             $h = spl_object_hash($refs[$k]);
220                             $vals[$k] = $hardRefs[$h] = &$refs[$k];
221                             $values[$h] = $v;
222                         }
223                         $vals[$k]->handle = ++$refsCounter;
224                     } else {
225                         $vals[$k] = $stub;
226                     }
227
228                     if ($a) {
229                         if ($i && 0 <= $maxItems) {
230                             $k = count($a);
231                             if ($pos < $maxItems) {
232                                 if ($maxItems < $pos += $k) {
233                                     $a = array_slice($a, 0, $maxItems - $pos);
234                                     if ($stub->cut >= 0) {
235                                         $stub->cut += $pos - $maxItems;
236                                     }
237                                 }
238                             } else {
239                                 if ($stub->cut >= 0) {
240                                     $stub->cut += $k;
241                                 }
242                                 $stub = $a = null;
243                                 unset($arrayRefs[$len]);
244                                 continue;
245                             }
246                         }
247                         $queue[$len] = $a;
248                         $stub->position = $len++;
249                     }
250                     $stub = $a = null;
251                 } elseif ($zval['zval_isref']) {
252                     if ($useExt) {
253                         $vals[$k] = $hardRefs[$zval['zval_hash']] = new Stub();
254                         $vals[$k]->value = $v;
255                     } else {
256                         $refs[$k] = $vals[$k] = new Stub();
257                         $refs[$k]->value = $v;
258                         $h = spl_object_hash($refs[$k]);
259                         $hardRefs[$h] = &$refs[$k];
260                         $values[$h] = $v;
261                     }
262                     $vals[$k]->handle = ++$refsCounter;
263                 }
264             }
265
266             if ($fromObjCast) {
267                 $refs = $vals;
268                 $vals = array();
269                 $j = -1;
270                 foreach ($queue[$i] as $k => $v) {
271                     foreach (array($k => $v) as $a => $v) {
272                     }
273                     if ($a !== $k) {
274                         $vals = (object) $vals;
275                         $vals->{$k} = $refs[++$j];
276                         $vals = (array) $vals;
277                     } else {
278                         $vals[$k] = $refs[++$j];
279                     }
280                 }
281             }
282
283             $queue[$i] = $vals;
284
285             if (isset($arrayRefs[$i])) {
286                 if ($indexed) {
287                     $arrayRefs[$i]->class = Stub::ARRAY_INDEXED;
288                 }
289                 unset($arrayRefs[$i]);
290             }
291         }
292
293         foreach ($values as $h => $v) {
294             $hardRefs[$h] = $v;
295         }
296
297         return $queue;
298     }
299
300     private static function initHashMask()
301     {
302         $obj = (object) array();
303         self::$hashOffset = 16 - PHP_INT_SIZE;
304         self::$hashMask = -1;
305
306         if (defined('HHVM_VERSION')) {
307             self::$hashOffset += 16;
308         } else {
309             // check if we are nested in an output buffering handler to prevent a fatal error with ob_start() below
310             $obFuncs = array('ob_clean', 'ob_end_clean', 'ob_flush', 'ob_end_flush', 'ob_get_contents', 'ob_get_flush');
311             foreach (debug_backtrace(\PHP_VERSION_ID >= 50400 ? DEBUG_BACKTRACE_IGNORE_ARGS : false) as $frame) {
312                 if (isset($frame['function'][0]) && !isset($frame['class']) && 'o' === $frame['function'][0] && in_array($frame['function'], $obFuncs)) {
313                     $frame['line'] = 0;
314                     break;
315                 }
316             }
317             if (!empty($frame['line'])) {
318                 ob_start();
319                 debug_zval_dump($obj);
320                 self::$hashMask = (int) substr(ob_get_clean(), 17);
321             }
322         }
323
324         self::$hashMask ^= hexdec(substr(spl_object_hash($obj), self::$hashOffset, PHP_INT_SIZE));
325     }
326 }