property-accessor.php

Simple property accessor library
git clone git://git.finwo.net/lib/property-accessor.php
Log | Files | Refs

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 }