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