mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-18 10:07:12 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6ff9f9360 | ||
|
|
ffb733ef78 | ||
|
|
818be8de6a | ||
|
|
268ae0f584 | ||
|
|
6b3854ad25 | ||
|
|
db125e8df4 | ||
|
|
62d62638b2 | ||
|
|
476f62b577 | ||
|
|
e5c8b970d6 | ||
|
|
d5d2d76892 | ||
|
|
fba297a2a7 | ||
|
|
0fd348d3fe | ||
|
|
67e478e26a | ||
|
|
e292151642 | ||
|
|
8bb2a5f9b7 | ||
|
|
ac3b854893 | ||
|
|
c6fad15f7c | ||
|
|
836d531676 | ||
|
|
bba116ba9d | ||
|
|
23fa082874 | ||
|
|
e6811e927f | ||
|
|
05596d7626 | ||
|
|
e5e172728c | ||
|
|
9a2f0eb8ba | ||
|
|
bf44b757fe | ||
|
|
e6526132ee | ||
|
|
6b7c110b33 | ||
|
|
ad1e63e15c | ||
|
|
6eafab0ff3 | ||
|
|
fe49c3d9ee | ||
|
|
3f6ec1c280 | ||
|
|
72df918400 | ||
|
|
c10c41e932 | ||
|
|
977da00f0f | ||
|
|
7e5538a638 | ||
|
|
295d2fba38 | ||
|
|
ef9f04dd59 | ||
|
|
6171834fde | ||
|
|
7f729dc9a7 | ||
|
|
144140cd39 | ||
|
|
be72a2d318 | ||
|
|
6d03fa81d9 | ||
|
|
3e7bb6e171 | ||
|
|
0f32bd0e61 | ||
|
|
b0b90f44f0 | ||
|
|
6b69015d83 | ||
|
|
053005ed4e | ||
|
|
64165bd4ba | ||
|
|
9b08619fbd | ||
|
|
3dcbd98b5d | ||
|
|
d411a2bc7e | ||
|
|
e6a969a640 | ||
|
|
708c2c9253 | ||
|
|
d68dbdc53d | ||
|
|
fb5dfa51d4 | ||
|
|
ae02953010 | ||
|
|
fd0ac20074 | ||
|
|
21feb61eb5 | ||
|
|
280a3ce56d | ||
|
|
0604d62704 | ||
|
|
a66d783afd | ||
|
|
84de81c009 | ||
|
|
38743b6779 | ||
|
|
0d52cb6389 | ||
|
|
2281569ebc | ||
|
|
e0faa7f04f | ||
|
|
39a08094f1 | ||
|
|
07b195e1ee | ||
|
|
0bca477a34 | ||
|
|
66a7d3bf31 | ||
|
|
2331fc92f6 | ||
|
|
b1922e00de | ||
|
|
53ba484043 | ||
|
|
cfb3ebea55 | ||
|
|
c33820b04b | ||
|
|
280c4a1d44 | ||
|
|
66ae84e9d7 | ||
|
|
3023fb2220 | ||
|
|
a9b6edf3ce | ||
|
|
f3d1a28bf7 | ||
|
|
fd9e6bd26d | ||
|
|
d5e4b2ec73 | ||
|
|
bae8be0dcd | ||
|
|
3a5d30e027 | ||
|
|
d2414cd3dc | ||
|
|
070b13745a | ||
|
|
df7807cd73 | ||
|
|
b07e3c36b7 | ||
|
|
163dd7877e | ||
|
|
6d705c1a0f | ||
|
|
ab0bcb8d31 | ||
|
|
ac6e37af97 | ||
|
|
4a7affe15b | ||
|
|
b7797b4e1a | ||
|
|
8b529636bf | ||
|
|
2e9fd6a2e8 | ||
|
|
cbb14bab1e | ||
|
|
d57128e65c | ||
|
|
d9c0928243 | ||
|
|
63c9de4e8b | ||
|
|
76619d4a16 | ||
|
|
47d3f86c53 | ||
|
|
cfb104b8c8 | ||
|
|
5c51ccf185 | ||
|
|
5d93f42598 | ||
|
|
eb83d810de | ||
|
|
6f0fc03c49 | ||
|
|
b09525332f | ||
|
|
c2796044a6 | ||
|
|
8418970624 | ||
|
|
0d38c84407 | ||
|
|
bfc443b40c | ||
|
|
4a9ddd15fe | ||
|
|
87ed522579 | ||
|
|
f01f8ea41d | ||
|
|
620b932272 | ||
|
|
beb96a487b | ||
|
|
7a03741e0b | ||
|
|
a79e4c174c | ||
|
|
be61012d74 | ||
|
|
99de44e96f | ||
|
|
b20c7da00b | ||
|
|
99054d341f | ||
|
|
9da4a65065 | ||
|
|
39faeec91d | ||
|
|
0c15eb99f5 | ||
|
|
21c36677ab | ||
|
|
ffcaa03337 | ||
|
|
37810ef836 | ||
|
|
587406073d | ||
|
|
aed654aa85 | ||
|
|
2812eac046 | ||
|
|
db20ae39fb | ||
|
|
cdaa97eb31 | ||
|
|
27bdc08a1e | ||
|
|
a18985a888 | ||
|
|
747e6d7cdf | ||
|
|
c2f692140f | ||
|
|
8eb596c1cc | ||
|
|
26e2eade91 | ||
|
|
1ac9106c48 | ||
|
|
1352ef56a6 | ||
|
|
6df31db139 | ||
|
|
829a92e5aa | ||
|
|
04936a7b39 | ||
|
|
ae82b8488f | ||
|
|
bae65d2afe | ||
|
|
28df2a4cd6 | ||
|
|
fedcd8cf49 | ||
|
|
a868c60f8e | ||
|
|
e6d2ec7c54 | ||
|
|
f19dac185a | ||
|
|
0ba0ad6b42 | ||
|
|
8b9591a7c5 | ||
|
|
082c9354d0 | ||
|
|
233e4b3ff7 | ||
|
|
d788b51755 | ||
|
|
bbd5775f1f | ||
|
|
044ebd68c9 | ||
|
|
e0a5a75364 | ||
|
|
607aa79601 | ||
|
|
a4107acb6d | ||
|
|
34eabb7d11 | ||
|
|
f07ab7eda0 | ||
|
|
d6119709ba | ||
|
|
21ec090933 | ||
|
|
ba1395e42d | ||
|
|
9a3c6f50ef | ||
|
|
009237bad2 | ||
|
|
6b707f7166 | ||
|
|
5236694595 | ||
|
|
ce57e5445f | ||
|
|
37f5090d90 | ||
|
|
5941846e99 | ||
|
|
95ad95d3be | ||
|
|
aa61de9761 | ||
|
|
a935cbfff9 | ||
|
|
ee412932f1 | ||
|
|
c8fe0e4e40 | ||
|
|
ad4e358fb0 | ||
|
|
1b3acdce18 | ||
|
|
4235beebfa | ||
|
|
60ba10fad1 | ||
|
|
f3cca4b7c7 | ||
|
|
e0d8988490 | ||
|
|
91ef535ae3 | ||
|
|
c999e7d32a | ||
|
|
2b05ea3221 | ||
|
|
33a2e55cbc | ||
|
|
3d97a72bac | ||
|
|
51210b581e | ||
|
|
cee3c0bc21 | ||
|
|
731e797dd8 | ||
|
|
838fbfa43f | ||
|
|
4d262053fc | ||
|
|
bbac3fa418 | ||
|
|
cd252c1f09 | ||
|
|
8f8cbd9ab6 | ||
|
|
a5b50ba482 | ||
|
|
f0a2901e56 | ||
|
|
bc982b7d51 | ||
|
|
b892415ca3 | ||
|
|
37a1ac2e19 | ||
|
|
0b096ce2e7 |
+10
-17
@@ -1,17 +1,10 @@
|
|||||||
/.gitattributes export-ignore
|
# Path-based git attributes
|
||||||
/.gitignore export-ignore
|
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
|
||||||
/.yamllint.yaml export-ignore
|
|
||||||
/composer-require-checker.json export-ignore
|
# Ignore all test and documentation with "export-ignore".
|
||||||
/composer.lock export-ignore
|
/.gitignore export-ignore
|
||||||
/phpmd.xml.dist export-ignore
|
/.gitattributes export-ignore
|
||||||
/phpunit.xml.dist export-ignore
|
/.travis.yml export-ignore
|
||||||
/Makefile export-ignore
|
/composer.lock export-ignore
|
||||||
/phive.xml export-ignore
|
/phpunit.xml.dist export-ignore
|
||||||
/phpcs.xml.dist export-ignore
|
/tests export-ignore
|
||||||
/phpstan.neon export-ignore
|
|
||||||
/phpstan-baseline.neon export-ignore
|
|
||||||
/psalm.xml export-ignore
|
|
||||||
/phpdoc.dist.xml export-ignore
|
|
||||||
/tests/ export-ignore
|
|
||||||
/docs/ export-ignore
|
|
||||||
/.github/ export-ignore
|
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "composer"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
open-pull-requests-limit: 10
|
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# https://docs.github.com/en/actions
|
|
||||||
|
|
||||||
name: "Documentation"
|
|
||||||
|
|
||||||
on: # yamllint disable-line rule:truthy
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- "6.x"
|
|
||||||
workflow_dispatch: null
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
run:
|
|
||||||
name: "Documentation"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/documentation.yml@main"
|
|
||||||
with:
|
|
||||||
deploy: true
|
|
||||||
component: "reflection-docblock"
|
|
||||||
secrets:
|
|
||||||
token: "${{ secrets.BOT_TOKEN }}"
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# https://docs.github.com/en/actions
|
|
||||||
|
|
||||||
name: "Integrate"
|
|
||||||
|
|
||||||
on: # yamllint disable-line rule:truthy
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- "6.x"
|
|
||||||
pull_request: null
|
|
||||||
# Allow manually triggering the workflow.
|
|
||||||
workflow_dispatch: null
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
code-coverage:
|
|
||||||
name: "Code Coverage"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/[email protected]"
|
|
||||||
with:
|
|
||||||
composer-root-version: "6.x-dev"
|
|
||||||
|
|
||||||
coding-standards:
|
|
||||||
name: "Coding Standards"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/[email protected]"
|
|
||||||
with:
|
|
||||||
composer-root-version: "6.x-dev"
|
|
||||||
|
|
||||||
dependency-analysis:
|
|
||||||
name: "Dependency analysis"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/[email protected]"
|
|
||||||
with:
|
|
||||||
composer-root-version: "6.x-dev"
|
|
||||||
|
|
||||||
lint-root:
|
|
||||||
name: "Lint root"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/[email protected]"
|
|
||||||
with:
|
|
||||||
composer-options: "--no-check-publish --ansi"
|
|
||||||
|
|
||||||
static-analysis:
|
|
||||||
name: "Static analysis"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/[email protected]"
|
|
||||||
with:
|
|
||||||
php-extensions: "none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, pcntl, posix"
|
|
||||||
composer-root-version: "6.x-dev"
|
|
||||||
|
|
||||||
unit-tests:
|
|
||||||
name: "Unit test"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/[email protected]"
|
|
||||||
with:
|
|
||||||
composer-root-version: "6.x-dev"
|
|
||||||
upcoming-releases: true
|
|
||||||
|
|
||||||
integration-tests:
|
|
||||||
name: "Integration test"
|
|
||||||
uses: "phpDocumentor/.github/.github/workflows/[email protected]"
|
|
||||||
with:
|
|
||||||
composer-root-version: "6.x-dev"
|
|
||||||
upcoming-releases: true
|
|
||||||
test-suite: "integration"
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
name: Unit Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "*"
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
php-tests:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
env:
|
||||||
|
COMPOSER_NO_INTERACTION: 1
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php: [8.4, 8.3, 8.2, 8.1, 8.0, 7.4, 7.3, 7.2]
|
||||||
|
dependency-version: [prefer-lowest, prefer-stable]
|
||||||
|
|
||||||
|
name: P${{ matrix.php }} - ${{ matrix.dependency-version }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php }}
|
||||||
|
coverage: none
|
||||||
|
tools: composer:v2
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress
|
||||||
|
|
||||||
|
- name: Execute Unit Tests
|
||||||
|
run: vendor/bin/phpunit
|
||||||
+2
-18
@@ -1,20 +1,4 @@
|
|||||||
# IDE Shizzle; it is recommended to use a global .gitignore for this but since this is an OSS project we want to make
|
|
||||||
# it easy to contribute
|
|
||||||
.idea
|
.idea
|
||||||
/nbproject/private/
|
|
||||||
.buildpath
|
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
|
|
||||||
# Build folder and vendor folder are generated code; no need to version this
|
|
||||||
build/
|
|
||||||
temp/
|
|
||||||
tools/
|
|
||||||
vendor/
|
|
||||||
*.phar
|
|
||||||
|
|
||||||
# By default the phpunit.xml.dist is provided; you can override this using a local config file
|
|
||||||
phpunit.xml
|
|
||||||
.phpunit.result.cache
|
.phpunit.result.cache
|
||||||
|
composer.lock
|
||||||
.phpdoc
|
vendor
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
extends: "default"
|
|
||||||
|
|
||||||
ignore: |
|
|
||||||
.build/
|
|
||||||
.notes/
|
|
||||||
vendor/
|
|
||||||
rules:
|
|
||||||
braces:
|
|
||||||
max-spaces-inside-empty: 0
|
|
||||||
max-spaces-inside: 1
|
|
||||||
min-spaces-inside-empty: 0
|
|
||||||
min-spaces-inside: 1
|
|
||||||
brackets:
|
|
||||||
max-spaces-inside-empty: 0
|
|
||||||
max-spaces-inside: 0
|
|
||||||
min-spaces-inside-empty: 0
|
|
||||||
min-spaces-inside: 0
|
|
||||||
colons:
|
|
||||||
max-spaces-after: 1
|
|
||||||
max-spaces-before: 0
|
|
||||||
commas:
|
|
||||||
max-spaces-after: 1
|
|
||||||
max-spaces-before: 0
|
|
||||||
min-spaces-after: 1
|
|
||||||
comments:
|
|
||||||
ignore-shebangs: true
|
|
||||||
min-spaces-from-content: 1
|
|
||||||
require-starting-space: true
|
|
||||||
comments-indentation: "enable"
|
|
||||||
document-end:
|
|
||||||
present: false
|
|
||||||
document-start:
|
|
||||||
present: false
|
|
||||||
indentation:
|
|
||||||
check-multi-line-strings: false
|
|
||||||
indent-sequences: true
|
|
||||||
spaces: 2
|
|
||||||
empty-lines:
|
|
||||||
max-end: 0
|
|
||||||
max-start: 0
|
|
||||||
max: 1
|
|
||||||
empty-values:
|
|
||||||
forbid-in-block-mappings: true
|
|
||||||
forbid-in-flow-mappings: true
|
|
||||||
hyphens:
|
|
||||||
max-spaces-after: 2
|
|
||||||
key-duplicates: "enable"
|
|
||||||
key-ordering: "disable"
|
|
||||||
line-length: "disable"
|
|
||||||
new-line-at-end-of-file: "enable"
|
|
||||||
new-lines:
|
|
||||||
type: "unix"
|
|
||||||
octal-values:
|
|
||||||
forbid-implicit-octal: true
|
|
||||||
quoted-strings:
|
|
||||||
quote-type: "double"
|
|
||||||
trailing-spaces: "enable"
|
|
||||||
truthy:
|
|
||||||
allowed-values:
|
|
||||||
- "false"
|
|
||||||
- "true"
|
|
||||||
|
|
||||||
yaml-files:
|
|
||||||
- "*.yaml"
|
|
||||||
- "*.yml"
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
.PHONY: help
|
|
||||||
help: ## Displays this list of targets with descriptions
|
|
||||||
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}'
|
|
||||||
|
|
||||||
.PHONY: code-style
|
|
||||||
code-style:
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest -d memory_limit=1024M -s
|
|
||||||
|
|
||||||
.PHONY: fix-code-style
|
|
||||||
fix-code-style:
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest phpcbf
|
|
||||||
|
|
||||||
.PHONY: static-code-analysis
|
|
||||||
static-code-analysis: vendor ## Runs a static code analysis with phpstan/phpstan and vimeo/psalm
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpstan --configuration=phpstan.neon --memory-limit=1024M
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/psalm.phar
|
|
||||||
|
|
||||||
.PHONY: test
|
|
||||||
test: test-unit ## Runs all test suites with phpunit/phpunit
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit
|
|
||||||
|
|
||||||
.PHONY: test-unit
|
|
||||||
test-unit: ## Runs unit tests with phpunit/phpunit
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit --testsuite=unit
|
|
||||||
|
|
||||||
.PHONY: dependency-analysis
|
|
||||||
dependency-analysis: vendor ## Runs a dependency analysis with maglnet/composer-require-checker
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 .phive/composer-require-checker check --config-file=/opt/project/composer-require-checker.json
|
|
||||||
|
|
||||||
vendor: composer.json composer.lock
|
|
||||||
composer validate --no-check-publish
|
|
||||||
composer install --no-interaction --no-progress
|
|
||||||
|
|
||||||
.PHONY: benchmark
|
|
||||||
benchmark:
|
|
||||||
docker run -it --rm -v${CURDIR}:/opt/project -w /opt/project php:7.4-cli tools/phpbench run
|
|
||||||
|
|
||||||
.PHONY: rector
|
|
||||||
rector: ## Refactor code using rector
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/rector process
|
|
||||||
|
|
||||||
.PHONY: pre-commit-test
|
|
||||||
pre-commit-test: fix-code-style test code-style static-code-analysis
|
|
||||||
|
|
||||||
.PHONY: docs
|
|
||||||
docs: ## Generate documentation with phpDocumentor
|
|
||||||
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpdoc:3
|
|
||||||
@@ -1,12 +1,8 @@
|
|||||||
[](https://opensource.org/licenses/MIT)
|
The ReflectionDocBlock Component
|
||||||
[](https://github.com/phpDocumentor/ReflectionDocBlock/actions/workflows/integrate.yaml)
|
================================
|
||||||
[](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionDocBlock/?branch=master)
|
|
||||||
[](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionDocBlock/?branch=master)
|
|
||||||
[](https://packagist.org/packages/phpdocumentor/reflection-docblock)
|
|
||||||
[](https://packagist.org/packages/phpdocumentor/reflection-docblock)
|
|
||||||
|
|
||||||
ReflectionDocBlock
|
> This is a fork of [phpDocumentor/ReflectionDocBlock 2.x](https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/2.x) combined with bits of [phpDocumentor/TypeResolver](https://github.com/phpDocumentor/TypeResolver) and various tweaks. The main reason for this fork is to add functionality for https://github.com/barryvdh/laravel-ide-helper
|
||||||
==================
|
> Any other use of this library is discouraged. You are probably better of using https://github.com/phpDocumentor/ReflectionDocBlock directly.
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
@@ -17,58 +13,48 @@ that is 100% compatible with the [PHPDoc standard](http://phpdoc.org/docs/latest
|
|||||||
With this component, a library can provide support for annotations via DocBlocks
|
With this component, a library can provide support for annotations via DocBlocks
|
||||||
or otherwise retrieve information that is embedded in a DocBlock.
|
or otherwise retrieve information that is embedded in a DocBlock.
|
||||||
|
|
||||||
|
> **Note**: *this is a core component of phpDocumentor and is constantly being
|
||||||
|
> optimized for performance.*
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
------------
|
------------
|
||||||
|
|
||||||
```bash
|
You can install the component in the following ways:
|
||||||
composer require phpdocumentor/reflection-docblock
|
|
||||||
```
|
* Use the official Github repository (https://github.com/phpDocumentor/ReflectionDocBlock)
|
||||||
|
* Via Composer (http://packagist.org/packages/phpdocumentor/reflection-docblock)
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
-----
|
-----
|
||||||
|
|
||||||
In order to parse the DocBlock one needs a DocBlockFactory that can be
|
The ReflectionDocBlock component is designed to work in an identical fashion to
|
||||||
instantiated using its `createInstance` factory method like this:
|
PHP's own Reflection extension (http://php.net/manual/en/book.reflection.php).
|
||||||
|
|
||||||
```php
|
Parsing can be initiated by instantiating the
|
||||||
$factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
|
`\phpDocumentor\Reflection\DocBlock()` class and passing it a string containing
|
||||||
```
|
a DocBlock (including asterisks) or by passing an object supporting the
|
||||||
|
`getDocComment()` method.
|
||||||
|
|
||||||
Then we can use the `create` method of the factory to interpret the DocBlock.
|
> *Examples of objects having the `getDocComment()` method are the
|
||||||
Please note that it is also possible to provide a class that has the
|
> `ReflectionClass` and the `ReflectionMethod` classes of the PHP
|
||||||
`getDocComment()` method, such as an object of type `ReflectionClass`, the
|
> Reflection extension*
|
||||||
create method will read that if it exists.
|
|
||||||
|
|
||||||
```php
|
Example:
|
||||||
$docComment = <<<DOCCOMMENT
|
|
||||||
/**
|
|
||||||
* This is an example of a summary.
|
|
||||||
*
|
|
||||||
* This is a Description. A Summary and Description are separated by either
|
|
||||||
* two subsequent newlines (thus a whiteline in between as can be seen in this
|
|
||||||
* example), or when the Summary ends with a dot (`.`) and some form of
|
|
||||||
* whitespace.
|
|
||||||
*/
|
|
||||||
DOCCOMMENT;
|
|
||||||
|
|
||||||
$docblock = $factory->create($docComment);
|
$class = new ReflectionClass('MyClass');
|
||||||
```
|
$phpdoc = new \phpDocumentor\Reflection\DocBlock($class);
|
||||||
|
|
||||||
The `create` method will yield an object of type `\phpDocumentor\Reflection\DocBlock`
|
or
|
||||||
whose methods can be queried:
|
|
||||||
|
|
||||||
```php
|
$docblock = <<<DOCBLOCK
|
||||||
// Contains the summary for this DocBlock
|
/**
|
||||||
$summary = $docblock->getSummary();
|
* This is a short description.
|
||||||
|
*
|
||||||
|
* This is a *long* description.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
DOCBLOCK;
|
||||||
|
|
||||||
// Contains \phpDocumentor\Reflection\DocBlock\Description object
|
$phpdoc = new \phpDocumentor\Reflection\DocBlock($docblock);
|
||||||
$description = $docblock->getDescription();
|
|
||||||
|
|
||||||
// You can either cast it to string
|
|
||||||
$description = (string) $docblock->getDescription();
|
|
||||||
|
|
||||||
// Or use the render method to get a string representation of the Description.
|
|
||||||
$description = $docblock->getDescription()->render();
|
|
||||||
```
|
|
||||||
|
|
||||||
> For more examples it would be best to review the scripts in the [`/examples` folder](/examples).
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"symbol-whitelist" : [
|
|
||||||
"null", "true", "false",
|
|
||||||
"static", "self", "parent",
|
|
||||||
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "XSLTProcessor",
|
|
||||||
"PHPStan\\PhpDocParser\\ParserConfig"
|
|
||||||
],
|
|
||||||
"php-core-extensions" : [
|
|
||||||
"Core",
|
|
||||||
"pcre",
|
|
||||||
"Reflection",
|
|
||||||
"tokenizer",
|
|
||||||
"SPL",
|
|
||||||
"standard"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
+11
-43
@@ -1,58 +1,26 @@
|
|||||||
{
|
{
|
||||||
"name": "phpdocumentor/reflection-docblock",
|
"name": "barryvdh/reflection-docblock",
|
||||||
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
|
"type": "library",
|
||||||
"type": "library",
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{"name": "Mike van Riel", "email": "[email protected]"}
|
||||||
"name": "Mike van Riel",
|
|
||||||
"email": "[email protected]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Jaap van Otterdijk",
|
|
||||||
"email": "[email protected]"
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.4 || ^8.0",
|
"php": ">=7.1"
|
||||||
"phpdocumentor/type-resolver": "^2.0",
|
|
||||||
"webmozart/assert": "^1.9.1 || ^2",
|
|
||||||
"phpdocumentor/reflection-common": "^2.2",
|
|
||||||
"ext-filter": "*",
|
|
||||||
"phpstan/phpdoc-parser": "^2.0",
|
|
||||||
"doctrine/deprecations": "^1.1"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"mockery/mockery": "~1.3.5 || ~1.6.0",
|
|
||||||
"phpunit/phpunit": "^9.5",
|
|
||||||
"phpstan/phpstan": "^1.8",
|
|
||||||
"phpstan/phpstan-mockery": "^1.1",
|
|
||||||
"phpstan/extension-installer": "^1.1",
|
|
||||||
"phpstan/phpstan-webmozart-assert": "^1.2",
|
|
||||||
"psalm/phar": "^5.26",
|
|
||||||
"shipmonk/dead-code-detector": "^0.5.1"
|
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-0": {"Barryvdh": ["src/"]}
|
||||||
"phpDocumentor\\Reflection\\": "src"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"autoload-dev": {
|
"require-dev": {
|
||||||
"psr-4": {
|
"phpunit/phpunit": "^8.5.14|^9"
|
||||||
"phpDocumentor\\Reflection\\": ["tests/unit", "tests/integration"]
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"config": {
|
"suggest": {
|
||||||
"platform": {
|
"dflydev/markdown": "~1.0",
|
||||||
"php":"7.4.0"
|
"erusev/parsedown": "~1.0"
|
||||||
},
|
|
||||||
"allow-plugins": {
|
|
||||||
"phpstan/extension-installer": true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "5.x-dev"
|
"dev-master": "2.3.x-dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
-2527
File diff suppressed because it is too large
Load Diff
@@ -1,12 +0,0 @@
|
|||||||
Contributing
|
|
||||||
============
|
|
||||||
|
|
||||||
Contributions are welcome! If you would like to contribute to ReflectionDocBlock, please follow these guidelines:
|
|
||||||
|
|
||||||
- Fork the repository and create your branch from ``main``.
|
|
||||||
- Ensure your code follows the project's coding standards.
|
|
||||||
- Write tests for your changes.
|
|
||||||
- Submit a pull request with a clear description of your changes.
|
|
||||||
|
|
||||||
For questions or discussions, please open an issue on GitHub.
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once(__DIR__ . '/../../vendor/autoload.php');
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
|
||||||
|
|
||||||
$docComment = <<<DOCCOMMENT
|
|
||||||
/**
|
|
||||||
* This is an example of a summary.
|
|
||||||
*
|
|
||||||
* This is a Description. A Summary and Description are separated by either
|
|
||||||
* two subsequent newlines (thus a whiteline in between as can be seen in this
|
|
||||||
* example), or when the Summary ends with a dot (`.`) and some form of
|
|
||||||
* whitespace.
|
|
||||||
*/
|
|
||||||
DOCCOMMENT;
|
|
||||||
|
|
||||||
$factory = DocBlockFactory::createInstance();
|
|
||||||
$docblock = $factory->create($docComment);
|
|
||||||
|
|
||||||
// Should contain the first line of the DocBlock
|
|
||||||
$summary = $docblock->getSummary();
|
|
||||||
|
|
||||||
// Contains an object of type Description; you can either cast it to string or use
|
|
||||||
// the render method to get a string representation of the Description.
|
|
||||||
//
|
|
||||||
// In subsequent examples we will be fiddling a bit more with the Description.
|
|
||||||
$description = $docblock->getDescription();
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once(__DIR__ . '/../../vendor/autoload.php');
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
|
||||||
|
|
||||||
$docComment = <<<DOCCOMMENT
|
|
||||||
/**
|
|
||||||
* This is an example of a summary.
|
|
||||||
*
|
|
||||||
* @see \phpDocumentor\Reflection\DocBlock\StandardTagFactory
|
|
||||||
*/
|
|
||||||
DOCCOMMENT;
|
|
||||||
|
|
||||||
$factory = DocBlockFactory::createInstance();
|
|
||||||
$docblock = $factory->create($docComment);
|
|
||||||
|
|
||||||
// You can check if a DocBlock has one or more see tags
|
|
||||||
$hasSeeTag = $docblock->hasTag('see');
|
|
||||||
|
|
||||||
// Or we can get a complete list of all tags
|
|
||||||
$tags = $docblock->getTags();
|
|
||||||
|
|
||||||
// But we can also grab all tags of a specific type, such as `see`
|
|
||||||
$seeTags = $docblock->getTagsByName('see');
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
require_once(__DIR__ . '/../../vendor/autoload.php');
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Serializer;
|
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
|
||||||
|
|
||||||
$docComment = <<<DOCCOMMENT
|
|
||||||
/**
|
|
||||||
* This is an example of a summary.
|
|
||||||
*
|
|
||||||
* And here is an example of the description
|
|
||||||
* of a DocBlock that can span multiple lines.
|
|
||||||
*
|
|
||||||
* @see \phpDocumentor\Reflection\DocBlock\StandardTagFactory
|
|
||||||
*/
|
|
||||||
DOCCOMMENT;
|
|
||||||
|
|
||||||
$factory = DocBlockFactory::createInstance();
|
|
||||||
$docblock = $factory->create($docComment);
|
|
||||||
|
|
||||||
// Create the serializer that will reconstitute the DocBlock back to its original form.
|
|
||||||
$serializer = new Serializer(0, '', true, null, null, PHP_EOL);
|
|
||||||
|
|
||||||
// Reconstitution is performed by the `getDocComment()` method.
|
|
||||||
$reconstitutedDocComment = $serializer->getDocComment($docblock);
|
|
||||||
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* In this example we demonstrate how you can add your own Tag using a Static Factory method in your Tag class.
|
|
||||||
*/
|
|
||||||
|
|
||||||
require_once(__DIR__ . '/../../vendor/autoload.php');
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Serializer;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\BaseTag;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An example of a custom tag called `my-tag` with an optional description.
|
|
||||||
*
|
|
||||||
* A Custom Tag is a class that can consist of two parts:
|
|
||||||
*
|
|
||||||
* 1. a method `create` that is a static factory for this class.
|
|
||||||
* 2. methods and properties that have this object act as an immutable Value Object representing a Tag instance.
|
|
||||||
*
|
|
||||||
* The static factory `create` is used to convert a tag line (without the tag name) into an instance of the
|
|
||||||
* same tag object with the right constructor parameters set. This method has a dynamic list of parameters so that you
|
|
||||||
* can inject various dependencies, see the method's DocBlock for more information.
|
|
||||||
*
|
|
||||||
* An object of this class, and its methods and properties, represent a single instance of that tag in your
|
|
||||||
* documentation in the form of a Value Object whose properties should not be changed after instantiation (it should be
|
|
||||||
* immutable).
|
|
||||||
*
|
|
||||||
* > Important: Tag classes that act as Factories using the `create` method should implement the Tag interface.
|
|
||||||
* > Instead, you could extend the abstract class BaseTag that already implements the Tag interface
|
|
||||||
*/
|
|
||||||
final class MyTag extends BaseTag
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* A required property that is used by Formatters to reconstitute the complete tag line.
|
|
||||||
*
|
|
||||||
* @see Formatter
|
|
||||||
*
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected string $name = 'my-tag';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The constructor for this Tag; this should contain all properties for this object.
|
|
||||||
*
|
|
||||||
* @param Description $description An example of how to add a Description to the tag; the Description is often
|
|
||||||
* an optional variable so passing null is allowed in this instance (though you can
|
|
||||||
* also construct an empty description object).
|
|
||||||
*
|
|
||||||
* @see BaseTag for the declaration of the description property and getDescription method.
|
|
||||||
*/
|
|
||||||
public function __construct(Description $description = null)
|
|
||||||
{
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A static Factory that creates a new instance of the current Tag.
|
|
||||||
*
|
|
||||||
* In this example the MyTag tag can be created by passing a description text as $body. Because we have added
|
|
||||||
* a $descriptionFactory that is type-hinted as DescriptionFactory we can now construct a new Description object
|
|
||||||
* and pass that to the constructor.
|
|
||||||
*
|
|
||||||
* > You could directly instantiate a Description object here but that won't be parsed for inline tags and Types
|
|
||||||
* > won't be resolved. The DescriptionFactory will take care of those actions.
|
|
||||||
*
|
|
||||||
* The `create` method's interface states that this method only features a single parameter (`$body`) but the
|
|
||||||
* {@see TagFactory} will read the signature of this method and if it has more parameters then it will try
|
|
||||||
* to find declarations for it in the ServiceLocator of the TagFactory (see {@see TagFactory::$serviceLocator}).
|
|
||||||
*
|
|
||||||
* > Important: all properties following the `$body` should default to `null`, otherwise PHP will error because
|
|
||||||
* > it no longer matches the interface. This is why you often see the default tags check that an optional argument
|
|
||||||
* > is not null nonetheless.
|
|
||||||
*
|
|
||||||
* @param string $body
|
|
||||||
* @param DescriptionFactory $descriptionFactory
|
|
||||||
* @param Context|null $context The Context is used to resolve Types and FQSENs, although optional
|
|
||||||
* it is highly recommended to pass it. If you omit it then it is assumed that
|
|
||||||
* the DocBlock is in the global namespace and has no `use` statements.
|
|
||||||
*
|
|
||||||
* @see Tag for the interface declaration of the `create` method.
|
|
||||||
* @see Tag::create() for more information on this method's workings.
|
|
||||||
*/
|
|
||||||
public static function create(string $body, DescriptionFactory $descriptionFactory = null, Context $context = null): self
|
|
||||||
{
|
|
||||||
Assert::notNull($descriptionFactory);
|
|
||||||
|
|
||||||
return new static($descriptionFactory->create($body, $context));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a rendition of the original tag line.
|
|
||||||
*
|
|
||||||
* This method is used to reconstitute a DocBlock into its original form by the {@see Serializer}. It should
|
|
||||||
* feature all parts of the tag so that the serializer can put it back together.
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
return (string)$this->description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$docComment = <<<DOCCOMMENT
|
|
||||||
/**
|
|
||||||
* This is an example of a summary.
|
|
||||||
*
|
|
||||||
* @my-tag I have a description
|
|
||||||
*/
|
|
||||||
DOCCOMMENT;
|
|
||||||
|
|
||||||
// Make a mapping between the tag name `my-tag` and the Tag class containing the Factory Method `create`.
|
|
||||||
$customTags = ['my-tag' => MyTag::class];
|
|
||||||
|
|
||||||
// Do pass the list of custom tags to the Factory for the DocBlockFactory.
|
|
||||||
$factory = DocBlockFactory::createInstance($customTags);
|
|
||||||
// You can also add Tags later using `$factory->registerTagHandler()` with a tag name and Tag class name.
|
|
||||||
|
|
||||||
// Create the DocBlock
|
|
||||||
$docblock = $factory->create($docComment);
|
|
||||||
|
|
||||||
// Take a look: the $customTagObjects now contain an array with your newly added tag
|
|
||||||
$customTagObjects = $docblock->getTagsByName('my-tag');
|
|
||||||
|
|
||||||
// As an experiment: let's reconstitute the DocBlock and observe that because we added a __toString() method
|
|
||||||
// to the tag class that we can now also see it.
|
|
||||||
$serializer = new Serializer(0, '',true, null, null, PHP_EOL);
|
|
||||||
$reconstitutedDocComment = $serializer->getDocComment($docblock);
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
require_once(__DIR__ . '/../../../vendor/autoload.php');
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlockFactory;
|
|
||||||
|
|
||||||
$docComment = <<<DOCCOMMENT
|
|
||||||
/**
|
|
||||||
* This is an example of a summary.
|
|
||||||
*
|
|
||||||
* You can escape the @-sign by surrounding it with braces, for example: {@}. And escape a closing brace within an
|
|
||||||
* inline tag by adding an opening brace in front of it like this: {}.
|
|
||||||
*
|
|
||||||
* Here are example texts where you can see how they could be used in a real life situation:
|
|
||||||
*
|
|
||||||
* This is a text with an {@internal inline tag where a closing brace ({}) is shown}.
|
|
||||||
* Or an {@internal inline tag with a literal {{@}link{} in it}.
|
|
||||||
*
|
|
||||||
* Do note that an {@internal inline tag that has an opening brace ({) does not break out}.
|
|
||||||
*/
|
|
||||||
DOCCOMMENT;
|
|
||||||
|
|
||||||
$factory = DocBlockFactory::createInstance();
|
|
||||||
$docblock = $factory->create($docComment);
|
|
||||||
|
|
||||||
// Escaping is automatic so this happens in the DescriptionFactory.
|
|
||||||
$description = $docblock->getDescription();
|
|
||||||
|
|
||||||
// This is the rendition that we will receive of the Description.
|
|
||||||
$receivedDocComment = <<<DOCCOMMENT
|
|
||||||
/**
|
|
||||||
* This is an example of a summary.
|
|
||||||
*
|
|
||||||
* You can escape the @-sign by surrounding it with braces, for example: {@}. And escape a closing brace within an
|
|
||||||
* inline tag by adding an opening brace in front of it like this: {}.
|
|
||||||
*
|
|
||||||
* Here are example texts where you can see how they could be used in a real life situation:
|
|
||||||
*
|
|
||||||
* This is a text with an {@internal inline tag where a closing brace ({}) is shown}.
|
|
||||||
* Or an {@internal inline tag with a literal {{@}link{} in it}.
|
|
||||||
*
|
|
||||||
* Do note that an {@internal inline tag that has an opening brace ({) does not break out}.
|
|
||||||
*/
|
|
||||||
DOCCOMMENT;
|
|
||||||
|
|
||||||
// Render it using the default PassthroughFormatter
|
|
||||||
$foundDescription = $description->render();
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Add Your Own Tag
|
|
||||||
=======================
|
|
||||||
|
|
||||||
This guide demonstrates how to add your own custom tag to a DocBlock using ReflectionDocBlock.
|
|
||||||
|
|
||||||
.. literalinclude:: ../examples/04-adding-your-own-tag.php
|
|
||||||
:language: php
|
|
||||||
:linenos:
|
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
How-to
|
|
||||||
=============
|
|
||||||
|
|
||||||
Practical guides for common tasks with ReflectionDocBlock:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 1
|
|
||||||
|
|
||||||
interpreting-a-simple-docblock
|
|
||||||
interpreting-tags
|
|
||||||
reconstituting-a-docblock
|
|
||||||
adding-your-own-tag
|
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Interpret a Simple DocBlock
|
|
||||||
==================================
|
|
||||||
|
|
||||||
This guide demonstrates how to parse a simple DocBlock and extract its summary and description using ReflectionDocBlock.
|
|
||||||
|
|
||||||
.. literalinclude:: ../examples/01-interpreting-a-simple-docblock.php
|
|
||||||
:language: php
|
|
||||||
:linenos:
|
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Interpret Tags in a DocBlock
|
|
||||||
===================================
|
|
||||||
|
|
||||||
This guide demonstrates how to interpret tags within a DocBlock using ReflectionDocBlock.
|
|
||||||
|
|
||||||
.. literalinclude:: ../examples/02-interpreting-tags.php
|
|
||||||
:language: php
|
|
||||||
:linenos:
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
Reconstituting a DocBlock
|
|
||||||
=========================
|
|
||||||
|
|
||||||
ReflectionDocBlock not only allows you to read and parse DocBlocks, but also to reconstruct them. This is useful if you need to add, remove, or modify tags in your codebase programmatically. For example, you might want to update type information, add custom tags, or strip deprecated tags as part of a refactoring or code generation process.
|
|
||||||
|
|
||||||
Below is a practical example of how to reconstitute a DocBlock using this library:
|
|
||||||
|
|
||||||
.. literalinclude:: ../examples/03-reconstituting-a-docblock.php
|
|
||||||
:language: php
|
|
||||||
:caption: examples/03-reconstituting-a-docblock.php
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
ReflectionDocBlock Documentation
|
|
||||||
===========================================
|
|
||||||
|
|
||||||
ReflectionDocBlock is a PHP library that provides a DocBlock parser fully compatible with the PHPDoc standard. It allows you to parse, interpret, and extract information from DocBlocks in your PHP code, enabling support for annotations and metadata extraction.
|
|
||||||
|
|
||||||
Key Features and Use Cases
|
|
||||||
--------------------------
|
|
||||||
- **Documentation Generation**: Used as a core component in tools like phpDocumentor to generate API documentation from your code's DocBlocks.
|
|
||||||
- **Type and Metadata Extraction**: Integrations and tools use this library to gather type information and other metadata, enabling advanced features such as static analysis, code introspection, and automated serialization.
|
|
||||||
- **Serializer Support**: Helps serializers and similar tools to interpret type information in array and object structures, making it easier to transform nested objects correctly.
|
|
||||||
- **DocBlock Reconstitution**: Not only can you read DocBlocks, but you can also reconstruct them. This is useful for adding, removing, or modifying tags in your codebase programmatically.
|
|
||||||
- **Standalone or Integrated**: Designed for standalone use, but also serves as a key component of the phpDocumentor suite.
|
|
||||||
|
|
||||||
Unique Advantages
|
|
||||||
-----------------
|
|
||||||
- **Simple, Intuitive API**: Focus on ease of use, so you can work with DocBlocks without needing to understand the complexities of parsing.
|
|
||||||
- **Widely Adopted**: Used by over 1000 packages on Packagist, making it a proven and reliable choice for PHP developers.
|
|
||||||
- **Actively Maintained**: Supports PHP 7.4 and 8+, and is maintained by the phpDocumentor team and contributors.
|
|
||||||
|
|
||||||
Quick Start Example
|
|
||||||
-------------------
|
|
||||||
Here's a minimal example of how to use ReflectionDocBlock in your project:
|
|
||||||
|
|
||||||
.. literalinclude:: examples/01-interpreting-a-simple-docblock.php
|
|
||||||
:language: php
|
|
||||||
:caption: examples/01-interpreting-a-simple-docblock.php
|
|
||||||
|
|
||||||
For more detailed usage and how-to guides, see the ``how-to/`` section.
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
:hidden:
|
|
||||||
|
|
||||||
installation
|
|
||||||
how-to/index
|
|
||||||
upgrade-to-v6
|
|
||||||
contributing
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
To install ReflectionDocBlock, use Composer:
|
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
composer require phpdocumentor/reflection-docblock
|
|
||||||
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
Upgrade Guide to v6
|
|
||||||
===================
|
|
||||||
|
|
||||||
This guide helps you upgrade your project to ReflectionDocBlock v6. It covers breaking changes, removals, new features, and migration tips to ensure a smooth transition.
|
|
||||||
|
|
||||||
Supported PHP Versions
|
|
||||||
----------------------
|
|
||||||
- v6 requires PHP 7.4 or higher (PHP 8+ recommended).
|
|
||||||
|
|
||||||
Breaking Changes & Removals
|
|
||||||
---------------------------
|
|
||||||
- **Removal of `::create` static method for type-based tags**
|
|
||||||
- The `create` static method has been removed from tag classes that represent type definitions, such as `@param` and `@return` tags. Most users will not be affected, as these methods are rarely used directly. The deprecation notice for these methods was present throughout v5.
|
|
||||||
- **Migration:**
|
|
||||||
- If you are instantiating these tag objects directly, use the tag factory or the recommended construction pattern instead.
|
|
||||||
- Before:
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$tag = Param::create($body);
|
|
||||||
- After:
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$factory = \phpDocumentor\Reflection\DocBlock\Tags\Factory\StandardTagFactory::createInstance();
|
|
||||||
$tag = $factory->create('@param int $foo');
|
|
||||||
|
|
||||||
- **StandardTagFactory instantiation**
|
|
||||||
- `StandardTagFactory` must now be created via `createInstance()`.
|
|
||||||
- **Migration:**
|
|
||||||
- Before:
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$factory = new StandardTagFactory();
|
|
||||||
- After:
|
|
||||||
.. code-block:: php
|
|
||||||
|
|
||||||
$factory = StandardTagFactory::createInstance();
|
|
||||||
|
|
||||||
- **Removed methods**
|
|
||||||
- `Method::getArguments` has been removed.
|
|
||||||
- `Method::create` has been removed.
|
|
||||||
- **Migration:**
|
|
||||||
- Refactor code to use the new API for method arguments and creation.
|
|
||||||
|
|
||||||
TypeResolver Upgrade
|
|
||||||
-------------------
|
|
||||||
- **Generics Support**: The TypeResolver component now supports generics,
|
|
||||||
which replaces the previous `Collection` type handling. This allows
|
|
||||||
for more accurate and expressive type definitions, such as `MyClass<int, MyClass>` or `Collection<MyClass>`,
|
|
||||||
and improves compatibility with modern PHPDoc standards.
|
|
||||||
|
|
||||||
- For more details and advanced migration scenarios, consult the `TypeResolver upgrade guide <https://docs.phpdoc.org/components/type-resolver/guides/upgrade-v1-to-v2.html#upgrade-to-version-2>`_
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<phive xmlns="https://phar.io/phive">
|
|
||||||
<phar name="phpunit" version="^9.5" installed="9.5.8" location="./tools/phpunit" copy="true"/>
|
|
||||||
</phive>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<ruleset name="ReflectionDocBlock">
|
|
||||||
<description>The coding standard for this library.</description>
|
|
||||||
|
|
||||||
<file>src</file>
|
|
||||||
<file>tests/unit</file>
|
|
||||||
<exclude-pattern>*/tests/unit/Types/ContextFactoryTest\.php</exclude-pattern>
|
|
||||||
<exclude-pattern>*/tests/unit/Assets/*</exclude-pattern>
|
|
||||||
<arg value="p"/>
|
|
||||||
|
|
||||||
<!-- Set the minimum PHP version for PHPCompatibility.
|
|
||||||
This should be kept in sync with the requirements in the composer.json file. -->
|
|
||||||
<config name="testVersion" value="7.4-"/>
|
|
||||||
|
|
||||||
<rule ref="phpDocumentor">
|
|
||||||
<exclude name="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException" />
|
|
||||||
</rule>
|
|
||||||
|
|
||||||
<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix">
|
|
||||||
<exclude-pattern>*/src/*/Abstract*\.php</exclude-pattern>
|
|
||||||
</rule>
|
|
||||||
</ruleset>
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<phpdocumentor
|
|
||||||
configVersion="3"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns="https://www.phpdoc.org"
|
|
||||||
xsi:noNamespaceSchemaLocation="data/xsd/phpdoc.xsd"
|
|
||||||
>
|
|
||||||
<title>Reflection Docblock</title>
|
|
||||||
<paths>
|
|
||||||
<output>build/docs</output>
|
|
||||||
</paths>
|
|
||||||
<version number="6.0.0">
|
|
||||||
<folder>latest</folder>
|
|
||||||
<api>
|
|
||||||
<source dsn="./">
|
|
||||||
<path>src/</path>
|
|
||||||
</source>
|
|
||||||
<output>api</output>
|
|
||||||
<ignore hidden="true" symlinks="true">
|
|
||||||
<path>tests/**/*</path>
|
|
||||||
<path>build/**/*</path>
|
|
||||||
<path>var/**/*</path>
|
|
||||||
<path>vendor/**/*</path>
|
|
||||||
</ignore>
|
|
||||||
<extensions>
|
|
||||||
<extension>php</extension>
|
|
||||||
</extensions>
|
|
||||||
<ignore-tags>
|
|
||||||
<ignore-tag>template</ignore-tag>
|
|
||||||
<ignore-tag>template-extends</ignore-tag>
|
|
||||||
<ignore-tag>template-implements</ignore-tag>
|
|
||||||
<ignore-tag>extends</ignore-tag>
|
|
||||||
<ignore-tag>implements</ignore-tag>
|
|
||||||
</ignore-tags>
|
|
||||||
<default-package-name>phpDocumentor</default-package-name>
|
|
||||||
</api>
|
|
||||||
<guide>
|
|
||||||
<source dsn=".">
|
|
||||||
<path>docs</path>
|
|
||||||
</source>
|
|
||||||
<output>guides</output>
|
|
||||||
</guide>
|
|
||||||
</version>
|
|
||||||
<setting name="guides.enabled" value="true"/>
|
|
||||||
<template name="default" />
|
|
||||||
</phpdocumentor>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
<ruleset
|
|
||||||
name="ProxyManager rules"
|
|
||||||
xmlns="http://pmd.sf.net/ruleset/1.0.0"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
|
|
||||||
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
|
|
||||||
>
|
|
||||||
<rule ref="rulesets/codesize.xml"/>
|
|
||||||
<rule ref="rulesets/unusedcode.xml"/>
|
|
||||||
<rule ref="rulesets/design.xml">
|
|
||||||
<!-- eval is needed to generate runtime classes -->
|
|
||||||
<exclude name="EvalExpression"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/naming.xml">
|
|
||||||
<exclude name="LongVariable"/>
|
|
||||||
</rule>
|
|
||||||
<rule ref="rulesets/naming.xml/LongVariable">
|
|
||||||
<properties>
|
|
||||||
<property name="minimum">40</property>
|
|
||||||
</properties>
|
|
||||||
</rule>
|
|
||||||
</ruleset>
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
parameters:
|
|
||||||
ignoreErrors:
|
|
||||||
-
|
|
||||||
message: "#^Unused phpDocumentor\\\\Reflection\\\\DocBlock\\\\ExampleFinder\\:\\:getExampleDirectories$#"
|
|
||||||
count: 1
|
|
||||||
path: src/DocBlock/ExampleFinder.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Unused phpDocumentor\\\\Reflection\\\\DocBlock\\\\ExampleFinder\\:\\:setExampleDirectories$#"
|
|
||||||
count: 1
|
|
||||||
path: src/DocBlock/ExampleFinder.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Unused phpDocumentor\\\\Reflection\\\\DocBlock\\\\ExampleFinder\\:\\:setSourceDirectory$#"
|
|
||||||
count: 1
|
|
||||||
path: src/DocBlock/ExampleFinder.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Unused phpDocumentor\\\\Reflection\\\\DocBlock\\\\Tags\\\\Factory\\\\MethodParameterFactory\\:\\:formatNull$#"
|
|
||||||
count: 1
|
|
||||||
path: src/DocBlock/Tags/Factory/MethodParameterFactory.php
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
includes:
|
|
||||||
- phpstan-baseline.neon
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
level: max
|
|
||||||
ignoreErrors:
|
|
||||||
- '#Method phpDocumentor\\Reflection\\DocBlock\\StandardTagFactory::createTag\(\) should return phpDocumentor\\Reflection\\DocBlock\\Tag but returns mixed#'
|
|
||||||
- '#Offset 2 on array\{string, 28, int\} on left side of \?\? always exists and is not nullable\.#'
|
|
||||||
-
|
|
||||||
path: src/DocBlockFactoryInterface.php
|
|
||||||
identifier: shipmonk.deadMethod
|
|
||||||
-
|
|
||||||
path: src/DocBlock/TagFactory.php
|
|
||||||
identifier: shipmonk.deadMethod
|
|
||||||
paths:
|
|
||||||
- src
|
|
||||||
- tests/unit
|
|
||||||
+3
-14
@@ -1,24 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd" colors="true" convertDeprecationsToExceptions="false" beStrictAboutOutputDuringTests="false" forceCoversAnnotation="true" verbose="true" bootstrap="vendor/autoload.php">
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" colors="true" bootstrap="vendor/autoload.php" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||||
<coverage>
|
<coverage>
|
||||||
<include>
|
<include>
|
||||||
<directory suffix=".php">./src/</directory>
|
<directory suffix=".php">./src/</directory>
|
||||||
</include>
|
</include>
|
||||||
<report>
|
|
||||||
<clover outputFile="build/logs/clover.xml"/>
|
|
||||||
<html outputDirectory="build/coverage" lowUpperBound="35" highLowerBound="70"/>
|
|
||||||
</report>
|
|
||||||
</coverage>
|
</coverage>
|
||||||
<testsuites>
|
<testsuites>
|
||||||
<testsuite name="unit">
|
<testsuite name="phpDocumentor\Reflection\DocBlock">
|
||||||
<directory>./tests/unit</directory>
|
<directory>./tests/</directory>
|
||||||
</testsuite>
|
|
||||||
<testsuite name="integration">
|
|
||||||
<directory>./tests/integration</directory>
|
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
<logging/>
|
|
||||||
<listeners>
|
|
||||||
<listener class="Mockery\Adapter\Phpunit\TestListener" file="vendor/mockery/mockery/library/Mockery/Adapter/Phpunit/TestListener.php"/>
|
|
||||||
</listeners>
|
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|||||||
@@ -1,79 +0,0 @@
|
|||||||
<?xml version="1.0"?>
|
|
||||||
<psalm
|
|
||||||
errorLevel="2"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns="https://getpsalm.org/schema/config"
|
|
||||||
xsi:schemaLocation="https://getpsalm.org/schema/config file:///composer/vendor/vimeo/psalm/config.xsd"
|
|
||||||
>
|
|
||||||
<projectFiles>
|
|
||||||
<directory name="src" />
|
|
||||||
<ignoreFiles>
|
|
||||||
<directory name="vendor" />
|
|
||||||
</ignoreFiles>
|
|
||||||
</projectFiles>
|
|
||||||
|
|
||||||
<issueHandlers>
|
|
||||||
<RedundantConditionGivenDocblockType>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<!-- Psalm is very strict and believe that because we documented a type, it is redundant to assert it -->
|
|
||||||
<file name="src/DocBlock/StandardTagFactory.php"/>
|
|
||||||
</errorLevel>
|
|
||||||
</RedundantConditionGivenDocblockType>
|
|
||||||
|
|
||||||
<PossiblyNullArrayOffset>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<!-- Psalm forbid accessing an array with a null offset but it's still working code without notice -->
|
|
||||||
<file name="src/DocBlock/StandardTagFactory.php"/>
|
|
||||||
</errorLevel>
|
|
||||||
</PossiblyNullArrayOffset>
|
|
||||||
|
|
||||||
<DeprecatedInterface>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<!-- Will be removed in 6.0.0 issues/211 -->
|
|
||||||
<referencedClass name="phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod"/>
|
|
||||||
</errorLevel>
|
|
||||||
</DeprecatedInterface>
|
|
||||||
|
|
||||||
<DeprecatedMethod>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<!-- Will be removed in 6.0.0 issues/361 -->
|
|
||||||
<referencedMethod name="phpDocumentor\Reflection\DocBlock\Tags\Param::create"/>
|
|
||||||
</errorLevel>
|
|
||||||
</DeprecatedMethod>
|
|
||||||
|
|
||||||
<NoInterfaceProperties>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<file name="src/DocBlock/Tags/Factory/ParamFactory.php"/>
|
|
||||||
<file name="src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php"/>
|
|
||||||
</errorLevel>
|
|
||||||
</NoInterfaceProperties>
|
|
||||||
|
|
||||||
<TooManyArguments>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<file name="src/DocBlock/Tags/Factory/AbstractPHPStanFactory.php"/>
|
|
||||||
</errorLevel>
|
|
||||||
</TooManyArguments>
|
|
||||||
|
|
||||||
<RedundantConditionGivenDocblockType>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<!-- Psalm manage to infer a more precise type than PHPStan. notNull assert is needed for PHPStan but
|
|
||||||
Psalm sees it as redundant -->
|
|
||||||
<directory name="src/DocBlock/Tags/"/>
|
|
||||||
</errorLevel>
|
|
||||||
</RedundantConditionGivenDocblockType>
|
|
||||||
|
|
||||||
<ArgumentTypeCoercion>
|
|
||||||
<errorLevel type="info">
|
|
||||||
<!-- PHP handles invalid preg_split flags just fine. -->
|
|
||||||
<file name="src/Utils.php"/>
|
|
||||||
</errorLevel>
|
|
||||||
</ArgumentTypeCoercion>
|
|
||||||
|
|
||||||
<InvalidArgument>
|
|
||||||
<errorLevel type="suppress">
|
|
||||||
<referencedFunction name="PHPStan\PhpDocParser\Parser\PhpDocParser::__construct"/>
|
|
||||||
<referencedFunction name="PHPStan\PhpDocParser\Parser\TypeParser::__construct"/>
|
|
||||||
</errorLevel>
|
|
||||||
</InvalidArgument>
|
|
||||||
</issueHandlers>
|
|
||||||
</psalm>
|
|
||||||
@@ -0,0 +1,503 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
use Barryvdh\Reflection\DocBlock\Context;
|
||||||
|
use Barryvdh\Reflection\DocBlock\Location;
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag\TemplateTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the DocBlock for any structure.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class DocBlock implements \Reflector
|
||||||
|
{
|
||||||
|
/** @var string The opening line for this docblock. */
|
||||||
|
protected $short_description = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var DocBlock\Description The actual
|
||||||
|
* description for this docblock.
|
||||||
|
*/
|
||||||
|
protected $long_description = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Tag[] An array containing all
|
||||||
|
* the tags in this docblock; except inline.
|
||||||
|
*/
|
||||||
|
protected $tags = array();
|
||||||
|
|
||||||
|
/** @var string[] An array containing all the generics in this docblock. */
|
||||||
|
protected $generics = array();
|
||||||
|
|
||||||
|
/** @var Context Information about the context of this DocBlock. */
|
||||||
|
protected $context = null;
|
||||||
|
|
||||||
|
/** @var Location Information about the location of this DocBlock. */
|
||||||
|
protected $location = null;
|
||||||
|
|
||||||
|
/** @var bool Is this DocBlock (the start of) a template? */
|
||||||
|
protected $isTemplateStart = false;
|
||||||
|
|
||||||
|
/** @var bool Does this DocBlock signify the end of a DocBlock template? */
|
||||||
|
protected $isTemplateEnd = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the given docblock and populates the member fields.
|
||||||
|
*
|
||||||
|
* The constructor may also receive namespace information such as the
|
||||||
|
* current namespace and aliases. This information is used by some tags
|
||||||
|
* (e.g. @return, @param, etc.) to turn a relative Type into a FQCN.
|
||||||
|
*
|
||||||
|
* @param \Reflector|string $docblock A docblock comment (including
|
||||||
|
* asterisks) or reflector supporting the getDocComment method.
|
||||||
|
* @param Context $context The context in which the DocBlock
|
||||||
|
* occurs.
|
||||||
|
* @param Location $location The location within the file that this
|
||||||
|
* DocBlock occurs in.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if the given argument does not have the
|
||||||
|
* getDocComment method.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$docblock,
|
||||||
|
?Context $context = null,
|
||||||
|
?Location $location = null
|
||||||
|
) {
|
||||||
|
if (is_object($docblock)) {
|
||||||
|
if (!method_exists($docblock, 'getDocComment')) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid object passed; the given reflector must support '
|
||||||
|
. 'the getDocComment method'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$docblock = $docblock->getDocComment();
|
||||||
|
}
|
||||||
|
|
||||||
|
$docblock = $this->cleanInput($docblock);
|
||||||
|
|
||||||
|
list($templateMarker, $short, $long, $tags) = $this->splitDocBlock($docblock);
|
||||||
|
$this->isTemplateStart = $templateMarker === '#@+';
|
||||||
|
$this->isTemplateEnd = $templateMarker === '#@-';
|
||||||
|
$this->short_description = $short;
|
||||||
|
$this->long_description = new DocBlock\Description($long, $this);
|
||||||
|
$this->parseTags($tags);
|
||||||
|
|
||||||
|
$this->context = $context;
|
||||||
|
$this->location = $location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips the asterisks from the DocBlock comment.
|
||||||
|
*
|
||||||
|
* @param string $comment String containing the comment text.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function cleanInput($comment)
|
||||||
|
{
|
||||||
|
$comment = trim(
|
||||||
|
preg_replace(
|
||||||
|
'#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u',
|
||||||
|
'$1',
|
||||||
|
$comment
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// reg ex above is not able to remove */ from a single line docblock
|
||||||
|
if (substr($comment, -2) == '*/') {
|
||||||
|
$comment = trim(substr($comment, 0, -2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalize strings
|
||||||
|
$comment = str_replace(array("\r\n", "\r"), "\n", $comment);
|
||||||
|
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Splits the DocBlock into a template marker, summary, description and block of tags.
|
||||||
|
*
|
||||||
|
* @param string $comment Comment to split into the sub-parts.
|
||||||
|
*
|
||||||
|
* @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
|
||||||
|
* @author Mike van Riel <[email protected]> for extending the regex with template marker support.
|
||||||
|
*
|
||||||
|
* @return string[] containing the template marker (if any), summary, description and a string containing the tags.
|
||||||
|
*/
|
||||||
|
protected function splitDocBlock($comment)
|
||||||
|
{
|
||||||
|
// Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
|
||||||
|
// method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
|
||||||
|
// performance impact of running a regular expression
|
||||||
|
if (strpos($comment, '@') === 0) {
|
||||||
|
return array('', '', '', $comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clears all extra horizontal whitespace from the line endings to prevent parsing issues
|
||||||
|
$comment = preg_replace('/\h*$/Sum', '', $comment);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Splits the docblock into a template marker, short description, long description and tags section
|
||||||
|
*
|
||||||
|
* - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may
|
||||||
|
* occur after it and will be stripped).
|
||||||
|
* - The short description is started from the first character until a dot is encountered followed by a
|
||||||
|
* newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing
|
||||||
|
* errors). This is optional.
|
||||||
|
* - The long description, any character until a new line is encountered followed by an @ and word
|
||||||
|
* characters (a tag). This is optional.
|
||||||
|
* - Tags; the remaining characters
|
||||||
|
*
|
||||||
|
* Big thanks to RichardJ for contributing this Regular Expression
|
||||||
|
*/
|
||||||
|
preg_match(
|
||||||
|
'/
|
||||||
|
\A
|
||||||
|
# 1. Extract the template marker
|
||||||
|
(?:(\#\@\+|\#\@\-)\n?)?
|
||||||
|
|
||||||
|
# 2. Extract the summary
|
||||||
|
(?:
|
||||||
|
(?! @\pL ) # The summary may not start with an @
|
||||||
|
(
|
||||||
|
[^\n.]+
|
||||||
|
(?:
|
||||||
|
(?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines
|
||||||
|
[\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
|
||||||
|
[^\n.]+ # Include anything else
|
||||||
|
)*
|
||||||
|
\.?
|
||||||
|
)?
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Extract the description
|
||||||
|
(?:
|
||||||
|
\s* # Some form of whitespace _must_ precede a description because a summary must be there
|
||||||
|
(?! @\pL ) # The description may not start with an @
|
||||||
|
(
|
||||||
|
[^\n]+
|
||||||
|
(?: \n+
|
||||||
|
(?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line
|
||||||
|
[^\n]+ # Include anything else
|
||||||
|
)*
|
||||||
|
)
|
||||||
|
)?
|
||||||
|
|
||||||
|
# 4. Extract the tags (anything that follows)
|
||||||
|
(\s+ [\s\S]*)? # everything that follows
|
||||||
|
/ux',
|
||||||
|
$comment,
|
||||||
|
$matches
|
||||||
|
);
|
||||||
|
array_shift($matches);
|
||||||
|
|
||||||
|
while (count($matches) < 4) {
|
||||||
|
$matches[] = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the tag objects.
|
||||||
|
*
|
||||||
|
* @param string $tags Tag block to parse.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function parseTags($tags)
|
||||||
|
{
|
||||||
|
$result = array();
|
||||||
|
$tags = trim($tags);
|
||||||
|
if ('' !== $tags) {
|
||||||
|
if ('@' !== $tags[0]) {
|
||||||
|
throw new \LogicException(
|
||||||
|
'A tag block started with text instead of an actual tag,'
|
||||||
|
. ' this makes the tag block invalid: ' . $tags
|
||||||
|
);
|
||||||
|
}
|
||||||
|
foreach (explode("\n", $tags) as $tag_line) {
|
||||||
|
if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
|
||||||
|
$result[] = $tag_line;
|
||||||
|
} else {
|
||||||
|
$result[count($result) - 1] .= "\n" . $tag_line;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create proper Tag objects
|
||||||
|
foreach ($result as $key => $tag_line) {
|
||||||
|
$tag = Tag::createInstance(trim($tag_line), $this);
|
||||||
|
if ($tag instanceof TemplateTag) {
|
||||||
|
$this->generics[] = $tag->getTemplateName();
|
||||||
|
}
|
||||||
|
$result[$key] = $tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tags = $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the text portion of the doc block.
|
||||||
|
*
|
||||||
|
* Gets the text portion (short and long description combined) of the doc
|
||||||
|
* block.
|
||||||
|
*
|
||||||
|
* @return string The text portion of the doc block.
|
||||||
|
*/
|
||||||
|
public function getText()
|
||||||
|
{
|
||||||
|
$short = $this->getShortDescription();
|
||||||
|
$long = $this->getLongDescription()->getContents();
|
||||||
|
|
||||||
|
if ($long) {
|
||||||
|
return "{$short}\n\n{$long}";
|
||||||
|
} else {
|
||||||
|
return $short;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the text portion of the doc block.
|
||||||
|
*
|
||||||
|
* Sets the text portion (short and long description combined) of the doc
|
||||||
|
* block.
|
||||||
|
*
|
||||||
|
* @param string $docblock The new text portion of the doc block.
|
||||||
|
*
|
||||||
|
* @return $this This doc block.
|
||||||
|
*/
|
||||||
|
public function setText($comment)
|
||||||
|
{
|
||||||
|
list(,$short, $long) = $this->splitDocBlock($comment);
|
||||||
|
$this->short_description = $short;
|
||||||
|
$this->long_description = new DocBlock\Description($long, $this);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Returns the opening line or also known as short description.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getShortDescription()
|
||||||
|
{
|
||||||
|
return $this->short_description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the full description or also known as long description.
|
||||||
|
*
|
||||||
|
* @return DocBlock\Description
|
||||||
|
*/
|
||||||
|
public function getLongDescription()
|
||||||
|
{
|
||||||
|
return $this->long_description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this DocBlock is the start of a Template section.
|
||||||
|
*
|
||||||
|
* A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
|
||||||
|
* (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
|
||||||
|
*
|
||||||
|
* An example of such an opening is:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* /**#@+
|
||||||
|
* * My DocBlock
|
||||||
|
* * /
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
|
||||||
|
* elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
|
||||||
|
*
|
||||||
|
* @see self::isTemplateEnd() for the check whether a closing marker was provided.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isTemplateStart()
|
||||||
|
{
|
||||||
|
return $this->isTemplateStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this DocBlock is the end of a Template section.
|
||||||
|
*
|
||||||
|
* @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isTemplateEnd()
|
||||||
|
{
|
||||||
|
return $this->isTemplateEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current context.
|
||||||
|
*
|
||||||
|
* @return Context
|
||||||
|
*/
|
||||||
|
public function getContext()
|
||||||
|
{
|
||||||
|
return $this->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current location.
|
||||||
|
*
|
||||||
|
* @return Location
|
||||||
|
*/
|
||||||
|
public function getLocation()
|
||||||
|
{
|
||||||
|
return $this->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tags for this DocBlock.
|
||||||
|
*
|
||||||
|
* @return Tag[]
|
||||||
|
*/
|
||||||
|
public function getTags()
|
||||||
|
{
|
||||||
|
return $this->tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array of tags matching the given name. If no tags are found
|
||||||
|
* an empty array is returned.
|
||||||
|
*
|
||||||
|
* @param string $name String to search by.
|
||||||
|
*
|
||||||
|
* @return Tag[]
|
||||||
|
*/
|
||||||
|
public function getTagsByName($name)
|
||||||
|
{
|
||||||
|
$result = array();
|
||||||
|
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($this->getTags() as $tag) {
|
||||||
|
if ($tag->getName() != $name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[] = $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a tag of a certain type is present in this DocBlock.
|
||||||
|
*
|
||||||
|
* @param string $name Tag name to check for.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasTag($name)
|
||||||
|
{
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($this->getTags() as $tag) {
|
||||||
|
if ($tag->getName() == $name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a tag at the end of the list of tags.
|
||||||
|
*
|
||||||
|
* @param Tag $tag The tag to add.
|
||||||
|
*
|
||||||
|
* @return Tag The newly added tag.
|
||||||
|
*
|
||||||
|
* @throws \LogicException When the tag belongs to a different DocBlock.
|
||||||
|
*/
|
||||||
|
public function appendTag(Tag $tag)
|
||||||
|
{
|
||||||
|
if (null === $tag->getDocBlock()) {
|
||||||
|
$tag->setDocBlock($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tag->getDocBlock() === $this) {
|
||||||
|
$this->tags[] = $tag;
|
||||||
|
} else {
|
||||||
|
throw new \LogicException(
|
||||||
|
'This tag belongs to a different DocBlock object.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a tag from the list of tags.
|
||||||
|
*
|
||||||
|
* @param Tag $tag The tag to be deleted.
|
||||||
|
*
|
||||||
|
* @return bool True if the tag was deleted.
|
||||||
|
*/
|
||||||
|
public function deleteTag(Tag $tag)
|
||||||
|
{
|
||||||
|
if (($key = array_search($tag, $this->tags)) !== false) {
|
||||||
|
unset($this->tags[$key]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the generics for this DocBlock.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getGenerics()
|
||||||
|
{
|
||||||
|
return $this->generics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a string representation of this object.
|
||||||
|
*
|
||||||
|
* @todo determine the exact format as used by PHP Reflection and
|
||||||
|
* implement it.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @codeCoverageIgnore Not yet implemented
|
||||||
|
*/
|
||||||
|
public static function export()
|
||||||
|
{
|
||||||
|
throw new \Exception('Not yet implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the exported information (we should use the export static method
|
||||||
|
* BUT this throws an exception at this point).
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
* @codeCoverageIgnore Not yet implemented
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return 'Not yet implemented';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context in which a DocBlock occurs.
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class Context
|
||||||
|
{
|
||||||
|
/** @var string The current namespace. */
|
||||||
|
protected $namespace = '';
|
||||||
|
|
||||||
|
/** @var array List of namespace aliases => Fully Qualified Namespace. */
|
||||||
|
protected $namespace_aliases = array();
|
||||||
|
|
||||||
|
/** @var string Name of the structural element, within the namespace. */
|
||||||
|
protected $lsen = '';
|
||||||
|
|
||||||
|
/** @var string[] List of generics */
|
||||||
|
protected $generics = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cteates a new context.
|
||||||
|
* @param string $namespace The namespace where this DocBlock
|
||||||
|
* resides in.
|
||||||
|
* @param array $namespace_aliases List of namespace aliases => Fully
|
||||||
|
* Qualified Namespace.
|
||||||
|
* @param string $lsen Name of the structural element, within
|
||||||
|
* the namespace.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$namespace = '',
|
||||||
|
array $namespace_aliases = array(),
|
||||||
|
$lsen = '',
|
||||||
|
array $generics = array()
|
||||||
|
) {
|
||||||
|
if (!empty($namespace)) {
|
||||||
|
$this->setNamespace($namespace);
|
||||||
|
}
|
||||||
|
$this->setNamespaceAliases($namespace_aliases);
|
||||||
|
$this->setLSEN($lsen);
|
||||||
|
$this->setGenerics($generics);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string The namespace where this DocBlock resides in.
|
||||||
|
*/
|
||||||
|
public function getNamespace()
|
||||||
|
{
|
||||||
|
return $this->namespace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array List of namespace aliases => Fully Qualified Namespace.
|
||||||
|
*/
|
||||||
|
public function getNamespaceAliases()
|
||||||
|
{
|
||||||
|
return $this->namespace_aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Local Structural Element Name.
|
||||||
|
*
|
||||||
|
* @return string Name of the structural element, within the namespace.
|
||||||
|
*/
|
||||||
|
public function getLSEN()
|
||||||
|
{
|
||||||
|
return $this->lsen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the list of generics.
|
||||||
|
*
|
||||||
|
* @return string[] List of generics
|
||||||
|
*/
|
||||||
|
public function getGenerics()
|
||||||
|
{
|
||||||
|
return $this->generics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new namespace.
|
||||||
|
*
|
||||||
|
* Sets a new namespace for the context. Leading and trailing slashes are
|
||||||
|
* trimmed, and the keywords "global" and "default" are treated as aliases
|
||||||
|
* to no namespace.
|
||||||
|
*
|
||||||
|
* @param string $namespace The new namespace to set.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setNamespace($namespace)
|
||||||
|
{
|
||||||
|
if ('global' !== $namespace
|
||||||
|
&& 'default' !== $namespace
|
||||||
|
) {
|
||||||
|
// Srip leading and trailing slash
|
||||||
|
$this->namespace = trim((string)$namespace, '\\');
|
||||||
|
} else {
|
||||||
|
$this->namespace = '';
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the namespace aliases, replacing all previous ones.
|
||||||
|
*
|
||||||
|
* @param array $namespace_aliases List of namespace aliases => Fully
|
||||||
|
* Qualified Namespace.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setNamespaceAliases(array $namespace_aliases)
|
||||||
|
{
|
||||||
|
$this->namespace_aliases = array();
|
||||||
|
foreach ($namespace_aliases as $alias => $fqnn) {
|
||||||
|
$this->setNamespaceAlias($alias, $fqnn);
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a namespace alias to the context.
|
||||||
|
*
|
||||||
|
* @param string $alias The alias name (the part after "as", or the last
|
||||||
|
* part of the Fully Qualified Namespace Name) to add.
|
||||||
|
* @param string $fqnn The Fully Qualified Namespace Name for this alias.
|
||||||
|
* Any form of leading/trailing slashes are accepted, but what will be
|
||||||
|
* stored is a name, prefixed with a slash, and no trailing slash.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setNamespaceAlias($alias, $fqnn)
|
||||||
|
{
|
||||||
|
$this->namespace_aliases[$alias] = '\\' . trim((string)$fqnn, '\\');
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new Local Structural Element Name.
|
||||||
|
*
|
||||||
|
* Sets a new Local Structural Element Name. A local name also contains
|
||||||
|
* punctuation determining the kind of structural element (e.g. trailing "("
|
||||||
|
* and ")" for functions and methods).
|
||||||
|
*
|
||||||
|
* @param string $lsen The new local name of a structural element.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLSEN($lsen)
|
||||||
|
{
|
||||||
|
$this->lsen = (string)$lsen;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new list of generics.
|
||||||
|
*
|
||||||
|
* @param string[] $generics The new list of generics.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setGenerics(array $generics)
|
||||||
|
{
|
||||||
|
$this->generics = $generics;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,422 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file is part of phpDocumentor.
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
use ArrayIterator;
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use ReflectionClass;
|
||||||
|
use ReflectionClassConstant;
|
||||||
|
use ReflectionMethod;
|
||||||
|
use ReflectionParameter;
|
||||||
|
use ReflectionProperty;
|
||||||
|
use Reflector;
|
||||||
|
use RuntimeException;
|
||||||
|
use UnexpectedValueException;
|
||||||
|
|
||||||
|
use function define;
|
||||||
|
use function defined;
|
||||||
|
use function file_exists;
|
||||||
|
use function file_get_contents;
|
||||||
|
use function get_class;
|
||||||
|
use function in_array;
|
||||||
|
use function is_string;
|
||||||
|
use function strrpos;
|
||||||
|
use function substr;
|
||||||
|
use function token_get_all;
|
||||||
|
use function trim;
|
||||||
|
|
||||||
|
use const T_AS;
|
||||||
|
use const T_CLASS;
|
||||||
|
use const T_CURLY_OPEN;
|
||||||
|
use const T_DOLLAR_OPEN_CURLY_BRACES;
|
||||||
|
use const T_NAME_FULLY_QUALIFIED;
|
||||||
|
use const T_NAME_QUALIFIED;
|
||||||
|
use const T_NAMESPACE;
|
||||||
|
use const T_NS_SEPARATOR;
|
||||||
|
use const T_STRING;
|
||||||
|
use const T_TRAIT;
|
||||||
|
use const T_USE;
|
||||||
|
|
||||||
|
if (!defined('T_NAME_QUALIFIED')) {
|
||||||
|
define('T_NAME_QUALIFIED', 10001);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!defined('T_NAME_FULLY_QUALIFIED')) {
|
||||||
|
define('T_NAME_FULLY_QUALIFIED', 10002);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor.
|
||||||
|
*
|
||||||
|
* For a DocBlock to be able to resolve types that use partial namespace names or rely on namespace imports we need to
|
||||||
|
* provide a bit of context so that the DocBlock can read that and based on it decide how to resolve the types to
|
||||||
|
* Fully Qualified names.
|
||||||
|
*
|
||||||
|
* @see Context for more information.
|
||||||
|
*/
|
||||||
|
final class ContextFactory
|
||||||
|
{
|
||||||
|
/** The literal used at the end of a use statement. */
|
||||||
|
private const T_LITERAL_END_OF_USE = ';';
|
||||||
|
|
||||||
|
/** The literal used between sets of use statements */
|
||||||
|
private const T_LITERAL_USE_SEPARATOR = ',';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a Context given a Class Reflection.
|
||||||
|
*
|
||||||
|
* @see Context for more information on Contexts.
|
||||||
|
*/
|
||||||
|
public function createFromReflector(Reflector $reflector): Context
|
||||||
|
{
|
||||||
|
if ($reflector instanceof ReflectionClass) {
|
||||||
|
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
|
||||||
|
/** @var ReflectionClass<object> $reflector */
|
||||||
|
|
||||||
|
return $this->createFromReflectionClass($reflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reflector instanceof ReflectionParameter) {
|
||||||
|
return $this->createFromReflectionParameter($reflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reflector instanceof ReflectionMethod) {
|
||||||
|
return $this->createFromReflectionMethod($reflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reflector instanceof ReflectionProperty) {
|
||||||
|
return $this->createFromReflectionProperty($reflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reflector instanceof ReflectionClassConstant) {
|
||||||
|
return $this->createFromReflectionClassConstant($reflector);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnexpectedValueException('Unhandled \Reflector instance given: ' . get_class($reflector));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createFromReflectionParameter(ReflectionParameter $parameter): Context
|
||||||
|
{
|
||||||
|
$class = $parameter->getDeclaringClass();
|
||||||
|
if (!$class) {
|
||||||
|
throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->createFromReflectionClass($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createFromReflectionMethod(ReflectionMethod $method): Context
|
||||||
|
{
|
||||||
|
$class = $method->getDeclaringClass();
|
||||||
|
|
||||||
|
return $this->createFromReflectionClass($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createFromReflectionProperty(ReflectionProperty $property): Context
|
||||||
|
{
|
||||||
|
$class = $property->getDeclaringClass();
|
||||||
|
|
||||||
|
return $this->createFromReflectionClass($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createFromReflectionClassConstant(ReflectionClassConstant $constant): Context
|
||||||
|
{
|
||||||
|
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
|
||||||
|
/** @phpstan-var ReflectionClass<object> $class */
|
||||||
|
$class = $constant->getDeclaringClass();
|
||||||
|
|
||||||
|
return $this->createFromReflectionClass($class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @phpstan-param ReflectionClass<object> $class
|
||||||
|
*/
|
||||||
|
private function createFromReflectionClass(ReflectionClass $class): Context
|
||||||
|
{
|
||||||
|
$fileName = $class->getFileName();
|
||||||
|
$namespace = $class->getNamespaceName();
|
||||||
|
|
||||||
|
if (is_string($fileName) && file_exists($fileName)) {
|
||||||
|
$contents = file_get_contents($fileName);
|
||||||
|
if ($contents === false) {
|
||||||
|
throw new RuntimeException('Unable to read file "' . $fileName . '"');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->createForNamespace($namespace, $contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Context($namespace, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build a Context for a namespace in the provided file contents.
|
||||||
|
*
|
||||||
|
* @see Context for more information on Contexts.
|
||||||
|
*
|
||||||
|
* @param string $namespace It does not matter if a `\` precedes the namespace name,
|
||||||
|
* this method first normalizes.
|
||||||
|
* @param string $fileContents The file's contents to retrieve the aliases from with the given namespace.
|
||||||
|
*/
|
||||||
|
public function createForNamespace(string $namespace, string $fileContents): Context
|
||||||
|
{
|
||||||
|
$namespace = trim($namespace, '\\');
|
||||||
|
$useStatements = [];
|
||||||
|
$currentNamespace = '';
|
||||||
|
$tokens = new ArrayIterator(token_get_all($fileContents));
|
||||||
|
|
||||||
|
while ($tokens->valid()) {
|
||||||
|
$currentToken = $tokens->current();
|
||||||
|
switch ($currentToken[0]) {
|
||||||
|
case T_NAMESPACE:
|
||||||
|
$currentNamespace = $this->parseNamespace($tokens);
|
||||||
|
break;
|
||||||
|
case T_CLASS:
|
||||||
|
case T_TRAIT:
|
||||||
|
// Fast-forward the iterator through the class so that any
|
||||||
|
// T_USE tokens found within are skipped - these are not
|
||||||
|
// valid namespace use statements so should be ignored.
|
||||||
|
$braceLevel = 0;
|
||||||
|
$firstBraceFound = false;
|
||||||
|
while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) {
|
||||||
|
$currentToken = $tokens->current();
|
||||||
|
if (
|
||||||
|
$currentToken === '{'
|
||||||
|
|| in_array($currentToken[0], [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true)
|
||||||
|
) {
|
||||||
|
if (!$firstBraceFound) {
|
||||||
|
$firstBraceFound = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
++$braceLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($currentToken === '}') {
|
||||||
|
--$braceLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case T_USE:
|
||||||
|
if ($currentNamespace === $namespace) {
|
||||||
|
$useStatements += $this->parseUseStatement($tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Context($namespace, $useStatements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deduce the name from tokens when we are at the T_NAMESPACE token.
|
||||||
|
*
|
||||||
|
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
|
||||||
|
*/
|
||||||
|
private function parseNamespace(ArrayIterator $tokens): string
|
||||||
|
{
|
||||||
|
// skip to the first string or namespace separator
|
||||||
|
$this->skipToNextStringOrNamespaceSeparator($tokens);
|
||||||
|
|
||||||
|
$name = '';
|
||||||
|
$acceptedTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED];
|
||||||
|
while ($tokens->valid() && in_array($tokens->current()[0], $acceptedTokens, true)) {
|
||||||
|
$name .= $tokens->current()[1];
|
||||||
|
$tokens->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deduce the names of all imports when we are at the T_USE token.
|
||||||
|
*
|
||||||
|
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return array<string, string>
|
||||||
|
*/
|
||||||
|
private function parseUseStatement(ArrayIterator $tokens): array
|
||||||
|
{
|
||||||
|
$uses = [];
|
||||||
|
|
||||||
|
while ($tokens->valid()) {
|
||||||
|
$this->skipToNextStringOrNamespaceSeparator($tokens);
|
||||||
|
|
||||||
|
$uses += $this->extractUseStatements($tokens);
|
||||||
|
$currentToken = $tokens->current();
|
||||||
|
if ($currentToken[0] === self::T_LITERAL_END_OF_USE) {
|
||||||
|
return $uses;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $uses;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.
|
||||||
|
*
|
||||||
|
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
|
||||||
|
*/
|
||||||
|
private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens): void
|
||||||
|
{
|
||||||
|
while ($tokens->valid()) {
|
||||||
|
$currentToken = $tokens->current();
|
||||||
|
if (in_array($currentToken[0], [T_STRING, T_NS_SEPARATOR], true)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($currentToken[0] === T_NAME_QUALIFIED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (defined('T_NAME_FULLY_QUALIFIED') && $currentToken[0] === T_NAME_FULLY_QUALIFIED) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of
|
||||||
|
* a USE statement yet. This will return a key/value array of the alias => namespace.
|
||||||
|
*
|
||||||
|
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
* @psalm-return array<string, string>
|
||||||
|
*
|
||||||
|
* @psalm-suppress TypeDoesNotContainType
|
||||||
|
*/
|
||||||
|
private function extractUseStatements(ArrayIterator $tokens): array
|
||||||
|
{
|
||||||
|
$extractedUseStatements = [];
|
||||||
|
$groupedNs = '';
|
||||||
|
$currentNs = '';
|
||||||
|
$currentAlias = '';
|
||||||
|
$state = 'start';
|
||||||
|
|
||||||
|
while ($tokens->valid()) {
|
||||||
|
$currentToken = $tokens->current();
|
||||||
|
$tokenId = is_string($currentToken) ? $currentToken : $currentToken[0];
|
||||||
|
$tokenValue = is_string($currentToken) ? null : $currentToken[1];
|
||||||
|
switch ($state) {
|
||||||
|
case 'start':
|
||||||
|
switch ($tokenId) {
|
||||||
|
case T_STRING:
|
||||||
|
case T_NS_SEPARATOR:
|
||||||
|
$currentNs .= (string) $tokenValue;
|
||||||
|
$currentAlias = $tokenValue;
|
||||||
|
break;
|
||||||
|
case T_NAME_QUALIFIED:
|
||||||
|
case T_NAME_FULLY_QUALIFIED:
|
||||||
|
$currentNs .= (string) $tokenValue;
|
||||||
|
$currentAlias = substr(
|
||||||
|
(string) $tokenValue,
|
||||||
|
(int) (strrpos((string) $tokenValue, '\\')) + 1
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case T_CURLY_OPEN:
|
||||||
|
case '{':
|
||||||
|
$state = 'grouped';
|
||||||
|
$groupedNs = $currentNs;
|
||||||
|
break;
|
||||||
|
case T_AS:
|
||||||
|
$state = 'start-alias';
|
||||||
|
break;
|
||||||
|
case self::T_LITERAL_USE_SEPARATOR:
|
||||||
|
case self::T_LITERAL_END_OF_USE:
|
||||||
|
$state = 'end';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'start-alias':
|
||||||
|
switch ($tokenId) {
|
||||||
|
case T_STRING:
|
||||||
|
$currentAlias = $tokenValue;
|
||||||
|
break;
|
||||||
|
case self::T_LITERAL_USE_SEPARATOR:
|
||||||
|
case self::T_LITERAL_END_OF_USE:
|
||||||
|
$state = 'end';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'grouped':
|
||||||
|
switch ($tokenId) {
|
||||||
|
case T_STRING:
|
||||||
|
case T_NS_SEPARATOR:
|
||||||
|
$currentNs .= (string) $tokenValue;
|
||||||
|
$currentAlias = $tokenValue;
|
||||||
|
break;
|
||||||
|
case T_AS:
|
||||||
|
$state = 'grouped-alias';
|
||||||
|
break;
|
||||||
|
case self::T_LITERAL_USE_SEPARATOR:
|
||||||
|
$state = 'grouped';
|
||||||
|
$extractedUseStatements[(string) $currentAlias] = $currentNs;
|
||||||
|
$currentNs = $groupedNs;
|
||||||
|
$currentAlias = '';
|
||||||
|
break;
|
||||||
|
case self::T_LITERAL_END_OF_USE:
|
||||||
|
$state = 'end';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 'grouped-alias':
|
||||||
|
switch ($tokenId) {
|
||||||
|
case T_STRING:
|
||||||
|
$currentAlias = $tokenValue;
|
||||||
|
break;
|
||||||
|
case self::T_LITERAL_USE_SEPARATOR:
|
||||||
|
$state = 'grouped';
|
||||||
|
$extractedUseStatements[(string) $currentAlias] = $currentNs;
|
||||||
|
$currentNs = $groupedNs;
|
||||||
|
$currentAlias = '';
|
||||||
|
break;
|
||||||
|
case self::T_LITERAL_END_OF_USE:
|
||||||
|
$state = 'end';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($state === 'end') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($groupedNs !== $currentNs) {
|
||||||
|
$extractedUseStatements[(string) $currentAlias] = $currentNs;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $extractedUseStatements;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,223 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a Description of a DocBlock or tag.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class Description implements \Reflector
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $contents = '';
|
||||||
|
|
||||||
|
/** @var array The contents, as an array of strings and Tag objects. */
|
||||||
|
protected $parsedContents = null;
|
||||||
|
|
||||||
|
/** @var DocBlock The DocBlock which this description belongs to. */
|
||||||
|
protected $docblock = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populates the fields of a description.
|
||||||
|
*
|
||||||
|
* @param string $content The description's contents.
|
||||||
|
* @param DocBlock $docblock The DocBlock which this description belongs to.
|
||||||
|
*/
|
||||||
|
public function __construct($content, ?DocBlock $docblock = null)
|
||||||
|
{
|
||||||
|
$this->setContent($content)->setDocBlock($docblock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the text of this description.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContents()
|
||||||
|
{
|
||||||
|
return $this->contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the text of this description.
|
||||||
|
*
|
||||||
|
* @param string $content The new text of this description.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
$this->contents = trim($content);
|
||||||
|
|
||||||
|
$this->parsedContents = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parsed text of this description.
|
||||||
|
*
|
||||||
|
* @return array An array of strings and tag objects, in the order they
|
||||||
|
* occur within the description.
|
||||||
|
*/
|
||||||
|
public function getParsedContents()
|
||||||
|
{
|
||||||
|
if (null === $this->parsedContents) {
|
||||||
|
$this->parsedContents = preg_split(
|
||||||
|
'/\{
|
||||||
|
# "{@}" is not a valid inline tag. This ensures that
|
||||||
|
# we do not treat it as one, but treat it literally.
|
||||||
|
(?!@\})
|
||||||
|
# We want to capture the whole tag line, but without the
|
||||||
|
# inline tag delimiters.
|
||||||
|
(\@
|
||||||
|
# Match everything up to the next delimiter.
|
||||||
|
[^{}]*
|
||||||
|
# Nested inline tag content should not be captured, or
|
||||||
|
# it will appear in the result separately.
|
||||||
|
(?:
|
||||||
|
# Match nested inline tags.
|
||||||
|
(?:
|
||||||
|
# Because we did not catch the tag delimiters
|
||||||
|
# earlier, we must be explicit with them here.
|
||||||
|
# Notice that this also matches "{}", as a way
|
||||||
|
# to later introduce it as an escape sequence.
|
||||||
|
\{(?1)?\}
|
||||||
|
|
|
||||||
|
# Make sure we match hanging "{".
|
||||||
|
\{
|
||||||
|
)
|
||||||
|
# Match content after the nested inline tag.
|
||||||
|
[^{}]*
|
||||||
|
)* # If there are more inline tags, match them as well.
|
||||||
|
# We use "*" since there may not be any nested inline
|
||||||
|
# tags.
|
||||||
|
)
|
||||||
|
\}/Sux',
|
||||||
|
$this->contents,
|
||||||
|
-1,
|
||||||
|
PREG_SPLIT_DELIM_CAPTURE
|
||||||
|
);
|
||||||
|
|
||||||
|
$count = count($this->parsedContents);
|
||||||
|
for ($i=1; $i<$count; $i += 2) {
|
||||||
|
$this->parsedContents[$i] = Tag::createInstance(
|
||||||
|
$this->parsedContents[$i],
|
||||||
|
$this->docblock
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//In order to allow "literal" inline tags, the otherwise invalid
|
||||||
|
//sequence "{@}" is changed to "@", and "{}" is changed to "}".
|
||||||
|
//See unit tests for examples.
|
||||||
|
for ($i=0; $i<$count; $i += 2) {
|
||||||
|
$this->parsedContents[$i] = str_replace(
|
||||||
|
array('{@}', '{}'),
|
||||||
|
array('@', '}'),
|
||||||
|
$this->parsedContents[$i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $this->parsedContents;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a formatted variant of the Long Description using MarkDown.
|
||||||
|
*
|
||||||
|
* @todo this should become a more intelligent piece of code where the
|
||||||
|
* configuration contains a setting what format long descriptions are.
|
||||||
|
*
|
||||||
|
* @codeCoverageIgnore Will be removed soon, in favor of adapters at
|
||||||
|
* PhpDocumentor itself that will process text in various formats.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getFormattedContents()
|
||||||
|
{
|
||||||
|
$result = $this->contents;
|
||||||
|
|
||||||
|
// if the long description contains a plain HTML <code> element, surround
|
||||||
|
// it with a pre element. Please note that we explicitly used str_replace
|
||||||
|
// and not preg_replace to gain performance
|
||||||
|
if (strpos($result, '<code>') !== false) {
|
||||||
|
$result = str_replace(
|
||||||
|
array('<code>', "<code>\r\n", "<code>\n", "<code>\r", '</code>'),
|
||||||
|
array('<pre><code>', '<code>', '<code>', '<code>', '</code></pre>'),
|
||||||
|
$result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (class_exists('Parsedown')) {
|
||||||
|
$markdown = \Parsedown::instance();
|
||||||
|
$result = $markdown->parse($result);
|
||||||
|
} elseif (class_exists('dflydev\markdown\MarkdownExtraParser')) {
|
||||||
|
$markdown = new \dflydev\markdown\MarkdownExtraParser();
|
||||||
|
$result = $markdown->transformMarkdown($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return trim($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the docblock this tag belongs to.
|
||||||
|
*
|
||||||
|
* @return DocBlock The docblock this description belongs to.
|
||||||
|
*/
|
||||||
|
public function getDocBlock()
|
||||||
|
{
|
||||||
|
return $this->docblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the docblock this tag belongs to.
|
||||||
|
*
|
||||||
|
* @param DocBlock $docblock The new docblock this description belongs to.
|
||||||
|
* Setting NULL removes any association.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setDocBlock(?DocBlock $docblock = null)
|
||||||
|
{
|
||||||
|
$this->docblock = $docblock;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a string representation of this object.
|
||||||
|
*
|
||||||
|
* @todo determine the exact format as used by PHP Reflection
|
||||||
|
* and implement it.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @codeCoverageIgnore Not yet implemented
|
||||||
|
*/
|
||||||
|
public static function export()
|
||||||
|
{
|
||||||
|
throw new \Exception('Not yet implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the long description as a string.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return $this->getContents();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The location a DocBlock occurs within a file.
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class Location
|
||||||
|
{
|
||||||
|
/** @var int Line where the DocBlock text starts. */
|
||||||
|
protected $lineNumber = 0;
|
||||||
|
|
||||||
|
/** @var int Column where the DocBlock text starts. */
|
||||||
|
protected $columnNumber = 0;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
$lineNumber = 0,
|
||||||
|
$columnNumber = 0
|
||||||
|
) {
|
||||||
|
$this->setLineNumber($lineNumber)->setColumnNumber($columnNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int Line where the DocBlock text starts.
|
||||||
|
*/
|
||||||
|
public function getLineNumber()
|
||||||
|
{
|
||||||
|
return $this->lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param type $lineNumber
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLineNumber($lineNumber)
|
||||||
|
{
|
||||||
|
$this->lineNumber = (int)$lineNumber;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int Column where the DocBlock text starts.
|
||||||
|
*/
|
||||||
|
public function getColumnNumber()
|
||||||
|
{
|
||||||
|
return $this->columnNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param int $columnNumber
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setColumnNumber($columnNumber)
|
||||||
|
{
|
||||||
|
$this->columnNumber = (int)$columnNumber;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,235 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Barry vd. Heuvel <[email protected]>
|
||||||
|
* @copyright 2013 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serializes a DocBlock instance.
|
||||||
|
*
|
||||||
|
* @author Barry vd. Heuvel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class Serializer
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var string The string to indent the comment with. */
|
||||||
|
protected $indentString = ' ';
|
||||||
|
|
||||||
|
/** @var int The number of times the indent string is repeated. */
|
||||||
|
protected $indent = 0;
|
||||||
|
|
||||||
|
/** @var bool Whether to indent the first line. */
|
||||||
|
protected $isFirstLineIndented = true;
|
||||||
|
|
||||||
|
/** @var int|null The max length of a line. */
|
||||||
|
protected $lineLength = null;
|
||||||
|
|
||||||
|
/** @var bool Separate tag groups. */
|
||||||
|
protected $separateTags = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Serializer instance.
|
||||||
|
*
|
||||||
|
* @param int $indent The number of times the indent string is
|
||||||
|
* repeated.
|
||||||
|
* @param string $indentString The string to indent the comment with.
|
||||||
|
* @param bool $indentFirstLine Whether to indent the first line.
|
||||||
|
* @param int|null $lineLength The max length of a line or NULL to
|
||||||
|
* disable line wrapping.
|
||||||
|
* @param bool $separateTags Separate tag groups.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$indent = 0,
|
||||||
|
$indentString = ' ',
|
||||||
|
$indentFirstLine = true,
|
||||||
|
$lineLength = null,
|
||||||
|
$separateTags = false
|
||||||
|
) {
|
||||||
|
$this->setIndentationString($indentString);
|
||||||
|
$this->setIndent($indent);
|
||||||
|
$this->setIsFirstLineIndented($indentFirstLine);
|
||||||
|
$this->setLineLength($lineLength);
|
||||||
|
$this->setSeparateTags($separateTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the string to indent comments with.
|
||||||
|
*
|
||||||
|
* @param string $indentationString The string to indent comments with.
|
||||||
|
*
|
||||||
|
* @return $this This serializer object.
|
||||||
|
*/
|
||||||
|
public function setIndentationString($indentString)
|
||||||
|
{
|
||||||
|
$this->indentString = (string)$indentString;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the string to indent comments with.
|
||||||
|
*
|
||||||
|
* @return string The indent string.
|
||||||
|
*/
|
||||||
|
public function getIndentationString()
|
||||||
|
{
|
||||||
|
return $this->indentString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of indents.
|
||||||
|
*
|
||||||
|
* @param int $indent The number of times the indent string is repeated.
|
||||||
|
*
|
||||||
|
* @return $this This serializer object.
|
||||||
|
*/
|
||||||
|
public function setIndent($indent)
|
||||||
|
{
|
||||||
|
$this->indent = (int)$indent;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of indents.
|
||||||
|
*
|
||||||
|
* @return int The number of times the indent string is repeated.
|
||||||
|
*/
|
||||||
|
public function getIndent()
|
||||||
|
{
|
||||||
|
return $this->indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether or not the first line should be indented.
|
||||||
|
*
|
||||||
|
* Sets whether or not the first line (the one with the "/**") should be
|
||||||
|
* indented.
|
||||||
|
*
|
||||||
|
* @param bool $indentFirstLine The new value for this setting.
|
||||||
|
*
|
||||||
|
* @return $this This serializer object.
|
||||||
|
*/
|
||||||
|
public function setIsFirstLineIndented($indentFirstLine)
|
||||||
|
{
|
||||||
|
$this->isFirstLineIndented = (bool)$indentFirstLine;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether or not the first line should be indented.
|
||||||
|
*
|
||||||
|
* @return bool Whether or not the first line should be indented.
|
||||||
|
*/
|
||||||
|
public function isFirstLineIndented()
|
||||||
|
{
|
||||||
|
return $this->isFirstLineIndented;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the line length.
|
||||||
|
*
|
||||||
|
* Sets the length of each line in the serialization. Content will be
|
||||||
|
* wrapped within this limit.
|
||||||
|
*
|
||||||
|
* @param int|null $lineLength The length of each line. NULL to disable line
|
||||||
|
* wrapping altogether.
|
||||||
|
*
|
||||||
|
* @return $this This serializer object.
|
||||||
|
*/
|
||||||
|
public function setLineLength($lineLength)
|
||||||
|
{
|
||||||
|
$this->lineLength = null === $lineLength ? null : (int)$lineLength;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the line length.
|
||||||
|
*
|
||||||
|
* @return int|null The length of each line or NULL if line wrapping is
|
||||||
|
* disabled.
|
||||||
|
*/
|
||||||
|
public function getLineLength()
|
||||||
|
{
|
||||||
|
return $this->lineLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether there should be an empty line between tag groups.
|
||||||
|
*
|
||||||
|
* @param bool $separateTags The new value for this setting.
|
||||||
|
*
|
||||||
|
* @return $this This serializer object.
|
||||||
|
*/
|
||||||
|
public function setSeparateTags($separateTags)
|
||||||
|
{
|
||||||
|
$this->separateTags = (bool)$separateTags;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether there should be an empty line between tag groups.
|
||||||
|
*
|
||||||
|
* @return bool Whether there should be an empty line between tag groups.
|
||||||
|
*/
|
||||||
|
public function getSeparateTags()
|
||||||
|
{
|
||||||
|
return $this->separateTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a DocBlock comment.
|
||||||
|
*
|
||||||
|
* @param DocBlock The DocBlock to serialize.
|
||||||
|
*
|
||||||
|
* @return string The serialized doc block.
|
||||||
|
*/
|
||||||
|
public function getDocComment(DocBlock $docblock)
|
||||||
|
{
|
||||||
|
$indent = str_repeat($this->indentString, $this->indent);
|
||||||
|
$firstIndent = $this->isFirstLineIndented ? $indent : '';
|
||||||
|
|
||||||
|
$text = $docblock->getText();
|
||||||
|
if ($this->lineLength) {
|
||||||
|
//3 === strlen(' * ')
|
||||||
|
$wrapLength = $this->lineLength - strlen($indent) - 3;
|
||||||
|
$text = wordwrap($text, $wrapLength);
|
||||||
|
}
|
||||||
|
$text = str_replace("\n", "\n{$indent} * ", $text);
|
||||||
|
|
||||||
|
$comment = "{$firstIndent}/**\n{$indent} * {$text}\n{$indent} *\n";
|
||||||
|
|
||||||
|
$tags = array_values($docblock->getTags());
|
||||||
|
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($tags as $key => $tag) {
|
||||||
|
$nextTag = isset($tags[$key + 1]) ? $tags[$key + 1] : null;
|
||||||
|
|
||||||
|
$tagText = (string) $tag;
|
||||||
|
if ($this->lineLength) {
|
||||||
|
$tagText = wordwrap($tagText, $wrapLength);
|
||||||
|
}
|
||||||
|
$tagText = str_replace("\n", "\n{$indent} * ", $tagText);
|
||||||
|
|
||||||
|
$comment .= "{$indent} * {$tagText}\n";
|
||||||
|
|
||||||
|
if ($this->separateTags && $nextTag !== null && ! $tag->inSameGroup($nextTag)) {
|
||||||
|
$comment .= "{$indent} *\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$comment .= $indent . ' */';
|
||||||
|
|
||||||
|
return $comment;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,414 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a tag definition for a DocBlock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class Tag implements \Reflector
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* PCRE regular expression matching a tag name.
|
||||||
|
*/
|
||||||
|
const REGEX_TAGNAME = '[\w\-\_\\\\]+';
|
||||||
|
|
||||||
|
/** @var string Name of the tag */
|
||||||
|
protected $tag = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string|null Content of the tag.
|
||||||
|
* When set to NULL, it means it needs to be regenerated.
|
||||||
|
*/
|
||||||
|
protected $content = '';
|
||||||
|
|
||||||
|
/** @var string Description of the content of this tag */
|
||||||
|
protected $description = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array|null The description, as an array of strings and Tag objects.
|
||||||
|
* When set to NULL, it means it needs to be regenerated.
|
||||||
|
*/
|
||||||
|
protected $parsedDescription = null;
|
||||||
|
|
||||||
|
/** @var Location Location of the tag. */
|
||||||
|
protected $location = null;
|
||||||
|
|
||||||
|
/** @var DocBlock The DocBlock which this tag belongs to. */
|
||||||
|
protected $docblock = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array An array with a tag as a key, and an FQCN to a class that
|
||||||
|
* handles it as an array value. The class is expected to inherit this
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
private static $tagHandlerMappings = array(
|
||||||
|
'author'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\AuthorTag',
|
||||||
|
'covers'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\CoversTag',
|
||||||
|
'deprecated'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\DeprecatedTag',
|
||||||
|
'example'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\ExampleTag',
|
||||||
|
'link'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\LinkTag',
|
||||||
|
'method'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\MethodTag',
|
||||||
|
'param'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\ParamTag',
|
||||||
|
'property-read'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\PropertyReadTag',
|
||||||
|
'property'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\PropertyTag',
|
||||||
|
'property-write'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\PropertyWriteTag',
|
||||||
|
'return'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\ReturnTag',
|
||||||
|
'see'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\SeeTag',
|
||||||
|
'since'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\SinceTag',
|
||||||
|
'source'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\SourceTag',
|
||||||
|
'throw'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\ThrowsTag',
|
||||||
|
'throws'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\ThrowsTag',
|
||||||
|
'uses'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\UsesTag',
|
||||||
|
'var'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\VarTag',
|
||||||
|
'version'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\VersionTag',
|
||||||
|
'SuppressWarnings'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\SuppressWarningsTag',
|
||||||
|
'template'
|
||||||
|
=> '\Barryvdh\Reflection\DocBlock\Tag\TemplateTag'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method responsible for instantiating the correct sub type.
|
||||||
|
*
|
||||||
|
* @param string $tag_line The text for this tag, including description.
|
||||||
|
* @param DocBlock $docblock The DocBlock which this tag belongs to.
|
||||||
|
* @param Location $location Location of the tag.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if an invalid tag line was presented.
|
||||||
|
*
|
||||||
|
* @return static A new tag object.
|
||||||
|
*/
|
||||||
|
final public static function createInstance(
|
||||||
|
$tag_line,
|
||||||
|
?DocBlock $docblock = null,
|
||||||
|
?Location $location = null
|
||||||
|
) {
|
||||||
|
if (!preg_match(
|
||||||
|
'/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)?/us',
|
||||||
|
$tag_line,
|
||||||
|
$matches
|
||||||
|
)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid tag_line detected: ' . $tag_line
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$handler = __CLASS__;
|
||||||
|
if (isset(self::$tagHandlerMappings[$matches[1]])) {
|
||||||
|
$handler = self::$tagHandlerMappings[$matches[1]];
|
||||||
|
} elseif (isset($docblock)) {
|
||||||
|
$tagName = (string)new Type\Collection(
|
||||||
|
array($matches[1]),
|
||||||
|
$docblock->getContext()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset(self::$tagHandlerMappings[$tagName])) {
|
||||||
|
$handler = self::$tagHandlerMappings[$tagName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new $handler(
|
||||||
|
$matches[1],
|
||||||
|
isset($matches[2]) ? $matches[2] : '',
|
||||||
|
$docblock,
|
||||||
|
$location
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a handler for tags.
|
||||||
|
*
|
||||||
|
* Registers a handler for tags. The class specified is autoloaded if it's
|
||||||
|
* not available. It must inherit from this class.
|
||||||
|
*
|
||||||
|
* @param string $tag Name of tag to regiser a handler for. When
|
||||||
|
* registering a namespaced tag, the full name, along with a prefixing
|
||||||
|
* slash MUST be provided.
|
||||||
|
* @param string|null $handler FQCN of handler. Specifing NULL removes the
|
||||||
|
* handler for the specified tag, if any.
|
||||||
|
*
|
||||||
|
* @return bool TRUE on success, FALSE on failure.
|
||||||
|
*/
|
||||||
|
final public static function registerTagHandler($tag, $handler)
|
||||||
|
{
|
||||||
|
$tag = trim((string)$tag);
|
||||||
|
|
||||||
|
if (null === $handler) {
|
||||||
|
unset(self::$tagHandlerMappings[$tag]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('' !== $tag
|
||||||
|
&& class_exists($handler, true)
|
||||||
|
&& is_subclass_of($handler, __CLASS__)
|
||||||
|
&& !strpos($tag, '\\') //Accept no slash, and 1st slash at offset 0.
|
||||||
|
) {
|
||||||
|
self::$tagHandlerMappings[$tag] = $handler;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a tag and populates the member variables.
|
||||||
|
*
|
||||||
|
* @param string $name Name of the tag.
|
||||||
|
* @param string $content The contents of the given tag.
|
||||||
|
* @param DocBlock $docblock The DocBlock which this tag belongs to.
|
||||||
|
* @param Location $location Location of the tag.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
$name,
|
||||||
|
$content,
|
||||||
|
?DocBlock $docblock = null,
|
||||||
|
?Location $location = null
|
||||||
|
) {
|
||||||
|
$this
|
||||||
|
->setName($name)
|
||||||
|
->setContent($content)
|
||||||
|
->setDocBlock($docblock)
|
||||||
|
->setLocation($location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of this tag.
|
||||||
|
*
|
||||||
|
* @return string The name of this tag.
|
||||||
|
*/
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name of this tag.
|
||||||
|
*
|
||||||
|
* @param string $name The new name of this tag.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
* @throws \InvalidArgumentException When an invalid tag name is provided.
|
||||||
|
*/
|
||||||
|
public function setName($name)
|
||||||
|
{
|
||||||
|
if (!preg_match('/^' . self::REGEX_TAGNAME . '$/u', $name)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'Invalid tag name supplied: ' . $name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tag = $name;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the content of this tag.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the content of this tag.
|
||||||
|
*
|
||||||
|
* @param string $content The new content of this tag.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
$this->setDescription($content);
|
||||||
|
$this->content = $content;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the description component of this tag.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDescription()
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the description component of this tag.
|
||||||
|
*
|
||||||
|
* @param string $description The new description component of this tag.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setDescription($description)
|
||||||
|
{
|
||||||
|
$this->content = null;
|
||||||
|
$this->parsedDescription = null;
|
||||||
|
$this->description = trim($description);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the parsed text of this description.
|
||||||
|
*
|
||||||
|
* @return array An array of strings and tag objects, in the order they
|
||||||
|
* occur within the description.
|
||||||
|
*/
|
||||||
|
public function getParsedDescription()
|
||||||
|
{
|
||||||
|
if (null === $this->parsedDescription) {
|
||||||
|
$description = new Description($this->description, $this->docblock);
|
||||||
|
$this->parsedDescription = $description->getParsedContents();
|
||||||
|
}
|
||||||
|
return $this->parsedDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the docblock this tag belongs to.
|
||||||
|
*
|
||||||
|
* @return DocBlock The docblock this tag belongs to.
|
||||||
|
*/
|
||||||
|
public function getDocBlock()
|
||||||
|
{
|
||||||
|
return $this->docblock;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the docblock this tag belongs to.
|
||||||
|
*
|
||||||
|
* @param DocBlock $docblock The new docblock this tag belongs to. Setting
|
||||||
|
* NULL removes any association.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setDocBlock(?DocBlock $docblock = null)
|
||||||
|
{
|
||||||
|
$this->docblock = $docblock;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the location of the tag.
|
||||||
|
*
|
||||||
|
* @return Location The tag's location.
|
||||||
|
*/
|
||||||
|
public function getLocation()
|
||||||
|
{
|
||||||
|
return $this->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the location of the tag.
|
||||||
|
*
|
||||||
|
* @param Location $location The new location of the tag.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLocation(?Location $location = null)
|
||||||
|
{
|
||||||
|
$this->location = $location;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the given tags should be together or apart.
|
||||||
|
*
|
||||||
|
* @param Tag $tag
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function inSameGroup(Tag $tag)
|
||||||
|
{
|
||||||
|
$firstName = $this->getName();
|
||||||
|
$secondName = $tag->getName();
|
||||||
|
|
||||||
|
if ($firstName === $secondName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups = array(
|
||||||
|
array('deprecated', 'link', 'see', 'since'),
|
||||||
|
array('author', 'copyright', 'license'),
|
||||||
|
array('category', 'package', 'subpackage'),
|
||||||
|
array('property', 'property-read', 'property-write'),
|
||||||
|
array('param', 'return'),
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
if (in_array($firstName, $group, true) && in_array($secondName, $group, true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a string representation of this object.
|
||||||
|
*
|
||||||
|
* @todo determine the exact format as used by PHP Reflection and implement it.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
* @codeCoverageIgnore Not yet implemented
|
||||||
|
*/
|
||||||
|
public static function export()
|
||||||
|
{
|
||||||
|
throw new \Exception('Not yet implemented');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the tag as a serialized string
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return "@{$this->getName()} {$this->getContent()}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for an @author tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class AuthorTag extends Tag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* PCRE regular expression matching any valid value for the name component.
|
||||||
|
*/
|
||||||
|
const REGEX_AUTHOR_NAME = '[^\<]*';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PCRE regular expression matching any valid value for the email component.
|
||||||
|
*/
|
||||||
|
const REGEX_AUTHOR_EMAIL = '[^\>]*';
|
||||||
|
|
||||||
|
/** @var string The name of the author */
|
||||||
|
protected $authorName = '';
|
||||||
|
|
||||||
|
/** @var string The email of the author */
|
||||||
|
protected $authorEmail = '';
|
||||||
|
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = $this->authorName;
|
||||||
|
if ('' != $this->authorEmail) {
|
||||||
|
$this->content .= "<{$this->authorEmail}>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
parent::setContent($content);
|
||||||
|
if (preg_match(
|
||||||
|
'/^(' . self::REGEX_AUTHOR_NAME .
|
||||||
|
')(\<(' . self::REGEX_AUTHOR_EMAIL .
|
||||||
|
')\>)?$/u',
|
||||||
|
$this->description,
|
||||||
|
$matches
|
||||||
|
)) {
|
||||||
|
$this->authorName = trim($matches[1]);
|
||||||
|
if (isset($matches[3])) {
|
||||||
|
$this->authorEmail = trim($matches[3]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the author's name.
|
||||||
|
*
|
||||||
|
* @return string The author's name.
|
||||||
|
*/
|
||||||
|
public function getAuthorName()
|
||||||
|
{
|
||||||
|
return $this->authorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the author's name.
|
||||||
|
*
|
||||||
|
* @param string $authorName The new author name.
|
||||||
|
* An invalid value will set an empty string.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAuthorName($authorName)
|
||||||
|
{
|
||||||
|
$this->content = null;
|
||||||
|
$this->authorName
|
||||||
|
= preg_match('/^' . self::REGEX_AUTHOR_NAME . '$/u', $authorName)
|
||||||
|
? $authorName : '';
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the author's email.
|
||||||
|
*
|
||||||
|
* @return string The author's email.
|
||||||
|
*/
|
||||||
|
public function getAuthorEmail()
|
||||||
|
{
|
||||||
|
return $this->authorEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the author's email.
|
||||||
|
*
|
||||||
|
* @param string $authorEmail The new author email.
|
||||||
|
* An invalid value will set an empty string.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setAuthorEmail($authorEmail)
|
||||||
|
{
|
||||||
|
$this->authorEmail
|
||||||
|
= preg_match('/^' . self::REGEX_AUTHOR_EMAIL . '$/u', $authorEmail)
|
||||||
|
? $authorEmail : '';
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @covers tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class CoversTag extends SeeTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag\VersionTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @deprecated tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class DeprecatedTag extends VersionTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @example tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class ExampleTag extends SourceTag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string Path to a file to use as an example.
|
||||||
|
* May also be an absolute URI.
|
||||||
|
*/
|
||||||
|
protected $filePath = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var bool Whether the file path component represents an URI.
|
||||||
|
* This determines how the file portion appears at {@link getContent()}.
|
||||||
|
*/
|
||||||
|
protected $isURI = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$filePath = '';
|
||||||
|
if ($this->isURI) {
|
||||||
|
if (false === strpos($this->filePath, ':')) {
|
||||||
|
$filePath = str_replace(
|
||||||
|
'%2F',
|
||||||
|
'/',
|
||||||
|
rawurlencode($this->filePath)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$filePath = $this->filePath;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$filePath = '"' . $this->filePath . '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->content = $filePath . ' ' . parent::getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
Tag::setContent($content);
|
||||||
|
if (preg_match(
|
||||||
|
'/^
|
||||||
|
# File component
|
||||||
|
(?:
|
||||||
|
# File path in quotes
|
||||||
|
\"([^\"]+)\"
|
||||||
|
|
|
||||||
|
# File URI
|
||||||
|
(\S+)
|
||||||
|
)
|
||||||
|
# Remaining content (parsed by SourceTag)
|
||||||
|
(?:\s+(.*))?
|
||||||
|
$/sux',
|
||||||
|
$this->description,
|
||||||
|
$matches
|
||||||
|
)) {
|
||||||
|
if ('' !== $matches[1]) {
|
||||||
|
$this->setFilePath($matches[1]);
|
||||||
|
} else {
|
||||||
|
$this->setFileURI($matches[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($matches[3])) {
|
||||||
|
parent::setContent($matches[3]);
|
||||||
|
} else {
|
||||||
|
$this->setDescription('');
|
||||||
|
}
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the file path.
|
||||||
|
*
|
||||||
|
* @return string Path to a file to use as an example.
|
||||||
|
* May also be an absolute URI.
|
||||||
|
*/
|
||||||
|
public function getFilePath()
|
||||||
|
{
|
||||||
|
return $this->filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the file path.
|
||||||
|
*
|
||||||
|
* @param string $filePath The new file path to use for the example.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setFilePath($filePath)
|
||||||
|
{
|
||||||
|
$this->isURI = false;
|
||||||
|
$this->filePath = trim($filePath);
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the file path as an URI.
|
||||||
|
*
|
||||||
|
* This function is equivalent to {@link setFilePath()}, except that it
|
||||||
|
* convers an URI to a file path before that.
|
||||||
|
*
|
||||||
|
* There is no getFileURI(), as {@link getFilePath()} is compatible.
|
||||||
|
*
|
||||||
|
* @param type $uri The new file URI to use as an example.
|
||||||
|
*/
|
||||||
|
public function setFileURI($uri)
|
||||||
|
{
|
||||||
|
$this->isURI = true;
|
||||||
|
if (false === strpos($uri, ':')) {
|
||||||
|
//Relative URL
|
||||||
|
$this->filePath = rawurldecode(
|
||||||
|
str_replace(array('/', '\\'), '%2F', $uri)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
//Absolute URL or URI.
|
||||||
|
$this->filePath = $uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Ben Selby <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @link tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Ben Selby <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class LinkTag extends Tag
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $link = '';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = "{$this->link} {$this->description}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
parent::setContent($content);
|
||||||
|
$parts = preg_split('/\s+/Su', $this->description, 2);
|
||||||
|
|
||||||
|
$this->link = $parts[0];
|
||||||
|
|
||||||
|
$this->setDescription(isset($parts[1]) ? $parts[1] : $parts[0]);
|
||||||
|
|
||||||
|
$this->content = $content;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the link
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getLink()
|
||||||
|
{
|
||||||
|
return $this->link;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the link
|
||||||
|
*
|
||||||
|
* @param string $link The link
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLink($link)
|
||||||
|
{
|
||||||
|
$this->link = $link;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @method in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class MethodTag extends ReturnTag
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
protected $method_name = '';
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
protected $arguments = '';
|
||||||
|
|
||||||
|
/** @var bool */
|
||||||
|
protected $isStatic = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = '';
|
||||||
|
if ($this->isStatic) {
|
||||||
|
$this->content .= 'static ';
|
||||||
|
}
|
||||||
|
$this->content .= $this->type .
|
||||||
|
" {$this->method_name}({$this->arguments}) " .
|
||||||
|
$this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
Tag::setContent($content);
|
||||||
|
// 1. none or more whitespace
|
||||||
|
// 2. optionally the keyword "static" followed by whitespace
|
||||||
|
// 3. optionally a word with underscores followed by whitespace : as
|
||||||
|
// type for the return value
|
||||||
|
// 4. then optionally a word with underscores followed by () and
|
||||||
|
// whitespace : as method name as used by phpDocumentor
|
||||||
|
// 5. then a word with underscores, followed by ( and any character
|
||||||
|
// until a ) and whitespace : as method name with signature
|
||||||
|
// 6. any remaining text : as description
|
||||||
|
if (preg_match(
|
||||||
|
'/^
|
||||||
|
# Static keyword
|
||||||
|
# Declates a static method ONLY if type is also present
|
||||||
|
(?:
|
||||||
|
(static)
|
||||||
|
\s+
|
||||||
|
)?
|
||||||
|
# Return type
|
||||||
|
(?:
|
||||||
|
(
|
||||||
|
(?:[\w\|_\\\\]*\$this[\w\|_\\\\]*)
|
||||||
|
|
|
||||||
|
(?:
|
||||||
|
(?:[\w\|_\\\\]+(?:<[\s\S]*>)?)
|
||||||
|
# array notation
|
||||||
|
(?:\[\])*
|
||||||
|
)*
|
||||||
|
|
|
||||||
|
(?:\([\s\S]*\))?
|
||||||
|
)
|
||||||
|
\s+
|
||||||
|
)?
|
||||||
|
# Legacy method name (not captured)
|
||||||
|
(?:
|
||||||
|
[\w_]+\(\)\s+
|
||||||
|
)?
|
||||||
|
# Method name
|
||||||
|
([\w\|_\\\\]+)
|
||||||
|
# Arguments
|
||||||
|
\(([^\)]*)\)
|
||||||
|
\s*
|
||||||
|
# Description
|
||||||
|
(.*)
|
||||||
|
$/sux',
|
||||||
|
$this->description,
|
||||||
|
$matches
|
||||||
|
)) {
|
||||||
|
list(
|
||||||
|
,
|
||||||
|
$static,
|
||||||
|
$this->type,
|
||||||
|
$this->method_name,
|
||||||
|
$this->arguments,
|
||||||
|
$this->description
|
||||||
|
) = $matches;
|
||||||
|
if ($static) {
|
||||||
|
if (!$this->type) {
|
||||||
|
$this->type = 'static';
|
||||||
|
} else {
|
||||||
|
$this->isStatic = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!$this->type) {
|
||||||
|
$this->type = 'void';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->parsedDescription = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the name of this method.
|
||||||
|
*
|
||||||
|
* @param string $method_name The name of the method.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setMethodName($method_name)
|
||||||
|
{
|
||||||
|
$this->method_name = $method_name;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the method name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getMethodName()
|
||||||
|
{
|
||||||
|
return $this->method_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the arguments for this method.
|
||||||
|
*
|
||||||
|
* @param string $arguments A comma-separated arguments line.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function setArguments($arguments)
|
||||||
|
{
|
||||||
|
$this->arguments = $arguments;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an array containing each argument as array of type and name.
|
||||||
|
*
|
||||||
|
* Please note that the argument sub-array may only contain 1 element if no
|
||||||
|
* type was specified.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getArguments()
|
||||||
|
{
|
||||||
|
if (empty($this->arguments)) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$arguments = explode(',', $this->arguments);
|
||||||
|
foreach ($arguments as $key => $value) {
|
||||||
|
$arguments[$key] = explode(' ', trim($value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $arguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the method tag describes a static method or not.
|
||||||
|
*
|
||||||
|
* @return bool TRUE if the method declaration is for a static method, FALSE
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
public function isStatic()
|
||||||
|
{
|
||||||
|
return $this->isStatic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new value for whether the method is static or not.
|
||||||
|
*
|
||||||
|
* @param bool $isStatic The new value to set.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setIsStatic($isStatic)
|
||||||
|
{
|
||||||
|
$this->isStatic = $isStatic;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @param tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class ParamTag extends ReturnTag
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $variableName = '';
|
||||||
|
|
||||||
|
/** @var bool determines whether this is a variadic argument */
|
||||||
|
protected $isVariadic = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content
|
||||||
|
= "{$this->type} {$this->variableName} {$this->description}";
|
||||||
|
}
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
Tag::setContent($content);
|
||||||
|
|
||||||
|
$parts = [];
|
||||||
|
$rest = $this->description;
|
||||||
|
|
||||||
|
// parsing generics and closures to detect types
|
||||||
|
for($pos = 0, $stacks = []; $pos < strlen($rest); $pos++) {
|
||||||
|
$char = $rest[$pos];
|
||||||
|
|
||||||
|
if(in_array($char, ['<', '(', '[', '{'])) {
|
||||||
|
array_unshift($stacks, $char);
|
||||||
|
}
|
||||||
|
if(
|
||||||
|
($char === '>' && isset($stacks[0]) && $stacks[0] === '<')
|
||||||
|
|| ($char === ')' && isset($stacks[0]) && $stacks[0] === '(')
|
||||||
|
|| ($char === ']' && isset($stacks[0]) && $stacks[0] === '[')
|
||||||
|
|| ($char === '}' && isset($stacks[0]) && $stacks[0] === '{')
|
||||||
|
) {
|
||||||
|
array_shift($stacks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$stacks && preg_match('/\A(\s+)(.*)/su', substr($rest, $pos), $matches)) {
|
||||||
|
$parts[0] = substr($rest, 0, $pos);
|
||||||
|
$parts[1] = $matches[1];
|
||||||
|
$rest = $matches[2];
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
array_push($parts, ...preg_split('/(\s+)/u', $rest, 2, PREG_SPLIT_DELIM_CAPTURE));
|
||||||
|
|
||||||
|
// if the first item that is encountered is not a variable; it is a type
|
||||||
|
if (isset($parts[0])
|
||||||
|
&& (strlen($parts[0]) > 0)
|
||||||
|
&& ($parts[0][0] !== '$')
|
||||||
|
) {
|
||||||
|
$this->type = array_shift($parts);
|
||||||
|
array_shift($parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the next item starts with a $ or ...$ it must be the variable name
|
||||||
|
if (isset($parts[0])
|
||||||
|
&& (strlen($parts[0]) > 0)
|
||||||
|
&& ($parts[0][0] == '$' || substr($parts[0], 0, 4) === '...$')
|
||||||
|
) {
|
||||||
|
$this->variableName = array_shift($parts);
|
||||||
|
array_shift($parts);
|
||||||
|
|
||||||
|
if (substr($this->variableName, 0, 3) === '...') {
|
||||||
|
$this->isVariadic = true;
|
||||||
|
$this->variableName = substr($this->variableName, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setDescription(implode('', $parts));
|
||||||
|
|
||||||
|
$this->content = $content;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the variable's name.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getVariableName()
|
||||||
|
{
|
||||||
|
return $this->variableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the variable's name.
|
||||||
|
*
|
||||||
|
* @param string $name The new name for this variable.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setVariableName($name)
|
||||||
|
{
|
||||||
|
$this->variableName = $name;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this tag is variadic.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isVariadic()
|
||||||
|
{
|
||||||
|
return $this->isVariadic;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @property-read tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class PropertyReadTag extends PropertyTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @property tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class PropertyTag extends ParamTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @property-write tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class PropertyWriteTag extends PropertyTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
use Barryvdh\Reflection\DocBlock\Type\Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @return tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <[email protected]>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class ReturnTag extends Tag
|
||||||
|
{
|
||||||
|
/** @var string The raw type component. */
|
||||||
|
protected $type = '';
|
||||||
|
|
||||||
|
/** @var Collection The parsed type component. */
|
||||||
|
protected $types = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = "{$this->getType()} {$this->description}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
parent::setContent($content);
|
||||||
|
|
||||||
|
$parts = preg_split('/(?<!,)\s+/Su', $this->description, 2);
|
||||||
|
|
||||||
|
// any output is considered a type
|
||||||
|
$this->type = $parts[0];
|
||||||
|
$this->types = null;
|
||||||
|
|
||||||
|
$this->setDescription(isset($parts[1]) ? $parts[1] : '');
|
||||||
|
|
||||||
|
$this->content = $content;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unique types of the variable.
|
||||||
|
*
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
public function getTypes()
|
||||||
|
{
|
||||||
|
return $this->getTypesCollection()->getArrayCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type section of the variable.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getType()
|
||||||
|
{
|
||||||
|
return (string) $this->getTypesCollection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the type section of the variable
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setType($type)
|
||||||
|
{
|
||||||
|
$this->type = $type;
|
||||||
|
$this->types = null;
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a type to the type section of the variable
|
||||||
|
*
|
||||||
|
* @param string $type
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function addType($type)
|
||||||
|
{
|
||||||
|
$this->type = $this->type . Collection::OPERATOR_OR . $type;
|
||||||
|
$this->types = null;
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type collection.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
protected function getTypesCollection()
|
||||||
|
{
|
||||||
|
if (null === $this->types) {
|
||||||
|
$this->types = new Collection(
|
||||||
|
array($this->type),
|
||||||
|
$this->docblock ? $this->docblock->getContext() : null,
|
||||||
|
$this->docblock ? $this->docblock->getGenerics() : array()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $this->types;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @see tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class SeeTag extends Tag
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $refers = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = "{$this->refers} {$this->description}";
|
||||||
|
}
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
parent::setContent($content);
|
||||||
|
$parts = preg_split('/\s+/Su', $this->description, 2);
|
||||||
|
|
||||||
|
// any output is considered a type
|
||||||
|
$this->refers = $parts[0];
|
||||||
|
|
||||||
|
$this->setDescription(isset($parts[1]) ? $parts[1] : '');
|
||||||
|
|
||||||
|
$this->content = $content;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the structural element this tag refers to.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getReference()
|
||||||
|
{
|
||||||
|
return $this->refers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the structural element this tag refers to.
|
||||||
|
*
|
||||||
|
* @param string $refers The new type this tag refers to.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setReference($refers)
|
||||||
|
{
|
||||||
|
$this->refers = $refers;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag\VersionTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @since tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class SinceTag extends VersionTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,137 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @source tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class SourceTag extends Tag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int The starting line, relative to the structural element's
|
||||||
|
* location.
|
||||||
|
*/
|
||||||
|
protected $startingLine = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var int|null The number of lines, relative to the starting line. NULL
|
||||||
|
* means "to the end".
|
||||||
|
*/
|
||||||
|
protected $lineCount = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content
|
||||||
|
= "{$this->startingLine} {$this->lineCount} {$this->description}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
parent::setContent($content);
|
||||||
|
if (preg_match(
|
||||||
|
'/^
|
||||||
|
# Starting line
|
||||||
|
([1-9]\d*)
|
||||||
|
\s*
|
||||||
|
# Number of lines
|
||||||
|
(?:
|
||||||
|
((?1))
|
||||||
|
\s+
|
||||||
|
)?
|
||||||
|
# Description
|
||||||
|
(.*)
|
||||||
|
$/sux',
|
||||||
|
$this->description,
|
||||||
|
$matches
|
||||||
|
)) {
|
||||||
|
$this->startingLine = (int)$matches[1];
|
||||||
|
if (isset($matches[2]) && '' !== $matches[2]) {
|
||||||
|
$this->lineCount = (int)$matches[2];
|
||||||
|
}
|
||||||
|
$this->setDescription($matches[3]);
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the starting line.
|
||||||
|
*
|
||||||
|
* @return int The starting line, relative to the structural element's
|
||||||
|
* location.
|
||||||
|
*/
|
||||||
|
public function getStartingLine()
|
||||||
|
{
|
||||||
|
return $this->startingLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the starting line.
|
||||||
|
*
|
||||||
|
* @param int $startingLine The new starting line, relative to the
|
||||||
|
* structural element's location.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setStartingLine($startingLine)
|
||||||
|
{
|
||||||
|
$this->startingLine = $startingLine;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the number of lines.
|
||||||
|
*
|
||||||
|
* @return int|null The number of lines, relative to the starting line. NULL
|
||||||
|
* means "to the end".
|
||||||
|
*/
|
||||||
|
public function getLineCount()
|
||||||
|
{
|
||||||
|
return $this->lineCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the number of lines.
|
||||||
|
*
|
||||||
|
* @param int|null $lineCount The new number of lines, relative to the
|
||||||
|
* starting line. NULL means "to the end".
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setLineCount($lineCount)
|
||||||
|
{
|
||||||
|
$this->lineCount = $lineCount;
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Andrew Smith <espadav8@gmail.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @SuppressWarnings tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Andrew Smith <espadav8@gmail.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class SuppressWarningsTag extends Tag
|
||||||
|
{
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return "@{$this->getName()}{$this->getContent()}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @template tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author chack1172 <chack1172@gmail.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class TemplateTag extends ParamTag
|
||||||
|
{
|
||||||
|
/** @var string */
|
||||||
|
protected $templateName = null;
|
||||||
|
|
||||||
|
/** @var string|null */
|
||||||
|
protected $bound = null;
|
||||||
|
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = $this->templateName;
|
||||||
|
if (null !== $this->bound) {
|
||||||
|
$this->content .= ' of ' . $this->bound;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
$parts = explode(' of ', $content);
|
||||||
|
$this->templateName = $parts[0];
|
||||||
|
if (isset($parts[1])) {
|
||||||
|
$this->bound = $parts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setDescription('');
|
||||||
|
$this->content = $content;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the template name
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getTemplateName()
|
||||||
|
{
|
||||||
|
return $this->templateName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the template name
|
||||||
|
*
|
||||||
|
* @param string $templateName
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setTemplateName($templateName)
|
||||||
|
{
|
||||||
|
$this->templateName = $templateName;
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the bound type
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getBound()
|
||||||
|
{
|
||||||
|
return $this->bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the bound type
|
||||||
|
* @param string|null $bound
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setBound($bound)
|
||||||
|
{
|
||||||
|
$this->bound = $bound;
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @throws tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class ThrowsTag extends ReturnTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @uses tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class UsesTag extends SeeTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @var tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class VarTag extends ParamTag
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Tag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection class for a @version tag in a Docblock.
|
||||||
|
*
|
||||||
|
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class VersionTag extends Tag
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* PCRE regular expression matching a version vector.
|
||||||
|
* Assumes the "x" modifier.
|
||||||
|
*/
|
||||||
|
const REGEX_VECTOR = '(?:
|
||||||
|
# Normal release vectors.
|
||||||
|
\d\S*
|
||||||
|
|
|
||||||
|
# VCS version vectors. Per PHPCS, they are expected to
|
||||||
|
# follow the form of the VCS name, followed by ":", followed
|
||||||
|
# by the version vector itself.
|
||||||
|
# By convention, popular VCSes like CVS, SVN and GIT use "$"
|
||||||
|
# around the actual version vector.
|
||||||
|
[^\s\:]+\:\s*\$[^\$]+\$
|
||||||
|
)';
|
||||||
|
|
||||||
|
/** @var string The version vector. */
|
||||||
|
protected $version = '';
|
||||||
|
|
||||||
|
public function getContent()
|
||||||
|
{
|
||||||
|
if (null === $this->content) {
|
||||||
|
$this->content = "{$this->version} {$this->description}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function setContent($content)
|
||||||
|
{
|
||||||
|
parent::setContent($content);
|
||||||
|
|
||||||
|
if (preg_match(
|
||||||
|
'/^
|
||||||
|
# The version vector
|
||||||
|
(' . self::REGEX_VECTOR . ')
|
||||||
|
\s*
|
||||||
|
# The description
|
||||||
|
(.+)?
|
||||||
|
$/sux',
|
||||||
|
$this->description,
|
||||||
|
$matches
|
||||||
|
)) {
|
||||||
|
$this->version = $matches[1];
|
||||||
|
$this->setDescription(isset($matches[2]) ? $matches[2] : '');
|
||||||
|
$this->content = $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version section of the tag.
|
||||||
|
*
|
||||||
|
* @return string The version section of the tag.
|
||||||
|
*/
|
||||||
|
public function getVersion()
|
||||||
|
{
|
||||||
|
return $this->version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the version section of the tag.
|
||||||
|
*
|
||||||
|
* @param string $version The new version section of the tag.
|
||||||
|
* An invalid value will set an empty string.
|
||||||
|
*
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setVersion($version)
|
||||||
|
{
|
||||||
|
$this->version
|
||||||
|
= preg_match('/^' . self::REGEX_VECTOR . '$/ux', $version)
|
||||||
|
? $version
|
||||||
|
: '';
|
||||||
|
|
||||||
|
$this->content = null;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,327 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* phpDocumentor
|
||||||
|
*
|
||||||
|
* PHP Version 5.3
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Barryvdh\Reflection\DocBlock\Type;
|
||||||
|
|
||||||
|
use Barryvdh\Reflection\DocBlock\Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collection
|
||||||
|
*
|
||||||
|
* @author Mike van Riel <mike.vanriel@naenius.com>
|
||||||
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
||||||
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
||||||
|
* @link http://phpdoc.org
|
||||||
|
*/
|
||||||
|
class Collection extends \ArrayObject
|
||||||
|
{
|
||||||
|
/** @var string Definition of the OR operator for types */
|
||||||
|
const OPERATOR_OR = '|';
|
||||||
|
|
||||||
|
/** @var string Definition of the ARRAY operator for types */
|
||||||
|
const OPERATOR_ARRAY = '[]';
|
||||||
|
|
||||||
|
/** @var string Definition of the NAMESPACE operator in PHP */
|
||||||
|
const OPERATOR_NAMESPACE = '\\';
|
||||||
|
|
||||||
|
/** @var string[] List of recognized keywords */
|
||||||
|
protected static $keywords = array(
|
||||||
|
'string', 'int', 'integer', 'bool', 'boolean', 'float', 'double',
|
||||||
|
'object', 'mixed', 'array', 'resource', 'void', 'null', 'scalar',
|
||||||
|
'callback', 'callable', 'false', 'true', 'self', '$this', 'static',
|
||||||
|
'array-key', 'number', 'iterable', 'pure-callable', 'closed-resource',
|
||||||
|
'open-resource', 'positive-int', 'negative-int', 'non-positive-int',
|
||||||
|
'non-negative-int', 'non-zero-int', 'non-empty-array', 'list',
|
||||||
|
'non-empty-list', 'key-of', 'value-of', 'template-type', 'class-string',
|
||||||
|
'callable-string', 'numeric-string', 'non-empty-string',
|
||||||
|
'non-falsy-string', 'literal-string', 'lowercase-string', 'never',
|
||||||
|
'never-return', 'never-returns', 'no-return', 'int-mask', 'int-mask-of'
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current invoking location.
|
||||||
|
*
|
||||||
|
* This is used to prepend to type with a relative location.
|
||||||
|
* May also be 'default' or 'global', in which case they are ignored.
|
||||||
|
*
|
||||||
|
* @var Context
|
||||||
|
*/
|
||||||
|
protected $context = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of generics types
|
||||||
|
*
|
||||||
|
* @var string[]
|
||||||
|
*/
|
||||||
|
protected $generics = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the namespace and aliases; uses that to add and expand the
|
||||||
|
* given types.
|
||||||
|
*
|
||||||
|
* @param string[] $types Array containing a list of types to add to this
|
||||||
|
* container.
|
||||||
|
* @param Context $location The current invoking location.
|
||||||
|
*/
|
||||||
|
public function __construct(
|
||||||
|
array $types = array(),
|
||||||
|
?Context $context = null,
|
||||||
|
array $generics = array()
|
||||||
|
) {
|
||||||
|
$this->context = null === $context ? new Context() : $context;
|
||||||
|
$this->generics = array_merge($this->context->getGenerics(), $generics);
|
||||||
|
|
||||||
|
foreach ($types as $type) {
|
||||||
|
$this->add($type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current invoking location.
|
||||||
|
*
|
||||||
|
* @return Context
|
||||||
|
*/
|
||||||
|
public function getContext()
|
||||||
|
{
|
||||||
|
return $this->context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new type to the collection and expands it if it contains a
|
||||||
|
* relative namespace.
|
||||||
|
*
|
||||||
|
* If a class in the type contains a relative namespace than this collection
|
||||||
|
* will try to expand that into a FQCN.
|
||||||
|
*
|
||||||
|
* @param string $type A 'Type' as defined in the phpDocumentor
|
||||||
|
* documentation.
|
||||||
|
*
|
||||||
|
* @throws \InvalidArgumentException if a non-string argument is passed.
|
||||||
|
*
|
||||||
|
* @see http://phpdoc.org/docs/latest/for-users/types.html for the
|
||||||
|
* definition of a type.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add($type)
|
||||||
|
{
|
||||||
|
if (!is_string($type)) {
|
||||||
|
throw new \InvalidArgumentException(
|
||||||
|
'A type should be represented by a string, received: '
|
||||||
|
.var_export($type, true)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// separate the type by the OR operator
|
||||||
|
$type_parts = $this->explode($type);
|
||||||
|
foreach ($type_parts as $part) {
|
||||||
|
$expanded_type = $this->expand($part);
|
||||||
|
if ($expanded_type) {
|
||||||
|
$this[] = $expanded_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string representation of the collection.
|
||||||
|
*
|
||||||
|
* @return string The resolved types across the collection, separated with
|
||||||
|
* {@link self::OPERATOR_OR}.
|
||||||
|
*/
|
||||||
|
public function __toString()
|
||||||
|
{
|
||||||
|
return implode(self::OPERATOR_OR, $this->getArrayCopy());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyzes the given union of types and returns separated by OR operator
|
||||||
|
* single types.
|
||||||
|
*
|
||||||
|
* @param string $type The type or union of types
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function explode($type)
|
||||||
|
{
|
||||||
|
$type_parts = [];
|
||||||
|
$curr_type = '';
|
||||||
|
$nest_level = 0;
|
||||||
|
|
||||||
|
foreach (str_split($type) as $char) {
|
||||||
|
if ($char === self::OPERATOR_OR && $nest_level === 0) {
|
||||||
|
$type_parts[] = $curr_type;
|
||||||
|
$curr_type = '';
|
||||||
|
} else {
|
||||||
|
if (in_array($char, ['<', '(', '[', '{'])) {
|
||||||
|
$nest_level++;
|
||||||
|
} else if (in_array($char, ['>', ')', ']', '}'])) {
|
||||||
|
$nest_level--;
|
||||||
|
}
|
||||||
|
|
||||||
|
$curr_type .= $char;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_parts[] = $curr_type;
|
||||||
|
|
||||||
|
return $type_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyzes the given type and returns the FQCN variant.
|
||||||
|
*
|
||||||
|
* When a type is provided this method checks whether it is not a keyword or
|
||||||
|
* Fully Qualified Class Name. If so it will use the given namespace and
|
||||||
|
* aliases to expand the type to a FQCN representation.
|
||||||
|
*
|
||||||
|
* This method only works as expected if the namespace and aliases are set;
|
||||||
|
* no dynamic reflection is being performed here.
|
||||||
|
*
|
||||||
|
* @param string $type The relative or absolute type.
|
||||||
|
*
|
||||||
|
* @uses getNamespace to determine with what to prefix the type name.
|
||||||
|
* @uses getNamespaceAliases to check whether the first part of the relative
|
||||||
|
* type name should not be replaced with another namespace.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function expand($type)
|
||||||
|
{
|
||||||
|
$type = trim($type);
|
||||||
|
if (!$type) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for generics values and array shapes
|
||||||
|
if (preg_match('/^[\w-]+(<.+>|\[.+\]|{.+})$/', $type)) {
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for callable types
|
||||||
|
if (preg_match('/\(.*?(?=\:)/', $type)) {
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($type[0] === '(') {
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Literal strings
|
||||||
|
if ($type[0] === '"' || $type[0] === "'") {
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isTypeAnArray($type)) {
|
||||||
|
return $this->expand(substr($type, 0, -2)) . self::OPERATOR_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isRelativeType($type) && !$this->isTypeAKeyword($type) && !$this->isTypeAGeneric($type)) {
|
||||||
|
|
||||||
|
if($this->shouldBeAbsolute($type)){
|
||||||
|
return self::OPERATOR_NAMESPACE . $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_parts = explode(self::OPERATOR_NAMESPACE, $type, 2);
|
||||||
|
|
||||||
|
$namespace_aliases = $this->context->getNamespaceAliases();
|
||||||
|
// if the first segment is not an alias; prepend namespace name and
|
||||||
|
// return
|
||||||
|
if (!isset($namespace_aliases[$type_parts[0]]) &&
|
||||||
|
!isset($namespace_aliases[strstr($type_parts[0], '::', true)])) {
|
||||||
|
$namespace = $this->context->getNamespace();
|
||||||
|
if ('' !== $namespace) {
|
||||||
|
$namespace .= self::OPERATOR_NAMESPACE;
|
||||||
|
}
|
||||||
|
return self::OPERATOR_NAMESPACE . $namespace . $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strpos($type_parts[0], '::')) {
|
||||||
|
$type_parts[] = strstr($type_parts[0], '::');
|
||||||
|
$type_parts[0] = $namespace_aliases[strstr($type_parts[0], '::', true)];
|
||||||
|
return implode('', $type_parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_parts[0] = $namespace_aliases[$type_parts[0]];
|
||||||
|
$type = implode(self::OPERATOR_NAMESPACE, $type_parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects whether the given type represents an array.
|
||||||
|
*
|
||||||
|
* @param string $type A relative or absolute type as defined in the
|
||||||
|
* phpDocumentor documentation.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isTypeAnArray($type)
|
||||||
|
{
|
||||||
|
return substr($type, -2) === self::OPERATOR_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects whether the given type represents a PHPDoc keyword.
|
||||||
|
*
|
||||||
|
* @param string $type A relative or absolute type as defined in the
|
||||||
|
* phpDocumentor documentation.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isTypeAKeyword($type)
|
||||||
|
{
|
||||||
|
return in_array(strtolower($type), static::$keywords, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects whether the given type represents a relative or absolute path.
|
||||||
|
*
|
||||||
|
* This method will detect keywords as being absolute; even though they are
|
||||||
|
* not preceeded by a namespace separator.
|
||||||
|
*
|
||||||
|
* @param string $type A relative or absolute type as defined in the
|
||||||
|
* phpDocumentor documentation.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isRelativeType($type)
|
||||||
|
{
|
||||||
|
return ($type[0] !== self::OPERATOR_NAMESPACE)
|
||||||
|
|| $this->isTypeAKeyword($type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects whether the given type represents a generic.
|
||||||
|
*
|
||||||
|
* @param string $type A relative or absolute type as defined in the
|
||||||
|
* phpDocumentor documentation.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function isTypeAGeneric($type)
|
||||||
|
{
|
||||||
|
return in_array($type, $this->generics, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects if the type should actually be absolute, by checking if it exists.
|
||||||
|
*
|
||||||
|
* @param string $type A relative or absolute type as defined in the
|
||||||
|
* phpDocumentor documentation.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function shouldBeAbsolute($type){
|
||||||
|
return class_exists($type);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\TagWithType;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
final class DocBlock
|
|
||||||
{
|
|
||||||
/** @var string The opening line for this docblock. */
|
|
||||||
private string $summary;
|
|
||||||
|
|
||||||
/** @var DocBlock\Description The actual description for this docblock. */
|
|
||||||
private DocBlock\Description $description;
|
|
||||||
|
|
||||||
/** @var Tag[] An array containing all the tags in this docblock; except inline. */
|
|
||||||
private array $tags = [];
|
|
||||||
|
|
||||||
/** @var Types\Context|null Information about the context of this DocBlock. */
|
|
||||||
private ?Types\Context $context = null;
|
|
||||||
|
|
||||||
/** @var Location|null Information about the location of this DocBlock. */
|
|
||||||
private ?Location $location = null;
|
|
||||||
|
|
||||||
/** @var bool Is this DocBlock (the start of) a template? */
|
|
||||||
private bool $isTemplateStart;
|
|
||||||
|
|
||||||
/** @var bool Does this DocBlock signify the end of a DocBlock template? */
|
|
||||||
private bool $isTemplateEnd;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DocBlock\Tag[] $tags
|
|
||||||
* @param Types\Context $context The context in which the DocBlock occurs.
|
|
||||||
* @param Location $location The location within the file that this DocBlock occurs in.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
string $summary = '',
|
|
||||||
?DocBlock\Description $description = null,
|
|
||||||
array $tags = [],
|
|
||||||
?Types\Context $context = null,
|
|
||||||
?Location $location = null,
|
|
||||||
bool $isTemplateStart = false,
|
|
||||||
bool $isTemplateEnd = false
|
|
||||||
) {
|
|
||||||
Assert::allIsInstanceOf($tags, Tag::class);
|
|
||||||
|
|
||||||
$this->summary = $summary;
|
|
||||||
$this->description = $description ?: new DocBlock\Description('');
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$this->addTag($tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->context = $context;
|
|
||||||
$this->location = $location;
|
|
||||||
|
|
||||||
$this->isTemplateEnd = $isTemplateEnd;
|
|
||||||
$this->isTemplateStart = $isTemplateStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSummary(): string
|
|
||||||
{
|
|
||||||
return $this->summary;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDescription(): DocBlock\Description
|
|
||||||
{
|
|
||||||
return $this->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current context.
|
|
||||||
*/
|
|
||||||
public function getContext(): ?Types\Context
|
|
||||||
{
|
|
||||||
return $this->context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current location.
|
|
||||||
*/
|
|
||||||
public function getLocation(): ?Location
|
|
||||||
{
|
|
||||||
return $this->location;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this DocBlock is the start of a Template section.
|
|
||||||
*
|
|
||||||
* A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
|
|
||||||
* (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
|
|
||||||
*
|
|
||||||
* An example of such an opening is:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* /**#@+
|
|
||||||
* * My DocBlock
|
|
||||||
* * /
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
|
|
||||||
* elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
|
|
||||||
*
|
|
||||||
* @see self::isTemplateEnd() for the check whether a closing marker was provided.
|
|
||||||
*/
|
|
||||||
public function isTemplateStart(): bool
|
|
||||||
{
|
|
||||||
return $this->isTemplateStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this DocBlock is the end of a Template section.
|
|
||||||
*
|
|
||||||
* @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
|
|
||||||
*/
|
|
||||||
public function isTemplateEnd(): bool
|
|
||||||
{
|
|
||||||
return $this->isTemplateEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the tags for this DocBlock.
|
|
||||||
*
|
|
||||||
* @return Tag[]
|
|
||||||
*/
|
|
||||||
public function getTags(): array
|
|
||||||
{
|
|
||||||
return $this->tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of tags matching the given name. If no tags are found
|
|
||||||
* an empty array is returned.
|
|
||||||
*
|
|
||||||
* @param string $name String to search by.
|
|
||||||
*
|
|
||||||
* @return Tag[]
|
|
||||||
*/
|
|
||||||
public function getTagsByName(string $name): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($this->getTags() as $tag) {
|
|
||||||
if ($tag->getName() !== $name) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result[] = $tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an array of tags with type matching the given name. If no tags are found
|
|
||||||
* an empty array is returned.
|
|
||||||
*
|
|
||||||
* @param string $name String to search by.
|
|
||||||
*
|
|
||||||
* @return TagWithType[]
|
|
||||||
*/
|
|
||||||
public function getTagsWithTypeByName(string $name): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
|
|
||||||
foreach ($this->getTagsByName($name) as $tag) {
|
|
||||||
if (!$tag instanceof TagWithType) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$result[] = $tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a tag of a certain type is present in this DocBlock.
|
|
||||||
*
|
|
||||||
* @param string $name Tag name to check for.
|
|
||||||
*/
|
|
||||||
public function hasTag(string $name): bool
|
|
||||||
{
|
|
||||||
foreach ($this->getTags() as $tag) {
|
|
||||||
if ($tag->getName() === $name) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a tag from this DocBlock.
|
|
||||||
*
|
|
||||||
* @param Tag $tagToRemove The tag to remove.
|
|
||||||
*/
|
|
||||||
public function removeTag(Tag $tagToRemove): void
|
|
||||||
{
|
|
||||||
foreach ($this->tags as $key => $tag) {
|
|
||||||
if ($tag === $tagToRemove) {
|
|
||||||
unset($this->tags[$key]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a tag to this DocBlock.
|
|
||||||
*
|
|
||||||
* @param Tag $tag The tag to add.
|
|
||||||
*/
|
|
||||||
private function addTag(Tag $tag): void
|
|
||||||
{
|
|
||||||
$this->tags[] = $tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter;
|
|
||||||
|
|
||||||
use function vsprintf;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Object representing to description for a DocBlock.
|
|
||||||
*
|
|
||||||
* A Description object can consist of plain text but can also include tags. A Description Formatter can then combine
|
|
||||||
* a body template with sprintf-style placeholders together with formatted tags in order to reconstitute a complete
|
|
||||||
* description text using the format that you would prefer.
|
|
||||||
*
|
|
||||||
* Because parsing a Description text can be a verbose process this is handled by the {@see DescriptionFactory}. It is
|
|
||||||
* thus recommended to use that to create a Description object, like this:
|
|
||||||
*
|
|
||||||
* $description = $descriptionFactory->create('This is a {@see Description}', $context);
|
|
||||||
*
|
|
||||||
* The description factory will interpret the given body and create a body template and list of tags from them, and pass
|
|
||||||
* that onto the constructor if this class.
|
|
||||||
*
|
|
||||||
* > The $context variable is a class of type {@see \phpDocumentor\Reflection\Types\Context} and contains the namespace
|
|
||||||
* > and the namespace aliases that apply to this DocBlock. These are used by the Factory to resolve and expand partial
|
|
||||||
* > type names and FQSENs.
|
|
||||||
*
|
|
||||||
* If you do not want to use the DescriptionFactory you can pass a body template and tag listing like this:
|
|
||||||
*
|
|
||||||
* $description = new Description(
|
|
||||||
* 'This is a %1$s',
|
|
||||||
* [ new See(new Fqsen('\phpDocumentor\Reflection\DocBlock\Description')) ]
|
|
||||||
* );
|
|
||||||
*
|
|
||||||
* It is generally recommended to use the Factory as that will also apply escaping rules, while the Description object
|
|
||||||
* is mainly responsible for rendering.
|
|
||||||
*
|
|
||||||
* @see DescriptionFactory to create a new Description.
|
|
||||||
* @see Tags\Formatter for the formatting of the body and tags.
|
|
||||||
*/
|
|
||||||
class Description
|
|
||||||
{
|
|
||||||
private string $bodyTemplate;
|
|
||||||
|
|
||||||
/** @var Tag[] */
|
|
||||||
private array $tags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a Description with its body (template) and a listing of the tags used in the body template.
|
|
||||||
*
|
|
||||||
* @param Tag[] $tags
|
|
||||||
*/
|
|
||||||
public function __construct(string $bodyTemplate, array $tags = [])
|
|
||||||
{
|
|
||||||
$this->bodyTemplate = $bodyTemplate;
|
|
||||||
$this->tags = $tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the body template.
|
|
||||||
*/
|
|
||||||
public function getBodyTemplate(): string
|
|
||||||
{
|
|
||||||
return $this->bodyTemplate;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the tags for this DocBlock.
|
|
||||||
*
|
|
||||||
* @return Tag[]
|
|
||||||
*/
|
|
||||||
public function getTags(): array
|
|
||||||
{
|
|
||||||
return $this->tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders this description as a string where the provided formatter will format the tags in the expected string
|
|
||||||
* format.
|
|
||||||
*/
|
|
||||||
public function render(?Formatter $formatter = null): string
|
|
||||||
{
|
|
||||||
if ($this->tags === []) {
|
|
||||||
return vsprintf($this->bodyTemplate, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($formatter === null) {
|
|
||||||
$formatter = new PassthroughFormatter();
|
|
||||||
}
|
|
||||||
|
|
||||||
$tags = [];
|
|
||||||
foreach ($this->tags as $tag) {
|
|
||||||
$tags[] = '{' . $formatter->format($tag) . '}';
|
|
||||||
}
|
|
||||||
|
|
||||||
return vsprintf($this->bodyTemplate, $tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a plain string representation of this description.
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
return $this->render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
use phpDocumentor\Reflection\Utils;
|
|
||||||
|
|
||||||
use function count;
|
|
||||||
use function implode;
|
|
||||||
use function ltrim;
|
|
||||||
use function min;
|
|
||||||
use function str_replace;
|
|
||||||
use function strlen;
|
|
||||||
use function strpos;
|
|
||||||
use function substr;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
use const PREG_SPLIT_DELIM_CAPTURE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new Description object given a body of text.
|
|
||||||
*
|
|
||||||
* Descriptions in phpDocumentor are somewhat complex entities as they can contain one or more tags inside their
|
|
||||||
* body that can be replaced with a readable output. The replacing is done by passing a Formatter object to the
|
|
||||||
* Description object's `render` method.
|
|
||||||
*
|
|
||||||
* In addition to the above does a Description support two types of escape sequences:
|
|
||||||
*
|
|
||||||
* 1. `{@}` to escape the `@` character to prevent it from being interpreted as part of a tag, i.e. `{{@}link}`
|
|
||||||
* 2. `{}` to escape the `}` character, this can be used if you want to use the `}` character in the description
|
|
||||||
* of an inline tag.
|
|
||||||
*
|
|
||||||
* If a body consists of multiple lines then this factory will also remove any superfluous whitespace at the beginning
|
|
||||||
* of each line while maintaining any indentation that is used. This will prevent formatting parsers from tripping
|
|
||||||
* over unexpected spaces as can be observed with tag descriptions.
|
|
||||||
*/
|
|
||||||
class DescriptionFactory
|
|
||||||
{
|
|
||||||
private Factory $tagFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes this factory with the means to construct (inline) tags.
|
|
||||||
*/
|
|
||||||
public function __construct(Factory $tagFactory)
|
|
||||||
{
|
|
||||||
$this->tagFactory = $tagFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the parsed text of this description.
|
|
||||||
*/
|
|
||||||
public function create(string $contents, ?TypeContext $context = null): Description
|
|
||||||
{
|
|
||||||
$tokens = $this->lex($contents);
|
|
||||||
$count = count($tokens);
|
|
||||||
$tagCount = 0;
|
|
||||||
$tags = [];
|
|
||||||
|
|
||||||
for ($i = 1; $i < $count; $i += 2) {
|
|
||||||
$tags[] = $this->tagFactory->create($tokens[$i], $context);
|
|
||||||
$tokens[$i] = '%' . ++$tagCount . '$s';
|
|
||||||
}
|
|
||||||
|
|
||||||
//In order to allow "literal" inline tags, the otherwise invalid
|
|
||||||
//sequence "{@}" is changed to "@", and "{}" is changed to "}".
|
|
||||||
//"%" is escaped to "%%" because of vsprintf.
|
|
||||||
//See unit tests for examples.
|
|
||||||
for ($i = 0; $i < $count; $i += 2) {
|
|
||||||
$tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Description(implode('', $tokens), $tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Strips the contents from superfluous whitespace and splits the description into a series of tokens.
|
|
||||||
*
|
|
||||||
* @return string[] A series of tokens of which the description text is composed.
|
|
||||||
*/
|
|
||||||
private function lex(string $contents): array
|
|
||||||
{
|
|
||||||
$contents = $this->removeSuperfluousStartingWhitespace($contents);
|
|
||||||
|
|
||||||
// performance optimalization; if there is no inline tag, don't bother splitting it up.
|
|
||||||
if (strpos($contents, '{@') === false) {
|
|
||||||
return [$contents];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Utils::pregSplit(
|
|
||||||
'/\{
|
|
||||||
# "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally.
|
|
||||||
(?!@\})
|
|
||||||
# We want to capture the whole tag line, but without the inline tag delimiters.
|
|
||||||
(\@
|
|
||||||
# Match everything up to the next delimiter.
|
|
||||||
[^{}]*
|
|
||||||
# Nested inline tag content should not be captured, or it will appear in the result separately.
|
|
||||||
(?:
|
|
||||||
# Match nested inline tags.
|
|
||||||
(?:
|
|
||||||
# Because we did not catch the tag delimiters earlier, we must be explicit with them here.
|
|
||||||
# Notice that this also matches "{}", as a way to later introduce it as an escape sequence.
|
|
||||||
\{(?1)?\}
|
|
||||||
|
|
|
||||||
# Make sure we match hanging "{".
|
|
||||||
\{
|
|
||||||
)
|
|
||||||
# Match content after the nested inline tag.
|
|
||||||
[^{}]*
|
|
||||||
)* # If there are more inline tags, match them as well. We use "*" since there may not be any
|
|
||||||
# nested inline tags.
|
|
||||||
)
|
|
||||||
\}/Sux',
|
|
||||||
$contents,
|
|
||||||
0,
|
|
||||||
PREG_SPLIT_DELIM_CAPTURE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the superfluous from a multi-line description.
|
|
||||||
*
|
|
||||||
* When a description has more than one line then it can happen that the second and subsequent lines have an
|
|
||||||
* additional indentation. This is commonly in use with tags like this:
|
|
||||||
*
|
|
||||||
* {@}since 1.1.0 This is an example
|
|
||||||
* description where we have an
|
|
||||||
* indentation in the second and
|
|
||||||
* subsequent lines.
|
|
||||||
*
|
|
||||||
* If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent
|
|
||||||
* lines and this may cause rendering issues when, for example, using a Markdown converter.
|
|
||||||
*/
|
|
||||||
private function removeSuperfluousStartingWhitespace(string $contents): string
|
|
||||||
{
|
|
||||||
$lines = Utils::pregSplit("/\r\n?|\n/", $contents);
|
|
||||||
|
|
||||||
// if there is only one line then we don't have lines with superfluous whitespace and
|
|
||||||
// can use the contents as-is
|
|
||||||
if (count($lines) <= 1) {
|
|
||||||
return $contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine how many whitespace characters need to be stripped
|
|
||||||
$startingSpaceCount = 9999999;
|
|
||||||
for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) {
|
|
||||||
// lines with a no length do not count as they are not indented at all
|
|
||||||
if (trim($lines[$i]) === '') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// determine the number of prefixing spaces by checking the difference in line length before and after
|
|
||||||
// an ltrim
|
|
||||||
$startingSpaceCount = min($startingSpaceCount, strlen($lines[$i]) - strlen(ltrim($lines[$i])));
|
|
||||||
}
|
|
||||||
|
|
||||||
// strip the number of spaces from each line
|
|
||||||
if ($startingSpaceCount > 0) {
|
|
||||||
for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) {
|
|
||||||
$lines[$i] = substr($lines[$i], $startingSpaceCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode("\n", $lines);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Example;
|
|
||||||
|
|
||||||
use function array_slice;
|
|
||||||
use function file;
|
|
||||||
use function getcwd;
|
|
||||||
use function implode;
|
|
||||||
use function is_readable;
|
|
||||||
use function rtrim;
|
|
||||||
use function sprintf;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
use const DIRECTORY_SEPARATOR;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class used to find an example file's location based on a given ExampleDescriptor.
|
|
||||||
*/
|
|
||||||
class ExampleFinder
|
|
||||||
{
|
|
||||||
private string $sourceDirectory = '';
|
|
||||||
|
|
||||||
/** @var string[] */
|
|
||||||
private array $exampleDirectories = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to find the example contents for the given descriptor.
|
|
||||||
*/
|
|
||||||
public function find(Example $example): string
|
|
||||||
{
|
|
||||||
$filename = $example->getFilePath();
|
|
||||||
|
|
||||||
$file = $this->getExampleFileContents($filename);
|
|
||||||
if ($file === null) {
|
|
||||||
return sprintf('** File not found : %s **', $filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the project's root directory where an 'examples' folder can be expected.
|
|
||||||
*/
|
|
||||||
public function setSourceDirectory(string $directory = ''): void
|
|
||||||
{
|
|
||||||
$this->sourceDirectory = $directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the project's root directory where an 'examples' folder can be expected.
|
|
||||||
*/
|
|
||||||
public function getSourceDirectory(): string
|
|
||||||
{
|
|
||||||
return $this->sourceDirectory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a series of directories that may contain examples.
|
|
||||||
*
|
|
||||||
* @param string[] $directories
|
|
||||||
*/
|
|
||||||
public function setExampleDirectories(array $directories): void
|
|
||||||
{
|
|
||||||
$this->exampleDirectories = $directories;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a series of directories that may contain examples.
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
public function getExampleDirectories(): array
|
|
||||||
{
|
|
||||||
return $this->exampleDirectories;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to find the requested example file and returns its contents or null if no file was found.
|
|
||||||
*
|
|
||||||
* This method will try several methods in search of the given example file, the first one it encounters is
|
|
||||||
* returned:
|
|
||||||
*
|
|
||||||
* 1. Iterates through all examples folders for the given filename
|
|
||||||
* 2. Checks the source folder for the given filename
|
|
||||||
* 3. Checks the 'examples' folder in the current working directory for examples
|
|
||||||
* 4. Checks the path relative to the current working directory for the given filename
|
|
||||||
*
|
|
||||||
* @return string[] all lines of the example file
|
|
||||||
*/
|
|
||||||
private function getExampleFileContents(string $filename): ?array
|
|
||||||
{
|
|
||||||
$normalizedPath = null;
|
|
||||||
|
|
||||||
foreach ($this->exampleDirectories as $directory) {
|
|
||||||
$exampleFileFromConfig = $this->constructExamplePath($directory, $filename);
|
|
||||||
if (is_readable($exampleFileFromConfig)) {
|
|
||||||
$normalizedPath = $exampleFileFromConfig;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($normalizedPath === null) {
|
|
||||||
if (is_readable($this->getExamplePathFromSource($filename))) {
|
|
||||||
$normalizedPath = $this->getExamplePathFromSource($filename);
|
|
||||||
} elseif (is_readable($this->getExamplePathFromExampleDirectory($filename))) {
|
|
||||||
$normalizedPath = $this->getExamplePathFromExampleDirectory($filename);
|
|
||||||
} elseif (is_readable($filename)) {
|
|
||||||
$normalizedPath = $filename;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$lines = $normalizedPath !== null && is_readable($normalizedPath) ? file($normalizedPath) : false;
|
|
||||||
|
|
||||||
return $lines !== false ? $lines : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get example filepath based on the example directory inside your project.
|
|
||||||
*/
|
|
||||||
private function getExamplePathFromExampleDirectory(string $file): string
|
|
||||||
{
|
|
||||||
return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a path to the example file in the given directory..
|
|
||||||
*/
|
|
||||||
private function constructExamplePath(string $directory, string $file): string
|
|
||||||
{
|
|
||||||
return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get example filepath based on sourcecode.
|
|
||||||
*/
|
|
||||||
private function getExamplePathFromSource(string $file): string
|
|
||||||
{
|
|
||||||
return sprintf(
|
|
||||||
'%s%s%s',
|
|
||||||
trim($this->getSourceDirectory(), '\\/'),
|
|
||||||
DIRECTORY_SEPARATOR,
|
|
||||||
trim($file, '"')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter;
|
|
||||||
|
|
||||||
use function sprintf;
|
|
||||||
use function str_repeat;
|
|
||||||
use function str_replace;
|
|
||||||
use function strlen;
|
|
||||||
use function wordwrap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a DocBlock back from an object to a complete DocComment including Asterisks.
|
|
||||||
*/
|
|
||||||
class Serializer
|
|
||||||
{
|
|
||||||
/** @var string The string to indent the comment with. */
|
|
||||||
protected string $indentString = ' ';
|
|
||||||
|
|
||||||
/** @var int The number of times the indent string is repeated. */
|
|
||||||
protected int $indent = 0;
|
|
||||||
|
|
||||||
/** @var bool Whether to indent the first line with the given indent amount and string. */
|
|
||||||
protected bool $isFirstLineIndented = true;
|
|
||||||
|
|
||||||
/** @var int|null The max length of a line. */
|
|
||||||
protected ?int $lineLength = null;
|
|
||||||
|
|
||||||
/** @var Formatter A custom tag formatter. */
|
|
||||||
protected Formatter $tagFormatter;
|
|
||||||
private string $lineEnding;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a Serializer instance.
|
|
||||||
*
|
|
||||||
* @param int $indent The number of times the indent string is repeated.
|
|
||||||
* @param string $indentString The string to indent the comment with.
|
|
||||||
* @param bool $indentFirstLine Whether to indent the first line.
|
|
||||||
* @param int|null $lineLength The max length of a line or NULL to disable line wrapping.
|
|
||||||
* @param Formatter $tagFormatter A custom tag formatter, defaults to PassthroughFormatter.
|
|
||||||
* @param string $lineEnding Line ending used in the output, by default \n is used.
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
int $indent = 0,
|
|
||||||
string $indentString = ' ',
|
|
||||||
bool $indentFirstLine = true,
|
|
||||||
?int $lineLength = null,
|
|
||||||
?Formatter $tagFormatter = null,
|
|
||||||
string $lineEnding = "\n"
|
|
||||||
) {
|
|
||||||
$this->indent = $indent;
|
|
||||||
$this->indentString = $indentString;
|
|
||||||
$this->isFirstLineIndented = $indentFirstLine;
|
|
||||||
$this->lineLength = $lineLength;
|
|
||||||
$this->tagFormatter = $tagFormatter ?: new PassthroughFormatter();
|
|
||||||
$this->lineEnding = $lineEnding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a DocBlock comment.
|
|
||||||
*
|
|
||||||
* @param DocBlock $docblock The DocBlock to serialize.
|
|
||||||
*
|
|
||||||
* @return string The serialized doc block.
|
|
||||||
*/
|
|
||||||
public function getDocComment(DocBlock $docblock): string
|
|
||||||
{
|
|
||||||
$indent = str_repeat($this->indentString, $this->indent);
|
|
||||||
$firstIndent = $this->isFirstLineIndented ? $indent : '';
|
|
||||||
// 3 === strlen(' * ')
|
|
||||||
$wrapLength = $this->lineLength !== null ? $this->lineLength - strlen($indent) - 3 : null;
|
|
||||||
|
|
||||||
$text = $this->removeTrailingSpaces(
|
|
||||||
$indent,
|
|
||||||
$this->addAsterisksForEachLine(
|
|
||||||
$indent,
|
|
||||||
$this->getSummaryAndDescriptionTextBlock($docblock, $wrapLength)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$comment = $firstIndent . "/**\n";
|
|
||||||
if ($text) {
|
|
||||||
$comment .= $indent . ' * ' . $text . "\n";
|
|
||||||
$comment .= $indent . " *\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
$comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment);
|
|
||||||
|
|
||||||
return str_replace("\n", $this->lineEnding, $comment . $indent . ' */');
|
|
||||||
}
|
|
||||||
|
|
||||||
private function removeTrailingSpaces(string $indent, string $text): string
|
|
||||||
{
|
|
||||||
return str_replace(
|
|
||||||
sprintf("\n%s * \n", $indent),
|
|
||||||
sprintf("\n%s *\n", $indent),
|
|
||||||
$text
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addAsterisksForEachLine(string $indent, string $text): string
|
|
||||||
{
|
|
||||||
return str_replace(
|
|
||||||
"\n",
|
|
||||||
sprintf("\n%s * ", $indent),
|
|
||||||
$text
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, ?int $wrapLength): string
|
|
||||||
{
|
|
||||||
$text = $docblock->getSummary() . ((string) $docblock->getDescription() ? "\n\n" . $docblock->getDescription()
|
|
||||||
: '');
|
|
||||||
if ($wrapLength !== null) {
|
|
||||||
$text = wordwrap($text, $wrapLength);
|
|
||||||
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addTagBlock(DocBlock $docblock, ?int $wrapLength, string $indent, string $comment): string
|
|
||||||
{
|
|
||||||
foreach ($docblock->getTags() as $tag) {
|
|
||||||
$tagText = $this->tagFormatter->format($tag);
|
|
||||||
if ($wrapLength !== null) {
|
|
||||||
$tagText = wordwrap($tagText, $wrapLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tagText = str_replace(
|
|
||||||
"\n",
|
|
||||||
sprintf("\n%s * ", $indent),
|
|
||||||
$tagText
|
|
||||||
);
|
|
||||||
|
|
||||||
$comment .= sprintf("%s * %s\n", $indent, $tagText);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $comment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,392 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Author;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Covers;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\AbstractPHPStanFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\ExtendsFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\ImplementsFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\MethodFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\MixinFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyReadFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyWriteFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\ReturnFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\TemplateCovariantFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\TemplateFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\ThrowsFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\VarFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\See as SeeTag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Since;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Source;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Uses;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Version;
|
|
||||||
use phpDocumentor\Reflection\FqsenResolver;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
use ReflectionMethod;
|
|
||||||
use ReflectionNamedType;
|
|
||||||
use ReflectionParameter;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function array_key_exists;
|
|
||||||
use function array_merge;
|
|
||||||
use function array_slice;
|
|
||||||
use function call_user_func_array;
|
|
||||||
use function get_class;
|
|
||||||
use function is_object;
|
|
||||||
use function preg_match;
|
|
||||||
use function sprintf;
|
|
||||||
use function strpos;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a Tag object given the contents of a tag.
|
|
||||||
*
|
|
||||||
* This Factory is capable of determining the appropriate class for a tag and instantiate it using its `create`
|
|
||||||
* factory method. The `create` factory method of a Tag can have a variable number of arguments; this way you can
|
|
||||||
* pass the dependencies that you need to construct a tag object.
|
|
||||||
*
|
|
||||||
* > Important: each parameter in addition to the body variable for the `create` method must default to null, otherwise
|
|
||||||
* > it violates the constraint with the interface; it is recommended to use the {@see Assert::notNull()} method to
|
|
||||||
* > verify that a dependency is actually passed.
|
|
||||||
*
|
|
||||||
* This Factory also features a Service Locator component that is used to pass the right dependencies to the
|
|
||||||
* `create` method of a tag; each dependency should be registered as a service or as a parameter.
|
|
||||||
*
|
|
||||||
* When you want to use a Tag of your own with custom handling you need to call the `registerTagHandler` method, pass
|
|
||||||
* the name of the tag and a Fully Qualified Class Name pointing to a class that implements the Tag interface.
|
|
||||||
*/
|
|
||||||
final class StandardTagFactory implements TagFactory
|
|
||||||
{
|
|
||||||
/** PCRE regular expression matching a tag name. */
|
|
||||||
public const REGEX_TAGNAME = '[\w\-\_\\\\:]+';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<string, class-string<Tag>|Tag|Factory> An array with a tag as a key, and an
|
|
||||||
* FQCN to a class that handles it as an array value.
|
|
||||||
*/
|
|
||||||
private array $tagHandlerMappings = [
|
|
||||||
'author' => Author::class,
|
|
||||||
'covers' => Covers::class,
|
|
||||||
'deprecated' => Deprecated::class,
|
|
||||||
'link' => LinkTag::class,
|
|
||||||
'see' => SeeTag::class,
|
|
||||||
'since' => Since::class,
|
|
||||||
'source' => Source::class,
|
|
||||||
'uses' => Uses::class,
|
|
||||||
'version' => Version::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var array<class-string<Tag>> An array with an annotation as a key, and an
|
|
||||||
* FQCN to a class that handles it as an array value.
|
|
||||||
*/
|
|
||||||
private array $annotationMappings = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ReflectionParameter[][] a lazy-loading cache containing parameters
|
|
||||||
* for each tagHandler that has been used.
|
|
||||||
*/
|
|
||||||
private array $tagHandlerParameterCache = [];
|
|
||||||
|
|
||||||
private FqsenResolver $fqsenResolver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var mixed[] an array representing a simple Service Locator where we can store parameters and
|
|
||||||
* services that can be inserted into the Factory Methods of Tag Handlers.
|
|
||||||
*/
|
|
||||||
private array $serviceLocator = [];
|
|
||||||
|
|
||||||
private function __construct(FqsenResolver $fqsenResolver)
|
|
||||||
{
|
|
||||||
$this->fqsenResolver = $fqsenResolver;
|
|
||||||
|
|
||||||
$this->addService($fqsenResolver, FqsenResolver::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize this tag factory with the means to resolve an FQSEN.
|
|
||||||
*
|
|
||||||
* @see self::registerTagHandler() to add a new tag handler to the existing default list.
|
|
||||||
*/
|
|
||||||
public static function createInstance(FqsenResolver $fqsenResolver): self
|
|
||||||
{
|
|
||||||
$tagFactory = new self($fqsenResolver);
|
|
||||||
$descriptionFactory = new DescriptionFactory($tagFactory);
|
|
||||||
|
|
||||||
$typeResolver = new TypeResolver($fqsenResolver);
|
|
||||||
|
|
||||||
$phpstanTagFactory = new AbstractPHPStanFactory(
|
|
||||||
new ParamFactory($typeResolver, $descriptionFactory),
|
|
||||||
new VarFactory($typeResolver, $descriptionFactory),
|
|
||||||
new ReturnFactory($typeResolver, $descriptionFactory),
|
|
||||||
new PropertyFactory($typeResolver, $descriptionFactory),
|
|
||||||
new PropertyReadFactory($typeResolver, $descriptionFactory),
|
|
||||||
new PropertyWriteFactory($typeResolver, $descriptionFactory),
|
|
||||||
new MethodFactory($typeResolver, $descriptionFactory),
|
|
||||||
new MixinFactory($typeResolver, $descriptionFactory),
|
|
||||||
new ImplementsFactory($typeResolver, $descriptionFactory),
|
|
||||||
new ExtendsFactory($typeResolver, $descriptionFactory),
|
|
||||||
new TemplateFactory($typeResolver, $descriptionFactory),
|
|
||||||
new TemplateCovariantFactory($typeResolver, $descriptionFactory),
|
|
||||||
new ThrowsFactory($typeResolver, $descriptionFactory),
|
|
||||||
);
|
|
||||||
|
|
||||||
$tagFactory->addService($descriptionFactory);
|
|
||||||
$tagFactory->addService($typeResolver);
|
|
||||||
$tagFactory->registerTagHandler('param', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('var', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('return', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('property', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('property-read', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('property-write', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('method', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('mixin', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('extends', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('implements', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('template', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('template-covariant', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('template-extends', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('template-implements', $phpstanTagFactory);
|
|
||||||
$tagFactory->registerTagHandler('throws', $phpstanTagFactory);
|
|
||||||
|
|
||||||
return $tagFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(string $tagLine, ?TypeContext $context = null): Tag
|
|
||||||
{
|
|
||||||
if (!$context) {
|
|
||||||
$context = new TypeContext('');
|
|
||||||
}
|
|
||||||
|
|
||||||
[$tagName, $tagBody] = $this->extractTagParts($tagLine);
|
|
||||||
|
|
||||||
return $this->createTag(trim($tagBody), $tagName, $context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $value
|
|
||||||
*/
|
|
||||||
public function addParameter(string $name, $value): void
|
|
||||||
{
|
|
||||||
$this->serviceLocator[$name] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addService(object $service, ?string $alias = null): void
|
|
||||||
{
|
|
||||||
$this->serviceLocator[$alias ?? get_class($service)] = $service;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
|
||||||
public function registerTagHandler(string $tagName, $handler): void
|
|
||||||
{
|
|
||||||
Assert::stringNotEmpty($tagName);
|
|
||||||
if (strpos($tagName, '\\') !== false && $tagName[0] !== '\\') {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'A namespaced tag must have a leading backslash as it must be fully qualified'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_object($handler)) {
|
|
||||||
Assert::isInstanceOf($handler, Factory::class);
|
|
||||||
$this->tagHandlerMappings[$tagName] = $handler;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert::classExists($handler);
|
|
||||||
Assert::implementsInterface($handler, Tag::class);
|
|
||||||
$this->tagHandlerMappings[$tagName] = $handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Extracts all components for a tag.
|
|
||||||
*
|
|
||||||
* @return string[]
|
|
||||||
*/
|
|
||||||
private function extractTagParts(string $tagLine): array
|
|
||||||
{
|
|
||||||
$matches = [];
|
|
||||||
if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\s\(\{])\s*([^\s].*)|$)/us', $tagLine, $matches)) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return array_slice($matches, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new tag object with the given name and body or returns null if the tag name was recognized but the
|
|
||||||
* body was invalid.
|
|
||||||
*/
|
|
||||||
private function createTag(string $body, string $name, TypeContext $context): Tag
|
|
||||||
{
|
|
||||||
$handlerClassName = $this->findHandlerClassName($name, $context);
|
|
||||||
$arguments = $this->getArgumentsForParametersFromWiring(
|
|
||||||
$this->fetchParametersForHandlerFactoryMethod($handlerClassName),
|
|
||||||
$this->getServiceLocatorWithDynamicParameters($context, $name, $body)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (array_key_exists('tagLine', $arguments)) {
|
|
||||||
$arguments['tagLine'] = sprintf('@%s %s', $name, $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$callable = [$handlerClassName, 'create'];
|
|
||||||
Assert::isCallable($callable);
|
|
||||||
/** @phpstan-var callable(string): ?Tag $callable */
|
|
||||||
$tag = call_user_func_array($callable, $arguments);
|
|
||||||
|
|
||||||
return $tag ?? InvalidTag::create($body, $name);
|
|
||||||
} catch (InvalidArgumentException $e) {
|
|
||||||
return InvalidTag::create($body, $name)->withError($e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
|
|
||||||
*
|
|
||||||
* @return class-string<Tag>|Tag|Factory
|
|
||||||
*/
|
|
||||||
private function findHandlerClassName(string $tagName, TypeContext $context)
|
|
||||||
{
|
|
||||||
$handlerClassName = Generic::class;
|
|
||||||
if (isset($this->tagHandlerMappings[$tagName])) {
|
|
||||||
$handlerClassName = $this->tagHandlerMappings[$tagName];
|
|
||||||
} elseif ($this->isAnnotation($tagName)) {
|
|
||||||
// TODO: Annotation support is planned for a later stage and as such is disabled for now
|
|
||||||
$tagName = (string) $this->fqsenResolver->resolve($tagName, $context);
|
|
||||||
if (isset($this->annotationMappings[$tagName])) {
|
|
||||||
$handlerClassName = $this->annotationMappings[$tagName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $handlerClassName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the arguments that need to be passed to the Factory Method with the given Parameters.
|
|
||||||
*
|
|
||||||
* @param ReflectionParameter[] $parameters
|
|
||||||
* @param mixed[] $locator
|
|
||||||
*
|
|
||||||
* @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters
|
|
||||||
* is provided with this method.
|
|
||||||
*/
|
|
||||||
private function getArgumentsForParametersFromWiring(array $parameters, array $locator): array
|
|
||||||
{
|
|
||||||
$arguments = [];
|
|
||||||
foreach ($parameters as $parameter) {
|
|
||||||
$type = $parameter->getType();
|
|
||||||
$typeHint = null;
|
|
||||||
if ($type instanceof ReflectionNamedType) {
|
|
||||||
$typeHint = $type->getName();
|
|
||||||
if ($typeHint === 'self') {
|
|
||||||
$declaringClass = $parameter->getDeclaringClass();
|
|
||||||
if ($declaringClass !== null) {
|
|
||||||
$typeHint = $declaringClass->getName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$parameterName = $parameter->getName();
|
|
||||||
if (isset($locator[$typeHint ?? ''])) {
|
|
||||||
$arguments[$parameterName] = $locator[$typeHint ?? ''];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($locator[$parameterName])) {
|
|
||||||
$arguments[$parameterName] = $locator[$parameterName];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$arguments[$parameterName] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
|
|
||||||
* tag handler class name.
|
|
||||||
*
|
|
||||||
* @param class-string<Tag>|Tag|Factory $handler
|
|
||||||
*
|
|
||||||
* @return ReflectionParameter[]
|
|
||||||
*/
|
|
||||||
private function fetchParametersForHandlerFactoryMethod($handler): array
|
|
||||||
{
|
|
||||||
$handlerClassName = is_object($handler) ? get_class($handler) : $handler;
|
|
||||||
|
|
||||||
if (!isset($this->tagHandlerParameterCache[$handlerClassName])) {
|
|
||||||
$methodReflection = new ReflectionMethod($handlerClassName, 'create');
|
|
||||||
$this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->tagHandlerParameterCache[$handlerClassName];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a copy of this class' Service Locator with added dynamic parameters,
|
|
||||||
* such as the tag's name, body and Context.
|
|
||||||
*
|
|
||||||
* @param TypeContext $context The Context (namespace and aliases) that may be
|
|
||||||
* passed and is used to resolve FQSENs.
|
|
||||||
* @param string $tagName The name of the tag that may be
|
|
||||||
* passed onto the factory method of the Tag class.
|
|
||||||
* @param string $tagBody The body of the tag that may be
|
|
||||||
* passed onto the factory method of the Tag class.
|
|
||||||
*
|
|
||||||
* @return mixed[]
|
|
||||||
*/
|
|
||||||
private function getServiceLocatorWithDynamicParameters(
|
|
||||||
TypeContext $context,
|
|
||||||
string $tagName,
|
|
||||||
string $tagBody
|
|
||||||
): array {
|
|
||||||
return array_merge(
|
|
||||||
$this->serviceLocator,
|
|
||||||
[
|
|
||||||
'name' => $tagName,
|
|
||||||
'body' => $tagBody,
|
|
||||||
TypeContext::class => $context,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the given tag belongs to an annotation.
|
|
||||||
*
|
|
||||||
* @todo this method should be populated once we implement Annotation notation support.
|
|
||||||
*/
|
|
||||||
private function isAnnotation(string $tagContent): bool
|
|
||||||
{
|
|
||||||
// 1. Contains a namespace separator
|
|
||||||
// 2. Contains parenthesis
|
|
||||||
// 3. Is present in a list of known annotations (make the algorithm smart by first checking is the last part
|
|
||||||
// of the annotation class name matches the found tag name
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
|
|
||||||
|
|
||||||
interface Tag
|
|
||||||
{
|
|
||||||
public function getName(): string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Tag|mixed Class that implements Tag
|
|
||||||
* @phpstan-return ?Tag
|
|
||||||
*/
|
|
||||||
public static function create(string $body);
|
|
||||||
|
|
||||||
public function render(?Formatter $formatter = null): string;
|
|
||||||
|
|
||||||
public function __toString(): string;
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
|
|
||||||
|
|
||||||
interface TagFactory extends Factory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Adds a parameter to the service locator that can be injected in a tag's factory method.
|
|
||||||
*
|
|
||||||
* When calling a tag's "create" method we always check the signature for dependencies to inject. One way is to
|
|
||||||
* typehint a parameter in the signature so that we can use that interface or class name to inject a dependency
|
|
||||||
* (see {@see addService()} for more information on that).
|
|
||||||
*
|
|
||||||
* Another way is to check the name of the argument against the names in the Service Locator. With this method
|
|
||||||
* you can add a variable that will be inserted when a tag's create method is not typehinted and has a matching
|
|
||||||
* name.
|
|
||||||
*
|
|
||||||
* Be aware that there are two reserved names:
|
|
||||||
*
|
|
||||||
* - name, representing the name of the tag.
|
|
||||||
* - body, representing the complete body of the tag.
|
|
||||||
*
|
|
||||||
* These parameters are injected at the last moment and will override any existing parameter with those names.
|
|
||||||
*
|
|
||||||
* @param mixed $value
|
|
||||||
*/
|
|
||||||
public function addParameter(string $name, $value): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a service with the Service Locator using the FQCN of the class or the alias, if provided.
|
|
||||||
*
|
|
||||||
* When calling a tag's "create" method we always check the signature for dependencies to inject. If a parameter
|
|
||||||
* has a typehint then the ServiceLocator is queried to see if a Service is registered for that typehint.
|
|
||||||
*
|
|
||||||
* Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the
|
|
||||||
* interface is passed as alias then every time that interface is requested the provided service will be returned.
|
|
||||||
*/
|
|
||||||
public function addService(object $service): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a handler for tags.
|
|
||||||
*
|
|
||||||
* If you want to use your own tags then you can use this method to instruct the TagFactory
|
|
||||||
* to register the name of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement
|
|
||||||
* the {@see Tag} interface (and thus the create method).
|
|
||||||
*
|
|
||||||
* @param string $tagName Name of tag to register a handler for. When registering a namespaced
|
|
||||||
* tag, the full name, along with a prefixing slash MUST be provided.
|
|
||||||
* @param class-string<Tag>|Factory $handler FQCN of handler.
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException If the tag name is not a string.
|
|
||||||
* @throws InvalidArgumentException If the tag name is namespaced (contains backslashes) but
|
|
||||||
* does not start with a backslash.
|
|
||||||
* @throws InvalidArgumentException If the handler is not a string.
|
|
||||||
* @throws InvalidArgumentException If the handler is not an existing class.
|
|
||||||
* @throws InvalidArgumentException If the handler does not implement the {@see Tag} interface.
|
|
||||||
*/
|
|
||||||
public function registerTagHandler(string $tagName, $handler): void;
|
|
||||||
}
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
|
|
||||||
use function filter_var;
|
|
||||||
use function preg_match;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
use const FILTER_VALIDATE_EMAIL;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflection class for an {@}author tag in a Docblock.
|
|
||||||
*/
|
|
||||||
final class Author extends BaseTag
|
|
||||||
{
|
|
||||||
/** @var string register that this is the author tag. */
|
|
||||||
protected string $name = 'author';
|
|
||||||
|
|
||||||
/** @var string The name of the author */
|
|
||||||
private string $authorName;
|
|
||||||
|
|
||||||
/** @var string The email of the author */
|
|
||||||
private string $authorEmail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes this tag with the author name and e-mail.
|
|
||||||
*/
|
|
||||||
public function __construct(string $authorName, string $authorEmail)
|
|
||||||
{
|
|
||||||
if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) {
|
|
||||||
throw new InvalidArgumentException('The author tag does not have a valid e-mail address');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->authorName = $authorName;
|
|
||||||
$this->authorEmail = $authorEmail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the author's name.
|
|
||||||
*
|
|
||||||
* @return string The author's name.
|
|
||||||
*/
|
|
||||||
public function getAuthorName(): string
|
|
||||||
{
|
|
||||||
return $this->authorName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the author's email.
|
|
||||||
*
|
|
||||||
* @return string The author's email.
|
|
||||||
*/
|
|
||||||
public function getEmail(): string
|
|
||||||
{
|
|
||||||
return $this->authorEmail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns this tag in string form.
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
if ($this->authorEmail) {
|
|
||||||
$authorEmail = '<' . $this->authorEmail . '>';
|
|
||||||
} else {
|
|
||||||
$authorEmail = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$authorName = $this->authorName;
|
|
||||||
|
|
||||||
return $authorName . ($authorEmail !== '' ? ($authorName !== '' ? ' ' : '') . $authorEmail : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to create a new Author object based on the tag body.
|
|
||||||
*/
|
|
||||||
public static function create(string $body): ?self
|
|
||||||
{
|
|
||||||
$splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $body, $matches);
|
|
||||||
if (!$splitTagContent) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$authorName = trim($matches[1]);
|
|
||||||
$email = isset($matches[2]) ? trim($matches[2]) : '';
|
|
||||||
|
|
||||||
return new static($authorName, $email);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a tag definition for a DocBlock.
|
|
||||||
*/
|
|
||||||
abstract class BaseTag implements DocBlock\Tag
|
|
||||||
{
|
|
||||||
/** @var string Name of the tag */
|
|
||||||
protected string $name = '';
|
|
||||||
|
|
||||||
/** @var Description|null Description of the tag. */
|
|
||||||
protected ?Description $description = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of this tag.
|
|
||||||
*
|
|
||||||
* @return string The name of this tag.
|
|
||||||
*/
|
|
||||||
public function getName(): string
|
|
||||||
{
|
|
||||||
return $this->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDescription(): ?Description
|
|
||||||
{
|
|
||||||
return $this->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(?Formatter $formatter = null): string
|
|
||||||
{
|
|
||||||
if ($formatter === null) {
|
|
||||||
$formatter = new Formatter\PassthroughFormatter();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formatter->format($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\Fqsen;
|
|
||||||
use phpDocumentor\Reflection\FqsenResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
use phpDocumentor\Reflection\Utils;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function array_key_exists;
|
|
||||||
use function explode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflection class for a @covers tag in a Docblock.
|
|
||||||
*/
|
|
||||||
final class Covers extends BaseTag
|
|
||||||
{
|
|
||||||
protected string $name = 'covers';
|
|
||||||
|
|
||||||
private Fqsen $refers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes this tag.
|
|
||||||
*/
|
|
||||||
public function __construct(Fqsen $refers, ?Description $description = null)
|
|
||||||
{
|
|
||||||
$this->refers = $refers;
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function create(
|
|
||||||
string $body,
|
|
||||||
?DescriptionFactory $descriptionFactory = null,
|
|
||||||
?FqsenResolver $resolver = null,
|
|
||||||
?TypeContext $context = null
|
|
||||||
): self {
|
|
||||||
Assert::stringNotEmpty($body);
|
|
||||||
Assert::notNull($descriptionFactory);
|
|
||||||
Assert::notNull($resolver);
|
|
||||||
|
|
||||||
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
|
|
||||||
|
|
||||||
return new static(
|
|
||||||
self::resolveFqsen($parts[0], $resolver, $context),
|
|
||||||
$descriptionFactory->create($parts[1] ?? '', $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context): Fqsen
|
|
||||||
{
|
|
||||||
Assert::notNull($fqsenResolver);
|
|
||||||
$fqsenParts = explode('::', $parts);
|
|
||||||
$resolved = $fqsenResolver->resolve($fqsenParts[0], $context);
|
|
||||||
|
|
||||||
if (!array_key_exists(1, $fqsenParts)) {
|
|
||||||
return $resolved;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Fqsen($resolved . '::' . $fqsenParts[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the structural element this tag refers to.
|
|
||||||
*/
|
|
||||||
public function getReference(): Fqsen
|
|
||||||
{
|
|
||||||
return $this->refers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation of this tag.
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
if ($this->description) {
|
|
||||||
$description = $this->description->render();
|
|
||||||
} else {
|
|
||||||
$description = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$refers = (string) $this->refers;
|
|
||||||
|
|
||||||
return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function preg_match;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflection class for a {@}deprecated tag in a Docblock.
|
|
||||||
*/
|
|
||||||
final class Deprecated extends BaseTag
|
|
||||||
{
|
|
||||||
protected string $name = 'deprecated';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PCRE regular expression matching a version vector.
|
|
||||||
* Assumes the "x" modifier.
|
|
||||||
*/
|
|
||||||
public const REGEX_VECTOR = '(?:
|
|
||||||
# Normal release vectors.
|
|
||||||
\d\S*
|
|
||||||
|
|
|
||||||
# VCS version vectors. Per PHPCS, they are expected to
|
|
||||||
# follow the form of the VCS name, followed by ":", followed
|
|
||||||
# by the version vector itself.
|
|
||||||
# By convention, popular VCSes like CVS, SVN and GIT use "$"
|
|
||||||
# around the actual version vector.
|
|
||||||
[^\s\:]+\:\s*\$[^\$]+\$
|
|
||||||
)';
|
|
||||||
|
|
||||||
/** @var string|null The version vector. */
|
|
||||||
private ?string $version = null;
|
|
||||||
|
|
||||||
public function __construct(?string $version = null, ?Description $description = null)
|
|
||||||
{
|
|
||||||
Assert::nullOrNotEmpty($version);
|
|
||||||
|
|
||||||
$this->version = $version;
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return static
|
|
||||||
*/
|
|
||||||
public static function create(
|
|
||||||
?string $body,
|
|
||||||
?DescriptionFactory $descriptionFactory = null,
|
|
||||||
?TypeContext $context = null
|
|
||||||
): self {
|
|
||||||
if ($body === null || $body === '') {
|
|
||||||
return new static();
|
|
||||||
}
|
|
||||||
|
|
||||||
$matches = [];
|
|
||||||
if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
|
|
||||||
return new static(
|
|
||||||
null,
|
|
||||||
$descriptionFactory !== null ? $descriptionFactory->create($body, $context) : null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert::notNull($descriptionFactory);
|
|
||||||
|
|
||||||
return new static(
|
|
||||||
$matches[1],
|
|
||||||
$descriptionFactory->create($matches[2] ?? '', $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the version section of the tag.
|
|
||||||
*/
|
|
||||||
public function getVersion(): ?string
|
|
||||||
{
|
|
||||||
return $this->version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation for this tag.
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
if ($this->description) {
|
|
||||||
$description = $this->description->render();
|
|
||||||
} else {
|
|
||||||
$description = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$version = (string) $this->version;
|
|
||||||
|
|
||||||
return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function array_key_exists;
|
|
||||||
use function preg_match;
|
|
||||||
use function rawurlencode;
|
|
||||||
use function str_replace;
|
|
||||||
use function strpos;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflection class for a {@}example tag in a Docblock.
|
|
||||||
*/
|
|
||||||
final class Example implements Tag
|
|
||||||
{
|
|
||||||
/** @var string Path to a file to use as an example. May also be an absolute URI. */
|
|
||||||
private string $filePath;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool Whether the file path component represents an URI. This determines how the file portion
|
|
||||||
* appears at {@link getContent()}.
|
|
||||||
*/
|
|
||||||
private bool $isURI;
|
|
||||||
|
|
||||||
private int $startingLine;
|
|
||||||
|
|
||||||
private int $lineCount;
|
|
||||||
|
|
||||||
private ?string $content = null;
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
string $filePath,
|
|
||||||
bool $isURI,
|
|
||||||
int $startingLine,
|
|
||||||
int $lineCount,
|
|
||||||
?string $content
|
|
||||||
) {
|
|
||||||
Assert::stringNotEmpty($filePath);
|
|
||||||
Assert::greaterThanEq($startingLine, 1);
|
|
||||||
Assert::greaterThanEq($lineCount, 0);
|
|
||||||
|
|
||||||
$this->filePath = $filePath;
|
|
||||||
$this->startingLine = $startingLine;
|
|
||||||
$this->lineCount = $lineCount;
|
|
||||||
if ($content !== null) {
|
|
||||||
$this->content = trim($content);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->isURI = $isURI;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContent(): string
|
|
||||||
{
|
|
||||||
if ($this->content === null || $this->content === '') {
|
|
||||||
$filePath = $this->filePath;
|
|
||||||
if ($this->isURI) {
|
|
||||||
$filePath = $this->isUriRelative($this->filePath)
|
|
||||||
? str_replace('%2F', '/', rawurlencode($this->filePath))
|
|
||||||
: $this->filePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return trim($filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDescription(): ?string
|
|
||||||
{
|
|
||||||
return $this->content;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function create(string $body): ?Tag
|
|
||||||
{
|
|
||||||
// File component: File path in quotes or File URI / Source information
|
|
||||||
if (!preg_match('/^\s*(?:(\"[^\"]+\")|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$filePath = null;
|
|
||||||
$fileUri = null;
|
|
||||||
if (array_key_exists(1, $matches) && $matches[1] !== '') {
|
|
||||||
$filePath = $matches[1];
|
|
||||||
} else {
|
|
||||||
$fileUri = array_key_exists(2, $matches) ? $matches[2] : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$startingLine = 1;
|
|
||||||
$lineCount = 0;
|
|
||||||
$description = null;
|
|
||||||
|
|
||||||
if (array_key_exists(3, $matches)) {
|
|
||||||
$description = $matches[3];
|
|
||||||
|
|
||||||
// Starting line / Number of lines / Description
|
|
||||||
if (preg_match('/^([1-9]\d*)(?:\s+((?1))\s*)?(.*)$/sux', $matches[3], $contentMatches)) {
|
|
||||||
$startingLine = (int) $contentMatches[1];
|
|
||||||
if (isset($contentMatches[2])) {
|
|
||||||
$lineCount = (int) $contentMatches[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (array_key_exists(3, $contentMatches)) {
|
|
||||||
$description = $contentMatches[3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new static(
|
|
||||||
$filePath ?? ($fileUri ?? ''),
|
|
||||||
$fileUri !== null,
|
|
||||||
$startingLine,
|
|
||||||
$lineCount,
|
|
||||||
$description
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the file path.
|
|
||||||
*
|
|
||||||
* @return string Path to a file to use as an example.
|
|
||||||
* May also be an absolute URI.
|
|
||||||
*/
|
|
||||||
public function getFilePath(): string
|
|
||||||
{
|
|
||||||
return trim($this->filePath, '"');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation for this tag.
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
$filePath = $this->filePath;
|
|
||||||
$isDefaultLine = $this->startingLine === 1 && $this->lineCount === 0;
|
|
||||||
$startingLine = !$isDefaultLine ? (string) $this->startingLine : '';
|
|
||||||
$lineCount = !$isDefaultLine ? (string) $this->lineCount : '';
|
|
||||||
$content = (string) $this->content;
|
|
||||||
|
|
||||||
return $filePath
|
|
||||||
. ($startingLine !== ''
|
|
||||||
? ($filePath !== '' ? ' ' : '') . $startingLine
|
|
||||||
: '')
|
|
||||||
. ($lineCount !== ''
|
|
||||||
? ($filePath !== '' || $startingLine !== '' ? ' ' : '') . $lineCount
|
|
||||||
: '')
|
|
||||||
. ($content !== ''
|
|
||||||
? ($filePath !== '' || $startingLine !== '' || $lineCount !== '' ? ' ' : '') . $content
|
|
||||||
: '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute).
|
|
||||||
*/
|
|
||||||
private function isUriRelative(string $uri): bool
|
|
||||||
{
|
|
||||||
return strpos($uri, ':') === false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStartingLine(): int
|
|
||||||
{
|
|
||||||
return $this->startingLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLineCount(): int
|
|
||||||
{
|
|
||||||
return $this->lineCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getName(): string
|
|
||||||
{
|
|
||||||
return 'example';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(?Formatter $formatter = null): string
|
|
||||||
{
|
|
||||||
if ($formatter === null) {
|
|
||||||
$formatter = new Formatter\PassthroughFormatter();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formatter->format($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
use phpDocumentor\Reflection\Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflection class for a {@}extends tag in a Docblock.
|
|
||||||
*/
|
|
||||||
class Extends_ extends TagWithType
|
|
||||||
{
|
|
||||||
public function __construct(Type $type, ?Description $description = null)
|
|
||||||
{
|
|
||||||
$this->name = 'extends';
|
|
||||||
$this->type = $type;
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
|
||||||
use PHPStan\PhpDocParser\Parser\ConstExprParser;
|
|
||||||
use PHPStan\PhpDocParser\Parser\ParserException;
|
|
||||||
use PHPStan\PhpDocParser\Parser\PhpDocParser;
|
|
||||||
use PHPStan\PhpDocParser\Parser\TokenIterator;
|
|
||||||
use PHPStan\PhpDocParser\Parser\TypeParser;
|
|
||||||
use PHPStan\PhpDocParser\ParserConfig;
|
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
use function ltrim;
|
|
||||||
use function property_exists;
|
|
||||||
use function rtrim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory class creating tags using phpstan's parser
|
|
||||||
*
|
|
||||||
* This class uses {@see PHPStanFactory} implementations to create tags
|
|
||||||
* from the ast of the phpstan docblock parser.
|
|
||||||
*
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
class AbstractPHPStanFactory implements Factory
|
|
||||||
{
|
|
||||||
private PhpDocParser $parser;
|
|
||||||
private Lexer $lexer;
|
|
||||||
/** @var PHPStanFactory[] */
|
|
||||||
private array $factories;
|
|
||||||
|
|
||||||
public function __construct(PHPStanFactory ...$factories)
|
|
||||||
{
|
|
||||||
$config = new ParserConfig(['indexes' => true, 'lines' => true]);
|
|
||||||
$this->lexer = new Lexer($config);
|
|
||||||
$constParser = new ConstExprParser($config);
|
|
||||||
$this->parser = new PhpDocParser(
|
|
||||||
$config,
|
|
||||||
new TypeParser($config, $constParser),
|
|
||||||
$constParser
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->factories = $factories;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(string $tagLine, ?TypeContext $context = null): Tag
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$tokens = $this->tokenizeLine($tagLine . "\n");
|
|
||||||
$ast = $this->parser->parseTag($tokens);
|
|
||||||
if (property_exists($ast->value, 'description') === true) {
|
|
||||||
$ast->value->setAttribute(
|
|
||||||
'description',
|
|
||||||
rtrim($ast->value->description . $tokens->joinUntil(Lexer::TOKEN_END), "\n")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (ParserException $e) {
|
|
||||||
return InvalidTag::create($tagLine, '')->withError($e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($context === null) {
|
|
||||||
$context = new TypeContext('');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
foreach ($this->factories as $factory) {
|
|
||||||
if ($factory->supports($ast, $context)) {
|
|
||||||
return $factory->create($ast, $context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (RuntimeException $e) {
|
|
||||||
return InvalidTag::create((string) $ast->value, 'method')->withError($e);
|
|
||||||
} catch (ParserException $e) {
|
|
||||||
return InvalidTag::create((string) $ast->value, $ast->name)->withError($e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return InvalidTag::create(
|
|
||||||
(string) $ast->value,
|
|
||||||
$ast->name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Solve the issue with the lexer not tokenizing the line correctly
|
|
||||||
*
|
|
||||||
* This method is a workaround for the lexer that includes newline tokens with spaces. For
|
|
||||||
* phpstan this isn't an issue, as it doesn't do a lot of things with the indentation of descriptions.
|
|
||||||
* But for us is important to keep the indentation of the descriptions, so we need to fix the lexer output.
|
|
||||||
*/
|
|
||||||
private function tokenizeLine(string $tagLine): TokenIterator
|
|
||||||
{
|
|
||||||
$tokens = $this->lexer->tokenize($tagLine);
|
|
||||||
$fixed = [];
|
|
||||||
foreach ($tokens as $token) {
|
|
||||||
if (($token[1] === Lexer::TOKEN_PHPDOC_EOL) && rtrim($token[0], " \t") !== $token[0]) {
|
|
||||||
$fixed[] = [
|
|
||||||
rtrim($token[Lexer::VALUE_OFFSET], " \t"),
|
|
||||||
Lexer::TOKEN_PHPDOC_EOL,
|
|
||||||
$token[2] ?? 0,
|
|
||||||
];
|
|
||||||
$fixed[] = [
|
|
||||||
ltrim($token[Lexer::VALUE_OFFSET], "\n\r"),
|
|
||||||
Lexer::TOKEN_HORIZONTAL_WS,
|
|
||||||
($token[2] ?? 0) + 1,
|
|
||||||
];
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$fixed[] = $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TokenIterator($fixed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Extends_;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class ExtendsFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof ExtendsTagValueNode && $node->name === '@extends';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, ExtendsTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Extends_(
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
/*
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
|
|
||||||
interface Factory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Factory method responsible for instantiating the correct sub type.
|
|
||||||
*
|
|
||||||
* @param string $tagLine The text for this tag, including description.
|
|
||||||
*
|
|
||||||
* @return Tag A new tag object.
|
|
||||||
*
|
|
||||||
* @throws InvalidArgumentException If an invalid tag line was presented.
|
|
||||||
*/
|
|
||||||
public function create(string $tagLine, ?TypeContext $context = null): Tag;
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Implements_;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ImplementsTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class ImplementsFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof ImplementsTagValueNode && $node->name === '@implements';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, ImplementsTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Implements_(
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Method;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\MethodParameter;
|
|
||||||
use phpDocumentor\Reflection\Type;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use phpDocumentor\Reflection\Types\Mixed_;
|
|
||||||
use phpDocumentor\Reflection\Types\Void_;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function array_map;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class MethodFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, MethodTagValueNode::class);
|
|
||||||
|
|
||||||
return new Method(
|
|
||||||
$tagValue->methodName,
|
|
||||||
array_map(
|
|
||||||
function (MethodTagValueParameterNode $param) use ($context) {
|
|
||||||
return new MethodParameter(
|
|
||||||
trim($param->parameterName, '$'),
|
|
||||||
$param->type === null ? new Mixed_() : $this->typeResolver->createType(
|
|
||||||
$param->type,
|
|
||||||
$context
|
|
||||||
),
|
|
||||||
$param->isReference,
|
|
||||||
$param->isVariadic,
|
|
||||||
$param->defaultValue === null ?
|
|
||||||
MethodParameter::NO_DEFAULT_VALUE :
|
|
||||||
(string) $param->defaultValue
|
|
||||||
);
|
|
||||||
},
|
|
||||||
$tagValue->parameters
|
|
||||||
),
|
|
||||||
$this->createReturnType($tagValue, $context),
|
|
||||||
$tagValue->isStatic,
|
|
||||||
$this->descriptionFactory->create($tagValue->description, $context),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof MethodTagValueNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function createReturnType(MethodTagValueNode $tagValue, Context $context): Type
|
|
||||||
{
|
|
||||||
if ($tagValue->returnType === null) {
|
|
||||||
return new Void_();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->typeResolver->createType($tagValue->returnType, $context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,100 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use function array_key_last;
|
|
||||||
use function get_class;
|
|
||||||
use function gettype;
|
|
||||||
use function method_exists;
|
|
||||||
use function ucfirst;
|
|
||||||
use function var_export;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class MethodParameterFactory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Formats the given default value to a string-able mixin
|
|
||||||
*
|
|
||||||
* @param mixed $defaultValue
|
|
||||||
*/
|
|
||||||
public function format($defaultValue): string
|
|
||||||
{
|
|
||||||
$method = 'format' . ucfirst(gettype($defaultValue));
|
|
||||||
if (method_exists($this, $method)) {
|
|
||||||
return $this->{$method}($defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatDouble(float $defaultValue): string
|
|
||||||
{
|
|
||||||
return var_export($defaultValue, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $defaultValue
|
|
||||||
*/
|
|
||||||
private function formatNull($defaultValue): string
|
|
||||||
{
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatInteger(int $defaultValue): string
|
|
||||||
{
|
|
||||||
return var_export($defaultValue, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatString(string $defaultValue): string
|
|
||||||
{
|
|
||||||
return var_export($defaultValue, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatBoolean(bool $defaultValue): string
|
|
||||||
{
|
|
||||||
return var_export($defaultValue, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<(array<mixed>|int|float|bool|string|object|null)> $defaultValue
|
|
||||||
*/
|
|
||||||
private function formatArray(array $defaultValue): string
|
|
||||||
{
|
|
||||||
$formatedValue = '[';
|
|
||||||
|
|
||||||
foreach ($defaultValue as $key => $value) {
|
|
||||||
$method = 'format' . ucfirst(gettype($value));
|
|
||||||
if (!method_exists($this, $method)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$formatedValue .= $this->{$method}($value);
|
|
||||||
|
|
||||||
if ($key === array_key_last($defaultValue)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$formatedValue .= ',';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formatedValue . ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
private function formatObject(object $defaultValue): string
|
|
||||||
{
|
|
||||||
return 'new ' . get_class($defaultValue) . '()';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Mixin;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\MixinTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class MixinFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, MixinTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Mixin(
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof MixinTagValueNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
|
|
||||||
interface PHPStanFactory
|
|
||||||
{
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag;
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool;
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
|
||||||
use phpDocumentor\Reflection\Exception\ParserException;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\TypelessParamTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\Type\OffsetAccessTypeNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class ParamFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
|
|
||||||
if ($tagValue instanceof InvalidTagValueNode) {
|
|
||||||
return InvalidTag::create($tagValue->value, 'param')->withError(
|
|
||||||
ParserException::from($tagValue->exception)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert::isInstanceOfAny(
|
|
||||||
$tagValue,
|
|
||||||
[
|
|
||||||
ParamTagValueNode::class,
|
|
||||||
TypelessParamTagValueNode::class,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (($tagValue->type ?? null) instanceof OffsetAccessTypeNode) {
|
|
||||||
return InvalidTag::create(
|
|
||||||
(string) $tagValue,
|
|
||||||
'param'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Param(
|
|
||||||
trim($tagValue->parameterName, '$'),
|
|
||||||
$this->typeResolver->createType($tagValue->type ?? new IdentifierTypeNode('mixed'), $context),
|
|
||||||
$tagValue->isVariadic,
|
|
||||||
$this->descriptionFactory->create($description, $context),
|
|
||||||
$tagValue->isReference
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof ParamTagValueNode
|
|
||||||
|| $node->value instanceof TypelessParamTagValueNode
|
|
||||||
|| $node->name === '@param';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Property;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class PropertyFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Property(
|
|
||||||
trim($tagValue->propertyName, '$'),
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof PropertyTagValueNode && $node->name === '@property';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\PropertyRead;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class PropertyReadFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PropertyRead(
|
|
||||||
trim($tagValue->propertyName, '$'),
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof PropertyTagValueNode && $node->name === '@property-read';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PropertyTagValueNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class PropertyWriteFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, PropertyTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PropertyWrite(
|
|
||||||
trim($tagValue->propertyName, '$'),
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof PropertyTagValueNode && $node->name === '@property-write';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class ReturnFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, ReturnTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Return_(
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof ReturnTagValueNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\TemplateCovariant;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class TemplateCovariantFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof TemplateTagValueNode && $node->name === '@template-covariant';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, TemplateTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new TemplateCovariant(
|
|
||||||
$this->typeResolver->createType(new IdentifierTypeNode($tagValue->name), $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Template;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class TemplateFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
|
|
||||||
Assert::isInstanceOf($tagValue, TemplateTagValueNode::class);
|
|
||||||
$name = $tagValue->name;
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Template(
|
|
||||||
$name,
|
|
||||||
$this->typeResolver->createType($tagValue->bound, $context),
|
|
||||||
$this->typeResolver->createType($tagValue->default, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof TemplateTagValueNode && $node->name === '@template';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Throws;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class ThrowsFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, ThrowsTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Throws(
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof ThrowsTagValueNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
|
|
||||||
use phpDocumentor\Reflection\TypeResolver;
|
|
||||||
use phpDocumentor\Reflection\Types\Context;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|
||||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function is_string;
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @internal This class is not part of the BC promise of this library.
|
|
||||||
*/
|
|
||||||
final class VarFactory implements PHPStanFactory
|
|
||||||
{
|
|
||||||
private DescriptionFactory $descriptionFactory;
|
|
||||||
private TypeResolver $typeResolver;
|
|
||||||
|
|
||||||
public function __construct(TypeResolver $typeResolver, DescriptionFactory $descriptionFactory)
|
|
||||||
{
|
|
||||||
$this->descriptionFactory = $descriptionFactory;
|
|
||||||
$this->typeResolver = $typeResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function create(PhpDocTagNode $node, Context $context): Tag
|
|
||||||
{
|
|
||||||
$tagValue = $node->value;
|
|
||||||
Assert::isInstanceOf($tagValue, VarTagValueNode::class);
|
|
||||||
|
|
||||||
$description = $tagValue->getAttribute('description');
|
|
||||||
if (is_string($description) === false) {
|
|
||||||
$description = $tagValue->description;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Var_(
|
|
||||||
trim($tagValue->variableName, '$'),
|
|
||||||
$this->typeResolver->createType($tagValue->type, $context),
|
|
||||||
$this->descriptionFactory->create($description, $context)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function supports(PhpDocTagNode $node, Context $context): bool
|
|
||||||
{
|
|
||||||
return $node->value instanceof VarTagValueNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
|
|
||||||
interface Formatter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Formats a tag into a string representation according to a specific format, such as Markdown.
|
|
||||||
*/
|
|
||||||
public function format(Tag $tag): string;
|
|
||||||
}
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
|
|
||||||
|
|
||||||
use function max;
|
|
||||||
use function str_repeat;
|
|
||||||
use function strlen;
|
|
||||||
|
|
||||||
class AlignFormatter implements Formatter
|
|
||||||
{
|
|
||||||
/** @var int The maximum tag name length. */
|
|
||||||
protected int $maxLen = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Tag[] $tags All tags that should later be aligned with the formatter.
|
|
||||||
*/
|
|
||||||
public function __construct(array $tags)
|
|
||||||
{
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$this->maxLen = max($this->maxLen, strlen($tag->getName()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Formats the given tag to return a simple plain text version.
|
|
||||||
*/
|
|
||||||
public function format(Tag $tag): string
|
|
||||||
{
|
|
||||||
return '@' . $tag->getName() .
|
|
||||||
str_repeat(
|
|
||||||
' ',
|
|
||||||
$this->maxLen - strlen($tag->getName()) + 1
|
|
||||||
) .
|
|
||||||
$tag;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
|
|
||||||
|
|
||||||
use function trim;
|
|
||||||
|
|
||||||
class PassthroughFormatter implements Formatter
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Formats the given tag to return a simple plain text version.
|
|
||||||
*/
|
|
||||||
public function format(Tag $tag): string
|
|
||||||
{
|
|
||||||
return trim('@' . $tag->getName() . ' ' . $tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
use function preg_match;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a tag definition for a DocBlock.
|
|
||||||
*/
|
|
||||||
final class Generic extends BaseTag
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Parses a tag and populates the member variables.
|
|
||||||
*
|
|
||||||
* @param string $name Name of the tag.
|
|
||||||
* @param Description $description The contents of the given tag.
|
|
||||||
*/
|
|
||||||
public function __construct(string $name, ?Description $description = null)
|
|
||||||
{
|
|
||||||
$this->validateTagName($name);
|
|
||||||
|
|
||||||
$this->name = $name;
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new tag that represents any unknown tag type.
|
|
||||||
*
|
|
||||||
* @return static
|
|
||||||
*/
|
|
||||||
public static function create(
|
|
||||||
string $body,
|
|
||||||
string $name = '',
|
|
||||||
?DescriptionFactory $descriptionFactory = null,
|
|
||||||
?TypeContext $context = null
|
|
||||||
): self {
|
|
||||||
Assert::stringNotEmpty($name);
|
|
||||||
Assert::notNull($descriptionFactory);
|
|
||||||
|
|
||||||
$description = $body !== '' ? $descriptionFactory->create($body, $context) : null;
|
|
||||||
|
|
||||||
return new static($name, $description);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the tag as a serialized string
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
if ($this->description) {
|
|
||||||
$description = $this->description->render();
|
|
||||||
} else {
|
|
||||||
$description = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates if the tag name matches the expected format, otherwise throws an exception.
|
|
||||||
*/
|
|
||||||
private function validateTagName(string $name): void
|
|
||||||
{
|
|
||||||
if (!preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, '
|
|
||||||
. 'hyphens and backslashes.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
use phpDocumentor\Reflection\Type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflection class for a {@}implements tag in a Docblock.
|
|
||||||
*/
|
|
||||||
class Implements_ extends TagWithType
|
|
||||||
{
|
|
||||||
public function __construct(Type $type, ?Description $description = null)
|
|
||||||
{
|
|
||||||
$this->name = 'implements';
|
|
||||||
$this->type = $type;
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use Closure;
|
|
||||||
use Exception;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
||||||
use ReflectionClass;
|
|
||||||
use ReflectionException;
|
|
||||||
use ReflectionFunction;
|
|
||||||
use Throwable;
|
|
||||||
|
|
||||||
use function array_map;
|
|
||||||
use function get_class;
|
|
||||||
use function get_resource_type;
|
|
||||||
use function is_array;
|
|
||||||
use function is_object;
|
|
||||||
use function is_resource;
|
|
||||||
use function sprintf;
|
|
||||||
|
|
||||||
use const PHP_VERSION_ID;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class represents an exception during the tag creation
|
|
||||||
*
|
|
||||||
* Since the internals of the library are relaying on the correct syntax of a docblock
|
|
||||||
* we cannot simply throw exceptions at all time because the exceptions will break the creation of a
|
|
||||||
* docklock. Just silently ignore the exceptions is not an option because the user as an issue to fix.
|
|
||||||
*
|
|
||||||
* This tag holds that error information until a using application is able to display it. The object will just behave
|
|
||||||
* like any normal tag. So the normal application flow will not break.
|
|
||||||
*/
|
|
||||||
final class InvalidTag implements Tag
|
|
||||||
{
|
|
||||||
private string $name;
|
|
||||||
|
|
||||||
private string $body;
|
|
||||||
|
|
||||||
private ?Throwable $throwable = null;
|
|
||||||
|
|
||||||
private function __construct(string $name, string $body)
|
|
||||||
{
|
|
||||||
$this->name = $name;
|
|
||||||
$this->body = $body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getException(): ?Throwable
|
|
||||||
{
|
|
||||||
return $this->throwable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getName(): string
|
|
||||||
{
|
|
||||||
return $this->name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function create(string $body, string $name = ''): self
|
|
||||||
{
|
|
||||||
return new self($name, $body);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function withError(Throwable $exception): self
|
|
||||||
{
|
|
||||||
$this->flattenExceptionBacktrace($exception);
|
|
||||||
$tag = new self($this->name, $this->body);
|
|
||||||
$tag->throwable = $exception;
|
|
||||||
|
|
||||||
return $tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all complex types from backtrace
|
|
||||||
*
|
|
||||||
* Not all objects are serializable. So we need to remove them from the
|
|
||||||
* stored exception to be sure that we do not break existing library usage.
|
|
||||||
*/
|
|
||||||
private function flattenExceptionBacktrace(Throwable $exception): void
|
|
||||||
{
|
|
||||||
$traceProperty = (new ReflectionClass(Exception::class))->getProperty('trace');
|
|
||||||
if (PHP_VERSION_ID < 80100) {
|
|
||||||
$traceProperty->setAccessible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
$trace = $exception->getTrace();
|
|
||||||
if (isset($trace[0]['args'])) {
|
|
||||||
$trace = array_map(
|
|
||||||
function (array $call): array {
|
|
||||||
$call['args'] = array_map([$this, 'flattenArguments'], $call['args'] ?? []);
|
|
||||||
|
|
||||||
return $call;
|
|
||||||
},
|
|
||||||
$trace
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$traceProperty->setValue($exception, $trace);
|
|
||||||
$exception = $exception->getPrevious();
|
|
||||||
} while ($exception !== null);
|
|
||||||
|
|
||||||
if (PHP_VERSION_ID >= 80100) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$traceProperty->setAccessible(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $value
|
|
||||||
*
|
|
||||||
* @return mixed
|
|
||||||
*
|
|
||||||
* @throws ReflectionException
|
|
||||||
*/
|
|
||||||
private function flattenArguments($value)
|
|
||||||
{
|
|
||||||
if ($value instanceof Closure) {
|
|
||||||
$closureReflection = new ReflectionFunction($value);
|
|
||||||
$value = sprintf(
|
|
||||||
'(Closure at %s:%s)',
|
|
||||||
$closureReflection->getFileName(),
|
|
||||||
$closureReflection->getStartLine()
|
|
||||||
);
|
|
||||||
} elseif (is_object($value)) {
|
|
||||||
$value = sprintf('object(%s)', get_class($value));
|
|
||||||
} elseif (is_resource($value)) {
|
|
||||||
$value = sprintf('resource(%s)', get_resource_type($value));
|
|
||||||
} elseif (is_array($value)) {
|
|
||||||
$value = array_map([$this, 'flattenArguments'], $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function render(?Formatter $formatter = null): string
|
|
||||||
{
|
|
||||||
if ($formatter === null) {
|
|
||||||
$formatter = new Formatter\PassthroughFormatter();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $formatter->format($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
return $this->body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This file is part of phpDocumentor.
|
|
||||||
*
|
|
||||||
* For the full copyright and license information, please view the LICENSE
|
|
||||||
* file that was distributed with this source code.
|
|
||||||
*
|
|
||||||
* @link http://phpdoc.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace phpDocumentor\Reflection\DocBlock\Tags;
|
|
||||||
|
|
||||||
use phpDocumentor\Reflection\DocBlock\Description;
|
|
||||||
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
|
|
||||||
use phpDocumentor\Reflection\Types\Context as TypeContext;
|
|
||||||
use phpDocumentor\Reflection\Utils;
|
|
||||||
use Webmozart\Assert\Assert;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reflection class for a {@}link tag in a Docblock.
|
|
||||||
*/
|
|
||||||
final class Link extends BaseTag
|
|
||||||
{
|
|
||||||
protected string $name = 'link';
|
|
||||||
|
|
||||||
private string $link;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes a link to a URL.
|
|
||||||
*/
|
|
||||||
public function __construct(string $link, ?Description $description = null)
|
|
||||||
{
|
|
||||||
$this->link = $link;
|
|
||||||
$this->description = $description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function create(
|
|
||||||
string $body,
|
|
||||||
?DescriptionFactory $descriptionFactory = null,
|
|
||||||
?TypeContext $context = null
|
|
||||||
): self {
|
|
||||||
Assert::notNull($descriptionFactory);
|
|
||||||
|
|
||||||
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
|
|
||||||
$description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
|
|
||||||
|
|
||||||
return new static($parts[0], $description);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the link
|
|
||||||
*/
|
|
||||||
public function getLink(): string
|
|
||||||
{
|
|
||||||
return $this->link;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string representation for this tag.
|
|
||||||
*/
|
|
||||||
public function __toString(): string
|
|
||||||
{
|
|
||||||
if ($this->description) {
|
|
||||||
$description = $this->description->render();
|
|
||||||
} else {
|
|
||||||
$description = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$link = $this->link;
|
|
||||||
|
|
||||||
return $link . ($description !== '' ? ($link !== '' ? ' ' : '') . $description : '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user