PageRenderTime 378ms CodeModel.GetById 141ms app.highlight 146ms RepoModel.GetById 76ms app.codeStats 1ms

/lib/reflection_php5.php

https://bitbucket.org/photocrati/simpletest-for-wordpress
PHP | 386 lines | 188 code | 26 blank | 172 comment | 32 complexity | 82b294fc03459ddb03fffe000100e76c MD5 | raw file
  1<?php
  2/**
  3 *  base include file for SimpleTest
  4 *  @package    SimpleTest
  5 *  @subpackage UnitTester
  6 *  @version    $Id: reflection_php5.php 2011 2011-04-29 08:22:48Z pp11 $
  7 */
  8
  9/**
 10 *    Version specific reflection API.
 11 *    @package SimpleTest
 12 *    @subpackage UnitTester
 13 */
 14class SimpleReflection {
 15    private $interface;
 16
 17    /**
 18     *    Stashes the class/interface.
 19     *    @param string $interface    Class or interface
 20     *                                to inspect.
 21     */
 22    function __construct($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    protected 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    protected 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    protected 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    protected 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    protected 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    protected 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    protected 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
298        // This wonky try-catch is a work around for a faulty method_exists()
299        // in early versions of PHP 5 which would return false for static
300        // methods. The Reflection classes work fine, but hasMethod()
301        // doesn't exist prior to PHP 5.1.0, so we need to use a more crude
302        // detection method.
303        try {
304            $interface = new ReflectionClass($this->interface);
305            $interface->getMethod($name);
306        } catch (ReflectionException $e) {
307            return "function $name()";
308        }
309        return $this->getFullSignature($name);
310    }
311
312    /**
313     *    For a signature specified in an interface, full
314     *    details must be replicated to be a valid implementation.
315     *    @param string $name    Method name.
316     *    @return string         Method signature up to last
317     *                           bracket.
318     *    @access private
319     */
320    protected function getFullSignature($name) {
321        $interface = new ReflectionClass($this->interface);
322        $method = $interface->getMethod($name);
323        $reference = $method->returnsReference() ? '&' : '';
324        $static = $method->isStatic() ? 'static ' : '';
325        return "{$static}function $reference$name(" .
326                implode(', ', $this->getParameterSignatures($method)) .
327                ")";
328    }
329
330    /**
331     *    Gets the source code for each parameter.
332     *    @param ReflectionMethod $method   Method object from
333     *                                      reflection API
334     *    @return array                     List of strings, each
335     *                                      a snippet of code.
336     *    @access private
337     */
338    protected function getParameterSignatures($method) {
339        $signatures = array();
340        foreach ($method->getParameters() as $parameter) {
341            $signature = '';
342            $type = $parameter->getClass();
343            if (is_null($type) && version_compare(phpversion(), '5.1.0', '>=') && $parameter->isArray()) {
344                $signature .= 'array ';
345            } elseif (!is_null($type)) {
346                $signature .= $type->getName() . ' ';
347            }
348            if ($parameter->isPassedByReference()) {
349                $signature .= '&';
350            }
351            $signature .= '$' . $this->suppressSpurious($parameter->getName());
352            if ($this->isOptional($parameter)) {
353                $signature .= ' = null';
354            }
355            $signatures[] = $signature;
356        }
357        return $signatures;
358    }
359
360    /**
361     *    The SPL library has problems with the
362     *    Reflection library. In particular, you can
363     *    get extra characters in parameter names :(.
364     *    @param string $name    Parameter name.
365     *    @return string         Cleaner name.
366     *    @access private
367     */
368    protected function suppressSpurious($name) {
369        return str_replace(array('[', ']', ' '), '', $name);
370    }
371
372    /**
373     *    Test of a reflection parameter being optional
374     *    that works with early versions of PHP5.
375     *    @param reflectionParameter $parameter    Is this optional.
376     *    @return boolean                          True if optional.
377     *    @access private
378     */
379    protected function isOptional($parameter) {
380        if (method_exists($parameter, 'isOptional')) {
381            return $parameter->isOptional();
382        }
383        return false;
384    }
385}
386?>