http://phing.info/

Source Code Coverage

Designed for use with PHPUnit2, Xdebug and Phing.

Methods: 15 LOC: 374 Statements: 145

Source file Statements Methods Total coverage
Type.php 86.9% 100.0% 88.1%
   
1
<?php
2
/**
3
 * Xyster Framework
4
 *
5
 * This source file is subject to the new BSD license that is bundled
6
 * with this package in the file LICENSE.txt.
7
 * It is also available through the world-wide-web at this URL:
8
 * http://www.opensource.org/licenses/bsd-license.php
9
 *
10
 * @category  Xyster
11
 * @package   Xyster_Type
12
 * @copyright Copyright (c) 2007-2008 Irrational Logic (http://irrationallogic.net)
13
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
14
 * @version   $Id: Type.php 173 2007-12-30 21:35:48Z doublecompile $
15
 */
16
/**
17
 * Type wrapper class
18
 *
19
 * @category  Xyster
20
 * @package   Xyster_Type
21
 * @copyright Copyright (c) 2007-2008 Irrational Logic (http://irrationallogic.net)
22
 * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
23
 */
24
class Xyster_Type
25
{
26
    private static $_nesting = 10;
27
28
    private static $_types = array('boolean', 'integer', 'double', 'string',
29
        'array');
30
31
    private static $_classes = array();
32
33
    protected $_type;
34
35
    /**
36
     * @var ReflectionClass
37
     */
38
    protected $_class;
39
40
    /**
41
     * Creates a new type representation
42
     *
43
     * @param string $type
44
     */
45
    public function __construct( $type )
46
    {
47 13
        if ( $type instanceof ReflectionClass ) {
48
            $type = $type->getName();
49
        }
50 13
        if ( !in_array($type, self::$_types) && !class_exists($type, false) && !interface_exists($type, false) ) {
51 1
            require_once 'Zend/Exception.php';
52 1
            throw new Zend_Exception('Invalid type: ' . $type);
53
        }
54 13
        $this->_type = $type;
55 13
        if ( class_exists($type, false) || interface_exists($type, false) ) {
56 13
            $this->_class = self::_getReflectionClass($type);
57 13
        }
58
    }
59
60
    /**
61
     * Tests another value for equality
62
     *
63
     * @param mixed $object
64
     * @return boolean
65
     */
66
    public function equals( $object )
67
    {
68 3
        return $object === $this ||
69 3
            ($object instanceof Xyster_Type && $object->_type == $this->_type);
70
    }
71
72
    /**
73
     * If this type is a class this returns the ReflectionClass object for it
74
     *
75
     * @return ReflectionClass
76
     */
77
    public function getClass()
78
    {
79 4
        return $this->_class;
80
    }
81
82
    /**
83
     * Gets the name of this type
84
     *
85
     * @return string
86
     */
87
    public function getName()
88
    {
89 2
        return $this->_type;
90
    }
91
92
    /**
93
     * Gets a hash code for this type
94
     *
95
     * @return int
96
     */
97
    public function hashCode()
98
    {
99 1
        return self::hash($this->_type);
100
    }
101
102
    /**
103
     * Determines if the supplied type is a parent of the current type
104
     *
105
     * The $type argument can be either a string, a ReflectionClass object or a
106
     * Xyster_Type object
107
     *
108
     * @param mixed $type
109
     */
110
    public function isAssignableFrom( $type )
111
    {
112 1
        $name = null;
113 1
        $class = null;
114 1
        if ( is_string($type) ) {
115 1
            $name = $type;
116 1
            $class = new ReflectionClass($type);
117 1
        } else if ( $type instanceof ReflectionClass ) {
118 1
            $name = $type->getName();
119 1
            $class = $type;
120 1
        } else if ( $type instanceof Xyster_Type ) {
121 1
            $name = $type->getName();
122 1
            $class = $type->getClass();
123 1
        }
124
        /* @var $class ReflectionClass */
125 1
        return $this->_type == $name ||
126 1
            ($class && $this->_class && $class->isSubclassOf($this->_class));
127
    }
128
129
    /**
130
     * Gets whether this type is an object type
131
     *
132
     * @return boolean
133
     */
134
    public function isObject()
135
    {
136 3
        return $this->_class instanceof ReflectionClass;
137
    }
138
139
    /**
140
     * Gets the string representation of this object
141
     *
142
     * @return string
143
     */
144
    public function __toString()
145
    {
146 1
        return (( $this->isObject() ) ? 'Class ' : '') . $this->_type;
147
    }
148
149
    /**
150
     * Checks for shallow equality
151
     *
152
     * If the object you supply for $arg1 has a method named 'equals', that
153
     * method will be called using $arg2 for the argument.
154
     *
155
     * @param mixed $arg1
156
     * @param mixed $arg2
157
     * @return boolean
158
     */
159
    public static function areEqual( $arg1, $arg2 )
160
    {
161 1
        if ( $arg1 === $arg2 ) {
162 1
            return true;
163 0
        }
164
165 1
        if ( is_numeric($arg1) XOR is_numeric($arg2) ) {
166 1
            return false;
167 0
        }
168 1
        if ( is_array($arg1) XOR is_array($arg2) ) {
169 1
            return false;
170 0
        }
171 1
        if ( is_object($arg1) XOR is_object($arg2) ) {
172 1
            return false;
173 0
        }
174 1
        if ( is_object($arg1) && is_object($arg2) &&
175 1
            get_class($arg1) !== get_class($arg2) ) {
176 1
            return false;
177 0
        }
178
179 1
        if ( is_scalar($arg1) || is_scalar($arg2) ) {
180 1
            return $arg1 == $arg2;
181 0
        }
182
183 1
        if ( is_object($arg1) && method_exists($arg1, 'equals') ) {
184 1
            return $arg1->equals($arg2);
185 0
        }
186
187 1
        return false;
188
    }
189
190
    /**
191
     * Compares two values for equality
192
     *
193
     * If two objects are not identical (that is, are exactly the same instance)
194
     * they will be compared property-by-property recursively if need be.  This
195
     * method will not nest any deeper than 10 levels to compare objects.
196
     *
197
     * Arrays will be compared in the same manner.
198
     *
199
     * If the object you supply for $arg1 has a method named 'equals', that
200
     * method will be called using $arg2 for the argument.
201
     *
202
     * @param mixed $arg1
203
     * @param mixed $arg2
204
     * @return boolean
205
     */
206
    public static function areDeeplyEqual( $arg1, $arg2 )
207
    {
208 1
        return self::_areEqual($arg1, $arg2);
209
    }
210
211
    /**
212
     * Gets the Java-style hash code of a value
213
     *
214
     * If you supply an object for the argument and it has a method named
215
     * 'hashCode', that method will be called.
216
     *
217
     * @param mixed $value
218
     * @return int
219
     */
220
    public static function hash( $value )
221
    {
222 31
        $hash = 0;
223
224 31
        if ( is_object($value) ) {
225 30
            $hash = self::_objHash($value);
226 31
        } else if ( is_string($value) ) {
227 2
            $hash = self::_strHash($value);
228 31
        } else if ( is_bool($value) ) {
229 1
            $hash = $value ? 1231 : 1237;
230 30
        } else if ( is_float($value) ) {
231 1
            $res = unpack('l', pack('f', $value));
232 1
            $hash = $res[1];
233 30
        } else if ( is_int($value) ) {
234 30
            $hash = $value;
235 30
        } else if ( is_array($value) ) {
236 30
            $max = (float)PHP_INT_MAX;
237 30
            $min = (float)(0 - PHP_INT_MAX);
238 30
            $h = 0.0;
239 30
            foreach( $value as $v ) {
240 30
                $result = 31 * $h + self::hash($v);
241 30
                if ( $result > $max ) {
242 30
                    $h = $result % $max;
243 30
                } else if ( $result < $min ) {
244 30
                    $h = 0-(abs($result) % $max);
245 30
                } else {
246 30
                    $h = $result;
247
                }
248 30
            }
249 30
            $hash = $h;
250 30
        }
251
252 31
        return $hash;
253
    }
254
255
    /**
256
     * Compares two values for equality
257
     *
258
     * @param mixed $arg1
259
     * @param mixed $arg2
260
     * @param int $depth  The current search depth
261
     * @return boolean
262
     */
263
    private static function _areEqual( $arg1, $arg2, $depth = 0 )
264
    {
265 1
        if ( $arg1 === $arg2 ) {
266 1
            return true;
267 0
        }
268
269 1
        if ( $depth >= self::$_nesting ) {
270 1
            return true;
271 0
        }
272
273 1
        if ( is_numeric($arg1) XOR is_numeric($arg2) ) {
274 1
            return false;
275 0
        }
276 1
        if ( is_array($arg1) XOR is_array($arg2) ) {
277 1
            return false;
278 0
        }
279 1
        if ( is_object($arg1) XOR is_object($arg2) ) {
280 1
            return false;
281 0
        }
282 1
        if ( is_object($arg1) && is_object($arg2) &&
283 1
            get_class($arg1) !== get_class($arg2) ) {
284 1
            return false;
285 0
        }
286
287 1
        if ( is_scalar($arg1) || is_scalar($arg2) ) {
288 1
            return $arg1 == $arg2;
289 0
        }
290
291 1
        if ( is_object($arg1) && method_exists($arg1, 'equals') ) {
292 1
            return $arg1->equals($arg2);
293 0
        }
294
295 1
        if ( is_object($arg1) ) {
296 1
            $arg1 = (array) $arg1;
297 1
            $arg2 = (array) $arg2;
298 1
        }
299
300 1
        foreach ( $arg1 as $key => $v ) {
301 1
            if (!array_key_exists($key, $arg2)) {
302 1
                return false;
303 0
            }
304
305 1
            if ( !self::_areEqual($arg1[$key], $arg2[$key], $depth + 1) ) {
306 1
                return false;
307 0
            }
308
309 1
            unset($arg2[$key]);
310 1
        }
311
312 1
        if ( count($arg2) ) {
313 1
            return false;
314 0
        }
315
316 1
        return true;
317
    }
318
319
    /**
320
     * Gets the ReflectionClass for a class name and stores it
321
     *
322
     * @param string $name
323
     * @return ReflectionClass
324
     */
325
    protected static function _getReflectionClass( $name )
326
    {
327 13
        if ( !isset(self::$_classes[$name]) ) {
328 2
            self::$_classes[$name] = new ReflectionClass($name);
329 2
        }
330 13
        return self::$_classes[$name];
331
    }
332
333
    /**
334
     * Gets the hash code for an object
335
     *
336
     * @param object $object
337
     * @return int
338
     */
339
    private static function _objHash( $object )
340
    {
341 30
        if ( method_exists($object, 'hashCode') ) {
342 1
            return (int)$object->hashCode();
343 0
        }
344
345 30
        $hex = str_split(spl_object_hash($object), 2);
346
347 30
        return self::hash(array_map('hexdec', $hex));
348
    }
349
350
    /**
351
     * Gets the hash code for a string
352
     *
353
     * @param string $string
354
     * @return int
355
     */
356
    private static function _strHash( $string )
357
    {
358 2
        $max = (float)PHP_INT_MAX;
359 2
        $min = (float)(0 - PHP_INT_MAX);
360 2
        $h = 0.0;
361
        // mmm... modular arithmetic...
362 2
        for( $i=0; $i<strlen($string); $i++ ) {
363 2
            $result = 31 * $h + ord($string[$i]);
364 2
            if ( $result > $max ) {
365 2
                $h = $result % $max;
366 2
            } else if ( $result < $min ) {
367 2
                $h = 0-(abs($result) % $max);
368 2
            } else {
369 2
                $h = $result;
370
            }
371 2
        }
372 2
        return (int)$h;
373
    }
374
}


Report generated at 2008-01-20T12:13:39-05:00