source: trunk/server/www/vendors/simpletest/reflection_php5.php @ 6

Last change on this file since 6 was 6, checked in by sander, 11 years ago

Added SimpleTest? test framework

File size: 12.2 KB
Line 
1<?php
2/**
3 *  base include file for SimpleTest
4 *  @package    SimpleTest
5 *  @subpackage UnitTester
6 *  @version    $Id: reflection_php5.php 1683 2008-03-05 21:57:08Z lastcraft $
7 */
8
9/**
10 *    Version specific reflection API.
11 *    @package SimpleTest
12 *    @subpackage UnitTester
13 */
14class SimpleReflection {
15    var $_interface;
16
17    /**
18     *    Stashes the class/interface.
19     *    @param string $interface    Class or interface
20     *                                to inspect.
21     */
22    function SimpleReflection($interface) {
23        $this->_interface = $interface;
24    }
25
26    /**
27     *    Checks that a class has been declared. Versions
28     *    before PHP5.0.2 need a check that it's not really
29     *    an interface.
30     *    @return boolean            True if defined.
31     *    @access public
32     */
33    function classExists() {
34        if (! class_exists($this->_interface)) {
35            return false;
36        }
37        $reflection = new ReflectionClass($this->_interface);
38        return ! $reflection->isInterface();
39    }
40
41    /**
42     *    Needed to kill the autoload feature in PHP5
43     *    for classes created dynamically.
44     *    @return boolean        True if defined.
45     *    @access public
46     */
47    function classExistsSansAutoload() {
48        return class_exists($this->_interface, false);
49    }
50
51    /**
52     *    Checks that a class or interface has been
53     *    declared.
54     *    @return boolean            True if defined.
55     *    @access public
56     */
57    function classOrInterfaceExists() {
58        return $this->_classOrInterfaceExistsWithAutoload($this->_interface, true);
59    }
60
61    /**
62     *    Needed to kill the autoload feature in PHP5
63     *    for classes created dynamically.
64     *    @return boolean        True if defined.
65     *    @access public
66     */
67    function classOrInterfaceExistsSansAutoload() {
68        return $this->_classOrInterfaceExistsWithAutoload($this->_interface, false);
69    }
70
71    /**
72     *    Needed to select the autoload feature in PHP5
73     *    for classes created dynamically.
74     *    @param string $interface       Class or interface name.
75     *    @param boolean $autoload       True totriggerautoload.
76     *    @return boolean                True if interface defined.
77     *    @access private
78     */
79    function _classOrInterfaceExistsWithAutoload($interface, $autoload) {
80        if (function_exists('interface_exists')) {
81            if (interface_exists($this->_interface, $autoload)) {
82                return true;
83            }
84        }
85        return class_exists($this->_interface, $autoload);
86    }
87
88    /**
89     *    Gets the list of methods on a class or
90     *    interface.
91     *    @returns array              List of method names.
92     *    @access public
93     */
94    function getMethods() {
95        return array_unique(get_class_methods($this->_interface));
96    }
97
98    /**
99     *    Gets the list of interfaces from a class. If the
100     *    class name is actually an interface then just that
101     *    interface is returned.
102     *    @returns array          List of interfaces.
103     *    @access public
104     */
105    function getInterfaces() {
106        $reflection = new ReflectionClass($this->_interface);
107        if ($reflection->isInterface()) {
108            return array($this->_interface);
109        }
110        return $this->_onlyParents($reflection->getInterfaces());
111    }
112
113    /**
114     *    Gets the list of methods for the implemented
115     *    interfaces only.
116     *    @returns array      List of enforced method signatures.
117     *    @access public
118     */
119    function getInterfaceMethods() {
120        $methods = array();
121        foreach ($this->getInterfaces() as $interface) {
122            $methods = array_merge($methods, get_class_methods($interface));
123        }
124        return array_unique($methods);
125    }
126
127    /**
128     *    Checks to see if the method signature has to be tightly
129     *    specified.
130     *    @param string $method        Method name.
131     *    @returns boolean             True if enforced.
132     *    @access private
133     */
134    function _isInterfaceMethod($method) {
135        return in_array($method, $this->getInterfaceMethods());
136    }
137
138    /**
139     *    Finds the parent class name.
140     *    @returns string      Parent class name.
141     *    @access public
142     */
143    function getParent() {
144        $reflection = new ReflectionClass($this->_interface);
145        $parent = $reflection->getParentClass();
146        if ($parent) {
147            return $parent->getName();
148        }
149        return false;
150    }
151
152    /**
153     *    Trivially determines if the class is abstract.
154     *    @returns boolean      True if abstract.
155     *    @access public
156     */
157    function isAbstract() {
158        $reflection = new ReflectionClass($this->_interface);
159        return $reflection->isAbstract();
160    }
161
162    /**
163     *    Trivially determines if the class is an interface.
164     *    @returns boolean      True if interface.
165     *    @access public
166     */
167    function isInterface() {
168        $reflection = new ReflectionClass($this->_interface);
169        return $reflection->isInterface();
170    }
171
172    /**
173     *    Scans for final methods, as they screw up inherited
174     *    mocks by not allowing you to override them.
175     *    @returns boolean   True if the class has a final method.
176     *    @access public
177     */
178    function hasFinal() {
179        $reflection = new ReflectionClass($this->_interface);
180        foreach ($reflection->getMethods() as $method) {
181            if ($method->isFinal()) {
182                return true;
183            }
184        }
185        return false;
186    }
187
188    /**
189     *    Whittles a list of interfaces down to only the
190     *    necessary top level parents.
191     *    @param array $interfaces     Reflection API interfaces
192     *                                 to reduce.
193     *    @returns array               List of parent interface names.
194     *    @access private
195     */
196    function _onlyParents($interfaces) {
197        $parents = array();
198        $blacklist = array();
199        foreach ($interfaces as $interface) {
200            foreach($interfaces as $possible_parent) {
201                if ($interface->getName() == $possible_parent->getName()) {
202                    continue;
203                }
204                if ($interface->isSubClassOf($possible_parent)) {
205                    $blacklist[$possible_parent->getName()] = true;
206                }
207            }
208            if (!isset($blacklist[$interface->getName()])) {
209                $parents[] = $interface->getName();
210            }
211        }
212        return $parents;
213    }
214
215    /**
216     * Checks whether a method is abstract or not.
217     * @param   string   $name  Method name.
218     * @return  bool            true if method is abstract, else false
219     * @access  private
220     */
221    function _isAbstractMethod($name) {
222        $interface = new ReflectionClass($this->_interface);
223        if (! $interface->hasMethod($name)) {
224            return false;
225        }
226        return $interface->getMethod($name)->isAbstract();
227    }
228
229    /**
230     * Checks whether a method is the constructor.
231     * @param   string   $name  Method name.
232     * @return  bool            true if method is the constructor
233     * @access  private
234     */
235    function _isConstructor($name) {
236        return ($name == '__construct') || ($name == $this->_interface);
237    }
238
239    /**
240     * Checks whether a method is abstract in all parents or not.
241     * @param   string   $name  Method name.
242     * @return  bool            true if method is abstract in parent, else false
243     * @access  private
244     */
245    function _isAbstractMethodInParents($name) {
246        $interface = new ReflectionClass($this->_interface);
247        $parent = $interface->getParentClass();
248        while($parent) {
249            if (! $parent->hasMethod($name)) {
250                return false;
251            }
252            if ($parent->getMethod($name)->isAbstract()) {
253                return true;
254            }
255            $parent = $parent->getParentClass();
256        }
257        return false;
258    }
259
260    /**
261     * Checks whether a method is static or not.
262     * @param   string  $name   Method name
263     * @return  bool            true if method is static, else false
264     * @access  private
265     */
266    function _isStaticMethod($name) {
267        $interface = new ReflectionClass($this->_interface);
268        if (! $interface->hasMethod($name)) {
269            return false;
270        }
271        return $interface->getMethod($name)->isStatic();
272    }
273
274    /**
275     *    Writes the source code matching the declaration
276     *    of a method.
277     *    @param string $name    Method name.
278     *    @return string         Method signature up to last
279     *                           bracket.
280     *    @access public
281     */
282    function getSignature($name) {
283        if ($name == '__set') {
284            return 'function __set($key, $value)';
285        }
286        if ($name == '__call') {
287            return 'function __call($method, $arguments)';
288        }
289        if (version_compare(phpversion(), '5.1.0', '>=')) {
290            if (in_array($name, array('__get', '__isset', $name == '__unset'))) {
291                return "function {$name}(\$key)";
292            }
293        }
294        if ($name == '__toString') {
295            return "function $name()";
296        }
297        if ($this->_isInterfaceMethod($name) ||
298                $this->_isAbstractMethod($name) ||
299                $this->_isAbstractMethodInParents($name) ||
300                $this->_isStaticMethod($name)) {
301            return $this->_getFullSignature($name);
302        }
303        return "function $name()";
304    }
305
306    /**
307     *    For a signature specified in an interface, full
308     *    details must be replicated to be a valid implementation.
309     *    @param string $name    Method name.
310     *    @return string         Method signature up to last
311     *                           bracket.
312     *    @access private
313     */
314    function _getFullSignature($name) {
315        $interface = new ReflectionClass($this->_interface);
316        $method = $interface->getMethod($name);
317        $reference = $method->returnsReference() ? '&' : '';
318        $static = $method->isStatic() ? 'static ' : '';
319        return "{$static}function $reference$name(" .
320                implode(', ', $this->_getParameterSignatures($method)) .
321                ")";
322    }
323
324    /**
325     *    Gets the source code for each parameter.
326     *    @param ReflectionMethod $method   Method object from
327     *                                      reflection API
328     *    @return array                     List of strings, each
329     *                                      a snippet of code.
330     *    @access private
331     */
332    function _getParameterSignatures($method) {
333        $signatures = array();
334        foreach ($method->getParameters() as $parameter) {
335            $signature = '';
336            $type = $parameter->getClass();
337            if (is_null($type) && version_compare(phpversion(), '5.1.0', '>=') && $parameter->isArray()) {
338                $signature .= 'array ';
339            } elseif (!is_null($type)) {
340                $signature .= $type->getName() . ' ';
341            }
342            if ($parameter->isPassedByReference()) {
343                $signature .= '&';
344            }
345            $signature .= '$' . $this->_suppressSpurious($parameter->getName());
346            if ($this->_isOptional($parameter)) {
347                $signature .= ' = null';
348            }
349            $signatures[] = $signature;
350        }
351        return $signatures;
352    }
353
354    /**
355     *    The SPL library has problems with the
356     *    Reflection library. In particular, you can
357     *    get extra characters in parameter names :(.
358     *    @param string $name    Parameter name.
359     *    @return string         Cleaner name.
360     *    @access private
361     */
362    function _suppressSpurious($name) {
363        return str_replace(array('[', ']', ' '), '', $name);
364    }
365
366    /**
367     *    Test of a reflection parameter being optional
368     *    that works with early versions of PHP5.
369     *    @param reflectionParameter $parameter    Is this optional.
370     *    @return boolean                          True if optional.
371     *    @access private
372     */
373    function _isOptional($parameter) {
374        if (method_exists($parameter, 'isOptional')) {
375            return $parameter->isOptional();
376        }
377        return false;
378    }
379}
380?>
Note: See TracBrowser for help on using the repository browser.