init without trunk

This commit is contained in:
Kevin Adametz 2020-07-09 12:49:32 +02:00
parent ed24ac4994
commit bb809e7233
14652 changed files with 177862 additions and 94817 deletions

View file

@ -0,0 +1,347 @@
# Changelog
All notable changes to this project will be documented in this file, in reverse chronological order by release.
## 3.2.0 - TBD
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 3.1.1 - TBD
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 3.1.0 - 2016-10-24
### Added
- [#87](https://github.com/zendframework/zend-code/pull/95) support for
PHP 7.1's `void` return type declaration.
- [#87](https://github.com/zendframework/zend-code/pull/95) support for
PHP 7.1's nullable type declarations.
- [#87](https://github.com/zendframework/zend-code/pull/95) support for
PHP 7.1's `iterable` type declaration.
- [#62](https://github.com/zendframework/zend-code/pull/62) added
`Zend\Code\Generator\MethodGenerator#getReturnType()` accessor.
- [#68](https://github.com/zendframework/zend-code/pull/68)
[#26](https://github.com/zendframework/zend-code/pull/26) added mutators
to allow removing/checking for existence of methods, properties, constants,
parameters and type declarations across all the code generator API.
- [#65](https://github.com/zendframework/zend-code/pull/65) continuous
integration testing now checks locked, newest and oldest dependency
sets.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- Nothing.
## 3.0.5 - 2016-10-24
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#92](https://github.com/zendframework/zend-code/pull/92) corrected
`Zend\Code\Scanner\ClassScanner` to detect multiple interface inheritance.
- [#95](https://github.com/zendframework/zend-code/pull/95) corrected
`Zend\Code\Generator\ParameterGenerator` to allow copying parameter signatures
for non-optional parameters that are still nullable via a default `= null`
value.
- [#94](https://github.com/zendframework/zend-code/pull/94) corrected
`Zend\Code\Generator\ValueGenerator` so that class constants can now
be generated with arrays as default value (supported since PHP 5.6).
## 3.0.4 - 2016-06-30
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#59](https://github.com/zendframework/zend-code/pull/59) fixes an issue with
detection of multiple trait `use` statements.
- [#75](https://github.com/zendframework/zend-code/pull/75) provides a patch to
ensure that `extends` statements qualify the parent class based on the current
namespace and/or import statements.
## 3.0.3 - 2016-06-27
### Added
- [#66](https://github.com/zendframework/zend-code/pull/66) publishes the
documentation to https://docs.zendframework.com/zend-code/.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#61](https://github.com/zendframework/zend-code/pull/61) fixes an issue with
how parameter typehints were generated; previously, fully-qualified class
names were not being generated with the leading backslash, causing them to
attempt to resolve as if they were relative to the current namespace.
- [#69](https://github.com/zendframework/zend-code/pull/69) fixes an issue with
how class names under the same namespace are generated when generating
typehints, extends, and implements values; they now strip the
common namespace from the class name.
- [#72](https://github.com/zendframework/zend-code/pull/72) fixes an issue
within the `TokenArrayScanner` when scanning closures.
## 3.0.2 - 2016-04-20
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#52](https://github.com/zendframework/zend-code/pull/52) updates several
dependency constraints:
- zend-stdlib now allows either the 2.7 or 3.0 series, as the APIs consumed by
zend-code are compatible across versions.
- PHP now excludes the 7.0.5 release, as it has known issues in its tokenizer
implementation that make the zend-code token scanner unusable.
- [#46](https://github.com/zendframework/zend-code/pull/46) updates all
generators to use `\n` for line endings in generated code, vs `PHP_EOL`,
ensuring cross-platform consistency.
## 3.0.1 - 2016-01-26
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#34](https://github.com/zendframework/zend-code/pull/34) method name cannot be optional when adding a method
to a class generator.
- [#38](https://github.com/zendframework/zend-code/pull/38) PHP_CodeSniffer was moved to dev dependencies
## 3.0.0 - 2016-01-13
### Changed
This section refers to breaking changes: please refer to
[doc/book/migration.md](doc/book/migration.md) for migration instructions.
- Types `string`, `int`, `float`, `bool` passed to `Zend\Code\Generator\ParameterGenerator#setType()`
are no longer ignored in generated code [#30](https://github.com/zendframework/zend-code/pull/30)
- Types declared in DocBlocks are now ignored when creating a `Zend\Code\Generator\ParameterGenerator` via
`Zend\Code\Generator\ParameterGenerator::fromReflection()`. [#30](https://github.com/zendframework/zend-code/pull/30)
- Type strings are now validated: passing an invalid type to any method in the generator API
may lead to a `Zend\Code\Generator\InvalidArgumentException` being thrown.
[#30](https://github.com/zendframework/zend-code/pull/30)
- `Zend\Code\Generator\ParameterGenerator::$simple` was removed. [#30](https://github.com/zendframework/zend-code/pull/30)
- `Zend\Code\Generator\ParameterGenerator#$type` is now a `null|Zend\Code\Generator\TypeGenerator`: was a
`string` before. [#30](https://github.com/zendframework/zend-code/pull/30)
- `Zend\Code\Generator` type-hints are now always prefixed with the namespace separator `\`.
[#30](https://github.com/zendframework/zend-code/pull/30)
- `Zend\Code\Reflection\ParameterReflection#getType()` was renamed
to `Zend\Code\Reflection\ParameterReflection#detectType()` in order to not override the inherited
`ReflectionParameter#getType()`, introduced in PHP 7. [#30](https://github.com/zendframework/zend-code/pull/30)
### Added
- PHP 7 return type hints generation support via `Zend\Code\Generator\MethodGenerator#setReturnType()`.
[#30](https://github.com/zendframework/zend-code/pull/30)
- PHP 7 scalar type hints generation support via `Zend\Code\Generator\ParameterGenerator#setType()` and
`Zend\Code\Generator\ParameterGenerator#getType()`. [#30](https://github.com/zendframework/zend-code/pull/30)
- PHP 5.6 variadic arguments support via `Zend\Code\Generator\ParameterGenerator#setVariadic()` and
`Zend\Code\Generator\ParameterGenerator#getVariadic()`. [#30](https://github.com/zendframework/zend-code/pull/30)
- Generation of methods returning by reference is supported via `Zend\Code\Generator\ParameterGenerator#setReturnsReference()`.
[#30](https://github.com/zendframework/zend-code/pull/30)
### Deprecated
- Nothing.
### Removed
- `Zend\Code\ParameterGenerator::$simple` was removed. [#30](https://github.com/zendframework/zend-code/pull/30)
### Fixed
- Nothing.
## 2.6.2 - 2015-01-05
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#31](https://github.com/zendframework/zend-code/pull/31) updated license year.
## 2.6.2 - 2015-01-05
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#31](https://github.com/zendframework/zend-code/pull/31) updated license year.
## 2.6.1 - 2015-11-24
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#25](https://github.com/zendframework/zend-code/pull/25) changes the
`doctrine/common` suggestion/dev-dependency to the more specific
`doctrine/annotations` package (which is what is actually consumed).
## 2.6.0 - 2015-11-18
### Added
- [#12](https://github.com/zendframework/zend-code/pull/12) adds the ability to
generate arrays using either long/standard syntax (`array(...)`) or short
syntax (`[...]`). This can be accomplished by setting the value type to
`ValueGenerator::TYPE_ARRAY_SHORT` instead of using `TYPE_ARRAY`.
Additionally, you can use `TYPE_ARRAY_LONG` instead of `TYPE_ARRAY`; the two
constants are synonyms.
- [#11](https://github.com/zendframework/zend-code/pull/11) adds the ability to
generate interfaces via the new class `Zend\Code\Generator\InterfaceGenerator`.
### Deprecated
- Nothing.
### Removed
- Nothing.
### Fixed
- [#20](https://github.com/zendframework/zend-code/pull/20) updates
the zend-eventmanager dependency to `^2.6|^3.0`, and changes its
internal usage to use the `triggerEventUntil()` signature.
## 2.5.3 - 2015-11-18
### Added
- Nothing.
### Deprecated
- Nothing.
### Removed
- [#10](https://github.com/zendframework/zend-code/pull/10) removes a
development dependency on zendframework/zend-version.
- [#23](https://github.com/zendframework/zend-code/pull/23) removes a
requirement on zendframework/zend-stdlib. This results in a slight change in
`Zend\Code\Generator\ValueGenerator`: `setConstants()` and `getConstants()`
can now receive/emit *either* an SPL `ArrayObject` or
`Zend\Stdlib\ArrayObject`. Since these are functionally equivalent, however,
you will experience no change in behavior.
### Fixed
- Nothing.

View file

@ -0,0 +1,43 @@
# Contributor Code of Conduct
The Zend Framework project adheres to [The Code Manifesto](http://codemanifesto.com)
as its guidelines for contributor interactions.
## The Code Manifesto
We want to work in an ecosystem that empowers developers to reach their
potential — one that encourages growth and effective collaboration. A space that
is safe for all.
A space such as this benefits everyone that participates in it. It encourages
new developers to enter our field. It is through discussion and collaboration
that we grow, and through growth that we improve.
In the effort to create such a place, we hold to these values:
1. **Discrimination limits us.** This includes discrimination on the basis of
race, gender, sexual orientation, gender identity, age, nationality, technology
and any other arbitrary exclusion of a group of people.
2. **Boundaries honor us.** Your comfort levels are not everyones comfort
levels. Remember that, and if brought to your attention, heed it.
3. **We are our biggest assets.** None of us were born masters of our trade.
Each of us has been helped along the way. Return that favor, when and where
you can.
4. **We are resources for the future.** As an extension of #3, share what you
know. Make yourself a resource to help those that come after you.
5. **Respect defines us.** Treat others as you wish to be treated. Make your
discussions, criticisms and debates from a position of respectfulness. Ask
yourself, is it true? Is it necessary? Is it constructive? Anything less is
unacceptable.
6. **Reactions require grace.** Angry responses are valid, but abusive language
and vindictive actions are toxic. When something happens that offends you,
handle it assertively, but be respectful. Escalate reasonably, and try to
allow the offender an opportunity to explain themselves, and possibly correct
the issue.
7. **Opinions are just that: opinions.** Each and every one of us, due to our
background and upbringing, have varying opinions. The fact of the matter, is
that is perfectly acceptable. Remember this: if you respect your own
opinions, you should respect the opinions of others.
8. **To err is human.** You might not intend it, but mistakes do happen and
contribute to build experience. Tolerate honest mistakes, and don't hesitate
to apologize if you make one yourself.

View file

@ -0,0 +1,234 @@
# CONTRIBUTING
## RESOURCES
If you wish to contribute to Zend Framework, please be sure to
read/subscribe to the following resources:
- [Coding Standards](https://github.com/zendframework/zf2/wiki/Coding-Standards)
- [Contributor's Guide](http://framework.zend.com/participate/contributor-guide)
- ZF Contributor's mailing list:
Archives: http://zend-framework-community.634137.n4.nabble.com/ZF-Contributor-f680267.html
Subscribe: zf-contributors-subscribe@lists.zend.com
- ZF Contributor's IRC channel:
#zftalk.dev on Freenode.net
If you are working on new features or refactoring [create a proposal](https://github.com/zendframework/zend-code/issues/new).
## Reporting Potential Security Issues
If you have encountered a potential security vulnerability, please **DO NOT** report it on the public
issue tracker: send it to us at [zf-security@zend.com](mailto:zf-security@zend.com) instead.
We will work with you to verify the vulnerability and patch it as soon as possible.
When reporting issues, please provide the following information:
- Component(s) affected
- A description indicating how to reproduce the issue
- A summary of the security vulnerability and impact
We request that you contact us via the email address above and give the project
contributors a chance to resolve the vulnerability and issue a new release prior
to any public exposure; this helps protect users and provides them with a chance
to upgrade and/or update in order to protect their applications.
For sensitive email communications, please use [our PGP key](http://framework.zend.com/zf-security-pgp-key.asc).
## RUNNING TESTS
> ### Note: testing versions prior to 2.4
>
> This component originates with Zend Framework 2. During the lifetime of ZF2,
> testing infrastructure migrated from PHPUnit 3 to PHPUnit 4. In most cases, no
> changes were necessary. However, due to the migration, tests may not run on
> versions < 2.4. As such, you may need to change the PHPUnit dependency if
> attempting a fix on such a version.
To run tests:
- Clone the repository:
```console
$ git clone git@github.com:zendframework/zend-code.git
$ cd
```
- Install dependencies via composer:
```console
$ curl -sS https://getcomposer.org/installer | php --
$ ./composer.phar install
```
If you don't have `curl` installed, you can also download `composer.phar` from https://getcomposer.org/
- Run the tests via `phpunit` and the provided PHPUnit config, like in this example:
```console
$ ./vendor/bin/phpunit
```
You can turn on conditional tests with the phpunit.xml file.
To do so:
- Copy `phpunit.xml.dist` file to `phpunit.xml`
- Edit `phpunit.xml` to enable any specific functionality you
want to test, as well as to provide test values to utilize.
## Running Coding Standards Checks
This component uses [php-cs-fixer](http://cs.sensiolabs.org/) for coding
standards checks, and provides configuration for our selected checks.
`php-cs-fixer` is installed by default via Composer.
To run checks only:
```console
$ ./vendor/bin/php-cs-fixer fix . -v --diff --dry-run --config-file=.php_cs
```
To have `php-cs-fixer` attempt to fix problems for you, omit the `--dry-run`
flag:
```console
$ ./vendor/bin/php-cs-fixer fix . -v --diff --config-file=.php_cs
```
If you allow php-cs-fixer to fix CS issues, please re-run the tests to ensure
they pass, and make sure you add and commit the changes after verification.
## Recommended Workflow for Contributions
Your first step is to establish a public repository from which we can
pull your work into the master repository. We recommend using
[GitHub](https://github.com), as that is where the component is already hosted.
1. Setup a [GitHub account](http://github.com/), if you haven't yet
2. Fork the repository (http://github.com/zendframework/zend-code)
3. Clone the canonical repository locally and enter it.
```console
$ git clone git://github.com:zendframework/zend-code.git
$ cd zend-code
```
4. Add a remote to your fork; substitute your GitHub username in the command
below.
```console
$ git remote add {username} git@github.com:{username}/zend-code.git
$ git fetch {username}
```
### Keeping Up-to-Date
Periodically, you should update your fork or personal repository to
match the canonical ZF repository. Assuming you have setup your local repository
per the instructions above, you can do the following:
```console
$ git checkout master
$ git fetch origin
$ git rebase origin/master
# OPTIONALLY, to keep your remote up-to-date -
$ git push {username} master:master
```
If you're tracking other branches -- for example, the "develop" branch, where
new feature development occurs -- you'll want to do the same operations for that
branch; simply substitute "develop" for "master".
### Working on a patch
We recommend you do each new feature or bugfix in a new branch. This simplifies
the task of code review as well as the task of merging your changes into the
canonical repository.
A typical workflow will then consist of the following:
1. Create a new local branch based off either your master or develop branch.
2. Switch to your new local branch. (This step can be combined with the
previous step with the use of `git checkout -b`.)
3. Do some work, commit, repeat as necessary.
4. Push the local branch to your remote repository.
5. Send a pull request.
The mechanics of this process are actually quite trivial. Below, we will
create a branch for fixing an issue in the tracker.
```console
$ git checkout -b hotfix/9295
Switched to a new branch 'hotfix/9295'
```
... do some work ...
```console
$ git commit
```
... write your log message ...
```console
$ git push {username} hotfix/9295:hotfix/9295
Counting objects: 38, done.
Delta compression using up to 2 threads.
Compression objects: 100% (18/18), done.
Writing objects: 100% (20/20), 8.19KiB, done.
Total 20 (delta 12), reused 0 (delta 0)
To ssh://git@github.com/{username}/zend-code.git
b5583aa..4f51698 HEAD -> master
```
To send a pull request, you have two options.
If using GitHub, you can do the pull request from there. Navigate to
your repository, select the branch you just created, and then select the
"Pull Request" button in the upper right. Select the user/organization
"zendframework" as the recipient.
If using your own repository - or even if using GitHub - you can use `git
format-patch` to create a patchset for us to apply; in fact, this is
**recommended** for security-related patches. If you use `format-patch`, please
send the patches as attachments to:
- zf-devteam@zend.com for patches without security implications
- zf-security@zend.com for security patches
#### What branch to issue the pull request against?
Which branch should you issue a pull request against?
- For fixes against the stable release, issue the pull request against the
"master" branch.
- For new features, or fixes that introduce new elements to the public API (such
as new public methods or properties), issue the pull request against the
"develop" branch.
### Branch Cleanup
As you might imagine, if you are a frequent contributor, you'll start to
get a ton of branches both locally and on your remote.
Once you know that your changes have been accepted to the master
repository, we suggest doing some cleanup of these branches.
- Local branch cleanup
```console
$ git branch -d <branchname>
```
- Remote branch removal
```console
$ git push {username} :<branchname>
```
## Conduct
Please see our [CONDUCT.md](CONDUCT.md) to understand expected behavior when interacting with others in the project.

View file

@ -0,0 +1,28 @@
Copyright (c) 2005-2016, Zend Technologies USA, Inc.
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 Zend Technologies USA, Inc. 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.

23
vendor/zendframework/zend-code/Makefile vendored Normal file
View file

@ -0,0 +1,23 @@
# zend-expressive Makefile
#
# Primary purpose is for generating the mkdocs.yml from the bookdown.json
# sources.
#
# Configurable variables:
# - BOOKDOWN2MKDOCS - specify the path to the executable; defaults to
# ./vendor/bin/bookdown2mkdocs
#
# Available targets:
# - mkdocs - regenerate mkdocs.yml
# - all - synonym for mkdocs target
BOOKDOWN2MKDOCS?=$(CURDIR)/vendor/bin/bookdown2mkdocs.php
.PHONY : all mkdocs bookdown2mkdocs
all : mkdocs
mkdocs :
@echo "Generating mkdocs.yml from bookdown.json..."
-$(BOOKDOWN2MKDOCS) convert --site-name=zend-code --repo-url=https://github.com/zendframework/zend-code --copyright-url=http://www.zend.com/ --copyright-author="Zend Technologies USA Inc."
@echo "[DONE] Generating mkdocs.yml from bookdown.json"

View file

@ -0,0 +1,14 @@
# zend-code
[![Build Status](https://secure.travis-ci.org/zendframework/zend-code.svg?branch=master)](https://secure.travis-ci.org/zendframework/zend-code)
[![Coverage Status](https://coveralls.io/repos/zendframework/zend-code/badge.svg?branch=master)](https://coveralls.io/r/zendframework/zend-code?branch=master)
`Zend\Code\Generator` provides facilities to generate arbitrary code using an
object-oriented interface, both to create new code as well as to update existing
code. While the current implementation is limited to generating PHP code, you
can easily extend the base class in order to provide code generation for other
tasks: JavaScript, configuration files, apache vhosts, etc.
- File issues at https://github.com/zendframework/zend-code/issues
- Documentation is at http://zend-code.rtfd.org
- Migration documentation from v2 to v3 is at http://zend-code.rtfd.org/en/latest/migration/

View file

@ -0,0 +1,54 @@
{
"name": "zendframework/zend-code",
"description": "provides facilities to generate arbitrary code using an object oriented interface",
"license": "BSD-3-Clause",
"keywords": [
"zf2",
"code"
],
"homepage": "https://github.com/zendframework/zend-code",
"autoload": {
"psr-4": {
"Zend\\Code\\": "src/"
}
},
"require": {
"php": "^5.6 || 7.0.0 - 7.0.4 || ^7.0.6",
"zendframework/zend-eventmanager": "^2.6 || ^3.0"
},
"require-dev": {
"ext-phar": "*",
"doctrine/annotations": "~1.0",
"zendframework/zend-stdlib": "^2.7 || ^3.0",
"squizlabs/php_codesniffer": "^2.5",
"phpunit/PHPUnit": "^4.8.21"
},
"suggest": {
"doctrine/annotations": "Doctrine\\Common\\Annotations >=1.0 for annotation features",
"zendframework/zend-stdlib": "Zend\\Stdlib component"
},
"minimum-stability": "dev",
"prefer-stable": true,
"extra": {
"branch-alias": {
"dev-master": "3.1-dev",
"dev-develop": "3.2-dev"
}
},
"autoload-dev": {
"psr-4": {
"ZendTest\\Code\\": "test/"
}
},
"scripts": {
"check": [
"@cs-check",
"@test"
],
"upload-coverage": "coveralls -v",
"cs-check": "phpcs",
"cs-fix": "phpcbf",
"test": "phpunit --colors=always",
"test-coverage": "phpunit --colors=always --coverage-clover clover.xml"
}
}

1279
vendor/zendframework/zend-code/composer.lock generated vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,8 @@
{
"title": "zend-code Generator Component",
"content": [
{"Intro": "intro.md"},
{"Examples": "examples.md"},
{"Reference": "reference.md"}
]
}

View file

@ -0,0 +1,372 @@
# Zend\\Code\\Generator Examples
## Generating PHP classes
The following example generates an empty class with a class-level DocBlock.
```php
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\DocBlockGenerator;
$foo = new ClassGenerator();
$docblock = DocBlockGenerator::fromArray([
'shortDescription' => 'Sample generated class',
'longDescription' => 'This is a class generated with Zend\Code\Generator.',
'tags' => [
[
'name' => 'version',
'description' => '$Rev:$',
],
[
'name' => 'license',
'description' => 'New BSD',
],
],
]);
$foo->setName('Foo')
->setDocblock($docblock);
echo $foo->generate();
```
The above code will result in the following:
```php
/**
* Sample generated class
*
* This is a class generated with Zend\Code\Generator.
*
* @version $Rev:$
* @license New BSD
*
*/
class Foo
{
}
```
### Generating PHP classes with class properties
Building on the previous example, we now add properties to our generated class.
```php
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Generator\PropertyGenerator;
$foo = new ClassGenerator();
$docblock = DocBlockGenerator::fromArray([
'shortDescription' => 'Sample generated class',
'longDescription' => 'This is a class generated with Zend\Code\Generator.',
'tags' => [
[
'name' => 'version',
'description' => '$Rev:$',
],
[
'name' => 'license',
'description' => 'New BSD',
],
],
]);
$foo->setName('Foo')
->setDocblock($docblock)
->addProperties([
['_bar', 'baz', PropertyGenerator::FLAG_PROTECTED],
['baz', 'bat', PropertyGenerator::FLAG_PUBLIC]
])
->addConstants([
['bat', 'foobarbazbat']
]);
echo $foo->generate();
```
The above results in the following class definition:
```php
/**
* Sample generated class
*
* This is a class generated with Zend\Code\Generator.
*
* @version $Rev:$
* @license New BSD
*
*/
class Foo
{
protected $_bar = 'baz';
public $baz = 'bat';
const bat = 'foobarbazbat';
}
```
### Generating PHP classes with class methods
`Zend\Code\Generator\ClassGenerator` allows you to attach methods with optional content to your
classes. Methods may be attached as either arrays or concrete `Zend\Code\Generator\MethodGenerator`
instances.
```php
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\MethodGenerator;
use Zend\Code\Generator\PropertyGenerator;
$foo = new ClassGenerator();
$docblock = DocBlockGenerator::fromArray([
'shortDescription' => 'Sample generated class',
'longDescription' => 'This is a class generated with Zend\Code\Generator.',
'tags' => [
[
'name' => 'version',
'description' => '$Rev:$',
],
[
'name' => 'license',
'description' => 'New BSD',
],
],
]);
$foo->setName('Foo')
->setDocblock($docblock)
->addProperties([
['_bar', 'baz', PropertyGenerator::FLAG_PROTECTED],
['baz', 'bat', PropertyGenerator::FLAG_PUBLIC]
])
->addConstants([
['bat', 'foobarbazbat', PropertyGenerator::FLAG_CONSTANT]
])
->addMethods([
// Method passed as array
MethodGenerator::fromArray([
'name' => 'setBar',
'parameters' => ['bar'],
'body' => '$this->_bar = $bar;' . "\n" . 'return $this;',
'docblock' => DocBlockGenerator::fromArray([
'shortDescription' => 'Set the bar property',
'longDescription' => null,
'tags' => [
new Tag\ParamTag([
'paramName' => 'bar',
'datatype' => 'string'
]),
new Tag\ReturnTag([
'datatype' => 'string',
]),
],
]),
]),
// Method passed as concrete instance
new MethodGenerator(
'getBar',
[],
MethodGenerator::FLAG_PUBLIC,
'return $this->_bar;',
DocBlockGenerator::fromArray([
'shortDescription' => 'Retrieve the bar property',
'longDescription' => null,
'tags' => [
new Tag\ReturnTag([
'datatype' => 'string|null',
]),
],
])
),
]);
echo $foo->generate();
```
The above generates the following output:
```php
/**
* Sample generated class
*
* This is a class generated with Zend\Code\Generator.
*
* @version $Rev:$
* @license New BSD
*/
class Foo
{
protected $_bar = 'baz';
public $baz = 'bat';
const bat = 'foobarbazbat';
/**
* Set the bar property
*
* @param string bar
* @return string
*/
public function setBar($bar)
{
$this->_bar = $bar;
return $this;
}
/**
* Retrieve the bar property
*
* @return string|null
*/
public function getBar()
{
return $this->_bar;
}
}
```
## Generating PHP files
`Zend\Code\Generator\FileGenerator` can be used to generate the contents of a *PHP* file. You can
include classes as well as arbitrary content body. When attaching classes, you should attach either
concrete `Zend\Code\Generator\ClassGenerator` instances or an array defining the class.
In the example below, we will assume you've defined `$foo` per one of the class definitions in a
previous example.
```php
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Generator\FileGenerator;
$file = FileGenerator::fromArray([
'classes' => [$foo],
'docblock' => DocBlockGenerator::fromArray([
'shortDescription' => 'Foo class file',
'longDescription' => null,
'tags' => [
[
'name' => 'license',
'description' => 'New BSD',
],
],
]),
'body' => 'define(\'APPLICATION_ENV\', \'testing\');',
]);
```
Calling `generate()` will generate the code -- but not write it to a file. You will need to capture
the contents and write them to a file yourself. As an example:
```php
$code = $file->generate();
file_put_contents('Foo.php', $code);
```
The above will generate the following file:
```php
<?php
/**
* Foo class file
*
* @license New BSD
*/
/**
* Sample generated class
*
* This is a class generated with Zend\Code\Generator.
*
* @version $Rev:$
* @license New BSD
*/
class Foo
{
protected $_bar = 'baz';
public $baz = 'bat';
const bat = 'foobarbazbat';
/**
* Set the bar property
*
* @param string bar
* @return string
*/
public function setBar($bar)
{
$this->_bar = $bar;
return $this;
}
/**
* Retrieve the bar property
*
* @return string|null
*/
public function getBar()
{
return $this->_bar;
}
}
define('APPLICATION_ENV', 'testing');
```
## Add code to existing PHP files and classes
### Seeding PHP file code generation via reflection
You can add *PHP* code to an existing *PHP* file using the code generator. To do so, you need to
first do reflection on it. The static method `fromReflectedFileName()` allows you to do this.
```php
$generator = Zend\Code\Generator\FileGenerator::fromReflectedFileName($path);
$generator->setBody("\$foo->bar();");
file_put_contents($path, $generator->generate());
```
### Seeding PHP class generation via reflection
You may add code to an existing class. To do so, first use the static `fromReflection()` method to
map the class into a generator object. From there, you may add additional properties or methods, and
then regenerate the class.
```php
use Zend\Code\Generator\ClassGenerator;
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\MethodGenerator;
use Zend\Code\Reflection\ClassReflection;
$generator = ClassGenerator::fromReflection(
new ClassReflection($class)
);
$generator->addMethod(
'setBaz',
['baz'],
MethodGenerator::FLAG_PUBLIC,
'$this->_baz = $baz;' . "\n" . 'return $this;',
DocBlockGenerator::fromArray([
'shortDescription' => 'Set the baz property',
'longDescription' => null,
'tags' => [
new Tag\ParamTag([
'paramName' => 'baz',
'datatype' => 'string'
]),
new Tag\ReturnTag([
'datatype' => 'string',
]),
],
])
);
$code = $generator->generate();
```

View file

@ -0,0 +1,126 @@
# Introduction
`Zend\Code\Generator` provides facilities to generate arbitrary code using an object-oriented
interface, both to create new code as well as to update existing code. While the current
implementation is limited to generating *PHP* code, you can easily extend the base class in order to
provide code generation for other tasks: JavaScript, configuration files, apache vhosts, etc.
## Theory of Operation
In the most typical use case, you will simply instantiate a code generator class and either pass it
the appropriate configuration or configure it after instantiation. To generate the code, you will
simply echo the object or call its `generate()` method.
```php
// Passing configuration to the constructor:
$file = new Zend\Code\Generator\FileGenerator(array(
'classes' => array(
new Zend\Code\Generator\ClassGenerator(
'World', // name
null, // namespace
null, // flags
null, // extends
array(), // interfaces
array(), // properties
array(
new Zend\Code\Generator\MethodGenerator(
'hello', // name
array(), // parameters
'public', // visibility
'echo \'Hello world!\';' // body
),
)
),
),
));
// Render the generated file
echo $file->generate();
// or write it to a file:
file_put_contents('World.php', $file->generate());
// OR
// Configuring after instantiation
$method = new Zend\Code\Generator\MethodGenerator();
$method->setName('hello')
->setBody('echo \'Hello world!\';');
$class = new Zend\Code\Generator\ClassGenerator();
$class->setName('World')
->addMethodFromGenerator($method);
$file = new Zend\Code\Generator\FileGenerator();
$file->setClass($class);
// Render the generated file
echo $file->generate();
// or write it to a file:
file_put_contents('World.php', $file->generate());
```
Both of the above samples will render the same result:
```php
<?php
class World
{
public function hello()
{
echo 'Hello world!';
}
}
```
Another common use case is to update existing code -- for instance, to add a method to a class. In
such a case, you must first inspect the existing code using reflection, and then add your new
method. `Zend\Code\Generator` makes this trivially simple, by leveraging `Zend\Code\Reflection`.
As an example, let's say we've saved the above to the file `World.php`, and have already included
it. We could then do the following:
```php
$class = Zend\Code\Generator\ClassGenerator::fromReflection(
new Zend\Code\Reflection\ClassReflection('World')
);
$method = new Zend\Code\Generator\MethodGenerator();
$method->setName('mrMcFeeley')
->setBody('echo \'Hello, Mr. McFeeley!\';');
$class->addMethodFromGenerator($method);
$file = new Zend\Code\Generator\FileGenerator();
$file->setClass($class);
// Render the generated file
echo $file->generate();
// Or, better yet, write it back to the original file:
file_put_contents('World.php', $file->generate());
```
The resulting class file will now look like this:
```php
<?php
class World
{
public function hello()
{
echo 'Hello world!';
}
public function mrMcFeeley()
{
echo 'Hellow Mr. McFeeley!';
}
}
```

View file

@ -0,0 +1,403 @@
# Zend\\Code\\Generator Reference
## Abstract Classes and Interfaces
### Zend\\Code\\Generator\\GeneratorInterface
The base interface from which all CodeGenerator classes implement provides the minimal functionality
necessary. It's *API* is as follows:
```php
interface Zend\Code\Generator\GeneratorInterface
{
public function generate();
}
```
### Zend\\Code\\Generator\\AbstractGenerator
`Zend\Code\Generator\AbstractGenerator` implements `Zend\Code\Generator\GeneratorInterface`, and
adds some properties for tracking whether content has changed as well as the amount of indentation
that should appear before generated content. Its *API* is as follows:
```php
abstract class Zend\Code\Generator\AbstractGenerator
implements Zend\Code\Generator\GeneratorInterface
{
public function __construct(Array|Traversable $options = [])
public function setOptions(Array $options)
public function setSourceContent($sourceContent)
public function getSourceContent()
public function setSourceDirty($isSourceDirty = true)
public function isSourceDirty()
public function setIndentation($indentation)
public function getIndentation()
}
```
The constructor passes the `$options` parameter to `setOptions()`.
Like most classes in Zend Framework, `setOptions()` compares an option key to existing setters in
the class, and passes the value on to that method if found.
`setSourceContent()` and `getSourceContent()` are intended to either set the default content for the
code being generated, or to replace said content once all generation tasks are complete.
### Zend\\Code\\Generator\\AbstractMemberGenerator
`Zend\Code\Generator\AbstractMemberGenerator` is a base class for generating class members --
properties and methods -- and provides accessors and mutators for establishing visibility; whether
or not the member is abstract, static, or final; and the name of the member. Its *API* is as
follows:
```php
abstract class Zend\Code\Generator\AbstractMemberGenerator
extends Zend\Code\Generator\AbstractGenerator
{
public function setAbstract($isAbstract)
public function isAbstract()
public function setStatic($isStatic)
public function isStatic()
public function setVisibility($visibility)
public function getVisibility()
public function setName($name)
public function getName()
}
```
## Concrete CodeGenerator Classes
### Zend\\Code\\Generator\\BodyGenerator
`Zend\Code\Generator\BodyGenerator` is intended for generating arbitrary procedural code to include
within a file. As such, you simply set content for the object, and it will return that content when
you invoke `generate()`.
The *API* of the class is as follows:
```php
class Zend\Code\Generator\BodyGenerator extends Zend\Code\Generator\AbstractGenerator
{
public function setContent($content)
public function getContent()
public function generate()
}
```
### Zend\\Code\\Generator\\ClassGenerator
`Zend\Code\Generator\ClassGenerator` is intended for generating *PHP* classes. The basic
functionality just generates the *PHP* class itself, as well as optionally the related *PHP*
DocBlock. Classes may implement or inherit from other classes, and may be marked as abstract.
Utilizing other code generator classes, you can also attach class constants, properties, and
methods.
The *API* is as follows:
```php
class Zend\Code\Generator\ClassGenerator extends Zend\Code\Generator\AbstractGenerator
{
public static function fromReflection(
Zend\Code\Reflection\ClassReflection $reflectionClass
)
public function addConstants(Array $properties)
public function addConstant($property)
public function getConstants()
public function getConstant($propertyName)
public function removeConstant($constantName)
public function setDocblock(Zend\Code\Generator\DocBlockGenerator $docblock)
public function getDocblock()
public function setName($name)
public function getName()
public function setAbstract($isAbstract)
public function isAbstract()
public function setExtendedClass($extendedClass)
public function getExtendedClass()
public function hasExtentedClass()
public function removeExtentedClass()
public function setImplementedInterfaces(Array $implementedInterfaces)
public function getImplementedInterfaces()
public function addProperties(Array $properties)
public function addProperty($property)
public function getProperties()
public function getProperty($propertyName)
public function removeProperty($propertyName)
public function addMethods(Array $methods)
public function addMethod(
$name,
Array $parameters = [],
$flags = Zend\Code\Generator\MethodGenerator::FLAG_PUBLIC,
$body = null,
$docBlock = null
)
public function getMethods()
public function getMethod($methodName)
public function hasMethod($methodName)
public function hasUse($use)
public function removeUse($use)
public function hasUseAlias($use)
public function removeUseAlias($use)
public function hasImplementedInterface($implementedInterface)
public function removeImplementedInterface($implementedInterface)
public function isSourceDirty()
public function generate()
}
```
The `addProperty()` method accepts an array of information that may be used to generate a
`Zend\Code\Generator\PropertyGenerator` instance -- or simply an instance of
`Zend\Code\Generator\PropertyGenerator`. Likewise, `addMethod()` accepts either an array of
information for generating a `Zend\Code\Generator\MethodGenerator` instance or a concrete instance
of that class.
Note that `setDocBlock()` expects an instance of `Zend\Code\Generator\DocBlockGenerator`.
### Zend\\Code\\Generator\\DocBlockGenerator
`Zend\Code\Generator\DocBlockGenerator` can be used to generate arbitrary *PHP* docblocks, including
all the standard docblock features: short and long descriptions and annotation tags.
Annotation tags may be set using the `setTag()` and `setTags()` methods; these each take either an
array describing the tag that may be passed to the `Zend\Code\Generator\DocBlock\Tag` constructor,
or an instance of that class.
The *API* is as follows:
```php
class Zend\Code\Generator\DocBlockGenerator extends Zend\Code\Generator\AbstractGenerator
{
public static function fromReflection(
Zend\Code\Reflection\DocblockReflection $reflectionDocblock
)
public function setShortDescription($shortDescription)
public function getShortDescription()
public function setLongDescription($longDescription)
public function getLongDescription()
public function setTags(Array $tags)
public function setTag($tag)
public function getTags()
public function generate()
}
```
### Zend\\Code\\Generator\\DocBlock\\Tag
`Zend\Code\Generator\DocBlock\Tag` is intended for creating arbitrary annotation tags for inclusion
in *PHP* docblocks. Tags are expected to contain a name (the portion immediately following the '@'
symbol) and a description (everything following the tag name).
The class *API* is as follows:
```php
class Zend\Code\Generator\DocBlock\Tag
extends Zend\Code\Generator\AbstractGenerator
{
public static function fromReflection(
Zend\Code\Reflection\DocBlock\Tag\TagInterface $reflectionTag
)
public function setName($name)
public function getName()
public function setDescription($description)
public function getDescription()
public function generate()
}
```
### Zend\\Code\\Generator\\DocBlock\\Tag\\ParamTag
`Zend\Code\Generator\DocBlock\Tag\ParamTag` is a specialized version of
`Zend\Code\Generator\DocBlock\Tag`, and represents a method parameter. The tag name is therefor
known ("param"), but due to the format of this annotation tag, additional information is required in
order to generate it: the parameter name and data type it represents.
The class *API* is as follows:
```php
class Zend\Code\Generator\DocBlock\Tag\ParamTag
extends Zend\Code\Generator\DocBlock\Tag
{
public static function fromReflection(
Zend\Code\Reflection\DocBlock\Tag\TagInterface $reflectionTagParam
)
public function setDatatype($datatype)
public function getDatatype()
public function setParamName($paramName)
public function getParamName()
public function generate()
}
```
### Zend\\Code\\Generator\\DocBlock\\Tag\\ReturnTag
Like the param docblock tag variant, `Zend\Code\Generator\DocBlock\Tag\ReturnTag` is an annotation
tag variant for representing a method return value. In this case, the annotation tag name is known
("return"), but requires a return type.
The class *API* is as follows:
```php
class Zend\Code\Generator\DocBlock\Tag\ParamTag
extends Zend\Code\Generator\DocBlock\Tag
{
public static function fromReflection(
Zend\Code\Reflection\DocBlock\Tag\TagInterface $reflectionTagReturn
)
public function setDatatype($datatype)
public function getDatatype()
public function generate()
}
```
### Zend\\Code\\Generator\\FileGenerator
`Zend\Code\Generator\FileGenerator` is used to generate the full contents of a file that will
contain *PHP* code. The file may contain classes or arbitrary *PHP* code, as well as a file-level
docblock if desired.
When adding classes to the file, you will need to pass either an array of information to pass to the
`Zend\Code\Generator\ClassGenerator` constructor, or an instance of that class. Similarly, with
docblocks, you will need to pass information for the `Zend\Code\Generator\DocBlockGenerator`
constructor to consume or an instance of the class.
The *API* of the class is as follows:
```php
class Zend\Code\Generator\FileGenerator extends Zend\Code\Generator\AbstractGenerator
{
public static function fromReflectedFilePath(
$filePath,
$usePreviousCodeGeneratorIfItExists = true,
$includeIfNotAlreadyIncluded = true)
public static function fromReflection(Zend\Code\Reflection\FileReflection $reflectionFile)
public function setDocblock(Zend\Code\Generator\DocBlockGenerator $docblock)
public function getDocblock()
public function setRequiredFiles($requiredFiles)
public function getRequiredFiles()
public function setClasses(Array $classes)
public function getClass($name = null)
public function setClass($class)
public function setFilename($filename)
public function getFilename()
public function getClasses()
public function setBody($body)
public function getBody()
public function isSourceDirty()
public function generate()
}
```
### Zend\\Code\\Generator\\Member\\ContainerGenerator
`Zend\Code\Generator\Member\ContainerGenerator` is used internally by
`Zend\Code\Generator\ClassGenerator` to keep track of class members -- properties and methods alike.
These are indexed by name, using the concrete instances of the members as values.
The *API* of the class is as follows:
```php
class Zend\Code\Generator\Member\ContainerGenerator extends ArrayObject
{
public function __construct($type = self::TYPE_PROPERTY)
}
```
### Zend\\Code\\Generator\\MethodGenerator
`Zend\Code\Generator\MethodGenerator` describes a class method, and can generate both the code and
the docblock for the method. The visibility and status as static, abstract, or final may be
indicated, per its parent class, `Zend\Code\Generator\AbstractMemberGenerator`. Finally, the
parameters and return value for the method may be specified.
Parameters may be set using `setParameter()` or `setParameters()`. In each case, a parameter should
either be an array of information to pass to the `Zend\Code\Generator\ParameterGenerator`
constructor or an instance of that class.
The *API* of the class is as follows:
```php
class Zend\Code\Generator\MethodGenerator
extends Zend\Code\Generator\AbstractMemberGenerator
{
public static function fromReflection(
Zend\Code\Reflection\MethodReflection $reflectionMethod
)
public function setDocblock(Zend\Code\Generator\DocBlockGenerator $docblock)
public function getDocblock()
public function setFinal($isFinal)
public function setParameters(Array $parameters)
public function setParameter($parameter)
public function getParameters()
public function setBody($body)
public function getBody()
public function generate()
}
```
### Zend\\Code\\Generator\\ParameterGenerator
`Zend\Code\Generator\ParameterGenerator` may be used to specify method parameters. Each parameter
may have a position (if unspecified, the order in which they are registered with the method will be
used), a default value, and a data type; a parameter name is required.
The *API* of the class is as follows:
```php
class Zend\Code\Generator\ParameterGenerator extends Zend\Code\Generator\AbstractGenerator
{
public static function fromReflection(
Zend\Code\Reflection\ParameterReflection $reflectionParameter
)
public function setType($type)
public function getType()
public function setName($name)
public function getName()
public function setDefaultValue($defaultValue)
public function getDefaultValue()
public function setPosition($position)
public function getPosition()
public function getPassedByReference()
public function setPassedByReference($passedByReference)
public function generate()
}
```
There are several problems that might occur when trying to set `NULL`, booleans or arrays as default
values. For this the value holder object `Zend\Code\Generator\ParameterDefaultValueGenerator` can be
used, for example:
```php
$parameter = new Zend\Code\Generator\ParameterGenerator();
$parameter->setDefaultValue(
new Zend\Code\Generator\ValueGenerator("null")
);
$parameter->setDefaultValue(
new Zend\Code\Generator\ValueGenerator("['foo', 'bar']")
);
```
Internally `setDefaultValue()` also converts the values which can't be expressed in *PHP* into the
value holder.
### Zend\\Code\\Generator\\PropertyGenerator
`Zend\Code\Generator\PropertyGenerator` describes a class property, which may be either a constant
or a variable. In each case, the property may have an optional default value associated with it.
Additionally, the visibility of variable properties may be set, per the parent class,
`Zend\Code\Generator\AbstractMemberGenerator`.
The *API* of the class is as follows:
```php
class Zend\Code\Generator\PropertyGenerator
extends Zend\Code\Generator\AbstractMemberGenerator
{
public static function fromReflection(
Zend\Code\Reflection\PropertyReflection $reflectionProperty
)
public function setConst($const)
public function isConst()
public function setDefaultValue($defaultValue)
public function getDefaultValue()
public function generate()
}
```

View file

@ -0,0 +1,10 @@
<div class="container">
<div class="jumbotron">
<h1>zend-code</h1>
<p>Extensions to the PHP Reflection API, static code scanning, and code generation.</p>
<pre><code class="language-bash">$ composer require zendframework/zend-code</code></pre>
</div>
</div>

View file

@ -0,0 +1 @@
../../README.md

View file

@ -0,0 +1,130 @@
# Migrating from zend-code v2 to v3
## `string`, `int`, `float`, `bool` are no longer ignored
In 2.x, a `Zend\Code\Generator\ParameterGenerator` with name `foo` and type
`string`, `int`, `float` or `bool` simply generated code `"$foo"`:
```php
$generator = new \Zend\Code\ParameterGenerator('foo');
$generator->setType('string');
echo $generator->generate(); // "$foo"
```
In 3.x, this code will instead produce `"string $foo"`.
If you generate code that should run in PHP 5.x, it is advisable to strip
`string`, `int`, `float` and `bool` from type definitions passed to
`Zend\Code\ParameterGenerator` instances. The quickest way is to set the
type to `null`, if it matches any of these scalar types:
```php
if (in_array($type, ['string', 'int', 'float', 'bool'])) {
$type = null;
}
$generator->setType($type);
```
## `ParameterReflection::getType()` changes
PHP 7 introduced [`ReflectionParameter#getType()`](http://php.net/manual/en/reflectionparameter.gettype.php).
In order to not override this method, `Zend\Code\Reflection\ParameterReflection#getType()`
was renamed to `Zend\Code\Reflection\ParameterReflection#detectType()`.
If you relied on `Zend\Code\Reflection\ParameterReflection#getType()`, you can
simply replace the method calls in your code.
## DocBlock types ignored by `ParameterGenerator::fromReflection()`
As a direct consequence of the previous change, calls to
`Zend\Code\Generator\ParameterGenerator::fromReflection()` will not mirror the
type hints read from a method's DocBlock.
As an example, take following code:
```php
class Foo
{
/**
* @param string $baz
*/
public function bar($baz)
{
}
}
$methodGenerator = \Zend\Code\Generator\MethodGenerator::fromReflection(
new \Zend\Code\Reflection\MethodReflection('Foo', 'bar')
);
var_dump($methodGenerator->getParameters()[0]->getType());
```
In version 2.x, this code produces `"string"`, in version 3.x it returns `null`. If you
need to rely on the types in the annotations, please use
`Zend\Code\Reflection\ParameterReflection#detectType()` instead, and build a
`MethodGenerator` instance manually.
This change is required: since signatures in PHP 7 include scalar type hints.
That also means that reflecting scalar type hints from DocBlocks into the
signature of a generated method may lead to fatal errors (due to signature
mismatch) at runtime.
## Type strings are validated
If you attempt to generate type-hints for parameters or return types, those types are
now validated before the code is generated.
Be sure to check which values you pass to `Zend\Code\Generator\MethodGenerator#setReturnType()`
or `Zend\Code\Generator\ParameterGenerator#setType()`, as you may incur in a
`Zend\Code\Generator\Exception\InvalidArgumentException` being thrown if any
of those types are invalid strings:
```php
$parameterGenerator->setType('foo'); // valid
$parameterGenerator->setType('array'); // valid
$parameterGenerator->setType('bool'); // valid
$parameterGenerator->setType('123'); // invalid (throws exception)
$parameterGenerator->setType(''); // invalid (throws exception)
$parameterGenerator->setType('*'); // invalid (throws exception)
$parameterGenerator->setType('\\'); // invalid (throws exception)
```
## Generated type-hints are now prefixed by `"\"`
Generated type-hints are now prefixed with the `NAMESPACE_SEPARATOR`,
`"\"`.
Take following example code:
```php
$parameter = new \Zend\Code\Generator\ParameterGenerator('bar', 'baz');
$method = new \Zend\Code\Generator\MethodGenerator('foo', [$parameter]);
$method->setReturnType('tab');
echo $method->generate();
```
This code produces `public function foo(baz $bar) {}` in 2.x.
In version 3.x, it produces `public function foo(\baz $bar) : \tab {}`.
In order to avoid migration problems, be sure to always pass fully qualified class
names to `Zend\Code\Generator\MethodGenerator` and `Zend\Code\Generator\ParameterGenerator`.
## `ParameterGenerator::$simple` was removed
If you extended `Zend\Code\Generator\ParameterGenerator`, be sure to check if you
are accessing the protected static variable `$simple`: it was removed, and you should
adapt your code by either copying it into your class or avoiding its usage.
## `ParameterGenerator::$type` has changed
If you extended `Zend\Code\Generator\ParameterGenerator`, be sure to check if you
are accessing the protected variable `$type`: its type has changed.
While it can still be used as a string via an explicit `(string)` cast, the type of
this protected member is now `null|Zend\Code\Generator\TypeGenerator`.

View file

@ -0,0 +1,13 @@
docs_dir: doc/book
site_dir: doc/html
pages:
- index.md
- "Code Generation":
- Intro: generator/intro.md
- Examples: generator/examples.md
- Reference: generator/reference.md
- Migration: migration.md
site_name: zend-code
site_description: 'zend-code: Tools for reflecting, scanning, and generating PHP code'
repo_url: 'https://github.com/zendframework/zend-code'
copyright: 'Copyright (c) 2016 <a href="http://www.zend.com/">Zend Technologies USA Inc.</a>'

View file

@ -0,0 +1,32 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Annotation;
use ArrayObject;
class AnnotationCollection extends ArrayObject
{
/**
* Checks if the collection has annotations for a class
*
* @param string $class
* @return bool
*/
public function hasAnnotation($class)
{
foreach ($this as $annotation) {
if (get_class($annotation) == $class) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Annotation;
interface AnnotationInterface
{
/**
* Initialize
*
* @param string $content
* @return void
*/
public function initialize($content);
}

View file

@ -0,0 +1,109 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Annotation;
use Zend\Code\Annotation\Parser\ParserInterface;
use Zend\EventManager\Event;
use Zend\EventManager\EventManager;
use Zend\EventManager\EventManagerAwareInterface;
use Zend\EventManager\EventManagerInterface;
/**
* Pluggable annotation manager
*
* Simply composes an EventManager. When createAnnotation() is called, it fires
* off an event of the same name, passing it the resolved annotation class, the
* annotation content, and the raw annotation string; the first listener to
* return an object will halt execution of the event, and that object will be
* returned as the annotation.
*/
class AnnotationManager implements EventManagerAwareInterface
{
const EVENT_CREATE_ANNOTATION = 'createAnnotation';
/**
* @var EventManagerInterface
*/
protected $events;
/**
* Set the event manager instance
*
* @param EventManagerInterface $events
* @return AnnotationManager
*/
public function setEventManager(EventManagerInterface $events)
{
$events->setIdentifiers([
__CLASS__,
get_class($this),
]);
$this->events = $events;
return $this;
}
/**
* Retrieve event manager
*
* Lazy loads an instance if none registered.
*
* @return EventManagerInterface
*/
public function getEventManager()
{
if (null === $this->events) {
$this->setEventManager(new EventManager());
}
return $this->events;
}
/**
* Attach a parser to listen to the createAnnotation event
*
* @param ParserInterface $parser
* @return AnnotationManager
*/
public function attach(ParserInterface $parser)
{
$this->getEventManager()
->attach(self::EVENT_CREATE_ANNOTATION, [$parser, 'onCreateAnnotation']);
return $this;
}
/**
* Create Annotation
*
* @param string[] $annotationData
* @return false|\stdClass
*/
public function createAnnotation(array $annotationData)
{
$event = new Event();
$event->setName(self::EVENT_CREATE_ANNOTATION);
$event->setTarget($this);
$event->setParams([
'class' => $annotationData[0],
'content' => $annotationData[1],
'raw' => $annotationData[2],
]);
$eventManager = $this->getEventManager();
$results = $eventManager->triggerEventUntil(function ($r) {
return (is_object($r));
}, $event);
$annotation = $results->last();
return (is_object($annotation) ? $annotation : false);
}
}

View file

@ -0,0 +1,153 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Annotation\Parser;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Doctrine\Common\Annotations\DocParser;
use Traversable;
use Zend\Code\Exception;
use Zend\EventManager\EventInterface;
/**
* A parser for docblock annotations that utilizes the annotation parser from
* Doctrine\Common.
*
* Consumes Doctrine\Common\Annotations\DocParser, and responds to events from
* AnnotationManager. If the annotation examined is in the list of classes we
* are interested in, the raw annotation is passed to the DocParser in order to
* retrieve the annotation object instance. Otherwise, it is skipped.
*/
class DoctrineAnnotationParser implements ParserInterface
{
/**
* @var array Annotation classes we support on this iteration
*/
protected $allowedAnnotations = [];
/**
* @var DocParser
*/
protected $docParser;
public function __construct()
{
// Hack to ensure an attempt to autoload an annotation class is made
AnnotationRegistry::registerLoader(function ($class) {
return (bool) class_exists($class);
});
}
/**
* Set the DocParser instance
*
* @param DocParser $docParser
* @return DoctrineAnnotationParser
*/
public function setDocParser(DocParser $docParser)
{
$this->docParser = $docParser;
return $this;
}
/**
* Retrieve the DocParser instance
*
* If none is registered, lazy-loads a new instance.
*
* @return DocParser
*/
public function getDocParser()
{
if (!$this->docParser instanceof DocParser) {
$this->setDocParser(new DocParser());
}
return $this->docParser;
}
/**
* Handle annotation creation
*
* @param EventInterface $e
* @return false|\stdClass
*/
public function onCreateAnnotation(EventInterface $e)
{
$annotationClass = $e->getParam('class', false);
if (!$annotationClass) {
return false;
}
if (!isset($this->allowedAnnotations[$annotationClass])) {
return false;
}
$annotationString = $e->getParam('raw', false);
if (!$annotationString) {
return false;
}
// Annotation classes provided by the AnnotationScanner are already
// resolved to fully-qualified class names. Adding the global namespace
// prefix allows the Doctrine annotation parser to locate the annotation
// class correctly.
$annotationString = preg_replace('/^(@)/', '$1\\', $annotationString);
$parser = $this->getDocParser();
$annotations = $parser->parse($annotationString);
if (empty($annotations)) {
return false;
}
$annotation = array_shift($annotations);
if (!is_object($annotation)) {
return false;
}
return $annotation;
}
/**
* Specify an allowed annotation class
*
* @param string $annotation
* @return DoctrineAnnotationParser
*/
public function registerAnnotation($annotation)
{
$this->allowedAnnotations[$annotation] = true;
return $this;
}
/**
* Set many allowed annotations at once
*
* @param array|Traversable $annotations Array or traversable object of
* annotation class names
* @throws Exception\InvalidArgumentException
* @return DoctrineAnnotationParser
*/
public function registerAnnotations($annotations)
{
if (!is_array($annotations) && !$annotations instanceof Traversable) {
throw new Exception\InvalidArgumentException(sprintf(
'%s: expects an array or Traversable; received "%s"',
__METHOD__,
(is_object($annotations) ? get_class($annotations) : gettype($annotations))
));
}
foreach ($annotations as $annotation) {
$this->allowedAnnotations[$annotation] = true;
}
return $this;
}
}

View file

@ -0,0 +1,224 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Annotation\Parser;
use Traversable;
use Zend\Code\Annotation\AnnotationInterface;
use Zend\Code\Exception;
use Zend\EventManager\EventInterface;
/**
* Generic annotation parser
*
* Expects registration of AnnotationInterface instances. Such instances
* will be passed annotation content to their initialize() method, which
* they are then responsible for parsing.
*/
class GenericAnnotationParser implements ParserInterface
{
/**
* @var array
*/
protected $aliases = [];
/**
* @var array
*/
protected $annotationNames = [];
/**
* @var AnnotationInterface[]
*/
protected $annotations = [];
/**
* Listen to onCreateAnnotation, and attempt to return an annotation object
* instance.
*
* If the annotation class or alias is not registered, immediately returns
* false. Otherwise, resolves the class, clones it, and, if any content is
* present, calls {@link AnnotationInterface::initialize()} with the
* content.
*
* @param EventInterface $e
* @return false|AnnotationInterface
*/
public function onCreateAnnotation(EventInterface $e)
{
$class = $e->getParam('class', false);
if (!$class || !$this->hasAnnotation($class)) {
return false;
}
$content = $e->getParam('content', '');
$content = trim($content, '()');
if ($this->hasAlias($class)) {
$class = $this->resolveAlias($class);
}
$index = array_search($class, $this->annotationNames);
$annotation = $this->annotations[$index];
$newAnnotation = clone $annotation;
if ($content) {
$newAnnotation->initialize($content);
}
return $newAnnotation;
}
/**
* Register annotations
*
* @param string|AnnotationInterface $annotation String class name of an
* AnnotationInterface implementation, or actual instance
* @return GenericAnnotationParser
* @throws Exception\InvalidArgumentException
*/
public function registerAnnotation($annotation)
{
$class = false;
if (is_string($annotation) && class_exists($annotation)) {
$class = $annotation;
$annotation = new $annotation();
}
if (!$annotation instanceof AnnotationInterface) {
throw new Exception\InvalidArgumentException(sprintf(
'%s: expects an instance of %s\AnnotationInterface; received "%s"',
__METHOD__,
__NAMESPACE__,
(is_object($annotation) ? get_class($annotation) : gettype($annotation))
));
}
$class = $class ?: get_class($annotation);
if (in_array($class, $this->annotationNames)) {
throw new Exception\InvalidArgumentException(sprintf(
'An annotation for this class %s already exists',
$class
));
}
$this->annotations[] = $annotation;
$this->annotationNames[] = $class;
}
/**
* Register many annotations at once
*
* @param array|Traversable $annotations
* @throws Exception\InvalidArgumentException
* @return GenericAnnotationParser
*/
public function registerAnnotations($annotations)
{
if (!is_array($annotations) && !$annotations instanceof Traversable) {
throw new Exception\InvalidArgumentException(sprintf(
'%s: expects an array or Traversable; received "%s"',
__METHOD__,
(is_object($annotations) ? get_class($annotations) : gettype($annotations))
));
}
foreach ($annotations as $annotation) {
$this->registerAnnotation($annotation);
}
return $this;
}
/**
* Checks if the manager has annotations for a class
*
* @param string $class
* @return bool
*/
public function hasAnnotation($class)
{
if (in_array($class, $this->annotationNames)) {
return true;
}
if ($this->hasAlias($class)) {
return true;
}
return false;
}
/**
* Alias an annotation name
*
* @param string $alias
* @param string $class May be either a registered annotation name or another alias
* @throws Exception\InvalidArgumentException
* @return GenericAnnotationParser
*/
public function setAlias($alias, $class)
{
if (!in_array($class, $this->annotationNames) && !$this->hasAlias($class)) {
throw new Exception\InvalidArgumentException(sprintf(
'%s: Cannot alias "%s" to "%s", as class "%s" is not currently a registered annotation or alias',
__METHOD__,
$alias,
$class,
$class
));
}
$alias = $this->normalizeAlias($alias);
$this->aliases[$alias] = $class;
return $this;
}
/**
* Normalize an alias name
*
* @param string $alias
* @return string
*/
protected function normalizeAlias($alias)
{
return strtolower(str_replace(['-', '_', ' ', '\\', '/'], '', $alias));
}
/**
* Do we have an alias by the provided name?
*
* @param string $alias
* @return bool
*/
protected function hasAlias($alias)
{
$alias = $this->normalizeAlias($alias);
return (isset($this->aliases[$alias]));
}
/**
* Resolve an alias to a class name
*
* @param string $alias
* @return string
*/
protected function resolveAlias($alias)
{
do {
$normalized = $this->normalizeAlias($alias);
$class = $this->aliases[$normalized];
} while ($this->hasAlias($class));
return $class;
}
}

View file

@ -0,0 +1,39 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Annotation\Parser;
use Zend\EventManager\EventInterface;
interface ParserInterface
{
/**
* Respond to the "createAnnotation" event
*
* @param EventInterface $e
* @return false|\stdClass
*/
public function onCreateAnnotation(EventInterface $e);
/**
* Register an annotation this parser will accept
*
* @param mixed $annotation
* @return void
*/
public function registerAnnotation($annotation);
/**
* Register multiple annotations this parser will accept
*
* @param array|\Traversable $annotations
* @return void
*/
public function registerAnnotations($annotations);
}

View file

@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Exception;
class BadMethodCallException extends \BadMethodCallException implements
ExceptionInterface
{
}

View file

@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Exception;
interface ExceptionInterface
{
}

View file

@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Exception;
class InvalidArgumentException extends \InvalidArgumentException implements
ExceptionInterface
{
}

View file

@ -0,0 +1,14 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Exception;
class RuntimeException extends \RuntimeException implements ExceptionInterface
{
}

View file

@ -0,0 +1,124 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Traversable;
abstract class AbstractGenerator implements GeneratorInterface
{
/**
* Line feed to use in place of EOL
*/
const LINE_FEED = "\n";
/**
* @var bool
*/
protected $isSourceDirty = true;
/**
* @var int|string 4 spaces by default
*/
protected $indentation = ' ';
/**
* @var string
*/
protected $sourceContent = null;
/**
* @param array $options
*/
public function __construct($options = [])
{
if ($options) {
$this->setOptions($options);
}
}
/**
* @param bool $isSourceDirty
* @return AbstractGenerator
*/
public function setSourceDirty($isSourceDirty = true)
{
$this->isSourceDirty = (bool) $isSourceDirty;
return $this;
}
/**
* @return bool
*/
public function isSourceDirty()
{
return $this->isSourceDirty;
}
/**
* @param string $indentation
* @return AbstractGenerator
*/
public function setIndentation($indentation)
{
$this->indentation = (string) $indentation;
return $this;
}
/**
* @return string
*/
public function getIndentation()
{
return $this->indentation;
}
/**
* @param string $sourceContent
* @return AbstractGenerator
*/
public function setSourceContent($sourceContent)
{
$this->sourceContent = (string) $sourceContent;
return $this;
}
/**
* @return string
*/
public function getSourceContent()
{
return $this->sourceContent;
}
/**
* @param array|Traversable $options
* @throws Exception\InvalidArgumentException
* @return AbstractGenerator
*/
public function setOptions($options)
{
if (!is_array($options) && !$options instanceof Traversable) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects an array or Traversable object; received "%s"',
__METHOD__,
(is_object($options) ? get_class($options) : gettype($options))
));
}
foreach ($options as $optionName => $optionValue) {
$methodName = 'set' . $optionName;
if (method_exists($this, $methodName)) {
$this->{$methodName}($optionValue);
}
}
return $this;
}
}

View file

@ -0,0 +1,242 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
abstract class AbstractMemberGenerator extends AbstractGenerator
{
/**#@+
* @const int Flags for construction usage
*/
const FLAG_ABSTRACT = 0x01;
const FLAG_FINAL = 0x02;
const FLAG_STATIC = 0x04;
const FLAG_INTERFACE = 0x08;
const FLAG_PUBLIC = 0x10;
const FLAG_PROTECTED = 0x20;
const FLAG_PRIVATE = 0x40;
/**#@-*/
/**#@+
* @param const string
*/
const VISIBILITY_PUBLIC = 'public';
const VISIBILITY_PROTECTED = 'protected';
const VISIBILITY_PRIVATE = 'private';
/**#@-*/
/**
* @var DocBlockGenerator
*/
protected $docBlock = null;
/**
* @var string
*/
protected $name = null;
/**
* @var int
*/
protected $flags = self::FLAG_PUBLIC;
/**
* @param int|array $flags
* @return AbstractMemberGenerator
*/
public function setFlags($flags)
{
if (is_array($flags)) {
$flagsArray = $flags;
$flags = 0x00;
foreach ($flagsArray as $flag) {
$flags |= $flag;
}
}
// check that visibility is one of three
$this->flags = $flags;
return $this;
}
/**
* @param int $flag
* @return AbstractMemberGenerator
*/
public function addFlag($flag)
{
$this->setFlags($this->flags | $flag);
return $this;
}
/**
* @param int $flag
* @return AbstractMemberGenerator
*/
public function removeFlag($flag)
{
$this->setFlags($this->flags & ~$flag);
return $this;
}
/**
* @param bool $isAbstract
* @return AbstractMemberGenerator
*/
public function setAbstract($isAbstract)
{
return (($isAbstract) ? $this->addFlag(self::FLAG_ABSTRACT) : $this->removeFlag(self::FLAG_ABSTRACT));
}
/**
* @return bool
*/
public function isAbstract()
{
return (bool) ($this->flags & self::FLAG_ABSTRACT);
}
/**
* @param bool $isInterface
* @return AbstractMemberGenerator
*/
public function setInterface($isInterface)
{
return (($isInterface) ? $this->addFlag(self::FLAG_INTERFACE) : $this->removeFlag(self::FLAG_INTERFACE));
}
/**
* @return bool
*/
public function isInterface()
{
return (bool) ($this->flags & self::FLAG_INTERFACE);
}
/**
* @param bool $isFinal
* @return AbstractMemberGenerator
*/
public function setFinal($isFinal)
{
return (($isFinal) ? $this->addFlag(self::FLAG_FINAL) : $this->removeFlag(self::FLAG_FINAL));
}
/**
* @return bool
*/
public function isFinal()
{
return (bool) ($this->flags & self::FLAG_FINAL);
}
/**
* @param bool $isStatic
* @return AbstractMemberGenerator
*/
public function setStatic($isStatic)
{
return (($isStatic) ? $this->addFlag(self::FLAG_STATIC) : $this->removeFlag(self::FLAG_STATIC));
}
/**
* @return bool
*/
public function isStatic()
{
return (bool) ($this->flags & self::FLAG_STATIC); // is FLAG_STATIC in flags
}
/**
* @param string $visibility
* @return AbstractMemberGenerator
*/
public function setVisibility($visibility)
{
switch ($visibility) {
case self::VISIBILITY_PUBLIC:
$this->removeFlag(self::FLAG_PRIVATE | self::FLAG_PROTECTED); // remove both
$this->addFlag(self::FLAG_PUBLIC);
break;
case self::VISIBILITY_PROTECTED:
$this->removeFlag(self::FLAG_PUBLIC | self::FLAG_PRIVATE); // remove both
$this->addFlag(self::FLAG_PROTECTED);
break;
case self::VISIBILITY_PRIVATE:
$this->removeFlag(self::FLAG_PUBLIC | self::FLAG_PROTECTED); // remove both
$this->addFlag(self::FLAG_PRIVATE);
break;
}
return $this;
}
/**
* @return string
*/
public function getVisibility()
{
switch (true) {
case ($this->flags & self::FLAG_PROTECTED):
return self::VISIBILITY_PROTECTED;
case ($this->flags & self::FLAG_PRIVATE):
return self::VISIBILITY_PRIVATE;
default:
return self::VISIBILITY_PUBLIC;
}
}
/**
* @param string $name
* @return AbstractMemberGenerator
*/
public function setName($name)
{
$this->name = (string) $name;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param DocBlockGenerator|string $docBlock
* @throws Exception\InvalidArgumentException
* @return AbstractMemberGenerator
*/
public function setDocBlock($docBlock)
{
if (is_string($docBlock)) {
$docBlock = new DocBlockGenerator($docBlock);
} elseif (!$docBlock instanceof DocBlockGenerator) {
throw new Exception\InvalidArgumentException(sprintf(
'%s is expecting either a string, array or an instance of %s\DocBlockGenerator',
__METHOD__,
__NAMESPACE__
));
}
$this->docBlock = $docBlock;
return $this;
}
/**
* @return DocBlockGenerator
*/
public function getDocBlock()
{
return $this->docBlock;
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
class BodyGenerator extends AbstractGenerator
{
/**
* @var string
*/
protected $content = null;
/**
* @param string $content
* @return BodyGenerator
*/
public function setContent($content)
{
$this->content = (string) $content;
return $this;
}
/**
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @return string
*/
public function generate()
{
return $this->getContent();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,50 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock;
use Zend\Code\Generator\DocBlock\Tag\GenericTag;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface as ReflectionTagInterface;
/**
* @deprecated Deprecated in 2.3. Use GenericTag instead
*/
class Tag extends GenericTag
{
/**
* @param ReflectionTagInterface $reflectionTag
* @return Tag
* @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead
*/
public static function fromReflection(ReflectionTagInterface $reflectionTag)
{
$tagManager = new TagManager();
$tagManager->initializeDefaultTags();
return $tagManager->createTagFromReflection($reflectionTag);
}
/**
* @param string $description
* @return Tag
* @deprecated Deprecated in 2.3. Use GenericTag::setContent() instead
*/
public function setDescription($description)
{
return $this->setContent($description);
}
/**
* @return string
* @deprecated Deprecated in 2.3. Use GenericTag::getContent() instead
*/
public function getDescription()
{
return $this->getContent();
}
}

View file

@ -0,0 +1,96 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\AbstractGenerator;
/**
* This abstract class can be used as parent for all tags
* that use a type part in their content.
* @see http://www.phpdoc.org/docs/latest/for-users/phpdoc/types.html
*/
abstract class AbstractTypeableTag extends AbstractGenerator
{
/**
* @var string
*/
protected $description = null;
/**
* @var array
*/
protected $types = [];
/**
* @param array $types
* @param string $description
*/
public function __construct($types = [], $description = null)
{
if (!empty($types)) {
$this->setTypes($types);
}
if (!empty($description)) {
$this->setDescription($description);
}
}
/**
* @param string $description
* @return ReturnTag
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Array of types or string with types delimited by pipe (|)
* e.g. array('int', 'null') or "int|null"
*
* @param array|string $types
* @return ReturnTag
*/
public function setTypes($types)
{
if (is_string($types)) {
$types = explode('|', $types);
}
$this->types = $types;
return $this;
}
/**
* @return array
*/
public function getTypes()
{
return $this->types;
}
/**
* @param string $delimiter
* @return string
*/
public function getTypesAsString($delimiter = '|')
{
return implode($delimiter, $this->types);
}
}

View file

@ -0,0 +1,110 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\AbstractGenerator;
use Zend\Code\Generator\DocBlock\TagManager;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface as ReflectionTagInterface;
class AuthorTag extends AbstractGenerator implements TagInterface
{
/**
* @var string
*/
protected $authorName = null;
/**
* @var string
*/
protected $authorEmail = null;
/**
* @param string $authorName
* @param string $authorEmail
*/
public function __construct($authorName = null, $authorEmail = null)
{
if (!empty($authorName)) {
$this->setAuthorName($authorName);
}
if (!empty($authorEmail)) {
$this->setAuthorEmail($authorEmail);
}
}
/**
* @param ReflectionTagInterface $reflectionTag
* @return ReturnTag
* @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead
*/
public static function fromReflection(ReflectionTagInterface $reflectionTag)
{
$tagManager = new TagManager();
$tagManager->initializeDefaultTags();
return $tagManager->createTagFromReflection($reflectionTag);
}
/**
* @return string
*/
public function getName()
{
return 'author';
}
/**
* @param string $authorEmail
* @return AuthorTag
*/
public function setAuthorEmail($authorEmail)
{
$this->authorEmail = $authorEmail;
return $this;
}
/**
* @return string
*/
public function getAuthorEmail()
{
return $this->authorEmail;
}
/**
* @param string $authorName
* @return AuthorTag
*/
public function setAuthorName($authorName)
{
$this->authorName = $authorName;
return $this;
}
/**
* @return string
*/
public function getAuthorName()
{
return $this->authorName;
}
/**
* @return string
*/
public function generate()
{
$output = '@author'
. ((!empty($this->authorName)) ? ' ' . $this->authorName : '')
. ((!empty($this->authorEmail)) ? ' <' . $this->authorEmail . '>' : '');
return $output;
}
}

View file

@ -0,0 +1,88 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\AbstractGenerator;
use Zend\Code\Generic\Prototype\PrototypeGenericInterface;
class GenericTag extends AbstractGenerator implements TagInterface, PrototypeGenericInterface
{
/**
* @var string
*/
protected $name = null;
/**
* @var string
*/
protected $content = null;
/**
* @param string $name
* @param string $content
*/
public function __construct($name = null, $content = null)
{
if (!empty($name)) {
$this->setName($name);
}
if (!empty($content)) {
$this->setContent($content);
}
}
/**
* @param string $name
* @return GenericTag
*/
public function setName($name)
{
$this->name = ltrim($name, '@');
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $content
* @return GenericTag
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @return string
*/
public function generate()
{
$output = '@' . $this->name
. ((!empty($this->content)) ? ' ' . $this->content : '');
return $output;
}
}

View file

@ -0,0 +1,110 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\AbstractGenerator;
use Zend\Code\Generator\DocBlock\TagManager;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface as ReflectionTagInterface;
class LicenseTag extends AbstractGenerator implements TagInterface
{
/**
* @var string
*/
protected $url = null;
/**
* @var string
*/
protected $licenseName = null;
/**
* @param string $url
* @param string $licenseName
*/
public function __construct($url = null, $licenseName = null)
{
if (!empty($url)) {
$this->setUrl($url);
}
if (!empty($licenseName)) {
$this->setLicenseName($licenseName);
}
}
/**
* @param ReflectionTagInterface $reflectionTag
* @return ReturnTag
* @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead
*/
public static function fromReflection(ReflectionTagInterface $reflectionTag)
{
$tagManager = new TagManager();
$tagManager->initializeDefaultTags();
return $tagManager->createTagFromReflection($reflectionTag);
}
/**
* @return string
*/
public function getName()
{
return 'license';
}
/**
* @param string $url
* @return LicenseTag
*/
public function setUrl($url)
{
$this->url = $url;
return $this;
}
/**
* @return string
*/
public function getUrl()
{
return $this->url;
}
/**
* @param string $name
* @return LicenseTag
*/
public function setLicenseName($name)
{
$this->licenseName = $name;
return $this;
}
/**
* @return string
*/
public function getLicenseName()
{
return $this->licenseName;
}
/**
* @return string
*/
public function generate()
{
$output = '@license'
. ((!empty($this->url)) ? ' ' . $this->url : '')
. ((!empty($this->licenseName)) ? ' ' . $this->licenseName : '');
return $output;
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
class MethodTag extends AbstractTypeableTag implements TagInterface
{
/**
* @var string
*/
protected $methodName = null;
/**
* @var bool
*/
protected $isStatic = false;
/**
* @param string $methodName
* @param array $types
* @param string $description
* @param bool $isStatic
*/
public function __construct($methodName = null, $types = [], $description = null, $isStatic = false)
{
if (!empty($methodName)) {
$this->setMethodName($methodName);
}
$this->setIsStatic((bool) $isStatic);
parent::__construct($types, $description);
}
/**
* @return string
*/
public function getName()
{
return 'method';
}
/**
* @param boolean $isStatic
* @return MethodTag
*/
public function setIsStatic($isStatic)
{
$this->isStatic = $isStatic;
return $this;
}
/**
* @return boolean
*/
public function isStatic()
{
return $this->isStatic;
}
/**
* @param string $methodName
* @return MethodTag
*/
public function setMethodName($methodName)
{
$this->methodName = rtrim($methodName, ')(');
return $this;
}
/**
* @return string
*/
public function getMethodName()
{
return $this->methodName;
}
/**
* @return string
*/
public function generate()
{
$output = '@method'
. (($this->isStatic) ? ' static' : '')
. ((!empty($this->types)) ? ' ' . $this->getTypesAsString() : '')
. ((!empty($this->methodName)) ? ' ' . $this->methodName . '()' : '')
. ((!empty($this->description)) ? ' ' . $this->description : '');
return $output;
}
}

View file

@ -0,0 +1,124 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\DocBlock\TagManager;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface as ReflectionTagInterface;
class ParamTag extends AbstractTypeableTag implements TagInterface
{
/**
* @var string
*/
protected $variableName = null;
/**
* @param string $variableName
* @param array $types
* @param string $description
*/
public function __construct($variableName = null, $types = [], $description = null)
{
if (!empty($variableName)) {
$this->setVariableName($variableName);
}
parent::__construct($types, $description);
}
/**
* @param ReflectionTagInterface $reflectionTag
* @return ReturnTag
* @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead
*/
public static function fromReflection(ReflectionTagInterface $reflectionTag)
{
$tagManager = new TagManager();
$tagManager->initializeDefaultTags();
return $tagManager->createTagFromReflection($reflectionTag);
}
/**
* @return string
*/
public function getName()
{
return 'param';
}
/**
* @param string $variableName
* @return ParamTag
*/
public function setVariableName($variableName)
{
$this->variableName = ltrim($variableName, '$');
return $this;
}
/**
* @return string
*/
public function getVariableName()
{
return $this->variableName;
}
/**
* @param string $datatype
* @return ReturnTag
* @deprecated Deprecated in 2.3. Use setTypes() instead
*/
public function setDatatype($datatype)
{
return $this->setTypes($datatype);
}
/**
* @return string
* @deprecated Deprecated in 2.3. Use getTypes() or getTypesAsString() instead
*/
public function getDatatype()
{
return $this->getTypesAsString();
}
/**
* @param string $paramName
* @return ParamTag
* @deprecated Deprecated in 2.3. Use setVariableName() instead
*/
public function setParamName($paramName)
{
return $this->setVariableName($paramName);
}
/**
* @return string
* @deprecated Deprecated in 2.3. Use getVariableName() instead
*/
public function getParamName()
{
return $this->getVariableName();
}
/**
* @return string
*/
public function generate()
{
$output = '@param'
. ((!empty($this->types)) ? ' ' . $this->getTypesAsString() : '')
. ((!empty($this->variableName)) ? ' $' . $this->variableName : '')
. ((!empty($this->description)) ? ' ' . $this->description : '');
return $output;
}
}

View file

@ -0,0 +1,71 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
class PropertyTag extends AbstractTypeableTag implements TagInterface
{
/**
* @var string
*/
protected $propertyName = null;
/**
* @param string $propertyName
* @param array $types
* @param string $description
*/
public function __construct($propertyName = null, $types = [], $description = null)
{
if (!empty($propertyName)) {
$this->setPropertyName($propertyName);
}
parent::__construct($types, $description);
}
/**
* @return string
*/
public function getName()
{
return 'property';
}
/**
* @param string $propertyName
* @return self
*/
public function setPropertyName($propertyName)
{
$this->propertyName = ltrim($propertyName, '$');
return $this;
}
/**
* @return string
*/
public function getPropertyName()
{
return $this->propertyName;
}
/**
* @return string
*/
public function generate()
{
$output = '@property'
. ((!empty($this->types)) ? ' ' . $this->getTypesAsString() : '')
. ((!empty($this->propertyName)) ? ' $' . $this->propertyName : '')
. ((!empty($this->description)) ? ' ' . $this->description : '');
return $output;
}
}

View file

@ -0,0 +1,67 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\DocBlock\TagManager;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface as ReflectionTagInterface;
class ReturnTag extends AbstractTypeableTag implements TagInterface
{
/**
* @param ReflectionTagInterface $reflectionTag
* @return ReturnTag
* @deprecated Deprecated in 2.3. Use TagManager::createTagFromReflection() instead
*/
public static function fromReflection(ReflectionTagInterface $reflectionTag)
{
$tagManager = new TagManager();
$tagManager->initializeDefaultTags();
return $tagManager->createTagFromReflection($reflectionTag);
}
/**
* @return string
*/
public function getName()
{
return 'return';
}
/**
* @param string $datatype
* @return ReturnTag
* @deprecated Deprecated in 2.3. Use setTypes() instead
*/
public function setDatatype($datatype)
{
return $this->setTypes($datatype);
}
/**
* @return string
* @deprecated Deprecated in 2.3. Use getTypes() or getTypesAsString() instead
*/
public function getDatatype()
{
return $this->getTypesAsString();
}
/**
* @return string
*/
public function generate()
{
$output = '@return '
. $this->getTypesAsString()
. ((!empty($this->description)) ? ' ' . $this->description : '');
return $output;
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generic\Prototype\PrototypeInterface;
interface TagInterface extends PrototypeInterface
{
}

View file

@ -0,0 +1,33 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock\Tag;
class ThrowsTag extends AbstractTypeableTag implements TagInterface
{
/**
* @return string
*/
public function getName()
{
return 'throws';
}
/**
* @return string
*/
public function generate()
{
$output = '@throws'
. ((!empty($this->types)) ? ' ' . $this->getTypesAsString() : '')
. ((!empty($this->description)) ? ' ' . $this->description : '');
return $output;
}
}

View file

@ -0,0 +1,69 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\DocBlock;
use Zend\Code\Generator\DocBlock\Tag\TagInterface;
use Zend\Code\Generic\Prototype\PrototypeClassFactory;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface as ReflectionTagInterface;
/**
* This class is used in DocBlockGenerator and creates the needed
* Tag classes depending on the tag. So for example an @author tag
* will trigger the creation of an AuthorTag class.
*
* If none of the classes is applicable, the GenericTag class will be
* created
*/
class TagManager extends PrototypeClassFactory
{
/**
* @return void
*/
public function initializeDefaultTags()
{
$this->addPrototype(new Tag\ParamTag());
$this->addPrototype(new Tag\ReturnTag());
$this->addPrototype(new Tag\MethodTag());
$this->addPrototype(new Tag\PropertyTag());
$this->addPrototype(new Tag\AuthorTag());
$this->addPrototype(new Tag\LicenseTag());
$this->addPrototype(new Tag\ThrowsTag());
$this->setGenericPrototype(new Tag\GenericTag());
}
/**
* @param ReflectionTagInterface $reflectionTag
* @return TagInterface
*/
public function createTagFromReflection(ReflectionTagInterface $reflectionTag)
{
$tagName = $reflectionTag->getName();
/* @var TagInterface $newTag */
$newTag = $this->getClonedPrototype($tagName);
// transport any properties via accessors and mutators from reflection to codegen object
$reflectionClass = new \ReflectionClass($reflectionTag);
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $method) {
if (substr($method->getName(), 0, 3) == 'get') {
$propertyName = substr($method->getName(), 3);
if (method_exists($newTag, 'set' . $propertyName)) {
$newTag->{'set' . $propertyName}($reflectionTag->{'get' . $propertyName}());
}
} elseif (substr($method->getName(), 0, 2) == 'is') {
$propertyName = ucfirst($method->getName());
if (method_exists($newTag, 'set' . $propertyName)) {
$newTag->{'set' . $propertyName}($reflectionTag->{$method->getName()}());
}
}
}
return $newTag;
}
}

View file

@ -0,0 +1,274 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Zend\Code\Generator\DocBlock\Tag;
use Zend\Code\Generator\DocBlock\Tag\TagInterface;
use Zend\Code\Generator\DocBlock\TagManager;
use Zend\Code\Reflection\DocBlockReflection;
class DocBlockGenerator extends AbstractGenerator
{
/**
* @var string
*/
protected $shortDescription = null;
/**
* @var string
*/
protected $longDescription = null;
/**
* @var array
*/
protected $tags = [];
/**
* @var string
*/
protected $indentation = '';
/**
* @var bool
*/
protected $wordwrap = true;
protected static $tagManager;
/**
* Build a DocBlock generator object from a reflection object
*
* @param DocBlockReflection $reflectionDocBlock
* @return DocBlockGenerator
*/
public static function fromReflection(DocBlockReflection $reflectionDocBlock)
{
$docBlock = new static();
$docBlock->setSourceContent($reflectionDocBlock->getContents());
$docBlock->setSourceDirty(false);
$docBlock->setShortDescription($reflectionDocBlock->getShortDescription());
$docBlock->setLongDescription($reflectionDocBlock->getLongDescription());
foreach ($reflectionDocBlock->getTags() as $tag) {
$docBlock->setTag(self::getTagManager()->createTagFromReflection($tag));
}
return $docBlock;
}
/**
* Generate from array
*
* @configkey shortdescription string The short description for this doc block
* @configkey longdescription string The long description for this doc block
* @configkey tags array
*
* @throws Exception\InvalidArgumentException
* @param array $array
* @return DocBlockGenerator
*/
public static function fromArray(array $array)
{
$docBlock = new static();
foreach ($array as $name => $value) {
// normalize key
switch (strtolower(str_replace(['.', '-', '_'], '', $name))) {
case 'shortdescription':
$docBlock->setShortDescription($value);
break;
case 'longdescription':
$docBlock->setLongDescription($value);
break;
case 'tags':
$docBlock->setTags($value);
break;
}
}
return $docBlock;
}
protected static function getTagManager()
{
if (!isset(static::$tagManager)) {
static::$tagManager = new TagManager();
static::$tagManager->initializeDefaultTags();
}
return static::$tagManager;
}
/**
* @param string $shortDescription
* @param string $longDescription
* @param array $tags
*/
public function __construct($shortDescription = null, $longDescription = null, array $tags = [])
{
if ($shortDescription) {
$this->setShortDescription($shortDescription);
}
if ($longDescription) {
$this->setLongDescription($longDescription);
}
if (is_array($tags) && $tags) {
$this->setTags($tags);
}
}
/**
* @param string $shortDescription
* @return DocBlockGenerator
*/
public function setShortDescription($shortDescription)
{
$this->shortDescription = $shortDescription;
return $this;
}
/**
* @return string
*/
public function getShortDescription()
{
return $this->shortDescription;
}
/**
* @param string $longDescription
* @return DocBlockGenerator
*/
public function setLongDescription($longDescription)
{
$this->longDescription = $longDescription;
return $this;
}
/**
* @return string
*/
public function getLongDescription()
{
return $this->longDescription;
}
/**
* @param array $tags
* @return DocBlockGenerator
*/
public function setTags(array $tags)
{
foreach ($tags as $tag) {
$this->setTag($tag);
}
return $this;
}
/**
* @param array|TagInterface $tag
* @throws Exception\InvalidArgumentException
* @return DocBlockGenerator
*/
public function setTag($tag)
{
if (is_array($tag)) {
// use deprecated Tag class for backward compatiblity to old array-keys
$genericTag = new Tag();
$genericTag->setOptions($tag);
$tag = $genericTag;
} elseif (!$tag instanceof TagInterface) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects either an array of method options or an instance of %s\DocBlock\Tag\TagInterface',
__METHOD__,
__NAMESPACE__
));
}
$this->tags[] = $tag;
return $this;
}
/**
* @return TagInterface[]
*/
public function getTags()
{
return $this->tags;
}
/**
* @param bool $value
* @return DocBlockGenerator
*/
public function setWordWrap($value)
{
$this->wordwrap = (bool) $value;
return $this;
}
/**
* @return bool
*/
public function getWordWrap()
{
return $this->wordwrap;
}
/**
* @return string
*/
public function generate()
{
if (!$this->isSourceDirty()) {
return $this->docCommentize(trim($this->getSourceContent()));
}
$output = '';
if (null !== ($sd = $this->getShortDescription())) {
$output .= $sd . self::LINE_FEED . self::LINE_FEED;
}
if (null !== ($ld = $this->getLongDescription())) {
$output .= $ld . self::LINE_FEED . self::LINE_FEED;
}
/* @var $tag GeneratorInterface */
foreach ($this->getTags() as $tag) {
$output .= $tag->generate() . self::LINE_FEED;
}
return $this->docCommentize(trim($output));
}
/**
* @param string $content
* @return string
*/
protected function docCommentize($content)
{
$indent = $this->getIndentation();
$output = $indent . '/**' . self::LINE_FEED;
$content = $this->getWordWrap() == true ? wordwrap($content, 80, self::LINE_FEED) : $content;
$lines = explode(self::LINE_FEED, $content);
foreach ($lines as $line) {
$output .= $indent . ' *';
if ($line) {
$output .= " $line";
}
$output .= self::LINE_FEED;
}
$output .= $indent . ' */' . self::LINE_FEED;
return $output;
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\Exception;
use Zend\Code\Exception\ExceptionInterface as Exception;
interface ExceptionInterface extends Exception
{
}

View file

@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\Exception;
use Zend\Code\Exception;
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View file

@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator\Exception;
use Zend\Code\Exception;
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View file

@ -0,0 +1,569 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Zend\Code\Reflection\Exception as ReflectionException;
use Zend\Code\Reflection\FileReflection;
class FileGenerator extends AbstractGenerator
{
/**
* @var string
*/
protected $filename = null;
/**
* @var DocBlockGenerator
*/
protected $docBlock = null;
/**
* @var array
*/
protected $requiredFiles = [];
/**
* @var string
*/
protected $namespace = null;
/**
* @var array
*/
protected $uses = [];
/**
* @var array
*/
protected $classes = [];
/**
* @var string
*/
protected $body = null;
/**
* Passes $options to {@link setOptions()}.
*
* @param array|\Traversable $options
*/
public function __construct($options = null)
{
if (null !== $options) {
$this->setOptions($options);
}
}
/**
* Use this if you intend on generating code generation objects based on the same file.
* This will keep previous changes to the file in tact during the same PHP process
*
* @param string $filePath
* @param bool $includeIfNotAlreadyIncluded
* @throws ReflectionException\InvalidArgumentException If file does not exists
* @throws ReflectionException\RuntimeException If file exists but is not included or required
* @return FileGenerator
*/
public static function fromReflectedFileName($filePath, $includeIfNotAlreadyIncluded = true)
{
$fileReflector = new FileReflection($filePath, $includeIfNotAlreadyIncluded);
$codeGenerator = static::fromReflection($fileReflector);
return $codeGenerator;
}
/**
* @param FileReflection $fileReflection
* @return FileGenerator
*/
public static function fromReflection(FileReflection $fileReflection)
{
$file = new static();
$file->setSourceContent($fileReflection->getContents());
$file->setSourceDirty(false);
$uses = $fileReflection->getUses();
foreach ($fileReflection->getClasses() as $class) {
$phpClass = ClassGenerator::fromReflection($class);
$phpClass->setContainingFileGenerator($file);
foreach ($uses as $fileUse) {
$phpClass->addUse($fileUse['use'], $fileUse['as']);
}
$file->setClass($phpClass);
}
$namespace = $fileReflection->getNamespace();
if ($namespace != '') {
$file->setNamespace($namespace);
}
if ($uses) {
$file->setUses($uses);
}
if (($fileReflection->getDocComment() != '')) {
$docBlock = $fileReflection->getDocBlock();
$file->setDocBlock(DocBlockGenerator::fromReflection($docBlock));
}
return $file;
}
/**
* @param array $values
* @return FileGenerator
*/
public static function fromArray(array $values)
{
$fileGenerator = new static;
foreach ($values as $name => $value) {
switch (strtolower(str_replace(['.', '-', '_'], '', $name))) {
case 'filename':
$fileGenerator->setFilename($value);
continue;
case 'class':
$fileGenerator->setClass(
($value instanceof ClassGenerator)
? $value
: ClassGenerator::fromArray($value)
);
continue;
case 'requiredfiles':
$fileGenerator->setRequiredFiles($value);
continue;
default:
if (property_exists($fileGenerator, $name)) {
$fileGenerator->{$name} = $value;
} elseif (method_exists($fileGenerator, 'set' . $name)) {
$fileGenerator->{'set' . $name}($value);
}
}
}
return $fileGenerator;
}
/**
* @param DocBlockGenerator|array|string $docBlock
* @throws Exception\InvalidArgumentException
* @return FileGenerator
*/
public function setDocBlock($docBlock)
{
if (is_string($docBlock)) {
$docBlock = ['shortDescription' => $docBlock];
}
if (is_array($docBlock)) {
$docBlock = new DocBlockGenerator($docBlock);
} elseif (!$docBlock instanceof DocBlockGenerator) {
throw new Exception\InvalidArgumentException(sprintf(
'%s is expecting either a string, array or an instance of %s\DocBlockGenerator',
__METHOD__,
__NAMESPACE__
));
}
$this->docBlock = $docBlock;
return $this;
}
/**
* @return DocBlockGenerator
*/
public function getDocBlock()
{
return $this->docBlock;
}
/**
* @param array $requiredFiles
* @return FileGenerator
*/
public function setRequiredFiles(array $requiredFiles)
{
$this->requiredFiles = $requiredFiles;
return $this;
}
/**
* @return array
*/
public function getRequiredFiles()
{
return $this->requiredFiles;
}
/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* @param string $namespace
* @return FileGenerator
*/
public function setNamespace($namespace)
{
$this->namespace = (string) $namespace;
return $this;
}
/**
* Returns an array with the first element the use statement, second is the as part.
* If $withResolvedAs is set to true, there will be a third element that is the
* "resolved" as statement, as the second part is not required in use statements
*
* @param bool $withResolvedAs
* @return array
*/
public function getUses($withResolvedAs = false)
{
$uses = $this->uses;
if ($withResolvedAs) {
for ($useIndex = 0, $count = count($uses); $useIndex < $count; $useIndex++) {
if ($uses[$useIndex][1] == '') {
if (($lastSeparator = strrpos($uses[$useIndex][0], '\\')) !== false) {
$uses[$useIndex][2] = substr($uses[$useIndex][0], $lastSeparator + 1);
} else {
$uses[$useIndex][2] = $uses[$useIndex][0];
}
} else {
$uses[$useIndex][2] = $uses[$useIndex][1];
}
}
}
return $uses;
}
/**
* @param array $uses
* @return FileGenerator
*/
public function setUses(array $uses)
{
foreach ($uses as $use) {
$use = (array) $use;
if (array_key_exists('use', $use) && array_key_exists('as', $use)) {
$import = $use['use'];
$alias = $use['as'];
} elseif (count($use) == 2) {
list($import, $alias) = $use;
} else {
$import = current($use);
$alias = null;
}
$this->setUse($import, $alias);
}
return $this;
}
/**
* @param string $use
* @param null|string $as
* @return FileGenerator
*/
public function setUse($use, $as = null)
{
if (!in_array([$use, $as], $this->uses)) {
$this->uses[] = [$use, $as];
}
return $this;
}
/**
* @param array $classes
* @return FileGenerator
*/
public function setClasses(array $classes)
{
foreach ($classes as $class) {
$this->setClass($class);
}
return $this;
}
/**
* @param string $name
* @return ClassGenerator
*/
public function getClass($name = null)
{
if ($name === null) {
reset($this->classes);
return current($this->classes);
}
return $this->classes[(string) $name];
}
/**
* @param array|string|ClassGenerator $class
* @throws Exception\InvalidArgumentException
* @return FileGenerator
*/
public function setClass($class)
{
if (is_array($class)) {
$class = ClassGenerator::fromArray($class);
} elseif (is_string($class)) {
$class = new ClassGenerator($class);
} elseif (!$class instanceof ClassGenerator) {
throw new Exception\InvalidArgumentException(sprintf(
'%s is expecting either a string, array or an instance of %s\ClassGenerator',
__METHOD__,
__NAMESPACE__
));
}
// @todo check for dup here
$className = $class->getName();
$this->classes[$className] = $class;
return $this;
}
/**
* @param string $filename
* @return FileGenerator
*/
public function setFilename($filename)
{
$this->filename = (string) $filename;
return $this;
}
/**
* @return string
*/
public function getFilename()
{
return $this->filename;
}
/**
* @return ClassGenerator[]
*/
public function getClasses()
{
return $this->classes;
}
/**
* @param string $body
* @return FileGenerator
*/
public function setBody($body)
{
$this->body = (string) $body;
return $this;
}
/**
* @return string
*/
public function getBody()
{
return $this->body;
}
/**
* @return bool
*/
public function isSourceDirty()
{
$docBlock = $this->getDocBlock();
if ($docBlock && $docBlock->isSourceDirty()) {
return true;
}
foreach ($this->classes as $class) {
if ($class->isSourceDirty()) {
return true;
}
}
return parent::isSourceDirty();
}
/**
* @return string
*/
public function generate()
{
if ($this->isSourceDirty() === false) {
return $this->sourceContent;
}
$output = '';
// @note body gets populated when FileGenerator created
// from a file. @see fromReflection and may also be set
// via FileGenerator::setBody
$body = $this->getBody();
// start with the body (if there), or open tag
if (preg_match('#(?:\s*)<\?php#', $body) == false) {
$output = '<?php' . self::LINE_FEED;
}
// if there are markers, put the body into the output
if (preg_match('#/\* Zend_Code_Generator_Php_File-(.*?)Marker:#m', $body)) {
$tokens = token_get_all($body);
foreach ($tokens as $token) {
if (is_array($token) && in_array($token[0], [T_OPEN_TAG, T_COMMENT, T_DOC_COMMENT, T_WHITESPACE])
) {
$output .= $token[1];
}
}
$body = '';
}
// Add file DocBlock, if any
if (null !== ($docBlock = $this->getDocBlock())) {
$docBlock->setIndentation('');
if (preg_match('#/\* Zend_Code_Generator_FileGenerator-DocBlockMarker \*/#m', $output)) {
// @codingStandardsIgnoreStart
$output = preg_replace('#/\* Zend_Code_Generator_FileGenerator-DocBlockMarker \*/#m', $docBlock->generate(), $output, 1);
// @codingStandardsIgnoreEnd
} else {
$output .= $docBlock->generate() . self::LINE_FEED;
}
}
// newline
$output .= self::LINE_FEED;
// namespace, if any
$namespace = $this->getNamespace();
if ($namespace) {
$namespace = sprintf('namespace %s;%s', $namespace, str_repeat(self::LINE_FEED, 2));
if (preg_match('#/\* Zend_Code_Generator_FileGenerator-NamespaceMarker \*/#m', $output)) {
$output = preg_replace(
'#/\* Zend_Code_Generator_FileGenerator-NamespaceMarker \*/#m',
$namespace,
$output,
1
);
} else {
$output .= $namespace;
}
}
// process required files
// @todo marker replacement for required files
$requiredFiles = $this->getRequiredFiles();
if (!empty($requiredFiles)) {
foreach ($requiredFiles as $requiredFile) {
$output .= 'require_once \'' . $requiredFile . '\';' . self::LINE_FEED;
}
$output .= self::LINE_FEED;
}
$classes = $this->getClasses();
$classUses = [];
//build uses array
foreach ($classes as $class) {
//check for duplicate use statements
$uses = $class->getUses();
if (!empty($uses) && is_array($uses)) {
$classUses = array_merge($classUses, $uses);
}
}
// process import statements
$uses = $this->getUses();
if (!empty($uses)) {
$useOutput = '';
foreach ($uses as $use) {
list($import, $alias) = $use;
if (null === $alias) {
$tempOutput = sprintf('%s', $import);
} else {
$tempOutput = sprintf('%s as %s', $import, $alias);
}
//don't duplicate use statements
if (!in_array($tempOutput, $classUses)) {
$useOutput .= "use ". $tempOutput .";";
$useOutput .= self::LINE_FEED;
}
}
$useOutput .= self::LINE_FEED;
if (preg_match('#/\* Zend_Code_Generator_FileGenerator-UseMarker \*/#m', $output)) {
$output = preg_replace(
'#/\* Zend_Code_Generator_FileGenerator-UseMarker \*/#m',
$useOutput,
$output,
1
);
} else {
$output .= $useOutput;
}
}
// process classes
if (!empty($classes)) {
foreach ($classes as $class) {
// @codingStandardsIgnoreStart
$regex = str_replace('&', $class->getName(), '/\* Zend_Code_Generator_Php_File-ClassMarker: \{[A-Za-z0-9\\\]+?&\} \*/');
// @codingStandardsIgnoreEnd
if (preg_match('#' . $regex . '#m', $output)) {
$output = preg_replace('#' . $regex . '#', $class->generate(), $output, 1);
} else {
if ($namespace) {
$class->setNamespaceName(null);
}
$output .= $class->generate() . self::LINE_FEED;
}
}
}
if (!empty($body)) {
// add an extra space between classes and
if (!empty($classes)) {
$output .= self::LINE_FEED;
}
$output .= $body;
}
return $output;
}
/**
* @return FileGenerator
* @throws Exception\RuntimeException
*/
public function write()
{
if ($this->filename == '' || !is_writable(dirname($this->filename))) {
throw new Exception\RuntimeException('This code generator object is not writable.');
}
file_put_contents($this->filename, $this->generate());
return $this;
}
}

View file

@ -0,0 +1,44 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Zend\Code\Generator\Exception\RuntimeException;
class FileGeneratorRegistry
{
/**
* @var array $fileCodeGenerators
*/
private static $fileCodeGenerators = [];
/**
* Registry for the Zend\Code package.
*
* @param FileGenerator $fileCodeGenerator
* @param string $fileName
* @throws RuntimeException
*/
public static function registerFileCodeGenerator(FileGenerator $fileCodeGenerator, $fileName = null)
{
if ($fileName === null) {
$fileName = $fileCodeGenerator->getFilename();
}
if ($fileName == '') {
throw new RuntimeException('FileName does not exist.');
}
// cannot use realpath since the file might not exist, but we do need to have the index
// in the same DIRECTORY_SEPARATOR that realpath would use:
$fileName = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $fileName);
static::$fileCodeGenerators[$fileName] = $fileCodeGenerator;
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
interface GeneratorInterface
{
public function generate();
}

View file

@ -0,0 +1,150 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Zend\Code\Reflection\ClassReflection;
class InterfaceGenerator extends ClassGenerator
{
const OBJECT_TYPE = 'interface';
const IMPLEMENTS_KEYWORD = 'extends';
/**
* Build a Code Generation Php Object from a Class Reflection
*
* @param ClassReflection $classReflection
* @return InterfaceGenerator
*/
public static function fromReflection(ClassReflection $classReflection)
{
if (!$classReflection->isInterface()) {
throw new Exception\InvalidArgumentException(sprintf(
'Class %s is not a interface',
$classReflection->getName()
));
}
// class generator
$cg = new static($classReflection->getName());
$methods = [];
$cg->setSourceContent($cg->getSourceContent());
$cg->setSourceDirty(false);
if ($classReflection->getDocComment() != '') {
$cg->setDocBlock(DocBlockGenerator::fromReflection($classReflection->getDocBlock()));
}
// set the namespace
if ($classReflection->inNamespace()) {
$cg->setNamespaceName($classReflection->getNamespaceName());
}
foreach ($classReflection->getMethods() as $reflectionMethod) {
$className = ($cg->getNamespaceName())
? $cg->getNamespaceName() . '\\' . $cg->getName()
: $cg->getName();
if ($reflectionMethod->getDeclaringClass()->getName() == $className) {
$methods[] = MethodGenerator::fromReflection($reflectionMethod);
}
}
foreach ($classReflection->getConstants() as $name => $value) {
$cg->addConstant($name, $value);
}
$cg->addMethods($methods);
return $cg;
}
/**
* Generate from array
*
* @configkey name string [required] Class Name
* @configkey filegenerator FileGenerator File generator that holds this class
* @configkey namespacename string The namespace for this class
* @configkey docblock string The docblock information
* @configkey constants
* @configkey methods
*
* @throws Exception\InvalidArgumentException
* @param array $array
* @return InterfaceGenerator
*/
public static function fromArray(array $array)
{
if (! isset($array['name'])) {
throw new Exception\InvalidArgumentException(
'Class generator requires that a name is provided for this object'
);
}
$cg = new static($array['name']);
foreach ($array as $name => $value) {
// normalize key
switch (strtolower(str_replace(['.', '-', '_'], '', $name))) {
case 'containingfile':
$cg->setContainingFileGenerator($value);
break;
case 'namespacename':
$cg->setNamespaceName($value);
break;
case 'docblock':
$docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value);
$cg->setDocBlock($docBlock);
break;
case 'methods':
$cg->addMethods($value);
break;
case 'constants':
$cg->addConstants($value);
break;
}
}
return $cg;
}
/**
* {@inheritdoc}
*/
public function addPropertyFromGenerator(PropertyGenerator $property)
{
return $this;
}
/**
* {@inheritdoc}
*/
public function addMethodFromGenerator(MethodGenerator $method)
{
$method->setInterface(true);
return parent::addMethodFromGenerator($method);
}
/**
* {@inheritdoc}
*/
public function setExtendedClass($extendedClass)
{
return $this;
}
/**
* {@inheritdoc}
*/
public function setAbstract($isAbstract)
{
return $this;
}
}

View file

@ -0,0 +1,419 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use ReflectionMethod;
use Zend\Code\Reflection\MethodReflection;
class MethodGenerator extends AbstractMemberGenerator
{
/**
* @var DocBlockGenerator
*/
protected $docBlock = null;
/**
* @var ParameterGenerator[]
*/
protected $parameters = [];
/**
* @var string
*/
protected $body = null;
/**
* @var null|TypeGenerator
*/
private $returnType;
/**
* @var bool
*/
private $returnsReference = false;
/**
* @param MethodReflection $reflectionMethod
* @return MethodGenerator
*/
public static function fromReflection(MethodReflection $reflectionMethod)
{
$method = new static();
$declaringClass = $reflectionMethod->getDeclaringClass();
$method->setSourceContent($reflectionMethod->getContents(false));
$method->setSourceDirty(false);
$method->setReturnType(self::extractReturnTypeFromMethodReflection($reflectionMethod));
if ($reflectionMethod->getDocComment() != '') {
$method->setDocBlock(DocBlockGenerator::fromReflection($reflectionMethod->getDocBlock()));
}
$method->setFinal($reflectionMethod->isFinal());
if ($reflectionMethod->isPrivate()) {
$method->setVisibility(self::VISIBILITY_PRIVATE);
} elseif ($reflectionMethod->isProtected()) {
$method->setVisibility(self::VISIBILITY_PROTECTED);
} else {
$method->setVisibility(self::VISIBILITY_PUBLIC);
}
$method->setInterface($declaringClass->isInterface());
$method->setStatic($reflectionMethod->isStatic());
$method->setReturnsReference($reflectionMethod->returnsReference());
$method->setName($reflectionMethod->getName());
foreach ($reflectionMethod->getParameters() as $reflectionParameter) {
$method->setParameter(ParameterGenerator::fromReflection($reflectionParameter));
}
$method->setBody(static::clearBodyIndention($reflectionMethod->getBody()));
return $method;
}
/**
* Identify the space indention from the first line and remove this indention
* from all lines
*
* @param string $body
*
* @return string
*/
protected static function clearBodyIndention($body)
{
if (empty($body)) {
return $body;
}
$lines = explode("\n", $body);
$indention = str_replace(trim($lines[1]), '', $lines[1]);
foreach ($lines as $key => $line) {
if (substr($line, 0, strlen($indention)) == $indention) {
$lines[$key] = substr($line, strlen($indention));
}
}
$body = implode("\n", $lines);
return $body;
}
/**
* Generate from array
*
* @configkey name string [required] Class Name
* @configkey docblock string The docblock information
* @configkey flags int Flags, one of MethodGenerator::FLAG_ABSTRACT MethodGenerator::FLAG_FINAL
* @configkey parameters string Class which this class is extending
* @configkey body string
* @configkey abstract bool
* @configkey final bool
* @configkey static bool
* @configkey visibility string
*
* @throws Exception\InvalidArgumentException
* @param array $array
* @return MethodGenerator
*/
public static function fromArray(array $array)
{
if (!isset($array['name'])) {
throw new Exception\InvalidArgumentException(
'Method generator requires that a name is provided for this object'
);
}
$method = new static($array['name']);
foreach ($array as $name => $value) {
// normalize key
switch (strtolower(str_replace(['.', '-', '_'], '', $name))) {
case 'docblock':
$docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value);
$method->setDocBlock($docBlock);
break;
case 'flags':
$method->setFlags($value);
break;
case 'parameters':
$method->setParameters($value);
break;
case 'body':
$method->setBody($value);
break;
case 'abstract':
$method->setAbstract($value);
break;
case 'final':
$method->setFinal($value);
break;
case 'interface':
$method->setInterface($value);
break;
case 'static':
$method->setStatic($value);
break;
case 'visibility':
$method->setVisibility($value);
break;
case 'returntype':
$method->setReturnType($value);
break;
}
}
return $method;
}
/**
* @param string $name
* @param array $parameters
* @param int $flags
* @param string $body
* @param DocBlockGenerator|string $docBlock
*/
public function __construct(
$name = null,
array $parameters = [],
$flags = self::FLAG_PUBLIC,
$body = null,
$docBlock = null
) {
if ($name) {
$this->setName($name);
}
if ($parameters) {
$this->setParameters($parameters);
}
if ($flags !== self::FLAG_PUBLIC) {
$this->setFlags($flags);
}
if ($body) {
$this->setBody($body);
}
if ($docBlock) {
$this->setDocBlock($docBlock);
}
}
/**
* @param array $parameters
* @return MethodGenerator
*/
public function setParameters(array $parameters)
{
foreach ($parameters as $parameter) {
$this->setParameter($parameter);
}
return $this;
}
/**
* @param ParameterGenerator|array|string $parameter
* @throws Exception\InvalidArgumentException
* @return MethodGenerator
*/
public function setParameter($parameter)
{
if (is_string($parameter)) {
$parameter = new ParameterGenerator($parameter);
}
if (is_array($parameter)) {
$parameter = ParameterGenerator::fromArray($parameter);
}
if (!$parameter instanceof ParameterGenerator) {
throw new Exception\InvalidArgumentException(sprintf(
'%s is expecting either a string, array or an instance of %s\ParameterGenerator',
__METHOD__,
__NAMESPACE__
));
}
$this->parameters[$parameter->getName()] = $parameter;
return $this;
}
/**
* @return ParameterGenerator[]
*/
public function getParameters()
{
return $this->parameters;
}
/**
* @param string $body
* @return MethodGenerator
*/
public function setBody($body)
{
$this->body = $body;
return $this;
}
/**
* @return string
*/
public function getBody()
{
return $this->body;
}
/**
* @param string|null
*
* @return MethodGenerator
*/
public function setReturnType($returnType = null)
{
$this->returnType = null === $returnType
? null
: TypeGenerator::fromTypeString($returnType);
return $this;
}
/**
* @return TypeGenerator|null
*/
public function getReturnType()
{
return $this->returnType;
}
/**
* @param bool $returnsReference
*
* @return MethodGenerator
*/
public function setReturnsReference($returnsReference)
{
$this->returnsReference = (bool) $returnsReference;
return $this;
}
/**
* @return string
*/
public function generate()
{
$output = '';
$indent = $this->getIndentation();
if (($docBlock = $this->getDocBlock()) !== null) {
$docBlock->setIndentation($indent);
$output .= $docBlock->generate();
}
$output .= $indent;
if ($this->isAbstract()) {
$output .= 'abstract ';
} else {
$output .= (($this->isFinal()) ? 'final ' : '');
}
$output .= $this->getVisibility()
. (($this->isStatic()) ? ' static' : '')
. ' function '
. ($this->returnsReference ? '& ' : '')
. $this->getName() . '(';
$parameters = $this->getParameters();
if (!empty($parameters)) {
foreach ($parameters as $parameter) {
$parameterOutput[] = $parameter->generate();
}
$output .= implode(', ', $parameterOutput);
}
$output .= ')';
if ($this->returnType) {
$output .= ' : ' . $this->returnType->generate();
}
if ($this->isAbstract()) {
return $output . ';';
}
if ($this->isInterface()) {
return $output . ';';
}
$output .= self::LINE_FEED . $indent . '{' . self::LINE_FEED;
if ($this->body) {
$output .= preg_replace('#^((?![a-zA-Z0-9_-]+;).+?)$#m', $indent . $indent . '$1', trim($this->body))
. self::LINE_FEED;
}
$output .= $indent . '}' . self::LINE_FEED;
return $output;
}
public function __toString()
{
return $this->generate();
}
/**
* @param MethodReflection $methodReflection
*
* @return null|string
*/
private static function extractReturnTypeFromMethodReflection(MethodReflection $methodReflection)
{
$returnType = method_exists($methodReflection, 'getReturnType')
? $methodReflection->getReturnType()
: null;
if (! $returnType) {
return null;
}
if (! method_exists($returnType, 'getName')) {
return self::expandLiteralType((string) $returnType, $methodReflection);
}
return ($returnType->allowsNull() ? '?' : '')
. self::expandLiteralType($returnType->getName(), $methodReflection);
}
/**
* @param string $literalReturnType
* @param ReflectionMethod $methodReflection
*
* @return string
*/
private static function expandLiteralType($literalReturnType, ReflectionMethod $methodReflection)
{
if ('self' === strtolower($literalReturnType)) {
return $methodReflection->getDeclaringClass()->getName();
}
if ('parent' === strtolower($literalReturnType)) {
return $methodReflection->getDeclaringClass()->getParentClass()->getName();
}
return $literalReturnType;
}
}

View file

@ -0,0 +1,402 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use ReflectionParameter;
use Zend\Code\Reflection\ParameterReflection;
class ParameterGenerator extends AbstractGenerator
{
/**
* @var string
*/
protected $name = null;
/**
* @var TypeGenerator|null
*/
protected $type = null;
/**
* @var string|ValueGenerator
*/
protected $defaultValue = null;
/**
* @var int
*/
protected $position = null;
/**
* @var bool
*/
protected $passedByReference = false;
/**
* @var bool
*/
private $variadic = false;
/**
* @param ParameterReflection $reflectionParameter
* @return ParameterGenerator
*/
public static function fromReflection(ParameterReflection $reflectionParameter)
{
$param = new ParameterGenerator();
$param->setName($reflectionParameter->getName());
if ($type = self::extractFQCNTypeFromReflectionType($reflectionParameter)) {
$param->setType($type);
}
$param->setPosition($reflectionParameter->getPosition());
$variadic = method_exists($reflectionParameter, 'isVariadic') && $reflectionParameter->isVariadic();
$param->setVariadic($variadic);
if (! $variadic && ($reflectionParameter->isOptional() || $reflectionParameter->isDefaultValueAvailable())) {
try {
$param->setDefaultValue($reflectionParameter->getDefaultValue());
} catch (\ReflectionException $e) {
$param->setDefaultValue(null);
}
}
$param->setPassedByReference($reflectionParameter->isPassedByReference());
return $param;
}
/**
* Generate from array
*
* @configkey name string [required] Class Name
* @configkey type string
* @configkey defaultvalue null|bool|string|int|float|array|ValueGenerator
* @configkey passedbyreference bool
* @configkey position int
* @configkey sourcedirty bool
* @configkey indentation string
* @configkey sourcecontent string
*
* @throws Exception\InvalidArgumentException
* @param array $array
* @return ParameterGenerator
*/
public static function fromArray(array $array)
{
if (!isset($array['name'])) {
throw new Exception\InvalidArgumentException(
'Paramerer generator requires that a name is provided for this object'
);
}
$param = new static($array['name']);
foreach ($array as $name => $value) {
// normalize key
switch (strtolower(str_replace(['.', '-', '_'], '', $name))) {
case 'type':
$param->setType($value);
break;
case 'defaultvalue':
$param->setDefaultValue($value);
break;
case 'passedbyreference':
$param->setPassedByReference($value);
break;
case 'position':
$param->setPosition($value);
break;
case 'sourcedirty':
$param->setSourceDirty($value);
break;
case 'indentation':
$param->setIndentation($value);
break;
case 'sourcecontent':
$param->setSourceContent($value);
break;
}
}
return $param;
}
/**
* @param string $name
* @param string $type
* @param mixed $defaultValue
* @param int $position
* @param bool $passByReference
*/
public function __construct(
$name = null,
$type = null,
$defaultValue = null,
$position = null,
$passByReference = false
) {
if (null !== $name) {
$this->setName($name);
}
if (null !== $type) {
$this->setType($type);
}
if (null !== $defaultValue) {
$this->setDefaultValue($defaultValue);
}
if (null !== $position) {
$this->setPosition($position);
}
if (false !== $passByReference) {
$this->setPassedByReference(true);
}
}
/**
* @param string $type
* @return ParameterGenerator
*/
public function setType($type)
{
$this->type = TypeGenerator::fromTypeString($type);
return $this;
}
/**
* @return string
*/
public function getType()
{
return $this->type
? (string) $this->type
: null;
}
/**
* @param string $name
* @return ParameterGenerator
*/
public function setName($name)
{
$this->name = (string) $name;
return $this;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set the default value of the parameter.
*
* Certain variables are difficult to express
*
* @param null|bool|string|int|float|array|ValueGenerator $defaultValue
* @return ParameterGenerator
*/
public function setDefaultValue($defaultValue)
{
if (!($defaultValue instanceof ValueGenerator)) {
$defaultValue = new ValueGenerator($defaultValue);
}
$this->defaultValue = $defaultValue;
return $this;
}
/**
* @return string
*/
public function getDefaultValue()
{
return $this->defaultValue;
}
/**
* @param int $position
* @return ParameterGenerator
*/
public function setPosition($position)
{
$this->position = (int) $position;
return $this;
}
/**
* @return int
*/
public function getPosition()
{
return $this->position;
}
/**
* @return bool
*/
public function getPassedByReference()
{
return $this->passedByReference;
}
/**
* @param bool $passedByReference
* @return ParameterGenerator
*/
public function setPassedByReference($passedByReference)
{
$this->passedByReference = (bool) $passedByReference;
return $this;
}
/**
* @param bool $variadic
*
* @return ParameterGenerator
*/
public function setVariadic($variadic)
{
$this->variadic = (bool) $variadic;
return $this;
}
/**
* @return bool
*/
public function getVariadic()
{
return $this->variadic;
}
/**
* @return string
*/
public function generate()
{
$output = $this->generateTypeHint();
if (true === $this->passedByReference) {
$output .= '&';
}
if ($this->variadic) {
$output .= '... ';
}
$output .= '$' . $this->name;
if ($this->defaultValue !== null) {
$output .= ' = ';
if (is_string($this->defaultValue)) {
$output .= ValueGenerator::escape($this->defaultValue);
} elseif ($this->defaultValue instanceof ValueGenerator) {
$this->defaultValue->setOutputMode(ValueGenerator::OUTPUT_SINGLE_LINE);
$output .= $this->defaultValue;
} else {
$output .= $this->defaultValue;
}
}
return $output;
}
/**
* @param ParameterReflection $reflectionParameter
*
* @return null|string
*/
private static function extractFQCNTypeFromReflectionType(ParameterReflection $reflectionParameter)
{
if (! method_exists($reflectionParameter, 'getType')) {
return self::prePhp7ExtractFQCNTypeFromReflectionType($reflectionParameter);
}
$type = method_exists($reflectionParameter, 'getType')
? $reflectionParameter->getType()
: null;
if (! $type) {
return null;
}
if (! method_exists($type, 'getName')) {
return self::expandLiteralParameterType((string) $type, $reflectionParameter);
}
return ($type->allowsNull() ? '?' : '')
. self::expandLiteralParameterType($type->getName(), $reflectionParameter);
}
/**
* For ancient PHP versions (yes, you should upgrade to 7.0):
*
* @param ParameterReflection $reflectionParameter
*
* @return string|null
*/
private static function prePhp7ExtractFQCNTypeFromReflectionType(ParameterReflection $reflectionParameter)
{
if ($reflectionParameter->isCallable()) {
return 'callable';
}
if ($reflectionParameter->isArray()) {
return 'array';
}
if ($class = $reflectionParameter->getClass()) {
return $class->getName();
}
return null;
}
/**
* @param string $literalParameterType
* @param ReflectionParameter $reflectionParameter
*
* @return string
*/
private static function expandLiteralParameterType($literalParameterType, ReflectionParameter $reflectionParameter)
{
if ('self' === strtolower($literalParameterType)) {
return $reflectionParameter->getDeclaringClass()->getName();
}
if ('parent' === strtolower($literalParameterType)) {
return $reflectionParameter->getDeclaringClass()->getParentClass()->getName();
}
return $literalParameterType;
}
/**
* @param string|null $type
*
* @return string
*/
private function generateTypeHint()
{
if (null === $this->type) {
return '';
}
return $this->type->generate() . ' ';
}
}

View file

@ -0,0 +1,229 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Zend\Code\Reflection\PropertyReflection;
class PropertyGenerator extends AbstractMemberGenerator
{
const FLAG_CONSTANT = 0x08;
/**
* @var bool
*/
protected $isConst = null;
/**
* @var PropertyValueGenerator
*/
protected $defaultValue = null;
/**
* @param PropertyReflection $reflectionProperty
* @return PropertyGenerator
*/
public static function fromReflection(PropertyReflection $reflectionProperty)
{
$property = new static();
$property->setName($reflectionProperty->getName());
$allDefaultProperties = $reflectionProperty->getDeclaringClass()->getDefaultProperties();
$property->setDefaultValue($allDefaultProperties[$reflectionProperty->getName()]);
if ($reflectionProperty->getDocComment() != '') {
$property->setDocBlock(DocBlockGenerator::fromReflection($reflectionProperty->getDocBlock()));
}
if ($reflectionProperty->isStatic()) {
$property->setStatic(true);
}
if ($reflectionProperty->isPrivate()) {
$property->setVisibility(self::VISIBILITY_PRIVATE);
} elseif ($reflectionProperty->isProtected()) {
$property->setVisibility(self::VISIBILITY_PROTECTED);
} else {
$property->setVisibility(self::VISIBILITY_PUBLIC);
}
$property->setSourceDirty(false);
return $property;
}
/**
* Generate from array
*
* @configkey name string [required] Class Name
* @configkey const bool
* @configkey defaultvalue null|bool|string|int|float|array|ValueGenerator
* @configkey flags int
* @configkey abstract bool
* @configkey final bool
* @configkey static bool
* @configkey visibility string
*
* @throws Exception\InvalidArgumentException
* @param array $array
* @return PropertyGenerator
*/
public static function fromArray(array $array)
{
if (!isset($array['name'])) {
throw new Exception\InvalidArgumentException(
'Property generator requires that a name is provided for this object'
);
}
$property = new static($array['name']);
foreach ($array as $name => $value) {
// normalize key
switch (strtolower(str_replace(['.', '-', '_'], '', $name))) {
case 'const':
$property->setConst($value);
break;
case 'defaultvalue':
$property->setDefaultValue($value);
break;
case 'docblock':
$docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value);
$property->setDocBlock($docBlock);
break;
case 'flags':
$property->setFlags($value);
break;
case 'abstract':
$property->setAbstract($value);
break;
case 'final':
$property->setFinal($value);
break;
case 'static':
$property->setStatic($value);
break;
case 'visibility':
$property->setVisibility($value);
break;
}
}
return $property;
}
/**
* @param string $name
* @param PropertyValueGenerator|string|array $defaultValue
* @param int $flags
*/
public function __construct($name = null, $defaultValue = null, $flags = self::FLAG_PUBLIC)
{
if (null !== $name) {
$this->setName($name);
}
if (null !== $defaultValue) {
$this->setDefaultValue($defaultValue);
}
if ($flags !== self::FLAG_PUBLIC) {
$this->setFlags($flags);
}
}
/**
* @param bool $const
* @return PropertyGenerator
*/
public function setConst($const)
{
if ($const) {
$this->removeFlag(self::FLAG_PUBLIC | self::FLAG_PRIVATE | self::FLAG_PROTECTED);
$this->setFlags(self::FLAG_CONSTANT);
} else {
$this->removeFlag(self::FLAG_CONSTANT);
}
return $this;
}
/**
* @return bool
*/
public function isConst()
{
return (bool) ($this->flags & self::FLAG_CONSTANT);
}
/**
* @param PropertyValueGenerator|mixed $defaultValue
* @param string $defaultValueType
* @param string $defaultValueOutputMode
*
* @return PropertyGenerator
*/
public function setDefaultValue(
$defaultValue,
$defaultValueType = PropertyValueGenerator::TYPE_AUTO,
$defaultValueOutputMode = PropertyValueGenerator::OUTPUT_MULTIPLE_LINE
) {
if (!($defaultValue instanceof PropertyValueGenerator)) {
$defaultValue = new PropertyValueGenerator($defaultValue, $defaultValueType, $defaultValueOutputMode);
}
$this->defaultValue = $defaultValue;
return $this;
}
/**
* @return PropertyValueGenerator
*/
public function getDefaultValue()
{
return $this->defaultValue;
}
/**
* @throws Exception\RuntimeException
* @return string
*/
public function generate()
{
$name = $this->getName();
$defaultValue = $this->getDefaultValue();
$output = '';
if (($docBlock = $this->getDocBlock()) !== null) {
$docBlock->setIndentation(' ');
$output .= $docBlock->generate();
}
if ($this->isConst()) {
if ($defaultValue !== null && !$defaultValue->isValidConstantType()) {
throw new Exception\RuntimeException(sprintf(
'The property %s is said to be '
. 'constant but does not have a valid constant value.',
$this->name
));
}
$output .= $this->indentation . 'const ' . $name . ' = '
. (($defaultValue !== null) ? $defaultValue->generate() : 'null;');
} else {
$output .= $this->indentation
. $this->getVisibility()
. (($this->isStatic()) ? ' static' : '')
. ' $' . $name . ' = '
. (($defaultValue !== null) ? $defaultValue->generate() : 'null;');
}
return $output;
}
}

View file

@ -0,0 +1,23 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
class PropertyValueGenerator extends ValueGenerator
{
protected $arrayDepth = 1;
/**
* @return string
*/
public function generate()
{
return parent::generate() . ';';
}
}

View file

@ -0,0 +1,173 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Zend\Code\Reflection\ClassReflection;
class TraitGenerator extends ClassGenerator
{
const OBJECT_TYPE = 'trait';
/**
* Build a Code Generation Php Object from a Class Reflection
*
* @param ClassReflection $classReflection
* @return TraitGenerator
*/
public static function fromReflection(ClassReflection $classReflection)
{
// class generator
$cg = new static($classReflection->getName());
$cg->setSourceContent($cg->getSourceContent());
$cg->setSourceDirty(false);
if ($classReflection->getDocComment() != '') {
$cg->setDocBlock(DocBlockGenerator::fromReflection($classReflection->getDocBlock()));
}
// set the namespace
if ($classReflection->inNamespace()) {
$cg->setNamespaceName($classReflection->getNamespaceName());
}
$properties = [];
foreach ($classReflection->getProperties() as $reflectionProperty) {
if ($reflectionProperty->getDeclaringClass()->getName() == $classReflection->getName()) {
$properties[] = PropertyGenerator::fromReflection($reflectionProperty);
}
}
$cg->addProperties($properties);
$methods = [];
foreach ($classReflection->getMethods() as $reflectionMethod) {
$className = ($cg->getNamespaceName())
? $cg->getNamespaceName() . '\\' . $cg->getName()
: $cg->getName();
if ($reflectionMethod->getDeclaringClass()->getName() == $className) {
$methods[] = MethodGenerator::fromReflection($reflectionMethod);
}
}
$cg->addMethods($methods);
return $cg;
}
/**
* Generate from array
*
* @configkey name string [required] Class Name
* @configkey filegenerator FileGenerator File generator that holds this class
* @configkey namespacename string The namespace for this class
* @configkey docblock string The docblock information
* @configkey properties
* @configkey methods
*
* @throws Exception\InvalidArgumentException
* @param array $array
* @return TraitGenerator
*/
public static function fromArray(array $array)
{
if (! isset($array['name'])) {
throw new Exception\InvalidArgumentException(
'Class generator requires that a name is provided for this object'
);
}
$cg = new static($array['name']);
foreach ($array as $name => $value) {
// normalize key
switch (strtolower(str_replace(['.', '-', '_'], '', $name))) {
case 'containingfile':
$cg->setContainingFileGenerator($value);
break;
case 'namespacename':
$cg->setNamespaceName($value);
break;
case 'docblock':
$docBlock = ($value instanceof DocBlockGenerator) ? $value : DocBlockGenerator::fromArray($value);
$cg->setDocBlock($docBlock);
break;
case 'properties':
$cg->addProperties($value);
break;
case 'methods':
$cg->addMethods($value);
break;
}
}
return $cg;
}
/**
* @param array|string $flags
* @return self
*/
public function setFlags($flags)
{
return $this;
}
/**
* @param string $flag
* @return self
*/
public function addFlag($flag)
{
return $this;
}
/**
* @param string $flag
* @return self
*/
public function removeFlag($flag)
{
return $this;
}
/**
* @param bool $isFinal
* @return self
*/
public function setFinal($isFinal)
{
return $this;
}
/**
* @param string $extendedClass
* @return self
*/
public function setExtendedClass($extendedClass)
{
return $this;
}
/**
* @param array $implementedInterfaces
* @return self
*/
public function setImplementedInterfaces(array $implementedInterfaces)
{
return $this;
}
/**
* @param bool $isAbstract
* @return self
*/
public function setAbstract($isAbstract)
{
return $this;
}
}

View file

@ -0,0 +1,419 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Reflection;
use ReflectionMethod;
class TraitUsageGenerator extends AbstractGenerator
{
/**
* @var ClassGenerator
*/
protected $classGenerator;
/**
* @var array Array of trait names
*/
protected $traits = [];
/**
* @var array Array of trait aliases
*/
protected $traitAliases = [];
/**
* @var array Array of trait overrides
*/
protected $traitOverrides = [];
/**
* @var array Array of string names
*/
protected $uses = [];
public function __construct(ClassGenerator $classGenerator)
{
$this->classGenerator = $classGenerator;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function addUse($use, $useAlias = null)
{
$this->removeUse($use);
if (! empty($useAlias)) {
$use .= ' as ' . $useAlias;
}
$this->uses[$use] = $use;
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function getUses()
{
return array_values($this->uses);
}
/**
* @param $use
* @return bool
*/
public function hasUse($use)
{
foreach ($this->uses as $key => $value) {
$parts = explode(' ', $value);
if ($parts[0] === $use) {
return true;
}
}
return false;
}
/**
* @param $use
* @return bool
*/
public function hasUseAlias($use)
{
foreach ($this->uses as $key => $value) {
$parts = explode(' as ', $value);
if ($parts[0] === $use and count($parts) == 2) {
return true;
}
};
return false;
}
/**
* @param $use
* @return TraitUsageGenerator
*/
public function removeUse($use)
{
foreach ($this->uses as $key => $value) {
$parts = explode(' ', $value);
if ($parts[0] === $use) {
unset($this->uses[$value]);
}
};
return $this;
}
/**
* @param $use
* @return TraitUsageGenerator
*/
public function removeUseAlias($use)
{
foreach ($this->uses as $key => $value) {
$parts = explode(' as ', $value);
if ($parts[0] === $use and count($parts) == 2) {
unset($this->uses[$value]);
}
};
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function addTrait($trait)
{
$traitName = $trait;
if (is_array($trait)) {
if (! array_key_exists('traitName', $trait)) {
throw new Exception\InvalidArgumentException('Missing required value for traitName');
}
$traitName = $trait['traitName'];
if (array_key_exists('aliases', $trait)) {
foreach ($trait['aliases'] as $alias) {
$this->addAlias($alias);
}
}
if (array_key_exists('insteadof', $trait)) {
foreach ($trait['insteadof'] as $insteadof) {
$this->addTraitOverride($insteadof);
}
}
}
if (! $this->hasTrait($traitName)) {
$this->traits[] = $traitName;
}
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function addTraits(array $traits)
{
foreach ($traits as $trait) {
$this->addTrait($trait);
}
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function hasTrait($traitName)
{
return in_array($traitName, $this->traits);
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function getTraits()
{
return $this->traits;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function removeTrait($traitName)
{
$key = array_search($traitName, $this->traits);
if (false !== $key) {
unset($this->traits[$key]);
}
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function addTraitAlias($method, $alias, $visibility = null)
{
$traitAndMethod = $method;
if (is_array($method)) {
if (! array_key_exists('traitName', $method)) {
throw new Exception\InvalidArgumentException('Missing required argument "traitName" for $method');
}
if (! array_key_exists('method', $method)) {
throw new Exception\InvalidArgumentException('Missing required argument "method" for $method');
}
$traitAndMethod = $method['traitName'] . '::' . $method['method'];
}
// Validations
if (false === strpos($traitAndMethod, "::")) {
throw new Exception\InvalidArgumentException(
'Invalid Format: $method must be in the format of trait::method'
);
}
if (! is_string($alias)) {
throw new Exception\InvalidArgumentException('Invalid Alias: $alias must be a string or array.');
}
if ($this->classGenerator->hasMethod($alias)) {
throw new Exception\InvalidArgumentException('Invalid Alias: Method name already exists on this class.');
}
if (null !== $visibility
&& $visibility !== ReflectionMethod::IS_PUBLIC
&& $visibility !== ReflectionMethod::IS_PRIVATE
&& $visibility !== ReflectionMethod::IS_PROTECTED
) {
throw new Exception\InvalidArgumentException(
'Invalid Type: $visibility must of ReflectionMethod::IS_PUBLIC,'
. ' ReflectionMethod::IS_PRIVATE or ReflectionMethod::IS_PROTECTED'
);
}
list($trait, $method) = explode('::', $traitAndMethod);
if (! $this->hasTrait($trait)) {
throw new Exception\InvalidArgumentException('Invalid trait: Trait does not exists on this class');
}
$this->traitAliases[$traitAndMethod] = [
'alias' => $alias,
'visibility' => $visibility
];
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function getTraitAliases()
{
return $this->traitAliases;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function addTraitOverride($method, $traitsToReplace)
{
if (false === is_array($traitsToReplace)) {
$traitsToReplace = [$traitsToReplace];
}
$traitAndMethod = $method;
if (is_array($method)) {
if (! array_key_exists('traitName', $method)) {
throw new Exception\InvalidArgumentException('Missing required argument "traitName" for $method');
}
if (! array_key_exists('method', $method)) {
throw new Exception\InvalidArgumentException('Missing required argument "method" for $method');
}
$traitAndMethod = (string) $method['traitName'] . '::' . (string) $method['method'];
}
// Validations
if (false === strpos($traitAndMethod, "::")) {
throw new Exception\InvalidArgumentException(
'Invalid Format: $method must be in the format of trait::method'
);
}
list($trait, $method) = explode("::", $traitAndMethod);
if (! $this->hasTrait($trait)) {
throw new Exception\InvalidArgumentException('Invalid trait: Trait does not exists on this class');
}
if (! array_key_exists($traitAndMethod, $this->traitOverrides)) {
$this->traitOverrides[$traitAndMethod] = [];
}
foreach ($traitsToReplace as $traitToReplace) {
if (! is_string($traitToReplace)) {
throw new Exception\InvalidArgumentException(
'Invalid Argument: $traitToReplace must be a string or array of strings'
);
}
if (! in_array($traitToReplace, $this->traitOverrides[$traitAndMethod])) {
$this->traitOverrides[$traitAndMethod][] = $traitToReplace;
}
}
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function removeTraitOverride($method, $overridesToRemove = null)
{
if (! array_key_exists($method, $this->traitOverrides)) {
return $this;
}
if (null === $overridesToRemove) {
unset($this->traitOverrides[$method]);
return $this;
}
$overridesToRemove = (! is_array($overridesToRemove))
? [$overridesToRemove]
: $overridesToRemove;
foreach ($overridesToRemove as $traitToRemove) {
$key = array_search($traitToRemove, $this->traitOverrides[$method]);
if (false !== $key) {
unset($this->traitOverrides[$method][$key]);
}
}
return $this;
}
/**
* @inherit Zend\Code\Generator\TraitUsageInterface
*/
public function getTraitOverrides()
{
return $this->traitOverrides;
}
/**
* @inherit Zend\Code\Generator\GeneratorInterface
*/
public function generate()
{
$output = '';
$indent = $this->getIndentation();
$traits = $this->getTraits();
if (empty($traits)) {
return $output;
}
$output .= $indent . 'use ' . implode(', ', $traits);
$aliases = $this->getTraitAliases();
$overrides = $this->getTraitOverrides();
if (empty($aliases) && empty($overrides)) {
$output .= ";" . self::LINE_FEED . self::LINE_FEED;
return $output;
}
$output .= ' {' . self::LINE_FEED;
foreach ($aliases as $method => $alias) {
$visibility = (null !== $alias['visibility'])
? current(Reflection::getModifierNames($alias['visibility'])) . ' '
: '';
// validation check
if ($this->classGenerator->hasMethod($alias['alias'])) {
throw new Exception\RuntimeException(sprintf(
'Generation Error: Aliased method %s already exists on this class',
$alias['alias']
));
}
$output .=
$indent
. $indent
. $method
. ' as '
. $visibility
. $alias['alias']
. ';'
. self::LINE_FEED;
}
foreach ($overrides as $method => $insteadofTraits) {
foreach ($insteadofTraits as $insteadofTrait) {
$output .=
$indent
. $indent
. $method
. ' insteadof '
. $insteadofTrait
. ';'
. self::LINE_FEED;
}
}
$output .= self::LINE_FEED . $indent . '}' . self::LINE_FEED . self::LINE_FEED;
return $output;
}
}

View file

@ -0,0 +1,159 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
interface TraitUsageInterface
{
/**
* Add a class to "use" classes
*
* @param string $use
* @param string|null $useAlias
* @return self
*/
public function addUse($use, $useAlias = null);
/**
* Returns the "use" classes
*
* @return array
*/
public function getUses();
/**
* Add trait takes an array of trait options or string as arguments.
*
* Array Format:
* key: traitName value: String
*
* key: aliases value: array of arrays
* key: method value: @see addTraitAlias
* key: alias value: @see addTraitAlias
* key: visibility value: @see addTraitAlias
*
* key: insteadof value: array of arrays
* key: method value: @see self::addTraitOverride
* key: traitToReplace value: @see self::addTraitOverride
*
* @param mixed $trait String | Array
* @return self
*/
public function addTrait($trait);
/**
* Add multiple traits. Trait can be an array of trait names or array of trait
* configurations
*
* @param array $traitName Array of string names or configurations (@see addTrait)
* @return self
*/
public function addTraits(array $traits);
/**
* Check to see if the class has a trait defined
*
* @param strint $traitName
* @return bool
*/
public function hasTrait($traitName);
/**
* Get a list of trait names
*
* @return array
*/
public function getTraits();
/**
* Remove a trait by its name
*
* @param $traitName
*/
public function removeTrait($traitName);
/**
* Add a trait alias. This will be used to generate the AS portion of the use statement.
*
* $method:
* This method provides 2 ways for defining the trait method.
* Option 1: String
* Option 2: Array
* key: traitName value: name of trait
* key: method value: trait method
*
* $alias:
* Alias is a string representing the new method name.
*
* $visibilty:
* ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PRIVATE| ReflectionMethod::IS_PROTECTED
*
* @param mixed $method String or Array
* @param string $alias
* @param int $visiblity
*/
public function addTraitAlias($method, $alias, $visibility = null);
/**
* @return array
*/
public function getTraitAliases();
/**
* Add a trait method override. This will be used to generate the INSTEADOF portion of the use
* statement.
*
* $method:
* This method provides 2 ways for defining the trait method.
* Option 1: String Format: <trait name>::<method name>
* Option 2: Array
* key: traitName value: trait name
* key: method value: method name
*
* $traitToReplace:
* The name of the trait that you wish to supersede.
*
* This method provides 2 ways for defining the trait method.
* Option 1: String of trait to replace
* Option 2: Array of strings of traits to replace
* @param mixed $method
* @param mixed $traitToReplace
*/
public function addTraitOverride($method, $traitsToReplace);
/**
* Remove an override for a given trait::method
*
* $method:
* This method provides 2 ways for defining the trait method.
* Option 1: String Format: <trait name>::<method name>
* Option 2: Array
* key: traitName value: trait name
* key: method value: method name
*
* $overridesToRemove:
* The name of the trait that you wish to remove.
*
* This method provides 2 ways for defining the trait method.
* Option 1: String of trait to replace
* Option 2: Array of strings of traits to replace
*
* @param $traitAndMethod
* @param null $overridesToRemove
* @return $this
*/
public function removeTraitOverride($method, $overridesToRemove = null);
/**
* Return trait overrides
*
* @return array
*/
public function getTraitOverrides();
}

View file

@ -0,0 +1,151 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use Zend\Code\Generator\Exception\InvalidArgumentException;
final class TypeGenerator implements GeneratorInterface
{
/**
* @var bool
*/
private $isInternalPhpType;
/**
* @var string
*/
private $type;
/**
* @var bool
*/
private $nullable;
/**
* @var string[]
*
* @link http://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration
*/
private static $internalPhpTypes = ['void', 'int', 'float', 'string', 'bool', 'array', 'callable', 'iterable'];
/**
* @var string a regex pattern to match valid class names or types
*/
private static $validIdentifierMatcher = '/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*'
. '(\\\\[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)*$/';
/**
* @param string $type
*
* @return TypeGenerator
*
* @throws InvalidArgumentException
*/
public static function fromTypeString($type)
{
list($nullable, $trimmedNullable) = self::trimNullable($type);
list($wasTrimmed, $trimmedType) = self::trimType($trimmedNullable);
if (! preg_match(self::$validIdentifierMatcher, $trimmedType)) {
throw new InvalidArgumentException(sprintf(
'Provided type "%s" is invalid: must conform "%s"',
$type,
self::$validIdentifierMatcher
));
}
$isInternalPhpType = self::isInternalPhpType($trimmedType);
if ($wasTrimmed && $isInternalPhpType) {
throw new InvalidArgumentException(sprintf(
'Provided type "%s" is an internal PHP type, but was provided with a namespace separator prefix',
$type
));
}
if ($nullable && $isInternalPhpType && 'void' === strtolower($trimmedType)) {
throw new InvalidArgumentException(sprintf('Provided type "%s" cannot be nullable', $type));
}
$instance = new self();
$instance->type = $trimmedType;
$instance->nullable = $nullable;
$instance->isInternalPhpType = self::isInternalPhpType($trimmedType);
return $instance;
}
private function __construct()
{
}
/**
* {@inheritDoc}
*/
public function generate()
{
$nullable = $this->nullable ? '?' : '';
if ($this->isInternalPhpType) {
return $nullable . strtolower($this->type);
}
return $nullable . '\\' . $this->type;
}
/**
* @return string the cleaned type string
*/
public function __toString()
{
return ltrim($this->generate(), '?\\');
}
/**
* @param string $type
*
* @return bool[]|string[] ordered tuple, first key represents whether the type is nullable, second is the
* trimmed string
*/
private static function trimNullable($type)
{
if (0 === strpos($type, '?')) {
return [true, substr($type, 1)];
}
return [false, $type];
}
/**
* @param string $type
*
* @return bool[]|string[] ordered tuple, first key represents whether the values was trimmed, second is the
* trimmed string
*/
private static function trimType($type)
{
if (0 === strpos($type, '\\')) {
return [true, substr($type, 1)];
}
return [false, $type];
}
/**
* @param string $type
*
* @return bool
*/
private static function isInternalPhpType($type)
{
return in_array(strtolower($type), self::$internalPhpTypes, true);
}
}

View file

@ -0,0 +1,469 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generator;
use ArrayObject as SplArrayObject;
use Zend\Code\Exception\InvalidArgumentException;
use Zend\Stdlib\ArrayObject as StdlibArrayObject;
class ValueGenerator extends AbstractGenerator
{
/**#@+
* Constant values
*/
const TYPE_AUTO = 'auto';
const TYPE_BOOLEAN = 'boolean';
const TYPE_BOOL = 'bool';
const TYPE_NUMBER = 'number';
const TYPE_INTEGER = 'integer';
const TYPE_INT = 'int';
const TYPE_FLOAT = 'float';
const TYPE_DOUBLE = 'double';
const TYPE_STRING = 'string';
const TYPE_ARRAY = 'array';
const TYPE_ARRAY_SHORT = 'array_short';
const TYPE_ARRAY_LONG = 'array_long';
const TYPE_CONSTANT = 'constant';
const TYPE_NULL = 'null';
const TYPE_OBJECT = 'object';
const TYPE_OTHER = 'other';
/**#@-*/
const OUTPUT_MULTIPLE_LINE = 'multipleLine';
const OUTPUT_SINGLE_LINE = 'singleLine';
/**
* @var mixed
*/
protected $value = null;
/**
* @var string
*/
protected $type = self::TYPE_AUTO;
/**
* @var int
*/
protected $arrayDepth = 0;
/**
* @var string
*/
protected $outputMode = self::OUTPUT_MULTIPLE_LINE;
/**
* @var array
*/
protected $allowedTypes = null;
/**
* Autodetectable constants
* @var SplArrayObject|StdlibArrayObject
*/
protected $constants;
/**
* @param mixed $value
* @param string $type
* @param string $outputMode
* @param null|SplArrayObject|StdlibArrayObject $constants
*/
public function __construct(
$value = null,
$type = self::TYPE_AUTO,
$outputMode = self::OUTPUT_MULTIPLE_LINE,
$constants = null
) {
// strict check is important here if $type = AUTO
if ($value !== null) {
$this->setValue($value);
}
if ($type !== self::TYPE_AUTO) {
$this->setType($type);
}
if ($outputMode !== self::OUTPUT_MULTIPLE_LINE) {
$this->setOutputMode($outputMode);
}
if ($constants === null) {
$constants = new SplArrayObject();
} elseif (!(($constants instanceof SplArrayObject) || ($constants instanceof StdlibArrayObject))) {
throw new InvalidArgumentException(
'$constants must be an instance of ArrayObject or Zend\Stdlib\ArrayObject'
);
}
$this->constants = $constants;
}
/**
* Init constant list by defined and magic constants
*/
public function initEnvironmentConstants()
{
$constants = [
'__DIR__',
'__FILE__',
'__LINE__',
'__CLASS__',
'__TRAIT__',
'__METHOD__',
'__FUNCTION__',
'__NAMESPACE__',
'::'
];
$constants = array_merge($constants, array_keys(get_defined_constants()), $this->constants->getArrayCopy());
$this->constants->exchangeArray($constants);
}
/**
* Add constant to list
*
* @param string $constant
*
* @return $this
*/
public function addConstant($constant)
{
$this->constants->append($constant);
return $this;
}
/**
* Delete constant from constant list
*
* @param string $constant
*
* @return bool
*/
public function deleteConstant($constant)
{
if (($index = array_search($constant, $this->constants->getArrayCopy())) !== false) {
$this->constants->offsetUnset($index);
}
return $index !== false;
}
/**
* Return constant list
*
* @return SplArrayObject|StdlibArrayObject
*/
public function getConstants()
{
return $this->constants;
}
/**
* @return bool
*/
public function isValidConstantType()
{
if ($this->type == self::TYPE_AUTO) {
$type = $this->getAutoDeterminedType($this->value);
} else {
$type = $this->type;
}
$validConstantTypes = [
self::TYPE_ARRAY,
self::TYPE_ARRAY_LONG,
self::TYPE_ARRAY_SHORT,
self::TYPE_BOOLEAN,
self::TYPE_BOOL,
self::TYPE_NUMBER,
self::TYPE_INTEGER,
self::TYPE_INT,
self::TYPE_FLOAT,
self::TYPE_DOUBLE,
self::TYPE_STRING,
self::TYPE_CONSTANT,
self::TYPE_NULL
];
return in_array($type, $validConstantTypes);
}
/**
* @param mixed $value
* @return ValueGenerator
*/
public function setValue($value)
{
$this->value = $value;
return $this;
}
/**
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* @param string $type
* @return ValueGenerator
*/
public function setType($type)
{
$this->type = (string) $type;
return $this;
}
/**
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* @param int $arrayDepth
* @return ValueGenerator
*/
public function setArrayDepth($arrayDepth)
{
$this->arrayDepth = (int) $arrayDepth;
return $this;
}
/**
* @return int
*/
public function getArrayDepth()
{
return $this->arrayDepth;
}
/**
* @param string $type
* @return string
*/
protected function getValidatedType($type)
{
$types = [
self::TYPE_AUTO,
self::TYPE_BOOLEAN,
self::TYPE_BOOL,
self::TYPE_NUMBER,
self::TYPE_INTEGER,
self::TYPE_INT,
self::TYPE_FLOAT,
self::TYPE_DOUBLE,
self::TYPE_STRING,
self::TYPE_ARRAY,
self::TYPE_ARRAY_SHORT,
self::TYPE_ARRAY_LONG,
self::TYPE_CONSTANT,
self::TYPE_NULL,
self::TYPE_OBJECT,
self::TYPE_OTHER
];
if (in_array($type, $types)) {
return $type;
}
return self::TYPE_AUTO;
}
/**
* @param mixed $value
* @return string
*/
public function getAutoDeterminedType($value)
{
switch (gettype($value)) {
case 'boolean':
return self::TYPE_BOOLEAN;
case 'string':
foreach ($this->constants as $constant) {
if (strpos($value, $constant) !== false) {
return self::TYPE_CONSTANT;
}
}
return self::TYPE_STRING;
case 'double':
case 'float':
case 'integer':
return self::TYPE_NUMBER;
case 'array':
return self::TYPE_ARRAY;
case 'NULL':
return self::TYPE_NULL;
case 'object':
case 'resource':
case 'unknown type':
default:
return self::TYPE_OTHER;
}
}
/**
* @throws Exception\RuntimeException
* @return string
*/
public function generate()
{
$type = $this->type;
if ($type != self::TYPE_AUTO) {
$type = $this->getValidatedType($type);
}
$value = $this->value;
if ($type == self::TYPE_AUTO) {
$type = $this->getAutoDeterminedType($value);
}
$isArrayType = in_array($type, [self::TYPE_ARRAY, self::TYPE_ARRAY_LONG, self::TYPE_ARRAY_SHORT]);
if ($isArrayType) {
foreach ($value as &$curValue) {
if ($curValue instanceof self) {
continue;
}
if (is_array($curValue)) {
$newType = $type;
} else {
$newType = self::TYPE_AUTO;
}
$curValue = new self($curValue, $newType, self::OUTPUT_MULTIPLE_LINE, $this->getConstants());
}
}
$output = '';
switch ($type) {
case self::TYPE_BOOLEAN:
case self::TYPE_BOOL:
$output .= ($value ? 'true' : 'false');
break;
case self::TYPE_STRING:
$output .= self::escape($value);
break;
case self::TYPE_NULL:
$output .= 'null';
break;
case self::TYPE_NUMBER:
case self::TYPE_INTEGER:
case self::TYPE_INT:
case self::TYPE_FLOAT:
case self::TYPE_DOUBLE:
case self::TYPE_CONSTANT:
$output .= $value;
break;
case self::TYPE_ARRAY:
case self::TYPE_ARRAY_LONG:
case self::TYPE_ARRAY_SHORT:
if ($type == self::TYPE_ARRAY_SHORT) {
$startArray = '[';
$endArray = ']';
} else {
$startArray = 'array(';
$endArray = ')';
}
$output .= $startArray;
if ($this->outputMode == self::OUTPUT_MULTIPLE_LINE) {
$output .= self::LINE_FEED . str_repeat($this->indentation, $this->arrayDepth + 1);
}
$outputParts = [];
$noKeyIndex = 0;
foreach ($value as $n => $v) {
/* @var $v ValueGenerator */
$v->setArrayDepth($this->arrayDepth + 1);
$partV = $v->generate();
$short = false;
if (is_int($n)) {
if ($n === $noKeyIndex) {
$short = true;
$noKeyIndex++;
} else {
$noKeyIndex = max($n + 1, $noKeyIndex);
}
}
if ($short) {
$outputParts[] = $partV;
} else {
$outputParts[] = (is_int($n) ? $n : self::escape($n)) . ' => ' . $partV;
}
}
$padding = ($this->outputMode == self::OUTPUT_MULTIPLE_LINE)
? self::LINE_FEED . str_repeat($this->indentation, $this->arrayDepth + 1)
: ' ';
$output .= implode(',' . $padding, $outputParts);
if ($this->outputMode == self::OUTPUT_MULTIPLE_LINE) {
if (count($outputParts) > 0) {
$output .= ',';
}
$output .= self::LINE_FEED . str_repeat($this->indentation, $this->arrayDepth);
}
$output .= $endArray;
break;
case self::TYPE_OTHER:
default:
throw new Exception\RuntimeException(
sprintf('Type "%s" is unknown or cannot be used as property default value.', get_class($value))
);
}
return $output;
}
/**
* Quotes value for PHP code.
*
* @param string $input Raw string.
* @param bool $quote Whether add surrounding quotes or not.
* @return string PHP-ready code.
*/
public static function escape($input, $quote = true)
{
$output = addcslashes($input, "\\'");
// adds quoting strings
if ($quote) {
$output = "'" . $output . "'";
}
return $output;
}
/**
* @param string $outputMode
* @return ValueGenerator
*/
public function setOutputMode($outputMode)
{
$this->outputMode = (string) $outputMode;
return $this;
}
/**
* @return string
*/
public function getOutputMode()
{
return $this->outputMode;
}
public function __toString()
{
return $this->generate();
}
}

View file

@ -0,0 +1,121 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generic\Prototype;
use Zend\Code\Reflection\Exception;
/**
* This is a factory for classes which are identified by name.
*
* All classes that this factory can supply need to
* be registered before (prototypes). This prototypes need to implement
* an interface which ensures every prototype has a name.
*
* If the factory can not supply the class someone is asking for
* it tries to fallback on a generic default prototype, which would
* have need to be set before.
*/
class PrototypeClassFactory
{
/**
* @var array
*/
protected $prototypes = [];
/**
* @var PrototypeGenericInterface
*/
protected $genericPrototype = null;
/**
* @param PrototypeInterface[] $prototypes
* @param PrototypeGenericInterface $genericPrototype
*/
public function __construct($prototypes = [], PrototypeGenericInterface $genericPrototype = null)
{
foreach ((array)$prototypes as $prototype) {
$this->addPrototype($prototype);
}
if ($genericPrototype) {
$this->setGenericPrototype($genericPrototype);
}
}
/**
* @param PrototypeInterface $prototype
* @throws Exception\InvalidArgumentException
*/
public function addPrototype(PrototypeInterface $prototype)
{
$prototypeName = $this->normalizeName($prototype->getName());
if (isset($this->prototypes[$prototypeName])) {
throw new Exception\InvalidArgumentException('A prototype with this name already exists in this manager');
}
$this->prototypes[$prototypeName] = $prototype;
}
/**
* @param PrototypeGenericInterface $prototype
* @throws Exception\InvalidArgumentException
*/
public function setGenericPrototype(PrototypeGenericInterface $prototype)
{
if (isset($this->genericPrototype)) {
throw new Exception\InvalidArgumentException('A default prototype is already set');
}
$this->genericPrototype = $prototype;
}
/**
* @param string $name
* @return string
*/
protected function normalizeName($name)
{
return str_replace(['-', '_'], '', $name);
}
/**
* @param string $name
* @return bool
*/
public function hasPrototype($name)
{
$name = $this->normalizeName($name);
return isset($this->prototypes[$name]);
}
/**
* @param string $prototypeName
* @return PrototypeInterface
* @throws Exception\RuntimeException
*/
public function getClonedPrototype($prototypeName)
{
$prototypeName = $this->normalizeName($prototypeName);
if (!$this->hasPrototype($prototypeName) && !isset($this->genericPrototype)) {
throw new Exception\RuntimeException('This tag name is not supported by this tag manager');
}
if (!$this->hasPrototype($prototypeName)) {
$newPrototype = clone $this->genericPrototype;
$newPrototype->setName($prototypeName);
} else {
$newPrototype = clone $this->prototypes[$prototypeName];
}
return $newPrototype;
}
}

View file

@ -0,0 +1,18 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generic\Prototype;
interface PrototypeGenericInterface extends PrototypeInterface
{
/**
* @param string $name
*/
public function setName($name);
}

View file

@ -0,0 +1,18 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Generic\Prototype;
interface PrototypeInterface
{
/**
* @return string
*/
public function getName();
}

View file

@ -0,0 +1,156 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code;
class NameInformation
{
/**
* @var string
*/
protected $namespace = null;
/**
* @var array
*/
protected $uses = [];
/**
* @param string $namespace
* @param array $uses
*/
public function __construct($namespace = null, array $uses = [])
{
if ($namespace) {
$this->setNamespace($namespace);
}
if ($uses) {
$this->setUses($uses);
}
}
/**
* @param string $namespace
* @return NameInformation
*/
public function setNamespace($namespace)
{
$this->namespace = (string) $namespace;
return $this;
}
/**
* @return string
*/
public function getNamespace()
{
return $this->namespace;
}
/**
* @return bool
*/
public function hasNamespace()
{
return ($this->namespace !== null);
}
/**
* @param array $uses
* @return NameInformation
*/
public function setUses(array $uses)
{
$this->uses = [];
$this->addUses($uses);
return $this;
}
/**
* @param array $uses
* @return NameInformation
*/
public function addUses(array $uses)
{
foreach ($uses as $use => $as) {
if (is_int($use)) {
$this->addUse($as);
} elseif (is_string($use)) {
$this->addUse($use, $as);
}
}
return $this;
}
/**
* @param array|string $use
* @param string $as
*/
public function addUse($use, $as = null)
{
if (is_array($use) && array_key_exists('use', $use) && array_key_exists('as', $use)) {
$uses = $use;
$use = $uses['use'];
$as = $uses['as'];
}
$use = trim($use, '\\');
if ($as === null) {
$as = trim($use, '\\');
$nsSeparatorPosition = strrpos($as, '\\');
if ($nsSeparatorPosition !== false && $nsSeparatorPosition !== 0 && $nsSeparatorPosition != strlen($as)) {
$as = substr($as, $nsSeparatorPosition + 1);
}
}
$this->uses[$use] = $as;
}
/**
* @return array
*/
public function getUses()
{
return $this->uses;
}
/**
* @param string $name
* @return string
*/
public function resolveName($name)
{
if ($this->namespace && !$this->uses && strlen($name) > 0 && $name{0} != '\\') {
return $this->namespace . '\\' . $name;
}
if (!$this->uses || strlen($name) <= 0 || $name{0} == '\\') {
return ltrim($name, '\\');
}
if ($this->namespace || $this->uses) {
$firstPart = $name;
if (($firstPartEnd = strpos($firstPart, '\\')) !== false) {
$firstPart = substr($firstPart, 0, $firstPartEnd);
} else {
$firstPartEnd = strlen($firstPart);
}
if (($fqns = array_search($firstPart, $this->uses)) !== false) {
return substr_replace($name, $fqns, 0, $firstPartEnd);
}
if ($this->namespace) {
return $this->namespace . '\\' . $name;
}
}
return $name;
}
}

View file

@ -0,0 +1,283 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use ReflectionClass;
use Zend\Code\Annotation\AnnotationCollection;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Scanner\AnnotationScanner;
use Zend\Code\Scanner\FileScanner;
class ClassReflection extends ReflectionClass implements ReflectionInterface
{
/**
* @var AnnotationScanner
*/
protected $annotations = null;
/**
* @var DocBlockReflection
*/
protected $docBlock = null;
/**
* Return the reflection file of the declaring file.
*
* @return FileReflection
*/
public function getDeclaringFile()
{
$instance = new FileReflection($this->getFileName());
return $instance;
}
/**
* Return the classes DocBlock reflection object
*
* @return DocBlockReflection
* @throws Exception\ExceptionInterface for missing DocBock or invalid reflection class
*/
public function getDocBlock()
{
if (isset($this->docBlock)) {
return $this->docBlock;
}
if ('' == $this->getDocComment()) {
return false;
}
$this->docBlock = new DocBlockReflection($this);
return $this->docBlock;
}
/**
* @param AnnotationManager $annotationManager
* @return AnnotationCollection
*/
public function getAnnotations(AnnotationManager $annotationManager)
{
$docComment = $this->getDocComment();
if ($docComment == '') {
return false;
}
if ($this->annotations) {
return $this->annotations;
}
$fileScanner = $this->createFileScanner($this->getFileName());
$nameInformation = $fileScanner->getClassNameInformation($this->getName());
if (!$nameInformation) {
return false;
}
$this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation);
return $this->annotations;
}
/**
* Return the start line of the class
*
* @param bool $includeDocComment
* @return int
*/
public function getStartLine($includeDocComment = false)
{
if ($includeDocComment && $this->getDocComment() != '') {
return $this->getDocBlock()->getStartLine();
}
return parent::getStartLine();
}
/**
* Return the contents of the class
*
* @param bool $includeDocBlock
* @return string
*/
public function getContents($includeDocBlock = true)
{
$fileName = $this->getFileName();
if (false === $fileName || ! file_exists($fileName)) {
return '';
}
$filelines = file($fileName);
$startnum = $this->getStartLine($includeDocBlock);
$endnum = $this->getEndLine() - $this->getStartLine();
// Ensure we get between the open and close braces
$lines = array_slice($filelines, $startnum, $endnum);
array_unshift($lines, $filelines[$startnum-1]);
return strstr(implode('', $lines), '{');
}
/**
* Get all reflection objects of implemented interfaces
*
* @return ClassReflection[]
*/
public function getInterfaces()
{
$phpReflections = parent::getInterfaces();
$zendReflections = [];
while ($phpReflections && ($phpReflection = array_shift($phpReflections))) {
$instance = new ClassReflection($phpReflection->getName());
$zendReflections[] = $instance;
unset($phpReflection);
}
unset($phpReflections);
return $zendReflections;
}
/**
* Return method reflection by name
*
* @param string $name
* @return MethodReflection
*/
public function getMethod($name)
{
$method = new MethodReflection($this->getName(), parent::getMethod($name)->getName());
return $method;
}
/**
* Get reflection objects of all methods
*
* @param int $filter
* @return MethodReflection[]
*/
public function getMethods($filter = -1)
{
$methods = [];
foreach (parent::getMethods($filter) as $method) {
$instance = new MethodReflection($this->getName(), $method->getName());
$methods[] = $instance;
}
return $methods;
}
/**
* Returns an array of reflection classes of traits used by this class.
*
* @return array|null
*/
public function getTraits()
{
$vals = [];
$traits = parent::getTraits();
if ($traits === null) {
return;
}
foreach ($traits as $trait) {
$vals[] = new ClassReflection($trait->getName());
}
return $vals;
}
/**
* Get parent reflection class of reflected class
*
* @return ClassReflection|bool
*/
public function getParentClass()
{
$phpReflection = parent::getParentClass();
if ($phpReflection) {
$zendReflection = new ClassReflection($phpReflection->getName());
unset($phpReflection);
return $zendReflection;
}
return false;
}
/**
* Return reflection property of this class by name
*
* @param string $name
* @return PropertyReflection
*/
public function getProperty($name)
{
$phpReflection = parent::getProperty($name);
$zendReflection = new PropertyReflection($this->getName(), $phpReflection->getName());
unset($phpReflection);
return $zendReflection;
}
/**
* Return reflection properties of this class
*
* @param int $filter
* @return PropertyReflection[]
*/
public function getProperties($filter = -1)
{
$phpReflections = parent::getProperties($filter);
$zendReflections = [];
while ($phpReflections && ($phpReflection = array_shift($phpReflections))) {
$instance = new PropertyReflection($this->getName(), $phpReflection->getName());
$zendReflections[] = $instance;
unset($phpReflection);
}
unset($phpReflections);
return $zendReflections;
}
/**
* @return string
*/
public function toString()
{
return parent::__toString();
}
/**
* @return string
*/
public function __toString()
{
return parent::__toString();
}
/**
* Creates a new FileScanner instance.
*
* By having this as a seperate method it allows the method to be overridden
* if a different FileScanner is needed.
*
* @param string $filename
*
* @return FileScanner
*/
protected function createFileScanner($filename)
{
return new FileScanner($filename);
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
class AuthorTag implements TagInterface
{
/**
* @var string
*/
protected $authorName = null;
/**
* @var string
*/
protected $authorEmail = null;
/**
* @return string
*/
public function getName()
{
return 'author';
}
/**
* Initializer
*
* @param string $tagDocblockLine
*/
public function initialize($tagDocblockLine)
{
$match = [];
if (!preg_match('/^([^\<]*)(\<([^\>]*)\>)?(.*)$/u', $tagDocblockLine, $match)) {
return;
}
if ($match[1] !== '') {
$this->authorName = rtrim($match[1]);
}
if (isset($match[3]) && $match[3] !== '') {
$this->authorEmail = $match[3];
}
}
/**
* @return null|string
*/
public function getAuthorName()
{
return $this->authorName;
}
/**
* @return null|string
*/
public function getAuthorEmail()
{
return $this->authorEmail;
}
public function __toString()
{
return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . "\n";
}
}

View file

@ -0,0 +1,109 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
use Zend\Code\Generic\Prototype\PrototypeGenericInterface;
class GenericTag implements TagInterface, PrototypeGenericInterface
{
/**
* @var string
*/
protected $name = null;
/**
* @var string
*/
protected $content = null;
/**
* @var null|string
*/
protected $contentSplitCharacter = null;
/**
* @var array
*/
protected $values = [];
/**
* @param string $contentSplitCharacter
*/
public function __construct($contentSplitCharacter = ' ')
{
$this->contentSplitCharacter = $contentSplitCharacter;
}
/**
* @param string $tagDocBlockLine
* @return void
*/
public function initialize($tagDocBlockLine)
{
$this->parse($tagDocBlockLine);
}
/**
* Get annotation tag name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @param string $name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* @return string
*/
public function getContent()
{
return $this->content;
}
/**
* @param int $position
* @return string
*/
public function returnValue($position)
{
return $this->values[$position];
}
/**
* Serialize to string
*
* Required by Reflector
*
* @todo What should this do?
* @return string
*/
public function __toString()
{
return 'DocBlock Tag [ * @' . $this->name . ' ]' . "\n";
}
/**
* @param string $docBlockLine
*/
protected function parse($docBlockLine)
{
$this->content = trim($docBlockLine);
$this->values = explode($this->contentSplitCharacter, $docBlockLine);
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
class LicenseTag implements TagInterface
{
/**
* @var string
*/
protected $url = null;
/**
* @var string
*/
protected $licenseName = null;
/**
* @return string
*/
public function getName()
{
return 'license';
}
/**
* Initializer
*
* @param string $tagDocblockLine
*/
public function initialize($tagDocblockLine)
{
$match = [];
if (!preg_match('#^([\S]*)(?:\s+(.*))?$#m', $tagDocblockLine, $match)) {
return;
}
if ($match[1] !== '') {
$this->url = trim($match[1]);
}
if (isset($match[2]) && $match[2] !== '') {
$this->licenseName = $match[2];
}
}
/**
* @return null|string
*/
public function getUrl()
{
return $this->url;
}
/**
* @return null|string
*/
public function getLicenseName()
{
return $this->licenseName;
}
public function __toString()
{
return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . "\n";
}
}

View file

@ -0,0 +1,122 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
class MethodTag implements TagInterface, PhpDocTypedTagInterface
{
/**
* Return value type
*
* @var array
*/
protected $types = [];
/**
* @var string
*/
protected $methodName = null;
/**
* @var string
*/
protected $description = null;
/**
* Is static method
*
* @var bool
*/
protected $isStatic = false;
/**
* @return string
*/
public function getName()
{
return 'method';
}
/**
* Initializer
*
* @param string $tagDocblockLine
*/
public function initialize($tagDocblockLine)
{
$match = [];
if (!preg_match('#^(static[\s]+)?(.+[\s]+)?(.+\(\))[\s]*(.*)$#m', $tagDocblockLine, $match)) {
return;
}
if ($match[1] !== '') {
$this->isStatic = true;
}
if ($match[2] !== '') {
$this->types = explode('|', rtrim($match[2]));
}
$this->methodName = $match[3];
if ($match[4] !== '') {
$this->description = $match[4];
}
}
/**
* Get return value type
*
* @return null|string
* @deprecated 2.0.4 use getTypes instead
*/
public function getReturnType()
{
if (empty($this->types)) {
return;
}
return $this->types[0];
}
public function getTypes()
{
return $this->types;
}
/**
* @return string
*/
public function getMethodName()
{
return $this->methodName;
}
/**
* @return null|string
*/
public function getDescription()
{
return $this->description;
}
/**
* @return bool
*/
public function isStatic()
{
return $this->isStatic;
}
public function __toString()
{
return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . "\n";
}
}

View file

@ -0,0 +1,98 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
class ParamTag implements TagInterface, PhpDocTypedTagInterface
{
/**
* @var array
*/
protected $types = [];
/**
* @var string
*/
protected $variableName = null;
/**
* @var string
*/
protected $description = null;
/**
* @return string
*/
public function getName()
{
return 'param';
}
/**
* Initializer
*
* @param string $tagDocBlockLine
*/
public function initialize($tagDocBlockLine)
{
$matches = [];
if (!preg_match('#((?:[\w|\\\]+(?:\[\])*\|?)+)(?:\s+(\$\S+))?(?:\s+(.*))?#s', $tagDocBlockLine, $matches)) {
return;
}
$this->types = explode('|', $matches[1]);
if (isset($matches[2])) {
$this->variableName = $matches[2];
}
if (isset($matches[3])) {
$this->description = trim(preg_replace('#\s+#', ' ', $matches[3]));
}
}
/**
* Get parameter variable type
*
* @return string
* @deprecated 2.0.4 use getTypes instead
*/
public function getType()
{
if (empty($this->types)) {
return '';
}
return $this->types[0];
}
public function getTypes()
{
return $this->types;
}
/**
* Get parameter name
*
* @return string
*/
public function getVariableName()
{
return $this->variableName;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
interface PhpDocTypedTagInterface
{
/**
* Return all types supported by the tag definition
*
* @return string[]
*/
public function getTypes();
}

View file

@ -0,0 +1,100 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
class PropertyTag implements TagInterface, PhpDocTypedTagInterface
{
/**
* @var array
*/
protected $types = [];
/**
* @var string
*/
protected $propertyName = null;
/**
* @var string
*/
protected $description = null;
/**
* @return string
*/
public function getName()
{
return 'property';
}
/**
* Initializer
*
* @param string $tagDocblockLine
*/
public function initialize($tagDocblockLine)
{
$match = [];
if (!preg_match('#^(.+)?(\$[\S]+)[\s]*(.*)$#m', $tagDocblockLine, $match)) {
return;
}
if ($match[1] !== '') {
$this->types = explode('|', rtrim($match[1]));
}
if ($match[2] !== '') {
$this->propertyName = $match[2];
}
if ($match[3] !== '') {
$this->description = $match[3];
}
}
/**
* @return null|string
* @deprecated 2.0.4 use getTypes instead
*/
public function getType()
{
if (empty($this->types)) {
return;
}
return $this->types[0];
}
public function getTypes()
{
return $this->types;
}
/**
* @return null|string
*/
public function getPropertyName()
{
return $this->propertyName;
}
/**
* @return null|string
*/
public function getDescription()
{
return $this->description;
}
public function __toString()
{
return 'DocBlock Tag [ * @' . $this->getName() . ' ]' . "\n";
}
}

View file

@ -0,0 +1,75 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
class ReturnTag implements TagInterface, PhpDocTypedTagInterface
{
/**
* @var array
*/
protected $types = [];
/**
* @var string
*/
protected $description = null;
/**
* @return string
*/
public function getName()
{
return 'return';
}
/**
* @param string $tagDocBlockLine
* @return void
*/
public function initialize($tagDocBlockLine)
{
$matches = [];
if (!preg_match('#((?:[\w|\\\]+(?:\[\])*\|?)+)(?:\s+(.*))?#s', $tagDocBlockLine, $matches)) {
return;
}
$this->types = explode('|', $matches[1]);
if (isset($matches[2])) {
$this->description = trim(preg_replace('#\s+#', ' ', $matches[2]));
}
}
/**
* @return string
* @deprecated 2.0.4 use getTypes instead
*/
public function getType()
{
if (empty($this->types)) {
return '';
}
return $this->types[0];
}
public function getTypes()
{
return $this->types;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
}

View file

@ -0,0 +1,21 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
use Zend\Code\Generic\Prototype\PrototypeInterface;
interface TagInterface extends PrototypeInterface
{
/**
* @param string $content
* @return void
*/
public function initialize($content);
}

View file

@ -0,0 +1,74 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock\Tag;
class ThrowsTag implements TagInterface, PhpDocTypedTagInterface
{
/**
* @var array
*/
protected $types = [];
/**
* @var string
*/
protected $description = null;
/**
* @return string
*/
public function getName()
{
return 'throws';
}
/**
* @param string $tagDocBlockLine
* @return void
*/
public function initialize($tagDocBlockLine)
{
$matches = [];
preg_match('#([\w|\\\]+)(?:\s+(.*))?#', $tagDocBlockLine, $matches);
$this->types = explode('|', $matches[1]);
if (isset($matches[2])) {
$this->description = $matches[2];
}
}
/**
* Get return variable type
*
* @return string
* @deprecated 2.0.4 use getTypes instead
*/
public function getType()
{
return implode('|', $this->getTypes());
}
/**
* @return array
*/
public function getTypes()
{
return $this->types;
}
/**
* @return string
*/
public function getDescription()
{
return $this->description;
}
}

View file

@ -0,0 +1,48 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\DocBlock;
use Zend\Code\Generic\Prototype\PrototypeClassFactory;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface;
class TagManager extends PrototypeClassFactory
{
/**
* @return void
*/
public function initializeDefaultTags()
{
$this->addPrototype(new Tag\ParamTag());
$this->addPrototype(new Tag\ReturnTag());
$this->addPrototype(new Tag\MethodTag());
$this->addPrototype(new Tag\PropertyTag());
$this->addPrototype(new Tag\AuthorTag());
$this->addPrototype(new Tag\LicenseTag());
$this->addPrototype(new Tag\ThrowsTag());
$this->setGenericPrototype(new Tag\GenericTag());
}
/**
* @param string $tagName
* @param string $content
* @return TagInterface
*/
public function createTag($tagName, $content = null)
{
/* @var TagInterface $newTag */
$newTag = $this->getClonedPrototype($tagName);
if ($content) {
$newTag->initialize($content);
}
return $newTag;
}
}

View file

@ -0,0 +1,298 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use Reflector;
use Zend\Code\Reflection\DocBlock\Tag\TagInterface as DocBlockTagInterface;
use Zend\Code\Reflection\DocBlock\TagManager as DocBlockTagManager;
use Zend\Code\Scanner\DocBlockScanner;
class DocBlockReflection implements ReflectionInterface
{
/**
* @var Reflector
*/
protected $reflector = null;
/**
* @var string
*/
protected $docComment = null;
/**
* @var DocBlockTagManager
*/
protected $tagManager = null;
/**#@+
* @var int
*/
protected $startLine = null;
protected $endLine = null;
/**#@-*/
/**
* @var string
*/
protected $cleanDocComment = null;
/**
* @var string
*/
protected $longDescription = null;
/**
* @var string
*/
protected $shortDescription = null;
/**
* @var array
*/
protected $tags = [];
/**
* @var bool
*/
protected $isReflected = false;
/**
* Export reflection
*
* Required by the Reflector interface.
*
* @todo What should this do?
* @return void
*/
public static function export()
{
}
/**
* @param Reflector|string $commentOrReflector
* @param null|DocBlockTagManager $tagManager
* @throws Exception\InvalidArgumentException
* @return DocBlockReflection
*/
public function __construct($commentOrReflector, DocBlockTagManager $tagManager = null)
{
if (!$tagManager) {
$tagManager = new DocBlockTagManager();
$tagManager->initializeDefaultTags();
}
$this->tagManager = $tagManager;
if ($commentOrReflector instanceof Reflector) {
$this->reflector = $commentOrReflector;
if (!method_exists($commentOrReflector, 'getDocComment')) {
throw new Exception\InvalidArgumentException('Reflector must contain method "getDocComment"');
}
/* @var MethodReflection $commentOrReflector */
$this->docComment = $commentOrReflector->getDocComment();
// determine line numbers
$lineCount = substr_count($this->docComment, "\n");
$this->startLine = $this->reflector->getStartLine() - $lineCount - 1;
$this->endLine = $this->reflector->getStartLine() - 1;
} elseif (is_string($commentOrReflector)) {
$this->docComment = $commentOrReflector;
} else {
throw new Exception\InvalidArgumentException(sprintf(
'%s must have a (string) DocComment or a Reflector in the constructor',
get_class($this)
));
}
if ($this->docComment == '') {
throw new Exception\InvalidArgumentException('DocComment cannot be empty');
}
$this->reflect();
}
/**
* Retrieve contents of DocBlock
*
* @return string
*/
public function getContents()
{
$this->reflect();
return $this->cleanDocComment;
}
/**
* Get start line (position) of DocBlock
*
* @return int
*/
public function getStartLine()
{
$this->reflect();
return $this->startLine;
}
/**
* Get last line (position) of DocBlock
*
* @return int
*/
public function getEndLine()
{
$this->reflect();
return $this->endLine;
}
/**
* Get DocBlock short description
*
* @return string
*/
public function getShortDescription()
{
$this->reflect();
return $this->shortDescription;
}
/**
* Get DocBlock long description
*
* @return string
*/
public function getLongDescription()
{
$this->reflect();
return $this->longDescription;
}
/**
* Does the DocBlock contain the given annotation tag?
*
* @param string $name
* @return bool
*/
public function hasTag($name)
{
$this->reflect();
foreach ($this->tags as $tag) {
if ($tag->getName() == $name) {
return true;
}
}
return false;
}
/**
* Retrieve the given DocBlock tag
*
* @param string $name
* @return DocBlockTagInterface|false
*/
public function getTag($name)
{
$this->reflect();
foreach ($this->tags as $tag) {
if ($tag->getName() == $name) {
return $tag;
}
}
return false;
}
/**
* Get all DocBlock annotation tags
*
* @param string $filter
* @return DocBlockTagInterface[]
*/
public function getTags($filter = null)
{
$this->reflect();
if ($filter === null || !is_string($filter)) {
return $this->tags;
}
$returnTags = [];
foreach ($this->tags as $tag) {
if ($tag->getName() == $filter) {
$returnTags[] = $tag;
}
}
return $returnTags;
}
/**
* Parse the DocBlock
*
* @return void
*/
protected function reflect()
{
if ($this->isReflected) {
return;
}
$docComment = preg_replace('#[ ]{0,1}\*/$#', '', $this->docComment);
// create a clean docComment
$this->cleanDocComment = preg_replace("#[ \t]*(?:/\*\*|\*/|\*)[ ]{0,1}(.*)?#", '$1', $docComment);
// @todo should be changed to remove first and last empty line
$this->cleanDocComment = ltrim($this->cleanDocComment, "\r\n");
$scanner = new DocBlockScanner($docComment);
$this->shortDescription = ltrim($scanner->getShortDescription());
$this->longDescription = ltrim($scanner->getLongDescription());
foreach ($scanner->getTags() as $tag) {
$this->tags[] = $this->tagManager->createTag(ltrim($tag['name'], '@'), ltrim($tag['value']));
}
$this->isReflected = true;
}
/**
* @return string
*/
public function toString()
{
$str = "DocBlock [ /* DocBlock */ ] {" . "\n" . "\n";
$str .= " - Tags [" . count($this->tags) . "] {" . "\n";
foreach ($this->tags as $tag) {
$str .= " " . $tag;
}
$str .= " }" . "\n";
$str .= "}" . "\n";
return $str;
}
/**
* Serialize to string
*
* Required by the Reflector interface
*
* @return string
*/
public function __toString()
{
return $this->toString();
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\Exception;
use Zend\Code\Exception;
class BadMethodCallException extends Exception\BadMethodCallException implements
ExceptionInterface
{
}

View file

@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\Exception;
use Zend\Code\Exception\ExceptionInterface as Exception;
interface ExceptionInterface extends Exception
{
}

View file

@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\Exception;
use Zend\Code\Exception;
class InvalidArgumentException extends Exception\InvalidArgumentException implements
ExceptionInterface
{
}

View file

@ -0,0 +1,17 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection\Exception;
use Zend\Code\Exception;
class RuntimeException extends Exception\RuntimeException implements
ExceptionInterface
{
}

View file

@ -0,0 +1,317 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use Zend\Code\Scanner\CachingFileScanner;
class FileReflection implements ReflectionInterface
{
/**
* @var string
*/
protected $filePath = null;
/**
* @var string
*/
protected $docComment = null;
/**
* @var int
*/
protected $startLine = 1;
/**
* @var int
*/
protected $endLine = null;
/**
* @var string[]
*/
protected $namespaces = [];
/**
* @var string[]
*/
protected $uses = [];
/**
* @var string[]
*/
protected $requiredFiles = [];
/**
* @var ClassReflection[]
*/
protected $classes = [];
/**
* @var FunctionReflection[]
*/
protected $functions = [];
/**
* @var string
*/
protected $contents = null;
/**
* @param string $filename
* @param bool $includeIfNotAlreadyIncluded
* @throws Exception\InvalidArgumentException If file does not exists
* @throws Exception\RuntimeException If file exists but is not included or required
*/
public function __construct($filename, $includeIfNotAlreadyIncluded = false)
{
if (($fileRealPath = realpath($filename)) === false) {
$fileRealPath = stream_resolve_include_path($filename);
}
if (!$fileRealPath) {
throw new Exception\InvalidArgumentException(sprintf(
'No file for %s was found.',
$filename
));
}
if (!in_array($fileRealPath, get_included_files())) {
if (!$includeIfNotAlreadyIncluded) {
throw new Exception\RuntimeException(sprintf(
'File %s must be required before it can be reflected',
$filename
));
}
include $fileRealPath;
}
$this->filePath = $fileRealPath;
$this->reflect();
}
/**
* Required by the Reflector interface.
*
* @todo What should this do?
* @return null
*/
public static function export()
{
return;
}
/**
* Return the file name of the reflected file
*
* @return string
*/
public function getFileName()
{
return basename($this->filePath);
}
/**
* Get the start line - Always 1, staying consistent with the Reflection API
*
* @return int
*/
public function getStartLine()
{
return $this->startLine;
}
/**
* Get the end line / number of lines
*
* @return int
*/
public function getEndLine()
{
return $this->endLine;
}
/**
* @return string
*/
public function getDocComment()
{
return $this->docComment;
}
/**
* @return DocBlockReflection
*/
public function getDocBlock()
{
if (!($docComment = $this->getDocComment())) {
return false;
}
$instance = new DocBlockReflection($docComment);
return $instance;
}
/**
* @return string[]
*/
public function getNamespaces()
{
return $this->namespaces;
}
/**
* @return string
*/
public function getNamespace()
{
if (count($this->namespaces) == 0) {
return;
}
return $this->namespaces[0];
}
/**
* @return array
*/
public function getUses()
{
return $this->uses;
}
/**
* Return the reflection classes of the classes found inside this file
*
* @return ClassReflection[]
*/
public function getClasses()
{
$classes = [];
foreach ($this->classes as $class) {
$classes[] = new ClassReflection($class);
}
return $classes;
}
/**
* Return the reflection functions of the functions found inside this file
*
* @return FunctionReflection[]
*/
public function getFunctions()
{
$functions = [];
foreach ($this->functions as $function) {
$functions[] = new FunctionReflection($function);
}
return $functions;
}
/**
* Retrieve the reflection class of a given class found in this file
*
* @param null|string $name
* @return ClassReflection
* @throws Exception\InvalidArgumentException for invalid class name or invalid reflection class
*/
public function getClass($name = null)
{
if (null === $name) {
reset($this->classes);
$selected = current($this->classes);
return new ClassReflection($selected);
}
if (in_array($name, $this->classes)) {
return new ClassReflection($name);
}
throw new Exception\InvalidArgumentException(sprintf(
'Class by name %s not found.',
$name
));
}
/**
* Return the full contents of file
*
* @return string
*/
public function getContents()
{
return file_get_contents($this->filePath);
}
public function toString()
{
return ''; // @todo
}
/**
* Serialize to string
*
* Required by the Reflector interface
*
* @todo What should this serialization look like?
* @return string
*/
public function __toString()
{
return '';
}
/**
* This method does the work of "reflecting" the file
*
* Uses Zend\Code\Scanner\FileScanner to gather file information
*
* @return void
*/
protected function reflect()
{
$scanner = new CachingFileScanner($this->filePath);
$this->docComment = $scanner->getDocComment();
$this->requiredFiles = $scanner->getIncludes();
$this->classes = $scanner->getClassNames();
$this->namespaces = $scanner->getNamespaces();
$this->uses = $scanner->getUses();
}
/**
* Validate / check a file level DocBlock
*
* @param array $tokens Array of tokenizer tokens
* @return void
*/
protected function checkFileDocBlock($tokens)
{
foreach ($tokens as $token) {
$type = $token[0];
$value = $token[1];
$lineNum = $token[2];
if (($type == T_OPEN_TAG) || ($type == T_WHITESPACE)) {
continue;
} elseif ($type == T_DOC_COMMENT) {
$this->docComment = $value;
$this->startLine = $lineNum + substr_count($value, "\n") + 1;
return;
} else {
// Only whitespace is allowed before file DocBlocks
return;
}
}
}
}

View file

@ -0,0 +1,275 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use ReflectionFunction;
class FunctionReflection extends ReflectionFunction implements ReflectionInterface
{
/**
* Constant use in @MethodReflection to display prototype as an array
*/
const PROTOTYPE_AS_ARRAY = 'prototype_as_array';
/**
* Constant use in @MethodReflection to display prototype as a string
*/
const PROTOTYPE_AS_STRING = 'prototype_as_string';
/**
* Get function DocBlock
*
* @throws Exception\InvalidArgumentException
* @return DocBlockReflection
*/
public function getDocBlock()
{
if ('' == ($comment = $this->getDocComment())) {
throw new Exception\InvalidArgumentException(sprintf(
'%s does not have a DocBlock',
$this->getName()
));
}
$instance = new DocBlockReflection($comment);
return $instance;
}
/**
* Get start line (position) of function
*
* @param bool $includeDocComment
* @return int
*/
public function getStartLine($includeDocComment = false)
{
if ($includeDocComment) {
if ($this->getDocComment() != '') {
return $this->getDocBlock()->getStartLine();
}
}
return parent::getStartLine();
}
/**
* Get contents of function
*
* @param bool $includeDocBlock
* @return string
*/
public function getContents($includeDocBlock = true)
{
$fileName = $this->getFileName();
if (false === $fileName) {
return '';
}
$startLine = $this->getStartLine();
$endLine = $this->getEndLine();
// eval'd protect
if (preg_match('#\((\d+)\) : eval\(\)\'d code$#', $fileName, $matches)) {
$fileName = preg_replace('#\(\d+\) : eval\(\)\'d code$#', '', $fileName);
$startLine = $endLine = $matches[1];
}
$lines = array_slice(
file($fileName, FILE_IGNORE_NEW_LINES),
$startLine - 1,
($endLine - ($startLine - 1)),
true
);
$functionLine = implode("\n", $lines);
$content = '';
if ($this->isClosure()) {
preg_match('#function\s*\([^\)]*\)\s*(use\s*\([^\)]+\))?\s*\{(.*\;)?\s*\}#s', $functionLine, $matches);
if (isset($matches[0])) {
$content = $matches[0];
}
} else {
$name = substr($this->getName(), strrpos($this->getName(), '\\')+1);
preg_match(
'#function\s+' . preg_quote($name) . '\s*\([^\)]*\)\s*{([^{}]+({[^}]+})*[^}]+)?}#',
$functionLine,
$matches
);
if (isset($matches[0])) {
$content = $matches[0];
}
}
$docComment = $this->getDocComment();
return $includeDocBlock && $docComment ? $docComment . "\n" . $content : $content;
}
/**
* Get method prototype
*
* @return array
*/
public function getPrototype($format = FunctionReflection::PROTOTYPE_AS_ARRAY)
{
$returnType = 'mixed';
$docBlock = $this->getDocBlock();
if ($docBlock) {
$return = $docBlock->getTag('return');
$returnTypes = $return->getTypes();
$returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0];
}
$prototype = [
'namespace' => $this->getNamespaceName(),
'name' => substr($this->getName(), strlen($this->getNamespaceName()) + 1),
'return' => $returnType,
'arguments' => [],
];
$parameters = $this->getParameters();
foreach ($parameters as $parameter) {
$prototype['arguments'][$parameter->getName()] = [
'type' => $parameter->detectType(),
'required' => !$parameter->isOptional(),
'by_ref' => $parameter->isPassedByReference(),
'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null,
];
}
if ($format == FunctionReflection::PROTOTYPE_AS_STRING) {
$line = $prototype['return'] . ' ' . $prototype['name'] . '(';
$args = [];
foreach ($prototype['arguments'] as $name => $argument) {
$argsLine = ($argument['type']
? $argument['type'] . ' '
: '') . ($argument['by_ref'] ? '&' : '') . '$' . $name;
if (!$argument['required']) {
$argsLine .= ' = ' . var_export($argument['default'], true);
}
$args[] = $argsLine;
}
$line .= implode(', ', $args);
$line .= ')';
return $line;
}
return $prototype;
}
/**
* Get function parameters
*
* @return ParameterReflection[]
*/
public function getParameters()
{
$phpReflections = parent::getParameters();
$zendReflections = [];
while ($phpReflections && ($phpReflection = array_shift($phpReflections))) {
$instance = new ParameterReflection($this->getName(), $phpReflection->getName());
$zendReflections[] = $instance;
unset($phpReflection);
}
unset($phpReflections);
return $zendReflections;
}
/**
* Get return type tag
*
* @throws Exception\InvalidArgumentException
* @return DocBlockReflection
*/
public function getReturn()
{
$docBlock = $this->getDocBlock();
if (!$docBlock->hasTag('return')) {
throw new Exception\InvalidArgumentException(
'Function does not specify an @return annotation tag; cannot determine return type'
);
}
$tag = $docBlock->getTag('return');
return new DocBlockReflection('@return ' . $tag->getDescription());
}
/**
* Get method body
*
* @return string|false
*/
public function getBody()
{
$fileName = $this->getFileName();
if (false === $fileName) {
throw new Exception\InvalidArgumentException(
'Cannot determine internals functions body'
);
}
$startLine = $this->getStartLine();
$endLine = $this->getEndLine();
// eval'd protect
if (preg_match('#\((\d+)\) : eval\(\)\'d code$#', $fileName, $matches)) {
$fileName = preg_replace('#\(\d+\) : eval\(\)\'d code$#', '', $fileName);
$startLine = $endLine = $matches[1];
}
$lines = array_slice(
file($fileName, FILE_IGNORE_NEW_LINES),
$startLine - 1,
($endLine - ($startLine - 1)),
true
);
$functionLine = implode("\n", $lines);
$body = false;
if ($this->isClosure()) {
preg_match('#function\s*\([^\)]*\)\s*(use\s*\([^\)]+\))?\s*\{(.*\;)\s*\}#s', $functionLine, $matches);
if (isset($matches[2])) {
$body = $matches[2];
}
} else {
$name = substr($this->getName(), strrpos($this->getName(), '\\')+1);
preg_match('#function\s+' . $name . '\s*\([^\)]*\)\s*{([^{}]+({[^}]+})*[^}]+)}#', $functionLine, $matches);
if (isset($matches[1])) {
$body = $matches[1];
}
}
return $body;
}
/**
* @return string
*/
public function toString()
{
return $this->__toString();
}
/**
* Required due to bug in php
*
* @return string
*/
public function __toString()
{
return parent::__toString();
}
}

View file

@ -0,0 +1,495 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use ReflectionMethod as PhpReflectionMethod;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Scanner\AnnotationScanner;
use Zend\Code\Scanner\CachingFileScanner;
class MethodReflection extends PhpReflectionMethod implements ReflectionInterface
{
/**
* Constant use in @MethodReflection to display prototype as an array
*/
const PROTOTYPE_AS_ARRAY = 'prototype_as_array';
/**
* Constant use in @MethodReflection to display prototype as a string
*/
const PROTOTYPE_AS_STRING = 'prototype_as_string';
/**
* @var AnnotationScanner
*/
protected $annotations = null;
/**
* Retrieve method DocBlock reflection
*
* @return DocBlockReflection|false
*/
public function getDocBlock()
{
if ('' == $this->getDocComment()) {
return false;
}
$instance = new DocBlockReflection($this);
return $instance;
}
/**
* @param AnnotationManager $annotationManager
* @return AnnotationScanner
*/
public function getAnnotations(AnnotationManager $annotationManager)
{
if (($docComment = $this->getDocComment()) == '') {
return false;
}
if ($this->annotations) {
return $this->annotations;
}
$cachingFileScanner = $this->createFileScanner($this->getFileName());
$nameInformation = $cachingFileScanner->getClassNameInformation($this->getDeclaringClass()->getName());
if (!$nameInformation) {
return false;
}
$this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation);
return $this->annotations;
}
/**
* Get start line (position) of method
*
* @param bool $includeDocComment
* @return int
*/
public function getStartLine($includeDocComment = false)
{
if ($includeDocComment) {
if ($this->getDocComment() != '') {
return $this->getDocBlock()->getStartLine();
}
}
return parent::getStartLine();
}
/**
* Get reflection of declaring class
*
* @return ClassReflection
*/
public function getDeclaringClass()
{
$phpReflection = parent::getDeclaringClass();
$zendReflection = new ClassReflection($phpReflection->getName());
unset($phpReflection);
return $zendReflection;
}
/**
* Get method prototype
*
* @return array
*/
public function getPrototype($format = MethodReflection::PROTOTYPE_AS_ARRAY)
{
$returnType = 'mixed';
$docBlock = $this->getDocBlock();
if ($docBlock) {
$return = $docBlock->getTag('return');
$returnTypes = $return->getTypes();
$returnType = count($returnTypes) > 1 ? implode('|', $returnTypes) : $returnTypes[0];
}
$declaringClass = $this->getDeclaringClass();
$prototype = [
'namespace' => $declaringClass->getNamespaceName(),
'class' => substr($declaringClass->getName(), strlen($declaringClass->getNamespaceName()) + 1),
'name' => $this->getName(),
'visibility' => ($this->isPublic() ? 'public' : ($this->isPrivate() ? 'private' : 'protected')),
'return' => $returnType,
'arguments' => [],
];
$parameters = $this->getParameters();
foreach ($parameters as $parameter) {
$prototype['arguments'][$parameter->getName()] = [
'type' => $parameter->detectType(),
'required' => !$parameter->isOptional(),
'by_ref' => $parameter->isPassedByReference(),
'default' => $parameter->isDefaultValueAvailable() ? $parameter->getDefaultValue() : null,
];
}
if ($format == MethodReflection::PROTOTYPE_AS_STRING) {
$line = $prototype['visibility'] . ' ' . $prototype['return'] . ' ' . $prototype['name'] . '(';
$args = [];
foreach ($prototype['arguments'] as $name => $argument) {
$argsLine = ($argument['type'] ?
$argument['type'] . ' '
: '') . ($argument['by_ref'] ? '&' : '') . '$' . $name;
if (!$argument['required']) {
$argsLine .= ' = ' . var_export($argument['default'], true);
}
$args[] = $argsLine;
}
$line .= implode(', ', $args);
$line .= ')';
return $line;
}
return $prototype;
}
/**
* Get all method parameter reflection objects
*
* @return ParameterReflection[]
*/
public function getParameters()
{
$phpReflections = parent::getParameters();
$zendReflections = [];
while ($phpReflections && ($phpReflection = array_shift($phpReflections))) {
$instance = new ParameterReflection(
[$this->getDeclaringClass()->getName(), $this->getName()],
$phpReflection->getName()
);
$zendReflections[] = $instance;
unset($phpReflection);
}
unset($phpReflections);
return $zendReflections;
}
/**
* Get method contents
*
* @param bool $includeDocBlock
* @return string
*/
public function getContents($includeDocBlock = true)
{
$docComment = $this->getDocComment();
$content = ($includeDocBlock && !empty($docComment)) ? $docComment . "\n" : '';
$content .= $this->extractMethodContents();
return $content;
}
/**
* Get method body
*
* @return string
*/
public function getBody()
{
return $this->extractMethodContents(true);
}
/**
* Tokenize method string and return concatenated body
*
* @param bool $bodyOnly
* @return string
*/
protected function extractMethodContents($bodyOnly = false)
{
$fileName = $this->getFileName();
if ((class_exists($this->class) && false === $fileName) || ! file_exists($fileName)) {
return '';
}
$lines = array_slice(
file($fileName, FILE_IGNORE_NEW_LINES),
$this->getStartLine() - 1,
($this->getEndLine() - ($this->getStartLine() - 1)),
true
);
$functionLine = implode("\n", $lines);
$tokens = token_get_all("<?php ". $functionLine);
//remove first entry which is php open tag
array_shift($tokens);
if (!count($tokens)) {
return '';
}
$capture = false;
$firstBrace = false;
$body = '';
foreach ($tokens as $key => $token) {
$tokenType = (is_array($token)) ? token_name($token[0]) : $token;
$tokenValue = (is_array($token)) ? $token[1] : $token;
switch ($tokenType) {
case "T_FINAL":
case "T_ABSTRACT":
case "T_PUBLIC":
case "T_PROTECTED":
case "T_PRIVATE":
case "T_STATIC":
case "T_FUNCTION":
// check to see if we have a valid function
// then check if we are inside function and have a closure
if ($this->isValidFunction($tokens, $key, $this->getName())) {
if ($bodyOnly === false) {
//if first instance of tokenType grab prefixed whitespace
//and append to body
if ($capture === false) {
$body .= $this->extractPrefixedWhitespace($tokens, $key);
}
$body .= $tokenValue;
}
$capture = true;
} else {
//closure test
if ($firstBrace && $tokenType == "T_FUNCTION") {
$body .= $tokenValue;
continue;
}
$capture = false;
continue;
}
break;
case "{":
if ($capture === false) {
continue;
}
if ($firstBrace === false) {
$firstBrace = true;
if ($bodyOnly === true) {
continue;
}
}
$body .= $tokenValue;
break;
case "}":
if ($capture === false) {
continue;
}
//check to see if this is the last brace
if ($this->isEndingBrace($tokens, $key)) {
//capture the end brace if not bodyOnly
if ($bodyOnly === false) {
$body .= $tokenValue;
}
break 2;
}
$body .= $tokenValue;
break;
default:
if ($capture === false) {
continue;
}
// if returning body only wait for first brace before capturing
if ($bodyOnly === true && $firstBrace !== true) {
continue;
}
$body .= $tokenValue;
break;
}
}
//remove ending whitespace and return
return rtrim($body);
}
/**
* Take current position and find any whitespace
*
* @param array $haystack
* @param int $position
* @return string
*/
protected function extractPrefixedWhitespace($haystack, $position)
{
$content = '';
$count = count($haystack);
if ($position+1 == $count) {
return $content;
}
for ($i = $position-1; $i >= 0; $i--) {
$tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i];
$tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i];
//search only for whitespace
if ($tokenType == "T_WHITESPACE") {
$content .= $tokenValue;
} else {
break;
}
}
return $content;
}
/**
* Test for ending brace
*
* @param array $haystack
* @param int $position
* @return bool
*/
protected function isEndingBrace($haystack, $position)
{
$count = count($haystack);
//advance one position
$position = $position+1;
if ($position == $count) {
return true;
}
for ($i = $position; $i < $count; $i++) {
$tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i];
switch ($tokenType) {
case "T_FINAL":
case "T_ABSTRACT":
case "T_PUBLIC":
case "T_PROTECTED":
case "T_PRIVATE":
case "T_STATIC":
return true;
case "T_FUNCTION":
// If a function is encountered and that function is not a closure
// then return true. otherwise the function is a closure, return false
if ($this->isValidFunction($haystack, $i)) {
return true;
}
return false;
case "}":
case ";":
case "T_BREAK":
case "T_CATCH":
case "T_DO":
case "T_ECHO":
case "T_ELSE":
case "T_ELSEIF":
case "T_EVAL":
case "T_EXIT":
case "T_FINALLY":
case "T_FOR":
case "T_FOREACH":
case "T_GOTO":
case "T_IF":
case "T_INCLUDE":
case "T_INCLUDE_ONCE":
case "T_PRINT":
case "T_STRING":
case "T_STRING_VARNAME":
case "T_THROW":
case "T_USE":
case "T_VARIABLE":
case "T_WHILE":
case "T_YIELD":
return false;
}
}
}
/**
* Test to see if current position is valid function or
* closure. Returns true if it's a function and NOT a closure
*
* @param array $haystack
* @param int $position
* @param string $functionName
* @return bool
*/
protected function isValidFunction($haystack, $position, $functionName = null)
{
$isValid = false;
$count = count($haystack);
for ($i = $position+1; $i < $count; $i++) {
$tokenType = (is_array($haystack[$i])) ? token_name($haystack[$i][0]) : $haystack[$i];
$tokenValue = (is_array($haystack[$i])) ? $haystack[$i][1] : $haystack[$i];
//check for occurance of ( or
if ($tokenType == "T_STRING") {
//check to see if function name is passed, if so validate against that
if ($functionName !== null && $tokenValue != $functionName) {
$isValid = false;
break;
}
$isValid = true;
break;
} elseif ($tokenValue == "(") {
break;
}
}
return $isValid;
}
/**
* @return string
*/
public function toString()
{
return parent::__toString();
}
/**
* @return string
*/
public function __toString()
{
return parent::__toString();
}
/**
* Creates a new FileScanner instance.
*
* By having this as a seperate method it allows the method to be overridden
* if a different FileScanner is needed.
*
* @param string $filename
*
* @return CachingFileScanner
*/
protected function createFileScanner($filename)
{
return new CachingFileScanner($filename);
}
}

View file

@ -0,0 +1,129 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use ReflectionParameter;
class ParameterReflection extends ReflectionParameter implements ReflectionInterface
{
/**
* @var bool
*/
protected $isFromMethod = false;
/**
* Get declaring class reflection object
*
* @return ClassReflection
*/
public function getDeclaringClass()
{
$phpReflection = parent::getDeclaringClass();
$zendReflection = new ClassReflection($phpReflection->getName());
unset($phpReflection);
return $zendReflection;
}
/**
* Get class reflection object
*
* @return ClassReflection
*/
public function getClass()
{
$phpReflection = parent::getClass();
if ($phpReflection === null) {
return;
}
$zendReflection = new ClassReflection($phpReflection->getName());
unset($phpReflection);
return $zendReflection;
}
/**
* Get declaring function reflection object
*
* @return FunctionReflection|MethodReflection
*/
public function getDeclaringFunction()
{
$phpReflection = parent::getDeclaringFunction();
if ($phpReflection instanceof \ReflectionMethod) {
$zendReflection = new MethodReflection($this->getDeclaringClass()->getName(), $phpReflection->getName());
} else {
$zendReflection = new FunctionReflection($phpReflection->getName());
}
unset($phpReflection);
return $zendReflection;
}
/**
* Get parameter type
*
* @return string|null
*/
public function detectType()
{
if (method_exists($this, 'getType')
&& ($type = $this->getType())
&& $type->isBuiltin()
) {
return (string) $type;
}
// can be dropped when dropping PHP7 support:
if ($this->isArray()) {
return 'array';
}
// can be dropped when dropping PHP7 support:
if ($this->isCallable()) {
return 'callable';
}
if (($class = $this->getClass()) instanceof \ReflectionClass) {
return $class->getName();
}
$docBlock = $this->getDeclaringFunction()->getDocBlock();
if (! $docBlock instanceof DocBlockReflection) {
return null;
}
$params = $docBlock->getTags('param');
if (isset($params[$this->getPosition()])) {
return $params[$this->getPosition()]->getType();
}
return null;
}
/**
* @return string
*/
public function toString()
{
return parent::__toString();
}
/**
* @return string
*/
public function __toString()
{
return parent::__toString();
}
}

View file

@ -0,0 +1,114 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use ReflectionProperty as PhpReflectionProperty;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Scanner\AnnotationScanner;
use Zend\Code\Scanner\CachingFileScanner;
/**
* @todo implement line numbers
*/
class PropertyReflection extends PhpReflectionProperty implements ReflectionInterface
{
/**
* @var AnnotationScanner
*/
protected $annotations;
/**
* Get declaring class reflection object
*
* @return ClassReflection
*/
public function getDeclaringClass()
{
$phpReflection = parent::getDeclaringClass();
$zendReflection = new ClassReflection($phpReflection->getName());
unset($phpReflection);
return $zendReflection;
}
/**
* Get DocBlock comment
*
* @return string|false False if no DocBlock defined
*/
public function getDocComment()
{
return parent::getDocComment();
}
/**
* @return false|DocBlockReflection
*/
public function getDocBlock()
{
if (!($docComment = $this->getDocComment())) {
return false;
}
$docBlockReflection = new DocBlockReflection($docComment);
return $docBlockReflection;
}
/**
* @param AnnotationManager $annotationManager
* @return AnnotationScanner
*/
public function getAnnotations(AnnotationManager $annotationManager)
{
if (null !== $this->annotations) {
return $this->annotations;
}
if (($docComment = $this->getDocComment()) == '') {
return false;
}
$class = $this->getDeclaringClass();
$cachingFileScanner = $this->createFileScanner($class->getFileName());
$nameInformation = $cachingFileScanner->getClassNameInformation($class->getName());
if (!$nameInformation) {
return false;
}
$this->annotations = new AnnotationScanner($annotationManager, $docComment, $nameInformation);
return $this->annotations;
}
/**
* @return string
*/
public function toString()
{
return $this->__toString();
}
/**
* Creates a new FileScanner instance.
*
* By having this as a seperate method it allows the method to be overridden
* if a different FileScanner is needed.
*
* @param string $filename
*
* @return CachingFileScanner
*/
protected function createFileScanner($filename)
{
return new CachingFileScanner($filename);
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Reflection;
use Reflector;
interface ReflectionInterface extends Reflector
{
/**
* @return string
*/
public function toString();
}

View file

@ -0,0 +1,117 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Exception;
class AggregateDirectoryScanner extends DirectoryScanner
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @param bool $returnScannerClass
* @todo not implemented
*/
public function getNamespaces($returnScannerClass = false)
{
// @todo
}
/*
public function getUses($returnScannerClass = false)
{}
*/
public function getIncludes($returnScannerClass = false)
{
}
public function getClasses($returnScannerClass = false, $returnDerivedScannerClass = false)
{
$classes = [];
foreach ($this->directories as $scanner) {
$classes += $scanner->getClasses();
}
if ($returnScannerClass) {
foreach ($classes as $index => $class) {
$classes[$index] = $this->getClass($class, $returnScannerClass, $returnDerivedScannerClass);
}
}
return $classes;
}
/**
* @param string $class
* @return bool
*/
public function hasClass($class)
{
foreach ($this->directories as $scanner) {
if ($scanner->hasClass($class)) {
break;
} else {
unset($scanner);
}
}
return (isset($scanner));
}
/**
* @param string $class
* @param bool $returnScannerClass
* @param bool $returnDerivedScannerClass
* @return ClassScanner|DerivedClassScanner
* @throws Exception\RuntimeException
*/
public function getClass($class, $returnScannerClass = true, $returnDerivedScannerClass = false)
{
foreach ($this->directories as $scanner) {
if ($scanner->hasClass($class)) {
break;
} else {
unset($scanner);
}
}
if (!isset($scanner)) {
throw new Exception\RuntimeException('Class by that name was not found.');
}
$classScanner = $scanner->getClass($class);
return new DerivedClassScanner($classScanner, $this);
}
/**
* @param bool $returnScannerClass
*/
public function getFunctions($returnScannerClass = false)
{
$this->scan();
if (!$returnScannerClass) {
$functions = [];
foreach ($this->infos as $info) {
if ($info['type'] == 'function') {
$functions[] = $info['name'];
}
}
return $functions;
}
$scannerClass = new FunctionScanner();
// @todo
}
}

View file

@ -0,0 +1,367 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Annotation\AnnotationCollection;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\NameInformation;
class AnnotationScanner extends AnnotationCollection implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string
*/
protected $docComment = null;
/**
* @var NameInformation
*/
protected $nameInformation = null;
/**
* @var AnnotationManager
*/
protected $annotationManager = null;
/**
* @var array
*/
protected $annotations = [];
/**
* @param AnnotationManager $annotationManager
* @param string $docComment
* @param NameInformation $nameInformation
* @return AnnotationScanner
*/
public function __construct(
AnnotationManager $annotationManager,
$docComment,
NameInformation $nameInformation = null
) {
$this->annotationManager = $annotationManager;
$this->docComment = $docComment;
$this->nameInformation = $nameInformation;
$this->scan($this->tokenize());
}
/**
* @param NameInformation $nameInformation
*/
public function setNameInformation(NameInformation $nameInformation)
{
$this->nameInformation = $nameInformation;
}
/**
* @param array $tokens
*/
protected function scan(array $tokens)
{
$annotations = [];
$annotationIndex = -1;
$contentEnd = false;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
switch ($token[0]) {
case 'ANNOTATION_CLASS':
$contentEnd = false;
$annotationIndex++;
$class = substr($token[1], 1);
$class = $this->nameInformation->resolveName($class);
$annotations[$annotationIndex] = [$class, null];
goto SCANNER_CONTINUE;
// goto no break needed
case 'ANNOTATION_CONTENT_START':
$annotations[$annotationIndex][1] = '';
// fall-through
case 'ANNOTATION_CONTENT_END':
case 'ANNOTATION_CONTENT':
case 'ANNOTATION_WHITESPACE':
case 'ANNOTATION_NEWLINE':
if (!$contentEnd
&& isset($annotations[$annotationIndex])
&& is_string($annotations[$annotationIndex][1])
) {
$annotations[$annotationIndex][1] .= $token[1];
}
if ($token[0] === 'ANNOTATION_CONTENT_END') {
$contentEnd = true;
}
goto SCANNER_CONTINUE;
// goto no break needed
}
SCANNER_CONTINUE:
if (next($tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
foreach ($annotations as $annotation) {
$annotation[] = '@' . $annotation[0] . $annotation[1];
$annotationObject = $this->annotationManager->createAnnotation($annotation);
if ($annotationObject) {
$this->append($annotationObject);
}
}
}
/**
* @return array
*/
protected function tokenize()
{
static $CONTEXT_DOCBLOCK = 0x01;
static $CONTEXT_ASTERISK = 0x02;
static $CONTEXT_CLASS = 0x04;
static $CONTEXT_CONTENT = 0x08;
$context = 0x00;
$stream = $this->docComment;
$streamIndex = null;
$tokens = [];
$tokenIndex = null;
$currentChar = null;
$currentWord = null;
$currentLine = null;
$annotationParentCount = 0;
$MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (
&$stream,
&$streamIndex,
&$currentChar,
&$currentWord,
&$currentLine
) {
$positionsForward = ($positionsForward > 0) ? $positionsForward : 1;
$streamIndex = ($streamIndex === null) ? 0 : $streamIndex + $positionsForward;
if (!isset($stream[$streamIndex])) {
$currentChar = false;
return false;
}
$currentChar = $stream[$streamIndex];
$matches = [];
$currentLine = (preg_match('#(.*?)(?:\n|\r\n?)#', $stream, $matches, null, $streamIndex) === 1)
? $matches[1]
: substr($stream, $streamIndex);
if ($currentChar === ' ') {
$currentWord = (preg_match('#( +)#', $currentLine, $matches) === 1) ? $matches[1] : $currentLine;
} else {
$currentWord = (($matches = strpos($currentLine, ' ')) !== false)
? substr($currentLine, 0, $matches)
: $currentLine;
}
return $currentChar;
};
$MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord));
};
$MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine));
};
$MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) {
$tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1;
$tokens[$tokenIndex] = ['ANNOTATION_UNKNOWN', ''];
};
$MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) {
$tokens[$tokenIndex][0] = $type;
};
$MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentChar;
};
$MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentWord;
};
$MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentLine;
};
$MACRO_HAS_CONTEXT = function ($which) use (&$context) {
return (($context & $which) === $which);
};
$MACRO_STREAM_ADVANCE_CHAR();
$MACRO_TOKEN_ADVANCE();
TOKENIZER_TOP:
if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTSTART');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context |= $CONTEXT_DOCBLOCK;
$context |= $CONTEXT_ASTERISK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($MACRO_HAS_CONTEXT($CONTEXT_CLASS)) {
if (in_array($currentChar, [' ', '(', "\n", "\r"])) {
$context &= ~$CONTEXT_CLASS;
$MACRO_TOKEN_ADVANCE();
} else {
$MACRO_TOKEN_APPEND_CHAR();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
}
// Since we don't know what line endings are used in the file, we check for all scenarios. If we find a
// cariage return (\r), we check the next character for a line feed (\n). If so we consume it and act as
// if the cariage return was a line feed.
$lineEnded = $currentChar === "\n";
if ($currentChar === "\r") {
$lineEnded = true;
$nextChar = $MACRO_STREAM_ADVANCE_CHAR();
if ($nextChar !== "\n") {
$streamIndex--;
}
$currentChar = "\n";
}
if ($lineEnded) {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_NEWLINE');
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_ASTERISK;
$context &= ~$CONTEXT_CLASS;
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === ' ') {
$MACRO_TOKEN_SET_TYPE(
($MACRO_HAS_CONTEXT($CONTEXT_ASTERISK))
? 'ANNOTATION_WHITESPACE'
: 'ANNOTATION_WHITESPACE_INDENT'
);
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($MACRO_HAS_CONTEXT($CONTEXT_CONTENT) && $MACRO_HAS_CONTEXT($CONTEXT_ASTERISK)) {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT');
$annotationParentCount += substr_count($currentWord, '(');
$annotationParentCount -= substr_count($currentWord, ')');
if ($annotationParentCount === 0) {
$context &= ~$CONTEXT_CONTENT;
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_END');
}
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '(' && $tokens[$tokenIndex - 1][0] === 'ANNOTATION_CLASS') {
$context |= $CONTEXT_CONTENT;
$annotationParentCount = 1;
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_START');
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && $currentWord === '*/') {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTEND');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_DOCBLOCK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '*') {
if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && ($MACRO_HAS_CONTEXT($CONTEXT_ASTERISK))) {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE');
} else {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_ASTERISK');
$context |= $CONTEXT_ASTERISK;
}
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '@') {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_CLASS');
$context |= $CONTEXT_CLASS;
$MACRO_TOKEN_APPEND_CHAR();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
TOKENIZER_CONTINUE:
if ($context && $CONTEXT_CONTENT) {
$MACRO_TOKEN_APPEND_CHAR();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
} else {
$MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE');
$MACRO_TOKEN_APPEND_LINE();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_LINE() === false) {
goto TOKENIZER_END;
}
}
goto TOKENIZER_TOP;
TOKENIZER_END:
array_pop($tokens);
return $tokens;
}
}

View file

@ -0,0 +1,162 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Exception;
use Zend\Code\NameInformation;
class CachingFileScanner extends FileScanner
{
/**
* @var array
*/
protected static $cache = [];
/**
* @var null|FileScanner
*/
protected $fileScanner = null;
/**
* @param string $file
* @param AnnotationManager $annotationManager
* @throws Exception\InvalidArgumentException
*/
public function __construct($file, AnnotationManager $annotationManager = null)
{
if (!file_exists($file)) {
throw new Exception\InvalidArgumentException(sprintf(
'File "%s" not found',
$file
));
}
$file = realpath($file);
$cacheId = md5($file) . '/' . ((isset($annotationManager)
? spl_object_hash($annotationManager)
: 'no-annotation'));
if (isset(static::$cache[$cacheId])) {
$this->fileScanner = static::$cache[$cacheId];
} else {
$this->fileScanner = new FileScanner($file, $annotationManager);
static::$cache[$cacheId] = $this->fileScanner;
}
}
/**
* @return void
*/
public static function clearCache()
{
static::$cache = [];
}
/**
* @return AnnotationManager
*/
public function getAnnotationManager()
{
return $this->fileScanner->getAnnotationManager();
}
/**
* @return array|null|string
*/
public function getFile()
{
return $this->fileScanner->getFile();
}
/**
* @return null|string
*/
public function getDocComment()
{
return $this->fileScanner->getDocComment();
}
/**
* @return array
*/
public function getNamespaces()
{
return $this->fileScanner->getNamespaces();
}
/**
* @param null|string $namespace
* @return array|null
*/
public function getUses($namespace = null)
{
return $this->fileScanner->getUses($namespace);
}
/**
* @return array
*/
public function getIncludes()
{
return $this->fileScanner->getIncludes();
}
/**
* @return array
*/
public function getClassNames()
{
return $this->fileScanner->getClassNames();
}
/**
* @return array
*/
public function getClasses()
{
return $this->fileScanner->getClasses();
}
/**
* @param int|string $className
* @return ClassScanner
*/
public function getClass($className)
{
return $this->fileScanner->getClass($className);
}
/**
* @param string $className
* @return bool|null|NameInformation
*/
public function getClassNameInformation($className)
{
return $this->fileScanner->getClassNameInformation($className);
}
/**
* @return array
*/
public function getFunctionNames()
{
return $this->fileScanner->getFunctionNames();
}
/**
* @return array
*/
public function getFunctions()
{
return $this->fileScanner->getFunctions();
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,236 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Annotation;
use Zend\Code\Exception;
use Zend\Code\NameInformation;
class ConstantScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var array
*/
protected $tokens;
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var string
*/
protected $class;
/**
* @var ClassScanner
*/
protected $scannerClass;
/**
* @var int
*/
protected $lineStart;
/**
* @var string
*/
protected $docComment;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $value;
/**
* Constructor
*
* @param array $constantTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $constantTokens, NameInformation $nameInformation = null)
{
$this->tokens = $constantTokens;
$this->nameInformation = $nameInformation;
}
/**
* @param string $class
*/
public function setClass($class)
{
$this->class = $class;
}
/**
* @param ClassScanner $scannerClass
*/
public function setScannerClass(ClassScanner $scannerClass)
{
$this->scannerClass = $scannerClass;
}
/**
* @return ClassScanner
*/
public function getClassScanner()
{
return $this->scannerClass;
}
/**
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* @return string
*/
public function getValue()
{
$this->scan();
return $this->value;
}
/**
* @return string
*/
public function getDocComment()
{
$this->scan();
return $this->docComment;
}
/**
* @param Annotation\AnnotationManager $annotationManager
* @return AnnotationScanner
*/
public function getAnnotations(Annotation\AnnotationManager $annotationManager)
{
if (($docComment = $this->getDocComment()) == '') {
return false;
}
return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
}
/**
* @return string
*/
public function __toString()
{
$this->scan();
return var_export($this, true);
}
/**
* Scan tokens
*
* @throws Exception\RuntimeException
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
if (!$this->tokens) {
throw new Exception\RuntimeException('No tokens were provided');
}
/**
* Variables & Setup
*/
$tokens = &$this->tokens;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
if (!is_string($token)) {
list($tokenType, $tokenContent, $tokenLine) = $token;
switch ($tokenType) {
case T_DOC_COMMENT:
if ($this->docComment === null && $this->name === null) {
$this->docComment = $tokenContent;
}
goto SCANNER_CONTINUE;
// fall-through
case T_STRING:
$string = (is_string($token)) ? $token : $tokenContent;
if (null === $this->name) {
$this->name = $string;
} else {
if ('self' == strtolower($string)) {
list($tokenNextType, $tokenNextContent, $tokenNextLine) = next($tokens);
if ('::' == $tokenNextContent) {
list($tokenNextType, $tokenNextContent, $tokenNextLine) = next($tokens);
if ($this->getClassScanner()->getConstant($tokenNextContent)) {
$this->value = $this->getClassScanner()->getConstant($tokenNextContent)->getValue();
}
}
}
}
goto SCANNER_CONTINUE;
// fall-through
case T_CONSTANT_ENCAPSED_STRING:
case T_DNUMBER:
case T_LNUMBER:
$string = (is_string($token)) ? $token : $tokenContent;
if (substr($string, 0, 1) === '"' || substr($string, 0, 1) === "'") {
$this->value = substr($string, 1, -1); // Remove quotes
} else {
$this->value = $string;
}
goto SCANNER_CONTINUE;
// fall-trough
default:
goto SCANNER_CONTINUE;
}
}
SCANNER_CONTINUE:
if (next($this->tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
$this->isScanned = true;
}
}

View file

@ -0,0 +1,381 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Exception;
class DerivedClassScanner extends ClassScanner
{
/**
* @var DirectoryScanner
*/
protected $directoryScanner = null;
/**
* @var ClassScanner
*/
protected $classScanner = null;
/**
* @var array
*/
protected $parentClassScanners = [];
/**
* @var array
*/
protected $interfaceClassScanners = [];
/**
* @param ClassScanner $classScanner
* @param DirectoryScanner $directoryScanner
*/
public function __construct(ClassScanner $classScanner, DirectoryScanner $directoryScanner)
{
$this->classScanner = $classScanner;
$this->directoryScanner = $directoryScanner;
$currentScannerClass = $classScanner;
while ($currentScannerClass && $currentScannerClass->hasParentClass()) {
$currentParentClassName = $currentScannerClass->getParentClass();
if ($directoryScanner->hasClass($currentParentClassName)) {
$currentParentClass = $directoryScanner->getClass($currentParentClassName);
$this->parentClassScanners[$currentParentClassName] = $currentParentClass;
$currentScannerClass = $currentParentClass;
} else {
$currentScannerClass = false;
}
}
foreach ($interfaces = $this->classScanner->getInterfaces() as $iName) {
if ($directoryScanner->hasClass($iName)) {
$this->interfaceClassScanners[$iName] = $directoryScanner->getClass($iName);
}
}
}
/**
* @return null|string
*/
public function getName()
{
return $this->classScanner->getName();
}
/**
* @return null|string
*/
public function getShortName()
{
return $this->classScanner->getShortName();
}
/**
* @return bool
*/
public function isInstantiable()
{
return $this->classScanner->isInstantiable();
}
/**
* @return bool
*/
public function isFinal()
{
return $this->classScanner->isFinal();
}
/**
* @return bool
*/
public function isAbstract()
{
return $this->classScanner->isAbstract();
}
/**
* @return bool
*/
public function isInterface()
{
return $this->classScanner->isInterface();
}
/**
* @return array
*/
public function getParentClasses()
{
return array_keys($this->parentClassScanners);
}
/**
* @return bool
*/
public function hasParentClass()
{
return ($this->classScanner->getParentClass() !== null);
}
/**
* @return null|string
*/
public function getParentClass()
{
return $this->classScanner->getParentClass();
}
/**
* @param bool $returnClassScanners
* @return array
*/
public function getInterfaces($returnClassScanners = false)
{
if ($returnClassScanners) {
return $this->interfaceClassScanners;
}
$interfaces = $this->classScanner->getInterfaces();
foreach ($this->parentClassScanners as $pClassScanner) {
$interfaces = array_merge($interfaces, $pClassScanner->getInterfaces());
}
return $interfaces;
}
/**
* Return a list of constant names
*
* @return array
*/
public function getConstantNames()
{
$constants = $this->classScanner->getConstantNames();
foreach ($this->parentClassScanners as $pClassScanner) {
$constants = array_merge($constants, $pClassScanner->getConstantNames());
}
return $constants;
}
/**
* Return a list of constants
*
* @param bool $namesOnly Set false to return instances of ConstantScanner
* @return array|ConstantScanner[]
*/
public function getConstants($namesOnly = true)
{
if (true === $namesOnly) {
trigger_error('Use method getConstantNames() instead', E_USER_DEPRECATED);
return $this->getConstantNames();
}
$constants = $this->classScanner->getConstants();
foreach ($this->parentClassScanners as $pClassScanner) {
$constants = array_merge($constants, $pClassScanner->getConstants($namesOnly));
}
return $constants;
}
/**
* Return a single constant by given name or index of info
*
* @param string|int $constantNameOrInfoIndex
* @throws Exception\InvalidArgumentException
* @return bool|ConstantScanner
*/
public function getConstant($constantNameOrInfoIndex)
{
if ($this->classScanner->hasConstant($constantNameOrInfoIndex)) {
return $this->classScanner->getConstant($constantNameOrInfoIndex);
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasConstant($constantNameOrInfoIndex)) {
return $pClassScanner->getConstant($constantNameOrInfoIndex);
}
}
throw new Exception\InvalidArgumentException(sprintf(
'Constant %s not found in %s',
$constantNameOrInfoIndex,
$this->classScanner->getName()
));
}
/**
* Verify if class or parent class has constant
*
* @param string $name
* @return bool
*/
public function hasConstant($name)
{
if ($this->classScanner->hasConstant($name)) {
return true;
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasConstant($name)) {
return true;
}
}
return false;
}
/**
* Return a list of property names
*
* @return array
*/
public function getPropertyNames()
{
$properties = $this->classScanner->getPropertyNames();
foreach ($this->parentClassScanners as $pClassScanner) {
$properties = array_merge($properties, $pClassScanner->getPropertyNames());
}
return $properties;
}
/**
* @param bool $returnScannerProperty
* @return array
*/
public function getProperties($returnScannerProperty = false)
{
$properties = $this->classScanner->getProperties($returnScannerProperty);
foreach ($this->parentClassScanners as $pClassScanner) {
$properties = array_merge($properties, $pClassScanner->getProperties($returnScannerProperty));
}
return $properties;
}
/**
* Return a single property by given name or index of info
*
* @param string|int $propertyNameOrInfoIndex
* @throws Exception\InvalidArgumentException
* @return bool|PropertyScanner
*/
public function getProperty($propertyNameOrInfoIndex)
{
if ($this->classScanner->hasProperty($propertyNameOrInfoIndex)) {
return $this->classScanner->getProperty($propertyNameOrInfoIndex);
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasProperty($propertyNameOrInfoIndex)) {
return $pClassScanner->getProperty($propertyNameOrInfoIndex);
}
}
throw new Exception\InvalidArgumentException(sprintf(
'Property %s not found in %s',
$propertyNameOrInfoIndex,
$this->classScanner->getName()
));
}
/**
* Verify if class or parent class has property
*
* @param string $name
* @return bool
*/
public function hasProperty($name)
{
if ($this->classScanner->hasProperty($name)) {
return true;
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasProperty($name)) {
return true;
}
}
return false;
}
/**
* @return array
*/
public function getMethodNames()
{
$methods = $this->classScanner->getMethodNames();
foreach ($this->parentClassScanners as $pClassScanner) {
$methods = array_merge($methods, $pClassScanner->getMethodNames());
}
return $methods;
}
/**
* @return MethodScanner[]
*/
public function getMethods()
{
$methods = $this->classScanner->getMethods();
foreach ($this->parentClassScanners as $pClassScanner) {
$methods = array_merge($methods, $pClassScanner->getMethods());
}
return $methods;
}
/**
* @param int|string $methodNameOrInfoIndex
* @return MethodScanner
* @throws Exception\InvalidArgumentException
*/
public function getMethod($methodNameOrInfoIndex)
{
if ($this->classScanner->hasMethod($methodNameOrInfoIndex)) {
return $this->classScanner->getMethod($methodNameOrInfoIndex);
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasMethod($methodNameOrInfoIndex)) {
return $pClassScanner->getMethod($methodNameOrInfoIndex);
}
}
throw new Exception\InvalidArgumentException(sprintf(
'Method %s not found in %s',
$methodNameOrInfoIndex,
$this->classScanner->getName()
));
}
/**
* Verify if class or parent class has method by given name
*
* @param string $name
* @return bool
*/
public function hasMethod($name)
{
if ($this->classScanner->hasMethod($name)) {
return true;
}
foreach ($this->parentClassScanners as $pClassScanner) {
if ($pClassScanner->hasMethod($name)) {
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,272 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use Zend\Code\Exception;
class DirectoryScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string[]|DirectoryScanner[]
*/
protected $directories = [];
/**
* @var FileScanner[]
*/
protected $fileScanners = [];
/**
* @var array
*/
protected $classToFileScanner = null;
/**
* @param null|string|array $directory
*/
public function __construct($directory = null)
{
if ($directory) {
if (is_string($directory)) {
$this->addDirectory($directory);
} elseif (is_array($directory)) {
foreach ($directory as $d) {
$this->addDirectory($d);
}
}
}
}
/**
* @param DirectoryScanner|string $directory
* @return void
* @throws Exception\InvalidArgumentException
*/
public function addDirectory($directory)
{
if ($directory instanceof DirectoryScanner) {
$this->directories[] = $directory;
} elseif (is_string($directory)) {
$realDir = realpath($directory);
if (!$realDir || !is_dir($realDir)) {
throw new Exception\InvalidArgumentException(sprintf(
'Directory "%s" does not exist',
$realDir
));
}
$this->directories[] = $realDir;
} else {
throw new Exception\InvalidArgumentException(
'The argument provided was neither a DirectoryScanner or directory path'
);
}
}
/**
* @param DirectoryScanner $directoryScanner
* @return void
*/
public function addDirectoryScanner(DirectoryScanner $directoryScanner)
{
$this->addDirectory($directoryScanner);
}
/**
* @param FileScanner $fileScanner
* @return void
*/
public function addFileScanner(FileScanner $fileScanner)
{
$this->fileScanners[] = $fileScanner;
}
/**
* @return void
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
// iterate directories creating file scanners
foreach ($this->directories as $directory) {
if ($directory instanceof DirectoryScanner) {
$directory->scan();
if ($directory->fileScanners) {
$this->fileScanners = array_merge($this->fileScanners, $directory->fileScanners);
}
} else {
$rdi = new RecursiveDirectoryIterator($directory);
foreach (new RecursiveIteratorIterator($rdi) as $item) {
if ($item->isFile() && pathinfo($item->getRealPath(), PATHINFO_EXTENSION) == 'php') {
$this->fileScanners[] = new FileScanner($item->getRealPath());
}
}
}
}
$this->isScanned = true;
}
/**
* @todo implement method
*/
public function getNamespaces()
{
// @todo
}
/**
* @param bool $returnFileScanners
* @return array
*/
public function getFiles($returnFileScanners = false)
{
$this->scan();
$return = [];
foreach ($this->fileScanners as $fileScanner) {
$return[] = ($returnFileScanners) ? $fileScanner : $fileScanner->getFile();
}
return $return;
}
/**
* @return array
*/
public function getClassNames()
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
return array_keys($this->classToFileScanner);
}
/**
* @param bool $returnDerivedScannerClass
* @return array
*/
public function getClasses($returnDerivedScannerClass = false)
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
$returnClasses = [];
foreach ($this->classToFileScanner as $className => $fsIndex) {
$classScanner = $this->fileScanners[$fsIndex]->getClass($className);
if ($returnDerivedScannerClass) {
$classScanner = new DerivedClassScanner($classScanner, $this);
}
$returnClasses[] = $classScanner;
}
return $returnClasses;
}
/**
* @param string $class
* @return bool
*/
public function hasClass($class)
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
return (isset($this->classToFileScanner[$class]));
}
/**
* @param string $class
* @param bool $returnDerivedScannerClass
* @return ClassScanner|DerivedClassScanner
* @throws Exception\InvalidArgumentException
*/
public function getClass($class, $returnDerivedScannerClass = false)
{
$this->scan();
if ($this->classToFileScanner === null) {
$this->createClassToFileScannerCache();
}
if (!isset($this->classToFileScanner[$class])) {
throw new Exception\InvalidArgumentException('Class not found.');
}
/** @var FileScanner $fs */
$fs = $this->fileScanners[$this->classToFileScanner[$class]];
$returnClass = $fs->getClass($class);
if (($returnClass instanceof ClassScanner) && $returnDerivedScannerClass) {
return new DerivedClassScanner($returnClass, $this);
}
return $returnClass;
}
/**
* Create class to file scanner cache
*
* @return void
*/
protected function createClassToFileScannerCache()
{
if ($this->classToFileScanner !== null) {
return;
}
$this->classToFileScanner = [];
/** @var FileScanner $fileScanner */
foreach ($this->fileScanners as $fsIndex => $fileScanner) {
$fsClasses = $fileScanner->getClassNames();
foreach ($fsClasses as $fsClassName) {
$this->classToFileScanner[$fsClassName] = $fsIndex;
}
}
}
/**
* Export
*
* @todo implement method
*/
public static function export()
{
// @todo
}
/**
* __ToString
*
* @todo implement method
*/
public function __toString()
{
// @todo
}
}

View file

@ -0,0 +1,341 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\NameInformation;
class DocBlockScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string
*/
protected $docComment = null;
/**
* @var NameInformation
*/
protected $nameInformation = null;
/**
* @var AnnotationManager
*/
protected $annotationManager = null;
/**
* @var string
*/
protected $shortDescription = null;
/**
* @var string
*/
protected $longDescription = '';
/**
* @var array
*/
protected $tags = [];
/**
* @var array
*/
protected $annotations = [];
/**
* @param string $docComment
* @param null|NameInformation $nameInformation
*/
public function __construct($docComment, NameInformation $nameInformation = null)
{
$this->docComment = $docComment;
$this->nameInformation = $nameInformation;
}
/**
* @return string
*/
public function getShortDescription()
{
$this->scan();
return $this->shortDescription;
}
/**
* @return string
*/
public function getLongDescription()
{
$this->scan();
return $this->longDescription;
}
/**
* @return array
*/
public function getTags()
{
$this->scan();
return $this->tags;
}
/**
* @return array
*/
public function getAnnotations()
{
$this->scan();
return $this->annotations;
}
/**
* @return void
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
$mode = 1;
$tokens = $this->tokenize();
$tagIndex = null;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
switch ($token[0]) {
case 'DOCBLOCK_NEWLINE':
if ($this->shortDescription != '' && $tagIndex === null) {
$mode = 2;
} else {
$this->longDescription .= $token[1];
}
goto SCANNER_CONTINUE;
//goto no break needed
case 'DOCBLOCK_WHITESPACE':
case 'DOCBLOCK_TEXT':
if ($tagIndex !== null) {
$this->tags[$tagIndex]['value'] .= ($this->tags[$tagIndex]['value'] == '')
? $token[1]
: ' ' . $token[1];
goto SCANNER_CONTINUE;
} elseif ($mode <= 2) {
if ($mode == 1) {
$this->shortDescription .= $token[1];
} else {
$this->longDescription .= $token[1];
}
goto SCANNER_CONTINUE;
}
//gotos no break needed
case 'DOCBLOCK_TAG':
array_push($this->tags, ['name' => $token[1],
'value' => '']);
end($this->tags);
$tagIndex = key($this->tags);
$mode = 3;
goto SCANNER_CONTINUE;
//goto no break needed
case 'DOCBLOCK_COMMENTEND':
goto SCANNER_END;
}
SCANNER_CONTINUE:
if (next($tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
$this->shortDescription = trim($this->shortDescription);
$this->longDescription = trim($this->longDescription);
$this->isScanned = true;
}
/**
* @return array
*/
protected function tokenize()
{
static $CONTEXT_INSIDE_DOCBLOCK = 0x01;
static $CONTEXT_INSIDE_ASTERISK = 0x02;
$context = 0x00;
$stream = $this->docComment;
$streamIndex = null;
$tokens = [];
$tokenIndex = null;
$currentChar = null;
$currentWord = null;
$currentLine = null;
$MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (
&$stream,
&$streamIndex,
&$currentChar,
&$currentWord,
&$currentLine
) {
$positionsForward = ($positionsForward > 0) ? $positionsForward : 1;
$streamIndex = ($streamIndex === null) ? 0 : $streamIndex + $positionsForward;
if (!isset($stream[$streamIndex])) {
$currentChar = false;
return false;
}
$currentChar = $stream[$streamIndex];
$matches = [];
$currentLine = (preg_match('#(.*?)\r?\n#', $stream, $matches, null, $streamIndex) === 1)
? $matches[1]
: substr($stream, $streamIndex);
if ($currentChar === ' ') {
$currentWord = (preg_match('#( +)#', $currentLine, $matches) === 1) ? $matches[1] : $currentLine;
} else {
$currentWord = (($matches = strpos($currentLine, ' ')) !== false)
? substr($currentLine, 0, $matches)
: $currentLine;
}
return $currentChar;
};
$MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord));
};
$MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) {
return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine));
};
$MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) {
$tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1;
$tokens[$tokenIndex] = ['DOCBLOCK_UNKNOWN', ''];
};
$MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) {
$tokens[$tokenIndex][0] = $type;
};
$MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentChar;
};
$MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentWord;
};
$MACRO_TOKEN_APPEND_WORD_PARTIAL = function ($length) use (&$currentWord, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= substr($currentWord, 0, $length);
};
$MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) {
$tokens[$tokenIndex][1] .= $currentLine;
};
$MACRO_STREAM_ADVANCE_CHAR();
$MACRO_TOKEN_ADVANCE();
TOKENIZER_TOP:
if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTSTART');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context |= $CONTEXT_INSIDE_DOCBLOCK;
$context |= $CONTEXT_INSIDE_ASTERISK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($context & $CONTEXT_INSIDE_DOCBLOCK && $currentWord === '*/') {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTEND');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_INSIDE_DOCBLOCK;
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === ' ' || $currentChar === "\t") {
$MACRO_TOKEN_SET_TYPE(
($context & $CONTEXT_INSIDE_ASTERISK)
? 'DOCBLOCK_WHITESPACE'
: 'DOCBLOCK_WHITESPACE_INDENT'
);
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '*') {
if (($context & $CONTEXT_INSIDE_DOCBLOCK) && ($context & $CONTEXT_INSIDE_ASTERISK)) {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT');
} else {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_ASTERISK');
$context |= $CONTEXT_INSIDE_ASTERISK;
}
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === '@') {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_TAG');
$MACRO_TOKEN_APPEND_WORD();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_WORD() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
if ($currentChar === "\n") {
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_NEWLINE');
$MACRO_TOKEN_APPEND_CHAR();
$MACRO_TOKEN_ADVANCE();
$context &= ~$CONTEXT_INSIDE_ASTERISK;
if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
}
$MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT');
$MACRO_TOKEN_APPEND_LINE();
$MACRO_TOKEN_ADVANCE();
if ($MACRO_STREAM_ADVANCE_LINE() === false) {
goto TOKENIZER_END;
}
goto TOKENIZER_TOP;
TOKENIZER_END:
array_pop($tokens);
return $tokens;
}
}

View file

@ -0,0 +1,46 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Exception;
class FileScanner extends TokenArrayScanner implements ScannerInterface
{
/**
* @var string
*/
protected $file = null;
/**
* @param string $file
* @param null|AnnotationManager $annotationManager
* @throws Exception\InvalidArgumentException
*/
public function __construct($file, AnnotationManager $annotationManager = null)
{
$this->file = $file;
if (!file_exists($file)) {
throw new Exception\InvalidArgumentException(sprintf(
'File "%s" not found',
$file
));
}
parent::__construct(token_get_all(file_get_contents($file)), $annotationManager);
}
/**
* @return string
*/
public function getFile()
{
return $this->file;
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
class FunctionScanner
{
// @todo
// Should this extend something similar to MethodScanner? Similar to ReflectionFunctionAbstract
}

View file

@ -0,0 +1,612 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Annotation\AnnotationManager;
use Zend\Code\Exception;
use Zend\Code\NameInformation;
class MethodScanner implements ScannerInterface
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var string
*/
protected $docComment = null;
/**
* @var ClassScanner
*/
protected $scannerClass = null;
/**
* @var string
*/
protected $class = null;
/**
* @var string
*/
protected $name = null;
/**
* @var int
*/
protected $lineStart = null;
/**
* @var int
*/
protected $lineEnd = null;
/**
* @var bool
*/
protected $isFinal = false;
/**
* @var bool
*/
protected $isAbstract = false;
/**
* @var bool
*/
protected $isPublic = true;
/**
* @var bool
*/
protected $isProtected = false;
/**
* @var bool
*/
protected $isPrivate = false;
/**
* @var bool
*/
protected $isStatic = false;
/**
* @var string
*/
protected $body = '';
/**
* @var array
*/
protected $tokens = [];
/**
* @var NameInformation
*/
protected $nameInformation = null;
/**
* @var array
*/
protected $infos = [];
/**
* @param array $methodTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $methodTokens, NameInformation $nameInformation = null)
{
$this->tokens = $methodTokens;
$this->nameInformation = $nameInformation;
}
/**
* @param string $class
* @return MethodScanner
*/
public function setClass($class)
{
$this->class = (string) $class;
return $this;
}
/**
* @param ClassScanner $scannerClass
* @return MethodScanner
*/
public function setScannerClass(ClassScanner $scannerClass)
{
$this->scannerClass = $scannerClass;
return $this;
}
/**
* @return MethodScanner
*/
public function getClassScanner()
{
return $this->scannerClass;
}
/**
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* @return int
*/
public function getLineStart()
{
$this->scan();
return $this->lineStart;
}
/**
* @return int
*/
public function getLineEnd()
{
$this->scan();
return $this->lineEnd;
}
/**
* @return string
*/
public function getDocComment()
{
$this->scan();
return $this->docComment;
}
/**
* @param AnnotationManager $annotationManager
* @return AnnotationScanner
*/
public function getAnnotations(AnnotationManager $annotationManager)
{
if (($docComment = $this->getDocComment()) == '') {
return false;
}
return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
}
/**
* @return bool
*/
public function isFinal()
{
$this->scan();
return $this->isFinal;
}
/**
* @return bool
*/
public function isAbstract()
{
$this->scan();
return $this->isAbstract;
}
/**
* @return bool
*/
public function isPublic()
{
$this->scan();
return $this->isPublic;
}
/**
* @return bool
*/
public function isProtected()
{
$this->scan();
return $this->isProtected;
}
/**
* @return bool
*/
public function isPrivate()
{
$this->scan();
return $this->isPrivate;
}
/**
* @return bool
*/
public function isStatic()
{
$this->scan();
return $this->isStatic;
}
/**
* Override the given name for a method, this is necessary to
* support traits.
*
* @param $name
* @return self
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Visibility must be of T_PUBLIC, T_PRIVATE or T_PROTECTED
* Needed to support traits
*
* @param $visibility T_PUBLIC | T_PRIVATE | T_PROTECTED
* @return self
* @throws \Zend\Code\Exception
*/
public function setVisibility($visibility)
{
switch (strtolower($visibility)) {
case T_PUBLIC:
$this->isPublic = true;
$this->isPrivate = false;
$this->isProtected = false;
break;
case T_PRIVATE:
$this->isPublic = false;
$this->isPrivate = true;
$this->isProtected = false;
break;
case T_PROTECTED:
$this->isPublic = false;
$this->isPrivate = false;
$this->isProtected = true;
break;
default:
throw new Exception("Invalid visibility argument passed to setVisibility.");
}
return $this;
}
/**
* @return int
*/
public function getNumberOfParameters()
{
return count($this->getParameters());
}
/**
* @param bool $returnScanner
* @return array
*/
public function getParameters($returnScanner = false)
{
$this->scan();
$return = [];
foreach ($this->infos as $info) {
if ($info['type'] != 'parameter') {
continue;
}
if (!$returnScanner) {
$return[] = $info['name'];
} else {
$return[] = $this->getParameter($info['name']);
}
}
return $return;
}
/**
* @param int|string $parameterNameOrInfoIndex
* @return ParameterScanner
* @throws Exception\InvalidArgumentException
*/
public function getParameter($parameterNameOrInfoIndex)
{
$this->scan();
if (is_int($parameterNameOrInfoIndex)) {
$info = $this->infos[$parameterNameOrInfoIndex];
if ($info['type'] != 'parameter') {
throw new Exception\InvalidArgumentException('Index of info offset is not about a parameter');
}
} elseif (is_string($parameterNameOrInfoIndex)) {
foreach ($this->infos as $info) {
if ($info['type'] === 'parameter' && $info['name'] === $parameterNameOrInfoIndex) {
break;
}
unset($info);
}
if (!isset($info)) {
throw new Exception\InvalidArgumentException('Index of info offset is not about a parameter');
}
}
$p = new ParameterScanner(
array_slice($this->tokens, $info['tokenStart'], $info['tokenEnd'] - $info['tokenStart']),
$this->nameInformation
);
$p->setDeclaringFunction($this->name);
$p->setDeclaringScannerFunction($this);
$p->setDeclaringClass($this->class);
$p->setDeclaringScannerClass($this->scannerClass);
$p->setPosition($info['position']);
return $p;
}
/**
* @return string
*/
public function getBody()
{
$this->scan();
return $this->body;
}
public static function export()
{
// @todo
}
public function __toString()
{
$this->scan();
return var_export($this, true);
}
protected function scan()
{
if ($this->isScanned) {
return;
}
if (!$this->tokens) {
throw new Exception\RuntimeException('No tokens were provided');
}
/**
* Variables & Setup
*/
$tokens = &$this->tokens; // localize
$infos = &$this->infos; // localize
$tokenIndex = null;
$token = null;
$tokenType = null;
$tokenContent = null;
$tokenLine = null;
$infoIndex = 0;
$parentCount = 0;
/*
* MACRO creation
*/
$MACRO_TOKEN_ADVANCE = function () use (
&$tokens,
&$tokenIndex,
&$token,
&$tokenType,
&$tokenContent,
&$tokenLine
) {
static $lastTokenArray = null;
$tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1;
if (!isset($tokens[$tokenIndex])) {
$token = false;
$tokenContent = false;
$tokenType = false;
$tokenLine = false;
return false;
}
$token = $tokens[$tokenIndex];
if (is_string($token)) {
$tokenType = null;
$tokenContent = $token;
$tokenLine = $tokenLine + substr_count(
$lastTokenArray[1],
"\n"
); // adjust token line by last known newline count
} else {
list($tokenType, $tokenContent, $tokenLine) = $token;
}
return $tokenIndex;
};
$MACRO_INFO_START = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) {
$infos[$infoIndex] = [
'type' => 'parameter',
'tokenStart' => $tokenIndex,
'tokenEnd' => null,
'lineStart' => $tokenLine,
'lineEnd' => $tokenLine,
'name' => null,
'position' => $infoIndex + 1, // position is +1 of infoIndex
];
};
$MACRO_INFO_ADVANCE = function () use (&$infoIndex, &$infos, &$tokenIndex, &$tokenLine) {
$infos[$infoIndex]['tokenEnd'] = $tokenIndex;
$infos[$infoIndex]['lineEnd'] = $tokenLine;
$infoIndex++;
return $infoIndex;
};
/**
* START FINITE STATE MACHINE FOR SCANNING TOKENS
*/
// Initialize token
$MACRO_TOKEN_ADVANCE();
SCANNER_TOP:
$this->lineStart = ($this->lineStart) ? : $tokenLine;
switch ($tokenType) {
case T_DOC_COMMENT:
$this->lineStart = null;
if ($this->docComment === null && $this->name === null) {
$this->docComment = $tokenContent;
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_FINAL:
$this->isFinal = true;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_ABSTRACT:
$this->isAbstract = true;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_PUBLIC:
// use defaults
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_PROTECTED:
$this->setVisibility(T_PROTECTED);
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_PRIVATE:
$this->setVisibility(T_PRIVATE);
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_STATIC:
$this->isStatic = true;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_NS_SEPARATOR:
if (!isset($infos[$infoIndex])) {
$MACRO_INFO_START();
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case T_VARIABLE:
case T_STRING:
if ($tokenType === T_STRING && $parentCount === 0) {
$this->name = $tokenContent;
}
if ($parentCount === 1) {
if (!isset($infos[$infoIndex])) {
$MACRO_INFO_START();
}
if ($tokenType === T_VARIABLE) {
$infos[$infoIndex]['name'] = ltrim($tokenContent, '$');
}
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case null:
switch ($tokenContent) {
case '&':
if (!isset($infos[$infoIndex])) {
$MACRO_INFO_START();
}
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case '(':
$parentCount++;
goto SCANNER_CONTINUE_SIGNATURE;
// goto (no break needed);
case ')':
$parentCount--;
if ($parentCount > 0) {
goto SCANNER_CONTINUE_SIGNATURE;
}
if ($parentCount === 0) {
if ($infos) {
$MACRO_INFO_ADVANCE();
}
$context = 'body';
}
goto SCANNER_CONTINUE_BODY;
// goto (no break needed);
case ',':
if ($parentCount === 1) {
$MACRO_INFO_ADVANCE();
}
goto SCANNER_CONTINUE_SIGNATURE;
}
}
SCANNER_CONTINUE_SIGNATURE:
if ($MACRO_TOKEN_ADVANCE() === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_CONTINUE_BODY:
$braceCount = 0;
while ($MACRO_TOKEN_ADVANCE() !== false) {
if ($tokenContent == '}') {
$braceCount--;
}
if ($braceCount > 0) {
$this->body .= $tokenContent;
}
if ($tokenContent == '{') {
$braceCount++;
}
$this->lineEnd = $tokenLine;
}
SCANNER_END:
$this->isScanned = true;
return;
}
}

View file

@ -0,0 +1,352 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\NameInformation;
class ParameterScanner
{
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var null|ClassScanner
*/
protected $declaringScannerClass = null;
/**
* @var null|string
*/
protected $declaringClass = null;
/**
* @var null|MethodScanner
*/
protected $declaringScannerFunction = null;
/**
* @var null|string
*/
protected $declaringFunction = null;
/**
* @var null|string
*/
protected $defaultValue = null;
/**
* @var null|string
*/
protected $class = null;
/**
* @var null|string
*/
protected $name = null;
/**
* @var null|int
*/
protected $position = null;
/**
* @var bool
*/
protected $isArray = false;
/**
* @var bool
*/
protected $isDefaultValueAvailable = false;
/**
* @var bool
*/
protected $isOptional = false;
/**
* @var bool
*/
protected $isPassedByReference = false;
/**
* @var array|null
*/
protected $tokens = null;
/**
* @var null|NameInformation
*/
protected $nameInformation = null;
/**
* @param array $parameterTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $parameterTokens, NameInformation $nameInformation = null)
{
$this->tokens = $parameterTokens;
$this->nameInformation = $nameInformation;
}
/**
* Set declaring class
*
* @param string $class
* @return void
*/
public function setDeclaringClass($class)
{
$this->declaringClass = (string) $class;
}
/**
* Set declaring scanner class
*
* @param ClassScanner $scannerClass
* @return void
*/
public function setDeclaringScannerClass(ClassScanner $scannerClass)
{
$this->declaringScannerClass = $scannerClass;
}
/**
* Set declaring function
*
* @param string $function
* @return void
*/
public function setDeclaringFunction($function)
{
$this->declaringFunction = $function;
}
/**
* Set declaring scanner function
*
* @param MethodScanner $scannerFunction
* @return void
*/
public function setDeclaringScannerFunction(MethodScanner $scannerFunction)
{
$this->declaringScannerFunction = $scannerFunction;
}
/**
* Set position
*
* @param int $position
* @return void
*/
public function setPosition($position)
{
$this->position = $position;
}
/**
* Scan
*
* @return void
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
$tokens = &$this->tokens;
reset($tokens);
SCANNER_TOP:
$token = current($tokens);
if (is_string($token)) {
// check pass by ref
if ($token === '&') {
$this->isPassedByReference = true;
goto SCANNER_CONTINUE;
}
if ($token === '=') {
$this->isOptional = true;
$this->isDefaultValueAvailable = true;
goto SCANNER_CONTINUE;
}
} else {
if ($this->name === null && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
$this->class .= $token[1];
goto SCANNER_CONTINUE;
}
if ($token[0] === T_VARIABLE) {
$this->name = ltrim($token[1], '$');
goto SCANNER_CONTINUE;
}
}
if ($this->name !== null) {
$this->defaultValue .= trim((is_string($token)) ? $token : $token[1]);
}
SCANNER_CONTINUE:
if (next($this->tokens) === false) {
goto SCANNER_END;
}
goto SCANNER_TOP;
SCANNER_END:
if ($this->class && $this->nameInformation) {
$this->class = $this->nameInformation->resolveName($this->class);
}
$this->isScanned = true;
}
/**
* Get declaring scanner class
*
* @return ClassScanner
*/
public function getDeclaringScannerClass()
{
return $this->declaringScannerClass;
}
/**
* Get declaring class
*
* @return string
*/
public function getDeclaringClass()
{
return $this->declaringClass;
}
/**
* Get declaring scanner function
*
* @return MethodScanner
*/
public function getDeclaringScannerFunction()
{
return $this->declaringScannerFunction;
}
/**
* Get declaring function
*
* @return string
*/
public function getDeclaringFunction()
{
return $this->declaringFunction;
}
/**
* Get default value
*
* @return string
*/
public function getDefaultValue()
{
$this->scan();
return $this->defaultValue;
}
/**
* Get class
*
* @return string
*/
public function getClass()
{
$this->scan();
return $this->class;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* Get position
*
* @return int
*/
public function getPosition()
{
$this->scan();
return $this->position;
}
/**
* Check if is array
*
* @return bool
*/
public function isArray()
{
$this->scan();
return $this->isArray;
}
/**
* Check if default value is available
*
* @return bool
*/
public function isDefaultValueAvailable()
{
$this->scan();
return $this->isDefaultValueAvailable;
}
/**
* Check if is optional
*
* @return bool
*/
public function isOptional()
{
$this->scan();
return $this->isOptional;
}
/**
* Check if is passed by reference
*
* @return bool
*/
public function isPassedByReference()
{
$this->scan();
return $this->isPassedByReference;
}
}

View file

@ -0,0 +1,317 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
use Zend\Code\Annotation;
use Zend\Code\Exception;
use Zend\Code\NameInformation;
class PropertyScanner implements ScannerInterface
{
const T_BOOLEAN = "boolean";
const T_INTEGER = "int";
const T_STRING = "string";
const T_ARRAY = "array";
const T_UNKNOWN = "unknown";
/**
* @var bool
*/
protected $isScanned = false;
/**
* @var array
*/
protected $tokens;
/**
* @var NameInformation
*/
protected $nameInformation;
/**
* @var string
*/
protected $class;
/**
* @var ClassScanner
*/
protected $scannerClass;
/**
* @var int
*/
protected $lineStart;
/**
* @var bool
*/
protected $isProtected = false;
/**
* @var bool
*/
protected $isPublic = true;
/**
* @var bool
*/
protected $isPrivate = false;
/**
* @var bool
*/
protected $isStatic = false;
/**
* @var string
*/
protected $docComment;
/**
* @var string
*/
protected $name;
/**
* @var string
*/
protected $value;
/**
* @var string
*/
protected $valueType;
/**
* Constructor
*
* @param array $propertyTokens
* @param NameInformation $nameInformation
*/
public function __construct(array $propertyTokens, NameInformation $nameInformation = null)
{
$this->tokens = $propertyTokens;
$this->nameInformation = $nameInformation;
}
/**
* @param string $class
*/
public function setClass($class)
{
$this->class = $class;
}
/**
* @param ClassScanner $scannerClass
*/
public function setScannerClass(ClassScanner $scannerClass)
{
$this->scannerClass = $scannerClass;
}
/**
* @return ClassScanner
*/
public function getClassScanner()
{
return $this->scannerClass;
}
/**
* @return string
*/
public function getName()
{
$this->scan();
return $this->name;
}
/**
* @return string
*/
public function getValueType()
{
$this->scan();
return $this->valueType;
}
/**
* @return bool
*/
public function isPublic()
{
$this->scan();
return $this->isPublic;
}
/**
* @return bool
*/
public function isPrivate()
{
$this->scan();
return $this->isPrivate;
}
/**
* @return bool
*/
public function isProtected()
{
$this->scan();
return $this->isProtected;
}
/**
* @return bool
*/
public function isStatic()
{
$this->scan();
return $this->isStatic;
}
/**
* @return string
*/
public function getValue()
{
$this->scan();
return $this->value;
}
/**
* @return string
*/
public function getDocComment()
{
$this->scan();
return $this->docComment;
}
/**
* @param Annotation\AnnotationManager $annotationManager
* @return AnnotationScanner
*/
public function getAnnotations(Annotation\AnnotationManager $annotationManager)
{
if (($docComment = $this->getDocComment()) == '') {
return false;
}
return new AnnotationScanner($annotationManager, $docComment, $this->nameInformation);
}
/**
* @return string
*/
public function __toString()
{
$this->scan();
return var_export($this, true);
}
/**
* Scan tokens
*
* @throws \Zend\Code\Exception\RuntimeException
*/
protected function scan()
{
if ($this->isScanned) {
return;
}
if (!$this->tokens) {
throw new Exception\RuntimeException('No tokens were provided');
}
/**
* Variables & Setup
*/
$value = '';
$concatenateValue = false;
$tokens = &$this->tokens;
reset($tokens);
foreach ($tokens as $token) {
$tempValue = $token;
if (!is_string($token)) {
list($tokenType, $tokenContent, $tokenLine) = $token;
switch ($tokenType) {
case T_DOC_COMMENT:
if ($this->docComment === null && $this->name === null) {
$this->docComment = $tokenContent;
}
break;
case T_VARIABLE:
$this->name = ltrim($tokenContent, '$');
break;
case T_PUBLIC:
// use defaults
break;
case T_PROTECTED:
$this->isProtected = true;
$this->isPublic = false;
break;
case T_PRIVATE:
$this->isPrivate = true;
$this->isPublic = false;
break;
case T_STATIC:
$this->isStatic = true;
break;
default:
$tempValue = trim($tokenContent);
break;
}
}
//end value concatenation
if (!is_array($token) && trim($token) == ";") {
$concatenateValue = false;
}
if (true === $concatenateValue) {
$value .= $tempValue;
}
//start value concatenation
if (!is_array($token) && trim($token) == "=") {
$concatenateValue = true;
}
}
$this->valueType = self::T_UNKNOWN;
if ($value == "false" || $value == "true") {
$this->valueType = self::T_BOOLEAN;
} elseif (is_numeric($value)) {
$this->valueType = self::T_INTEGER;
} elseif (0 === strpos($value, 'array') || 0 === strpos($value, "[")) {
$this->valueType = self::T_ARRAY;
} elseif (substr($value, 0, 1) === '"' || substr($value, 0, 1) === "'") {
$value = substr($value, 1, -1); // Remove quotes
$this->valueType = self::T_STRING;
}
$this->value = empty($value) ? null : $value;
$this->isScanned = true;
}
}

View file

@ -0,0 +1,16 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2016 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace Zend\Code\Scanner;
interface ScannerInterface
{
/* public static function export($tokens); */
/* public function toString(); */
}

Some files were not shown because too many files have changed in this diff Show more