Chris@0
|
1 <?php
|
Chris@0
|
2 /*
|
Chris@0
|
3 * This file is part of the Recursion Context package.
|
Chris@0
|
4 *
|
Chris@0
|
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
|
Chris@0
|
6 *
|
Chris@0
|
7 * For the full copyright and license information, please view the LICENSE
|
Chris@0
|
8 * file that was distributed with this source code.
|
Chris@0
|
9 */
|
Chris@0
|
10
|
Chris@0
|
11 namespace SebastianBergmann\RecursionContext;
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * A context containing previously processed arrays and objects
|
Chris@0
|
15 * when recursively processing a value.
|
Chris@0
|
16 */
|
Chris@0
|
17 final class Context
|
Chris@0
|
18 {
|
Chris@0
|
19 /**
|
Chris@0
|
20 * @var array[]
|
Chris@0
|
21 */
|
Chris@0
|
22 private $arrays;
|
Chris@0
|
23
|
Chris@0
|
24 /**
|
Chris@0
|
25 * @var \SplObjectStorage
|
Chris@0
|
26 */
|
Chris@0
|
27 private $objects;
|
Chris@0
|
28
|
Chris@0
|
29 /**
|
Chris@0
|
30 * Initialises the context
|
Chris@0
|
31 */
|
Chris@0
|
32 public function __construct()
|
Chris@0
|
33 {
|
Chris@0
|
34 $this->arrays = array();
|
Chris@0
|
35 $this->objects = new \SplObjectStorage;
|
Chris@0
|
36 }
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * Adds a value to the context.
|
Chris@0
|
40 *
|
Chris@0
|
41 * @param array|object $value The value to add.
|
Chris@0
|
42 *
|
Chris@0
|
43 * @return int|string The ID of the stored value, either as a string or integer.
|
Chris@0
|
44 *
|
Chris@0
|
45 * @throws InvalidArgumentException Thrown if $value is not an array or object
|
Chris@0
|
46 */
|
Chris@0
|
47 public function add(&$value)
|
Chris@0
|
48 {
|
Chris@0
|
49 if (is_array($value)) {
|
Chris@0
|
50 return $this->addArray($value);
|
Chris@0
|
51 } elseif (is_object($value)) {
|
Chris@0
|
52 return $this->addObject($value);
|
Chris@0
|
53 }
|
Chris@0
|
54
|
Chris@0
|
55 throw new InvalidArgumentException(
|
Chris@0
|
56 'Only arrays and objects are supported'
|
Chris@0
|
57 );
|
Chris@0
|
58 }
|
Chris@0
|
59
|
Chris@0
|
60 /**
|
Chris@0
|
61 * Checks if the given value exists within the context.
|
Chris@0
|
62 *
|
Chris@0
|
63 * @param array|object $value The value to check.
|
Chris@0
|
64 *
|
Chris@0
|
65 * @return int|string|false The string or integer ID of the stored value if it has already been seen, or false if the value is not stored.
|
Chris@0
|
66 *
|
Chris@0
|
67 * @throws InvalidArgumentException Thrown if $value is not an array or object
|
Chris@0
|
68 */
|
Chris@0
|
69 public function contains(&$value)
|
Chris@0
|
70 {
|
Chris@0
|
71 if (is_array($value)) {
|
Chris@0
|
72 return $this->containsArray($value);
|
Chris@0
|
73 } elseif (is_object($value)) {
|
Chris@0
|
74 return $this->containsObject($value);
|
Chris@0
|
75 }
|
Chris@0
|
76
|
Chris@0
|
77 throw new InvalidArgumentException(
|
Chris@0
|
78 'Only arrays and objects are supported'
|
Chris@0
|
79 );
|
Chris@0
|
80 }
|
Chris@0
|
81
|
Chris@0
|
82 /**
|
Chris@0
|
83 * @param array $array
|
Chris@0
|
84 *
|
Chris@0
|
85 * @return bool|int
|
Chris@0
|
86 */
|
Chris@0
|
87 private function addArray(array &$array)
|
Chris@0
|
88 {
|
Chris@0
|
89 $key = $this->containsArray($array);
|
Chris@0
|
90
|
Chris@0
|
91 if ($key !== false) {
|
Chris@0
|
92 return $key;
|
Chris@0
|
93 }
|
Chris@0
|
94
|
Chris@0
|
95 $this->arrays[] = &$array;
|
Chris@0
|
96
|
Chris@0
|
97 return count($this->arrays) - 1;
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@0
|
100 /**
|
Chris@0
|
101 * @param object $object
|
Chris@0
|
102 *
|
Chris@0
|
103 * @return string
|
Chris@0
|
104 */
|
Chris@0
|
105 private function addObject($object)
|
Chris@0
|
106 {
|
Chris@0
|
107 if (!$this->objects->contains($object)) {
|
Chris@0
|
108 $this->objects->attach($object);
|
Chris@0
|
109 }
|
Chris@0
|
110
|
Chris@0
|
111 return spl_object_hash($object);
|
Chris@0
|
112 }
|
Chris@0
|
113
|
Chris@0
|
114 /**
|
Chris@0
|
115 * @param array $array
|
Chris@0
|
116 *
|
Chris@0
|
117 * @return int|false
|
Chris@0
|
118 */
|
Chris@0
|
119 private function containsArray(array &$array)
|
Chris@0
|
120 {
|
Chris@0
|
121 $keys = array_keys($this->arrays, $array, true);
|
Chris@0
|
122 $hash = '_Key_' . microtime(true);
|
Chris@0
|
123
|
Chris@0
|
124 foreach ($keys as $key) {
|
Chris@0
|
125 $this->arrays[$key][$hash] = $hash;
|
Chris@0
|
126
|
Chris@0
|
127 if (isset($array[$hash]) && $array[$hash] === $hash) {
|
Chris@0
|
128 unset($this->arrays[$key][$hash]);
|
Chris@0
|
129
|
Chris@0
|
130 return $key;
|
Chris@0
|
131 }
|
Chris@0
|
132
|
Chris@0
|
133 unset($this->arrays[$key][$hash]);
|
Chris@0
|
134 }
|
Chris@0
|
135
|
Chris@0
|
136 return false;
|
Chris@0
|
137 }
|
Chris@0
|
138
|
Chris@0
|
139 /**
|
Chris@0
|
140 * @param object $value
|
Chris@0
|
141 *
|
Chris@0
|
142 * @return string|false
|
Chris@0
|
143 */
|
Chris@0
|
144 private function containsObject($value)
|
Chris@0
|
145 {
|
Chris@0
|
146 if ($this->objects->contains($value)) {
|
Chris@0
|
147 return spl_object_hash($value);
|
Chris@0
|
148 }
|
Chris@0
|
149
|
Chris@0
|
150 return false;
|
Chris@0
|
151 }
|
Chris@0
|
152 }
|