Context.php (3765B)
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 * @return int|string The ID of the stored value, either as 43 * a string or integer. 44 * @throws InvalidArgumentException Thrown if $value is not an array or 45 * object 46 */ 47 public function add(&$value) 48 { 49 if (is_array($value)) { 50 return $this->addArray($value); 51 } 52 53 else if (is_object($value)) { 54 return $this->addObject($value); 55 } 56 57 throw new InvalidArgumentException( 58 'Only arrays and objects are supported' 59 ); 60 } 61 62 /** 63 * Checks if the given value exists within the context. 64 * 65 * @param array|object $value The value to check. 66 * @return int|string|false The string or integer ID of the stored 67 * value if it has already been seen, or 68 * false if the value is not stored. 69 * @throws InvalidArgumentException Thrown if $value is not an array or 70 * object 71 */ 72 public function contains(&$value) 73 { 74 if (is_array($value)) { 75 return $this->containsArray($value); 76 } 77 78 else if (is_object($value)) { 79 return $this->containsObject($value); 80 } 81 82 throw new InvalidArgumentException( 83 'Only arrays and objects are supported' 84 ); 85 } 86 87 /** 88 * @param array $array 89 * @return bool|int 90 */ 91 private function addArray(array &$array) 92 { 93 $key = $this->containsArray($array); 94 95 if ($key !== false) { 96 return $key; 97 } 98 99 $this->arrays[] = &$array; 100 101 return count($this->arrays) - 1; 102 } 103 104 /** 105 * @param object $object 106 * @return string 107 */ 108 private function addObject($object) 109 { 110 if (!$this->objects->contains($object)) { 111 $this->objects->attach($object); 112 } 113 114 return spl_object_hash($object); 115 } 116 117 /** 118 * @param array $array 119 * @return int|false 120 */ 121 private function containsArray(array &$array) 122 { 123 $keys = array_keys($this->arrays, $array, true); 124 $hash = '_Key_' . hash('sha512', microtime(true)); 125 126 foreach ($keys as $key) { 127 $this->arrays[$key][$hash] = $hash; 128 129 if (isset($array[$hash]) && $array[$hash] === $hash) { 130 unset($this->arrays[$key][$hash]); 131 132 return $key; 133 } 134 135 unset($this->arrays[$key][$hash]); 136 } 137 138 return false; 139 } 140 141 /** 142 * @param object $value 143 * @return string|false 144 */ 145 private function containsObject($value) 146 { 147 if ($this->objects->contains($value)) { 148 return spl_object_hash($value); 149 } 150 151 return false; 152 } 153 }