├── .gitattributes
├── .github
├── dependabot.yml
└── workflows
│ ├── dependabot-auto-merge.yml
│ ├── documentation.yaml
│ └── integrate.yaml
├── .gitignore
├── .phive
└── phars.xml
├── .phpdoc
└── template
│ └── base.html.twig
├── .yamllint.yaml
├── LICENSE
├── Makefile
├── README.md
├── composer-require-checker.json
├── composer.json
├── composer.lock
├── docs
├── extending
│ ├── index.rst
│ └── meta-data.rst
├── getting-started.rst
├── index.rst
└── reflection-structure.rst
├── example.php
├── phpbench.json
├── phpcs.xml.dist
├── phpdoc.dist.xml
├── phpstan.neon
├── phpunit.xml.dist
├── psalm-baseline.xml
├── psalm.xml
├── rector.php
├── src
├── php-parser
│ └── Modifiers.php
└── phpDocumentor
│ └── Reflection
│ ├── Exception.php
│ ├── File
│ └── LocalFile.php
│ ├── Metadata
│ ├── MetaDataContainer.php
│ └── Metadata.php
│ ├── Middleware
│ ├── ChainFactory.php
│ ├── Command.php
│ └── Middleware.php
│ ├── NodeVisitor
│ ├── ElementNameResolver.php
│ └── FindingVisitor.php
│ ├── Php
│ ├── Argument.php
│ ├── AsymmetricVisibility.php
│ ├── Attribute.php
│ ├── AttributeContainer.php
│ ├── CallArgument.php
│ ├── Class_.php
│ ├── Constant.php
│ ├── EnumCase.php
│ ├── Enum_.php
│ ├── Factory
│ │ ├── AbstractFactory.php
│ │ ├── ClassConstant.php
│ │ ├── ClassConstantIterator.php
│ │ ├── Class_.php
│ │ ├── ConstructorPromotion.php
│ │ ├── ContextStack.php
│ │ ├── Define.php
│ │ ├── EnumCase.php
│ │ ├── Enum_.php
│ │ ├── File.php
│ │ ├── File
│ │ │ └── CreateCommand.php
│ │ ├── Function_.php
│ │ ├── GlobalConstant.php
│ │ ├── GlobalConstantIterator.php
│ │ ├── IfStatement.php
│ │ ├── Interface_.php
│ │ ├── Method.php
│ │ ├── Namespace_.php
│ │ ├── Noop.php
│ │ ├── Property.php
│ │ ├── PropertyBuilder.php
│ │ ├── PropertyIterator.php
│ │ ├── Reducer
│ │ │ ├── Attribute.php
│ │ │ ├── Parameter.php
│ │ │ └── Reducer.php
│ │ ├── TraitUse.php
│ │ ├── Trait_.php
│ │ └── Type.php
│ ├── File.php
│ ├── Function_.php
│ ├── HasAttributes.php
│ ├── Interface_.php
│ ├── MetadataContainer.php
│ ├── Method.php
│ ├── Namespace_.php
│ ├── NodesFactory.php
│ ├── Project.php
│ ├── ProjectFactory.php
│ ├── ProjectFactoryStrategies.php
│ ├── ProjectFactoryStrategy.php
│ ├── Property.php
│ ├── PropertyHook.php
│ ├── StrategyContainer.php
│ ├── Trait_.php
│ ├── ValueEvaluator
│ │ └── ConstantEvaluator.php
│ └── Visibility.php
│ └── Types
│ └── NamespaceNodeToContext.php
└── tests
├── assets
└── phpunit_assert.php
├── bench
└── ProjectFactoryBench.php
├── coverage-checker.php
├── example.file.php
├── integration
├── AsymmetricAccessorTest.php
├── ClassesTest.php
├── EnumTest.php
├── FileDocblockTest.php
├── Metadata
│ ├── Hook.php
│ ├── HookStrategy.php
│ └── example.php
├── MetadataTest.php
├── PHP8
│ ├── ConstructorPromotionTest.php
│ ├── MixedTypeTest.php
│ ├── StaticTypeTest.php
│ └── UnionTypesTest.php
├── ProjectCreationTest.php
├── ProjectNamespaceTest.php
├── PropertyHookTest.php
└── data
│ ├── Enums
│ ├── EnumConsumer.php
│ ├── backedEnum.php
│ ├── base.php
│ └── enumWithConstant.php
│ ├── GlobalFiles
│ ├── conditional_function.php
│ ├── docblock_followed_by_html.php
│ ├── empty.php
│ ├── empty_shebang.php
│ ├── empty_with_declare.php
│ ├── empty_with_html.php
│ ├── global_namspaced_function.php
│ ├── inline_function.php
│ └── psr12.php
│ ├── Luigi
│ ├── ExampleNestedTrait.php
│ ├── Packing.php
│ ├── Pizza.php
│ ├── StyleFactory.php
│ ├── Valued.php
│ └── constants.php
│ ├── PHP8
│ ├── ConstructorPromotion.php
│ ├── MixedType.php
│ ├── StaticType.php
│ └── UnionTypes.php
│ ├── PHP84
│ ├── AsymmetricAccessor.php
│ ├── AsymmetricPropertyPromotion.php
│ ├── PropertyHook.php
│ ├── PropertyHookAsymmetric.php
│ ├── PropertyHookPromotion.php
│ └── PropertyHookVirtual.php
│ ├── Packing.php
│ ├── Pizza.php
│ └── simpleFunction.php
└── unit
└── phpDocumentor
└── Reflection
├── File
└── LocalFileTest.php
├── Middleware
└── ChainFactoryTest.php
├── NodeVisitor
└── ElementNameResolverTest.php
├── Php
├── ArgumentTest.php
├── Class_Test.php
├── ConstantTest.php
├── EnumCaseTest.php
├── Enum_Test.php
├── Factory
│ ├── ClassConstantIteratorTest.php
│ ├── ClassConstantTest.php
│ ├── Class_Test.php
│ ├── ConstructorPromotionTest.php
│ ├── ContextStackTest.php
│ ├── DefineTest.php
│ ├── DummyFactoryStrategy.php
│ ├── EnumCaseTest.php
│ ├── Enum_Test.php
│ ├── File
│ │ └── CreateCommandTest.php
│ ├── FileTest.php
│ ├── Function_Test.php
│ ├── GlobalConstantIteratorTest.php
│ ├── GlobalConstantTest.php
│ ├── Interface_Test.php
│ ├── MethodTest.php
│ ├── Namespace_Test.php
│ ├── PropertyBuilderTest.php
│ ├── PropertyIteratorTest.php
│ ├── PropertyTest.php
│ ├── TestCase.php
│ ├── TraitUseTest.php
│ ├── Trait_Test.php
│ └── TypeTest.php
├── FileTest.php
├── Function_Test.php
├── Interface_Test.php
├── MetadataContainerTestHelper.php
├── MetadataStub.php
├── MethodTest.php
├── Namespace_Test.php
├── NodesFactoryTest.php
├── ProjectFactoryStrategiesTest.php
├── ProjectFactoryTest.php
├── ProjectTest.php
├── PropertyTest.php
├── TestCase.php
├── Trait_Test.php
├── ValueEvaluator
│ └── ConstantEvaluatorTest.php
└── VisibilityTest.php
└── Types
└── NamespaceNodeToContextTest.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.php text eol=lf
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "composer"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | open-pull-requests-limit: 10
8 |
9 | - package-ecosystem: "github-actions"
10 | directory: "/"
11 | schedule:
12 | interval: "weekly"
13 |
--------------------------------------------------------------------------------
/.github/workflows/dependabot-auto-merge.yml:
--------------------------------------------------------------------------------
1 | name: "Dependabot auto-merge"
2 |
3 | on: # yamllint disable-line rule:truthy
4 | pull_request_target: null
5 |
6 | permissions:
7 | contents: "write"
8 |
9 | jobs:
10 | dependabot:
11 | runs-on: "ubuntu-latest"
12 | if: "${{ github.actor == 'dependabot[bot]' }}"
13 | steps:
14 | - name: "Dependabot metadata"
15 | id: "metadata"
16 | uses: "dependabot/fetch-metadata@v2.4.0"
17 | with:
18 | github-token: "${{ secrets.GITHUB_TOKEN }}"
19 | - name: "Enable auto-merge for Dependabot PRs"
20 | if: "${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}"
21 | run: "gh pr merge --auto --merge \"$PR_URL\""
22 | env:
23 | PR_URL: "${{github.event.pull_request.html_url}}"
24 | GITHUB_TOKEN: "${{secrets.GITHUB_TOKEN}}"
25 |
--------------------------------------------------------------------------------
/.github/workflows/documentation.yaml:
--------------------------------------------------------------------------------
1 | name: "Documentation"
2 |
3 | on: # yamllint disable-line rule:truthy
4 | push:
5 | branches:
6 | - "6.x"
7 | pull_request: null
8 |
9 | jobs:
10 | documentation:
11 | name: "Documentation"
12 | runs-on: "ubuntu-latest"
13 | steps:
14 | - name: "Checkout"
15 | uses: "actions/checkout@v4"
16 |
17 | - name: "Build"
18 | uses: "phpDocumentor/phpDocumentor@master"
19 |
20 | - name: "Deploy"
21 | if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/6.x' }}"
22 | uses: "actions/upload-artifact@v4"
23 | with:
24 | name: "documentation"
25 | path: "build/docs"
26 | retention-days: 1
27 |
28 | deploy:
29 | name: "Deploy"
30 | if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/6.x' }}"
31 | runs-on: "ubuntu-latest"
32 | needs: "documentation"
33 | steps:
34 | - name: "Checkout"
35 | uses: "actions/checkout@v4"
36 | with:
37 | repository: "phpDocumentor/docs"
38 | token: "${{ secrets.BOT_TOKEN }}"
39 | path: "docs"
40 |
41 | - name: "Download"
42 | uses: "actions/download-artifact@v4"
43 | with:
44 | name: "documentation"
45 | path: "build/docs"
46 |
47 | - name: "Copy files"
48 | run: "rsync -r --delete build/docs/* docs/docs/components/reflection"
49 |
50 | - name: "Commit"
51 | uses: "stefanzweifel/git-auto-commit-action@v5"
52 | with:
53 | repository: "docs"
54 | commit_message: "Update reflection documentation"
55 |
56 | - name: "Push"
57 | uses: "ad-m/github-push-action@master"
58 | with:
59 | directory: "docs"
60 | github_token: "${{ secrets.BOT_TOKEN }}"
61 | repository: "phpDocumentor/docs"
62 |
--------------------------------------------------------------------------------
/.github/workflows/integrate.yaml:
--------------------------------------------------------------------------------
1 | # https://docs.github.com/en/actions
2 |
3 | name: "Integrate"
4 |
5 | on: # yamllint disable-line rule:truthy
6 | push:
7 | branches:
8 | - "6.x"
9 | pull_request: null
10 | # Allow manually triggering the workflow.
11 | workflow_dispatch: null
12 |
13 | jobs:
14 | code-coverage:
15 | name: "Code Coverage"
16 | uses: "phpDocumentor/.github/.github/workflows/code-coverage.yml@v0.8"
17 | with:
18 | php-version: "8.2"
19 |
20 | coding-standards:
21 | name: "Coding Standards"
22 | runs-on: "ubuntu-22.04"
23 | steps:
24 | - name: "Checkout"
25 | uses: "actions/checkout@v4"
26 |
27 | - name: "Install PHP"
28 | uses: "shivammathur/setup-php@v2"
29 | with:
30 | coverage: "none"
31 | php-version: "8.2"
32 | tools: "cs2pr"
33 |
34 | - name: "Install dependencies with Composer"
35 | uses: "ramsey/composer-install@v3"
36 | with:
37 | dependency-versions: "locked"
38 |
39 | - name: "Run PHP_CodeSniffer"
40 | run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
41 |
42 | dependency-analysis:
43 | name: "Dependency analysis"
44 | uses: "phpDocumentor/.github/.github/workflows/dependency-analysis.yml@v0.8"
45 | with:
46 | php-version: "8.2"
47 |
48 | lint-root:
49 | name: "Lint root"
50 | uses: "phpDocumentor/.github/.github/workflows/lint.yml@v0.8"
51 | with:
52 | php-version: "8.2"
53 | composer-options: "--no-check-publish --ansi"
54 |
55 | static-analysis:
56 | name: "Static analysis"
57 | uses: "phpDocumentor/.github/.github/workflows/static-analysis.yml@main"
58 | with:
59 | php-version: "8.2"
60 | php-extensions: "none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, pcntl, posix"
61 |
62 | unit-tests:
63 | name: "Unit test"
64 | uses: "phpDocumentor/.github/.github/workflows/continuous-integration.yml@v0.8"
65 |
66 | integration-tests:
67 | name: "Integration test"
68 | uses: "phpDocumentor/.github/.github/workflows/continuous-integration.yml@v0.8"
69 | needs: "unit-tests"
70 | with:
71 | test-suite: "integration"
72 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # IDE Shizzle; it is recommended to use a global .gitignore for this but since this is an OSS project we want to make
2 | # it easy to contribute
3 | .idea
4 | /nbproject/private/
5 | .buildpath
6 | .project
7 | .settings
8 |
9 | # Build folder and vendor folder are generated code; no need to version this
10 | build/
11 | tools/
12 | temp/
13 | vendor/
14 | *.phar
15 |
16 | # By default the phpunit.xml.dist is provided; you can override this using a local config file
17 | phpunit.xml
18 | .phpunit.result.cache
19 | .phpunit.cache
20 |
21 | .phpdoc/cache
22 |
--------------------------------------------------------------------------------
/.phive/phars.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.phpdoc/template/base.html.twig:
--------------------------------------------------------------------------------
1 | {% extends 'layout.html.twig' %}
2 |
3 | {% set topMenu = {
4 | "menu": [
5 | { "name": "About", "url": "https://phpdoc.org/"},
6 | { "name": "Components", "url": "https://phpdoc.org/components.html"},
7 | { "name": "Documentation", "url": "https://docs.phpdoc.org/"},
8 | ],
9 | "social": [
10 | { "iconClass": "fab fa-mastodon", "url": "https://phpc.social/@phpdoc"},
11 | { "iconClass": "fab fa-github", "url": "https://github.com/phpdocumentor/typeresolver"},
12 | { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/orgs/phpDocumentor/discussions"}
13 | ]
14 | }
15 | %}
16 |
--------------------------------------------------------------------------------
/.yamllint.yaml:
--------------------------------------------------------------------------------
1 | extends: "default"
2 |
3 | ignore: |
4 | .build/
5 | .notes/
6 | vendor/
7 | rules:
8 | braces:
9 | max-spaces-inside-empty: 0
10 | max-spaces-inside: 1
11 | min-spaces-inside-empty: 0
12 | min-spaces-inside: 1
13 | brackets:
14 | max-spaces-inside-empty: 0
15 | max-spaces-inside: 0
16 | min-spaces-inside-empty: 0
17 | min-spaces-inside: 0
18 | colons:
19 | max-spaces-after: 1
20 | max-spaces-before: 0
21 | commas:
22 | max-spaces-after: 1
23 | max-spaces-before: 0
24 | min-spaces-after: 1
25 | comments:
26 | ignore-shebangs: true
27 | min-spaces-from-content: 1
28 | require-starting-space: true
29 | comments-indentation: "enable"
30 | document-end:
31 | present: false
32 | document-start:
33 | present: false
34 | indentation:
35 | check-multi-line-strings: false
36 | indent-sequences: true
37 | spaces: 2
38 | empty-lines:
39 | max-end: 0
40 | max-start: 0
41 | max: 1
42 | empty-values:
43 | forbid-in-block-mappings: true
44 | forbid-in-flow-mappings: true
45 | hyphens:
46 | max-spaces-after: 2
47 | key-duplicates: "enable"
48 | key-ordering: "disable"
49 | line-length: "disable"
50 | new-line-at-end-of-file: "enable"
51 | new-lines:
52 | type: "unix"
53 | octal-values:
54 | forbid-implicit-octal: true
55 | quoted-strings:
56 | quote-type: "double"
57 | trailing-spaces: "enable"
58 | truthy:
59 | allowed-values:
60 | - "false"
61 | - "true"
62 |
63 | yaml-files:
64 | - "*.yaml"
65 | - "*.yml"
66 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2010 Mike van Riel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | .PHONY: help
2 | help: ## Displays this list of targets with descriptions
3 | @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}'
4 |
5 | .PHONY: code-style
6 | code-style:
7 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpcs
8 |
9 | .PHONY: fix-code-style
10 | fix-code-style:
11 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpcbf
12 |
13 | .PHONY: static-code-analysis
14 | static-code-analysis: #vendor ## Runs a static code analysis with phpstan/phpstan and vimeo/psalm
15 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.2-cli vendor/bin/phpstan --configuration=phpstan.neon
16 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.2-cli vendor/bin/psalm.phar --show-info=true --threads=4
17 |
18 | .PHONY: test
19 | test: test-unit test-functional ## Runs all test suites with phpunit/phpunit
20 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit
21 |
22 | .PHONY: test-unit
23 | test-unit: ## Runs unit tests with phpunit/phpunit
24 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit --testsuite=unit
25 |
26 | .PHONY: test-functional
27 | test-functional: ## Runs unit tests with phpunit/phpunit
28 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit --testsuite=integration
29 |
30 | .PHONY: dependency-analysis
31 | dependency-analysis: vendor ## Runs a dependency analysis with maglnet/composer-require-checker
32 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli .phive/composer-require-checker check --config-file=/opt/project/composer-require-checker.json
33 |
34 | vendor: composer.json composer.lock
35 | composer validate --no-check-publish
36 | composer install --no-interaction --no-progress
37 |
38 | .PHONY: benchmark
39 | benchmark:
40 | docker run -it --rm -v${CURDIR}:/opt/project -w /opt/project php:8.1-cli tools/phpbench run
41 |
42 | .PHONY: rector
43 | rector: ## Refactor code using rector
44 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/rector process
45 |
46 | .PHONY: pre-commit-test
47 | pre-commit-test: fix-code-style test code-style static-code-analysis
48 |
49 | .PHONY: docs
50 | docs: ## Generate documentation
51 | docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpdoc:3-unstable
52 |
--------------------------------------------------------------------------------
/composer-require-checker.json:
--------------------------------------------------------------------------------
1 | {
2 | "symbol-whitelist" : [
3 | "null", "true", "false",
4 | "static", "self", "parent",
5 | "array", "string", "int", "float", "bool", "iterable", "callable", "void", "object",
6 | "PhpParser\\Node\\Stmt\\PropertyProperty"
7 | ],
8 | "php-core-extensions" : [
9 | "Core",
10 | "pcre",
11 | "Reflection",
12 | "tokenizer",
13 | "SPL",
14 | "standard"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phpdocumentor/reflection",
3 | "description": "Reflection library to do Static Analysis for PHP Projects",
4 | "keywords": ["phpdoc", "phpDocumentor", "reflection", "static analysis"],
5 | "homepage": "http://www.phpdoc.org",
6 | "license": "MIT",
7 | "autoload": {
8 | "files": [
9 | "src/php-parser/Modifiers.php"
10 | ],
11 | "psr-4": {
12 | "phpDocumentor\\": "src/phpDocumentor"
13 | }
14 | },
15 | "autoload-dev": {
16 | "psr-4": {
17 | "phpDocumentor\\": [
18 | "tests/unit/phpDocumentor",
19 | "tests/bench/"
20 | ],
21 | "phpDocumentor\\Reflection\\": [
22 | "tests/integration"
23 | ]
24 | }
25 | },
26 | "require": {
27 | "php": "8.1.*|8.2.*|8.3.*|8.4.*",
28 | "composer-runtime-api": "^2",
29 | "nikic/php-parser": "~4.18 || ^5.0",
30 | "phpdocumentor/reflection-common": "^2.1",
31 | "phpdocumentor/reflection-docblock": "^5",
32 | "phpdocumentor/type-resolver": "^1.2",
33 | "symfony/polyfill-php80": "^1.28",
34 | "webmozart/assert": "^1.7"
35 | },
36 | "require-dev": {
37 | "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
38 | "doctrine/coding-standard": "^13.0",
39 | "eliashaeussler/phpunit-attributes": "^1.7",
40 | "mikey179/vfsstream": "~1.2",
41 | "mockery/mockery": "~1.6.0",
42 | "phpspec/prophecy-phpunit": "^2.0",
43 | "phpstan/extension-installer": "^1.1",
44 | "phpstan/phpstan": "^1.8",
45 | "phpstan/phpstan-webmozart-assert": "^1.2",
46 | "phpunit/phpunit": "^10.0",
47 | "psalm/phar": "^6.0",
48 | "rector/rector": "^1.0.0",
49 | "squizlabs/php_codesniffer": "^3.8"
50 | },
51 | "config": {
52 | "preferred-install": {
53 | "*": "dist"
54 | },
55 | "sort-packages": true,
56 | "platform": {
57 | "php": "8.1.0"
58 | },
59 | "allow-plugins": {
60 | "phpstan/extension-installer": true,
61 | "dealerdirect/phpcodesniffer-composer-installer": true
62 | }
63 | },
64 | "extra": {
65 | "branch-alias": {
66 | "dev-5.x": "5.3.x-dev",
67 | "dev-6.x": "6.0.x-dev"
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/docs/extending/index.rst:
--------------------------------------------------------------------------------
1 | .. _extending:
2 |
3 | Extend the library
4 | ==================
5 |
6 | The model exposed by this library is closed for inheritance. We did this to ensure the model is stable and does not
7 | change via external factors. The complexity of this project makes it hard to keep all the internal classes stable.
8 | The model is designed to be cached and constructed very carefully to ensure performance and memory usage are optimal.
9 |
10 | Metadata
11 | --------
12 |
13 | Metadata is a way to extend the model with additional information. We call this metadata, as all first class
14 | elements in the reflected codebase are part of the model. Extra data can be added to these elements using metadata.
15 |
16 | Elements supporting metadata are:
17 |
18 | .. phpdoc:class-list:: [?(@.interfaces contains "\phpDocumentor\Reflection\Metadata\MetaDataContainer")]
19 |
20 | .. phpdoc:name::
21 |
22 | .. warning::
23 |
24 | Adding metadata might break the posibilty to cache the model. Be carefull with circular references and large
25 | objects. We do recommend to keep the metadata small and simple.
26 |
27 | Continue reading :doc:`Creating your first metadata `_ to learn how to create your own metadata.
28 |
29 | .. toctree::
30 | :maxdepth: 1
31 | :titlesonly:
32 | :hidden:
33 |
34 | meta-data
35 |
--------------------------------------------------------------------------------
/docs/extending/meta-data.rst:
--------------------------------------------------------------------------------
1 | Metadata
2 | =====
3 |
4 | The model of this library is as closed as possible.
5 | Main reason is because consumers of the library should rely on cache.
6 | A mutable and flexible interface of the model would most likely break the caching.
7 | However after some time the users to this library started requesting for a more flexible format.
8 | This is why metadata was introduced.
9 |
10 | Create your first metadata
11 | --------------------------
12 |
13 | First step is to create your own metadata implementation.
14 |
15 | .. code:: php
16 | final class Hook implements \phpDocumentor\Reflection\Metadata\Metadata
17 | {
18 | private string $hook;
19 |
20 | public function __construct(string $hook)
21 | {
22 | $this->hook = $hook;
23 | }
24 |
25 | public function key(): string
26 | {
27 | return "project-metadata";
28 | }
29 |
30 | public function hook(): string
31 | {
32 | return $this->hook;
33 | }
34 | }
35 |
36 | .. note::
37 | We do highly recommend to keep your metadata objects small.
38 | When reflecting a large project the number of objects will grow fast.
39 |
40 | Now we have an class that can be used it is time to create a :php:class:`\phpDocumentor\Reflection\Php\ProjectFactoryStrategy`.
41 | Strategies are used to reflect nodes in the AST of `phpparser`_.
42 |
43 | In the example below we are adding the Hook metadata to any functions containing a function call.
44 |
45 | .. code:: php
46 |
47 | use \phpDocumentor\Reflection\Php\Function;
48 |
49 | final class HookStrategy implements \phpDocumentor\Reflection\Php\ProjectFactoryStrategy
50 | {
51 | public function matches(ContextStack $context, object $object): bool
52 | {
53 | return $this->context->peek() instanceof Function_ &&
54 | $object instanceof \PhpParser\Node\Expr\FuncCall &&
55 | ((string)$object->name) === 'hook'
56 | }
57 |
58 | public function create(ContextStack $context, object $object, StrategyContainer $strategies): void
59 | {
60 | $method = $context->peek();
61 | $method->addMetadata(new Hook($object->args[0]->value));
62 | }
63 | }
64 |
65 | .. note::
66 | To speed up the reflection of your project the default factory instance has a Noop strategy. This strategy will
67 | ignore all statements that are not handled by any strategy. Keep this in mind when implementing your own strategies
68 | especially the statements you are looking for are nested in other statements like a ``while`` loop.
69 |
70 | Finally add your new strategy to the project factory.
71 |
72 | .. code:: php
73 |
74 | $factory = \phpDocumentor\Reflection\Php\ProjectFactory::createInstance();
75 | $factory->addStrategy(new HookStrategy());
76 |
77 | .. _phpparser: https://github.com/nikic/PHP-Parser/
78 |
--------------------------------------------------------------------------------
/docs/getting-started.rst:
--------------------------------------------------------------------------------
1 | Getting started
2 | ===============
3 |
4 | This page will give you a quick introduction to the `phpdocumentor/reflection` package and how to get started with it.
5 |
6 | Installation
7 | ------------
8 |
9 | In order to inspect a codebase you need to tell composer to include the `phpdocumentor/reflection` package. This
10 | can easily be done using the following command in your command line terminal:
11 |
12 | .. code-block:: bash
13 |
14 | composer require phpdocumentor/reflection:~6.0
15 |
16 | In order to use the library you need to include the autoloader of composer in your code.
17 | This can be done by adding the following line to your code:
18 |
19 | .. code-block:: php
20 |
21 | create('My Project', $projectFiles);
43 |
44 | When the process is ready a new object of type :php:class:`phpDocumentor\Reflection\Php\Project` will be returned that
45 | contains a complete hierarchy of all files with their classes, traits and interfaces (and everything in there), but also
46 | all namespaces and packages as a hierarchical tree.
47 | This library does not provide a way to access the structure of the codebase in a searchable way.
48 | This is up to the consumer of the library to implement.
49 |
50 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | Reflection
2 | ==========
3 |
4 | Using this library it is possible to statically reflect one or more files and create an object graph representing
5 | your application's structure, including accompanying in-source documentation using DocBlocks.
6 |
7 | The information that this library provides is similar to what the (built-in) Reflection extension of PHP provides; there
8 | are however several advantages to using this library:
9 |
10 | - Due to its Static nature it does not execute procedural code in your reflected files where Dynamic Reflection does.
11 | - Because the none of the code is interpreted by PHP (and executed) Static Reflection uses less memory.
12 | - Can reflect complete files
13 | - Can reflect a whole project by reflecting multiple files.
14 | - Reflects the contents of a DocBlock instead of just mentioning there is one.
15 | - Is capable of analyzing code written for any PHP version (starting at 5.2) up to the lastest version, even if your installed
16 | PHP version is lower than the code you are reflecting.
17 |
18 | .. note::
19 | As this library focuses on reflecting the structure of the codebase, it does not provide any options to manipulate
20 | the output. If you want to collect more information from the codebase you can read about :ref:`extending the library `.
21 |
22 | .. toctree::
23 | :hidden:
24 | :maxdepth: 2
25 |
26 | getting-started
27 | reflection-structure
28 | extending/index
29 |
--------------------------------------------------------------------------------
/docs/reflection-structure.rst:
--------------------------------------------------------------------------------
1 | Reflection structure
2 | ====================
3 |
4 | The project created by the :php:class:`\phpDocumentor\Reflection\Php\ProjectFactory` class contains a hierarchy of objects
5 | that represent the structure of your codebase. This hierarchy includes:
6 |
7 | Files
8 | Each file is represented by an object of type :php:class:`\phpDocumentor\Reflection\Php\File` which contains
9 | information about the file such as its name, path, and contents. But also the elements that are defined in the file.
10 | Files can be accessed through the :php:method:`\phpDocumentor\Reflection\Php\Project::getFiles()` method of the project object.
11 | Files are a flat list of all files that were analyzed, regardless of their location in the directory structure.
12 |
13 | Namespaces
14 | Namespaces are represented by objects of type :php:class:`\phpDocumentor\Reflection\Php\Namespace_`. Each namespace
15 | contains a list of classes, interfaces, traits, and functions that are defined within it. Namespaces can be accessed
16 | through the :php:method:`\phpDocumentor\Reflection\Php\Project::getNamespaces()` method of the project object.
17 | Namespaces are hierarchical and can contain sub-namespaces.
18 |
19 | Both namespaces and files do contain the other structural elements that are defined in them, such as classes, interfaces, traits, and functions.
20 | This library does not provide a way to access the structure of the codebase in a searchable way. This is up to the consumer of the library to implement.
21 |
--------------------------------------------------------------------------------
/example.php:
--------------------------------------------------------------------------------
1 | create('MyProject', $files);
30 |
31 | // As an example of what you can do, let's list all class names in the file 'tests/example.file.php'.
32 | echo 'List all classes in the example source file: ' . PHP_EOL;
33 |
34 | /** @var \phpDocumentor\Reflection\Php\Class_ $class */
35 | foreach ($project->getFiles()['tests/example.file.php']->getClasses() as $class) {
36 | echo '- ' . $class->getFqsen() . PHP_EOL;
37 | }
38 |
--------------------------------------------------------------------------------
/phpbench.json:
--------------------------------------------------------------------------------
1 | {
2 | "bootstrap": "vendor/autoload.php",
3 | "path": "tests/bench",
4 | "extensions": [
5 | "PhpBench\\Extensions\\XDebug\\XDebugExtension"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/phpcs.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 | The coding standard for this library.
4 |
5 | src
6 | tests/unit
7 | */tests/unit/Types/ContextFactoryTest\.php
8 |
9 |
10 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | packages/guides/src/Setup/QuickStart.php
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | */tests/unit/*
60 |
61 |
62 |
63 | */tests/unit/*
64 | */src/phpDocumentor/Reflection/Php/*
65 |
66 |
67 |
--------------------------------------------------------------------------------
/phpdoc.dist.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 | Guides
9 |
10 |
11 |
12 |
13 | latest
14 |
15 |
16 | src/phpDocumentor
17 |
18 |
19 |
20 | tests/**/*
21 | build/**/*
22 | var/**/*
23 | vendor/**/*
24 |
25 |
26 | php
27 |
28 |
29 | template
30 | template-extends
31 | template-implements
32 | extends
33 | implements
34 |
35 | phpDocumentor
36 |
37 |
38 |
39 | docs
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/phpstan.neon:
--------------------------------------------------------------------------------
1 | parameters:
2 | paths:
3 | - src
4 |
5 | level: max
6 | ignoreErrors:
7 |
8 | - '#Method phpDocumentor\\Reflection\\File\\LocalFile::md5\(\) should return string but returns string\|false\.#'
9 | - '#Else branch is unreachable because ternary operator condition is always true\.#'
10 | #
11 | # all these $fqsen errors indicate the need for a decorator class around PhpParser\Node to hold the public $fqsen that Reflection is giving it)
12 | #
13 | # src/phpDocumentor/Reflection/NodeVisitor/ElementNameResolver.php
14 | - '#Method phpDocumentor\\Reflection\\Php\\Factory\\(.*)::getFqsen\(\) should return phpDocumentor\\Reflection\\Fqsen but returns mixed\.#'
15 | - '#Parameter \#1 \$fqsen of class phpDocumentor\\Reflection\\Php\\(.*) constructor expects phpDocumentor\\Reflection\\Fqsen, mixed given\.#'
16 | - '#Parameter \#1 \$fqsen of method phpDocumentor\\Reflection\\Php\\File::addNamespace\(\) expects phpDocumentor\\Reflection\\Fqsen, mixed given\.#'
17 | #
18 | # Type hint in php-parser is incorrect.
19 | - '#Cannot cast PhpParser\\Node\\Expr\|string to string.#'
20 |
21 | - '#Parameter \#2 \$object of method phpDocumentor\\Reflection\\Php\\ProjectFactoryStrategy::matches\(\) expects object, mixed given.#'
22 | - '#Method phpDocumentor\\Reflection\\Php\\ValueEvaluator\\ConstantEvaluator::evaluate\(\) should return string but returns mixed.#'
23 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ./tests/unit
15 |
16 |
17 | ./tests/integration
18 |
19 |
20 |
21 |
22 |
23 | ./src/
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/psalm.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/rector.php:
--------------------------------------------------------------------------------
1 | paths([
11 | __DIR__ . '/src',
12 | __DIR__ . '/tests/unit'
13 | ]);
14 |
15 | // register a single rule
16 | $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
17 | $rectorConfig->rule(Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector::class);
18 | $rectorConfig->importNames();
19 |
20 | // define sets of rules
21 | $rectorConfig->sets([
22 | LevelSetList::UP_TO_PHP_81,
23 | ]);
24 | };
25 |
--------------------------------------------------------------------------------
/src/php-parser/Modifiers.php:
--------------------------------------------------------------------------------
1 | path = $path;
42 | }
43 |
44 | /**
45 | * Returns the content of the file as a string.
46 | */
47 | #[Override]
48 | public function getContents(): string
49 | {
50 | return (string) file_get_contents($this->path);
51 | }
52 |
53 | /**
54 | * Returns md5 hash of the file.
55 | */
56 | #[Override]
57 | public function md5(): string
58 | {
59 | return md5_file($this->path);
60 | }
61 |
62 | /**
63 | * Returns a relative path to the file.
64 | */
65 | #[Override]
66 | public function path(): string
67 | {
68 | return $this->path;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Metadata/MetaDataContainer.php:
--------------------------------------------------------------------------------
1 | $middleware->execute($command, $lastCallable);
39 | }
40 |
41 | return $lastCallable;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Middleware/Command.php:
--------------------------------------------------------------------------------
1 | foundNode = null;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Argument.php:
--------------------------------------------------------------------------------
1 | type = $type;
48 | }
49 |
50 | /**
51 | * Returns the name of this argument.
52 | */
53 | public function getName(): string
54 | {
55 | return $this->name;
56 | }
57 |
58 | public function getType(): Type|null
59 | {
60 | return $this->type;
61 | }
62 |
63 | public function getDefault(): string|null
64 | {
65 | return $this->default;
66 | }
67 |
68 | public function isByReference(): bool
69 | {
70 | return $this->byReference;
71 | }
72 |
73 | public function isVariadic(): bool
74 | {
75 | return $this->isVariadic;
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/AsymmetricVisibility.php:
--------------------------------------------------------------------------------
1 | readVisibility;
20 | }
21 |
22 | public function getWriteVisibility(): Visibility
23 | {
24 | return $this->writeVisibility;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Attribute.php:
--------------------------------------------------------------------------------
1 | fqsen;
23 | }
24 |
25 | /** @return CallArgument[] */
26 | public function getArguments(): array
27 | {
28 | return $this->arguments;
29 | }
30 |
31 | #[Override]
32 | public function getName(): string
33 | {
34 | return $this->fqsen->getName();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/AttributeContainer.php:
--------------------------------------------------------------------------------
1 | value;
21 | }
22 |
23 | public function getName(): string|null
24 | {
25 | return $this->name;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Constant.php:
--------------------------------------------------------------------------------
1 | location = $location ?: new Location(-1);
52 | $this->endLocation = $endLocation ?: new Location(-1);
53 | $this->visibility = $visibility ?: new Visibility(Visibility::PUBLIC_);
54 | }
55 |
56 | /**
57 | * Returns the value of this constant.
58 | */
59 | public function getValue(): string|null
60 | {
61 | return $this->value;
62 | }
63 |
64 | /**
65 | * Returns the Fqsen of the element.
66 | */
67 | #[Override]
68 | public function getFqsen(): Fqsen
69 | {
70 | return $this->fqsen;
71 | }
72 |
73 | /**
74 | * Returns the name of the element.
75 | */
76 | #[Override]
77 | public function getName(): string
78 | {
79 | return $this->fqsen->getName();
80 | }
81 |
82 | /**
83 | * Returns DocBlock of this constant if available.
84 | */
85 | public function getDocBlock(): DocBlock|null
86 | {
87 | return $this->docBlock;
88 | }
89 |
90 | public function getLocation(): Location
91 | {
92 | return $this->location;
93 | }
94 |
95 | public function getEndLocation(): Location
96 | {
97 | return $this->endLocation;
98 | }
99 |
100 | public function getVisibility(): Visibility
101 | {
102 | return $this->visibility;
103 | }
104 |
105 | public function isFinal(): bool
106 | {
107 | return $this->final;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/EnumCase.php:
--------------------------------------------------------------------------------
1 | location = $location;
44 | $this->endLocation = $endLocation;
45 | }
46 |
47 | #[Override]
48 | public function getFqsen(): Fqsen
49 | {
50 | return $this->fqsen;
51 | }
52 |
53 | #[Override]
54 | public function getName(): string
55 | {
56 | return $this->fqsen->getName();
57 | }
58 |
59 | public function getDocBlock(): DocBlock|null
60 | {
61 | return $this->docBlock;
62 | }
63 |
64 | public function getLocation(): Location
65 | {
66 | return $this->location;
67 | }
68 |
69 | public function getEndLocation(): Location
70 | {
71 | return $this->endLocation;
72 | }
73 |
74 | public function getValue(): string|null
75 | {
76 | return $this->value;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/AbstractFactory.php:
--------------------------------------------------------------------------------
1 | $reducers */
33 | public function __construct(
34 | protected readonly DocBlockFactoryInterface $docBlockFactory,
35 | protected readonly iterable $reducers = [],
36 | ) {
37 | }
38 |
39 | /**
40 | * Returns true when the strategy is able to handle the object.
41 | *
42 | * @param object $object object to check.
43 | */
44 | #[Override]
45 | abstract public function matches(ContextStack $context, object $object): bool;
46 |
47 | #[Override]
48 | public function create(ContextStack $context, object $object, StrategyContainer $strategies): void
49 | {
50 | if (!$this->matches($context, $object)) {
51 | throw new InvalidArgumentException(
52 | sprintf(
53 | '%s cannot handle objects with the type %s',
54 | self::class,
55 | get_debug_type($object),
56 | ),
57 | );
58 | }
59 |
60 | $element = $this->doCreate($context, $object, $strategies);
61 | foreach ($this->reducers as $reducer) {
62 | $element = $reducer->reduce($context, $object, $strategies, $element);
63 | }
64 | }
65 |
66 | /**
67 | * Creates an Element out of the given object.
68 | *
69 | * Since an object might contain other objects that need to be converted the $factory is passed so it can be
70 | * used to create nested Elements.
71 | *
72 | * @param NodeAbstract|object $object object to convert to an Element
73 | */
74 | abstract protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): object|null;
75 |
76 | protected function createDocBlock(Doc|null $docBlock = null, Context|null $context = null): DocBlock|null
77 | {
78 | if ($docBlock === null) {
79 | return null;
80 | }
81 |
82 | return $this->docBlockFactory->create($docBlock->getText(), $context);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Class_.php:
--------------------------------------------------------------------------------
1 | createDocBlock($object->getDocComment(), $context->getTypeContext());
50 |
51 | $classElement = new ClassElement(
52 | $object->getAttribute('fqsen'),
53 | $docBlock,
54 | isset($object->extends) ? new Fqsen('\\' . $object->extends) : null,
55 | $object->isAbstract(),
56 | $object->isFinal(),
57 | new Location($object->getLine()),
58 | new Location($object->getEndLine()),
59 | $object->isReadonly(),
60 | );
61 |
62 | foreach ($object->implements as $interfaceClassName) {
63 | $classElement->addInterface(
64 | new Fqsen('\\' . $interfaceClassName->toString()),
65 | );
66 | }
67 |
68 | $file = $context->peek();
69 | assert($file instanceof FileElement);
70 | $file->addClass($classElement);
71 |
72 | foreach ($object->stmts as $stmt) {
73 | $thisContext = $context->push($classElement);
74 | $strategy = $strategies->findMatching($thisContext, $stmt);
75 | $strategy->create($thisContext, $stmt, $strategies);
76 | }
77 |
78 | return $classElement;
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/ContextStack.php:
--------------------------------------------------------------------------------
1 | elements = $elements;
31 |
32 | return $self;
33 | }
34 |
35 | public function push(Element|FileElement|PropertyHook $element): self
36 | {
37 | $elements = $this->elements;
38 | $elements[] = $element;
39 |
40 | return self::createFromSelf($this->project, $this->typeContext, $elements);
41 | }
42 |
43 | public function withTypeContext(TypeContext $typeContext): ContextStack
44 | {
45 | return self::createFromSelf($this->project, $typeContext, $this->elements);
46 | }
47 |
48 | public function getTypeContext(): TypeContext|null
49 | {
50 | return $this->typeContext;
51 | }
52 |
53 | public function getProject(): Project
54 | {
55 | return $this->project;
56 | }
57 |
58 | public function peek(): Element|FileElement|PropertyHook
59 | {
60 | $element = end($this->elements);
61 | if ($element === false) {
62 | throw new OutOfBoundsException('Stack is empty');
63 | }
64 |
65 | return $element;
66 | }
67 |
68 | /**
69 | * Returns the first element of type.
70 | *
71 | * Will reverse search the stack for an element matching $type. Will return null when the element type is not
72 | * in the current stack.
73 | *
74 | * @param class-string $type
75 | */
76 | public function search(string $type): Element|FileElement|PropertyHook|null
77 | {
78 | $reverseElements = array_reverse($this->elements);
79 | foreach ($reverseElements as $element) {
80 | if ($element instanceof $type) {
81 | return $element;
82 | }
83 | }
84 |
85 | return null;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/EnumCase.php:
--------------------------------------------------------------------------------
1 | $reducers */
22 | public function __construct(
23 | DocBlockFactoryInterface $docBlockFactory,
24 | private readonly PrettyPrinter $prettyPrinter,
25 | iterable $reducers = [],
26 | ) {
27 | parent::__construct($docBlockFactory, $reducers);
28 | }
29 |
30 | #[Override]
31 | public function matches(ContextStack $context, object $object): bool
32 | {
33 | return $object instanceof EnumCaseNode;
34 | }
35 |
36 | /** @param EnumCaseNode $object */
37 | #[Override]
38 | protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): object|null
39 | {
40 | $docBlock = $this->createDocBlock($object->getDocComment(), $context->getTypeContext());
41 | $enum = $context->peek();
42 | assert($enum instanceof EnumElement);
43 |
44 | $case = new EnumCaseElement(
45 | $object->getAttribute('fqsen'),
46 | $docBlock,
47 | new Location($object->getLine()),
48 | new Location($object->getEndLine()),
49 | $object->expr !== null ? $this->prettyPrinter->prettyPrintExpr($object->expr) : null,
50 | );
51 |
52 | $enum->addCase($case);
53 |
54 | return $case;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Enum_.php:
--------------------------------------------------------------------------------
1 | createDocBlock($object->getDocComment(), $context->getTypeContext());
38 |
39 | $enum = new \phpDocumentor\Reflection\Php\Enum_(
40 | $object->getAttribute('fqsen'),
41 | (new Type())->fromPhpParser($object->scalarType),
42 | $docBlock,
43 | new Location($object->getLine()),
44 | new Location($object->getEndLine()),
45 | );
46 |
47 | foreach ($object->implements as $interfaceClassName) {
48 | $enum->addInterface(
49 | new Fqsen('\\' . $interfaceClassName->toString()),
50 | );
51 | }
52 |
53 | $file = $context->peek();
54 | assert($file instanceof FileElement);
55 | $file->addEnum($enum);
56 |
57 | foreach ($object->stmts as $stmt) {
58 | $thisContext = $context->push($enum);
59 | $strategy = $strategies->findMatching($thisContext, $stmt);
60 | $strategy->create($thisContext, $stmt, $strategies);
61 | }
62 |
63 | return $enum;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/File/CreateCommand.php:
--------------------------------------------------------------------------------
1 | strategies;
40 | }
41 |
42 | public function getFile(): File
43 | {
44 | return $this->file;
45 | }
46 |
47 | public function getContext(): ContextStack
48 | {
49 | return $this->context;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Function_.php:
--------------------------------------------------------------------------------
1 | peek() instanceof FileElement;
37 | }
38 |
39 | /**
40 | * Creates a FunctionDescriptor out of the given object including its child elements.
41 | *
42 | * @param ContextStack $context of the created object
43 | * @param FunctionNode $object
44 | */
45 | #[Override]
46 | protected function doCreate(
47 | ContextStack $context,
48 | object $object,
49 | StrategyContainer $strategies,
50 | ): object|null {
51 | $file = $context->peek();
52 | Assert::isInstanceOf($file, FileElement::class);
53 |
54 | $function = new FunctionDescriptor(
55 | $object->getAttribute('fqsen'),
56 | $this->createDocBlock($object->getDocComment(), $context->getTypeContext()),
57 | new Location($object->getLine()),
58 | new Location($object->getEndLine()),
59 | (new Type())->fromPhpParser($object->getReturnType()),
60 | $object->byRef ?: false,
61 | );
62 |
63 | $file->addFunction($function);
64 |
65 | $thisContext = $context->push($function);
66 | foreach ($object->stmts as $stmt) {
67 | $strategy = $strategies->findMatching($thisContext, $stmt);
68 | $strategy->create($thisContext, $stmt, $strategies);
69 | }
70 |
71 | return $function;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/GlobalConstant.php:
--------------------------------------------------------------------------------
1 | peek();
66 | Assert::isInstanceOf($file, FileElement::class);
67 |
68 | foreach ($constants as $const) {
69 | $file->addConstant(
70 | new ConstantElement(
71 | $const->getFqsen(),
72 | $this->createDocBlock($const->getDocComment(), $context->getTypeContext()),
73 | $const->getValue() !== null ? $this->valueConverter->prettyPrintExpr($const->getValue()) : null,
74 | new Location($const->getLine()),
75 | new Location($const->getEndLine()),
76 | ),
77 | );
78 | }
79 |
80 | return null;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/GlobalConstantIterator.php:
--------------------------------------------------------------------------------
1 | */
24 | final class GlobalConstantIterator implements Iterator
25 | {
26 | /** @var int index of the current constant to use */
27 | private int $index = 0;
28 |
29 | /**
30 | * Initializes the class with source data.
31 | */
32 | public function __construct(private readonly Const_ $constant)
33 | {
34 | }
35 |
36 | /**
37 | * Gets line the node started in.
38 | *
39 | * @return int Line
40 | */
41 | public function getLine(): int
42 | {
43 | return $this->constant->getLine();
44 | }
45 |
46 | /**
47 | * Gets line the node ended in.
48 | *
49 | * @return int Line
50 | */
51 | public function getEndLine(): int
52 | {
53 | return $this->constant->getEndLine();
54 | }
55 |
56 | /**
57 | * Returns the name of the current constant.
58 | */
59 | public function getName(): string
60 | {
61 | return (string) $this->constant->consts[$this->index]->name;
62 | }
63 |
64 | /**
65 | * Returns the fqsen of the current constant.
66 | */
67 | public function getFqsen(): Fqsen
68 | {
69 | return $this->constant->consts[$this->index]->getAttribute('fqsen');
70 | }
71 |
72 | /**
73 | * Gets the doc comment of the node.
74 | *
75 | * The doc comment has to be the last comment associated with the node.
76 | */
77 | public function getDocComment(): Doc|null
78 | {
79 | $docComment = $this->constant->consts[$this->index]->getDocComment();
80 | if ($docComment === null) {
81 | $docComment = $this->constant->getDocComment();
82 | }
83 |
84 | return $docComment;
85 | }
86 |
87 | public function getValue(): Expr
88 | {
89 | return $this->constant->consts[$this->index]->value;
90 | }
91 |
92 | /** @link http://php.net/manual/en/iterator.current.php */
93 | #[Override]
94 | public function current(): self
95 | {
96 | return $this;
97 | }
98 |
99 | /** @link http://php.net/manual/en/iterator.next.php */
100 | #[Override]
101 | public function next(): void
102 | {
103 | ++$this->index;
104 | }
105 |
106 | /** @link http://php.net/manual/en/iterator.key.php */
107 | #[Override]
108 | public function key(): int|null
109 | {
110 | return $this->index;
111 | }
112 |
113 | /** @link http://php.net/manual/en/iterator.valid.php */
114 | #[Override]
115 | public function valid(): bool
116 | {
117 | return isset($this->constant->consts[$this->index]);
118 | }
119 |
120 | /** @link http://php.net/manual/en/iterator.rewind.php */
121 | #[Override]
122 | public function rewind(): void
123 | {
124 | $this->index = 0;
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/IfStatement.php:
--------------------------------------------------------------------------------
1 | stmts as $stmt) {
26 | $strategies->findMatching($context, $stmt)->create($context, $stmt, $strategies);
27 | }
28 |
29 | foreach ($object->elseifs as $elseIf) {
30 | foreach ($elseIf->stmts as $stmt) {
31 | $strategies->findMatching($context, $stmt)->create($context, $stmt, $strategies);
32 | }
33 | }
34 |
35 | if (!($object->else instanceof Else_)) {
36 | return;
37 | }
38 |
39 | foreach ($object->else->stmts as $stmt) {
40 | $strategies->findMatching($context, $stmt)->create($context, $stmt, $strategies);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Interface_.php:
--------------------------------------------------------------------------------
1 | createDocBlock($object->getDocComment(), $context->getTypeContext());
54 | $parents = [];
55 | foreach ($object->extends as $extend) {
56 | $parents['\\' . (string) $extend] = new Fqsen('\\' . (string) $extend);
57 | }
58 |
59 | $interface = new InterfaceElement(
60 | $object->getAttribute('fqsen'),
61 | $parents,
62 | $docBlock,
63 | new Location($object->getLine()),
64 | new Location($object->getEndLine()),
65 | );
66 | $file = $context->peek();
67 | Assert::isInstanceOf($file, FileElement::class);
68 | $file->addInterface($interface);
69 |
70 | foreach ($object->stmts as $stmt) {
71 | $thisContext = $context->push($interface);
72 | $strategy = $strategies->findMatching($thisContext, $stmt);
73 | $strategy->create($thisContext, $stmt, $strategies);
74 | }
75 |
76 | return $interface;
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Method.php:
--------------------------------------------------------------------------------
1 | peek();
54 | Assert::isInstanceOfAny(
55 | $methodContainer,
56 | [
57 | Class_::class,
58 | Interface_::class,
59 | Trait_::class,
60 | Enum_::class,
61 | ],
62 | );
63 |
64 | $method = new MethodDescriptor(
65 | $object->getAttribute('fqsen'),
66 | $this->buildVisibility($object),
67 | $this->createDocBlock($object->getDocComment(), $context->getTypeContext()),
68 | $object->isAbstract(),
69 | $object->isStatic(),
70 | $object->isFinal(),
71 | new Location($object->getLine(), $object->getStartFilePos()),
72 | new Location($object->getEndLine(), $object->getEndFilePos()),
73 | (new Type())->fromPhpParser($object->getReturnType()),
74 | $object->byRef ?: false,
75 | );
76 | $methodContainer->addMethod($method);
77 |
78 | if (!is_array($object->stmts)) {
79 | return $method;
80 | }
81 |
82 | $thisContext = $context->push($method);
83 | foreach ($object->stmts as $stmt) {
84 | $strategy = $strategies->findMatching($thisContext, $stmt);
85 | $strategy->create($thisContext, $stmt, $strategies);
86 | }
87 |
88 | return $method;
89 | }
90 |
91 | /**
92 | * Converts the visibility of the method to a valid Visibility object.
93 | */
94 | private function buildVisibility(ClassMethod $node): Visibility
95 | {
96 | if ($node->isPrivate()) {
97 | return new Visibility(Visibility::PRIVATE_);
98 | }
99 |
100 | if ($node->isProtected()) {
101 | return new Visibility(Visibility::PROTECTED_);
102 | }
103 |
104 | return new Visibility(Visibility::PUBLIC_);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Namespace_.php:
--------------------------------------------------------------------------------
1 | matches($context, $object)) {
33 | throw new InvalidArgumentException(
34 | sprintf(
35 | '%s cannot handle objects with the type %s',
36 | self::class,
37 | get_debug_type($object),
38 | ),
39 | );
40 | }
41 |
42 | $file = $context->peek();
43 | Assert::isInstanceOf($file, FileElement::class);
44 | $file->addNamespace($object->getAttribute('fqsen') ?? new Fqsen('\\'));
45 | $typeContext = (new NamespaceNodeToContext())($object);
46 | foreach ($object->stmts as $stmt) {
47 | $strategy = $strategies->findMatching($context, $stmt);
48 | $strategy->create($context->withTypeContext($typeContext), $stmt, $strategies);
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Noop.php:
--------------------------------------------------------------------------------
1 | printer = new Standard();
30 | }
31 |
32 | #[Override]
33 | public function reduce(
34 | ContextStack $context,
35 | object $object,
36 | StrategyContainer $strategies,
37 | object|null $carry,
38 | ): object|null {
39 | if ($carry === null) {
40 | return null;
41 | }
42 |
43 | if (property_exists($object, 'attrGroups') === false || isset($object->attrGroups) === false) {
44 | return $carry;
45 | }
46 |
47 | if ($carry instanceof AttributeContainer === false) {
48 | throw new InvalidArgumentException(sprintf('Attribute can not be added on %s', $carry::class));
49 | }
50 |
51 | foreach ($object->attrGroups as $attrGroup) {
52 | assert($attrGroup instanceof AttributeGroup);
53 | foreach ($attrGroup->attrs as $attr) {
54 | $carry->addAttribute(
55 | new \phpDocumentor\Reflection\Php\Attribute(
56 | new Fqsen('\\' . $attr->name->toString()),
57 | array_map($this->buildCallArgument(...), $attr->args),
58 | ),
59 | );
60 | }
61 | }
62 |
63 | return $carry;
64 | }
65 |
66 | private function buildCallArgument(Arg $arg): CallArgument
67 | {
68 | return new CallArgument(
69 | $this->printer->prettyPrintExpr($arg->value),
70 | $arg->name?->toString(),
71 | );
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Reducer/Parameter.php:
--------------------------------------------------------------------------------
1 | getParams() as $param) {
44 | Assert::isInstanceOf($param->var, Variable::class);
45 |
46 | $carry->addArgument(
47 | new ArgumentDescriptor(
48 | is_string($param->var->name) ? $param->var->name : $this->valueConverter->prettyPrintExpr($param->var->name),
49 | (new Type())->fromPhpParser($param->type),
50 | $param->default !== null ? $this->valueConverter->prettyPrintExpr($param->default) : null,
51 | $param->byRef,
52 | $param->variadic,
53 | ),
54 | );
55 | }
56 |
57 | return $carry;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Reducer/Reducer.php:
--------------------------------------------------------------------------------
1 | matches($context, $object) === false) {
33 | throw new InvalidArgumentException('Does not match expected node');
34 | }
35 |
36 | $class = $context->peek();
37 |
38 | if (
39 | $class instanceof Class_ === false
40 | && $class instanceof Trait_ === false
41 | && $class instanceof Enum_ === false
42 | ) {
43 | throw new InvalidArgumentException('Traits can only be used in classes, enums or other traits');
44 | }
45 |
46 | foreach ($object->traits as $trait) {
47 | $class->addUsedTrait(new Fqsen($trait->toCodeString()));
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Trait_.php:
--------------------------------------------------------------------------------
1 | getAttribute('fqsen'),
46 | $this->createDocBlock($object->getDocComment(), $context->getTypeContext()),
47 | new Location($object->getLine()),
48 | new Location($object->getEndLine()),
49 | );
50 |
51 | $file = $context->peek();
52 | Assert::isInstanceOf($file, FileElement::class);
53 | $file->addTrait($trait);
54 |
55 | foreach ($object->stmts as $stmt) {
56 | $thisContext = $context->push($trait);
57 | $strategy = $strategies->findMatching($thisContext, $stmt);
58 | $strategy->create($thisContext, $stmt, $strategies);
59 | }
60 |
61 | return $trait;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Factory/Type.php:
--------------------------------------------------------------------------------
1 | resolve($this->convertPhpParserTypeToString($type), $context);
43 | }
44 |
45 | private function convertPhpParserTypeToString(NodeAbstract|string $type): string
46 | {
47 | if (is_string($type)) {
48 | return $type;
49 | }
50 |
51 | if ($type instanceof Identifier) {
52 | return $type->toString();
53 | }
54 |
55 | if ($type instanceof Name) {
56 | return $type->toString();
57 | }
58 |
59 | if ($type instanceof NullableType) {
60 | return '?' . $this->convertPhpParserTypeToString($type->type);
61 | }
62 |
63 | if ($type instanceof UnionType) {
64 | $typesAsStrings = array_map(
65 | fn ($typeObject): string => $this->convertPhpParserTypeToString($typeObject),
66 | $type->types,
67 | );
68 |
69 | return implode('|', $typesAsStrings);
70 | }
71 |
72 | if ($type instanceof IntersectionType) {
73 | $typesAsStrings = array_map(
74 | fn ($typeObject): string => $this->convertPhpParserTypeToString($typeObject),
75 | $type->types,
76 | );
77 |
78 | return implode('&', $typesAsStrings);
79 | }
80 |
81 | throw new InvalidArgumentException(sprintf('Unsupported complex type %s', $type::class));
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/HasAttributes.php:
--------------------------------------------------------------------------------
1 | attributes[] = $attribute;
15 | }
16 |
17 | /** @return Attribute[] */
18 | public function getAttributes(): array
19 | {
20 | return $this->attributes;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/MetadataContainer.php:
--------------------------------------------------------------------------------
1 | key(), $this->metadata)) {
32 | throw new Exception(sprintf('Metadata with key "%s" already exists', $metadata->key()));
33 | }
34 |
35 | $this->metadata[$metadata->key()] = $metadata;
36 | }
37 |
38 | /** @return Metadata[] */
39 | public function getMetadata(): array
40 | {
41 | return $this->metadata;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/NodesFactory.php:
--------------------------------------------------------------------------------
1 | createForNewestSupportedVersion();
51 | $traverser = new NodeTraverser();
52 | $traverser->addVisitor(new NameResolver());
53 | $traverser->addVisitor(new ElementNameResolver());
54 |
55 | return new static($parser, $traverser);
56 | }
57 |
58 | /**
59 | * Will convert the provided code to nodes.
60 | *
61 | * @param string $code code to process.
62 | *
63 | * @return Node[]
64 | */
65 | public function create(string $code): array
66 | {
67 | $nodes = $this->parser->parse($code);
68 | Assert::isArray($nodes);
69 |
70 | return $this->traverser->traverse($nodes);
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Project.php:
--------------------------------------------------------------------------------
1 | rootNamespace !== null) {
42 | return;
43 | }
44 |
45 | $this->rootNamespace = new Namespace_(new Fqsen('\\'));
46 | }
47 |
48 | /**
49 | * Returns the name of this project.
50 | */
51 | #[Override]
52 | public function getName(): string
53 | {
54 | return $this->name;
55 | }
56 |
57 | /**
58 | * Returns all files with their sub-elements.
59 | *
60 | * @return File[]
61 | */
62 | public function getFiles(): array
63 | {
64 | return $this->files;
65 | }
66 |
67 | /**
68 | * Add a file to this project.
69 | */
70 | public function addFile(File $file): void
71 | {
72 | $this->files[$file->getPath()] = $file;
73 | }
74 |
75 | /**
76 | * Returns all namespaces with their sub-elements.
77 | *
78 | * @return Namespace_[]
79 | */
80 | public function getNamespaces(): array
81 | {
82 | return $this->namespaces;
83 | }
84 |
85 | /**
86 | * Add a namespace to the project.
87 | */
88 | public function addNamespace(Namespace_ $namespace): void
89 | {
90 | $this->namespaces[(string) $namespace->getFqsen()] = $namespace;
91 | }
92 |
93 | /**
94 | * Returns the root (global) namespace.
95 | */
96 | public function getRootNamespace(): Namespace_|null
97 | {
98 | return $this->rootNamespace;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategies.php:
--------------------------------------------------------------------------------
1 | */
29 | private readonly SplPriorityQueue $strategies;
30 |
31 | /**
32 | * Initializes the factory with a number of strategies.
33 | *
34 | * @param ProjectFactoryStrategy[] $strategies
35 | */
36 | public function __construct(array $strategies)
37 | {
38 | $this->strategies = new SplPriorityQueue();
39 | foreach ($strategies as $strategy) {
40 | $this->addStrategy($strategy);
41 | }
42 | }
43 |
44 | /**
45 | * Find the ProjectFactoryStrategy that matches $object.
46 | *
47 | * @throws OutOfBoundsException When no matching strategy was found.
48 | */
49 | #[Override]
50 | public function findMatching(ContextStack $context, mixed $object): ProjectFactoryStrategy
51 | {
52 | foreach (clone $this->strategies as $strategy) {
53 | if ($strategy->matches($context, $object)) {
54 | return $strategy;
55 | }
56 | }
57 |
58 | throw new OutOfBoundsException(
59 | sprintf(
60 | 'No matching factory found for %s',
61 | get_debug_type($object),
62 | ),
63 | );
64 | }
65 |
66 | /**
67 | * Add a strategy to this container.
68 | */
69 | public function addStrategy(ProjectFactoryStrategy $strategy, int $priority = self::DEFAULT_PRIORITY): void
70 | {
71 | $this->strategies->insert($strategy, $priority);
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategy.php:
--------------------------------------------------------------------------------
1 | location = $location ?? new Location(-1);
33 | $this->endLocation = $endLocation ?? new Location(-1);
34 | }
35 |
36 | /**
37 | * Returns true when this hook is final. Otherwise, returns false.
38 | */
39 | public function isFinal(): bool
40 | {
41 | return $this->final;
42 | }
43 |
44 | /**
45 | * Returns the Visibility of this hook.
46 | */
47 | public function getVisibility(): Visibility|null
48 | {
49 | return $this->visibility;
50 | }
51 |
52 | /**
53 | * Returns the arguments of this hook.
54 | *
55 | * @return Argument[]
56 | */
57 | public function getArguments(): array
58 | {
59 | return $this->arguments;
60 | }
61 |
62 | /**
63 | * Add new argument to this hook.
64 | */
65 | public function addArgument(Argument $argument): void
66 | {
67 | $this->arguments[] = $argument;
68 | }
69 |
70 | /**
71 | * Returns the name of this hook.
72 | */
73 | public function getName(): string
74 | {
75 | return $this->name;
76 | }
77 |
78 | /**
79 | * Returns the DocBlock of this method if available.
80 | */
81 | public function getDocBlock(): DocBlock|null
82 | {
83 | return $this->docBlock;
84 | }
85 |
86 | public function getLocation(): Location
87 | {
88 | return $this->location;
89 | }
90 |
91 | public function getEndLocation(): Location
92 | {
93 | return $this->endLocation;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/StrategyContainer.php:
--------------------------------------------------------------------------------
1 | $this->evaluateFallback($expr, $contextStack));
23 |
24 | return $evaluator->evaluateSilently($expr);
25 | // @codeCoverageIgnoreEnd
26 | }
27 |
28 | /** @throws ConstExprEvaluationException */
29 | private function evaluateFallback(Expr $expr, ContextStack $contextStack): string
30 | {
31 | $typeContext = $contextStack->getTypeContext();
32 | if ($typeContext === null) {
33 | throw new ConstExprEvaluationException(
34 | sprintf('Expression of type %s cannot be evaluated', $expr->getType()),
35 | );
36 | }
37 |
38 | if ($expr instanceof Namespace_) {
39 | return $typeContext->getNamespace();
40 | }
41 |
42 | throw new ConstExprEvaluationException(
43 | sprintf('Expression of type %s cannot be evaluated', $expr->getType()),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Php/Visibility.php:
--------------------------------------------------------------------------------
1 | visibility = $visibility;
64 | }
65 |
66 | /**
67 | * Will return a string representation of visibility.
68 | */
69 | #[Override]
70 | public function __toString(): string
71 | {
72 | return $this->visibility;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/phpDocumentor/Reflection/Types/NamespaceNodeToContext.php:
--------------------------------------------------------------------------------
1 | name ? $namespace->name->toString() : '',
28 | $this->aliasesToFullyQualifiedNames($namespace),
29 | );
30 | }
31 |
32 | /** @return string[] indexed by alias */
33 | private function aliasesToFullyQualifiedNames(Namespace_ $namespace): array
34 | {
35 | // flatten(flatten(map(stuff)))
36 | return array_merge([], ...array_merge([], ...array_map(
37 | static fn ($use): array => array_map(
38 | static function (Node\UseItem|UseUse $useUse) use ($use): array {
39 | if ($use instanceof GroupUse) {
40 | return [
41 | (string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(),
42 | ];
43 | }
44 |
45 | return [(string) $useUse->getAlias() => $useUse->name->toString()];
46 | },
47 | $use->uses,
48 | ),
49 | $this->classAlikeUses($namespace),
50 | )));
51 | }
52 |
53 | /** @return Use_[]|GroupUse[] */
54 | private function classAlikeUses(Namespace_ $namespace): array
55 | {
56 | return array_filter(
57 | $namespace->stmts,
58 | static fn (Node $node): bool => (
59 | $node instanceof Use_
60 | || $node instanceof GroupUse
61 | ) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true),
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/bench/ProjectFactoryBench.php:
--------------------------------------------------------------------------------
1 | factory = \phpDocumentor\Reflection\Php\ProjectFactory::createInstance();
21 | }
22 |
23 | /**
24 | * @Revs({1, 8, 64, 1024})
25 | */
26 | public function benchCreateSingleFileProject()
27 | {
28 | $this->factory->create('myProject', [new LocalFile(__DIR__ . '/../assets/phpunit_assert.php')]);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/coverage-checker.php:
--------------------------------------------------------------------------------
1 | xpath('//metrics');
16 | $totalElements = 0;
17 | $checkedElements = 0;
18 |
19 | foreach ($metrics as $metric) {
20 | $totalElements += (int) $metric['elements'];
21 | $checkedElements += (int) $metric['coveredelements'];
22 | }
23 |
24 | $coverage = ($checkedElements / $totalElements) * 100;
25 |
26 | if ($coverage < $percentage) {
27 | echo 'Code coverage is ' . $coverage . '%, which is below the accepted ' . $percentage . '%' . PHP_EOL;
28 | exit(1);
29 | }
30 |
31 | echo 'Code coverage is ' . $coverage . '% - OK!' . PHP_EOL;
32 |
--------------------------------------------------------------------------------
/tests/integration/AsymmetricAccessorTest.php:
--------------------------------------------------------------------------------
1 | = 5.2')]
14 | final class AsymmetricAccessorTest extends TestCase
15 | {
16 | public function testAsymmetricAccessor(): void
17 | {
18 | $file = __DIR__ . '/data/PHP84/AsymmetricAccessor.php';
19 | $projectFactory = ProjectFactory::createInstance();
20 | $project = $projectFactory->create('My project', [new LocalFile($file)]);
21 |
22 | $class = $project->getFiles()[$file]->getClasses()['\AsymmetricAccessor'];
23 |
24 | self::assertEquals(
25 | 'public',
26 | $class->getProperties()['\AsymmetricAccessor::$pizza']->getVisibility()->getReadVisibility(),
27 | );
28 | self::assertEquals(
29 | 'private',
30 | $class->getProperties()['\AsymmetricAccessor::$pizza']->getVisibility()->getWriteVisibility(),
31 | );
32 | }
33 |
34 | public function testAsyncPropertyPromotion(): void
35 | {
36 | $file = __DIR__ . '/data/PHP84/AsymmetricPropertyPromotion.php';
37 | $projectFactory = ProjectFactory::createInstance();
38 | $project = $projectFactory->create('My project', [new LocalFile($file)]);
39 |
40 |
41 | $class = $project->getFiles()[$file]->getClasses()['\AsymmetricPropertyPromotion'];
42 |
43 | self::assertEquals(
44 | 'public',
45 | $class->getProperties()['\AsymmetricPropertyPromotion::$pizza']->getVisibility()->getReadVisibility(),
46 | );
47 | self::assertEquals(
48 | 'protected',
49 | $class->getProperties()['\AsymmetricPropertyPromotion::$pizza']->getVisibility()->getWriteVisibility(),
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/integration/FileDocblockTest.php:
--------------------------------------------------------------------------------
1 | fixture = ProjectFactory::createInstance();
24 | }
25 |
26 | /**
27 | * @dataProvider fileProvider
28 | */
29 | public function testFileDocblock(string $fileName) : void
30 | {
31 | $project = $this->fixture->create(
32 | 'MyProject',
33 | [new LocalFile($fileName)]
34 | );
35 |
36 | $this->assertEquals(
37 | 'This file is part of phpDocumentor.',
38 | $project->getFiles()[$fileName]->getDocBlock()->getSummary()
39 | );
40 | }
41 |
42 | public static function fileProvider() : array
43 | {
44 | return [
45 | [ __DIR__ . '/data/GlobalFiles/empty.php' ],
46 | [ __DIR__ . '/data/GlobalFiles/empty_with_declare.php' ],
47 | [ __DIR__ . '/data/GlobalFiles/empty_shebang.php' ],
48 | [ __DIR__ . '/data/GlobalFiles/psr12.php' ],
49 | [ __DIR__ . '/data/GlobalFiles/docblock_followed_by_html.php' ],
50 | ];
51 | }
52 |
53 | public function testConditionalFunctionDefine() : void
54 | {
55 | $fileName = __DIR__ . '/data/GlobalFiles/conditional_function.php';
56 | $project = $this->fixture->create(
57 | 'MyProject',
58 | [new LocalFile($fileName)]
59 | );
60 |
61 | $this->assertCount(
62 | 4,
63 | $project->getFiles()[$fileName]->getFunctions()
64 | );
65 | }
66 |
67 | public function testGlobalNamespacedFunctionDefine() : void
68 | {
69 | $fileName = __DIR__ . '/data/GlobalFiles/global_namspaced_function.php';
70 | $project = $this->fixture->create(
71 | 'MyProject',
72 | [new LocalFile($fileName)]
73 | );
74 |
75 | $this->assertCount(
76 | 1,
77 | $project->getFiles()[$fileName]->getFunctions()
78 | );
79 | }
80 |
81 | public function testFileWithInlineFunction() : void
82 | {
83 | $fileName = __DIR__ . '/data/GlobalFiles/inline_function.php';
84 | $project = $this->fixture->create(
85 | 'MyProject',
86 | [new LocalFile($fileName)]
87 | );
88 |
89 | $this->assertCount(
90 | 1,
91 | $project->getFiles()[$fileName]->getClasses()
92 | );
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/tests/integration/Metadata/Hook.php:
--------------------------------------------------------------------------------
1 | hook = $hook;
14 | }
15 |
16 | public function key(): string
17 | {
18 | return "project-metadata";
19 | }
20 |
21 | public function hook(): string
22 | {
23 | return $this->hook;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/integration/Metadata/HookStrategy.php:
--------------------------------------------------------------------------------
1 | expr instanceof FuncCall && ((string)$object->expr->name) === 'hook';
22 | }
23 |
24 | public function create(ContextStack $context, object $object, StrategyContainer $strategies): void
25 | {
26 | $method = $context->peek();
27 | $method->addMetadata(new Hook($object->expr->args[0]->value->value));
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/tests/integration/Metadata/example.php:
--------------------------------------------------------------------------------
1 | addStrategy(new HookStrategy());
24 |
25 | /** @var Project $project */
26 | $project = $projectFactory->create('My project', [new LocalFile(self::FILE)]);
27 | $class = $project->getFiles()[self::FILE]->getClasses()['\myHookUsingClass'];
28 |
29 | self::assertArrayHasKey('project-metadata', $class->getMethods()['\myHookUsingClass::test()']->getMetadata());
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/integration/PHP8/MixedTypeTest.php:
--------------------------------------------------------------------------------
1 | fixture = ProjectFactory::createInstance();
25 |
26 | /** @var Project $project */
27 | $project = $this->fixture->create(
28 | 'PHP8',
29 | [
30 | new LocalFile(self::FILE),
31 | ]
32 | );
33 |
34 | $file = $project->getFiles()[self::FILE];
35 |
36 | $class = $file->getClasses()['\PHP8\MixedType'];
37 |
38 | self::assertEquals(new Mixed_(), $class->getMethods()['\PHP8\MixedType::getProperty()']->getReturnType());
39 | self::assertEquals(new Mixed_(), $class->getMethods()['\PHP8\MixedType::setProperty()']->getArguments()[0]->getType());
40 | self::assertEquals(new Mixed_(), $class->getProperties()['\PHP8\MixedType::$property']->getType());
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/integration/PHP8/StaticTypeTest.php:
--------------------------------------------------------------------------------
1 | fixture = ProjectFactory::createInstance();
26 |
27 | /** @var Project $project */
28 | $project = $this->fixture->create(
29 | 'PHP8',
30 | [
31 | new LocalFile(self::FILE),
32 | ]
33 | );
34 |
35 | $file = $project->getFiles()[self::FILE];
36 |
37 | $class = $file->getClasses()['\PHP8\StaticType'];
38 |
39 | self::assertEquals(new Static_(), $class->getMethods()['\PHP8\StaticType::getProperty()']->getReturnType());
40 | self::assertEquals(new Mixed_(), $class->getMethods()['\PHP8\StaticType::setProperty()']->getArguments()[0]->getType());
41 | self::assertEquals(null, $class->getProperties()['\PHP8\StaticType::$property']->getType());
42 | self::assertTrue($class->getProperties()['\PHP8\StaticType::$property']->isStatic());
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/integration/PHP8/UnionTypesTest.php:
--------------------------------------------------------------------------------
1 | fixture = ProjectFactory::createInstance();
33 |
34 | /** @var Project $project */
35 | $project = $this->fixture->create(
36 | 'PHP8',
37 | [
38 | new LocalFile(self::FILE),
39 | ]
40 | );
41 |
42 | $file = $project->getFiles()[self::FILE];
43 |
44 | $class = $file->getClasses()['\PHP8\UnionTypes'];
45 |
46 | self::assertEquals(new Compound([new String_(), new Null_(), new Object_(new Fqsen('\Foo\Date'))]), $class->getMethods()['\PHP8\UnionTypes::union()']->getReturnType());
47 | self::assertEquals(new Compound([new Integer(), new False_()]), $class->getMethods()['\PHP8\UnionTypes::union()']->getArguments()[0]->getType());
48 | self::assertEquals(new Compound([new String_(), new Null_(), new False_()]), $class->getProperties()['\PHP8\UnionTypes::$property']->getType());
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/tests/integration/ProjectNamespaceTest.php:
--------------------------------------------------------------------------------
1 | fixture = $this->fixture = ProjectFactory::createInstance();
35 | }
36 |
37 | public function testWithNamespacedClass() : void
38 | {
39 | $fileName = __DIR__ . '/data/Luigi/Pizza.php';
40 | $project = $this->fixture->create(
41 | 'My Project',
42 | [ new LocalFile($fileName) ]
43 | );
44 |
45 | $this->assertArrayHasKey($fileName, $project->getFiles());
46 | $this->assertArrayHasKey('\\Luigi', $project->getNamespaces());
47 | $this->assertEquals(
48 | ['\\Luigi\\Pizza' => new Fqsen('\\Luigi\\Pizza')],
49 | $project->getNamespaces()['\\Luigi']->getClasses()
50 | );
51 | }
52 |
53 | public function testWithNamespacedConstant() : void
54 | {
55 | $fileName = __DIR__ . '/data/Luigi/constants.php';
56 | $project = $this->fixture->create(
57 | 'My Project',
58 | [ new LocalFile($fileName) ]
59 | );
60 |
61 | $this->assertArrayHasKey($fileName, $project->getFiles());
62 | $this->assertArrayHasKey('\\Luigi', $project->getNamespaces());
63 | $this->assertEquals(
64 | [
65 | '\\Luigi\\OVEN_TEMPERATURE' => new Fqsen('\\Luigi\\OVEN_TEMPERATURE'),
66 | '\\Luigi\\MAX_OVEN_TEMPERATURE' => new Fqsen('\\Luigi\\MAX_OVEN_TEMPERATURE'),
67 | ],
68 | $project->getNamespaces()['\\Luigi']->getConstants()
69 | );
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/integration/data/Enums/EnumConsumer.php:
--------------------------------------------------------------------------------
1 | myEnum = $enum;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/tests/integration/data/Enums/backedEnum.php:
--------------------------------------------------------------------------------
1 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 | ?>
13 | Test
14 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 |
13 | require 'Pizza.php';
14 |
--------------------------------------------------------------------------------
/tests/integration/data/GlobalFiles/empty_shebang.php:
--------------------------------------------------------------------------------
1 | #!/bin/php
2 |
11 | * @license http://www.opensource.org/licenses/mit-license.php MIT
12 | * @link http://phpdoc.org
13 | */
14 |
15 | require 'Pizza.php';
16 |
--------------------------------------------------------------------------------
/tests/integration/data/GlobalFiles/empty_with_declare.php:
--------------------------------------------------------------------------------
1 |
10 | * @license http://www.opensource.org/licenses/mit-license.php MIT
11 | * @link http://phpdoc.org
12 | */
13 |
14 | require 'Pizza.php';
15 |
--------------------------------------------------------------------------------
/tests/integration/data/GlobalFiles/empty_with_html.php:
--------------------------------------------------------------------------------
1 | Test
2 |
10 | * @license http://www.opensource.org/licenses/mit-license.php MIT
11 | * @link http://phpdoc.org
12 | */
13 |
14 | require 'Pizza.php';
15 |
--------------------------------------------------------------------------------
/tests/integration/data/GlobalFiles/global_namspaced_function.php:
--------------------------------------------------------------------------------
1 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 |
13 | declare(strict_types=1);
14 |
15 | require 'Pizza.php';
16 |
--------------------------------------------------------------------------------
/tests/integration/data/Luigi/ExampleNestedTrait.php:
--------------------------------------------------------------------------------
1 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 |
13 | namespace Luigi {
14 |
15 | trait ExampleNestedTrait
16 | {
17 | private function exampleTraitMethod()
18 | {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/integration/data/Luigi/Packing.php:
--------------------------------------------------------------------------------
1 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 |
13 | namespace Luigi;
14 |
15 | interface Packing extends \Packing
16 | {
17 | }
18 |
--------------------------------------------------------------------------------
/tests/integration/data/Luigi/Pizza.php:
--------------------------------------------------------------------------------
1 | style = $style;
52 | }
53 |
54 | /**
55 | * Creates a new instance of a Pizza.
56 | *
57 | * This method can be used to instantiate a new object of this class which can then be retrieved using
58 | * {@see self::getInstance()}.
59 | *
60 | * @param Pizza\Style $style
61 | *
62 | * @see self::getInstance to retrieve the pizza object.
63 | *
64 | * @return void
65 | */
66 | public static function createInstance(Pizza\Style $style)
67 | {
68 | self::$instance = new static($style);
69 | }
70 |
71 | /**
72 | * @return self
73 | */
74 | static function getInstance()
75 | {
76 | return self::$instance;
77 | }
78 |
79 | final public function setSauce(Pizza\Sauce $sauce)
80 | {
81 | $this->sauce = $sauce;
82 | }
83 |
84 | final public function addTopping(Pizza\Topping $topping)
85 | {
86 | $this->toppings[] = $topping;
87 | }
88 |
89 | public function setSize(&$size = \Luigi\Pizza\SIZE_20CM)
90 | {
91 | }
92 |
93 | public function getPrice()
94 | {
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/integration/data/Luigi/StyleFactory.php:
--------------------------------------------------------------------------------
1 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 |
13 | namespace Luigi;
14 |
15 | use Luigi\Pizza\PizzaComponentFactory;
16 |
17 | final class StyleFactory extends PizzaComponentFactory
18 | {
19 | public function getPrice()
20 | {
21 | }
22 |
23 | protected function calculatePrice()
24 | {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/tests/integration/data/Luigi/Valued.php:
--------------------------------------------------------------------------------
1 |
10 | * @license http://www.opensource.org/licenses/mit-license.php MIT
11 | * @link http://phpdoc.org
12 | */
13 |
14 | namespace Luigi;
15 |
16 | /**
17 | * Any class implementing this interface has an associated price.
18 | *
19 | * Using this interface we can easily add the price of all components in a pizza by checking for this interface and
20 | * adding the prices together for all components.
21 | */
22 | interface Valued
23 | {
24 | const BASE_PRICE = 1;
25 |
26 | function getPrice();
27 | }
28 |
--------------------------------------------------------------------------------
/tests/integration/data/Luigi/constants.php:
--------------------------------------------------------------------------------
1 | property;
14 | }
15 |
16 | public function setProperty(mixed $value): void
17 | {
18 | $this->property = $value;
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/integration/data/PHP8/StaticType.php:
--------------------------------------------------------------------------------
1 | property;
15 | }
16 |
17 | public function setProperty($value): void
18 | {
19 | $this->property = $value;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/integration/data/PHP8/UnionTypes.php:
--------------------------------------------------------------------------------
1 | modified) {
16 | return $this->foo . ' (modified)';
17 | }
18 | return $this->foo;
19 | }
20 | /** Not sure this works, but it sets */
21 | #[Setter(new DateTimeImmutable())]
22 | set(string|int $value) {
23 | $this->foo = strtolower($value);
24 | $this->modified = true;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tests/integration/data/PHP84/PropertyHookAsymmetric.php:
--------------------------------------------------------------------------------
1 | modified) {
14 | return $this->foo . ' (modified)';
15 | }
16 | return $this->foo;
17 | }
18 | set(string|int $value) {
19 | $this->foo = strtolower($value);
20 | $this->modified = true;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/tests/integration/data/PHP84/PropertyHookPromotion.php:
--------------------------------------------------------------------------------
1 | modified) {
18 | return $this->foo . ' (modified)';
19 | }
20 | return $this->foo;
21 | }
22 | /** Not sure this works, but it sets */
23 | #[Setter(new DateTimeImmutable())]
24 | set(string|int $value) {
25 | $this->foo = strtolower($value);
26 | $this->modified = true;
27 | }
28 | }
29 | )
30 | {
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tests/integration/data/PHP84/PropertyHookVirtual.php:
--------------------------------------------------------------------------------
1 | fullName
13 | get {
14 | return $this->firstName . ' ' . $this->lastName;
15 | }
16 | }
17 |
18 | /**
19 | * A virtual property that decomposes a full name into first and last name
20 | */
21 | public string $compositeName {
22 | // This is a virtual property with a setter
23 | // It doesn't reference $this->compositeName
24 | set(string $value) {
25 | [$this->firstName, $this->lastName] = explode(' ', $value, 2);
26 | }
27 | }
28 |
29 | /**
30 | * A virtual property with both getter and setter
31 | */
32 | public string $completeFullName {
33 | // Getter doesn't reference $this->completeFullName
34 | get {
35 | return $this->firstName . ' ' . $this->lastName;
36 | }
37 | // Setter doesn't reference $this->completeFullName
38 | set(string $value) {
39 | [$this->firstName, $this->lastName] = explode(' ', $value, 2);
40 | }
41 | }
42 |
43 | /**
44 | * A non-virtual property that references itself in its hook
45 | */
46 | public string $nonVirtualName {
47 | get {
48 | return $this->nonVirtualName ?? $this->firstName;
49 | }
50 | set(string $value) {
51 | $this->nonVirtualName = $value;
52 | }
53 | }
54 |
55 | public function __construct(
56 | private string $firstName = 'John',
57 | private string $lastName = 'Doe'
58 | ) {
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tests/integration/data/Packing.php:
--------------------------------------------------------------------------------
1 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 |
13 | interface Packing
14 | {
15 | public function getName(): string;
16 | }
17 |
--------------------------------------------------------------------------------
/tests/integration/data/Pizza.php:
--------------------------------------------------------------------------------
1 |
9 | * @license http://www.opensource.org/licenses/mit-license.php MIT
10 | * @link http://phpdoc.org
11 | */
12 |
13 | /**
14 | * This needs a docblock to separate from
15 | * file docblock
16 | */
17 | const OVEN_TEMPERATURE = 9001;
18 | define('MAX_OVEN_TEMPERATURE', 9002);
19 |
20 | /**
21 | * Pizza base class
22 | */
23 | class Pizza
24 | {
25 | /**
26 | * The packaging method used to transport the pizza.
27 | */
28 | const PACKAGING = 'box';
29 | }
30 |
--------------------------------------------------------------------------------
/tests/integration/data/simpleFunction.php:
--------------------------------------------------------------------------------
1 | assertStringEqualsFile(__FILE__, $file->getContents());
29 | }
30 |
31 | public function testMd5(): void
32 | {
33 | $file = new LocalFile(__FILE__);
34 | $this->assertEquals(md5_file(__FILE__), $file->md5());
35 | }
36 |
37 | public function testNotExistingFileThrowsException(): void
38 | {
39 | $this->expectException(InvalidArgumentException::class);
40 | new LocalFile('aa');
41 | }
42 |
43 | public function testPath(): void
44 | {
45 | $file = new LocalFile(__FILE__);
46 | $this->assertEquals(__FILE__, $file->path());
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Middleware/ChainFactoryTest.php:
--------------------------------------------------------------------------------
1 | givenAMiddleware('c');
30 | $middleware2 = $this->givenAMiddleware('b');
31 |
32 | $chain = ChainFactory::createExecutionChain(
33 | [$middleware1, $middleware2],
34 | static function (): stdClass {
35 | $result = new stdClass();
36 | $result->counter = 'a';
37 |
38 | return $result;
39 | },
40 | );
41 |
42 | $this->assertInstanceOf(stdClass::class, $chain(new $exampleCommand()));
43 | $this->assertSame('abc', $chain(new $exampleCommand())->counter);
44 | }
45 |
46 | public function testItThrowsAnExceptionIfAnythingOtherThanAMiddlewareIsPassed(): void
47 | {
48 | $this->expectException(InvalidArgumentException::class);
49 | $this->expectExceptionMessage(
50 | 'Middleware must be an instance of phpDocumentor\Reflection\Middleware\Middleware but string was given',
51 | );
52 | $middleware = '1';
53 |
54 | ChainFactory::createExecutionChain(
55 | [$middleware],
56 | static fn (): stdClass => new stdClass(),
57 | );
58 | }
59 |
60 | private function givenAMiddleware(string $exampleValue): Middleware
61 | {
62 | return new class ($exampleValue) implements Middleware {
63 | public function __construct(private readonly string $exampleAddedValue)
64 | {
65 | }
66 |
67 | public function execute(Command $command, callable $next): object
68 | {
69 | $result = $next($command);
70 | $result->counter .= $this->exampleAddedValue;
71 |
72 | return $result;
73 | }
74 | };
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/ArgumentTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(Mixed_::class, $argument->getType());
31 |
32 | $argument = new Argument(
33 | 'myArgument',
34 | new String_(),
35 | 'myDefaultValue',
36 | true,
37 | true,
38 | );
39 | $this->assertEquals(new String_(), $argument->getType());
40 | }
41 |
42 | public function testGetName(): void
43 | {
44 | $argument = new Argument('myArgument', null, 'myDefault', true, true);
45 | $this->assertEquals('myArgument', $argument->getName());
46 | }
47 |
48 | public function testGetDefault(): void
49 | {
50 | $argument = new Argument('myArgument', null, 'myDefaultValue', true, true);
51 | $this->assertEquals('myDefaultValue', $argument->getDefault());
52 |
53 | $argument = new Argument('myArgument', null, null, true, true);
54 | $this->assertNull($argument->getDefault());
55 | }
56 |
57 | public function testGetWhetherArgumentIsPassedByReference(): void
58 | {
59 | $argument = new Argument('myArgument', null, 'myDefaultValue', true, true);
60 | $this->assertTrue($argument->isByReference());
61 |
62 | $argument = new Argument('myArgument', null, null, false, true);
63 | $this->assertFalse($argument->isByReference());
64 | }
65 |
66 | public function testGetWhetherArgumentisVariadic(): void
67 | {
68 | $argument = new Argument('myArgument', null, 'myDefaultValue', true, true);
69 | $this->assertTrue($argument->isVariadic());
70 |
71 | $argument = new Argument('myArgument', null, 'myDefaultValue', true, false);
72 | $this->assertFalse($argument->isVariadic());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/ConstantTest.php:
--------------------------------------------------------------------------------
1 | fqsen = new Fqsen('\MySpace\CONSTANT');
44 | $this->docBlock = new DocBlock('');
45 | $this->fixture = new Constant($this->fqsen, $this->docBlock, $this->value);
46 | }
47 |
48 | private function getFixture(): MetaDataContainerInterface
49 | {
50 | return $this->fixture;
51 | }
52 |
53 | public function testGetValue(): void
54 | {
55 | $this->assertSame($this->value, $this->fixture->getValue());
56 | }
57 |
58 | public function testIsFinal(): void
59 | {
60 | $this->assertFalse($this->fixture->isFinal());
61 | }
62 |
63 | public function testGetFqsen(): void
64 | {
65 | $this->assertSame($this->fqsen, $this->fixture->getFqsen());
66 | $this->assertSame($this->fqsen->getName(), $this->fixture->getName());
67 | }
68 |
69 | public function testGetDocblock(): void
70 | {
71 | $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
72 | }
73 |
74 | public function testGetVisibility(): void
75 | {
76 | $this->assertEquals(new Visibility(Visibility::PUBLIC_), $this->fixture->getVisibility());
77 | }
78 |
79 | public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
80 | {
81 | $fixture = new Constant($this->fqsen, $this->docBlock, null, new Location(100, 20), new Location(101, 20));
82 | $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/EnumCaseTest.php:
--------------------------------------------------------------------------------
1 | fqsen = new Fqsen('\Enum::VALUE');
40 | $this->docBlock = new DocBlock('');
41 |
42 | $this->fixture = new EnumCase($this->fqsen, $this->docBlock);
43 | }
44 |
45 | private function getFixture(): MetaDataContainerInterface
46 | {
47 | return $this->fixture;
48 | }
49 |
50 | public function testGettingName(): void
51 | {
52 | $this->assertSame($this->fqsen->getName(), $this->fixture->getName());
53 | }
54 |
55 | public function testGettingFqsen(): void
56 | {
57 | $this->assertSame($this->fqsen, $this->fixture->getFqsen());
58 | }
59 |
60 | public function testGettingDocBlock(): void
61 | {
62 | $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
63 | }
64 |
65 | public function testGetValue(): void
66 | {
67 | $this->assertNull($this->fixture->getValue());
68 | }
69 |
70 | public function testGetLocationReturnsDefault(): void
71 | {
72 | self::assertEquals(new Location(-1), $this->fixture->getLocation());
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php:
--------------------------------------------------------------------------------
1 | setAttribute('fqsen', new Fqsen((string) $const1->name));
32 | $const2 = new Const_('\Space\MyClass::MY_CONST2', new Variable('2'));
33 | $const2->setAttribute('fqsen', new Fqsen((string) $const2->name));
34 |
35 | $classConstantNode = new ClassConst([$const1, $const2]);
36 |
37 | $i = 1;
38 | foreach (new ClassConstantIterator($classConstantNode) as $constant) {
39 | $this->assertEquals('\Space\MyClass::MY_CONST' . $i, $constant->getName());
40 | $this->assertEquals('\Space\MyClass::MY_CONST' . $i, (string) $constant->getFqsen());
41 | $this->assertEquals($i, $constant->getValue()->name);
42 | ++$i;
43 | }
44 | }
45 |
46 | public function testKey(): void
47 | {
48 | $constantMock = m::mock(ClassConst::class);
49 |
50 | $fixture = new ClassConstantIterator($constantMock);
51 |
52 | $this->assertEquals(0, $fixture->key());
53 | $fixture->next();
54 | $this->assertEquals(1, $fixture->key());
55 | }
56 |
57 | public function testProxyMethods(): void
58 | {
59 | $constantMock = m::mock(ClassConst::class);
60 | $constantMock->shouldReceive('getLine')->once()->andReturn(10);
61 |
62 | $fixture = new ClassConstantIterator($constantMock);
63 |
64 | $this->assertEquals(10, $fixture->getLine());
65 | }
66 |
67 | public function testGetDocCommentPropFirst(): void
68 | {
69 | $const = m::mock(Const_::class);
70 | $classConstants = m::mock(ClassConst::class);
71 | $classConstants->consts = [$const];
72 |
73 | $const->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
74 | $classConstants->shouldReceive('getDocComment')->never();
75 |
76 | $fixture = new ClassConstantIterator($classConstants);
77 |
78 | $this->assertEquals('test', $fixture->getDocComment()->getText());
79 | }
80 |
81 | public function testGetDocComment(): void
82 | {
83 | $const = m::mock(Const_::class);
84 | $classConstants = m::mock(ClassConst::class);
85 | $classConstants->consts = [$const];
86 |
87 | $const->shouldReceive('getDocComment')->once()->andReturnNull();
88 | $classConstants->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
89 |
90 | $fixture = new ClassConstantIterator($classConstants);
91 |
92 | $this->assertEquals('test', $fixture->getDocComment()->getText());
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/ContextStackTest.php:
--------------------------------------------------------------------------------
1 | getProject());
26 | self::assertSame($typeContext, $context->getTypeContext());
27 | }
28 |
29 | public function testPeekThowsWhenEmpty(): void
30 | {
31 | $this->expectException(OutOfBoundsException::class);
32 | $project = new Project('myProject');
33 | $typeContext = new Context('myNamespace');
34 | $context = new ContextStack($project, $typeContext);
35 |
36 | $context->peek();
37 | }
38 |
39 | public function testPeekReturnsTopOfStack(): void
40 | {
41 | $class = new ClassElement(new Fqsen('\MyClass'));
42 |
43 | $project = new Project('myProject');
44 | $typeContext = new Context('myNamespace');
45 | $context = new ContextStack($project, $typeContext);
46 | $context = $context->push($class);
47 |
48 | self::assertSame($class, $context->peek());
49 | self::assertSame($project, $context->getProject());
50 | self::assertSame($typeContext, $context->getTypeContext());
51 | }
52 |
53 | public function testCreateWithTypeContext(): void
54 | {
55 | $class = new ClassElement(new Fqsen('\MyClass'));
56 |
57 | $project = new Project('myProject');
58 | $typeContext = new Context('myNamespace');
59 | $context = new ContextStack($project);
60 | $context = $context->push($class)->withTypeContext($typeContext);
61 |
62 | self::assertSame($class, $context->peek());
63 | self::assertSame($project, $context->getProject());
64 | self::assertSame($typeContext, $context->getTypeContext());
65 | }
66 |
67 | public function testSearchEmptyStackResultsInNull(): void
68 | {
69 | $project = new Project('myProject');
70 | $context = new ContextStack($project);
71 |
72 | self::assertNull($context->search(ClassElement::class));
73 | }
74 |
75 | public function testSearchStackForExistingElementTypeWillReturnTheFirstHit(): void
76 | {
77 | $class = new ClassElement(new Fqsen('\MyClass'));
78 | $project = new Project('myProject');
79 | $context = new ContextStack($project);
80 | $context = $context
81 | ->push(new ClassElement(new Fqsen('\OtherClass')))
82 | ->push($class)
83 | ->push(new Method(new Fqsen('\MyClass::method()')));
84 |
85 | self::assertSame($class, $context->search(ClassElement::class));
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/DummyFactoryStrategy.php:
--------------------------------------------------------------------------------
1 | docblockFactory = $this->prophesize(DocBlockFactoryInterface::class);
41 | $this->fixture = new EnumCase($this->docblockFactory->reveal(), new Standard());
42 | }
43 |
44 | public function testMatches(): void
45 | {
46 | self::assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
47 | self::assertTrue(
48 | $this->fixture->matches(
49 | self::createContext(null),
50 | $this->prophesize(EnumCaseNode::class)->reveal(),
51 | ),
52 | );
53 | }
54 |
55 | public function testSimpleCreate(): void
56 | {
57 | $containerMock = $this->prophesize(StrategyContainer::class)->reveal();
58 | $enumMock = $this->buildEnumCaseMock();
59 | $enumMock->getDocComment()->willReturn(null);
60 |
61 | $result = $this->performCreate($enumMock->reveal());
62 |
63 | self::assertInstanceOf(EnumElement::class, $result);
64 | self::assertEquals(
65 | [
66 | '\Space\MyEnum::VALUE' => new EnumCaseElement(
67 | new Fqsen('\Space\MyEnum::VALUE'),
68 | null,
69 | new Location(1),
70 | new Location(2),
71 | ),
72 | ],
73 | $result->getCases(),
74 | );
75 | }
76 |
77 | private function performCreate(EnumCaseNode $enumCase): EnumElement
78 | {
79 | $factory = new ProjectFactoryStrategies([]);
80 | $enum = new EnumElement(new Fqsen('\myEnum'), null);
81 | $this->fixture->create(self::createContext(null)->push($enum), $enumCase, $factory);
82 |
83 | return $enum;
84 | }
85 |
86 | private function buildEnumCaseMock(): ObjectProphecy
87 | {
88 | $enumMock = $this->prophesize(EnumCaseNode::class);
89 | $enumMock->expr = null;
90 | $enumMock->getAttribute('fqsen')->willReturn(new Fqsen('\Space\MyEnum::VALUE'));
91 | $enumMock->getLine()->willReturn(1);
92 | $enumMock->getEndLine()->willReturn(2);
93 |
94 | return $enumMock;
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/File/CreateCommandTest.php:
--------------------------------------------------------------------------------
1 | file = new LocalFile(__FILE__);
38 | $this->strategies = new ProjectFactoryStrategies([]);
39 | $this->fixture = new CreateCommand(
40 | new ContextStack(new Project('test')),
41 | $this->file,
42 | $this->strategies,
43 | );
44 | }
45 |
46 | public function testGetFile(): void
47 | {
48 | $this->assertSame($this->file, $this->fixture->getFile());
49 | }
50 |
51 | public function testGetStrategies(): void
52 | {
53 | $this->assertSame($this->strategies, $this->fixture->getStrategies());
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/GlobalConstantIteratorTest.php:
--------------------------------------------------------------------------------
1 | setAttribute('fqsen', new Fqsen((string) $const1->name));
31 | $const2 = new Const_('\Space\MY_CONST2', new Variable('b'));
32 | $const2->setAttribute('fqsen', new Fqsen((string) $const2->name));
33 |
34 | $globalConstantNode = new ConstStatement([$const1, $const2]);
35 |
36 | $i = 1;
37 | foreach (new GlobalConstantIterator($globalConstantNode) as $constant) {
38 | $this->assertEquals('\Space\MY_CONST' . $i, $constant->getName());
39 | $this->assertEquals('\Space\MY_CONST' . $i, (string) $constant->getFqsen());
40 |
41 | ++$i;
42 | }
43 | }
44 |
45 | public function testKey(): void
46 | {
47 | $constant = m::mock(ConstStatement::class);
48 |
49 | $fixture = new GlobalConstantIterator($constant);
50 |
51 | $this->assertEquals(0, $fixture->key());
52 | $fixture->next();
53 | $this->assertEquals(1, $fixture->key());
54 | }
55 |
56 | public function testProxyMethods(): void
57 | {
58 | $constant = m::mock(ConstStatement::class);
59 | $constant->shouldReceive('getLine')->once()->andReturn(10);
60 |
61 | $fixture = new GlobalConstantIterator($constant);
62 |
63 | $this->assertEquals(10, $fixture->getLine());
64 | }
65 |
66 | public function testGetDocCommentPropFirst(): void
67 | {
68 | $const = m::mock(Const_::class);
69 | $constants = m::mock(ConstStatement::class);
70 | $constants->consts = [$const];
71 |
72 | $const->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
73 | $constants->shouldReceive('getDocComment')->never();
74 |
75 | $fixture = new GlobalConstantIterator($constants);
76 |
77 | $this->assertEquals('test', $fixture->getDocComment()->getText());
78 | }
79 |
80 | public function testGetDocComment(): void
81 | {
82 | $const = m::mock(Const_::class);
83 | $constants = m::mock(ConstStatement::class);
84 | $constants->consts = [$const];
85 |
86 | $const->shouldReceive('getDocComment')->once()->andReturnNull();
87 | $constants->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
88 |
89 | $fixture = new GlobalConstantIterator($constants);
90 |
91 | $this->assertEquals('test', $fixture->getDocComment()->getText());
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/Namespace_Test.php:
--------------------------------------------------------------------------------
1 | fixture = new Namespace_();
31 | }
32 |
33 | public function testMatches(): void
34 | {
35 | $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
36 | $this->assertTrue($this->fixture->matches(
37 | self::createContext(null),
38 | $this->prophesize(NamespaceNode::class)->reveal(),
39 | ));
40 | }
41 |
42 | public function testCreateThrowsException(): void
43 | {
44 | $this->expectException(InvalidArgumentException::class);
45 | $this->fixture->create(
46 | self::createContext(null),
47 | new stdClass(),
48 | $this->prophesize(StrategyContainer::class)->reveal(),
49 | );
50 | }
51 |
52 | public function testIteratesStatements(): void
53 | {
54 | $class = new ClassNode('\MyClass');
55 | $classElement = new ClassElement(new Fqsen('\MyClass'));
56 | $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
57 | $containerMock = $this->prophesize(StrategyContainer::class);
58 | $namespace = new NamespaceNode(new Name('MyNamespace'));
59 | $namespace->setAttribute('fqsen', new Fqsen('\MyNamespace'));
60 | $namespace->stmts = [$class];
61 |
62 | $strategyMock->create(Argument::type(ContextStack::class), $class, $containerMock)
63 | ->will(function ($args) use ($classElement): void {
64 | $args[0]->peek()->addClass($classElement);
65 | })
66 | ->shouldBeCalled();
67 |
68 | $containerMock->findMatching(
69 | Argument::type(ContextStack::class),
70 | $class,
71 | )->willReturn($strategyMock->reveal());
72 |
73 | $file = new File('hash', 'path');
74 | $this->fixture->create(self::createContext(null)->push($file), $namespace, $containerMock->reveal());
75 | $class = current($file->getClasses());
76 | $fqsen = current($file->getNamespaces());
77 |
78 | $this->assertInstanceOf(ClassElement::class, $class);
79 | $this->assertEquals('\MyClass', (string) $class->getFqsen());
80 | $this->assertEquals(new Fqsen('\MyNamespace'), $fqsen);
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyBuilderTest.php:
--------------------------------------------------------------------------------
1 | createMock(DocBlockFactoryInterface::class);
29 | $valueConverter = $this->createMock(PrettyPrinter\Standard::class);
30 | $strategies = $this->createMock(StrategyContainer::class);
31 | $reducers = [];
32 |
33 | $prop1 = new PropertyProperty('prop1');
34 | $propertyNode = new PropertyNode(1, [$prop1]);
35 | $properties = new PropertyIterator($propertyNode);
36 |
37 | $builder = PropertyBuilder::create($valueConverter, $docBlockFactory, $strategies, $reducers);
38 | $builder->fqsen($fqsen)
39 | ->visibility($properties)
40 | ->docblock($properties->getDocComment())
41 | ->default($properties->getDefault())
42 | ->static(true)
43 | ->startLocation($startLocation)
44 | ->endLocation($endLocation)
45 | ->type($properties->getType())
46 | ->readOnly(true)
47 | ->hooks($properties->getHooks());
48 |
49 | $context = \phpDocumentor\Reflection\Php\Factory\TestCase::createContext();
50 | $property = $builder->build($context);
51 |
52 | $this->assertSame($fqsen, $property->getFqsen());
53 | $this->assertEquals($visibility, $property->getVisibility());
54 | $this->assertNull($property->getDocBlock());
55 | $this->assertNull($property->getDefault());
56 | $this->assertTrue($property->isStatic());
57 | $this->assertSame($startLocation, $property->getLocation());
58 | $this->assertSame($endLocation, $property->getEndLocation());
59 | $this->assertNull($property->getType());
60 | $this->assertTrue($property->isReadOnly());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/TestCase.php:
--------------------------------------------------------------------------------
1 | expectException(InvalidArgumentException::class);
45 | $this->fixture->create(self::createContext(null), new stdClass(), m::mock(StrategyContainer::class));
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/TraitUseTest.php:
--------------------------------------------------------------------------------
1 | fixture = new TraitUse();
37 | }
38 |
39 | public function testMatchesOnlyTraitUseNode(): void
40 | {
41 | self::assertTrue(
42 | $this->fixture->matches(
43 | self::createContext(),
44 | $this->givenTraitUse(),
45 | ),
46 | );
47 | }
48 |
49 | public function testCreateThrowsExceptionWhenStackDoesNotContainClass(): void
50 | {
51 | $this->expectException(InvalidArgumentException::class);
52 |
53 | $context = self::createContext()->push(new Interface_(new Fqsen('\Interface')));
54 | $this->fixture->create($context, $this->givenTraitUse(), new ProjectFactoryStrategies([]));
55 | }
56 |
57 | /** @param Class_Element|Trait_Element $traitConsumer */
58 | #[DataProvider('consumerProvider')]
59 | public function testCreateWillAddUsedTraitToContextTop(Element $traitConsumer): void
60 | {
61 | $context = self::createContext()->push($traitConsumer);
62 | $this->fixture->create($context, $this->givenTraitUse(), new ProjectFactoryStrategies([]));
63 |
64 | self::assertEquals(['\Foo' => new Fqsen('\Foo')], $traitConsumer->getUsedTraits());
65 | }
66 |
67 | private function givenTraitUse(): TraitUseNode
68 | {
69 | return new TraitUseNode([new FullyQualified('Foo')]);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Factory/TypeTest.php:
--------------------------------------------------------------------------------
1 | fromPhpParser(null);
38 |
39 | $this->assertNull($result);
40 | }
41 |
42 | public function testReturnsReflectedType(): void
43 | {
44 | $factory = new Type();
45 | $given = new Name('integer');
46 | $expected = new Integer();
47 |
48 | $result = $factory->fromPhpParser($given);
49 |
50 | $this->assertEquals($expected, $result);
51 | }
52 |
53 | public function testReturnsNullableTypeWhenPassedAPhpParserNullable(): void
54 | {
55 | $factory = new Type();
56 | $given = new NullableType(new Identifier('integer'));
57 | $expected = new Nullable(new Integer());
58 |
59 | $result = $factory->fromPhpParser($given);
60 |
61 | $this->assertEquals($expected, $result);
62 | }
63 |
64 | public function testReturnsUnion(): void
65 | {
66 | $factory = new Type();
67 | $given = new UnionType([new Identifier('integer'), new Identifier('string')]);
68 | $expected = new Compound([new Integer(), new String_()]);
69 |
70 | $result = $factory->fromPhpParser($given);
71 |
72 | $this->assertEquals($expected, $result);
73 | }
74 |
75 | public function testReturnsUnionGivenVariousTypes(): void
76 | {
77 | $factory = new Type();
78 | $given = new UnionType(['integer', new Name('string'), new Identifier('float')]);
79 | $expected = new Compound([new Integer(), new String_(), new Float_()]);
80 |
81 | $result = $factory->fromPhpParser($given);
82 |
83 | $this->assertEquals($expected, $result);
84 | }
85 |
86 | public function testReturnsInterseptionType(): void
87 | {
88 | $factory = new Type();
89 | $given = new IntersectionType(['integer', new Name('string')]);
90 | $expected = new Intersection([new Integer(), new String_()]);
91 |
92 | $result = $factory->fromPhpParser($given);
93 |
94 | self::assertEquals($expected, $result);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Function_Test.php:
--------------------------------------------------------------------------------
1 | fqsen = new Fqsen('\space\MyFunction()');
44 | $this->docBlock = new DocBlock('aa');
45 | $this->fixture = new Function_($this->fqsen, $this->docBlock);
46 | }
47 |
48 | private function getFixture(): MetaDataContainerInterface
49 | {
50 | return $this->fixture;
51 | }
52 |
53 | public function testGetName(): void
54 | {
55 | $this->assertEquals('MyFunction', $this->fixture->getName());
56 | }
57 |
58 | public function testAddAndGetArguments(): void
59 | {
60 | $argument = new Argument('firstArgument');
61 | $this->fixture->addArgument($argument);
62 |
63 | $this->assertEquals([$argument], $this->fixture->getArguments());
64 | }
65 |
66 | public function testGetFqsen(): void
67 | {
68 | $this->assertSame($this->fqsen, $this->fixture->getFqsen());
69 | }
70 |
71 | public function testGetDocblock(): void
72 | {
73 | $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
74 | }
75 |
76 | public function testGetDefaultReturnType(): void
77 | {
78 | $function = new Function_($this->fqsen);
79 | $this->assertEquals(new Mixed_(), $function->getReturnType());
80 | }
81 |
82 | public function testGetReturnTypeFromConstructor(): void
83 | {
84 | $returnType = new String_();
85 | $function = new Function_($this->fqsen, null, null, null, $returnType);
86 |
87 | $this->assertSame($returnType, $function->getReturnType());
88 | }
89 |
90 | public function testGetHasReturnByReference(): void
91 | {
92 | $function = new Function_($this->fqsen);
93 | $this->assertSame(false, $function->getHasReturnByReference());
94 | }
95 |
96 | public function testGetHasReturnByReferenceFromConstructor(): void
97 | {
98 | $function = new Function_($this->fqsen, null, null, null, null, true);
99 | $this->assertSame(true, $function->getHasReturnByReference());
100 | }
101 |
102 | public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
103 | {
104 | $fixture = new Function_($this->fqsen, $this->docBlock, new Location(100, 20), new Location(101, 20));
105 | $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/MetadataContainerTestHelper.php:
--------------------------------------------------------------------------------
1 | getFixture()->addMetadata($stub);
17 |
18 | self::assertSame(['stub' => $stub], $this->getFixture()->getMetadata());
19 | }
20 |
21 | public function testSetMetaDataWithExistingKeyThrows(): void
22 | {
23 | self::expectException(Exception::class);
24 |
25 | $stub = new MetadataStub('stub');
26 |
27 | $this->getFixture()->addMetadata($stub);
28 | $this->getFixture()->addMetadata($stub);
29 | }
30 |
31 | abstract public function getFixture(): MetaDataContainerInterface;
32 | }
33 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/MetadataStub.php:
--------------------------------------------------------------------------------
1 | key;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/Namespace_Test.php:
--------------------------------------------------------------------------------
1 | fqsen = new Fqsen('\MySpace');
44 | $this->docBlock = new DocBlock('');
45 |
46 | $this->fixture = new Namespace_($this->fqsen, $this->docBlock);
47 | }
48 |
49 | private function getFixture(): MetaDataContainerInterface
50 | {
51 | return $this->fixture;
52 | }
53 |
54 | public function testAddAndGetClasses(): void
55 | {
56 | $this->assertEmpty($this->fixture->getClasses());
57 |
58 | $class = new Fqsen('\MySpace\MyClass');
59 | $this->fixture->addClass($class);
60 |
61 | $this->assertEquals(['\MySpace\MyClass' => $class], $this->fixture->getClasses());
62 | }
63 |
64 | public function testAddAndGetConstants(): void
65 | {
66 | $this->assertEmpty($this->fixture->getConstants());
67 |
68 | $constant = new Fqsen('\MySpace::MY_CONSTANT');
69 | $this->fixture->addConstant($constant);
70 |
71 | $this->assertEquals(['\MySpace::MY_CONSTANT' => $constant], $this->fixture->getConstants());
72 | }
73 |
74 | public function testAddAndGetFunctions(): void
75 | {
76 | $this->assertEmpty($this->fixture->getFunctions());
77 |
78 | $function = new Fqsen('\MySpace\MyFunction()');
79 | $this->fixture->addFunction($function);
80 |
81 | $this->assertEquals(['\MySpace\MyFunction()' => $function], $this->fixture->getFunctions());
82 | }
83 |
84 | public function testAddAndGetInterfaces(): void
85 | {
86 | $this->assertEmpty($this->fixture->getInterfaces());
87 |
88 | $interface = new Fqsen('\MySpace\MyInterface');
89 | $this->fixture->addInterface($interface);
90 |
91 | $this->assertEquals(['\MySpace\MyInterface' => $interface], $this->fixture->getInterfaces());
92 | }
93 |
94 | public function testAddAndGetTraits(): void
95 | {
96 | $this->assertEmpty($this->fixture->getTraits());
97 |
98 | $trait = new Fqsen('\MySpace\MyTrait');
99 | $this->fixture->addTrait($trait);
100 |
101 | $this->assertEquals(['\MySpace\MyTrait' => $trait], $this->fixture->getTraits());
102 | }
103 |
104 | public function testGetFqsen(): void
105 | {
106 | $this->assertSame($this->fqsen, $this->fixture->getFqsen());
107 | $this->assertEquals($this->fqsen->getName(), $this->fixture->getName());
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/NodesFactoryTest.php:
--------------------------------------------------------------------------------
1 | assertInstanceOf(NodesFactory::class, $factory);
42 | $this->assertEquals($this->givenTheExpectedDefaultNodesFactory(), $factory);
43 | }
44 |
45 | public function testThatCodeGetsConvertedIntoNodes(): void
46 | {
47 | $parser = $this->prophesize(Parser::class);
48 | $parser->parse('this is my code')->willReturn(['parsed code']);
49 |
50 | $nodeTraverser = $this->prophesize(NodeTraverserInterface::class);
51 | $nodeTraverser->traverse(['parsed code'])->willReturn(['traversed code']);
52 |
53 | $factory = new NodesFactory($parser->reveal(), $nodeTraverser->reveal());
54 |
55 | $result = $factory->create('this is my code');
56 |
57 | $this->assertSame(['traversed code'], $result);
58 | }
59 |
60 | private function givenTheExpectedDefaultNodesFactory(): NodesFactory
61 | {
62 | $parser = (new ParserFactory())->createForNewestSupportedVersion();
63 | $traverser = new NodeTraverser();
64 | $traverser->addVisitor(new NameResolver());
65 | $traverser->addVisitor(new ElementNameResolver());
66 |
67 | return new NodesFactory($parser, $traverser);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/ProjectFactoryStrategiesTest.php:
--------------------------------------------------------------------------------
1 | assertTrue(true);
34 | }
35 |
36 | public function testFindMatching(): void
37 | {
38 | $strategy = new DummyFactoryStrategy();
39 | $container = new ProjectFactoryStrategies([$strategy]);
40 | $actual = $container->findMatching(
41 | new ContextStack(new Project('name'), new Context('global')),
42 | new stdClass(),
43 | );
44 |
45 | $this->assertSame($strategy, $actual);
46 | }
47 |
48 | public function testCreateThrowsExceptionWhenStrategyNotFound(): void
49 | {
50 | $this->expectException(OutOfBoundsException::class);
51 | $container = new ProjectFactoryStrategies([]);
52 | $container->findMatching(
53 | new ContextStack(new Project('name'), new Context('global')),
54 | new stdClass(),
55 | );
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/ProjectTest.php:
--------------------------------------------------------------------------------
1 | fixture = new Project(self::EXAMPLE_NAME);
39 | }
40 |
41 | public function testGetSetName(): void
42 | {
43 | $this->assertEquals(self::EXAMPLE_NAME, $this->fixture->getName());
44 | }
45 |
46 | public function testGetAddFiles(): void
47 | {
48 | $this->assertEmpty($this->fixture->getFiles());
49 |
50 | $include = new File('foo-bar', 'foo/bar');
51 | $this->fixture->addFile($include);
52 |
53 | $this->assertSame(['foo/bar' => $include], $this->fixture->getFiles());
54 | }
55 |
56 | public function testGetRootNamespace(): void
57 | {
58 | $this->assertInstanceOf(Namespace_::class, $this->fixture->getRootNamespace());
59 |
60 | $namespaceDescriptor = new Namespace_(new Fqsen('\MySpace'));
61 | $project = new Project(self::EXAMPLE_NAME, $namespaceDescriptor);
62 |
63 | $this->assertSame($namespaceDescriptor, $project->getRootNamespace());
64 | }
65 |
66 | public function testGetAddNamespace(): void
67 | {
68 | $this->assertEmpty($this->fixture->getNamespaces());
69 |
70 | $namespace = new Namespace_(new Fqsen('\MySpace'));
71 | $this->fixture->addNamespace($namespace);
72 |
73 | $this->assertSame(['\MySpace' => $namespace], $this->fixture->getNamespaces());
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/TestCase.php:
--------------------------------------------------------------------------------
1 | assertSame(-1, $this->fixture->getLocation()->getLineNumber());
35 | $this->assertSame(0, $this->fixture->getLocation()->getColumnNumber());
36 |
37 | $this->assertSame(-1, $this->fixture->getEndLocation()->getLineNumber());
38 | $this->assertSame(0, $this->fixture->getEndLocation()->getColumnNumber());
39 | }
40 |
41 | public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
42 | {
43 | }
44 |
45 | protected function assertLineAndColumnNumberIsReturnedWhenALocationIsProvided(Element|MetaDataContainerInterface $fixture): void
46 | {
47 | $this->assertSame(100, $fixture->getLocation()->getLineNumber());
48 | $this->assertSame(20, $fixture->getLocation()->getColumnNumber());
49 |
50 | $this->assertSame(101, $fixture->getEndLocation()->getLineNumber());
51 | $this->assertSame(20, $fixture->getEndLocation()->getColumnNumber());
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/ValueEvaluator/ConstantEvaluatorTest.php:
--------------------------------------------------------------------------------
1 | expectException(ConstExprEvaluationException::class);
22 |
23 | $evaluator = new ConstantEvaluator();
24 | $evaluator->evaluate(new Namespace_(), new ContextStack(new Project('test')));
25 | }
26 |
27 | public function testEvaluateThrowsOnUnknownExpression(): void
28 | {
29 | $this->expectException(ConstExprEvaluationException::class);
30 |
31 | $evaluator = new ConstantEvaluator();
32 | $result = $evaluator->evaluate(new ShellExec([]), new ContextStack(new Project('test'), new Context('Test')));
33 | }
34 |
35 | public function testEvaluateReturnsNamespaceFromContext(): void
36 | {
37 | $evaluator = new ConstantEvaluator();
38 | $result = $evaluator->evaluate(new Namespace_(), new ContextStack(new Project('test'), new Context('Test')));
39 |
40 | self::assertSame('Test', $result);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/tests/unit/phpDocumentor/Reflection/Php/VisibilityTest.php:
--------------------------------------------------------------------------------
1 | assertEquals($expected, (string) $visibility);
33 | }
34 |
35 | /** @return string[][] */
36 | public static function visibilityProvider(): array
37 | {
38 | return [
39 | ['public', 'public'],
40 | ['protected', 'protected'],
41 | ['private', 'private'],
42 | ['PrIvate', 'private'],
43 | ];
44 | }
45 |
46 | public function testVisibilityChecksInput(): void
47 | {
48 | $this->expectException(InvalidArgumentException::class);
49 | new Visibility('fooBar');
50 | }
51 | }
52 |
--------------------------------------------------------------------------------