diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..288b592 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,8 @@ +filter: + paths: + - source/* + +checks: + php: + code_rating: true + duplication: true diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..8eac82e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: php +php: + - 5.3 + - 5.4 + - 5.5 + - 5.6 +before_script: + - composer self-update + - composer install --dev + - phpenv rehash +script: phpunit -v --colors --coverage-text +notifications: + email: + - artodeto@bazzline.net diff --git a/README.md b/README.md new file mode 100644 index 0000000..1b5f59f --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# Process Pipe Component in PHP + +This component easy up creation of a [pipe](http://en.wikipedia.org/wiki/Pipeline_(computing)) for processes in php. + +Indeed, it is a [pseudo pipeline](http://en.wikipedia.org/wiki/Pipeline_(software)#Pseudo-pipelines) (process collection or process batch) since the php process is single threaded so far. + +Currently, there is no plan to bloat the code base with an implementation of [STDIN](http://en.wikipedia.org/wiki/Standard_streams#Standard_input_.28stdin.29), [STDOUT](http://en.wikipedia.org/wiki/Standard_streams#Standard_output_.28stdout.29) or [STDERR](http://en.wikipedia.org/wiki/Standard_streams#Standard_error_.28stderr.29). +Errors can be handled by the thrown exception. Input is defined by the ExecutableInterface, as well as the output (return value). + + +@todo +The build status of the current master branch is tracked by Travis CI: +[![Build Status](https://travis-ci.org/bazzline/php_component_process_pipe.png?branch=master)](http://travis-ci.org/bazzline/php_component_process_pipe) +[![Latest stable](https://img.shields.io/packagist/v/net_bazzline/php_component_process_pipe.svg)](https://packagist.org/packages/net_bazzline/php_component_process_pipe) + + +@todo +The scrutinizer status are: +[![code quality](https://scrutinizer-ci.com/g/bazzline/php_component_process_pipe/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/bazzline/php_component_process_pipe/) | [![code coverage](https://scrutinizer-ci.com/g/bazzline/php_component_process_pipe/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/bazzline/php_component_process_pipe/) | [![build status](https://scrutinizer-ci.com/g/bazzline/php_component_process_pipe/badges/build.png?b=master)](https://scrutinizer-ci.com/g/bazzline/php_component_process_pipe/) + +@todo +The versioneye status is: +[![dependencies](https://www.versioneye.com/user/projects/53e48c23e0a229172f000146/badge.svg?style=flat)](https://www.versioneye.com/user/projects/53e48c23e0a229172f000146) + +Downloads: +[![Downloads this Month](https://img.shields.io/packagist/dm/net_bazzline/php_component_process_pipe.svg)](https://packagist.org/packages/net_bazzline/php_component_process_pipe) + +@todo +It is also available at [openhub.net](http://www.openhub.net/p/718154). + +# Examples + +* [no input](https://github.com/bazzline/php_component_process_pipe/tree/master/example/Example/NoInput/run.php] +* [input array](https://github.com/bazzline/php_component_process_pipe/tree/master/example/Example/InputArray/run.php] +* [failing execution](https://github.com/bazzline/php_component_process_pipe/tree/master/example/Example/FailingExecution/run.php] +* [input generator](https://github.com/bazzline/php_component_process_pipe/tree/master/example/Example/InputGenerator/run.php] +* [input transformer](https://github.com/bazzline/php_component_process_pipe/tree/master/example/Example/InputTransformer/run.php] +* [input validator](https://github.com/bazzline/php_component_process_pipe/tree/master/example/Example/InputValidator/run.php] +* [data flow manipulator](https://github.com/bazzline/php_component_process_pipe/tree/master/example/Example/DataFlowManipulator/run.php] + +# Install + +## Manuel + + mkdir -p vendor/net_bazzline/php_component_process_pipe + cd vendor/net_bazzline/php_component_process_pipe + git clone https://github.com/bazzline/php_component_process_pipe + +## With [Packagist](https://packagist.org/packages/net_bazzline/php_component_process_pipe) + + composer require net_bazzline/php_component_process_pipe:dev-master + +# Usage + +## By using the pipe method for multiple process + +```php +$pipe = new Pipe(); + +$pipe->pipe( + new ProcessOne(), + new ProcessTwo() +); + +$output = $pipe->execute($input); + +``` +## By using the pipe method once for each process + +```php +$pipe = new Pipe(); + +$pipe->pipe(new ProcessOne()); +$pipe->pipe(new ProcessTwo()); + +$output = $pipe->execute($input); +``` + +## By instantiation + +```php +$pipe = new Pipe( + new ProcessOne(), + new ProcessTwo() +); + +$output = $pipe->execute($input); +``` + + +# API + +Thanks to [apigen](https://github.com/apigen/apigen), the api is available in the [document](https://github.com/bazzline/php_component_process_pipe/blob/master/document/index.html) section or [online](http://code.bazzline.net/). + +# History + +* [1.0.1](https://github.com/bazzline/php_component_process_pipe/tree/1.0.1) - not released yet +* [1.0.0](https://github.com/bazzline/php_component_process_pipe/tree/1.0.0) - not released yet + * initial release + +# Links + +* [pipes](https://github.com/vkartaviy/pipes) +* [php-pipeline](https://github.com/JosephMoniz/php-pipeline) +* [php-pipeline-lib](https://github.com/phppro/php-pipeline-lib) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..15c996e --- /dev/null +++ b/composer.json @@ -0,0 +1,32 @@ +{ + "description": "php component process to easy up creation of pipe process in php", + "keywords": ["php", "pipe", "process", "component", "process pipe"], + "license": "LGPLv3", + "name": "net_bazzline/php_component_process_pipe", + "type": "library", + "authors": [ + { + "email": "artodeto@bazzline.net", + "homepage": "https://artodeto.bazzline.et", + "name": "Stev Leibelt", + "role": "Developer" + } + ], + "minimum-stability": "dev", + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "apigen/apigen": "2.8.1", + "mockery/mockery": "0.9.2", + "phpmd/phpmd": "2.1.3", + "phpunit/phpunit": "4.3.4" + }, + "autoload": { + "psr-0": { + "Example": "example/", + "Net\\Bazzline\\Component\\ProcessPipe": "source/", + "Test\\Net\\Bazzline\\Component\\ProcessPipe": "test/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..f3358bc --- /dev/null +++ b/composer.lock @@ -0,0 +1,1339 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "b63723a93f3ae58d45d4071e20fa6689", + "packages": [], + "packages-dev": [ + { + "name": "andrewsville/php-token-reflection", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/Andrewsville/PHP-Token-Reflection.git", + "reference": "3e3a36de17f32889fd2d4b8108af16d3033ce9bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Andrewsville/PHP-Token-Reflection/zipball/3e3a36de17f32889fd2d4b8108af16d3033ce9bf", + "reference": "3e3a36de17f32889fd2d4b8108af16d3033ce9bf", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "psr-0": { + "TokenReflection": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3" + ], + "authors": [ + { + "name": "Ondřej Nešpor", + "homepage": "https://github.com/andrewsville" + }, + { + "name": "Jaroslav Hanslík", + "homepage": "https://github.com/kukulich" + } + ], + "description": "Library emulating the PHP internal reflection using just the tokenized source code.", + "homepage": "http://andrewsville.github.com/PHP-Token-Reflection/", + "keywords": [ + "library", + "reflection", + "tokenizer" + ], + "time": "2012-08-25 21:26:44" + }, + { + "name": "apigen/apigen", + "version": "v2.8.1", + "source": { + "type": "git", + "url": "https://github.com/apigen/apigen.git", + "reference": "bca0954e92621a48c6870be57115026a8d032706" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/apigen/apigen/zipball/bca0954e92621a48c6870be57115026a8d032706", + "reference": "bca0954e92621a48c6870be57115026a8d032706", + "shasum": "" + }, + "require": { + "andrewsville/php-token-reflection": "~1.3.1", + "ext-json": "*", + "ext-mbstring": "*", + "kukulich/fshl": "~2.1.0", + "nette/nette": "~2.1.1", + "php": ">=5.3.0", + "texy/texy": "~2.4.0" + }, + "suggest": { + "ext-bz2": "*", + "ext-phar": "*", + "ext-zip": "*", + "ext-zlib": "*" + }, + "bin": [ + "apigen" + ], + "type": "project", + "extra": { + "branch-alias": { + "dev-develop": "3.0.0-dev" + } + }, + "autoload": { + "psr-0": { + "ApiGen": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "http://davidgrudl.com" + }, + { + "name": "Ondřej Nešpor", + "homepage": "https://github.com/andrewsville" + }, + { + "name": "Jaroslav Hanslík", + "homepage": "https://github.com/kukulich" + } + ], + "description": "API documentation generator for PHP 5.3+", + "homepage": "http://apigen.org/", + "keywords": [ + "api", + "docblock", + "documentation", + "generator", + "phpDocumentor", + "phpdoc" + ], + "time": "2014-09-01 18:06:36" + }, + { + "name": "doctrine/instantiator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f976e5de371104877ebc89bd8fecb0019ed9c119", + "reference": "f976e5de371104877ebc89bd8fecb0019ed9c119", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "2.0.*@ALPHA" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Instantiator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2014-10-13 12:58:55" + }, + { + "name": "kukulich/fshl", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/kukulich/fshl.git", + "reference": "974c294ade5d76c0c16b6fe3fd3a584ba999b24f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kukulich/fshl/zipball/974c294ade5d76c0c16b6fe3fd3a584ba999b24f", + "reference": "974c294ade5d76c0c16b6fe3fd3a584ba999b24f", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "psr-0": { + "FSHL": "./" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+" + ], + "authors": [ + { + "name": "Jaroslav Hanslík", + "homepage": "https://github.com/kukulich" + } + ], + "description": "FSHL is a free, open source, universal, fast syntax highlighter written in PHP.", + "homepage": "http://fshl.kukulich.cz/", + "keywords": [ + "highlight", + "library", + "syntax" + ], + "time": "2012-09-08 19:00:07" + }, + { + "name": "mockery/mockery", + "version": "0.9.2", + "source": { + "type": "git", + "url": "https://github.com/padraic/mockery.git", + "reference": "95a4855380dc70176c51807c678fb3bd6198529a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/padraic/mockery/zipball/95a4855380dc70176c51807c678fb3bd6198529a", + "reference": "95a4855380dc70176c51807c678fb3bd6198529a", + "shasum": "" + }, + "require": { + "lib-pcre": ">=7.0", + "php": ">=5.3.2" + }, + "require-dev": { + "hamcrest/hamcrest-php": "~1.1", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "~0.7@dev" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, + "autoload": { + "psr-0": { + "Mockery": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "http://blog.astrumfutura.com" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "http://davedevelopment.co.uk" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succint API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.", + "homepage": "http://github.com/padraic/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "time": "2014-09-03 10:11:10" + }, + { + "name": "nette/nette", + "version": "2.1.x-dev", + "source": { + "type": "git", + "url": "https://github.com/nette/nette.git", + "reference": "34cdcbff5b2c22a17e8efeb0a9035d8b7a29a53d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nette/nette/zipball/34cdcbff5b2c22a17e8efeb0a9035d8b7a29a53d", + "reference": "34cdcbff5b2c22a17e8efeb0a9035d8b7a29a53d", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "ext-tokenizer": "*", + "php": ">=5.3.1" + }, + "require-dev": { + "nette/tester": "~1.3" + }, + "suggest": { + "ext-fileinfo": "", + "ext-gd": "", + "ext-mbstring": "", + "ext-pdo": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "Nette/" + ], + "files": [ + "Nette/common/shortcuts.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "http://davidgrudl.com" + }, + { + "name": "Nette Community", + "homepage": "http://nette.org/contributors" + } + ], + "description": "Nette Framework - innovative framework for fast and easy development of secured web applications in PHP. Write less, have cleaner code and your work will bring you joy.", + "homepage": "http://nette.org", + "keywords": [ + "Forms", + "database", + "debugging", + "framework", + "mailing", + "mvc", + "templating" + ], + "time": "2014-11-08 18:49:54" + }, + { + "name": "pdepend/pdepend", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/pdepend/pdepend.git", + "reference": "dc582a3c0180664a8fbfc5a34efaf4cc13fccc60" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/dc582a3c0180664a8fbfc5a34efaf4cc13fccc60", + "reference": "dc582a3c0180664a8fbfc5a34efaf4cc13fccc60", + "shasum": "" + }, + "require": { + "symfony/config": "@stable", + "symfony/dependency-injection": "@stable", + "symfony/filesystem": "@stable" + }, + "require-dev": { + "phpunit/phpunit": "3.*@stable", + "squizlabs/php_codesniffer": "@stable" + }, + "bin": [ + "src/bin/pdepend" + ], + "type": "library", + "autoload": { + "psr-0": { + "PDepend\\": "src/main/php/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Official version of pdepend to be handled with Composer", + "time": "2014-10-08 06:54:50" + }, + { + "name": "phpmd/phpmd", + "version": "2.1.3", + "source": { + "type": "git", + "url": "https://github.com/phpmd/phpmd.git", + "reference": "1a485d9db869137af5e9678bd844568c92998b25" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/1a485d9db869137af5e9678bd844568c92998b25", + "reference": "1a485d9db869137af5e9678bd844568c92998b25", + "shasum": "" + }, + "require": { + "pdepend/pdepend": "2.0.*", + "php": ">=5.3.0", + "symfony/config": "2.5.*", + "symfony/dependency-injection": "2.5.*", + "symfony/filesystem": "2.5.*" + }, + "bin": [ + "src/bin/phpmd" + ], + "type": "library", + "autoload": { + "psr-0": { + "PHPMD\\": "src/main/php" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "Official version of PHPMD handled with Composer.", + "time": "2014-09-25 15:56:22" + }, + { + "name": "phpunit/php-code-coverage", + "version": "2.0.x-dev", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "c3e185a27ae59680237c836caecef66c8bd839a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c3e185a27ae59680237c836caecef66c8bd839a8", + "reference": "c3e185a27ae59680237c836caecef66c8bd839a8", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "phpunit/php-file-iterator": "~1.3", + "phpunit/php-text-template": "~1.2", + "phpunit/php-token-stream": "~1.3", + "sebastian/environment": "~1.0", + "sebastian/version": "~1.0" + }, + "require-dev": { + "ext-xdebug": ">=2.1.4", + "phpunit/phpunit": "~4.1" + }, + "suggest": { + "ext-dom": "*", + "ext-xdebug": ">=2.2.1", + "ext-xmlwriter": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "time": "2014-10-31 10:06:54" + }, + { + "name": "phpunit/php-file-iterator", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", + "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "File/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "time": "2013-10-10 15:34:57" + }, + { + "name": "phpunit/php-text-template", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "Text/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "time": "2014-01-30 17:20:04" + }, + { + "name": "phpunit/php-timer", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "autoload": { + "classmap": [ + "PHP/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "time": "2013-08-02 07:42:54" + }, + { + "name": "phpunit/php-token-stream", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-token-stream.git", + "reference": "f8d5d08c56de5cfd592b3340424a81733259a876" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/f8d5d08c56de5cfd592b3340424a81733259a876", + "reference": "f8d5d08c56de5cfd592b3340424a81733259a876", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Wrapper around PHP's tokenizer extension.", + "homepage": "https://github.com/sebastianbergmann/php-token-stream/", + "keywords": [ + "tokenizer" + ], + "time": "2014-08-31 06:12:13" + }, + { + "name": "phpunit/phpunit", + "version": "4.3.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "23e4e0310f037aae873cc81b8658dbbb82878f71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/23e4e0310f037aae873cc81b8658dbbb82878f71", + "reference": "23e4e0310f037aae873cc81b8658dbbb82878f71", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=5.3.3", + "phpunit/php-code-coverage": "~2.0", + "phpunit/php-file-iterator": "~1.3.2", + "phpunit/php-text-template": "~1.2", + "phpunit/php-timer": "~1.0.2", + "phpunit/phpunit-mock-objects": "~2.3", + "sebastian/comparator": "~1.0", + "sebastian/diff": "~1.1", + "sebastian/environment": "~1.0", + "sebastian/exporter": "~1.0", + "sebastian/version": "~1.0", + "symfony/yaml": "~2.0" + }, + "suggest": { + "phpunit/php-invoker": "~1.1" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "", + "../../symfony/yaml/" + ], + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "http://www.phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "time": "2014-10-22 11:43:12" + }, + { + "name": "phpunit/phpunit-mock-objects", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", + "reference": "96c5b81f9842f38fe6c73ad0020306cc4862a9e3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/96c5b81f9842f38fe6c73ad0020306cc4862a9e3", + "reference": "96c5b81f9842f38fe6c73ad0020306cc4862a9e3", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "~1.0,>=1.0.2", + "php": ">=5.3.3", + "phpunit/php-text-template": "~1.2" + }, + "require-dev": { + "phpunit/phpunit": "4.4.*@dev" + }, + "suggest": { + "ext-soap": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.4.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sb@sebastian-bergmann.de", + "role": "lead" + } + ], + "description": "Mock Object library for PHPUnit", + "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/", + "keywords": [ + "mock", + "xunit" + ], + "time": "2014-10-04 10:04:20" + }, + { + "name": "sebastian/comparator", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "6f67d2ae044ba17ba30573941f4ac96c4777be97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/6f67d2ae044ba17ba30573941f4ac96c4777be97", + "reference": "6f67d2ae044ba17ba30573941f4ac96c4777be97", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "sebastian/diff": "~1.1", + "sebastian/exporter": "~1.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "http://www.github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "time": "2014-10-21 10:04:18" + }, + { + "name": "sebastian/diff", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "3e22c89be2e1cddf7db89699cb23a9159df12e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3e22c89be2e1cddf7db89699cb23a9159df12e0c", + "reference": "3e22c89be2e1cddf7db89699cb23a9159df12e0c", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Diff implementation", + "homepage": "http://www.github.com/sebastianbergmann/diff", + "keywords": [ + "diff" + ], + "time": "2014-11-05 15:28:21" + }, + { + "name": "sebastian/environment", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e6c71d918088c251b181ba8b3088af4ac336dd7", + "reference": "6e6c71d918088c251b181ba8b3088af4ac336dd7", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "http://www.github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "time": "2014-10-25 08:00:45" + }, + { + "name": "sebastian/exporter", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", + "reference": "c7d59948d6e82818e1bdff7cadb6c34710eb7dc0", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "http://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "time": "2014-09-10 00:51:36" + }, + { + "name": "sebastian/version", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43", + "shasum": "" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "time": "2014-03-07 15:35:33" + }, + { + "name": "symfony/config", + "version": "2.5.x-dev", + "target-dir": "Symfony/Component/Config", + "source": { + "type": "git", + "url": "https://github.com/symfony/Config.git", + "reference": "9332a28782d97a1cb80deb5aa3459df5aabe75d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Config/zipball/9332a28782d97a1cb80deb5aa3459df5aabe75d6", + "reference": "9332a28782d97a1cb80deb5aa3459df5aabe75d6", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/filesystem": "~2.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Config\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Config Component", + "homepage": "http://symfony.com", + "time": "2014-11-03 03:54:42" + }, + { + "name": "symfony/dependency-injection", + "version": "2.5.x-dev", + "target-dir": "Symfony/Component/DependencyInjection", + "source": { + "type": "git", + "url": "https://github.com/symfony/DependencyInjection.git", + "reference": "0bac5e7436e2627785b0497d72130a015e881fdc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/DependencyInjection/zipball/0bac5e7436e2627785b0497d72130a015e881fdc", + "reference": "0bac5e7436e2627785b0497d72130a015e881fdc", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "symfony/config": "~2.2", + "symfony/expression-language": "~2.4", + "symfony/yaml": "~2.0" + }, + "suggest": { + "symfony/config": "", + "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", + "symfony/yaml": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\DependencyInjection\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DependencyInjection Component", + "homepage": "http://symfony.com", + "time": "2014-11-03 03:54:42" + }, + { + "name": "symfony/filesystem", + "version": "2.5.x-dev", + "target-dir": "Symfony/Component/Filesystem", + "source": { + "type": "git", + "url": "https://github.com/symfony/Filesystem.git", + "reference": "4e62fab0060a826561c78b665925b37c870c45f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Filesystem/zipball/4e62fab0060a826561c78b665925b37c870c45f5", + "reference": "4e62fab0060a826561c78b665925b37c870c45f5", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Filesystem\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Filesystem Component", + "homepage": "http://symfony.com", + "time": "2014-09-22 09:14:18" + }, + { + "name": "symfony/yaml", + "version": "dev-master", + "target-dir": "Symfony/Component/Yaml", + "source": { + "type": "git", + "url": "https://github.com/symfony/Yaml.git", + "reference": "3db1d6cb51b49840e0f3b1663da23cccf0286bf6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/3db1d6cb51b49840e0f3b1663da23cccf0286bf6", + "reference": "3db1d6cb51b49840e0f3b1663da23cccf0286bf6", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-0": { + "Symfony\\Component\\Yaml\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Yaml Component", + "homepage": "http://symfony.com", + "time": "2014-11-10 18:00:54" + }, + { + "name": "texy/texy", + "version": "v2.4", + "source": { + "type": "git", + "url": "https://github.com/dg/texy.git", + "reference": "67d02cd95e4aaa7dae96b24a7d04ba924d641015" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dg/texy/zipball/67d02cd95e4aaa7dae96b24a7d04ba924d641015", + "reference": "67d02cd95e4aaa7dae96b24a7d04ba924d641015", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "replace": { + "dg/texy": "self.version" + }, + "require-dev": { + "nette/tester": "~1.0.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/texy.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause", + "GPL-2.0", + "GPL-3.0" + ], + "authors": [ + { + "name": "David Grudl", + "homepage": "http://davidgrudl.com" + } + ], + "description": "Texy converts plain text in easy to read Texy syntax into structurally valid (X)HTML. It supports adding of images, links, nested lists, tables and has full support for CSS. Texy supports hyphenation of long words (which reflects language rules), clickable emails and URL (emails are obfuscated against spambots), national typographic single and double quotation marks, ellipses, em dashes, dimension sign, nonbreakable spaces (e.g. in phone numbers), acronyms, arrows and many others. Texy code can optionally contain HTML tags.", + "homepage": "http://texy.info", + "keywords": [ + "html", + "markdown", + "markup language", + "plain text", + "text", + "textile", + "texy", + "wiki", + "xhtml" + ], + "time": "2014-02-10 02:34:57" + } + ], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "platform": { + "php": ">=5.3.3" + }, + "platform-dev": [] +} diff --git a/example/Example/DataFlowManipulator/run.php b/example/Example/DataFlowManipulator/run.php new file mode 100644 index 0000000..fe58b80 --- /dev/null +++ b/example/Example/DataFlowManipulator/run.php @@ -0,0 +1,115 @@ + + * @since 2014-11-09 + */ + +namespace Example\DataFlowManipulator; + +use Net\Bazzline\Component\ProcessPipe\ExecutableException; +use Net\Bazzline\Component\ProcessPipe\ExecutableInterface; +use Net\Bazzline\Component\ProcessPipe\Pipe; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +/** + * Class ArrayProcess + * @package De\Leibelt\ProcessPipe\Example\DataFlowManipulator + */ +class ArrayProcess implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws ExecutableException + */ + public function execute($input = null) + { + $input[] = __METHOD__; + + return $input; + } +} + +/** + * Class StringProcess + * @package De\Leibelt\ProcessPipe\Example\DataFlowManipulator + */ +class StringProcess implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws ExecutableException + */ + public function execute($input = null) + { + $input .= PHP_EOL . __METHOD__; + + return $input; + } +} + +/** + * Class DataFlowManipulator + */ +class DataFlowManipulator implements ExecutableInterface +{ + /** @var ArrayProcess */ + private $arrayProcess; + + /** @var StringProcess */ + private $stringProcess; + + /** + * @param ArrayProcess $process + * @return $this + */ + public function setArrayProcess(ArrayProcess $process) + { + $this->arrayProcess = $process; + + return $this; + } + + /** + * @param StringProcess $process + * @return $this + */ + public function setStringProcess(StringProcess $process) + { + $this->stringProcess = $process; + + return $this; + } + + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + if (is_array($input)) { + return $this->arrayProcess->execute($input); + } else if (is_string($input)) { + return $this->stringProcess->execute($input); + } else { + throw new ExecutableException('input must be from type of array or string'); + } + } +} + +$dataFlowManipulator = new DataFlowManipulator(); +$dataFlowManipulator->setArrayProcess(new ArrayProcess()) + ->setStringProcess(new StringProcess()); + +$pipe = new Pipe($dataFlowManipulator); + +$output = $pipe->execute('Hello World'); +echo 'string' . PHP_EOL; +echo var_export($output, true) . PHP_EOL; + +$output = $pipe->execute(array('Hello World')); +echo 'array' . PHP_EOL; +echo var_export($output, true) . PHP_EOL; diff --git a/example/Example/FailingExecution/run.php b/example/Example/FailingExecution/run.php new file mode 100644 index 0000000..c0c5b21 --- /dev/null +++ b/example/Example/FailingExecution/run.php @@ -0,0 +1,57 @@ + + * @since 2014-11-08 + */ + +namespace Example\FailingExecution; + +use Net\Bazzline\Component\ProcessPipe\ExecutableException; +use Net\Bazzline\Component\ProcessPipe\ExecutableInterface; +use Net\Bazzline\Component\ProcessPipe\Pipe; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +/** + * Class ProcessOne + */ +class ProcessOne implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + return $input; + } +} + +/** + * Class ProcessTwo + */ +class ProcessTwo implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + throw new ExecutableException(__METHOD__ . ' has failed'); + } +} + +$pipe = new Pipe( + new ProcessOne(), + new ProcessTwo() +); + +try { + $pipe->execute(); +} catch (ExecutableException $exception) { + echo 'error occurred:' . PHP_EOL; + echo $exception->getMessage() . PHP_EOL; +} diff --git a/example/Example/InputArray/run.php b/example/Example/InputArray/run.php new file mode 100644 index 0000000..6a968af --- /dev/null +++ b/example/Example/InputArray/run.php @@ -0,0 +1,72 @@ + + * @since 2014-11-08 + */ + +namespace Example\InputArray; + +use Net\Bazzline\Component\ProcessPipe\ExecutableInterface; +use Net\Bazzline\Component\ProcessPipe\Pipe; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +/** + * Class ProcessOne + */ +class ProcessOne implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + $input['name'] = 'bar'; + $input['steps'][] = __METHOD__; + $input['times'][] = microtime(true); + + return $input; + } +} + +/** + * Class ProcessTwo + */ +class ProcessTwo implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + $input['name'] = 'foobar'; + $input['steps'][] = __METHOD__; + $input['times'][] = microtime(true); + + return $input; + } +} + +$input = array( + 'name' => 'foo', + 'steps' => array(), + 'times' => array() +); +$pipe = new Pipe(); + +$pipe->pipe( + new ProcessOne(), + new ProcessTwo() +); + +echo 'input' . PHP_EOL; +echo var_export($input, true) . PHP_EOL; + +$output = $pipe->execute($input); + +echo 'output' . PHP_EOL; +echo var_export($output, true) . PHP_EOL; diff --git a/example/Example/InputGenerator/run.php b/example/Example/InputGenerator/run.php new file mode 100644 index 0000000..78a71d2 --- /dev/null +++ b/example/Example/InputGenerator/run.php @@ -0,0 +1,68 @@ + + * @since 2014-11-08 + */ + +namespace Example\InputGenerator; + +use Net\Bazzline\Component\ProcessPipe\ExecutableInterface; +use Net\Bazzline\Component\ProcessPipe\Pipe; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +/** + * Class DataGeneratorProcess + */ +class DataGeneratorProcess implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + $input = array(); + $input[] = array( + microtime(true), + 'debug', + 'new generated log data' + ); + + return $input; + } +} + +/** + * Class ProcessTwo + */ +class ProcessTwo implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + $input[] = array( + microtime(true), + 'debug', + 'hello world' + ); + + return $input; + } +} + +$pipe = new Pipe( + new DataGeneratorProcess(), + new ProcessTwo() +); + +$output = $pipe->execute(); + +foreach ($output as $log) { + echo '[' . $log[0] . '] [' . $log[1] . '] - ' . $log[2] . PHP_EOL; +} diff --git a/example/Example/InputTransformer/run.php b/example/Example/InputTransformer/run.php new file mode 100644 index 0000000..f2fd263 --- /dev/null +++ b/example/Example/InputTransformer/run.php @@ -0,0 +1,74 @@ + + * @since 2014-11-08 + */ + +namespace Example\InputTransformer; + +use Net\Bazzline\Component\ProcessPipe\ExecutableException; +use Net\Bazzline\Component\ProcessPipe\ExecutableInterface; +use Net\Bazzline\Component\ProcessPipe\Pipe; +use stdClass; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +/** + * Class ObjectToArrayTransformer + */ +class ObjectToArrayTransformer implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + if (!is_object($input)) { + throw new ExecutableException('input must be instance of object'); + } + + $array = array(); + + foreach (get_object_vars($input) as $property => $value) { + $array[$property] = $value; + } + + return $array; + } +} + +/** + * Class ArrayToJSONTransformer + * @package De\Leibelt\ProcessPipe\Example\WithDataTransformer + */ +class ArrayToJSONTransformer implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws ExecutableException + */ + public function execute($input = null) + { + if (!is_array($input)) { + throw new ExecutableException('input must be an array'); + } + + return json_encode($input); + } +} + +$pipe = new Pipe( + new ObjectToArrayTransformer(), + new ArrayToJSONTransformer() +); + +$object = new stdClass(); + +$object->foo = 'bar'; +$object->bar = 'foo'; +$object->foobar = 'barfoo'; + +echo $pipe->execute($object) . PHP_EOL; diff --git a/example/Example/InputValidator/run.php b/example/Example/InputValidator/run.php new file mode 100644 index 0000000..12de615 --- /dev/null +++ b/example/Example/InputValidator/run.php @@ -0,0 +1,70 @@ + + * @since 2014-11-09 + */ + +namespace Example\InputValidator; + +use Net\Bazzline\Component\ProcessPipe\ExecutableException; +use Net\Bazzline\Component\ProcessPipe\ExecutableInterface; +use Net\Bazzline\Component\ProcessPipe\Pipe; +use Exception; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +/** + * Class ProcessOne + */ +class ProcessOne implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + $input .= ' ' . __METHOD__; + + return $input; + } +} + +/** + * Class ProcessTwo + */ +class ProcessTwo implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + if (!is_array($input)) { + throw new ExecutableException('input must be type of array'); + } + + $input[] = __METHOD__; + + return $input; + } +} + +$input = 'string'; + +$pipe = new Pipe(); + +$pipe->pipe( + new ProcessOne(), + new ProcessTwo() +); + +try { + $output = $pipe->execute($input); + echo $output . PHP_EOL; +} catch (Exception $exception) { + echo 'caught exception with message: ' . $exception->getMessage() . PHP_EOL; +} diff --git a/example/Example/NoInput/run.php b/example/Example/NoInput/run.php new file mode 100644 index 0000000..a01f562 --- /dev/null +++ b/example/Example/NoInput/run.php @@ -0,0 +1,58 @@ + + * @since 2014-11-08 + */ + +namespace Example\NoInput; + +use Net\Bazzline\Component\ProcessPipe\ExecutableInterface; +use Net\Bazzline\Component\ProcessPipe\Pipe; + +require_once __DIR__ . '/../../../vendor/autoload.php'; + +/** + * Class ProcessOne + */ +class ProcessOne implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + echo __METHOD__ . PHP_EOL; + sleep(1); + + return $input; + } +} + +/** + * Class ProcessTwo + */ +class ProcessTwo implements ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws \Net\Bazzline\Component\ProcessPipe\ExecutableException + */ + public function execute($input = null) + { + echo __METHOD__ . PHP_EOL; + + return $input; + } +} + +$pipe = new Pipe(); +$processOne = new ProcessOne(); +$processTwo = new ProcessTwo(); + +$pipe->pipe($processOne); +$pipe->pipe($processTwo); + +$pipe->execute(); diff --git a/generate_api.php b/generate_api.php new file mode 100755 index 0000000..b3fa2af --- /dev/null +++ b/generate_api.php @@ -0,0 +1,6 @@ +#!/bin/php + + + + + test/ + + + diff --git a/source/Net/Bazzline/Component/ProcessPipe/ExecutableException.php b/source/Net/Bazzline/Component/ProcessPipe/ExecutableException.php new file mode 100644 index 0000000..cec54cf --- /dev/null +++ b/source/Net/Bazzline/Component/ProcessPipe/ExecutableException.php @@ -0,0 +1,15 @@ + + * @since 2014-11-07 + */ + +namespace Net\Bazzline\Component\ProcessPipe; + +use RuntimeException; + +/** + * Class ExecutableException + * @package De\Leibelt\ProcessPipeline + */ +class ExecutableException extends RuntimeException {} \ No newline at end of file diff --git a/source/Net/Bazzline/Component/ProcessPipe/ExecutableInterface.php b/source/Net/Bazzline/Component/ProcessPipe/ExecutableInterface.php new file mode 100644 index 0000000..fba3231 --- /dev/null +++ b/source/Net/Bazzline/Component/ProcessPipe/ExecutableInterface.php @@ -0,0 +1,21 @@ + + * @since 2014-11-07 + */ + +namespace Net\Bazzline\Component\ProcessPipe; + +/** + * Interface ExecutableInterface + * @package De\Leibelt\ProcessPipeline + */ +interface ExecutableInterface +{ + /** + * @param mixed $input + * @return mixed + * @throws ExecutableException + */ + public function execute($input = null); +} \ No newline at end of file diff --git a/source/Net/Bazzline/Component/ProcessPipe/Pipe.php b/source/Net/Bazzline/Component/ProcessPipe/Pipe.php new file mode 100644 index 0000000..21caea1 --- /dev/null +++ b/source/Net/Bazzline/Component/ProcessPipe/Pipe.php @@ -0,0 +1,59 @@ + + * @since 2014-11-07 + */ + +namespace Net\Bazzline\Component\ProcessPipe; + +/** + * Class ProcessPipe + * @package De\Leibelt\ProcessPipeline + */ +class Pipe implements PipeInterface +{ + /** @var array|ExecutableInterface[] */ + private $processes; + + /** + * @param ExecutableInterface $process + * [@param ExecutableInterface $process] + */ + public function __construct() + { + $this->processes = array(); + + if (func_num_args() > 0) { + call_user_func_array(array($this, 'pipe'), func_get_args()); + } + } + + /** + * @param mixed $input + * @return mixed + * @throws ExecutableException + */ + public function execute($input = null) + { + foreach ($this->processes as $process) { + $input = $process->execute($input); + } + + return $input; + } + + /** + * @param ExecutableInterface $process + * @return $this + */ + public function pipe(ExecutableInterface $process) + { + foreach (func_get_args() as $process) { + if ($process instanceof ExecutableInterface) { + $this->processes[] = $process; + } + } + + return $this; + } +} \ No newline at end of file diff --git a/source/Net/Bazzline/Component/ProcessPipe/PipeInterface.php b/source/Net/Bazzline/Component/ProcessPipe/PipeInterface.php new file mode 100644 index 0000000..ea3e623 --- /dev/null +++ b/source/Net/Bazzline/Component/ProcessPipe/PipeInterface.php @@ -0,0 +1,20 @@ + + * @since 2014-11-07 + */ + +namespace Net\Bazzline\Component\ProcessPipe; + +/** + * Interface PipeInterface + * @package De\Leibelt\ProcessPipeline + */ +interface PipeInterface extends ExecutableInterface +{ + /** + * @param ExecutableInterface $process - or more + * @return $this + */ + public function pipe(ExecutableInterface $process); +} \ No newline at end of file diff --git a/test/Test/Net/Bazzline/Component/ProcessPipe/PipeTest.php b/test/Test/Net/Bazzline/Component/ProcessPipe/PipeTest.php new file mode 100644 index 0000000..bdc6a08 --- /dev/null +++ b/test/Test/Net/Bazzline/Component/ProcessPipe/PipeTest.php @@ -0,0 +1,16 @@ + + * @since 2014-11-10 + */ + +namespace Test\Net\Bazzline\Component\ProcessPipe; + +/** + * Class PipeTest + * @package Test\Net\Bazzline\Component\ProcessPipe + */ +class PipeTest +{ + +} \ No newline at end of file diff --git a/test/bootstrap.php b/test/bootstrap.php new file mode 100644 index 0000000..6c8c4f5 --- /dev/null +++ b/test/bootstrap.php @@ -0,0 +1,3 @@ +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. diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Broker.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Broker.php new file mode 100644 index 0000000..0276e3f --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Broker.php @@ -0,0 +1,542 @@ +cache = 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); + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Broker/Backend.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Broker/Backend.php new file mode 100644 index 0000000..2c36a30 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Broker/Backend.php @@ -0,0 +1,212 @@ +files[$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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Dummy/ReflectionClass.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Dummy/ReflectionClass.php new file mode 100644 index 0000000..d8322a7 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Dummy/ReflectionClass.php @@ -0,0 +1,1091 @@ +name = 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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/BaseException.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/BaseException.php new file mode 100644 index 0000000..aaebfe1 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/BaseException.php @@ -0,0 +1,102 @@ +getDetail(); + + 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"; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/BrokerException.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/BrokerException.php new file mode 100644 index 0000000..eb7e21d --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/BrokerException.php @@ -0,0 +1,66 @@ +broker = $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 ''; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/FileProcessingException.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/FileProcessingException.php new file mode 100644 index 0000000..48da374 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/FileProcessingException.php @@ -0,0 +1,80 @@ +getName()), + 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 ''; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/ParseException.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/ParseException.php new file mode 100644 index 0000000..c9161c6 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/ParseException.php @@ -0,0 +1,264 @@ +sender = $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) + ); + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/RuntimeException.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/RuntimeException.php new file mode 100644 index 0000000..9857779 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/RuntimeException.php @@ -0,0 +1,72 @@ +sender = $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()); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/StreamException.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/StreamException.php new file mode 100644 index 0000000..5aa7519 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Exception/StreamException.php @@ -0,0 +1,96 @@ +stream = $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()); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/IReflection.php b/vendor/andrewsville/php-token-reflection/TokenReflection/IReflection.php new file mode 100644 index 0000000..911dd63 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/IReflection.php @@ -0,0 +1,80 @@ + 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(); +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/IReflectionFunctionBase.php b/vendor/andrewsville/php-token-reflection/TokenReflection/IReflectionFunctionBase.php new file mode 100644 index 0000000..9076590 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/IReflectionFunctionBase.php @@ -0,0 +1,135 @@ +name = 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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionConstant.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionConstant.php new file mode 100644 index 0000000..6fb75bb --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionConstant.php @@ -0,0 +1,403 @@ +name = $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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionElement.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionElement.php new file mode 100644 index 0000000..344d62b --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionElement.php @@ -0,0 +1,53 @@ +reasons[] = $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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionFunction.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionFunction.php new file mode 100644 index 0000000..475914b --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Invalid/ReflectionFunction.php @@ -0,0 +1,490 @@ +name = 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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Php/IReflection.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/IReflection.php new file mode 100644 index 0000000..ce8353e --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/IReflection.php @@ -0,0 +1,36 @@ +broker = $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()); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionConstant.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionConstant.php new file mode 100644 index 0000000..e0de08f --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionConstant.php @@ -0,0 +1,486 @@ +name = $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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionExtension.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionExtension.php new file mode 100644 index 0000000..20bbe7b --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionExtension.php @@ -0,0 +1,282 @@ +broker = $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()]; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionFunction.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionFunction.php new file mode 100644 index 0000000..63e4737 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionFunction.php @@ -0,0 +1,271 @@ +broker = $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()); + } +} \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionMethod.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionMethod.php new file mode 100644 index 0000000..7a0c90f --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionMethod.php @@ -0,0 +1,385 @@ +broker = $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]; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionParameter.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionParameter.php new file mode 100644 index 0000000..35cd7bb --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionParameter.php @@ -0,0 +1,368 @@ +broker = $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]; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionProperty.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionProperty.php new file mode 100644 index 0000000..fc0c192 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Php/ReflectionProperty.php @@ -0,0 +1,348 @@ +broker = $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]; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionAnnotation.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionAnnotation.php new file mode 100644 index 0000000..6cd8def --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionAnnotation.php @@ -0,0 +1,484 @@ +reflection = $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; + } + } + } + } + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionBase.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionBase.php new file mode 100644 index 0000000..c7b15b0 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionBase.php @@ -0,0 +1,273 @@ +broker = $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; + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionClass.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionClass.php new file mode 100644 index 0000000..088ebca --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionClass.php @@ -0,0 +1,1986 @@ +::] => 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionConstant.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionConstant.php new file mode 100644 index 0000000..dbd06ad --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionConstant.php @@ -0,0 +1,392 @@ +getName(); + 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionElement.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionElement.php new file mode 100644 index 0000000..b0fb331 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionElement.php @@ -0,0 +1,352 @@ +count()) { + 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFile.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFile.php new file mode 100644 index 0000000..863b358 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFile.php @@ -0,0 +1,144 @@ +namespaces; + } + + /** + * 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFileNamespace.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFileNamespace.php new file mode 100644 index 0000000..6304256 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFileNamespace.php @@ -0,0 +1,412 @@ +classes; + } + + /** + * 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFunction.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFunction.php new file mode 100644 index 0000000..7ef59af --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFunction.php @@ -0,0 +1,204 @@ +hasAnnotation('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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFunctionBase.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFunctionBase.php new file mode 100644 index 0000000..786ab1e --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionFunctionBase.php @@ -0,0 +1,440 @@ +namespaceName && 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionMethod.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionMethod.php new file mode 100644 index 0000000..eb70542 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionMethod.php @@ -0,0 +1,775 @@ +declaringClassName ? 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionNamespace.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionNamespace.php new file mode 100644 index 0000000..527ca0e --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionNamespace.php @@ -0,0 +1,558 @@ +name = $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); + } +} \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionParameter.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionParameter.php new file mode 100644 index 0000000..d099458 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionParameter.php @@ -0,0 +1,639 @@ +declaringClassName ? 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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionProperty.php b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionProperty.php new file mode 100644 index 0000000..f85a8cb --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/ReflectionProperty.php @@ -0,0 +1,572 @@ +getBroker()->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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Resolver.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Resolver.php new file mode 100644 index 0000000..4d97f18 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Resolver.php @@ -0,0 +1,288 @@ +getNamespaceName(); + } 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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/FileStream.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/FileStream.php new file mode 100644 index 0000000..44e3675 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/FileStream.php @@ -0,0 +1,50 @@ +fileName = 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); + } +} \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/StreamBase.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/StreamBase.php new file mode 100644 index 0000000..85bdcee --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/StreamBase.php @@ -0,0 +1,487 @@ + 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(); + } +} diff --git a/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/StringStream.php b/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/StringStream.php new file mode 100644 index 0000000..16f7c2b --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/TokenReflection/Stream/StringStream.php @@ -0,0 +1,38 @@ +fileName = $fileName; + $this->processSource($source); + } +} \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/build.xml b/vendor/andrewsville/php-token-reflection/build.xml new file mode 100644 index 0000000..933efdd --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/build.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/build/phpcs.xml b/vendor/andrewsville/php-token-reflection/build/phpcs.xml new file mode 100644 index 0000000..0b2edf2 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/build/phpcs.xml @@ -0,0 +1,10 @@ + + + TokenReflection coding standard + + + + + 0 + + \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/build/phpmd.xml b/vendor/andrewsville/php-token-reflection/build/phpmd.xml new file mode 100644 index 0000000..c6ac54d --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/build/phpmd.xml @@ -0,0 +1,15 @@ + + + + TokenReflection PHPMD rule set + + + + + + \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/build/phpunit.xml b/vendor/andrewsville/php-token-reflection/build/phpunit.xml new file mode 100644 index 0000000..740245b --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/build/phpunit.xml @@ -0,0 +1,18 @@ + + + + + ../tests/TokenReflection + + + + + ../TokenReflection + + + + + + + + \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/composer.json b/vendor/andrewsville/php-token-reflection/composer.json new file mode 100644 index 0000000..09ff6ba --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/composer.json @@ -0,0 +1,29 @@ +{ + "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": "./" + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/package.xml b/vendor/andrewsville/php-token-reflection/package.xml new file mode 100644 index 0000000..df6380c --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/package.xml @@ -0,0 +1,121 @@ + + +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 + + + + + \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ConsistencyTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ConsistencyTest.php new file mode 100644 index 0000000..5b881f4 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ConsistencyTest.php @@ -0,0 +1,270 @@ +createBroker(); + $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())); + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/DuplicitiesTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/DuplicitiesTest.php new file mode 100644 index 0000000..9a0390e --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/DuplicitiesTest.php @@ -0,0 +1,170 @@ +getBroker(); + 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()); + } + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ParseErrorTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ParseErrorTest.php new file mode 100644 index 0000000..c72df3c --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ParseErrorTest.php @@ -0,0 +1,199 @@ +getBroker()); + } + + /** + * 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)); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/PharTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/PharTest.php new file mode 100644 index 0000000..c29256b --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/PharTest.php @@ -0,0 +1,263 @@ +markTestSkipped('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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionAnnotationTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionAnnotationTest.php new file mode 100644 index 0000000..420aa16 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionAnnotationTest.php @@ -0,0 +1,50 @@ +getBroker(); + $broker->processString('assertTrue($broker->hasClass('AnnotationInvalidTemplate')); + $class = $broker->getClass('AnnotationInvalidTemplate'); + + $a = new ReflectionAnnotation($class); + $a->setTemplates(array(new \Exception())); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionBrokerTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionBrokerTest.php new file mode 100644 index 0000000..8974baa --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionBrokerTest.php @@ -0,0 +1,292 @@ +getFileTokenReflection('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() + ) + ); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionClassTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionClassTest.php new file mode 100644 index 0000000..c32a411 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionClassTest.php @@ -0,0 +1,1495 @@ +getBroker(); + + 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); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionConstantTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionConstantTest.php new file mode 100644 index 0000000..9bf3be4 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionConstantTest.php @@ -0,0 +1,764 @@ +getConstantTokenReflection('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()); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionExtensionTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionExtensionTest.php new file mode 100644 index 0000000..36020d6 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionExtensionTest.php @@ -0,0 +1,41 @@ +getBroker()); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionFileTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionFileTest.php new file mode 100644 index 0000000..75fa319 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionFileTest.php @@ -0,0 +1,103 @@ +getFilePath('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#'); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionFunctionTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionFunctionTest.php new file mode 100644 index 0000000..98e1ae1 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionFunctionTest.php @@ -0,0 +1,375 @@ +getFunctionReflection('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'); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionMethodTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionMethodTest.php new file mode 100644 index 0000000..88aa619 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionMethodTest.php @@ -0,0 +1,600 @@ +getMethodReflection('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(); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionParameterTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionParameterTest.php new file mode 100644 index 0000000..fc76480 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionParameterTest.php @@ -0,0 +1,289 @@ +getFunctionReflection('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()); + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionPropertyTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionPropertyTest.php new file mode 100644 index 0000000..fb35522 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/ReflectionPropertyTest.php @@ -0,0 +1,436 @@ +getPropertyTokenReflection('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); + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/SourceCodeTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/SourceCodeTest.php new file mode 100644 index 0000000..314ddce --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/SourceCodeTest.php @@ -0,0 +1,56 @@ +createBroker(); + $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()); + } + } + } + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/StreamTest.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/StreamTest.php new file mode 100644 index 0000000..e32916a --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/StreamTest.php @@ -0,0 +1,131 @@ +getFileStream('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)); + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/TokenReflection/Test.php b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/Test.php new file mode 100644 index 0000000..223fe3b --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/TokenReflection/Test.php @@ -0,0 +1,390 @@ +getBroker()->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; + } +} diff --git a/vendor/andrewsville/php-token-reflection/tests/bootstrap.php b/vendor/andrewsville/php-token-reflection/tests/bootstrap.php new file mode 100644 index 0000000..ff8c13f --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/bootstrap.php @@ -0,0 +1,29 @@ +private = $private; + } +} + +class TokenReflection_Test_ClassInstancesChild extends TokenReflection_Test_ClassInstances +{ +} diff --git a/vendor/andrewsville/php-token-reflection/tests/data/class/interface.php b/vendor/andrewsville/php-token-reflection/tests/data/class/interface.php new file mode 100644 index 0000000..ea4cb0c --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/data/class/interface.php @@ -0,0 +1,5 @@ +check = true; + } + +} diff --git a/vendor/andrewsville/php-token-reflection/tests/data/class/no-abstract.php b/vendor/andrewsville/php-token-reflection/tests/data/class/no-abstract.php new file mode 100644 index 0000000..09992dc --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/data/class/no-abstract.php @@ -0,0 +1,5 @@ + 1); + static $array2 = array(1 => 1, 2 => 2); + static $constant = TOKENREFLECTION_FUNCTION_STATIC_VARIABLE_VALUE; +} diff --git a/vendor/andrewsville/php-token-reflection/tests/data/function/user-defined.php b/vendor/andrewsville/php-token-reflection/tests/data/function/user-defined.php new file mode 100644 index 0000000..c906c4c --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/data/function/user-defined.php @@ -0,0 +1,5 @@ + 1); + static $array2 = array(1 => 1, 2 => 2); + static $constants = array( + TokenReflection_Test_MethodStaticVariables::SELF, + TokenReflection_Test_MethodStaticVariablesParent::PARENT + ); + } +} \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/tests/data/method/user-defined.php b/vendor/andrewsville/php-token-reflection/tests/data/method/user-defined.php new file mode 100644 index 0000000..0c0cb42 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/data/method/user-defined.php @@ -0,0 +1,8 @@ + 1); +} \ No newline at end of file diff --git a/vendor/andrewsville/php-token-reflection/tests/data/property/type-boolean.php b/vendor/andrewsville/php-token-reflection/tests/data/property/type-boolean.php new file mode 100644 index 0000000..20f2bf3 --- /dev/null +++ b/vendor/andrewsville/php-token-reflection/tests/data/property/type-boolean.php @@ -0,0 +1,6 @@ +generator = $generator; + $this->cacheTokenStreams = $cacheTokenStreams; + } + + /** + * Destructor. + * + * Deletes all cached token streams. + */ + public function __destruct() + { + foreach ($this->fileCache as $file) { + unlink($file); + } + } + + /** + * 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) + { + if ($this->cacheTokenStreams) { + $this->fileCache[$file->getName()] = $cacheFile = tempnam(sys_get_temp_dir(), 'trc'); + file_put_contents($cacheFile, serialize($tokenStream)); + } + + parent::addFile($tokenStream, $file); + + return $this; + } + + /** + * Returns an array of tokens for a particular file. + * + * @param string $fileName File name + * @return \TokenReflection\Stream + * @throws \RuntimeException If the token stream could not be returned. + */ + public function getFileTokens($fileName) + { + try { + if (!$this->isFileProcessed($fileName)) { + throw new InvalidArgumentException('File was not processed'); + } + + $realName = Broker::getRealPath($fileName); + if (!isset($this->fileCache[$realName])) { + throw new InvalidArgumentException('File is not in the cache'); + } + + $data = @file_get_contents($this->fileCache[$realName]); + if (false === $data) { + throw new RuntimeException('Cached file is not readable'); + } + $file = @unserialize($data); + if (false === $file) { + throw new RuntimeException('Stream could not be loaded from cache'); + } + + return $file; + } catch (\Exception $e) { + throw new RuntimeException(sprintf('Could not return token stream for file %s', $fileName), 0, $e); + } + } + + /** + * Prepares and returns used class lists. + * + * @return array + */ + protected function parseClassLists() + { + $allClasses = array( + self::TOKENIZED_CLASSES => array(), + self::INTERNAL_CLASSES => array(), + self::NONEXISTENT_CLASSES => array() + ); + + $declared = array_flip(array_merge(get_declared_classes(), get_declared_interfaces())); + + foreach ($this->getNamespaces() as $namespace) { + foreach ($namespace->getClasses() as $name => $trClass) { + $class = new ReflectionClass($trClass, $this->generator); + $allClasses[self::TOKENIZED_CLASSES][$name] = $class; + if (!$class->isDocumented()) { + continue; + } + + foreach (array_merge($trClass->getParentClasses(), $trClass->getInterfaces()) as $parentName => $parent) { + if ($parent->isInternal()) { + if (!isset($allClasses[self::INTERNAL_CLASSES][$parentName])) { + $allClasses[self::INTERNAL_CLASSES][$parentName] = $parent; + } + } elseif (!$parent->isTokenized()) { + if (!isset($allClasses[self::NONEXISTENT_CLASSES][$parentName])) { + $allClasses[self::NONEXISTENT_CLASSES][$parentName] = $parent; + } + } + } + + $this->generator->checkMemory(); + } + } + + foreach ($allClasses[self::TOKENIZED_CLASSES] as $class) { + if (!$class->isDocumented()) { + continue; + } + + foreach ($class->getOwnMethods() as $method) { + $allClasses = $this->processFunction($declared, $allClasses, $method); + } + + foreach ($class->getOwnProperties() as $property) { + $annotations = $property->getAnnotations(); + + if (!isset($annotations['var'])) { + continue; + } + + foreach ($annotations['var'] as $doc) { + foreach (explode('|', preg_replace('~\\s.*~', '', $doc)) as $name) { + if ($name = rtrim($name, '[]')) { + $name = Resolver::resolveClassFQN($name, $class->getNamespaceAliases(), $class->getNamespaceName()); + $allClasses = $this->addClass($declared, $allClasses, $name); + } + } + } + } + + $this->generator->checkMemory(); + } + + foreach ($this->getFunctions() as $function) { + $allClasses = $this->processFunction($declared, $allClasses, $function); + } + + array_walk_recursive($allClasses, function(&$reflection, $name, Generator $generator) { + if (!$reflection instanceof ReflectionClass) { + $reflection = new ReflectionClass($reflection, $generator); + } + }, $this->generator); + + return $allClasses; + } + + /** + * Processes a function/method and adds classes from annotations to the overall class array. + * + * @param array $declared Array of declared classes + * @param array $allClasses Array with all classes parsed so far + * @param \ApiGen\ReflectionFunction|\TokenReflection\IReflectionFunctionBase $function Function/method reflection + * @return array + */ + private function processFunction(array $declared, array $allClasses, $function) + { + static $parsedAnnotations = array('param', 'return', 'throws'); + + $annotations = $function->getAnnotations(); + foreach ($parsedAnnotations as $annotation) { + if (!isset($annotations[$annotation])) { + continue; + } + + foreach ($annotations[$annotation] as $doc) { + foreach (explode('|', preg_replace('~\\s.*~', '', $doc)) as $name) { + if ($name) { + $name = Resolver::resolveClassFQN(rtrim($name, '[]'), $function->getNamespaceAliases(), $function->getNamespaceName()); + $allClasses = $this->addClass($declared, $allClasses, $name); + } + } + } + } + + foreach ($function->getParameters() as $param) { + if ($hint = $param->getClassName()) { + $allClasses = $this->addClass($declared, $allClasses, $hint); + } + } + + return $allClasses; + } + + /** + * Adds a class to list of classes. + * + * @param array $declared Array of declared classes + * @param array $allClasses Array with all classes parsed so far + * @param string $name Class name + * @return array + */ + private function addClass(array $declared, array $allClasses, $name) + { + $name = ltrim($name, '\\'); + + if (!isset($declared[$name]) || isset($allClasses[self::TOKENIZED_CLASSES][$name]) + || isset($allClasses[self::INTERNAL_CLASSES][$name]) || isset($allClasses[self::NONEXISTENT_CLASSES][$name]) + ) { + return $allClasses; + } + + $parameterClass = $this->getBroker()->getClass($name); + if ($parameterClass->isInternal()) { + $allClasses[self::INTERNAL_CLASSES][$name] = $parameterClass; + foreach (array_merge($parameterClass->getInterfaces(), $parameterClass->getParentClasses()) as $parentClass) { + if (!isset($allClasses[self::INTERNAL_CLASSES][$parentName = $parentClass->getName()])) { + $allClasses[self::INTERNAL_CLASSES][$parentName] = $parentClass; + } + } + } elseif (!$parameterClass->isTokenized() && !isset($allClasses[self::NONEXISTENT_CLASSES][$name])) { + $allClasses[self::NONEXISTENT_CLASSES][$name] = $parameterClass; + } + + return $allClasses; + } + + /** + * Returns all constants from all namespaces. + * + * @return array + */ + public function getConstants() + { + $generator = $this->generator; + return array_map(function(IReflectionConstant $constant) use ($generator) { + return new ReflectionConstant($constant, $generator); + }, parent::getConstants()); + } + + /** + * Returns all functions from all namespaces. + * + * @return array + */ + public function getFunctions() + { + $generator = $this->generator; + return array_map(function(IReflectionFunction $function) use ($generator) { + return new ReflectionFunction($function, $generator); + }, parent::getFunctions()); + } +} diff --git a/vendor/apigen/apigen/ApiGen/Config.php b/vendor/apigen/apigen/ApiGen/Config.php new file mode 100644 index 0000000..b98a437 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/Config.php @@ -0,0 +1,596 @@ + '', + 'source' => array(), + 'destination' => '', + 'extensions' => array('php'), + 'exclude' => array(), + 'skipDocPath' => array(), + 'skipDocPrefix' => array(), + 'charset' => array('auto'), + 'main' => '', + 'title' => '', + 'baseUrl' => '', + 'googleCseId' => '', + 'googleAnalytics' => '', + 'templateConfig' => '', + 'allowedHtml' => array('b', 'i', 'a', 'ul', 'ol', 'li', 'p', 'br', 'var', 'samp', 'kbd', 'tt'), + 'groups' => 'auto', + 'autocomplete' => array('classes', 'constants', 'functions'), + 'accessLevels' => array('public', 'protected'), + 'internal' => false, + 'php' => true, + 'tree' => true, + 'deprecated' => false, + 'todo' => false, + 'download' => false, + 'sourceCode' => true, + 'report' => '', + 'undocumented' => '', + 'wipeout' => true, + 'quiet' => false, + 'progressbar' => true, + 'colors' => true, + 'updateCheck' => true, + 'debug' => false + ); + + /** + * File or directory path options. + * + * @var array + */ + private static $pathOptions = array( + 'config', + 'source', + 'destination', + 'templateConfig', + 'report' + ); + + /** + * Possible values for options. + * + * @var array + */ + private static $possibleOptionsValues = array( + 'groups' => array('auto', 'namespaces', 'packages', 'none'), + 'autocomplete' => array('classes', 'constants', 'functions', 'methods', 'properties', 'classconstants'), + 'accessLevels' => array('public', 'protected', 'private') + ); + + /** + * Initializes default configuration. + */ + public function __construct() + { + $templateDir = self::isInstalledByPear() ? '@data_dir@' . DIRECTORY_SEPARATOR . 'ApiGen' : realpath(__DIR__ . DIRECTORY_SEPARATOR . '..'); + self::$defaultConfig['templateConfig'] = $templateDir . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'default' . DIRECTORY_SEPARATOR . 'config.neon'; + self::$defaultConfig['colors'] = 'WIN' === substr(PHP_OS, 0, 3) ? false : (function_exists('posix_isatty') && defined('STDOUT') ? posix_isatty(STDOUT) : true); + $this->config = self::$defaultConfig; + } + + /** + * Processes command line options. + * + * @param array $options + * @return \ApiGen\Config + */ + public function processCliOptions(array $options) + { + while ($option = current($options)) { + if (preg_match('~^--([a-z][-a-z]*[a-z])(?:=(.+))?$~', $option, $matches) || preg_match('~^-([a-z])=?(.*)~', $option, $matches)) { + $name = $matches[1]; + + if (!empty($matches[2])) { + $value = $matches[2]; + } else { + $next = next($options); + if (false === $next || '-' === $next{0}) { + prev($options); + $value = ''; + } else { + $value = $next; + } + } + + $this->options[$name][] = $value; + } + + next($options); + } + $this->options = array_map(function($value) { + return 1 === count($value) ? $value[0] : $value; + }, $this->options); + + // Compatibility with ApiGen 1.0 + foreach (array('config', 'source', 'destination') as $option) { + if (isset($this->options[$option{0}]) && !isset($this->options[$option])) { + $this->options[$option] = $this->options[$option{0}]; + } + unset($this->options[$option{0}]); + } + + return $this; + } + + /** + * Prepares configuration. + * + * @return \ApiGen\Config + * @throws \ApiGen\ConfigException If something in configuration is wrong. + */ + public function prepare() + { + // Command line options + $cli = array(); + $translator = array(); + foreach ($this->options as $option => $value) { + $converted = preg_replace_callback('~-([a-z])~', function($matches) { + return strtoupper($matches[1]); + }, $option); + + $cli[$converted] = $value; + $translator[$converted] = $option; + } + + $unknownOptions = array_keys(array_diff_key($cli, self::$defaultConfig)); + if (!empty($unknownOptions)) { + $originalOptions = array_map(function($option) { + return (1 === strlen($option) ? '-' : '--') . $option; + }, array_values(array_diff_key($translator, self::$defaultConfig))); + + $message = count($unknownOptions) > 1 + ? sprintf('Unknown command line options "%s"', implode('", "', $originalOptions)) + : sprintf('Unknown command line option "%s"', $originalOptions[0]); + throw new ConfigException($message); + } + + // Config file + $neon = array(); + if (empty($this->options) && $this->defaultConfigExists()) { + $this->options['config'] = $this->getDefaultConfigPath(); + } + if (isset($this->options['config']) && is_file($this->options['config'])) { + $neon = Neon::decode(file_get_contents($this->options['config'])); + foreach (self::$pathOptions as $option) { + if (!empty($neon[$option])) { + if (is_array($neon[$option])) { + foreach ($neon[$option] as $key => $value) { + $neon[$option][$key] = $this->getAbsolutePath($value); + } + } else { + $neon[$option] = $this->getAbsolutePath($neon[$option]); + } + } + } + + $unknownOptions = array_keys(array_diff_key($neon, self::$defaultConfig)); + if (!empty($unknownOptions)) { + $message = count($unknownOptions) > 1 + ? sprintf('Unknown config file options "%s"', implode('", "', $unknownOptions)) + : sprintf('Unknown config file option "%s"', $unknownOptions[0]); + throw new ConfigException($message); + } + } + + // Merge options + $this->config = array_merge(self::$defaultConfig, $neon, $cli); + + // Compatibility with old option name "undocumented" + if (!isset($this->config['report']) && isset($this->config['undocumented'])) { + $this->config['report'] = $this->config['undocumented']; + unset($this->config['undocumented']); + } + + foreach (self::$defaultConfig as $option => $valueDefinition) { + if (is_array($this->config[$option]) && !is_array($valueDefinition)) { + throw new ConfigException(sprintf('Option "%s" must be set only once', $option)); + } + + if (is_bool($this->config[$option]) && !is_bool($valueDefinition)) { + throw new ConfigException(sprintf('Option "%s" expects value', $option)); + } + + if (is_bool($valueDefinition) && !is_bool($this->config[$option])) { + // Boolean option + $value = strtolower($this->config[$option]); + if ('on' === $value || 'yes' === $value || 'true' === $value || '' === $value) { + $value = true; + } elseif ('off' === $value || 'no' === $value || 'false' === $value) { + $value = false; + } + $this->config[$option] = (bool) $value; + } elseif (is_array($valueDefinition)) { + // Array option + $this->config[$option] = array_unique((array) $this->config[$option]); + foreach ($this->config[$option] as $key => $value) { + $value = explode(',', $value); + while (count($value) > 1) { + array_push($this->config[$option], array_shift($value)); + } + $this->config[$option][$key] = array_shift($value); + } + $this->config[$option] = array_filter($this->config[$option]); + } + + // Check posssible values + if (!empty(self::$possibleOptionsValues[$option])) { + $values = self::$possibleOptionsValues[$option]; + + if (is_array($valueDefinition)) { + $this->config[$option] = array_filter($this->config[$option], function($value) use ($values) { + return in_array($value, $values); + }); + } elseif (!in_array($this->config[$option], $values)) { + $this->config[$option] = ''; + } + } + } + + // Unify character sets + $this->config['charset'] = array_map('strtoupper', $this->config['charset']); + + // Process options that specify a filesystem path + foreach (self::$pathOptions as $option) { + if (is_array($this->config[$option])) { + array_walk($this->config[$option], function(&$value) { + if (file_exists($value)) { + $value = realpath($value); + } + }); + usort($this->config[$option], 'strcasecmp'); + } else { + if (file_exists($this->config[$option])) { + $this->config[$option] = realpath($this->config[$option]); + } + } + } + + // Unify directory separators + foreach (array('exclude', 'skipDocPath') as $option) { + $this->config[$option] = array_map(function($mask) { + return str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $mask); + }, $this->config[$option]); + usort($this->config[$option], 'strcasecmp'); + } + + // Unify prefixes + $this->config['skipDocPrefix'] = array_map(function($prefix) { + return ltrim($prefix, '\\'); + }, $this->config['skipDocPrefix']); + usort($this->config['skipDocPrefix'], 'strcasecmp'); + + // Base url without slash at the end + $this->config['baseUrl'] = rtrim($this->config['baseUrl'], '/'); + + // No progressbar in quiet mode + if ($this->config['quiet']) { + $this->config['progressbar'] = false; + } + + // Check + $this->check(); + + // Default template config + $this->config['template'] = array( + 'require' => array(), + 'resources' => array(), + 'templates' => array( + 'common' => array(), + 'optional' => array() + ) + ); + + // Merge template config + $this->config = array_merge_recursive($this->config, array('template' => Neon::decode(file_get_contents($fileName = $this->config['templateConfig'])))); + $this->config['template']['config'] = realpath($fileName); + + // Check template + $this->checkTemplate(); + + return $this; + } + + /** + * Checks configuration. + * + * @return \ApiGen\Config + * @throws \ApiGen\ConfigException If something in configuration is wrong. + */ + private function check() + { + if (!empty($this->config['config']) && !is_file($this->config['config'])) { + throw new ConfigException(sprintf('Config file "%s" doesn\'t exist', $this->config['config'])); + } + + if (empty($this->config['source'])) { + throw new ConfigException('Source is not set'); + } + foreach ($this->config['source'] as $source) { + if (!file_exists($source)) { + throw new ConfigException(sprintf('Source "%s" doesn\'t exist', $source)); + } + } + + if (empty($this->config['destination'])) { + throw new ConfigException('Destination is not set'); + } + + foreach ($this->config['extensions'] as $extension) { + if (!preg_match('~^[a-z\\d]+$~i', $extension)) { + throw new ConfigException(sprintf('Invalid file extension "%s"', $extension)); + } + } + + if (!is_file($this->config['templateConfig'])) { + throw new ConfigException(sprintf('Template config "%s" doesn\'t exist', $this->config['templateConfig'])); + } + + if (!empty($this->config['googleCseId']) && !preg_match('~^\d{21}:[-a-z0-9_]{11}$~', $this->config['googleCseId'])) { + throw new ConfigException(sprintf('Invalid Google Custom Search ID "%s"', $this->config['googleCseId'])); + } + + if (!empty($this->config['googleAnalytics']) && !preg_match('~^UA\\-\\d+\\-\\d+$~', $this->config['googleAnalytics'])) { + throw new ConfigException(sprintf('Invalid Google Analytics tracking code "%s"', $this->config['googleAnalytics'])); + } + + if (empty($this->config['groups'])) { + throw new ConfigException('No supported groups value given'); + } + + if (empty($this->config['autocomplete'])) { + throw new ConfigException('No supported autocomplete value given'); + } + + if (empty($this->config['accessLevels'])) { + throw new ConfigException('No supported access level given'); + } + + return $this; + } + + /** + * Checks template configuration. + * + * @return \ApiGen\Config + * @throws \ApiGen\ConfigException If something in template configuration is wrong. + */ + private function checkTemplate() + { + $require = $this->config['template']['require']; + if (isset($require['min']) && !preg_match('~^\\d+(?:\\.\\d+){0,2}$~', $require['min'])) { + throw new ConfigException(sprintf('Invalid minimal version definition "%s"', $require['min'])); + } + if (isset($require['max']) && !preg_match('~^\\d+(?:\\.\\d+){0,2}$~', $require['max'])) { + throw new ConfigException(sprintf('Invalid maximal version definition "%s"', $require['max'])); + } + + $isMinOk = function($min) { + $min .= str_repeat('.0', 2 - substr_count($min, '.')); + return version_compare($min, Generator::VERSION, '<='); + }; + $isMaxOk = function($max) { + $max .= str_repeat('.0', 2 - substr_count($max, '.')); + return version_compare($max, Generator::VERSION, '>='); + }; + + if (isset($require['min'], $require['max']) && (!$isMinOk($require['min']) || !$isMaxOk($require['max']))) { + throw new ConfigException(sprintf('The template requires version from "%s" to "%s", you are using version "%s"', $require['min'], $require['max'], Generator::VERSION)); + } elseif (isset($require['min']) && !$isMinOk($require['min'])) { + throw new ConfigException(sprintf('The template requires version "%s" or newer, you are using version "%s"', $require['min'], Generator::VERSION)); + } elseif (isset($require['max']) && !$isMaxOk($require['max'])) { + throw new ConfigException(sprintf('The template requires version "%s" or older, you are using version "%s"', $require['max'], Generator::VERSION)); + } + + foreach (array('main', 'optional') as $section) { + foreach ($this->config['template']['templates'][$section] as $type => $config) { + if (!isset($config['filename'])) { + throw new ConfigException(sprintf('Filename for "%s" is not defined', $type)); + } + if (!isset($config['template'])) { + throw new ConfigException(sprintf('Template for "%s" is not defined', $type)); + } + if (!is_file(dirname($this->config['templateConfig']) . DIRECTORY_SEPARATOR . $config['template'])) { + throw new ConfigException(sprintf('Template for "%s" doesn\'t exist', $type)); + } + } + } + + return $this; + } + + /** + * Returns default configuration file path. + * + * @return string + */ + private function getDefaultConfigPath() + { + return getcwd() . DIRECTORY_SEPARATOR . 'apigen.neon'; + } + + /** + * Checks if default configuration file exists. + * + * @return boolean + */ + private function defaultConfigExists() + { + return is_file($this->getDefaultConfigPath()); + } + + /** + * Returns absolute path. + * + * @param string $path Path + * @return string + */ + private function getAbsolutePath($path) + { + if (preg_match('~/|[a-z]:~Ai', $path)) { + return $path; + } + + return dirname($this->options['config']) . DIRECTORY_SEPARATOR . $path; + } + + /** + * Checks if a configuration option exists. + * + * @param string $name Option name + * @return boolean + */ + public function __isset($name) + { + return isset($this->config[$name]); + } + + /** + * Returns a configuration option value. + * + * @param string $name Option name + * @return mixed + */ + public function __get($name) + { + return isset($this->config[$name]) ? $this->config[$name] : null; + } + + /** + * If the user requests help. + * + * @return boolean + */ + public function isHelpRequested() + { + if (empty($this->options) && !$this->defaultConfigExists()) { + return true; + } + + if (isset($this->options['h']) || isset($this->options['help'])) { + return true; + } + + return false; + } + + /** + * Returns help. + * + * @return string + */ + public function getHelp() + { + return <<<"HELP" +Usage: + apigen @option@--config@c <@value@path@c> [options] + apigen @option@--source@c <@value@dir@c|@value@file@c> @option@--destination@c <@value@dir@c> [options] + +Options: + @option@--config@c|@option@-c@c <@value@file@c> Config file + @option@--source@c|@option@-s@c <@value@dir@c|@value@file@c> Source file or directory to parse (can be used multiple times) + @option@--destination@c|@option@-d@c <@value@dir@c> Directory where to save the generated documentation + @option@--extensions@c <@value@list@c> List of allowed file extensions, default "@value@php@c" + @option@--exclude@c <@value@mask@c> Mask (case sensitive) to exclude file or directory from processing (can be used multiple times) + @option@--skip-doc-path@c <@value@mask@c> Don't generate documentation for elements from file or directory with this (case sensitive) mask (can be used multiple times) + @option@--skip-doc-prefix@c <@value@value@c> Don't generate documentation for elements with this (case sensitive) name prefix (can be used multiple times) + @option@--charset@c <@value@list@c> Character set of source files, default "@value@auto@c" + @option@--main@c <@value@value@c> Main project name prefix + @option@--title@c <@value@value@c> Title of generated documentation + @option@--base-url@c <@value@value@c> Documentation base URL + @option@--google-cse-id@c <@value@value@c> Google Custom Search ID + @option@--google-analytics@c <@value@value@c> Google Analytics tracking code + @option@--template-config@c <@value@file@c> Template config file, default "@value@{$this->config['templateConfig']}@c" + @option@--allowed-html@c <@value@list@c> List of allowed HTML tags in documentation, default "@value@b,i,a,ul,ol,li,p,br,var,samp,kbd,tt@c" + @option@--groups@c <@value@value@c> How should elements be grouped in the menu. Possible options are "auto", "namespaces", "packages" and "none". Default value is "@value@auto@c" (namespaces if available, packages otherwise) + @option@--autocomplete@c <@value@list@c> Element types for search input autocomplete. Default value is "@value@classes,constants,functions@c" + @option@--access-levels@c <@value@list@c> Generate documentation for methods and properties with given access level, default "@value@public,protected@c" + @option@--internal@c <@value@yes@c|@value@no@c> Generate documentation for elements marked as internal and display internal documentation parts, default "@value@no@c" + @option@--php@c <@value@yes@c|@value@no@c> Generate documentation for PHP internal classes, default "@value@yes@c" + @option@--tree@c <@value@yes@c|@value@no@c> Generate tree view of classes, interfaces, traits and exceptions, default "@value@yes@c" + @option@--deprecated@c <@value@yes@c|@value@no@c> Generate documentation for deprecated elements, default "@value@no@c" + @option@--todo@c <@value@yes@c|@value@no@c> Generate documentation of tasks, default "@value@no@c" + @option@--source-code@c <@value@yes@c|@value@no@c> Generate highlighted source code files, default "@value@yes@c" + @option@--download@c <@value@yes@c|@value@no@c> Add a link to download documentation as a ZIP archive, default "@value@no@c" + @option@--report@c <@value@file@c> Save a checkstyle report of poorly documented elements into a file + @option@--wipeout@c <@value@yes@c|@value@no@c> Wipe out the destination directory first, default "@value@yes@c" + @option@--quiet@c <@value@yes@c|@value@no@c> Don't display scaning and generating messages, default "@value@no@c" + @option@--progressbar@c <@value@yes@c|@value@no@c> Display progressbars, default "@value@yes@c" + @option@--colors@c <@value@yes@c|@value@no@c> Use colors, default "@value@no@c" on Windows, "@value@yes@c" on other systems + @option@--update-check@c <@value@yes@c|@value@no@c> Check for update, default "@value@yes@c" + @option@--debug@c <@value@yes@c|@value@no@c> Display additional information in case of an error, default "@value@no@c" + @option@--help@c|@option@-h@c Display this help + +Only source and destination directories are required - either set explicitly or using a config file. Configuration parameters passed via command line have precedence over parameters from a config file. + +Boolean options (those with possible values @value@yes@c|@value@no@c) do not have to have their values defined explicitly. Using @option@--debug@c and @option@--debug@c=@value@yes@c is exactly the same. + +Some options can have multiple values. You can do so either by using them multiple times or by separating values by a comma. That means that writing @option@--source@c=@value@file1.php@c @option@--source@c=@value@file2.php@c or @option@--source@c=@value@file1.php,file2.php@c is exactly the same. + +Files or directories specified by @option@--exclude@c will not be processed at all. +Elements from files within @option@--skip-doc-path@c or with @option@--skip-doc-prefix@c will be parsed but will not have their documentation generated. However if classes have any child classes, the full class tree will be generated and their inherited methods, properties and constants will be displayed (but will not be clickable). + +HELP; + } + + /** + * Checks if ApiGen is installed by PEAR. + * + * @return boolean + */ + public static function isInstalledByPear() + { + return false === strpos('@data_dir@', '@data_dir'); + } + + /** + * Checks if ApiGen is installed from downloaded archive. + * + * @return boolean + */ + public static function isInstalledByDownload() + { + return !self::isInstalledByPear(); + } +} diff --git a/vendor/apigen/apigen/ApiGen/ConfigException.php b/vendor/apigen/apigen/ApiGen/ConfigException.php new file mode 100644 index 0000000..0770911 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ConfigException.php @@ -0,0 +1,25 @@ + '[%s] %\' 6.2f%% %\' 3dMB', + 'width' => 80, + 'bar' => 64, + 'current' => 0, + 'maximum' => 1 + ); + + /** + * Sets configuration. + * + * @param array $config + */ + public function __construct(Config $config) + { + $this->config = $config; + $this->parsedClasses = new \ArrayObject(); + $this->parsedConstants = new \ArrayObject(); + $this->parsedFunctions = new \ArrayObject(); + } + + /** + * Scans and parses PHP files. + * + * @return array + * @throws \RuntimeException If no PHP files have been found. + */ + public function parse() + { + $files = array(); + + $flags = \RecursiveDirectoryIterator::CURRENT_AS_FILEINFO | \RecursiveDirectoryIterator::SKIP_DOTS; + if (defined('\\RecursiveDirectoryIterator::FOLLOW_SYMLINKS')) { + // Available from PHP 5.3.1 + $flags |= \RecursiveDirectoryIterator::FOLLOW_SYMLINKS; + } + + foreach ($this->config->source as $source) { + $entries = array(); + if (is_dir($source)) { + foreach (new \RecursiveIteratorIterator(new SourceFilesFilterIterator(new \RecursiveDirectoryIterator($source, $flags), $this->config->exclude)) as $entry) { + if (!$entry->isFile()) { + continue; + } + $entries[] = $entry; + } + } elseif ($this->isPhar($source)) { + if (!extension_loaded('phar')) { + throw new RuntimeException('Phar extension is not loaded'); + } + foreach (new \RecursiveIteratorIterator(new \Phar($source, $flags)) as $entry) { + if (!$entry->isFile()) { + continue; + } + $entries[] = $entry; + } + } else { + $entries[] = new \SplFileInfo($source); + } + + $regexp = '~\\.' . implode('|', $this->config->extensions) . '$~i'; + foreach ($entries as $entry) { + if (!preg_match($regexp, $entry->getFilename())) { + continue; + } + + $pathName = $this->normalizePath($entry->getPathName()); + $files[$pathName] = $entry->getSize(); + if (false !== $entry->getRealPath() && $pathName !== $entry->getRealPath()) { + $this->symlinks[$entry->getRealPath()] = $pathName; + } + } + } + + if (empty($files)) { + throw new RuntimeException('No PHP files found'); + } + + if ($this->config->progressbar) { + $this->prepareProgressBar(array_sum($files)); + } + + $broker = new Broker(new Backend($this, !empty($this->config->report)), Broker::OPTION_DEFAULT & ~(Broker::OPTION_PARSE_FUNCTION_BODY | Broker::OPTION_SAVE_TOKEN_STREAM)); + + $errors = array(); + + foreach ($files as $fileName => $size) { + $content = file_get_contents($fileName); + $charset = $this->detectCharset($content); + $this->charsets[$fileName] = $charset; + $content = $this->toUtf($content, $charset); + + try { + $broker->processString($content, $fileName); + } catch (\Exception $e) { + $errors[] = $e; + } + + $this->incrementProgressBar($size); + $this->checkMemory(); + } + + // Classes + $this->parsedClasses->exchangeArray($broker->getClasses(Backend::TOKENIZED_CLASSES | Backend::INTERNAL_CLASSES | Backend::NONEXISTENT_CLASSES)); + $this->parsedClasses->uksort('strcasecmp'); + + // Constants + $this->parsedConstants->exchangeArray($broker->getConstants()); + $this->parsedConstants->uksort('strcasecmp'); + + // Functions + $this->parsedFunctions->exchangeArray($broker->getFunctions()); + $this->parsedFunctions->uksort('strcasecmp'); + + $documentedCounter = function($count, $element) { + return $count += (int) $element->isDocumented(); + }; + + return (object) array( + 'classes' => count($broker->getClasses(Backend::TOKENIZED_CLASSES)), + 'constants' => count($this->parsedConstants), + 'functions' => count($this->parsedFunctions), + 'internalClasses' => count($broker->getClasses(Backend::INTERNAL_CLASSES)), + 'documentedClasses' => array_reduce($broker->getClasses(Backend::TOKENIZED_CLASSES), $documentedCounter), + 'documentedConstants' => array_reduce($this->parsedConstants->getArrayCopy(), $documentedCounter), + 'documentedFunctions' => array_reduce($this->parsedFunctions->getArrayCopy(), $documentedCounter), + 'documentedInternalClasses' => array_reduce($broker->getClasses(Backend::INTERNAL_CLASSES), $documentedCounter), + 'errors' => $errors + ); + } + + /** + * Returns configuration. + * + * @return mixed + */ + public function getConfig() + { + return $this->config; + } + + /** + * Returns parsed class list. + * + * @return \ArrayObject + */ + public function getParsedClasses() + { + return $this->parsedClasses; + } + + /** + * Returns parsed constant list. + * + * @return \ArrayObject + */ + public function getParsedConstants() + { + return $this->parsedConstants; + } + + /** + * Returns parsed function list. + * + * @return \ArrayObject + */ + public function getParsedFunctions() + { + return $this->parsedFunctions; + } + + /** + * Wipes out the destination directory. + * + * @return boolean + */ + public function wipeOutDestination() + { + foreach ($this->getGeneratedFiles() as $path) { + if (is_file($path) && !@unlink($path)) { + return false; + } + } + + $archive = $this->getArchivePath(); + if (is_file($archive) && !@unlink($archive)) { + return false; + } + + return true; + } + + /** + * Generates API documentation. + * + * @throws \RuntimeException If destination directory is not writable. + */ + public function generate() + { + @mkdir($this->config->destination, 0755, true); + if (!is_dir($this->config->destination) || !is_writable($this->config->destination)) { + throw new RuntimeException(sprintf('Directory "%s" isn\'t writable', $this->config->destination)); + } + + // Copy resources + foreach ($this->config->template['resources'] as $resourceSource => $resourceDestination) { + // File + $resourcePath = $this->getTemplateDir() . DIRECTORY_SEPARATOR . $resourceSource; + if (is_file($resourcePath)) { + copy($resourcePath, $this->forceDir($this->config->destination . DIRECTORY_SEPARATOR . $resourceDestination)); + continue; + } + + // Dir + $iterator = Nette\Utils\Finder::findFiles('*')->from($resourcePath)->getIterator(); + foreach ($iterator as $item) { + copy($item->getPathName(), $this->forceDir($this->config->destination . DIRECTORY_SEPARATOR . $resourceDestination . DIRECTORY_SEPARATOR . $iterator->getSubPathName())); + } + } + + // Categorize by packages and namespaces + $this->categorize(); + + // Prepare progressbar + if ($this->config->progressbar) { + $max = count($this->packages) + + count($this->namespaces) + + count($this->classes) + + count($this->interfaces) + + count($this->traits) + + count($this->exceptions) + + count($this->constants) + + count($this->functions) + + count($this->config->template['templates']['common']) + + (int) !empty($this->config->report) + + (int) $this->config->tree + + (int) $this->config->deprecated + + (int) $this->config->todo + + (int) $this->config->download + + (int) $this->isSitemapEnabled() + + (int) $this->isOpensearchEnabled() + + (int) $this->isRobotsEnabled(); + + if ($this->config->sourceCode) { + $tokenizedFilter = function(ReflectionClass $class) { + return $class->isTokenized(); + }; + $max += count(array_filter($this->classes, $tokenizedFilter)) + + count(array_filter($this->interfaces, $tokenizedFilter)) + + count(array_filter($this->traits, $tokenizedFilter)) + + count(array_filter($this->exceptions, $tokenizedFilter)) + + count($this->constants) + + count($this->functions); + unset($tokenizedFilter); + } + + $this->prepareProgressBar($max); + } + + // Prepare template + $tmp = $this->config->destination . DIRECTORY_SEPARATOR . 'tmp'; + $this->deleteDir($tmp); + @mkdir($tmp, 0755, true); + $template = new Template($this); + $template->setCacheStorage(new Nette\Caching\Storages\PhpFileStorage($tmp)); + $template->generator = self::NAME; + $template->version = self::VERSION; + $template->config = $this->config; + $template->basePath = dirname($this->config->templateConfig); + + $this->registerCustomTemplateMacros($template); + + // Common files + $this->generateCommon($template); + + // Optional files + $this->generateOptional($template); + + // List of poorly documented elements + if (!empty($this->config->report)) { + $this->generateReport(); + } + + // List of deprecated elements + if ($this->config->deprecated) { + $this->generateDeprecated($template); + } + + // List of tasks + if ($this->config->todo) { + $this->generateTodo($template); + } + + // Classes/interfaces/traits/exceptions tree + if ($this->config->tree) { + $this->generateTree($template); + } + + // Generate packages summary + $this->generatePackages($template); + + // Generate namespaces summary + $this->generateNamespaces($template); + + // Generate classes, interfaces, traits, exceptions, constants and functions files + $this->generateElements($template); + + // Generate ZIP archive + if ($this->config->download) { + $this->generateArchive(); + } + + // Delete temporary directory + $this->deleteDir($tmp); + } + + /** + * Loads template-specific macro and helper libraries. + * + * @param \ApiGen\Template $template Template instance + */ + private function registerCustomTemplateMacros(Template $template) + { + $latte = new Nette\Latte\Engine(); + + if (!empty($this->config->template['options']['extensions'])) { + $this->output("Loading custom template macro and helper libraries\n"); + $broker = new Broker(new Broker\Backend\Memory(), 0); + + $baseDir = dirname($this->config->template['config']); + foreach ((array) $this->config->template['options']['extensions'] as $fileName) { + $pathName = $baseDir . DIRECTORY_SEPARATOR . $fileName; + if (is_file($pathName)) { + try { + $reflectionFile = $broker->processFile($pathName, true); + + foreach ($reflectionFile->getNamespaces() as $namespace) { + foreach ($namespace->getClasses() as $class) { + if ($class->isSubclassOf('ApiGen\\MacroSet')) { + // Macro set + + include $pathName; + call_user_func(array($class->getName(), 'install'), $latte->compiler); + + $this->output(sprintf(" %s (macro set)\n", $class->getName())); + } elseif ($class->implementsInterface('ApiGen\\IHelperSet')) { + // Helpers set + + include $pathName; + $className = $class->getName(); + $template->registerHelperLoader(callback(new $className($template), 'loader')); + + $this->output(sprintf(" %s (helper set)\n", $class->getName())); + } + } + } + } catch (\Exception $e) { + throw new \Exception(sprintf('Could not load macros and helpers from file "%s"', $pathName), 0, $e); + } + } else { + throw new \Exception(sprintf('Helper file "%s" does not exist.', $pathName)); + } + } + } + + $template->registerFilter($latte); + } + + /** + * Categorizes by packages and namespaces. + * + * @return \ApiGen\Generator + */ + private function categorize() + { + foreach (array('classes', 'constants', 'functions') as $type) { + foreach ($this->{'parsed' . ucfirst($type)} as $elementName => $element) { + if (!$element->isDocumented()) { + continue; + } + + $packageName = $element->getPseudoPackageName(); + $namespaceName = $element->getPseudoNamespaceName(); + + if ($element instanceof ReflectionConstant) { + $this->constants[$elementName] = $element; + $this->packages[$packageName]['constants'][$elementName] = $element; + $this->namespaces[$namespaceName]['constants'][$element->getShortName()] = $element; + } elseif ($element instanceof ReflectionFunction) { + $this->functions[$elementName] = $element; + $this->packages[$packageName]['functions'][$elementName] = $element; + $this->namespaces[$namespaceName]['functions'][$element->getShortName()] = $element; + } elseif ($element->isInterface()) { + $this->interfaces[$elementName] = $element; + $this->packages[$packageName]['interfaces'][$elementName] = $element; + $this->namespaces[$namespaceName]['interfaces'][$element->getShortName()] = $element; + } elseif ($element->isTrait()) { + $this->traits[$elementName] = $element; + $this->packages[$packageName]['traits'][$elementName] = $element; + $this->namespaces[$namespaceName]['traits'][$element->getShortName()] = $element; + } elseif ($element->isException()) { + $this->exceptions[$elementName] = $element; + $this->packages[$packageName]['exceptions'][$elementName] = $element; + $this->namespaces[$namespaceName]['exceptions'][$element->getShortName()] = $element; + } else { + $this->classes[$elementName] = $element; + $this->packages[$packageName]['classes'][$elementName] = $element; + $this->namespaces[$namespaceName]['classes'][$element->getShortName()] = $element; + } + } + } + + // Select only packages or namespaces + $userPackagesCount = count(array_diff(array_keys($this->packages), array('PHP', 'None'))); + $userNamespacesCount = count(array_diff(array_keys($this->namespaces), array('PHP', 'None'))); + + $namespacesEnabled = ('auto' === $this->config->groups && ($userNamespacesCount > 0 || 0 === $userPackagesCount)) || 'namespaces' === $this->config->groups; + $packagesEnabled = ('auto' === $this->config->groups && !$namespacesEnabled) || 'packages' === $this->config->groups; + + if ($namespacesEnabled) { + $this->packages = array(); + $this->namespaces = $this->sortGroups($this->namespaces); + } elseif ($packagesEnabled) { + $this->namespaces = array(); + $this->packages = $this->sortGroups($this->packages); + } else { + $this->namespaces = array(); + $this->packages = array(); + } + + return $this; + } + + /** + * Sorts and filters groups. + * + * @param array $groups + * @return array + */ + private function sortGroups(array $groups) + { + // Don't generate only 'None' groups + if (1 === count($groups) && isset($groups['None'])) { + return array(); + } + + $emptyList = array('classes' => array(), 'interfaces' => array(), 'traits' => array(), 'exceptions' => array(), 'constants' => array(), 'functions' => array()); + + $groupNames = array_keys($groups); + $lowerGroupNames = array_flip(array_map(function($y) { + return strtolower($y); + }, $groupNames)); + + foreach ($groupNames as $groupName) { + // Add missing parent groups + $parent = ''; + foreach (explode('\\', $groupName) as $part) { + $parent = ltrim($parent . '\\' . $part, '\\'); + if (!isset($lowerGroupNames[strtolower($parent)])) { + $groups[$parent] = $emptyList; + } + } + + // Add missing element types + foreach ($this->getElementTypes() as $type) { + if (!isset($groups[$groupName][$type])) { + $groups[$groupName][$type] = array(); + } + } + } + + $main = $this->config->main; + uksort($groups, function($one, $two) use ($main) { + // \ as separator has to be first + $one = str_replace('\\', ' ', $one); + $two = str_replace('\\', ' ', $two); + + if ($main) { + if (0 === strpos($one, $main) && 0 !== strpos($two, $main)) { + return -1; + } elseif (0 !== strpos($one, $main) && 0 === strpos($two, $main)) { + return 1; + } + } + + return strcasecmp($one, $two); + }); + + return $groups; + } + + /** + * Generates common files. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + */ + private function generateCommon(Template $template) + { + $template->namespace = null; + $template->namespaces = array_keys($this->namespaces); + $template->package = null; + $template->packages = array_keys($this->packages); + $template->class = null; + $template->classes = array_filter($this->classes, $this->getMainFilter()); + $template->interfaces = array_filter($this->interfaces, $this->getMainFilter()); + $template->traits = array_filter($this->traits, $this->getMainFilter()); + $template->exceptions = array_filter($this->exceptions, $this->getMainFilter()); + $template->constant = null; + $template->constants = array_filter($this->constants, $this->getMainFilter()); + $template->function = null; + $template->functions = array_filter($this->functions, $this->getMainFilter()); + $template->archive = basename($this->getArchivePath()); + + // Elements for autocomplete + $elements = array(); + $autocomplete = array_flip($this->config->autocomplete); + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $element) { + if ($element instanceof ReflectionClass) { + if (isset($autocomplete['classes'])) { + $elements[] = array('c', $element->getPrettyName()); + } + if (isset($autocomplete['methods'])) { + foreach ($element->getOwnMethods() as $method) { + $elements[] = array('m', $method->getPrettyName()); + } + foreach ($element->getOwnMagicMethods() as $method) { + $elements[] = array('mm', $method->getPrettyName()); + } + } + if (isset($autocomplete['properties'])) { + foreach ($element->getOwnProperties() as $property) { + $elements[] = array('p', $property->getPrettyName()); + } + foreach ($element->getOwnMagicProperties() as $property) { + $elements[] = array('mp', $property->getPrettyName()); + } + } + if (isset($autocomplete['classconstants'])) { + foreach ($element->getOwnConstants() as $constant) { + $elements[] = array('cc', $constant->getPrettyName()); + } + } + } elseif ($element instanceof ReflectionConstant && isset($autocomplete['constants'])) { + $elements[] = array('co', $element->getPrettyName()); + } elseif ($element instanceof ReflectionFunction && isset($autocomplete['functions'])) { + $elements[] = array('f', $element->getPrettyName()); + } + } + } + usort($elements, function($one, $two) { + return strcasecmp($one[1], $two[1]); + }); + $template->elements = $elements; + + foreach ($this->config->template['templates']['common'] as $source => $destination) { + $template + ->setFile($this->getTemplateDir() . DIRECTORY_SEPARATOR . $source) + ->save($this->forceDir($this->config->destination . DIRECTORY_SEPARATOR . $destination)); + + $this->incrementProgressBar(); + } + + unset($template->elements); + + $this->checkMemory(); + + return $this; + } + + /** + * Generates optional files. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + */ + private function generateOptional(Template $template) + { + if ($this->isSitemapEnabled()) { + $template + ->setFile($this->getTemplatePath('sitemap', 'optional')) + ->save($this->forceDir($this->getTemplateFileName('sitemap', 'optional'))); + $this->incrementProgressBar(); + } + if ($this->isOpensearchEnabled()) { + $template + ->setFile($this->getTemplatePath('opensearch', 'optional')) + ->save($this->forceDir($this->getTemplateFileName('opensearch', 'optional'))); + $this->incrementProgressBar(); + } + if ($this->isRobotsEnabled()) { + $template + ->setFile($this->getTemplatePath('robots', 'optional')) + ->save($this->forceDir($this->getTemplateFileName('robots', 'optional'))); + $this->incrementProgressBar(); + } + + $this->checkMemory(); + + return $this; + } + + /** + * Generates list of poorly documented elements. + * + * @return \ApiGen\Generator + * @throws \RuntimeException If file isn't writable. + */ + private function generateReport() + { + // Function for element labels + $that = $this; + $labeler = function($element) use ($that) { + if ($element instanceof ReflectionClass) { + if ($element->isInterface()) { + $label = 'interface'; + } elseif ($element->isTrait()) { + $label = 'trait'; + } elseif ($element->isException()) { + $label = 'exception'; + } else { + $label = 'class'; + } + } elseif ($element instanceof ReflectionMethod) { + $label = 'method'; + } elseif ($element instanceof ReflectionFunction) { + $label = 'function'; + } elseif ($element instanceof ReflectionConstant) { + $label = 'constant'; + } elseif ($element instanceof ReflectionProperty) { + $label = 'property'; + } elseif ($element instanceof ReflectionParameter) { + $label = 'parameter'; + } + return sprintf('%s %s', $label, $element->getPrettyName()); + }; + + $list = array(); + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $parentElement) { + $fileName = $this->unPharPath($parentElement->getFileName()); + + if (!$parentElement->isValid()) { + $list[$fileName][] = array('error', 0, sprintf('Duplicate %s', $labeler($parentElement))); + continue; + } + + // Skip elements not from the main project + if (!$parentElement->isMain()) { + continue; + } + + // Internal elements don't have documentation + if ($parentElement->isInternal()) { + continue; + } + + $elements = array($parentElement); + if ($parentElement instanceof ReflectionClass) { + $elements = array_merge( + $elements, + array_values($parentElement->getOwnMethods()), + array_values($parentElement->getOwnConstants()), + array_values($parentElement->getOwnProperties()) + ); + } + + $tokens = $parentElement->getBroker()->getFileTokens($parentElement->getFileName()); + + foreach ($elements as $element) { + $line = $element->getStartLine(); + $label = $labeler($element); + + $annotations = $element->getAnnotations(); + + // Documentation + if (empty($element->longDescription)) { + if (empty($annotations)) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of %s', $label)); + continue; + } + // Description + $list[$fileName][] = array('error', $line, sprintf('Missing description of %s', $label)); + } + + // Documentation of method + if ($element instanceof ReflectionMethod || $element instanceof ReflectionFunction) { + // Parameters + $unlimited = false; + foreach ($element->getParameters() as $no => $parameter) { + if (!isset($annotations['param'][$no])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of %s', $labeler($parameter))); + continue; + } + + if (!preg_match('~^[\\w\\\\]+(?:\\[\\])?(?:\\|[\\w\\\\]+(?:\\[\\])?)*(?:\\s+\\$' . $parameter->getName() . ($parameter->isUnlimited() ? ',\\.{3}' : '') . ')?(?:\\s+.+)?$~s', $annotations['param'][$no])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of %s', $annotations['param'][$no], $labeler($parameter))); + } + + if ($unlimited && $parameter->isUnlimited()) { + $list[$fileName][] = array('warning', $line, sprintf('More than one unlimited parameters of %s', $labeler($element))); + } elseif ($parameter->isUnlimited()) { + $unlimited = true; + } + + unset($annotations['param'][$no]); + } + if (isset($annotations['param'])) { + foreach ($annotations['param'] as $annotation) { + $list[$fileName][] = array('warning', $line, sprintf('Existing documentation "%s" of nonexistent parameter of %s', $annotation, $label)); + } + } + + // Return values + $return = false; + $tokens->seek($element->getStartPosition()) + ->find(T_FUNCTION); + while ($tokens->next() && $tokens->key() < $element->getEndPosition()) { + $type = $tokens->getType(); + if (T_FUNCTION === $type) { + // Skip annonymous functions + $tokens->find('{')->findMatchingBracket(); + } elseif (T_RETURN === $type && !$tokens->skipWhitespaces()->is(';')) { + // Skip return without return value + $return = true; + break; + } + } + if ($return && !isset($annotations['return'])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of return value of %s', $label)); + } elseif (isset($annotations['return'])) { + if (!$return && 'void' !== $annotations['return'][0] && ($element instanceof ReflectionFunction || (!$parentElement->isInterface() && !$element->isAbstract()))) { + $list[$fileName][] = array('warning', $line, sprintf('Existing documentation "%s" of nonexistent return value of %s', $annotations['return'][0], $label)); + } elseif (!preg_match('~^[\\w\\\\]+(?:\\[\\])?(?:\\|[\\w\\\\]+(?:\\[\\])?)*(?:\\s+.+)?$~s', $annotations['return'][0])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of return value of %s', $annotations['return'][0], $label)); + } + } + if (isset($annotations['return'][1])) { + $list[$fileName][] = array('warning', $line, sprintf('Duplicate documentation "%s" of return value of %s', $annotations['return'][1], $label)); + } + + // Throwing exceptions + $throw = false; + $tokens->seek($element->getStartPosition()) + ->find(T_FUNCTION); + while ($tokens->next() && $tokens->key() < $element->getEndPosition()) { + $type = $tokens->getType(); + if (T_TRY === $type) { + // Skip try + $tokens->find('{')->findMatchingBracket(); + } elseif (T_THROW === $type) { + $throw = true; + break; + } + } + if ($throw && !isset($annotations['throws'])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of throwing an exception of %s', $label)); + } elseif (isset($annotations['throws']) && !preg_match('~^[\\w\\\\]+(?:\\|[\\w\\\\]+)*(?:\\s+.+)?$~s', $annotations['throws'][0])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of throwing an exception of %s', $annotations['throws'][0], $label)); + } + } + + // Data type of constants & properties + if ($element instanceof ReflectionProperty || $element instanceof ReflectionConstant) { + if (!isset($annotations['var'])) { + $list[$fileName][] = array('error', $line, sprintf('Missing documentation of the data type of %s', $label)); + } elseif (!preg_match('~^[\\w\\\\]+(?:\\[\\])?(?:\\|[\\w\\\\]+(?:\\[\\])?)*(?:\\s+.+)?$~s', $annotations['var'][0])) { + $list[$fileName][] = array('warning', $line, sprintf('Invalid documentation "%s" of the data type of %s', $annotations['var'][0], $label)); + } + + if (isset($annotations['var'][1])) { + $list[$fileName][] = array('warning', $line, sprintf('Duplicate documentation "%s" of the data type of %s', $annotations['var'][1], $label)); + } + } + } + unset($tokens); + } + } + uksort($list, 'strcasecmp'); + + $file = @fopen($this->config->report, 'w'); + if (false === $file) { + throw new RuntimeException(sprintf('File "%s" isn\'t writable', $this->config->report)); + } + fwrite($file, sprintf('%s', "\n")); + fwrite($file, sprintf('%s', "\n")); + foreach ($list as $fileName => $reports) { + fwrite($file, sprintf('%s%s', "\t", $fileName, "\n")); + + // Sort by line + usort($reports, function($one, $two) { + return strnatcmp($one[1], $two[1]); + }); + + foreach ($reports as $report) { + list($severity, $line, $message) = $report; + $message = preg_replace('~\\s+~u', ' ', $message); + fwrite($file, sprintf('%s%s', "\t\t", $severity, $line, htmlspecialchars($message), "\n")); + } + + fwrite($file, sprintf('%s%s', "\t", "\n")); + } + fwrite($file, sprintf('%s', "\n")); + fclose($file); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates list of deprecated elements. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateDeprecated(Template $template) + { + $this->prepareTemplate('deprecated'); + + $deprecatedFilter = function($element) { + return $element->isDeprecated(); + }; + + $template->deprecatedMethods = array(); + $template->deprecatedConstants = array(); + $template->deprecatedProperties = array(); + foreach (array_reverse($this->getElementTypes()) as $type) { + $template->{'deprecated' . ucfirst($type)} = array_filter(array_filter($this->$type, $this->getMainFilter()), $deprecatedFilter); + + if ('constants' === $type || 'functions' === $type) { + continue; + } + + foreach ($this->$type as $class) { + if (!$class->isMain()) { + continue; + } + + if ($class->isDeprecated()) { + continue; + } + + $template->deprecatedMethods = array_merge($template->deprecatedMethods, array_values(array_filter($class->getOwnMethods(), $deprecatedFilter))); + $template->deprecatedConstants = array_merge($template->deprecatedConstants, array_values(array_filter($class->getOwnConstants(), $deprecatedFilter))); + $template->deprecatedProperties = array_merge($template->deprecatedProperties, array_values(array_filter($class->getOwnProperties(), $deprecatedFilter))); + } + } + usort($template->deprecatedMethods, array($this, 'sortMethods')); + usort($template->deprecatedConstants, array($this, 'sortConstants')); + usort($template->deprecatedFunctions, array($this, 'sortFunctions')); + usort($template->deprecatedProperties, array($this, 'sortProperties')); + + $template + ->setFile($this->getTemplatePath('deprecated')) + ->save($this->forceDir($this->getTemplateFileName('deprecated'))); + + foreach ($this->getElementTypes() as $type) { + unset($template->{'deprecated' . ucfirst($type)}); + } + unset($template->deprecatedMethods); + unset($template->deprecatedProperties); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates list of tasks. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateTodo(Template $template) + { + $this->prepareTemplate('todo'); + + $todoFilter = function($element) { + return $element->hasAnnotation('todo'); + }; + + $template->todoMethods = array(); + $template->todoConstants = array(); + $template->todoProperties = array(); + foreach (array_reverse($this->getElementTypes()) as $type) { + $template->{'todo' . ucfirst($type)} = array_filter(array_filter($this->$type, $this->getMainFilter()), $todoFilter); + + if ('constants' === $type || 'functions' === $type) { + continue; + } + + foreach ($this->$type as $class) { + if (!$class->isMain()) { + continue; + } + + $template->todoMethods = array_merge($template->todoMethods, array_values(array_filter($class->getOwnMethods(), $todoFilter))); + $template->todoConstants = array_merge($template->todoConstants, array_values(array_filter($class->getOwnConstants(), $todoFilter))); + $template->todoProperties = array_merge($template->todoProperties, array_values(array_filter($class->getOwnProperties(), $todoFilter))); + } + } + usort($template->todoMethods, array($this, 'sortMethods')); + usort($template->todoConstants, array($this, 'sortConstants')); + usort($template->todoFunctions, array($this, 'sortFunctions')); + usort($template->todoProperties, array($this, 'sortProperties')); + + $template + ->setFile($this->getTemplatePath('todo')) + ->save($this->forceDir($this->getTemplateFileName('todo'))); + + foreach ($this->getElementTypes() as $type) { + unset($template->{'todo' . ucfirst($type)}); + } + unset($template->todoMethods); + unset($template->todoProperties); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates classes/interfaces/traits/exceptions tree. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateTree(Template $template) + { + $this->prepareTemplate('tree'); + + $classTree = array(); + $interfaceTree = array(); + $traitTree = array(); + $exceptionTree = array(); + + $processed = array(); + foreach ($this->parsedClasses as $className => $reflection) { + if (!$reflection->isMain() || !$reflection->isDocumented() || isset($processed[$className])) { + continue; + } + + if (null === $reflection->getParentClassName()) { + // No parent classes + if ($reflection->isInterface()) { + $t = &$interfaceTree; + } elseif ($reflection->isTrait()) { + $t = &$traitTree; + } elseif ($reflection->isException()) { + $t = &$exceptionTree; + } else { + $t = &$classTree; + } + } else { + foreach (array_values(array_reverse($reflection->getParentClasses())) as $level => $parent) { + if (0 === $level) { + // The topmost parent decides about the reflection type + if ($parent->isInterface()) { + $t = &$interfaceTree; + } elseif ($parent->isTrait()) { + $t = &$traitTree; + } elseif ($parent->isException()) { + $t = &$exceptionTree; + } else { + $t = &$classTree; + } + } + $parentName = $parent->getName(); + + if (!isset($t[$parentName])) { + $t[$parentName] = array(); + $processed[$parentName] = true; + ksort($t, SORT_STRING); + } + + $t = &$t[$parentName]; + } + } + $t[$className] = array(); + ksort($t, SORT_STRING); + $processed[$className] = true; + unset($t); + } + + $template->classTree = new Tree($classTree, $this->parsedClasses); + $template->interfaceTree = new Tree($interfaceTree, $this->parsedClasses); + $template->traitTree = new Tree($traitTree, $this->parsedClasses); + $template->exceptionTree = new Tree($exceptionTree, $this->parsedClasses); + + $template + ->setFile($this->getTemplatePath('tree')) + ->save($this->forceDir($this->getTemplateFileName('tree'))); + + unset($template->classTree); + unset($template->interfaceTree); + unset($template->traitTree); + unset($template->exceptionTree); + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Generates packages summary. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generatePackages(Template $template) + { + if (empty($this->packages)) { + return $this; + } + + $this->prepareTemplate('package'); + + $template->namespace = null; + + foreach ($this->packages as $packageName => $package) { + $template->package = $packageName; + $template->subpackages = array_filter($template->packages, function($subpackageName) use ($packageName) { + return (bool) preg_match('~^' . preg_quote($packageName) . '\\\\[^\\\\]+$~', $subpackageName); + }); + $template->classes = $package['classes']; + $template->interfaces = $package['interfaces']; + $template->traits = $package['traits']; + $template->exceptions = $package['exceptions']; + $template->constants = $package['constants']; + $template->functions = $package['functions']; + $template + ->setFile($this->getTemplatePath('package')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getPackageUrl($packageName)); + + $this->incrementProgressBar(); + } + unset($template->subpackages); + + $this->checkMemory(); + + return $this; + } + + /** + * Generates namespaces summary. + * + * @param \ApiGen\Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateNamespaces(Template $template) + { + if (empty($this->namespaces)) { + return $this; + } + + $this->prepareTemplate('namespace'); + + $template->package = null; + + foreach ($this->namespaces as $namespaceName => $namespace) { + $template->namespace = $namespaceName; + $template->subnamespaces = array_filter($template->namespaces, function($subnamespaceName) use ($namespaceName) { + return (bool) preg_match('~^' . preg_quote($namespaceName) . '\\\\[^\\\\]+$~', $subnamespaceName); + }); + $template->classes = $namespace['classes']; + $template->interfaces = $namespace['interfaces']; + $template->traits = $namespace['traits']; + $template->exceptions = $namespace['exceptions']; + $template->constants = $namespace['constants']; + $template->functions = $namespace['functions']; + $template + ->setFile($this->getTemplatePath('namespace')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getNamespaceUrl($namespaceName)); + + $this->incrementProgressBar(); + } + unset($template->subnamespaces); + + $this->checkMemory(); + + return $this; + } + + /** + * Generate classes, interfaces, traits, exceptions, constants and functions files. + * + * @param Template $template Template + * @return \ApiGen\Generator + * @throws \RuntimeException If template is not set. + */ + private function generateElements(Template $template) + { + if (!empty($this->classes) || !empty($this->interfaces) || !empty($this->traits) || !empty($this->exceptions)) { + $this->prepareTemplate('class'); + } + if (!empty($this->constants)) { + $this->prepareTemplate('constant'); + } + if (!empty($this->functions)) { + $this->prepareTemplate('function'); + } + if ($this->config->sourceCode) { + $this->prepareTemplate('source'); + + $fshl = new FSHL\Highlighter(new FSHL\Output\Html(), FSHL\Highlighter::OPTION_TAB_INDENT | FSHL\Highlighter::OPTION_LINE_COUNTER); + $fshl->setLexer(new FSHL\Lexer\Php()); + } + + // Add @usedby annotation + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $parentElement) { + $elements = array($parentElement); + if ($parentElement instanceof ReflectionClass) { + $elements = array_merge( + $elements, + array_values($parentElement->getOwnMethods()), + array_values($parentElement->getOwnConstants()), + array_values($parentElement->getOwnProperties()) + ); + } + foreach ($elements as $element) { + $uses = $element->getAnnotation('uses'); + if (null === $uses) { + continue; + } + foreach ($uses as $value) { + list($link, $description) = preg_split('~\s+|$~', $value, 2); + $resolved = $this->resolveElement($link, $element); + if (null !== $resolved) { + $resolved->addAnnotation('usedby', $element->getPrettyName() . ' ' . $description); + } + } + } + } + } + + $template->package = null; + $template->namespace = null; + $template->classes = $this->classes; + $template->interfaces = $this->interfaces; + $template->traits = $this->traits; + $template->exceptions = $this->exceptions; + $template->constants = $this->constants; + $template->functions = $this->functions; + foreach ($this->getElementTypes() as $type) { + foreach ($this->$type as $element) { + if (!empty($this->namespaces)) { + $template->namespace = $namespaceName = $element->getPseudoNamespaceName(); + $template->classes = $this->namespaces[$namespaceName]['classes']; + $template->interfaces = $this->namespaces[$namespaceName]['interfaces']; + $template->traits = $this->namespaces[$namespaceName]['traits']; + $template->exceptions = $this->namespaces[$namespaceName]['exceptions']; + $template->constants = $this->namespaces[$namespaceName]['constants']; + $template->functions = $this->namespaces[$namespaceName]['functions']; + } elseif (!empty($this->packages)) { + $template->package = $packageName = $element->getPseudoPackageName(); + $template->classes = $this->packages[$packageName]['classes']; + $template->interfaces = $this->packages[$packageName]['interfaces']; + $template->traits = $this->packages[$packageName]['traits']; + $template->exceptions = $this->packages[$packageName]['exceptions']; + $template->constants = $this->packages[$packageName]['constants']; + $template->functions = $this->packages[$packageName]['functions']; + } + + $template->class = null; + $template->constant = null; + $template->function = null; + if ($element instanceof ReflectionClass) { + // Class + $template->tree = array_merge(array_reverse($element->getParentClasses()), array($element)); + + $template->directSubClasses = $element->getDirectSubClasses(); + uksort($template->directSubClasses, 'strcasecmp'); + $template->indirectSubClasses = $element->getIndirectSubClasses(); + uksort($template->indirectSubClasses, 'strcasecmp'); + + $template->directImplementers = $element->getDirectImplementers(); + uksort($template->directImplementers, 'strcasecmp'); + $template->indirectImplementers = $element->getIndirectImplementers(); + uksort($template->indirectImplementers, 'strcasecmp'); + + $template->directUsers = $element->getDirectUsers(); + uksort($template->directUsers, 'strcasecmp'); + $template->indirectUsers = $element->getIndirectUsers(); + uksort($template->indirectUsers, 'strcasecmp'); + + $template->class = $element; + + $template + ->setFile($this->getTemplatePath('class')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getClassUrl($element)); + } elseif ($element instanceof ReflectionConstant) { + // Constant + $template->constant = $element; + + $template + ->setFile($this->getTemplatePath('constant')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getConstantUrl($element)); + } elseif ($element instanceof ReflectionFunction) { + // Function + $template->function = $element; + + $template + ->setFile($this->getTemplatePath('function')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getFunctionUrl($element)); + } + + $this->incrementProgressBar(); + + // Generate source codes + if ($this->config->sourceCode && $element->isTokenized()) { + $template->fileName = $this->getRelativePath($element->getFileName()); + $template->source = $fshl->highlight($this->toUtf(file_get_contents($element->getFileName()), $this->charsets[$element->getFileName()])); + $template + ->setFile($this->getTemplatePath('source')) + ->save($this->config->destination . DIRECTORY_SEPARATOR . $template->getSourceUrl($element, false)); + + $this->incrementProgressBar(); + } + + $this->checkMemory(); + } + } + + return $this; + } + + /** + * Creates ZIP archive. + * + * @return \ApiGen\Generator + * @throws \RuntimeException If something went wrong. + */ + private function generateArchive() + { + if (!extension_loaded('zip')) { + throw new RuntimeException('Extension zip is not loaded'); + } + + $archive = new \ZipArchive(); + if (true !== $archive->open($this->getArchivePath(), \ZipArchive::CREATE)) { + throw new RuntimeException('Could not open ZIP archive'); + } + + $archive->setArchiveComment(trim(sprintf('%s API documentation generated by %s %s on %s', $this->config->title, self::NAME, self::VERSION, date('Y-m-d H:i:s')))); + + $directory = Nette\Utils\Strings::webalize(trim(sprintf('%s API documentation', $this->config->title)), null, false); + $destinationLength = strlen($this->config->destination); + foreach ($this->getGeneratedFiles() as $file) { + if (is_file($file)) { + $archive->addFile($file, $directory . DIRECTORY_SEPARATOR . substr($file, $destinationLength + 1)); + } + } + + if (false === $archive->close()) { + throw new RuntimeException('Could not save ZIP archive'); + } + + $this->incrementProgressBar(); + $this->checkMemory(); + + return $this; + } + + /** + * Tries to resolve string as class, interface or exception name. + * + * @param string $className Class name description + * @param string $namespace Namespace name + * @return \ApiGen\ReflectionClass + */ + public function getClass($className, $namespace = '') + { + if (isset($this->parsedClasses[$namespace . '\\' . $className])) { + $class = $this->parsedClasses[$namespace . '\\' . $className]; + } elseif (isset($this->parsedClasses[ltrim($className, '\\')])) { + $class = $this->parsedClasses[ltrim($className, '\\')]; + } else { + return null; + } + + // Class is not "documented" + if (!$class->isDocumented()) { + return null; + } + + return $class; + } + + /** + * Tries to resolve type as constant name. + * + * @param string $constantName Constant name + * @param string $namespace Namespace name + * @return \ApiGen\ReflectionConstant + */ + public function getConstant($constantName, $namespace = '') + { + if (isset($this->parsedConstants[$namespace . '\\' . $constantName])) { + $constant = $this->parsedConstants[$namespace . '\\' . $constantName]; + } elseif (isset($this->parsedConstants[ltrim($constantName, '\\')])) { + $constant = $this->parsedConstants[ltrim($constantName, '\\')]; + } else { + return null; + } + + // Constant is not "documented" + if (!$constant->isDocumented()) { + return null; + } + + return $constant; + } + + /** + * Tries to resolve type as function name. + * + * @param string $functionName Function name + * @param string $namespace Namespace name + * @return \ApiGen\ReflectionFunction + */ + public function getFunction($functionName, $namespace = '') + { + if (isset($this->parsedFunctions[$namespace . '\\' . $functionName])) { + $function = $this->parsedFunctions[$namespace . '\\' . $functionName]; + } elseif (isset($this->parsedFunctions[ltrim($functionName, '\\')])) { + $function = $this->parsedFunctions[ltrim($functionName, '\\')]; + } else { + return null; + } + + // Function is not "documented" + if (!$function->isDocumented()) { + return null; + } + + return $function; + } + + /** + * Tries to parse a definition of a class/method/property/constant/function and returns the appropriate instance if successful. + * + * @param string $definition Definition + * @param \ApiGen\ReflectionElement|\ApiGen\ReflectionParameter $context Link context + * @param string $expectedName Expected element name + * @return \ApiGen\ReflectionElement|null + */ + public function resolveElement($definition, $context, &$expectedName = null) + { + // No simple type resolving + static $types = array( + 'boolean' => 1, 'integer' => 1, 'float' => 1, 'string' => 1, + 'array' => 1, 'object' => 1, 'resource' => 1, 'callback' => 1, + 'callable' => 1, 'null' => 1, 'false' => 1, 'true' => 1, 'mixed' => 1 + ); + + if (empty($definition) || isset($types[$definition])) { + return null; + } + + $originalContext = $context; + + if ($context instanceof ReflectionParameter && null === $context->getDeclaringClassName()) { + // Parameter of function in namespace or global space + $context = $this->getFunction($context->getDeclaringFunctionName()); + } elseif ($context instanceof ReflectionMethod || $context instanceof ReflectionParameter + || ($context instanceof ReflectionConstant && null !== $context->getDeclaringClassName()) + || $context instanceof ReflectionProperty + ) { + // Member of a class + $context = $this->getClass($context->getDeclaringClassName()); + } + + if (null === $context) { + return null; + } + + // self, $this references + if ('self' === $definition || '$this' === $definition) { + return $context instanceof ReflectionClass ? $context : null; + } + + $definitionBase = substr($definition, 0, strcspn($definition, '\\:')); + $namespaceAliases = $context->getNamespaceAliases(); + if (!empty($definitionBase) && isset($namespaceAliases[$definitionBase]) && $definition !== ($className = \TokenReflection\Resolver::resolveClassFQN($definition, $namespaceAliases, $context->getNamespaceName()))) { + // Aliased class + $expectedName = $className; + + if (false === strpos($className, ':')) { + return $this->getClass($className, $context->getNamespaceName()); + } else { + $definition = $className; + } + } elseif ($class = $this->getClass($definition, $context->getNamespaceName())) { + // Class + return $class; + } elseif ($constant = $this->getConstant($definition, $context->getNamespaceName())) { + // Constant + return $constant; + } elseif (($function = $this->getFunction($definition, $context->getNamespaceName())) + || ('()' === substr($definition, -2) && ($function = $this->getFunction(substr($definition, 0, -2), $context->getNamespaceName()))) + ) { + // Function + return $function; + } + + if (($pos = strpos($definition, '::')) || ($pos = strpos($definition, '->'))) { + // Class::something or Class->something + if (0 === strpos($definition, 'parent::') && ($parentClassName = $context->getParentClassName())) { + $context = $this->getClass($parentClassName); + } elseif (0 !== strpos($definition, 'self::')) { + $class = $this->getClass(substr($definition, 0, $pos), $context->getNamespaceName()); + + if (null === $class) { + $class = $this->getClass(\TokenReflection\Resolver::resolveClassFQN(substr($definition, 0, $pos), $context->getNamespaceAliases(), $context->getNamespaceName())); + } + + $context = $class; + } + + $definition = substr($definition, $pos + 2); + } elseif ($originalContext instanceof ReflectionParameter) { + return null; + } + + // No usable context + if (null === $context || $context instanceof ReflectionConstant || $context instanceof ReflectionFunction) { + return null; + } + + if ($context->hasProperty($definition)) { + // Class property + return $context->getProperty($definition); + } elseif ('$' === $definition{0} && $context->hasProperty(substr($definition, 1))) { + // Class $property + return $context->getProperty(substr($definition, 1)); + } elseif ($context->hasMethod($definition)) { + // Class method + return $context->getMethod($definition); + } elseif ('()' === substr($definition, -2) && $context->hasMethod(substr($definition, 0, -2))) { + // Class method() + return $context->getMethod(substr($definition, 0, -2)); + } elseif ($context->hasConstant($definition)) { + // Class constant + return $context->getConstant($definition); + } + + return null; + } + + /** + * Prints message if printing is enabled. + * + * @param string $message Output message + */ + public function output($message) + { + if (!$this->config->quiet) { + echo $this->colorize($message); + } + } + + /** + * Colorizes message or removes placeholders if OS doesn't support colors. + * + * @param string $message + * @return string + */ + public function colorize($message) + { + static $placeholders = array( + '@header@' => "\x1b[1;34m", + '@count@' => "\x1b[1;34m", + '@option@' => "\x1b[0;36m", + '@value@' => "\x1b[0;32m", + '@error@' => "\x1b[0;31m", + '@c' => "\x1b[0m" + ); + + if (!$this->config->colors) { + $placeholders = array_fill_keys(array_keys($placeholders), ''); + } + + return strtr($message, $placeholders); + } + + /** + * Returns header. + * + * @return string + */ + public function getHeader() + { + $name = sprintf('%s %s', self::NAME, self::VERSION); + return sprintf("@header@%s@c\n%s\n", $name, str_repeat('-', strlen($name))); + } + + /** + * Removes phar:// from the path. + * + * @param string $path Path + * @return string + */ + public function unPharPath($path) + { + if (0 === strpos($path, 'phar://')) { + $path = substr($path, 7); + } + return $path; + } + + /** + * Adds phar:// to the path. + * + * @param string $path Path + * @return string + */ + private function pharPath($path) + { + return 'phar://' . $path; + } + + /** + * Checks if given path is a phar. + * + * @param string $path + * @return boolean + */ + private function isPhar($path) + { + return (bool) preg_match('~\\.phar(?:\\.zip|\\.tar|(?:(?:\\.tar)?(?:\\.gz|\\.bz2))|$)~i', $path); + } + + /** + * Normalizes directory separators in given path. + * + * @param string $path Path + * @return string + */ + private function normalizePath($path) + { + $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path); + $path = str_replace('phar:\\\\', 'phar://', $path); + return $path; + } + + /** + * Prepares the progressbar. + * + * @param integer $maximum Maximum progressbar value + */ + private function prepareProgressBar($maximum = 1) + { + if (!$this->config->progressbar) { + return; + } + + $this->progressbar['current'] = 0; + $this->progressbar['maximum'] = $maximum; + } + + /** + * Increments the progressbar by one. + * + * @param integer $increment Progressbar increment + */ + private function incrementProgressBar($increment = 1) + { + if (!$this->config->progressbar) { + return; + } + + echo str_repeat(chr(0x08), $this->progressbar['width']); + + $this->progressbar['current'] += $increment; + + $percent = $this->progressbar['current'] / $this->progressbar['maximum']; + + $progress = str_pad(str_pad('>', round($percent * $this->progressbar['bar']), '=', STR_PAD_LEFT), $this->progressbar['bar'], ' ', STR_PAD_RIGHT); + + echo sprintf($this->progressbar['skeleton'], $progress, $percent * 100, round(memory_get_usage(true) / 1024 / 1024)); + + if ($this->progressbar['current'] === $this->progressbar['maximum']) { + echo "\n"; + } + } + + /** + * Checks memory usage. + * + * @return \ApiGen\Generator + * @throws \RuntimeException If there is unsufficient reserve of memory. + */ + public function checkMemory() + { + static $limit = null; + if (null === $limit) { + $value = ini_get('memory_limit'); + $unit = substr($value, -1); + if ('-1' === $value) { + $limit = 0; + } elseif ('G' === $unit) { + $limit = (int) $value * 1024 * 1024 * 1024; + } elseif ('M' === $unit) { + $limit = (int) $value * 1024 * 1024; + } else { + $limit = (int) $value; + } + } + + if ($limit && memory_get_usage(true) / $limit >= 0.9) { + throw new RuntimeException(sprintf('Used %d%% of the current memory limit, please increase the limit to generate the whole documentation.', round(memory_get_usage(true) / $limit * 100))); + } + + return $this; + } + + /** + * Detects character set for the given text. + * + * @param string $text Text + * @return string + */ + private function detectCharset($text) + { + // One character set + if (1 === count($this->config->charset) && 'AUTO' !== $this->config->charset[0]) { + return $this->config->charset[0]; + } + + static $charsets = array(); + if (empty($charsets)) { + if (1 === count($this->config->charset) && 'AUTO' === $this->config->charset[0]) { + // Autodetection + $charsets = array( + 'Windows-1251', 'Windows-1252', 'ISO-8859-2', 'ISO-8859-1', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5', 'ISO-8859-6', + 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15' + ); + } else { + // More character sets + $charsets = $this->config->charset; + if (false !== ($key = array_search('WINDOWS-1250', $charsets))) { + // WINDOWS-1250 is not supported + $charsets[$key] = 'ISO-8859-2'; + } + } + // Only supported character sets + $charsets = array_intersect($charsets, mb_list_encodings()); + + // UTF-8 have to be first + array_unshift($charsets, 'UTF-8'); + } + + $charset = mb_detect_encoding($text, $charsets); + // The previous function can not handle WINDOWS-1250 and returns ISO-8859-2 instead + if ('ISO-8859-2' === $charset && preg_match('~[\x7F-\x9F\xBC]~', $text)) { + $charset = 'WINDOWS-1250'; + } + + return $charset; + } + + /** + * Converts text from given character set to UTF-8. + * + * @param string $text Text + * @param string $charset Character set + * @return string + */ + private function toUtf($text, $charset) + { + if ('UTF-8' === $charset) { + return $text; + } + + return @iconv($charset, 'UTF-8//TRANSLIT//IGNORE', $text); + } + + /** + * Checks if sitemap.xml is enabled. + * + * @return boolean + */ + private function isSitemapEnabled() + { + return !empty($this->config->baseUrl) && $this->templateExists('sitemap', 'optional'); + } + + /** + * Checks if opensearch.xml is enabled. + * + * @return boolean + */ + private function isOpensearchEnabled() + { + return !empty($this->config->googleCseId) && !empty($this->config->baseUrl) && $this->templateExists('opensearch', 'optional'); + } + + /** + * Checks if robots.txt is enabled. + * + * @return boolean + */ + private function isRobotsEnabled() + { + return !empty($this->config->baseUrl) && $this->templateExists('robots', 'optional'); + } + + /** + * Sorts methods by FQN. + * + * @param \ApiGen\ReflectionMethod $one + * @param \ApiGen\ReflectionMethod $two + * @return integer + */ + private function sortMethods(ReflectionMethod $one, ReflectionMethod $two) + { + return strcasecmp($one->getDeclaringClassName() . '::' . $one->getName(), $two->getDeclaringClassName() . '::' . $two->getName()); + } + + /** + * Sorts constants by FQN. + * + * @param \ApiGen\ReflectionConstant $one + * @param \ApiGen\ReflectionConstant $two + * @return integer + */ + private function sortConstants(ReflectionConstant $one, ReflectionConstant $two) + { + return strcasecmp(($one->getDeclaringClassName() ?: $one->getNamespaceName()) . '\\' . $one->getName(), ($two->getDeclaringClassName() ?: $two->getNamespaceName()) . '\\' . $two->getName()); + } + + /** + * Sorts functions by FQN. + * + * @param \ApiGen\ReflectionFunction $one + * @param \ApiGen\ReflectionFunction $two + * @return integer + */ + private function sortFunctions(ReflectionFunction $one, ReflectionFunction $two) + { + return strcasecmp($one->getNamespaceName() . '\\' . $one->getName(), $two->getNamespaceName() . '\\' . $two->getName()); + } + + /** + * Sorts functions by FQN. + * + * @param \ApiGen\ReflectionProperty $one + * @param \ApiGen\ReflectionProperty $two + * @return integer + */ + private function sortProperties(ReflectionProperty $one, ReflectionProperty $two) + { + return strcasecmp($one->getDeclaringClassName() . '::' . $one->getName(), $two->getDeclaringClassName() . '::' . $two->getName()); + } + + /** + * Returns list of element types. + * + * @return array + */ + private function getElementTypes() + { + static $types = array('classes', 'interfaces', 'traits', 'exceptions', 'constants', 'functions'); + return $types; + } + + /** + * Returns main filter. + * + * @return \Closure + */ + private function getMainFilter() + { + return function($element) { + return $element->isMain(); + }; + } + + /** + * Returns ZIP archive path. + * + * @return string + */ + private function getArchivePath() + { + $name = trim(sprintf('%s API documentation', $this->config->title)); + return $this->config->destination . DIRECTORY_SEPARATOR . Nette\Utils\Strings::webalize($name) . '.zip'; + } + + /** + * Returns filename relative path to the source directory. + * + * @param string $fileName + * @return string + * @throws \InvalidArgumentException If relative path could not be determined. + */ + public function getRelativePath($fileName) + { + if (isset($this->symlinks[$fileName])) { + $fileName = $this->symlinks[$fileName]; + } + foreach ($this->config->source as $source) { + if ($this->isPhar($source)) { + $source = $this->pharPath($source); + } + if (0 === strpos($fileName, $source)) { + return is_dir($source) ? str_replace('\\', '/', substr($fileName, strlen($source) + 1)) : basename($fileName); + } + } + + throw new InvalidArgumentException(sprintf('Could not determine "%s" relative path', $fileName)); + } + + /** + * Returns template directory. + * + * @return string + */ + private function getTemplateDir() + { + return dirname($this->config->templateConfig); + } + + /** + * Returns template path. + * + * @param string $name Template name + * @param string $type Template type + * @return string + */ + private function getTemplatePath($name, $type = 'main') + { + return $this->getTemplateDir() . DIRECTORY_SEPARATOR . $this->config->template['templates'][$type][$name]['template']; + } + + /** + * Returns template filename. + * + * @param string $name Template name + * @param string $type Template type + * @return string + */ + private function getTemplateFileName($name, $type = 'main') + { + return $this->config->destination . DIRECTORY_SEPARATOR . $this->config->template['templates'][$type][$name]['filename']; + } + + /** + * Checks if template exists. + * + * @param string $name Template name + * @param string $type Template type + * @return string + */ + private function templateExists($name, $type = 'main') + { + return isset($this->config->template['templates'][$type][$name]); + } + + /** + * Checks if template exists and creates dir. + * + * @param string $name + * @throws \RuntimeException If template is not set. + */ + private function prepareTemplate($name) + { + if (!$this->templateExists($name)) { + throw new RuntimeException(sprintf('Template for "%s" is not set', $name)); + } + + $this->forceDir($this->getTemplateFileName($name)); + return $this; + } + + /** + * Returns list of all generated files. + * + * @return array + */ + private function getGeneratedFiles() + { + $files = array(); + + // Resources + foreach ($this->config->template['resources'] as $item) { + $path = $this->getTemplateDir() . DIRECTORY_SEPARATOR . $item; + if (is_dir($path)) { + $iterator = Nette\Utils\Finder::findFiles('*')->from($path)->getIterator(); + foreach ($iterator as $innerItem) { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $item . DIRECTORY_SEPARATOR . $iterator->getSubPathName(); + } + } else { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $item; + } + } + + // Common files + foreach ($this->config->template['templates']['common'] as $item) { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $item; + } + + // Optional files + foreach ($this->config->template['templates']['optional'] as $optional) { + $files[] = $this->config->destination . DIRECTORY_SEPARATOR . $optional['filename']; + } + + // Main files + $masks = array_map(function($config) { + return preg_replace('~%[^%]*?s~', '*', $config['filename']); + }, $this->config->template['templates']['main']); + $filter = function($item) use ($masks) { + foreach ($masks as $mask) { + if (fnmatch($mask, $item->getFilename())) { + return true; + } + } + return false; + }; + + foreach (Nette\Utils\Finder::findFiles('*')->filter($filter)->from($this->config->destination) as $item) { + $files[] = $item->getPathName(); + } + + return $files; + } + + /** + * Ensures a directory is created. + * + * @param string $path Directory path + * @return string + */ + private function forceDir($path) + { + @mkdir(dirname($path), 0755, true); + return $path; + } + + /** + * Deletes a directory. + * + * @param string $path Directory path + * @return boolean + */ + private function deleteDir($path) + { + if (!is_dir($path)) { + return true; + } + + foreach (Nette\Utils\Finder::find('*')->from($path)->childFirst() as $item) { + if ($item->isDir()) { + if (!@rmdir($item)) { + return false; + } + } elseif ($item->isFile()) { + if (!@unlink($item)) { + return false; + } + } + } + if (!@rmdir($path)) { + return false; + } + + return true; + } +} diff --git a/vendor/apigen/apigen/ApiGen/IHelperSet.php b/vendor/apigen/apigen/ApiGen/IHelperSet.php new file mode 100644 index 0000000..911c838 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/IHelperSet.php @@ -0,0 +1,28 @@ +getConfig(); + self::$parsedClasses = $generator->getParsedClasses(); + self::$parsedConstants = $generator->getParsedConstants(); + self::$parsedFunctions = $generator->getParsedFunctions(); + } + + $this->reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + + $this->reflection = $reflection; + } + + /** + * Retrieves a property or method value. + * + * First tries the envelope object's property storage, then its methods + * and finally the inspected element reflection. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return $this->reflection->__get($name); + } + + /** + * Checks if the given property exists. + * + * First tries the envelope object's property storage, then its methods + * and finally the inspected element reflection. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]) || $this->reflection->__isset($name); + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->reflection->getBroker(); + } + + /** + * Returns the name (FQN). + * + * @return string + */ + public function getName() + { + return $this->reflection->getName(); + } + + /** + * Returns an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return $this->reflection->getPrettyName(); + } + + /** + * Returns if the reflection object is internal. + * + * @return boolean + */ + public function isInternal() + { + return $this->reflection->isInternal(); + } + + /** + * Returns if the reflection object is user defined. + * + * @return boolean + */ + public function isUserDefined() + { + return $this->reflection->isUserDefined(); + } + + /** + * Returns if the current reflection comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return $this->reflection->isTokenized(); + } + + /** + * Returns the file name the reflection object is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->reflection->getFileName(); + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + $startLine = $this->reflection->getStartLine(); + + if ($doc = $this->getDocComment()) { + $startLine -= substr_count($doc, "\n") + 1; + } + + return $startLine; + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return $this->reflection->getEndLine(); + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionClass.php b/vendor/apigen/apigen/ApiGen/ReflectionClass.php new file mode 100644 index 0000000..8053cd6 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionClass.php @@ -0,0 +1,1421 @@ +accessLevels as $level) { + switch (strtolower($level)) { + case 'public': + self::$methodAccessLevels |= InternalReflectionMethod::IS_PUBLIC; + self::$propertyAccessLevels |= InternalReflectionProperty::IS_PUBLIC; + break; + case 'protected': + self::$methodAccessLevels |= InternalReflectionMethod::IS_PROTECTED; + self::$propertyAccessLevels |= InternalReflectionProperty::IS_PROTECTED; + break; + case 'private': + self::$methodAccessLevels |= InternalReflectionMethod::IS_PRIVATE; + self::$propertyAccessLevels |= InternalReflectionProperty::IS_PRIVATE; + break; + default: + break; + } + } + } + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + return $this->reflection->getShortName(); + } + + /** + * Returns modifiers. + * + * @return array + */ + public function getModifiers() + { + return $this->reflection->getModifiers(); + } + + /** + * Returns if the class is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return $this->reflection->isAbstract(); + } + + /** + * Returns if the class is final. + * + * @return boolean + */ + public function isFinal() + { + return $this->reflection->isFinal(); + } + + /** + * Returns if the class is an interface. + * + * @return boolean + */ + public function isInterface() + { + return $this->reflection->isInterface(); + } + + /** + * Returns if the class is an exception or its descendant. + * + * @return boolean + */ + public function isException() + { + return $this->reflection->isException(); + } + + /** + * Returns if the current class is a subclass of the given class. + * + * @param string $class Class name + * @return boolean + */ + public function isSubclassOf($class) + { + return $this->reflection->isSubclassOf($class); + } + + /** + * Returns visible methods. + * + * @return array + */ + public function getMethods() + { + if (null === $this->methods) { + $this->methods = $this->getOwnMethods(); + foreach ($this->reflection->getMethods(self::$methodAccessLevels) as $method) { + if (isset($this->methods[$method->getName()])) { + continue; + } + $apiMethod = new ReflectionMethod($method, self::$generator); + if (!$this->isDocumented() || $apiMethod->isDocumented()) { + $this->methods[$method->getName()] = $apiMethod; + } + } + } + return $this->methods; + } + + /** + * Returns visible methods declared by inspected class. + * + * @return array + */ + public function getOwnMethods() + { + if (null === $this->ownMethods) { + $this->ownMethods = array(); + foreach ($this->reflection->getOwnMethods(self::$methodAccessLevels) as $method) { + $apiMethod = new ReflectionMethod($method, self::$generator); + if (!$this->isDocumented() || $apiMethod->isDocumented()) { + $this->ownMethods[$method->getName()] = $apiMethod; + } + } + } + return $this->ownMethods; + } + + /** + * Returns visible magic methods. + * + * @return array + */ + public function getMagicMethods() + { + $methods = $this->getOwnMagicMethods(); + + $parent = $this->getParentClass(); + while ($parent) { + foreach ($parent->getOwnMagicMethods() as $method) { + if (isset($methods[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $method->isDocumented()) { + $methods[$method->getName()] = $method; + } + } + $parent = $parent->getParentClass(); + } + + foreach ($this->getTraits() as $trait) { + foreach ($trait->getOwnMagicMethods() as $method) { + if (isset($methods[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $method->isDocumented()) { + $methods[$method->getName()] = $method; + } + } + } + + return $methods; + } + + /** + * Returns visible magic methods declared by inspected class. + * + * @return array + */ + public function getOwnMagicMethods() + { + if (null === $this->ownMagicMethods) { + $this->ownMagicMethods = array(); + + if (!(self::$methodAccessLevels & InternalReflectionMethod::IS_PUBLIC) || false === $this->getDocComment()) { + return $this->ownMagicMethods; + } + + $annotations = $this->getAnnotation('method'); + if (null === $annotations) { + return $this->ownMagicMethods; + } + + foreach ($annotations as $annotation) { + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?(&)?\\s*(\\w+)\\s*\\(\\s*(.*)\\s*\\)\\s*(.*|$)~s', $annotation, $matches)) { + // Wrong annotation format + continue; + } + + list(, $returnTypeHint, $returnsReference, $name, $args, $shortDescription) = $matches; + + $doc = $this->getDocComment(); + $tmp = $annotation; + if ($delimiter = strpos($annotation, "\n")) { + $tmp = substr($annotation, 0, $delimiter); + } + + $startLine = $this->getStartLine() + substr_count(substr($doc, 0, strpos($doc, $tmp)), "\n"); + $endLine = $startLine + substr_count($annotation, "\n"); + + $method = new ReflectionMethodMagic(null, self::$generator); + $method + ->setName($name) + ->setShortDescription(str_replace("\n", ' ', $shortDescription)) + ->setStartLine($startLine) + ->setEndLine($endLine) + ->setReturnsReference('&' === $returnsReference) + ->setDeclaringClass($this) + ->addAnnotation('return', $returnTypeHint); + + $this->ownMagicMethods[$name] = $method; + + $parameters = array(); + foreach (array_filter(preg_split('~\\s*,\\s*~', $args)) as $position => $arg) { + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?(&)?\\s*\\$(\\w+)(?:\\s*=\\s*(.*))?($)~s', $arg, $matches)) { + // Wrong annotation format + continue; + } + + list(, $typeHint, $passedByReference, $name, $defaultValueDefinition) = $matches; + + if (empty($typeHint)) { + $typeHint = 'mixed'; + } + + $parameter = new ReflectionParameterMagic(null, self::$generator); + $parameter + ->setName($name) + ->setPosition($position) + ->setTypeHint($typeHint) + ->setDefaultValueDefinition($defaultValueDefinition) + ->setUnlimited(false) + ->setPassedByReference('&' === $passedByReference) + ->setDeclaringFunction($method); + + $parameters[$name] = $parameter; + + $method->addAnnotation('param', ltrim(sprintf('%s $%s', $typeHint, $name))); + } + $method->setParameters($parameters); + } + } + return $this->ownMagicMethods; + } + + /** + * Returns visible methods declared by traits. + * + * @return array + */ + public function getTraitMethods() + { + $methods = array(); + foreach ($this->reflection->getTraitMethods(self::$methodAccessLevels) as $method) { + $apiMethod = new ReflectionMethod($method, self::$generator); + if (!$this->isDocumented() || $apiMethod->isDocumented()) { + $methods[$method->getName()] = $apiMethod; + } + } + return $methods; + } + + /** + * Returns a method reflection. + * + * @param string $name Method name + * @return \ApiGen\ReflectionMethod + * @throws \InvalidArgumentException If required method does not exist. + */ + public function getMethod($name) + { + if ($this->hasMethod($name)) { + return $this->methods[$name]; + } + + throw new InvalidArgumentException(sprintf('Method %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns visible properties. + * + * @return array + */ + public function getProperties() + { + if (null === $this->properties) { + $this->properties = $this->getOwnProperties(); + foreach ($this->reflection->getProperties(self::$propertyAccessLevels) as $property) { + if (isset($this->properties[$property->getName()])) { + continue; + } + $apiProperty = new ReflectionProperty($property, self::$generator); + if (!$this->isDocumented() || $apiProperty->isDocumented()) { + $this->properties[$property->getName()] = $apiProperty; + } + } + } + return $this->properties; + } + + /** + * Returns visible magic properties. + * + * @return array + */ + public function getMagicProperties() + { + $properties = $this->getOwnMagicProperties(); + + $parent = $this->getParentClass(); + while ($parent) { + foreach ($parent->getOwnMagicProperties() as $property) { + if (isset($properties[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $property->isDocumented()) { + $properties[$property->getName()] = $property; + } + } + $parent = $parent->getParentClass(); + } + + foreach ($this->getTraits() as $trait) { + foreach ($trait->getOwnMagicProperties() as $property) { + if (isset($properties[$method->getName()])) { + continue; + } + + if (!$this->isDocumented() || $property->isDocumented()) { + $properties[$property->getName()] = $property; + } + } + } + + return $properties; + } + + /** + * Returns visible properties declared by inspected class. + * + * @return array + */ + public function getOwnProperties() + { + if (null === $this->ownProperties) { + $this->ownProperties = array(); + foreach ($this->reflection->getOwnProperties(self::$propertyAccessLevels) as $property) { + $apiProperty = new ReflectionProperty($property, self::$generator); + if (!$this->isDocumented() || $apiProperty->isDocumented()) { + $this->ownProperties[$property->getName()] = $apiProperty; + } + } + } + return $this->ownProperties; + } + + /** + * Returns visible properties magicly declared by inspected class. + * + * @return array + */ + public function getOwnMagicProperties() + { + if (null === $this->ownMagicProperties) { + $this->ownMagicProperties = array(); + + if (!(self::$propertyAccessLevels & InternalReflectionProperty::IS_PUBLIC) || false === $this->getDocComment()) { + return $this->ownMagicProperties; + } + + foreach (array('property', 'property-read', 'property-write') as $annotationName) { + $annotations = $this->getAnnotation($annotationName); + if (null === $annotations) { + continue; + } + + foreach ($annotations as $annotation) { + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?\\$(\\w+)(?:\\s+(.*))?($)~s', $annotation, $matches)) { + // Wrong annotation format + continue; + } + + list(, $typeHint, $name, $shortDescription) = $matches; + + if (empty($typeHint)) { + $typeHint = 'mixed'; + } + + $doc = $this->getDocComment(); + $tmp = $annotation; + if ($delimiter = strpos($annotation, "\n")) { + $tmp = substr($annotation, 0, $delimiter); + } + + $startLine = $this->getStartLine() + substr_count(substr($doc, 0, strpos($doc, $tmp)), "\n"); + $endLine = $startLine + substr_count($annotation, "\n"); + + $magicProperty = new ReflectionPropertyMagic(null, self::$generator); + $magicProperty + ->setName($name) + ->setTypeHint($typeHint) + ->setShortDescription(str_replace("\n", ' ', $shortDescription)) + ->setStartLine($startLine) + ->setEndLine($endLine) + ->setReadOnly('property-read' === $annotationName) + ->setWriteOnly('property-write' === $annotationName) + ->setDeclaringClass($this) + ->addAnnotation('var', $typeHint); + + $this->ownMagicProperties[$name] = $magicProperty; + } + } + } + + return $this->ownMagicProperties; + } + + /** + * Returns visible properties declared by traits. + * + * @return array + */ + public function getTraitProperties() + { + $properties = array(); + foreach ($this->reflection->getTraitProperties(self::$propertyAccessLevels) as $property) { + $apiProperty = new ReflectionProperty($property, self::$generator); + if (!$this->isDocumented() || $apiProperty->isDocumented()) { + $properties[$property->getName()] = $apiProperty; + } + } + return $properties; + } + + /** + * Returns a method property. + * + * @param string $name Method name + * @return \ApiGen\ReflectionProperty + * @throws \InvalidArgumentException If required property does not exist. + */ + public function getProperty($name) + { + if ($this->hasProperty($name)) { + return $this->properties[$name]; + } + + throw new InvalidArgumentException(sprintf('Property %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns visible properties. + * + * @return array + */ + public function getConstants() + { + if (null === $this->constants) { + $this->constants = array(); + foreach ($this->reflection->getConstantReflections() as $constant) { + $apiConstant = new ReflectionConstant($constant, self::$generator); + if (!$this->isDocumented() || $apiConstant->isDocumented()) { + $this->constants[$constant->getName()] = $apiConstant; + } + } + } + + return $this->constants; + } + + /** + * Returns constants declared by inspected class. + * + * @return array + */ + public function getOwnConstants() + { + if (null === $this->ownConstants) { + $this->ownConstants = array(); + $className = $this->reflection->getName(); + foreach ($this->getConstants() as $constantName => $constant) { + if ($className === $constant->getDeclaringClassName()) { + $this->ownConstants[$constantName] = $constant; + } + } + } + return $this->ownConstants; + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + * @throws \InvalidArgumentException If required constant does not exist. + */ + public function getConstantReflection($name) + { + if (null === $this->constants) { + $this->getConstants(); + } + + if (isset($this->constants[$name])) { + return $this->constants[$name]; + } + + throw new InvalidArgumentException(sprintf('Constant %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + */ + public function getConstant($name) + { + return $this->getConstantReflection($name); + } + + /** + * Checks if there is a constant of the given name. + * + * @param string $constantName Constant name + * @return boolean + */ + public function hasConstant($constantName) + { + if (null === $this->constants) { + $this->getConstants(); + } + + return isset($this->constants[$constantName]); + } + + /** + * Checks if there is a constant of the given name. + * + * @param string $constantName Constant name + * @return boolean + */ + public function hasOwnConstant($constantName) + { + if (null === $this->ownConstants) { + $this->getOwnConstants(); + } + + return isset($this->ownConstants[$constantName]); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + * @throws \InvalidArgumentException If required constant does not exist. + */ + public function getOwnConstantReflection($name) + { + if (null === $this->ownConstants) { + $this->getOwnConstants(); + } + + if (isset($this->ownConstants[$name])) { + return $this->ownConstants[$name]; + } + + throw new InvalidArgumentException(sprintf('Constant %s does not exist in class %s', $name, $this->reflection->getName())); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant + */ + public function getOwnConstant($name) + { + return $this->getOwnConstantReflection($name); + } + + /** + * Returns a parent class reflection encapsulated by this class. + * + * @return \ApiGen\ReflectionClass + */ + public function getParentClass() + { + if ($className = $this->reflection->getParentClassName()) { + return self::$parsedClasses[$className]; + } + return $className; + } + + /** + * Returns the parent class name. + * + * @return string|null + */ + public function getParentClassName() + { + return $this->reflection->getParentClassName(); + } + + /** + * Returns all parent classes reflections encapsulated by this class. + * + * @return array + */ + public function getParentClasses() + { + if (null === $this->parentClasses) { + $classes = self::$parsedClasses; + $this->parentClasses = array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getParentClasses()); + } + return $this->parentClasses; + } + + + /** + * Returns the parent classes names. + * + * @return array + */ + public function getParentClassNameList() + { + return $this->reflection->getParentClassNameList(); + } + + /** + * Returns if the class implements the given interface. + * + * @param string|object $interface Interface name or reflection object + * @return boolean + */ + public function implementsInterface($interface) + { + return $this->reflection->implementsInterface($interface); + } + + /** + * Returns all interface reflections encapsulated by this class. + * + * @return array + */ + public function getInterfaces() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getInterfaces()); + } + + /** + * Returns interface names. + * + * @return array + */ + public function getInterfaceNames() + { + return $this->reflection->getInterfaceNames(); + } + + /** + * Returns all interfaces implemented by the inspected class and not its parents. + * + * @return array + */ + public function getOwnInterfaces() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getOwnInterfaces()); + } + + /** + * Returns names of interfaces implemented by this class, not its parents. + * + * @return array + */ + public function getOwnInterfaceNames() + { + return $this->reflection->getOwnInterfaceNames(); + } + + /** + * Returns all traits reflections encapsulated by this class. + * + * @return array + */ + public function getTraits() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + return $classes[$class->getName()]; + }, $this->reflection->getTraits()); + } + + /** + * Returns names of used traits. + * + * @return array + */ + public function getTraitNames() + { + return $this->reflection->getTraitNames(); + } + + /** + * Returns names of traits used by this class an not its parents. + * + * @return array + */ + public function getOwnTraitNames() + { + return $this->reflection->getOwnTraitNames(); + } + + /** + * Returns method aliases from traits. + * + * @return array + */ + public function getTraitAliases() + { + return $this->reflection->getTraitAliases(); + } + + /** + * Returns all traits used by the inspected class and not its parents. + * + * @return array + */ + public function getOwnTraits() + { + $classes = self::$parsedClasses; + return array_map(function(IReflectionClass $class) use ($classes) { + if (!isset($classes[$class->getName()])) { + throw new InvalidArgumentException(sprintf('The class %s is in use but has not been found in the defined sources.', $class->getName())); + } + return $classes[$class->getName()]; + }, $this->reflection->getOwnTraits()); + } + + /** + * Returns if the class is a trait. + * + * @return boolean + */ + public function isTrait() + { + return $this->reflection->isTrait(); + } + + /** + * Returns if the class uses a particular trait. + * + * @param string $trait Trait name + * @return boolean + */ + public function usesTrait($trait) + { + return $this->reflection->usesTrait($trait); + } + + /** + * Returns reflections of direct subclasses. + * + * @return array + */ + public function getDirectSubClasses() + { + $subClasses = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($name === $class->getParentClassName()) { + $subClasses[] = $class; + } + } + return $subClasses; + } + + /** + * Returns reflections of indirect subclasses. + * + * @return array + */ + public function getIndirectSubClasses() + { + $subClasses = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($name !== $class->getParentClassName() && $class->isSubclassOf($name)) { + $subClasses[] = $class; + } + } + return $subClasses; + } + + /** + * Returns reflections of classes directly implementing this interface. + * + * @return array + */ + public function getDirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $implementers = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if (in_array($name, $class->getOwnInterfaceNames())) { + $implementers[] = $class; + } + } + return $implementers; + } + + /** + * Returns reflections of classes indirectly implementing this interface. + * + * @return array + */ + public function getIndirectImplementers() + { + if (!$this->isInterface()) { + return array(); + } + + $implementers = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($class->implementsInterface($name) && !in_array($name, $class->getOwnInterfaceNames())) { + $implementers[] = $class; + } + } + return $implementers; + } + + /** + * Returns reflections of classes directly using this trait. + * + * @return array + */ + public function getDirectUsers() + { + if (!$this->isTrait()) { + return array(); + } + + $users = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + + if (in_array($name, $class->getOwnTraitNames())) { + $users[] = $class; + } + } + return $users; + } + + /** + * Returns reflections of classes indirectly using this trait. + * + * @return array + */ + public function getIndirectUsers() + { + if (!$this->isTrait()) { + return array(); + } + + $users = array(); + $name = $this->reflection->getName(); + foreach (self::$parsedClasses as $class) { + if (!$class->isDocumented()) { + continue; + } + if ($class->usesTrait($name) && !in_array($name, $class->getOwnTraitNames())) { + $users[] = $class; + } + } + return $users; + } + + /** + * Returns an array of inherited methods from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedMethods() + { + $methods = array(); + $allMethods = array_flip(array_map(function($method) { + return $method->getName(); + }, $this->getOwnMethods())); + + foreach (array_merge($this->getParentClasses(), $this->getInterfaces()) as $class) { + $inheritedMethods = array(); + foreach ($class->getOwnMethods() as $method) { + if (!array_key_exists($method->getName(), $allMethods) && !$method->isPrivate()) { + $inheritedMethods[$method->getName()] = $method; + $allMethods[$method->getName()] = null; + } + } + + if (!empty($inheritedMethods)) { + ksort($inheritedMethods); + $methods[$class->getName()] = array_values($inheritedMethods); + } + } + + return $methods; + } + + /** + * Returns an array of inherited magic methods from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedMagicMethods() + { + $methods = array(); + $allMethods = array_flip(array_map(function($method) { + return $method->getName(); + }, $this->getOwnMagicMethods())); + + foreach (array_merge($this->getParentClasses(), $this->getInterfaces()) as $class) { + $inheritedMethods = array(); + foreach ($class->getOwnMagicMethods() as $method) { + if (!array_key_exists($method->getName(), $allMethods)) { + $inheritedMethods[$method->getName()] = $method; + $allMethods[$method->getName()] = null; + } + } + + if (!empty($inheritedMethods)) { + ksort($inheritedMethods); + $methods[$class->getName()] = array_values($inheritedMethods); + } + } + + return $methods; + } + + /** + * Returns an array of used methods from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedMethods() + { + $usedMethods = array(); + foreach ($this->getMethods() as $method) { + if (null === $method->getDeclaringTraitName() || $this->getName() === $method->getDeclaringTraitName()) { + continue; + } + + $usedMethods[$method->getDeclaringTraitName()][$method->getName()]['method'] = $method; + if (null !== $method->getOriginalName() && $method->getName() !== $method->getOriginalName()) { + $usedMethods[$method->getDeclaringTraitName()][$method->getName()]['aliases'][$method->getName()] = $method; + } + } + + // Sort + array_walk($usedMethods, function(&$methods) { + ksort($methods); + array_walk($methods, function(&$aliasedMethods) { + if (!isset($aliasedMethods['aliases'])) { + $aliasedMethods['aliases'] = array(); + } + ksort($aliasedMethods['aliases']); + }); + }); + + return $usedMethods; + } + + /** + * Returns an array of used magic methods from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedMagicMethods() + { + $usedMethods = array(); + + foreach ($this->getMagicMethods() as $method) { + if (null === $method->getDeclaringTraitName() || $this->getName() === $method->getDeclaringTraitName()) { + continue; + } + + $usedMethods[$method->getDeclaringTraitName()][$method->getName()]['method'] = $method; + } + + // Sort + array_walk($usedMethods, function(&$methods) { + ksort($methods); + array_walk($methods, function(&$aliasedMethods) { + if (!isset($aliasedMethods['aliases'])) { + $aliasedMethods['aliases'] = array(); + } + ksort($aliasedMethods['aliases']); + }); + }); + + return $usedMethods; + } + + /** + * Returns an array of inherited constants from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedConstants() + { + return array_filter( + array_map( + function(ReflectionClass $class) { + $reflections = $class->getOwnConstants(); + ksort($reflections); + return $reflections; + }, + array_merge($this->getParentClasses(), $this->getInterfaces()) + ) + ); + } + + /** + * Returns an array of inherited properties from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnProperties())); + + foreach ($this->getParentClasses() as $class) { + $inheritedProperties = array(); + foreach ($class->getOwnProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties) && !$property->isPrivate()) { + $inheritedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($inheritedProperties)) { + ksort($inheritedProperties); + $properties[$class->getName()] = array_values($inheritedProperties); + } + } + + return $properties; + } + + /** + * Returns an array of inherited magic properties from parent classes grouped by the declaring class name. + * + * @return array + */ + public function getInheritedMagicProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnMagicProperties())); + + foreach ($this->getParentClasses() as $class) { + $inheritedProperties = array(); + foreach ($class->getOwnMagicProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties)) { + $inheritedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($inheritedProperties)) { + ksort($inheritedProperties); + $properties[$class->getName()] = array_values($inheritedProperties); + } + } + + return $properties; + } + + /** + * Returns an array of used properties from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnProperties())); + + foreach ($this->getTraits() as $trait) { + $usedProperties = array(); + foreach ($trait->getOwnProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties)) { + $usedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($usedProperties)) { + ksort($usedProperties); + $properties[$trait->getName()] = array_values($usedProperties); + } + } + + return $properties; + } + + /** + * Returns an array of used magic properties from used traits grouped by the declaring trait name. + * + * @return array + */ + public function getUsedMagicProperties() + { + $properties = array(); + $allProperties = array_flip(array_map(function($property) { + return $property->getName(); + }, $this->getOwnMagicProperties())); + + foreach ($this->getTraits() as $trait) { + $usedProperties = array(); + foreach ($trait->getOwnMagicProperties() as $property) { + if (!array_key_exists($property->getName(), $allProperties)) { + $usedProperties[$property->getName()] = $property; + $allProperties[$property->getName()] = null; + } + } + + if (!empty($usedProperties)) { + ksort($usedProperties); + $properties[$trait->getName()] = array_values($usedProperties); + } + } + + return $properties; + } + + /** + * Checks if there is a property of the given name. + * + * @param string $propertyName Property name + * @return boolean + */ + public function hasProperty($propertyName) + { + if (null === $this->properties) { + $this->getProperties(); + } + + return isset($this->properties[$propertyName]); + } + + /** + * Checks if there is a property of the given name. + * + * @param string $propertyName Property name + * @return boolean + */ + public function hasOwnProperty($propertyName) + { + if (null === $this->ownProperties) { + $this->getOwnProperties(); + } + + return isset($this->ownProperties[$propertyName]); + } + + /** + * Checks if there is a property of the given name. + * + * @param string $propertyName Property name + * @return boolean + */ + public function hasTraitProperty($propertyName) + { + $properties = $this->getTraitProperties(); + return isset($properties[$propertyName]); + } + + /** + * Checks if there is a method of the given name. + * + * @param string $methodName Method name + * @return boolean + */ + public function hasMethod($methodName) + { + if (null === $this->methods) { + $this->getMethods(); + } + + return isset($this->methods[$methodName]); + } + + /** + * Checks if there is a method of the given name. + * + * @param string $methodName Method name + * @return boolean + */ + public function hasOwnMethod($methodName) + { + if (null === $this->ownMethods) { + $this->getOwnMethods(); + } + + return isset($this->ownMethods[$methodName]); + } + + /** + * Checks if there is a method of the given name. + * + * @param string $methodName Method name + * @return boolean + */ + public function hasTraitMethod($methodName) + { + $methods = $this->getTraitMethods(); + return isset($methods[$methodName]); + } + + /** + * Returns if the class is valid. + * + * @return boolean + */ + public function isValid() + { + if ($this->reflection instanceof TokenReflection\Invalid\ReflectionClass) { + return false; + } + + return true; + } + + /** + * Returns if the class should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented && parent::isDocumented()) { + $fileName = self::$generator->unPharPath($this->reflection->getFilename()); + foreach (self::$config->skipDocPath as $mask) { + if (fnmatch($mask, $fileName, FNM_NOESCAPE)) { + $this->isDocumented = false; + break; + } + } + if (true === $this->isDocumented) { + foreach (self::$config->skipDocPrefix as $prefix) { + if (0 === strpos($this->reflection->getName(), $prefix)) { + $this->isDocumented = false; + break; + } + } + } + } + + return $this->isDocumented; + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionConstant.php b/vendor/apigen/apigen/ApiGen/ReflectionConstant.php new file mode 100644 index 0000000..c7993aa --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionConstant.php @@ -0,0 +1,145 @@ +reflection->getShortName(); + } + + /** + * Returns constant type hint. + * + * @return string + */ + public function getTypeHint() + { + if ($annotations = $this->getAnnotation('var')) { + list($types) = preg_split('~\s+|$~', $annotations[0], 2); + if (!empty($types)) { + return $types; + } + } + + try { + $type = gettype($this->getValue()); + if ('null' !== strtolower($type)) { + return $type; + } + } catch (\Exception $e) { + // Nothing + } + + return 'mixed'; + } + + /** + * Returns the constant declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + $className = $this->reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the name of the declaring class. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * Returns the constant value. + * + * @return mixed + */ + public function getValue() + { + return $this->reflection->getValue(); + } + + /** + * Returns the constant value definition. + * + * @return string + */ + public function getValueDefinition() + { + return $this->reflection->getValueDefinition(); + } + + /** + * Returns if the constant is valid. + * + * @return boolean + */ + public function isValid() + { + if ($this->reflection instanceof \TokenReflection\Invalid\ReflectionConstant) { + return false; + } + + if ($class = $this->getDeclaringClass()) { + return $class->isValid(); + } + + return true; + } + + /** + * Returns if the constant should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented && parent::isDocumented() && null === $this->reflection->getDeclaringClassName()) { + $fileName = self::$generator->unPharPath($this->reflection->getFilename()); + foreach (self::$config->skipDocPath as $mask) { + if (fnmatch($mask, $fileName, FNM_NOESCAPE)) { + $this->isDocumented = false; + break; + } + } + if (true === $this->isDocumented) { + foreach (self::$config->skipDocPrefix as $prefix) { + if (0 === strpos($this->reflection->getName(), $prefix)) { + $this->isDocumented = false; + break; + } + } + } + } + + return $this->isDocumented; + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionElement.php b/vendor/apigen/apigen/ApiGen/ReflectionElement.php new file mode 100644 index 0000000..8f12485 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionElement.php @@ -0,0 +1,373 @@ +reflection->getExtension(); + return null === $extension ? null : new ReflectionExtension($extension, self::$generator); + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return $this->reflection->getExtensionName(); + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return $this->reflection->getStartPosition(); + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return $this->reflection->getEndPosition(); + } + + /** + * Returns if the element belongs to main project. + * + * @return boolean + */ + public function isMain() + { + return empty(self::$config->main) || 0 === strpos($this->getName(), self::$config->main); + } + + /** + * Returns if the element should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented) { + $this->isDocumented = $this->reflection->isTokenized() || $this->reflection->isInternal(); + + if ($this->isDocumented) { + if (!self::$config->php && $this->reflection->isInternal()) { + $this->isDocumented = false; + } elseif (!self::$config->deprecated && $this->reflection->isDeprecated()) { + $this->isDocumented = false; + } elseif (!self::$config->internal && ($internal = $this->reflection->getAnnotation('internal')) && empty($internal[0])) { + $this->isDocumented = false; + } elseif (count($this->reflection->getAnnotation('ignore')) > 0) { + $this->isDocumented = false; + } + } + } + + return $this->isDocumented; + } + + /** + * Returns if the element is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + if ($this->reflection->isDeprecated()) { + return true; + } + + if (($this instanceof ReflectionMethod || $this instanceof ReflectionProperty || $this instanceof ReflectionConstant) + && $class = $this->getDeclaringClass() + ) { + return $class->isDeprecated(); + } + + return false; + } + + /** + * Returns if the element is in package. + * + * @return boolean + */ + public function inPackage() + { + return '' !== $this->getPackageName(); + } + + /** + * Returns element package name (including subpackage name). + * + * @return string + */ + public function getPackageName() + { + static $packages = array(); + + if ($package = $this->getAnnotation('package')) { + $packageName = preg_replace('~\s+.*~s', '', $package[0]); + if (empty($packageName)) { + return ''; + } + + if ($subpackage = $this->getAnnotation('subpackage')) { + $subpackageName = preg_replace('~\s+.*~s', '', $subpackage[0]); + if (empty($subpackageName)) { + // Do nothing + } elseif (0 === strpos($subpackageName, $packageName)) { + $packageName = $subpackageName; + } else { + $packageName .= '\\' . $subpackageName; + } + } + $packageName = strtr($packageName, '._/', '\\\\\\'); + + $lowerPackageName = strtolower($packageName); + if (!isset($packages[$lowerPackageName])) { + $packages[$lowerPackageName] = $packageName; + } + + return $packages[$lowerPackageName]; + } + + return ''; + } + + /** + * Returns element package name (including subpackage name). + * + * For internal elements returns "PHP", for elements in global space returns "None". + * + * @return string + */ + public function getPseudoPackageName() + { + if ($this->isInternal()) { + return 'PHP'; + } + + return $this->getPackageName() ?: 'None'; + } + + /** + * Returns if the element is defined within a namespace. + * + * @return boolean + */ + public function inNamespace() + { + return '' !== $this->getNamespaceName(); + } + + /** + * Returns element namespace name. + * + * @return string + */ + public function getNamespaceName() + { + static $namespaces = array(); + + $namespaceName = $this->reflection->getNamespaceName(); + + if (!$namespaceName) { + return $namespaceName; + } + + $lowerNamespaceName = strtolower($namespaceName); + if (!isset($namespaces[$lowerNamespaceName])) { + $namespaces[$lowerNamespaceName] = $namespaceName; + } + + return $namespaces[$lowerNamespaceName]; + } + + /** + * Returns element namespace name. + * + * For internal elements returns "PHP", for elements in global space returns "None". + * + * @return string + */ + public function getPseudoNamespaceName() + { + return $this->isInternal() ? 'PHP' : $this->getNamespaceName() ?: 'None'; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->reflection->getNamespaceAliases(); + } + + /** + * Returns the short description. + * + * @return string + */ + public function getShortDescription() + { + $short = $this->reflection->getAnnotation(\TokenReflection\ReflectionAnnotation::SHORT_DESCRIPTION); + if (!empty($short)) { + return $short; + } + + if ($this instanceof ReflectionProperty || $this instanceof ReflectionConstant) { + $var = $this->getAnnotation('var'); + list(, $short) = preg_split('~\s+|$~', $var[0], 2); + } + + return $short; + } + + /** + * Returns the long description. + * + * @return string + */ + public function getLongDescription() + { + $short = $this->getShortDescription(); + $long = $this->reflection->getAnnotation(\TokenReflection\ReflectionAnnotation::LONG_DESCRIPTION); + + if (!empty($long)) { + $short .= "\n\n" . $long; + } + + return $short; + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + return $this->reflection->getDocComment(); + } + + /** + * Returns reflection element annotations. + * + * Removes the short and long description. + * + * In case of classes, functions and constants, @package, @subpackage, @author and @license annotations + * are added from declaring files if not already present. + * + * @return array + */ + public function getAnnotations() + { + if (null === $this->annotations) { + static $fileLevel = array('package' => true, 'subpackage' => true, 'author' => true, 'license' => true, 'copyright' => true); + + $annotations = $this->reflection->getAnnotations(); + unset($annotations[\TokenReflection\ReflectionAnnotation::SHORT_DESCRIPTION]); + unset($annotations[\TokenReflection\ReflectionAnnotation::LONG_DESCRIPTION]); + + if ($this->reflection instanceof \TokenReflection\ReflectionClass || $this->reflection instanceof \TokenReflection\ReflectionFunction || ($this->reflection instanceof \TokenReflection\ReflectionConstant && null === $this->reflection->getDeclaringClassName())) { + foreach ($this->reflection->getFileReflection()->getAnnotations() as $name => $value) { + if (isset($fileLevel[$name]) && empty($annotations[$name])) { + $annotations[$name] = $value; + } + } + } + + $this->annotations = $annotations; + } + + return $this->annotations; + } + + /** + * Returns reflection element annotation. + * + * @param string $annotation Annotation name + * @return array + */ + public function getAnnotation($annotation) + { + $annotations = $this->getAnnotations(); + return isset($annotations[$annotation]) ? $annotations[$annotation] : null; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $annotation Annotation name + * @return boolean + */ + public function hasAnnotation($annotation) + { + $annotations = $this->getAnnotations(); + return isset($annotations[$annotation]); + } + + /** + * Adds element annotation. + * + * @param string $annotation Annotation name + * @param string $value Annotation value + * @return \ApiGen\ReflectionElement + */ + public function addAnnotation($annotation, $value) + { + if (null === $this->annotations) { + $this->getAnnotations(); + } + $this->annotations[$annotation][] = $value; + + return $this; + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionExtension.php b/vendor/apigen/apigen/ApiGen/ReflectionExtension.php new file mode 100644 index 0000000..1d636bc --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionExtension.php @@ -0,0 +1,135 @@ +reflection->getClass($name); + if (null === $class) { + return null; + } + if (isset(self::$parsedClasses[$name])) { + return self::$parsedClasses[$name]; + } + return new ReflectionClass($class, self::$generator); + } + + /** + * Returns classes defined by this extension. + * + * @return array + */ + public function getClasses() + { + $generator = self::$generator; + $classes = self::$parsedClasses; + return array_map(function(TokenReflection\IReflectionClass $class) use ($generator, $classes) { + return isset($classes[$class->getName()]) ? $classes[$class->getName()] : new ReflectionClass($class, $generator); + }, $this->reflection->getClasses()); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant|null + */ + public function getConstant($name) + { + return $this->getConstantReflection($name); + } + + /** + * Returns a constant reflection. + * + * @param string $name Constant name + * @return \ApiGen\ReflectionConstant|null + */ + public function getConstantReflection($name) + { + $constant = $this->reflection->getConstantReflection($name); + return null === $constant ? null : new ReflectionConstant($constant, self::$generator); + } + + /** + * Returns reflections of defined constants. + * + * @return array + */ + public function getConstants() + { + return $this->getConstantReflections(); + } + + /** + * Returns reflections of defined constants. + * + * @return array + */ + public function getConstantReflections() + { + $generator = self::$generator; + return array_map(function(TokenReflection\IReflectionConstant $constant) use ($generator) { + return new ReflectionConstant($constant, $generator); + }, $this->reflection->getConstantReflections()); + } + + /** + * Returns a function reflection. + * + * @param string $name Function name + * @return \ApiGen\ReflectionFunction + */ + public function getFunction($name) + { + $function = $this->reflection->getFunction($name); + return null === $function ? null : new ReflectionFunction($function, self::$generator); + } + + /** + * Returns functions defined by this extension. + * + * @return array + */ + public function getFunctions() + { + $generator = self::$generator; + return array_map(function(TokenReflection\IReflectionFunction $function) use ($generator) { + return new ReflectionFunction($function, $generator); + }, $this->reflection->getFunctions()); + } + + /** + * Returns names of functions defined by this extension. + * + * @return array + */ + public function getFunctionNames() + { + return $this->reflection->getFunctionNames(); + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionFunction.php b/vendor/apigen/apigen/ApiGen/ReflectionFunction.php new file mode 100644 index 0000000..75f2e40 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionFunction.php @@ -0,0 +1,64 @@ +reflection instanceof \TokenReflection\Invalid\ReflectionFunction) { + return false; + } + + return true; + } + + /** + * Returns if the function should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented && parent::isDocumented()) { + $fileName = self::$generator->unPharPath($this->reflection->getFilename()); + foreach (self::$config->skipDocPath as $mask) { + if (fnmatch($mask, $fileName, FNM_NOESCAPE)) { + $this->isDocumented = false; + break; + } + } + if (true === $this->isDocumented) { + foreach (self::$config->skipDocPrefix as $prefix) { + if (0 === strpos($this->reflection->getName(), $prefix)) { + $this->isDocumented = false; + break; + } + } + } + } + + return $this->isDocumented; + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionFunctionBase.php b/vendor/apigen/apigen/ApiGen/ReflectionFunctionBase.php new file mode 100644 index 0000000..a86326f --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionFunctionBase.php @@ -0,0 +1,151 @@ +reflection->getShortName(); + } + + /** + * Returns if the function/method returns its value as reference. + * + * @return boolean + */ + public function returnsReference() + { + return $this->reflection->returnsReference(); + } + + /** + * Returns a list of function/method parameters. + * + * @return array + */ + public function getParameters() + { + if (null === $this->parameters) { + $generator = self::$generator; + $this->parameters = array_map(function(TokenReflection\IReflectionParameter $parameter) use ($generator) { + return new ReflectionParameter($parameter, $generator); + }, $this->reflection->getParameters()); + + $annotations = $this->getAnnotation('param'); + if (null !== $annotations) { + foreach ($annotations as $position => $annotation) { + if (isset($parameters[$position])) { + // Standard parameter + continue; + } + + if (!preg_match('~^(?:([\\w\\\\]+(?:\\|[\\w\\\\]+)*)\\s+)?\\$(\\w+),\\.{3}(?:\\s+(.*))?($)~s', $annotation, $matches)) { + // Wrong annotation format + continue; + } + + list(, $typeHint, $name) = $matches; + + if (empty($typeHint)) { + $typeHint = 'mixed'; + } + + $parameter = new ReflectionParameterMagic(null, self::$generator); + $parameter + ->setName($name) + ->setPosition($position) + ->setTypeHint($typeHint) + ->setDefaultValueDefinition(null) + ->setUnlimited(true) + ->setPassedByReference(false) + ->setDeclaringFunction($this); + + $this->parameters[$position] = $parameter; + } + } + } + + return $this->parameters; + } + + /** + * Returns a particular function/method parameter. + * + * @param integer|string $parameterName Parameter name or position + * @return \ApiGen\ReflectionParameter + * @throws \InvalidArgumentException If there is no parameter of the given name. + * @throws \InvalidArgumentException If there is no parameter at the given position. + */ + public function getParameter($parameterName) + { + $parameters = $this->getParameters(); + + if (is_numeric($parameterName)) { + if (isset($parameters[$parameterName])) { + return $parameters[$parameterName]; + } + + throw new InvalidArgumentException(sprintf('There is no parameter at position "%d" in function/method "%s"', $parameterName, $this->getName()), Exception\Runtime::DOES_NOT_EXIST); + } else { + foreach ($parameters as $parameter) { + if ($parameter->getName() === $parameterName) { + return $parameter; + } + } + + throw new InvalidArgumentException(sprintf('There is no parameter "%s" in function/method "%s"', $parameterName, $this->getName()), Exception\Runtime::DOES_NOT_EXIST); + } + } + + /** + * Returns the number of parameters. + * + * @return integer + */ + public function getNumberOfParameters() + { + return $this->reflection->getNumberOfParameters(); + } + + /** + * Returns the number of required parameters. + * + * @return integer + */ + public function getNumberOfRequiredParameters() + { + return $this->reflection->getNumberOfRequiredParameters(); + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionMethod.php b/vendor/apigen/apigen/ApiGen/ReflectionMethod.php new file mode 100644 index 0000000..007935c --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionMethod.php @@ -0,0 +1,250 @@ +reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * Returns method modifiers. + * + * @return integer + */ + public function getModifiers() + { + return $this->reflection->getModifiers(); + } + + /** + * Returns if the method is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return $this->reflection->isAbstract(); + } + + /** + * Returns if the method is final. + * + * @return boolean + */ + public function isFinal() + { + return $this->reflection->isFinal(); + } + + /** + * Returns if the method is private. + * + * @return boolean + */ + public function isPrivate() + { + return $this->reflection->isPrivate(); + } + + /** + * Returns if the method is protected. + * + * @return boolean + */ + public function isProtected() + { + return $this->reflection->isProtected(); + } + + /** + * Returns if the method is public. + * + * @return boolean + */ + public function isPublic() + { + return $this->reflection->isPublic(); + } + + /** + * Returns if the method is static. + * + * @return boolean + */ + public function isStatic() + { + return $this->reflection->isStatic(); + } + + /** + * Returns if the method is a constructor. + * + * @return boolean + */ + public function isConstructor() + { + return $this->reflection->isConstructor(); + } + + /** + * Returns if the method is a destructor. + * + * @return boolean + */ + public function isDestructor() + { + return $this->reflection->isDestructor(); + } + + /** + * Returns the method declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + $traitName = $this->reflection->getDeclaringTraitName(); + return null === $traitName ? null : self::$parsedClasses[$traitName]; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return $this->reflection->getDeclaringTraitName(); + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getImplementedMethod() + { + foreach ($this->getDeclaringClass()->getOwnInterfaces() as $interface) { + if ($interface->hasMethod($this->getName())) { + return $interface->getMethod($this->getName()); + } + } + + return null; + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOverriddenMethod() + { + $parent = $this->getDeclaringClass()->getParentClass(); + if (null === $parent) { + return null; + } + + foreach ($parent->getMethods() as $method) { + if ($this->getName() === $method->getName()) { + if (!$method->isPrivate() && !$method->isAbstract()) { + return $method; + } else { + return null; + } + } + } + + return null; + } + + /** + * Returns the original name when importing from a trait. + * + * @return string|null + */ + public function getOriginalName() + { + return $this->reflection->getOriginalName(); + } + + /** + * Returns the original modifiers value when importing from a trait. + * + * @return integer|null + */ + public function getOriginalModifiers() + { + return $this->reflection->getOriginalModifiers(); + } + + /** + * Returns the original method when importing from a trait. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOriginal() + { + $originalName = $this->reflection->getOriginalName(); + return null === $originalName ? null : self::$parsedClasses[$this->reflection->getOriginal()->getDeclaringClassName()]->getMethod($originalName); + } + + /** + * Returns if the method is valid. + * + * @return boolean + */ + public function isValid() + { + if ($class = $this->getDeclaringClass()) { + return $class->isValid(); + } + + return true; + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionMethodMagic.php b/vendor/apigen/apigen/ApiGen/ReflectionMethodMagic.php new file mode 100644 index 0000000..05b4514 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionMethodMagic.php @@ -0,0 +1,729 @@ +reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + } + + /** + * Sets method name. + * + * @param string $name + * @return \Apigen\ReflectionMethodMagic + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + + } + + /** + * Sets short description. + * + * @param string $shortDescription + * @return \Apigen\ReflectionMethodMagic + */ + public function setShortDescription($shortDescription) + { + $this->shortDescription = (string) $shortDescription; + return $this; + } + + /** + * Sets start line. + * + * @param integer $startLine + * @return \Apigen\ReflectionMethodMagic + */ + public function setStartLine($startLine) + { + $this->startLine = (int) $startLine; + return $this; + } + + /** + * Sets end line. + * + * @param integer $endLine + * @return \Apigen\ReflectionMethodMagic + */ + public function setEndLine($endLine) + { + $this->endLine = (int) $endLine; + return $this; + } + + /** + * Sets if the method returns reference. + * + * @param boolean $returnsReference + * @return \Apigen\ReflectionMethodMagic + */ + public function setReturnsReference($returnsReference) + { + $this->returnsReference = (bool) $returnsReference; + return $this; + } + + /** + * Sets parameters. + * + * @param array $parameters + * @return \Apigen\ReflectionMethodMagic + */ + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + return $this; + } + + /** + * Sets declaring class. + * + * @param \ApiGen\ReflectionClass $declaringClass + * @return \ApiGen\ReflectionMethodMagic + */ + public function setDeclaringClass(ReflectionClass $declaringClass) + { + $this->declaringClass = $declaringClass; + return $this; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->declaringClass->getBroker(); + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return $this->declaringClass->getStartPosition(); + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return $this->declaringClass->getEndPosition(); + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the short description. + * + * @return string + */ + public function getShortDescription() + { + return $this->shortDescription; + } + + /** + * Returns the long description. + * + * @return string + */ + public function getLongDescription() + { + return $this->shortDescription; + } + + /** + * 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 if the function/method returns its value as reference. + * + * @return boolean + */ + public function returnsReference() + { + return $this->returnsReference; + } + + /** + * Returns if the property is magic. + * + * @return boolean + */ + public function isMagic() + { + return true; + } + + /** + * Returns the unqualified name (UQN). + * + * @return string + */ + public function getShortName() + { + return $this->name; + } + + /** + * Returns the PHP extension reflection. + * + * @return \ApiGen\ReflectionExtension|null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns if the method should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented) { + $this->isDocumented = self::$config->deprecated || !$this->isDeprecated(); + } + + return $this->isDocumented; + } + + /** + * Returns if the property is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return $this->declaringClass->isDeprecated(); + } + + /** + * Returns property package name (including subpackage name). + * + * @return string + */ + public function getPackageName() + { + return $this->declaringClass->getPackageName(); + } + + /** + * Returns property namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return $this->declaringClass->getNamespaceName(); + } + + /** + * Returns property annotations. + * + * @return array + */ + public function getAnnotations() + { + if (null === $this->annotations) { + $this->annotations = array(); + } + return $this->annotations; + } + + /** + * Returns the method declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + return $this->declaringClass; + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringClass->getName(); + } + + /** + * Returns method modifiers. + * + * @return integer + */ + public function getModifiers() + { + return InternalReflectionMethod::IS_PUBLIC; + } + + /** + * Returns if the method is abstract. + * + * @return boolean + */ + public function isAbstract() + { + return false; + } + + /** + * Returns if the method is final. + * + * @return boolean + */ + public function isFinal() + { + return false; + } + + /** + * Returns if the method is private. + * + * @return boolean + */ + public function isPrivate() + { + return false; + } + + /** + * Returns if the method is protected. + * + * @return boolean + */ + public function isProtected() + { + return false; + } + + /** + * Returns if the method is public. + * + * @return boolean + */ + public function isPublic() + { + return true; + } + + /** + * Returns if the method is static. + * + * @return boolean + */ + public function isStatic() + { + return false; + } + + /** + * Returns if the property is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns if the method is a constructor. + * + * @return boolean + */ + public function isConstructor() + { + return false; + } + + /** + * Returns if the method is a destructor. + * + * @return boolean + */ + public function isDestructor() + { + return false; + } + + /** + * Returns the method declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + return $this->declaringClass->isTrait() ? $this->declaringClass : null; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + if ($declaringTrait = $this->getDeclaringTrait()) { + return $declaringTrait->getName(); + } + return null; + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getImplementedMethod() + { + return null; + } + + /** + * Returns the overridden method. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOverriddenMethod() + { + $parent = $this->declaringClass->getParentClass(); + if (null === $parent) { + return null; + } + + foreach ($parent->getMagicMethods() as $method) { + if ($this->name === $method->getName()) { + return $method; + } + } + + return null; + } + + /** + * Returns the original name when importing from a trait. + * + * @return string|null + */ + public function getOriginalName() + { + return $this->getName(); + } + + /** + * Returns the original modifiers value when importing from a trait. + * + * @return integer|null + */ + public function getOriginalModifiers() + { + return $this->getModifiers(); + } + + /** + * Returns the original method when importing from a trait. + * + * @return \ApiGen\ReflectionMethod|null + */ + public function getOriginal() + { + return null; + } + + /** + * Returns a list of method 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 imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->declaringClass->getNamespaceAliases(); + } + + /** + * Returns an property pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::%s()', $this->declaringClass->getName(), $this->name); + } + + /** + * Returns the file name the method is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->declaringClass->getFileName(); + } + + /** + * Returns if the method is user defined. + + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the method comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + $docComment = "/**\n"; + + if (!empty($this->shortDescription)) { + $docComment .= $this->shortDescription . "\n\n"; + } + + if ($annotations = $this->getAnnotation('param')) { + foreach ($annotations as $annotation) { + $docComment .= sprintf("@param %s\n", $annotation); + } + } + + if ($annotations = $this->getAnnotation('return')) { + foreach ($annotations as $annotation) { + $docComment .= sprintf("@return %s\n", $annotation); + } + } + + $docComment .= "*/\n"; + + return $docComment; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + $annotations = $this->getAnnotations(); + return array_key_exists($name, $annotations); + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return string|array|null + */ + public function getAnnotation($name) + { + $annotations = $this->getAnnotations(); + if (array_key_exists($name, $annotations)) { + return $annotations[$name]; + } + return null; + } + + /** + * Retrieves a property or method value. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return null; + } + + /** + * Checks if the given property exists. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]); + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionParameter.php b/vendor/apigen/apigen/ApiGen/ReflectionParameter.php new file mode 100644 index 0000000..92fefbd --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionParameter.php @@ -0,0 +1,231 @@ +isArray()) { + return 'array'; + } elseif ($this->isCallable()) { + return 'callable'; + } elseif ($className = $this->getClassName()) { + return $className; + } elseif ($annotations = $this->getDeclaringFunction()->getAnnotation('param')) { + if (!empty($annotations[$this->getPosition()])) { + list($types) = preg_split('~\s+|$~', $annotations[$this->getPosition()], 2); + if (!empty($types) && '$' !== $types[0]) { + return $types; + } + } + } + + return 'mixed'; + } + + /** + * Returns parameter description. + * + * @return string + */ + public function getDescription() + { + $annotations = $this->getDeclaringFunction()->getAnnotation('param'); + if (empty($annotations[$this->getPosition()])) { + return ''; + } + + $description = trim(strpbrk($annotations[$this->getPosition()], "\n\r\t ")); + return preg_replace('~^(\\$' . $this->getName() . '(?:,\\.{3})?)(\\s+|$)~i', '\\2', $description, 1); + } + + /** + * Returns the part of the source code defining the parameter default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return $this->reflection->getDefaultValueDefinition(); + } + + /** + * Retutns if a default value for the parameter is available. + * + * @return boolean + */ + public function isDefaultValueAvailable() + { + return $this->reflection->isDefaultValueAvailable(); + } + + /** + * Returns the position within all parameters. + * + * @return integer + */ + public function getPosition() + { + return $this->reflection->position; + } + + /** + * Returns if the parameter expects an array. + * + * @return boolean + */ + public function isArray() + { + return $this->reflection->isArray(); + } + + /** + * Returns if the parameter expects a callback. + * + * @return boolean + */ + public function isCallable() + { + return $this->reflection->isCallable(); + } + + /** + * Returns reflection of the required class of the parameter. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getClass() + { + $className = $this->reflection->getClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the required class name of the value. + * + * @return string|null + */ + public function getClassName() + { + return $this->reflection->getClassName(); + } + + /** + * Returns if the the parameter allows NULL. + * + * @return boolean + */ + public function allowsNull() + { + return $this->reflection->allowsNull(); + } + + /** + * Returns if the parameter is optional. + * + * @return boolean + */ + public function isOptional() + { + return $this->reflection->isOptional(); + } + + /** + * Returns if the parameter value is passed by reference. + * + * @return boolean + */ + public function isPassedByReference() + { + return $this->reflection->isPassedByReference(); + } + + /** + * Returns if the paramter value can be passed by value. + * + * @return boolean + */ + public function canBePassedByValue() + { + return $this->reflection->canBePassedByValue(); + } + + /** + * Returns the declaring function. + * + * @return \ApiGen\ReflectionFunctionBase + */ + public function getDeclaringFunction() + { + $functionName = $this->reflection->getDeclaringFunctionName(); + + if ($className = $this->reflection->getDeclaringClassName()) { + return self::$parsedClasses[$className]->getMethod($functionName); + } else { + return self::$parsedFunctions[$functionName]; + } + } + + /** + * Returns the declaring function name. + * + * @return string + */ + public function getDeclaringFunctionName() + { + return $this->reflection->getDeclaringFunctionName(); + } + + /** + * Returns the function/method declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + $className = $this->reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * If the parameter can be used unlimited. + * + * @return boolean + */ + public function isUnlimited() + { + return false; + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionParameterMagic.php b/vendor/apigen/apigen/ApiGen/ReflectionParameterMagic.php new file mode 100644 index 0000000..1482928 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionParameterMagic.php @@ -0,0 +1,479 @@ +reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + } + + /** + * Sets parameter name. + * + * @param string $name + * @return \ApiGen\ReflectionParameterMagic + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + } + + /** + * Sets type hint. + * + * @param string $typeHint + * @return \ApiGen\ReflectionParameterMagic + */ + public function setTypeHint($typeHint) + { + $this->typeHint = (string) $typeHint; + return $this; + } + + /** + * Sets position of the parameter in the function/method. + * + * @param integer $position + * @return \ApiGen\ReflectionParameterMagic + */ + public function setPosition($position) + { + $this->position = (int) $position; + return $this; + } + + /** + * Sets the part of the source code defining the parameter default value. + * + * @param string|null $defaultValueDefinition + * @return \ApiGen\ReflectionParameterMagic + */ + public function setDefaultValueDefinition($defaultValueDefinition) + { + $this->defaultValueDefinition = $defaultValueDefinition; + return $this; + } + + /** + * Sets if the parameter can be used unlimited times. + * + * @param boolean $unlimited + * @return \ApiGen\ReflectionParameterMagic + */ + public function setUnlimited($unlimited) + { + $this->unlimited = (bool) $unlimited; + return $this; + } + + /** + * Sets if the parameter value is passed by reference. + * + * @param boolean $passedByReference + * @return \ApiGen\ReflectionParameterMagic + */ + public function setPassedByReference($passedByReference) + { + $this->passedByReference = (bool) $passedByReference; + return $this; + } + + /** + * Sets declaring function. + * + * @param \ApiGen\ReflectionFunctionBase $declaringFunction + * @return \ApiGen\ReflectionParameterMagic + */ + public function setDeclaringFunction(ReflectionFunctionBase $declaringFunction) + { + $this->declaringFunction = $declaringFunction; + return $this; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->declaringFunction->getBroker(); + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the type hint. + * + * @return string + */ + public function getTypeHint() + { + return $this->typeHint; + } + + /** + * Returns the file name the parameter is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->declaringFunction->getFileName(); + } + + /** + * 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 an element pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return str_replace('()', '($' . $this->name . ')', $this->declaringFunction->getPrettyName()); + } + + /** + * Returns the declaring class. + * + * @return \Apigen\ReflectionClass|null + */ + public function getDeclaringClass() + { + return $this->declaringFunction->getDeclaringClass(); + } + + /** + * Returns the declaring class name. + * + * @return string|null + */ + public function getDeclaringClassName() + { + return $this->declaringFunction->getDeclaringClassName(); + } + + /** + * Returns the declaring function. + * + * @return \ApiGen\ReflectionFunctionBase + */ + public function getDeclaringFunction() + { + return $this->declaringFunction; + } + + /** + * Returns the declaring function name. + * + * @return string + */ + public function getDeclaringFunctionName() + { + return $this->declaringFunction->getName(); + } + + /** + * Returns the definition start line number in the file. + * + * @return integer + */ + public function getStartLine() + { + return $this->declaringFunction->getStartLine(); + } + + /** + * Returns the definition end line number in the file. + * + * @return integer + */ + public function getEndLine() + { + return $this->declaringFunction->getEndLine(); + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + return false; + } + + /** + * Returns the part of the source code defining the parameter default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return $this->defaultValueDefinition; + } + + /** + * Returns if a default value for the parameter is available. + * + * @return boolean + */ + public function isDefaultValueAvailable() + { + return (bool) $this->defaultValueDefinition; + } + + /** + * 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 TokenReflection\ReflectionParameter::ARRAY_TYPE_HINT === $this->typeHint; + } + + public function isCallable() + { + return TokenReflection\ReflectionParameter::CALLABLE_TYPE_HINT === $this->typeHint; + } + + /** + * Returns reflection of the required class of the value. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getClass() + { + $className = $this->getClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the required class name of the value. + * + * @return string|null + */ + public function getClassName() + { + if ($this->isArray() || $this->isCallable()) { + return null; + } + + if (isset(self::$parsedClasses[$this->typeHint])) { + return $typeHint; + } + + return null; + } + + /** + * Returns if the the parameter allows NULL. + * + * @return boolean + */ + public function allowsNull() + { + if ($this->isArray() || $this->isCallable()) { + return 'null' === strtolower($this->defaultValueDefinition); + } + + return !empty($this->defaultValueDefinition); + } + + /** + * Returns if the parameter is optional. + * + * @return boolean + */ + public function isOptional() + { + return $this->isDefaultValueAvailable(); + } + + /** + * Returns if the parameter value is passed by reference. + * + * @return boolean + */ + public function isPassedByReference() + { + return $this->passedByReference; + } + + /** + * Returns if the parameter value can be passed by value. + * + * @return boolean + */ + public function canBePassedByValue() + { + return false; + } + + /** + * Returns if the parameter can be used unlimited times. + * + * @return boolean + */ + public function isUnlimited() + { + return $this->unlimited; + } + + /** + * Retrieves a property or method value. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return null; + } + + /** + * Checks if the given property exists. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]); + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionProperty.php b/vendor/apigen/apigen/ApiGen/ReflectionProperty.php new file mode 100644 index 0000000..414cb1c --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionProperty.php @@ -0,0 +1,214 @@ +getAnnotation('var')) { + list($types) = preg_split('~\s+|$~', $annotations[0], 2); + if (!empty($types) && '$' !== $types[0]) { + return $types; + } + } + + try { + $type = gettype($this->getDefaultValue()); + if ('null' !== strtolower($type)) { + return $type; + } + } catch (\Exception $e) { + // Nothing + } + + return 'mixed'; + } + + /** + * Returns the property declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + $className = $this->reflection->getDeclaringClassName(); + return null === $className ? null : self::$parsedClasses[$className]; + } + + /** + * Returns the name of the declaring class. + * + * @return string + */ + public function getDeclaringClassName() + { + return $this->reflection->getDeclaringClassName(); + } + + /** + * Returns the property default value. + * + * @return mixed + */ + public function getDefaultValue() + { + return $this->reflection->getDefaultValue(); + } + + /** + * Returns the part of the source code defining the property default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return $this->reflection->getDefaultValueDefinition(); + } + + /** + * Returns if the property was created at compile time. + * + * @return boolean + */ + public function isDefault() + { + return $this->reflection->isDefault(); + } + + /** + * Returns property modifiers. + * + * @return integer + */ + public function getModifiers() + { + return $this->reflection->getModifiers(); + } + + /** + * Returns if the property is private. + * + * @return boolean + */ + public function isPrivate() + { + return $this->reflection->isPrivate(); + } + + /** + * Returns if the property is protected. + * + * @return boolean + */ + public function isProtected() + { + return $this->reflection->isProtected(); + } + + /** + * Returns if the property is public. + * + * @return boolean + */ + public function isPublic() + { + return $this->reflection->isPublic(); + } + + /** + * Returns if the poperty is static. + * + * @return boolean + */ + public function isStatic() + { + return $this->reflection->isStatic(); + } + + /** + * Returns the property declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + $traitName = $this->reflection->getDeclaringTraitName(); + return null === $traitName ? null : self::$parsedClasses[$traitName]; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + return $this->reflection->getDeclaringTraitName(); + } + + /** + * Returns if the property is valid. + * + * @return boolean + */ + public function isValid() + { + if ($class = $this->getDeclaringClass()) { + return $class->isValid(); + } + + return true; + } +} diff --git a/vendor/apigen/apigen/ApiGen/ReflectionPropertyMagic.php b/vendor/apigen/apigen/ApiGen/ReflectionPropertyMagic.php new file mode 100644 index 0000000..98e7e24 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/ReflectionPropertyMagic.php @@ -0,0 +1,651 @@ +reflectionType = get_class($this); + if (!isset(self::$reflectionMethods[$this->reflectionType])) { + self::$reflectionMethods[$this->reflectionType] = array_flip(get_class_methods($this)); + } + } + + /** + * Sets property name. + * + * @param string $name + * @return \Apigen\ReflectionPropertyMagic + */ + public function setName($name) + { + $this->name = (string) $name; + return $this; + + } + + /** + * Sets type hint. + * + * @param string $typeHint + * @return \ApiGen\ReflectionParameterUnlimited + */ + public function setTypeHint($typeHint) + { + $this->typeHint = (string) $typeHint; + return $this; + } + + /** + * Sets short description. + * + * @param string $shortDescription + * @return \Apigen\ReflectionPropertyMagic + */ + public function setShortDescription($shortDescription) + { + $this->shortDescription = (string) $shortDescription; + return $this; + } + + /** + * Sets start line. + * + * @param integer $startLine + * @return \Apigen\ReflectionPropertyMagic + */ + public function setStartLine($startLine) + { + $this->startLine = (int) $startLine; + return $this; + } + + /** + * Sets end line. + * + * @param integer $endLine + * @return \Apigen\ReflectionPropertyMagic + */ + public function setEndLine($endLine) + { + $this->endLine = (int) $endLine; + return $this; + } + + /** + * Sets if the property is read-only. + * + * @param boolean $readOnly + * @return \Apigen\ReflectionPropertyMagic + */ + public function setReadOnly($readOnly) + { + $this->readOnly = (bool) $readOnly; + return $this; + } + + /** + * Sets if the property is write only. + * + * @param boolean $writeOnly + * @return \Apigen\ReflectionPropertyMagic + */ + public function setWriteOnly($writeOnly) + { + $this->writeOnly = (bool) $writeOnly; + return $this; + } + + /** + * Sets declaring class. + * + * @param \ApiGen\ReflectionClass $declaringClass + * @return \ApiGen\ReflectionPropertyMagic + */ + public function setDeclaringClass(ReflectionClass $declaringClass) + { + $this->declaringClass = $declaringClass; + return $this; + } + + /** + * Returns the reflection broker used by this reflection object. + * + * @return \TokenReflection\Broker + */ + public function getBroker() + { + return $this->declaringClass->getBroker(); + } + + /** + * Returns the start position in the file token stream. + * + * @return integer + */ + public function getStartPosition() + { + return $this->declaringClass->getStartPosition(); + } + + /** + * Returns the end position in the file token stream. + * + * @return integer + */ + public function getEndPosition() + { + return $this->declaringClass->getEndPosition(); + } + + /** + * Returns the name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Returns the type hint. + * + * @return string + */ + public function getTypeHint() + { + return $this->typeHint; + } + + /** + * Returns the short description. + * + * @return string + */ + public function getShortDescription() + { + return $this->shortDescription; + } + + /** + * Returns the long description. + * + * @return string + */ + public function getLongDescription() + { + return $this->shortDescription; + } + + /** + * 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 if the property is read-only. + * + * @return boolean + */ + public function isReadOnly() + { + return $this->readOnly; + } + + /** + * Returns if the property is write-only. + * + * @return boolean + */ + public function isWriteOnly() + { + return $this->writeOnly; + } + + /** + * Returns if the property is magic. + * + * @return boolean + */ + public function isMagic() + { + return true; + } + + /** + * Returns the PHP extension reflection. + * + * @return \ApiGen\ReflectionExtension|null + */ + public function getExtension() + { + return null; + } + + /** + * Returns the PHP extension name. + * + * @return boolean + */ + public function getExtensionName() + { + return false; + } + + /** + * Returns if the property should be documented. + * + * @return boolean + */ + public function isDocumented() + { + if (null === $this->isDocumented) { + $this->isDocumented = self::$config->deprecated || !$this->isDeprecated(); + } + + return $this->isDocumented; + } + + /** + * Returns if the property is deprecated. + * + * @return boolean + */ + public function isDeprecated() + { + return $this->declaringClass->isDeprecated(); + } + + /** + * Returns property package name (including subpackage name). + * + * @return string + */ + public function getPackageName() + { + return $this->declaringClass->getPackageName(); + } + + /** + * Returns property namespace name. + * + * @return string + */ + public function getNamespaceName() + { + return $this->declaringClass->getNamespaceName(); + } + + /** + * Returns property annotations. + * + * @return array + */ + public function getAnnotations() + { + if (null === $this->annotations) { + $this->annotations = array(); + } + return $this->annotations; + } + + /** + * Returns the property declaring class. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringClass() + { + return $this->declaringClass; + } + + /** + * Returns the name of the declaring class. + * + * @return string + */ + public function getDeclaringClassName() + { + return $this->declaringClass->getName(); + } + + /** + * Returns the property default value. + * + * @return mixed + */ + public function getDefaultValue() + { + return null; + } + + /** + * Returns the part of the source code defining the property default value. + * + * @return string + */ + public function getDefaultValueDefinition() + { + return ''; + } + + /** + * Returns if the property was created at compile time. + * + * @return boolean + */ + public function isDefault() + { + return false; + } + + /** + * Returns property modifiers. + * + * @return integer + */ + public function getModifiers() + { + return InternalReflectionProperty::IS_PUBLIC; + } + + /** + * Returns if the property is private. + * + * @return boolean + */ + public function isPrivate() + { + return false; + } + + /** + * Returns if the property is protected. + * + * @return boolean + */ + public function isProtected() + { + return false; + } + + /** + * Returns if the property is public. + * + * @return boolean + */ + public function isPublic() + { + return true; + } + + /** + * Returns if the poperty is static. + * + * @return boolean + */ + public function isStatic() + { + return false; + } + + /** + * Returns if the property is internal. + * + * @return boolean + */ + public function isInternal() + { + return false; + } + + /** + * Returns the property declaring trait. + * + * @return \ApiGen\ReflectionClass|null + */ + public function getDeclaringTrait() + { + return $this->declaringClass->isTrait() ? $this->declaringClass : null; + } + + /** + * Returns the declaring trait name. + * + * @return string|null + */ + public function getDeclaringTraitName() + { + if ($declaringTrait = $this->getDeclaringTrait()) { + return $declaringTrait->getName(); + } + return null; + } + + /** + * Returns imported namespaces and aliases from the declaring namespace. + * + * @return array + */ + public function getNamespaceAliases() + { + return $this->declaringClass->getNamespaceAliases(); + } + + /** + * Returns an property pretty (docblock compatible) name. + * + * @return string + */ + public function getPrettyName() + { + return sprintf('%s::$%s', $this->declaringClass->getName(), $this->name); + } + + /** + * Returns the file name the property is defined in. + * + * @return string + */ + public function getFileName() + { + return $this->declaringClass->getFileName(); + } + + /** + * Returns if the property is user defined. + + * @return boolean + */ + public function isUserDefined() + { + return true; + } + + /** + * Returns if the property comes from a tokenized source. + * + * @return boolean + */ + public function isTokenized() + { + return true; + } + + /** + * Returns the appropriate docblock definition. + * + * @return string|boolean + */ + public function getDocComment() + { + $docComment = "/**\n"; + + if (!empty($this->shortDescription)) { + $docComment .= $this->shortDescription . "\n\n"; + } + + if ($annotations = $this->getAnnotation('var')) { + $docComment .= sprintf("@var %s\n", $annotations[0]); + } + + $docComment .= "*/\n"; + + return $docComment; + } + + /** + * Checks if there is a particular annotation. + * + * @param string $name Annotation name + * @return boolean + */ + public function hasAnnotation($name) + { + $annotations = $this->getAnnotations(); + return array_key_exists($name, $annotations); + } + + /** + * Returns a particular annotation value. + * + * @param string $name Annotation name + * @return string|array|null + */ + public function getAnnotation($name) + { + $annotations = $this->getAnnotations(); + if (array_key_exists($name, $annotations)) { + return $annotations[$name]; + } + return null; + } + + /** + * Retrieves a property or method value. + * + * @param string $name Property name + * @return mixed + */ + public function __get($name) + { + $key = ucfirst($name); + if (isset(self::$reflectionMethods[$this->reflectionType]['get' . $key])) { + return $this->{'get' . $key}(); + } + + if (isset(self::$reflectionMethods[$this->reflectionType]['is' . $key])) { + return $this->{'is' . $key}(); + } + + return null; + } + + /** + * Checks if the given property exists. + * + * @param mixed $name Property name + * @return boolean + */ + public function __isset($name) + { + $key = ucfirst($name); + return isset(self::$reflectionMethods[$this->reflectionType]['get' . $key]) || isset(self::$reflectionMethods[$this->reflectionType]['is' . $key]); + } +} diff --git a/vendor/apigen/apigen/ApiGen/SourceFilesFilterIterator.php b/vendor/apigen/apigen/ApiGen/SourceFilesFilterIterator.php new file mode 100644 index 0000000..dc84108 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/SourceFilesFilterIterator.php @@ -0,0 +1,75 @@ +excludeMasks = $excludeMasks; + } + + /** + * Returns if the current file/directory should be processed. + * + * @return boolean + */ + public function accept() { + /** @var \SplFileInfo */ + $current = $this->current(); + + foreach ($this->excludeMasks as $mask) { + if (fnmatch($mask, $current->getPathName(), FNM_NOESCAPE)) { + return false; + } + } + + if (!is_readable($current->getPathname())) { + throw new \InvalidArgumentException(sprintf('File/directory "%s" is not readable.', $current->getPathname())); + } + + return true; + } + + /** + * Returns the iterator of the current element's children. + * + * @return \ApiGen\SourceFilesFilterIterator + */ + public function getChildren() + { + return new static($this->getInnerIterator()->getChildren(), $this->excludeMasks); + } +} diff --git a/vendor/apigen/apigen/ApiGen/Template.php b/vendor/apigen/apigen/ApiGen/Template.php new file mode 100644 index 0000000..8bf4172 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/Template.php @@ -0,0 +1,804 @@ +generator = $generator; + $this->config = $generator->getConfig(); + + $that = $this; + + // Output in HTML5 + Nette\Utils\Html::$xhtml = false; + + // FSHL + $fshl = new FSHL\Highlighter(new FSHL\Output\Html()); + $fshl->setLexer(new FSHL\Lexer\Php()); + + // Texy + $this->texy = new \Texy(); + $this->texy->allowedTags = array_flip($this->config->allowedHtml); + $this->texy->allowed['list/definition'] = false; + $this->texy->allowed['phrase/em-alt'] = false; + $this->texy->allowed['longwords'] = false; + $this->texy->allowed['typography'] = false; + $this->texy->linkModule->shorten = false; + // Highlighting ,
+		$this->texy->addHandler('beforeParse', function($texy, &$text, $singleLine) {
+			$text = preg_replace('~(.+?)~', '#code#\\1#/code#', $text);
+		});
+		$this->texy->registerLinePattern(
+			function($parser, $matches, $name) use ($fshl) {
+				$content = $parser->getTexy()->protect($fshl->highlight($matches[1]), \Texy::CONTENT_MARKUP);
+				return \TexyHtml::el('code', $content);
+			},
+			'~#code#(.+?)#/code#~',
+			'codeInlineSyntax'
+		);
+		$this->texy->registerBlockPattern(
+			function($parser, $matches, $name) use ($fshl) {
+				if ('code' === $matches[1]) {
+					$lines = array_filter(explode("\n", $matches[2]));
+					if (!empty($lines)) {
+						$firstLine = array_shift($lines);
+
+						$indent = '';
+						$li = 0;
+
+						while (isset($firstLine[$li]) && preg_match('~\s~', $firstLine[$li])) {
+							foreach ($lines as $line) {
+								if (!isset($line[$li]) || $firstLine[$li] !== $line[$li]) {
+									break 2;
+								}
+							}
+
+							$indent .= $firstLine[$li++];
+						}
+
+						if (!empty($indent)) {
+							$matches[2] = str_replace(
+								"\n" . $indent,
+								"\n",
+								0 === strpos($matches[2], $indent) ? substr($matches[2], $li) : $matches[2]
+							);
+						}
+					}
+
+					$content = $fshl->highlight($matches[2]);
+				} else {
+					$content = htmlspecialchars($matches[2]);
+				}
+
+				$content = $parser->getTexy()->protect($content, \Texy::CONTENT_BLOCK);
+				return \TexyHtml::el('pre', $content);
+			},
+			'~<(code|pre)>(.+?)~s',
+			'codeBlockSyntax'
+		);
+
+		// Common operations
+		$this->registerHelperLoader('Nette\Templating\Helpers::loader');
+
+		// PHP source highlight
+		$this->registerHelper('highlightPHP', function($source, $context) use ($that, $fshl) {
+			return $that->resolveLink($that->getTypeName($source), $context) ?: $fshl->highlight((string) $source);
+		});
+		$this->registerHelper('highlightValue', function($definition, $context) use ($that) {
+			return $that->highlightPHP(preg_replace('~^(?:[ ]{4}|\t)~m', '', $definition), $context);
+		});
+
+		// Urls
+		$this->registerHelper('packageUrl', new Nette\Callback($this, 'getPackageUrl'));
+		$this->registerHelper('namespaceUrl', new Nette\Callback($this, 'getNamespaceUrl'));
+		$this->registerHelper('groupUrl', new Nette\Callback($this, 'getGroupUrl'));
+		$this->registerHelper('classUrl', new Nette\Callback($this, 'getClassUrl'));
+		$this->registerHelper('methodUrl', new Nette\Callback($this, 'getMethodUrl'));
+		$this->registerHelper('propertyUrl', new Nette\Callback($this, 'getPropertyUrl'));
+		$this->registerHelper('constantUrl', new Nette\Callback($this, 'getConstantUrl'));
+		$this->registerHelper('functionUrl', new Nette\Callback($this, 'getFunctionUrl'));
+		$this->registerHelper('elementUrl', new Nette\Callback($this, 'getElementUrl'));
+		$this->registerHelper('sourceUrl', new Nette\Callback($this, 'getSourceUrl'));
+		$this->registerHelper('manualUrl', new Nette\Callback($this, 'getManualUrl'));
+
+		// Packages & namespaces
+		$this->registerHelper('packageLinks', new Nette\Callback($this, 'getPackageLinks'));
+		$this->registerHelper('namespaceLinks', new Nette\Callback($this, 'getNamespaceLinks'));
+		$this->registerHelper('subgroupName', function($groupName) {
+			if ($pos = strrpos($groupName, '\\')) {
+				return substr($groupName, $pos + 1);
+			}
+			return $groupName;
+		});
+
+		// Types
+		$this->registerHelper('typeLinks', new Nette\Callback($this, 'getTypeLinks'));
+
+		// Docblock descriptions
+		$this->registerHelper('description', function($annotation, $context) use ($that) {
+			$description = trim(strpbrk($annotation, "\n\r\t $")) ?: $annotation;
+			return $that->doc($description, $context);
+		});
+		$this->registerHelper('shortDescription', function($element, $block = false) use ($that) {
+			return $that->doc($element->getShortDescription(), $element, $block);
+		});
+		$this->registerHelper('longDescription', function($element) use ($that) {
+			$long = $element->getLongDescription();
+
+			// Merge lines
+			$long = preg_replace_callback('~(?:<(code|pre)>.+?)|([^<]*)~s', function($matches) {
+				return !empty($matches[2])
+					? preg_replace('~\n(?:\t|[ ])+~', ' ', $matches[2])
+					: $matches[0];
+			}, $long);
+
+			return $that->doc($long, $element, true);
+		});
+
+		// Individual annotations processing
+		$this->registerHelper('annotation', function($value, $name, ReflectionElement $context) use ($that, $generator) {
+			switch ($name) {
+				case 'return':
+				case 'throws':
+					$description = $that->description($value, $context);
+					return sprintf('%s%s', $that->getTypeLinks($value, $context), $description ? '
' . $description : ''); + case 'license': + list($url, $description) = $that->split($value); + return $that->link($url, $description ?: $url); + case 'link': + list($url, $description) = $that->split($value); + if (Nette\Utils\Validators::isUrl($url)) { + return $that->link($url, $description ?: $url); + } + break; + case 'see': + $doc = array(); + foreach (preg_split('~\\s*,\\s*~', $value) as $link) { + if (null !== $generator->resolveElement($link, $context)) { + $doc[] = sprintf('%s', $that->getTypeLinks($link, $context)); + } else { + $doc[] = $that->doc($link, $context); + } + } + return implode(', ', $doc); + case 'uses': + case 'usedby': + list($link, $description) = $that->split($value); + $separator = $context instanceof ReflectionClass || !$description ? ' ' : '
'; + if (null !== $generator->resolveElement($link, $context)) { + return sprintf('%s%s%s', $that->getTypeLinks($link, $context), $separator, $description); + } + break; + default: + break; + } + + // Default + return $that->doc($value, $context); + }); + + $todo = $this->config->todo; + $internal = $this->config->internal; + $this->registerHelper('annotationFilter', function(array $annotations, array $filter = array()) use ($todo, $internal) { + // Filtered, unsupported or deprecated annotations + static $filtered = array( + 'package', 'subpackage', 'property', 'property-read', 'property-write', 'method', 'abstract', + 'access', 'final', 'filesource', 'global', 'name', 'static', 'staticvar' + ); + foreach ($filtered as $annotation) { + unset($annotations[$annotation]); + } + + // Custom filter + foreach ($filter as $annotation) { + unset($annotations[$annotation]); + } + + // Show/hide internal + if (!$internal) { + unset($annotations['internal']); + } + + // Show/hide tasks + if (!$todo) { + unset($annotations['todo']); + } + + return $annotations; + }); + + $this->registerHelper('annotationSort', function(array $annotations) { + uksort($annotations, function($one, $two) { + static $order = array( + 'deprecated' => 0, 'category' => 1, 'copyright' => 2, 'license' => 3, 'author' => 4, 'version' => 5, + 'since' => 6, 'see' => 7, 'uses' => 8, 'usedby' => 9, 'link' => 10, 'internal' => 11, + 'example' => 12, 'tutorial' => 13, 'todo' => 14 + ); + + if (isset($order[$one], $order[$two])) { + return $order[$one] - $order[$two]; + } elseif (isset($order[$one])) { + return -1; + } elseif (isset($order[$two])) { + return 1; + } else { + return strcasecmp($one, $two); + } + }); + return $annotations; + }); + + $this->registerHelper('annotationBeautify', function($annotation) { + static $names = array( + 'usedby' => 'Used by' + ); + + if (isset($names[$annotation])) { + return $names[$annotation]; + } + + return Nette\Utils\Strings::firstUpper($annotation); + }); + + // Static files versioning + $destination = $this->config->destination; + $this->registerHelper('staticFile', function($name) use ($destination) { + static $versions = array(); + + $filename = $destination . DIRECTORY_SEPARATOR . $name; + if (!isset($versions[$filename]) && is_file($filename)) { + $versions[$filename] = sprintf('%u', crc32(file_get_contents($filename))); + } + if (isset($versions[$filename])) { + $name .= '?' . $versions[$filename]; + } + return $name; + }); + + // Source anchors + $this->registerHelper('sourceAnchors', function($source) { + // Classes, interfaces, traits and exceptions + $source = preg_replace_callback('~((?:class|interface|trait)\\s+)(\\w+)~i', function($matches) { + $link = sprintf('%1$s', $matches[2]); + return $matches[1] . $link; + }, $source); + + // Methods and functions + $source = preg_replace_callback('~(function\\s+)(\\w+)~i', function($matches) { + $link = sprintf('%1$s', $matches[2]); + return $matches[1] . $link; + }, $source); + + // Constants + $source = preg_replace_callback('~(const)(.*?)(;)~is', function($matches) { + $links = preg_replace_callback('~(\\s|,)([A-Z_]+)(\\s+=)~', function($matches) { + return $matches[1] . sprintf('%1$s', $matches[2]) . $matches[3]; + }, $matches[2]); + return $matches[1] . $links . $matches[3]; + }, $source); + + // Properties + $source = preg_replace_callback('~((?:private|protected|public|var|static)\\s+)(.*?)(;)~is', function($matches) { + $links = preg_replace_callback('~()(\\$\\w+)~i', function($matches) { + return $matches[1] . sprintf('%1$s', $matches[2]); + }, $matches[2]); + return $matches[1] . $links . $matches[3]; + }, $source); + + return $source; + }); + + $this->registerHelper('urlize', array($this, 'urlize')); + + $this->registerHelper('relativePath', array($generator, 'getRelativePath')); + $this->registerHelper('resolveElement', array($generator, 'resolveElement')); + $this->registerHelper('getClass', array($generator, 'getClass')); + } + + /** + * Returns unified type value definition (class name or member data type). + * + * @param string $name + * @param boolean $trimNamespaceSeparator + * @return string + */ + public function getTypeName($name, $trimNamespaceSeparator = true) + { + static $names = array( + 'int' => 'integer', + 'bool' => 'boolean', + 'double' => 'float', + 'void' => '', + 'FALSE' => 'false', + 'TRUE' => 'true', + 'NULL' => 'null', + 'callback' => 'callable' + ); + + // Simple type + if (isset($names[$name])) { + return $names[$name]; + } + + // Class, constant or function + return $trimNamespaceSeparator ? ltrim($name, '\\') : $name; + } + + /** + * Returns links for types. + * + * @param string $annotation + * @param \ApiGen\ReflectionElement $context + * @return string + */ + public function getTypeLinks($annotation, ReflectionElement $context) + { + $links = array(); + + list($types) = $this->split($annotation); + if (!empty($types) && '$' === $types{0}) { + $types = null; + } + + if (empty($types)) { + $types = 'mixed'; + } + + foreach (explode('|', $types) as $type) { + $type = $this->getTypeName($type, false); + $links[] = $this->resolveLink($type, $context) ?: $this->escapeHtml(ltrim($type, '\\')); + } + + return implode('|', $links); + } + + /** + * Returns links for package/namespace and its parent packages. + * + * @param string $package + * @param boolean $last + * @return string + */ + public function getPackageLinks($package, $last = true) + { + if (empty($this->packages)) { + return $package; + } + + $links = array(); + + $parent = ''; + foreach (explode('\\', $package) as $part) { + $parent = ltrim($parent . '\\' . $part, '\\'); + $links[] = $last || $parent !== $package + ? $this->link($this->getPackageUrl($parent), $part) + : $this->escapeHtml($part); + } + + return implode('\\', $links); + } + + /** + * Returns links for namespace and its parent namespaces. + * + * @param string $namespace + * @param boolean $last + * @return string + */ + public function getNamespaceLinks($namespace, $last = true) + { + if (empty($this->namespaces)) { + return $namespace; + } + + $links = array(); + + $parent = ''; + foreach (explode('\\', $namespace) as $part) { + $parent = ltrim($parent . '\\' . $part, '\\'); + $links[] = $last || $parent !== $namespace + ? $this->link($this->getNamespaceUrl($parent), $part) + : $this->escapeHtml($part); + } + + return implode('\\', $links); + } + + /** + * Returns a link to a namespace summary file. + * + * @param string $namespaceName Namespace name + * @return string + */ + public function getNamespaceUrl($namespaceName) + { + return sprintf($this->config->template['templates']['main']['namespace']['filename'], $this->urlize($namespaceName)); + } + + /** + * Returns a link to a package summary file. + * + * @param string $packageName Package name + * @return string + */ + public function getPackageUrl($packageName) + { + return sprintf($this->config->template['templates']['main']['package']['filename'], $this->urlize($packageName)); + } + + /** + * Returns a link to a group summary file. + * + * @param string $groupName Group name + * @return string + */ + public function getGroupUrl($groupName) + { + if (!empty($this->packages)) { + return $this->getPackageUrl($groupName); + } + + return $this->getNamespaceUrl($groupName); + } + + /** + * Returns a link to class summary file. + * + * @param string|\ApiGen\ReflectionClass $class Class reflection or name + * @return string + */ + public function getClassUrl($class) + { + $className = $class instanceof ReflectionClass ? $class->getName() : $class; + return sprintf($this->config->template['templates']['main']['class']['filename'], $this->urlize($className)); + } + + /** + * Returns a link to method in class summary file. + * + * @param \ApiGen\ReflectionMethod $method Method reflection + * @param \ApiGen\ReflectionClass $class Method declaring class + * @return string + */ + public function getMethodUrl(ReflectionMethod $method, ReflectionClass $class = null) + { + $className = null !== $class ? $class->getName() : $method->getDeclaringClassName(); + return $this->getClassUrl($className) . '#' . ($method->isMagic() ? 'm' : '') . '_' . ($method->getOriginalName() ?: $method->getName()); + } + + /** + * Returns a link to property in class summary file. + * + * @param \ApiGen\ReflectionProperty $property Property reflection + * @param \ApiGen\ReflectionClass $class Property declaring class + * @return string + */ + public function getPropertyUrl(ReflectionProperty $property, ReflectionClass $class = null) + { + $className = null !== $class ? $class->getName() : $property->getDeclaringClassName(); + return $this->getClassUrl($className) . '#' . ($property->isMagic() ? 'm' : '') . '$' . $property->getName(); + } + + /** + * Returns a link to constant in class summary file or to constant summary file. + * + * @param \ApiGen\ReflectionConstant $constant Constant reflection + * @return string + */ + public function getConstantUrl(ReflectionConstant $constant) + { + // Class constant + if ($className = $constant->getDeclaringClassName()) { + return $this->getClassUrl($className) . '#' . $constant->getName(); + } + // Constant in namespace or global space + return sprintf($this->config->template['templates']['main']['constant']['filename'], $this->urlize($constant->getName())); + } + + /** + * Returns a link to function summary file. + * + * @param \ApiGen\ReflectionFunction $function Function reflection + * @return string + */ + public function getFunctionUrl(ReflectionFunction $function) + { + return sprintf($this->config->template['templates']['main']['function']['filename'], $this->urlize($function->getName())); + } + + /** + * Returns a link to element summary file. + * + * @param \ApiGen\ReflectionElement $element Element reflection + * @return string + */ + public function getElementUrl(ReflectionElement $element) + { + if ($element instanceof ReflectionClass) { + return $this->getClassUrl($element); + } elseif ($element instanceof ReflectionMethod) { + return $this->getMethodUrl($element); + } elseif ($element instanceof ReflectionProperty) { + return $this->getPropertyUrl($element); + } elseif ($element instanceof ReflectionConstant) { + return $this->getConstantUrl($element); + } elseif ($element instanceof ReflectionFunction) { + return $this->getFunctionUrl($element); + } + } + + /** + * Returns a link to a element source code. + * + * @param \ApiGen\ReflectionElement $element Element reflection + * @param boolean $withLine Include file line number into the link + * @return string + */ + public function getSourceUrl(ReflectionElement $element, $withLine = true) + { + if ($element instanceof ReflectionClass || $element instanceof ReflectionFunction || ($element instanceof ReflectionConstant && null === $element->getDeclaringClassName())) { + $elementName = $element->getName(); + + if ($element instanceof ReflectionClass) { + $file = 'class-'; + } elseif ($element instanceof ReflectionConstant) { + $file = 'constant-'; + } elseif ($element instanceof ReflectionFunction) { + $file = 'function-'; + } + } else { + $elementName = $element->getDeclaringClassName(); + $file = 'class-'; + } + + $file .= $this->urlize($elementName); + + $lines = null; + if ($withLine) { + $lines = $element->getStartLine() !== $element->getEndLine() ? sprintf('%s-%s', $element->getStartLine(), $element->getEndLine()) : $element->getStartLine(); + } + + return sprintf($this->config->template['templates']['main']['source']['filename'], $file) . (null !== $lines ? '#' . $lines : ''); + } + + /** + * Returns a link to a element documentation at php.net. + * + * @param \ApiGen\ReflectionBase $element Element reflection + * @return string + */ + public function getManualUrl(ReflectionBase $element) + { + static $manual = 'http://php.net/manual'; + static $reservedClasses = array('stdClass', 'Closure', 'Directory'); + + // Extension + if ($element instanceof ReflectionExtension) { + $extensionName = strtolower($element->getName()); + if ('core' === $extensionName) { + return $manual; + } + + if ('date' === $extensionName) { + $extensionName = 'datetime'; + } + + return sprintf('%s/book.%s.php', $manual, $extensionName); + } + + // Class and its members + $class = $element instanceof ReflectionClass ? $element : $element->getDeclaringClass(); + + if (in_array($class->getName(), $reservedClasses)) { + return $manual . '/reserved.classes.php'; + } + + $className = strtolower($class->getName()); + $classUrl = sprintf('%s/class.%s.php', $manual, $className); + $elementName = strtolower(strtr(ltrim($element->getName(), '_'), '_', '-')); + + if ($element instanceof ReflectionClass) { + return $classUrl; + } elseif ($element instanceof ReflectionMethod) { + return sprintf('%s/%s.%s.php', $manual, $className, $elementName); + } elseif ($element instanceof ReflectionProperty) { + return sprintf('%s#%s.props.%s', $classUrl, $className, $elementName); + } elseif ($element instanceof ReflectionConstant) { + return sprintf('%s#%s.constants.%s', $classUrl, $className, $elementName); + } + } + + /** + * Tries to parse a definition of a class/method/property/constant/function and returns the appropriate link if successful. + * + * @param string $definition Definition + * @param \ApiGen\ReflectionElement $context Link context + * @return string|null + */ + public function resolveLink($definition, ReflectionElement $context) + { + if (empty($definition)) { + return null; + } + + $suffix = ''; + if ('[]' === substr($definition, -2)) { + $definition = substr($definition, 0, -2); + $suffix = '[]'; + } + + $element = $this->generator->resolveElement($definition, $context, $expectedName); + if (null === $element) { + return $expectedName; + } + + $classes = array(); + if ($element->isDeprecated()) { + $classes[] = 'deprecated'; + } + if (!$element->isValid()) { + $classes[] = 'invalid'; + } + + if ($element instanceof ReflectionClass) { + $link = $this->link($this->getClassUrl($element), $element->getName(), true, $classes); + } elseif ($element instanceof ReflectionConstant && null === $element->getDeclaringClassName()) { + $text = $element->inNamespace() + ? $this->escapeHtml($element->getNamespaceName()) . '\\' . $this->escapeHtml($element->getShortName()) . '' + : '' . $this->escapeHtml($element->getName()) . ''; + $link = $this->link($this->getConstantUrl($element), $text, false, $classes); + } elseif ($element instanceof ReflectionFunction) { + $link = $this->link($this->getFunctionUrl($element), $element->getName() . '()', true, $classes); + } else { + $text = $this->escapeHtml($element->getDeclaringClassName()); + if ($element instanceof ReflectionProperty) { + $url = $this->propertyUrl($element); + $text .= '::$' . $this->escapeHtml($element->getName()) . ''; + } elseif ($element instanceof ReflectionMethod) { + $url = $this->methodUrl($element); + $text .= '::' . $this->escapeHtml($element->getName()) . '()'; + } elseif ($element instanceof ReflectionConstant) { + $url = $this->constantUrl($element); + $text .= '::' . $this->escapeHtml($element->getName()) . ''; + } + + $link = $this->link($url, $text, false, $classes); + } + + return sprintf('%s', $link . $suffix); + } + + /** + * Resolves links in documentation. + * + * @param string $text Processed documentation text + * @param \ApiGen\ReflectionElement $context Reflection object + * @return string + */ + private function resolveLinks($text, ReflectionElement $context) + { + $that = $this; + return preg_replace_callback('~{@(?:link|see)\\s+([^}]+)}~', function ($matches) use ($context, $that) { + // Texy already added so it has to be stripped + list($url, $description) = $that->split(strip_tags($matches[1])); + if (Nette\Utils\Validators::isUrl($url)) { + return $that->link($url, $description ?: $url); + } + return $that->resolveLink($matches[1], $context) ?: $matches[1]; + }, $text); + } + + /** + * Resolves internal annotation. + * + * @param string $text + * @return string + */ + private function resolveInternal($text) + { + $internal = $this->config->internal; + return preg_replace_callback('~\\{@(\\w+)(?:(?:\\s+((?>(?R)|[^{}]+)*)\\})|\\})~', function($matches) use ($internal) { + // Replace only internal + if ('internal' !== $matches[1]) { + return $matches[0]; + } + return $internal && isset($matches[2]) ? $matches[2] : ''; + }, $text); + } + + /** + * Formats text as documentation block or line. + * + * @param string $text Text + * @param \ApiGen\ReflectionElement $context Reflection object + * @param boolean $block Parse text as block + * @return string + */ + public function doc($text, ReflectionElement $context, $block = false) + { + return $this->resolveLinks($this->texy->process($this->resolveInternal($text), !$block), $context); + } + + /** + * Parses annotation value. + * + * @param string $value + * @return array + */ + public function split($value) + { + return preg_split('~\s+|$~', $value, 2); + } + + /** + * Returns link. + * + * @param string $url + * @param string $text + * @param boolean $escape If the text should be escaped + * @param array $classes List of classes + * @return string + */ + public function link($url, $text, $escape = true, array $classes = array()) + { + $class = !empty($classes) ? sprintf(' class="%s"', implode(' ', $classes)) : ''; + return sprintf('%s', $url, $class, $escape ? $this->escapeHtml($text) : $text); + } + + /** + * Converts string to url safe characters. + * + * @param string $string + * @return string + */ + public function urlize($string) + { + return preg_replace('~[^\w]~', '.', $string); + } +} diff --git a/vendor/apigen/apigen/ApiGen/Tree.php b/vendor/apigen/apigen/ApiGen/Tree.php new file mode 100644 index 0000000..01f47b6 --- /dev/null +++ b/vendor/apigen/apigen/ApiGen/Tree.php @@ -0,0 +1,90 @@ +setPrefixPart(RecursiveTreeIterator::PREFIX_END_HAS_NEXT, self::HAS_NEXT); + $this->setPrefixPart(RecursiveTreeIterator::PREFIX_END_LAST, self::LAST); + $this->rewind(); + + $this->reflections = $reflections; + } + + /** + * Returns if the current item has a sibling on the same level. + * + * @return boolean + */ + public function hasSibling() + { + $prefix = $this->getPrefix(); + return !empty($prefix) && self::HAS_NEXT === substr($prefix, -1); + } + + /** + * Returns the current reflection. + * + * @return \ApiGen\Reflection + * @throws \UnexpectedValueException If current is not reflection array. + */ + public function current() + { + $className = $this->key(); + if (!isset($this->reflections[$className])) { + throw new RuntimeException(sprintf('Class "%s" is not in the reflection array', $className)); + } + + return $this->reflections[$className]; + } +} diff --git a/vendor/apigen/apigen/CHANGELOG.md b/vendor/apigen/apigen/CHANGELOG.md new file mode 100644 index 0000000..a836e55 --- /dev/null +++ b/vendor/apigen/apigen/CHANGELOG.md @@ -0,0 +1,128 @@ +## ApiGen 2.8.0 (2012-09-08) ## + +* Added support for @property and @method annotations +* Added support for variable length parameters +* Enabled selection of more rows in source code +* Templates can specify minimum and maximum required ApiGen version +* Added template for 404 page +* Improved support for malformed @param annotations +* Fixed excluding files and directories and detecting non accessible files and directories +* Fixed internal error when no timezone is specified in php.ini +* Fixed autocomplate in Opera browser +* Nette framework updated to version 2.0.5 +* TokenReflection library updated to version 1.3.1 +* FSHL library updated to version 2.1.0 + +## ApiGen 2.7.0 (2012-07-15) ## + +* Support of custom template macros and helpers +* Information about overridden methods in class method list +* Template UX fixes +* Fixed bugs causing ApiGen to crash +* TokenReflection library updated to version 1.3.0 +* Bootstrap2 based template +* Removed template with frames + +## ApiGen 2.6.1 (2012-03-27) ## + +* Fixed resolving element names in annotations +* Nette framework updated to version 2.0.1 +* TokenReflection library updated to version 1.2.2 + +## ApiGen 2.6.0 (2012-03-11) ## + +* Better error reporting, especially about duplicate classes, functions and constants +* Character set autodetection is on by default +* Changed visualization of deprecated elements +* Improved packages parsing and visualization +* Improved @license and @link visualization +* Improved `````` parsing +* Added option ```--extensions``` to specify file extensions of parsed files +* Minor visualization improvements +* Fixed autocomplete for classes in namespaces +* TokenReflection library updated to version 1.2.0 + +## ApiGen 2.5.0 (2012-02-12) ## + +* Added option ```--groups``` for grouping classes, interfaces, traits and exceptions in the menu +* Added option ```--autocomplete``` for choosing elements in the search autocomplete +* Inheriting some annotations from the file-level docblock +* @uses annotations create a @usedby annotation in the target documentation +* Added warning for unknown options +* Added support of comma-separated values for @see +* Changed all path options to be relative to the configuration file +* Fixed dependencies check +* Nette framework updated to 2.0.0 stable version +* TokenReflection library updated to version 1.1.0 + +## ApiGen 2.4.1 (2012-01-25) ## + +* TokenReflection library updated to version 1.0.2 +* Nette framework updated to version 2.0.0RC1 + +## ApiGen 2.4.0 (2011-12-24) ## + +* TokenReflection library updated to version 1.0.0 +* Fixed support for older PHP versions of the 5.3 branch +* Option ```templateConfig``` is relative to the config file (was relative to cwd) + +## ApiGen 2.3.0 (2011-11-13) ## + +* Added support for default configuration file +* Added link to download documentation as ZIP archive +* Added option ```--charset``` and autodetection of charsets +* Added support for @ignore annotation +* Added PHAR support +* Added support for ClassName[] +* Added memory usage reporting in progressbar +* Improved templates for small screens +* Changed option name ```--undocumented``` to ```--report``` +* FSHL library updated to version 2.0.1 + +## ApiGen 2.2.1 (2011-10-26) ## + +* Fixed processing of magic constants +* Fixed resize.png +* TokenReflection library updated to version 1.0.0RC2 + +## ApiGen 2.2.0 (2011-10-16) ## + +* Added an option to check for updates +* Added an option to initially display elements in alphabetical order +* Added an option to generate the robots.txt file +* Added required extensions check +* Changed reporting of undocumented elements to the checkstyle format +* Improved deprecated elements highlighting +* Highlighting the linked source code line +* Unknown annotations are sorted alphabetically +* Fixed class parameter description parsing +* Fixed command line options parsing +* Fixed include path setting of the GitHub version +* Fixed frames template + +## ApiGen 2.1.0 (2011-09-04) ## + +* Experimental support of PHP 5.4 traits +* Added option ```--colors``` +* Added template with frames +* Added templates option to make element details expanded by default + +## ApiGen 2.0.3 (2011-08-22) ## + +* @param, @return and @throw annotations are inherited + +## ApiGen 2.0.2 (2011-07-21) ## + +* Fixed inherited methods listing +* Interfaces are not labeled "Abstract interface" +* Fixed Google CSE ID validation +* Fixed filtering by ```--exclude``` and ```--skip-doc-path``` +* Fixed exception output when using ```--debug``` + +## ApiGen 2.0.1 (2011-07-17) ## + +* Updated TokenReflection library to 1.0.0beta5 +* Requires FSHL 2.0.0RC +* Fixed url in footer + +## ApiGen 2.0.0 (2011-06-28) ## diff --git a/vendor/apigen/apigen/LICENSE.md b/vendor/apigen/apigen/LICENSE.md new file mode 100644 index 0000000..afe8631 --- /dev/null +++ b/vendor/apigen/apigen/LICENSE.md @@ -0,0 +1,32 @@ +# Licenses # + +You may use ApiGen under the terms of either the New BSD License or the GNU General Public License (GPL) version 2 or 3. + +The BSD License is recommended for most projects. It is easy to understand and it places almost no restrictions on what you can do with the framework. If the GPL fits better to your project, you can use the framework under this license. + +You don't have to notify anyone which license you are using. You can freely use ApiGen in commercial projects as long as the copyright header remains intact. + +## New BSD License ## + +Copyright (c) 2010 [David Grudl](http://davidgrudl.com) +Copyright (c) 2011-2012 [Jaroslav Hanslík](https://github.com/kukulich) +Copyright (c) 2011-2012 [Ondřej Nešpor](https://github.com/Andrewsville) + +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. + +* Neither the name of "ApiGen" nor the names of its contributors may 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. + +## GNU General Public License ## + +GPL licenses are very very long, so instead of including them here we offer you URLs with full text: + +* [GPL version 2](http://www.gnu.org/licenses/gpl-2.0.html) +* [GPL version 3](http://www.gnu.org/licenses/gpl-3.0.html) diff --git a/vendor/apigen/apigen/README.md b/vendor/apigen/apigen/README.md new file mode 100644 index 0000000..71a56b0 --- /dev/null +++ b/vendor/apigen/apigen/README.md @@ -0,0 +1,259 @@ +# Welcome to ApiGen # + +ApiGen is the tool for creating professional API documentation from PHP source code, similar to discontinued phpDocumentor/phpDoc. + +ApiGen has support for PHP 5.3 namespaces, packages, linking between documentation, cross referencing to PHP standard classes and general documentation, creation of highlighted source code and experimental support for PHP 5.4 **traits**. + +## Support & Bug Reports ## + +For all support questions please use our [mailing list](https://groups.google.com/group/apigen). For bug reports and issues the [issue tracker](https://github.com/apigen/apigen/issues) is available. Changes between versions are described in the [change log](https://github.com/apigen/apigen/blob/master/CHANGELOG.md). + +## Features ## + +* Our own [TokenReflection library](https://github.com/Andrewsville/PHP-Token-Reflection) is used to describe the source code. It is **safe** (documented source code does not get included and thus parsed) and **simple** (you do not need to include or autoload all libraries you use in your source code). +* Detailed documentation of classes, functions and constants. +* Highlighted source code. +* Support of namespaces and packages with subpackages. +* Experimental support of traits. +* A page with trees of classes, interfaces, traits and exceptions. +* A page with a list of deprecated elements. +* A page with Todo tasks. +* Link to download documentation as ZIP archive. +* Checkstyle report of poorly documented elements. +* Support for docblock templates. +* Support for @inheritdoc. +* Support for {@link}. +* Active links in @see and @uses tags. +* Documentation of used internal PHP classes. +* Links to the start line in the highlighted source code for every described element. +* List of direct and indirect known subclasses, implementers and users for every class/interface/trait/exception. +* Check for a new version. +* Google CSE support with suggest. +* Google Analytics support. +* Support for multiple/custom templates. +* Sitemap and opensearch support. +* Support for different charsets and line endings. +* Lots of configuration options (see below). + +## Installation ## + +The preferred installation way is using the standalone package but there are three more ways how to install ApiGen. + +### Standalone package ### + +To download the actual release visit the [Downloads section](https://github.com/apigen/apigen/downloads). There you find separate packages for each release in two formats - zip and tar.gz. These packages are prepared by the ApiGen team and are truly standalone; they contain all required libraries in appropriate versions. You just need to extract the contents of an archive and you can start using ApiGen. + +### Composer ### + +Just create a `composer.json` file and run the `php composer.phar install` command to install it: + +``` +{ + "require-dev": { + "apigen/apigen": "~2.8.0" + } +} +``` + +### GitHub built archive ### + +GitHub allows you to download any repository as a zip or tar.gz archive. You can use this feature to download an archive with the current version of ApiGen. However this approach has one disadvantage. Such archive (in contrast to the standalone packages) does not contain required libraries. They are included as git submodules in the repository and GitHub simply ignores them when generating the archive. It means that you will have to obtain required libraries manually. + +### Cloning the repository ### + +The last way how to install ApiGen is simply to clone our repository. If you do so, remember to fetch and rebase to get new versions and do not forget to update submodules in the libs directory. + +## Usage ## + +``` + apigen --config [options] + apigen --source --destination [options] +``` + +As you can see, you can use ApiGen either by providing individual parameters via the command line or using a config file. Moreover you can combine the two methods and the command line parameters will have precedence over those in the config file. + +Every configuration option has to be followed by its value. And it is exactly the same to write ```--config=file.conf``` and ```--config file.conf```. The only exceptions are boolean options (those with yes|no values). When using these options on the command line you do not have to provide the "yes" value explicitly. If ommited, it is assumed that you wanted to turn the option on. So using ```--debug=yes``` and ```--debug``` does exactly the same (and the opposite is ```--debug=no```). + +Some options can have multiple values. To do so, you can either use them multiple times or separate their values by a comma. It means that ```--source=file1.php --source=file2.php``` and ```--source=file1.php,file2.php``` is exactly the same. + +### Options ### + +```--config|-c ``` + +Path to the config file. + +```--source|-s ``` **required** + +Path to the directory or file to be processed. You can use the parameter multiple times to provide a list of directories or files. All types of PHAR archives are supported (requires the PHAR extension). To process gz/bz2 compressed archives you need the appropriate extension (see requirements). + +```--destination|-d ``` **required** + +Documentation will be generated into this directory. + +```--extensions ``` + +List of allowed file extensions, default is "php". + +```--exclude ``` + +Directories and files matching this file mask will not be parsed. You can exclude for example tests from processing this way. This parameter is case sensitive and can be used multiple times. + +```--skip-doc-path ``` +```--skip-doc-prefix ``` + +Using this parameters you can tell ApiGen not to generate documentation for elements from certain files or with certain name prefix. Such classes will appear in class trees, but will not create a link to their documentation. These parameters are case sensitive and can be used multiple times. + +```--charset ``` + +Character set of source files, default is "auto" that lets ApiGen choose from all supported character sets. However if you use only one characters set across your source files you should set it explicitly to avoid autodetection because it can be tricky (and is not completely realiable). Moreover autodetection slows down the process of generating documentation. You can also use the parameter multiple times to provide a list of all used character sets in your documentation. In that case ApiGen will choose one of provided character sets for each file. + +```--main ``` + +Elements with this name prefix will be considered as the "main project" (the rest will be considered as libraries). + +```--title ``` + +Title of the generated documentation. + +```--base-url ``` + +Documentation base URL used in the sitemap. Only needed if you plan to make your documentation public. + +```--google-cse-id ``` + +If you have a Google CSE ID, the search box will use it when you do not enter an exact class, constant or function name. + +```--google-analytics ``` + +A Google Analytics tracking code. If provided, an ansynchronous tracking code will be placed into every generated page. + +```--template-config ``` + +Template config file, default is the config file of ApiGen default template. + +```--allowed-html ``` + +List of allowed HTML tags in documentation separated by comma. Default value is "b,i,a,ul,ol,li,p,br,var,samp,kbd,tt". + +```--groups ``` + +How should elements be grouped in the menu. Possible options are "auto", "namespaces", "packages" and "none". Default value is "auto" (namespaces are used if the source code uses them, packages otherwise). + +```--autocomplete ``` + +List of element types that will appear in the search input autocomplete. Possible values are "classes", "constants", "functions", "methods", "properties" and "classconstants". Default value is "classes,constants,functions". + +```--access-levels ``` + +Access levels of methods and properties that should get their documentation parsed. Default value is "public,protected" (don't generate private class members). + +```--internal ``` + +Generate documentation for elements marked as internal (```@internal``` without description) and display parts of the documentation that are marked as internal (```@internal with description ...``` or inline ```{@internal ...}```), default is "No". + +```--php ``` + +Generate documentation for PHP internal classes, default is "Yes". + +```--tree ``` + +Generate tree view of classes, interfaces, traits and exceptions, default is "Yes". + +```--deprecated ``` + +Generate documentation for deprecated elements, default is "No". + +```--todo ``` + +Generate a list of tasks, default is "No". + +```--source-code ``` + +Generate highlighted source code for user defined elements, default is "Yes". + +```--download ``` + +Add a link to download documentation as a ZIP archive, default is "No". + +```--report ``` + +Save a checkstyle report of poorly documented elements into a file. + +```--wipeout ``` + +Delete files generated in the previous run, default is "Yes". + +```--quiet ``` + +Do not print any messages to the console, default is "No". + +```--progressbar ``` + +Display progressbars, default is "Yes". + +```--colors ``` + +Use colors, default "No" on Windows, "Yes" on other systems. Windows doesn't support colors in console however you can enable it with [Ansicon](http://adoxa.110mb.com/ansicon/). + +```--update-check ``` + +Check for a new version of ApiGen, default is "Yes". + +```--debug ``` + +Display additional information (exception trace) in case of an error, default is "No". + +```--help|-h ``` + +Display the list of possible options. + +Only ```--source``` and ```--destination``` parameters are required. You can provide them via command line or a configuration file. + +### Config files ### + +Instead of providing individual parameters via the command line, you can prepare a config file for later use. You can use all the above listed parameters (with one exception: the ```--config``` option) only without dashes and with an uppercase letter after each dash (so ```--access-level``` becomes ```accessLevel```). + +ApiGen uses the [NEON file format](http://ne-on.org) for all its config files. You can try the [online parser](http://ne-on.org) to debug your config files and see how they get parsed. + +Then you can call ApiGen with a single parameter ```--config``` specifying the config file to load. + +``` + apigen --config [options] +``` + +Even when using a config file, you can still provide additional parameters via the command line. Such parameters will have precedence over parameters from the config file. + +Keep in mind, that any values in the config file will be **overwritten** by values from the command line. That means that providing the ```--source``` parameter values both in the config file and via the command line will not result in using all the provided values but only those from the command line. + +If you provide no command line parameters at all, ApiGen will try to load a default config file called ```apigen.neon``` in the current working directory. If found it will work as if you used the ```--config``` option. Note that when using any command line option, you have to specify the config file if you have one. ApiGen will try to load one automatically only when no command line parameters are used. Option names have to be in camelCase in config files (```--template-config``` on the command line becomes ```templateConfig``` in a config file). You can see a full list of configuration options with short descriptions in the example config file [apigen.neon.example](https://github.com/apigen/apigen/blob/master/apigen.neon.example). + +### Example ### + +We are generating documentation for the Nella Framework. We want Nette and Doctrine to be parsed as well because we want their classes to appear in class trees, lists of parent classes and their members in lists of inherited properties, methods and constants. However we do not want to generate their full documentation along with highlighted source codes. And we do not want to process any "test" directories, because there might be classes that do not belong to the project actually. + +``` + apigen --source ~/nella/Nella --source ~/doctrine2/lib/Doctrine --source ~/doctrine2/lib/vendor --source ~/nette/Nette --skip-doc-path "~/doctrine2/*" --skip-doc-prefix Nette --exclude "*/tests/*" --destination ~/docs/ --title "Nella Framework" +``` + +## Requirements ## + +ApiGen requires PHP 5.3 or later. Four libraries it uses ([Nette](https://github.com/nette/nette), [Texy](https://github.com/dg/texy), [TokenReflection](https://github.com/Andrewsville/PHP-Token-Reflection) and [FSHL](https://github.com/kukulich/fshl)) require four additional PHP extensions: [tokenizer](http://php.net/manual/book.tokenizer.php), [mbstring](http://php.net/manual/book.mbstring.php), [iconv](http://php.net/manual/book.iconv.php) and [json](http://php.net/manual/book.json.php). For documenting PHAR archives you need the [phar extension](http://php.net/manual/book.phar.php) and for documenting gz or bz2 compressed PHARs, you need the [zlib](http://php.net/manual/book.zlib.php) or [bz2](http://php.net/manual/book.bzip2.php) extension respectively. To generate the ZIP file with documentation you need the [zip extension](http://php.net/manual/book.zip.php). + +When generating documentation of large libraries (Zend Framework for example) we recommend not to have the Xdebug PHP extension loaded (it does not need to be used, it significantly slows down the generating process even when only loaded). + +## Authors ## + +* [Jaroslav Hanslík](https://github.com/kukulich) +* [Ondřej Nešpor](https://github.com/Andrewsville) +* [David Grudl](https://github.com/dg) + +## Usage examples ## + +* [Doctrine](http://www.doctrine-project.org/api/orm/2.2/index.html) +* [Nette Framework](http://api.nette.org/2.0/) +* [TokenReflection library](http://andrewsville.github.com/PHP-Token-Reflection/) +* [FSHL library](http://fshl.kukulich.cz/api/) +* [Nella Framework](http://api.nellafw.org/) +* Jyxo PHP Libraries, both [namespaced](http://jyxo.github.com/php/) and [non-namespaced](http://jyxo.github.com/php-no-namespace/) + +Besides from these publicly visible examples there are companies that use ApiGen to generate their inhouse documentation: [Medio Interactive](http://www.medio.cz/), [Wikidi](http://wikidi.com/). \ No newline at end of file diff --git a/vendor/apigen/apigen/apigen b/vendor/apigen/apigen/apigen new file mode 100755 index 0000000..0e52ab5 --- /dev/null +++ b/vendor/apigen/apigen/apigen @@ -0,0 +1,262 @@ +#!/usr/bin/env php +processCliOptions($options); + $generator = new Generator($config); + + // Help + if ($config->isHelpRequested()) { + echo $generator->colorize($generator->getHeader()); + echo $generator->colorize($config->getHelp()); + die(); + } + + // Prepare configuration + $config->prepare(); + + if ($config->debug) { + Debugger::$onFatalError = array(); + Debugger::enable(Debugger::DEVELOPMENT, false); + } + + $generator->output($generator->getHeader()); + + // Check for update (only in production mode) + if ($config->updateCheck && !$config->debug) { + ini_set('default_socket_timeout', 5); + $latestVersion = @file_get_contents('http://pear.apigen.org/rest/r/apigen/latest.txt'); + if (false !== $latestVersion && version_compare(trim($latestVersion), Generator::VERSION, '>')) { + $generator->output(sprintf("New version @header@%s@c available\n\n", $latestVersion)); + } + } + + // Scan + if (count($config->source) > 1) { + $generator->output(sprintf("Scanning\n @value@%s@c\n", implode("\n ", $config->source))); + } else { + $generator->output(sprintf("Scanning @value@%s@c\n", $config->source[0])); + } + if (count($config->exclude) > 1) { + $generator->output(sprintf("Excluding\n @value@%s@c\n", implode("\n ", $config->exclude))); + } elseif (!empty($config->exclude)) { + $generator->output(sprintf("Excluding @value@%s@c\n", $config->exclude[0])); + } + + $parsed = $generator->parse(); + + if (count($parsed->errors) > 1) { + $generator->output(sprintf("@error@Found %d errors@c\n\n", count($parsed->errors))); + + $no = 1; + foreach ($parsed->errors as $e) { + + if ($e instanceof TokenReflection\Exception\ParseException) { + $generator->output(sprintf("@error@%d.@c The TokenReflection library threw an exception while parsing the file @value@%s@c.\n", $no, $e->getFileName())); + if ($config->debug) { + $generator->output("\nThis can have two reasons: a) the source code in the file is not valid or b) you have just found a bug in the TokenReflection library.\n\n"); + $generator->output("If the license allows it please send the whole file or at least the following fragment describing where exacly is the problem along with the backtrace to apigen@apigen.org. Thank you!\n\n"); + + $token = $e->getToken(); + $sender = $e->getSender(); + if (!empty($token)) { + $generator->output( + sprintf( + "The cause of the exception \"%s\" was the @value@%s@c token (line @count@%d@c) in following part of %s source code:\n\n", + $e->getMessage(), + $e->getTokenName(), + $e->getExceptionLine(), + $sender && $sender->getName() ? '@value@' . $sender->getPrettyName() . '@c' : 'the' + ) + ); + } else { + $generator->output( + sprintf( + "The exception \"%s\" was thrown when processing %s source code:\n\n", + $e->getMessage(), + $sender && $sender->getName() ? '@value@' . $sender->getPrettyName() . '@c' : 'the' + ) + ); + } + + $generator->output($e->getSourcePart(true) . "\n\nThe exception backtrace is following:\n\n" . $e->getTraceAsString() . "\n\n"); + } + } elseif ($e instanceof TokenReflection\Exception\FileProcessingException) { + $generator->output(sprintf("@error@%d.@c %s\n", $no, $e->getMessage())); + if ($config->debug) { + $generator->output("\n" . $e->getDetail() . "\n\n"); + } + } else { + $generator->output(sprintf("@error@%d.@c %s\n", $no, $e->getMessage())); + if ($config->debug) { + $trace = $e->getTraceAsString(); + while ($e = $e->getPrevious()) { + $generator->output(sprintf("\n%s", $e->getMessage())); + $trace = $e->getTraceAsString(); + } + $generator->output(sprintf("\n%s\n\n", $trace)); + } + } + + $no++; + } + + if (!$config->debug) { + $generator->output("\nEnable the debug mode (@option@--debug@c) to see more details.\n\n"); + } + } + + $generator->output(sprintf("Found @count@%d@c classes, @count@%d@c constants, @count@%d@c functions and other @count@%d@c used PHP internal classes\n", $parsed->classes, $parsed->constants, $parsed->functions, $parsed->internalClasses)); + $generator->output(sprintf("Documentation for @count@%d@c classes, @count@%d@c constants, @count@%d@c functions and other @count@%d@c used PHP internal classes will be generated\n", $parsed->documentedClasses, $parsed->documentedConstants, $parsed->documentedFunctions, $parsed->documentedInternalClasses)); + + // Generating + $generator->output(sprintf("Using template config file @value@%s@c\n", $config->templateConfig)); + + if ($config->wipeout && is_dir($config->destination)) { + $generator->output("Wiping out destination directory\n"); + if (!$generator->wipeOutDestination()) { + throw new \RuntimeException('Cannot wipe out destination directory'); + } + } + + $generator->output(sprintf("Generating to directory @value@%s@c\n", $config->destination)); + $skipping = array_merge($config->skipDocPath, $config->skipDocPrefix); + if (count($skipping) > 1) { + $generator->output(sprintf("Will not generate documentation for\n @value@%s@c\n", implode("\n ", $skipping))); + } elseif (!empty($skipping)) { + $generator->output(sprintf("Will not generate documentation for @value@%s@c\n", $skipping[0])); + } + $generator->generate(); + + // End + $end = new \DateTime(); + $interval = $end->diff($start); + $parts = array(); + if ($interval->h > 0) { + $parts[] = sprintf('@count@%d@c hours', $interval->h); + } + if ($interval->i > 0) { + $parts[] = sprintf('@count@%d@c min', $interval->i); + } + if ($interval->s > 0) { + $parts[] = sprintf('@count@%d@c sec', $interval->s); + } + if (empty($parts)) { + $parts[] = sprintf('@count@%d@c sec', 1); + } + + $duration = implode(' ', $parts); + $generator->output(sprintf("Done. Total time: %s, used: @count@%d@c MB RAM\n", $duration, round(memory_get_peak_usage(true) / 1024 / 1024))); + +} catch (ConfigException $e) { + // Configuration error + echo $generator->colorize($generator->getHeader() . sprintf("\n@error@%s@c\n\n", $e->getMessage()) . $config->getHelp()); + + die(2); +} catch (\Exception $e) { + // Everything else + if ($config->debug) { + do { + echo $generator->colorize(sprintf("\n%s(%d): @error@%s@c", $e->getFile(), $e->getLine(), $e->getMessage())); + $trace = $e->getTraceAsString(); + } while ($e = $e->getPrevious()); + + printf("\n\n%s\n", $trace); + } else { + echo $generator->colorize(sprintf("\n@error@%s@c\n", $e->getMessage())); + } + + die(1); +} \ No newline at end of file diff --git a/vendor/apigen/apigen/apigen.bat b/vendor/apigen/apigen/apigen.bat new file mode 100644 index 0000000..9be5228 --- /dev/null +++ b/vendor/apigen/apigen/apigen.bat @@ -0,0 +1,16 @@ +@echo off +REM ApiGen 2.8.0 - API documentation generator for PHP 5.3+ +REM +REM Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +REM Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +REM Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +REM +REM For the full copyright and license information, please view +REM the file LICENCE.md that was distributed with this source code. +REM + +IF EXIST "@php_bin@" ( + "@php_bin@" "@bin_dir@\apigen" %* +) ELSE ( + "php.exe" "%~dp0apigen.php" %* +) diff --git a/vendor/apigen/apigen/apigen.neon.example b/vendor/apigen/apigen/apigen.neon.example new file mode 100644 index 0000000..e6ad37b --- /dev/null +++ b/vendor/apigen/apigen/apigen.neon.example @@ -0,0 +1,65 @@ +# Source file or directory to parse +source: +# Directory where to save the generated documentation +destination: +# List of allowed file extensions +extensions: [php] +# Mask to exclude file or directory from processing +exclude: +# Don't generate documentation for classes from file or directory with this mask +skipDocPath: +# Don't generate documentation for classes with this name prefix +skipDocPrefix: +# Character set of source files +charset: auto +# Main project name prefix +main: + +# Title of generated documentation +title: +# Documentation base URL +baseUrl: +# Google Custom Search ID +googleCseId: +# Google Analytics tracking code +googleAnalytics: +# Template config file +templateConfig: './templates/default/config.neon' +# Grouping of classes +groups: auto +# List of allowed HTML tags in documentation +allowedHtml: [b, i, a, ul, ol, li, p, br, var, samp, kbd, tt] +# Element types for search input autocomplete +autocomplete: [classes, constants, functions] + +# Generate documentation for methods and properties with given access level +accessLevels: [public, protected] +# Generate documentation for elements marked as internal and display internal documentation parts +internal: No +# Generate documentation for PHP internal classes +php: Yes +# Generate tree view of classes, interfaces and exceptions +tree: Yes +# Generate documentation for deprecated classes, methods, properties and constants +deprecated: No +# Generate documentation of tasks +todo: No +# Generate highlighted source code files +sourceCode: Yes +# Add a link to download documentation as a ZIP archive +download: No +# Save a checkstyle report of poorly documented elements into a file +report: + +# Wipe out the destination directory first +wipeout: Yes +# Don't display scanning and generating messages +quiet: No +# Display progressbars +progressbar: Yes +# Use colors +colors: No +# Check for update +updateCheck: Yes +# Display additional information in case of an error +debug: No diff --git a/vendor/apigen/apigen/composer.json b/vendor/apigen/apigen/composer.json new file mode 100644 index 0000000..0c6cf0f --- /dev/null +++ b/vendor/apigen/apigen/composer.json @@ -0,0 +1,56 @@ +{ + "name": "apigen/apigen", + "description": "API documentation generator for PHP 5.3+", + "type": "project", + "keywords": ["documentation", "docblock", "phpdoc", "phpdocumentor", "generator", "api"], + "homepage": "http://apigen.org/", + "license": "BSD-3-Clause", + "authors": [ + { + "name": "Jaroslav Hanslík", + "homepage": "https://github.com/kukulich" + }, + { + "name": "Ondřej Nešpor", + "homepage": "https://github.com/andrewsville" + }, + { + "name": "David Grudl", + "homepage": "http://davidgrudl.com" + } + ], + "support": { + "issues": "https://github.com/apigen/apigen/issues", + "forum": "https://groups.google.com/group/apigen", + "source": "https://github.com/apigen/apigen" + }, + "require": { + "php": ">=5.3.0", + "ext-json": "*", + "ext-mbstring": "*", + "nette/nette": "~2.1.1", + "texy/texy": "~2.4.0", + "kukulich/fshl": "~2.1.0", + "andrewsville/php-token-reflection": "~1.3.1" + }, + "suggest": { + "ext-bz2": "*", + "ext-phar": "*", + "ext-zip": "*", + "ext-zlib": "*" + }, + "autoload": { + "psr-0": { + "ApiGen": "./" + } + }, + "bin": [ + "apigen" + ], + "extra": { + "branch-alias": { + "dev-master": "2.8.0", + "dev-develop": "3.0.0-dev" + } + } +} diff --git a/vendor/apigen/apigen/templates/bootstrap/404.latte b/vendor/apigen/apigen/templates/bootstrap/404.latte new file mode 100644 index 0000000..3b0d1f8 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/404.latte @@ -0,0 +1,23 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $robots = false} + +{block title}Page not found{/block} + +{block content} +
+

{include title}

+

The requested page could not be found.

+

You have probably clicked on a link that is outdated and points to a page that does not exist any more or you have made an typing error in the address.

+

To continue please try to find requested page in the menu,{if $config->tree} take a look at the tree view of the whole project{/if} or use search field on the top.

+
+{/block} diff --git a/vendor/apigen/apigen/templates/bootstrap/@elementlist.latte b/vendor/apigen/apigen/templates/bootstrap/@elementlist.latte new file mode 100644 index 0000000..06a5754 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/@elementlist.latte @@ -0,0 +1,59 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{define elements} + + {if $namespace}{$element->shortName}{else}{$element->name}{/if} + {$element|shortDescription|noescape} + +{/define} + +{if $classes} +

Classes summary

+ +{include elements, elements => $classes} +
+{/if} + +{if $interfaces} +

Interfaces summary

+ +{include elements, elements => $interfaces} +
+{/if} + +{if $traits} +

Traits summary

+ +{include elements, elements => $traits} +
+{/if} + +{if $exceptions} +

Exceptions summary

+ +{include elements, elements => $exceptions} +
+{/if} + +{if $constants} +

Constants summary

+ +{include elements, elements => $constants} +
+{/if} + +{if $functions} +

Functions summary

+ +{include elements, elements => $functions} +
+{/if} diff --git a/vendor/apigen/apigen/templates/bootstrap/@layout.latte b/vendor/apigen/apigen/templates/bootstrap/@layout.latte new file mode 100644 index 0000000..45f7db6 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/@layout.latte @@ -0,0 +1,175 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{default $robots = true} +{default $active = ''} + + + + + + + + {include title}{if 'overview' !== $active && $config->title} | {$config->title}{/if} + + + + + + + + + + + +
+ +
+ +
+ + + + + + diff --git a/vendor/apigen/apigen/templates/bootstrap/class.latte b/vendor/apigen/apigen/templates/bootstrap/class.latte new file mode 100644 index 0000000..2a9b937 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/class.latte @@ -0,0 +1,423 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'class'} + +{block title}{if $class->deprecated}Deprecated {/if}{if $class->interface}Interface{elseif $class->trait}Trait{else}Class{/if} {$class->name}{/block} + +{block content} +
+

{if $class->interface}Interface{elseif $class->trait}Trait{else}Class{/if} {$class->shortName}

+ + {if $class->valid} + +
+ {$class|longDescription|noescape} +
+ +
+
+ Extended by + {if $item->documented} + {last}{/last}{$item->name}{last}{/last} + {else}{$item->name}{/if} + {var $itemOwnInterfaces = $item->ownInterfaces} + {if $itemOwnInterfaces} implements {foreach $itemOwnInterfaces as $interface} + {$interface->name}{sep}, {/sep} + {/foreach}{/if} + {var $itemOwnTraits = $item->ownTraits} + {if $itemOwnTraits} uses {foreach $itemOwnTraits as $trait} + {$trait->name}{sep}, {/sep} + {/foreach}{/if} +
+
+ + {define children} +

+ {foreach $children as $child} + {$child->name}{sep}, {/sep} + {/foreach} +

+ {/define} + +
+

Direct known subclasses

+ {include children, children => $directSubClasses} +
+ +
+

Indirect known subclasses

+ {include children, children => $indirectSubClasses} +
+ +
+

Direct known implementers

+ {include children, children => $directImplementers} +
+ +
+

Indirect known implementers

+ {include children, children => $indirectImplementers} +
+ +
+

Direct Known Users

+ {include children, children => $directUsers} +
+ +
+

Indirect Known Users

+ {include children, children => $indirectUsers} +
+ +
+ {if !$class->interface && !$class->trait && ($class->abstract || $class->final)}{if $class->abstract}Abstract{else}Final{/if}
{/if} + {if $class->internal}PHP Extension: {$class->extension->name|firstUpper}
{/if} + {if $class->inNamespace()}Namespace: {$class->namespaceName|namespaceLinks|noescape}
{/if} + {if $class->inPackage()}Package: {$class->packageName|packageLinks|noescape}
{/if} + + {foreach $template->annotationSort($template->annotationFilter($class->annotations)) as $annotation => $values} + {foreach $values as $value} + {$annotation|annotationBeautify}{if $value}:{/if} + {$value|annotation:$annotation:$class|noescape}
+ {/foreach} + {/foreach} + {if $class->internal}Documented at php.net{else}Located at {$class->fileName|relativePath}{/if}
+
+ + {var $ownMethods = $class->ownMethods} + {var $inheritedMethods = $class->inheritedMethods} + {var $usedMethods = $class->usedMethods} + {var $ownMagicMethods = $class->ownMagicMethods} + {var $inheritedMagicMethods = $class->inheritedMagicMethods} + {var $usedMagicMethods = $class->usedMagicMethods} + + {if $ownMethods || $inheritedMethods || $usedMethods || $ownMagicMethods || $usedMagicMethods} + {define method} + + {var $annotations = $method->annotations} + + + {if !$class->interface && $method->abstract}abstract{elseif $method->final}final{/if} {if $method->protected}protected{elseif $method->private}private{else}public{/if} {if $method->static}static{/if} + {ifset $annotations['return']}{$annotations['return'][0]|typeLinks:$method|noescape}{/ifset} + {if $method->returnsReference()}&{/if} + + + +
+ # + {block|strip} + {if $class->internal} + {$method->name}( + {else} + {$method->name}( + {/if} + {foreach $method->parameters as $parameter} + {$parameter->typeHint|typeLinks:$method|noescape} + {if $parameter->passedByReference}& {/if}${$parameter->name}{if $parameter->defaultValueAvailable} = {$parameter->defaultValueDefinition|highlightPHP:$class|noescape}{elseif $parameter->unlimited},…{/if}{sep}, {/sep} + {/foreach} + ){/block} + + {if $config->template['options']['elementDetailsCollapsed']} +
+ {$method|shortDescription:true|noescape} +
+ {/if} + +
+ {$method|longDescription|noescape} + + {if !$class->deprecated && $method->deprecated} +

Deprecated

+ {ifset $annotations['deprecated']} +
+ {foreach $annotations['deprecated'] as $description} + {if $description} + {$description|annotation:'deprecated':$method|noescape}
+ {/if} + {/foreach} +
+ {/ifset} + {/if} + + {if $method->parameters && isset($annotations['param'])} +

Parameters

+
+ {foreach $method->parameters as $parameter} +
${$parameter->name}{if $parameter->unlimited},…{/if}
+
{$parameter->description|description:$method}
+ {/foreach} +
+ {/if} + + {if isset($annotations['return']) && 'void' !== $annotations['return'][0]} +

Returns

+
+ {foreach $annotations['return'] as $description} + {$description|annotation:'return':$method|noescape}
+ {/foreach} +
+ {/if} + + {ifset $annotations['throws']} +

Throws

+
+ {foreach $annotations['throws'] as $description} + {$description|annotation:'throws':$method|noescape}
+ {/foreach} +
+ {/ifset} + + {foreach $template->annotationSort($template->annotationFilter($annotations, array('deprecated', 'param', 'return', 'throws'))) as $annotation => $descriptions} +

{$annotation|annotationBeautify}

+
+ {foreach $descriptions as $description} + {if $description} + {$description|annotation:$annotation:$method|noescape}
+ {/if} + {/foreach} +
+ {/foreach} + + {var $overriddenMethod = $method->overriddenMethod} + {if $overriddenMethod} +

Overrides

+ + {/if} + + {var $implementedMethod = $method->implementedMethod} + {if $implementedMethod} +

Implementation of

+ + {/if} +
+
+ + {/define} + +

Methods summary

+ + {foreach $ownMethods as $method} + {include method, method => $method} + {/foreach} +
+ + {foreach $inheritedMethods as $parentName => $methods} +

Methods inherited from {$parentName}

+

+ {foreach $methods as $method} + {$method->name}(){sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedMethods as $traitName => $methods} +

Methods used from {$traitName}

+

+ {foreach $methods as $data} + {$data['method']->name}(){if $data['aliases']}(as {foreach $data['aliases'] as $alias}{$alias->name}(){sep}, {/sep}{/foreach}){/if}{sep}, {/sep} + {/foreach} +

+ {/foreach} + +

Magic methods summary

+ + {foreach $ownMagicMethods as $method} + {include method, method => $method} + {/foreach} +
+ + {foreach $inheritedMagicMethods as $parentName => $methods} +

Magic methods inherited from {$parentName}

+

+ {foreach $methods as $method} + {$method->name}(){sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedMagicMethods as $traitName => $methods} +

Magic methods used from {$traitName}

+

+ {foreach $methods as $data} + {$data['method']->name}(){if $data['aliases']}(as {foreach $data['aliases'] as $alias}{$alias->name}(){sep}, {/sep}{/foreach}){/if}{sep}, {/sep} + {/foreach} +

+ {/foreach} + {/if} + + + {var $ownConstants = $class->ownConstants} + {var $inheritedConstants = $class->inheritedConstants} + + {if $ownConstants || $inheritedConstants} +

Constants summary

+ + + {var $annotations = $constant->annotations} + + + + + +
{$constant->typeHint|typeLinks:$constant|noescape} + + {if $class->internal} + {$constant->name} + {else} + {$constant->name} + {/if} + + +
+ {$constant|shortDescription:true|noescape} +
+ +
+ {$constant|longDescription|noescape} + + {foreach $template->annotationSort($template->annotationFilter($annotations, array('var'))) as $annotation => $descriptions} +

{$annotation|annotationBeautify}

+
+ {foreach $descriptions as $description} + {if $description} + {$description|annotation:$annotation:$constant|noescape}
+ {/if} + {/foreach} +
+ {/foreach} +
+
#{!$constant->valueDefinition|highlightValue:$class}
+ + {foreach $inheritedConstants as $parentName => $constants} +

Constants inherited from {$parentName}

+

+ {foreach $constants as $constant} + {$constant->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + {/if} + + {var $ownProperties = $class->ownProperties} + {var $inheritedProperties = $class->inheritedProperties} + {var $usedProperties = $class->usedProperties} + {var $ownMagicProperties = $class->ownMagicProperties} + {var $inheritedMagicProperties = $class->inheritedMagicProperties} + {var $usedMagicProperties = $class->usedMagicProperties} + + {if $ownProperties || $inheritedProperties || $usedProperties || $ownMagicProperties || $inheritedMagicProperties || $usedMagicProperties} + {define property} + + + {if $property->protected}protected{elseif $property->private}private{else}public{/if} {if $property->static}static{/if} {if $property->readOnly}read-only{elseif $property->writeOnly}write-only{/if} + {$property->typeHint|typeLinks:$property|noescape} + + + + {if $class->internal} + ${$property->name} + {else} + ${$property->name} + {/if} + +
+ {$property|shortDescription:true|noescape} +
+ +
+ {$property|longDescription|noescape} + + {foreach $template->annotationSort($template->annotationFilter($property->annotations, array('var'))) as $annotation => $descriptions} +

{$annotation|annotationBeautify}

+
+ {foreach $descriptions as $description} + {if $description} + {$description|annotation:$annotation:$property|noescape}
+ {/if} + {/foreach} +
+ {/foreach} +
+ +
#{!$property->defaultValueDefinition|highlightValue:$class}
+ + {/define} + +

Properties summary

+ + {foreach $ownProperties as $property} + {include property, property => $property} + {/foreach} +
+ + {foreach $inheritedProperties as $parentName => $properties} +

Properties inherited from {$parentName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedProperties as $traitName => $properties} +

Properties used from {$traitName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + + {if $ownMagicProperties} +

Magic properties

+ + {foreach $ownMagicProperties as $property} + {include property, property => $property} + {/foreach} +
+ {/if} + + {foreach $inheritedMagicProperties as $parentName => $properties} +

Magic properties inherited from {$parentName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + + {foreach $usedMagicProperties as $traitName => $properties} +

Magic properties used from {$traitName}

+

+ {foreach $properties as $property} + ${$property->name}{sep}, {/sep} + {/foreach} +

+ {/foreach} + {/if} + + {else} +
+

+ Documentation of this class could not be generated. +

+

+ Class was originally declared in {$class->fileName|relativePath} and is invalid because of: +

+
    +
  • Class was redeclared in {$reason->getSender()->getFileName()|relativePath}.
  • +
+
+ {/if} +
+{/block} diff --git a/vendor/apigen/apigen/templates/bootstrap/combined.js.latte b/vendor/apigen/apigen/templates/bootstrap/combined.js.latte new file mode 100644 index 0000000..5346f34 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/combined.js.latte @@ -0,0 +1,21 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{contentType javascript} + +var ApiGen = ApiGen || {}; +ApiGen.config = {$config->template}; + +{var $scripts = ['jquery.min.js', 'jquery.cookie.js', 'jquery.sprintf.js', 'jquery.autocomplete.js', 'jquery.sortElements.js', 'main.js']} + +{foreach $scripts as $script} + {file_get_contents("$basePath/js/$script")|noescape} +{/foreach} diff --git a/vendor/apigen/apigen/templates/bootstrap/config.neon b/vendor/apigen/apigen/templates/bootstrap/config.neon new file mode 100644 index 0000000..6ddc2f6 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/config.neon @@ -0,0 +1,56 @@ +require: + min: 2.8.0 + +resources: + resources: resources + +templates: + common: + overview.latte: index.html + combined.js.latte: resources/combined.js + elementlist.js.latte: elementlist.js + 404.latte: 404.html + + main: + package: + filename: package-%s.html + template: package.latte + namespace: + filename: namespace-%s.html + template: namespace.latte + class: + filename: class-%s.html + template: class.latte + constant: + filename: constant-%s.html + template: constant.latte + function: + filename: function-%s.html + template: function.latte + source: + filename: source-%s.html + template: source.latte + tree: + filename: tree.html + template: tree.latte + deprecated: + filename: deprecated.html + template: deprecated.latte + todo: + filename: todo.html + template: todo.latte + + optional: + sitemap: + filename: sitemap.xml + template: sitemap.xml.latte + opensearch: + filename: opensearch.xml + template: opensearch.xml.latte + robots: + filename: robots.txt + template: robots.txt.latte + +options: + elementDetailsCollapsed: Yes + elementsOrder: natural # alphabetical diff --git a/vendor/apigen/apigen/templates/bootstrap/constant.latte b/vendor/apigen/apigen/templates/bootstrap/constant.latte new file mode 100644 index 0000000..23f777a --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/constant.latte @@ -0,0 +1,66 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'constant'} + +{block title}{if $constant->deprecated}Deprecated {/if}Constant {$constant->name}{/block} + +{block content} +
+

Constant {$constant->shortName}

+ + {if $constant->valid} + +
+ {$constant|longDescription|noescape} +
+ +
+ {if $constant->inNamespace()}Namespace: {$constant->namespaceName|namespaceLinks|noescape}
{/if} + {if $constant->inPackage()}Package: {$constant->packageName|packageLinks|noescape}
{/if} + {foreach $template->annotationSort($template->annotationFilter($constant->annotations, array('var'))) as $annotation => $values} + {foreach $values as $value} + {$annotation|annotationBeautify}{if $value}:{/if} + {$value|annotation:$annotation:$constant|noescape}
+ {/foreach} + {/foreach} + Located at {$constant->fileName|relativePath}
+
+ + {var $annotations = $constant->annotations} + +

Value summary

+ + + + + +
{$constant->typeHint|typeLinks:$constant|noescape}{block|strip} + {var $element = $template->resolveElement($constant->valueDefinition, $constant)} + {if $element}{$constant->valueDefinition}{else}{$constant->valueDefinition|highlightValue:$constant|noescape}{/if} + {/block}
+ + {else} +
+

+ Documentation of this constant could not be generated. +

+

+ Constant was originally declared in {$constant->fileName|relativePath} and is invalid because of: +

+
    +
  • Constant was redeclared in {$reason->getSender()->getFileName()|relativePath}.
  • +
+
+ {/if} +
+{/block} diff --git a/vendor/apigen/apigen/templates/bootstrap/deprecated.latte b/vendor/apigen/apigen/templates/bootstrap/deprecated.latte new file mode 100644 index 0000000..ca191a8 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/deprecated.latte @@ -0,0 +1,137 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'deprecated'} + +{block title}Deprecated{/block} + +{block content} +
+

{include title}

+ + {define classes} + + {$class->name} + + {foreach $class->annotations['deprecated'] as $description} + {if $description} + {$description|annotation:'deprecated':$class|noescape}
+ {/if} + {/foreach} + + + {/define} + + {if $deprecatedClasses} +

Classes summary

+ + {include classes, items => $deprecatedClasses} +
+ {/if} + + {if $deprecatedInterfaces} +

Interfaces summary

+ + {include classes, items => $deprecatedInterfaces} +
+ {/if} + + {if $deprecatedTraits} +

Traits summary

+ + {include classes, items => $deprecatedTraits} +
+ {/if} + + {if $deprecatedExceptions} +

Exceptions summary

+ + {include classes, items => $deprecatedExceptions} +
+ {/if} + + {if $deprecatedMethods} +

Methods summary

+ + + + + + +
{$method->declaringClassName}{$method->name}() + {if $method->hasAnnotation('deprecated')} + {foreach $method->annotations['deprecated'] as $description} + {if $description} + {$description|annotation:'deprecated':$method|noescape}
+ {/if} + {/foreach} + {/if} +
+ {/if} + + {if $deprecatedConstants} +

Constants summary

+ + + {if $constant->declaringClassName} + + + {else} + + + {/if} + + +
{$constant->declaringClassName}{$constant->name}{$constant->namespaceName}{$constant->shortName} + {foreach $constant->annotations['deprecated'] as $description} + {if $description} + {$description|annotation:'deprecated':$constant|noescape}
+ {/if} + {/foreach} +
+ {/if} + + {if $deprecatedProperties} +

Properties summary

+ + + + + + +
{$property->declaringClassName}${$property->name} + {foreach $property->annotations['deprecated'] as $description} + {if $description} + {$description|annotation:'deprecated':$property|noescape}
+ {/if} + {/foreach} +
+ {/if} + + {if $deprecatedFunctions} +

Functions summary

+ + + + + + +
{$function->namespaceName}{$function->shortName} + {foreach $function->annotations['deprecated'] as $description} + {if $description} + {$description|annotation:'deprecated':$function|noescape}
+ {/if} + {/foreach} +
+ {/if} +
+{/block} diff --git a/vendor/apigen/apigen/templates/bootstrap/elementlist.js.latte b/vendor/apigen/apigen/templates/bootstrap/elementlist.js.latte new file mode 100644 index 0000000..176b164 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/elementlist.js.latte @@ -0,0 +1,15 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{contentType javascript} + +var ApiGen = ApiGen || {}; +ApiGen.elements = {$elements}; diff --git a/vendor/apigen/apigen/templates/bootstrap/function.latte b/vendor/apigen/apigen/templates/bootstrap/function.latte new file mode 100644 index 0000000..85af19a --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/function.latte @@ -0,0 +1,96 @@ +{* +ApiGen 2.8.0 - API documentation generator for PHP 5.3+ + +Copyright (c) 2010-2011 David Grudl (http://davidgrudl.com) +Copyright (c) 2011-2012 Jaroslav Hanslík (https://github.com/kukulich) +Copyright (c) 2011-2012 Ondřej Nešpor (https://github.com/Andrewsville) +Copyright (c) 2012 Olivier Laviale (https://github.com/olvlvl) + +For the full copyright and license information, please view +the file LICENSE.md that was distributed with this source code. +*} +{layout '@layout.latte'} +{var $active = 'function'} + +{block title}{if $function->deprecated}Deprecated {/if}Function {$function->name}{/block} + +{block content} +
+

Function {$function->shortName}

+ + {if $function->valid} + +
+ {$function|longDescription|noescape} +
+ +
+ {if $function->inNamespace()}Namespace: {$function->namespaceName|namespaceLinks|noescape}
{/if} + {if $function->inPackage()}Package: {$function->packageName|packageLinks|noescape}
{/if} + {foreach $template->annotationSort($template->annotationFilter($function->annotations, array('param', 'return', 'throws'))) as $annotation => $values} + {foreach $values as $value} + {$annotation|annotationBeautify}{if $value}:{/if} + {$value|annotation:$annotation:$function|noescape}
+ {/foreach} + {/foreach} + Located at {$function->fileName|relativePath}
+
+ + {var $annotations = $function->annotations} + + {if $function->numberOfParameters} +

Parameters summary

+ + + + + + +
{$parameter->typeHint|typeLinks:$function|noescape}{block|strip} + {if $parameter->passedByReference}& {/if}${$parameter->name}{if $parameter->defaultValueAvailable} = {$parameter->defaultValueDefinition|highlightPHP:$function|noescape}{elseif $parameter->unlimited},…{/if} + {/block}{$parameter->description|description:$function}
+ {/if} + + {if isset($annotations['return']) && 'void' !== $annotations['return'][0]} +

Return value summary

+ + + + + +
+ {$annotations['return'][0]|typeLinks:$function|noescape} + + {$annotations['return'][0]|description:$function|noescape} +
+ {/if} + + {if isset($annotations['throws'])} +

Thrown exceptions summary

+ + + + + +
+ {$throws|typeLinks:$function|noescape} + + {$throws|description:$function|noescape} +
+ {/if} + + {else} +
+

+ Documentation of this function could not be generated. +

+

+ Function was originally declared in {$function->fileName|relativePath} and is invalid because of: +

+
    +
  • Function was redeclared in {$reason->getSender()->getFileName()|relativePath}.
  • +
+
+ {/if} +
+{/block} diff --git a/vendor/apigen/apigen/templates/bootstrap/js/jquery.autocomplete.js b/vendor/apigen/apigen/templates/bootstrap/js/jquery.autocomplete.js new file mode 100644 index 0000000..b9f51e3 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/js/jquery.autocomplete.js @@ -0,0 +1,843 @@ +/* + * jQuery Autocomplete plugin 1.2.3 + * + * Copyright (c) 2009 Jörn Zaefferer + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + * With small modifications by Alfonso Gómez-Arzola. + * See changelog for details. + * + */ + +;(function($) { + +$.fn.extend({ + autocomplete: function(urlOrData, options) { + var isUrl = typeof urlOrData == "string"; + options = $.extend({}, $.Autocompleter.defaults, { + url: isUrl ? urlOrData : null, + data: isUrl ? null : urlOrData, + delay: isUrl ? $.Autocompleter.defaults.delay : 10, + max: options && !options.scroll ? 10 : 150, + noRecord: "No Records." + }, options); + + // if highlight is set to false, replace it with a do-nothing function + options.highlight = options.highlight || function(value) { return value; }; + + // if the formatMatch option is not specified, then use formatItem for backwards compatibility + options.formatMatch = options.formatMatch || options.formatItem; + + return this.each(function() { + new $.Autocompleter(this, options); + }); + }, + result: function(handler) { + return this.bind("result", handler); + }, + search: function(handler) { + return this.trigger("search", [handler]); + }, + flushCache: function() { + return this.trigger("flushCache"); + }, + setOptions: function(options){ + return this.trigger("setOptions", [options]); + }, + unautocomplete: function() { + return this.trigger("unautocomplete"); + } +}); + +$.Autocompleter = function(input, options) { + + var KEY = { + UP: 38, + DOWN: 40, + DEL: 46, + TAB: 9, + RETURN: 13, + ESC: 27, + COMMA: 188, + PAGEUP: 33, + PAGEDOWN: 34, + BACKSPACE: 8 + }; + + var globalFailure = null; + if(options.failure != null && typeof options.failure == "function") { + globalFailure = options.failure; + } + + // Create $ object for input element + var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass); + + var timeout; + var previousValue = ""; + var cache = $.Autocompleter.Cache(options); + var hasFocus = 0; + var lastKeyPressCode; + var config = { + mouseDownOnSelect: false + }; + var select = $.Autocompleter.Select(options, input, selectCurrent, config); + + var blockSubmit; + + // prevent form submit in opera when selecting with return key + navigator.userAgent.indexOf("Opera") != -1 && $(input.form).bind("submit.autocomplete", function() { + if (blockSubmit) { + blockSubmit = false; + return false; + } + }); + + // older versions of opera don't trigger keydown multiple times while pressed, others don't work with keypress at all + $input.bind((navigator.userAgent.indexOf("Opera") != -1 && !'KeyboardEvent' in window ? "keypress" : "keydown") + ".autocomplete", function(event) { + // a keypress means the input has focus + // avoids issue where input had focus before the autocomplete was applied + hasFocus = 1; + // track last key pressed + lastKeyPressCode = event.keyCode; + switch(event.keyCode) { + + case KEY.UP: + if ( select.visible() ) { + event.preventDefault(); + select.prev(); + } else { + onChange(0, true); + } + break; + + case KEY.DOWN: + if ( select.visible() ) { + event.preventDefault(); + select.next(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEUP: + if ( select.visible() ) { + event.preventDefault(); + select.pageUp(); + } else { + onChange(0, true); + } + break; + + case KEY.PAGEDOWN: + if ( select.visible() ) { + event.preventDefault(); + select.pageDown(); + } else { + onChange(0, true); + } + break; + + // matches also semicolon + case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA: + case KEY.TAB: + case KEY.RETURN: + if( selectCurrent() ) { + // stop default to prevent a form submit, Opera needs special handling + event.preventDefault(); + blockSubmit = true; + return false; + } + break; + + case KEY.ESC: + select.hide(); + break; + + default: + clearTimeout(timeout); + timeout = setTimeout(onChange, options.delay); + break; + } + }).focus(function(){ + // track whether the field has focus, we shouldn't process any + // results if the field no longer has focus + hasFocus++; + }).blur(function() { + hasFocus = 0; + if (!config.mouseDownOnSelect) { + hideResults(); + } + }).click(function() { + // show select when clicking in a focused field + // but if clickFire is true, don't require field + // to be focused to begin with; just show select + if( options.clickFire ) { + if ( !select.visible() ) { + onChange(0, true); + } + } else { + if ( hasFocus++ > 1 && !select.visible() ) { + onChange(0, true); + } + } + }).bind("search", function() { + // TODO why not just specifying both arguments? + var fn = (arguments.length > 1) ? arguments[1] : null; + function findValueCallback(q, data) { + var result; + if( data && data.length ) { + for (var i=0; i < data.length; i++) { + if( data[i].result.toLowerCase() == q.toLowerCase() ) { + result = data[i]; + break; + } + } + } + if( typeof fn == "function" ) fn(result); + else $input.trigger("result", result && [result.data, result.value]); + } + $.each(trimWords($input.val()), function(i, value) { + request(value, findValueCallback, findValueCallback); + }); + }).bind("flushCache", function() { + cache.flush(); + }).bind("setOptions", function() { + $.extend(true, options, arguments[1]); + // if we've updated the data, repopulate + if ( "data" in arguments[1] ) + cache.populate(); + }).bind("unautocomplete", function() { + select.unbind(); + $input.unbind(); + $(input.form).unbind(".autocomplete"); + }); + + + function selectCurrent() { + var selected = select.selected(); + if( !selected ) + return false; + + var v = selected.result; + previousValue = v; + + if ( options.multiple ) { + var words = trimWords($input.val()); + if ( words.length > 1 ) { + var seperator = options.multipleSeparator.length; + var cursorAt = $(input).selection().start; + var wordAt, progress = 0; + $.each(words, function(i, word) { + progress += word.length; + if (cursorAt <= progress) { + wordAt = i; + return false; + } + progress += seperator; + }); + words[wordAt] = v; + // TODO this should set the cursor to the right position, but it gets overriden somewhere + //$.Autocompleter.Selection(input, progress + seperator, progress + seperator); + v = words.join( options.multipleSeparator ); + } + v += options.multipleSeparator; + } + + $input.val(v); + hideResultsNow(); + $input.trigger("result", [selected.data, selected.value]); + return true; + } + + function onChange(crap, skipPrevCheck) { + if( lastKeyPressCode == KEY.DEL ) { + select.hide(); + return; + } + + var currentValue = $input.val(); + + if ( !skipPrevCheck && currentValue == previousValue ) + return; + + previousValue = currentValue; + + currentValue = lastWord(currentValue); + if ( currentValue.length >= options.minChars) { + $input.addClass(options.loadingClass); + if (!options.matchCase) + currentValue = currentValue.toLowerCase(); + request(currentValue, receiveData, hideResultsNow); + } else { + stopLoading(); + select.hide(); + } + }; + + function trimWords(value) { + if (!value) + return [""]; + if (!options.multiple) + return [$.trim(value)]; + return $.map(value.split(options.multipleSeparator), function(word) { + return $.trim(value).length ? $.trim(word) : null; + }); + } + + function lastWord(value) { + if ( !options.multiple ) + return value; + var words = trimWords(value); + if (words.length == 1) + return words[0]; + var cursorAt = $(input).selection().start; + if (cursorAt == value.length) { + words = trimWords(value) + } else { + words = trimWords(value.replace(value.substring(cursorAt), "")); + } + return words[words.length - 1]; + } + + // fills in the input box w/the first match (assumed to be the best match) + // q: the term entered + // sValue: the first matching result + function autoFill(q, sValue){ + // autofill in the complete box w/the first match as long as the user hasn't entered in more data + // if the last user key pressed was backspace, don't autofill + if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) { + // fill in the value (keep the case the user has typed) + $input.val($input.val() + sValue.substring(lastWord(previousValue).length)); + // select the portion of the value not typed by the user (so the next character will erase) + $(input).selection(previousValue.length, previousValue.length + sValue.length); + } + }; + + function hideResults() { + clearTimeout(timeout); + timeout = setTimeout(hideResultsNow, 200); + }; + + function hideResultsNow() { + var wasVisible = select.visible(); + select.hide(); + clearTimeout(timeout); + stopLoading(); + if (options.mustMatch) { + // call search and run callback + $input.search( + function (result){ + // if no value found, clear the input box + if( !result ) { + if (options.multiple) { + var words = trimWords($input.val()).slice(0, -1); + $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); + } + else { + $input.val( "" ); + $input.trigger("result", null); + } + } + } + ); + } + }; + + function receiveData(q, data) { + if ( data && data.length && hasFocus ) { + stopLoading(); + select.display(data, q); + autoFill(q, data[0].value); + select.show(); + } else { + hideResultsNow(); + } + }; + + function request(term, success, failure) { + if (!options.matchCase) + term = term.toLowerCase(); + var data = cache.load(term); + // recieve the cached data + if (data) { + if(data.length) { + success(term, data); + } + else{ + var parsed = options.parse && options.parse(options.noRecord) || parse(options.noRecord); + success(term,parsed); + } + // if an AJAX url has been supplied, try loading the data now + } else if( (typeof options.url == "string") && (options.url.length > 0) ){ + + var extraParams = { + timestamp: +new Date() + }; + $.each(options.extraParams, function(key, param) { + extraParams[key] = typeof param == "function" ? param() : param; + }); + + $.ajax({ + // try to leverage ajaxQueue plugin to abort previous requests + mode: "abort", + // limit abortion to this input + port: "autocomplete" + input.name, + dataType: options.dataType, + url: options.url, + data: $.extend({ + q: lastWord(term), + limit: options.max + }, extraParams), + success: function(data) { + var parsed = options.parse && options.parse(data) || parse(data); + cache.add(term, parsed); + success(term, parsed); + } + }); + } else { + // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match + select.emptyList(); + if(globalFailure != null) { + globalFailure(); + } + else { + failure(term); + } + } + }; + + function parse(data) { + var parsed = []; + var rows = data.split("\n"); + for (var i=0; i < rows.length; i++) { + var row = $.trim(rows[i]); + if (row) { + row = row.split("|"); + parsed[parsed.length] = { + data: row, + value: row[0], + result: options.formatResult && options.formatResult(row, row[0]) || row[0] + }; + } + } + return parsed; + }; + + function stopLoading() { + $input.removeClass(options.loadingClass); + }; + +}; + +$.Autocompleter.defaults = { + inputClass: "ac_input", + resultsClass: "ac_results", + loadingClass: "ac_loading", + minChars: 1, + delay: 400, + matchCase: false, + matchSubset: true, + matchContains: false, + cacheLength: 100, + max: 1000, + mustMatch: false, + extraParams: {}, + selectFirst: true, + formatItem: function(row) { return row[0]; }, + formatMatch: null, + autoFill: false, + width: 0, + multiple: false, + multipleSeparator: " ", + inputFocus: true, + clickFire: false, + highlight: function(value, term) { + return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "$1"); + }, + scroll: true, + scrollHeight: 180, + scrollJumpPosition: true +}; + +$.Autocompleter.Cache = function(options) { + + var data = {}; + var length = 0; + + function matchSubset(s, sub) { + return (new RegExp(sub.toUpperCase().replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1").replace(/[A-Z0-9]/g, function(m, offset) { + return offset === 0 ? '(?:' + m + '|^' + m.toLowerCase() + ')' : '(?:.*' + m + '|' + m.toLowerCase() + ')'; + }))).test(s); // find by initials + }; + + function add(q, value) { + if (length > options.cacheLength){ + flush(); + } + if (!data[q]){ + length++; + } + data[q] = value; + } + + function populate(){ + if( !options.data ) return false; + // track the matches + var stMatchSets = {}, + nullData = 0; + + // no url was specified, we need to adjust the cache length to make sure it fits the local data store + if( !options.url ) options.cacheLength = 1; + + // track all options for minChars = 0 + stMatchSets[""] = []; + + // loop through the array and create a lookup structure + for ( var i = 0, ol = options.data.length; i < ol; i++ ) { + var rawValue = options.data[i]; + // if rawValue is a string, make an array otherwise just reference the array + rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue; + + var value = options.formatMatch(rawValue, i+1, options.data.length); + if ( typeof(value) === 'undefined' || value === false ) + continue; + + var firstChar = value.charAt(0).toLowerCase(); + // if no lookup array for this character exists, look it up now + if( !stMatchSets[firstChar] ) + stMatchSets[firstChar] = []; + + // if the match is a string + var row = { + value: value, + data: rawValue, + result: options.formatResult && options.formatResult(rawValue) || value + }; + + // push the current match into the set list + stMatchSets[firstChar].push(row); + + // keep track of minChars zero items + if ( nullData++ < options.max ) { + stMatchSets[""].push(row); + } + }; + + // add the data items to the cache + $.each(stMatchSets, function(i, value) { + // increase the cache size + options.cacheLength++; + // add to the cache + add(i, value); + }); + } + + // populate any existing data + setTimeout(populate, 25); + + function flush(){ + data = {}; + length = 0; + } + + return { + flush: flush, + add: add, + populate: populate, + load: function(q) { + if (!options.cacheLength || !length) + return null; + /* + * if dealing w/local data and matchContains than we must make sure + * to loop through all the data collections looking for matches + */ + if( !options.url && options.matchContains ){ + // track all matches + var csub = []; + // loop through all the data grids for matches + for( var k in data ){ + // don't search through the stMatchSets[""] (minChars: 0) cache + // this prevents duplicates + if( k.length > 0 ){ + var c = data[k]; + $.each(c, function(i, x) { + // if we've got a match, add it to the array + if (matchSubset(x.value, q)) { + csub.push(x); + } + }); + } + } + return csub; + } else + // if the exact item exists, use it + if (data[q]){ + return data[q]; + } else + if (options.matchSubset) { + for (var i = q.length - 1; i >= options.minChars; i--) { + var c = data[q.substr(0, i)]; + if (c) { + var csub = []; + $.each(c, function(i, x) { + if (matchSubset(x.value, q)) { + csub[csub.length] = x; + } + }); + return csub; + } + } + } + return null; + } + }; +}; + +$.Autocompleter.Select = function (options, input, select, config) { + var CLASSES = { + ACTIVE: "ac_over" + }; + + var listItems, + active = -1, + data, + term = "", + needsInit = true, + element, + list; + + // Create results + function init() { + if (!needsInit) + return; + element = $("
") + .hide() + .addClass(options.resultsClass) + .css("position", "absolute") + .appendTo(document.body) + .hover(function(event) { + // Browsers except FF do not fire mouseup event on scrollbars, resulting in mouseDownOnSelect remaining true, and results list not always hiding. + if($(this).is(":visible")) { + input.focus(); + } + config.mouseDownOnSelect = false; + }); + + list = $("
    ").appendTo(element).mouseover( function(event) { + if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { + active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); + $(target(event)).addClass(CLASSES.ACTIVE); + } + }).click(function(event) { + $(target(event)).addClass(CLASSES.ACTIVE); + select(); + if( options.inputFocus ) + input.focus(); + return false; + }).mousedown(function() { + config.mouseDownOnSelect = true; + }).mouseup(function() { + config.mouseDownOnSelect = false; + }); + + if( options.width > 0 ) + element.css("width", options.width); + + needsInit = false; + } + + function target(event) { + var element = event.target; + while(element && element.tagName != "LI") + element = element.parentNode; + // more fun with IE, sometimes event.target is empty, just ignore it then + if(!element) + return []; + return element; + } + + function moveSelect(step) { + listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE); + movePosition(step); + var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); + if(options.scroll) { + var offset = 0; + listItems.slice(0, active).each(function() { + offset += this.offsetHeight; + }); + if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) { + list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight()); + } else if(offset < list.scrollTop()) { + list.scrollTop(offset); + } + } + }; + + function movePosition(step) { + if (options.scrollJumpPosition || (!options.scrollJumpPosition && !((step < 0 && active == 0) || (step > 0 && active == listItems.size() - 1)) )) { + active += step; + if (active < 0) { + active = listItems.size() - 1; + } else if (active >= listItems.size()) { + active = 0; + } + } + } + + + function limitNumberOfItems(available) { + return options.max && options.max < available + ? options.max + : available; + } + + function fillList() { + list.empty(); + var max = limitNumberOfItems(data.length); + for (var i=0; i < max; i++) { + if (!data[i]) + continue; + var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); + if ( formatted === false ) + continue; + var li = $("
  • ").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; + $.data(li, "ac_data", data[i]); + } + listItems = list.find("li"); + if ( options.selectFirst ) { + listItems.slice(0, 1).addClass(CLASSES.ACTIVE); + active = 0; + } + // apply bgiframe if available + if ( $.fn.bgiframe ) + list.bgiframe(); + } + + return { + display: function(d, q) { + init(); + data = d; + term = q; + fillList(); + }, + next: function() { + moveSelect(1); + }, + prev: function() { + moveSelect(-1); + }, + pageUp: function() { + if (active != 0 && active - 8 < 0) { + moveSelect( -active ); + } else { + moveSelect(-8); + } + }, + pageDown: function() { + if (active != listItems.size() - 1 && active + 8 > listItems.size()) { + moveSelect( listItems.size() - 1 - active ); + } else { + moveSelect(8); + } + }, + hide: function() { + element && element.hide(); + listItems && listItems.removeClass(CLASSES.ACTIVE); + active = -1; + }, + visible : function() { + return element && element.is(":visible"); + }, + current: function() { + return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]); + }, + show: function() { + var offset = $(input).offset(); + element.css({ + width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(), + top: offset.top + input.offsetHeight, + left: offset.left + }).show(); + if(options.scroll) { + list.scrollTop(0); + list.css({ + maxHeight: options.scrollHeight, + overflow: 'auto' + }); + + if(navigator.userAgent.indexOf("MSIE") != -1 && typeof document.body.style.maxHeight === "undefined") { + var listHeight = 0; + listItems.each(function() { + listHeight += this.offsetHeight; + }); + var scrollbarsVisible = listHeight > options.scrollHeight; + list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight ); + if (!scrollbarsVisible) { + // IE doesn't recalculate width when scrollbar disappears + listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) ); + } + } + + } + }, + selected: function() { + var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE); + return selected && selected.length && $.data(selected[0], "ac_data"); + }, + emptyList: function (){ + list && list.empty(); + }, + unbind: function() { + element && element.remove(); + } + }; +}; + +$.fn.selection = function(start, end) { + if (start !== undefined) { + return this.each(function() { + if( this.createTextRange ){ + var selRange = this.createTextRange(); + if (end === undefined || start == end) { + selRange.move("character", start); + selRange.select(); + } else { + selRange.collapse(true); + selRange.moveStart("character", start); + selRange.moveEnd("character", end); + selRange.select(); + } + } else if( this.setSelectionRange ){ + this.setSelectionRange(start, end); + } else if( this.selectionStart ){ + this.selectionStart = start; + this.selectionEnd = end; + } + }); + } + var field = this[0]; + if ( field.createTextRange ) { + var range = document.selection.createRange(), + orig = field.value, + teststring = "<->", + textLength = range.text.length; + range.text = teststring; + var caretAt = field.value.indexOf(teststring); + field.value = orig; + this.selection(caretAt, caretAt + textLength); + return { + start: caretAt, + end: caretAt + textLength + } + } else if( field.selectionStart !== undefined ){ + return { + start: field.selectionStart, + end: field.selectionEnd + } + } +}; + +})(jQuery); diff --git a/vendor/apigen/apigen/templates/bootstrap/js/jquery.cookie.js b/vendor/apigen/apigen/templates/bootstrap/js/jquery.cookie.js new file mode 100644 index 0000000..3fb201c --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/js/jquery.cookie.js @@ -0,0 +1,90 @@ +/*! + * jQuery Cookie Plugin v1.3.1 + * https://github.com/carhartl/jquery-cookie + * + * Copyright 2013 Klaus Hartl + * Released under the MIT license + */ +(function ($, document, undefined) { + + var pluses = /\+/g; + + function raw(s) { + return s; + } + + function decoded(s) { + return unRfc2068(decodeURIComponent(s.replace(pluses, ' '))); + } + + function unRfc2068(value) { + if (value.indexOf('"') === 0) { + // This is a quoted cookie as according to RFC2068, unescape + value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); + } + return value; + } + + function fromJSON(value) { + return config.json ? JSON.parse(value) : value; + } + + var config = $.cookie = function (key, value, options) { + + // write + if (value !== undefined) { + options = $.extend({}, config.defaults, options); + + if (value === null) { + options.expires = -1; + } + + if (typeof options.expires === 'number') { + var days = options.expires, t = options.expires = new Date(); + t.setDate(t.getDate() + days); + } + + value = config.json ? JSON.stringify(value) : String(value); + + return (document.cookie = [ + encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value), + options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE + options.path ? '; path=' + options.path : '', + options.domain ? '; domain=' + options.domain : '', + options.secure ? '; secure' : '' + ].join('')); + } + + // read + var decode = config.raw ? raw : decoded; + var cookies = document.cookie.split('; '); + var result = key ? null : {}; + for (var i = 0, l = cookies.length; i < l; i++) { + var parts = cookies[i].split('='); + var name = decode(parts.shift()); + var cookie = decode(parts.join('=')); + + if (key && key === name) { + result = fromJSON(cookie); + break; + } + + if (!key) { + result[name] = fromJSON(cookie); + } + } + + return result; + }; + + config.defaults = {}; + + $.removeCookie = function (key, options) { + if ($.cookie(key) !== null) { + $.cookie(key, null, options); + return true; + } + return false; + }; + +})(jQuery, document); diff --git a/vendor/apigen/apigen/templates/bootstrap/js/jquery.min.js b/vendor/apigen/apigen/templates/bootstrap/js/jquery.min.js new file mode 100644 index 0000000..ce1b6b6 --- /dev/null +++ b/vendor/apigen/apigen/templates/bootstrap/js/jquery.min.js @@ -0,0 +1,5 @@ +/*! jQuery v1.10.2 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +*/ +(function(e,t){var n,r,i=typeof t,o=e.location,a=e.document,s=a.documentElement,l=e.jQuery,u=e.$,c={},p=[],f="1.10.2",d=p.concat,h=p.push,g=p.slice,m=p.indexOf,y=c.toString,v=c.hasOwnProperty,b=f.trim,x=function(e,t){return new x.fn.init(e,t,r)},w=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=/\S+/g,C=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,k=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,E=/^[\],:{}\s]*$/,S=/(?:^|:|,)(?:\s*\[)+/g,A=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,j=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,D=/^-ms-/,L=/-([\da-z])/gi,H=function(e,t){return t.toUpperCase()},q=function(e){(a.addEventListener||"load"===e.type||"complete"===a.readyState)&&(_(),x.ready())},_=function(){a.addEventListener?(a.removeEventListener("DOMContentLoaded",q,!1),e.removeEventListener("load",q,!1)):(a.detachEvent("onreadystatechange",q),e.detachEvent("onload",q))};x.fn=x.prototype={jquery:f,constructor:x,init:function(e,n,r){var i,o;if(!e)return this;if("string"==typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof x?n[0]:n,x.merge(this,x.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:a,!0)),k.test(i[1])&&x.isPlainObject(n))for(i in n)x.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(o=a.getElementById(i[2]),o&&o.parentNode){if(o.id!==i[2])return r.find(e);this.length=1,this[0]=o}return this.context=a,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return g.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(g.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},l=1,u=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},l=2),"object"==typeof s||x.isFunction(s)||(s={}),u===l&&(s=this,--l);u>l;l++)if(null!=(o=arguments[l]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(x.isPlainObject(r)||(n=x.isArray(r)))?(n?(n=!1,a=e&&x.isArray(e)?e:[]):a=e&&x.isPlainObject(e)?e:{},s[i]=x.extend(c,a,r)):r!==t&&(s[i]=r));return s},x.extend({expando:"jQuery"+(f+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=l),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){if(e===!0?!--x.readyWait:!x.isReady){if(!a.body)return setTimeout(x.ready);x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(a,[x]),x.fn.trigger&&x(a).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray||function(e){return"array"===x.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?c[y.call(e)]||"object":typeof e},isPlainObject:function(e){var n;if(!e||"object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!v.call(e,"constructor")&&!v.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(r){return!1}if(x.support.ownLast)for(n in e)return v.call(e,n);for(n in e);return n===t||v.call(e,n)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||a;var r=k.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=x.trim(n),n&&E.test(n.replace(A,"@").replace(j,"]").replace(S,"")))?Function("return "+n)():(x.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||x.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&x.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(D,"ms-").replace(L,H)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:b&&!b.call("\ufeff\u00a0")?function(e){return null==e?"":b.call(e)}:function(e){return null==e?"":(e+"").replace(C,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(m)return m.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return d.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),x.isFunction(e)?(r=g.call(arguments,2),i=function(){return e.apply(n||this,r.concat(g.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):t},access:function(e,n,r,i,o,a,s){var l=0,u=e.length,c=null==r;if("object"===x.type(r)){o=!0;for(l in r)x.access(e,n,l,r[l],!0,a,s)}else if(i!==t&&(o=!0,x.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(x(e),n)})),n))for(;u>l;l++)n(e[l],r,s?i:i.call(e[l],l,n(e[l],r)));return o?e:c?n.call(e):u?n(e[0],r):a},now:function(){return(new Date).getTime()},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),x.ready.promise=function(t){if(!n)if(n=x.Deferred(),"complete"===a.readyState)setTimeout(x.ready);else if(a.addEventListener)a.addEventListener("DOMContentLoaded",q,!1),e.addEventListener("load",q,!1);else{a.attachEvent("onreadystatechange",q),e.attachEvent("onload",q);var r=!1;try{r=null==e.frameElement&&a.documentElement}catch(i){}r&&r.doScroll&&function o(){if(!x.isReady){try{r.doScroll("left")}catch(e){return setTimeout(o,50)}_(),x.ready()}}()}return n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){c["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=x(a),function(e,t){var n,r,i,o,a,s,l,u,c,p,f,d,h,g,m,y,v,b="sizzle"+-new Date,w=e.document,T=0,C=0,N=st(),k=st(),E=st(),S=!1,A=function(e,t){return e===t?(S=!0,0):0},j=typeof t,D=1<<31,L={}.hasOwnProperty,H=[],q=H.pop,_=H.push,M=H.push,O=H.slice,F=H.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},B="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",P="[\\x20\\t\\r\\n\\f]",R="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",W=R.replace("w","w#"),$="\\["+P+"*("+R+")"+P+"*(?:([*^$|!~]?=)"+P+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+W+")|)|)"+P+"*\\]",I=":("+R+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+$.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+P+"+|((?:^|[^\\\\])(?:\\\\.)*)"+P+"+$","g"),X=RegExp("^"+P+"*,"+P+"*"),U=RegExp("^"+P+"*([>+~]|"+P+")"+P+"*"),V=RegExp(P+"*[+~]"),Y=RegExp("="+P+"*([^\\]'\"]*)"+P+"*\\]","g"),J=RegExp(I),G=RegExp("^"+W+"$"),Q={ID:RegExp("^#("+R+")"),CLASS:RegExp("^\\.("+R+")"),TAG:RegExp("^("+R.replace("w","w*")+")"),ATTR:RegExp("^"+$),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+P+"*(even|odd|(([+-]|)(\\d*)n|)"+P+"*(?:([+-]|)"+P+"*(\\d+)|))"+P+"*\\)|)","i"),bool:RegExp("^(?:"+B+")$","i"),needsContext:RegExp("^"+P+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+P+"*((?:-\\d)?\\d*)"+P+"*\\)|)(?=[^-]|$)","i")},K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,et=/^(?:input|select|textarea|button)$/i,tt=/^h\d$/i,nt=/'|\\/g,rt=RegExp("\\\\([\\da-f]{1,6}"+P+"?|("+P+")|.)","ig"),it=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{M.apply(H=O.call(w.childNodes),w.childNodes),H[w.childNodes.length].nodeType}catch(ot){M={apply:H.length?function(e,t){_.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function at(e,t,n,i){var o,a,s,l,u,c,d,m,y,x;if((t?t.ownerDocument||t:w)!==f&&p(t),t=t||f,n=n||[],!e||"string"!=typeof e)return n;if(1!==(l=t.nodeType)&&9!==l)return[];if(h&&!i){if(o=Z.exec(e))if(s=o[1]){if(9===l){if(a=t.getElementById(s),!a||!a.parentNode)return n;if(a.id===s)return n.push(a),n}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(s))&&v(t,a)&&a.id===s)return n.push(a),n}else{if(o[2])return M.apply(n,t.getElementsByTagName(e)),n;if((s=o[3])&&r.getElementsByClassName&&t.getElementsByClassName)return M.apply(n,t.getElementsByClassName(s)),n}if(r.qsa&&(!g||!g.test(e))){if(m=d=b,y=t,x=9===l&&e,1===l&&"object"!==t.nodeName.toLowerCase()){c=mt(e),(d=t.getAttribute("id"))?m=d.replace(nt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",u=c.length;while(u--)c[u]=m+yt(c[u]);y=V.test(e)&&t.parentNode||t,x=c.join(",")}if(x)try{return M.apply(n,y.querySelectorAll(x)),n}catch(T){}finally{d||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,n,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>o.cacheLength&&delete t[e.shift()],t[n]=r}return t}function lt(e){return e[b]=!0,e}function ut(e){var t=f.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function ct(e,t){var n=e.split("|"),r=e.length;while(r--)o.attrHandle[n[r]]=t}function pt(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function dt(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return lt(function(t){return t=+t,lt(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}s=at.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},r=at.support={},p=at.setDocument=function(e){var n=e?e.ownerDocument||e:w,i=n.defaultView;return n!==f&&9===n.nodeType&&n.documentElement?(f=n,d=n.documentElement,h=!s(n),i&&i.attachEvent&&i!==i.top&&i.attachEvent("onbeforeunload",function(){p()}),r.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),r.getElementsByTagName=ut(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),r.getElementsByClassName=ut(function(e){return e.innerHTML="
    ",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),r.getById=ut(function(e){return d.appendChild(e).id=b,!n.getElementsByName||!n.getElementsByName(b).length}),r.getById?(o.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){return e.getAttribute("id")===t}}):(delete o.find.ID,o.filter.ID=function(e){var t=e.replace(rt,it);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),o.find.TAG=r.getElementsByTagName?function(e,n){return typeof n.getElementsByTagName!==j?n.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},o.find.CLASS=r.getElementsByClassName&&function(e,n){return typeof n.getElementsByClassName!==j&&h?n.getElementsByClassName(e):t},m=[],g=[],(r.qsa=K.test(n.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||g.push("\\["+P+"*(?:value|"+B+")"),e.querySelectorAll(":checked").length||g.push(":checked")}),ut(function(e){var t=n.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&g.push("[*^$]="+P+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||g.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),g.push(",.*:")})),(r.matchesSelector=K.test(y=d.webkitMatchesSelector||d.mozMatchesSelector||d.oMatchesSelector||d.msMatchesSelector))&&ut(function(e){r.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),m.push("!=",I)}),g=g.length&&RegExp(g.join("|")),m=m.length&&RegExp(m.join("|")),v=K.test(d.contains)||d.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},A=d.compareDocumentPosition?function(e,t){if(e===t)return S=!0,0;var i=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t);return i?1&i||!r.sortDetached&&t.compareDocumentPosition(e)===i?e===n||v(w,e)?-1:t===n||v(w,t)?1:c?F.call(c,e)-F.call(c,t):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return S=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:c?F.call(c,e)-F.call(c,t):0;if(o===a)return pt(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++;return i?pt(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},n):f},at.matches=function(e,t){return at(e,null,null,t)},at.matchesSelector=function(e,t){if((e.ownerDocument||e)!==f&&p(e),t=t.replace(Y,"='$1']"),!(!r.matchesSelector||!h||m&&m.test(t)||g&&g.test(t)))try{var n=y.call(e,t);if(n||r.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(i){}return at(t,f,null,[e]).length>0},at.contains=function(e,t){return(e.ownerDocument||e)!==f&&p(e),v(e,t)},at.attr=function(e,n){(e.ownerDocument||e)!==f&&p(e);var i=o.attrHandle[n.toLowerCase()],a=i&&L.call(o.attrHandle,n.toLowerCase())?i(e,n,!h):t;return a===t?r.attributes||!h?e.getAttribute(n):(a=e.getAttributeNode(n))&&a.specified?a.value:null:a},at.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},at.uniqueSort=function(e){var t,n=[],i=0,o=0;if(S=!r.detectDuplicates,c=!r.sortStable&&e.slice(0),e.sort(A),S){while(t=e[o++])t===e[o]&&(i=n.push(o));while(i--)e.splice(n[i],1)}return e},a=at.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=a(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=a(t);return n},o=at.selectors={cacheLength:50,createPseudo:lt,match:Q,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(rt,it),e[3]=(e[4]||e[5]||"").replace(rt,it),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||at.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&at.error(e[0]),e},PSEUDO:function(e){var n,r=!e[5]&&e[2];return Q.CHILD.test(e[0])?null:(e[3]&&e[4]!==t?e[2]=e[4]:r&&J.test(r)&&(n=mt(r,!0))&&(n=r.indexOf(")",r.length-n)-r.length)&&(e[0]=e[0].slice(0,n),e[2]=r.slice(0,n)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(rt,it).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=RegExp("(^|"+P+")"+e+"("+P+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=at.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,l){var u,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!l&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[b]||(m[b]={}),u=c[e]||[],d=u[0]===T&&u[1],f=u[0]===T&&u[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[T,d,f];break}}else if(v&&(u=(t[b]||(t[b]={}))[e])&&u[0]===T)f=u[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[b]||(p[b]={}))[e]=[T,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=o.pseudos[e]||o.setFilters[e.toLowerCase()]||at.error("unsupported pseudo: "+e);return r[b]?r(t):r.length>1?(n=[e,e,"",t],o.setFilters.hasOwnProperty(e.toLowerCase())?lt(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=F.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:lt(function(e){var t=[],n=[],r=l(e.replace(z,"$1"));return r[b]?lt(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:lt(function(e){return function(t){return at(e,t).length>0}}),contains:lt(function(e){return function(t){return(t.textContent||t.innerText||a(t)).indexOf(e)>-1}}),lang:lt(function(e){return G.test(e||"")||at.error("unsupported lang: "+e),e=e.replace(rt,it).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===d},focus:function(e){return e===f.activeElement&&(!f.hasFocus||f.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!o.pseudos.empty(e)},header:function(e){return tt.test(e.nodeName)},input:function(e){return et.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},o.pseudos.nth=o.pseudos.eq;for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})o.pseudos[n]=ft(n);for(n in{submit:!0,reset:!0})o.pseudos[n]=dt(n);function gt(){}gt.prototype=o.filters=o.pseudos,o.setFilters=new gt;function mt(e,t){var n,r,i,a,s,l,u,c=k[e+" "];if(c)return t?0:c.slice(0);s=e,l=[],u=o.preFilter;while(s){(!n||(r=X.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),l.push(i=[])),n=!1,(r=U.exec(s))&&(n=r.shift(),i.push({value:n,type:r[0].replace(z," ")}),s=s.slice(n.length));for(a in o.filter)!(r=Q[a].exec(s))||u[a]&&!(r=u[a](r))||(n=r.shift(),i.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?at.error(e):k(e,l).slice(0)}function yt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function vt(e,t,n){var r=t.dir,o=n&&"parentNode"===r,a=C++;return t.first?function(t,n,i){while(t=t[r])if(1===t.nodeType||o)return e(t,n,i)}:function(t,n,s){var l,u,c,p=T+" "+a;if(s){while(t=t[r])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[r])if(1===t.nodeType||o)if(c=t[b]||(t[b]={}),(u=c[r])&&u[0]===p){if((l=u[1])===!0||l===i)return l===!0}else if(u=c[r]=[p],u[1]=e(t,n,s)||i,u[1]===!0)return!0}}function bt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,a=[],s=0,l=e.length,u=null!=t;for(;l>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),u&&t.push(s));return a}function wt(e,t,n,r,i,o){return r&&!r[b]&&(r=wt(r)),i&&!i[b]&&(i=wt(i,o)),lt(function(o,a,s,l){var u,c,p,f=[],d=[],h=a.length,g=o||Nt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:xt(g,f,e,s,l),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,l),r){u=xt(y,d),r(u,[],s,l),c=u.length;while(c--)(p=u[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){u=[],c=y.length;while(c--)(p=y[c])&&u.push(m[c]=p);i(null,y=[],u,l)}c=y.length;while(c--)(p=y[c])&&(u=i?F.call(o,p):f[c])>-1&&(o[u]=!(a[u]=p))}}else y=xt(y===a?y.splice(h,y.length):y),i?i(null,a,y,l):M.apply(a,y)})}function Tt(e){var t,n,r,i=e.length,a=o.relative[e[0].type],s=a||o.relative[" "],l=a?1:0,c=vt(function(e){return e===t},s,!0),p=vt(function(e){return F.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;i>l;l++)if(n=o.relative[e[l].type])f=[vt(bt(f),n)];else{if(n=o.filter[e[l].type].apply(null,e[l].matches),n[b]){for(r=++l;i>r;r++)if(o.relative[e[r].type])break;return wt(l>1&&bt(f),l>1&&yt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&Tt(e.slice(l,r)),i>r&&Tt(e=e.slice(r)),i>r&&yt(e))}f.push(n)}return bt(f)}function Ct(e,t){var n=0,r=t.length>0,a=e.length>0,s=function(s,l,c,p,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,C=u,N=s||a&&o.find.TAG("*",d&&l.parentNode||l),k=T+=null==C?1:Math.random()||.1;for(w&&(u=l!==f&&l,i=n);null!=(h=N[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,l,c)){p.push(h);break}w&&(T=k,i=++n)}r&&((h=!m&&h)&&v--,s&&x.push(h))}if(v+=b,r&&b!==v){g=0;while(m=t[g++])m(x,y,l,c);if(s){if(v>0)while(b--)x[b]||y[b]||(y[b]=q.call(p));y=xt(y)}M.apply(p,y),w&&!s&&y.length>0&&v+t.length>1&&at.uniqueSort(p)}return w&&(T=k,u=C),x};return r?lt(s):s}l=at.compile=function(e,t){var n,r=[],i=[],o=E[e+" "];if(!o){t||(t=mt(e)),n=t.length;while(n--)o=Tt(t[n]),o[b]?r.push(o):i.push(o);o=E(e,Ct(i,r))}return o};function Nt(e,t,n){var r=0,i=t.length;for(;i>r;r++)at(e,t[r],n);return n}function kt(e,t,n,i){var a,s,u,c,p,f=mt(e);if(!i&&1===f.length){if(s=f[0]=f[0].slice(0),s.length>2&&"ID"===(u=s[0]).type&&r.getById&&9===t.nodeType&&h&&o.relative[s[1].type]){if(t=(o.find.ID(u.matches[0].replace(rt,it),t)||[])[0],!t)return n;e=e.slice(s.shift().value.length)}a=Q.needsContext.test(e)?0:s.length;while(a--){if(u=s[a],o.relative[c=u.type])break;if((p=o.find[c])&&(i=p(u.matches[0].replace(rt,it),V.test(s[0].type)&&t.parentNode||t))){if(s.splice(a,1),e=i.length&&yt(s),!e)return M.apply(n,i),n;break}}}return l(e,f)(i,t,!h,n,V.test(e)),n}r.sortStable=b.split("").sort(A).join("")===b,r.detectDuplicates=S,p(),r.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(f.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||ct("type|href|height|width",function(e,n,r){return r?t:e.getAttribute(n,"type"===n.toLowerCase()?1:2)}),r.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||ct("value",function(e,n,r){return r||"input"!==e.nodeName.toLowerCase()?t:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||ct(B,function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&i.specified?i.value:e[n]===!0?n.toLowerCase():null}),x.find=at,x.expr=at.selectors,x.expr[":"]=x.expr.pseudos,x.unique=at.uniqueSort,x.text=at.getText,x.isXMLDoc=at.isXML,x.contains=at.contains}(e);var O={};function F(e){var t=O[e]={};return x.each(e.match(T)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?O[e]||F(e):x.extend({},e);var n,r,i,o,a,s,l=[],u=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=l.length,n=!0;l&&o>a;a++)if(l[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,l&&(u?u.length&&c(u.shift()):r?l=[]:p.disable())},p={add:function(){if(l){var t=l.length;(function i(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&p.has(n)||l.push(n):n&&n.length&&"string"!==r&&i(n)})})(arguments),n?o=l.length:r&&(s=t,c(r))}return this},remove:function(){return l&&x.each(arguments,function(e,t){var r;while((r=x.inArray(t,l,r))>-1)l.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?x.inArray(e,l)>-1:!(!l||!l.length)},empty:function(){return l=[],o=0,this},disable:function(){return l=u=r=t,this},disabled:function(){return!l},lock:function(){return u=t,r||p.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!l||i&&!u||(t=t||[],t=[e,t.slice?t.slice():t],n?u.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var a=o[0],s=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=g.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?g.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,l,u;if(r>1)for(s=Array(r),l=Array(r),u=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(a(t,u,n)).fail(o.reject).progress(a(t,l,s)):--i;return i||o.resolveWith(u,n),o.promise()}}),x.support=function(t){var n,r,o,s,l,u,c,p,f,d=a.createElement("div");if(d.setAttribute("className","t"),d.innerHTML="
    a",n=d.getElementsByTagName("*")||[],r=d.getElementsByTagName("a")[0],!r||!r.style||!n.length)return t;s=a.createElement("select"),u=s.appendChild(a.createElement("option")),o=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t.getSetAttribute="t"!==d.className,t.leadingWhitespace=3===d.firstChild.nodeType,t.tbody=!d.getElementsByTagName("tbody").length,t.htmlSerialize=!!d.getElementsByTagName("link").length,t.style=/top/.test(r.getAttribute("style")),t.hrefNormalized="/a"===r.getAttribute("href"),t.opacity=/^0.5/.test(r.style.opacity),t.cssFloat=!!r.style.cssFloat,t.checkOn=!!o.value,t.optSelected=u.selected,t.enctype=!!a.createElement("form").enctype,t.html5Clone="<:nav>"!==a.createElement("nav").cloneNode(!0).outerHTML,t.inlineBlockNeedsLayout=!1,t.shrinkWrapBlocks=!1,t.pixelPosition=!1,t.deleteExpando=!0,t.noCloneEvent=!0,t.reliableMarginRight=!0,t.boxSizingReliable=!0,o.checked=!0,t.noCloneChecked=o.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!u.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}o=a.createElement("input"),o.setAttribute("value",""),t.input=""===o.getAttribute("value"),o.value="t",o.setAttribute("type","radio"),t.radioValue="t"===o.value,o.setAttribute("checked","t"),o.setAttribute("name","t"),l=a.createDocumentFragment(),l.appendChild(o),t.appendChecked=o.checked,t.checkClone=l.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip;for(f in x(t))break;return t.ownLast="0"!==f,x(function(){var n,r,o,s="padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",l=a.getElementsByTagName("body")[0];l&&(n=a.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",l.appendChild(n).appendChild(d),d.innerHTML="
    t
    ",o=d.getElementsByTagName("td"),o[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===o[0].offsetHeight,o[0].style.display="",o[1].style.display="none",t.reliableHiddenOffsets=p&&0===o[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",x.swap(l,null!=l.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===d.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).width,r=d.appendChild(a.createElement("div")),r.style.cssText=d.style.cssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="
    ",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(l.style.zoom=1)),l.removeChild(n),n=d=o=r=null)}),n=s=l=u=r=o=null,t +}({});var B=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;function R(e,n,r,i){if(x.acceptData(e)){var o,a,s=x.expando,l=e.nodeType,u=l?x.cache:e,c=l?e[s]:e[s]&&s;if(c&&u[c]&&(i||u[c].data)||r!==t||"string"!=typeof n)return c||(c=l?e[s]=p.pop()||x.guid++:s),u[c]||(u[c]=l?{}:{toJSON:x.noop}),("object"==typeof n||"function"==typeof n)&&(i?u[c]=x.extend(u[c],n):u[c].data=x.extend(u[c].data,n)),a=u[c],i||(a.data||(a.data={}),a=a.data),r!==t&&(a[x.camelCase(n)]=r),"string"==typeof n?(o=a[n],null==o&&(o=a[x.camelCase(n)])):o=a,o}}function W(e,t,n){if(x.acceptData(e)){var r,i,o=e.nodeType,a=o?x.cache:e,s=o?e[x.expando]:x.expando;if(a[s]){if(t&&(r=n?a[s]:a[s].data)){x.isArray(t)?t=t.concat(x.map(t,x.camelCase)):t in r?t=[t]:(t=x.camelCase(t),t=t in r?[t]:t.split(" ")),i=t.length;while(i--)delete r[t[i]];if(n?!I(r):!x.isEmptyObject(r))return}(n||(delete a[s].data,I(a[s])))&&(o?x.cleanData([e],!0):x.support.deleteExpando||a!=a.window?delete a[s]:a[s]=null)}}}x.extend({cache:{},noData:{applet:!0,embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(e){return e=e.nodeType?x.cache[e[x.expando]]:e[x.expando],!!e&&!I(e)},data:function(e,t,n){return R(e,t,n)},removeData:function(e,t){return W(e,t)},_data:function(e,t,n){return R(e,t,n,!0)},_removeData:function(e,t){return W(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&x.noData[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")===t}}),x.fn.extend({data:function(e,n){var r,i,o=null,a=0,s=this[0];if(e===t){if(this.length&&(o=x.data(s),1===s.nodeType&&!x._data(s,"parsedAttrs"))){for(r=s.attributes;r.length>a;a++)i=r[a].name,0===i.indexOf("data-")&&(i=x.camelCase(i.slice(5)),$(s,i,o[i]));x._data(s,"parsedAttrs",!0)}return o}return"object"==typeof e?this.each(function(){x.data(this,e)}):arguments.length>1?this.each(function(){x.data(this,e,n)}):s?$(s,e,x.data(s,e)):null},removeData:function(e){return this.each(function(){x.removeData(this,e)})}});function $(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(P,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:B.test(r)?x.parseJSON(r):r}catch(o){}x.data(e,n,r)}else r=t}return r}function I(e){var t;for(t in e)if(("data"!==t||!x.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}x.extend({queue:function(e,n,r){var i;return e?(n=(n||"fx")+"queue",i=x._data(e,n),r&&(!i||x.isArray(r)?i=x._data(e,n,x.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),a=function(){x.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return x._data(e,n)||x._data(e,n,{empty:x.Callbacks("once memory").add(function(){x._removeData(e,t+"queue"),x._removeData(e,n)})})}}),x.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?x.queue(this[0],e):n===t?this:this.each(function(){var t=x.queue(this,e,n);x._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=x.Deferred(),a=this,s=this.length,l=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=x._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(l));return l(),o.promise(n)}});var z,X,U=/[\t\r\n\f]/g,V=/\r/g,Y=/^(?:input|select|textarea|button|object)$/i,J=/^(?:a|area)$/i,G=/^(?:checked|selected)$/i,Q=x.support.getSetAttribute,K=x.support.input;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return e=x.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,l="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,l=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(l)for(t=(e||"").match(T)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(U," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,r=0,o=x(this),a=e.match(T)||[];while(t=a[r++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===i||"boolean"===n)&&(this.className&&x._data(this,"__className__",this.className),this.className=this.className||e===!1?"":x._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(U," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=x.isFunction(e),this.each(function(n){var o;1===this.nodeType&&(o=i?e.call(this,n,x(this).val()):e,null==o?o="":"number"==typeof o?o+="":x.isArray(o)&&(o=x.map(o,function(e){return null==e?"":e+""})),r=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=x.valHooks[o.type]||x.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(V,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=x.find.attr(e,"value");return null!=t?t:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,l=0>i?s:o?i:0;for(;s>l;l++)if(n=r[l],!(!n.selected&&l!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),a=i.length;while(a--)r=i[a],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,n,r){var o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===i?x.prop(e,n,r):(1===s&&x.isXMLDoc(e)||(n=n.toLowerCase(),o=x.attrHooks[n]||(x.expr.match.bool.test(n)?X:z)),r===t?o&&"get"in o&&null!==(a=o.get(e,n))?a:(a=x.find.attr(e,n),null==a?t:a):null!==r?o&&"set"in o&&(a=o.set(e,r,n))!==t?a:(e.setAttribute(n,r+""),r):(x.removeAttr(e,n),t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(T);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)?K&&Q||!G.test(n)?e[r]=!1:e[x.camelCase("default-"+n)]=e[r]=!1:x.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!x.isXMLDoc(e),a&&(n=x.propFix[n]||n,o=x.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var t=x.find.attr(e,"tabindex");return t?parseInt(t,10):Y.test(e.nodeName)||J.test(e.nodeName)&&e.href?0:-1}}}}),X={set:function(e,t,n){return t===!1?x.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&x.propFix[n]||n,n):e[x.camelCase("default-"+n)]=e[n]=!0,n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,n){var r=x.expr.attrHandle[n]||x.find.attr;x.expr.attrHandle[n]=K&&Q||!G.test(n)?function(e,n,i){var o=x.expr.attrHandle[n],a=i?t:(x.expr.attrHandle[n]=t)!=r(e,n,i)?n.toLowerCase():null;return x.expr.attrHandle[n]=o,a}:function(e,n,r){return r?t:e[x.camelCase("default-"+n)]?n.toLowerCase():null}}),K&&Q||(x.attrHooks.value={set:function(e,n,r){return x.nodeName(e,"input")?(e.defaultValue=n,t):z&&z.set(e,n,r)}}),Q||(z={set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},x.expr.attrHandle.id=x.expr.attrHandle.name=x.expr.attrHandle.coords=function(e,n,r){var i;return r?t:(i=e.getAttributeNode(n))&&""!==i.value?i.value:null},x.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&r.specified?r.value:t},set:z.set},x.attrHooks.contenteditable={set:function(e,t,n){z.set(e,""===t?!1:t,n)}},x.each(["width","height"],function(e,n){x.attrHooks[n]={set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}}})),x.support.hrefNormalized||x.each(["href","src"],function(e,t){x.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}}),x.support.style||(x.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.support.enctype||(x.propFix.enctype="encoding"),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,n){return x.isArray(n)?e.checked=x.inArray(x(e).val(),n)>=0:t}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}function at(){try{return a.activeElement}catch(e){}}x.event={global:{},add:function(e,n,r,o,a){var s,l,u,c,p,f,d,h,g,m,y,v=x._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=x.guid++),(l=v.events)||(l=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof x===i||e&&x.event.triggered===e.type?t:x.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(T)||[""],u=n.length;while(u--)s=rt.exec(n[u])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),g&&(p=x.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=x.event.special[g]||{},d=x.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&x.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=l[g])||(h=l[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),x.event.global[g]=!0);e=null}},remove:function(e,t,n,r,i){var o,a,s,l,u,c,p,f,d,h,g,m=x.hasData(e)&&x._data(e);if(m&&(c=m.events)){t=(t||"").match(T)||[""],u=t.length;while(u--)if(s=rt.exec(t[u])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=x.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),l=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));l&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||x.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)x.event.remove(e,d+t[u],n,r,!0);x.isEmptyObject(c)&&(delete m.handle,x._removeData(e,"events"))}},trigger:function(n,r,i,o){var s,l,u,c,p,f,d,h=[i||a],g=v.call(n,"type")?n.type:n,m=v.call(n,"namespace")?n.namespace.split("."):[];if(u=f=i=i||a,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+x.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),l=0>g.indexOf(":")&&"on"+g,n=n[x.expando]?n:new x.Event(g,"object"==typeof n&&n),n.isTrigger=o?2:3,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:x.makeArray(r,[n]),p=x.event.special[g]||{},o||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!o&&!p.noBubble&&!x.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(u=u.parentNode);u;u=u.parentNode)h.push(u),f=u;f===(i.ownerDocument||a)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((u=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(x._data(u,"events")||{})[n.type]&&x._data(u,"handle"),s&&s.apply(u,r),s=l&&u[l],s&&x.acceptData(u)&&s.apply&&s.apply(u,r)===!1&&n.preventDefault();if(n.type=g,!o&&!n.isDefaultPrevented()&&(!p._default||p._default.apply(h.pop(),r)===!1)&&x.acceptData(i)&&l&&i[g]&&!x.isWindow(i)){f=i[l],f&&(i[l]=null),x.event.triggered=g;try{i[g]()}catch(y){}x.event.triggered=t,f&&(i[l]=f)}return n.result}},dispatch:function(e){e=x.event.fix(e);var n,r,i,o,a,s=[],l=g.call(arguments),u=(x._data(this,"events")||{})[e.type]||[],c=x.event.special[e.type]||{};if(l[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((x.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,l),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],l=n.delegateCount,u=e.target;if(l&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!=this;u=u.parentNode||this)if(1===u.nodeType&&(u.disabled!==!0||"click"!==e.type)){for(o=[],a=0;l>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?x(r,this).index(u)>=0:x.find(r,this,null,[u]).length),o[r]&&o.push(i);o.length&&s.push({elem:u,handlers:o})}return n.length>l&&s.push({elem:this,handlers:n.slice(l)}),s},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,o=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new x.Event(o),t=r.length;while(t--)n=r[t],e[n]=o[n];return e.target||(e.target=o.srcElement||a),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,o):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,o,s=n.button,l=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||a,o=i.documentElement,r=i.body,e.pageX=n.clientX+(o&&o.scrollLeft||r&&r.scrollLeft||0)-(o&&o.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(o&&o.scrollTop||r&&r.scrollTop||0)-(o&&o.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&l&&(e.relatedTarget=l===e.target?n.toElement:l),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},focus:{trigger:function(){if(this!==at()&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===at()&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},click:{trigger:function(){return x.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=a.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},x.Event=function(e,n){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&x.extend(this,n),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,t):new x.Event(e,n)},x.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.preventDefault?e.preventDefault():e.returnValue=!1)},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.submitBubbles||(x.event.special.submit={setup:function(){return x.nodeName(this,"form")?!1:(x.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=x.nodeName(n,"input")||x.nodeName(n,"button")?n.form:t;r&&!x._data(r,"submitBubbles")&&(x.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),x._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&x.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return x.nodeName(this,"form")?!1:(x.event.remove(this,"._submit"),t)}}),x.support.changeBubbles||(x.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(x.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),x.event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),x.event.simulate("change",this,e,!0)})),!1):(x.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!x._data(t,"changeBubbles")&&(x.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||x.event.simulate("change",this.parentNode,e,!0)}),x._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return x.event.remove(this,"._change"),!Z.test(this.nodeName)}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&a.addEventListener(e,r,!0)},teardown:function(){0===--n&&a.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return x().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=x.guid++)),this.each(function(){x.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,x(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){x.event.remove(this,e,r,n)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?x.event.trigger(e,n,r,!0):t}});var st=/^.[^:#\[\.,]*$/,lt=/^(?:parents|prev(?:Until|All))/,ut=x.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t,n=x(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(x.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e||[],!0))},filter:function(e){return this.pushStack(ft(this,e||[],!1))},is:function(e){return!!ft(this,"string"==typeof e&&ut.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],a=ut.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(a?a.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?x.inArray(this[0],x(e)):x.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return x.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(ct[e]||(i=x.unique(i)),lt.test(e)&&(i=i.reverse())),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeType||!x(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(st.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return x.inArray(e,t)>=0!==n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/\s*$/g,At={option:[1,""],legend:[1,"
    ","
    "],area:[1,"",""],param:[1,"",""],thead:[1,"","
    "],tr:[2,"","
    "],col:[2,"","
    "],td:[3,"","
    "],_default:x.support.htmlSerialize?[0,"",""]:[1,"X
    ","
    "]},jt=dt(a),Dt=jt.appendChild(a.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===t?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||a).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Lt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(Ft(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&_t(Ft(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&x.cleanData(Ft(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&x.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!x.support.htmlSerialize&&mt.test(e)||!x.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1>");try{for(;i>r;r++)n=this[r]||{},1===n.nodeType&&(x.cleanData(Ft(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=d.apply([],e);var r,i,o,a,s,l,u=0,c=this.length,p=this,f=c-1,h=e[0],g=x.isFunction(h);if(g||!(1>=c||"string"!=typeof h||x.support.checkClone)&&Nt.test(h))return this.each(function(r){var i=p.eq(r);g&&(e[0]=h.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(l=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),r=l.firstChild,1===l.childNodes.length&&(l=r),r)){for(a=x.map(Ft(l,"script"),Ht),o=a.length;c>u;u++)i=l,u!==f&&(i=x.clone(i,!0,!0),o&&x.merge(a,Ft(i,"script"))),t.call(this[u],i,u);if(o)for(s=a[a.length-1].ownerDocument,x.map(a,qt),u=0;o>u;u++)i=a[u],kt.test(i.type||"")&&!x._data(i,"globalEval")&&x.contains(s,i)&&(i.src?x._evalUrl(i.src):x.globalEval((i.text||i.textContent||i.innerHTML||"").replace(St,"")));l=r=null}return this}});function Lt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function Ht(e){return e.type=(null!==x.find.attr(e,"type"))+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function _t(e,t){var n,r=0;for(;null!=(n=e[r]);r++)x._data(n,"globalEval",!t||x._data(t[r],"globalEval"))}function Mt(e,t){if(1===t.nodeType&&x.hasData(e)){var n,r,i,o=x._data(e),a=x._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)x.event.add(t,n,s[n][r])}a.data&&(a.data=x.extend({},a.data))}}function Ot(e,t){var n,r,i;if(1===t.nodeType){if(n=t.nodeName.toLowerCase(),!x.support.noCloneEvent&&t[x.expando]){i=x._data(t);for(r in i.events)x.removeEvent(t,r,i.handle);t.removeAttribute(x.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),x.support.html5Clone&&e.innerHTML&&!x.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Ct.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=0,i=[],o=x(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),x(o[r])[t](n),h.apply(i,n.get());return this.pushStack(i)}});function Ft(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNodes||e;null!=(o=r[a]);a++)!n||x.nodeName(o,n)?s.push(o):x.merge(s,Ft(o,n));return n===t||n&&x.nodeName(e,n)?x.merge([e],s):s}function Bt(e){Ct.test(e.type)&&(e.defaultChecked=e.checked)}x.extend({clone:function(e,t,n){var r,i,o,a,s,l=x.contains(e.ownerDocument,e);if(x.support.html5Clone||x.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(x.support.noCloneEvent&&x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(r=Ft(o),s=Ft(e),a=0;null!=(i=s[a]);++a)r[a]&&Ot(i,r[a]);if(t)if(n)for(s=s||Ft(e),r=r||Ft(o),a=0;null!=(i=s[a]);a++)Mt(i,r[a]);else Mt(e,o);return r=Ft(o,"script"),r.length>0&&_t(r,!l&&Ft(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,l,u,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===x.type(o))x.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),l=(bt.exec(o)||["",""])[1].toLowerCase(),c=At[l]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1>")+c[2],i=c[0];while(i--)s=s.lastChild;if(!x.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!x.support.tbody){o="table"!==l||xt.test(o)?""!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)x.nodeName(u=o.childNodes[i],"tbody")&&!u.childNodes.length&&o.removeChild(u)}x.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),x.support.appendChecked||x.grep(Ft(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===x.inArray(o,r))&&(a=x.contains(o.ownerDocument,o),s=Ft(f.appendChild(o),"script"),a&&_t(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,l=x.expando,u=x.cache,c=x.support.deleteExpando,f=x.event.special;for(;null!=(n=e[s]);s++)if((t||x.acceptData(n))&&(o=n[l],a=o&&u[o])){if(a.events)for(r in a.events)f[r]?x.event.remove(n,r):x.removeEvent(n,r,a.handle); +u[o]&&(delete u[o],c?delete n[l]:typeof n.removeAttribute!==i?n.removeAttribute(l):n[l]=null,p.push(o))}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}}),x.fn.extend({wrapAll:function(e){if(x.isFunction(e))return this.each(function(t){x(this).wrapAll(e.call(this,t))});if(this[0]){var t=x(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+w+")(.*)$","i"),Yt=RegExp("^("+w+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+-])=("+w+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=x._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=x._data(r,"olddisplay",ln(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&x._data(r,"olddisplay",i?n:x.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}x.fn.extend({css:function(e,n){return x.access(this,function(e,n,r){var i,o,a={},s=0;if(x.isArray(n)){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=x.css(e,n[s],!1,o);return a}return r!==t?x.style(e,n,r):x.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){nn(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":x.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,l=x.camelCase(n),u=e.style;if(n=x.cssProps[l]||(x.cssProps[l]=tn(u,l)),s=x.cssHooks[n]||x.cssHooks[l],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:u[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(x.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==a||x.cssNumber[l]||(r+="px"),x.support.clearCloneStyle||""!==r||0!==n.indexOf("background")||(u[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{u[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,l=x.camelCase(n);return n=x.cssProps[l]||(x.cssProps[l]=tn(e.style,l)),s=x.cssHooks[n]||x.cssHooks[l],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||x.isNumeric(o)?o||0:a):a}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s.getPropertyValue(n)||s[n]:t,u=e.style;return s&&(""!==l||x.contains(e.ownerDocument,e)||(l=x.style(e,n)),Yt.test(l)&&Ut.test(n)&&(i=u.width,o=u.minWidth,a=u.maxWidth,u.minWidth=u.maxWidth=u.width=l,l=s.width,u.width=i,u.minWidth=o,u.maxWidth=a)),l}):a.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),l=s?s[n]:t,u=e.style;return null==l&&u&&u[n]&&(l=u[n]),Yt.test(l)&&!zt.test(n)&&(i=u.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),u.left="fontSize"===n?"1em":l,l=u.pixelLeft+"px",u.left=i,a&&(o.left=a)),""===l?"auto":l});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=x.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=x.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=x.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=x.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=x.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,t,n||(a?"border":"content"),r,o)+"px"}function ln(e){var t=a,n=Gt[e];return n||(n=un(e,t),"none"!==n&&n||(Pt=(Pt||x("