Code

< 40 %
40-60 %
> 60 %
1
<?php
2
/**
3
 * Fwk
4
 *
5
 * Copyright (c) 2011-2012, Julien Ballestracci <[email protected]>.
6
 * All rights reserved.
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
12
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
13
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
15
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
16
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
17
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
18
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
19
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
20
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
21
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22
 * POSSIBILITY OF SUCH DAMAGE.
23
 *
24
 * PHP Version 5.3
25
 *
26
 * @category  Core
27
 * @package   Fwk\Core
28
 * @author    Julien Ballestracci <[email protected]>
29
 * @copyright 2011-2012 Julien Ballestracci <[email protected]>
30
 * @license   http://www.opensource.org/licenses/bsd-license.php  BSD License
31
 * @link      http://www.fwk.pw
32
 */
33
namespace Fwk\Core;
34
35
/**
36
 * This class is a simple accessor for values of objects
37
 *
38
 * If getters/setters are found, we use them first.
39
 * If not, we try to directly get/set the value (\ArrayAccess or \stdClass)
40
 *
41
 * @category Utilities
42
 * @package  Fwk\Core
43
 * @author   Julien Ballestracci <[email protected]>
44
 * @license  http://www.opensource.org/licenses/bsd-license.php  BSD License
45
 * @link     http://www.phpfwk.com
46
 */
47
class Accessor
48
{
49
    /**
50
     * The object to access
51
     *
52
     * @var mixed
53
     */
54
    protected $object;
55
56
    /**
57
     * Reflector for the object
58
     *
59
     * @var \ReflectionObject
60
     */
61
    protected $reflector;
62
63
    /**
64
     * Override properties visibility ?
65
     *
66
     * @var boolean
67
     */
68
    protected $force = false;
69
70
    /**
71
     * Constructor
72
     *
73
     * @param mixed $object The object we want to access
74
     *
75
     * @throws \InvalidArgumentException if $object is not an object
76
     * @return void
77
     */
78 17
    public function __construct($object)
79
    {
80 17
        if (!is_object($object)) {
81 1
            throw new \InvalidArgumentException("Argument is not an object");
82
        }
83
84 17
        $this->object   = $object;
85 17
    }
86
87
    /**
88
     * Try to retrieve a value from the object
89
     *
90
     * @param string $key Propertie's name
91
     *
92
     * @return mixed Actual value if reached or false
93
     */
94 10
    public function get($key)
95
    {
96 10
        $obj        = $this->object;
97 10
        $getter     = "get". ucfirst($key);
98
99 10
        if (\method_exists($obj, $getter) && \is_callable(array($obj, $getter))) {
100 7
            return $obj->{$getter}();
101 10
        } elseif ($obj instanceof \stdClass && isset($obj->{$key})) {
102 1
            return  $obj->{$key};
103 10
        } elseif ($obj instanceof \ArrayAccess && $obj->offsetExists($key)) {
104 1
            return  $obj->offsetGet($key);
105
        } else {
106 9
            $this->getReflector();
107
            try {
108 9
                $props   = $this->getClassProperties($this->reflector->getName());
109 9
                if (!isset($props[$key])) {
110 1
                    return false;
111
                }
112 8
                $prop    = $props[$key];
113 8
                if (($prop->isPrivate() || $prop->isProtected()) && $this->force) {
114 3
                    $prop->setAccessible(true);
115 3
                }
116
117 8
                return $prop->getValue($obj);
118 2
            } catch (\ReflectionException $e) {
119
            }
120
        }
121
122 2
        return false;
123
    }
124
125
    /**
126
     * Try to set a value
127
     *
128
     * @param string $key   Propertie's name
129
     * @param mixed  $value Desired value
130
     *
131
     * @return boolean true if successful
132
     */
133 7
    public function set($key, $value)
134
    {
135 7
        $obj        = $this->object;
136 7
        $setter     = "set". ucfirst($key);
137
138 7
        if (\method_exists($obj, $setter) && \is_callable(array($obj, $setter))) {
139 3
            $obj->{$setter}($value);
140 3
            return true;
141
        }
142
143 7
        if ($obj instanceof \stdClass) {
144 1
            $obj->{$key}    = $value;
145 1
            return true;
146
        }
147
148 6
        if ($obj instanceof \ArrayAccess) {
149 1
            $obj->offsetSet($key, $value);
150 1
            return true;
151
        }
152
153 5
        $reflector  = $this->getReflector();
154
        try {
155 5
            $prop   = $reflector->getProperty($key);
156
157 5
            if (($prop->isPrivate() || $prop->isProtected()) && $this->force) {
158 1
                $prop->setAccessible(true);
159 1
            }
160
161 5
            if ($prop->isPublic() || $this->force === true) {
162 5
                $prop->setValue($obj, $value);
163
164 5
                return true;
165
            }
166 1
        } catch (\ReflectionException $e) {
167
        }
168
169 1
        return false;
170
    }
171
172
    /**
173
     * Set multiple values at once
174
     *
175
     * @param array $values Array of keys->values to be set
176
     *
177
     * @return void
178
     */
179 2
    public function setValues(array $values)
180
    {
181 2
        foreach ($values as $key => $value) {
182 2
            $this->set($key, $value);
183 2
        }
184 2
    }
185
186
    /**
187
     * Gets a reflector for the object
188
     *
189
     * @return \ReflectionObject
190
     */
191 14
    public function getReflector()
192
    {
193 14
        if (!isset($this->reflector)) {
194 14
            $this->reflector = new \ReflectionObject($this->object);
195 14
        }
196
197 14
        return $this->reflector;
198
    }
199
200
    /**
201
     * Make an array of keys->values (eventually filtered by $modifier) from
202
     * object's properties.
203
     *
204
     * @param mixed $modifier Filtering callable
205
     *
206
     * @return array The resulting array
207
     */
208 8
    public function toArray($modifier = null)
209
    {
210 8
        $this->getReflector();
211 8
        $final      = array();
212 8
        $props      = $this->getClassProperties($this->reflector->getName());
213
214 8
        foreach ($props as $property) {
215 6
            $value = $this->get($property->getName());
216
217 6
            if (\is_callable($modifier)) {
218 1
                $value  = \call_user_func_array($modifier, array($value));
219 1
            }
220
221 6
            $final[$property->getName()] = $value;
222 8
        }
223
224 8
        return $final;
225
    }
226
    
227
    /**
228
     * Recursive function to get an associative array of class properties by 
229
     * property name => ReflectionProperty() object including inherited ones 
230
     * from extended classes.
231
     * 
232
     * @param string  $className Class name
233
     * @param integer $filter    ReflectionProperty filter
234
     * 
235
     * @author muratyaman at gmail dot com
236
     * @return array
237
     */
238 11
    protected function getClassProperties($className, $filter = null)
239
    {
240 11
        $ref            = new \ReflectionClass($className);
241 11
        if (null === $filter) {
242
            $filter = \ReflectionProperty::IS_PUBLIC 
243 11
                    | \ReflectionProperty::IS_PRIVATE 
244 11
                    | \ReflectionProperty::IS_PROTECTED 
245 11
                    | \ReflectionProperty::IS_STATIC;
246 11
        }
247
        
248 11
        $props          = $ref->getProperties($filter);
249 11
        $props_arr      = array();
250 11
        $parentClass    = $ref->getParentClass();
251
        
252 11
        foreach ($props as $prop) {
253 8
            $f              = $prop->getName();
254 8
            $props_arr[$f]  = $prop;
255 11
        }
256
        
257 11
        if ($parentClass) {
258 5
            $props_arr = array_merge(
259 5
                $this->getClassProperties($parentClass->getName(), $filter), 
260
                $props_arr
261 5
            );
262 5
        }
263
        
264 11
        return $props_arr;
265
    } 
266
267
    /**
268
     * Returns class attributes
269
     *
270
     * @return array The resulting array
271
     */
272 7
    public function getAttributes()
273
    {
274 7
        $reflector  = $this->getReflector();
275 7
        $final      = array();
276
277 7
        foreach ($reflector->getProperties() as $property) {
278 5
            $final[] = $property->getName();
279 7
        }
280
281 7
        return $final;
282
    }
283
284
    /**
285
     * Produces a unique hash code based on values
286
     *
287
     * @param string $algo Desired algorythm
288
     *
289
     * @return string
290
     */
291 1
    public function hashCode($algo  = 'md5')
292
    {
293 1
        $old    = $this->force;
294 1
        $this->overrideVisibility(true);
295 1
        $array  = $this->toArray(array($this, 'everythingAsArrayModifier'));
296 1
        \ksort($array);
297 1
        $str    = \get_class($this->object);
298
299 1
        foreach ($array as $value) {
300 1
            $str .= (is_array($value) ? json_encode($value) : $value);
301 1
        }
302
303 1
        $this->overrideVisibility($old);
304
305 1
        return \hash($algo, $str);
306
    }
307
308
    /**
309
     * Static toArray() modifier to handle objects and relations.
310
     * {@see Accessor::toArray()}
311
     *
312
     * @param mixed $value Actual value
313
     *
314
     * @return mixed Filtered value
315
     */
316 1
    public function everythingAsArrayModifier($value)
317
    {
318 1
        if (is_object($value)) {
319 1
            $accessor   = self::factory($value);
320 1
            $value      = $accessor->toArray(array($this, __METHOD__));
321 1
        }
322
323 1
        if (is_array($value)) {
324 1
            foreach ($value as $key => $val) {
325 1
                $value[$key] = $val;
326 1
            }
327 1
        }
328
329 1
        return $value;
330
    }
331
332
    /**
333
     * Factory utility
334
     *
335
     * @param mixed $object The object we want to access
336
     *
337
     * @return Accessor
338
     */
339 2
    public static function factory($object)
340
    {
341 2
        return new static($object);
342
    }
343
344
    /**
345
     * Should we force properties visibility ?
346
     *
347
     * @param boolean $bool yes or no
348
     *
349
     * @return void
350
     */
351 4
    public function overrideVisibility($bool)
352
    {
353 4
        $this->force = (bool) $bool;
354
    }
355
}