.gitignore000064400000000116144760110310006527 0ustar00/build/api/ /build/code-browser/ /build/coverage/ /build/logs/ /build/pdepend/.travis.yml000064400000000277144760110310006660 0ustar00language: php php: - 5.3 - 5.4 env: - TRAVISCI=1 branches: only: - develop - master notifications: email: - ci@andrewsville.cz script: phpunit tests/TokenReflectionLICENSE000064400000002751144760110310005553 0ustar00PHP Token Reflection Copyright (c) 2011-2012, Ondřej Nešpor, Jaroslav Hanslík. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of authors and contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. README.md000064400000021551144760110310006024 0ustar00# PHP Token Reflection # [![Build Status](https://secure.travis-ci.org/Andrewsville/PHP-Token-Reflection.png?branch=develop)](http://travis-ci.org/Andrewsville/PHP-Token-Reflection) In short, this library emulates the PHP reflection model using the tokenized PHP source. ## Brief history Everything started with [ApiGen](http://apigen.org). It is a pretty cool tool for generating documentation. It uses docblocks and... yes, reflection. It makes perfect sense, because reflection is - besides other things - a great tool for generating documentation, however it has its limitations by design. The biggest one is that you have to include/require the described source. It means that: * the described source affects the generator's environment, * it is very memory-consuming, * you have to include sources of all libraries (you cannot generate documentation of a Zend Framework based application without having at least a big part of the ZF loaded as well). One day we thought to ourselves: "Would it be possible to emulate reflection using just the tokenized source? Well, why not." And that was the beginning of our library. ## Some technical info The basic concept is, that any reflection is possible to process the particular part of the token array describing the reflected element. It is also able to find out if there are any child elements (a class reflection is able to find method definitions in the source, for example), create their reflections and pass the appropriate part of the token array to them. This concept allows us to keep the parser code relatively simple and easily maintainable. And we are able to to create all reflections in a single pass. That is absolutely crucial for the performance of the library. All reflection instances are being kept in a [TokenReflection\\Broker](https://github.com/Andrewsville/PHP-Token-Reflection/blob/master/library/TokenReflection/Broker.php) instance and all reflections know the broker that created them. This is very important, because a class reflection, for example, holds all its constants, methods and properties reflections instantiated inside, however it knows absolutely nothing about its parent class or the interfaces it implements. It knows just their fully qualified names. So when you call ```$reflectionClass->getParentClass();```, the class reflection asks the Broker for a reflection of a class by its name and returns it. An interesting thing happens when there is a parent class defined but it was not processed (in other words, you ask the Broker for a class that it does not know). It still returns a reflection! Yes, we do have reflections for classes that do not exist! COOL! There are reflections for file (\*), file-namespace (\*), namespace, class, function/method, constant, property and parameter. You will not normally get in touch with those marked with an asterisk but they are used internally. **ReflectionFile** is the topmost structure in our reflection tree. It gets the whole tokenized source and tries to find namespaces there. If it does, it creates ReflectionFileNamespace instances and passes them the appropriate part of the tokens array. If not, it creates a single pseudo-namespace (called no-namespace) a passes the whole tokenized source to it. **ReflectionFileNamespace** gets the namespace definition from the file, finds out its name, other aliased namespaces and tries to find any defined constants, functions and classes. If it finds any, it creates their reflections and passes them the appropriate parts of the tokens array. **ReflectionNamespace** is a similar (in name) yet quite different (in meaning) structure. It is a unique structure for every namespace and it holds all constants, functions and classes from this particular namespace inside. In fact, it is a simple container. It also is not created directly by any parent reflection, but the Broker creates it. Why do we need two separate classes? Because namespaces can be split into many files and in each file it can have individual namespace aliases. And those have to be taken into consideration when resolving parent class/interface names. It means that a ReflectionFileNamespace is created for every namespace in every file and it parses its contents, resolves fully qualified names of all classes, their parents and interfaces. Later, the Broker takes all ReflectionFileNamespace instances of the same namespace and merges them into a single ReflectionNameaspace instance. **ReflectionClass**, **ReflectionFunction**, **ReflectionMethod**, **ReflectionParameter** and **ReflectionProperty** work the same way like their internal reflection namesakes. **ReflectionConstants** is our addition to the reflection model. There is not much it can do - it can return its name, value (we will speak about values later) and how it was defined. (Almost) all reflection classes share a common base class, that defines some common functionality and interface. This means that our reflection model is much more unified than the internal one. There are reflections for the tokenized source (those mentioned above), but also descendants of the internal reflection that implement our additional features (they both use the same interface). They represent the PHP's internal classes, functions, ... So when you ask the Broker for an internal class, it returns a [TokenReflection\\Php\\ReflectionClass](https://github.com/Andrewsville/PHP-Token-Reflection/blob/master/library/TokenReflection/Php/ReflectionClass.php) instance that encapsulates the internal reflection functionality and adds our features. And there is also the [TokenReflection\\Php\\ReflectionConstant](https://github.com/Andrewsville/PHP-Token-Reflection/blob/master/library/TokenReflection/Php/ReflectionConstant.php) class that has no parent in the internal reflection model. ## Remarks From the beginning we tried to be as compatible as possible with the internal reflection (including things like returning the interface list in the same - pretty weird - order). However there are situations where it is just impossible. We are limited in the way we can handle constant values and property and parameter default values. When defined as a constant, we try to resolve its value (within parsed and internal constants) and use it. This is eventually made via a combination of ```var_export()``` and ```eval()```. Yes, that sucks, but there is no better way. Moreover the referenced constant may not exist. In that case it is replaced by a ```~~NOT RESOLVED~~``` string. At the moment we do not support constants declared using the define() function. We will implement support for names defined using a single string and simple values, but there is no way to implement support for something like ``` define('CONSTANT', $a ? 1 : 0); ``` When the library encounters a duplicate class, function or constant name, it converts the previously created reflection into an "invalid reflection" instance. That means that the parser is unable to distinguish between such classes and it is unable to build a proper class tree for example. And it throws an exception. When you catch this exception and continue to work with the Broker instance, the duplicate classes, functions or constants will have only one reflection and it will be an instance of **Invalid\ReflectionClass**, **Invalid\ReflectionFunction** or **Invalid\ReflectionConstant** respectively. ## Usage To be able to work with reflections you have to let the library parse the source code first. That is what [TokenReflection\\Broker](https://github.com/Andrewsville/PHP-Token-Reflection/blob/master/library/TokenReflection/Broker.php) does. It walks through the given directories, tokenizes PHP sources and caches reflection objects. Moreover, you cannot just instantiate a reflection class. You have to ask the Broker for the reflection. And once you have a reflection instance, everything works as expected :) ```php processDirectory('~/lib/Zend_Framework'); $class = $broker->getClass('Zend_Version'); // returns a TokenReflection\ReflectionClass instance $class = $broker->getClass('Exception'); // returns a TokenReflection\Php\ReflectionClass instance $class = $broker->getClass('Nonexistent'); // returns a TokenReflection\Dummy\ReflectionClass instance $function = $broker->getFunction(...); $constant = $broker->getConstant(...); ``` ## Requirements The library requires PHP 5.3 with the [tokenizer extension](http://cz.php.net/manual/en/book.tokenizer.php) enabled. If you want to process PHAR archives, you will require the [appropriate extension](http://cz.php.net/manual/en/book.phar.php) enabled as well. ## Current status The current version is 1.2. It should support the vast majority of PHP internal reflection features and add many more. Every release is tested using our testing package (several PHP frameworks and other libraries) and its compatibility is tested on all PHP versions of the 5.3 and 5.4 branch and the actual trunk. TokenReflection/Broker.php000064400000034132144760110310011574 0ustar00cache = array( self::CACHE_NAMESPACE => array(), self::CACHE_CLASS => array(), self::CACHE_CONSTANT => array(), self::CACHE_FUNCTION => array() ); $this->options = $options; $this->backend = $backend ->setBroker($this) ->setStoringTokenStreams((bool) ($options & self::OPTION_SAVE_TOKEN_STREAM)); } /** * Returns broker/parser options. * * @return integer */ public function getOptions() { return $this->options; } /** * Returns if a particular option setting is set. * * @param integer $option Option setting * @return boolean */ public function isOptionSet($option) { return (bool) ($this->options & $option); } /** * Parses a string with the PHP source code using the given file name and returns the appropriate reflection object. * * @param string $source PHP source code * @param string $fileName Used file name * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) * @return boolean|\TokenReflection\ReflectionFile */ public function processString($source, $fileName, $returnReflectionFile = false) { if ($this->backend->isFileProcessed($fileName)) { $tokens = $this->backend->getFileTokens($fileName); } else { $tokens = new Stream\StringStream($source, $fileName); } $reflectionFile = new ReflectionFile($tokens, $this); if (!$this->backend->isFileProcessed($fileName)) { $this->backend->addFile($tokens, $reflectionFile); // Clear the cache - leave only tokenized reflections foreach ($this->cache as $type => $cached) { if (!empty($cached)) { $this->cache[$type] = array_filter($cached, function(IReflection $reflection) { return $reflection->isTokenized(); }); } } } return $returnReflectionFile ? $reflectionFile : true; } /** * Parses a file and returns the appropriate reflection object. * * @param string $fileName Filename * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) * @return boolean|\TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\BrokerException If the file could not be processed. */ public function processFile($fileName, $returnReflectionFile = false) { try { if ($this->backend->isFileProcessed($fileName)) { $tokens = $this->backend->getFileTokens($fileName); } else { $tokens = new Stream\FileStream($fileName); } $reflectionFile = new ReflectionFile($tokens, $this); if (!$this->backend->isFileProcessed($fileName)) { $this->backend->addFile($tokens, $reflectionFile); // Clear the cache - leave only tokenized reflections foreach ($this->cache as $type => $cached) { if (!empty($cached)) { $this->cache[$type] = array_filter($cached, function(IReflection $reflection) { return $reflection->isTokenized(); }); } } } return $returnReflectionFile ? $reflectionFile : true; } catch (Exception\ParseException $e) { throw $e; } catch (Exception\StreamException $e) { throw new Exception\BrokerException($this, 'Could not process the file.', 0, $e); } } /** * Processes a PHAR archive. * * @param string $fileName Archive filename. * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) * @return boolean|array of \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\BrokerException If the PHAR PHP extension is not loaded. * @throws \TokenReflection\Exception\BrokerException If the given archive could not be read. * @throws \TokenReflection\Exception\BrokerException If the given archive could not be processed. */ public function processPhar($fileName, $returnReflectionFile = false) { if (!is_file($fileName)) { throw new Exception\BrokerException($this, 'File does not exist.', Exception\BrokerException::DOES_NOT_EXIST); } if (!extension_loaded('Phar')) { throw new Exception\BrokerException($this, 'The PHAR PHP extension is not loaded.', Exception\BrokerException::PHP_EXT_MISSING); } try { $result = array(); foreach (new RecursiveIteratorIterator(new \Phar($fileName)) as $entry) { if ($entry->isFile()) { $result[$entry->getPathName()] = $this->processFile($entry->getPathName(), $returnReflectionFile); } } return $returnReflectionFile ? $result : true; } catch (Exception\ParseException $e) { throw $e; } catch (Exception\StreamException $e) { throw new Exception\BrokerException($this, 'Could not process the archive.', 0, $e); } } /** * Processes recursively a directory and returns an array of file reflection objects. * * @param string $path Directora path * @param string|array $filters Filename filters * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) * @return boolean|array of \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\BrokerException If the given directory does not exist. * @throws \TokenReflection\Exception\BrokerException If the given directory could not be processed. */ public function processDirectory($path, $filters = array(), $returnReflectionFile = false) { $realPath = realpath($path); if (!is_dir($realPath)) { throw new Exception\BrokerException($this, 'File does not exist.', Exception\BrokerException::DOES_NOT_EXIST); } try { $result = array(); foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realPath)) as $entry) { if ($entry->isFile()) { $process = empty($filters); if (!$process) { foreach ((array) $filters as $filter) { $whitelisting = '!' !== $filter{0}; if (fnmatch($whitelisting ? $filter : substr($filter, 1), $entry->getPathName(), FNM_NOESCAPE)) { $process = $whitelisting; } } } if ($process) { $result[$entry->getPathName()] = $this->processFile($entry->getPathName(), $returnReflectionFile); } } } return $returnReflectionFile ? $result : true; } catch (Exception\ParseException $e) { throw $e; } catch (Exception\StreamException $e) { throw new Exception\BrokerException($this, 'Could not process the directory.', 0, $e); } } /** * Process a file, directory or a PHAR archive. * * @param string $path Path * @param boolean $returnReflectionFile Returns the appropriate \TokenReflection\ReflectionFile instance(s) * @return boolean|array|\TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\BrokerException If the target does not exist. */ public function process($path, $returnReflectionFile = false) { if (is_dir($path)) { return $this->processDirectory($path, array(), $returnReflectionFile); } elseif (is_file($path)) { if (preg_match('~\\.phar(?:$|\\.)~i', $path)) { return $this->processPhar($path, $returnReflectionFile); } return $this->processFile($path, $returnReflectionFile); } else { throw new Exception\BrokerException($this, 'The given directory/file does not exist.', Exception\BrokerException::DOES_NOT_EXIST); } } /** * Returns if the broker contains a namespace of the given name. * * @param string $namespaceName Namespace name * @return boolean */ public function hasNamespace($namespaceName) { return isset($this->cache[self::CACHE_NAMESPACE][$namespaceName]) || $this->backend->hasNamespace($namespaceName); } /** * Returns a reflection object of the given namespace. * * @param string $namespaceName Namespace name * @return \TokenReflection\ReflectionNamespace|null */ public function getNamespace($namespaceName) { $namespaceName = ltrim($namespaceName, '\\'); if (isset($this->cache[self::CACHE_NAMESPACE][$namespaceName])) { return $this->cache[self::CACHE_NAMESPACE][$namespaceName]; } $namespace = $this->backend->getNamespace($namespaceName); if (null !== $namespace) { $this->cache[self::CACHE_NAMESPACE][$namespaceName] = $namespace; } return $namespace; } /** * Returns if the broker contains a class of the given name. * * @param string $className Class name * @return boolean */ public function hasClass($className) { return isset($this->cache[self::CACHE_CLASS][$className]) || $this->backend->hasClass($className); } /** * Returns a reflection object of the given class (FQN expected). * * @param string $className CLass bame * @return \TokenReflection\ReflectionClass|null */ public function getClass($className) { $className = ltrim($className, '\\'); if (isset($this->cache[self::CACHE_CLASS][$className])) { return $this->cache[self::CACHE_CLASS][$className]; } $this->cache[self::CACHE_CLASS][$className] = $this->backend->getClass($className); return $this->cache[self::CACHE_CLASS][$className]; } /** * Returns all classes from all namespaces. * * @param integer $types Returned class types (multiple values may be OR-ed) * @return array */ public function getClasses($types = Broker\Backend::TOKENIZED_CLASSES) { return $this->backend->getClasses($types); } /** * Returns if the broker contains a constant of the given name. * * @param string $constantName Constant name * @return boolean */ public function hasConstant($constantName) { return isset($this->cache[self::CACHE_CONSTANT][$constantName]) || $this->backend->hasConstant($constantName); } /** * Returns a reflection object of a constant (FQN expected). * * @param string $constantName Constant name * @return \TokenReflection\ReflectionConstant|null */ public function getConstant($constantName) { $constantName = ltrim($constantName, '\\'); if (isset($this->cache[self::CACHE_CONSTANT][$constantName])) { return $this->cache[self::CACHE_CONSTANT][$constantName]; } if ($constant = $this->backend->getConstant($constantName)) { $this->cache[self::CACHE_CONSTANT][$constantName] = $constant; } return $constant; } /** * Returns all constants from all namespaces. * * @return array */ public function getConstants() { return $this->backend->getConstants(); } /** * Returns if the broker contains a function of the given name. * * @param string $functionName Function name * @return boolean */ public function hasFunction($functionName) { return isset($this->cache[self::CACHE_FUNCTION][$functionName]) || $this->backend->hasFunction($functionName); } /** * Returns a reflection object of a function (FQN expected). * * @param string $functionName Function name * @return \TokenReflection\ReflectionFunction|null */ public function getFunction($functionName) { $functionName = ltrim($functionName, '\\'); if (isset($this->cache[self::CACHE_FUNCTION][$functionName])) { return $this->cache[self::CACHE_FUNCTION][$functionName]; } if ($function = $this->backend->getFunction($functionName)) { $this->cache[self::CACHE_FUNCTION][$functionName] = $function; } return $function; } /** * Returns all functions from all namespaces. * * @return array */ public function getFunctions() { return $this->backend->getFunctions(); } /** * Returns if the broker contains a file reflection of the given name. * * @param string $fileName File name * @return boolean */ public function hasFile($fileName) { return $this->backend->hasFile($fileName); } /** * Returns a reflection object of a file. * * @param string $fileName File name * @return \TokenReflection\ReflectionFile|null */ public function getFile($fileName) { return $this->backend->getFile($fileName); } /** * Returns all processed files reflections. * * @return array */ public function getFiles() { return $this->backend->getFiles(); } /** * Returns an array of tokens from a processed file. * * @param string $fileName File name * @return \TokenReflection\Stream\StreamBase|null */ public function getFileTokens($fileName) { return $this->backend->getFileTokens($fileName); } /** * Returns a real system path. * * @param string $path Source path * @return string|boolean */ public static function getRealPath($path) { if (0 === strpos($path, 'phar://')) { return is_file($path) || is_dir($path) ? $path : false; } else { return realpath($path); } } } TokenReflection/Broker/Backend.php000064400000011257144760110310013126 0ustar00files[$fileName]); } /** * Returns a file reflection. * * @param string $fileName File name * @return \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\BrokerException If the requested file has not been processed */ public function getFile($fileName) { if (!isset($this->files[$fileName])) { throw new Exception\BrokerException($this->getBroker(), sprintf('File "%s" has not been processed.', $fileName), Exception\BrokerException::DOES_NOT_EXIST); } return $this->files[$fileName]; } /** * Returns file reflections. * * @return array */ public function getFiles() { return $this->files; } /** * Returns if there was such namespace processed (FQN expected). * * @param string $namespaceName Namespace name * @return boolean */ public function hasNamespace($namespaceName) { return isset($this->namespaces[ltrim($namespaceName, '\\')]); } /** * Returns a reflection object of the given namespace. * * @param string $namespaceName Namespace name * @return \TokenReflection\IReflectionNamespace * @throws \TokenReflection\Exception\BrokerException If the requested namespace does not exist. */ public function getNamespace($namespaceName) { if (!isset($this->namespaces[TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME])) { $this->namespaces[TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME] = new TokenReflection\ReflectionNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME, $this->broker); } $namespaceName = ltrim($namespaceName, '\\'); if (!isset($this->namespaces[$namespaceName])) { throw new Exception\BrokerException($this->getBroker(), sprintf('Namespace %s does not exist.', $namespaceName), Exception\BrokerException::DOES_NOT_EXIST); } return $this->namespaces[$namespaceName]; } /** * Returns all present namespaces. * * @return array */ public function getNamespaces() { return $this->namespaces; } /** * Returns if there was such class processed (FQN expected). * * @param string $className Class name * @return boolean */ public function hasClass($className) { $className = ltrim($className, '\\'); if ($pos = strrpos($className, '\\')) { $namespace = substr($className, 0, $pos); if (!isset($this->namespaces[$namespace])) { return false; } $namespace = $this->getNamespace($namespace); $className = substr($className, $pos + 1); } else { $namespace = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); } return $namespace->hasClass($className); } /** * Returns a reflection object of the given class (FQN expected). * * @param string $className CLass bame * @return \TokenReflection\IReflectionClass */ public function getClass($className) { if (empty($this->declaredClasses)) { $this->declaredClasses = array_flip(array_merge(get_declared_classes(), get_declared_interfaces())); } $className = ltrim($className, '\\'); try { $ns = $this->getNamespace( ($boundary = strrpos($className, '\\')) // Class within a namespace ? substr($className, 0, $boundary) // Class without a namespace : TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME ); return $ns->getClass($className); } catch (Exception\BaseException $e) { if (isset($this->declaredClasses[$className])) { $reflection = new Php\ReflectionClass($className, $this->broker); if ($reflection->isInternal()) { return $reflection; } } return new Dummy\ReflectionClass($className, $this->broker); } } /** * Returns all classes from all namespaces. * * @param integer $type Returned class types (multiple values may be OR-ed) * @return array */ public function getClasses($type = self::TOKENIZED_CLASSES) { if (null === $this->allClasses) { $this->allClasses = $this->parseClassLists(); } $result = array(); foreach ($this->allClasses as $classType => $classes) { if ($type & $classType) { $result = array_merge($result, $classes); } } return $result; } /** * Returns if there was such constant processed (FQN expected). * * @param string $constantName Constant name * @return boolean */ public function hasConstant($constantName) { $constantName = ltrim($constantName, '\\'); if ($pos = strpos($constantName, '::')) { $className = substr($constantName, 0, $pos); $constantName = substr($constantName, $pos + 2); if (!$this->hasClass($className)) { return false; } $parent = $this->getClass($className); } else { if ($pos = strrpos($constantName, '\\')) { $namespace = substr($constantName, 0, $pos); if (!$this->hasNamespace($namespace)) { return false; } $parent = $this->getNamespace($namespace); $constantName = substr($constantName, $pos + 1); } else { $parent = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); } } return $parent->hasConstant($constantName); } /** * Returns a reflection object of a constant (FQN expected). * * @param string $constantName Constant name * @return \TokenReflection\IReflectionConstant * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. */ public function getConstant($constantName) { static $declared = array(); if (empty($declared)) { $declared = get_defined_constants(); } if ($boundary = strpos($constantName, '::')) { // Class constant $className = substr($constantName, 0, $boundary); $constantName = substr($constantName, $boundary + 2); return $this->getClass($className)->getConstantReflection($constantName); } try { $constantName = ltrim($constantName, '\\'); if ($boundary = strrpos($constantName, '\\')) { $ns = $this->getNamespace(substr($constantName, 0, $boundary)); $constantName = substr($constantName, $boundary + 1); } else { $ns = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); } return $ns->getConstant($constantName); } catch (Exception\BaseException $e) { if (isset($declared[$constantName])) { $reflection = new Php\ReflectionConstant($constantName, $declared[$constantName], $this->broker); if ($reflection->isInternal()) { return $reflection; } } throw new Exception\BrokerException($this->getBroker(), sprintf('Constant %s does not exist.', $constantName), Exception\BrokerException::DOES_NOT_EXIST); } } /** * Returns all constants from all namespaces. * * @return array */ public function getConstants() { if (null === $this->allConstants) { $this->allConstants = array(); foreach ($this->namespaces as $namespace) { foreach ($namespace->getConstants() as $constant) { $this->allConstants[$constant->getName()] = $constant; } } } return $this->allConstants; } /** * Returns if there was such function processed (FQN expected). * * @param string $functionName Function name * @return boolean */ public function hasFunction($functionName) { $functionName = ltrim($functionName, '\\'); if ($pos = strrpos($functionName, '\\')) { $namespace = substr($functionName, 0, $pos); if (!isset($this->namespaces[$namespace])) { return false; } $namespace = $this->getNamespace($namespace); $functionName = substr($functionName, $pos + 1); } else { $namespace = $this->getNamespace(TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME); } return $namespace->hasFunction($functionName); } /** * Returns a reflection object of a function (FQN expected). * * @param string $functionName Function name * @return \TokenReflection\IReflectionFunction * @throws \TokenReflection\Exception\RuntimeException If the requested function does not exist. */ public function getFunction($functionName) { static $declared = array(); if (empty($declared)) { $functions = get_defined_functions(); $declared = array_flip($functions['internal']); } $functionName = ltrim($functionName, '\\'); try { $ns = $this->getNamespace( ($boundary = strrpos($functionName, '\\')) // Function within a namespace ? substr($functionName, 0, $boundary) // Function wihout a namespace : TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME ); return $ns->getFunction($functionName); } catch (Exception\BaseException $e) { if (isset($declared[$functionName])) { return new Php\ReflectionFunction($functionName, $this->broker); } throw new Exception\BrokerException($this->getBroker(), sprintf('Function %s does not exist.', $functionName), Exception\BrokerException::DOES_NOT_EXIST); } } /** * Returns all functions from all namespaces. * * @return array */ public function getFunctions() { if (null === $this->allFunctions) { $this->allFunctions = array(); foreach ($this->namespaces as $namespace) { foreach ($namespace->getFunctions() as $function) { $this->allFunctions[$function->getName()] = $function; } } } return $this->allFunctions; } /** * Returns if the given file was already processed. * * @param string $fileName File name * @return boolean */ public function isFileProcessed($fileName) { return isset($this->tokenStreams[Broker::getRealPath($fileName)]); } /** * Returns an array of tokens for a particular file. * * @param string $fileName File name * @return \TokenReflection\Stream\StreamBase * @throws \TokenReflection\Exception\BrokerException If the requested file was not processed. */ public function getFileTokens($fileName) { $realName = Broker::getRealPath($fileName); if (!isset($this->tokenStreams[$realName])) { throw new Exception\BrokerException($this->getBroker(), sprintf('File "%s" was not processed yet.', $fileName), Exception\BrokerException::DOES_NOT_EXIST); } return true === $this->tokenStreams[$realName] ? new FileStream($realName) : $this->tokenStreams[$realName]; } /** * Adds a file to the backend storage. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token stream * @param \TokenReflection\ReflectionFile $file File reflection object * @return \TokenReflection\Broker\Backend\Memory */ public function addFile(TokenReflection\Stream\StreamBase $tokenStream, TokenReflection\ReflectionFile $file) { $this->tokenStreams[$file->getName()] = $this->storingTokenStreams ? $tokenStream : true; $this->files[$file->getName()] = $file; $errors = array(); foreach ($file->getNamespaces() as $fileNamespace) { try { $namespaceName = $fileNamespace->getName(); if (!isset($this->namespaces[$namespaceName])) { $this->namespaces[$namespaceName] = new TokenReflection\ReflectionNamespace($namespaceName, $file->getBroker()); } $this->namespaces[$namespaceName]->addFileNamespace($fileNamespace); } catch (Exception\FileProcessingException $e) { $errors = array_merge($errors, $e->getReasons()); } catch (\Exception $e) { echo $e->getTraceAsString(); die($e->getMessage()); } } // Reset all-*-cache $this->allClasses = null; $this->allFunctions = null; $this->allConstants = null; if (!empty($errors)) { throw new Exception\FileProcessingException($errors, $file); } return $this; } /** * Sets the reflection broker instance. * * @param \TokenReflection\Broker $broker Reflection broker * @return \TokenReflection\Broker\Backend\Memory */ public function setBroker(Broker $broker) { $this->broker = $broker; return $this; } /** * Returns the reflection broker instance. * * @return \TokenReflection\Broker $broker Reflection broker */ public function getBroker() { return $this->broker; } /** * Sets if token streams are stored in the backend. * * @param boolean $store * @return \TokenReflection\Broker\Backend */ public function setStoringTokenStreams($store) { $this->storingTokenStreams = (bool) $store; return $this; } /** * Returns if token streams are stored in the backend. * * @return boolean */ public function getStoringTokenStreams() { return $this->storingTokenStreams; } /** * Prepares and returns used class lists. * * @return array */ protected function parseClassLists() { // Initialize the all-classes-cache $allClasses = array( self::TOKENIZED_CLASSES => array(), self::INTERNAL_CLASSES => array(), self::NONEXISTENT_CLASSES => array() ); foreach ($this->namespaces as $namespace) { foreach ($namespace->getClasses() as $class) { $allClasses[self::TOKENIZED_CLASSES][$class->getName()] = $class; } } foreach ($allClasses[self::TOKENIZED_CLASSES] as $className => $class) { foreach (array_merge($class->getParentClasses(), $class->getInterfaces()) as $parent) { if ($parent->isInternal()) { $allClasses[self::INTERNAL_CLASSES][$parent->getName()] = $parent; } elseif (!$parent->isTokenized()) { $allClasses[self::NONEXISTENT_CLASSES][$parent->getName()] = $parent; } } } return $allClasses; } } TokenReflection/Dummy/ReflectionClass.php000064400000050420144760110310014521 0ustar00name = ltrim($className, '\\'); $this->broker = $broker; } /** * Returns the name (FQN). * * @return string */ public function getName() { return $this->name; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->name; } /** * Returns the unqualified name (UQN). * * @return string */ public function getShortName() { $pos = strrpos($this->name, '\\'); return false === $pos ? $this->name : substr($this->name, $pos + 1); } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { $pos = strrpos($this->name, '\\'); return false === $pos ? '' : substr($this->name, 0, $pos); } /** * Returns if the class is defined within a namespace. * * @return boolean */ public function inNamespace() { return false !== strrpos($this->name, '\\'); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Returns the PHP extension reflection. * * @return null */ public function getExtension() { return null; } /** * Returns the PHP extension name. * * @return boolean */ public function getExtensionName() { return false; } /** * Returns the file name the reflection object is defined in. * * @return null */ public function getFileName() { return null; } /** * Returns a file reflection. * * @return \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker */ public function getFileReflection() { throw new Exception\BrokerException($this->getBroker(), sprintf('Class was not parsed from a file', $this->getName()), Exception\BrokerException::UNSUPPORTED); } /** * Returns the definition start line number in the file. * * @return null */ public function getStartLine() { return null; } /** * Returns the definition end line number in the file. * * @return null */ public function getEndLine() { return null; } /** * Returns the appropriate docblock definition. * * @return boolean */ public function getDocComment() { return false; } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns modifiers. * * @return integer */ public function getModifiers() { return 0; } /** * Returns if the class is abstract. * * @return boolean */ public function isAbstract() { return false; } /** * Returns if the class is final. * * @return boolean */ public function isFinal() { return false; } /** * Returns if the class is an interface. * * @return boolean */ public function isInterface() { return false; } /** * Returns if the class is an exception or its descendant. * * @return boolean */ public function isException() { return false; } /** * Returns if it is possible to create an instance of this class. * * @return boolean */ public function isInstantiable() { return false; } /** * Returns traits used by this class. * * @return array */ public function getTraits() { return array(); } /** * Returns traits used by this class and not its parents. * * @return array */ public function getOwnTraits() { return array(); } /** * Returns names of used traits. * * @return array */ public function getTraitNames() { return array(); } /** * Returns traits used by this class and not its parents. * * @return array */ public function getOwnTraitNames() { return array(); } /** * Returns method aliases from traits. * * @return array */ public function getTraitAliases() { return array(); } /** * Returns if the class is a trait. * * @return boolean */ public function isTrait() { return false; } /** * Returns if the class uses a particular trait. * * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name * @return boolean */ public function usesTrait($trait) { return false; } /** * Returns if objects of this class are cloneable. * * Introduced in PHP 5.4. * * @return boolean * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 */ public function isCloneable() { return false; } /** * Returns if the class is iterateable. * * Returns true if the class implements the Traversable interface. * * @return boolean */ public function isIterateable() { return false; } /** * Returns if the reflection object is internal. * * @return boolean */ public function isInternal() { return false; } /** * Returns if the reflection object is user defined. * * @return boolean */ public function isUserDefined() { return false; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns if the current class is a subclass of the given class. * * @param string|object $class Class name or reflection object * @return boolean */ public function isSubclassOf($class) { return false; } /** * Returns the parent class reflection. * * @return null */ public function getParentClass() { return false; } /** * Returns the parent classes reflections. * * @return array */ public function getParentClasses() { return array(); } /** * Returns the parent classes names. * * @return array */ public function getParentClassNameList() { return array(); } /** * Returns the parent class reflection. * * @return null */ public function getParentClassName() { return null; } /** * Returns if the class implements the given interface. * * @param string|object $interface Interface name or reflection object * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. */ public function implementsInterface($interface) { if (is_object($interface)) { if (!$interface instanceof IReflectionClass) { throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $interfaceName = $interface->getName(); if (!$interface->isInterface()) { throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } } // Only validation, always returns false return false; } /** * Returns interface reflections. * * @return array */ public function getInterfaces() { return array(); } /** * Returns interface names. * * @return array */ public function getInterfaceNames() { return array(); } /** * Returns interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaces() { return array(); } /** * Returns names of interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaceNames() { return array(); } /** * Returns the class constructor reflection. * * @return null */ public function getConstructor() { return null; } /** * Returns the class desctructor reflection. * * @return null */ public function getDestructor() { return null; } /** * Returns if the class implements the given method. * * @param string $name Method name * @return boolean */ public function hasMethod($name) { return false; } /** * Returns a method reflection. * * @param string $name Method name * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. */ public function getMethod($name) { throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns method reflections. * * @param integer $filter Methods filter * @return array */ public function getMethods($filter = null) { return array(); } /** * Returns if the class implements (and not its parents) the given method. * * @param string $name Method name * @return boolean */ public function hasOwnMethod($name) { return false; } /** * Returns methods declared by this class, not its parents. * * @param integer $filter Methods filter * @return array */ public function getOwnMethods($filter = null) { return array(); } /** * Returns if the class imports the given method from traits. * * @param string $name Method name * @return boolean */ public function hasTraitMethod($name) { return false; } /** * Returns method reflections imported from traits. * * @param integer $filter Methods filter * @return array */ public function getTraitMethods($filter = null) { return array(); } /** * Returns if the class defines the given constant. * * @param string $name Constant name. * @return boolean */ public function hasConstant($name) { return false; } /** * Returns a constant value. * * @param string $name Constant name * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. */ public function getConstant($name) { throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns a constant reflection. * * @param string $name Constant name * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. */ public function getConstantReflection($name) { throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns an array of constant values. * * @return array */ public function getConstants() { return array(); } /** * Returns an array of constant reflections. * * @return array */ public function getConstantReflections() { return array(); } /** * Returns if the class (and not its parents) defines the given constant. * * @param string $name Constant name. * @return boolean */ public function hasOwnConstant($name) { return false; } /** * Returns constants declared by this class, not its parents. * * @return array */ public function getOwnConstants() { return array(); } /** * Returns an array of constant reflections defined by this class not its parents. * * @return array */ public function getOwnConstantReflections() { return array(); } /** * Returns default properties. * * @return array */ public function getDefaultProperties() { return array(); } /** * Returns if the class implements the given property. * * @param string $name Property name * @return boolean */ public function hasProperty($name) { return false; } /** * Returns class properties. * * @param integer $filter Property types * @return array */ public function getProperties($filter = null) { return array(); } /** * Return a property reflections. * * @param string $name Property name * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. */ public function getProperty($name) { throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns if the class (and not its parents) implements the given property. * * @param string $name Property name * @return boolean */ public function hasOwnProperty($name) { return false; } /** * Returns properties declared by this class, not its parents. * * @param integer $filter Properties filter * @return array */ public function getOwnProperties($filter = null) { return array(); } /** * Returns if the class imports the given property from traits. * * @param string $name Property name * @return boolean */ public function hasTraitProperty($name) { return false; } /** * Returns property reflections imported from traits. * * @param integer $filter Properties filter * @return array */ public function getTraitProperties($filter = null) { return array(); } /** * Returns static properties reflections. * * @return array */ public function getStaticProperties() { return array(); } /** * Returns a value of a static property. * * @param string $name Property name * @param mixed $default Default value * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. */ public function getStaticPropertyValue($name, $default = null) { throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns reflections of direct subclasses. * * @return array */ public function getDirectSubclasses() { return array(); } /** * Returns names of direct subclasses. * * @return array */ public function getDirectSubclassNames() { return array(); } /** * Returns reflections of indirect subclasses. * * @return array */ public function getIndirectSubclasses() { return array(); } /** * Returns names of indirect subclasses. * * @return array */ public function getIndirectSubclassNames() { return array(); } /** * Returns reflections of classes directly implementing this interface. * * @return array */ public function getDirectImplementers() { return array(); } /** * Returns names of classes directly implementing this interface. * * @return array */ public function getDirectImplementerNames() { return array(); } /** * Returns reflections of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementers() { return array(); } /** * Returns names of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementerNames() { return array(); } /** * Returns if the given object is an instance of this class. * * @param object $object Instance * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object. */ public function isInstance($object) { if (!is_object($object)) { throw new Exception\RuntimeException(sprintf('Parameter must be a class instance, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this); } return $this->name === get_class($object) || is_subclass_of($object, $this->name); } /** * Creates a new class instance without using a constructor. * * @return object * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. */ public function newInstanceWithoutConstructor() { if (!class_exists($this->name, true)) { throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $reflection = new \TokenReflection\Php\ReflectionClass($this->name, $this->getBroker()); return $reflection->newInstanceWithoutConstructor(); } /** * Creates a new instance using variable number of parameters. * * Use any number of constructor parameters as function parameters. * * @param mixed $args * @return object */ public function newInstance($args) { return $this->newInstanceArgs(func_get_args()); } /** * Creates a new instance using an array of parameters. * * @param array $args Array of constructor parameters * @return object * @throws \TokenReflection\Exception\RuntimeException If the required class does not exist. */ public function newInstanceArgs(array $args = array()) { if (!class_exists($this->name, true)) { throw new Exception\RuntimeException('Could not create an instance of class; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $reflection = new InternalReflectionClass($this->name); return $reflection->newInstanceArgs($args); } /** * Sets a static property value. * * @param string $name Property name * @param mixed $value Property value * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. */ public function setStaticPropertyValue($name, $value) { throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { return sprintf( "Class|Interface [ class|interface %s ] {\n %s%s%s%s%s\n}\n", $this->getName(), "\n\n - Constants [0] {\n }", "\n\n - Static properties [0] {\n }", "\n\n - Static methods [0] {\n }", "\n\n - Properties [0] {\n }", "\n\n - Methods [0] {\n }" ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string|object $className Class name or class instance * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $className, $return = false) { TokenReflection\ReflectionClass::export($broker, $className, $return); } /** * Outputs the reflection subject source code. * * @return string */ public function getSource() { return ''; } /** * Returns the start position in the file token stream. * * @return integer */ public function getStartPosition() { return -1; } /** * Returns the end position in the file token stream. * * @return integer */ public function getEndPosition() { return -1; } /** * Returns if the class definition is complete. * * Dummy classes never have the definition complete. * * @return boolean */ public function isComplete() { return false; } /** * Returns if the class definition is valid. * * Dummy classes are always valid. * * @return boolean */ public function isValid() { return true; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return ReflectionBase::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return ReflectionBase::exists($this, $key); } } TokenReflection/Exception/BaseException.php000064400000003321144760110310015033 0ustar00getDetail(); return sprintf( "exception '%s'%s in %s on line %d\n%s\nStack trace:\n%s", get_class($this), $this->getMessage() ? " with message '" . $this->getMessage() . "'" : '', $this->getFile(), $this->getLine(), empty($detail) ? '' : $detail . "\n", $this->getTraceAsString() ); } /** * Returns the exception details as string. * * @return string */ final public function __toString() { $output = ''; if ($ex = $this->getPrevious()) { $output .= (string) $ex . "\n\nNext "; } return $output . $this->getOutput() . "\n"; } } TokenReflection/Exception/BrokerException.php000064400000002277144760110310015416 0ustar00broker = $broker; } /** * Returns the current Broker. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns an exception description detail. * * @return string */ public function getDetail() { return ''; } } TokenReflection/Exception/FileProcessingException.php000064400000003215144760110310017077 0ustar00getName()), 0, $sender ); $this->reasons = $reasons; } /** * Returns a list of reasons why the file could not be processed. * * @return array */ public function getReasons() { return $this->reasons; } /** * Returns an exception description detail. * * @return string */ public function getDetail() { if (!empty($this->reasons)) { $reasons = array_map(function(BaseException $reason) { if ($reason instanceof ParseException) { return $reason->getDetail(); } else { return $reason->getMessage(); } }, $this->reasons); return "There were following reasons for this exception:\n" . implode("\n", $reasons); } return ''; } } TokenReflection/Exception/ParseException.php000064400000013226144760110310015240 0ustar00sender = $sender; $token = $tokenStream->current(); $position = $tokenStream->key(); if (!empty($token) && !empty($position)) { $this->token = $token; $this->tokenName = $tokenStream->getTokenName(); $line = $this->token[2]; $min = $max = $position; } else { $min = $max = $tokenStream->count() - 1; $line = $tokenStream[$min][2]; } $this->exceptionLine = $line; static $skip = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true); $significant = array(); while (isset($tokenStream[$min - 1])) { if (!isset($significant[$tokenStream[$min][2]])) { if (self::SOURCE_LINES_AROUND <= array_sum($significant)) { break; } $significant[$tokenStream[$min][2]] = !isset($skip[$tokenStream[$min][0]]); } else { $significant[$tokenStream[$min][2]] |= !isset($skip[$tokenStream[$min][0]]); } $min--; } $significant = array(); while (isset($tokenStream[$max + 1])) { if (!isset($significant[$tokenStream[$max][2]])) { if (self::SOURCE_LINES_AROUND <= array_sum($significant)) { break; } $significant[$tokenStream[$max][2]] = !isset($skip[$tokenStream[$max][0]]); } else { $significant[$tokenStream[$max][2]] |= !isset($skip[$tokenStream[$max][0]]); } $max++; } $this->scopeBoundaries = array($min, $max); } /** * Returns the token where the problem was detected or NULL if the token stream was empty or an end was reached. * * @return array|null */ public function getToken() { return $this->token; } /** * Returns the name of the token where the problem was detected or NULL if the token stream was empty or an end was reached. * * @return string|null */ public function getTokenName() { return $this->tokenName; } /** * Returns the line where the exception was thrown. * * @return integer */ public function getExceptionLine() { return $this->exceptionLine; } /** * Returns the file line with the token or null. * * @return integer|null */ public function getTokenLine() { return null === $this->token ? null : $this->token[2]; } /** * Returns the source code part around the token. * * @param boolean $lineNumbers Returns the source code part with line numbers * @return string|null */ public function getSourcePart($lineNumbers = false) { if (empty($this->scopeBoundaries)) { return null; } list($lo, $hi) = $this->scopeBoundaries; $stream = $this->getStream(); $code = $stream->getSourcePart($lo, $hi); if ($lineNumbers) { $lines = explode("\n", $code); $startLine = $stream[$lo][2]; $width = strlen($startLine + count($lines) - 1); $errorLine = $this->token[2]; $actualLine = $startLine; $code = implode( "\n", array_map(function($line) use (&$actualLine, $width, $errorLine) { return ($actualLine === $errorLine ? '*' : ' ') . str_pad($actualLine++, $width, ' ', STR_PAD_LEFT) . ': ' . $line; }, $lines) ); } return $code; } /** * Returns the reflection element that caused the exception to be raised. * * @return \TokenReflection\IReflection */ public function getSender() { return $this->sender; } /** * Returns an exception description detail. * * @return string */ public function getDetail() { if (0 === $this->getStream()->count()) { return parent::getDetail() . 'The token stream was empty.'; } elseif (empty($this->token)) { return parent::getDetail() . 'The token stream was read out of its bounds.'; } else { return parent::getDetail() . sprintf( "\nThe cause of the exception was the %s token (line %s) in following part of %s source code:\n\n%s", $this->tokenName, $this->token[2], $this->sender && $this->sender->getName() ? $this->sender->getPrettyName() : 'the', $this->getSourcePart(true) ); } } } TokenReflection/Exception/RuntimeException.php000064400000002635144760110310015613 0ustar00sender = $sender; } /** * Returns the reflection element that caused the exception to be raised. * * @return \TokenReflection\IReflection */ public function getSender() { return $this->sender; } /** * Returns an exception description detail. * * @return string */ public function getDetail() { return null === $this->sender ? '' : sprintf('Thrown when working with "%s".', $this->sender->getPrettyName()); } } TokenReflection/Exception/StreamException.php000064400000003412144760110310015415 0ustar00stream = $stream; } /** * Returns the reflection element that caused the exception to be raised. * * @return \TokenReflection\Stream\StreamBase */ public function getStream() { return $this->stream; } /** * Returns the processed file name. * * @return string */ public function getFileName() { return $this->stream->getFileName(); } /** * Returns an exception description detail. * * @return string */ public function getDetail() { return sprintf('Thrown when working with file "%s" token stream.', $this->getFileName()); } } TokenReflection/IReflection.php000064400000002470144760110310012553 0ustar00 5.3.0, you can uncomment it. * * @return mixed */ // public function invoke(); /** * Calls the function. * * @param array $args Function parameter values * @return mixed */ public function invokeArgs(array $args); /** * Returns the function/method as closure. * * @return \Closure */ public function getClosure(); /** * Returns if the function definition is valid. * * That means that the source code is valid and the function name is unique within parsed files. * * @return boolean */ public function isValid(); /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases(); } TokenReflection/IReflectionFunctionBase.php000064400000004567144760110310015065 0ustar00name = ltrim($className, '\\'); $this->fileName = $fileName; $this->broker = $broker; } /** * Returns the name (FQN). * * @return string */ public function getName() { return $this->name; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->name; } /** * Returns the unqualified name (UQN). * * @return string */ public function getShortName() { $pos = strrpos($this->name, '\\'); return false === $pos ? $this->name : substr($this->name, $pos + 1); } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { $pos = strrpos($this->name, '\\'); return false === $pos ? '' : substr($this->name, 0, $pos); } /** * Returns if the class is defined within a namespace. * * @return boolean */ public function inNamespace() { return false !== strrpos($this->name, '\\'); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Returns the PHP extension reflection. * * @return null */ public function getExtension() { return null; } /** * Returns the PHP extension name. * * @return boolean */ public function getExtensionName() { return false; } /** * Returns the file name the reflection object is defined in. * * @return null */ public function getFileName() { return $this->fileName; } /** * Returns a file reflection. * * @return \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker */ public function getFileReflection() { throw new Exception\BrokerException($this->getBroker(), sprintf('Class was not parsed from a file', $this->getName()), Exception\BrokerException::UNSUPPORTED); } /** * Returns the definition start line number in the file. * * @return null */ public function getStartLine() { return null; } /** * Returns the definition end line number in the file. * * @return null */ public function getEndLine() { return null; } /** * Returns the appropriate docblock definition. * * @return boolean */ public function getDocComment() { return false; } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns modifiers. * * @return integer */ public function getModifiers() { return 0; } /** * Returns if the class is abstract. * * @return boolean */ public function isAbstract() { return false; } /** * Returns if the class is final. * * @return boolean */ public function isFinal() { return false; } /** * Returns if the class is an interface. * * @return boolean */ public function isInterface() { return false; } /** * Returns if the class is an exception or its descendant. * * @return boolean */ public function isException() { return false; } /** * Returns if it is possible to create an instance of this class. * * @return boolean */ public function isInstantiable() { return false; } /** * Returns traits used by this class. * * @return array */ public function getTraits() { return array(); } /** * Returns traits used by this class and not its parents. * * @return array */ public function getOwnTraits() { return array(); } /** * Returns names of used traits. * * @return array */ public function getTraitNames() { return array(); } /** * Returns traits used by this class and not its parents. * * @return array */ public function getOwnTraitNames() { return array(); } /** * Returns method aliases from traits. * * @return array */ public function getTraitAliases() { return array(); } /** * Returns if the class is a trait. * * @return boolean */ public function isTrait() { return false; } /** * Returns if the class uses a particular trait. * * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name * @return boolean */ public function usesTrait($trait) { return false; } /** * Returns if objects of this class are cloneable. * * Introduced in PHP 5.4. * * @return boolean * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 */ public function isCloneable() { return false; } /** * Returns if the class is iterateable. * * Returns true if the class implements the Traversable interface. * * @return boolean */ public function isIterateable() { return false; } /** * Returns if the reflection object is internal. * * @return boolean */ public function isInternal() { return false; } /** * Returns if the reflection object is user defined. * * @return boolean */ public function isUserDefined() { return true; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return true; } /** * Returns if the current class is a subclass of the given class. * * @param string|object $class Class name or reflection object * @return boolean */ public function isSubclassOf($class) { return false; } /** * Returns the parent class reflection. * * @return null */ public function getParentClass() { return false; } /** * Returns the parent classes reflections. * * @return array */ public function getParentClasses() { return array(); } /** * Returns the parent classes names. * * @return array */ public function getParentClassNameList() { return array(); } /** * Returns the parent class reflection. * * @return null */ public function getParentClassName() { return null; } /** * Returns if the class implements the given interface. * * @param string|object $interface Interface name or reflection object * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. */ public function implementsInterface($interface) { if (is_object($interface)) { if (!$interface instanceof IReflectionClass) { throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $interfaceName = $interface->getName(); if (!$interface->isInterface()) { throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } } // Only validation, always returns false return false; } /** * Returns interface reflections. * * @return array */ public function getInterfaces() { return array(); } /** * Returns interface names. * * @return array */ public function getInterfaceNames() { return array(); } /** * Returns interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaces() { return array(); } /** * Returns names of interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaceNames() { return array(); } /** * Returns the class constructor reflection. * * @return null */ public function getConstructor() { return null; } /** * Returns the class desctructor reflection. * * @return null */ public function getDestructor() { return null; } /** * Returns if the class implements the given method. * * @param string $name Method name * @return boolean */ public function hasMethod($name) { return false; } /** * Returns a method reflection. * * @param string $name Method name * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. */ public function getMethod($name) { throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns method reflections. * * @param integer $filter Methods filter * @return array */ public function getMethods($filter = null) { return array(); } /** * Returns if the class implements (and not its parents) the given method. * * @param string $name Method name * @return boolean */ public function hasOwnMethod($name) { return false; } /** * Returns methods declared by this class, not its parents. * * @param integer $filter Methods filter * @return array */ public function getOwnMethods($filter = null) { return array(); } /** * Returns if the class imports the given method from traits. * * @param string $name Method name * @return boolean */ public function hasTraitMethod($name) { return false; } /** * Returns method reflections imported from traits. * * @param integer $filter Methods filter * @return array */ public function getTraitMethods($filter = null) { return array(); } /** * Returns if the class defines the given constant. * * @param string $name Constant name. * @return boolean */ public function hasConstant($name) { return false; } /** * Returns a constant value. * * @param string $name Constant name * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. */ public function getConstant($name) { throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns a constant reflection. * * @param string $name Constant name * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. */ public function getConstantReflection($name) { throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns an array of constant values. * * @return array */ public function getConstants() { return array(); } /** * Returns an array of constant reflections. * * @return array */ public function getConstantReflections() { return array(); } /** * Returns if the class (and not its parents) defines the given constant. * * @param string $name Constant name. * @return boolean */ public function hasOwnConstant($name) { return false; } /** * Returns constants declared by this class, not its parents. * * @return array */ public function getOwnConstants() { return array(); } /** * Returns an array of constant reflections defined by this class not its parents. * * @return array */ public function getOwnConstantReflections() { return array(); } /** * Returns default properties. * * @return array */ public function getDefaultProperties() { return array(); } /** * Returns if the class implements the given property. * * @param string $name Property name * @return boolean */ public function hasProperty($name) { return false; } /** * Returns class properties. * * @param integer $filter Property types * @return array */ public function getProperties($filter = null) { return array(); } /** * Return a property reflections. * * @param string $name Property name * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. */ public function getProperty($name) { throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns if the class (and not its parents) implements the given property. * * @param string $name Property name * @return boolean */ public function hasOwnProperty($name) { return false; } /** * Returns properties declared by this class, not its parents. * * @param integer $filter Properties filter * @return array */ public function getOwnProperties($filter = null) { return array(); } /** * Returns if the class imports the given property from traits. * * @param string $name Property name * @return boolean */ public function hasTraitProperty($name) { return false; } /** * Returns property reflections imported from traits. * * @param integer $filter Properties filter * @return array */ public function getTraitProperties($filter = null) { return array(); } /** * Returns static properties reflections. * * @return array */ public function getStaticProperties() { return array(); } /** * Returns a value of a static property. * * @param string $name Property name * @param mixed $default Default value * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. */ public function getStaticPropertyValue($name, $default = null) { throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns reflections of direct subclasses. * * @return array */ public function getDirectSubclasses() { return array(); } /** * Returns names of direct subclasses. * * @return array */ public function getDirectSubclassNames() { return array(); } /** * Returns reflections of indirect subclasses. * * @return array */ public function getIndirectSubclasses() { return array(); } /** * Returns names of indirect subclasses. * * @return array */ public function getIndirectSubclassNames() { return array(); } /** * Returns reflections of classes directly implementing this interface. * * @return array */ public function getDirectImplementers() { return array(); } /** * Returns names of classes directly implementing this interface. * * @return array */ public function getDirectImplementerNames() { return array(); } /** * Returns reflections of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementers() { return array(); } /** * Returns names of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementerNames() { return array(); } /** * Returns if the given object is an instance of this class. * * @param object $object Instance * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object. */ public function isInstance($object) { if (!is_object($object)) { throw new Exception\RuntimeException(sprintf('Parameter must be a class instance, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this); } return $this->name === get_class($object) || is_subclass_of($object, $this->name); } /** * Creates a new class instance without using a constructor. * * @return object * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. */ public function newInstanceWithoutConstructor() { if (!class_exists($this->name, true)) { throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $reflection = new \TokenReflection\Php\ReflectionClass($this->name, $this->getBroker()); return $reflection->newInstanceWithoutConstructor(); } /** * Creates a new instance using variable number of parameters. * * Use any number of constructor parameters as function parameters. * * @param mixed $args * @return object */ public function newInstance($args) { return $this->newInstanceArgs(func_get_args()); } /** * Creates a new instance using an array of parameters. * * @param array $args Array of constructor parameters * @return object * @throws \TokenReflection\Exception\RuntimeException If the required class does not exist. */ public function newInstanceArgs(array $args = array()) { if (!class_exists($this->name, true)) { throw new Exception\RuntimeException('Could not create an instance of class; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $reflection = new InternalReflectionClass($this->name); return $reflection->newInstanceArgs($args); } /** * Sets a static property value. * * @param string $name Property name * @param mixed $value Property value * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. */ public function setStaticPropertyValue($name, $value) { throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { return sprintf( "Class|Interface [ class|interface %s ] {\n %s%s%s%s%s\n}\n", $this->getName(), "\n\n - Constants [0] {\n }", "\n\n - Static properties [0] {\n }", "\n\n - Static methods [0] {\n }", "\n\n - Properties [0] {\n }", "\n\n - Methods [0] {\n }" ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string|object $className Class name or class instance * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $className, $return = false) { TokenReflection\ReflectionClass::export($broker, $className, $return); } /** * Outputs the reflection subject source code. * * @return string */ public function getSource() { return ''; } /** * Returns the start position in the file token stream. * * @return integer */ public function getStartPosition() { return -1; } /** * Returns the end position in the file token stream. * * @return integer */ public function getEndPosition() { return -1; } /** * Returns if the class definition is complete. * * Invalid classes are always complete. * * @return boolean */ public function isComplete() { return true; } /** * Returns if the class definition is valid. * * @return boolean */ public function isValid() { return false; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return ReflectionBase::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return ReflectionBase::exists($this, $key); } } TokenReflection/Invalid/ReflectionConstant.php000064400000014537144760110310015551 0ustar00name = $name; $this->broker = $broker; $this->fileName = $fileName; } /** * Returns the name. * * @return string */ public function getName() { return $this->name; } /** * Returns the unqualified name (UQN). * * @return string */ public function getShortName() { $pos = strrpos($this->name, '\\'); return false === $pos ? $this->name : substr($this->name, $pos + 1); } /** * Returns the declaring class reflection. * * @return null */ public function getDeclaringClass() { return null; } /** * Returns the declaring class name. * * @return null */ public function getDeclaringClassName() { return null; } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { $pos = strrpos($this->name, '\\'); return false === $pos ? '' : substr($this->name, 0, $pos); } /** * Returns if the function/method is defined within a namespace. * * @return boolean */ public function inNamespace() { return false !== strpos($this->name, '\\'); } /** * Returns the PHP extension reflection. * * @return null */ public function getExtension() { return null; } /** * Returns the PHP extension name. * * @return boolean */ public function getExtensionName() { return false; } /** * Returns the appropriate source code part. * * @return string */ public function getSource() { return ''; } /** * Returns the start position in the file token stream. * * @return integer */ public function getStartPosition() { return -1; } /** * Returns the end position in the file token stream. * * @return integer */ public function getEndPosition() { return -1; } /** * Returns the file name the reflection object is defined in. * * @return null */ public function getFileName() { return $this->fileName; } /** * Returns a file reflection. * * @return \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker */ public function getFileReflection() { throw new Exception\BrokerException($this->getBroker(), sprintf('Constant %s was not parsed from a file', $this->getPrettyName()), Exception\BrokerException::UNSUPPORTED); } /** * Returns the definition start line number in the file. * * @return null */ public function getStartLine() { return null; } /** * Returns the definition end line number in the file. * * @return null */ public function getEndLine() { return null; } /** * Returns the appropriate docblock definition. * * @return boolean */ public function getDocComment() { return false; } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns the constant value. * * @return mixed */ public function getValue() { return null; } /** * Returns the part of the source code defining the constant value. * * @return string */ public function getValueDefinition() { return null; } /** * Returns the originaly provided value definition. * * @return string */ public function getOriginalValueDefinition() { return null; } /** * Returns if the constant is internal. * * @return boolean */ public function isInternal() { return false; } /** * Returns if the constant is user defined. * * @return boolean */ public function isUserDefined() { return true; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return true; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->name; } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { return sprintf( "Constant [ %s %s ] { %s }\n", gettype(null), $this->getName(), null ); } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Returns if the constant definition is valid. * * @return boolean */ public function isValid() { return false; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return ReflectionBase::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return ReflectionBase::exists($this, $key); } } TokenReflection/Invalid/ReflectionElement.php000064400000001755144760110310015347 0ustar00reasons[] = $reason; return $this; } /** * Returns a list of reasons why this element's reflection is invalid. * * @return array */ public function getReasons() { return $this->reasons; } /** * Returns if there are any known reasons why this element's reflection is invalid. * * @return boolean */ public function hasReasons() { return !empty($this->reasons); } } TokenReflection/Invalid/ReflectionFunction.php000064400000020474144760110310015542 0ustar00name = ltrim($name, '\\'); $this->broker = $broker; $this->fileName = $fileName; } /** * Returns the name (FQN). * * @return string */ public function getName() { return $this->name; } /** * Returns the unqualified name (UQN). * * @return string */ public function getShortName() { $pos = strrpos($this->name, '\\'); return false === $pos ? $this->name : substr($this->name, $pos + 1); } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { $pos = strrpos($this->name, '\\'); return false === $pos ? '' : substr($this->name, 0, $pos); } /** * Returns if the class is defined within a namespace. * * @return boolean */ public function inNamespace() { return false !== strrpos($this->name, '\\'); } /** * Returns if the reflection object is internal. * * @return boolean */ public function isInternal() { return false; } /** * Returns if the reflection object is user defined. * * @return boolean */ public function isUserDefined() { return true; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return true; } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->name . '()'; } /** * Returns the PHP extension reflection. * * @return \TokenReflection\IReflectionExtension|null */ public function getExtension() { return null; } /** * Returns the PHP extension name. * * @return false */ public function getExtensionName() { return false; } /** * Returns the file name the reflection object is defined in. * * @return null */ public function getFileName() { return $this->fileName; } /** * Returns a file reflection. * * @return \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker */ public function getFileReflection() { throw new Exception\BrokerException($this->getBroker(), sprintf('Function was not parsed from a file', $this->getPrettyName()), Exception\BrokerException::UNSUPPORTED); } /** * Returns the appropriate source code part. * * @return string */ public function getSource() { return ''; } /** * Returns the start position in the file token stream. * * @return integer */ public function getStartPosition() { return -1; } /** * Returns the end position in the file token stream. * * @return integer */ public function getEndPosition() { return -1; } /** * Returns the definition start line number in the file. * * @return integer */ public function getStartLine() { return null; } /** * Returns the definition end line number in the file. * * @return integer */ public function getEndLine() { return null; } /** * Returns the appropriate docblock definition. * * @return boolean */ public function getDocComment() { return false; } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return string|array|null */ public function getAnnotation($name) { return null; } /** * Returns all annotations. * * @return array */ public function getAnnotations() { return array(); } /** * Returns if the function/method is a closure. * * @return boolean */ public function isClosure() { return false; } /** * Returns if the function/method is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns if the function/method returns its value as reference. * * @return boolean */ public function returnsReference() { return false; } /** * Returns a function/method parameter. * * @param integer|string $parameter Parameter name or position */ public function getParameter($parameter) { if (is_numeric($parameter)) { throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } else { throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } } /** * Returns function/method parameters. * * @return array */ public function getParameters(){ return array(); } /** * Returns the number of parameters. * * @return integer */ public function getNumberOfParameters() { return 0; } /** * Returns the number of required parameters. * * @return integer */ public function getNumberOfRequiredParameters() { return 0; } /** * Returns static variables. * * @return array */ public function getStaticVariables() { return array(); } /** * Returns if the method is is disabled via the disable_functions directive. * * @return boolean */ public function isDisabled() { return false; } /** * Calls the function. * * @return mixed */ public function invoke() { return $this->invokeArgs(array()); } /** * Calls the function. * * @param array $args Function parameter values * @return mixed */ public function invokeArgs(array $args) { throw new Exception\RuntimeException('Cannot invoke invalid functions', Exception\RuntimeException::UNSUPPORTED, $this); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Returns the function/method as closure. * * @return \Closure */ public function getClosure() { throw new Exception\RuntimeException('Cannot invoke invalid functions', Exception\RuntimeException::UNSUPPORTED, $this); } /** * Returns the closure scope class. * * @return null */ public function getClosureScopeClass() { return null; } /** * Returns this pointer bound to closure. * * @return null */ public function getClosureThis() { return null; } /** * Returns if the function definition is valid. * * @return boolean */ public function isValid() { return false; } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { return sprintf( "%sFunction [ function %s%s ] {\n @@ %s %d - %d\n}\n", $this->getDocComment() ? $this->getDocComment() . "\n" : '', $this->returnsReference() ? '&' : '', $this->getName(), $this->getFileName(), $this->getStartLine(), $this->getEndLine() ); } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return ReflectionBase::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return ReflectionBase::exists($this, $key); } } TokenReflection/Php/IReflection.php000064400000001460144760110310013300 0ustar00broker = $broker; } /** * Returns the PHP extension reflection. * * @return \TokenReflection\Php\ReflectionExtension */ public function getExtension() { return ReflectionExtension::create(parent::getExtension(), $this->broker); } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns if the class is an exception or its descendant. * * @return boolean */ public function isException() { return 'Exception' === $this->getName() || $this->isSubclassOf('Exception'); } /** * Returns if objects of this class are cloneable. * * Introduced in PHP 5.4. * * @return boolean * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 */ public function isCloneable() { if ($this->isInterface() || $this->isAbstract()) { return false; } $methods = $this->getMethods(); return isset($methods['__clone']) ? $methods['__clone']->isPublic() : true; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns if the current class is a subclass of the given class. * * @param string|object $class Class name or reflection object * @return boolean * @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided. */ public function isSubclassOf($class) { if (is_object($class)) { if (!$class instanceof InternalReflectionClass && !$class instanceof IReflectionClass) { throw new Exception\RuntimeException('Parameter must be a string or an instance of class reflection.', Exception\RuntimeException::INVALID_ARGUMENT, $this); } $class = $class->getName(); } return in_array($class, $this->getParentClassNameList()); } /** * Returns parent class reflection. * * @return \TokenReflection\Php\ReflectionClass */ public function getParentClass() { $parent = parent::getParentClass(); return $parent ? self::create($parent, $this->broker) : null; } /** * Returns the parent class name. * * @return string */ public function getParentClassName() { $parent = $this->getParentClass(); return $parent ? $parent->getName() : null; } /** * Returns the parent classes reflections. * * @return array */ public function getParentClasses() { $broker = $this->broker; return array_map(function($className) use ($broker) { return $broker->getClass($className); }, $this->getParentClassNameList()); } /** * Returns the parent classes names. * * @return array */ public function getParentClassNameList() { return class_parents($this->getName()); } /** * Returns if the class implements the given interface. * * @param string|object $interface Interface name or reflection object * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. */ public function implementsInterface($interface) { if (is_object($interface)) { if (!$interface instanceof InternalReflectionClass && !$interface instanceof IReflectionClass) { throw new Exception\RuntimeException('Parameter must be a string or an instance of class reflection.', Exception\RuntimeException::INVALID_ARGUMENT, $this); } $interfaceName = $interface->getName(); if (!$interface->isInterface()) { throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } } else { $reflection = $this->getBroker()->getClass($interface); if (!$reflection->isInterface()) { throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interface), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $interfaceName = $interface; } $interfaces = $this->getInterfaces(); return isset($interfaces[$interfaceName]); } /** * Returns an array of interface reflections. * * @return array */ public function getInterfaces() { if (null === $this->interfaces) { $broker = $this->broker; $interfaceNames = $this->getInterfaceNames(); if (empty($interfaceNames)) { $this->interfaces = array(); } else { $this->interfaces = array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) { return $broker->getClass($interfaceName); }, $interfaceNames)); } } return $this->interfaces; } /** * Returns interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaces() { $parent = $this->getParentClass(); return $parent ? array_diff_key($this->getInterfaces(), $parent->getInterfaces()) : $this->getInterfaces(); } /** * Returns names of interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaceNames() { return array_keys($this->getOwnInterfaces()); } /** * Returns class constructor reflection. * * @return \TokenReflection\Php\ReflectionClass|null */ public function getConstructor() { return ReflectionMethod::create(parent::getConstructor(), $this->broker); } /** * Returns class desctructor reflection. * * @return \TokenReflection\Php\ReflectionClass|null */ public function getDestructor() { foreach ($this->getMethods() as $method) { if ($method->isDestructor()) { return $method; } } return null; } /** * Returns a particular method reflection. * * @param string $name Method name * @return \TokenReflection\Php\ReflectionMethod * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. */ public function getMethod($name) { foreach ($this->getMethods() as $method) { if ($method->getName() === $name) { return $method; } } throw new Exception\RuntimeException(sprintf('Method %s does not exist.', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns class methods. * * @param integer $filter Methods filter * @return array */ public function getMethods($filter = null) { if (null === $this->methods) { $broker = $this->broker; $this->methods = array_map(function(InternalReflectionMethod $method) use ($broker) { return ReflectionMethod::create($method, $broker); }, parent::getMethods()); } if (null === $filter) { return $this->methods; } return array_filter($this->methods, function(ReflectionMethod $method) use ($filter) { return (bool) ($method->getModifiers() & $filter); }); } /** * Returns if the class implements (and not its parents) the given method. * * @param string $name Method name * @return boolean */ public function hasOwnMethod($name) { foreach ($this->getOwnMethods() as $method) { if ($name === $method->getName()) { return true; } } return false; } /** * Returns methods declared by this class, not its parents. * * @param integer $filter * @return array */ public function getOwnMethods($filter = null) { $me = $this->getName(); return array_filter($this->getMethods($filter), function(ReflectionMethod $method) use ($me) { return $method->getDeclaringClass()->getName() === $me; }); } /** * Returns if the class imports the given method from traits. * * @param string $name Method name * @return boolean * @todo Impossible with the current status of reflection */ public function hasTraitMethod($name) { return false; } /** * Returns method reflections imported from traits. * * @param integer $filter Methods filter * @return array * @todo Impossible with the current status of reflection */ public function getTraitMethods($filter = null) { return array(); } /** * Returns a constant reflection. * * @param string $name Constant name * @return \TokenReflection\ReflectionConstant * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. */ public function getConstantReflection($name) { if ($this->hasConstant($name)) { return new ReflectionConstant($name, $this->getConstant($name), $this->broker, $this); } throw new Exception\RuntimeException(sprintf('Constant "%s" does not exist.', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns an array of constant reflections. * * @return array */ public function getConstantReflections() { if (null === $this->constants) { $this->constants = array(); foreach ($this->getConstants() as $name => $value) { $this->constants[$name] = $this->getConstantReflection($name); } } return array_values($this->constants); } /** * Returns if the class (and not its parents) defines the given constant. * * @param string $name Constant name. * @return boolean */ public function hasOwnConstant($name) { $constants = $this->getOwnConstants(); return isset($constants[$name]); } /** * Returns constants declared by this class, not its parents. * * @return array */ public function getOwnConstants() { return array_diff_assoc($this->getConstants(), $this->getParentClass() ? $this->getParentClass()->getConstants() : array()); } /** * Returns an array of constant reflections defined by this class and not its parents. * * @return array */ public function getOwnConstantReflections() { $constants = array(); foreach ($this->getOwnConstants() as $name => $value) { $constants[] = $this->getConstantReflection($name); } return $constants; } /** * Returns a particular property reflection. * * @param string $name Property name * @return \TokenReflection\Php\ReflectionProperty * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. */ public function getProperty($name) { foreach ($this->getProperties() as $property) { if ($name === $property->getName()) { return $property; } } throw new Exception\RuntimeException(sprintf('Property %s does not exist.', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns class properties. * * @param integer $filter Properties filter * @return array */ public function getProperties($filter = null) { if (null === $this->properties) { $broker = $this->broker; $this->properties = array_map(function(InternalReflectionProperty $property) use ($broker) { return ReflectionProperty::create($property, $broker); }, parent::getProperties()); } if (null === $filter) { return $this->properties; } return array_filter($this->properties, function(ReflectionProperty $property) use ($filter) { return (bool) ($property->getModifiers() & $filter); }); } /** * Returns if the class has (and not its parents) the given property. * * @param string $name Property name * @return boolean */ public function hasOwnProperty($name) { foreach ($this->getOwnProperties() as $property) { if ($name === $property->getName()) { return true; } } return false; } /** * Returns properties declared by this class, not its parents. * * @param integer $filter * @return array */ public function getOwnProperties($filter = null) { $me = $this->getName(); return array_filter($this->getProperties($filter), function(ReflectionProperty $property) use ($me) { return $property->getDeclaringClass()->getName() === $me; }); } /** * Returns if the class imports the given property from traits. * * @param string $name Property name * @return boolean * @todo Impossible with the current status of reflection */ public function hasTraitProperty($name) { return false; } /** * Returns property reflections imported from traits. * * @param integer $filter Properties filter * @return array * @todo Impossible with the current status of reflection */ public function getTraitProperties($filter = null) { return array(); } /** * Returns static properties reflections. * * @return array */ public function getStaticProperties() { return $this->getProperties(InternalReflectionProperty::IS_STATIC); } /** * Returns reflections of direct subclasses. * * @return array */ public function getDirectSubclasses() { $that = $this->name; return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { if (!$class->isSubclassOf($that)) { return false; } return null === $class->getParentClassName() || !$class->getParentClass()->isSubClassOf($that); }); } /** * Returns names of direct subclasses. * * @return array */ public function getDirectSubclassNames() { return array_keys($this->getDirectSubclasses()); } /** * Returns reflections of indirect subclasses. * * @return array */ public function getIndirectSubclasses() { $that = $this->name; return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { if (!$class->isSubclassOf($that)) { return false; } return null !== $class->getParentClassName() && $class->getParentClass()->isSubClassOf($that); }); } /** * Returns names of indirect subclasses. * * @return array */ public function getIndirectSubclassNames() { return array_keys($this->getIndirectSubclasses()); } /** * Returns reflections of classes directly implementing this interface. * * @return array */ public function getDirectImplementers() { if (!$this->isInterface()) { return array(); } $that = $this->name; return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { if (!$class->implementsInterface($that)) { return false; } return null === $class->getParentClassName() || !$class->getParentClass()->implementsInterface($that); }); } /** * Returns names of classes directly implementing this interface. * * @return array */ public function getDirectImplementerNames() { return array_keys($this->getDirectImplementers()); } /** * Returns reflections of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementers() { if (!$this->isInterface()) { return array(); } $that = $this->name; return array_filter($this->getBroker()->getClasses(Broker\Backend::INTERNAL_CLASSES | Broker\Backend::TOKENIZED_CLASSES), function(IReflectionClass $class) use ($that) { if (!$class->implementsInterface($that)) { return false; } return null !== $class->getParentClassName() && $class->getParentClass()->implementsInterface($that); }); } /** * Returns names of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementerNames() { return array_keys($this->getIndirectImplementers()); } /** * Returns if the class definition is complete. * * Internal classes always have the definition complete. * * @return boolean */ public function isComplete() { return true; } /** * Returns if the class definition is valid. * * Internal classes are always valid. * * @return boolean */ public function isValid() { return true; } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return TokenReflection\ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return TokenReflection\ReflectionElement::exists($this, $key); } /** * Returns traits used by this class. * * @return array */ public function getTraits() { return NATIVE_TRAITS ? parent::getTraits() : array(); } /** * Returns traits used by this class and not its parents. * * @return array */ public function getOwnTraits() { if (!NATIVE_TRAITS) { return array(); } $parent = $this->getParentClass(); return $parent ? array_diff_key($this->getTraits(), $parent->getTraits()) : $this->getTraits(); } /** * Returns names of used traits. * * @return array */ public function getTraitNames() { return NATIVE_TRAITS ? parent::getTraitNames() : array(); } /** * Returns traits used by this class and not its parents. * * @return array */ public function getOwnTraitNames() { return array_keys($this->getOwnTraits()); } /** * Returns method aliases from traits. * * @return array */ public function getTraitAliases() { return NATIVE_TRAITS ? parent::getTraitAliases() : array(); } /** * Returns if the class is a trait. * * @return boolean */ public function isTrait() { return NATIVE_TRAITS && parent::isTrait(); } /** * Returns if the class uses a particular trait. * * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name * @return boolean * @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided. */ public function usesTrait($trait) { if (is_object($trait)) { if (!$trait instanceof InternalReflectionClass && !$trait instanceof TokenReflection\IReflectionClass) { throw new Exception\RuntimeException('Parameter must be a string or an instance of trait reflection.', Exception\RuntimeException::INVALID_ARGUMENT, $this); } $traitName = $trait->getName(); if (!$trait->isTrait()) { throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $traitName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } } else { $reflection = $this->getBroker()->getClass($trait); if (!$reflection->isTrait()) { throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $trait), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $traitName = $trait; } return in_array($traitName, $this->getTraitNames()); } /** * Creates a new class instance without using a constructor. * * @return object * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. */ public function newInstanceWithoutConstructor() { if ($this->isInternal()) { throw new Exception\RuntimeException('Could not create an instance; only user defined classes can be instantiated.', Exception\RuntimeException::UNSUPPORTED, $this); } foreach ($this->getParentClasses() as $parent) { if ($parent->isInternal()) { throw new Exception\RuntimeException('Could not create an instance; only user defined classes can be instantiated.', Exception\RuntimeException::UNSUPPORTED, $this); } } if (PHP_VERSION_ID >= 50400) { return parent::newInstanceWithoutConstructor(); } return unserialize(sprintf('O:%d:"%s":0:{}', strlen($this->getName()), $this->getName())); } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->getName(); } /** * Creates a reflection instance. * * @param \ReflectionClass $internalReflection Internal reflection instance * @param \TokenReflection\Broker $broker Reflection broker instance * @return \TokenReflection\Php\ReflectionClass * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. */ public static function create(Reflector $internalReflection, Broker $broker) { if (!$internalReflection instanceof InternalReflectionClass) { throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionClass expected.', Exception\RuntimeException::INVALID_ARGUMENT); } return $broker->getClass($internalReflection->getName()); } } TokenReflection/Php/ReflectionConstant.php000064400000022717144760110310014711 0ustar00name = $name; $this->value = $value; $this->broker = $broker; if (null !== $parent) { $realParent = null; if (array_key_exists($name, $parent->getOwnConstants())) { $realParent = $parent; } if (null === $realParent) { foreach ($parent->getParentClasses() as $grandParent) { if (array_key_exists($name, $grandParent->getOwnConstants())) { $realParent = $grandParent; break; } } } if (null === $realParent) { foreach ($parent->getInterfaces() as $interface) { if (array_key_exists($name, $interface->getOwnConstants())) { $realParent = $interface; break; } } } if (null === $realParent) { throw new Exception\RuntimeException('Could not determine constant real parent class.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $this->declaringClassName = $realParent->getName(); $this->userDefined = $realParent->isUserDefined(); } else { if (!array_key_exists($name, get_defined_constants(false))) { $this->userDefined = true; } else { $declared = get_defined_constants(true); $this->userDefined = array_key_exists($name, $declared['user']); } } } /** * Returns the name. * * @return string */ public function getName() { return $this->name; } /** * Returns the unqualified name (UQN). * * @return string */ public function getShortName() { $name = $this->getName(); if (null !== $this->namespaceName && $this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) { $name = substr($name, strlen($this->namespaceName) + 1); } return $name; } /** * Returns the declaring class reflection. * * @return \TokenReflection\IReflectionClass|null */ public function getDeclaringClass() { if (null === $this->declaringClassName) { return null; } return $this->getBroker()->getClass($this->declaringClassName); } /** * Returns the declaring class name. * * @return string|null */ public function getDeclaringClassName() { return $this->declaringClassName; } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { return $this->namespaceName === TokenReflection\ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; } /** * Returns if the function/method is defined within a namespace. * * @return boolean */ public function inNamespace() { return '' !== $this->getNamespaceName(); } /** * Returns the PHP extension reflection. * * @return null */ public function getExtension() { // @todo return null; } /** * Returns the PHP extension name. * * @return boolean */ public function getExtensionName() { return false; } /** * Returns the file name the reflection object is defined in. * * @return null */ public function getFileName() { return null; } /** * Returns the definition start line number in the file. * * @return null */ public function getStartLine() { return null; } /** * Returns the definition end line number in the file. * * @return null */ public function getEndLine() { return null; } /** * Returns the appropriate docblock definition. * * @return boolean */ public function getDocComment() { return false; } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns the constant value. * * @return mixed */ public function getValue() { return $this->value; } /** * Returns the part of the source code defining the constant value. * * @return string */ public function getValueDefinition() { return var_export($this->value, true); } /** * Returns the originaly provided value definition. * * @return string */ public function getOriginalValueDefinition() { return token_get_all($this->getValueDefinition()); } /** * Returns if the constant is internal. * * @return boolean */ public function isInternal() { return !$this->userDefined; } /** * Returns if the constant is user defined. * * @return boolean */ public function isUserDefined() { return $this->userDefined; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return null === $this->declaringClassName ? $this->name : sprintf('%s::%s', $this->declaringClassName, $this->name); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { return sprintf( "Constant [ %s %s ] { %s }\n", gettype($this->getValue()), $this->getName(), $this->getValue() ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string|object|null $class Class name, class instance or null * @param string $constant Constant name * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $class, $constant, $return = false) { $className = is_object($class) ? get_class($class) : $class; $constantName = $constant; if (null === $className) { try { $constant = $broker->getConstant($constantName); } catch (Exception\BrokerException $e) { throw new Exception\RuntimeException(sprintf('Constant %s does not exist.', $constantName), Exception\RuntimeException::DOES_NOT_EXIST); } } else { $class = $broker->getClass($className); if ($class instanceof Invalid\ReflectionClass) { throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); } elseif ($class instanceof Dummy\ReflectionClass) { throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST); } $constant = $class->getConstantReflection($constantName); } if ($return) { return $constant->__toString(); } echo $constant->__toString(); } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Returns if the constant definition is valid. * * Internal constants are always valid. * * @return boolean */ public function isValid() { return true; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return TokenReflection\ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return TokenReflection\ReflectionElement::exists($this, $key); } /** * Creates a reflection instance. * * Not supported for constants since there is no internal constant reflection. * * @param \ReflectionClass $internalReflection Internal reflection instance * @param \TokenReflection\Broker $broker Reflection broker instance * @return null */ public static function create(Reflector $internalReflection, Broker $broker) { return null; } } TokenReflection/Php/ReflectionExtension.php000064400000013275144760110310015073 0ustar00broker = $broker; } /** * Returns if the constant is internal. * * @return boolean */ public function isInternal() { return true; } /** * Returns if the constant is user defined. * * @return boolean */ public function isUserDefined() { return false; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns a class reflection. * * @param string $name Class name * @return \TokenReflection\IReflectionClass|null */ public function getClass($name) { $classes = $this->getClasses(); return isset($classes[$name]) ? $classes[$name] : null; } /** * Returns classes defined by this extension. * * @return array */ public function getClasses() { if (null === $this->classes) { $broker = $this->broker; $this->classes = array_map(function($className) use ($broker) { return $broker->getClass($className); }, $this->getClassNames()); } return $this->classes; } /** * Returns a constant value. * * @param string $name Constant name * @return mixed|false */ public function getConstant($name) { $constants = $this->getConstants(); return isset($constants[$name]) ? $constants[$name] : false; } /** * Returns a constant reflection. * * @param string $name Constant name * @return \TokenReflection\IReflectionConstant */ public function getConstantReflection($name) { $constants = $this->getConstantReflections(); return isset($constants[$name]) ? $constants[$name] : null; } /** * Returns reflections of defined constants. * * @return array */ public function getConstantReflections() { if (null === $this->constants) { $broker = $this->broker; $this->constants = array_map(function($constantName) use ($broker) { return $broker->getConstant($constantName); }, array_keys($this->getConstants())); } return $this->constants; } /** * Returns a function reflection. * * @param string $name Function name * @return \TokenReflection\IReflectionFunction */ public function getFunction($name) { $functions = $this->getFunctions(); return isset($functions[$name]) ? $functions[$name] : null; } /** * Returns functions defined by this extension. * * @return array */ public function getFunctions() { if (null === $this->functions) { $broker = $this->broker; $this->classes = array_map(function($functionName) use ($broker) { return $broker->getFunction($functionName); }, array_keys(parent::getFunctions())); } return $this->functions; } /** * Returns names of functions defined by this extension. * * @return array */ public function getFunctionNames() { return array_keys($this->getFunctions()); } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->getName(); } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return TokenReflection\ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return TokenReflection\ReflectionElement::exists($this, $key); } /** * Creates a reflection instance. * * @param \ReflectionClass $internalReflection Internal reflection instance * @param \TokenReflection\Broker $broker Reflection broker instance * @return \TokenReflection\Php\ReflectionExtension * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. */ public static function create(Reflector $internalReflection, Broker $broker) { static $cache = array(); if (!$internalReflection instanceof InternalReflectionExtension) { throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionExtension expected.', Exception\RuntimeException::INVALID_ARGUMENT); } if (!isset($cache[$internalReflection->getName()])) { $cache[$internalReflection->getName()] = new self($internalReflection->getName(), $broker); } return $cache[$internalReflection->getName()]; } } TokenReflection/Php/ReflectionFunction.php000064400000013536144760110310014704 0ustar00broker = $broker; } /** * Returns the PHP extension reflection. * * @return \TokenReflection\IReflectionExtension */ public function getExtension() { return ReflectionExtension::create(parent::getExtension(), $this->broker); } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns a particular parameter. * * @param integer|string $parameter Parameter name or position * @return \TokenReflection\Php\ReflectionParameter * @throws \TokenReflection\Exception\RuntimeException If there is no parameter of the given name. * @throws \TokenReflection\Exception\RuntimeException If there is no parameter at the given position. */ public function getParameter($parameter) { $parameters = $this->getParameters(); if (is_numeric($parameter)) { if (!isset($parameters[$parameter])) { throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } return $parameters[$parameter]; } else { foreach ($parameters as $reflection) { if ($reflection->getName() === $parameter) { return $reflection; } } throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } } /** * Returns function parameters. * * @return array */ public function getParameters() { if (null === $this->parameters) { $broker = $this->broker; $parent = $this; $this->parameters = array_map(function(InternalReflectionParameter $parameter) use ($broker, $parent) { return ReflectionParameter::create($parameter, $broker, $parent); }, parent::getParameters()); } return $this->parameters; } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return TokenReflection\ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return TokenReflection\ReflectionElement::exists($this, $key); } /** * Returns the function/method as closure. * * @return \Closure */ public function getClosure() { if (PHP_VERSION >= 50400) { return parent::getClosure(); } else { $that = $this; return function() use ($that) { return $that->invokeArgs(func_get_args()); }; } } /** * Returns the closure scope class. * * @return string|null */ public function getClosureScopeClass() { return PHP_VERSION >= 50400 ? parent::getClosureScopeClass() : null; } /** * Returns this pointer bound to closure. * * @return null */ public function getClosureThis() { return PHP_VERSION >= 50400 ? parent::getClosureThis() : null; } /** * Returns if the function definition is valid. * * Internal functions are always valid. * * @return boolean */ public function isValid() { return true; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->getName() . '()'; } /** * Creates a reflection instance. * * @param \ReflectionClass $internalReflection Internal reflection instance * @param \TokenReflection\Broker $broker Reflection broker instance * @return \TokenReflection\Php\ReflectionFunction * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. */ public static function create(Reflector $internalReflection, Broker $broker) { if (!$internalReflection instanceof InternalReflectionFunction) { throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionFunction expected.', Exception\RuntimeException::INVALID_ARGUMENT); } return $broker->getFunction($internalReflection->getName()); } }TokenReflection/Php/ReflectionMethod.php000064400000020534144760110310014333 0ustar00broker = $broker; } /** * Returns the declaring class reflection. * * @return \TokenReflection\IReflectionClass */ public function getDeclaringClass() { return ReflectionClass::create(parent::getDeclaringClass(), $this->broker); } /** * Returns the declaring class name. * * @return string */ public function getDeclaringClassName() { return $this->getDeclaringClass()->getName(); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return $this->getDeclaringClass()->getNamespaceAliases(); } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns the method prototype. * * @return \TokenReflection\Php\ReflectionMethod */ public function getPrototype() { return self::create(parent::getPrototype(), $this->broker); } /** * Returns a particular parameter. * * @param integer|string $parameter Parameter name or position * @return \TokenReflection\Php\ReflectionParameter * @throws \TokenReflection\Exception\RuntimeException If there is no parameter of the given name. * @throws \TokenReflection\Exception\RuntimeException If there is no parameter at the given position. */ public function getParameter($parameter) { $parameters = $this->getParameters(); if (is_numeric($parameter)) { if (!isset($parameters[$parameter])) { throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } return $parameters[$parameter]; } else { foreach ($parameters as $reflection) { if ($reflection->getName() === $parameter) { return $reflection; } } throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } } /** * Returns function parameters. * * @return array */ public function getParameters() { if (null === $this->parameters) { $broker = $this->broker; $parent = $this; $this->parameters = array_map(function(InternalReflectionParameter $parameter) use ($broker, $parent) { return ReflectionParameter::create($parameter, $broker, $parent); }, parent::getParameters()); } return $this->parameters; } /** * Returns if the method is set accessible. * * @return boolean */ public function isAccessible() { return $this->accessible; } /** * Sets a method to be accessible or not. * * Introduced in PHP 5.3.2. Throws an exception if run on an older version. * * @param boolean $accessible * @throws \TokenReflection\Exception\RuntimeException If run on PHP version < 5.3.2. */ public function setAccessible($accessible) { if (PHP_VERSION_ID < 50302) { throw new Exception\RuntimeException(sprintf('Method setAccessible was introduced the internal reflection in PHP 5.3.2, you are using %s.', PHP_VERSION), Exception\RuntimeException::UNSUPPORTED, $this); } $this->accessible = $accessible; parent::setAccessible($accessible); } /** * Shortcut for isPublic(), ... methods that allows or-ed modifiers. * * @param integer $filter Filter * @return boolean */ public function is($filter = null) { return null === $filter || ($this->getModifiers() & $filter); } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return TokenReflection\ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return TokenReflection\ReflectionElement::exists($this, $key); } /** * Returns the function/method as closure. * * @param object $object Object * @return \Closure */ public function getClosure($object) { if (PHP_VERSION >= 50400) { return parent::getClosure(); } else { $that = $this; return function() use ($object, $that) { return $that->invokeArgs($object, func_get_args()); }; } } /** * Returns the closure scope class. * * @return string|null */ public function getClosureScopeClass() { return PHP_VERSION >= 50400 ? parent::getClosureScopeClass() : null; } /** * Returns this pointer bound to closure. * * @return null */ public function getClosureThis() { return PHP_VERSION >= 50400 ? parent::getClosureThis() : null; } /** * Returns the original name when importing from a trait. * * @return string */ public function getOriginalName() { return null; } /** * Returns the original method when importing from a trait. * * @return null */ public function getOriginal() { return null; } /** * Returns the original modifiers value when importing from a trait. * * @return null */ public function getOriginalModifiers() { return null; } /** * Returns the defining trait. * * @return \TokenReflection\IReflectionClass|null */ public function getDeclaringTrait() { return null; } /** * Returns the declaring trait name. * * @return string|null */ public function getDeclaringTraitName() { return null; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return sprintf('%s::%s()', $this->getDeclaringClassName(), $this->getName()); } /** * Creates a reflection instance. * * @param \ReflectionClass $internalReflection Internal reflection instance * @param \TokenReflection\Broker $broker Reflection broker instance * @return \TokenReflection\Php\IReflection * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. */ public static function create(Reflector $internalReflection, Broker $broker) { static $cache = array(); if (!$internalReflection instanceof InternalReflectionMethod) { throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionMethod expected.', Exception\RuntimeException::INVALID_ARGUMENT); } $key = $internalReflection->getDeclaringClass()->getName() . '::' . $internalReflection->getName(); if (!isset($cache[$key])) { $cache[$key] = new self($internalReflection->getDeclaringClass()->getName(), $internalReflection->getName(), $broker); } return $cache[$key]; } } TokenReflection/Php/ReflectionParameter.php000064400000017477144760110310015047 0ustar00broker = $broker; $this->userDefined = $parent->isUserDefined(); } /** * Returns the declaring class reflection. * * @return \TokenReflection\IReflectionClass */ public function getDeclaringClass() { $class = parent::getDeclaringClass(); return $class ? ReflectionClass::create($class, $this->broker) : null; } /** * Returns the declaring class name. * * @return string|null */ public function getDeclaringClassName() { $class = parent::getDeclaringClass(); return $class ? $class->getName() : null; } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return $this->getDeclaringFunction()->getNamespaceAliases(); } /** * Returns the file name the reflection object is defined in. * * @return string */ public function getFileName() { return $this->getDeclaringFunction()->getFileName(); } /** * Returns the PHP extension reflection. * * @return \TokenReflection\Php\ReflectionExtension */ public function getExtension() { return $this->getDeclaringFunction()->getExtension(); } /** * Returns the PHP extension name. * * @return string|boolean */ public function getExtensionName() { $extension = $this->getExtension(); return $extension ? $extension->getName() : false; } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns the declaring function reflection. * * @return \TokenReflection\Php\ReflectionFunction|\TokenReflection\Php\ReflectionMethod */ public function getDeclaringFunction() { $class = $this->getDeclaringClass(); $function = parent::getDeclaringFunction(); return $class ? $class->getMethod($function->getName()) : ReflectionFunction::create($function, $this->broker); } /** * Returns the declaring function name. * * @return string|null */ public function getDeclaringFunctionName() { $function = parent::getDeclaringFunction(); return $function ? $function->getName() : $function; } /** * Returns the definition start line number in the file. * * @return null */ public function getStartLine() { return null; } /** * Returns the definition end line number in the file. * * @return null */ public function getEndLine() { return null; } /** * Returns the appropriate docblock definition. * * @return boolean */ public function getDocComment() { return false; } /** * Returns the part of the source code defining the paramter default value. * * @return string|null */ public function getDefaultValueDefinition() { $value = $this->getDefaultValue(); return null === $value ? null : var_export($value, true); } /** * Returns if the parameter expects a callback. * * @return boolean */ public function isCallable() { return PHP_VERSION >= 50400 && parent::isCallable(); } /** * Returns the original type hint as defined in the source code. * * @return string|null */ public function getOriginalTypeHint() { return !$this->isArray() && !$this->isCallable() ? $this->getClass() : null; } /** * Returns the required class name of the value. * * @return string|null */ public function getClassName() { return $this->getClass() ? $this->getClass()->getName() : null; } /** * Returns if the parameter is internal. * * @return boolean */ public function isInternal() { return !$this->userDefined; } /** * Returns if the parameter is user defined. * * @return boolean */ public function isUserDefined() { return $this->userDefined; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns if the paramter value can be passed by value. * * @return boolean */ public function canBePassedByValue() { return method_exists($this, 'canBePassedByValue') ? parent::canBePassedByValue() : !$this->isPassedByReference(); } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return str_replace('()', '($' . $this->getName() . ')', $this->getDeclaringFunction()->getPrettyName()); } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return TokenReflection\ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return TokenReflection\ReflectionElement::exists($this, $key); } /** * Creates a reflection instance. * * @param \ReflectionClass $internalReflection Internal reflection instance * @param \TokenReflection\Broker $broker Reflection broker instance * @return \TokenReflection\Php\ReflectionParameter * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. */ public static function create(Reflector $internalReflection, Broker $broker) { static $cache = array(); if (!$internalReflection instanceof InternalReflectionParameter) { throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionParameter expected.', Exception\RuntimeException::INVALID_ARGUMENT); } $class = $internalReflection->getDeclaringClass(); $function = $internalReflection->getDeclaringFunction(); $key = $class ? $class->getName() . '::' : ''; $key .= $function->getName() . '(' . $internalReflection->getName() . ')'; if (!isset($cache[$key])) { $cache[$key] = new self($class ? array($class->getName(), $function->getName()) : $function->getName(), $internalReflection->getName(), $broker, $function); } return $cache[$key]; } } TokenReflection/Php/ReflectionProperty.php000064400000015402144760110310014735 0ustar00broker = $broker; } /** * Returns the declaring class reflection. * * @return \TokenReflection\IReflectionClass */ public function getDeclaringClass() { return ReflectionClass::create(parent::getDeclaringClass(), $this->broker); } /** * Returns the declaring class name. * * @return string */ public function getDeclaringClassName() { return $this->getDeclaringClass()->getName(); } /** * Returns the definition start line number in the file. * * @return null */ public function getStartLine() { return null; } /** * Returns the definition end line number in the file. * * @return null */ public function getEndLine() { return null; } /** * Returns the appropriate docblock definition. * * @return boolean */ public function getDocComment() { return false; } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ public function hasAnnotation($name) { return false; } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return null */ public function getAnnotation($name) { return null; } /** * Returns parsed docblock. * * @return array */ public function getAnnotations() { return array(); } /** * Returns the property default value. * * @return mixed */ public function getDefaultValue() { $values = $this->getDeclaringClass()->getDefaultProperties(); return $values[$this->getName()]; } /** * Returns the part of the source code defining the property default value. * * @return string */ public function getDefaultValueDefinition() { $value = $this->getDefaultValue(); return null === $value ? null : var_export($value, true); } /** * Returns if the property is internal. * * @return boolean */ public function isInternal() { return $this->getDeclaringClass()->isInternal(); } /** * Returns if the property is user defined. * * @return boolean */ public function isUserDefined() { return $this->getDeclaringClass()->isUserDefined(); } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return false; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return false; } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return array(); } /** * Returns the defining trait. * * @return \TokenReflection\IReflectionClass|null */ public function getDeclaringTrait() { return null; } /** * Returns the declaring trait name. * * @return string|null */ public function getDeclaringTraitName() { return null; } /** * Returns if the property is set accessible. * * @return boolean */ public function isAccessible() { return $this->accessible; } /** * Sets a property to be accessible or not. * * @param boolean $accessible If the property should be accessible. */ public function setAccessible($accessible) { $this->accessible = (bool) $accessible; parent::setAccessible($accessible); } /** * Returns the PHP extension reflection. * * @return \TokenReflection\Php\ReflectionExtension */ public function getExtension() { return $this->getDeclaringClass()->getExtension(); } /** * Returns the PHP extension name. * * @return string|boolean */ public function getExtensionName() { $extension = $this->getExtension(); return $extension ? $extension->getName() : false; } /** * Returns the file name the reflection object is defined in. * * @return string */ public function getFileName() { return $this->getDeclaringClass()->getFileName(); } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return sprintf('%s::$%s', $this->getDeclaringClassName(), $this->getName()); } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return TokenReflection\ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return TokenReflection\ReflectionElement::exists($this, $key); } /** * Creates a reflection instance. * * @param \ReflectionClass $internalReflection Internal reflection instance * @param \TokenReflection\Broker $broker Reflection broker instance * @return \TokenReflection\Php\ReflectionProperty * @throws \TokenReflection\Exception\RuntimeException If an invalid internal reflection object was provided. */ public static function create(Reflector $internalReflection, Broker $broker) { static $cache = array(); if (!$internalReflection instanceof InternalReflectionProperty) { throw new Exception\RuntimeException('Invalid reflection instance provided, ReflectionProperty expected.', Exception\RuntimeException::INVALID_ARGUMENT); } $key = $internalReflection->getDeclaringClass()->getName() . '::' . $internalReflection->getName(); if (!isset($cache[$key])) { $cache[$key] = new self($internalReflection->getDeclaringClass()->getName(), $internalReflection->getName(), $broker); } return $cache[$key]; } } TokenReflection/ReflectionAnnotation.php000064400000032531144760110310014476 0ustar00reflection = $reflection; $this->docComment = $docComment ?: false; } /** * Returns the docblock. * * @return string|boolean */ public function getDocComment() { return $this->docComment; } /** * Returns if the current docblock contains the requrested annotation. * * @param string $annotation Annotation name * @return boolean */ public function hasAnnotation($annotation) { if (null === $this->annotations) { $this->parse(); } return isset($this->annotations[$annotation]); } /** * Returns a particular annotation value. * * @param string $annotation Annotation name * @return string|array|null */ public function getAnnotation($annotation) { if (null === $this->annotations) { $this->parse(); } return isset($this->annotations[$annotation]) ? $this->annotations[$annotation] : null; } /** * Returns all parsed annotations. * * @return array */ public function getAnnotations() { if (null === $this->annotations) { $this->parse(); } return $this->annotations; } /** * Sets Docblock templates. * * @param array $templates Docblock templates * @return \TokenReflection\ReflectionAnnotation * @throws \TokenReflection\Exception\RuntimeException If an invalid annotation template was provided. */ public function setTemplates(array $templates) { foreach ($templates as $template) { if (!$template instanceof ReflectionAnnotation) { throw new Exception\RuntimeException( sprintf( 'All templates have to be instances of \\TokenReflection\\ReflectionAnnotation; %s given.', is_object($template) ? get_class($template) : gettype($template) ), Exception\RuntimeException::INVALID_ARGUMENT, $this->reflection ); } } $this->templates = $templates; return $this; } /** * Parses reflection object documentation. */ private function parse() { $this->annotations = array(); if (false !== $this->docComment) { // Parse docblock $name = self::SHORT_DESCRIPTION; $docblock = trim( preg_replace( array( '~^' . preg_quote(ReflectionElement::DOCBLOCK_TEMPLATE_START, '~') . '~', '~^' . preg_quote(ReflectionElement::DOCBLOCK_TEMPLATE_END, '~') . '$~', '~^/\\*\\*~', '~\\*/$~' ), '', $this->docComment ) ); foreach (explode("\n", $docblock) as $line) { $line = preg_replace('~^\\*\\s?~', '', trim($line)); // End of short description if ('' === $line && self::SHORT_DESCRIPTION === $name) { $name = self::LONG_DESCRIPTION; continue; } // @annotation if (preg_match('~^\\s*@([\\S]+)\\s*(.*)~', $line, $matches)) { $name = $matches[1]; $this->annotations[$name][] = $matches[2]; continue; } // Continuation if (self::SHORT_DESCRIPTION === $name || self::LONG_DESCRIPTION === $name) { if (!isset($this->annotations[$name])) { $this->annotations[$name] = $line; } else { $this->annotations[$name] .= "\n" . $line; } } else { $this->annotations[$name][count($this->annotations[$name]) - 1] .= "\n" . $line; } } array_walk_recursive($this->annotations, function(&$value) { // {@*} is a placeholder for */ (phpDocumentor compatibility) $value = str_replace('{@*}', '*/', $value); $value = trim($value); }); } if ($this->reflection instanceof ReflectionElement) { // Merge docblock templates $this->mergeTemplates(); // Copy annotations if the @copydoc tag is present. if (!empty($this->annotations['copydoc'])) { $this->copyAnnotation(); } // Process docblock inheritance for supported reflections if ($this->reflection instanceof ReflectionClass || $this->reflection instanceof ReflectionMethod || $this->reflection instanceof ReflectionProperty) { $this->inheritAnnotations(); } } } /** * Copies annotations if the @copydoc tag is present. * * @throws \TokenReflection\Exception\RuntimeException When stuck in an infinite loop when resolving the @copydoc tag. */ private function copyAnnotation() { self::$copydocStack[] = $this->reflection; $broker = $this->reflection->getBroker(); $parentNames = $this->annotations['copydoc']; unset($this->annotations['copydoc']); foreach ($parentNames as $parentName) { try { if ($this->reflection instanceof ReflectionClass) { $parent = $broker->getClass($parentName); if ($parent instanceof Dummy\ReflectionClass) { // The class to copy from is not usable return; } } elseif ($this->reflection instanceof ReflectionFunction) { $parent = $broker->getFunction(rtrim($parentName, '()')); } elseif ($this->reflection instanceof ReflectionConstant && null === $this->reflection->getDeclaringClassName()) { $parent = $broker->getConstant($parentName); } elseif ($this->reflection instanceof ReflectionMethod || $this->reflection instanceof ReflectionProperty || $this->reflection instanceof ReflectionConstant) { if (false !== strpos($parentName, '::')) { list($className, $parentName) = explode('::', $parentName, 2); $class = $broker->getClass($className); } else { $class = $this->reflection->getDeclaringClass(); } if ($class instanceof Dummy\ReflectionClass) { // The source element class is not usable return; } if ($this->reflection instanceof ReflectionMethod) { $parent = $class->getMethod(rtrim($parentName, '()')); } elseif ($this->reflection instanceof ReflectionConstant) { $parent = $class->getConstantReflection($parentName); } else { $parent = $class->getProperty(ltrim($parentName, '$')); } } if (!empty($parent)) { // Don't get into an infinite recursion loop if (in_array($parent, self::$copydocStack, true)) { throw new Exception\RuntimeException('Infinite loop detected when copying annotations using the @copydoc tag.', Exception\RuntimeException::INVALID_ARGUMENT, $this->reflection); } self::$copydocStack[] = $parent; // We can get into an infinite loop here (e.g. when two methods @copydoc from each other) foreach ($parent->getAnnotations() as $name => $value) { // Add annotations that are not already present if (empty($this->annotations[$name])) { $this->annotations[$name] = $value; } } array_pop(self::$copydocStack); } } catch (Exception\BaseException $e) { // Ignoring links to non existent elements, ... } } array_pop(self::$copydocStack); } /** * Merges templates with the current docblock. */ private function mergeTemplates() { foreach ($this->templates as $index => $template) { if (0 === $index && $template->getDocComment() === $this->docComment) { continue; } foreach ($template->getAnnotations() as $name => $value) { if ($name === self::LONG_DESCRIPTION) { // Long description if (isset($this->annotations[self::LONG_DESCRIPTION])) { $this->annotations[self::LONG_DESCRIPTION] = $value . "\n" . $this->annotations[self::LONG_DESCRIPTION]; } else { $this->annotations[self::LONG_DESCRIPTION] = $value; } } elseif ($name !== self::SHORT_DESCRIPTION) { // Tags; short description is not inherited if (isset($this->annotations[$name])) { $this->annotations[$name] = array_merge($this->annotations[$name], $value); } else { $this->annotations[$name] = $value; } } } } } /** * Inherits annotations from parent classes/methods/properties if needed. * * @throws \TokenReflection\Exception\RuntimeException If unsupported reflection was used. */ private function inheritAnnotations() { if ($this->reflection instanceof ReflectionClass) { $declaringClass = $this->reflection; } elseif ($this->reflection instanceof ReflectionMethod || $this->reflection instanceof ReflectionProperty) { $declaringClass = $this->reflection->getDeclaringClass(); } $parents = array_filter(array_merge(array($declaringClass->getParentClass()), $declaringClass->getOwnInterfaces()), function($class) { return $class instanceof ReflectionClass; }); // In case of properties and methods, look for a property/method of the same name and return // and array of such members. $parentDefinitions = array(); if ($this->reflection instanceof ReflectionProperty) { $name = $this->reflection->getName(); foreach ($parents as $parent) { if ($parent->hasProperty($name)) { $parentDefinitions[] = $parent->getProperty($name); } } $parents = $parentDefinitions; } elseif ($this->reflection instanceof ReflectionMethod) { $name = $this->reflection->getName(); foreach ($parents as $parent) { if ($parent->hasMethod($name)) { $parentDefinitions[] = $parent->getMethod($name); } } $parents = $parentDefinitions; } if (false === $this->docComment) { // Inherit the entire docblock foreach ($parents as $parent) { $annotations = $parent->getAnnotations(); if (!empty($annotations)) { $this->annotations = $annotations; break; } } } else { if (isset($this->annotations[self::LONG_DESCRIPTION]) && false !== stripos($this->annotations[self::LONG_DESCRIPTION], '{@inheritdoc}')) { // Inherit long description foreach ($parents as $parent) { if ($parent->hasAnnotation(self::LONG_DESCRIPTION)) { $this->annotations[self::LONG_DESCRIPTION] = str_ireplace( '{@inheritdoc}', $parent->getAnnotation(self::LONG_DESCRIPTION), $this->annotations[self::LONG_DESCRIPTION] ); break; } } $this->annotations[self::LONG_DESCRIPTION] = str_ireplace('{@inheritdoc}', '', $this->annotations[self::LONG_DESCRIPTION]); } if (isset($this->annotations[self::SHORT_DESCRIPTION]) && false !== stripos($this->annotations[self::SHORT_DESCRIPTION], '{@inheritdoc}')) { // Inherit short description foreach ($parents as $parent) { if ($parent->hasAnnotation(self::SHORT_DESCRIPTION)) { $this->annotations[self::SHORT_DESCRIPTION] = str_ireplace( '{@inheritdoc}', $parent->getAnnotation(self::SHORT_DESCRIPTION), $this->annotations[self::SHORT_DESCRIPTION] ); break; } } $this->annotations[self::SHORT_DESCRIPTION] = str_ireplace('{@inheritdoc}', '', $this->annotations[self::SHORT_DESCRIPTION]); } } // In case of properties check if we need and can inherit the data type if ($this->reflection instanceof ReflectionProperty && empty($this->annotations['var'])) { foreach ($parents as $parent) { if ($parent->hasAnnotation('var')) { $this->annotations['var'] = $parent->getAnnotation('var'); break; } } } if ($this->reflection instanceof ReflectionMethod) { if (0 !== $this->reflection->getNumberOfParameters() && (empty($this->annotations['param']) || count($this->annotations['param']) < $this->reflection->getNumberOfParameters())) { // In case of methods check if we need and can inherit parameter descriptions $params = isset($this->annotations['param']) ? $this->annotations['param'] : array(); $complete = false; foreach ($parents as $parent) { if ($parent->hasAnnotation('param')) { $parentParams = array_slice($parent->getAnnotation('param'), count($params)); while (!empty($parentParams) && !$complete) { array_push($params, array_shift($parentParams)); if (count($params) === $this->reflection->getNumberOfParameters()) { $complete = true; } } } if ($complete) { break; } } if (!empty($params)) { $this->annotations['param'] = $params; } } // And check if we need and can inherit the return and throws value foreach (array('return', 'throws') as $paramName) { if (!isset($this->annotations[$paramName])) { foreach ($parents as $parent) { if ($parent->hasAnnotation($paramName)) { $this->annotations[$paramName] = $parent->getAnnotation($paramName); break; } } } } } } } TokenReflection/ReflectionBase.php000064400000012346144760110310013240 0ustar00broker = $broker; $this->parseStream($tokenStream, $parent); } /** * Parses the token substream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object */ abstract protected function parseStream(Stream $tokenStream, IReflection $parent = null); /** * Returns the name (FQN). * * @return string */ public function getName() { return $this->name; } /** * Returns the appropriate docblock definition. * * @return string|boolean */ public function getDocComment() { return $this->docComment->getDocComment(); } /** * Checks if there is a particular annotation. * * @param string $name Annotation name * @return boolean */ final public function hasAnnotation($name) { return $this->docComment->hasAnnotation($name); } /** * Returns a particular annotation value. * * @param string $name Annotation name * @return string|array|null */ final public function getAnnotation($name) { return $this->docComment->getAnnotation($name); } /** * Returns all annotations. * * @return array */ final public function getAnnotations() { return $this->docComment->getAnnotations(); } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker */ public function getBroker() { return $this->broker; } /** * Returns if the reflection object is internal. * * Always returns false - everything is user defined. * * @return boolean */ public function isInternal() { return false; } /** * Returns if the reflection object is user defined. * * Always returns true - everything is user defined. * * @return boolean */ public function isUserDefined() { return true; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return true; } /** * Returns if the reflection subject is deprecated. * * @return boolean */ public function isDeprecated() { return $this->hasAnnotation('deprecated'); } /** * Returns the appropriate source code part. * * @return string */ abstract public function getSource(); /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->name; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return self::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return self::exists($this, $key); } /** * Magic __get method helper. * * @param \TokenReflection\IReflection $object Reflection object * @param string $key Variable name * @return mixed * @throws \TokenReflection\Exception\RuntimeException If the requested parameter does not exist. */ final public static function get(IReflection $object, $key) { if (!empty($key)) { $className = get_class($object); if (!isset(self::$methodCache[$className])) { self::$methodCache[$className] = array_flip(get_class_methods($className)); } $methods = self::$methodCache[$className]; $key2 = ucfirst($key); if (isset($methods['get' . $key2])) { return $object->{'get' . $key2}(); } elseif (isset($methods['is' . $key2])) { return $object->{'is' . $key2}(); } } throw new Exception\RuntimeException(sprintf('Cannot read property "%s".', $key), Exception\RuntimeException::DOES_NOT_EXIST); } /** * Magic __isset method helper. * * @param \TokenReflection\IReflection $object Reflection object * @param string $key Variable name * @return boolean */ final public static function exists(IReflection $object, $key) { try { self::get($object, $key); return true; } catch (RuntimeException $e) { return false; } } } TokenReflection/ReflectionClass.php000064400000143307144760110310013435 0ustar00::] => array( * array(, [])|null * [, ...] * ) * * @var array */ private $traitImports = array(); /** * Stores if the class definition is complete. * * @var array */ private $methods = array(); /** * Constant reflections. * * @var array */ private $constants = array(); /** * Properties reflections. * * @var array */ private $properties = array(); /** * Stores if the class definition is complete. * * @var boolean */ private $definitionComplete = false; /** * Imported namespace/class aliases. * * @var array */ private $aliases = array(); /** * Returns the unqualified name (UQN). * * @return string */ public function getShortName() { $name = $this->getName(); if ($this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) { $name = substr($name, strlen($this->namespaceName) + 1); } return $name; } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { return $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; } /** * Returns if the class is defined within a namespace. * * @return boolean */ public function inNamespace() { return null !== $this->namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName; } /** * Returns modifiers. * * @return array */ public function getModifiers() { if (false === $this->modifiersComplete) { if (($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) && !($this->modifiers & InternalReflectionClass::IS_IMPLICIT_ABSTRACT)) { foreach ($this->getMethods() as $reflectionMethod) { if ($reflectionMethod->isAbstract()) { $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT; } } if (!empty($this->interfaces)) { $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT; } } if (!empty($this->interfaces)) { $this->modifiers |= self::IMPLEMENTS_INTERFACES; } if ($this->isInterface() && !empty($this->methods)) { $this->modifiers |= InternalReflectionClass::IS_IMPLICIT_ABSTRACT; } if (!empty($this->traits)) { $this->modifiers |= self::IMPLEMENTS_TRAITS; } $this->modifiersComplete = null === $this->parentClassName || $this->getParentClass()->isComplete(); if ($this->modifiersComplete) { foreach ($this->getInterfaces() as $interface) { if (!$interface->isComplete()) { $this->modifiersComplete = false; break; } } } if ($this->modifiersComplete) { foreach ($this->getTraits() as $trait) { if (!$trait->isComplete()) { $this->modifiersComplete = false; break; } } } } return $this->modifiers; } /** * Returns if the class is abstract. * * @return boolean */ public function isAbstract() { if ($this->modifiers & InternalReflectionClass::IS_EXPLICIT_ABSTRACT) { return true; } elseif ($this->isInterface() && !empty($this->methods)) { return true; } return false; } /** * Returns if the class is final. * * @return boolean */ public function isFinal() { return (bool) ($this->modifiers & InternalReflectionClass::IS_FINAL); } /** * Returns if the class is an interface. * * @return boolean */ public function isInterface() { return (bool) ($this->modifiers & self::IS_INTERFACE); } /** * Returns if the class is an exception or its descendant. * * @return boolean */ public function isException() { return 'Exception' === $this->name || $this->isSubclassOf('Exception'); } /** * Returns if it is possible to create an instance of this class. * * @return boolean */ public function isInstantiable() { if ($this->isInterface() || $this->isAbstract()) { return false; } if (null === ($constructor = $this->getConstructor())) { return true; } return $constructor->isPublic(); } /** * Returns if objects of this class are cloneable. * * Introduced in PHP 5.4. * * @return boolean * @see http://svn.php.net/viewvc/php/php-src/trunk/ext/reflection/php_reflection.c?revision=307971&view=markup#l4059 */ public function isCloneable() { if ($this->isInterface() || $this->isAbstract()) { return false; } if ($this->hasMethod('__clone')) { return $this->getMethod('__clone')->isPublic(); } return true; } /** * Returns if the class is iterateable. * * Returns true if the class implements the Traversable interface. * * @return boolean * @todo traits */ public function isIterateable() { return $this->implementsInterface('Traversable'); } /** * Returns if the current class is a subclass of the given class. * * @param string|object $class Class name or reflection object * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not a reflection class instance. */ public function isSubclassOf($class) { if (is_object($class)) { if ($class instanceof InternalReflectionClass || $class instanceof IReflectionClass) { $class = $class->getName(); } else { $class = get_class($class); } } if ($class === $this->parentClassName) { return true; } $parent = $this->getParentClass(); return false === $parent ? false : $parent->isSubclassOf($class); } /** * Returns the parent class reflection. * * @return \TokenReflection\ReflectionClass|boolean */ public function getParentClass() { $className = $this->getParentClassName(); if (null === $className) { return false; } return $this->getBroker()->getClass($className); } /** * Returns the parent class name. * * @return string|null */ public function getParentClassName() { return $this->parentClassName; } /** * Returns the parent classes reflections. * * @return array */ public function getParentClasses() { $parent = $this->getParentClass(); if (false === $parent) { return array(); } return array_merge(array($parent->getName() => $parent), $parent->getParentClasses()); } /** * Returns the parent classes names. * * @return array */ public function getParentClassNameList() { $parent = $this->getParentClass(); if (false === $parent) { return array(); } return array_merge(array($parent->getName()), $parent->getParentClassNameList()); } /** * Returns if the class implements the given interface. * * @param string|object $interface Interface name or reflection object * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided parameter is not an interface. */ public function implementsInterface($interface) { if (is_object($interface)) { if (!$interface instanceof InternalReflectionClass && !$interface instanceof IReflectionClass) { throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of class reflection, "%s" provided.', get_class($interface)), Exception\RuntimeException::INVALID_ARGUMENT, $this); } if (!$interface->isInterface()) { throw new Exception\RuntimeException(sprintf('"%s" is not an interface.', $interfaceName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $interfaceName = $interface->getName(); } else { $interfaceName = $interface; } return in_array($interfaceName, $this->getInterfaceNames()); } /** * Returns interface reflections. * * @return array */ public function getInterfaces() { $interfaceNames = $this->getInterfaceNames(); if (empty($interfaceNames)) { return array(); } $broker = $this->getBroker(); return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) { return $broker->getClass($interfaceName); }, $interfaceNames)); } /** * Returns interface names. * * @return array */ public function getInterfaceNames() { $parentClass = $this->getParentClass(); $names = false !== $parentClass ? array_reverse(array_flip($parentClass->getInterfaceNames())) : array(); foreach ($this->interfaces as $interfaceName) { $names[$interfaceName] = true; foreach (array_reverse($this->getBroker()->getClass($interfaceName)->getInterfaceNames()) as $parentInterfaceName) { $names[$parentInterfaceName] = true; } } return array_keys($names); } /** * Returns reflections of interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaces() { $interfaceNames = $this->getOwnInterfaceNames(); if (empty($interfaceNames)) { return array(); } $broker = $this->getBroker(); return array_combine($interfaceNames, array_map(function($interfaceName) use ($broker) { return $broker->getClass($interfaceName); }, $interfaceNames)); } /** * Returns names of interfaces implemented by this class, not its parents. * * @return array */ public function getOwnInterfaceNames() { return $this->interfaces; } /** * Returns the class constructor reflection. * * @return \TokenReflection\ReflectionMethod|null */ public function getConstructor() { foreach ($this->getMethods() as $method) { if ($method->isConstructor()) { return $method; } } return null; } /** * Returns the class destructor reflection. * * @return \TokenReflection\ReflectionMethod|null */ public function getDestructor() { foreach ($this->getMethods() as $method) { if ($method->isDestructor()) { return $method; } } return null; } /** * Returns if the class implements the given method. * * @param string $name Method name * @return boolean */ public function hasMethod($name) { foreach ($this->getMethods() as $method) { if ($name === $method->getName()) { return true; } } return false; } /** * Returns a method reflection. * * @param string $name Method name * @return \TokenReflection\ReflectionMethod * @throws \TokenReflection\Exception\RuntimeException If the requested method does not exist. */ public function getMethod($name) { if (isset($this->methods[$name])) { return $this->methods[$name]; } foreach ($this->getMethods() as $method) { if ($name === $method->getName()) { return $method; } } throw new Exception\RuntimeException(sprintf('There is no method "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns method reflections. * * @param integer $filter Methods filter * @return array */ public function getMethods($filter = null) { $methods = $this->methods; foreach ($this->getTraitMethods() as $traitMethod) { if (!isset($methods[$traitMethod->getName()])) { $methods[$traitMethod->getName()] = $traitMethod; } } if (null !== $this->parentClassName) { foreach ($this->getParentClass()->getMethods(null) as $parentMethod) { if (!isset($methods[$parentMethod->getName()])) { $methods[$parentMethod->getName()] = $parentMethod; } } } foreach ($this->getOwnInterfaces() as $interface) { foreach ($interface->getMethods(null) as $parentMethod) { if (!isset($methods[$parentMethod->getName()])) { $methods[$parentMethod->getName()] = $parentMethod; } } } if (null !== $filter) { $methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) { return $method->is($filter); }); } return array_values($methods); } /** * Returns if the class implements (and not its parents) the given method. * * @param string $name Method name * @return boolean */ public function hasOwnMethod($name) { return isset($this->methods[$name]); } /** * Returns reflections of methods declared by this class, not its parents. * * @param integer $filter Methods filter * @return array */ public function getOwnMethods($filter = null) { $methods = $this->methods; if (null !== $filter) { $methods = array_filter($methods, function(ReflectionMethod $method) use ($filter) { return $method->is($filter); }); } return array_values($methods); } /** * Returns if the class imports the given method from traits. * * @param string $name Method name * @return boolean */ public function hasTraitMethod($name) { if (isset($this->methods[$name])) { return false; } foreach ($this->getOwnTraits() as $trait) { if ($trait->hasMethod($name)) { return true; } } return false; } /** * Returns reflections of method imported from traits. * * @param integer $filter Methods filter * @return array * @throws \TokenReflection\Exception\RuntimeException If trait method was already imported. */ public function getTraitMethods($filter = null) { $methods = array(); foreach ($this->getOwnTraits() as $trait) { $traitName = $trait->getName(); foreach ($trait->getMethods(null) as $traitMethod) { $methodName = $traitMethod->getName(); $imports = array(); if (isset($this->traitImports[$traitName . '::' . $methodName])) { $imports = $this->traitImports[$traitName . '::' . $methodName]; } if (isset($this->traitImports[$methodName])) { $imports = empty($imports) ? $this->traitImports[$methodName] : array_merge($imports, $this->traitImports[$methodName]); } foreach ($imports as $import) { if (null !== $import) { list($newName, $accessLevel) = $import; if ('' === $newName) { $newName = $methodName; $imports[] = null; } if (!isset($this->methods[$newName])) { if (isset($methods[$newName])) { throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $newName), Exception\RuntimeException::ALREADY_EXISTS, $this); } $methods[$newName] = $traitMethod->alias($this, $newName, $accessLevel); } } } if (!in_array(null, $imports)) { if (!isset($this->methods[$methodName])) { if (isset($methods[$methodName])) { throw new Exception\RuntimeException(sprintf('Trait method "%s" was already imported.', $methodName), Exception\RuntimeException::ALREADY_EXISTS, $this); } $methods[$methodName] = $traitMethod->alias($this); } } } } if (null !== $filter) { $methods = array_filter($methods, function(IReflectionMethod $method) use ($filter) { return (bool) ($method->getModifiers() & $filter); }); } return array_values($methods); } /** * Returns if the class defines the given constant. * * @param string $name Constant name. * @return boolean */ public function hasConstant($name) { if (isset($this->constants[$name])) { return true; } foreach ($this->getConstantReflections() as $constant) { if ($name === $constant->getName()) { return true; } } return false; } /** * Returns a constant value. * * @param string $name Constant name * @return mixed|false */ public function getConstant($name) { try { return $this->getConstantReflection($name)->getValue(); } catch (Exception\BaseException $e) { return false; } } /** * Returns a constant reflection. * * @param string $name Constant name * @return \TokenReflection\ReflectionConstant * @throws \TokenReflection\Exception\RuntimeException If the requested constant does not exist. */ public function getConstantReflection($name) { if (isset($this->constants[$name])) { return $this->constants[$name]; } foreach ($this->getConstantReflections() as $constant) { if ($name === $constant->getName()) { return $constant; } } throw new Exception\RuntimeException(sprintf('There is no constant "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns constant values. * * @return array */ public function getConstants() { $constants = array(); foreach ($this->getConstantReflections() as $constant) { $constants[$constant->getName()] = $constant->getValue(); } return $constants; } /** * Returns constant reflections. * * @return array */ public function getConstantReflections() { if (null === $this->parentClassName && empty($this->interfaces)) { return array_values($this->constants); } else { $reflections = array_values($this->constants); if (null !== $this->parentClassName) { $reflections = array_merge($reflections, $this->getParentClass()->getConstantReflections()); } foreach ($this->getOwnInterfaces() as $interface) { $reflections = array_merge($reflections, $interface->getConstantReflections()); } return $reflections; } } /** * Returns if the class (and not its parents) defines the given constant. * * @param string $name Constant name. * @return boolean */ public function hasOwnConstant($name) { return isset($this->constants[$name]); } /** * Returns constants declared by this class, not by its parents. * * @return array */ public function getOwnConstants() { return array_map(function(ReflectionConstant $constant) { return $constant->getValue(); }, $this->constants); } /** * Returns reflections of constants declared by this class, not by its parents. * * @return array */ public function getOwnConstantReflections() { return array_values($this->constants); } /** * Returns if the class defines the given property. * * @param string $name Property name * @return boolean */ public function hasProperty($name) { foreach ($this->getProperties() as $property) { if ($name === $property->getName()) { return true; } } return false; } /** * Return a property reflection. * * @param string $name Property name * @return \TokenReflection\ReflectionProperty * @throws \TokenReflection\Exception\RuntimeException If the requested property does not exist. */ public function getProperty($name) { if (isset($this->properties[$name])) { return $this->properties[$name]; } foreach ($this->getProperties() as $property) { if ($name === $property->getName()) { return $property; } } throw new Exception\RuntimeException(sprintf('There is no property "%s".', $name, $this->name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns property reflections. * * @param integer $filter Properties filter * @return array */ public function getProperties($filter = null) { $properties = $this->properties; foreach ($this->getTraitProperties(null) as $traitProperty) { if (!isset($properties[$traitProperty->getName()])) { $properties[$traitProperty->getName()] = $traitProperty->alias($this); } } if (null !== $this->parentClassName) { foreach ($this->getParentClass()->getProperties(null) as $parentProperty) { if (!isset($properties[$parentProperty->getName()])) { $properties[$parentProperty->getName()] = $parentProperty; } } } if (null !== $filter) { $properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) { return (bool) ($property->getModifiers() & $filter); }); } return array_values($properties); } /** * Returns if the class (and not its parents) defines the given property. * * @param string $name Property name * @return boolean */ public function hasOwnProperty($name) { return isset($this->properties[$name]); } /** * Returns reflections of properties declared by this class, not its parents. * * @param integer $filter Properties filter * @return array */ public function getOwnProperties($filter = null) { $properties = $this->properties; if (null !== $filter) { $properties = array_filter($properties, function(ReflectionProperty $property) use ($filter) { return (bool) ($property->getModifiers() & $filter); }); } return array_values($properties); } /** * Returns if the class imports the given property from traits. * * @param string $name Property name * @return boolean */ public function hasTraitProperty($name) { if (isset($this->properties[$name])) { return false; } foreach ($this->getOwnTraits() as $trait) { if ($trait->hasProperty($name)) { return true; } } return false; } /** * Returns reflections of properties imported from traits. * * @param integer $filter Properties filter * @return array */ public function getTraitProperties($filter = null) { $properties = array(); foreach ($this->getOwnTraits() as $trait) { foreach ($trait->getProperties(null) as $traitProperty) { if (!isset($this->properties[$traitProperty->getName()]) && !isset($properties[$traitProperty->getName()])) { $properties[$traitProperty->getName()] = $traitProperty->alias($this); } } } if (null !== $filter) { $properties = array_filter($properties, function(IReflectionProperty $property) use ($filter) { return (bool) ($property->getModifiers() & $filter); }); } return array_values($properties); } /** * Returns default properties. * * @return array */ public function getDefaultProperties() { static $accessLevels = array(InternalReflectionProperty::IS_PUBLIC, InternalReflectionProperty::IS_PROTECTED, InternalReflectionProperty::IS_PRIVATE); $defaults = array(); $properties = $this->getProperties(); foreach (array(true, false) as $static) { foreach ($properties as $property) { foreach ($accessLevels as $level) { if ($property->isStatic() === $static && ($property->getModifiers() & $level)) { $defaults[$property->getName()] = $property->getDefaultValue(); } } } } return $defaults; } /** * Returns static properties reflections. * * @return array */ public function getStaticProperties() { $defaults = array(); foreach ($this->getProperties(InternalReflectionProperty::IS_STATIC) as $property) { if ($property instanceof ReflectionProperty) { $defaults[$property->getName()] = $property->getDefaultValue(); } } return $defaults; } /** * Returns a value of a static property. * * @param string $name Property name * @param mixed $default Default value * @return mixed * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. * @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible. */ public function getStaticPropertyValue($name, $default = null) { if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) { if (!$property->isPublic() && !$property->isAccessible()) { throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this); } return $property->getDefaultValue(); } throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns traits used by this class. * * @return array */ public function getTraits() { $traitNames = $this->getTraitNames(); if (empty($traitNames)) { return array(); } $broker = $this->getBroker(); return array_combine($traitNames, array_map(function($traitName) use ($broker) { return $broker->getClass($traitName); }, $traitNames)); } /** * Returns traits used by this class and not its parents. * * @return array */ public function getOwnTraits() { $ownTraitNames = $this->getOwnTraitNames(); if (empty($ownTraitNames)) { return array(); } $broker = $this->getBroker(); return array_combine($ownTraitNames, array_map(function($traitName) use ($broker) { return $broker->getClass($traitName); }, $ownTraitNames)); } /** * Returns names of used traits. * * @return array */ public function getTraitNames() { $parentClass = $this->getParentClass(); $names = $parentClass ? $parentClass->getTraitNames() : array(); foreach ($this->traits as $traitName) { $names[] = $traitName; } return array_unique($names); } /** * Returns names of traits used by this class an not its parents. * * @return array */ public function getOwnTraitNames() { return $this->traits; } /** * Returns method aliases from traits. * * @return array */ public function getTraitAliases() { return $this->traitAliases; } /** * Returns if the class is a trait. * * @return boolean */ public function isTrait() { return self::IS_TRAIT === $this->type; } /** * Returns if the class definition is valid. * * @return boolean */ public function isValid() { if (null !== $this->parentClassName && !$this->getParentClass()->isValid()) { return false; } foreach ($this->getInterfaces() as $interface) { if (!$interface->isValid()) { return false; } } foreach ($this->getTraits() as $trait) { if (!$trait->isValid()) { return false; } } return true; } /** * Returns if the class uses a particular trait. * * @param \ReflectionClass|\TokenReflection\IReflectionClass|string $trait Trait reflection or name * @return boolean * @throws \TokenReflection\Exception\RuntimeException If an invalid parameter was provided. */ public function usesTrait($trait) { if (is_object($trait)) { if (!$trait instanceof InternalReflectionClass && !$trait instanceof IReflectionClass) { throw new Exception\RuntimeException(sprintf('Parameter must be a string or an instance of trait reflection, "%s" provided.', get_class($trait)), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $traitName = $trait->getName(); if (!$trait->isTrait()) { throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $traitName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } } else { $reflection = $this->getBroker()->getClass($trait); if (!$reflection->isTrait()) { throw new Exception\RuntimeException(sprintf('"%s" is not a trait.', $trait), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $traitName = $trait; } return in_array($traitName, $this->getTraitNames()); } /** * Returns reflections of direct subclasses. * * @return array */ public function getDirectSubclasses() { $that = $this->name; return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { if (!$class->isSubclassOf($that)) { return false; } return null === $class->getParentClassName() || !$class->getParentClass()->isSubClassOf($that); }); } /** * Returns names of direct subclasses. * * @return array */ public function getDirectSubclassNames() { return array_keys($this->getDirectSubclasses()); } /** * Returns reflections of indirect subclasses. * * @return array */ public function getIndirectSubclasses() { $that = $this->name; return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { if (!$class->isSubclassOf($that)) { return false; } return null !== $class->getParentClassName() && $class->getParentClass()->isSubClassOf($that); }); } /** * Returns names of indirect subclasses. * * @return array */ public function getIndirectSubclassNames() { return array_keys($this->getIndirectSubclasses()); } /** * Returns reflections of classes directly implementing this interface. * * @return array */ public function getDirectImplementers() { if (!$this->isInterface()) { return array(); } $that = $this->name; return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { if ($class->isInterface() || !$class->implementsInterface($that)) { return false; } return null === $class->getParentClassName() || !$class->getParentClass()->implementsInterface($that); }); } /** * Returns names of classes directly implementing this interface. * * @return array */ public function getDirectImplementerNames() { return array_keys($this->getDirectImplementers()); } /** * Returns reflections of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementers() { if (!$this->isInterface()) { return array(); } $that = $this->name; return array_filter($this->getBroker()->getClasses(), function(ReflectionClass $class) use ($that) { if ($class->isInterface() || !$class->implementsInterface($that)) { return false; } return null !== $class->getParentClassName() && $class->getParentClass()->implementsInterface($that); }); } /** * Returns names of classes indirectly implementing this interface. * * @return array */ public function getIndirectImplementerNames() { return array_keys($this->getIndirectImplementers()); } /** * Returns if the given object is an instance of this class. * * @param object $object Instance * @return boolean * @throws \TokenReflection\Exception\RuntimeException If the provided argument is not an object. */ public function isInstance($object) { if (!is_object($object)) { throw new Exception\RuntimeException(sprintf('Parameter must be an object, "%s" provided.', gettype($object)), Exception\RuntimeException::INVALID_ARGUMENT, $this); } return $this->name === get_class($object) || is_subclass_of($object, $this->getName()); } /** * Creates a new class instance without using a constructor. * * @return object * @throws \TokenReflection\Exception\RuntimeException If the class inherits from an internal class. */ public function newInstanceWithoutConstructor() { if (!class_exists($this->name, true)) { throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $reflection = new \TokenReflection\Php\ReflectionClass($this->getName(), $this->getBroker()); return $reflection->newInstanceWithoutConstructor(); } /** * Creates a new instance using variable number of parameters. * * Use any number of constructor parameters as function parameters. * * @param mixed $args * @return object */ public function newInstance($args) { return $this->newInstanceArgs(func_get_args()); } /** * Creates a new instance using an array of parameters. * * @param array $args Array of constructor parameters * @return object * @throws \TokenReflection\Exception\RuntimeException If the required class does not exist. */ public function newInstanceArgs(array $args = array()) { if (!class_exists($this->name, true)) { throw new Exception\RuntimeException('Could not create an instance; class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $reflection = new InternalReflectionClass($this->name); return $reflection->newInstanceArgs($args); } /** * Sets a static property value. * * @param string $name Property name * @param mixed $value Property value * @throws \TokenReflection\Exception\RuntimeException If the requested static property does not exist. * @throws \TokenReflection\Exception\RuntimeException If the requested static property is not accessible. */ public function setStaticPropertyValue($name, $value) { if ($this->hasProperty($name) && ($property = $this->getProperty($name)) && $property->isStatic()) { if (!$property->isPublic() && !$property->isAccessible()) { throw new Exception\RuntimeException(sprintf('Static property "%s" is not accessible.', $name), Exception\RuntimeException::NOT_ACCESSBILE, $this); } $property->setDefaultValue($value); return; } throw new Exception\RuntimeException(sprintf('There is no static property "%s".', $name), Exception\RuntimeException::DOES_NOT_EXIST, $this); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { $implements = ''; $interfaceNames = $this->getInterfaceNames(); if (count($interfaceNames) > 0) { $implements = sprintf( ' %s %s', $this->isInterface() ? 'extends' : 'implements', implode(', ', $interfaceNames) ); } $buffer = ''; $count = 0; foreach ($this->getConstantReflections() as $constant) { $buffer .= ' ' . $constant->__toString(); $count++; } $constants = sprintf("\n\n - Constants [%d] {\n%s }", $count, $buffer); $sBuffer = ''; $sCount = 0; $buffer = ''; $count = 0; foreach ($this->getProperties() as $property) { $string = ' ' . preg_replace('~\n(?!$)~', "\n ", $property->__toString()); if ($property->isStatic()) { $sBuffer .= $string; $sCount++; } else { $buffer .= $string; $count++; } } $staticProperties = sprintf("\n\n - Static properties [%d] {\n%s }", $sCount, $sBuffer); $properties = sprintf("\n\n - Properties [%d] {\n%s }", $count, $buffer); $sBuffer = ''; $sCount = 0; $buffer = ''; $count = 0; foreach ($this->getMethods() as $method) { // Skip private methods of parent classes if ($method->getDeclaringClassName() !== $this->getName() && $method->isPrivate()) { continue; } // Indent $string = "\n "; $string .= preg_replace('~\n(?!$|\n|\s*\*)~', "\n ", $method->__toString()); // Add inherits if ($method->getDeclaringClassName() !== $this->getName()) { $string = preg_replace( array('~Method [ <[\w:]+~', '~, overwrites[^,]+~'), array('\0, inherits ' . $method->getDeclaringClassName(), ''), $string ); } if ($method->isStatic()) { $sBuffer .= $string; $sCount++; } else { $buffer .= $string; $count++; } } $staticMethods = sprintf("\n\n - Static methods [%d] {\n%s }", $sCount, ltrim($sBuffer, "\n")); $methods = sprintf("\n\n - Methods [%d] {\n%s }", $count, ltrim($buffer, "\n")); return sprintf( "%s%s [ %s %s%s%s %s%s%s ] {\n @@ %s %d-%d%s%s%s%s%s\n}\n", $this->getDocComment() ? $this->getDocComment() . "\n" : '', $this->isInterface() ? 'Interface' : 'Class', $this->isIterateable() ? ' ' : '', $this->isAbstract() && !$this->isInterface() ? 'abstract ' : '', $this->isFinal() ? 'final ' : '', $this->isInterface() ? 'interface' : 'class', $this->getName(), null !== $this->getParentClassName() ? ' extends ' . $this->getParentClassName() : '', $implements, $this->getFileName(), $this->getStartLine(), $this->getEndLine(), $constants, $staticProperties, $staticMethods, $properties, $methods ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string|object $className Class name or class instance * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $className, $return = false) { if (is_object($className)) { $className = get_class($className); } $class = $broker->getClass($className); if ($class instanceof Invalid\ReflectionClass) { throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); } elseif ($class instanceof Dummy\ReflectionClass) { throw new Exception\RuntimeException('Class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST); } if ($return) { return $class->__toString(); } echo $class->__toString(); } /** * Returns if the class definition is complete. * * @return boolean */ public function isComplete() { if (!$this->definitionComplete) { if (null !== $this->parentClassName && !$this->getParentClass()->isComplete()) { return false; } foreach ($this->getOwnInterfaces() as $interface) { if (!$interface->isComplete()) { return false; } } $this->definitionComplete = true; } return $this->definitionComplete; } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return $this->aliases; } /** * Processes the parent reflection object. * * @param \TokenReflection\IReflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionClass * @throws \TokenReflection\ParseException On invalid parent reflection provided */ protected function processParent(IReflection $parent, Stream $tokenStream) { if (!$parent instanceof ReflectionFileNamespace) { throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid parent reflection provided: "%s".', get_class($parent)), Exception\ParseException::INVALID_PARENT); } $this->namespaceName = $parent->getName(); $this->aliases = $parent->getNamespaceAliases(); return parent::processParent($parent, $tokenStream); } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionClass */ protected function parse(Stream $tokenStream, IReflection $parent) { return $this ->parseModifiers($tokenStream) ->parseName($tokenStream) ->parseParent($tokenStream, $parent) ->parseInterfaces($tokenStream, $parent); } /** * Parses class modifiers (abstract, final) and class type (class, interface). * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionClass */ private function parseModifiers(Stream $tokenStream) { while (true) { switch ($tokenStream->getType()) { case null: break 2; case T_ABSTRACT: $this->modifiers = InternalReflectionClass::IS_EXPLICIT_ABSTRACT; break; case T_FINAL: $this->modifiers = InternalReflectionClass::IS_FINAL; break; case T_INTERFACE: $this->modifiers = self::IS_INTERFACE; $this->type = self::IS_INTERFACE; $tokenStream->skipWhitespaces(true); break 2; case T_TRAIT: $this->modifiers = self::IS_TRAIT; $this->type = self::IS_TRAIT; $tokenStream->skipWhitespaces(true); break 2; case T_CLASS: $tokenStream->skipWhitespaces(true); break 2; default: break; } $tokenStream->skipWhitespaces(true); } return $this; } /** * Parses the class/interface name. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionClass * @throws \TokenReflection\Exception\ParseException If the class name could not be determined. */ protected function parseName(Stream $tokenStream) { if (!$tokenStream->is(T_STRING)) { throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); } if ($this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME) { $this->name = $tokenStream->getTokenValue(); } else { $this->name = $this->namespaceName . '\\' . $tokenStream->getTokenValue(); } $tokenStream->skipWhitespaces(true); return $this; } /** * Parses the parent class. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionClass */ private function parseParent(Stream $tokenStream, ReflectionElement $parent = null) { if (!$tokenStream->is(T_EXTENDS)) { return $this; } while (true) { $tokenStream->skipWhitespaces(true); $parentClassName = ''; while (true) { switch ($tokenStream->getType()) { case T_STRING: case T_NS_SEPARATOR: $parentClassName .= $tokenStream->getTokenValue(); break; default: break 2; } $tokenStream->skipWhitespaces(true); } $parentClassName = Resolver::resolveClassFQN($parentClassName, $this->aliases, $this->namespaceName); if ($this->isInterface()) { $this->interfaces[] = $parentClassName; if (',' === $tokenStream->getTokenValue()) { continue; } } else { $this->parentClassName = $parentClassName; } break; } return $this; } /** * Parses implemented interfaces. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionClass * @throws \TokenReflection\Exception\ParseException On error while parsing interfaces. */ private function parseInterfaces(Stream $tokenStream, ReflectionElement $parent = null) { if (!$tokenStream->is(T_IMPLEMENTS)) { return $this; } if ($this->isInterface()) { throw new Exception\ParseException($this, $tokenStream, 'Interfaces cannot implement interfaces.', Exception\ParseException::LOGICAL_ERROR); } while (true) { $interfaceName = ''; $tokenStream->skipWhitespaces(true); while (true) { switch ($tokenStream->getType()) { case T_STRING: case T_NS_SEPARATOR: $interfaceName .= $tokenStream->getTokenValue(); break; default: break 2; } $tokenStream->skipWhitespaces(true); } $this->interfaces[] = Resolver::resolveClassFQN($interfaceName, $this->aliases, $this->namespaceName); $type = $tokenStream->getType(); if ('{' === $type) { break; } elseif (',' !== $type) { throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found, expected "{" or ";".', Exception\ParseException::UNEXPECTED_TOKEN); } } return $this; } /** * Parses child reflection objects from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionClass * @throws \TokenReflection\Exception\ParseException If a parse error was detected. */ protected function parseChildren(Stream $tokenStream, IReflection $parent) { while (true) { switch ($type = $tokenStream->getType()) { case null: break 2; case T_COMMENT: case T_DOC_COMMENT: $docblock = $tokenStream->getTokenValue(); if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) { array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock)); } elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) { array_shift($this->docblockTemplates); } $tokenStream->next(); break; case '}': break 2; case T_PUBLIC: case T_PRIVATE: case T_PROTECTED: case T_STATIC: case T_VAR: case T_VARIABLE: static $searching = array(T_VARIABLE => true, T_FUNCTION => true); if (T_VAR !== $tokenStream->getType()) { $position = $tokenStream->key(); while (null !== ($type = $tokenStream->getType($position)) && !isset($searching[$type])) { $position++; } } if (T_VARIABLE === $type || T_VAR === $type) { $property = new ReflectionProperty($tokenStream, $this->getBroker(), $this); $this->properties[$property->getName()] = $property; $tokenStream->next(); break; } // Break missing on purpose case T_FINAL: case T_ABSTRACT: case T_FUNCTION: $method = new ReflectionMethod($tokenStream, $this->getBroker(), $this); $this->methods[$method->getName()] = $method; $tokenStream->next(); break; case T_CONST: $tokenStream->skipWhitespaces(true); while ($tokenStream->is(T_STRING)) { $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this); $this->constants[$constant->getName()] = $constant; if ($tokenStream->is(',')) { $tokenStream->skipWhitespaces(true); } else { $tokenStream->next(); } } break; case T_USE: $tokenStream->skipWhitespaces(true); while (true) { $traitName = ''; $type = $tokenStream->getType(); while (T_STRING === $type || T_NS_SEPARATOR === $type) { $traitName .= $tokenStream->getTokenValue(); $type = $tokenStream->skipWhitespaces(true)->getType(); } if ('' === trim($traitName, '\\')) { throw new Exception\ParseException($this, $tokenStream, 'An empty trait name found.', Exception\ParseException::LOGICAL_ERROR); } $this->traits[] = Resolver::resolveClassFQN($traitName, $this->aliases, $this->namespaceName); if (';' === $type) { // End of "use" $tokenStream->skipWhitespaces(); break; } elseif (',' === $type) { // Next trait name follows $tokenStream->skipWhitespaces(); continue; } elseif ('{' !== $type) { // Unexpected token throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found: "%s".', Exception\ParseException::UNEXPECTED_TOKEN); } // Aliases definition $type = $tokenStream->skipWhitespaces(true)->getType(); while (true) { if ('}' === $type) { $tokenStream->skipWhitespaces(); break 2; } $leftSide = ''; $rightSide = array('', null); $alias = true; while (T_STRING === $type || T_NS_SEPARATOR === $type || T_DOUBLE_COLON === $type) { $leftSide .= $tokenStream->getTokenValue(); $type = $tokenStream->skipWhitespaces(true)->getType(); } if (T_INSTEADOF === $type) { $alias = false; } elseif (T_AS !== $type) { throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); } $type = $tokenStream->skipWhitespaces(true)->getType(); if (T_PUBLIC === $type || T_PROTECTED === $type || T_PRIVATE === $type) { if (!$alias) { throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); } switch ($type) { case T_PUBLIC: $type = InternalReflectionMethod::IS_PUBLIC; break; case T_PROTECTED: $type = InternalReflectionMethod::IS_PROTECTED; break; case T_PRIVATE: $type = InternalReflectionMethod::IS_PRIVATE; break; default: break; } $rightSide[1] = $type; $type = $tokenStream->skipWhitespaces(true)->getType(); } while (T_STRING === $type || (T_NS_SEPARATOR === $type && !$alias)) { $rightSide[0] .= $tokenStream->getTokenValue(); $type = $tokenStream->skipWhitespaces(true)->getType(); } if (empty($leftSide)) { throw new Exception\ParseException($this, $tokenStream, 'An empty method name was found.', Exception\ParseException::LOGICAL_ERROR); } if ($alias) { // Alias if ($pos = strpos($leftSide, '::')) { $methodName = substr($leftSide, $pos + 2); $className = Resolver::resolveClassFQN(substr($leftSide, 0, $pos), $this->aliases, $this->namespaceName); $leftSide = $className . '::' . $methodName; $this->traitAliases[$rightSide[0]] = $leftSide; } else { $this->traitAliases[$rightSide[0]] = '(null)::' . $leftSide; } $this->traitImports[$leftSide][] = $rightSide; } else { // Insteadof if ($pos = strpos($leftSide, '::')) { $methodName = substr($leftSide, $pos + 2); } else { throw new Exception\ParseException($this, $tokenStream, 'A T_DOUBLE_COLON has to be present when using T_INSTEADOF.', Exception\ParseException::UNEXPECTED_TOKEN); } $this->traitImports[Resolver::resolveClassFQN($rightSide[0], $this->aliases, $this->namespaceName) . '::' . $methodName][] = null; } if (',' === $type) { $tokenStream->skipWhitespaces(true); continue; } elseif (';' !== $type) { throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); } $type = $tokenStream->skipWhitespaces()->getType(); } } break; default: $tokenStream->next(); break; } } return $this; } } TokenReflection/ReflectionConstant.php000064400000023755144760110310014165 0ustar00getName(); if (null !== $this->namespaceName && $this->namespaceName !== ReflectionNamespace::NO_NAMESPACE_NAME) { $name = substr($name, strlen($this->namespaceName) + 1); } return $name; } /** * Returns the name of the declaring class. * * @return string|null */ public function getDeclaringClassName() { return $this->declaringClassName; } /** * Returns a reflection of the declaring class. * * @return \TokenReflection\ReflectionClass|null */ public function getDeclaringClass() { if (null === $this->declaringClassName) { return null; } return $this->getBroker()->getClass($this->declaringClassName); } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { return null === $this->namespaceName || $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; } /** * Returns if the class is defined within a namespace. * * @return boolean */ public function inNamespace() { return '' !== $this->getNamespaceName(); } /** * Returns the constant value. * * @return mixed */ public function getValue() { if (is_array($this->valueDefinition)) { $this->value = Resolver::getValueDefinition($this->valueDefinition, $this); $this->valueDefinition = Resolver::getSourceCode($this->valueDefinition); } return $this->value; } /** * Returns the constant value definition. * * @return string */ public function getValueDefinition() { return is_array($this->valueDefinition) ? Resolver::getSourceCode($this->valueDefinition) : $this->valueDefinition; } /** * Returns the originaly provided value definition. * * @return string */ public function getOriginalValueDefinition() { return $this->valueDefinition; } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { return sprintf( "Constant [ %s %s ] { %s }\n", strtolower(gettype($this->getValue())), $this->getName(), $this->getValue() ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string|object|null $class Class name, class instance or null * @param string $constant Constant name * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $class, $constant, $return = false) { $className = is_object($class) ? get_class($class) : $class; $constantName = $constant; if (null === $className) { $constant = $broker->getConstant($constantName); if (null === $constant) { throw new Exception\RuntimeException('Constant does not exist.', Exception\RuntimeException::DOES_NOT_EXIST); } } else { $class = $broker->getClass($className); if ($class instanceof Invalid\ReflectionClass) { throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); } elseif ($class instanceof Dummy\ReflectionClass) { throw new Exception\RuntimeException('Class does not exist.', Exception\RuntimeException::DOES_NOT_EXIST, $class); } $constant = $class->getConstantReflection($constantName); } if ($return) { return $constant->__toString(); } echo $constant->__toString(); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return null === $this->declaringClassName ? $this->aliases : $this->getDeclaringClass()->getNamespaceAliases(); } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return null === $this->declaringClassName ? parent::getPrettyName() : sprintf('%s::%s', $this->declaringClassName, $this->name); } /** * Returns if the constant definition is valid. * * @return boolean */ public function isValid() { return true; } /** * Processes the parent reflection object. * * @param \TokenReflection\IReflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. */ protected function processParent(IReflection $parent, Stream $tokenStream) { if ($parent instanceof ReflectionFileNamespace) { $this->namespaceName = $parent->getName(); $this->aliases = $parent->getNamespaceAliases(); } elseif ($parent instanceof ReflectionClass) { $this->declaringClassName = $parent->getName(); } else { throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid parent reflection provided: "%s".', get_class($parent)), Exception\ParseException::INVALID_PARENT); } return parent::processParent($parent, $tokenStream); } /** * Find the appropriate docblock. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection * @return \TokenReflection\ReflectionConstant */ protected function parseDocComment(Stream $tokenStream, IReflection $parent) { $position = $tokenStream->key() - 1; while ($position > 0 && !$tokenStream->is(T_CONST, $position)) { $position--; } $actual = $tokenStream->key(); parent::parseDocComment($tokenStream->seek($position), $parent); $tokenStream->seek($actual); return $this; } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionConstant */ protected function parse(Stream $tokenStream, IReflection $parent) { if ($tokenStream->is(T_CONST)) { $tokenStream->skipWhitespaces(true); } if (false === $this->docComment->getDocComment()) { parent::parseDocComment($tokenStream, $parent); } return $this ->parseName($tokenStream) ->parseValue($tokenStream, $parent); } /** * Parses the constant name. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionConstant * @throws \TokenReflection\Exception\ParseReflection If the constant name could not be determined. */ protected function parseName(Stream $tokenStream) { if (!$tokenStream->is(T_STRING)) { throw new Exception\ParseException($this, $tokenStream, 'The constant name could not be determined.', Exception\ParseException::LOGICAL_ERROR); } if (null === $this->namespaceName || $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME) { $this->name = $tokenStream->getTokenValue(); } else { $this->name = $this->namespaceName . '\\' . $tokenStream->getTokenValue(); } $tokenStream->skipWhitespaces(true); return $this; } /** * Parses the constant value. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionConstant * @throws \TokenReflection\Exception\ParseException If the constant value could not be determined. */ private function parseValue(Stream $tokenStream, IReflection $parent) { if (!$tokenStream->is('=')) { throw new Exception\ParseException($this, $tokenStream, 'Could not find the definition start.', Exception\ParseException::UNEXPECTED_TOKEN); } $tokenStream->skipWhitespaces(true); static $acceptedTokens = array( '-' => true, '+' => true, T_STRING => true, T_NS_SEPARATOR => true, T_CONSTANT_ENCAPSED_STRING => true, T_DNUMBER => true, T_LNUMBER => true, T_DOUBLE_COLON => true, T_CLASS_C => true, T_DIR => true, T_FILE => true, T_FUNC_C => true, T_LINE => true, T_METHOD_C => true, T_NS_C => true, T_TRAIT_C => true ); while (null !== ($type = $tokenStream->getType())) { if (T_START_HEREDOC === $type) { $this->valueDefinition[] = $tokenStream->current(); while (null !== $type && T_END_HEREDOC !== $type) { $tokenStream->next(); $this->valueDefinition[] = $tokenStream->current(); $type = $tokenStream->getType(); }; $tokenStream->next(); } elseif (isset($acceptedTokens[$type])) { $this->valueDefinition[] = $tokenStream->current(); $tokenStream->next(); } elseif ($tokenStream->isWhitespace(true)) { $tokenStream->skipWhitespaces(true); } else { break; } } if (empty($this->valueDefinition)) { throw new Exception\ParseException($this, $tokenStream, 'Value definition is empty.', Exception\ParseException::LOGICAL_ERROR); } $value = $tokenStream->getTokenValue(); if (null === $type || (',' !== $value && ';' !== $value)) { throw new Exception\ParseException($this, $tokenStream, 'Invalid value definition.', Exception\ParseException::LOGICAL_ERROR); } return $this; } } TokenReflection/ReflectionElement.php000064400000020452144760110310013754 0ustar00count()) { throw new Exception\ParseException($this, $tokenStream, 'Reflection token stream must not be empty.', Exception\ParseException::INVALID_ARGUMENT); } parent::__construct($tokenStream, $broker, $parent); } /** * Parses the token substream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object */ final protected function parseStream(Stream $tokenStream, IReflection $parent = null) { $this->fileName = $tokenStream->getFileName(); $this ->processParent($parent, $tokenStream) ->parseStartLine($tokenStream) ->parseDocComment($tokenStream, $parent) ->parse($tokenStream, $parent) ->parseChildren($tokenStream, $parent) ->parseEndLine($tokenStream); } /** * Returns the file name the reflection object is defined in. * * @return string */ public function getFileName() { return $this->fileName; } /** * Returns a file reflection. * * @return \TokenReflection\ReflectionFile * @throws \TokenReflection\Exception\RuntimeException If the file is not stored inside the broker */ public function getFileReflection() { return $this->getBroker()->getFile($this->fileName); } /** * Returns the definition start line number in the file. * * @return integer */ public function getStartLine() { return $this->startLine; } /** * Returns the definition end line number in the file. * * @return integer */ public function getEndLine() { return $this->endLine; } /** * Returns the PHP extension reflection. * * Alwyas returns null - everything is user defined. * * @return null */ public function getExtension() { return null; } /** * Returns the PHP extension name. * * Alwyas returns false - everything is user defined. * * @return boolean */ public function getExtensionName() { return false; } /** * Returns the appropriate source code part. * * @return string */ public function getSource() { return $this->broker->getFileTokens($this->getFileName())->getSourcePart($this->startPosition, $this->endPosition); } /** * Returns the start position in the file token stream. * * @return integer */ public function getStartPosition() { return $this->startPosition; } /** * Returns the end position in the file token stream. * * @return integer */ public function getEndPosition() { return $this->endPosition; } /** * Returns the stack of docblock templates. * * @return array */ protected function getDocblockTemplates() { return $this->docblockTemplates; } /** * Processes the parent reflection object. * * @param \TokenReflection\Reflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement */ protected function processParent(IReflection $parent, Stream $tokenStream) { // To be defined in child classes return $this; } /** * Find the appropriate docblock. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection * @return \TokenReflection\ReflectionElement */ protected function parseDocComment(Stream $tokenStream, IReflection $parent) { if ($this instanceof ReflectionParameter) { $this->docComment = new ReflectionAnnotation($this); return $this; } $position = $tokenStream->key(); if ($tokenStream->is(T_DOC_COMMENT, $position - 1)) { $value = $tokenStream->getTokenValue($position - 1); if (self::DOCBLOCK_TEMPLATE_END !== $value) { $this->docComment = new ReflectionAnnotation($this, $value); $this->startPosition--; } } elseif ($tokenStream->is(T_DOC_COMMENT, $position - 2)) { $value = $tokenStream->getTokenValue($position - 2); if (self::DOCBLOCK_TEMPLATE_END !== $value) { $this->docComment = new ReflectionAnnotation($this, $value); $this->startPosition -= 2; } } elseif ($tokenStream->is(T_COMMENT, $position - 1) && preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $tokenStream->getTokenValue($position - 1))) { $this->docComment = new ReflectionAnnotation($this, $tokenStream->getTokenValue($position - 1)); $this->startPosition--; } elseif ($tokenStream->is(T_COMMENT, $position - 2) && preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $tokenStream->getTokenValue($position - 2))) { $this->docComment = new ReflectionAnnotation($this, $tokenStream->getTokenValue($position - 2)); $this->startPosition -= 2; } if (null === $this->docComment) { $this->docComment = new ReflectionAnnotation($this); } if ($parent instanceof ReflectionElement) { $this->docComment->setTemplates($parent->getDocblockTemplates()); } return $this; } /** * Saves the start line number. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token susbtream * @return \TokenReflection\ReflectionElement */ private final function parseStartLine(Stream $tokenStream) { $token = $tokenStream->current(); $this->startLine = $token[2]; $this->startPosition = $tokenStream->key(); return $this; } /** * Saves the end line number. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token susbtream * @return \TokenReflection\ReflectionElement */ private final function parseEndLine(Stream $tokenStream) { $token = $tokenStream->current(); $this->endLine = $token[2]; $this->endPosition = $tokenStream->key(); return $this; } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionElement */ abstract protected function parse(Stream $tokenStream, IReflection $parent); /** * Parses the reflection object name. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement */ abstract protected function parseName(Stream $tokenStream); /** * Parses child reflection objects from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\Reflection $parent Parent reflection object * @return \TokenReflection\ReflectionElement */ protected function parseChildren(Stream $tokenStream, IReflection $parent) { // To be defined in child classes return $this; } } TokenReflection/ReflectionFile.php000064400000007426144760110310013250 0ustar00namespaces; } /** * Returns the string representation of the reflection object. * * @throws \TokenReflection\Exception\RuntimeException If the method is called, because it's unsupported. */ public function __toString() { throw new Exception\RuntimeException('Casting to string is not supported.', Exception\RuntimeException::UNSUPPORTED, $this); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string $argument Reflection object name * @param boolean $return Return the export instead of outputting it * @throws \TokenReflection\Exception\RuntimeException If the method is called, because it's unsupported. */ public static function export(Broker $broker, $argument, $return = false) { throw new Exception\RuntimeException('Export is not supported.', Exception\RuntimeException::UNSUPPORTED); } /** * Outputs the file source code. * * @return string */ public function getSource() { return (string) $this->broker->getFileTokens($this->getName()); } /** * Parses the token substream and prepares namespace reflections from the file. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionFile */ protected function parseStream(Stream $tokenStream, IReflection $parent = null) { $this->name = $tokenStream->getFileName(); if (1 >= $tokenStream->count()) { // No PHP content $this->docComment = new ReflectionAnnotation($this, null); return $this; } $docCommentPosition = null; if (!$tokenStream->is(T_OPEN_TAG)) { $this->namespaces[] = new ReflectionFileNamespace($tokenStream, $this->broker, $this); } else { $tokenStream->skipWhitespaces(); while (null !== ($type = $tokenStream->getType())) { switch ($type) { case T_DOC_COMMENT: if (null === $docCommentPosition) { $docCommentPosition = $tokenStream->key(); } case T_WHITESPACE: case T_COMMENT: break; case T_DECLARE: // Intentionally twice call of skipWhitespaces() $tokenStream ->skipWhitespaces() ->findMatchingBracket() ->skipWhitespaces() ->skipWhitespaces(); break; case T_NAMESPACE: $docCommentPosition = $docCommentPosition ?: -1; break 2; default: $docCommentPosition = $docCommentPosition ?: -1; $this->namespaces[] = new ReflectionFileNamespace($tokenStream, $this->broker, $this); break 2; } $tokenStream->skipWhitespaces(); } while (null !== ($type = $tokenStream->getType())) { if (T_NAMESPACE === $type) { $this->namespaces[] = new ReflectionFileNamespace($tokenStream, $this->broker, $this); } else { $tokenStream->skipWhitespaces(); } } } if (null !== $docCommentPosition && !empty($this->namespaces) && $docCommentPosition === $this->namespaces[0]->getStartPosition()) { $docCommentPosition = null; } $this->docComment = new ReflectionAnnotation($this, null !== $docCommentPosition ? $tokenStream->getTokenValue($docCommentPosition) : null); return $this; } } TokenReflection/ReflectionFileNamespace.php000064400000026557144760110310015073 0ustar00classes; } /** * Returns constant reflections. * * @return array */ public function getConstants() { return $this->constants; } /** * Returns function reflections. * * @return array */ public function getFunctions() { return $this->functions; } /** * Returns all imported namespaces and aliases. * * @return array */ public function getNamespaceAliases() { return $this->aliases; } /** * Processes the parent reflection object. * * @param \TokenReflection\IReflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. */ protected function processParent(IReflection $parent, Stream $tokenStream) { if (!$parent instanceof ReflectionFile) { throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionFile.', Exception\ParseException::INVALID_PARENT); } return parent::processParent($parent, $tokenStream); } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionFileNamespace */ protected function parse(Stream $tokenStream, IReflection $parent) { return $this->parseName($tokenStream); } /** * Find the appropriate docblock. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection * @return \TokenReflection\ReflectionElement */ protected function parseDocComment(Stream $tokenStream, IReflection $parent) { if (!$tokenStream->is(T_NAMESPACE)) { $this->docComment = new ReflectionAnnotation($this); return $this; } else { return parent::parseDocComment($tokenStream, $parent); } } /** * Parses the namespace name. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionFileNamespace * @throws \TokenReflection\Exception\ParseException If the namespace name could not be determined. */ protected function parseName(Stream $tokenStream) { if (!$tokenStream->is(T_NAMESPACE)) { $this->name = ReflectionNamespace::NO_NAMESPACE_NAME; return $this; } $tokenStream->skipWhitespaces(); $name = ''; // Iterate over the token stream while (true) { switch ($tokenStream->getType()) { // If the current token is a T_STRING, it is a part of the namespace name case T_STRING: case T_NS_SEPARATOR: $name .= $tokenStream->getTokenValue(); break; default: // Stop iterating when other token than string or ns separator found break 2; } $tokenStream->skipWhitespaces(true); } $name = ltrim($name, '\\'); if (empty($name)) { $this->name = ReflectionNamespace::NO_NAMESPACE_NAME; } else { $this->name = $name; } if (!$tokenStream->is(';') && !$tokenStream->is('{')) { throw new Exception\ParseException($this, $tokenStream, 'Invalid namespace name end, expecting ";" or "{".', Exception\ParseException::UNEXPECTED_TOKEN); } $tokenStream->skipWhitespaces(); return $this; } /** * Parses child reflection objects from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionFileNamespace * @throws \TokenReflection\Exception\ParseException If child elements could not be parsed. */ protected function parseChildren(Stream $tokenStream, IReflection $parent) { static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true); $depth = 0; $firstChild = null; while (true) { switch ($tokenStream->getType()) { case T_USE: while (true) { $namespaceName = ''; $alias = null; $tokenStream->skipWhitespaces(true); while (true) { switch ($tokenStream->getType()) { case T_STRING: case T_NS_SEPARATOR: $namespaceName .= $tokenStream->getTokenValue(); break; default: break 2; } $tokenStream->skipWhitespaces(true); } $namespaceName = ltrim($namespaceName, '\\'); if (empty($namespaceName)) { throw new Exception\ParseException($this, $tokenStream, 'Imported namespace name could not be determined.', Exception\ParseException::LOGICAL_ERROR); } elseif ('\\' === substr($namespaceName, -1)) { throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid namespace name "%s".', $namespaceName), Exception\ParseException::LOGICAL_ERROR); } if ($tokenStream->is(T_AS)) { // Alias defined $tokenStream->skipWhitespaces(true); if (!$tokenStream->is(T_STRING)) { throw new Exception\ParseException($this, $tokenStream, sprintf('The imported namespace "%s" seems aliased but the alias name could not be determined.', $namespaceName), Exception\ParseException::LOGICAL_ERROR); } $alias = $tokenStream->getTokenValue(); $tokenStream->skipWhitespaces(true); } else { // No explicit alias if (false !== ($pos = strrpos($namespaceName, '\\'))) { $alias = substr($namespaceName, $pos + 1); } else { $alias = $namespaceName; } } if (isset($this->aliases[$alias])) { throw new Exception\ParseException($this, $tokenStream, sprintf('Namespace alias "%s" already defined.', $alias), Exception\ParseException::LOGICAL_ERROR); } $this->aliases[$alias] = $namespaceName; $type = $tokenStream->getType(); if (';' === $type) { $tokenStream->skipWhitespaces(); break 2; } elseif (',' === $type) { // Next namespace in the current "use" definition continue; } throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); } case T_COMMENT: case T_DOC_COMMENT: $docblock = $tokenStream->getTokenValue(); if (preg_match('~^' . preg_quote(self::DOCBLOCK_TEMPLATE_START, '~') . '~', $docblock)) { array_unshift($this->docblockTemplates, new ReflectionAnnotation($this, $docblock)); } elseif (self::DOCBLOCK_TEMPLATE_END === $docblock) { array_shift($this->docblockTemplates); } $tokenStream->next(); break; case '{': $tokenStream->next(); $depth++; break; case '}': if (0 === $depth--) { break 2; } $tokenStream->next(); break; case null: case T_NAMESPACE: break 2; case T_ABSTRACT: case T_FINAL: case T_CLASS: case T_TRAIT: case T_INTERFACE: $class = new ReflectionClass($tokenStream, $this->getBroker(), $this); $firstChild = $firstChild ?: $class; $className = $class->getName(); if (isset($this->classes[$className])) { if (!$this->classes[$className] instanceof Invalid\ReflectionClass) { $this->classes[$className] = new Invalid\ReflectionClass($className, $this->classes[$className]->getFileName(), $this->getBroker()); } if (!$this->classes[$className]->hasReasons()) { $this->classes[$className]->addReason(new Exception\ParseException( $this, $tokenStream, sprintf('Class %s is defined multiple times in the file.', $className), Exception\ParseException::ALREADY_EXISTS )); } } else { $this->classes[$className] = $class; } $tokenStream->next(); break; case T_CONST: $tokenStream->skipWhitespaces(true); do { $constant = new ReflectionConstant($tokenStream, $this->getBroker(), $this); $firstChild = $firstChild ?: $constant; $constantName = $constant->getName(); if (isset($this->constants[$constantName])) { if (!$this->constants[$constantName] instanceof Invalid\ReflectionConstant) { $this->constants[$constantName] = new Invalid\ReflectionConstant($constantName, $this->constants[$constantName]->getFileName(), $this->getBroker()); } if (!$this->constants[$constantName]->hasReasons()) { $this->constants[$constantName]->addReason(new Exception\ParseException( $this, $tokenStream, sprintf('Constant %s is defined multiple times in the file.', $constantName), Exception\ParseException::ALREADY_EXISTS )); } } else { $this->constants[$constantName] = $constant; } if ($tokenStream->is(',')) { $tokenStream->skipWhitespaces(true); } else { $tokenStream->next(); } } while ($tokenStream->is(T_STRING)); break; case T_FUNCTION: $position = $tokenStream->key() + 1; while (isset($skipped[$type = $tokenStream->getType($position)])) { $position++; } if ('(' === $type) { // Skipping anonymous functions $tokenStream ->seek($position) ->findMatchingBracket() ->skipWhiteSpaces(true); if ($tokenStream->is(T_USE)) { $tokenStream ->skipWhitespaces(true) ->findMatchingBracket() ->skipWhitespaces(true); } $tokenStream ->findMatchingBracket() ->next(); continue; } $function = new ReflectionFunction($tokenStream, $this->getBroker(), $this); $firstChild = $firstChild ?: $function; $functionName = $function->getName(); if (isset($this->functions[$functionName])) { if (!$this->functions[$functionName] instanceof Invalid\ReflectionFunction) { $this->functions[$functionName] = new Invalid\ReflectionFunction($functionName, $this->functions[$functionName]->getFileName(), $this->getBroker()); } if (!$this->functions[$functionName]->hasReasons()) { $this->functions[$functionName]->addReason(new Exception\ParseException( $this, $tokenStream, sprintf('Function %s is defined multiple times in the file.', $functionName), Exception\ParseException::ALREADY_EXISTS )); } } else { $this->functions[$functionName] = $function; } $tokenStream->next(); break; default: $tokenStream->next(); break; } } if ($firstChild) { $this->startPosition = min($this->startPosition, $firstChild->getStartPosition()); } return $this; } } TokenReflection/ReflectionFunction.php000064400000011776144760110310014161 0ustar00hasAnnotation('disabled'); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { $parameters = ''; if ($this->getNumberOfParameters() > 0) { $buffer = ''; foreach ($this->getParameters() as $parameter) { $buffer .= "\n " . $parameter->__toString(); } $parameters = sprintf( "\n\n - Parameters [%d] {%s\n }", $this->getNumberOfParameters(), $buffer ); } return sprintf( "%sFunction [ function %s%s ] {\n @@ %s %d - %d%s\n}\n", $this->getDocComment() ? $this->getDocComment() . "\n" : '', $this->returnsReference() ? '&' : '', $this->getName(), $this->getFileName(), $this->getStartLine(), $this->getEndLine(), $parameters ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string $function Function name * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $function, $return = false) { $functionName = $function; $function = $broker->getFunction($functionName); if (null === $function) { throw new Exception\RuntimeException(sprintf('Function %s() does not exist.', $functionName), Exception\RuntimeException::DOES_NOT_EXIST); } if ($return) { return $function->__toString(); } echo $function->__toString(); } /** * Calls the function. * * @return mixed */ public function invoke() { return $this->invokeArgs(func_get_args()); } /** * Calls the function. * * @param array $args Function parameter values * @return mixed * @throws \TokenReflection\Exception\RuntimeException If the required function does not exist. */ public function invokeArgs(array $args = array()) { if (!function_exists($this->getName())) { throw new Exception\RuntimeException('Could not invoke function; function is not defined.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } return call_user_func_array($this->getName(), $args); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return $this->aliases; } /** * Returns the function/method as closure. * * @return \Closure */ public function getClosure() { if (!function_exists($this->getName())) { throw new Exception\RuntimeException('Could not invoke function; function is not defined.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $that = $this; return function() use ($that) { return $that->invokeArgs(func_get_args()); }; } /** * Returns the closure scope class. * * @return null */ public function getClosureScopeClass() { return null; } /** * Returns if the function definition is valid. * * @return boolean */ public function isValid() { return true; } /** * Processes the parent reflection object. * * @param \TokenReflection\IReflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. */ protected function processParent(IReflection $parent, Stream $tokenStream) { if (!$parent instanceof ReflectionFileNamespace) { throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionFileNamespace.', Exception\ParseException::INVALID_PARENT); } $this->namespaceName = $parent->getName(); $this->aliases = $parent->getNamespaceAliases(); return parent::processParent($parent, $tokenStream); } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionFunction */ protected function parse(Stream $tokenStream, IReflection $parent) { return $this ->parseReturnsReference($tokenStream) ->parseName($tokenStream); } } TokenReflection/ReflectionFunctionBase.php000064400000025624144760110310014751 0ustar00namespaceName && ReflectionNamespace::NO_NAMESPACE_NAME !== $this->namespaceName) { return $this->namespaceName . '\\' . $this->name; } return $this->name; } /** * Returns the unqualified name (UQN). * * @return string */ public function getShortName() { return $this->name; } /** * Returns the namespace name. * * @return string */ public function getNamespaceName() { return null === $this->namespaceName || $this->namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? '' : $this->namespaceName; } /** * Returns if the function/method is defined within a namespace. * * @return boolean */ public function inNamespace() { return '' !== $this->getNamespaceName(); } /** * Returns if the function/method is a closure. * * @return boolean */ public function isClosure() { return false; } /** * Returns this pointer bound to closure. * * @return null */ public function getClosureThis() { return null; } /** * Returns the closure scope class. * * @return string|null */ public function getClosureScopeClass() { return null; } /** * Returns if the function/method returns its value as reference. * * @return boolean */ public function returnsReference() { return $this->returnsReference; } /** * Returns a particular function/method parameter. * * @param integer|string $parameter Parameter name or position * @return \TokenReflection\ReflectionParameter * @throws \TokenReflection\Exception\RuntimeException If there is no parameter of the given name. * @throws \TokenReflection\Exception\RuntimeException If there is no parameter at the given position. */ public function getParameter($parameter) { if (is_numeric($parameter)) { if (!isset($this->parameters[$parameter])) { throw new Exception\RuntimeException(sprintf('There is no parameter at position "%d".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } return $this->parameters[$parameter]; } else { foreach ($this->parameters as $reflection) { if ($reflection->getName() === $parameter) { return $reflection; } } throw new Exception\RuntimeException(sprintf('There is no parameter "%s".', $parameter), Exception\RuntimeException::DOES_NOT_EXIST, $this); } } /** * Returns parameters. * * @return array */ public function getParameters() { return $this->parameters; } /** * Returns the number of parameters. * * @return integer */ public function getNumberOfParameters() { return count($this->parameters); } /** * Returns the number of required parameters. * * @return integer */ public function getNumberOfRequiredParameters() { $count = 0; array_walk($this->parameters, function(ReflectionParameter $parameter) use (&$count) { if (!$parameter->isOptional()) { $count++; } }); return $count; } /** * Returns static variables. * * @return array */ public function getStaticVariables() { if (empty($this->staticVariables) && !empty($this->staticVariablesDefinition)) { foreach ($this->staticVariablesDefinition as $variableName => $variableDefinition) { $this->staticVariables[$variableName] = Resolver::getValueDefinition($variableDefinition, $this); } } return $this->staticVariables; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->name . '()'; } /** * Creates aliases to parameters. * * @throws \TokenReflection\Exception\RuntimeException When called on a ReflectionFunction instance. */ protected final function aliasParameters() { if (!$this instanceof ReflectionMethod) { throw new Exception\RuntimeException('Only method parameters can be aliased.', Exception\RuntimeException::UNSUPPORTED, $this); } foreach ($this->parameters as $index => $parameter) { $this->parameters[$index] = $parameter->alias($this); } } /** * Parses if the function/method returns its value as reference. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionFunctionBase * @throws \TokenReflection\Exception\ParseException If could not be determined if the function\method returns its value by reference. */ final protected function parseReturnsReference(Stream $tokenStream) { if (!$tokenStream->is(T_FUNCTION)) { throw new Exception\ParseException($this, $tokenStream, 'Could not find the function keyword.', Exception\ParseException::UNEXPECTED_TOKEN); } $tokenStream->skipWhitespaces(true); $type = $tokenStream->getType(); if ('&' === $type) { $this->returnsReference = true; $tokenStream->skipWhitespaces(true); } elseif (T_STRING !== $type) { throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); } return $this; } /** * Parses the function/method name. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionMethod * @throws \TokenReflection\Exception\ParseException If the class name could not be determined. */ final protected function parseName(Stream $tokenStream) { $this->name = $tokenStream->getTokenValue(); $tokenStream->skipWhitespaces(true); return $this; } /** * Parses child reflection objects from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionElement */ final protected function parseChildren(Stream $tokenStream, IReflection $parent) { return $this ->parseParameters($tokenStream) ->parseStaticVariables($tokenStream); } /** * Parses function/method parameters. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionFunctionBase * @throws \TokenReflection\Exception\ParseException If parameters could not be parsed. */ final protected function parseParameters(Stream $tokenStream) { if (!$tokenStream->is('(')) { throw new Exception\ParseException($this, $tokenStream, 'Could find the start token.', Exception\ParseException::UNEXPECTED_TOKEN); } static $accepted = array(T_NS_SEPARATOR => true, T_STRING => true, T_ARRAY => true, T_CALLABLE => true, T_VARIABLE => true, '&' => true); $tokenStream->skipWhitespaces(true); while (null !== ($type = $tokenStream->getType()) && ')' !== $type) { if (isset($accepted[$type])) { $parameter = new ReflectionParameter($tokenStream, $this->getBroker(), $this); $this->parameters[] = $parameter; } if ($tokenStream->is(')')) { break; } $tokenStream->skipWhitespaces(true); } $tokenStream->skipWhitespaces(); return $this; } /** * Parses static variables. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionFunctionBase * @throws \TokenReflection\Exception\ParseException If static variables could not be parsed. */ final protected function parseStaticVariables(Stream $tokenStream) { $type = $tokenStream->getType(); if ('{' === $type) { if ($this->getBroker()->isOptionSet(Broker::OPTION_PARSE_FUNCTION_BODY)) { $tokenStream->skipWhitespaces(true); while ('}' !== ($type = $tokenStream->getType())) { switch ($type) { case T_STATIC: $type = $tokenStream->skipWhitespaces(true)->getType(); if (T_VARIABLE !== $type) { // Late static binding break; } while (T_VARIABLE === $type) { $variableName = $tokenStream->getTokenValue(); $variableDefinition = array(); $type = $tokenStream->skipWhitespaces(true)->getType(); if ('=' === $type) { $type = $tokenStream->skipWhitespaces(true)->getType(); $level = 0; while ($tokenStream->valid()) { switch ($type) { case '(': case '[': case '{': case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: $level++; break; case ')': case ']': case '}': $level--; break; case ';': case ',': if (0 === $level) { break 2; } default: break; } $variableDefinition[] = $tokenStream->current(); $type = $tokenStream->skipWhitespaces(true)->getType(); } if (!$tokenStream->valid()) { throw new Exception\ParseException($this, $tokenStream, 'Invalid end of token stream.', Exception\ParseException::READ_BEYOND_EOS); } } $this->staticVariablesDefinition[substr($variableName, 1)] = $variableDefinition; if (',' === $type) { $type = $tokenStream->skipWhitespaces(true)->getType(); } else { break; } } break; case T_FUNCTION: // Anonymous function -> skip to its end if (!$tokenStream->find('{')) { throw new Exception\ParseException($this, $tokenStream, 'Could not find beginning of the anonymous function.', Exception\ParseException::UNEXPECTED_TOKEN); } // Break missing intentionally case '{': case '[': case '(': case T_CURLY_OPEN: case T_DOLLAR_OPEN_CURLY_BRACES: $tokenStream->findMatchingBracket()->skipWhitespaces(true); break; default: $tokenStream->skipWhitespaces(); break; } } } else { $tokenStream->findMatchingBracket(); } } elseif (';' !== $type) { throw new Exception\ParseException($this, $tokenStream, 'Unexpected token found.', Exception\ParseException::UNEXPECTED_TOKEN); } return $this; } } TokenReflection/ReflectionMethod.php000064400000050122144760110310013600 0ustar00declaringClassName ? null : $this->getBroker()->getClass($this->declaringClassName); } /** * Returns the declaring class name. * * @return string|null */ public function getDeclaringClassName() { return $this->declaringClassName; } /** * Returns method modifiers. * * @return integer */ public function getModifiers() { if (!$this->modifiersComplete && !($this->modifiers & (self::ACCESS_LEVEL_CHANGED | self::IS_IMPLEMENTED_ABSTRACT))) { $declaringClass = $this->getDeclaringClass(); $parentClass = $declaringClass->getParentClass(); if (false !== $parentClass && $parentClass->hasMethod($this->name)) { $parentClassMethod = $parentClass->getMethod($this->name); // Access level changed if (($this->isPublic() || $this->isProtected()) && $parentClassMethod->is(self::ACCESS_LEVEL_CHANGED | InternalReflectionMethod::IS_PRIVATE)) { $this->modifiers |= self::ACCESS_LEVEL_CHANGED; } // Implemented abstract if ($parentClassMethod->isAbstract() && !$this->isAbstract()) { $this->modifiers |= self::IS_IMPLEMENTED_ABSTRACT; } } else { // Check if it is an implementation of an interface method foreach ($declaringClass->getInterfaces() as $interface) { if ($interface->hasOwnMethod($this->name)) { $this->modifiers |= self::IS_IMPLEMENTED_ABSTRACT; break; } } } // Set if modifiers definition is complete $this->modifiersComplete = $this->isComplete() || (($this->modifiers & self::IS_IMPLEMENTED_ABSTRACT) && ($this->modifiers & self::ACCESS_LEVEL_CHANGED)); } return $this->modifiers; } /** * Returns if the method is abstract. * * @return boolean */ public function isAbstract() { return (bool) ($this->modifiers & InternalReflectionMethod::IS_ABSTRACT); } /** * Returns if the method is final. * * @return boolean */ public function isFinal() { return (bool) ($this->modifiers & InternalReflectionMethod::IS_FINAL); } /** * Returns if the method is private. * * @return boolean */ public function isPrivate() { return (bool) ($this->modifiers & InternalReflectionMethod::IS_PRIVATE); } /** * Returns if the method is protected. * * @return boolean */ public function isProtected() { return (bool) ($this->modifiers & InternalReflectionMethod::IS_PROTECTED); } /** * Returns if the method is public. * * @return boolean */ public function isPublic() { return (bool) ($this->modifiers & InternalReflectionMethod::IS_PUBLIC); } /** * Returns if the method is static. * * @return boolean */ public function isStatic() { return (bool) ($this->modifiers & InternalReflectionMethod::IS_STATIC); } /** * Shortcut for isPublic(), ... methods that allows or-ed modifiers. * * The {@see getModifiers()} method is called only when really necessary making this * a more efficient way of doing * * if ($method->getModifiers() & $filter) { * ... * } * * * @param integer $filter Filter * @return boolean */ public function is($filter = null) { // See self::ACCESS_LEVEL_CHANGED | self::IS_IMPLEMENTED_ABSTRACT static $computedModifiers = 0x808; if (null === $filter || ($this->modifiers & $filter)) { return true; } elseif (($filter & $computedModifiers) && !$this->modifiersComplete) { return (bool) ($this->getModifiers() & $filter); } return false; } /** * Returns if the method is a constructor. * * @return boolean */ public function isConstructor() { return (bool) ($this->modifiers & self::IS_CONSTRUCTOR); } /** * Returns if the method is a destructor. * * @return boolean */ public function isDestructor() { return (bool) ($this->modifiers & self::IS_DESTRUCTOR); } /** * Returns the method prototype. * * @return \TokenReflection\ReflectionMethod * @throws \TokenReflection\Exception\RuntimeException If the method has no prototype. */ public function getPrototype() { if (null === $this->prototype) { $prototype = null; $declaring = $this->getDeclaringClass(); if (($parent = $declaring->getParentClass()) && $parent->hasMethod($this->name)) { $method = $parent->getMethod($this->name); if (!$method->isPrivate()) { try { $prototype = $method->getPrototype(); } catch (Exception\RuntimeException $e) { $prototype = $method; } } } if (null === $prototype) { foreach ($declaring->getOwnInterfaces() as $interface) { if ($interface->hasMethod($this->name)) { $prototype = $interface->getMethod($this->name); break; } } } $this->prototype = $prototype ?: ($this->isComplete() ? false : null); } if (empty($this->prototype)) { throw new Exception\RuntimeException('Method has no prototype.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } return $this->prototype; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return sprintf('%s::%s', $this->declaringClassName ?: $this->declaringTraitName, parent::getPrettyName()); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { $internal = ''; $overwrite = ''; $prototype = ''; $declaringClassParent = $this->getDeclaringClass()->getParentClass(); try { $prototype = ', prototype ' . $this->getPrototype()->getDeclaringClassName(); } catch (Exception\RuntimeException $e) { if ($declaringClassParent && $declaringClassParent->isInternal()) { $internal = 'internal:' . $parentClass->getExtensionName(); } } if ($declaringClassParent && $declaringClassParent->hasMethod($this->name)) { $parentMethod = $declaringClassParent->getMethod($this->name); $overwrite = ', overwrites ' . $parentMethod->getDeclaringClassName(); } if ($this->isConstructor()) { $cdtor = ', ctor'; } elseif ($this->isDestructor()) { $cdtor = ', dtor'; } else { $cdtor = ''; } $parameters = ''; if ($this->getNumberOfParameters() > 0) { $buffer = ''; foreach ($this->getParameters() as $parameter) { $buffer .= "\n " . $parameter->__toString(); } $parameters = sprintf( "\n\n - Parameters [%d] {%s\n }", $this->getNumberOfParameters(), $buffer ); } // @todo support inherits return sprintf( "%sMethod [ <%s%s%s%s> %s%s%s%s%s%s method %s%s ] {\n @@ %s %d - %d%s\n}\n", $this->getDocComment() ? $this->getDocComment() . "\n" : '', !empty($internal) ? $internal : 'user', $overwrite, $prototype, $cdtor, $this->isAbstract() ? 'abstract ' : '', $this->isFinal() ? 'final ' : '', $this->isStatic() ? 'static ' : '', $this->isPublic() ? 'public' : '', $this->isPrivate() ? 'private' : '', $this->isProtected() ? 'protected' : '', $this->returnsReference() ? '&' : '', $this->getName(), $this->getFileName(), $this->getStartLine(), $this->getEndLine(), $parameters ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string|object $class Class name or class instance * @param string $method Method name * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $class, $method, $return = false) { $className = is_object($class) ? get_class($class) : $class; $methodName = $method; $class = $broker->getClass($className); if ($class instanceof Invalid\ReflectionClass) { throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); } elseif ($class instanceof Dummy\ReflectionClass) { throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST); } $method = $class->getMethod($methodName); if ($return) { return $method->__toString(); } echo $method->__toString(); } /** * Calls the method on an given instance. * * @param object $object Class instance * @param mixed $args * @return mixed */ public function invoke($object, $args) { $params = func_get_args(); return $this->invokeArgs(array_shift($params), $params); } /** * Calls the method on an given object. * * @param object $object Class instance * @param array $args Method parameter values * @return mixed * @throws \TokenReflection\Exception\RuntimeException If it is not possible to invoke the method. */ public function invokeArgs($object, array $args = array()) { $declaringClass = $this->getDeclaringClass(); if (!$declaringClass->isInstance($object)) { throw new Exception\RuntimeException(sprintf('Expected instance of or subclass of "%s".', $this->declaringClassName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } if ($this->isPublic()) { return call_user_func_array(array($object, $this->getName()), $args); } elseif ($this->isAccessible()) { $refClass = new InternalReflectionClass($object); $refMethod = $refClass->getMethod($this->name); $refMethod->setAccessible(true); $value = $refMethod->invokeArgs($object, $args); $refMethod->setAccessible(false); return $value; } throw new Exception\RuntimeException('Only public methods can be invoked.', Exception\RuntimeException::NOT_ACCESSBILE, $this); } /** * Returns if the property is set accessible. * * @return boolean */ public function isAccessible() { return $this->accessible; } /** * Sets a method to be accessible or not. * * @param boolean $accessible */ public function setAccessible($accessible) { $this->accessible = (bool) $accessible; } /** * Returns if the definition is complete. * * Technically returns if the declaring class definition is complete. * * @return boolean */ private function isComplete() { return $this->getDeclaringClass()->isComplete(); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return $this->getDeclaringClass()->getNamespaceAliases(); } /** * Returns the function/method as closure. * * @param object $object Object * @return \Closure */ public function getClosure($object) { $declaringClass = $this->getDeclaringClass(); if (!$declaringClass->isInstance($object)) { throw new Exception\RuntimeException(sprintf('Expected instance of or subclass of "%s".', $this->declaringClassName), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $that = $this; return function() use ($object, $that) { return $that->invokeArgs($object, func_get_args()); }; } /** * Creates a method alias of the given name and access level for the given class. * * @param \TokenReflection\ReflectionClass $parent New parent class * @param string $name New method name * @param integer $accessLevel New access level * @return \TokenReflection\ReflectionMethod * @throws \TokenReflection\Exception\RuntimeException If an invalid method access level was found. */ public function alias(ReflectionClass $parent, $name = null, $accessLevel = null) { static $possibleLevels = array(InternalReflectionMethod::IS_PUBLIC => true, InternalReflectionMethod::IS_PROTECTED => true, InternalReflectionMethod::IS_PRIVATE => true); $method = clone $this; $method->declaringClassName = $parent->getName(); if (null !== $name) { $method->originalName = $this->name; $method->name = $name; } if (null !== $accessLevel) { if (!isset($possibleLevels[$accessLevel])) { throw new Exception\RuntimeException(sprintf('Invalid method access level: "%s".', $accessLevel), Exception\RuntimeException::INVALID_ARGUMENT, $this); } $method->modifiers &= ~(InternalReflectionMethod::IS_PUBLIC | InternalReflectionMethod::IS_PROTECTED | InternalReflectionMethod::IS_PRIVATE); $method->modifiers |= $accessLevel; $method->originalModifiers = $this->getModifiers(); } foreach ($this->parameters as $parameterName => $parameter) { $method->parameters[$parameterName] = $parameter->alias($method); } return $method; } /** * Returns the original name when importing from a trait. * * @return string|null */ public function getOriginalName() { return $this->originalName; } /** * Returns the original method when importing from a trait. * * @return \TokenReflection\IReflectionMethod|null */ public function getOriginal() { return $this->original; } /** * Returns the original modifiers value when importing from a trait. * * @return integer|null */ public function getOriginalModifiers() { return $this->originalModifiers; } /** * Returns the defining trait. * * @return \TokenReflection\IReflectionClass|null */ public function getDeclaringTrait() { return null === $this->declaringTraitName ? null : $this->getBroker()->getClass($this->declaringTraitName); } /** * Returns the declaring trait name. * * @return string|null */ public function getDeclaringTraitName() { return $this->declaringTraitName; } /** * Processes the parent reflection object. * * @param \TokenReflection\IReflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. */ protected function processParent(IReflection $parent, Stream $tokenStream) { if (!$parent instanceof ReflectionClass) { throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionClass.', Exception\ParseException::INVALID_PARENT); } $this->declaringClassName = $parent->getName(); if ($parent->isTrait()) { $this->declaringTraitName = $parent->getName(); } return parent::processParent($parent, $tokenStream); } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionMethod * @throws \TokenReflection\Exception\Parse If the class could not be parsed. */ protected function parse(Stream $tokenStream, IReflection $parent) { return $this ->parseBaseModifiers($tokenStream) ->parseReturnsReference($tokenStream) ->parseName($tokenStream) ->parseInternalModifiers($parent); } /** * Parses base method modifiers (abstract, final, public, ...). * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionMethod */ private function parseBaseModifiers(Stream $tokenStream) { while (true) { switch ($tokenStream->getType()) { case T_ABSTRACT: $this->modifiers |= InternalReflectionMethod::IS_ABSTRACT; break; case T_FINAL: $this->modifiers |= InternalReflectionMethod::IS_FINAL; break; case T_PUBLIC: $this->modifiers |= InternalReflectionMethod::IS_PUBLIC; break; case T_PRIVATE: $this->modifiers |= InternalReflectionMethod::IS_PRIVATE; break; case T_PROTECTED: $this->modifiers |= InternalReflectionMethod::IS_PROTECTED; break; case T_STATIC: $this->modifiers |= InternalReflectionMethod::IS_STATIC; break; case T_FUNCTION: case null: break 2; default: break; } $tokenStream->skipWhitespaces(); } if (!($this->modifiers & (InternalReflectionMethod::IS_PRIVATE | InternalReflectionMethod::IS_PROTECTED))) { $this->modifiers |= InternalReflectionMethod::IS_PUBLIC; } return $this; } /** * Parses internal PHP method modifiers (abstract, final, public, ...). * * @param \TokenReflection\ReflectionClass $class Parent class * @return \TokenReflection\ReflectionMethod */ private function parseInternalModifiers(ReflectionClass $class) { $name = strtolower($this->name); // In PHP 5.3.3+ the ctor can be named only __construct in namespaced classes if ('__construct' === $name || ((!$class->inNamespace() || PHP_VERSION_ID < 50303) && strtolower($class->getShortName()) === $name)) { $this->modifiers |= self::IS_CONSTRUCTOR; } elseif ('__destruct' === $name) { $this->modifiers |= self::IS_DESTRUCTOR; } elseif ('__clone' === $name) { $this->modifiers |= self::IS_CLONE; } if ($class->isInterface()) { $this->modifiers |= InternalReflectionMethod::IS_ABSTRACT; } else { // Can be called statically, see http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3/Zend/zend_API.c?revision=309853&view=markup#l1795 static $notAllowed = array('__clone' => true, '__tostring' => true, '__get' => true, '__set' => true, '__isset' => true, '__unset' => true); if (!$this->isStatic() && !$this->isConstructor() && !$this->isDestructor() && !isset($notAllowed[$name])) { $this->modifiers |= self::IS_ALLOWED_STATIC; } } return $this; } } TokenReflection/ReflectionNamespace.php000064400000032740144760110310014262 0ustar00name = $name; $this->broker = $broker; } /** * Returns the name. * * @return string */ public function getName() { return $this->name; } /** * Returns if the namespace is internal. * * Always false. * * @return boolean */ public function isInternal() { return false; } /** * Returns if the namespace is user defined. * * Always true. * * @return boolean */ public function isUserDefined() { return true; } /** * Returns if the current reflection comes from a tokenized source. * * @return boolean */ public function isTokenized() { return true; } /** * Returns if the namespace contains a class of the given name. * * @param string $className Class name * @return boolean */ public function hasClass($className) { $className = ltrim($className, '\\'); if (false === strpos($className, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { $className = $this->getName() . '\\' . $className; } return isset($this->classes[$className]); } /** * Return a class reflection. * * @param string $className Class name * @return \TokenReflection\ReflectionClass * @throws \TokenReflection\Exception\RuntimeException If the requested class reflection does not exist. */ public function getClass($className) { $className = ltrim($className, '\\'); if (false === strpos($className, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { $className = $this->getName() . '\\' . $className; } if (!isset($this->classes[$className])) { throw new Exception\RuntimeException(sprintf('Class "%s" does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST, $this); } return $this->classes[$className]; } /** * Returns class reflections. * * @return array */ public function getClasses() { return $this->classes; } /** * Returns class names (FQN). * * @return array */ public function getClassNames() { return array_keys($this->classes); } /** * Returns class unqualified names (UQN). * * @return array */ public function getClassShortNames() { return array_map(function(IReflectionClass $class) { return $class->getShortName(); }, $this->classes); } /** * Returns if the namespace contains a constant of the given name. * * @param string $constantName Constant name * @return boolean */ public function hasConstant($constantName) { $constantName = ltrim($constantName, '\\'); if (false === strpos($constantName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { $constantName = $this->getName() . '\\' . $constantName; } return isset($this->constants[$constantName]); } /** * Returns a constant reflection. * * @param string $constantName Constant name * @return \TokenReflection\ReflectionConstant * @throws \TokenReflection\Exception\RuntimeException If the required constant does not exist. */ public function getConstant($constantName) { $constantName = ltrim($constantName, '\\'); if (false === strpos($constantName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { $constantName = $this->getName() . '\\' . $constantName; } if (!isset($this->constants[$constantName])) { throw new Exception\RuntimeException(sprintf('Constant "%s" does not exist.', $constantName), Exception\RuntimeException::DOES_NOT_EXIST, $this); } return $this->constants[$constantName]; } /** * Returns constant reflections. * * @return array */ public function getConstants() { return $this->constants; } /** * Returns constant names (FQN). * * @return array */ public function getConstantNames() { return array_keys($this->constants); } /** * Returns constant unqualified names (UQN). * * @return array */ public function getConstantShortNames() { return array_map(function(IReflectionConstant $constant) { return $constant->getShortName(); }, $this->constants); } /** * Returns if the namespace contains a function of the given name. * * @param string $functionName Function name * @return boolean */ public function hasFunction($functionName) { $functionName = ltrim($functionName, '\\'); if (false === strpos($functionName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { $functionName = $this->getName() . '\\' . $functionName; } return isset($this->functions[$functionName]); } /** * Returns a function reflection. * * @param string $functionName Function name * @return \TokenReflection\ReflectionFunction * @throws \TokenReflection\Exception\RuntimeException If the required function does not exist. */ public function getFunction($functionName) { $functionName = ltrim($functionName, '\\'); if (false === strpos($functionName, '\\') && self::NO_NAMESPACE_NAME !== $this->getName()) { $functionName = $this->getName() . '\\' . $functionName; } if (!isset($this->functions[$functionName])) { throw new Exception\RuntimeException(sprintf('Function "%s" does not exist.', $functionName), Exception\RuntimeException::DOES_NOT_EXIST, $this); } return $this->functions[$functionName]; } /** * Returns function reflections. * * @return array */ public function getFunctions() { return $this->functions; } /** * Returns function names (FQN). * * @return array */ public function getFunctionNames() { return array_keys($this->functions); } /** * Returns function unqualified names (UQN). * * @return array */ public function getFunctionShortNames() { return array_map(function(IReflectionFunction $function) { return $function->getShortName(); }, $this->functions); } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return $this->name; } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { $buffer = ''; $count = 0; foreach ($this->getClasses() as $class) { $string = "\n " . trim(str_replace("\n", "\n ", $class->__toString()), ' '); $string = str_replace(" \n - Parameters", "\n - Parameters", $string); $buffer .= $string; $count++; } $classes = sprintf("\n\n - Classes [%d] {\n%s }", $count, ltrim($buffer, "\n")); $buffer = ''; $count = 0; foreach ($this->getConstants() as $constant) { $buffer .= ' ' . $constant->__toString(); $count++; } $constants = sprintf("\n\n - Constants [%d] {\n%s }", $count, $buffer); $buffer = ''; $count = 0; foreach ($this->getFunctions() as $function) { $string = "\n " . trim(str_replace("\n", "\n ", $function->__toString()), ' '); $string = str_replace(" \n - Parameters", "\n - Parameters", $string); $buffer .= $string; $count++; } $functions = sprintf("\n\n - Functions [%d] {\n%s }", $count, ltrim($buffer, "\n")); return sprintf( "Namespace [ namespace %s ] { %s%s%s\n}\n", $this->getName(), $classes, $constants, $functions ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string $namespace Namespace name * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $namespace, $return = false) { $namespaceName = $namespace; $namespace = $broker->getNamespace($namespaceName); if (null === $namespace) { throw new Exception\RuntimeException(sprintf('Namespace %s does not exist.', $namespaceName), Exception\RuntimeException::DOES_NOT_EXIST); } if ($return) { return $namespace->__toString(); } echo $namespace->__toString(); } /** * Adds a namespace part from a file. * * @param \TokenReflection\ReflectionFileNamespace $namespace Namespace part * @return \TokenReflection\ReflectionNamespace * @throws \TokenReflection\Exception\FileProcessingException If one of classes, functions or constants form the namespace are already defined */ public function addFileNamespace(ReflectionFileNamespace $namespace) { $errors = array(); foreach ($namespace->getClasses() as $className => $reflection) { if ($reflection instanceof Invalid\ReflectionClass) { $errors = array_merge($errors, $reflection->getReasons()); } if (isset($this->classes[$className])) { if (!$this->classes[$className] instanceof Invalid\ReflectionClass) { $this->classes[$className] = new Invalid\ReflectionClass($className, $this->classes[$className]->getFileName(), $this->getBroker()); } $error = new Exception\RuntimeException( sprintf('Class %s was redeclared (previously declared in file %s).', $className, $this->classes[$className]->getFileName()), Exception\RuntimeException::ALREADY_EXISTS, $reflection ); $errors[] = $error; $this->classes[$className]->addReason($error); if ($reflection instanceof Invalid\ReflectionClass) { foreach ($reflection->getReasons() as $reason) { $this->classes[$className]->addReason($reason); } } } else { $this->classes[$className] = $reflection; } } foreach ($namespace->getFunctions() as $functionName => $reflection) { if ($reflection instanceof Invalid\ReflectionFunction) { $errors = array_merge($errors, $reflection->getReasons()); } if (isset($this->functions[$functionName])) { if (!$this->functions[$functionName] instanceof Invalid\ReflectionFunction) { $this->functions[$functionName] = new Invalid\ReflectionFunction($functionName, $this->functions[$functionName]->getFileName(), $this->getBroker()); } $error = new Exception\RuntimeException( sprintf('Function %s was redeclared (previousy declared in file %s).', $functionName, $this->functions[$functionName]->getFileName()), Exception\RuntimeException::ALREADY_EXISTS, $reflection ); $errors[] = $error; $this->functions[$functionName]->addReason($error); if ($reflection instanceof Invalid\ReflectionFunction) { foreach ($reflection->getReasons() as $reason) { $this->functions[$functionName]->addReason($reason); } } } else { $this->functions[$functionName] = $reflection; } } foreach ($namespace->getConstants() as $constantName => $reflection) { if ($reflection instanceof Invalid\ReflectionConstant) { $errors = array_merge($errors, $reflection->getReasons()); } if (isset($this->constants[$constantName])) { if (!$this->constants[$constantName] instanceof Invalid\ReflectionConstant) { $this->constants[$constantName] = new Invalid\ReflectionConstant($constantName, $this->constants[$constantName]->getFileName(), $this->getBroker()); } $error = new Exception\RuntimeException( sprintf('Constant %s was redeclared (previuosly declared in file %s).', $constantName, $this->constants[$constantName]->getFileName()), Exception\RuntimeException::ALREADY_EXISTS, $reflection ); $errors[] = $error; $this->constants[$constantName]->addReason($error); if ($reflection instanceof Invalid\ReflectionConstant) { foreach ($reflection->getReasons() as $reason) { $this->constants[$constantName]->addReason($reason); } } } else { $this->constants[$constantName] = $reflection; } } if (!empty($errors)) { throw new Exception\FileProcessingException($errors, null); } return $this; } /** * Returns the appropriate source code part. * * Impossible for namespaces. * * @throws \TokenReflection\Exception\RuntimeException If the method is called, because it's unsupported. */ public function getSource() { throw new Exception\RuntimeException('Cannot export source code of a namespace.', Exception\RuntimeException::UNSUPPORTED, $this); } /** * Returns the reflection broker used by this reflection object. * * @return \TokenReflection\Broker|null */ public function getBroker() { return $this->broker; } /** * Magic __get method. * * @param string $key Variable name * @return mixed */ final public function __get($key) { return ReflectionElement::get($this, $key); } /** * Magic __isset method. * * @param string $key Variable name * @return boolean */ final public function __isset($key) { return ReflectionElement::exists($this, $key); } }TokenReflection/ReflectionParameter.php000064400000040160144760110310014301 0ustar00declaringClassName ? null : $this->getBroker()->getClass($this->declaringClassName); } /** * Returns the declaring class name. * * @return string|null */ public function getDeclaringClassName() { return $this->declaringClassName; } /** * Returns the declaring function. * * @return \TokenReflection\ReflectionFunctionBase */ public function getDeclaringFunction() { if (null !== $this->declaringClassName) { // Method parameter $class = $this->getBroker()->getClass($this->declaringClassName); if (null !== $class) { return $class->getMethod($this->declaringFunctionName); } } else { // Function parameter return $this->getBroker()->getFunction($this->declaringFunctionName); } } /** * Returns the declaring function name. * * @return string */ public function getDeclaringFunctionName() { return $this->declaringFunctionName; } /** * Returns the default value. * * @return mixed * @throws \TokenReflection\Exception\RuntimeException If the property is not optional. * @throws \TokenReflection\Exception\RuntimeException If the property has no default value. */ public function getDefaultValue() { if (!$this->isOptional()) { throw new Exception\RuntimeException('Property is not optional.', Exception\RuntimeException::UNSUPPORTED, $this); } if (is_array($this->defaultValueDefinition)) { if (0 === count($this->defaultValueDefinition)) { throw new Exception\RuntimeException('Property has no default value.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $this->defaultValue = Resolver::getValueDefinition($this->defaultValueDefinition, $this); $this->defaultValueDefinition = Resolver::getSourceCode($this->defaultValueDefinition); } return $this->defaultValue; } /** * Returns the part of the source code defining the parameter default value. * * @return string */ public function getDefaultValueDefinition() { return is_array($this->defaultValueDefinition) ? Resolver::getSourceCode($this->defaultValueDefinition) : $this->defaultValueDefinition; } /** * Retutns if a default value for the parameter is available. * * @return boolean */ public function isDefaultValueAvailable() { return null !== $this->getDefaultValueDefinition(); } /** * Returns the position within all parameters. * * @return integer */ public function getPosition() { return $this->position; } /** * Returns if the parameter expects an array. * * @return boolean */ public function isArray() { return $this->typeHint === self::ARRAY_TYPE_HINT; } /** * Returns if the parameter expects a callback. * * @return boolean */ public function isCallable() { return $this->typeHint === self::CALLABLE_TYPE_HINT; } /** * Returns the original type hint as defined in the source code. * * @return string|null */ public function getOriginalTypeHint() { return !$this->isArray() && !$this->isCallable() ? ltrim($this->originalTypeHint, '\\') : null; } /** * Returns reflection of the required class of the value. * * @return \TokenReflection\IReflectionClass|null */ public function getClass() { $name = $this->getClassName(); if (null === $name) { return null; } return $this->getBroker()->getClass($name); } /** * Returns the required class name of the value. * * @return string|null * @throws \TokenReflection\Exception\RuntimeException If the type hint class FQN could not be determined. */ public function getClassName() { if ($this->isArray() || $this->isCallable()) { return null; } if (null === $this->typeHint && null !== $this->originalTypeHint) { if (null !== $this->declaringClassName) { $parent = $this->getDeclaringClass(); if (null === $parent) { throw new Exception\RuntimeException('Could not load class reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } } else { $parent = $this->getDeclaringFunction(); if (null === $parent || !$parent->isTokenized()) { throw new Exception\RuntimeException('Could not load function reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } } $lTypeHint = strtolower($this->originalTypeHint); if ('parent' === $lTypeHint || 'self' === $lTypeHint) { if (null === $this->declaringClassName) { throw new Exception\RuntimeException('Parameter type hint cannot be "self" nor "parent" when not a method.', Exception\RuntimeException::UNSUPPORTED, $this); } if ('parent' === $lTypeHint) { if ($parent->isInterface() || null === $parent->getParentClassName()) { throw new Exception\RuntimeException('Class has no parent.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $this->typeHint = $parent->getParentClassName(); } else { $this->typeHint = $this->declaringClassName; } } else { $this->typeHint = ltrim(Resolver::resolveClassFQN($this->originalTypeHint, $parent->getNamespaceAliases(), $parent->getNamespaceName()), '\\'); } } return $this->typeHint; } /** * Returns if the the parameter allows NULL. * * @return boolean */ public function allowsNull() { if ($this->isArray() || $this->isCallable()) { return 'null' === strtolower($this->getDefaultValueDefinition()); } return null === $this->originalTypeHint || !empty($this->defaultValueDefinition); } /** * Returns if the parameter is optional. * * @return boolean * @throws \TokenReflection\Exception\RuntimeException If it is not possible to determine if the parameter is optional. */ public function isOptional() { if (null === $this->isOptional) { $function = $this->getDeclaringFunction(); if (null === $function) { throw new Exception\RuntimeException('Could not get the declaring function reflection.', Exception\RuntimeException::DOES_NOT_EXIST, $this); } $this->isOptional = true; foreach (array_slice($function->getParameters(), $this->position) as $reflectionParameter) { if (!$reflectionParameter->isDefaultValueAvailable()) { $this->isOptional = false; break; } } } return $this->isOptional; } /** * Returns if the parameter value is passed by reference. * * @return boolean */ public function isPassedByReference() { return $this->passedByReference; } /** * Returns if the paramter value can be passed by value. * * @return boolean */ public function canBePassedByValue() { return !$this->isPassedByReference(); } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return str_replace('()', '($' . $this->name . ')', $this->getDeclaringFunction()->getPrettyName()); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { if ($this->getClass()) { $hint = $this->getClassName(); } elseif ($this->isArray()) { $hint = self::ARRAY_TYPE_HINT; } elseif ($this->isCallable()) { $hint = self::CALLABLE_TYPE_HINT; } else { $hint = ''; } if (!empty($hint) && $this->allowsNull()) { $hint .= ' or NULL'; } if ($this->isDefaultValueAvailable()) { $default = ' = '; if (null === $this->getDefaultValue()) { $default .= 'NULL'; } elseif (is_array($this->getDefaultValue())) { $default .= 'Array'; } elseif (is_bool($this->getDefaultValue())) { $default .= $this->getDefaultValue() ? 'true' : 'false'; } elseif (is_string($this->getDefaultValue())) { $default .= sprintf("'%s'", str_replace("'", "\\'", $this->getDefaultValue())); } else { $default .= $this->getDefaultValue(); } } else { $default = ''; } return sprintf( 'Parameter #%d [ <%s> %s%s$%s%s ]', $this->getPosition(), $this->isOptional() ? 'optional' : 'required', $hint ? $hint . ' ' : '', $this->isPassedByReference() ? '&' : '', $this->getName(), $default ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string $function Function name * @param string $parameter Parameter name * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $function, $parameter, $return = false) { $functionName = $function; $parameterName = $parameter; $function = $broker->getFunction($functionName); if (null === $function) { throw new Exception\RuntimeException(sprintf('Function %s() does not exist.', $functionName), Exception\RuntimeException::DOES_NOT_EXIST); } $parameter = $function->getParameter($parameterName); if ($return) { return $parameter->__toString(); } echo $parameter->__toString(); } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return $this->getDeclaringFunction()->getNamespaceAliases(); } /** * Creates a parameter alias for the given method. * * @param \TokenReflection\ReflectionMethod $parent New parent method * @return \TokenReflection\ReflectionParameter */ public function alias(ReflectionMethod $parent) { $parameter = clone $this; $parameter->declaringClassName = $parent->getDeclaringClassName(); $parameter->declaringFunctionName = $parent->getName(); return $parameter; } /** * Processes the parent reflection object. * * @param \TokenReflection\IReflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement * @throws \TokenReflection\Exception\ParseException If an invalid parent reflection object was provided. */ protected function processParent(IReflection $parent, Stream $tokenStream) { if (!$parent instanceof ReflectionFunctionBase) { throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionFunctionBase.', Exception\ParseException::INVALID_PARENT); } // Declaring function name $this->declaringFunctionName = $parent->getName(); // Position $this->position = count($parent->getParameters()); // Declaring class name if ($parent instanceof ReflectionMethod) { $this->declaringClassName = $parent->getDeclaringClassName(); } return parent::processParent($parent, $tokenStream); } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionParameter */ protected function parse(Stream $tokenStream, IReflection $parent) { return $this ->parseTypeHint($tokenStream) ->parsePassedByReference($tokenStream) ->parseName($tokenStream) ->parseDefaultValue($tokenStream); } /** * Parses the type hint. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionParameter * @throws \TokenReflection\Exception\ParseException If the type hint class name could not be determined. */ private function parseTypeHint(Stream $tokenStream) { $type = $tokenStream->getType(); if (T_ARRAY === $type) { $this->typeHint = self::ARRAY_TYPE_HINT; $this->originalTypeHint = self::ARRAY_TYPE_HINT; $tokenStream->skipWhitespaces(true); } elseif (T_CALLABLE === $type) { $this->typeHint = self::CALLABLE_TYPE_HINT; $this->originalTypeHint = self::CALLABLE_TYPE_HINT; $tokenStream->skipWhitespaces(true); } elseif (T_STRING === $type || T_NS_SEPARATOR === $type) { $className = ''; do { $className .= $tokenStream->getTokenValue(); $tokenStream->skipWhitespaces(true); $type = $tokenStream->getType(); } while (T_STRING === $type || T_NS_SEPARATOR === $type); if ('' === ltrim($className, '\\')) { throw new Exception\ParseException($this, $tokenStream, sprintf('Invalid class name definition: "%s".', $className), Exception\ParseException::LOGICAL_ERROR); } $this->originalTypeHint = $className; } return $this; } /** * Parses if parameter value is passed by reference. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionParameter */ private function parsePassedByReference(Stream $tokenStream) { if ($tokenStream->is('&')) { $this->passedByReference = true; $tokenStream->skipWhitespaces(true); } return $this; } /** * Parses the constant name. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionParameter * @throws \TokenReflection\Exception\ParseException If the parameter name could not be determined. */ protected function parseName(Stream $tokenStream) { if (!$tokenStream->is(T_VARIABLE)) { throw new Exception\ParseException($this, $tokenStream, 'The parameter name could not be determined.', Exception\ParseException::UNEXPECTED_TOKEN); } $this->name = substr($tokenStream->getTokenValue(), 1); $tokenStream->skipWhitespaces(true); return $this; } /** * Parses the parameter default value. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionParameter * @throws \TokenReflection\Exception\ParseException If the default value could not be determined. */ private function parseDefaultValue(Stream $tokenStream) { if ($tokenStream->is('=')) { $tokenStream->skipWhitespaces(true); $level = 0; while (null !== ($type = $tokenStream->getType())) { switch ($type) { case ')': if (0 === $level) { break 2; } case '}': case ']': $level--; break; case '(': case '{': case '[': $level++; break; case ',': if (0 === $level) { break 2; } break; default: break; } $this->defaultValueDefinition[] = $tokenStream->current(); $tokenStream->next(); } if (')' !== $type && ',' !== $type) { throw new Exception\ParseException($this, $tokenStream, 'The property default value is not terminated properly. Expected "," or ")".', Exception\ParseException::UNEXPECTED_TOKEN); } } return $this; } } TokenReflection/ReflectionProperty.php000064400000035457144760110310014222 0ustar00getBroker()->getClass($this->declaringClassName); } /** * Returns the name of the declaring class. * * @return string */ public function getDeclaringClassName() { return $this->declaringClassName; } /** * Returns the property default value. * * @return mixed */ public function getDefaultValue() { if (is_array($this->defaultValueDefinition)) { $this->defaultValue = Resolver::getValueDefinition($this->defaultValueDefinition, $this); $this->defaultValueDefinition = Resolver::getSourceCode($this->defaultValueDefinition); } return $this->defaultValue; } /** * Returns the part of the source code defining the property default value. * * @return string */ public function getDefaultValueDefinition() { return is_array($this->defaultValueDefinition) ? Resolver::getSourceCode($this->defaultValueDefinition) : $this->defaultValueDefinition; } /** * Returns the property value for a particular class instance. * * @param object $object * @return mixed * @throws \TokenReflection\Exception\RuntimeException If it is not possible to return the property value. */ public function getValue($object) { $declaringClass = $this->getDeclaringClass(); if (!$declaringClass->isInstance($object)) { throw new Exception\RuntimeException('The given class is not an instance or subclass of the current class.', Exception\RuntimeException::INVALID_ARGUMENT, $this); } if ($this->isPublic()) { return $object->{$this->name}; } elseif ($this->isAccessible()) { $refClass = new InternalReflectionClass($object); $refProperty = $refClass->getProperty($this->name); $refProperty->setAccessible(true); $value = $refProperty->getValue($object); $refProperty->setAccessible(false); return $value; } throw new Exception\RuntimeException('Only public and accessible properties can return their values.', Exception\RuntimeException::NOT_ACCESSBILE, $this); } /** * Returns if the property was created at compile time. * * All properties in the source code are. * * @return boolean */ public function isDefault() { return true; } /** * Returns property modifiers. * * @return integer */ public function getModifiers() { if (false === $this->modifiersComplete) { $declaringClass = $this->getDeclaringClass(); $declaringClassParent = $declaringClass->getParentClass(); if ($declaringClassParent && $declaringClassParent->hasProperty($this->name)) { $property = $declaringClassParent->getProperty($this->name); if (($this->isPublic() && !$property->isPublic()) || ($this->isProtected() && $property->isPrivate())) { $this->modifiers |= self::ACCESS_LEVEL_CHANGED; } } $this->modifiersComplete = ($this->modifiers & self::ACCESS_LEVEL_CHANGED) || $declaringClass->isComplete(); } return $this->modifiers; } /** * Returns if the property is private. * * @return boolean */ public function isPrivate() { return (bool) ($this->modifiers & InternalReflectionProperty::IS_PRIVATE); } /** * Returns if the property is protected. * * @return boolean */ public function isProtected() { return (bool) ($this->modifiers & InternalReflectionProperty::IS_PROTECTED); } /** * Returns if the property is public. * * @return boolean */ public function isPublic() { return (bool) ($this->modifiers & InternalReflectionProperty::IS_PUBLIC); } /** * Returns if the poperty is static. * * @return boolean */ public function isStatic() { return (bool) ($this->modifiers & InternalReflectionProperty::IS_STATIC); } /** * Returns the string representation of the reflection object. * * @return string */ public function __toString() { return sprintf( "Property [ %s%s%s%s%s\$%s ]\n", $this->isStatic() ? '' : ' ', $this->isPublic() ? 'public ' : '', $this->isPrivate() ? 'private ' : '', $this->isProtected() ? 'protected ' : '', $this->isStatic() ? 'static ' : '', $this->getName() ); } /** * Exports a reflected object. * * @param \TokenReflection\Broker $broker Broker instance * @param string|object $class Class name or class instance * @param string $property Property name * @param boolean $return Return the export instead of outputting it * @return string|null * @throws \TokenReflection\Exception\RuntimeException If requested parameter doesn't exist. */ public static function export(Broker $broker, $class, $property, $return = false) { $className = is_object($class) ? get_class($class) : $class; $propertyName = $property; $class = $broker->getClass($className); if ($class instanceof Invalid\ReflectionClass) { throw new Exception\RuntimeException('Class is invalid.', Exception\RuntimeException::UNSUPPORTED); } elseif ($class instanceof Dummy\ReflectionClass) { throw new Exception\RuntimeException(sprintf('Class %s does not exist.', $className), Exception\RuntimeException::DOES_NOT_EXIST); } $property = $class->getProperty($propertyName); if ($return) { return $property->__toString(); } echo $property->__toString(); } /** * Returns if the property is set accessible. * * @return boolean */ public function isAccessible() { return $this->accessible; } /** * Sets a property to be accessible or not. * * @param boolean $accessible If the property should be accessible. */ public function setAccessible($accessible) { $this->accessible = (bool) $accessible; } /** * Sets the property default value. * * @param mixed $value */ public function setDefaultValue($value) { $this->defaultValue = $value; $this->defaultValueDefinition = var_export($value, true); } /** * Sets value of a property for a particular class instance. * * @param object $object Class instance * @param mixed $value Poperty value * @throws \TokenReflection\Exception\RuntimeException If it is not possible to set the property value. */ public function setValue($object, $value) { $declaringClass = $this->getDeclaringClass(); if (!$declaringClass->isInstance($object)) { throw new Exception\RuntimeException('Instance of or subclass expected.', Exception\RuntimeException::INVALID_ARGUMENT, $this); } if ($this->isPublic()) { $object->{$this->name} = $value; } elseif ($this->isAccessible()) { $refClass = new InternalReflectionClass($object); $refProperty = $refClass->getProperty($this->name); $refProperty->setAccessible(true); $refProperty->setValue($object, $value); $refProperty->setAccessible(false); if ($this->isStatic()) { $this->setDefaultValue($value); } } else { throw new Exception\RuntimeException('Only public and accessible properties can be set.', Exception\RuntimeException::NOT_ACCESSBILE, $this); } } /** * Returns imported namespaces and aliases from the declaring namespace. * * @return array */ public function getNamespaceAliases() { return $this->getDeclaringClass()->getNamespaceAliases(); } /** * Creates a property alias for the given class. * * @param \TokenReflection\ReflectionClass $parent New parent class * @return \TokenReflection\ReflectionProperty */ public function alias(ReflectionClass $parent) { $property = clone $this; $property->declaringClassName = $parent->getName(); return $property; } /** * Returns the defining trait. * * @return \TokenReflection\IReflectionClass|null */ public function getDeclaringTrait() { return null === $this->declaringTraitName ? null : $this->getBroker()->getClass($this->declaringTraitName); } /** * Returns the declaring trait name. * * @return string|null */ public function getDeclaringTraitName() { return $this->declaringTraitName; } /** * Returns an element pretty (docblock compatible) name. * * @return string */ public function getPrettyName() { return sprintf('%s::$%s', $this->declaringClassName ?: $this->declaringTraitName, $this->name); } /** * Processes the parent reflection object. * * @param \TokenReflection\IReflection $parent Parent reflection object * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionElement * @throws \TokenReflection\Exception\Parse If an invalid parent reflection object was provided. */ protected function processParent(IReflection $parent, Stream $tokenStream) { if (!$parent instanceof ReflectionClass) { throw new Exception\ParseException($this, $tokenStream, 'The parent object has to be an instance of TokenReflection\ReflectionClass.', Exception\ParseException::INVALID_PARENT); } $this->declaringClassName = $parent->getName(); if ($parent->isTrait()) { $this->declaringTraitName = $parent->getName(); } return parent::processParent($parent, $tokenStream); } /** * Parses reflected element metadata from the token stream. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\IReflection $parent Parent reflection object * @return \TokenReflection\ReflectionProperty */ protected function parse(Stream $tokenStream, IReflection $parent) { $this->parseModifiers($tokenStream, $parent); if (false === $this->docComment->getDocComment()) { $this->parseDocComment($tokenStream, $parent); } return $this->parseName($tokenStream) ->parseDefaultValue($tokenStream); } /** * Parses class modifiers (abstract, final) and class type (class, interface). * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @param \TokenReflection\ReflectionClass $class Defining class * @return \TokenReflection\ReflectionClass * @throws \TokenReflection\Exception\ParseException If the modifiers value cannot be determined. */ private function parseModifiers(Stream $tokenStream, ReflectionClass $class) { while (true) { switch ($tokenStream->getType()) { case T_PUBLIC: case T_VAR: $this->modifiers |= InternalReflectionProperty::IS_PUBLIC; break; case T_PROTECTED: $this->modifiers |= InternalReflectionProperty::IS_PROTECTED; break; case T_PRIVATE: $this->modifiers |= InternalReflectionProperty::IS_PRIVATE; break; case T_STATIC: $this->modifiers |= InternalReflectionProperty::IS_STATIC; break; default: break 2; } $tokenStream->skipWhitespaces(true); } if (InternalReflectionProperty::IS_STATIC === $this->modifiers) { $this->modifiers |= InternalReflectionProperty::IS_PUBLIC; } elseif (0 === $this->modifiers) { $parentProperties = $class->getOwnProperties(); if (empty($parentProperties)) { throw new Exception\ParseException($this, $tokenStream, 'No access level defined and no previous defining class property present.', Exception\ParseException::LOGICAL_ERROR); } $sibling = array_pop($parentProperties); if ($sibling->isPublic()) { $this->modifiers = InternalReflectionProperty::IS_PUBLIC; } elseif ($sibling->isPrivate()) { $this->modifiers = InternalReflectionProperty::IS_PRIVATE; } elseif ($sibling->isProtected()) { $this->modifiers = InternalReflectionProperty::IS_PROTECTED; } else { throw new Exception\ParseException($this, $tokenStream, sprintf('Property sibling "%s" has no access level defined.', $sibling->getName()), Exception\Parse::PARSE_ELEMENT_ERROR); } if ($sibling->isStatic()) { $this->modifiers |= InternalReflectionProperty::IS_STATIC; } } return $this; } /** * Parses the property name. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionProperty * @throws \TokenReflection\Exception\ParseException If the property name could not be determined. */ protected function parseName(Stream $tokenStream) { if (!$tokenStream->is(T_VARIABLE)) { throw new Exception\ParseException($this, $tokenStream, 'The property name could not be determined.', Exception\ParseException::LOGICAL_ERROR); } $this->name = substr($tokenStream->getTokenValue(), 1); $tokenStream->skipWhitespaces(true); return $this; } /** * Parses the propety default value. * * @param \TokenReflection\Stream\StreamBase $tokenStream Token substream * @return \TokenReflection\ReflectionProperty * @throws \TokenReflection\Exception\ParseException If the property default value could not be determined. */ private function parseDefaultValue(Stream $tokenStream) { $type = $tokenStream->getType(); if (';' === $type || ',' === $type) { // No default value return $this; } if ('=' === $type) { $tokenStream->skipWhitespaces(true); } $level = 0; while (null !== ($type = $tokenStream->getType())) { switch ($type) { case ',': if (0 !== $level) { break; } case ';': break 2; case ')': case ']': case '}': $level--; break; case '(': case '{': case '[': $level++; break; default: break; } $this->defaultValueDefinition[] = $tokenStream->current(); $tokenStream->next(); } if (',' !== $type && ';' !== $type) { throw new Exception\ParseException($this, $tokenStream, 'The property default value is not terminated properly. Expected "," or ";".', Exception\ParseException::UNEXPECTED_TOKEN); } return $this; } } TokenReflection/Resolver.php000064400000022543144760110310012154 0ustar00getNamespaceName(); } elseif ($reflection instanceof ReflectionParameter) { $namespace = $reflection->getDeclaringFunction()->getNamespaceName(); } elseif ($reflection instanceof ReflectionProperty || $reflection instanceof ReflectionMethod) { $namespace = $reflection->getDeclaringClass()->getNamespaceName(); } else { throw new Exception\RuntimeException('Invalid reflection object given.', Exception\RuntimeException::INVALID_ARGUMENT, $reflection); } // Process __LINE__ constants; replace with the line number of the corresponding token foreach ($tokens as $index => $token) { if (T_LINE === $token[0]) { $tokens[$index] = array( T_LNUMBER, $token[2], $token[2] ); } } $source = self::getSourceCode($tokens); $constants = self::findConstants($tokens, $reflection); if (!empty($constants)) { foreach (array_reverse($constants, true) as $offset => $constant) { $value = ''; try { switch ($constant) { case '__LINE__': throw new Exception\RuntimeException('__LINE__ constant cannot be resolved this way.', Exception\RuntimeException::UNSUPPORTED, $reflection); case '__FILE__': $value = $reflection->getFileName(); break; case '__DIR__': $value = dirname($reflection->getFileName()); break; case '__FUNCTION__': if ($reflection instanceof IReflectionParameter) { $value = $reflection->getDeclaringFunctionName(); } elseif ($reflection instanceof IReflectionFunctionBase) { $value = $reflection->getName(); } break; case '__CLASS__': if ($reflection instanceof IReflectionConstant || $reflection instanceof IReflectionParameter || $reflection instanceof IReflectionProperty || $reflection instanceof IReflectionMethod) { $value = $reflection->getDeclaringClassName() ?: ''; } break; case '__TRAIT__': if ($reflection instanceof IReflectionMethod || $reflection instanceof IReflectionProperty) { $value = $reflection->getDeclaringTraitName() ?: ''; } elseif ($reflection instanceof IReflectionParameter) { $method = $reflection->getDeclaringFunction(); if ($method instanceof IReflectionMethod) { $value = $method->getDeclaringTraitName() ?: ''; } } break; case '__METHOD__': if ($reflection instanceof IReflectionParameter) { if (null !== $reflection->getDeclaringClassName()) { $value = $reflection->getDeclaringClassName() . '::' . $reflection->getDeclaringFunctionName(); } else { $value = $reflection->getDeclaringFunctionName(); } } elseif ($reflection instanceof IReflectionConstant || $reflection instanceof IReflectionProperty) { $value = $reflection->getDeclaringClassName() ?: ''; } elseif ($reflection instanceof IReflectionMethod) { $value = $reflection->getDeclaringClassName() . '::' . $reflection->getName(); } elseif ($reflection instanceof IReflectionFunction) { $value = $reflection->getName(); } break; case '__NAMESPACE__': if (($reflection instanceof IReflectionConstant && null !== $reflection->getDeclaringClassName()) || $reflection instanceof IReflectionProperty) { $value = $reflection->getDeclaringClass()->getNamespaceName(); } elseif ($reflection instanceof IReflectionParameter) { if (null !== $reflection->getDeclaringClassName()) { $value = $reflection->getDeclaringClass()->getNamespaceName(); } else { $value = $reflection->getDeclaringFunction()->getNamespaceName(); } } elseif ($reflection instanceof IReflectionMethod) { $value = $reflection->getDeclaringClass()->getNamespaceName(); } else { $value = $reflection->getNamespaceName(); } break; default: if (0 === stripos($constant, 'self::') || 0 === stripos($constant, 'parent::')) { // Handle self:: and parent:: definitions if ($reflection instanceof ReflectionConstant) { throw new Exception\RuntimeException('Constants cannot use self:: and parent:: references.', Exception\RuntimeException::UNSUPPORTED, $reflection); } elseif ($reflection instanceof ReflectionParameter && null === $reflection->getDeclaringClassName()) { throw new Exception\RuntimeException('Function parameters cannot use self:: and parent:: references.', Exception\RuntimeException::UNSUPPORTED, $reflection); } if (0 === stripos($constant, 'self::')) { $className = $reflection->getDeclaringClassName(); } else { $declaringClass = $reflection->getDeclaringClass(); $className = $declaringClass->getParentClassName() ?: self::CONSTANT_NOT_FOUND; } $constantName = $className . substr($constant, strpos($constant, '::')); } else { $constantName = self::resolveClassFQN($constant, $reflection->getNamespaceAliases(), $namespace); if ($cnt = strspn($constant, '\\')) { $constantName = str_repeat('\\', $cnt) . $constantName; } } $reflection = $reflection->getBroker()->getConstant($constantName); $value = $reflection->getValue(); } } catch (Exception\RuntimeException $e) { $value = self::CONSTANT_NOT_FOUND; } $source = substr_replace($source, var_export($value, true), $offset, strlen($constant)); } } return self::evaluate(sprintf("return %s;\n", $source)); } /** * Returns a part of the source code defined by given tokens. * * @param array $tokens Tokens array * @return array */ final public static function getSourceCode(array $tokens) { if (empty($tokens)) { return null; } $source = ''; foreach ($tokens as $token) { $source .= $token[1]; } return $source; } /** * Finds constant names in the token definition. * * @param array $tokens Tokenized source code * @param \TokenReflection\ReflectionElement $reflection Caller reflection * @return array */ final public static function findConstants(array $tokens, ReflectionElement $reflection) { static $accepted = array( T_DOUBLE_COLON => true, T_STRING => true, T_NS_SEPARATOR => true, T_CLASS_C => true, T_DIR => true, T_FILE => true, T_LINE => true, T_FUNC_C => true, T_METHOD_C => true, T_NS_C => true, T_TRAIT_C => true ); static $dontResolve = array('true' => true, 'false' => true, 'null' => true); // Adding a dummy token to the end $tokens[] = array(null); $constants = array(); $constant = ''; $offset = 0; foreach ($tokens as $token) { if (isset($accepted[$token[0]])) { $constant .= $token[1]; } elseif ('' !== $constant) { if (!isset($dontResolve[strtolower($constant)])) { $constants[$offset - strlen($constant)] = $constant; } $constant = ''; } if (null !== $token[0]) { $offset += strlen($token[1]); } } return $constants; } /** * Evaluates a source code. * * @param string $source Source code * @return mixed */ final private static function evaluate($source) { return eval($source); } } TokenReflection/Stream/FileStream.php000064400000002223144760110310013632 0ustar00fileName = Broker::getRealPath($fileName); if (false === $this->fileName) { throw new Exception\StreamException($this, 'File does not exist.', Exception\StreamException::DOES_NOT_EXIST); } $contents = @file_get_contents($this->fileName); if (false === $contents) { throw new Exception\StreamException($this, 'File is not readable.', Exception\StreamException::NOT_READABLE); } $this->processSource($contents); } }TokenReflection/Stream/StreamBase.php000064400000026127144760110310013636 0ustar00 true, T_WHITESPACE => true, T_DOC_COMMENT => true, T_INLINE_HTML => true, T_ENCAPSED_AND_WHITESPACE => true, T_CONSTANT_ENCAPSED_STRING => true); foreach ($stream as $position => $token) { if (is_array($token)) { if (!NATIVE_TRAITS && T_STRING === $token[0]) { $lValue = strtolower($token[1]); if ('trait' === $lValue) { $token[0] = T_TRAIT; } elseif ('insteadof' === $lValue) { $token[0] = T_INSTEADOF; } elseif ('__TRAIT__' === $token[1]) { $token[0] = T_TRAIT_C; } elseif ('callable' === $lValue) { $token[0] = T_CALLABLE; } } $this->tokens[] = $token; } else { $previous = $this->tokens[$position - 1]; $line = $previous[2]; if (isset($checkLines[$previous[0]])) { $line += substr_count($previous[1], "\n"); } $this->tokens[] = array($token, $token, $line); } } $this->count = count($this->tokens); } /** * Returns the file name this is a part of. * * @return string */ public function getFileName() { return $this->fileName; } /** * Returns the original source code. * * @return string */ public function getSource() { return $this->getSourcePart(); } /** * Returns a part of the source code. * * @param mixed $start Start offset * @param mixed $end End offset * @return string */ public function getSourcePart($start = null, $end = null) { $start = (int) $start; $end = null === $end ? ($this->count - 1) : (int) $end; $source = ''; for ($i = $start; $i <= $end; $i++) { $source .= $this->tokens[$i][1]; } return $source; } /** * Finds the position of the token of the given type. * * @param integer|string $type Token type * @return \TokenReflection\Stream|boolean */ public function find($type) { $actual = $this->position; while (isset($this->tokens[$this->position])) { if ($type === $this->tokens[$this->position][0]) { return $this; } $this->position++; } $this->position = $actual; return false; } /** * Returns the position of the token with the matching bracket. * * @return \TokenReflection\Stream * @throws \TokenReflection\Exception\RuntimeException If out of the token stream. * @throws \TokenReflection\Exception\RuntimeException If there is no bracket at the current position. * @throws \TokenReflection\Exception\RuntimeException If the matching bracket could not be found. */ public function findMatchingBracket() { static $brackets = array( '(' => ')', '{' => '}', '[' => ']', T_CURLY_OPEN => '}', T_DOLLAR_OPEN_CURLY_BRACES => '}' ); if (!$this->valid()) { throw new Exception\StreamException($this, 'Out of token stream.', Exception\StreamException::READ_BEYOND_EOS); } $position = $this->position; $bracket = $this->tokens[$this->position][0]; if (!isset($brackets[$bracket])) { throw new Exception\StreamException($this, sprintf('There is no usable bracket at position "%d".', $position), Exception\StreamException::DOES_NOT_EXIST); } $searching = $brackets[$bracket]; $level = 0; while (isset($this->tokens[$this->position])) { $type = $this->tokens[$this->position][0]; if ($searching === $type) { $level--; } elseif ($bracket === $type || ($searching === '}' && ('{' === $type || T_CURLY_OPEN === $type || T_DOLLAR_OPEN_CURLY_BRACES === $type))) { $level++; } if (0 === $level) { return $this; } $this->position++; } throw new Exception\StreamException($this, sprintf('Could not find the end bracket "%s" of the bracket at position "%d".', $searching, $position), Exception\StreamException::DOES_NOT_EXIST); } /** * Skips whitespaces and comments next to the current position. * * @param boolean $skipDocBlocks Skip docblocks as well * @return \TokenReflection\Stream\StreamBase */ public function skipWhitespaces($skipDocBlocks = false) { static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => true); do { $this->position++; } while (isset($this->tokens[$this->position]) && isset($skipped[$this->tokens[$this->position][0]]) && ($skipDocBlocks || $this->tokens[$this->position][0] !== T_DOC_COMMENT)); return $this; } /** * Returns if the token stream is at a whitespace position. * * @param boolean $docBlock Consider docblocks as whitespaces * @return boolean */ public function isWhitespace($docBlock = false) { static $skipped = array(T_WHITESPACE => true, T_COMMENT => true, T_DOC_COMMENT => false); if (!$this->valid()) { return false; } return $docBlock ? isset($skipped[$this->getType()]) : !empty($skipped[$this->getType()]); } /** * Checks if there is a token of the given type at the given position. * * @param integer|string $type Token type * @param integer $position Position; if none given, consider the current iteration position * @return boolean */ public function is($type, $position = -1) { return $type === $this->getType($position); } /** * Returns the type of a token. * * @param integer $position Token position; if none given, consider the current iteration position * @return string|integer|null */ public function getType($position = -1) { if (-1 === $position) { $position = $this->position; } return isset($this->tokens[$position]) ? $this->tokens[$position][0] : null; } /** * Returns the current token value. * * @param integer $position Token position; if none given, consider the current iteration position * @return stirng */ public function getTokenValue($position = -1) { if (-1 === $position) { $position = $this->position; } return isset($this->tokens[$position]) ? $this->tokens[$position][1] : null; } /** * Returns the token type name. * * @param integer $position Token position; if none given, consider the current iteration position * @return string|null */ public function getTokenName($position = -1) { $type = $this->getType($position); if (is_string($type)) { return $type; } elseif (T_TRAIT === $type) { return 'T_TRAIT'; } elseif (T_INSTEADOF === $type) { return 'T_INSTEADOF'; } elseif (T_CALLABLE === $type) { return 'T_CALLABLE'; } return token_name($type); } /** * Stream serialization. * * @return string */ public function serialize() { return serialize(array($this->fileName, $this->tokens)); } /** * Restores the stream from the serialized state. * * @param string $serialized Serialized form * @throws \TokenReflection\Exception\StreamException On deserialization error. */ public function unserialize($serialized) { $data = @unserialize($serialized); if (false === $data) { throw new Exception\StreamException($this, 'Could not deserialize the serialized data.', Exception\StreamException::SERIALIZATION_ERROR); } if (2 !== count($data) || !is_string($data[0]) || !is_array($data[1])) { throw new Exception\StreamException($this, 'Invalid serialization data.', Exception\StreamException::SERIALIZATION_ERROR); } $this->fileName = $data[0]; $this->tokens = $data[1]; $this->count = count($this->tokens); $this->position = 0; } /** * Checks of there is a token with the given index. * * @param integer $offset Token index * @return boolean */ public function offsetExists($offset) { return isset($this->tokens[$offset]); } /** * Removes a token. * * Unsupported. * * @param integer $offset Position * @throws \TokenReflection\Exception\StreamException Unsupported. */ public function offsetUnset($offset) { throw new Exception\StreamException($this, 'Removing of tokens from the stream is not supported.', Exception\StreamException::UNSUPPORTED); } /** * Returns a token at the given index. * * @param integer $offset Token index * @return mixed */ public function offsetGet($offset) { return isset($this->tokens[$offset]) ? $this->tokens[$offset] : null; } /** * Sets a value of a particular token. * * Unsupported * * @param integer $offset Position * @param mixed $value Value * @throws \TokenReflection\Exception\StreamException Unsupported. */ public function offsetSet($offset, $value) { throw new Exception\StreamException($this, 'Setting token values is not supported.', Exception\StreamException::UNSUPPORTED); } /** * Returns the current internal pointer value. * * @return integer */ public function key() { return $this->position; } /** * Advances the internal pointer. * * @return \TokenReflection\Stream */ public function next() { $this->position++; return $this; } /** * Sets the internal pointer to zero. * * @return \TokenReflection\Stream */ public function rewind() { $this->position = 0; return $this; } /** * Returns the current token. * * @return array|null */ public function current() { return isset($this->tokens[$this->position]) ? $this->tokens[$this->position] : null; } /** * Checks if there is a token on the current position. * * @return boolean */ public function valid() { return isset($this->tokens[$this->position]); } /** * Returns the number of tokens in the stream. * * @return integer */ public function count() { return $this->count; } /** * Sets the internal pointer to the given value. * * @param integer $position New position * @return \TokenReflection\Stream */ public function seek($position) { $this->position = (int) $position; return $this; } /** * Returns the stream source code. * * @return string */ public function __toString() { return $this->getSource(); } } TokenReflection/Stream/StringStream.php000064400000001254144760110310014224 0ustar00fileName = $fileName; $this->processSource($source); } }build.xml000064400000007541144760110310006371 0ustar00 build/phpcs.xml000064400000000414144760110310007476 0ustar00 TokenReflection coding standard 0 build/phpmd.xml000064400000001077144760110310007477 0ustar00 TokenReflection PHPMD rule set build/phpunit.xml000064400000001246144760110310010054 0ustar00 ../tests/TokenReflection ../TokenReflection composer.json000064400000001137144760110310007265 0ustar00{ "name": "andrewsville/php-token-reflection", "type": "library", "description": "Library emulating the PHP internal reflection using just the tokenized source code.", "keywords": ["library", "tokenizer", "reflection"], "homepage": "http://andrewsville.github.com/PHP-Token-Reflection/", "license": "BSD-3", "authors": [ { "name": "Ondřej Nešpor", "homepage": "https://github.com/Andrewsville" }, { "name": "Jaroslav Hanslík", "homepage": "https://github.com/kukulich" } ], "require": { "php": ">=5.3" }, "autoload": { "psr-0": { "TokenReflection": "./" } } } package.xml000064400000010472144760110310006662 0ustar00 TokenReflection pear.andrewsville.cz PHP reflection replacement. Reflection library that does not need to load the source code and uses just the tokenized source code. Compatible with the PHP reflection and adds many useful features. Ondřej Nešpor andrewsville andrew@andrewsville.cz yes Jaroslav Hanslík kukulich kontakt@kukulich.cz yes 2012-08-25 1.3.1 1.0.0 stable stable New BSD - 5.3.0 1.4.0 tokenizer tests/TokenReflection/ConsistencyTest.php000064400000017723144760110310014662 0ustar00createBroker(); $broker->processFile(__DIR__ . '/../data/constant/in-namespace.php'); try { $broker->processFile(__DIR__ . '/../data/duplicities/otherfile.php'); } catch (Exception\FileProcessingException $e) { // Expected } $this->assertNotSame(null, @constant('PHP_INT_MAX')); $constants = array( 'tokenized' => $broker->getConstant('TokenReflection\\Test\\CONSTANT_IN_NAMESPACE'), 'internal' => $broker->getConstant('PHP_INT_MAX'), 'invalid' => $broker->getConstant('DUPLICITIES_CONSTANTS_1') ); // Test cross-consistency foreach ($constants as $referenceType => $referenceConstant) { foreach ($constants as $type => $constant) { if ($referenceType !== $type) { $this->crossConsistencyTest($referenceConstant, $constant); } } } } /** * Tests reflection consistency. */ public function testClassReflectionConsistency() { $broker = $this->createBroker(); $broker->processFile(__FILE__); try { $broker->processFile(__DIR__ . '/../data/duplicities/otherfile.php'); } catch (Exception\FileProcessingException $e) { // Expected } $this->assertFalse(class_exists('Foo\\Bar', true)); $classes = array( 'tokenized' => $broker->getClass('TokenReflection\\ConsistencyTest'), 'internal' => $broker->getClass('Exception'), 'dummy' => $broker->getClass('Foo\\Bar'), 'invalid' => $broker->getClass('duplicitiesClasses1') ); // Test consistency with the internal reflection foreach ($classes as $class) { $this->internalConsistencyTest(new \ReflectionClass(new \stdClass()), $class); } // Test cross-consistency foreach ($classes as $referenceType => $referenceClass) { foreach ($classes as $type => $class) { if ($referenceType !== $type) { $this->crossConsistencyTest($referenceClass, $class); } } } } /** * Tests reflection consistency. */ public function testFunctionReflectionConsistency() { $broker = $this->createBroker(); $broker->processFile(__DIR__ . '/../data/function/in-namespace.php'); try { $broker->processFile(__DIR__ . '/../data/duplicities/otherfile.php'); } catch (Exception\FileProcessingException $e) { // Expected } $this->assertTrue(function_exists('constant')); $functions = array( 'tokenized' => $broker->getFunction('TokenReflection\\Test\\functionInNamespace'), 'internal' => $broker->getFunction('constant'), 'invalid' => $broker->getFunction('duplicitiesFunctions1') ); // Test consistency with the internal reflection foreach ($functions as $function) { $this->internalConsistencyTest(new \ReflectionFunction('constant'), $function); } // Test cross-consistency foreach ($functions as $referenceType => $referenceFunction) { foreach ($functions as $type => $function) { if ($referenceType !== $type) { $this->crossConsistencyTest($referenceFunction, $function); } } } } /** * Tests reflection consistency. */ public function testPropertyReflectionConsistency() { $broker = $this->createBroker(); $broker->processFile(__DIR__ . '/../data/property/lines.php'); $this->assertTrue(function_exists('constant')); $properties = array( 'tokenized' => $broker->getClass('TokenReflection_Test_PropertyLines')->getProperty('lines'), 'internal' => $broker->getClass('Exception')->getProperty('message') ); // Test consistency with the internal reflection foreach ($properties as $property) { $this->internalConsistencyTest(new \ReflectionProperty('Exception', 'message'), $property); } // Test cross-consistency foreach ($properties as $referenceType => $referenceProperty) { foreach ($properties as $type => $property) { if ($referenceType !== $type) { $this->crossConsistencyTest($referenceProperty, $property); } } } } /** * Tests reflection consistency. */ public function testMethodReflectionConsistency() { $broker = $this->createBroker(); $broker->processFile(__DIR__ . '/../data/method/access-level.php'); $methods = array( 'tokenized' => $broker->getClass('TokenReflection_Test_MethodAccessLevelParent')->getMethod('privateNoExtended'), 'internal' => $broker->getClass('Exception')->getMethod('getMessage') ); // Test consistency with the internal reflection foreach ($methods as $method) { $this->internalConsistencyTest(new \ReflectionMethod('Exception', 'getMessage'), $method); } // Test cross-consistency foreach ($methods as $referenceType => $referenceMethod) { foreach ($methods as $type => $method) { if ($referenceType !== $type) { $this->crossConsistencyTest($referenceMethod, $method); } } } } /** * Tests reflection consistency. */ public function testParameterReflectionConsistency() { $broker = $this->createBroker(); $broker->processFile(__DIR__ . '/../data/parameter/optional-false.php'); $parameters = array( 'tokenized' => $broker->getFunction('tokenReflectionParameterOptionalFalse')->getParameter('one'), 'internal' => $broker->getFunction('constant')->getParameter('const_name') ); // Test consistency with the internal reflection foreach ($parameters as $parameter) { $this->internalConsistencyTest(new \ReflectionParameter('constant', 'const_name'), $parameter); } // Test cross-consistency foreach ($parameters as $referenceType => $referenceParameter) { foreach ($parameters as $type => $parameter) { if ($referenceType !== $type) { $this->crossConsistencyTest($referenceParameter, $parameter); } } } } /** * Tests API consistency of two TR reflections. * * @param \TokenReflection\IReflection $reference Reference reflection * @param \TokenReflection\IReflection $token Tested reflection */ private function crossConsistencyTest(IReflection $reference, IReflection $token) { $this->performConsistencyTest(new \ReflectionClass($reference), new \ReflectionClass($token)); } /** * Tests API consistency of an internal reflection and TR. * * @param \Reflector $reference Reference reflection * @param \TokenReflection\IReflection $token Tested reflection */ private function internalConsistencyTest(\Reflector $reference, IReflection $token) { $this->performConsistencyTest(new \ReflectionClass($reference), new \ReflectionClass($token)); } /** * Tests API consistency of two classes. * * @param \ReflectionClass $reference Reference class reflection * @param \ReflectionClass $test Tested class reflection */ private function performConsistencyTest(\ReflectionClass $reference, \ReflectionClass $test) { static $skip = array( '*' => array('addReason' => true, 'getReasons' => true, 'hasReasons' => true), 'TokenReflection\\Php\\IReflection' => array('alias' => true, 'getFileReflection' => true, 'getSource' => true, 'getStartPosition' => true, 'getEndPosition' => true), 'TokenReflection\\Php\\ReflectionProperty' => array('setDefaultValue' => true) ); $methods = $reference->getMethods(\ReflectionMethod::IS_PUBLIC); foreach ($methods as $method) { if ($method->isStatic()) { continue; } if (isset($skip['*'][$method->getName()])) { continue; } foreach ($skip as $className => $skipping) { if (isset($skipping[$method->getName()]) && ($className === $test->getName() || $test->isSubclassOf($className))) { continue 2; } } $this->assertTrue($test->hasMethod($method->getName()), sprintf('%s::%s() (defined in %s)', $test->getName(), $method->getName(), $reference->getName())); } } } tests/TokenReflection/DuplicitiesTest.php000064400000011244144760110310014627 0ustar00getBroker(); try { $broker->processFile($fileName = $this->getFilePath('constants')); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (Exception\FileProcessingException $e) { // Expected } $this->assertTrue($broker->hasConstant('DUPLICITIES_CONSTANTS_1')); $this->assertTrue($broker->hasConstant('DUPLICITIES_CONSTANTS_2')); $this->assertTrue($broker->hasFunction('duplicitiesConstants')); $this->assertTrue($broker->hasClass('duplicitiesConstants')); $constant = $broker->getConstant('DUPLICITIES_CONSTANTS_1'); $this->assertInstanceOf('TokenReflection\\Invalid\\ReflectionConstant', $constant); $this->assertSame($fileName, $constant->getFileName()); $this->assertTrue($constant->hasReasons()); } /** * Tests duplicit functions. */ public function testFunctions() { $broker = $this->getBroker(); try { $broker->processFile($fileName = $this->getFilePath('functions')); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (Exception\FileProcessingException $e) { // Expected } $this->assertTrue($broker->hasFunction('duplicitiesFunctions1')); $this->assertTrue($broker->hasFunction('duplicitiesFunctions2')); $this->assertTrue($broker->hasClass('duplicitiesFunctions')); $this->assertTrue($broker->hasConstant('DUPLICITIES_FUNCTIONS')); $function = $broker->getFunction('duplicitiesFunctions1'); $this->assertInstanceOf('TokenReflection\\Invalid\\ReflectionFunction', $function); $this->assertSame($fileName, $function->getFileName()); $this->assertTrue($function->hasReasons()); } /** * Tests duplicit classes. */ public function testClasses() { $broker = $this->getBroker(); try { $broker->processFile($fileName = $this->getFilePath('classes')); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (Exception\FileProcessingException $e) { // Expected } $this->assertTrue($broker->hasClass('duplicitiesClasses1')); $this->assertTrue($broker->hasClass('duplicitiesClasses2')); $this->assertTrue($broker->hasFunction('duplicitiesClasses')); $this->assertTrue($broker->hasConstant('DUPLICITIES_CLASSES')); $class = $broker->getClass('duplicitiesClasses1'); $this->assertInstanceOf('TokenReflection\\Invalid\\ReflectionClass', $class); $this->assertSame($fileName, $class->getFileName()); $this->assertTrue($class->hasReasons()); } /** * Tests duplicities from an another file. */ public function testOtherFile() { $broker = $this->getBroker(); try { $broker->processFile($fileName = $this->getFilePath('otherfile')); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (Exception\FileProcessingException $e) { // Expected } static $elements = array( 'classes' => array( 'duplicitiesConstants', 'duplicitiesFunctions', 'duplicitiesClasses1', 'duplicitiesClasses2' ), 'functions' => array( 'duplicitiesConstants', 'duplicitiesFunctions1', 'duplicitiesFunctions2', 'duplicitiesClasses' ), 'constants' => array( 'DUPLICITIES_CONSTANTS_1', 'DUPLICITIES_CONSTANTS_2', 'DUPLICITIES_FUNCTIONS', 'DUPLICITIES_CLASSES' ) ); foreach ($elements as $type => $names) { foreach ($names as $name) { switch ($type) { case 'classes': $this->assertTrue($broker->hasClass($name)); $reflection = $broker->getClass($name); $this->assertInstanceOf('TokenReflection\\Invalid\\ReflectionClass', $reflection); break; case 'functions': $this->assertTrue($broker->hasFunction($name)); $reflection = $broker->getFunction($name); $this->assertInstanceOf('TokenReflection\\Invalid\\ReflectionFunction', $reflection); break; case 'constants': $this->assertTrue($broker->hasConstant($name)); $reflection = $broker->getConstant($name); $this->assertInstanceOf('TokenReflection\\Invalid\\ReflectionConstant', $reflection); break; } $this->assertTrue($reflection->hasReasons()); $this->assertNotSame($fileName, $reflection->getFileName()); } } } } tests/TokenReflection/ParseErrorTest.php000064400000010301144760110310014426 0ustar00getBroker()); } /** * Tests invalid source code handling. * * @param string $testName Test name * @dataProvider invalidClassSourceCodeProvider * @expectedException \TokenReflection\Exception\ParseException */ public function testClasses($testName) { $this->performTest($testName); } /** * Tests invalid source code handling. * * @param string $testName Test name * @dataProvider invalidConstantSourceCodeProvider * @expectedException \TokenReflection\Exception\ParseException */ public function testConstants($testName) { $this->performTest($testName); } /** * Tests invalid source code handling. * * @param string $testName Test name * @dataProvider invalidFileSourceCodeProvider * @expectedException \TokenReflection\Exception\ParseException */ public function testFiles($testName) { $this->performTest($testName); } /** * Tests invalid source code handling. * * @param string $testName Test name * @dataProvider invalidFunctionBaseSourceCodeProvider * @expectedException \TokenReflection\Exception\ParseException */ public function testFunctionBases($testName) { $this->performTest($testName); } /** * Tests invalid source code handling. * * @param string $testName Test name * @dataProvider invalidParameterSourceCodeProvider * @expectedException \TokenReflection\Exception\ParseException */ public function testParameter($testName) { $this->performTest($testName); } /** * Tests invalid source code handling. * * @param string $testName Test name * @dataProvider invalidPropertySourceCodeProvider * @expectedException \TokenReflection\Exception\ParseException */ public function testProperty($testName) { $this->performTest($testName); } /** * Performs a test. * * @param string $testName Test name */ private function performTest($testName) { $broker = $this->getBroker(); $broker->processFile($this->getFilePath($testName)); } /** * Provider for invalid class source code handling tests. * * @return array */ public function invalidClassSourceCodeProvider() { return $this->prepareTests('invalid-class', 10); } /** * Provider for invalid constant source code handling tests. * * @return array */ public function invalidConstantSourceCodeProvider() { return $this->prepareTests('invalid-constant', 4); } /** * Provider for invalid file source code handling tests. * * @return array */ public function invalidFileSourceCodeProvider() { return $this->prepareTests('invalid-file', 7); } /** * Provider for invalid function/method source code handling tests. * * @return array */ public function invalidFunctionBaseSourceCodeProvider() { return $this->prepareTests('invalid-functionbase', 3); } /** * Provider for invalid function/method source code handling tests. * * @return array */ public function invalidParameterSourceCodeProvider() { return $this->prepareTests('invalid-parameter', 3); } /** * Provider for invalid function/method source code handling tests. * * @return array */ public function invalidPropertySourceCodeProvider() { return $this->prepareTests('invalid-property', 3); } /** * Prepares test names. * * @param string $prefix Test name prefix * @param integer $count Test count * @return array */ private function prepareTests($prefix, $count) { return array_map(function($i) use ($prefix) { return array($prefix . '-' . $i); }, range(1, $count)); } } tests/TokenReflection/PharTest.php000064400000015730144760110310013247 0ustar00markTestSkipped('Not testing PHAR support on Travis CI.'); } if (!extension_loaded('phar')) { $this->markTestSkipped('The phar extension is required'); } if (ini_get('phar.readonly')) { $this->markTestSkipped('The phar extension must not be set to read-only.'); } } /** * Compares items parsed from filesystem and from a PHAR archive. * * @param \TokenReflection\Broker $filesystem Filesystem TR broker * @param \TokenReflection\Broker $phar PHAR TR broker * @param integer $format PHAR archive format * @param integer $compression PHAR archive compression * @param integer $wholeArchive Whole archive compressed */ private function archiveTest(Broker $filesystem, Broker $phar, $format, $compression, $wholeArchive) { $fsConstants = $filesystem->getConstants(); $pharConstants = $phar->getConstants(); $this->assertSame(count($fsConstants), count($pharConstants)); foreach (array_keys($fsConstants) as $name) { $this->assertArrayHasKey($name, $pharConstants); } $fsClasses = $filesystem->getClasses(); $pharClasses = $phar->getClasses(); $this->assertSame(count($fsClasses), count($pharClasses)); foreach (array_keys($fsClasses) as $name) { $this->assertArrayHasKey($name, $pharClasses); } $fsFunctions = $filesystem->getFunctions(); $pharFunctions = $phar->getFunctions(); $this->assertSame(count($fsFunctions), count($pharFunctions)); foreach (array_keys($fsFunctions) as $name) { $this->assertArrayHasKey($name, $pharFunctions); } } /** * Tests the PHAR file format. */ public function testPharArchive() { foreach ($this->prepareData() as $testData) { list($metadata, $filesystem, $phar) = $testData; $this->archiveTest($filesystem, $phar, $metadata['format'], $metadata['compression'], $metadata['wholeArchive']); } } /** * Tests the zipped PHAR file format. */ public function testZippedPharArchive() { if (!extension_loaded('zip')) { $this->markTestSkipped('The zip extension is required to run this test.'); } foreach ($this->prepareData(Phar::ZIP) as $testData) { list($metadata, $filesystem, $phar) = $testData; $this->archiveTest($filesystem, $phar, $metadata['format'], $metadata['compression'], $metadata['wholeArchive']); } } /** * Tests the gzipped PHAR file format. */ public function testGZippedPharArchive() { if (!extension_loaded('zlib')) { $this->markTestSkipped('The zlib extension is required to run this test.'); } $testData = array_merge( $this->prepareData(Phar::PHAR, Phar::GZ, false), $this->prepareData(Phar::TAR, Phar::GZ, false), $this->prepareData(Phar::PHAR, Phar::GZ, true), $this->prepareData(Phar::TAR, Phar::GZ, true) ); foreach ($testData as $testItem) { list($metadata, $filesystem, $phar) = $testItem; $this->archiveTest($filesystem, $phar, $metadata['format'], $metadata['compression'], $metadata['wholeArchive']); } } /** * Tests the bzipped PHAR file format. */ public function testBZippedPharArchive() { if (!extension_loaded('bz2')) { $this->markTestSkipped('The zlib extension is required to run this test.'); } $testData = array_merge( $this->prepareData(Phar::PHAR, Phar::BZ2, false), $this->prepareData(Phar::TAR, Phar::BZ2, false), $this->prepareData(Phar::PHAR, Phar::BZ2, true), $this->prepareData(Phar::TAR, Phar::BZ2, true) ); foreach ($testData as $testItem) { list($metadata, $filesystem, $phar) = $testItem; $this->archiveTest($filesystem, $phar, $metadata['format'], $metadata['compression'], $metadata['wholeArchive']); } } /** * Prepares the temporary storage and returns its path. * * @return string */ private function prepareTemporaryStorage() { $dirName = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('tr_phar_test'); if (!mkdir($dirName)) { $this->fail('Could not create the temporary storage.'); } return $dirName; } /** * Cleans up the temporary storage. * * @param string $path Storage path */ private function cleanUpTemporaryStorage($path) { $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path)); foreach ($iterator as $item) { if ($item->isFile()) { unlink($item->getPathName()); } elseif ($item->isDir() && !$item->isDot()) { rmdir($item->getPathName()); } } rmdir($path); } /** * Data preparer. * * Returns pairs of TokenReflection\Broker where one parses a directory of given type * and the second one parses a PHAR archive that was created from the same directory. * * @param integer $format Archive format * @param integer $compression Archive compression * @param boolean $wholeArchive Use compression for the whole archive * @return array */ private function prepareData($format = Phar::PHAR, $compression = Phar::NONE, $wholeArchive = true) { $dirName = $this->prepareTemporaryStorage(); $directory = realpath(__DIR__ . '/../data/'); $iterator = new \DirectoryIterator($directory); static $skip = array('broker' => true, 'parseerror' => true, 'duplicities' => true); $data = array(); foreach ($iterator as $item) { if (isset($skip[$item->getFileName()])) { continue; } if ($item->isDir() && !$item->isDot()) { $ext = '.phar'; $fileName = $dirName . DIRECTORY_SEPARATOR . uniqid($format . $compression); $phar = new Phar($fileName . $ext); $phar->buildFromDirectory($item->getPathName()); if ($format !== Phar::PHAR) { if ($format === Phar::TAR) { $ext .= '.tar'; } elseif ($format === Phar::ZIP) { $ext .= '.zip'; } $phar->convertToExecutable($format, $wholeArchive ? $compression : Phar::NONE, $ext); } if ($compression !== Phar::NONE && !$wholeArchive) { $phar->compressFiles($compression); } unset($phar); $dataItem = array( array( 'format' => $format, 'compression' => $compression, 'wholeArchive' => $wholeArchive, ) ); $broker = new Broker(new Broker\Backend\Memory(), 0); $broker->processDirectory($item->getPathName()); $dataItem[] = $broker; $broker2 = new Broker(new Broker\Backend\Memory(), 0); $broker2->process($fileName . $ext); $dataItem[] = $broker2; $data[] = $dataItem; } } $this->cleanUpTemporaryStorage($dirName); return $data; } } tests/TokenReflection/ReflectionAnnotationTest.php000064400000002116144760110310016474 0ustar00getBroker(); $broker->processString('assertTrue($broker->hasClass('AnnotationInvalidTemplate')); $class = $broker->getClass('AnnotationInvalidTemplate'); $a = new ReflectionAnnotation($class); $a->setTemplates(array(new \Exception())); } } tests/TokenReflection/ReflectionBrokerTest.php000064400000014644144760110310015617 0ustar00getFileTokenReflection('empty'); } /** * Tests filenames filtering. * * @dataProvider filenameFilterProvider * @param string|array $filters Filename filter(s) * @param array $fileNames Filtered filenames */ public function testFilenameFiltering($filters, array $fileNames) { $broker = new Broker(new Broker\Backend\Memory()); $files = $broker->processDirectory(realpath(__DIR__ . '/../data/class'), $filters, true); $brokerFileNames = array(); foreach ($files as $file) { $brokerFileNames[] = basename($file->getName()); } $this->compareFileLists($fileNames, $brokerFileNames); } /** * Tests directory and filename filtering. * * @dataProvider directoryFilterProvider * @param string|array $filters Filename filter(s) * @param array $fileNames Filtered filenames */ public function testDirectoryFiltering($filters, array $fileNames) { $broker = new Broker(new Broker\Backend\Memory()); $files = $broker->processDirectory(realpath(__DIR__ . '/../data'), $filters, true); $brokerFileNames = array(); foreach ($files as $file) { $brokerFileNames[] = basename($file->getName()); } $this->compareFileLists($fileNames, $brokerFileNames); } /** * Tests an exception thrown when a file could not be processed. * * @expectedException \TokenReflection\Exception\BrokerException */ public function testFileProcessingError() { $file = __DIR__ . DIRECTORY_SEPARATOR . '~#nonexistent#~'; if (is_file($file)) { $this->markTestSkipped(sprintf('File %s exists.', $file)); } $this->getBroker()->processFile($file); } /** * Tests an exception thrown when a file could not be processed. * * @expectedException \TokenReflection\Exception\BrokerException */ public function testDirectoryProcessingError() { $file = __DIR__ . DIRECTORY_SEPARATOR . '~#nonexistent#~' . DIRECTORY_SEPARATOR . '~#nonexistent#~'; if (is_dir($file)) { $this->markTestSkipped(sprintf('Directory %s exists.', $file)); } $this->getBroker()->processDirectory($file); } /** * Tests an exception thrown when a file could not be processed. * * @expectedException \TokenReflection\Exception\BrokerException */ public function testPharProcessingError() { $file = __DIR__ . DIRECTORY_SEPARATOR . '~#nonexistent#~'; if (is_file($file)) { $this->markTestSkipped(sprintf('File %s exists.', $file)); } $this->getBroker()->processPhar($file); } /** * Tests an exception thrown when a file could not be processed. * * @expectedException \TokenReflection\Exception\BrokerException */ public function testProcessingError() { $file = __DIR__ . DIRECTORY_SEPARATOR . '~#nonexistent#~'; if (is_file($file)) { $this->markTestSkipped(sprintf('File %s exists.', $file)); } $this->getBroker()->process($file); } /** * Compares a filename list to an expected one. * * PhpUnit does not seem to let one compare two arrays without having to * have elements in the same order (which is not important at all here). * * @param array $expected Expected filenames list * @param array $actual Actual filenames list */ private function compareFileLists(array $expected, array $actual) { $this->assertSame(count($expected), count($actual)); foreach ($expected as $fileName) { $this->assertTrue(in_array($fileName, $actual)); } } /** * Filename filters provider. * * @return array */ public function filenameFilterProvider() { return array( array( '*.php', array( 'abstract.php', 'abstract-implicit.php', 'constants.php', 'doc-comment.php', 'doc-comment-copydoc.php', 'doc-comment-inheritance.php', 'doc-comment-many-lines.php', 'double-properties.php', 'final.php', 'in-namespace.php', 'instances.php', 'interface.php', 'interfaces.php', 'iterator.php', 'lines.php', 'methods.php', 'modifiers.php', 'new-instance-without-constructor.php', 'no-abstract.php', 'no-constants.php', 'no-doc-comment.php', 'no-final.php', 'no-interface.php', 'no-interfaces.php', 'no-iterator.php', 'no-methods.php', 'no-namespace.php', 'no-parent.php', 'no-properties.php', 'parent.php', 'pretty-names.php', 'private-clone.php', 'private-constructor.php', 'properties.php', 'public-clone.php', 'public-constructor.php', 'traits.php', 'user-defined.php' ) ), array( '*no-*.php', array( 'no-abstract.php', 'no-constants.php', 'no-doc-comment.php', 'no-final.php', 'no-interface.php', 'no-interfaces.php', 'no-iterator.php', 'no-methods.php', 'no-namespace.php', 'no-parent.php', 'no-properties.php' ) ), array( '*-constructor.php', array( 'new-instance-without-constructor.php', 'private-constructor.php', 'public-constructor.php' ) ), ); } /** * Filename filters provider. * * @return array */ public function directoryFilterProvider() { return array( array( '*constant' . DIRECTORY_SEPARATOR . '*.php', array( 'doc-comment.php', 'doc-comment-copydoc.php', 'heredoc.php', 'in-namespace.php', 'interfaces.php', 'lines.php', 'magic.php', 'magic54.php', 'no-comment.php', 'no-namespace.php', 'overriding.php', 'pretty-names.php', 'type-boolean.php', 'type-constant.php', 'type-float.php', 'type-float-negative.php', 'type-integer.php', 'type-integer-negative.php', 'type-null.php', 'type-string.php', 'value-definitions.php' ) ), array( '*doc-comment.php', array( 'doc-comment.php', 'doc-comment.php', 'doc-comment.php', 'doc-comment.php', 'doc-comment.php', 'doc-comment.php', 'doc-comment.php', 'no-doc-comment.php' ) ), array( 'foo.php', array() ) ); } } tests/TokenReflection/ReflectionClassTest.php000064400000172514144760110310015441 0ustar00getBroker(); foreach ($classNames as $className) { $this->assertFalse($broker->hasClass($className)); $class = $broker->getClass($className); $this->assertInstanceOf('TokenReflection\Dummy\ReflectionClass', $class); $nameParts = explode('\\', $className); if (1 === count($nameParts)) { $shortName = $nameParts[0]; $namespaceName = ''; } else { $shortName = array_pop($nameParts); $namespaceName = implode('\\', $nameParts); } $this->assertSame($className, $class->getName()); $this->assertSame($className, $class->getPrettyName()); $this->assertSame($shortName, $class->getShortName()); $this->assertSame($namespaceName, $class->getNamespaceName()); if (empty($namespaceName)) { $this->assertFalse($class->inNamespace()); } else { $this->assertTrue($class->inNamespace()); } $this->assertNull($class->getExtension()); $this->assertFalse($class->getExtensionName()); $this->assertNull($class->getFileName()); $this->assertNull($class->getEndLine()); $this->assertNull($class->getStartLine()); $this->assertFalse($class->getDocComment()); $this->assertSame(array(), $class->getAnnotations()); $this->assertFalse($class->hasAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertNull($class->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame(0, $class->getModifiers()); $this->assertFalse($class->isAbstract()); $this->assertFalse($class->isFinal()); $this->assertFalse($class->isInternal()); $this->assertFalse($class->isInterface()); $this->assertFalse($class->isException()); $this->assertFalse($class->isInstantiable()); $this->assertFalse($class->isCloneable()); $this->assertFalse($class->isIterateable()); $this->assertFalse($class->isInternal()); $this->assertFalse($class->isUserDefined()); $this->assertFalse($class->isTokenized()); $this->assertFalse($class->isComplete()); $this->assertFalse($class->isTrait()); $this->assertSame(array(), $class->getTraits()); $this->assertSame(array(), $class->getTraitNames()); $this->assertSame(array(), $class->getOwnTraits()); $this->assertSame(array(), $class->getOwnTraitNames()); $this->assertSame(array(), $class->getTraitAliases()); $this->assertFalse($class->usesTrait('Any')); $this->assertFalse($class->isSubclassOf('Any')); $this->assertFalse($class->getParentClass()); $this->assertNull($class->getParentClassName()); $this->assertSame(array(), $class->getParentClasses()); $this->assertSame(array(), $class->getParentClassNameList()); $this->assertFalse($class->implementsInterface('Traversable')); $this->assertFalse($class->implementsInterface($broker->getClass('Traversable'))); $this->assertSame(array(), $class->getInterfaces()); $this->assertSame(array(), $class->getOwnInterfaces()); $this->assertSame(array(), $class->getInterfaceNames()); $this->assertSame(array(), $class->getOwnInterfaceNames()); $this->assertNull($class->getConstructor()); $this->assertNull($class->getDestructor()); $this->assertFalse($class->hasMethod('Any')); $this->assertFalse($class->hasOwnMethod('Any')); $this->assertFalse($class->hasTraitMethod('Any')); $this->assertSame(array(), $class->getMethods()); $this->assertSame(array(), $class->getOwnMethods()); $this->assertSame(array(), $class->getTraitMethods()); $this->assertFalse($class->hasConstant('Any')); $this->assertFalse($class->hasOwnConstant('Any')); $this->assertSame(array(), $class->getConstants()); $this->assertSame(array(), $class->getOwnConstants()); $this->assertSame(array(), $class->getConstantReflections()); $this->assertSame(array(), $class->getOwnConstantReflections()); $this->assertSame(array(), $class->getDefaultProperties()); $this->assertFalse($class->hasProperty('Any')); $this->assertFalse($class->hasOwnProperty('Any')); $this->assertFalse($class->hasTraitProperty('Any')); $this->assertSame(array(), $class->getProperties()); $this->assertSame(array(), $class->getOwnProperties()); $this->assertSame(array(), $class->getTraitProperties()); $this->assertSame(array(), $class->getStaticProperties()); $this->assertSame(array(), $class->getDirectSubclasses()); $this->assertSame(array(), $class->getDirectSubclassNames()); $this->assertSame(array(), $class->getDirectImplementers()); $this->assertSame(array(), $class->getDirectImplementerNames()); $this->assertSame(array(), $class->getIndirectSubclasses()); $this->assertSame(array(), $class->getIndirectSubclassNames()); $this->assertSame(array(), $class->getIndirectImplementers()); $this->assertSame(array(), $class->getIndirectImplementerNames()); $this->assertFalse($class->isInstance(new \Exception())); $this->assertSame('', $class->getSource()); $this->assertSame($broker, $class->getBroker()); } } /** * Tests an exception thrown when providing an invalid object. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassImplementsInterface1() { $this->getDummyClassReflection()->implementsInterface(new \Exception()); } /** * Tests an exception thrown when providing an invalid object. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassImplementsInterface2() { $this->getDummyClassReflection()->implementsInterface($this->getBroker()->getClass('Exception')); } /** * Tests an exception thrown when getting a method from a dummy class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassGetMethod() { $this->getDummyClassReflection()->getMethod('any'); } /** * Tests an exception thrown when getting a property from a dummy class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassGetProperty() { $this->getDummyClassReflection()->getProperty('any'); } /** * Tests an exception thrown when getting a static property from a dummy class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassGetStaticProperty() { $this->getDummyClassReflection()->getStaticPropertyValue('any', null); } /** * Tests an exception thrown when setting a static property from a dummy class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassSetStaticProperty() { $this->getDummyClassReflection()->setStaticPropertyValue('foo', 'bar'); } /** * Tests an exception thrown when getting a constant value from a dummy class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassGetConstantValue() { $this->getDummyClassReflection()->getConstant('any'); } /** * Tests an exception thrown when getting a constant reflection from a dummy class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassGetConstantReflection() { $this->getDummyClassReflection()->getConstantReflection('any'); } /** * Tests an exception thrown when providing an invalid argument to isInstance() method. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyClassIsInstance() { $this->getDummyClassReflection()->isInstance(true); } /** * Tests an exception thrown when trying to instantiate a non existent class. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyNewInstanceWithoutConstructor() { $this->getDummyClassReflection()->newInstanceWithoutConstructor(); } /** * Tests an exception thrown when trying to instantiate a non existent class. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyNewInstance() { $this->getDummyClassReflection()->newInstance(null); } /** * Tests an exception thrown when trying to instantiate a non existent class. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testDummyNewInstanceArgs() { $this->getDummyClassReflection()->newInstanceArgs(); } /** * Tests an exception thrown when providing an invalid object. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassIsSubclassOf() { $this->getInternalClassReflection()->isSubclassOf(new \Exception()); } /** * Tests an exception thrown when providing an invalid object. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassImplementsInterface1() { $this->getInternalClassReflection()->implementsInterface(new \Exception()); } /** * Tests an exception thrown when providing an invalid object. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassImplementsInterface2() { $this->getInternalClassReflection()->implementsInterface($this->getBroker()->getClass('Exception')); } /** * Tests an exception thrown when providing an invalid class name. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassImplementsInterface3() { $this->getInternalClassReflection()->implementsInterface('Exception'); } /** * Tests an exception thrown when getting a method from an internal class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassGetMethod() { $this->getDummyClassReflection()->getMethod('~non-existent~'); } /** * Tests an exception thrown when getting a property from an internal class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassGetProperty() { $this->getDummyClassReflection()->getProperty('~non-existent~'); } /** * Tests an exception thrown when getting a static property from an internal class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassGetStaticProperty() { $this->getDummyClassReflection()->getStaticPropertyValue('~non-existent~', null); } /** * Tests an exception thrown when setting a static property from an internal class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassSetStaticProperty() { $this->getDummyClassReflection()->setStaticPropertyValue('~non', 'existent~'); } /** * Tests an exception thrown when getting a constant value from an internal class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassGetConstantValue() { $this->getDummyClassReflection()->getConstant('~non-existent~'); } /** * Tests an exception thrown when getting a constant reflection from an internal class reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassGetConstantReflection() { $this->getDummyClassReflection()->getConstantReflection('~non-existent~'); } /** * Tests an exception thrown when providing an invalid object. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassUsesTrait1() { $this->getInternalClassReflection()->usesTrait(new \Exception()); } /** * Tests an exception thrown when providing an invalid object. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassUsesTrait2() { $this->getInternalClassReflection()->usesTrait($this->getBroker()->getClass('Exception')); } /** * Tests an exception thrown when providing an invalid class name. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassUsesTrait3() { $this->getInternalClassReflection()->usesTrait('Exception'); } /** * Tests an exception thrown when it is impossible to create an instance without invoking the constructor. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassNewInstanceWithoutConstructor1() { $this->getInternalClassReflection()->newInstanceWithoutConstructor(); } /** * Tests an exception thrown when it is impossible to create an instance without invoking the constructor. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassNewInstanceWithoutConstructor2() { $reflection = new Php\ReflectionClass('TokenReflection\Exception\RuntimeException', $this->getBroker()); $reflection->newInstanceWithoutConstructor(); } /** * Tests an exception thrown when trying to create the reflection from a PHP internal reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalClassReflectionCreate() { Php\ReflectionClass::create(new \ReflectionFunction('create_function'), $this->getBroker()); } /** * Tests getting of class constants. */ public function testConstants() { $rfl = $this->getClassReflection('constants'); $this->assertSame($rfl->internal->hasConstant('STRING'), $rfl->token->hasConstant('STRING')); $this->assertTrue($rfl->token->hasConstant('STRING')); $this->assertTrue($rfl->token->hasOwnConstant('STRING')); $this->assertSame($rfl->internal->hasConstant('NONEXISTENT'), $rfl->token->hasConstant('NONEXISTENT')); $this->assertFalse($rfl->token->hasConstant('NONEXISTENT')); $this->assertFalse($rfl->token->hasOwnConstant('NONEXISTENT')); $this->assertFalse($rfl->token->hasOwnConstant('PARENT')); $this->assertSame($rfl->internal->getConstant('STRING'), $rfl->token->getConstant('STRING')); $this->assertSame('string', $rfl->token->getConstant('STRING')); $this->assertSame($rfl->internal->getConstant('NONEXISTENT'), $rfl->token->getConstant('NONEXISTENT')); $this->assertFalse($rfl->token->getConstant('NONEXISTENT')); $this->assertSame($rfl->internal->getConstants(), $rfl->token->getConstants()); $this->assertSame(array('STRING' => 'string', 'INTEGER' => 1, 'FLOAT' => 1.1, 'BOOLEAN' => true, 'PARENT' => 'parent'), $rfl->token->getConstants()); $this->assertSame(array('STRING' => 'string', 'INTEGER' => 1, 'FLOAT' => 1.1, 'BOOLEAN' => true), $rfl->token->getOwnConstants()); $this->assertSame(range(0, 3), array_keys($rfl->token->getOwnConstantReflections())); foreach ($rfl->token->getOwnConstantReflections() as $constant) { $this->assertInstanceOf('TokenReflection\ReflectionConstant', $constant); } $rfl = $this->getClassReflection('noConstants'); $this->assertSame($rfl->internal->hasConstant('NONEXISTENT'), $rfl->token->hasConstant('NONEXISTENT')); $this->assertFalse($rfl->token->hasConstant('NONEXISTENT')); $this->assertFalse($rfl->token->hasOwnConstant('NONEXISTENT')); $this->assertSame($rfl->internal->getConstant('NONEXISTENT'), $rfl->token->getConstant('NONEXISTENT')); $this->assertFalse($rfl->token->getConstant('NONEXISTENT')); $this->assertSame($rfl->internal->getConstants(), $rfl->token->getConstants()); $this->assertSame(array(), $rfl->token->getConstants()); $this->assertSame(array(), $rfl->token->getOwnConstants()); $this->assertSame(array(), $rfl->token->getOwnConstantReflections()); $token = $this->getBroker()->getClass('RecursiveDirectoryIterator'); $this->assertTrue($token->hasConstant('CURRENT_AS_PATHNAME')); $this->assertFalse($token->hasOwnConstant('CURRENT_AS_PATHNAME')); $this->assertSame(0, count($token->getOwnConstants())); $this->assertSame(0, count($token->getOwnConstantReflections())); $this->assertSame('FilesystemIterator', $token->getConstantReflection('CURRENT_AS_PATHNAME')->getDeclaringClassName()); } /** * Tests getting of class properties. */ public function testProperties() { $rfl = $this->getClassReflection('properties'); $filters = array(\ReflectionProperty::IS_STATIC, \ReflectionProperty::IS_PUBLIC, \ReflectionProperty::IS_PROTECTED, \ReflectionProperty::IS_PRIVATE); foreach ($this->getFilterCombinations($filters) as $filter) { $this->assertSame(array_keys($rfl->internal->getProperties($filter)), array_keys($rfl->token->getProperties($filter))); foreach ($rfl->token->getProperties($filter) as $property) { $this->assertInstanceOf('TokenReflection\ReflectionProperty', $property); } foreach ($rfl->token->getOwnProperties($filter) as $property) { $this->assertInstanceOf('TokenReflection\ReflectionProperty', $property); } } $this->assertSame($rfl->internal->getDefaultProperties(), $rfl->token->getDefaultProperties()); $this->assertSame(array('publicStatic' => true, 'privateStatic' => 'something', 'protectedStatic' => 1, 'public' => false, 'private' => '', 'protected' => 0), $rfl->token->getDefaultProperties()); $this->assertSame($rfl->internal->getStaticProperties(), $rfl->token->getStaticProperties()); $this->assertSame(array('publicStatic' => true, 'privateStatic' => 'something', 'protectedStatic' => 1), $rfl->token->getStaticProperties()); $properties = array('public', 'publicStatic', 'protectedStatic', 'protectedStatic', 'private', 'privateStatic'); foreach ($properties as $property) { $this->assertSame($rfl->internal->hasProperty($property), $rfl->token->hasProperty($property)); $this->assertTrue($rfl->token->hasProperty($property)); $this->assertInstanceOf('TokenReflection\ReflectionProperty', $rfl->token->getProperty($property)); } $properties = array('public', 'publicStatic', 'private', 'privateStatic'); foreach ($properties as $property) { $this->assertTrue($rfl->token->hasOwnProperty($property)); } $properties = array('protectedStatic', 'protectedStatic'); foreach ($properties as $property) { $this->assertFalse($rfl->token->hasOwnProperty($property)); } $this->assertFalse($rfl->token->hasProperty('nonExistent')); try { $rfl->token->getProperty('nonExistent'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $this->assertSame($rfl->internal->getStaticPropertyValue('publicStatic'), $rfl->token->getStaticPropertyValue('publicStatic')); $this->assertTrue($rfl->token->getStaticPropertyValue('publicStatic')); try { $rfl->token->getStaticPropertyValue('protectedStatic'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } try { $rfl->token->getStaticPropertyValue('privateStatic'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $this->assertSame($rfl->internal->setStaticPropertyValue('publicStatic', false), $rfl->token->setStaticPropertyValue('publicStatic', false)); $this->assertNull($rfl->token->setStaticPropertyValue('publicStatic', false)); $this->assertSame($rfl->internal->getStaticPropertyValue('publicStatic'), $rfl->token->getStaticPropertyValue('publicStatic')); $this->assertFalse($rfl->token->getStaticPropertyValue('publicStatic')); try { $rfl->token->setStaticPropertyValue('protectedStatic', 0); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } try { $rfl->token->setStaticPropertyValue('privateStatic', ''); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $rfl = $this->getClassReflection('noProperties'); $this->assertSame($rfl->internal->getDefaultProperties(), $rfl->token->getDefaultProperties()); $this->assertSame(array(), $rfl->token->getDefaultProperties()); $this->assertSame($rfl->internal->getProperties(), $rfl->token->getProperties()); $this->assertSame(array(), $rfl->token->getProperties()); $this->assertSame(array(), $rfl->token->getOwnProperties()); $this->assertSame($rfl->internal->getStaticProperties(), $rfl->token->getStaticProperties()); $this->assertSame(array(), $rfl->token->getStaticProperties()); $this->assertSame($rfl->internal->hasProperty('nonExistent'), $rfl->token->hasProperty('nonExistent')); $this->assertFalse($rfl->token->hasProperty('nonExistent')); $this->assertFalse($rfl->token->hasOwnProperty('nonExistent')); try { $rfl->token->getProperty('nonExistent'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } try { $rfl->token->getStaticPropertyValue('nonExistent'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } try { $rfl->token->setStaticPropertyValue('property', 'property'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $rfl = $this->getClassReflection('doubleProperties'); $filters = array(\ReflectionProperty::IS_STATIC, \ReflectionProperty::IS_PUBLIC, \ReflectionProperty::IS_PROTECTED, \ReflectionProperty::IS_PRIVATE); foreach ($this->getFilterCombinations($filters) as $filter) { $this->assertSame(array_keys($rfl->internal->getProperties($filter)), array_keys($rfl->token->getProperties($filter)), $filter); foreach ($rfl->token->getProperties($filter) as $property) { $this->assertInstanceOf('TokenReflection\ReflectionProperty', $property); } foreach ($rfl->token->getOwnProperties($filter) as $property) { $this->assertInstanceOf('TokenReflection\ReflectionProperty', $property); } } $this->assertSame($rfl->internal->getDefaultProperties(), $rfl->token->getDefaultProperties()); $this->assertSame(array('protectedOne' => 1, 'protectedTwo' => 0, 'publicOne' => true, 'publicTwo' => false, 'privateOne' => 'something', 'privateTwo' => ''), $rfl->token->getDefaultProperties()); $this->assertSame($rfl->internal->getStaticProperties(), $rfl->token->getStaticProperties()); $this->assertSame(array('protectedOne' => 1, 'protectedTwo' => 0), $rfl->token->getStaticProperties()); $properties = array('publicOne', 'publicTwo', 'protectedOne', 'protectedTwo', 'privateOne', 'privateTwo'); foreach ($properties as $property) { $this->assertSame($rfl->internal->hasProperty($property), $rfl->token->hasProperty($property)); $this->assertTrue($rfl->token->hasProperty($property)); $this->assertInstanceOf('TokenReflection\ReflectionProperty', $rfl->token->getProperty($property)); } } /** * Tests if class is instantiable or cloneable. */ public function testInstantiableCloneable() { $rfl = $this->getClassReflection('publicConstructor'); $this->assertSame($rfl->internal->isInstantiable(), $rfl->token->isInstantiable()); $this->assertTrue($rfl->token->isInstantiable()); if (PHP_VERSION_ID >= 50400) { $this->assertSame($rfl->internal->isCloneable(), $rfl->token->isCloneable()); } $this->assertTrue($rfl->token->isCloneable()); $rfl = $this->getClassReflection('privateConstructor'); $this->assertSame($rfl->internal->isInstantiable(), $rfl->token->isInstantiable()); $this->assertFalse($rfl->token->isInstantiable()); if (PHP_VERSION_ID >= 50400) { $this->assertSame($rfl->internal->isCloneable(), $rfl->token->isCloneable()); } $this->assertTrue($rfl->token->isCloneable()); $rfl = $this->getClassReflection('publicClone'); if (PHP_VERSION_ID >= 50400) { $this->assertSame($rfl->internal->isCloneable(), $rfl->token->isCloneable()); } $this->assertTrue($rfl->token->isCloneable()); $rfl = $this->getClassReflection('privateClone'); if (PHP_VERSION_ID >= 50400) { $this->assertSame($rfl->internal->isCloneable(), $rfl->token->isCloneable()); } $this->assertFalse($rfl->token->isCloneable()); } /** * Tests class modifiers. */ public function testModifiers() { static $classes = array( 'TokenReflection_Test_ClassModifiersIface1', 'TokenReflection_Test_ClassModifiersIface2', 'TokenReflection_Test_ClassModifiersIface3', 'TokenReflection_Test_ClassModifiersIface4', 'TokenReflection_Test_ClassModifiersClass1', 'TokenReflection_Test_ClassModifiersClass2', 'TokenReflection_Test_ClassModifiersClass3', 'TokenReflection_Test_ClassModifiersClass4', 'TokenReflection_Test_ClassModifiersClass5', 'TokenReflection_Test_ClassModifiersClass6', 'TokenReflection_Test_ClassModifiersClass7', 'TokenReflection_Test_ClassModifiersClass8', ); require_once $this->getFilePath('modifiers'); $this->getBroker()->process($this->getFilePath('modifiers')); foreach ($classes as $className) { $token = $this->getBroker()->getClass($className); $internal = new \ReflectionClass($className); $this->assertSame($internal->getModifiers(), $token->getModifiers(), $className); } } /** * Tests getting of class methods. */ public function testMethods() { $rfl = $this->getClassReflection('methods'); $filters = array(\ReflectionMethod::IS_STATIC, \ReflectionMethod::IS_PUBLIC, \ReflectionMethod::IS_PROTECTED, \ReflectionMethod::IS_PRIVATE, \ReflectionMethod::IS_ABSTRACT, \ReflectionMethod::IS_FINAL); foreach ($this->getFilterCombinations($filters) as $filter) { $this->assertSame(array_keys($rfl->internal->getMethods($filter)), array_keys($rfl->token->getMethods($filter))); foreach ($rfl->token->getMethods($filter) as $method) { $this->assertInstanceOf('TokenReflection\ReflectionMethod', $method); } foreach ($rfl->token->getOwnMethods($filter) as $method) { $this->assertInstanceOf('TokenReflection\ReflectionMethod', $method); } } $methods = array('__construct', '__destruct', 'publicFinalFunction', 'publicStaticFunction', 'protectedStaticFunction', 'privateStaticFunction', 'publicFunction', 'protectedFunction', 'privateFunction'); foreach ($methods as $method) { $this->assertSame($rfl->internal->hasMethod($method), $rfl->token->hasMethod($method)); $this->assertTrue($rfl->token->hasMethod($method)); $this->assertInstanceOf('TokenReflection\ReflectionMethod', $rfl->token->getMethod($method)); } $methods = array('__construct', '__destruct', 'publicFinalFunction', 'publicStaticFunction', 'privateStaticFunction', 'publicFunction', 'privateFunction'); foreach ($methods as $method) { $this->assertTrue($rfl->token->hasOwnMethod($method)); } $methods = array('protectedStaticFunction', 'protectedFunction'); foreach ($methods as $method) { $this->assertFalse($rfl->token->hasOwnMethod($method)); } $this->assertInstanceOf('TokenReflection\ReflectionMethod', $rfl->token->getConstructor()); $this->assertInstanceOf('TokenReflection\ReflectionMethod', $rfl->token->getDestructor()); $this->assertFalse($rfl->token->hasMethod('nonExistent')); try { $rfl->token->getMethod('nonExistent'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $rfl = $this->getClassReflection('noMethods'); $this->assertSame($rfl->internal->getMethods(), $rfl->token->getMethods()); $this->assertSame(array(), $rfl->token->getMethods()); $this->assertSame(array(), $rfl->token->getOwnMethods()); try { $rfl->token->getMethod('nonExistent'); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $this->assertSame($rfl->internal->hasMethod('nonExistent'), $rfl->token->hasMethod('nonExistent')); $this->assertFalse($rfl->token->hasMethod('nonExistent')); $this->assertFalse($rfl->token->hasOwnMethod('nonExistent')); $this->assertSame($rfl->internal->getConstructor(), $rfl->token->getConstructor()); $this->assertNull($rfl->token->getConstructor()); $this->assertNull($rfl->token->getDestructor()); } /** * Tests getting of start and end line. */ public function testLines() { $rfl = $this->getClassReflection('lines'); $this->assertSame($rfl->internal->getStartLine(), $rfl->token->getStartLine()); $this->assertSame(3, $rfl->token->getStartLine()); $this->assertSame($rfl->internal->getEndLine(), $rfl->token->getEndLine()); $this->assertSame(5, $rfl->token->getEndLine()); } /** * Tests if class is instance of a object and tests creating new instances. */ public function testInstances() { $rfl = $this->getClassReflection('instances'); $this->assertSame($rfl->internal->isInstance(new \TokenReflection_Test_ClassInstances(1)), $rfl->token->isInstance(new \TokenReflection_Test_ClassInstances(1))); $this->assertTrue($rfl->token->isInstance(new \TokenReflection_Test_ClassInstances(1))); $this->assertSame($rfl->internal->isInstance(new \TokenReflection_Test_ClassInstancesChild(1)), $rfl->token->isInstance(new \TokenReflection_Test_ClassInstancesChild(1))); $this->assertTrue($rfl->token->isInstance(new \TokenReflection_Test_ClassInstancesChild(1))); $this->assertSame($rfl->internal->isInstance(new \Exception()), $rfl->token->isInstance(new \Exception())); $this->assertFalse($rfl->token->isInstance(new \Exception())); $this->assertEquals($rfl->internal->newInstance(1), $rfl->token->newInstance(1)); $this->assertInstanceOf($this->getClassName('instances'), $rfl->token->newInstance(1)); $this->assertEquals($rfl->internal->newInstanceArgs(array(1)), $rfl->token->newInstanceArgs(array(1))); $this->assertInstanceOf($this->getClassName('instances'), $rfl->token->newInstanceArgs(array(1))); } /** * Tests if class is abstract. */ public function testAbstract() { $rfl = $this->getClassReflection('abstract'); $this->assertSame($rfl->internal->isAbstract(), $rfl->token->isAbstract()); $this->assertTrue($rfl->token->isAbstract()); $this->assertSame($rfl->internal->isInstantiable(), $rfl->token->isInstantiable()); $this->assertFalse($rfl->token->isInstantiable()); $this->assertSame($rfl->internal->getModifiers(), $rfl->token->getModifiers()); $this->assertSame(InternalReflectionClass::IS_EXPLICIT_ABSTRACT, $rfl->token->getModifiers()); $rfl = $this->getClassReflection('abstractImplicit'); $this->assertSame($rfl->internal->isAbstract(), $rfl->token->isAbstract()); $this->assertTrue($rfl->token->isAbstract()); $this->assertSame($rfl->internal->isInstantiable(), $rfl->token->isInstantiable()); $this->assertFalse($rfl->token->isInstantiable()); $this->assertSame($rfl->internal->getModifiers(), $rfl->token->getModifiers()); $this->assertSame(InternalReflectionClass::IS_IMPLICIT_ABSTRACT | InternalReflectionClass::IS_EXPLICIT_ABSTRACT, $rfl->token->getModifiers()); $rfl = $this->getClassReflection('noAbstract'); $this->assertSame($rfl->internal->isAbstract(), $rfl->token->isAbstract()); $this->assertFalse($rfl->token->isAbstract()); $this->assertSame($rfl->internal->isInstantiable(), $rfl->token->isInstantiable()); $this->assertTrue($rfl->token->isInstantiable()); $this->assertSame($rfl->internal->getModifiers(), $rfl->token->getModifiers()); $this->assertSame(0, $rfl->token->getModifiers()); } /** * Tests if class is final. */ public function testFinal() { $rfl = $this->getClassReflection('final'); $this->assertSame($rfl->internal->isFinal(), $rfl->token->isFinal()); $this->assertTrue($rfl->token->isFinal()); $this->assertSame($rfl->internal->getModifiers(), $rfl->token->getModifiers()); $this->assertSame(InternalReflectionClass::IS_FINAL, $rfl->token->getModifiers()); $rfl = $this->getClassReflection('noFinal'); $this->assertSame($rfl->internal->isFinal(), $rfl->token->isFinal()); $this->assertFalse($rfl->token->isFinal()); $this->assertSame($rfl->internal->getModifiers(), $rfl->token->getModifiers()); $this->assertSame(0, $rfl->token->getModifiers()); } /** * Tests if class is an interface. */ public function testInterface() { $rfl = $this->getClassReflection('interface'); $this->assertSame($rfl->internal->isInterface(), $rfl->token->isInterface()); $this->assertTrue($rfl->token->isInterface()); $this->assertSame($rfl->internal->isInstantiable(), $rfl->token->isInstantiable()); $this->assertFalse($rfl->token->isInstantiable()); $rfl = $this->getClassReflection('noInterface'); $this->assertSame($rfl->internal->isInterface(), $rfl->token->isInterface()); $this->assertFalse($rfl->token->isInterface()); $this->assertSame($rfl->internal->isInstantiable(), $rfl->token->isInstantiable()); $this->assertTrue($rfl->token->isInstantiable()); } /** * Tests if class implements interfaces. */ public function testInterfaces() { $rfl = $this->getClassReflection('interfaces'); $this->assertSame($rfl->internal->getModifiers(), $rfl->token->getModifiers()); $this->assertSame($rfl->internal->getInterfaceNames(), $rfl->token->getInterfaceNames()); $this->assertSame(array('Traversable', 'Iterator', 'Countable', 'ArrayAccess', 'Serializable'), $rfl->token->getInterfaceNames()); $this->assertSame(array('Countable', 'ArrayAccess', 'Serializable'), $rfl->token->getOwnInterfaceNames()); $this->assertSame(array_keys($rfl->internal->getInterfaces()), array_keys($rfl->token->getInterfaces())); $this->assertSame(array('Traversable', 'Iterator', 'Countable', 'ArrayAccess', 'Serializable'), array_keys($rfl->token->getInterfaces())); $this->assertSame(array('Countable', 'ArrayAccess', 'Serializable'), array_keys($rfl->token->getOwnInterfaces())); foreach ($rfl->token->getInterfaces() as $interface) { $this->assertInstanceOf('TokenReflection\Php\ReflectionClass', $interface); } foreach ($rfl->token->getOwnInterfaces() as $interface) { $this->assertInstanceOf('TokenReflection\Php\ReflectionClass', $interface); } $this->assertSame($rfl->internal->implementsInterface('Countable'), $rfl->token->implementsInterface('Countable')); $this->assertTrue($rfl->token->implementsInterface('Countable')); $this->assertTrue($rfl->token->implementsInterface(new InternalReflectionClass('Countable'))); $token = $this->getBroker()->getClass('Iterator'); $this->assertSame(array('Traversable'), array_keys($token->getInterfaces())); $this->assertSame(array('Traversable'), $token->getInterfaceNames()); $this->assertSame(array('Traversable'), array_keys($token->getOwnInterfaces())); $this->assertSame(array('Traversable'), $token->getOwnInterfaceNames()); $rfl = $this->getClassReflection('noInterfaces'); $this->assertSame($rfl->internal->getModifiers(), $rfl->token->getModifiers()); $this->assertSame($rfl->internal->getInterfaceNames(), $rfl->token->getInterfaceNames()); $this->assertSame(array(), $rfl->token->getOwnInterfaceNames()); $this->assertSame(array(), $rfl->token->getInterfaceNames()); $this->assertSame($rfl->internal->getInterfaces(), $rfl->token->getInterfaces()); $this->assertSame(array(), $rfl->token->getInterfaces()); $this->assertSame(array(), $rfl->token->getOwnInterfaces()); $this->assertSame($rfl->internal->implementsInterface('Countable'), $rfl->token->implementsInterface('Countable')); $this->assertFalse($rfl->token->implementsInterface('Countable')); $this->assertFalse($rfl->token->implementsInterface(new InternalReflectionClass('Countable'))); } /** * Tests if class is iterator. */ public function testIterator() { $rfl = $this->getClassReflection('iterator'); $this->assertSame($rfl->internal->isIterateable(), $rfl->token->isIterateable()); $this->assertTrue($rfl->token->isIterateable()); $rfl = $this->getClassReflection('noIterator'); $this->assertSame($rfl->internal->isIterateable(), $rfl->token->isIterateable()); $this->assertFalse($rfl->token->isIterateable()); } /** * Tests if class has parent. */ public function testParent() { $rfl = $this->getClassReflection('parent'); foreach (array('TokenReflection_Test_ClassGrandGrandParent', 'TokenReflection_Test_ClassGrandParent') as $parent) { $this->assertSame($rfl->internal->isSubclassOf($parent), $rfl->token->isSubclassOf($parent)); $this->assertTrue($rfl->token->isSubclassOf($parent)); $this->assertTrue($rfl->token->isSubclassOf($this->getBroker()->getClass($parent))); } foreach (array('TokenReflection_Test_ClassParent', 'Exception', 'DateTime') as $parent) { $this->assertSame($rfl->internal->isSubclassOf($parent), $rfl->token->isSubclassOf($parent)); $this->assertFalse($rfl->token->isSubclassOf($parent)); } $this->assertInstanceOf('TokenReflection\ReflectionClass', $rfl->token->getParentClass()); $this->assertSame('TokenReflection_Test_ClassGrandParent', $rfl->token->getParentClassName()); $this->assertSame(3, count($rfl->token->getParentClasses())); foreach ($rfl->token->getParentClasses() as $class) { $this->assertInstanceOf('TokenReflection\IReflectionClass', $class); } $this->assertSame(array('TokenReflection_Test_ClassGrandParent', 'TokenReflection_Test_ClassGrandGrandParent', 'ReflectionClass'), $rfl->token->getParentClassNameList()); $rfl = $this->getClassReflection('noParent'); $this->assertSame($rfl->internal->isSubclassOf('Exception'), $rfl->token->isSubclassOf('Exception')); $this->assertFalse($rfl->token->isSubclassOf('Exception')); $this->assertFalse($rfl->token->isSubclassOf(new InternalReflectionClass('Exception'))); $this->assertSame($rfl->internal->getParentClass(), $rfl->token->getParentClass()); $this->assertFalse($rfl->token->getParentClass()); $this->assertSame(array(), $rfl->token->getParentClasses()); $this->assertSame(array(), $rfl->token->getParentClassNameList()); } /** * Tests if class is user defined or internal. */ public function testUserDefined() { $rfl = $this->getClassReflection('userDefined'); $this->assertSame($rfl->internal->isUserDefined(), $rfl->token->isUserDefined()); $this->assertTrue($rfl->token->isUserDefined()); $this->assertSame($rfl->internal->getFileName(), $rfl->token->getFileName()); $this->assertSame($this->getFilePath('userDefined'), $rfl->token->getFileName()); $this->assertSame($rfl->internal->isInternal(), $rfl->token->isInternal()); $this->assertFalse($rfl->token->isInternal()); $this->assertSame($rfl->internal->getExtension(), $rfl->token->getExtension()); $this->assertNull($rfl->token->getExtension()); $this->assertSame($rfl->internal->getExtensionName(), $rfl->token->getExtensionName()); $this->assertFalse($rfl->token->getExtensionName()); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('Exception'); $rfl->token = $this->getBroker()->getClass('Exception'); $this->assertSame($rfl->internal->isUserDefined(), $rfl->token->isUserDefined()); $this->assertFalse($rfl->token->isUserDefined()); $this->assertSame($rfl->internal->getFileName(), $rfl->token->getFileName()); $this->assertFalse($rfl->token->getFileName()); $this->assertSame($rfl->internal->isInternal(), $rfl->token->isInternal()); $this->assertTrue($rfl->token->isInternal()); $this->assertInstanceOf('TokenReflection\Php\ReflectionExtension', $rfl->token->getExtension()); $this->assertSame($rfl->internal->getExtensionName(), $rfl->token->getExtensionName()); $this->assertSame('Core', $rfl->token->getExtensionName()); } /** * Tests getting of documentation comment. */ public function testDocComment() { $rfl = $this->getClassReflection('docComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame("/**\n * TokenReflection_Test_ClassDocComment.\n *\n * @copyright Copyright (c) 2011\n * @author author\n * @see http://php.net\n */", $rfl->token->getDocComment()); $rfl = $this->getClassReflection('noDocComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertFalse($rfl->token->getDocComment()); } /** * Test getting of documentation comment, when after docComment many line breaks. */ public function testDocCommentManyLines() { $rfl = $this->getClassReflection('docCommentManyLines'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame("/**\n * TokenReflection_Test_ClassDocCommentManyLines.\n *\n * @copyright Copyright (c) 2011\n * @author author\n * @see http://php.net\n */", $rfl->token->getDocComment()); $rfl = $this->getClassReflection('noDocComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertFalse($rfl->token->getDocComment()); } /** * Tests getting of inherited documentation comment. */ public function testDocCommentInheritance() { require_once $this->getFilePath('docCommentInheritance'); $this->getBroker()->processFile($this->getFilePath('docCommentInheritance')); $parent = new \stdClass(); $parent->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentInheritanceParent'); $parent->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentInheritanceParent'); $this->assertSame($parent->internal->getDocComment(), $parent->token->getDocComment()); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentInheritanceExplicit'); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentInheritanceExplicit'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame('My Short description.', $rfl->token->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame('Long description. Phew, that was long.', $rfl->token->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentInheritanceImplicit'); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentInheritanceImplicit'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame($parent->token->getAnnotations(), $rfl->token->getAnnotations()); } /** * Tests getting of copydoc documentation comment. */ public function testDocCommentCopydoc() { require_once $this->getFilePath('docCommentCopydoc'); $this->getBroker()->processFile($this->getFilePath('docCommentCopydoc')); $parent = new \stdClass(); $parent->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentCopydocParent'); $parent->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentCopydocParent'); $this->assertSame($parent->internal->getDocComment(), $parent->token->getDocComment()); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentCopydocFound'); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentCopydocFound'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame('Short description.', $rfl->token->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame('Long description.', $rfl->token->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentCopydocOverwritten'); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentCopydocOverwritten'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame('Whatever.', $rfl->token->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame('Long description.', $rfl->token->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame(array('None'), $rfl->token->getAnnotation('license')); $this->assertSame(array('Another author'), $rfl->token->getAnnotation('author')); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentCopydocDouble'); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentCopydocDouble'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame('Short description.', $rfl->token->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame('Long description.', $rfl->token->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame(array('None'), $rfl->token->getAnnotation('license')); $this->assertSame(array('Author'), $rfl->token->getAnnotation('author')); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('TokenReflection_Test_ClassDocCommentCopydocRecursive'); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_ClassDocCommentCopydocRecursive'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame('Short description.', $rfl->token->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame('Long description.', $rfl->token->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame(array('None'), $rfl->token->getAnnotation('license')); $this->assertSame(array('Author'), $rfl->token->getAnnotation('author')); static $emptys = array( 'TokenReflection_Test_ClassDocCommentCopydocNotFound', 'TokenReflection_Test_ClassDocCommentCopydocCircle11', 'TokenReflection_Test_ClassDocCommentCopydocCircle12', 'TokenReflection_Test_ClassDocCommentCopydocCircle21', 'TokenReflection_Test_ClassDocCommentCopydocCircle22', 'TokenReflection_Test_ClassDocCommentCopydocCircle23', 'TokenReflection_Test_ClassDocCommentCopydocCircleSelf' ); foreach ($emptys as $empty) { $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass($empty); $rfl->token = $this->getBroker()->getClass($empty); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame(array(), $rfl->token->getAnnotations()); } } /** * Tests if class is defined in namespace. */ public function testInNamespace() { require_once $this->getFilePath('inNamespace'); $this->getBroker()->processFile($this->getFilePath('inNamespace')); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionClass('TokenReflection\Test\ClassInNamespace'); $rfl->token = $this->getBroker()->getClass('TokenReflection\Test\ClassInNamespace'); $this->assertSame($rfl->internal->inNamespace(), $rfl->token->inNamespace()); $this->assertTrue($rfl->token->inNamespace()); $this->assertSame($rfl->internal->getNamespaceName(), $rfl->token->getNamespaceName()); $this->assertSame('TokenReflection\Test', $rfl->token->getNamespaceName()); $this->assertSame($rfl->internal->getName(), $rfl->token->getName()); $this->assertSame('TokenReflection\Test\ClassInNamespace', $rfl->token->getName()); $this->assertSame($rfl->internal->getShortName(), $rfl->token->getShortName()); $this->assertSame('ClassInNamespace', $rfl->token->getShortName()); $rfl = $this->getClassReflection('noNamespace'); $this->assertSame($rfl->internal->inNamespace(), $rfl->token->inNamespace()); $this->assertFalse($rfl->token->inNamespace()); $this->assertSame($rfl->internal->getNamespaceName(), $rfl->token->getNamespaceName()); $this->assertSame('', $rfl->token->getNamespaceName()); $this->assertSame($rfl->internal->getName(), $rfl->token->getName()); $this->assertSame($this->getClassName('noNamespace'), $rfl->token->getName()); $this->assertSame($rfl->internal->getShortName(), $rfl->token->getShortName()); $this->assertSame($this->getClassName('noNamespace'), $rfl->token->getShortName()); } /** * Tests getting of property source code. */ public function testPropertyGetSource() { static $expected = array( 'publicStatic' => 'public static $publicStatic = true;', 'privateStatic' => 'private static $privateStatic = \'something\';', 'protectedStatic' => 'protected static $protectedStatic = 1;', 'public' => 'public $public = false;', 'protected' => 'protected $protected = 0;', 'private' => 'private $private = \'\';' ); $rfl = $this->getClassReflection('properties')->token; foreach ($expected as $propertyName => $source) { $this->assertSame($source, $rfl->getProperty($propertyName)->getSource()); } } /** * Tests getting of method source code. */ public function testMethodGetSource() { static $expected = array( 'protectedStaticFunction' => "protected static function protectedStaticFunction(\$one = true)\n {\n }", 'protectedFunction' => "protected function protectedFunction(\$two = false)\n {\n }", 'publicStaticFunction' => "public static function publicStaticFunction(\$five = 1.1)\n {\n }" ); $rfl = $this->getClassReflection('methods')->token; foreach ($expected as $methodName => $source) { $this->assertSame($source, $rfl->getMethod($methodName)->getSource()); } } /** * Tests getting of constant source code. */ public function testConstantGetSource() { static $expected = array( 'PARENT' => 'PARENT = \'parent\';', 'STRING' => 'STRING = \'string\';', 'FLOAT' => 'FLOAT = 1.1;', 'BOOLEAN' => 'BOOLEAN = true;' ); $rfl = $this->getClassReflection('constants')->token; foreach ($expected as $constantName => $source) { $this->assertSame($source, $rfl->getConstantReflection($constantName)->getSource()); } } /** * Tests getting of class source code. */ public function testClassGetSource() { static $expected = array( 'methods' => "class TokenReflection_Test_ClassMethods extends TokenReflection_Test_ClassMethodsParent\n{\n public function __construct(\$three)\n {\n }\n\n public function __destruct()\n {\n }\n\n public final function publicFinalFunction(\$four = 1)\n {\n }\n\n public static function publicStaticFunction(\$five = 1.1)\n {\n }\n\n private static function privateStaticFunction(\$six = 'string', \$seven = null)\n {\n }\n\n public function publicFunction(array \$eight = array())\n {\n }\n\n private function privateFunction(Foo \$nine = null)\n {\n }\n}", 'constants' => "class TokenReflection_Test_ClassConstants extends TokenReflection_Test_ClassConstantsParent\n{\n const STRING = 'string';\n const INTEGER = 1;\n const FLOAT = 1.1;\n const BOOLEAN = true;\n}", 'docComment' => "/**\n * TokenReflection_Test_ClassDocComment.\n *\n * @copyright Copyright (c) 2011\n * @author author\n * @see http://php.net\n */\nclass TokenReflection_Test_ClassDocComment\n{\n}" ); foreach ($expected as $className => $source) { $this->assertSame( $source, $this->getClassReflection($className)->token->getSource() ); } } /** * Tests export. */ public function testToString() { $tests = array( 'lines', 'docComment', 'noDocComment', 'constants', 'noConstants', 'properties', 'noProperties', 'doubleProperties', 'publicConstructor', 'privateConstructor', 'publicClone', 'privateClone', 'methods', 'noMethods', 'instances', 'abstract', 'abstractImplicit', 'noAbstract', 'final', 'noFinal', 'interface', 'noInterface', 'interfaces', 'noInterfaces', 'iterator', 'noIterator', 'parent', 'noParent', 'userDefined', 'noNamespace', ); if (PHP_VERSION_ID >= 50400) { // Test traits only on PHP >= 5.4 $tests[] = 'traits'; } foreach ($tests as $test) { $rfl = $this->getClassReflection($test); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); $this->assertSame(InternalReflectionClass::export($this->getClassName($test), true), ReflectionClass::export($this->getBroker(), $this->getClassName($test), true)); // Test loading from a string $rfl = $this->getClassReflection($test, true); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); } $this->assertSame(InternalReflectionClass::export('ReflectionClass', true), ReflectionClass::export($this->getBroker(), 'ReflectionClass', true)); $this->assertSame(InternalReflectionClass::export(new InternalReflectionClass('ReflectionClass'), true), ReflectionClass::export($this->getBroker(), new InternalReflectionClass('ReflectionClass'), true)); } /** * Tests traits support comparing with the internal reflection. * * For PHP 5.4+ only. */ public function testTraits() { if (PHP_VERSION_ID < 50400) { $this->markTestSkipped('Requires PHP 5.4 or higher.'); } static $classes = array( 'TokenReflection_Test_ClassTraitsTrait1', 'TokenReflection_Test_ClassTraitsTrait2', 'TokenReflection_Test_ClassTraitsTrait3', 'TokenReflection_Test_ClassTraitsTrait4', 'TokenReflection_Test_ClassTraits', 'TokenReflection_Test_ClassTraits2', 'TokenReflection_Test_ClassTraits3', 'TokenReflection_Test_ClassTraits4' ); require_once $this->getFilePath('traits'); $this->getBroker()->process($this->getFilePath('traits')); foreach ($classes as $className) { $token = $this->getBroker()->getClass($className); $internal = new \ReflectionClass($className); $this->assertSame($internal->isTrait(), $token->isTrait(), $className); $this->assertSame($internal->getTraitAliases(), $token->getTraitAliases(), $className); $this->assertSame($internal->getTraitNames(), $token->getTraitNames(), $className); $this->assertSame(count($internal->getTraits()), count($token->getTraits()), $className); foreach ($internal->getTraits() as $trait) { $this->assertTrue($token->usesTrait($trait->getName()), $className); } } } /** * Tests traits support comparing with expected values. */ public function testTraits2() { static $expected = array( 'TokenReflection_Test_ClassTraitsTrait1' => array(true, array(), array(), array(), 0, 0), 'TokenReflection_Test_ClassTraitsTrait2' => array(true, array('t2privatef' => '(null)::privatef'), array('TokenReflection_Test_ClassTraitsTrait1'), array('TokenReflection_Test_ClassTraitsTrait1'), 6, 3), 'TokenReflection_Test_ClassTraitsTrait3' => array(true, array(), array(), array(), 0, 0), 'TokenReflection_Test_ClassTraitsTrait4' => array(true, array(), array(), array(), 0, 0), 'TokenReflection_Test_ClassTraits' => array(false, array('privatef2' => '(null)::publicf', 'publicf3' => '(null)::protectedf', 'publicfOriginal' => '(null)::publicf'), array('TokenReflection_Test_ClassTraitsTrait1'), array('TokenReflection_Test_ClassTraitsTrait1'), 6, 6), 'TokenReflection_Test_ClassTraits2' => array(false, array(), array('TokenReflection_Test_ClassTraitsTrait2'), array('TokenReflection_Test_ClassTraitsTrait2'), 6, 3), 'TokenReflection_Test_ClassTraits3' => array(false, array(), array('TokenReflection_Test_ClassTraitsTrait1'), array('TokenReflection_Test_ClassTraitsTrait1'), 6, 2), 'TokenReflection_Test_ClassTraits4' => array(false, array(), array('TokenReflection_Test_ClassTraitsTrait3', 'TokenReflection_Test_ClassTraitsTrait4'), array('TokenReflection_Test_ClassTraitsTrait3', 'TokenReflection_Test_ClassTraitsTrait4'), 2, 1) ); $this->getBroker()->process($this->getFilePath('traits')); foreach ($expected as $className => $definition) { $reflection = $this->getBroker()->getClass($className); $this->assertSame($definition[0], $reflection->isTrait(), $className); $this->assertSame($definition[1], $reflection->getTraitAliases(), $className); $this->assertSame($definition[2], $reflection->getTraitNames(), $className); $this->assertSame(count($definition[2]), count($reflection->getTraits()), $className); foreach ($definition[2] as $traitName) { $this->assertTrue($reflection->usesTrait($traitName), $className); } $this->assertSame($definition[3], $reflection->getOwnTraitNames(), $className); $this->assertSame(count($definition[3]), count($reflection->getOwnTraits()), $className); foreach ($definition[3] as $traitName) { $this->assertTrue($reflection->usesTrait($traitName), $className); } foreach ($reflection->getTraitProperties() as $property) { $this->assertTrue($reflection->hasProperty($property->getName()), $className); $this->assertNotNull($property->getDeclaringTraitName(), $className); } $this->assertSame($definition[4], count($reflection->getTraitProperties()), $className); foreach ($reflection->getTraitMethods() as $method) { $this->assertTrue($reflection->hasMethod($method->getName()), $className); $this->assertNotNull($method->getDeclaringTraitName(), $className); } $this->assertSame($definition[5], count($reflection->getTraitMethods()), $className); } } /** * Tests creating class instances without calling the constructor. */ public function testNewInstanceWithoutConstructor() { require_once $this->getFilePath('newInstanceWithoutConstructor'); $this->getBroker()->process($this->getFilePath('newInstanceWithoutConstructor')); $token = $this->getBroker()->getClass('TokenReflection_Test_NewInstanceWithoutConstructor1'); $this->assertInstanceOf('TokenReflection\ReflectionClass', $token); try { $token->newInstanceWithoutConstructor(); $this->fail('TokenReflection\Exception\RuntimeException expected.'); } catch (\Exception $e) { $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); if ($e->getCode() !== Exception\RuntimeException::UNSUPPORTED) { throw $e; } } if (PHP_VERSION_ID >= 50400) { // Try the internal reflection $internal = new \ReflectionClass('TokenReflection_Test_NewInstanceWithoutConstructor1'); try { $internal->newInstanceWithoutConstructor(); $this->fail('ReflectionException expected.'); } catch (\Exception $e) { $this->assertInstanceOf('ReflectionException', $e); } } $token = $this->getBroker()->getClass('Exception'); $this->assertInstanceOf('TokenReflection\Php\ReflectionClass', $token); try { $token->newInstanceWithoutConstructor(); $this->fail('TokenReflection\Exception\RuntimeException expected.'); } catch (\Exception $e) { $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); if ($e->getCode() !== Exception\RuntimeException::UNSUPPORTED) { throw $e; } } if (PHP_VERSION_ID >= 50400) { // Try the internal reflection $internal = new \ReflectionClass('Exception'); try { $internal->newInstanceWithoutConstructor(); $this->fail('ReflectionException expected.'); } catch (\Exception $e) { $this->assertInstanceOf('ReflectionException', $e); } } $token = $this->getBroker()->getClass('TokenReflection_Test_NewInstanceWithoutConstructor2'); $internal = new \ReflectionClass('TokenReflection_Test_NewInstanceWithoutConstructor2'); $this->assertInstanceOf('TokenReflection\ReflectionClass', $token); $instance = $token->newInstanceWithoutConstructor(); $this->assertFalse($instance->check); $instance2 = $token->newInstanceArgs(); $this->assertTrue($instance2->check); if (PHP_VERSION_ID >= 50400) { // Try the internal reflection $this->assertEquals($internal->newInstanceWithoutConstructor(), $token->newInstanceWithoutConstructor()); } } /** * Tests returning pretty class names. */ public function testPrettyNames() { static $names = array( 'ns1\\TokenReflection_Test_ClassPrettyNames', 'ns2\\ns3\\ns4\\TokenReflection_Test_ClassPrettyNames2', 'TokenReflection_Test_ClassPrettyNames3' ); $broker = $this->getBroker(); $broker->processFile($this->getFilePath('pretty-names')); foreach ($names as $name) { $this->assertTrue($broker->hasClass($name), $name); $rfl = $broker->getClass($name); $this->assertSame($rfl->getName(), $rfl->getPrettyName(), $name); } } /** * Returns an internal class reflection. * * @return \TokenReflection\Php\ReflectionClass */ private function getInternalClassReflection() { return $this->getBroker()->getClass('Exception'); } /** * Returns a non existent class reflection. * * @return \TokenReflection\Dummy\ReflectionClass */ private function getDummyClassReflection() { static $className = 'foo_bar'; if (class_exists($className, false)) { $this->markTestSkipped(sprintf('Class %s exists.', $className)); } return $this->getBroker()->getClass($className); } } tests/TokenReflection/ReflectionConstantTest.php000064400000077676144760110310016202 0ustar00getConstantTokenReflection('lines'); $this->assertSame(5, $token->getStartLine()); $this->assertSame(5, $token->getEndLine()); } /** * Tests getting of documentation comment. */ public function testComment() { $rfl = $this->getClassReflection('docComment'); foreach (array_keys($rfl->internal->getConstants()) as $constant) { $this->assertTrue($rfl->token->hasConstant($constant), $constant); $this->assertFalse(false === $rfl->token->getConstantReflection($constant)->getDocComment(), $constant); } $token = $this->getConstantTokenReflection('noComment'); $this->assertFalse($token->getDocComment()); } /** * Tests heredoc defined value. */ public function testHeredoc() { $rfl = $this->getClassReflection('heredoc'); $this->assertSame($rfl->internal->getConstant('HEREDOC'), $rfl->token->getConstant('HEREDOC')); $this->assertSame('constant value', $rfl->token->getConstant('HEREDOC')); $this->assertSame($rfl->internal->getConstant('NOWDOC'), $rfl->token->getConstant('NOWDOC')); $this->assertSame('constant value', $rfl->token->getConstant('NOWDOC')); } /** * Tests getting of copydoc documentation comment. */ public function testCommentCopydoc() { static $constants = array('DOC_COMMENT', 'DOC_COMMENT_COPY', 'DOC_COMMENT_COPY2'); $broker = $this->getBroker(); $broker->processFile($this->getFilePath('docCommentCopydoc')); $this->assertTrue($broker->hasClass('TokenReflection_Test_ConstantDocCommentCopydoc')); $reflection = $broker->getClass('TokenReflection_Test_ConstantDocCommentCopydoc'); foreach ($constants as $constant) { $this->assertSame('This is a constant.', $reflection->getConstantReflection($constant)->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION), $constant); } $this->assertSame('This is another constant.', $reflection->getConstantReflection('DOC_COMMENT_COPY_CLASS')->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame(null, $reflection->getConstantReflection('DOC_COMMENT_COPY_NO')->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); static $topLevelConstants = array( 'CONSTANT_DOC_COMMENT_COPYDOC' => 'Comment.', 'CONSTANT_DOC_COMMENT_COPYDOC2' => 'Comment.', 'CONSTANT_DOC_COMMENT_COPYDOC3' => null, ); foreach ($topLevelConstants as $constantName => $shortDescription) { $this->assertTrue($broker->hasConstant($constantName), $constantName); $this->assertSame($shortDescription, $broker->getConstant($constantName)->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION), $constantName); } } /** * Tests different types of constant value. */ public function testTypes() { $constants = array('string' => 'string', 'integer' => 1, 'integerNegative' => -1, 'float' => 1.1, 'floatNegative' => -1.1, 'boolean' => true, 'null' => null, 'constant' => E_NOTICE); foreach ($constants as $type => $value) { $test = 'type' . ucfirst($type); $token = $this->getConstantTokenReflection($test); $this->assertSame($this->getClassInternalReflection($test)->getConstant($this->getConstantName($test)), $token->getValue()); $this->assertSame($value, $token->getValue()); } } /** * Tests if constant is defined in namespace or in class. */ public function testInNamespace() { $this->getBroker()->processFile($this->getFilePath('inNamespace')); $token = $this->getBroker()->getConstant('TokenReflection\Test\CONSTANT_IN_NAMESPACE'); $this->assertInstanceOf('TokenReflection\ReflectionConstant', $token); $this->assertSame('constant-in-namespace', $token->getValue()); $this->assertTrue($token->inNamespace()); $this->assertSame('TokenReflection\\Test\\CONSTANT_IN_NAMESPACE', $token->getName()); $this->assertSame('CONSTANT_IN_NAMESPACE', $token->getShortName()); $this->assertNull($token->getDeclaringClassName()); $this->assertNull($token->getDeclaringClass()); $token = $this->getConstantTokenReflection('noNamespace'); $this->assertFalse($token->inNamespace()); $this->assertSame('NO_NAMESPACE', $token->getName()); $this->assertSame('NO_NAMESPACE', $token->getShortName()); $this->assertSame('TokenReflection_Test_ConstantNoNamespace', $token->getDeclaringClassName()); $this->assertSame('TokenReflection_Test_ConstantNoNamespace', $token->getDeclaringClass()->getName()); $this->assertInstanceOf('TokenReflection\ReflectionClass', $token->getDeclaringClass()); } /** * Tests export. */ public function testToString() { $tests = array( 'noNamespace' => "Constant [ string NO_NAMESPACE ] { no-namespace }\n", 'typeString' => "Constant [ string TYPE_STRING ] { string }\n", 'typeInteger' => "Constant [ integer TYPE_INTEGER ] { 1 }\n", 'typeIntegerNegative' => "Constant [ integer TYPE_INTEGER_NEGATIVE ] { -1 }\n", 'typeFloat' => "Constant [ double TYPE_FLOAT ] { 1.1 }\n", 'typeFloatNegative' => "Constant [ double TYPE_FLOAT_NEGATIVE ] { -1.1 }\n", 'typeBoolean' => "Constant [ boolean TYPE_BOOLEAN ] { 1 }\n", 'typeNull' => "Constant [ null TYPE_NULL ] { }\n" ); foreach ($tests as $test => $expected) { $this->assertSame($expected, $this->getConstantTokenReflection($test)->__toString()); $this->assertSame($expected, ReflectionConstant::export($this->getBroker(), $this->getClassName($test), $this->getConstantName($test), true)); // Test loading from a string $this->assertSame($expected, $this->getConstantTokenReflection($test, true)->__toString()); } $this->assertSame("Constant [ integer E_NOTICE ] { 8 }\n", ReflectionConstant::export($this->getBroker(), null, 'E_NOTICE', true)); } /** * Tests magic constants. */ public function testMagicConstants() { $broker = new Broker(new Broker\Backend\Memory()); $broker->process($this->getFilePath('magic')); require_once ($this->getFilePath('magic')); $internal_constants = get_defined_constants(true); $internal_constants = $internal_constants['user']; $token_constants = $broker->getConstants(); $this->assertSame(14, count($token_constants)); foreach ($token_constants as $name => $reflection) { $this->assertTrue(isset($internal_constants[$name])); $this->assertSame($internal_constants[$name], $reflection->getValue(), $name); } $token_functions = $broker->getFunctions(); $this->assertSame(2, count($token_functions)); foreach ($token_functions as $name => $token_function) { $this->assertTrue(function_exists($name)); $function = new \ReflectionFunction($name); // Parameters $this->assertGreaterThan(0, $function->getNumberOfParameters(), sprintf('%s()', $name)); $this->assertSame($function->getNumberOfParameters(), count($function->getParameters()), sprintf('%s()', $name)); foreach ($function->getParameters() as $parameter) { $parameter_name = $parameter->getName(); $token_parameter = $token_function->getParameter($parameter->getPosition()); $this->assertTrue($parameter->isDefaultValueAvailable(), sprintf('%s(%s)', $name, $parameter_name)); $this->assertSame($parameter->isDefaultValueAvailable(), $token_parameter->isDefaultValueAvailable(), sprintf('%s(%s)', $name, $parameter_name)); $this->assertSame($parameter->getDefaultValue(), $token_parameter->getDefaultValue(), sprintf('%s(%s)', $name, $parameter_name)); } // Static variables $internal_variables = $function->getStaticVariables(); $this->assertGreaterThan(0, count($internal_variables), sprintf('%s()', $name)); $token_variables = $token_function->getStaticVariables(); $this->assertSame(count($internal_variables), count($token_variables), sprintf('%s()', $name)); foreach ($internal_variables as $variable_name => $variable_value) { $this->assertTrue(isset($token_variables[$variable_name]), sprintf('%s()::%s', $name, $variable_name)); $this->assertSame($variable_value, $token_variables[$variable_name], sprintf('%s()::%s', $name, $variable_name)); } } $classes = array( 'TokenReflection_Test_ConstantMagic', 'ns\\TokenReflection_Test_ConstantMagic', 'ns2\\TokenReflection_Test_ConstantMagic', 'ns3\\TokenReflection_Test_ConstantMagic' ); foreach ($classes as $class) { $this->assertTrue(class_exists($class, false), $class); $token = $broker->getClass($class); $internal = new \ReflectionClass($class); $instance = new $class(); // Constants $this->assertSame(7, count($internal->getConstants())); $this->assertSame(count($internal->getConstants()), count($token->getConstantReflections()), $class); foreach ($internal->getConstants() as $name => $value) { $this->assertTrue($token->hasConstant($name), sprintf('%s::%s', $class, $name)); $this->assertSame($value, $token->getConstantReflection($name)->getValue(), sprintf('%s::%s', $class, $name)); $this->assertSame($value, $token->getConstant($name), sprintf('%s::%s', $class, $name)); } // Properties $this->assertSame(14, count($internal->getProperties())); $this->assertSame(count($internal->getProperties()), count($token->getProperties()), $class); foreach ($internal->getProperties() as $reflection) { $name = $reflection->getName(); $this->assertTrue($token->hasProperty($name), sprintf('%s::$%s', $class, $name)); $this->assertSame($reflection->isStatic(), $token->getProperty($name)->isStatic()); if ($reflection->isStatic()) { $this->assertSame($internal->getStaticPropertyValue($name), $token->getStaticPropertyValue($name), sprintf('%s::$%s', $class, $name)); } else { $this->assertSame($reflection->getValue($instance), $token->getProperty($name)->getValue($instance), sprintf('%s::$%s', $class, $name)); $this->assertSame($reflection->getValue($instance), $token->getProperty($name)->getDefaultValue(), sprintf('%s::$%s', $class, $name)); } } // Methods $this->assertGreaterThanOrEqual(1, count($internal->getMethods())); $this->assertSame(count($internal->getMethods()), count($token->getMethods()), $class); foreach ($internal->getMethods() as $method) { $name = $method->getName(); $this->assertTrue($token->hasMethod($name), sprintf('%s::%s()', $class, $name)); $token_method = $token->getMethod($name); // Parameters $this->assertGreaterThan(0, $method->getNumberOfParameters(), sprintf('%s::%s()', $class, $name)); $this->assertSame($method->getNumberOfParameters(), count($method->getParameters()), sprintf('%s::%s()', $class, $name)); foreach ($method->getParameters() as $parameter) { $parameter_name = $parameter->getName(); $token_parameter = $token_method->getParameter($parameter->getPosition()); $this->assertTrue($parameter->isDefaultValueAvailable(), sprintf('%s::%s(%s)', $class, $name, $parameter_name)); $this->assertSame($parameter->isDefaultValueAvailable(), $token_parameter->isDefaultValueAvailable(), sprintf('%s::%s(%s)', $class, $name, $parameter_name)); $this->assertSame($parameter->getDefaultValue(), $token_parameter->getDefaultValue(), sprintf('%s::%s(%s)', $class, $name, $parameter_name)); } // Static variables $internal_variables = $method->getStaticVariables(); $this->assertGreaterThan(0, count($internal_variables), sprintf('%s::%s()', $class, $name)); $token_variables = $token_method->getStaticVariables(); $this->assertSame(count($internal_variables), count($token_variables), sprintf('%s::%s()', $class, $name)); foreach ($internal_variables as $variable_name => $variable_value) { $this->assertTrue(isset($token_variables[$variable_name]), sprintf('%s::%s()::%s', $class, $name, $variable_name)); $this->assertSame($variable_value, $token_variables[$variable_name], sprintf('%s::%s()::%s', $class, $name, $variable_name)); } } } } /** * Tests the __TRAIT__ magic constant. * * For PHP >= 5.4 only. */ public function testMagicConstants54() { if (PHP_VERSION_ID < 50400) { $this->markTestSkipped('PHP >= 5.4 only'); } $broker = new Broker(new Broker\Backend\Memory()); $broker->process($this->getFilePath('magic54')); require_once ($this->getFilePath('magic54')); $internal_constants = get_defined_constants(true); $internal_constants = $internal_constants['user']; $token_constants = $broker->getConstants(); $this->assertSame(2, count($token_constants)); foreach ($token_constants as $name => $reflection) { $this->assertTrue(isset($internal_constants[$name])); $this->assertSame($internal_constants[$name], $reflection->getValue(), $name); } $token_functions = $broker->getFunctions(); $this->assertSame(2, count($token_functions)); foreach ($token_functions as $name => $token_function) { $this->assertTrue(function_exists($name)); $function = new \ReflectionFunction($name); // Parameters $this->assertGreaterThan(0, $function->getNumberOfParameters(), sprintf('%s()', $name)); $this->assertSame($function->getNumberOfParameters(), count($function->getParameters()), sprintf('%s()', $name)); foreach ($function->getParameters() as $parameter) { $parameter_name = $parameter->getName(); $token_parameter = $token_function->getParameter($parameter->getPosition()); $this->assertTrue($parameter->isDefaultValueAvailable(), sprintf('%s(%s)', $name, $parameter_name)); $this->assertSame($parameter->isDefaultValueAvailable(), $token_parameter->isDefaultValueAvailable(), sprintf('%s(%s)', $name, $parameter_name)); $this->assertSame($parameter->getDefaultValue(), $token_parameter->getDefaultValue(), sprintf('%s(%s)', $name, $parameter_name)); } // Static variables $internal_variables = $function->getStaticVariables(); $this->assertGreaterThan(0, count($internal_variables), sprintf('%s()', $name)); $token_variables = $token_function->getStaticVariables(); $this->assertSame(count($internal_variables), count($token_variables), sprintf('%s()', $name)); foreach ($internal_variables as $variable_name => $variable_value) { $this->assertTrue(isset($token_variables[$variable_name]), sprintf('%s()::%s', $name, $variable_name)); $this->assertSame($variable_value, $token_variables[$variable_name], sprintf('%s()::%s', $name, $variable_name)); } } $classes = array( 'TokenReflection_Test_ConstantMagic54Trait', 'TokenReflection_Test_ConstantMagic54', 'TokenReflection_Test_ConstantMagic54WithTrait', 'ns\\TokenReflection_Test_ConstantMagic54Trait', 'ns\\TokenReflection_Test_ConstantMagic54', 'ns\\TokenReflection_Test_ConstantMagic54WithTrait', 'ns2\\TokenReflection_Test_ConstantMagic54', 'ns2\\TokenReflection_Test_ConstantMagic54WithTrait', 'ns3\\TokenReflection_Test_ConstantMagic54', 'ns3\\TokenReflection_Test_ConstantMagic54WithTrait' ); foreach ($classes as $class) { $token = $broker->getClass($class); $internal = new \ReflectionClass($class); $this->assertSame($internal->isTrait(), $token->isTrait()); if (!$internal->isTrait()) { $instance = new $class(); } // Constants if ($internal->isTrait()) { $this->assertSame(0, count($internal->getConstants())); } else { $this->assertSame(1, count($internal->getConstants())); } $this->assertSame(count($internal->getConstants()), count($token->getConstantReflections()), $class); foreach ($internal->getConstants() as $name => $value) { $this->assertTrue($token->hasConstant($name), sprintf('%s::%s', $class, $name)); $this->assertSame($value, $token->getConstantReflection($name)->getValue(), sprintf('%s::%s', $class, $name)); $this->assertSame($value, $token->getConstant($name), sprintf('%s::%s', $class, $name)); } // Properties $this->assertGreaterThan(0, count($internal->getProperties())); $this->assertSame(count($internal->getProperties()), count($token->getProperties()), $class); foreach ($internal->getProperties() as $reflection) { $name = $reflection->getName(); $this->assertTrue($token->hasProperty($name), sprintf('%s::$%s', $class, $name)); $this->assertSame($reflection->isStatic(), $token->getProperty($name)->isStatic()); if ($reflection->isStatic()) { $this->assertSame($internal->getStaticPropertyValue($name), $token->getStaticPropertyValue($name), sprintf('%s::$%s', $class, $name)); } elseif (!$internal->isTrait()) { $this->assertSame($reflection->getValue($instance), $token->getProperty($name)->getValue($instance), sprintf('%s::$%s', $class, $name)); $this->assertSame($reflection->getValue($instance), $token->getProperty($name)->getDefaultValue(), sprintf('%s::$%s', $class, $name)); } } // Methods $this->assertGreaterThanOrEqual(1, count($internal->getMethods())); $this->assertSame(count($internal->getMethods()), count($token->getMethods()), $class); foreach ($internal->getMethods() as $method) { $name = $method->getName(); $this->assertTrue($token->hasMethod($name), sprintf('%s::%s()', $class, $name)); $token_method = $token->getMethod($name); // Parameters $this->assertGreaterThan(0, $method->getNumberOfParameters(), sprintf('%s::%s()', $class, $name)); $this->assertSame($method->getNumberOfParameters(), count($method->getParameters()), sprintf('%s::%s()', $class, $name)); foreach ($method->getParameters() as $parameter) { $parameter_name = $parameter->getName(); $token_parameter = $token_method->getParameter($parameter->getPosition()); $this->assertTrue($parameter->isDefaultValueAvailable(), sprintf('%s::%s(%s)', $class, $name, $parameter_name)); $this->assertSame($parameter->isDefaultValueAvailable(), $token_parameter->isDefaultValueAvailable(), sprintf('%s::%s(%s)', $class, $name, $parameter_name)); $this->assertSame($parameter->getDefaultValue(), $token_parameter->getDefaultValue(), sprintf('%s::%s(%s)', $class, $name, $parameter_name)); } // Static variables $internal_variables = $method->getStaticVariables(); $token_variables = $token_method->getStaticVariables(); $this->assertSame(count($internal_variables), count($token_variables), sprintf('%s::%s()', $class, $name)); foreach ($internal_variables as $variable_name => $variable_value) { $this->assertTrue(isset($token_variables[$variable_name]), sprintf('%s::%s()::%s', $class, $name, $variable_name)); $this->assertSame($variable_value, $token_variables[$variable_name], sprintf('%s::%s()::%s', $class, $name, $variable_name)); } } } } /** * Tests the __TRAIT__ magic constant. * * For PHP < 5.4 only. */ public function testMagicConstants54using53() { if (PHP_VERSION_ID >= 50400) { $this->markTestSkipped('PHP < 5.4 only'); } $broker = new Broker(new Broker\Backend\Memory()); $broker->process($this->getFilePath('magic54')); $token_constants = $broker->getConstants(); static $expected_constants = array('CONST_TRAIT' => '', 'ns\CONST_TRAIT' => ''); $this->assertSame(count($expected_constants), count($token_constants)); foreach ($token_constants as $name => $reflection) { $this->assertArrayHasKey($name, $expected_constants); $this->assertSame($expected_constants[$name], $reflection->getValue()); } $token_functions = $broker->getFunctions(); static $expected_functions = array( 'constantMagic54' => array( array('trait' => ''), array('trait' => '') ), 'ns\\constantMagic54' => array( array('trait' => ''), array('trait' => '') ) ); $this->assertSame(count($expected_functions), count($token_functions)); foreach ($token_functions as $name => $token_function) { $this->assertArrayHasKey($name, $expected_functions); // Parameters $this->assertSame(count($expected_functions[$name][0]), $token_function->getNumberOfParameters(), sprintf('%s()', $name)); $this->assertSame($token_function->getNumberOfParameters(), count($token_function->getParameters()), sprintf('%s()', $name)); foreach ($token_function->getParameters() as $parameter) { $parameter_name = $parameter->getName(); $this->assertArrayHasKey($parameter_name, $expected_functions[$name][0]); $this->assertTrue($parameter->isDefaultValueAvailable(), sprintf('%s(%s)', $name, $parameter_name)); $this->assertSame($expected_functions[$name][0][$parameter_name], $parameter->getDefaultValue(), sprintf('%s(%s)', $name, $parameter_name)); } // Static variables $token_variables = $token_function->getStaticVariables(); $this->assertSame(count($expected_functions[$name][1]), count($token_variables), sprintf('%s()', $name)); foreach ($token_variables as $variable_name => $variable_value) { $this->assertArrayHasKey($variable_name, $expected_functions[$name][1]); $this->assertSame($expected_functions[$name][1][$variable_name], $variable_value); } } $token_classes = $broker->getClasses(); static $expected_classes = array( 'TokenReflection_Test_ConstantMagic54Trait' => array( true, array(), array('t_trait' => array(false, 'TokenReflection_Test_ConstantMagic54Trait'), 't_strait' => array(true, 'TokenReflection_Test_ConstantMagic54Trait')), array('t_foo' => array(array('trait' => 'TokenReflection_Test_ConstantMagic54Trait'), array('trait' => 'TokenReflection_Test_ConstantMagic54Trait'))) ), 'TokenReflection_Test_ConstantMagic54' => array( false, array('CONST_TRAIT' => ''), array('trait' => array(false, ''), 'strait' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => ''))) ), 'TokenReflection_Test_ConstantMagic54WithTrait' => array( false, array('CONST_TRAIT' => ''), array('t_trait' => array(false, 'TokenReflection_Test_ConstantMagic54Trait'), 't_strait' => array(true, 'TokenReflection_Test_ConstantMagic54Trait'), 'trait' => array(false, ''), 'strait' => array(true, ''), 'trait2' => array(false, ''), 'strait2' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => '')), 'bar' => array(array('trait' => ''), array('trait' => '')), 't_foo' => array(array('trait' => 'TokenReflection_Test_ConstantMagic54Trait'), array('trait' => 'TokenReflection_Test_ConstantMagic54Trait'))), ), 'ns\\TokenReflection_Test_ConstantMagic54Trait' => array( true, array(), array('t_trait' => array(false, 'ns\\TokenReflection_Test_ConstantMagic54Trait'), 't_strait' => array(true, 'ns\\TokenReflection_Test_ConstantMagic54Trait')), array('t_foo' => array(array('trait' => 'ns\\TokenReflection_Test_ConstantMagic54Trait'), array('trait' => 'ns\\TokenReflection_Test_ConstantMagic54Trait'))) ), 'ns\\TokenReflection_Test_ConstantMagic54' => array( false, array('CONST_TRAIT' => ''), array('trait' => array(false, ''), 'strait' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => ''))) ), 'ns\\TokenReflection_Test_ConstantMagic54WithTrait' => array( false, array('CONST_TRAIT' => ''), array('t_trait' => array(false, 'ns\\TokenReflection_Test_ConstantMagic54Trait'), 't_strait' => array(true, 'ns\\TokenReflection_Test_ConstantMagic54Trait'), 'trait' => array(false, ''), 'strait' => array(true, ''), 'trait2' => array(false, ''), 'strait2' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => '')), 'bar' => array(array('trait' => ''), array('trait' => '')), 't_foo' => array(array('trait' => 'ns\\TokenReflection_Test_ConstantMagic54Trait'), array('trait' => 'ns\\TokenReflection_Test_ConstantMagic54Trait'))), ), 'ns2\\TokenReflection_Test_ConstantMagic54' => array( false, array('CONST_TRAIT' => ''), array('trait' => array(false, ''), 'strait' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => ''))) ), 'ns2\\TokenReflection_Test_ConstantMagic54WithTrait' => array( false, array('CONST_TRAIT' => ''), array('t_trait' => array(false, 'TokenReflection_Test_ConstantMagic54Trait'), 't_strait' => array(true, 'TokenReflection_Test_ConstantMagic54Trait'), 'trait' => array(false, ''), 'strait' => array(true, ''), 'trait2' => array(false, ''), 'strait2' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => '')), 'bar' => array(array('trait' => ''), array('trait' => '')), 't_foo' => array(array('trait' => 'TokenReflection_Test_ConstantMagic54Trait'), array('trait' => 'TokenReflection_Test_ConstantMagic54Trait'))), ), 'ns3\\TokenReflection_Test_ConstantMagic54' => array( false, array('CONST_TRAIT' => ''), array('trait' => array(false, ''), 'strait' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => ''))) ), 'ns3\\TokenReflection_Test_ConstantMagic54WithTrait' => array( false, array('CONST_TRAIT' => ''), array('t_trait' => array(false, 'ns\\TokenReflection_Test_ConstantMagic54Trait'), 't_strait' => array(true, 'ns\\TokenReflection_Test_ConstantMagic54Trait'), 'trait' => array(false, ''), 'strait' => array(true, ''), 'trait2' => array(false, ''), 'strait2' => array(true, '')), array('foo' => array(array('trait' => ''), array('trait' => '')), 'bar' => array(array('trait' => ''), array('trait' => '')), 't_foo' => array(array('trait' => 'ns\\TokenReflection_Test_ConstantMagic54Trait'), array('trait' => 'ns\\TokenReflection_Test_ConstantMagic54Trait'))), ), ); $this->assertSame(count($expected_classes), count($token_classes)); foreach ($token_classes as $name => $token) { $this->assertTrue(isset($expected_classes[$name]), $name); $this->assertSame($expected_classes[$name][0], $token->isTrait(), $name); // Constants $this->assertSame(count($expected_classes[$name][1]), count($token->getConstants()), $name); foreach ($token->getConstants() as $constant_name => $value) { $this->assertArrayHasKey($constant_name, $expected_classes[$name][1], sprintf('%s::%s', $name, $constant_name)); $this->assertSame($expected_classes[$name][1][$constant_name], $value, sprintf('%s::%s', $name, $constant_name)); } // Properties $this->assertSame(count($expected_classes[$name][2]), count($token->getProperties()), $name); foreach ($token->getProperties() as $reflection) { $property_name = $reflection->getName(); $this->assertArrayHasKey($property_name, $expected_classes[$name][2], sprintf('%s::$%s', $name, $property_name)); $this->assertSame($expected_classes[$name][2][$property_name][0], $token->getProperty($property_name)->isStatic(), sprintf('%s::$%s', $name, $property_name)); if ($reflection->isStatic()) { $this->assertSame($expected_classes[$name][2][$property_name][1], $token->getStaticPropertyValue($property_name), sprintf('%s::$%s', $name, $property_name)); } else { $this->assertSame($expected_classes[$name][2][$property_name][1], $token->getProperty($property_name)->getDefaultValue(), sprintf('%s::$%s', $name, $property_name)); } } // Methods $this->assertSame(count($expected_classes[$name][3]), count($token->getMethods()), $name); foreach ($token->getMethods() as $method) { $method_name = $method->getName(); $this->assertArrayHasKey($method_name, $expected_classes[$name][3], sprintf('%s::%s()', $name, $method_name)); // Parameters $this->assertSame(count($expected_classes[$name][3][$method_name][0]), $method->getNumberOfParameters(), sprintf('%s::%s()', $name, $method_name)); $this->assertSame($method->getNumberOfParameters(), count($method->getParameters()), sprintf('%s::%s()', $name, $method_name)); foreach ($method->getParameters() as $parameter) { $parameter_name = $parameter->getName(); $this->assertArrayHasKey($parameter_name, $expected_classes[$name][3][$method_name][0], sprintf('%s::%s(%s)', $name, $method_name, $parameter_name)); $this->assertTrue($parameter->isDefaultValueAvailable(), sprintf('%s::%s(%s)', $name, $method_name, $parameter_name)); $this->assertSame($expected_classes[$name][3][$method_name][0][$parameter_name], $parameter->getDefaultValue(), sprintf('%s::%s(%s)', $name, $method_name, $parameter_name)); } // Static variables $token_variables = $method->getStaticVariables(); $this->assertSame(count($expected_classes[$name][3][$method_name][1]), count($token_variables), sprintf('%s::%s()', $name, $method_name)); foreach ($token_variables as $variable_name => $variable_value) { $this->assertArrayHasKey($variable_name, $expected_classes[$name][3][$method_name][1], sprintf('%s::%s()::%s', $name, $method_name, $variable_name)); $this->assertSame($expected_classes[$name][3][$method_name][1][$variable_name], $variable_value, sprintf('%s::%s()::%s', $name, $method_name, $variable_name)); } } } } /** * Tests returning pretty constant names. */ public function testPrettyNames() { static $names = array( 'ns1\\CONST_PRETTY_NAMES_1', 'CONST_PRETTY_NAMES_1', 'ns1\\ConstPrettyNames::INTERNAL', 'ConstPrettyNames::INTERNAL', ); $broker = $this->getBroker(); $broker->processFile($this->getFilePath('pretty-names')); foreach ($names as $name) { $this->assertTrue($broker->hasConstant($name), $name); $rfl = $broker->getConstant($name); $this->assertSame($name, $rfl->getPrettyName(), $name); } } /** * Tests an exception thrown when trying to get instance of TokenReflection\Php\ReflectionConstant and providing an invalid parent reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalConstantConstructor() { new Php\ReflectionConstant('foo', 'bar', $this->getBroker(), new Php\ReflectionClass('Exception', $this->getBroker())); } /** * Tests an exception thrown when trying to export an constant. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalConstantExport1() { Php\ReflectionConstant::export($this->getBroker(), null, '~non-existent~', true); } /** * Tests an exception thrown when trying to export an constant. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalConstantExport2() { Php\ReflectionConstant::export($this->getBroker(), '~non-existent~', '~non-existent~', true); } /** * Tests various constant (mis)definitions. */ public function testValueDefinitions() { static $expected = array( 'VALUE_DEFINITION1' => true, 'VALUE_DEFINITION2' => true, 'VALUE_DEFINITION3' => true, 'VALUE_DEFINITION4' => true, 'VALUE_DEFINITION5' => true, 'VALUE_DEFINITION6' => true, 'VALUE_DEFINITION7' => true ); $broker = $this->getBroker(); $broker->processFile($this->getFilePath('valueDefinitions')); foreach ($expected as $name => $value) { $this->assertTrue($broker->hasConstant($name), $name); $rfl = $broker->getConstant($name); $this->assertSame($value, $rfl->getValue(), $name); } } /** * Tests constants defined in interfaces. */ public function testInterfaces() { $broker = new Broker(new Broker\Backend\Memory()); $broker->process($this->getFilePath('interfaces')); $class1 = $broker->getClass('TokenReflection_Test_ConstantInterfaceClass'); $this->assertTrue($class1->hasConstant('FIRST')); $class2 = $broker->getClass('TokenReflection_Test_ConstantInterfaceClass2'); $this->assertTrue($class2->hasConstant('FIRST')); $this->assertTrue($class2->hasConstant('SECOND')); } /** * Tests constants overriding. * * (btw that sucks even more than eval) */ public function testOverriding() { $token = $this->getClassTokenReflection('overriding'); $this->assertTrue($token->hasConstant('FOO')); $constant = $token->getConstantReflection('FOO'); $this->assertSame('notbar', $constant->getValue()); $this->assertSame('TokenReflection_Test_ConstantOverriding', $constant->getDeclaringClassName()); $this->assertTrue($token->getParentClass()->hasConstant('FOO')); $constant = $token->getParentClass()->getConstantReflection('FOO'); $this->assertSame('bar', $constant->getValue()); $this->assertSame('TokenReflection_Test_ConstantOverridingBase', $constant->getDeclaringClassName()); } } tests/TokenReflection/ReflectionExtensionTest.php000064400000001461144760110310016340 0ustar00getBroker()); } } tests/TokenReflection/ReflectionFileTest.php000064400000005405144760110310015245 0ustar00getFilePath('docComment'); $this->getBroker()->processFile($fileName); $this->assertTrue($this->getBroker()->hasFile($fileName)); $fileReflection = $this->getBroker()->getFile($fileName); $this->assertInstanceOf('\TokenReflection\ReflectionFile', $fileReflection); $this->assertSame($this->getFilePath('docComment'), $fileReflection->getPrettyName()); $this->assertTrue($fileReflection->hasAnnotation('package')); $this->assertTrue($fileReflection->hasAnnotation('author')); $this->assertFalse($fileReflection->hasAnnotation('licence')); $this->assertSame(array('package name'), $fileReflection->getAnnotation('package')); $this->assertSame(array('author name'), $fileReflection->getAnnotation('author')); } /** * Tests file level docblocks. */ public function testNoDocComment() { $fileName = $this->getFilePath('noDocComment'); $this->getBroker()->processFile($fileName); $this->assertTrue($this->getBroker()->hasFile($fileName)); $fileReflection = $this->getBroker()->getFile($fileName); $this->assertInstanceOf('\TokenReflection\ReflectionFile', $fileReflection); $this->assertSame($this->getFilePath('noDocComment'), $fileReflection->getPrettyName()); $this->assertFalse($fileReflection->hasAnnotation('package')); $this->assertFalse($fileReflection->hasAnnotation('author')); $this->assertFalse($fileReflection->getDocComment()); } /** * Tests returning file reflections. */ public function testReturningFileReflection() { $fileName = $this->getFilePath('docComment'); $rfl = $this->getClassReflection('docComment'); $this->assertTrue($this->getBroker()->hasFile($fileName)); $this->assertSame($rfl->token->getFileName(), $rfl->token->getFileReflection()->getName()); $this->assertSame($this->getBroker()->getFile($fileName), $rfl->token->getFileReflection()); } /** * Tests throwing exceptions when requesting reflections of files that were not processed. * * @expectedException \TokenReflection\Exception\BrokerException */ public function testExceptionReturningFileReflection() { $broker = $this->getBroker(); $this->assertFalse($broker->hasFile('#non~Existent#')); $broker->getFile('#non~Existent#'); } } tests/TokenReflection/ReflectionFunctionTest.php000064400000031442144760110310016153 0ustar00getFunctionReflection('lines'); $this->assertSame($rfl->internal->getStartLine(), $rfl->token->getStartLine()); $this->assertSame(3, $rfl->token->getStartLine()); $this->assertSame($rfl->internal->getEndLine(), $rfl->token->getEndLine()); $this->assertSame(5, $rfl->token->getEndLine()); } /** * Tests getting of documentation comment. */ public function testComment() { $rfl = $this->getFunctionReflection('docComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame("/**\n * This is a function.\n */", $rfl->token->getDocComment()); $rfl = $this->getFunctionReflection('noComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertFalse($rfl->token->getDocComment()); } /** * Tests getting of copydoc documentation comment. */ public function testCommentCopydoc() { static $functions = array( 'tokenReflectionFunctionDocCommentCopydoc' => 'This is a function.', 'tokenReflectionFunctionDocCommentCopydoc2' => 'This is a function.', 'tokenReflectionFunctionDocCommentCopydoc3' => 'This is a function.', 'tokenReflectionFunctionDocCommentCopydoc4' => null, 'tokenReflectionFunctionDocCommentCopydoc5' => null, ); $broker = $this->getBroker(); $broker->processFile($this->getFilePath('docCommentCopydoc')); foreach ($functions as $functionName => $shortDescription) { $this->assertTrue($broker->hasFunction($functionName), $functionName); $this->assertSame($shortDescription, $broker->getFunction($functionName)->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION), $functionName); } } /** * Tests getting of static variables. */ public function testStaticVariables() { $rfl = $this->getFunctionReflection('staticVariables'); $this->assertSame($rfl->internal->getStaticVariables(), $rfl->token->getStaticVariables()); $this->assertSame( array( 'string' => 'string', 'integer' => 1, 'float' => 1.1, 'boolean' => true, 'null' => null, 'array' => array(1 => 1), 'array2' => array(1 => 1, 2 => 2), 'constant' => 'constant value' ), $rfl->token->getStaticVariables() ); } /** * Tests if function is a closure. */ public function testClosure() { $rfl = $this->getFunctionReflection('noClosure'); $this->assertSame($rfl->internal->isClosure(), $rfl->token->isClosure()); $this->assertFalse($rfl->token->isClosure()); } /** * Tests if function is a closure. */ public function testGetClosure() { $broker = $this->getBroker(); $broker->processFile($this->getFilePath('getClosure')); require_once $this->getFilePath('getClosure'); $function = $broker->getFunction('tokenReflectionFunctionGetClosure1'); $this->assertNull($function->getClosureScopeClass()); $closure = $function->getClosure(); $this->assertInstanceOf('Closure', $closure); static $data1 = array(1 => 1, 4 => 2, 9 => 3); foreach ($data1 as $result => $input) { $this->assertSame($result, $closure($input)); } $function = $broker->getFunction('tokenReflectionFunctionGetClosure2'); $this->assertNull($function->getClosureScopeClass()); $closure = $function->getClosure(); $this->assertInstanceOf('Closure', $closure); static $data2 = array(-1 => 1, -2 => 2, -3 => 3); foreach ($data2 as $result => $input) { $this->assertSame($result, $closure($input)); } static $data3 = array(-1 => array(2, -.5), 1 => array(-100, -.01), 8 => array(2, 4)); foreach ($data3 as $result => $input) { list($a, $b) = $input; $this->assertEquals($result, $closure($a, $b)); } } /** * Tests if function is deprecated. */ public function testDeprecated() { $rfl = $this->getFunctionReflection('noDeprecated'); $this->assertSame($rfl->internal->isDeprecated(), $rfl->token->isDeprecated()); $this->assertFalse($rfl->token->isDeprecated()); } /** * Tests if function is disabled. */ public function testDisabled() { $rfl = $this->getFunctionReflection('noDisabled'); $this->assertSame($rfl->internal->isDisabled(), $rfl->token->isDisabled()); $this->assertFalse($rfl->token->isDisabled()); } /** * Tests if function is user defined or internal. */ public function testUserDefined() { $rfl = $this->getFunctionReflection('userDefined'); $this->assertSame($rfl->internal->isUserDefined(), $rfl->token->isUserDefined()); $this->assertTrue($rfl->token->isUserDefined()); $this->assertSame($rfl->internal->getFileName(), $rfl->token->getFileName()); $this->assertSame($this->getFilePath('userDefined'), $rfl->token->getFileName()); $this->assertSame($rfl->internal->isInternal(), $rfl->token->isInternal()); $this->assertFalse($rfl->token->isInternal()); $this->assertSame($rfl->internal->getExtension(), $rfl->token->getExtension()); $this->assertNull($rfl->token->getExtension()); $this->assertSame($rfl->internal->getExtensionName(), $rfl->token->getExtensionName()); $this->assertFalse($rfl->token->getExtensionName()); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionFunction('get_class'); $rfl->token = $this->getBroker()->getFunction('get_class'); $this->assertSame($rfl->internal->isUserDefined(), $rfl->token->isUserDefined()); $this->assertFalse($rfl->token->isUserDefined()); $this->assertSame($rfl->internal->getFileName(), $rfl->token->getFileName()); $this->assertFalse($rfl->token->getFileName()); $this->assertSame($rfl->internal->isInternal(), $rfl->token->isInternal()); $this->assertTrue($rfl->token->isInternal()); $this->assertInstanceOf('TokenReflection\Php\ReflectionExtension', $rfl->token->getExtension()); $this->assertSame($rfl->internal->getExtensionName(), $rfl->token->getExtensionName()); $this->assertSame('Core', $rfl->token->getExtensionName()); } /** * Tests if function is defined in namespace. */ public function testInNamespace() { require_once $this->getFilePath('inNamespace'); $this->getBroker()->processFile($this->getFilePath('inNamespace')); $rfl = new \stdClass(); $rfl->internal = new InternalReflectionFunction('TokenReflection\Test\functionInNamespace'); $rfl->token = $this->getBroker()->getFunction('TokenReflection\Test\functionInNamespace'); $this->assertSame($rfl->internal->inNamespace(), $rfl->token->inNamespace()); $this->assertTrue($rfl->token->inNamespace()); $this->assertSame($rfl->internal->getNamespaceName(), $rfl->token->getNamespaceName()); $this->assertSame('TokenReflection\Test', $rfl->token->getNamespaceName()); $this->assertSame($rfl->internal->getName(), $rfl->token->getName()); $this->assertSame('TokenReflection\Test\functionInNamespace', $rfl->token->getName()); $this->assertSame($rfl->internal->getShortName(), $rfl->token->getShortName()); $this->assertSame('functionInNamespace', $rfl->token->getShortName()); $rfl = $this->getFunctionReflection('noNamespace'); $this->assertSame($rfl->internal->inNamespace(), $rfl->token->inNamespace()); $this->assertFalse($rfl->token->inNamespace()); $this->assertSame($rfl->internal->getNamespaceName(), $rfl->token->getNamespaceName()); $this->assertSame('', $rfl->token->getNamespaceName()); $this->assertSame($rfl->internal->getName(), $rfl->token->getName()); $this->assertSame($this->getFunctionName('noNamespace'), $rfl->token->getName()); $this->assertSame($rfl->internal->getShortName(), $rfl->token->getShortName()); $this->assertSame($this->getFunctionName('noNamespace'), $rfl->token->getShortName()); } /** * Tests if function returns reference. */ public function testReference() { $rfl = $this->getFunctionReflection('reference'); $this->assertSame($rfl->internal->returnsReference(), $rfl->token->returnsReference()); $this->assertTrue($rfl->token->returnsReference()); $rfl = $this->getFunctionReflection('noReference'); $this->assertSame($rfl->internal->returnsReference(), $rfl->token->returnsReference()); $this->assertFalse($rfl->token->returnsReference()); } /** * Tests getting of function parameters. */ public function testParameters() { $rfl = $this->getFunctionReflection('parameters'); $this->assertSame($rfl->internal->getNumberOfParameters(), $rfl->token->getNumberOfParameters()); $this->assertSame(3, $rfl->token->getNumberOfParameters()); $this->assertSame($rfl->internal->getNumberOfRequiredParameters(), $rfl->token->getNumberOfRequiredParameters()); $this->assertSame(2, $rfl->token->getNumberOfRequiredParameters()); $this->assertSame(array_keys($rfl->internal->getParameters()), array_keys($rfl->token->getParameters())); $internalParameters = $rfl->internal->getParameters(); $tokenParameters = $rfl->token->getParameters(); for ($i = 0; $i < count($internalParameters); $i++) { $this->assertSame($internalParameters[$i]->getName(), $tokenParameters[$i]->getName()); $this->assertInstanceOf('TokenReflection\ReflectionParameter', $tokenParameters[$i]); } $rfl = $this->getFunctionReflection('noParameters'); $this->assertSame($rfl->internal->getNumberOfParameters(), $rfl->token->getNumberOfParameters()); $this->assertSame(0, $rfl->token->getNumberOfParameters()); $this->assertSame($rfl->internal->getNumberOfRequiredParameters(), $rfl->token->getNumberOfRequiredParameters()); $this->assertSame(0, $rfl->token->getNumberOfRequiredParameters()); $this->assertSame($rfl->internal->getParameters(), $rfl->token->getParameters()); $this->assertSame(array(), $rfl->token->getParameters()); } /** * Tests function invoking. */ public function testInvoke() { $rfl = $this->getFunctionReflection('invoke'); $this->assertSame($rfl->internal->invoke(1, 2), $rfl->token->invoke(1, 2)); $this->assertSame(3, $rfl->token->invoke(1, 2)); $this->assertSame($rfl->internal->invokeArgs(array(1, 2)), $rfl->token->invokeArgs(array(1, 2))); $this->assertSame(3, $rfl->token->invokeArgs(array(1, 2))); } /** * Tests export. */ public function testToString() { $tests = array( 'lines', 'docComment', 'noComment', 'invoke', 'noParameters', 'parameters', 'reference', 'noReference', 'noNamespace', 'userDefined', 'noClosure' ); foreach ($tests as $test) { $rfl = $this->getFunctionReflection($test); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); $this->assertSame(InternalReflectionFunction::export($this->getFunctionName($test), true), ReflectionFunction::export($this->getBroker(), $this->getFunctionName($test), true)); // Test loading from a string $rfl = $this->getFunctionReflection($test, true); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); } $this->assertSame(InternalReflectionFunction::export('strpos', true), ReflectionFunction::export($this->getBroker(), 'strpos', true)); } /** * Tests new PHP 5.4 features. */ public function test54features() { if (PHP_VERSION_ID < 50400) { $this->markTestSkipped('Tested only on PHP 5.4+'); } $rfl = $this->getFunctionReflection('54features'); $this->assertSame($rfl->internal->getStaticVariables(), $rfl->token->getStaticVariables()); $this->assertSame( array( 'one' => array(), 'two' => array(array(1), '2', array(array(array(array(true))))), 'three' => 21 ), $rfl->token->getStaticVariables() ); } /** * Tests an exception thrown when trying to create the reflection from a PHP internal reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalFunctionReflectionCreate() { Php\ReflectionExtension::create(new \ReflectionClass('Exception'), $this->getBroker()); } /** * Tests an exception thrown when trying to get a non-existent parameter. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalFunctionGetParameter1() { $this->getInternalFunctionReflection()->getParameter('~non-existent~'); } /** * Tests an exception thrown when trying to get a non-existent parameter. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalFunctionGetParameter2() { $this->getInternalFunctionReflection()->getParameter(999); } /** * Returns an internal function reflection. * * @return \TokenReflection\Php\ReflectionFunction */ private function getInternalFunctionReflection() { return $this->getBroker()->getFunction('create_function'); } } tests/TokenReflection/ReflectionMethodTest.php000064400000057430144760110310015613 0ustar00getMethodReflection('lines'); $this->assertSame($rfl->internal->getStartLine(), $rfl->token->getStartLine()); $this->assertSame(5, $rfl->token->getStartLine()); $this->assertSame($rfl->internal->getEndLine(), $rfl->token->getEndLine()); $this->assertSame(7, $rfl->token->getEndLine()); } /** * Tests getting of documentation comment. */ public function testComment() { $rfl = $this->getMethodReflection('docComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertSame("/**\n\t * This is a method.\n\t */", $rfl->token->getDocComment()); $rfl = $this->getMethodReflection('noComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertFalse($rfl->token->getDocComment()); } /** * Tests getting of copydoc documentation comment. */ public function testCommentCopydoc() { static $methods = array( 'method' => 'This is a method.', 'method2' => 'This is a method.', 'method3' => 'This is a method.', 'method4' => 'This is a method.', 'method5' => 'This is a method.', 'method6' => null, 'method7' => null ); $class = $this->getClassTokenReflection('docCommentCopydoc'); foreach ($methods as $methodName => $shortDescription) { $this->assertTrue($class->hasMethod($methodName), $methodName); $this->assertSame($shortDescription, $class->getMethod($methodName)->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION), $methodName); } } /** * Tests getting of inherited documentation comment. */ public function testDocCommentInheritance() { $this->getBroker()->processFile($this->getFilePath('docCommentInheritance')); $grandParent = new \stdClass(); $grandParent->token = $this->getBroker()->getClass('TokenReflection_Test_MethodDocCommentInheritanceGrandParent'); $parent = new \stdClass(); $parent->token = $this->getBroker()->getClass('TokenReflection_Test_MethodDocCommentInheritanceParent'); $rfl = new \stdClass(); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_MethodDocCommentInheritance'); $this->assertSame($parent->token->getMethod('method1')->getAnnotations(), $rfl->token->getMethod('method1')->getAnnotations()); $this->assertSame('Private1 short. Protected1 short.', $rfl->token->getMethod('method1')->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame('Protected1 long. Private1 long.', $rfl->token->getMethod('method1')->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame($parent->token->getMethod('method2')->getAnnotations(), $rfl->token->getMethod('method2')->getAnnotations()); $this->assertSame($grandParent->token->getMethod('method2')->getAnnotations(), $rfl->token->getMethod('method2')->getAnnotations()); $this->assertSame('Public3 Protected3 short.', $rfl->token->getMethod('method3')->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertNull($rfl->token->getMethod('method3')->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame(array(), $rfl->token->getMethod('method4')->getAnnotations()); $this->assertNull($rfl->token->getMethod('method4')->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame($grandParent->token->getMethod('method1')->getAnnotation('throws'), $parent->token->getMethod('method1')->getAnnotation('throws')); $this->assertSame($grandParent->token->getMethod('method1')->getAnnotation('throws'), $rfl->token->getMethod('method1')->getAnnotation('throws')); $this->assertSame(array('Exception'), $grandParent->token->getMethod('method1')->getAnnotation('throws')); $this->assertSame(array('string'), $parent->token->getMethod('method1')->getAnnotation('return')); $this->assertSame($grandParent->token->getMethod('method2')->getAnnotation('return'), $parent->token->getMethod('method2')->getAnnotation('return')); $this->assertSame($parent->token->getMethod('method2')->getAnnotation('return'), $rfl->token->getMethod('method2')->getAnnotation('return')); $this->assertSame(array('mixed'), $parent->token->getMethod('method2')->getAnnotation('return')); $this->assertSame($parent->token->getMethod('method3')->getAnnotation('return'), $rfl->token->getMethod('method3')->getAnnotation('return')); $this->assertSame(array('boolean'), $rfl->token->getMethod('method3')->getAnnotation('return')); } /** * Tests getting of static variables. */ public function testStaticVariables() { static $testName = 'staticVariables'; $rfl = $this->getMethodReflection($testName); $this->assertSame($rfl->internal->getStaticVariables(), $rfl->token->getStaticVariables()); $this->assertSame( array( 'string' => 'string', 'integer' => 1, 'float' => 1.1, 'boolean' => true, 'null' => null, 'array' => array(1 => 1), 'array2' => array(1 => 1, 2 => 2), 'constants' => array('self constant', 'parent constant') ), $rfl->token->getStaticVariables() ); // The same test with parsing method bodies turned off $broker = new Broker(new Broker\Backend\Memory(), Broker::OPTION_DEFAULT & ~Broker::OPTION_PARSE_FUNCTION_BODY); $broker->processFile($this->getFilePath($testName)); $reflection = $broker->getClass($this->getClassName($testName))->getMethod($this->getMethodName($testName)); $this->assertSame(array(), $reflection->getStaticVariables()); } /** * Tests if method is a closure. */ public function testClosure() { $rfl = $this->getMethodReflection('noClosure'); $this->assertSame($rfl->internal->isClosure(), $rfl->token->isClosure()); $this->assertFalse($rfl->token->isClosure()); } /** * Tests if method is deprecated. */ public function testDeprecated() { $rfl = $this->getMethodReflection('noDeprecated'); $this->assertSame($rfl->internal->isDeprecated(), $rfl->token->isDeprecated()); $this->assertFalse($rfl->token->isDeprecated()); } /** * Tests if method is constructor or destructor. */ public function testConstructorDestructor() { $rfl = $this->getClassReflection('constructorDestructor'); $internal = $rfl->internal->getMethod('__construct'); $token = $rfl->token->getMethod('__construct'); $this->assertSame($internal->isConstructor(), $token->isConstructor()); $this->assertTrue($token->isConstructor()); $this->assertSame($internal->isDestructor(), $token->isDestructor()); $this->assertFalse($token->isDestructor()); $internal = $rfl->internal->getMethod('__destruct'); $token = $rfl->token->getMethod('__destruct'); $this->assertSame($internal->isConstructor(), $token->isConstructor()); $this->assertFalse($token->isConstructor()); $this->assertSame($internal->isDestructor(), $token->isDestructor()); $this->assertTrue($token->isDestructor()); $rfl = $this->getClassReflection('namedConstructor'); $internal = $rfl->internal->getMethod($this->getClassName('namedConstructor')); $token = $rfl->token->getMethod($this->getClassName('namedConstructor')); $this->assertSame($internal->isConstructor(), $token->isConstructor()); $this->assertTrue($token->isConstructor()); require_once $this->getFilePath('namedConstructorInNamespace'); $this->getBroker()->processFile($this->getFilePath('namedConstructorInNamespace')); $class = new \ReflectionClass('TokenReflection\Test\MethodNamedConstructor'); $internal = $class->getMethod('MethodNamedConstructor'); $token = $this->getBroker()->getClass('TokenReflection\Test\MethodNamedConstructor')->getMethod('MethodNamedConstructor'); $this->assertSame($internal->isConstructor(), $token->isConstructor()); if (PHP_VERSION_ID >= 50303) { $this->assertFalse($token->isConstructor()); } else { $this->assertTrue($token->isConstructor()); } } /** * Tests if method can clone. */ public function testClone() { if (PHP_VERSION_ID >= 50400) { // @todo investigate http://svn.php.net/viewvc/php/php-src/trunk/Zend/zend_compile.h?revision=306938&view=markup#l199 $this->markTestSkipped(); } $rfl = $this->getClassReflection('clone'); $this->assertSame($rfl->internal->getMethod('__clone')->getModifiers(), $rfl->token->getMethod('__clone')->getModifiers()); $this->assertSame($rfl->internal->getMethod('noClone')->getModifiers(), $rfl->token->getMethod('noClone')->getModifiers()); } /** * Tests getting of declaring class. */ public function testDeclaringClass() { $rfl = $this->getClassReflection('declaringClass'); foreach (array('parent' => 'Parent', 'child' => '', 'parentOverlay' => '') as $method => $class) { $internal = $rfl->internal->getMethod($method); $token = $rfl->token->getMethod($method); $this->assertSame($internal->getDeclaringClass()->getName(), $token->getDeclaringClass()->getName()); $this->assertSame('TokenReflection_Test_MethodDeclaringClass' . $class, $token->getDeclaringClass()->getName()); $this->assertSame('TokenReflection_Test_MethodDeclaringClass' . $class, $token->getDeclaringClassName()); $this->assertInstanceOf('TokenReflection\ReflectionClass', $token->getDeclaringClass()); } } /** * Tests all method modifiers. */ public function testModifiers() { static $classes = array( 'TokenReflection_Test_MethodModifiersIface', 'TokenReflection_Test_MethodModifiersParent', 'TokenReflection_Test_MethodModifiers', 'TokenReflection_Test_MethodModifiersChild', 'TokenReflection_Test_MethodModifiersChild2', 'TokenReflection_Test_MethodModifiersChild3', 'TokenReflection_Test_MethodModifiersChild4' ); require_once $this->getFilePath('modifiers'); $this->getBroker()->process($this->getFilePath('modifiers')); foreach ($classes as $className) { $token = $this->getBroker()->getClass($className); $internal = new \ReflectionClass($className); foreach ($internal->getMethods() as $method) { $this->assertTrue($token->hasMethod($method->getName()), sprintf('%s::%s()', $className, $method->getName())); if (PHP_VERSION_ID >= 50400) { // @todo investigate http://svn.php.net/viewvc/php/php-src/trunk/Zend/zend_compile.h?revision=306938&view=markup#l199 continue; } $this->assertSame($method->getModifiers(), $token->getMethod($method->getName())->getModifiers(), sprintf('%s::%s()', $className, $method->getName())); } } } /** * Tests if method is user defined or internal. */ public function testUserDefined() { $rfl = $this->getMethodReflection('userDefined'); $this->assertSame($rfl->internal->isUserDefined(), $rfl->token->isUserDefined()); $this->assertTrue($rfl->token->isUserDefined()); $this->assertSame($rfl->internal->getFileName(), $rfl->token->getFileName()); $this->assertSame($this->getFilePath('userDefined'), $rfl->token->getFileName()); $this->assertSame($rfl->internal->isInternal(), $rfl->token->isInternal()); $this->assertFalse($rfl->token->isInternal()); $this->assertSame($rfl->internal->getExtension(), $rfl->token->getExtension()); $this->assertNull($rfl->token->getExtension()); $this->assertSame($rfl->internal->getExtensionName(), $rfl->token->getExtensionName()); $this->assertFalse($rfl->token->getExtensionName()); $rfl = new \stdClass(); $class = new \ReflectionClass('Exception'); $rfl->internal = $class->getMethod('getMessage'); $rfl->token = $this->getBroker()->getClass('Exception')->getMethod('getMessage'); $this->assertSame($rfl->internal->isUserDefined(), $rfl->token->isUserDefined()); $this->assertFalse($rfl->token->isUserDefined()); $this->assertSame($rfl->internal->getFileName(), $rfl->token->getFileName()); $this->assertFalse($rfl->token->getFileName()); $this->assertSame($rfl->internal->isInternal(), $rfl->token->isInternal()); $this->assertTrue($rfl->token->isInternal()); $this->assertEquals($rfl->internal->getExtension(), $rfl->token->getExtension()); $this->assertSame($rfl->internal->getExtensionName(), $rfl->token->getExtensionName()); $this->assertSame('Core', $rfl->token->getExtensionName()); } /** * Tests if method is defined in class in namespace. */ public function testInNamespace() { require_once $this->getFilePath('inNamespace'); $this->getBroker()->processFile($this->getFilePath('inNamespace')); $rfl = new \stdClass(); $class = new \ReflectionClass('TokenReflection\Test\MethodInNamespace'); $rfl->internal = $class->getMethod('inNamespace'); $rfl->token = $this->getBroker()->getClass('TokenReflection\Test\MethodInNamespace')->getMethod('inNamespace'); $this->assertSame($rfl->internal->inNamespace(), $rfl->token->inNamespace()); $this->assertFalse($rfl->token->inNamespace()); $this->assertSame($rfl->internal->getNamespaceName(), $rfl->token->getNamespaceName()); $this->assertSame('', $rfl->token->getNamespaceName()); $this->assertSame($rfl->internal->getName(), $rfl->token->getName()); $this->assertSame('inNamespace', $rfl->token->getName()); $this->assertSame($rfl->internal->getShortName(), $rfl->token->getShortName()); $this->assertSame('inNamespace', $rfl->token->getShortName()); $rfl = $this->getMethodReflection('noNamespace'); $this->assertSame($rfl->internal->inNamespace(), $rfl->token->inNamespace()); $this->assertFalse($rfl->token->inNamespace()); $this->assertSame($rfl->internal->getNamespaceName(), $rfl->token->getNamespaceName()); $this->assertSame('', $rfl->token->getNamespaceName()); $this->assertSame($rfl->internal->getName(), $rfl->token->getName()); $this->assertSame($this->getMethodName('noNamespace'), $rfl->token->getName()); $this->assertSame($rfl->internal->getShortName(), $rfl->token->getShortName()); $this->assertSame($this->getMethodName('noNamespace'), $rfl->token->getShortName()); } /** * Tests if method returns reference. */ public function testReference() { $rfl = $this->getMethodReflection('reference'); $this->assertSame($rfl->internal->returnsReference(), $rfl->token->returnsReference()); $this->assertTrue($rfl->token->returnsReference()); $rfl = $this->getMethodReflection('noReference'); $this->assertSame($rfl->internal->returnsReference(), $rfl->token->returnsReference()); $this->assertFalse($rfl->token->returnsReference()); } /** * Tests getting of method parameters. */ public function testParameters() { $rfl = $this->getMethodReflection('parameters'); $this->assertSame($rfl->internal->getNumberOfParameters(), $rfl->token->getNumberOfParameters()); $this->assertSame(3, $rfl->token->getNumberOfParameters()); $this->assertSame($rfl->internal->getNumberOfRequiredParameters(), $rfl->token->getNumberOfRequiredParameters()); $this->assertSame(2, $rfl->token->getNumberOfRequiredParameters()); $this->assertSame(array_keys($rfl->internal->getParameters()), array_keys($rfl->token->getParameters())); $internalParameters = $rfl->internal->getParameters(); $tokenParameters = $rfl->token->getParameters(); for ($i = 0; $i < count($internalParameters); $i++) { $this->assertSame($internalParameters[$i]->getName(), $tokenParameters[$i]->getName()); $this->assertInstanceOf('TokenReflection\ReflectionParameter', $tokenParameters[$i]); } $rfl = $this->getMethodReflection('noParameters'); $this->assertSame($rfl->internal->getNumberOfParameters(), $rfl->token->getNumberOfParameters()); $this->assertSame(0, $rfl->token->getNumberOfParameters()); $this->assertSame($rfl->internal->getNumberOfRequiredParameters(), $rfl->token->getNumberOfRequiredParameters()); $this->assertSame(0, $rfl->token->getNumberOfRequiredParameters()); $this->assertSame($rfl->internal->getParameters(), $rfl->token->getParameters()); $this->assertSame(array(), $rfl->token->getParameters()); } /** * Tests method invoking. */ public function testInvoke() { $rfl = $this->getClassReflection('invoke'); $className = $this->getClassName('invoke'); $object = new $className(); $internal = $rfl->internal->getMethod('publicInvoke'); $token = $rfl->token->getMethod('publicInvoke'); $this->assertSame($internal->invoke($object, 1, 2), $token->invoke($object, 1, 2)); $this->assertSame(3, $token->invoke($object, 1, 2)); $this->assertSame($internal->invokeArgs($object, array(1, 2)), $token->invokeArgs($object, array(1, 2))); $this->assertSame(3, $token->invokeArgs($object, array(1, 2))); if (PHP_VERSION_ID >= 50302) { $this->assertSame($internal->setAccessible(false), $token->setAccessible(false)); $this->assertSame($internal->invoke($object, 1, 2), $token->invoke($object, 1, 2)); } try { $token->invoke(new \Exception(), 1, 2); $this->fail('Expected exception TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } try { $token->invokeArgs(new \Exception(), array(1, 2)); $this->fail('Expected exception TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $internal = $rfl->internal->getMethod('protectedInvoke'); $token = $rfl->token->getMethod('protectedInvoke'); try { $token->invoke($object, 1, 2); $this->fail('Expected exception TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } try { $token->invokeArgs($object, array(1, 2)); $this->fail('Expected exception TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } if (PHP_VERSION_ID >= 50302) { $this->assertSame($internal->setAccessible(true), $token->setAccessible(true)); $this->assertSame($internal->invoke($object, 1, 2), $token->invoke($object, 1, 2)); $this->assertSame(3, $token->invoke($object, 1, 2)); $this->assertSame($internal->invokeArgs($object, array(1, 2)), $token->invokeArgs($object, array(1, 2))); $this->assertSame(3, $token->invokeArgs($object, array(1, 2))); } } /** * Tests if method has a prototype. */ public function testPrototype() { $rfl = $this->getMethodReflection('prototype'); $this->assertSame($rfl->internal->getPrototype()->getName(), $rfl->internal->getPrototype()->getName()); $this->assertSame($rfl->internal->getPrototype()->getDeclaringClass()->getName(), $rfl->internal->getPrototype()->getDeclaringClass()->getName()); $this->assertInstanceOf('TokenReflection\ReflectionMethod', $rfl->token->getPrototype()); $rfl = $this->getMethodReflection('noPrototype'); try { $rfl->token->getPrototype(); $this->fail('Expected exception TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } } /** * Tests export. */ public function testToString() { $tests = array( 'lines', 'docComment', 'noComment', 'prototype', 'noPrototype', 'parameters', 'reference', 'noReference', 'noClosure', 'noNamespace', 'userDefined', 'shadow' ); foreach ($tests as $test) { $rfl = $this->getMethodReflection($test); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); $this->assertSame(InternalReflectionMethod::export($this->getClassName($test), $test, true), ReflectionMethod::export($this->getBroker(), $this->getClassName($test), $test, true)); $rfl = $this->getMethodReflection($test, true); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); $this->assertSame(InternalReflectionMethod::export($this->getClassName($test), $test, true), ReflectionMethod::export($this->getBroker(), $this->getClassName($test), $test, true)); } $tests = array( 'constructorDestructor' => array('__construct', '__destruct'), 'clone' => array('__clone', 'noClone'), 'declaringClass' => array('parent', 'child', 'parentOverlay'), 'invoke' => array('publicInvoke', 'protectedInvoke'), 'accessLevel' => array('privateExtended', 'privateNoExtended', 'protectedExtended', 'protectedNoExtended'), 'modifiers' => array('publicAbstract', 'publicFinal', 'publicStatic', 'publicNoStatic', 'protectedAbstract', 'protectedFinal', 'protectedStatic', 'protectedNoStatic', 'privateFinal', 'privateStatic', 'privateNoStatic') ); foreach ($tests as $class => $classTests) { $rfl = $this->getClassReflection($class); $rfl_fromString = $this->getClassReflection($class, true); foreach ($classTests as $method) { // @todo inherits not supported yet $this->assertSame(preg_replace('~, inherits [\w]+~', '', $rfl->internal->getMethod($method)->__toString()), $rfl->token->getMethod($method)->__toString()); $this->assertSame(preg_replace('~, inherits [\w]+~', '', InternalReflectionMethod::export($this->getClassName($class), $method, true)), ReflectionMethod::export($this->getBroker(), $this->getClassName($class), $method, true)); $this->assertSame(preg_replace('~, inherits [\w]+~', '', $rfl_fromString->internal->getMethod($method)->__toString()), $rfl_fromString->token->getMethod($method)->__toString()); } } $this->assertSame(InternalReflectionMethod::export('ReflectionMethod', 'isFinal', true), ReflectionMethod::export($this->getBroker(), 'ReflectionMethod', 'isFinal', true)); $this->assertSame(InternalReflectionMethod::export(new InternalReflectionMethod('ReflectionMethod', 'isFinal'), 'isFinal', true), ReflectionMethod::export($this->getBroker(), new InternalReflectionMethod('ReflectionMethod', 'isFinal'), 'isFinal', true)); } /** * Tests new PHP 5.4 features. */ public function test54features() { if (PHP_VERSION_ID < 50400) { $this->markTestSkipped('Tested only on PHP 5.4+'); } $rfl = $this->getMethodReflection('features54'); $this->assertSame($rfl->internal->getStaticVariables(), $rfl->token->getStaticVariables()); $this->assertSame( array( 'one' => array(), 'two' => array(array(1), '2', array(array(array(array(true))))), 'three' => 21 ), $rfl->token->getStaticVariables() ); } /** * Tests an exception thrown when trying to create the reflection from a PHP internal reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalMethodReflectionCreate() { Php\ReflectionExtension::create(new \ReflectionClass('Exception'), $this->getBroker()); } /** * Tests an exception thrown when trying to get a non-existent parameter. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalMethodGetParameter1() { $this->getInternalMethodReflection()->getParameter('~non-existent~'); } /** * Tests an exception thrown when trying to get a non-existent parameter. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalMethodGetParameter2() { $this->getInternalMethodReflection()->getParameter(999); } /** * Returns an internal method reflection. * * @return \TokenReflection\Php\ReflectionMethod */ private function getInternalMethodReflection() { return $this->getBroker()->getClass('Exception')->getConstructor(); } } tests/TokenReflection/ReflectionParameterTest.php000064400000025042144760110310016305 0ustar00getFunctionReflection('position'); $internalParameters = $rfl->internal->getParameters(); $tokenParameters = $rfl->token->getParameters(); for ($i = 0; $i < 3; $i++) { $internal = $internalParameters[$i]; $token = $tokenParameters[$i]; $this->assertSame($internal->getPosition(), $token->getPosition()); $this->assertSame($i, $token->getPosition()); } } /** * Tests if parameter allows null. */ public function testAllowsNull() { foreach (array('Class', 'Array') as $type) { $rfl = $this->getParameterReflection('null' . $type); $this->assertSame($rfl->internal->allowsNull(), $rfl->token->allowsNull()); $this->assertTrue($rfl->token->allowsNull()); $rfl = $this->getParameterReflection('noNull' . $type); $this->assertSame($rfl->internal->allowsNull(), $rfl->token->allowsNull()); $this->assertFalse($rfl->token->allowsNull()); } } /** * Tests if parameters is optional. */ public function testOptional() { $types = array('null' => null, 'true' => true, 'false' => false, 'array' => array(), 'string' => 'string', 'integer' => 1, 'float' => 1.1, 'constant' => E_NOTICE); $definitions = array('null' => 'null', 'true' => 'true', 'false' => 'false', 'array' => 'array()', 'string' => "'string'", 'integer' => '1', 'float' => '1.1', 'constant' => 'E_NOTICE'); foreach ($types as $type => $value) { $rfl = $this->getParameterReflection('optional' . ucfirst($type)); $this->assertSame($rfl->internal->isOptional(), $rfl->token->isOptional()); $this->assertTrue($rfl->token->isOptional()); $this->assertSame($rfl->internal->isDefaultValueAvailable(), $rfl->token->isDefaultValueAvailable()); $this->assertTrue($rfl->token->isDefaultValueAvailable()); $this->assertSame($rfl->internal->getDefaultValue(), $rfl->token->getDefaultValue()); $this->assertSame($value, $rfl->token->getDefaultValue()); $this->assertSame($definitions[$type], $rfl->token->getDefaultValueDefinition()); } $rfl = $this->getParameterReflection('noOptional'); $this->assertSame($rfl->internal->isOptional(), $rfl->token->isOptional()); $this->assertFalse($rfl->token->isOptional()); $this->assertSame($rfl->internal->isDefaultValueAvailable(), $rfl->token->isDefaultValueAvailable()); $this->assertFalse($rfl->token->isDefaultValueAvailable()); try { $rfl->token->getDefaultValue(); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } } /** * Tests if parameter has array type hint. */ public function testArray() { $rfl = $this->getParameterReflection('array'); $this->assertSame($rfl->internal->isArray(), $rfl->token->isArray()); $this->assertTrue($rfl->token->isArray()); $rfl = $this->getParameterReflection('noArray'); $this->assertSame($rfl->internal->isArray(), $rfl->token->isArray()); $this->assertFalse($rfl->token->isArray()); } /** * Tests if parameter has callback type hint. */ public function testCallable() { if (PHP_VERSION_ID < 50400) { $this->markTestSkipped('Requires PHP 5.4 or higher.'); } $rfl = $this->getParameterReflection('callable'); $this->assertSame($rfl->internal->isCallable(), $rfl->token->isCallable()); $this->assertTrue($rfl->token->isCallable()); $rfl = $this->getParameterReflection('noCallable'); $this->assertSame($rfl->internal->isCallable(), $rfl->token->isCallable()); $this->assertFalse($rfl->token->isCallable()); } /** * Tests if parameter has class type hint. */ public function testClass() { $rfl = $this->getParameterReflection('class'); $this->assertSame($rfl->internal->getClass()->getName(), $rfl->token->getClass()->getName()); $this->assertSame('Exception', $rfl->token->getClass()->getName()); $this->assertSame('Exception', $rfl->token->getClassName()); $this->assertInstanceOf('TokenReflection\IReflectionClass', $rfl->token->getClass()); $rfl = $this->getParameterReflection('noClass'); $this->assertSame($rfl->internal->getClass(), $rfl->token->getClass()); $this->assertNull($rfl->token->getClass()); $this->assertNull($rfl->token->getClassName()); } /** * Tests if parameter is passed by reference. */ public function testReference() { $rfl = $this->getParameterReflection('reference'); $this->assertSame($rfl->internal->isPassedByReference(), $rfl->token->isPassedByReference()); $this->assertTrue($rfl->token->isPassedByReference()); $rfl = $this->getParameterReflection('noReference'); $this->assertSame($rfl->internal->isPassedByReference(), $rfl->token->isPassedByReference()); $this->assertFalse($rfl->token->isPassedByReference()); } /** * Tests getting of declaring method or function. */ public function testDeclaring() { $rfl = $this->getParameterReflection('declaringFunction'); $this->assertSame($rfl->internal->getDeclaringFunction()->getName(), $rfl->token->getDeclaringFunction()->getName()); $this->assertSame($this->getFunctionName('declaringFunction'), $rfl->token->getDeclaringFunction()->getName()); $this->assertSame($this->getFunctionName('declaringFunction'), $rfl->token->getDeclaringFunctionName()); $this->assertInstanceOf('TokenReflection\ReflectionFunction', $rfl->token->getDeclaringFunction()); $this->assertSame($rfl->internal->getDeclaringClass(), $rfl->token->getDeclaringClass()); $this->assertNull($rfl->token->getDeclaringClass()); $this->assertNull($rfl->token->getDeclaringClassName()); $rfl = $this->getMethodReflection('declaringMethod'); $internalParameters = $rfl->internal->getParameters(); $internal = $internalParameters[0]; $tokenParameters = $rfl->token->getParameters(); $token = $tokenParameters[0]; $this->assertSame($internal->getDeclaringFunction()->getName(), $token->getDeclaringFunction()->getName()); $this->assertSame($this->getMethodName('declaringMethod'), $token->getDeclaringFunction()->getName()); $this->assertSame($this->getMethodName('declaringMethod'), $token->getDeclaringFunctionName()); $this->assertInstanceOf('TokenReflection\ReflectionMethod', $token->getDeclaringFunction()); $this->assertSame($internal->getDeclaringClass()->getName(), $token->getDeclaringClass()->getName()); $this->assertSame($this->getClassName('declaringMethod'), $token->getDeclaringClass()->getName()); $this->assertSame($this->getClassName('declaringMethod'), $token->getDeclaringClassName()); $this->assertInstanceOf('TokenReflection\ReflectionClass', $token->getDeclaringClass()); } /** * Tests export. */ public function testToString() { $tests = array( 'declaringFunction', 'reference', 'noReference', 'class', 'noClass', 'array', 'noArray', 'nullClass', 'noNullClass', 'nullArray', 'noNullArray', 'noOptional', 'optionalNull', 'optionalTrue', 'optionalFalse', 'optionalArray', 'optionalString', 'optionalInteger', 'optionalFloat', 'optionalConstant' ); foreach ($tests as $test) { $rfl = $this->getParameterReflection($test); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); $this->assertSame(InternalReflectionParameter::export($this->getFunctionName($test), 0, true), ReflectionParameter::export($this->getBroker(), $this->getFunctionName($test), 0, true)); // Test loading from a string $rfl = $this->getParameterReflection($test, true); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); } $this->assertSame(InternalReflectionParameter::export('strpos', 0, true), ReflectionParameter::export($this->getBroker(), 'strpos', 0, true)); } /** * Tests getting of inherited documentation comment. */ public function testDocCommentInheritance() { $this->getBroker()->processFile($this->getFilePath('docCommentInheritance')); $grandParent = new \stdClass(); $grandParent->token = $this->getBroker()->getClass('TokenReflection_Test_ParameterDocCommentInheritanceGrandParent')->getMethod('m'); $parent = new \stdClass(); $parent->token = $this->getBroker()->getClass('TokenReflection_Test_ParameterDocCommentInheritanceParent')->getMethod('m'); $rfl = new \stdClass(); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_ParameterDocCommentInheritance')->getMethod('m'); $this->assertNotNull($grandParent->token); $this->assertNotNull($parent->token); $this->assertNotNull($rfl->token); $this->assertSame($grandParent->token->getAnnotation('param'), $parent->token->getAnnotation('param')); $this->assertSame(count($grandParent->token->getAnnotation('param')), count($rfl->token->getAnnotation('param'))); } /** * Tests new PHP 5.4 features. */ public function test54features() { if (PHP_VERSION_ID < 50400) { $this->markTestSkipped('Tested only on PHP 5.4+'); } $rfl = $this->getFunctionReflection('54features'); $this->assertSame(3, $rfl->internal->getNumberOfParameters()); foreach ($rfl->internal->getParameters() as $internal){ $token = $rfl->token->getParameter($internal->getPosition()); $this->assertSame($internal->getDefaultValue(), $token->getDefaultValue()); } } /** * Tests an exception thrown when trying to create the reflection from a PHP internal reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalParameterReflectionCreate() { Php\ReflectionParameter::create(new \ReflectionClass('Exception'), $this->getBroker()); } /** * Tests various constant (mis)definitions. */ public function testValueDefinitions() { $rfl = $this->getClassReflection('valueDefinitions'); $this->assertTrue($rfl->internal->hasMethod('method')); $internalMethod = $rfl->internal->getMethod('method'); $this->assertTrue($rfl->token->hasMethod('method')); $tokenMethod = $rfl->token->getMethod('method'); foreach ($internalMethod->getParameters() as $parameter) { $this->assertSame($parameter->getDefaultValue(), $tokenMethod->getParameter($parameter->getName())->getDefaultValue()); } } } tests/TokenReflection/ReflectionPropertyTest.php000064400000037376144760110310016226 0ustar00getPropertyTokenReflection('lines'); $this->assertSame(5, $token->getStartLine()); $this->assertSame(5, $token->getEndLine()); } /** * Tests getting of documentation comment. */ public function testComment() { $rfl = $this->getClassReflection('docComment'); foreach ($rfl->internal->getProperties() as $property) { $this->assertFalse(false === $property->getDocComment(), $property->getName()); $this->assertTrue($rfl->token->hasProperty($property->getName()), $property->getName()); $this->assertSame($property->getDocComment(), $rfl->token->getProperty($property->getName())->getDocComment(), $property->getName()); } $propertyName = 'docComment'; $this->assertTrue($rfl->token->hasProperty($propertyName)); /** @var \TokenReflection\ReflectionProperty */ $tokenProperty = $rfl->token->getProperty($propertyName); $this->assertTrue($tokenProperty->hasAnnotation('var')); $this->assertSame(array("String It is a string\n\tand this comment has multiple\n\tlines."), $tokenProperty->getAnnotation('var')); $rfl = $this->getPropertyReflection('noComment'); $this->assertSame($rfl->internal->getDocComment(), $rfl->token->getDocComment()); $this->assertFalse($rfl->token->getDocComment()); } /** * Tests heredoc defined value. */ public function testHeredoc() { $token = $this->getClassTokenReflection('heredoc'); $this->assertTrue($token->hasOwnProperty('heredoc')); $property = $token->getProperty('heredoc'); $this->assertTrue($property->isDefault()); $this->assertSame('property value', $property->getDefaultValue()); $this->assertTrue($token->hasOwnProperty('nowdoc')); $property = $token->getProperty('nowdoc'); $this->assertTrue($property->isDefault()); $this->assertSame('property value', $property->getDefaultValue()); } /** * Tests getting of copydoc documentation comment. */ public function testCommentCopydoc() { static $properties = array( 'property' => 'This is a property.', 'property2' => 'This is a property.', 'property3' => 'This is a property.', 'property4' => 'This is a property.', 'property5' => null, 'property6' => null, 'property7' => null ); $class = $this->getClassTokenReflection('docCommentCopydoc'); foreach ($properties as $propertyName => $shortDescription) { $this->assertTrue($class->hasProperty($propertyName), $propertyName); $this->assertSame($shortDescription, $class->getProperty($propertyName)->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION), $propertyName); } } /** * Tests getting of inherited documentation comment. */ public function testDocCommentInheritance() { require_once $this->getFilePath('docCommentInheritance'); $this->getBroker()->processFile($this->getFilePath('docCommentInheritance')); $grandParent = new \stdClass(); $grandParent->token = $this->getBroker()->getClass('TokenReflection_Test_PropertyDocCommentInheritanceGrandParent'); $parent = new \stdClass(); $parent->token = $this->getBroker()->getClass('TokenReflection_Test_PropertyDocCommentInheritanceParent'); $rfl = new \stdClass(); $rfl->token = $this->getBroker()->getClass('TokenReflection_Test_PropertyDocCommentInheritance'); $this->assertSame($parent->token->getProperty('param1')->getAnnotations(), $rfl->token->getProperty('param1')->getAnnotations()); $this->assertSame('Private1 short. Protected1 short.', $rfl->token->getProperty('param1')->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertSame('Protected1 long. Private1 long.', $rfl->token->getProperty('param1')->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame($parent->token->getProperty('param2')->getAnnotations(), $rfl->token->getProperty('param2')->getAnnotations()); $this->assertSame($grandParent->token->getProperty('param2')->getAnnotations(), $rfl->token->getProperty('param2')->getAnnotations()); $this->assertSame('Public3 Protected3 short.', $rfl->token->getProperty('param3')->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertNull($rfl->token->getProperty('param3')->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame('Protected4 short.', $rfl->token->getProperty('param4')->getAnnotation(ReflectionAnnotation::SHORT_DESCRIPTION)); $this->assertNull($rfl->token->getProperty('param4')->getAnnotation(ReflectionAnnotation::LONG_DESCRIPTION)); $this->assertSame(array('boolean'), $rfl->token->getProperty('param4')->getAnnotation('var')); } /** * Tests getting of documentation comment from templates. */ public function testCommentTemplate() { static $expected = array( 'public1' => array( // Template definition ReflectionAnnotation::SHORT_DESCRIPTION => 'Short description.', ReflectionAnnotation::LONG_DESCRIPTION => 'Long description.', 'var' => array('string') ), 'public2' => array( // No own docblock -> using template ReflectionAnnotation::LONG_DESCRIPTION => 'Long description.', 'var' => array('string') ), 'public3' => array( // Another template to the stack plus using the previuos template ReflectionAnnotation::SHORT_DESCRIPTION => 'Another short description.', ReflectionAnnotation::LONG_DESCRIPTION => "Long description.\nAnother long description.", 'var' => array('array', 'string') ), 'public4' => array( // Own short description, inheriting the rest from the two templates ReflectionAnnotation::SHORT_DESCRIPTION => 'Own short description.', ReflectionAnnotation::LONG_DESCRIPTION => "Long description.\nAnother long description.", 'var' => array('array', 'string') ), // Template end -> remove the second template from the stack 'public5' => array( ReflectionAnnotation::SHORT_DESCRIPTION => 'Another own short description.', ReflectionAnnotation::LONG_DESCRIPTION => "Long description.\nOwn long description.", 'var' => array('integer', 'string') ), // Template end -> remove the first template from the stack 'public6' => array( // No annotations ), 'public7' => array( ReflectionAnnotation::SHORT_DESCRIPTION => 'Outside of template.', 'var' => array('boolean') ), ); $rfl = $this->getClassReflection('docCommentTemplate')->token; foreach ($expected as $name => $annotations) { $property = $rfl->getProperty($name); $this->assertSame($annotations, $property->getAnnotations()); if (empty($annotations)) { $this->assertFalse($property->getDocComment()); } } } /** * Test property accessibility. */ public function testAccessible() { $rfl = $this->getClassReflection('accessible'); $className = $this->getClassName('accessible'); $object = new $className(); foreach (array('protected', 'private') as $property) { $internal = $rfl->internal->getProperty($property); $token = $rfl->token->getProperty($property); try { $token->getValue($object); $this->fail('Expected exception \TokenReflection\Exception\RuntimeException.'); } catch (\PHPUnit_Framework_AssertionFailedError $e) { throw $e; } catch (\Exception $e) { // Correctly thrown exception $this->assertInstanceOf('TokenReflection\Exception\RuntimeException', $e); } $this->assertSame($internal->setAccessible(true), $token->setAccessible(true)); $this->assertNull($token->setAccessible(true)); $this->assertSame($internal->getValue($object), $token->getValue($object)); $this->assertTrue($token->getValue($object)); $this->assertSame($internal->setValue($object, false), $token->setValue($object, false)); $this->assertNull($token->setValue($object, false)); $this->assertSame($internal->getValue($object), $token->getValue($object)); $this->assertFalse($token->getValue($object)); } $internal = $rfl->internal->getProperty('public'); $token = $rfl->token->getProperty('public'); $this->assertSame($internal->getValue($object), $token->getValue($object)); $this->assertTrue($token->getValue($object)); $this->assertSame($internal->setValue($object, false), $token->setValue($object, false)); $this->assertNull($token->setValue($object, false)); $this->assertSame($internal->getValue($object), $token->getValue($object)); $this->assertFalse($token->getValue($object)); $this->assertSame($internal->setAccessible(false), $token->setAccessible(false)); $this->assertNull($token->setAccessible(false)); $this->assertSame($internal->getValue($object), $token->getValue($object)); } /** * Tests getting of declaring class. */ public function testDeclaringClass() { $rfl = $this->getClassReflection('declaringClass'); foreach (array('parent' => 'Parent', 'child' => '', 'parentOverlay' => '') as $property => $class) { $internal = $rfl->internal->getProperty($property); $token = $rfl->token->getProperty($property); $this->assertSame($internal->getDeclaringClass()->getName(), $token->getDeclaringClass()->getName()); $this->assertSame('TokenReflection_Test_PropertyDeclaringClass' . $class, $token->getDeclaringClass()->getName()); $this->assertSame('TokenReflection_Test_PropertyDeclaringClass' . $class, $token->getDeclaringClassName()); $this->assertInstanceOf('TokenReflection\ReflectionClass', $token->getDeclaringClass()); } } /** * Tests getting of default value. */ public function testDefault() { $token = $this->getPropertyTokenReflection('default'); $this->assertTrue($token->isDefault()); $this->assertSame('default', $token->getDefaultValue()); $this->assertSame("'default'", $token->getDefaultValueDefinition()); $token = $this->getPropertyTokenReflection('noDefault'); $this->assertTrue($token->isDefault()); $this->assertNull($token->getDefaultValue()); } /** * Tests all property modifiers. */ public function testModifiers() { $rfl = $this->getClassReflection('modifiers'); foreach (array('public', 'protected', 'private') as $name) { $method = 'is' . ucfirst($name); $opposite = 'no' . ucfirst($name); $staticName = $name . 'Static'; $internal = $rfl->internal->getProperty($name); $token = $rfl->token->getProperty($name); $this->assertSame($internal->$method(), $internal->$method()); $this->assertTrue($token->$method()); $this->assertSame($internal->isStatic(), $internal->isStatic()); $this->assertFalse($token->isStatic()); $this->assertSame($internal->getModifiers(), $token->getModifiers()); $this->assertSame(constant('\ReflectionProperty::IS_' . strtoupper($name)), $token->getModifiers()); $internal = $rfl->internal->getProperty($opposite); $token = $rfl->token->getProperty($opposite); $this->assertSame($internal->$method(), $internal->$method()); $this->assertFalse($token->$method()); $this->assertSame($internal->getModifiers(), $token->getModifiers()); $internal = $rfl->internal->getProperty($staticName); $token = $rfl->token->getProperty($staticName); $this->assertSame($internal->$method(), $internal->$method()); $this->assertTrue($token->$method()); $this->assertSame($internal->isStatic(), $internal->isStatic()); $this->assertTrue($token->isStatic()); $this->assertSame($internal->getModifiers(), $token->getModifiers()); $this->assertSame(InternalReflectionProperty::IS_STATIC | constant('\ReflectionProperty::IS_' . strtoupper($name)), $token->getModifiers()); } } /** * Tests different types of property value. */ public function testTypes() { $constants = array('string' => 'string', 'integer' => 1, 'float' => 1.1, 'boolean' => true, 'null' => null, 'array' => array(1 => 1)); foreach ($constants as $type => $value) { $test = 'type' . ucfirst($type); $rfl = $this->getPropertyReflection($test); $className = $this->getClassName($test); $object = new $className(); $this->assertSame($rfl->internal->getValue($object), $rfl->token->getValue($object)); $this->assertSame($value, $rfl->token->getValue($object)); } } /** * Tests export. */ public function testToString() { $tests = array( 'lines', 'docComment', 'noComment', 'default', 'typeNull', 'typeArray', 'typeString', 'typeInteger', 'typeFloat' ); foreach ($tests as $test) { $rfl = $this->getPropertyReflection($test); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); $this->assertSame(InternalReflectionProperty::export($this->getClassName($test), $test, true), ReflectionProperty::export($this->getBroker(), $this->getClassName($test), $test, true)); // Test loading from a string $rfl = $this->getPropertyReflection($test, true); $this->assertSame($rfl->internal->__toString(), $rfl->token->__toString()); } $rfl = $this->getClassReflection('modifiers'); $rfl_fromString = $this->getClassReflection('modifiers'); foreach (array('public', 'protected', 'private') as $name) { $internal = $rfl->internal->getProperty($name); $token = $rfl->token->getProperty($name); $this->assertSame($internal->__toString(), $token->__toString()); $this->assertSame(InternalReflectionProperty::export($this->getClassName('modifiers'), $name, true), ReflectionProperty::export($this->getBroker(), $this->getClassName('modifiers'), $name, true)); // Test loading from a string $this->assertSame($internal->__toString(), $rfl_fromString->token->getProperty($name)->__toString()); } $this->assertSame(InternalReflectionProperty::export('ReflectionProperty', 'name', true), ReflectionProperty::export($this->getBroker(), 'ReflectionProperty', 'name', true)); $this->assertSame(InternalReflectionProperty::export(new InternalReflectionProperty('ReflectionProperty', 'name'), 'name', true), ReflectionProperty::export($this->getBroker(), new InternalReflectionProperty('ReflectionProperty', 'name'), 'name', true)); } /** * Tests new PHP 5.4 features. */ public function test54features() { if (PHP_VERSION_ID < 50400) { $this->markTestSkipped('Tested only on PHP 5.4+'); } $tests = array('public', 'protected', 'private'); $rfl = $this->getClassReflection('54features'); $class = $rfl->internal->newInstance(); foreach ($tests as $test) { $this->assertTrue($rfl->internal->hasProperty($test)); $this->assertTrue($rfl->token->hasProperty($test)); $internal = $rfl->internal->getProperty($test); $token = $rfl->token->getProperty($test); $internal->setAccessible(true); $token->setAccessible(true); $this->assertSame($internal->getValue($class), $token->getValue($class)); $this->assertSame($internal->getValue($class), $token->getDefaultValue()); } } /** * Tests an exception thrown when trying to create the reflection from a PHP internal reflection. * * @expectedException \TokenReflection\Exception\RuntimeException */ public function testInternalPropertyReflectionCreate() { Php\ReflectionProperty::create(new \ReflectionClass('Exception'), $this->getBroker()); } /** * Tests various constant (mis)definitions. */ public function testValueDefinitions() { static $expected = array( 'property1' => true, 'property2' => true, 'property3' => true, 'property4' => true, 'property5' => true, 'property6' => true, 'property7' => true, 'property8' => true ); $rfl = $this->getClassTokenReflection('valueDefinitions'); foreach ($expected as $name => $value) { $this->assertTrue($rfl->hasProperty($name), $name); $this->assertSame($value, $rfl->getProperty($name)->getDefaultValue(), $name); } } } tests/TokenReflection/SourceCodeTest.php000064400000002533144760110310014405 0ustar00createBroker(); $broker->processDirectory(__DIR__ . '/../../TokenReflection'); $classes = $broker->getClasses(); $this->assertGreaterThan(0, count($classes)); foreach ($classes as $class) { $this->assertNotSame(false, $class->getDocComment(), $class->getPrettyName()); foreach ($class->getMethods() as $method) { if (!$method->isInternal()) { $this->assertNotSame(false, $method->getDocComment(), $method->getPrettyName()); } } foreach ($class->getProperties() as $property) { if (!$property->isInternal()) { $this->assertNotSame(false, $property->getDocComment(), $property->getPrettyName()); } } foreach ($class->getConstantReflections() as $constant) { if (!$constant->isInternal()) { $this->assertNotSame(false, $constant->getDocComment(), $constant->getPrettyName()); } } } } } tests/TokenReflection/StreamTest.php000064400000006140144760110310013603 0ustar00getFileStream('invalid-stream'); unset($stream[666]); } /** * Tests the (im)possibility to set a token in a token stream. * * @expectedException \TokenReflection\Exception\StreamException */ public function testSetException() { $stream = $this->getFileStream('invalid-stream'); $stream[0] = null; } /** * Tests an exception thrown when calling findMatchingBracket and the current token is not a bracket. * * @expectedException \TokenReflection\Exception\StreamException */ public function testFindMatchingBracketException1() { $this->getFileStream('invalid-stream')->findMatchingBracket(); } /** * Tests an exception thrown when no matching bracket could be found. * * @expectedException \TokenReflection\Exception\StreamException */ public function testFindMatchingBracketException2() { $stream = $this->getFileStream('invalid-stream'); $this->assertInstanceOf('\TokenReflection\Stream\FileStream', $stream->find('{')); $stream->findMatchingBracket(); } /** * Tests an exception thrown when calling findMatchingBracket and being beyond the end of the token. * * @expectedException \TokenReflection\Exception\StreamException */ public function testFindMatchingBracketException3() { $stream = $this->getFileStream('invalid-stream'); $stream->seek(count($stream)); $this->assertFalse($stream->valid()); $stream->findMatchingBracket(); } /** * Tests an exception thrown when trying to load a non existent file. * * @expectedException \TokenReflection\Exception\StreamException */ public function testOpeningNonExistentFileException1() { $file = __DIR__ . DIRECTORY_SEPARATOR . '~#nonexistent#~'; if (is_file($file)) { $this->markTestSkipped(sprintf('File %s exists.', $file)); } $stream = new Stream\FileStream($file); } /** * Tests an exception thrown when trying to load a non existent file. * * @expectedException \TokenReflection\Exception\StreamException */ public function testOpeningNonExistentFileException2() { $file = __DIR__ . DIRECTORY_SEPARATOR . '~#nonexistent#~' . DIRECTORY_SEPARATOR . '~#nonexistent#~'; if (is_file($file)) { $this->markTestSkipped(sprintf('File %s exists.', $file)); } $stream = new Stream\FileStream($file); } /** * Returns a file token stream. * * @param string $name File name * @return \TokenReflection\Stream\FileStream */ private function getFileStream($name) { return new Stream\FileStream($this->getFilePath($name)); } } tests/TokenReflection/Test.php000064400000021537144760110310012436 0ustar00getBroker()->processFile($this->getFilePath($test), true); } /** * Returns class reflections. * * @param string $test * @param boolean $fromString * @return \stdClass */ protected function getClassReflection($test, $fromString = false) { $reflection = new \stdClass(); $reflection->internal = $this->getClassInternalReflection($test); $reflection->token = $this->getClassTokenReflection($test, $fromString); return $reflection; } /** * Returns method reflections. * * @param string $test * @param boolean $fromString * @return \stdClass */ protected function getMethodReflection($test, $fromString = false) { $reflection = new \stdClass(); $reflection->internal = $this->getMethodInternalReflection($test); $reflection->token = $this->getMethodTokenReflection($test, $fromString); return $reflection; } /** * Returns property reflections. * * @param string $test * @param boolean $fromString * @return \stdClass */ protected function getPropertyReflection($test, $fromString = false) { $reflection = new \stdClass(); $reflection->internal = $this->getPropertyInternalReflection($test); $reflection->token = $this->getPropertyTokenReflection($test, $fromString); return $reflection; } /** * Returns function reflections. * * @param string $test * @param boolean $fromString * @return \stdClass */ protected function getFunctionReflection($test, $fromString = false) { $reflection = new \stdClass(); $reflection->internal = $this->getFunctionInternalReflection($test); $reflection->token = $this->getFunctionTokenReflection($test, $fromString); return $reflection; } /** * Returns parameter reflections. * * @param string $test * @param boolean $fromString * @return \stdClass */ protected function getParameterReflection($test, $fromString = false) { $reflection = new \stdClass(); $reflection->internal = $this->getParameterInternalReflection($test); $reflection->token = $this->getParameterTokenReflection($test, $fromString); return $reflection; } /** * Returns internal class reflection. * * @param string $test * @return \ReflectionClass */ protected function getClassInternalReflection($test) { require_once $this->getFilePath($test); return new \ReflectionClass($this->getClassName($test)); } /** * Returns internal method reflection. * * @param string $test * @return \ReflectionMethod */ protected function getMethodInternalReflection($test) { return $this->getClassInternalReflection($test)->getMethod($this->getMethodName($test)); } /** * Returns internal property reflection. * * @param string $test * @return \ReflectionProperty */ protected function getPropertyInternalReflection($test) { return $this->getClassInternalReflection($test)->getProperty($this->getPropertyName($test)); } /** * Returns internal function reflection. * * @param string $test * @return \ReflectionFunction */ protected function getFunctionInternalReflection($test) { require_once $this->getFilePath($test); return new \ReflectionFunction($this->getFunctionName($test)); } /** * Returns internal parameter reflection. * * @param string $test * @return \ReflectionParameter */ protected function getParameterInternalReflection($test) { require_once $this->getFilePath($test); $function = new \ReflectionFunction($this->getFunctionName($test)); $parameters = $function->getParameters(); return $parameters[0]; } /** * Returns tokenized class reflection. * * @param string $test * @param boolean $fromString * @return \TokenReflection\ReflectionClass */ protected function getClassTokenReflection($test, $fromString = false) { $broker = $this->getBroker(); if ($fromString) { $source = file_get_contents($fileName = $this->getFilePath($test)); $broker->processString($source, $fileName); } else { $broker->processFile($this->getFilePath($test)); } return $broker->getClass($this->getClassName($test)); } /** * Returns tokenized method reflection. * * @param string $test * @param boolean $fromString * @return \TokenReflection\ReflectionMethod */ protected function getMethodTokenReflection($test, $fromString = false) { return $this->getClassTokenReflection($test, $fromString)->getMethod($this->getMethodName($test)); } /** * Returns tokenized property reflection. * * @param string $test * @param boolean $fromString * @return \TokenReflection\ReflectionProperty */ protected function getPropertyTokenReflection($test, $fromString = false) { return $this->getClassTokenReflection($test, $fromString)->getProperty($this->getPropertyName($test)); } /** * Returns tokenized constant reflection. * * @param string $test * @param boolean $fromString * @return \TokenReflection\ReflectionConstant */ protected function getConstantTokenReflection($test, $fromString = false) { return $this->getClassTokenReflection($test, $fromString)->getConstantReflection($this->getConstantName($test)); } /** * Returns tokenized function reflection. * * @param string $test * @param boolean $fromString * @return \TokenReflection\ReflectionFunction */ protected function getFunctionTokenReflection($test, $fromString = false) { $broker = $this->getBroker(); if ($fromString) { $source = file_get_contents($fileName = $this->getFilePath($test)); $broker->processString($source, $fileName); } else { $broker->processFile($this->getFilePath($test)); } return $broker->getFunction($this->getFunctionName($test)); } /** * Returns tokenized parameter reflection. * * @param string $test * @param boolean $fromString * @return \TokenReflection\ReflectionParameter */ protected function getParameterTokenReflection($test, $fromString = false) { $broker = $this->getBroker(); if ($fromString) { $source = file_get_contents($fileName = $this->getFilePath($test)); $broker->processString($source, $fileName); } else { $broker->processFile($this->getFilePath($test)); } $parameters = $broker->getFunction($this->getFunctionName($test))->getParameters(); return $parameters[0]; } /** * Returns test file path. * * @param string $test * @return string */ protected function getFilePath($test) { $file = preg_replace_callback('~[A-Z]~', function($matches) { return '-' . strtolower($matches[0]); }, $test); return realpath(__DIR__ . '/../data/' . $this->type . '/' . $file . '.php'); } /** * Returns test class name. * * @param string $test * @return string */ protected function getClassName($test) { return 'TokenReflection_Test_' . ucfirst($this->type) . ucfirst($test); } /** * Returns test method name. * * @param string $test * @return string */ protected function getMethodName($test) { return $test; } /** * Returns test property name. * * @param string $test * @return string */ protected function getPropertyName($test) { return $test; } /** * Returns test constant name. * * @param string $test * @return string */ protected function getConstantName($test) { return strtoupper(preg_replace_callback('~[A-Z]~', function($matches) { return '_' . $matches[0]; }, $test)); } /** * Returns test function name. * * @param string $test * @return string */ protected function getFunctionName($test) { return 'tokenReflection' . ucfirst($this->type) . ucfirst($test); } /** * Returns a new broker instance. * * @return \TokenReflection\Broker */ public function createBroker() { return new Broker(new Broker\Backend\Memory()); } /** * Returns broker instance. * * @return \TokenReflection\Broker */ protected function getBroker() { static $broker = null; if (null === $broker) { $broker = $this->createBroker(); } return $broker; } /** * Returns all filters combinations. * * @param array $filters * @return array */ protected function getFilterCombinations(array $filters) { $combinations = array(); for ($i = 0; $i < pow(2, count($filters)); $i++) { $combination = 0; for ($j = 0; $j < count($filters); $j++) { if ($i % pow(2, $j + 1) < pow(2, $j)) { $combination |= $filters[$j]; } } $combinations[] = $combination; } return $combinations; } } tests/bootstrap.php000064400000001266144760110310010436 0ustar00private = $private; } } class TokenReflection_Test_ClassInstancesChild extends TokenReflection_Test_ClassInstances { } tests/data/class/interface.php000064400000000070144760110310012367 0ustar00check = true; } } tests/data/class/no-abstract.php000064400000000065144760110310012650 0ustar00 1); static $array2 = array(1 => 1, 2 => 2); static $constant = TOKENREFLECTION_FUNCTION_STATIC_VARIABLE_VALUE; } tests/data/function/user-defined.php000064400000000070144760110310013521 0ustar00 1); static $array2 = array(1 => 1, 2 => 2); static $constants = array( TokenReflection_Test_MethodStaticVariables::SELF, TokenReflection_Test_MethodStaticVariablesParent::PARENT ); } }tests/data/method/user-defined.php000064400000000134144760110310013155 0ustar00 1); }tests/data/property/type-boolean.php000064400000000126144760110310013606 0ustar00