PropertyAccessor.php (8943B)
1 <?php 2 3 namespace Finwo\PropertyAccessor; 4 5 class PropertyAccessor 6 { 7 protected $debug = false; 8 9 public function __construct($debug = false) 10 { 11 $this->debug = $debug; 12 } 13 14 /** 15 * @param $subject 16 * @param string $path 17 * @param string $pathSplit 18 * @param null $default 19 * @param array $options 20 * 21 * @return array|mixed|null 22 * @throws \Exception 23 */ 24 public function getSafe(&$subject, $path = '', $pathSplit = '|', &$default = null, $options = array()) 25 { 26 // Fetch 27 $result = $this->get($subject, $path, $pathSplit); 28 29 // Filter down to options 30 if (!is_null($options) && count($options) && !in_array($result, $options)) { 31 $result = &$default; 32 } 33 34 // Make sure there is a value 35 if (is_null($result)) { 36 $result = &$default; 37 } 38 39 // And return offcourse 40 return $result; 41 } 42 43 /** 44 * @param $subject 45 * @param string $path 46 * @param string $pathSplit 47 * 48 * @return array|mixed|null 49 * @throws \Exception 50 */ 51 public function get(&$subject, $path = '', $pathSplit = '|') 52 { 53 // try array for legacy mapper 54 if (is_array($subject)) { 55 $path = explode($pathSplit, $path); 56 return $this->getArrayProperty($subject, $path, $pathSplit); 57 } 58 59 // throw error if needed 60 if (!is_object($subject)) { 61 throw new \Exception(sprintf( 62 'Subject must be an array or object, %s given', 63 gettype($subject) 64 )); 65 } 66 67 // handle in-depth request 68 if (strpos($path, $pathSplit)) { 69 $target = $subject; 70 $path = explode($pathSplit, $path); 71 foreach($path as $part) { 72 $target = &$this->get($target, $part, $pathSplit); 73 } 74 return $target; 75 } 76 77 $camelized = $this->camelize($path); 78 79 // try the default getter 80 if (method_exists($subject, $method = sprintf("get%s", $camelized))) { 81 return call_user_func(array( 82 $subject, 83 $method 84 )); 85 } 86 87 // try less-common getter 88 if (method_exists($subject, $method = lcfirst($camelized))) { 89 return call_user_func(array( 90 $subject, 91 $method 92 )); 93 } 94 95 // try magic getter 96 if (method_exists($subject, 'get')) { 97 return $subject->get($path); 98 } 99 100 // try magic hidden getter 101 if (method_exists($subject, '__get')) { 102 return $subject->__get($path); 103 } 104 105 // try getting directly 106 $rc = new \ReflectionObject($subject); 107 foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { 108 if ($property->getName() == $path) { 109 return $subject->{$path}; 110 } 111 } 112 113 // all methods failed, throw exception 114 if ($this->getDebug()) { 115 return null; 116 } 117 throw new \Exception(sprintf( 118 'The property "%s" from object of class %s was inaccessible.', 119 $path, 120 get_class($subject) 121 )); 122 } 123 124 /** 125 * @param $subject 126 * @param string $path 127 * @param $value 128 * @param string $pathSplit 129 * 130 * @return PropertyAccessor 131 * @throws \Exception 132 */ 133 public function set(&$subject, $path = '', &$value, $pathSplit = '|') 134 { 135 // try array for legacy mapper 136 if (is_array($subject)) { 137 $path = explode($pathSplit, $path); 138 return $this->setArrayProperty($subject, $path, $value); 139 } 140 141 // throw error if needed 142 if (!is_object($subject)) { 143 throw new \Exception(sprintf( 144 'Subject must be an array or object, %s given', 145 gettype($subject) 146 )); 147 } 148 149 // handle in-depth request 150 if (strpos($path, $pathSplit)) { 151 $target = &$subject; 152 $path = explode($pathSplit, $path); 153 $last = array_pop($path); 154 foreach($path as $part) { 155 $target = &$this->get($target, $part, $pathSplit); 156 } 157 return $this->set($target, $last, $value, $pathSplit); 158 } 159 160 $camelized = $this->camelize($path); 161 162 // try the default setter 163 if (method_exists($subject, $method = sprintf("set%s", $camelized))) { 164 call_user_func(array( 165 $subject, 166 $method 167 ), 168 $value 169 ); 170 return $this; 171 } 172 173 // try less-common setter 174 if (method_exists($subject, $method = lcfirst($camelized))) { 175 call_user_func(array( 176 $subject, 177 $method 178 ), 179 $value 180 ); 181 return $this; 182 } 183 184 // try magic getter 185 if (method_exists($subject, 'set')) { 186 $subject->set($path, $value); 187 return $this; 188 } 189 190 // try magic hidden setter 191 if (method_exists($subject, '__set')) { 192 $subject->__set($path, $value); 193 return $this; 194 } 195 196 // try getting directly 197 $rc = new \ReflectionObject($subject); 198 foreach ($rc->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { 199 if ($property->getName() == $path) { 200 $subject->{$path} = &$value; 201 return $this; 202 } 203 } 204 205 // all methods failed, throw exception 206 if ($this->getDebug()) { 207 return $this; 208 } 209 210 throw new \Exception(sprintf( 211 'The property "%s" from object of class %s was inaccessible.', 212 $path, 213 get_class($subject) 214 )); 215 } 216 217 protected function getArrayProperty($input = array(), $path = array(), $splitChar = '|') 218 { 219 $target = &$input; 220 foreach($path as $key) { 221 if(isset($target[$key])) { 222 $target = &$target[$key]; 223 } else { 224 if ($this->debug) return null; 225 throw new \Exception(sprintf( 226 'The property "%s" from array was inaccessible.', 227 implode($splitChar, $path) 228 )); 229 } 230 } 231 return $target; 232 } 233 234 public function remove(&$input, $path, $splitChar = '|') 235 { 236 // Do nothing if we can't use the property 237 if (!is_array($input) && !is_object($input)) { 238 return; 239 } 240 241 // Convert the path to something useful 242 if (!is_array($path)) { 243 244 // Do nothing if we can't use the path 245 if (!is_string($path)) { 246 return; 247 } 248 249 // Split by the split character 250 $path = explode($splitChar, $path); 251 } 252 253 // Fetch the last key 254 $lastKey = array_pop($path); 255 256 // Fetch the parent 257 if (count($path)) { 258 $target = &$this->get($input, implode('|', $path), '|'); 259 } else { 260 $target = &$input; 261 } 262 263 // Handle if the target is an array 264 if (is_array($target)) { 265 if (!isset($target[$lastKey])) { 266 return; 267 } 268 unset($target[$lastKey]); 269 return; 270 } 271 272 // Handle if the target is an object 273 if (is_object($target)) { 274 if (!isset($target->{$lastKey})) { 275 return; 276 } 277 unset($target->{$lastKey}); 278 return; 279 } 280 281 // We're not compatible with this type 282 return; 283 } 284 285 protected function setArrayProperty(&$input = array(), $path = array(), &$value) 286 { 287 $target = &$input; 288 foreach($path as $key) { 289 if(!isset($target[$key])) $target[$key] = array(); 290 $target = &$target[$key]; 291 } 292 $target = $value; 293 294 return $this; 295 } 296 297 /** 298 * Simple camelize function 299 * 300 * @param string $scored 301 * @return mixed 302 */ 303 protected function camelize( $scored = '' ) { 304 $output = str_replace(array('-', '_'), " ", $scored ); 305 return str_replace(' ','',ucwords($output)); 306 } 307 308 public function mergeArrays(&$original, $new) 309 { 310 $source = &$original; 311 312 foreach($new as $key => $value) { 313 if(is_array($value)) { 314 // nest deeper 315 if(!isset($source[$key])) { 316 $source[$key] = array(); 317 } 318 $this->mergeArrays($source[$key], $value); 319 } else { 320 // overwrite 321 $source[$key] = $value; 322 } 323 } 324 } 325 326 protected function getDebug() 327 { 328 return $this->debug; 329 } 330 }