diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 00000000..984ad2bf
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,12 @@
+version: 2
+updates:
+ - package-ecosystem: "composer"
+ directory: "/"
+ schedule:
+ interval: "daily"
+ open-pull-requests-limit: 10
+
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml
new file mode 100644
index 00000000..d0c1fb55
--- /dev/null
+++ b/.github/workflows/dependabot-auto-merge.yml
@@ -0,0 +1,24 @@
+name: "Dependabot auto-merge"
+
+on: # yamllint disable-line rule:truthy
+ pull_request_target: null
+
+permissions:
+ contents: "write"
+
+jobs:
+ dependabot:
+ runs-on: "ubuntu-latest"
+ if: "${{ github.actor == 'dependabot[bot]' }}"
+ steps:
+ - name: "Dependabot metadata"
+ id: "metadata"
+ uses: "dependabot/fetch-metadata@v2.4.0"
+ with:
+ github-token: "${{ secrets.GITHUB_TOKEN }}"
+ - name: "Enable auto-merge for Dependabot PRs"
+ if: "${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}"
+ run: "gh pr merge --auto --merge \"$PR_URL\""
+ env:
+ PR_URL: "${{github.event.pull_request.html_url}}"
+ GITHUB_TOKEN: "${{secrets.GITHUB_TOKEN}}"
diff --git a/.github/workflows/documentation.yaml b/.github/workflows/documentation.yaml
new file mode 100644
index 00000000..573e8685
--- /dev/null
+++ b/.github/workflows/documentation.yaml
@@ -0,0 +1,61 @@
+name: "Documentation"
+
+on: # yamllint disable-line rule:truthy
+ push:
+ branches:
+ - "6.x"
+ pull_request: null
+
+jobs:
+ documentation:
+ name: "Documentation"
+ runs-on: "ubuntu-latest"
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v5"
+
+ - name: "Build"
+ uses: "phpDocumentor/phpDocumentor@master"
+
+ - name: "Deploy"
+ if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/6.x' }}"
+ uses: "actions/upload-artifact@v4"
+ with:
+ name: "documentation"
+ path: "build/docs"
+ retention-days: 1
+
+ deploy:
+ name: "Deploy"
+ if: "${{ github.event_name == 'push' && github.ref == 'refs/heads/6.x' }}"
+ runs-on: "ubuntu-latest"
+ needs: "documentation"
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v5"
+ with:
+ repository: "phpDocumentor/docs"
+ token: "${{ secrets.BOT_TOKEN }}"
+ path: "docs"
+
+ - name: "Download"
+ uses: "actions/download-artifact@v6"
+ with:
+ name: "documentation"
+ path: "build/docs"
+
+ - name: "Copy files"
+ run: "rsync -r --delete build/docs/* docs/docs/components/reflection"
+
+ - name: "Commit"
+ uses: "stefanzweifel/git-auto-commit-action@v7"
+ with:
+ repository: "docs"
+ commit_message: "Update reflection documentation"
+
+ - name: "Push"
+ uses: "ad-m/github-push-action@master"
+ with:
+ directory: "docs"
+ github_token: "${{ secrets.BOT_TOKEN }}"
+ repository: "phpDocumentor/docs"
diff --git a/.github/workflows/integrate.yaml b/.github/workflows/integrate.yaml
new file mode 100644
index 00000000..44130606
--- /dev/null
+++ b/.github/workflows/integrate.yaml
@@ -0,0 +1,71 @@
+# https://docs.github.com/en/actions
+
+name: "Integrate"
+
+on: # yamllint disable-line rule:truthy
+ push:
+ branches:
+ - "6.x"
+ pull_request: null
+ # Allow manually triggering the workflow.
+ workflow_dispatch: null
+
+jobs:
+ code-coverage:
+ name: "Code Coverage"
+ uses: "phpDocumentor/.github/.github/workflows/code-coverage.yml@v0.9"
+ with:
+ php-version: "8.2"
+
+ coding-standards:
+ name: "Coding Standards"
+ runs-on: "ubuntu-22.04"
+ steps:
+ - name: "Checkout"
+ uses: "actions/checkout@v5"
+
+ - name: "Install PHP"
+ uses: "shivammathur/setup-php@v2"
+ with:
+ coverage: "none"
+ php-version: "8.2"
+ tools: "cs2pr"
+
+ - name: "Install dependencies with Composer"
+ uses: "ramsey/composer-install@v3"
+ with:
+ dependency-versions: "locked"
+
+ - name: "Run PHP_CodeSniffer"
+ run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr"
+
+ dependency-analysis:
+ name: "Dependency analysis"
+ uses: "phpDocumentor/.github/.github/workflows/dependency-analysis.yml@v0.9"
+ with:
+ php-version: "8.2"
+
+ lint-root:
+ name: "Lint root"
+ uses: "phpDocumentor/.github/.github/workflows/lint.yml@v0.9"
+ with:
+ php-version: "8.2"
+ composer-options: "--no-check-publish --ansi"
+
+ static-analysis:
+ name: "Static analysis"
+ uses: "phpDocumentor/.github/.github/workflows/static-analysis.yml@main"
+ with:
+ php-version: "8.2"
+ php-extensions: "none, ctype, dom, json, mbstring, phar, simplexml, tokenizer, xml, xmlwriter, fileinfo, pcntl, posix"
+
+ unit-tests:
+ name: "Unit test"
+ uses: "phpDocumentor/.github/.github/workflows/continuous-integration.yml@v0.9"
+
+ integration-tests:
+ name: "Integration test"
+ uses: "phpDocumentor/.github/.github/workflows/continuous-integration.yml@v0.9"
+ needs: "unit-tests"
+ with:
+ test-suite: "integration"
diff --git a/.gitignore b/.gitignore
index 3ce5adbb..9e915a18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,21 @@
+# IDE Shizzle; it is recommended to use a global .gitignore for this but since this is an OSS project we want to make
+# it easy to contribute
.idea
-vendor
+/nbproject/private/
+.buildpath
+.project
+.settings
+
+# Build folder and vendor folder are generated code; no need to version this
+build/
+tools/
+temp/
+vendor/
+*.phar
+
+# By default the phpunit.xml.dist is provided; you can override this using a local config file
+phpunit.xml
+.phpunit.result.cache
+.phpunit.cache
+
+.phpdoc/cache
diff --git a/.phive/phars.xml b/.phive/phars.xml
new file mode 100644
index 00000000..64218fe0
--- /dev/null
+++ b/.phive/phars.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/.phpdoc/template/base.html.twig b/.phpdoc/template/base.html.twig
new file mode 100644
index 00000000..194b2192
--- /dev/null
+++ b/.phpdoc/template/base.html.twig
@@ -0,0 +1,15 @@
+{% extends 'layout.html.twig' %}
+
+{% set topMenu = {
+ "menu": [
+ { "name": "About", "url": "https://phpdoc.org/"},
+ { "name": "Components", "url": "https://phpdoc.org/components.html"},
+ { "name": "Documentation", "url": "https://docs.phpdoc.org/"},
+ ],
+ "social": [
+ { "iconClass": "fab fa-mastodon", "url": "https://phpc.social/@phpdoc"},
+ { "iconClass": "fab fa-github", "url": "https://github.com/phpdocumentor/typeresolver"},
+ { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/orgs/phpDocumentor/discussions"}
+ ]
+}
+%}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index eef782c4..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-language: php
-php:
- - 5.3.3
- - 5.3
- - 5.4
- - 5.5
- - 5.6
- - hhvm
- - hhvm-nightly
-
-matrix:
- allow_failures:
- - php: hhvm
- - php: hhvm-nightly
-
-script:
- - vendor/bin/phpunit
-
-before_script:
- - sudo apt-get -qq update > /dev/null
- - phpenv rehash > /dev/null
- - composer selfupdate --quiet
- - composer install --no-interaction --prefer-source --dev
- - vendor/bin/phpunit
- - composer update --no-interaction --prefer-source --dev
-
-notifications:
- irc: "irc.freenode.org#phpdocumentor"
- email:
- - mike.vanriel@naenius.com
- - ashnazg@php.net
- - boen.robot@gmail.com
diff --git a/.yamllint.yaml b/.yamllint.yaml
new file mode 100644
index 00000000..55695cd5
--- /dev/null
+++ b/.yamllint.yaml
@@ -0,0 +1,65 @@
+extends: "default"
+
+ignore: |
+ .build/
+ .notes/
+ vendor/
+rules:
+ braces:
+ max-spaces-inside-empty: 0
+ max-spaces-inside: 1
+ min-spaces-inside-empty: 0
+ min-spaces-inside: 1
+ brackets:
+ max-spaces-inside-empty: 0
+ max-spaces-inside: 0
+ min-spaces-inside-empty: 0
+ min-spaces-inside: 0
+ colons:
+ max-spaces-after: 1
+ max-spaces-before: 0
+ commas:
+ max-spaces-after: 1
+ max-spaces-before: 0
+ min-spaces-after: 1
+ comments:
+ ignore-shebangs: true
+ min-spaces-from-content: 1
+ require-starting-space: true
+ comments-indentation: "enable"
+ document-end:
+ present: false
+ document-start:
+ present: false
+ indentation:
+ check-multi-line-strings: false
+ indent-sequences: true
+ spaces: 2
+ empty-lines:
+ max-end: 0
+ max-start: 0
+ max: 1
+ empty-values:
+ forbid-in-block-mappings: true
+ forbid-in-flow-mappings: true
+ hyphens:
+ max-spaces-after: 2
+ key-duplicates: "enable"
+ key-ordering: "disable"
+ line-length: "disable"
+ new-line-at-end-of-file: "enable"
+ new-lines:
+ type: "unix"
+ octal-values:
+ forbid-implicit-octal: true
+ quoted-strings:
+ quote-type: "double"
+ trailing-spaces: "enable"
+ truthy:
+ allowed-values:
+ - "false"
+ - "true"
+
+yaml-files:
+ - "*.yaml"
+ - "*.yml"
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..792e4040
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2010 Mike van Riel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..272b3d6c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,51 @@
+.PHONY: help
+help: ## Displays this list of targets with descriptions
+ @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}'
+
+.PHONY: code-style
+code-style:
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpcs
+
+.PHONY: fix-code-style
+fix-code-style:
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpcbf
+
+.PHONY: static-code-analysis
+static-code-analysis: #vendor ## Runs a static code analysis with phpstan/phpstan and vimeo/psalm
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.2-cli vendor/bin/phpstan --configuration=phpstan.neon
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.2-cli vendor/bin/psalm.phar --show-info=true --threads=4
+
+.PHONY: test
+test: test-unit test-functional ## Runs all test suites with phpunit/phpunit
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit
+
+.PHONY: test-unit
+test-unit: ## Runs unit tests with phpunit/phpunit
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit --testsuite=unit
+
+.PHONY: test-functional
+test-functional: ## Runs unit tests with phpunit/phpunit
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit --testsuite=integration
+
+.PHONY: dependency-analysis
+dependency-analysis: vendor ## Runs a dependency analysis with maglnet/composer-require-checker
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli .phive/composer-require-checker check --config-file=/opt/project/composer-require-checker.json
+
+vendor: composer.json composer.lock
+ composer validate --no-check-publish
+ composer install --no-interaction --no-progress
+
+.PHONY: benchmark
+benchmark:
+ docker run -it --rm -v${CURDIR}:/opt/project -w /opt/project php:8.1-cli tools/phpbench run
+
+.PHONY: rector
+rector: ## Refactor code using rector
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/rector process
+
+.PHONY: pre-commit-test
+pre-commit-test: fix-code-style test code-style static-code-analysis
+
+.PHONY: docs
+docs: ## Generate documentation
+ docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpdoc:3-unstable
diff --git a/README.md b/README.md
index 4e4e345a..ef4113d3 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,73 @@
-Reflection [](http://travis-ci.org/phpDocumentor/Reflection)
+[](https://opensource.org/licenses/MIT)
+
+[](https://coveralls.io/github/phpDocumentor/Reflection?branch=master)
+[](https://scrutinizer-ci.com/g/phpDocumentor/Reflection/?branch=master)
+[](https://scrutinizer-ci.com/g/phpDocumentor/Reflection/?branch=master)
+[](https://packagist.org/packages/phpDocumentor/Reflection)
+[](https://packagist.org/packages/phpDocumentor/Reflection)
+
+
+Reflection
==========
-Reflection library to do Static Analysis for PHP Projects
+Using this library it is possible to statically reflect one or more files and create an object graph representing
+your application's structure, including accompanying in-source documentation using DocBlocks.
+
+The information that this library provides is similar to what the (built-in) Reflection extension of PHP provides; there
+are however several advantages to using this library:
+
+- Due to its Static nature it does not execute procedural code in your reflected files where Dynamic Reflection does.
+- Because the none of the code is interpreted by PHP (and executed) Static Reflection uses less memory.
+- Can reflect complete files
+- Can reflect a whole project by reflecting multiple files.
+- Reflects the contents of a DocBlock instead of just mentioning there is one.
+- Is capable of analyzing code written for any PHP version (starting at 5.2) up to and including your installed
+ PHP version.
+
+## Features
+
+* [Creates an object graph] containing the structure of your application much like a site map shows the
+ structure of a website.
+* Can read and interpret code of any PHP version starting with 5.2 up to and including your currently installed version
+ of PHP.
+* Due it's clean interface it can be in any application without a complex setup.
+
+## Installation
+
+In order to inspect a codebase you need to tell composer to include the `phpdocumentor/reflection` package. This
+can easily be done using the following command in your command line terminal:
+
+ composer require phpdocumentor/reflection:~6.0
+
+After the installation is complete no further configuration is necessary and you can immediately start using it.
+
+## Basic Usage
+
+This Reflection library uses [PSR-4] and it is recommended to use a PSR-4 compatible autoloader to load all the
+files containing the classes for this library.
+
+An easy way to do this is by including the [composer] autoloader as shown here:
+
+ include 'vendor/autoload.php';
+
+Once that is done you can use the `createInstance()` method of the `\phpDocumentor\Reflection\Php\ProjectFactory` class to instantiate a new project factory and
+pre-configure it with sensible defaults. Optional you can specify the parser version that shall be used as an argument of `createInstance()`.
+By default the php7 parser is prefered. And php5 is used as a fallback. See the [documentation of phpparser] for more info.
+
+ $projectFactory = \phpDocumentor\Reflection\Php\ProjectFactory::createInstance();
+
+At this point we are ready to analyze your complete project or just one file at the time. Just pass an array of file paths to the `create` method of the project factory.
+
+ $projectFiles = [new \phpDocumentor\Reflection\File\LocalFile('tests/example.file.php')];
+ $project = $projectFactory->create('My Project', $projectFiles);
+
+When the process is ready a new object of type `phpDocumentor\Reflection\Php\Project` will be returned that
+contains a complete hierarchy of all files with their classes, traits and interfaces (and everything in there), but also
+all namespaces and packages as a hierarchical tree.
+
+> See the [example] script for a detailed and commented example
+
+[PSR-4]: http://php-fig.com
+[example]: example.php
+[composer]: http://getcomposer.org
+[documentation of phpparser]: https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-2.0.md#creating-a-parser-instance
diff --git a/composer-require-checker.json b/composer-require-checker.json
new file mode 100644
index 00000000..a4883184
--- /dev/null
+++ b/composer-require-checker.json
@@ -0,0 +1,16 @@
+{
+ "symbol-whitelist" : [
+ "null", "true", "false",
+ "static", "self", "parent",
+ "array", "string", "int", "float", "bool", "iterable", "callable", "void", "object",
+ "PhpParser\\Node\\Stmt\\PropertyProperty"
+ ],
+ "php-core-extensions" : [
+ "Core",
+ "pcre",
+ "Reflection",
+ "tokenizer",
+ "SPL",
+ "standard"
+ ]
+}
diff --git a/composer.json b/composer.json
index 3221ee85..5240c509 100644
--- a/composer.json
+++ b/composer.json
@@ -5,27 +5,66 @@
"homepage": "http://www.phpdoc.org",
"license": "MIT",
"autoload": {
- "psr-0": {
- "phpDocumentor": ["src/", "tests/unit/", "tests/mocks/"]
+ "files": [
+ "src/php-parser/Modifiers.php"
+ ],
+ "psr-4": {
+ "phpDocumentor\\": "src/phpDocumentor"
}
},
- "require": {
- "php": ">=5.3.3",
- "psr/log": "~1.0",
- "nikic/php-parser": "^1.0",
- "phpdocumentor/reflection-docblock": "~2.0"
+ "autoload-dev": {
+ "psr-4": {
+ "phpDocumentor\\": [
+ "tests/unit/phpDocumentor",
+ "tests/bench/"
+ ],
+ "phpDocumentor\\Reflection\\": [
+ "tests/integration"
+ ]
+ }
},
- "suggests": {
- "symfony/event-dispatcher": "~2.1"
+ "require": {
+ "php": "8.1.*|8.2.*|8.3.*|8.4.*",
+ "composer-runtime-api": "^2",
+ "nikic/php-parser": "~4.18 || ^5.0",
+ "phpdocumentor/reflection-common": "^2.1",
+ "phpdocumentor/reflection-docblock": "^5",
+ "phpdocumentor/type-resolver": "^1.2",
+ "symfony/polyfill-php80": "^1.28",
+ "webmozart/assert": "^1.7"
},
"require-dev": {
- "behat/behat": "~2.4",
- "phpunit/phpunit": "~4.0",
- "mockery/mockery": "~0.8"
+ "dealerdirect/phpcodesniffer-composer-installer": "^1.0",
+ "doctrine/coding-standard": "^13.0",
+ "eliashaeussler/phpunit-attributes": "^1.7",
+ "mikey179/vfsstream": "~1.2",
+ "mockery/mockery": "~1.6.0",
+ "phpspec/prophecy-phpunit": "^2.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^10.0",
+ "psalm/phar": "^6.0",
+ "rector/rector": "^1.0.0",
+ "squizlabs/php_codesniffer": "^3.8"
+ },
+ "config": {
+ "preferred-install": {
+ "*": "dist"
+ },
+ "sort-packages": true,
+ "platform": {
+ "php": "8.1.0"
+ },
+ "allow-plugins": {
+ "phpstan/extension-installer": true,
+ "dealerdirect/phpcodesniffer-composer-installer": true
+ }
},
"extra": {
"branch-alias": {
- "dev-master": "3.0.x-dev"
+ "dev-5.x": "5.3.x-dev",
+ "dev-6.x": "6.0.x-dev"
}
}
}
diff --git a/composer.lock b/composer.lock
index ba5b1620..ff197a7c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1,40 +1,96 @@
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "hash": "a95f5fcfa7881ef2a6d076533216cc21",
- "content-hash": "5f18bb38fd8e84b5f811b568bc546c0c",
+ "content-hash": "febb226458a175a8889e92fc89e662ce",
"packages": [
+ {
+ "name": "doctrine/deprecations",
+ "version": "1.1.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/deprecations.git",
+ "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
+ "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<=7.5 || >=13"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^9 || ^12 || ^13",
+ "phpstan/phpstan": "1.4.10 || 2.1.11",
+ "phpstan/phpstan-phpunit": "^1.0 || ^2",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12",
+ "psr/log": "^1 || ^2 || ^3"
+ },
+ "suggest": {
+ "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Doctrine\\Deprecations\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+ "homepage": "https://www.doctrine-project.org/",
+ "support": {
+ "issues": "https://github.com/doctrine/deprecations/issues",
+ "source": "https://github.com/doctrine/deprecations/tree/1.1.5"
+ },
+ "time": "2025-04-07T20:06:18+00:00"
+ },
{
"name": "nikic/php-parser",
- "version": "v1.4.1",
+ "version": "v5.6.2",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51"
+ "reference": "3a454ca033b9e06b63282ce19562e892747449bb"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51",
- "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
+ "reference": "3a454ca033b9e06b63282ce19562e892747449bb",
"shasum": ""
},
"require": {
+ "ext-ctype": "*",
+ "ext-json": "*",
"ext-tokenizer": "*",
- "php": ">=5.3"
+ "php": ">=7.4"
+ },
+ "require-dev": {
+ "ircmaxell/php-yacc": "^0.0.7",
+ "phpunit/phpunit": "^9.0"
},
+ "bin": [
+ "bin/php-parse"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.4-dev"
+ "dev-master": "5.x-dev"
}
},
"autoload": {
- "files": [
- "lib/bootstrap.php"
- ]
+ "psr-4": {
+ "PhpParser\\": "lib/PhpParser"
+ }
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -50,41 +106,219 @@
"parser",
"php"
],
- "time": "2015-09-19 14:15:08"
+ "support": {
+ "issues": "https://github.com/nikic/PHP-Parser/issues",
+ "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
+ },
+ "time": "2025-10-21T19:32:17+00:00"
+ },
+ {
+ "name": "phpdocumentor/reflection-common",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-2.x": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+ "homepage": "http://www.phpdoc.org",
+ "keywords": [
+ "FQSEN",
+ "phpDocumentor",
+ "phpdoc",
+ "reflection",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x"
+ },
+ "time": "2020-06-27T09:03:43+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
- "version": "2.0.3",
+ "version": "5.6.3",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
- "reference": "38743b677965c48a637097b2746a281264ae2347"
+ "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/38743b677965c48a637097b2746a281264ae2347",
- "reference": "38743b677965c48a637097b2746a281264ae2347",
+ "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9",
+ "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "doctrine/deprecations": "^1.1",
+ "ext-filter": "*",
+ "php": "^7.4 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.2",
+ "phpdocumentor/type-resolver": "^1.7",
+ "phpstan/phpdoc-parser": "^1.7|^2.0",
+ "webmozart/assert": "^1.9.1"
},
"require-dev": {
- "phpunit/phpunit": "3.7.*@stable"
+ "mockery/mockery": "~1.3.5 || ~1.6.0",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-mockery": "^1.1",
+ "phpstan/phpstan-webmozart-assert": "^1.2",
+ "phpunit/phpunit": "^9.5",
+ "psalm/phar": "^5.26"
},
- "suggest": {
- "dflydev/markdown": "1.0.*",
- "erusev/parsedown": "~0.7"
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ },
+ {
+ "name": "Jaap van Otterdijk",
+ "email": "opensource@ijaap.nl"
+ }
+ ],
+ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
+ "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3"
+ },
+ "time": "2025-08-01T19:43:32+00:00"
+ },
+ {
+ "name": "phpdocumentor/type-resolver",
+ "version": "1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpDocumentor/TypeResolver.git",
+ "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a",
+ "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/deprecations": "^1.0",
+ "php": "^7.3 || ^8.0",
+ "phpdocumentor/reflection-common": "^2.0",
+ "phpstan/phpdoc-parser": "^1.18|^2.0"
+ },
+ "require-dev": {
+ "ext-tokenizer": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/extension-installer": "^1.1",
+ "phpstan/phpstan": "^1.8",
+ "phpstan/phpstan-phpunit": "^1.1",
+ "phpunit/phpunit": "^9.5",
+ "rector/rector": "^0.13.9",
+ "vimeo/psalm": "^4.25"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-1.x": "1.x-dev"
}
},
"autoload": {
- "psr-0": {
- "phpDocumentor": [
+ "psr-4": {
+ "phpDocumentor\\Reflection\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Mike van Riel",
+ "email": "me@mikevanriel.com"
+ }
+ ],
+ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+ "support": {
+ "issues": "https://github.com/phpDocumentor/TypeResolver/issues",
+ "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0"
+ },
+ "time": "2024-11-09T15:12:26+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
+ "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^5.3.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/extension-installer": "^1.0",
+ "phpstan/phpstan": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpstan/phpstan-strict-rules": "^2.0",
+ "phpunit/phpunit": "^9.6",
+ "symfony/process": "^5.2"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\PhpDocParser\\": [
"src/"
]
}
@@ -93,32 +327,131 @@
"license": [
"MIT"
],
+ "description": "PHPDoc parser with support for nullable, intersection and generic types",
+ "support": {
+ "issues": "https://github.com/phpstan/phpdoc-parser/issues",
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0"
+ },
+ "time": "2025-07-13T07:04:09+00:00"
+ },
+ {
+ "name": "symfony/polyfill-php80",
+ "version": "v1.33.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-php80.git",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.2"
+ },
+ "type": "library",
+ "extra": {
+ "thanks": {
+ "url": "https://github.com/symfony/polyfill",
+ "name": "symfony/polyfill"
+ }
+ },
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ],
+ "psr-4": {
+ "Symfony\\Polyfill\\Php80\\": ""
+ },
+ "classmap": [
+ "Resources/stubs"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
"authors": [
{
- "name": "Mike van Riel",
- "email": "mike.vanriel@naenius.com"
+ "name": "Ion Bazan",
+ "email": "ion.bazan@gmail.com"
+ },
+ {
+ "name": "Nicolas Grekas",
+ "email": "p@tchwork.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "polyfill",
+ "portable",
+ "shim"
+ ],
+ "support": {
+ "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0"
+ },
+ "funding": [
+ {
+ "url": "https://symfony.com/sponsor",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/fabpot",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/nicolas-grekas",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+ "type": "tidelift"
}
],
- "time": "2014-08-09 10:27:07"
+ "time": "2025-01-02T08:10:11+00:00"
},
{
- "name": "psr/log",
- "version": "1.0.0",
+ "name": "webmozart/assert",
+ "version": "1.12.1",
"source": {
"type": "git",
- "url": "https://github.com/php-fig/log.git",
- "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b"
+ "url": "https://github.com/webmozarts/assert.git",
+ "reference": "9be6926d8b485f55b9229203f962b51ed377ba68"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b",
- "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b",
+ "url": "https://api.github.com/repos/webmozarts/assert/zipball/9be6926d8b485f55b9229203f962b51ed377ba68",
+ "reference": "9be6926d8b485f55b9229203f962b51ed377ba68",
"shasum": ""
},
+ "require": {
+ "ext-ctype": "*",
+ "ext-date": "*",
+ "ext-filter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "suggest": {
+ "ext-intl": "",
+ "ext-simplexml": "",
+ "ext-spl": ""
+ },
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.10-dev"
+ }
+ },
"autoload": {
- "psr-0": {
- "Psr\\Log\\": ""
+ "psr-4": {
+ "Webmozart\\Assert\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -127,176 +460,1119 @@
],
"authors": [
{
- "name": "PHP-FIG",
- "homepage": "http://www.php-fig.org/"
+ "name": "Bernhard Schussek",
+ "email": "bschussek@gmail.com"
}
],
- "description": "Common interface for logging libraries",
+ "description": "Assertions to validate method input/output with nice error messages.",
"keywords": [
- "log",
- "psr",
- "psr-3"
+ "assert",
+ "check",
+ "validate"
],
- "time": "2012-12-21 11:40:51"
+ "support": {
+ "issues": "https://github.com/webmozarts/assert/issues",
+ "source": "https://github.com/webmozarts/assert/tree/1.12.1"
+ },
+ "time": "2025-10-29T15:56:20+00:00"
}
],
"packages-dev": [
{
- "name": "behat/behat",
- "version": "v2.5.3",
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.1.2",
"source": {
"type": "git",
- "url": "https://github.com/Behat/Behat.git",
- "reference": "c3a105a3c0457df919879c72b63b910e63739e51"
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Behat/Behat/zipball/c3a105a3c0457df919879c72b63b910e63739e51",
- "reference": "c3a105a3c0457df919879c72b63b910e63739e51",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1",
+ "reference": "e9cf5e4bbf7eeaf9ef5db34938942602838fc2b1",
"shasum": ""
},
"require": {
- "behat/gherkin": "~2.3.0",
- "php": ">=5.3.1",
- "symfony/config": "~2.0",
- "symfony/console": "~2.0",
- "symfony/dependency-injection": "~2.0",
- "symfony/event-dispatcher": "~2.0",
- "symfony/finder": "~2.0",
- "symfony/translation": "~2.0",
- "symfony/yaml": "~2.0"
+ "composer-plugin-api": "^2.2",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
},
"require-dev": {
- "phpunit/phpunit": "~3.7.19"
+ "composer/composer": "^2.2",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "yoast/phpunit-polyfills": "^1.0"
},
- "suggest": {
- "behat/mink-extension": "for integration with Mink testing framework",
- "behat/symfony2-extension": "for integration with Symfony2 web framework",
- "behat/yii-extension": "for integration with Yii web framework"
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
},
- "bin": [
- "bin/behat"
+ "autoload": {
+ "psr-4": {
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "opensource@frenck.dev",
+ "homepage": "https://frenck.dev",
+ "role": "Open source developer"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcbf",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
+ "security": "https://github.com/PHPCSStandards/composer-installer/security/policy",
+ "source": "https://github.com/PHPCSStandards/composer-installer"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-07-17T20:45:56+00:00"
+ },
+ {
+ "name": "doctrine/coding-standard",
+ "version": "13.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/coding-standard.git",
+ "reference": "0affd62169186f32de725ca612e6129e81186a21"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/coding-standard/zipball/0affd62169186f32de725ca612e6129e81186a21",
+ "reference": "0affd62169186f32de725ca612e6129e81186a21",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0.0",
+ "php": "^7.4 || ^8.0",
+ "slevomat/coding-standard": "^8.16",
+ "squizlabs/php_codesniffer": "^3.7"
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Benjamin Eberlei",
+ "email": "kontakt@beberlei.de"
+ },
+ {
+ "name": "Steve Müller",
+ "email": "st.mueller@dzh-online.de"
+ }
],
+ "description": "The Doctrine Coding Standard is a set of PHPCS rules applied to all Doctrine projects.",
+ "homepage": "https://www.doctrine-project.org/projects/coding-standard.html",
+ "keywords": [
+ "checks",
+ "code",
+ "coding",
+ "cs",
+ "dev",
+ "doctrine",
+ "rules",
+ "sniffer",
+ "sniffs",
+ "standard",
+ "style"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/coding-standard/issues",
+ "source": "https://github.com/doctrine/coding-standard/tree/13.0.1"
+ },
+ "time": "2025-05-14T10:54:19+00:00"
+ },
+ {
+ "name": "doctrine/instantiator",
+ "version": "2.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/doctrine/instantiator.git",
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/doctrine/instantiator/zipball/c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "reference": "c6222283fa3f4ac679f8b9ced9a4e23f163e80d0",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^8.1"
+ },
+ "require-dev": {
+ "doctrine/coding-standard": "^11",
+ "ext-pdo": "*",
+ "ext-phar": "*",
+ "phpbench/phpbench": "^1.2",
+ "phpstan/phpstan": "^1.9.4",
+ "phpstan/phpstan-phpunit": "^1.3",
+ "phpunit/phpunit": "^9.5.27",
+ "vimeo/psalm": "^5.4"
+ },
"type": "library",
"autoload": {
- "psr-0": {
- "Behat\\Behat": "src/"
+ "psr-4": {
+ "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "authors": [
+ "authors": [
+ {
+ "name": "Marco Pivetta",
+ "email": "ocramius@gmail.com",
+ "homepage": "https://ocramius.github.io/"
+ }
+ ],
+ "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+ "homepage": "https://www.doctrine-project.org/projects/instantiator.html",
+ "keywords": [
+ "constructor",
+ "instantiate"
+ ],
+ "support": {
+ "issues": "https://github.com/doctrine/instantiator/issues",
+ "source": "https://github.com/doctrine/instantiator/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://www.doctrine-project.org/sponsorship.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://www.patreon.com/phpdoctrine",
+ "type": "patreon"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2022-12-30T00:23:10+00:00"
+ },
+ {
+ "name": "eliashaeussler/phpunit-attributes",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/eliashaeussler/phpunit-attributes.git",
+ "reference": "4c0832e0c7577500bd00ce570b23812abbdf95c2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/eliashaeussler/phpunit-attributes/zipball/4c0832e0c7577500bd00ce570b23812abbdf95c2",
+ "reference": "4c0832e0c7577500bd00ce570b23812abbdf95c2",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0",
+ "phpunit/phpunit": "^10.5.35 || ^11.4 || ^12.0"
+ },
+ "require-dev": {
+ "armin/editorconfig-cli": "^1.8 || ^2.0",
+ "eliashaeussler/php-cs-fixer-config": "^2.0",
+ "eliashaeussler/phpstan-config": "^2.5.1",
+ "eliashaeussler/rector-config": "^3.0",
+ "ergebnis/composer-normalize": "^2.30",
+ "phpstan/extension-installer": "^1.2",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpcov": "^9.0 || ^10.0 || ^11.0",
+ "symfony/console": "^6.4 || ^7.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "EliasHaeussler\\PHPUnitAttributes\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "GPL-3.0-or-later"
+ ],
+ "authors": [
+ {
+ "name": "Elias Häußler",
+ "email": "elias@haeussler.dev",
+ "homepage": "https://haeussler.dev",
+ "role": "Maintainer"
+ }
+ ],
+ "description": "Provides additional attributes for tests with PHPUnit",
+ "support": {
+ "issues": "https://github.com/eliashaeussler/phpunit-attributes/issues",
+ "source": "https://github.com/eliashaeussler/phpunit-attributes/tree/1.7.0"
+ },
+ "time": "2025-03-07T22:01:25+00:00"
+ },
+ {
+ "name": "hamcrest/hamcrest-php",
+ "version": "v2.0.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/hamcrest/hamcrest-php.git",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^5.3|^7.0|^8.0"
+ },
+ "replace": {
+ "cordoval/hamcrest-php": "*",
+ "davedevelopment/hamcrest-php": "*",
+ "kodova/hamcrest-php": "*"
+ },
+ "require-dev": {
+ "phpunit/php-file-iterator": "^1.4 || ^2.0",
+ "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.1-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "hamcrest"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "description": "This is the PHP port of Hamcrest Matchers",
+ "keywords": [
+ "test"
+ ],
+ "support": {
+ "issues": "https://github.com/hamcrest/hamcrest-php/issues",
+ "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1"
+ },
+ "time": "2020-07-09T08:09:16+00:00"
+ },
+ {
+ "name": "mikey179/vfsstream",
+ "version": "v1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/bovigo/vfsStream.git",
+ "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/bovigo/vfsStream/zipball/fe695ec993e0a55c3abdda10a9364eb31c6f1bf0",
+ "reference": "fe695ec993e0a55c3abdda10a9364eb31c6f1bf0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=7.1.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^7.5||^8.5||^9.6",
+ "yoast/phpunit-polyfills": "^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.6.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "org\\bovigo\\vfs\\": "src/main/php"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Frank Kleine",
+ "homepage": "http://frankkleine.de/",
+ "role": "Developer"
+ }
+ ],
+ "description": "Virtual file system to mock the real file system in unit tests.",
+ "homepage": "http://vfs.bovigo.org/",
+ "support": {
+ "issues": "https://github.com/bovigo/vfsStream/issues",
+ "source": "https://github.com/bovigo/vfsStream/tree/master",
+ "wiki": "https://github.com/bovigo/vfsStream/wiki"
+ },
+ "time": "2024-08-29T18:43:31+00:00"
+ },
+ {
+ "name": "mockery/mockery",
+ "version": "1.6.12",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/mockery/mockery.git",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699",
+ "shasum": ""
+ },
+ "require": {
+ "hamcrest/hamcrest-php": "^2.0.1",
+ "lib-pcre": ">=7.0",
+ "php": ">=7.3"
+ },
+ "conflict": {
+ "phpunit/phpunit": "<8.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^8.5 || ^9.6.17",
+ "symplify/easy-coding-standard": "^12.1.14"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "library/helpers.php",
+ "library/Mockery.php"
+ ],
+ "psr-4": {
+ "Mockery\\": "library/Mockery"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Pádraic Brady",
+ "email": "padraic.brady@gmail.com",
+ "homepage": "https://github.com/padraic",
+ "role": "Author"
+ },
+ {
+ "name": "Dave Marshall",
+ "email": "dave.marshall@atstsolutions.co.uk",
+ "homepage": "https://davedevelopment.co.uk",
+ "role": "Developer"
+ },
+ {
+ "name": "Nathanael Esayeas",
+ "email": "nathanael.esayeas@protonmail.com",
+ "homepage": "https://github.com/ghostwriter",
+ "role": "Lead Developer"
+ }
+ ],
+ "description": "Mockery is a simple yet flexible PHP mock object framework",
+ "homepage": "https://github.com/mockery/mockery",
+ "keywords": [
+ "BDD",
+ "TDD",
+ "library",
+ "mock",
+ "mock objects",
+ "mockery",
+ "stub",
+ "test",
+ "test double",
+ "testing"
+ ],
+ "support": {
+ "docs": "https://docs.mockery.io/",
+ "issues": "https://github.com/mockery/mockery/issues",
+ "rss": "https://github.com/mockery/mockery/releases.atom",
+ "security": "https://github.com/mockery/mockery/security/advisories",
+ "source": "https://github.com/mockery/mockery"
+ },
+ "time": "2024-05-16T03:13:13+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "faed855a7b5f4d4637717c2b3863e277116beb36"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36",
+ "reference": "faed855a7b5f4d4637717c2b3863e277116beb36",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.1 || ^8.0"
+ },
+ "conflict": {
+ "doctrine/collections": "<1.6.8",
+ "doctrine/common": "<2.13.3 || >=3 <3.2.2"
+ },
+ "require-dev": {
+ "doctrine/collections": "^1.6.8",
+ "doctrine/common": "^2.13.3 || ^3.2.2",
+ "phpspec/prophecy": "^1.10",
+ "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
+ },
+ "type": "library",
+ "autoload": {
+ "files": [
+ "src/DeepCopy/deep_copy.php"
+ ],
+ "psr-4": {
+ "DeepCopy\\": "src/DeepCopy/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Create deep copies (clones) of your objects",
+ "keywords": [
+ "clone",
+ "copy",
+ "duplicate",
+ "object",
+ "object graph"
+ ],
+ "support": {
+ "issues": "https://github.com/myclabs/DeepCopy/issues",
+ "source": "https://github.com/myclabs/DeepCopy/tree/1.13.3"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-05T12:25:42+00:00"
+ },
+ {
+ "name": "phar-io/manifest",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/manifest.git",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176",
+ "reference": "54750ef60c58e43759730615a392c31c80e23176",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-phar": "*",
+ "ext-xmlwriter": "*",
+ "phar-io/version": "^3.0.1",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.0.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+ "support": {
+ "issues": "https://github.com/phar-io/manifest/issues",
+ "source": "https://github.com/phar-io/manifest/tree/2.0.4"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/theseer",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-03T12:33:53+00:00"
+ },
+ {
+ "name": "phar-io/version",
+ "version": "3.2.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phar-io/version.git",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Heuer",
+ "email": "sebastian@phpeople.de",
+ "role": "Developer"
+ },
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "Library for handling version information and constraints",
+ "support": {
+ "issues": "https://github.com/phar-io/version/issues",
+ "source": "https://github.com/phar-io/version/tree/3.2.1"
+ },
+ "time": "2022-02-21T01:04:05+00:00"
+ },
+ {
+ "name": "phpspec/prophecy",
+ "version": "v1.22.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/prophecy.git",
+ "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/prophecy/zipball/35f1adb388946d92e6edab2aa2cb2b60e132ebd5",
+ "reference": "35f1adb388946d92e6edab2aa2cb2b60e132ebd5",
+ "shasum": ""
+ },
+ "require": {
+ "doctrine/instantiator": "^1.2 || ^2.0",
+ "php": "^7.4 || 8.0.* || 8.1.* || 8.2.* || 8.3.* || 8.4.*",
+ "phpdocumentor/reflection-docblock": "^5.2",
+ "sebastian/comparator": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0",
+ "sebastian/recursion-context": "^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
+ },
+ "require-dev": {
+ "friendsofphp/php-cs-fixer": "^3.40",
+ "phpspec/phpspec": "^6.0 || ^7.0",
+ "phpstan/phpstan": "^2.1.13",
+ "phpunit/phpunit": "^8.0 || ^9.0 || ^10.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Prophecy\\": "src/Prophecy"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Konstantin Kudryashov",
+ "email": "ever.zet@gmail.com",
+ "homepage": "http://everzet.com"
+ },
+ {
+ "name": "Marcello Duarte",
+ "email": "marcello.duarte@gmail.com"
+ }
+ ],
+ "description": "Highly opinionated mocking framework for PHP 5.3+",
+ "homepage": "https://github.com/phpspec/prophecy",
+ "keywords": [
+ "Double",
+ "Dummy",
+ "dev",
+ "fake",
+ "mock",
+ "spy",
+ "stub"
+ ],
+ "support": {
+ "issues": "https://github.com/phpspec/prophecy/issues",
+ "source": "https://github.com/phpspec/prophecy/tree/v1.22.0"
+ },
+ "time": "2025-04-29T14:58:06+00:00"
+ },
+ {
+ "name": "phpspec/prophecy-phpunit",
+ "version": "v2.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpspec/prophecy-phpunit.git",
+ "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpspec/prophecy-phpunit/zipball/d3c28041d9390c9bca325a08c5b2993ac855bded",
+ "reference": "d3c28041d9390c9bca325a08c5b2993ac855bded",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.3 || ^8",
+ "phpspec/prophecy": "^1.18",
+ "phpunit/phpunit": "^9.1 || ^10.1 || ^11.0 || ^12.0"
+ },
+ "require-dev": {
+ "phpstan/phpstan": "^1.10"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Prophecy\\PhpUnit\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Christophe Coevoet",
+ "email": "stof@notk.org"
+ }
+ ],
+ "description": "Integrating the Prophecy mocking library in PHPUnit test cases",
+ "homepage": "http://phpspec.net",
+ "keywords": [
+ "phpunit",
+ "prophecy"
+ ],
+ "support": {
+ "issues": "https://github.com/phpspec/prophecy-phpunit/issues",
+ "source": "https://github.com/phpspec/prophecy-phpunit/tree/v2.4.0"
+ },
+ "time": "2025-05-13T13:52:32+00:00"
+ },
+ {
+ "name": "phpstan/extension-installer",
+ "version": "1.4.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/extension-installer.git",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/85e90b3942d06b2326fba0403ec24fe912372936",
+ "reference": "85e90b3942d06b2326fba0403ec24fe912372936",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.0",
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.9.0 || ^2.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2.0",
+ "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPStan\\ExtensionInstaller\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\ExtensionInstaller\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Composer plugin for automatic installation of PHPStan extensions",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/phpstan/extension-installer/issues",
+ "source": "https://github.com/phpstan/extension-installer/tree/1.4.3"
+ },
+ "time": "2024-09-04T20:21:43+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "1.12.9",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "ceb937fb39a92deabc02d20709cf14b2c452502c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ceb937fb39a92deabc02d20709cf14b2c452502c",
+ "reference": "ceb937fb39a92deabc02d20709cf14b2c452502c",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2|^8.0"
+ },
+ "conflict": {
+ "phpstan/phpstan-shim": "*"
+ },
+ "bin": [
+ "phpstan",
+ "phpstan.phar"
+ ],
+ "type": "library",
+ "autoload": {
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan - PHP Static Analysis Tool",
+ "keywords": [
+ "dev",
+ "static analysis"
+ ],
+ "support": {
+ "docs": "https://phpstan.org/user-guide/getting-started",
+ "forum": "https://github.com/phpstan/phpstan/discussions",
+ "issues": "https://github.com/phpstan/phpstan/issues",
+ "security": "https://github.com/phpstan/phpstan/security/policy",
+ "source": "https://github.com/phpstan/phpstan-src"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/ondrejmirtes",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/phpstan",
+ "type": "github"
+ }
+ ],
+ "time": "2024-11-10T17:10:04+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-webmozart-assert",
+ "version": "1.2.11",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-webmozart-assert.git",
+ "reference": "960dd44e8466191590dd0d7940d3e9496eebebbd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-webmozart-assert/zipball/960dd44e8466191590dd0d7940d3e9496eebebbd",
+ "reference": "960dd44e8466191590dd0d7940d3e9496eebebbd",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.2 || ^8.0",
+ "phpstan/phpstan": "^1.12"
+ },
+ "require-dev": {
+ "nikic/php-parser": "^4.13.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^1.2",
+ "phpstan/phpstan-phpunit": "^1.4",
+ "phpstan/phpstan-strict-rules": "^1.6",
+ "phpunit/phpunit": "^9.5",
+ "webmozart/assert": "^1.11.0"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "PHPStan webmozart/assert extension",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-webmozart-assert/issues",
+ "source": "https://github.com/phpstan/phpstan-webmozart-assert/tree/1.2.11"
+ },
+ "time": "2024-09-11T15:48:08+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "10.1.16",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77",
+ "reference": "7e308268858ed6baedc8704a304727d20bc07c77",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^4.19.1 || ^5.1.0",
+ "php": ">=8.1",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-text-template": "^3.0.1",
+ "sebastian/code-unit-reverse-lookup": "^3.0.0",
+ "sebastian/complexity": "^3.2.0",
+ "sebastian/environment": "^6.1.0",
+ "sebastian/lines-of-code": "^2.0.2",
+ "sebastian/version": "^4.0.1",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.1"
+ },
+ "suggest": {
+ "ext-pcov": "PHP extension that provides line coverage",
+ "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "10.1.x-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+ "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "keywords": [
+ "coverage",
+ "testing",
+ "xunit"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
+ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16"
+ },
+ "funding": [
{
- "name": "Konstantin Kudryashov",
- "email": "ever.zet@gmail.com",
- "homepage": "http://everzet.com"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Scenario-oriented BDD framework for PHP 5.3",
- "homepage": "http://behat.org/",
- "keywords": [
- "BDD",
- "Behat",
- "Symfony2"
- ],
- "time": "2014-04-26 16:55:16"
+ "time": "2024-08-22T04:31:57+00:00"
},
{
- "name": "behat/gherkin",
- "version": "v2.3.5",
+ "name": "phpunit/php-file-iterator",
+ "version": "4.1.0",
"source": {
"type": "git",
- "url": "https://github.com/Behat/Gherkin.git",
- "reference": "2b33963da5525400573560c173ab5c9c057e1852"
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Behat/Gherkin/zipball/2b33963da5525400573560c173ab5c9c057e1852",
- "reference": "2b33963da5525400573560c173ab5c9c057e1852",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c",
+ "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c",
"shasum": ""
},
"require": {
- "php": ">=5.3.1",
- "symfony/finder": "~2.0"
+ "php": ">=8.1"
},
"require-dev": {
- "symfony/config": "~2.0",
- "symfony/translation": "~2.0",
- "symfony/yaml": "~2.0"
- },
- "suggest": {
- "symfony/config": "If you want to use Config component to manage resources",
- "symfony/translation": "If you want to use Symfony2 translations adapter",
- "symfony/yaml": "If you want to parse features, represented in YAML files"
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-develop": "2.2-dev"
+ "dev-main": "4.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Behat\\Gherkin": "src/"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Konstantin Kudryashov",
- "email": "ever.zet@gmail.com",
- "homepage": "http://everzet.com"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "Gherkin DSL parser for PHP 5.3",
- "homepage": "http://behat.org/",
+ "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+ "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
"keywords": [
- "BDD",
- "Behat",
- "DSL",
- "Symfony2",
- "parser"
+ "filesystem",
+ "iterator"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
+ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2013-10-15 11:22:17"
+ "time": "2023-08-31T06:24:48+00:00"
},
{
- "name": "mockery/mockery",
- "version": "0.9.1",
+ "name": "phpunit/php-invoker",
+ "version": "4.0.0",
"source": {
"type": "git",
- "url": "https://github.com/padraic/mockery.git",
- "reference": "17f63ee40ed14a8afb7ba1f0ae15cc4491d719d1"
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/padraic/mockery/zipball/17f63ee40ed14a8afb7ba1f0ae15cc4491d719d1",
- "reference": "17f63ee40ed14a8afb7ba1f0ae15cc4491d719d1",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
+ "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7",
"shasum": ""
},
"require": {
- "lib-pcre": ">=7.0",
- "php": ">=5.3.2"
+ "php": ">=8.1"
},
"require-dev": {
- "hamcrest/hamcrest-php": "~1.1",
- "phpunit/phpunit": "~4.0",
- "satooshi/php-coveralls": "~0.7@dev"
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "0.9.x-dev"
+ "dev-main": "4.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Mockery": "library/"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -304,436 +1580,487 @@
],
"authors": [
{
- "name": "Pádraic Brady",
- "email": "padraic.brady@gmail.com",
- "homepage": "http://blog.astrumfutura.com"
- },
- {
- "name": "Dave Marshall",
- "email": "dave.marshall@atstsolutions.co.uk",
- "homepage": "http://davedevelopment.co.uk"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "Mockery is a simple yet flexible PHP mock object framework for use in unit testing with PHPUnit, PHPSpec or any other testing framework. Its core goal is to offer a test double framework with a succint API capable of clearly defining all possible object operations and interactions using a human readable Domain Specific Language (DSL). Designed as a drop in alternative to PHPUnit's phpunit-mock-objects library, Mockery is easy to integrate with PHPUnit and can operate alongside phpunit-mock-objects without the World ending.",
- "homepage": "http://github.com/padraic/mockery",
+ "description": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
"keywords": [
- "BDD",
- "TDD",
- "library",
- "mock",
- "mock objects",
- "mockery",
- "stub",
- "test",
- "test double",
- "testing"
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2014-05-02 12:16:45"
+ "time": "2023-02-03T06:56:09+00:00"
},
{
- "name": "ocramius/instantiator",
- "version": "1.1.2",
+ "name": "phpunit/php-text-template",
+ "version": "3.0.1",
"source": {
"type": "git",
- "url": "https://github.com/Ocramius/Instantiator.git",
- "reference": "a7abbb5fc9df6e7126af741dd6c140d1a7369435"
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Ocramius/Instantiator/zipball/a7abbb5fc9df6e7126af741dd6c140d1a7369435",
- "reference": "a7abbb5fc9df6e7126af741dd6c140d1a7369435",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748",
+ "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748",
"shasum": ""
},
"require": {
- "ocramius/lazy-map": "1.0.*",
- "php": "~5.3"
+ "php": ">=8.1"
},
"require-dev": {
- "athletic/athletic": "~0.1.8",
- "ext-pdo": "*",
- "ext-phar": "*",
- "phpunit/phpunit": "~4.0",
- "squizlabs/php_codesniffer": "2.0.*@ALPHA"
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-main": "3.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Instantiator\\": "src"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Marco Pivetta",
- "email": "ocramius@gmail.com",
- "homepage": "http://ocramius.github.com/"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
- "homepage": "https://github.com/Ocramius/Instantiator",
+ "description": "Simple template engine.",
+ "homepage": "https://github.com/sebastianbergmann/php-text-template/",
"keywords": [
- "constructor",
- "instantiate"
+ "template"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-text-template/issues",
+ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2014-08-14 15:10:55"
+ "time": "2023-08-31T14:07:24+00:00"
},
{
- "name": "ocramius/lazy-map",
- "version": "1.0.0",
+ "name": "phpunit/php-timer",
+ "version": "6.0.0",
"source": {
"type": "git",
- "url": "https://github.com/Ocramius/LazyMap.git",
- "reference": "7fe3d347f5e618bcea7d39345ff83f3651d8b752"
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/Ocramius/LazyMap/zipball/7fe3d347f5e618bcea7d39345ff83f3651d8b752",
- "reference": "7fe3d347f5e618bcea7d39345ff83f3651d8b752",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d",
+ "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
},
"require-dev": {
- "athletic/athletic": "~0.1.6",
- "phpmd/phpmd": "1.5.*",
- "phpunit/phpunit": ">=3.7",
- "satooshi/php-coveralls": "~0.6",
- "squizlabs/php_codesniffer": "1.4.*"
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-main": "6.0-dev"
}
},
"autoload": {
- "psr-0": {
- "LazyMap\\": "src"
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Marco Pivetta",
- "email": "ocramius@gmail.com",
- "homepage": "http://ocramius.github.com/",
- "role": "Developer"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
}
],
- "description": "A library that provides lazy instantiation logic for a map of objects",
- "homepage": "https://github.com/Ocramius/LazyMap",
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
"keywords": [
- "lazy",
- "lazy instantiation",
- "lazy loading",
- "map",
- "service location"
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2013-11-09 22:30:54"
+ "time": "2023-02-03T06:57:52+00:00"
},
{
- "name": "phpunit/php-code-coverage",
- "version": "2.0.10",
+ "name": "phpunit/phpunit",
+ "version": "10.5.48",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
- "reference": "6d196af48e8c100a3ae881940123e693da5a9217"
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "6e0a2bc39f6fae7617989d690d76c48e6d2eb541"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6d196af48e8c100a3ae881940123e693da5a9217",
- "reference": "6d196af48e8c100a3ae881940123e693da5a9217",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6e0a2bc39f6fae7617989d690d76c48e6d2eb541",
+ "reference": "6e0a2bc39f6fae7617989d690d76c48e6d2eb541",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
- "phpunit/php-file-iterator": "~1.3.1",
- "phpunit/php-text-template": "~1.2.0",
- "phpunit/php-token-stream": "~1.2.2",
- "sebastian/environment": "~1.0.0",
- "sebastian/version": "~1.0.3"
- },
- "require-dev": {
- "ext-xdebug": ">=2.1.4",
- "phpunit/phpunit": "~4.0.14"
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.3",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.1",
+ "phpunit/php-code-coverage": "^10.1.16",
+ "phpunit/php-file-iterator": "^4.1.0",
+ "phpunit/php-invoker": "^4.0.0",
+ "phpunit/php-text-template": "^3.0.1",
+ "phpunit/php-timer": "^6.0.0",
+ "sebastian/cli-parser": "^2.0.1",
+ "sebastian/code-unit": "^2.0.0",
+ "sebastian/comparator": "^5.0.3",
+ "sebastian/diff": "^5.1.1",
+ "sebastian/environment": "^6.1.0",
+ "sebastian/exporter": "^5.1.2",
+ "sebastian/global-state": "^6.0.2",
+ "sebastian/object-enumerator": "^5.0.0",
+ "sebastian/recursion-context": "^5.0.0",
+ "sebastian/type": "^4.0.0",
+ "sebastian/version": "^4.0.1"
},
"suggest": {
- "ext-dom": "*",
- "ext-xdebug": ">=2.2.1",
- "ext-xmlwriter": "*"
+ "ext-soap": "To be able to generate mocks based on WSDL files"
},
+ "bin": [
+ "phpunit"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.0.x-dev"
+ "dev-main": "10.5-dev"
}
},
"autoload": {
+ "files": [
+ "src/Framework/Assert/Functions.php"
+ ],
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
- ],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
- "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
- "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+ "description": "The PHP Unit Testing framework.",
+ "homepage": "https://phpunit.de/",
"keywords": [
- "coverage",
+ "phpunit",
"testing",
"xunit"
],
- "time": "2014-08-06 06:39:42"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/phpunit/issues",
+ "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.48"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-11T04:07:17+00:00"
},
{
- "name": "phpunit/php-file-iterator",
- "version": "1.3.4",
+ "name": "psalm/phar",
+ "version": "6.8.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
- "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb"
+ "url": "https://github.com/psalm/phar.git",
+ "reference": "333a7a0d7805b699860262e1b4ea8cf14e1cd118"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb",
- "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb",
+ "url": "https://api.github.com/repos/psalm/phar/zipball/333a7a0d7805b699860262e1b4ea8cf14e1cd118",
+ "reference": "333a7a0d7805b699860262e1b4ea8cf14e1cd118",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": "^7.1 || ^8.0"
},
- "type": "library",
- "autoload": {
- "classmap": [
- "File/"
- ]
+ "conflict": {
+ "vimeo/psalm": "*"
},
- "notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
+ "bin": [
+ "psalm.phar"
],
+ "type": "library",
+ "notification-url": "https://packagist.org/downloads/",
"license": [
- "BSD-3-Clause"
- ],
- "authors": [
- {
- "name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
- }
- ],
- "description": "FilterIterator implementation that filters files based on a list of suffixes.",
- "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
- "keywords": [
- "filesystem",
- "iterator"
+ "MIT"
],
- "time": "2013-10-10 15:34:57"
+ "description": "Composer-based Psalm Phar",
+ "support": {
+ "issues": "https://github.com/psalm/phar/issues",
+ "source": "https://github.com/psalm/phar/tree/6.8.2"
+ },
+ "time": "2025-02-20T08:23:26+00:00"
},
{
- "name": "phpunit/php-text-template",
- "version": "1.2.0",
+ "name": "rector/rector",
+ "version": "1.2.10",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-text-template.git",
- "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a"
+ "url": "https://github.com/rectorphp/rector.git",
+ "reference": "40f9cf38c05296bd32f444121336a521a293fa61"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
- "reference": "206dfefc0ffe9cebf65c413e3d0e809c82fbf00a",
+ "url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61",
+ "reference": "40f9cf38c05296bd32f444121336a521a293fa61",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": "^7.2|^8.0",
+ "phpstan/phpstan": "^1.12.5"
+ },
+ "conflict": {
+ "rector/rector-doctrine": "*",
+ "rector/rector-downgrade-php": "*",
+ "rector/rector-phpunit": "*",
+ "rector/rector-symfony": "*"
},
+ "suggest": {
+ "ext-dom": "To manipulate phpunit.xml via the custom-rule command"
+ },
+ "bin": [
+ "bin/rector"
+ ],
"type": "library",
"autoload": {
- "classmap": [
- "Text/"
+ "files": [
+ "bootstrap.php"
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
- ],
"license": [
- "BSD-3-Clause"
+ "MIT"
],
- "authors": [
+ "description": "Instant Upgrade and Automated Refactoring of any PHP code",
+ "keywords": [
+ "automation",
+ "dev",
+ "migration",
+ "refactoring"
+ ],
+ "support": {
+ "issues": "https://github.com/rectorphp/rector/issues",
+ "source": "https://github.com/rectorphp/rector/tree/1.2.10"
+ },
+ "funding": [
{
- "name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
+ "url": "https://github.com/tomasvotruba",
+ "type": "github"
}
],
- "description": "Simple template engine.",
- "homepage": "https://github.com/sebastianbergmann/php-text-template/",
- "keywords": [
- "template"
- ],
- "time": "2014-01-30 17:20:04"
+ "time": "2024-11-08T13:59:10+00:00"
},
{
- "name": "phpunit/php-timer",
- "version": "1.0.5",
+ "name": "sebastian/cli-parser",
+ "version": "2.0.1",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-timer.git",
- "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c"
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
- "reference": "19689d4354b295ee3d8c54b4f42c3efb69cbc17c",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084",
+ "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "2.0-dev"
+ }
+ },
"autoload": {
"classmap": [
- "PHP/"
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
- ],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
- "description": "Utility class for timing",
- "homepage": "https://github.com/sebastianbergmann/php-timer/",
- "keywords": [
- "timer"
+ "description": "Library for parsing CLI options",
+ "homepage": "https://github.com/sebastianbergmann/cli-parser",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/cli-parser/issues",
+ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy",
+ "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2013-08-02 07:42:54"
+ "time": "2024-03-02T07:12:49+00:00"
},
{
- "name": "phpunit/php-token-stream",
- "version": "1.2.2",
+ "name": "sebastian/code-unit",
+ "version": "2.0.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/php-token-stream.git",
- "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32"
+ "url": "https://github.com/sebastianbergmann/code-unit.git",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/ad4e1e23ae01b483c16f600ff1bebec184588e32",
- "reference": "ad4e1e23ae01b483c16f600ff1bebec184588e32",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503",
+ "reference": "a81fee9eef0b7a76af11d121767abc44c104e503",
"shasum": ""
},
"require": {
- "ext-tokenizer": "*",
- "php": ">=5.3.3"
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.2-dev"
+ "dev-main": "2.0-dev"
}
},
"autoload": {
"classmap": [
- "PHP/"
+ "src/"
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
- ],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
+ "email": "sebastian@phpunit.de",
"role": "lead"
}
],
- "description": "Wrapper around PHP's tokenizer extension.",
- "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
- "keywords": [
- "tokenizer"
+ "description": "Collection of value objects that represent the PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/code-unit",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2014-03-03 05:10:30"
+ "time": "2023-02-03T06:58:43+00:00"
},
{
- "name": "phpunit/phpunit",
- "version": "4.2.2",
+ "name": "sebastian/code-unit-reverse-lookup",
+ "version": "3.0.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "a33fa68ece9f8c68589bfc2da8d2794e27b820bc"
+ "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a33fa68ece9f8c68589bfc2da8d2794e27b820bc",
- "reference": "a33fa68ece9f8c68589bfc2da8d2794e27b820bc",
+ "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
+ "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d",
"shasum": ""
},
"require": {
- "ext-dom": "*",
- "ext-json": "*",
- "ext-pcre": "*",
- "ext-reflection": "*",
- "ext-spl": "*",
- "php": ">=5.3.3",
- "phpunit/php-code-coverage": "~2.0",
- "phpunit/php-file-iterator": "~1.3.1",
- "phpunit/php-text-template": "~1.2",
- "phpunit/php-timer": "~1.0.2",
- "phpunit/phpunit-mock-objects": "~2.2",
- "sebastian/comparator": "~1.0",
- "sebastian/diff": "~1.1",
- "sebastian/environment": "~1.0",
- "sebastian/exporter": "~1.0",
- "sebastian/version": "~1.0",
- "symfony/yaml": "~2.0"
+ "php": ">=8.1"
},
- "suggest": {
- "phpunit/php-invoker": "~1.1"
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
},
- "bin": [
- "phpunit"
- ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "4.2.x-dev"
+ "dev-main": "3.0-dev"
}
},
"autoload": {
@@ -742,58 +2069,57 @@
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- "",
- "../../symfony/yaml/"
- ],
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "email": "sebastian@phpunit.de"
}
],
- "description": "The PHP Unit Testing framework.",
- "homepage": "http://www.phpunit.de/",
- "keywords": [
- "phpunit",
- "testing",
- "xunit"
+ "description": "Looks up which function or method a line of code belongs to",
+ "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
+ "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2014-08-18 05:12:30"
+ "time": "2023-02-03T06:59:15+00:00"
},
{
- "name": "phpunit/phpunit-mock-objects",
- "version": "2.2.0",
+ "name": "sebastian/comparator",
+ "version": "5.0.3",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
- "reference": "42e589e08bc86e3e9bdf20d385e948347788505b"
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/42e589e08bc86e3e9bdf20d385e948347788505b",
- "reference": "42e589e08bc86e3e9bdf20d385e948347788505b",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
+ "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e",
"shasum": ""
},
"require": {
- "ocramius/instantiator": "~1.0",
- "php": ">=5.3.3",
- "phpunit/php-text-template": "~1.2"
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/diff": "^5.0",
+ "sebastian/exporter": "^5.0"
},
"require-dev": {
- "phpunit/phpunit": "4.2.*@dev"
- },
- "suggest": {
- "ext-soap": "*"
+ "phpunit/phpunit": "^10.5"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.2.x-dev"
+ "dev-main": "5.0-dev"
}
},
"autoload": {
@@ -802,53 +2128,72 @@
]
},
"notification-url": "https://packagist.org/downloads/",
- "include-path": [
- ""
- ],
"license": [
"BSD-3-Clause"
],
"authors": [
{
- "name": "Sebastian Bergmann",
- "email": "sb@sebastian-bergmann.de",
- "role": "lead"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Volker Dusch",
+ "email": "github@wallbash.com"
+ },
+ {
+ "name": "Bernhard Schussek",
+ "email": "bschussek@2bepublished.at"
}
],
- "description": "Mock Object library for PHPUnit",
- "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+ "description": "Provides the functionality to compare PHP values for equality",
+ "homepage": "https://github.com/sebastianbergmann/comparator",
"keywords": [
- "mock",
- "xunit"
+ "comparator",
+ "compare",
+ "equality"
],
- "time": "2014-08-02 13:50:58"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/comparator/issues",
+ "security": "https://github.com/sebastianbergmann/comparator/security/policy",
+ "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-18T14:56:07+00:00"
},
{
- "name": "sebastian/comparator",
- "version": "1.0.0",
+ "name": "sebastian/complexity",
+ "version": "3.2.0",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/comparator.git",
- "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2"
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2",
- "reference": "f7069ee51fa9fb6c038e16a9d0e3439f5449dcf2",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799",
+ "reference": "68ff824baeae169ec9f2137158ee529584553799",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
- "sebastian/diff": "~1.1",
- "sebastian/exporter": "~1.0"
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
},
"require-dev": {
- "phpunit/phpunit": "~4.1"
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-main": "3.2-dev"
}
},
"autoload": {
@@ -865,50 +2210,48 @@
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de",
"role": "lead"
- },
- {
- "name": "Jeff Welch",
- "email": "whatthejeff@gmail.com"
- },
- {
- "name": "Volker Dusch",
- "email": "github@wallbash.com"
- },
- {
- "name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
}
],
- "description": "Provides the functionality to compare PHP values for equality",
- "homepage": "http://www.github.com/sebastianbergmann/comparator",
- "keywords": [
- "comparator",
- "compare",
- "equality"
+ "description": "Library for calculating the complexity of PHP code units",
+ "homepage": "https://github.com/sebastianbergmann/complexity",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/complexity/issues",
+ "security": "https://github.com/sebastianbergmann/complexity/security/policy",
+ "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2014-05-02 07:05:58"
+ "time": "2023-12-21T08:37:17+00:00"
},
{
"name": "sebastian/diff",
- "version": "1.1.0",
+ "version": "5.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d"
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d",
- "reference": "1e091702a5a38e6b4c1ba9ca816e3dd343df2e2d",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0",
+ "symfony/process": "^6.4"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.1-dev"
+ "dev-main": "5.1-dev"
}
},
"autoload": {
@@ -923,8 +2266,7 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "email": "sebastian@phpunit.de"
},
{
"name": "Kore Nordmann",
@@ -932,36 +2274,53 @@
}
],
"description": "Diff implementation",
- "homepage": "http://www.github.com/sebastianbergmann/diff",
+ "homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
- "diff"
+ "diff",
+ "udiff",
+ "unidiff",
+ "unified diff"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/diff/issues",
+ "security": "https://github.com/sebastianbergmann/diff/security/policy",
+ "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
],
- "time": "2013-08-03 16:46:33"
+ "time": "2024-03-02T07:15:17+00:00"
},
{
"name": "sebastian/environment",
- "version": "1.0.0",
+ "version": "6.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/environment.git",
- "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a"
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/79517609ec01139cd7e9fded0dd7ce08c952ef6a",
- "reference": "79517609ec01139cd7e9fded0dd7ce08c952ef6a",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984",
+ "reference": "8074dbcd93529b357029f5cc5058fd3e43666984",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
},
"require-dev": {
- "phpunit/phpunit": "4.0.*@dev"
+ "phpunit/phpunit": "^10.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-main": "6.1-dev"
}
},
"autoload": {
@@ -976,43 +2335,55 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "email": "sebastian@phpunit.de"
}
],
"description": "Provides functionality to handle HHVM/PHP environments",
- "homepage": "http://www.github.com/sebastianbergmann/environment",
+ "homepage": "https://github.com/sebastianbergmann/environment",
"keywords": [
"Xdebug",
"environment",
"hhvm"
],
- "time": "2014-02-18 16:17:19"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/environment/issues",
+ "security": "https://github.com/sebastianbergmann/environment/security/policy",
+ "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-23T08:47:14+00:00"
},
{
"name": "sebastian/exporter",
- "version": "1.0.1",
+ "version": "5.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
- "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529"
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529",
- "reference": "1f9a98e6f5dfe0524cb8c6166f7c82f3e9ae1529",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf",
+ "reference": "955288482d97c19a372d3f31006ab3f37da47adf",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "ext-mbstring": "*",
+ "php": ">=8.1",
+ "sebastian/recursion-context": "^5.0"
},
"require-dev": {
- "phpunit/phpunit": "4.0.*@dev"
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "1.0.x-dev"
+ "dev-main": "5.1-dev"
}
},
"autoload": {
@@ -1027,8 +2398,7 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
@@ -1040,37 +2410,61 @@
},
{
"name": "Adam Harvey",
- "email": "aharvey@php.net",
- "role": "Lead"
+ "email": "aharvey@php.net"
},
{
"name": "Bernhard Schussek",
- "email": "bschussek@2bepublished.at"
+ "email": "bschussek@gmail.com"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
- "homepage": "http://www.github.com/sebastianbergmann/exporter",
+ "homepage": "https://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
- "time": "2014-02-16 08:26:31"
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/exporter/issues",
+ "security": "https://github.com/sebastianbergmann/exporter/security/policy",
+ "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:17:12+00:00"
},
{
- "name": "sebastian/version",
- "version": "1.0.3",
+ "name": "sebastian/global-state",
+ "version": "6.0.2",
"source": {
"type": "git",
- "url": "https://github.com/sebastianbergmann/version.git",
- "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43"
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
- "reference": "b6e1f0cf6b9e1ec409a0d3e2f2a5fb0998e36b43",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
+ "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9",
"shasum": ""
},
+ "require": {
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^10.0"
+ },
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
"autoload": {
"classmap": [
"src/"
@@ -1083,435 +2477,581 @@
"authors": [
{
"name": "Sebastian Bergmann",
- "email": "sebastian@phpunit.de",
- "role": "lead"
+ "email": "sebastian@phpunit.de"
}
],
- "description": "Library that helps with managing the version number of Git-hosted PHP projects",
- "homepage": "https://github.com/sebastianbergmann/version",
- "time": "2014-03-07 15:35:33"
+ "description": "Snapshotting of global state",
+ "homepage": "https://www.github.com/sebastianbergmann/global-state",
+ "keywords": [
+ "global state"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/global-state/issues",
+ "security": "https://github.com/sebastianbergmann/global-state/security/policy",
+ "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2024-03-02T07:19:19+00:00"
},
{
- "name": "symfony/config",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/Config",
+ "name": "sebastian/lines-of-code",
+ "version": "2.0.2",
"source": {
"type": "git",
- "url": "https://github.com/symfony/config.git",
- "reference": "8d044668c7ccb4ade684e368d910e3aadcff6f6c"
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/config/zipball/8d044668c7ccb4ade684e368d910e3aadcff6f6c",
- "reference": "8d044668c7ccb4ade684e368d910e3aadcff6f6c",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0",
+ "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0",
"shasum": ""
},
"require": {
- "php": ">=5.3.3",
- "symfony/filesystem": "~2.3"
+ "nikic/php-parser": "^4.18 || ^5.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-main": "2.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Symfony\\Component\\Config\\": ""
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
- },
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for counting the lines of code in PHP source code",
+ "homepage": "https://github.com/sebastianbergmann/lines-of-code",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/lines-of-code/issues",
+ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy",
+ "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2"
+ },
+ "funding": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Symfony Config Component",
- "homepage": "http://symfony.com",
- "time": "2014-08-05 09:00:40"
+ "time": "2023-12-21T08:38:20+00:00"
},
{
- "name": "symfony/console",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/Console",
+ "name": "sebastian/object-enumerator",
+ "version": "5.0.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/console.git",
- "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63"
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/console/zipball/cd2d1e4bac2206b337326b0140ff475fe9ad5f63",
- "reference": "cd2d1e4bac2206b337326b0140ff475fe9ad5f63",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906",
+ "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1",
+ "sebastian/object-reflector": "^3.0",
+ "sebastian/recursion-context": "^5.0"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/event-dispatcher": "~2.1"
- },
- "suggest": {
- "psr/log": "For using the console logger",
- "symfony/event-dispatcher": ""
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-main": "5.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Symfony\\Component\\Console\\": ""
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
- },
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0"
+ },
+ "funding": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Symfony Console Component",
- "homepage": "http://symfony.com",
- "time": "2014-08-05 09:00:40"
+ "time": "2023-02-03T07:08:32+00:00"
},
{
- "name": "symfony/dependency-injection",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/DependencyInjection",
+ "name": "sebastian/object-reflector",
+ "version": "3.0.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/dependency-injection.git",
- "reference": "54529fdc797a88c030441773adadcc759bb102c2"
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/54529fdc797a88c030441773adadcc759bb102c2",
- "reference": "54529fdc797a88c030441773adadcc759bb102c2",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957",
+ "reference": "24ed13d98130f0e7122df55d06c5c4942a577957",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
},
"require-dev": {
- "symfony/config": "~2.2",
- "symfony/expression-language": "~2.4",
- "symfony/yaml": "~2.0"
- },
- "suggest": {
- "symfony/config": "",
- "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them",
- "symfony/yaml": ""
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-main": "3.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Symfony\\Component\\DependencyInjection\\": ""
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
- },
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0"
+ },
+ "funding": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Symfony DependencyInjection Component",
- "homepage": "http://symfony.com",
- "time": "2014-08-06 06:44:37"
+ "time": "2023-02-03T07:06:18+00:00"
},
{
- "name": "symfony/event-dispatcher",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/EventDispatcher",
+ "name": "sebastian/recursion-context",
+ "version": "5.0.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/event-dispatcher.git",
- "reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b"
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "05909fb5bc7df4c52992396d0116aed689f93712"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
- "reference": "8faf5cc7e80fde74a650a36e60d32ce3c3e0457b",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712",
+ "reference": "05909fb5bc7df4c52992396d0116aed689f93712",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
},
"require-dev": {
- "psr/log": "~1.0",
- "symfony/config": "~2.0",
- "symfony/dependency-injection": "~2.0",
- "symfony/stopwatch": "~2.2"
- },
- "suggest": {
- "symfony/dependency-injection": "",
- "symfony/http-kernel": ""
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-main": "5.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Symfony\\Component\\EventDispatcher\\": ""
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
},
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Symfony EventDispatcher Component",
- "homepage": "http://symfony.com",
- "time": "2014-07-28 13:20:46"
+ "time": "2023-02-03T07:05:40+00:00"
},
{
- "name": "symfony/filesystem",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/Filesystem",
+ "name": "sebastian/type",
+ "version": "4.0.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/filesystem.git",
- "reference": "c1309b0ee195ad264a4314435bdaecdfacb8ae9c"
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/filesystem/zipball/c1309b0ee195ad264a4314435bdaecdfacb8ae9c",
- "reference": "c1309b0ee195ad264a4314435bdaecdfacb8ae9c",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf",
+ "reference": "462699a16464c3944eefc02ebdd77882bd3925bf",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^10.0"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-main": "4.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Symfony\\Component\\Filesystem\\": ""
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
- },
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "source": "https://github.com/sebastianbergmann/type/tree/4.0.0"
+ },
+ "funding": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Symfony Filesystem Component",
- "homepage": "http://symfony.com",
- "time": "2014-07-09 09:05:48"
+ "time": "2023-02-03T07:10:45+00:00"
},
{
- "name": "symfony/finder",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/Finder",
+ "name": "sebastian/version",
+ "version": "4.0.1",
"source": {
"type": "git",
- "url": "https://github.com/symfony/Finder.git",
- "reference": "090fe4eaff414d8f2171c7a4748ea868d530775f"
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Finder/zipball/090fe4eaff414d8f2171c7a4748ea868d530775f",
- "reference": "090fe4eaff414d8f2171c7a4748ea868d530775f",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17",
+ "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "php": ">=8.1"
},
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-main": "4.0-dev"
}
},
"autoload": {
- "psr-0": {
- "Symfony\\Component\\Finder\\": ""
- }
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
- },
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "source": "https://github.com/sebastianbergmann/version/tree/4.0.1"
+ },
+ "funding": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
}
],
- "description": "Symfony Finder Component",
- "homepage": "http://symfony.com",
- "time": "2014-07-28 13:20:46"
+ "time": "2023-02-07T11:34:05+00:00"
},
{
- "name": "symfony/translation",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/Translation",
+ "name": "slevomat/coding-standard",
+ "version": "8.18.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/Translation.git",
- "reference": "ae573e45b099b1e2d332930ac626cd4270e09539"
+ "url": "https://github.com/slevomat/coding-standard.git",
+ "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Translation/zipball/ae573e45b099b1e2d332930ac626cd4270e09539",
- "reference": "ae573e45b099b1e2d332930ac626cd4270e09539",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/f3b23cb9b26301b8c3c7bb03035a1bee23974593",
+ "reference": "f3b23cb9b26301b8c3c7bb03035a1bee23974593",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpdoc-parser": "^2.1.0",
+ "squizlabs/php_codesniffer": "^3.12.2"
},
"require-dev": {
- "symfony/config": "~2.0",
- "symfony/yaml": "~2.2"
- },
- "suggest": {
- "symfony/config": "",
- "symfony/yaml": ""
- },
- "type": "library",
+ "phing/phing": "3.0.1",
+ "php-parallel-lint/php-parallel-lint": "1.4.0",
+ "phpstan/phpstan": "2.1.13",
+ "phpstan/phpstan-deprecation-rules": "2.0.2",
+ "phpstan/phpstan-phpunit": "2.0.6",
+ "phpstan/phpstan-strict-rules": "2.0.4",
+ "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.17|12.1.3"
+ },
+ "type": "phpcodesniffer-standard",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-master": "8.x-dev"
}
},
"autoload": {
- "psr-0": {
- "Symfony\\Component\\Translation\\": ""
+ "psr-4": {
+ "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
- "authors": [
+ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+ "keywords": [
+ "dev",
+ "phpcs"
+ ],
+ "support": {
+ "issues": "https://github.com/slevomat/coding-standard/issues",
+ "source": "https://github.com/slevomat/coding-standard/tree/8.18.0"
+ },
+ "funding": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
+ "url": "https://github.com/kukulich",
+ "type": "github"
},
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+ "type": "tidelift"
}
],
- "description": "Symfony Translation Component",
- "homepage": "http://symfony.com",
- "time": "2014-07-28 13:20:46"
+ "time": "2025-05-01T09:40:50+00:00"
},
{
- "name": "symfony/yaml",
- "version": "v2.5.3",
- "target-dir": "Symfony/Component/Yaml",
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.13.4",
"source": {
"type": "git",
- "url": "https://github.com/symfony/Yaml.git",
- "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f"
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/Yaml/zipball/5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f",
- "reference": "5a75366ae9ca8b4792cd0083e4ca4dff9fe96f1f",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/ad545ea9c1b7d270ce0fc9cbfb884161cd706119",
+ "reference": "ad545ea9c1b7d270ce0fc9cbfb884161cd706119",
"shasum": ""
},
"require": {
- "php": ">=5.3.3"
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
},
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "2.5-dev"
+ "dev-master": "3.x-dev"
}
},
- "autoload": {
- "psr-0": {
- "Symfony\\Component\\Yaml\\": ""
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
}
+ ],
+ "time": "2025-09-05T05:47:09+00:00"
+ },
+ {
+ "name": "theseer/tokenizer",
+ "version": "1.2.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/theseer/tokenizer.git",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": "^7.2 || ^8.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
- "MIT"
+ "BSD-3-Clause"
],
"authors": [
{
- "name": "Symfony Community",
- "homepage": "http://symfony.com/contributors"
- },
+ "name": "Arne Blankerts",
+ "email": "arne@blankerts.de",
+ "role": "Developer"
+ }
+ ],
+ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+ "support": {
+ "issues": "https://github.com/theseer/tokenizer/issues",
+ "source": "https://github.com/theseer/tokenizer/tree/1.2.3"
+ },
+ "funding": [
{
- "name": "Fabien Potencier",
- "email": "fabien@symfony.com"
+ "url": "https://github.com/theseer",
+ "type": "github"
}
],
- "description": "Symfony Yaml Component",
- "homepage": "http://symfony.com",
- "time": "2014-08-05 09:00:40"
+ "time": "2024-03-03T12:36:25+00:00"
}
],
"aliases": [],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
- "php": ">=5.3.3"
+ "php": "8.1.*|8.2.*|8.3.*|8.4.*",
+ "composer-runtime-api": "^2"
+ },
+ "platform-dev": {},
+ "platform-overrides": {
+ "php": "8.1.0"
},
- "platform-dev": []
+ "plugin-api-version": "2.6.0"
}
diff --git a/docs/extending/index.rst b/docs/extending/index.rst
new file mode 100644
index 00000000..3ad13ccf
--- /dev/null
+++ b/docs/extending/index.rst
@@ -0,0 +1,34 @@
+.. _extending:
+
+Extend the library
+==================
+
+The model exposed by this library is closed for inheritance. We did this to ensure the model is stable and does not
+change via external factors. The complexity of this project makes it hard to keep all the internal classes stable.
+The model is designed to be cached and constructed very carefully to ensure performance and memory usage are optimal.
+
+Metadata
+--------
+
+Metadata is a way to extend the model with additional information. We call this metadata, as all first class
+elements in the reflected codebase are part of the model. Extra data can be added to these elements using metadata.
+
+Elements supporting metadata are:
+
+.. phpdoc:class-list:: [?(@.interfaces contains "\phpDocumentor\Reflection\Metadata\MetaDataContainer")]
+
+ .. phpdoc:name::
+
+.. warning::
+
+ Adding metadata might break the posibilty to cache the model. Be carefull with circular references and large
+ objects. We do recommend to keep the metadata small and simple.
+
+Continue reading :doc:`Creating your first metadata `_ to learn how to create your own metadata.
+
+.. toctree::
+ :maxdepth: 1
+ :titlesonly:
+ :hidden:
+
+ meta-data
diff --git a/docs/extending/meta-data.rst b/docs/extending/meta-data.rst
new file mode 100644
index 00000000..8b3dc3c9
--- /dev/null
+++ b/docs/extending/meta-data.rst
@@ -0,0 +1,77 @@
+Metadata
+=====
+
+The model of this library is as closed as possible.
+Main reason is because consumers of the library should rely on cache.
+A mutable and flexible interface of the model would most likely break the caching.
+However after some time the users to this library started requesting for a more flexible format.
+This is why metadata was introduced.
+
+Create your first metadata
+--------------------------
+
+First step is to create your own metadata implementation.
+
+.. code:: php
+ final class Hook implements \phpDocumentor\Reflection\Metadata\Metadata
+ {
+ private string $hook;
+
+ public function __construct(string $hook)
+ {
+ $this->hook = $hook;
+ }
+
+ public function key(): string
+ {
+ return "project-metadata";
+ }
+
+ public function hook(): string
+ {
+ return $this->hook;
+ }
+ }
+
+.. note::
+ We do highly recommend to keep your metadata objects small.
+ When reflecting a large project the number of objects will grow fast.
+
+Now we have an class that can be used it is time to create a :php:class:`\phpDocumentor\Reflection\Php\ProjectFactoryStrategy`.
+Strategies are used to reflect nodes in the AST of `phpparser`_.
+
+In the example below we are adding the Hook metadata to any functions containing a function call.
+
+.. code:: php
+
+ use \phpDocumentor\Reflection\Php\Function;
+
+ final class HookStrategy implements \phpDocumentor\Reflection\Php\ProjectFactoryStrategy
+ {
+ public function matches(ContextStack $context, object $object): bool
+ {
+ return $this->context->peek() instanceof Function_ &&
+ $object instanceof \PhpParser\Node\Expr\FuncCall &&
+ ((string)$object->name) === 'hook'
+ }
+
+ public function create(ContextStack $context, object $object, StrategyContainer $strategies): void
+ {
+ $method = $context->peek();
+ $method->addMetadata(new Hook($object->args[0]->value));
+ }
+ }
+
+.. note::
+ To speed up the reflection of your project the default factory instance has a Noop strategy. This strategy will
+ ignore all statements that are not handled by any strategy. Keep this in mind when implementing your own strategies
+ especially the statements you are looking for are nested in other statements like a ``while`` loop.
+
+Finally add your new strategy to the project factory.
+
+.. code:: php
+
+ $factory = \phpDocumentor\Reflection\Php\ProjectFactory::createInstance();
+ $factory->addStrategy(new HookStrategy());
+
+.. _phpparser: https://github.com/nikic/PHP-Parser/
diff --git a/docs/getting-started.rst b/docs/getting-started.rst
new file mode 100644
index 00000000..447f37ad
--- /dev/null
+++ b/docs/getting-started.rst
@@ -0,0 +1,49 @@
+Getting started
+===============
+
+This page will give you a quick introduction to the `phpdocumentor/reflection` package and how to get started with it.
+
+Installation
+------------
+
+In order to inspect a codebase you need to tell composer to include the `phpdocumentor/reflection` package. This
+can easily be done using the following command in your command line terminal:
+
+.. code-block:: bash
+
+ composer require phpdocumentor/reflection:~6.0
+
+In order to use the library you need to include the autoloader of composer in your code.
+ This can be done by adding the following line to your code:
+
+.. code-block:: php
+
+ create('My Project', $projectFiles);
+
+When the process is ready a new object of type :php:class:`phpDocumentor\Reflection\Php\Project` will be returned that
+contains a complete hierarchy of all files with their classes, traits and interfaces (and everything in there), but also
+all namespaces and packages as a hierarchical tree.
+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.
+
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 00000000..52b3506e
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,28 @@
+Reflection
+==========
+
+Using this library it is possible to statically reflect one or more files and create an object graph representing
+your application's structure, including accompanying in-source documentation using DocBlocks.
+
+The information that this library provides is similar to what the (built-in) Reflection extension of PHP provides; there
+are however several advantages to using this library:
+
+- Due to its Static nature it does not execute procedural code in your reflected files where Dynamic Reflection does.
+- Because the none of the code is interpreted by PHP (and executed) Static Reflection uses less memory.
+- Can reflect complete files
+- Can reflect a whole project by reflecting multiple files.
+- Reflects the contents of a DocBlock instead of just mentioning there is one.
+- Is capable of analyzing code written for any PHP version (starting at 5.2) up to the lastest version, even if your installed
+ PHP version is lower than the code you are reflecting.
+
+.. note::
+ As this library focuses on reflecting the structure of the codebase, it does not provide any options to manipulate
+ the output. If you want to collect more information from the codebase you can read about :ref:`extending the library `.
+
+.. toctree::
+ :hidden:
+ :maxdepth: 2
+
+ getting-started
+ reflection-structure
+ extending/index
diff --git a/docs/reflection-structure.rst b/docs/reflection-structure.rst
new file mode 100644
index 00000000..55fb23d6
--- /dev/null
+++ b/docs/reflection-structure.rst
@@ -0,0 +1,20 @@
+Reflection structure
+====================
+
+The project created by the :php:class:`\phpDocumentor\Reflection\Php\ProjectFactory` class contains a hierarchy of objects
+that represent the structure of your codebase. This hierarchy includes:
+
+Files
+ Each file is represented by an object of type :php:class:`\phpDocumentor\Reflection\Php\File` which contains
+ information about the file such as its name, path, and contents. But also the elements that are defined in the file.
+ Files can be accessed through the :php:method:`\phpDocumentor\Reflection\Php\Project::getFiles()` method of the project object.
+ Files are a flat list of all files that were analyzed, regardless of their location in the directory structure.
+
+Namespaces
+ Namespaces are represented by objects of type :php:class:`\phpDocumentor\Reflection\Php\Namespace_`. Each namespace
+ contains a list of classes, interfaces, traits, and functions that are defined within it. Namespaces can be accessed
+ through the :php:method:`\phpDocumentor\Reflection\Php\Project::getNamespaces()` method of the project object.
+ Namespaces are hierarchical and can contain sub-namespaces.
+
+Both namespaces and files do contain the other structural elements that are defined in them, such as classes, interfaces, traits, and functions.
+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.
diff --git a/example.php b/example.php
new file mode 100644
index 00000000..d4266791
--- /dev/null
+++ b/example.php
@@ -0,0 +1,37 @@
+create('MyProject', $files);
+
+// As an example of what you can do, let's list all class names in the file 'tests/example.file.php'.
+echo 'List all classes in the example source file: ' . PHP_EOL;
+
+/** @var \phpDocumentor\Reflection\Php\Class_ $class */
+foreach ($project->getFiles()['tests/example.file.php']->getClasses() as $class) {
+ echo '- ' . $class->getFqsen() . PHP_EOL;
+}
diff --git a/phpbench.json b/phpbench.json
new file mode 100644
index 00000000..8049a251
--- /dev/null
+++ b/phpbench.json
@@ -0,0 +1,7 @@
+{
+ "bootstrap": "vendor/autoload.php",
+ "path": "tests/bench",
+ "extensions": [
+ "PhpBench\\Extensions\\XDebug\\XDebugExtension"
+ ]
+}
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 00000000..99089501
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,66 @@
+
+
+ The coding standard for this library.
+
+ src
+ tests/unit
+ */tests/unit/Types/ContextFactoryTest\.php
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ packages/guides/src/Setup/QuickStart.php
+
+
+
+
+
+
+
+
+
+ */tests/unit/*
+
+
+
+ */tests/unit/*
+ */src/phpDocumentor/Reflection/Php/*
+
+
diff --git a/phpdoc.dist.xml b/phpdoc.dist.xml
new file mode 100644
index 00000000..8c277c77
--- /dev/null
+++ b/phpdoc.dist.xml
@@ -0,0 +1,45 @@
+
+
+ Guides
+
+
+
+
+ latest
+
+
+ src/phpDocumentor
+
+
+
+ tests/**/*
+ build/**/*
+ var/**/*
+ vendor/**/*
+
+
+ php
+
+
+ template
+ template-extends
+ template-implements
+ extends
+ implements
+
+ phpDocumentor
+
+
+
+ docs
+
+
+
+
+
+
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 00000000..71dffd7c
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,22 @@
+parameters:
+ paths:
+ - src
+
+ level: max
+ ignoreErrors:
+
+ - '#Method phpDocumentor\\Reflection\\File\\LocalFile::md5\(\) should return string but returns string\|false\.#'
+ - '#Else branch is unreachable because ternary operator condition is always true\.#'
+ #
+ # all these $fqsen errors indicate the need for a decorator class around PhpParser\Node to hold the public $fqsen that Reflection is giving it)
+ #
+ # src/phpDocumentor/Reflection/NodeVisitor/ElementNameResolver.php
+ - '#Method phpDocumentor\\Reflection\\Php\\Factory\\(.*)::getFqsen\(\) should return phpDocumentor\\Reflection\\Fqsen but returns mixed\.#'
+ - '#Parameter \#1 \$fqsen of class phpDocumentor\\Reflection\\Php\\(.*) constructor expects phpDocumentor\\Reflection\\Fqsen, mixed given\.#'
+ - '#Parameter \#1 \$fqsen of method phpDocumentor\\Reflection\\Php\\File::addNamespace\(\) expects phpDocumentor\\Reflection\\Fqsen, mixed given\.#'
+ #
+ # Type hint in php-parser is incorrect.
+ - '#Cannot cast PhpParser\\Node\\Expr\|string to string.#'
+
+ - '#Parameter \#2 \$object of method phpDocumentor\\Reflection\\Php\\ProjectFactoryStrategy::matches\(\) expects object, mixed given.#'
+ - '#Method phpDocumentor\\Reflection\\Php\\ValueEvaluator\\ConstantEvaluator::evaluate\(\) should return string but returns mixed.#'
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index b99c69b5..cbc56b39 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,18 +1,26 @@
-
-
-
-
- ./tests/unit/
-
-
-
-
- src/phpDocumentor
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ ./tests/unit
+
+
+ ./tests/integration
+
+
+
+
+
+ ./src/
+
+
diff --git a/psalm-baseline.xml b/psalm-baseline.xml
new file mode 100644
index 00000000..2ec6bdbc
--- /dev/null
+++ b/psalm-baseline.xml
@@ -0,0 +1,236 @@
+
+
+
+
+ path)]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ name]]>
+ name]]>
+ name]]>
+ name]]>
+ name]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getValue() !== null]]>
+
+
+
+
+ classConstants->consts[$this->index]->getAttribute('fqsen')]]>
+
+
+
+
+
+
+
+
+ extends]]>
+
+
+ getAttribute('fqsen')]]>
+
+
+
+
+ getFqsen()]]>
+
+
+ var->name]]>
+
+
+ hooks]]>
+
+
+
+
+
+
+
+ getAttribute('fqsen')]]>
+
+
+
+
+ getAttribute('fqsen')]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ getAttribute('fqsen')]]>
+
+
+
+
+ getValue() !== null]]>
+
+
+
+
+ constant->consts[$this->index]->getAttribute('fqsen')]]>
+
+
+
+
+
+
+
+
+
+
+
+
+ getAttribute('fqsen')]]>
+
+
+
+
+ getAttribute('fqsen')]]>
+
+
+
+
+
+
+
+
+
+
+ getAttribute('fqsen') ?? new Fqsen('\\')]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ property->props[$this->index]->getAttribute('fqsen')]]>
+
+
+
+
+
+
+
+
+
+
+
+
+ getAttribute('fqsen')]]>
+
+
+
+
+
+
+
+
+
+
+
+
+ getFqsen()]]>
+ getFqsen()]]>
+ getFqsen()]]>
+ getFqsen()]]>
+ getFqsen()]]>
+ getFqsen()]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ evaluateSilently($expr)]]>
+
+
+
+
+
+
+
+ aliasesToFullyQualifiedNames($namespace)]]>
+
+
+
diff --git a/psalm.xml b/psalm.xml
new file mode 100644
index 00000000..bdac9dad
--- /dev/null
+++ b/psalm.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rector.php b/rector.php
new file mode 100644
index 00000000..08b30620
--- /dev/null
+++ b/rector.php
@@ -0,0 +1,24 @@
+paths([
+ __DIR__ . '/src',
+ __DIR__ . '/tests/unit'
+ ]);
+
+ // register a single rule
+ $rectorConfig->rule(InlineConstructorDefaultToPropertyRector::class);
+ $rectorConfig->rule(Rector\CodeQuality\Rector\Class_\CompleteDynamicPropertiesRector::class);
+ $rectorConfig->importNames();
+
+ // define sets of rules
+ $rectorConfig->sets([
+ LevelSetList::UP_TO_PHP_81,
+ ]);
+};
diff --git a/src/php-parser/Modifiers.php b/src/php-parser/Modifiers.php
new file mode 100644
index 00000000..69ac740b
--- /dev/null
+++ b/src/php-parser/Modifiers.php
@@ -0,0 +1,31 @@
+
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use Exception;
-use InvalidArgumentException;
-use phpDocumentor\Event\Dispatcher;
-use phpDocumentor\Reflection\DocBlock;
-use phpDocumentor\Reflection\DocBlock\Context;
-use phpDocumentor\Reflection\DocBlock\Location;
-use phpDocumentor\Reflection\Event\PostDocBlockExtractionEvent;
-use PhpParser\NodeAbstract;
-use Psr\Log\LogLevel;
-use PhpParser\Node\Expr;
-use PhpParser\Node\Stmt;
-use PhpParser\PrettyPrinterAbstract;
-
-/**
- * Basic reflection providing support for events and basic properties as a
- * DocBlock and names.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-abstract class BaseReflector extends ReflectionAbstract
-{
- /** @var \PhpParser\Node\Stmt */
- protected $node;
-
- /**
- * The package name that is passed on by the parent Reflector.
- *
- * May be overwritten and should be passed on to children supporting
- * packages.
- *
- * @var string
- */
- protected $default_package_name = '';
-
- /**
- * PHP AST pretty printer used to get representations of values.
- *
- * @var \PhpParser\PrettyPrinterAbstract
- */
- protected static $prettyPrinter = null;
-
- /**
- * Initializes this reflector with the correct node as produced by
- * PHP-Parser.
- *
- * @param NodeAbstract $node
- * @param Context $context
- *
- * @link http://github.com/nikic/PHP-Parser
- */
- public function __construct(NodeAbstract $node, Context $context)
- {
- $this->node = $node;
- $context->setLSEN($this->getLSEN());
- $this->context = $context;
- }
-
- /**
- * Returns the current PHP-Parser node that holds more detailed information
- * about the reflected object. e.g. position in the file and further attributes.
- * @return \PhpParser\Node\Stmt|\PhpParser\NodeAbstract
- */
- public function getNode()
- {
- return $this->node;
- }
-
- /**
- * Sets the name for the namespace.
- *
- * @param string $namespace
- *
- * @throws InvalidArgumentException if something other than a string is
- * passed.
- *
- * @return void
- */
- public function setNamespace($namespace)
- {
- if (!is_string($namespace)) {
- throw new InvalidArgumentException(
- 'Expected a string for the namespace'
- );
- }
-
- $this->context->setNamespace($namespace);
- }
-
- /**
- * Returns the parsed DocBlock.
- *
- * @return DocBlock|null
- */
- public function getDocBlock()
- {
- return $this->extractDocBlock($this->node);
- }
-
- /**
- * Extracts a parsed DocBlock from an object.
- *
- * @param object $node Any object with a "getDocComment()" method.
- *
- * @return DocBlock|null
- */
- protected function extractDocBlock($node)
- {
- $doc_block = null;
- $comment = $node->getDocComment();
- if ($comment) {
- try {
- $doc_block = new DocBlock(
- (string) $comment,
- $this->context,
- new Location($comment->getLine())
- );
- } catch (Exception $e) {
- $this->log($e->getMessage(), LogLevel::CRITICAL);
- }
- }
-
- if (class_exists('phpDocumentor\Event\Dispatcher')) {
- Dispatcher::getInstance()->dispatch(
- 'reflection.docblock-extraction.post',
- PostDocBlockExtractionEvent
- ::createInstance($this)->setDocblock($doc_block)
- );
- }
-
- return $doc_block;
- }
-
- /**
- * Returns the name for this Reflector instance.
- *
- * @return string
- */
- public function getName()
- {
- if (isset($this->node->namespacedName)) {
- return '\\'.implode('\\', $this->node->namespacedName->parts);
- }
-
- return $this->getShortName();
- }
-
- /**
- * Returns the last component of a namespaced name as a short form.
- *
- * @return string
- */
- public function getShortName()
- {
- return isset($this->node->name)
- ? $this->node->name
- : (string) $this->node;
- }
-
- /**
- * Gets the LSEN.
- *
- * Returns this element's Local Structural Element Name (LSEN). This name
- * consistents of the element's short name, along with punctuation that
- * hints at the kind of structural element. If the structural element is
- * part of a type (i.e. an interface/trait/class' property/method/constant),
- * it also contains the name of the owning type.
- *
- * @return string
- */
- public function getLSEN()
- {
- return '';
- }
-
- /**
- * Returns the namespace name for this object.
- *
- * If this object does not have a namespace then the word 'global' is
- * returned to indicate a global namespace.
- *
- * @return string
- */
- public function getNamespace()
- {
- if (!isset($this->node->namespacedName)) {
- return $this->context->getNamespace();
- }
-
- $parts = $this->node->namespacedName->parts;
- array_pop($parts);
-
- $namespace = implode('\\', $parts);
-
- return $namespace ? $namespace : 'global';
- }
-
- /**
- * Returns a listing of namespace aliases where the key represents the alias
- * and the value the Fully Qualified Namespace Name.
- *
- * @return string[]
- */
- public function getNamespaceAliases()
- {
- return $this->context->getNamespaceAliases();
- }
-
- /**
- * Sets a listing of namespace aliases.
- *
- * The keys represents the alias name and the value the
- * Fully Qualified Namespace Name (FQNN).
- *
- * @param string[] $aliases
- *
- * @return void
- */
- public function setNamespaceAliases(array $aliases)
- {
- $this->context->setNamespaceAliases($aliases);
- }
-
- /**
- * Sets the Fully Qualified Namespace Name (FQNN) for an alias.
- *
- * @param string $alias
- * @param string $fqnn
- *
- * @return void
- */
- public function setNamespaceAlias($alias, $fqnn)
- {
- $this->context->setNamespaceAlias($alias, $fqnn);
- }
-
- /**
- * Returns the line number where this object starts.
- *
- * @return int
- */
- public function getLinenumber()
- {
- return $this->node->getLine();
- }
-
- /**
- * Sets the default package name for this object.
- *
- * If the DocBlock contains a different package name then that overrides
- * this package name.
- *
- * @param string $default_package_name The name of the package as defined
- * in the PHPDoc Standard.
- *
- * @return void
- */
- public function setDefaultPackageName($default_package_name)
- {
- $this->default_package_name = $default_package_name;
- }
-
- /**
- * Returns the package name that is default for this element.
- *
- * This value may change after the DocBlock is interpreted. If that contains
- * a package tag then that tag overrides the Default package name.
- *
- * @return string
- */
- public function getDefaultPackageName()
- {
- return $this->default_package_name;
- }
-
- /**
- * Returns a simple human readable output for a value.
- *
- * @param \PhpParser\Node\Expr $value The value node as provided by
- * PHP-Parser.
- *
- * @return string
- */
- protected function getRepresentationOfValue(
- \PhpParser\Node\Expr $value = null
- ) {
- if (null === $value) {
- return '';
- }
-
- if (!self::$prettyPrinter) {
- self::$prettyPrinter = new PrettyPrinter();
- }
-
- return self::$prettyPrinter->prettyPrintExpr($value);
- }
-}
diff --git a/src/phpDocumentor/Reflection/ClassReflector.php b/src/phpDocumentor/Reflection/ClassReflector.php
deleted file mode 100644
index 1faa37eb..00000000
--- a/src/phpDocumentor/Reflection/ClassReflector.php
+++ /dev/null
@@ -1,100 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Name;
-use PhpParser\Node\Stmt\Class_;
-use PhpParser\Node\Stmt\TraitUse;
-
-/**
- * Provides static reflection for a class.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class ClassReflector extends InterfaceReflector
-{
- /** @var Class_ */
- protected $node;
-
- /** @var string[] */
- protected $traits = array();
-
- public function parseSubElements()
- {
- /** @var TraitUse $stmt */
- foreach ($this->node->stmts as $stmt) {
- if ($stmt instanceof TraitUse) {
- foreach ($stmt->traits as $trait) {
- $this->traits[] = '\\' . (string) $trait;
- }
- }
- }
-
- parent::parseSubElements();
- }
-
- /**
- * Returns whether this is an abstract class.
- *
- * @return bool
- */
- public function isAbstract()
- {
- return (bool) ($this->node->type & Class_::MODIFIER_ABSTRACT);
- }
-
- /**
- * Returns whether this class is final and thus cannot be extended.
- *
- * @return bool
- */
- public function isFinal()
- {
- return (bool) ($this->node->type & Class_::MODIFIER_FINAL);
- }
-
- /**
- * Returns a list of the names of traits used in this class.
- *
- * @return string[]
- */
- public function getTraits()
- {
- return $this->traits;
- }
-
- public function getParentClass()
- {
- return isset($this->node->extends) ? '\\'.(string) $this->node->extends : '';
- }
-
- /**
- * BC Break: used to be getParentInterfaces
- *
- * @return string[] Names of interfaces the class implements.
- */
- public function getInterfaces()
- {
- $names = array();
- if (isset($this->node->implements)) {
- /** @var Name */
- foreach ($this->node->implements as $node) {
- $names[] = '\\'.(string) $node;
- }
- }
-
- return $names;
- }
-}
diff --git a/src/phpDocumentor/Reflection/ClassReflector/ConstantReflector.php b/src/phpDocumentor/Reflection/ClassReflector/ConstantReflector.php
deleted file mode 100644
index 580a3189..00000000
--- a/src/phpDocumentor/Reflection/ClassReflector/ConstantReflector.php
+++ /dev/null
@@ -1,41 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection\ClassReflector;
-
-use phpDocumentor\Reflection\BaseReflector;
-use phpDocumentor\Reflection\ConstantReflector as BaseConstantReflector;
-use phpDocumentor\Reflection\DocBlock\Context;
-use PhpParser\Node\Stmt\ClassConst;
-use PhpParser\Node\Const_;
-
-class ConstantReflector extends BaseConstantReflector
-{
- /** @var ClassConst */
- protected $constant;
-
- /**
- * Registers the Constant Statement and Node with this reflector.
- *
- * @param ClassConst $stmt
- * @param Context $context
- * @param Const_ $node
- */
- public function __construct(
- ClassConst $stmt,
- Context $context,
- Const_ $node
- ) {
- BaseReflector::__construct($node, $context);
- $this->constant = $stmt;
- }
-}
diff --git a/src/phpDocumentor/Reflection/ClassReflector/MethodReflector.php b/src/phpDocumentor/Reflection/ClassReflector/MethodReflector.php
deleted file mode 100644
index 9fe6990c..00000000
--- a/src/phpDocumentor/Reflection/ClassReflector/MethodReflector.php
+++ /dev/null
@@ -1,78 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection\ClassReflector;
-
-use phpDocumentor\Reflection\FunctionReflector;
-use PhpParser\Node\Stmt\Class_;
-
-class MethodReflector extends FunctionReflector
-{
- /** @var \PhpParser\Node\Stmt\ClassMethod */
- protected $node;
-
- /**
- * Returns the visibility for this item.
- *
- * The returned value should match either of the following:
- *
- * * public
- * * protected
- * * private
- *
- * If a method has no visibility set in the class definition this method
- * will return 'public'.
- *
- * @return string
- */
- public function getVisibility()
- {
- if ($this->node->type & Class_::MODIFIER_PROTECTED) {
- return 'protected';
- }
- if ($this->node->type & Class_::MODIFIER_PRIVATE) {
- return 'private';
- }
-
- return 'public';
- }
-
- /**
- * Returns whether this method is static.
- *
- * @return bool
- */
- public function isAbstract()
- {
- return (bool) ($this->node->type & Class_::MODIFIER_ABSTRACT);
- }
-
- /**
- * Returns whether this method is static.
- *
- * @return bool
- */
- public function isStatic()
- {
- return (bool) ($this->node->type & Class_::MODIFIER_STATIC);
- }
-
- /**
- * Returns whether this method is final.
- *
- * @return bool
- */
- public function isFinal()
- {
- return (bool) ($this->node->type & Class_::MODIFIER_FINAL);
- }
-}
diff --git a/src/phpDocumentor/Reflection/ClassReflector/PropertyReflector.php b/src/phpDocumentor/Reflection/ClassReflector/PropertyReflector.php
deleted file mode 100644
index aba8e7be..00000000
--- a/src/phpDocumentor/Reflection/ClassReflector/PropertyReflector.php
+++ /dev/null
@@ -1,107 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection\ClassReflector;
-
-use phpDocumentor\Reflection\BaseReflector;
-use phpDocumentor\Reflection\DocBlock;
-use phpDocumentor\Reflection\DocBlock\Context;
-use PhpParser\Node\Stmt\Class_;
-use PhpParser\Node\Stmt\Property;
-use PhpParser\Node\Stmt\PropertyProperty;
-
-class PropertyReflector extends BaseReflector
-{
- /** @var Property */
- protected $property;
-
- /** @var PropertyProperty */
- protected $node;
-
- public function __construct(
- Property $property,
- Context $context,
- PropertyProperty $node
- ) {
- parent::__construct($node, $context);
- $this->property = $property;
- }
-
- public function getName()
- {
- return '$'.parent::getName();
- }
-
- /**
- * Returns the default value or null if none found.
- *
- * Please note that if the default value is null that this method returns
- * string 'null'.
- *
- * @return null|string
- */
- public function getDefault()
- {
- $result = null;
- if ($this->node->default) {
- $result = $this->getRepresentationOfValue($this->node->default);
- }
-
- return $result;
- }
-
- /**
- * Returns the visibility for this item.
- *
- * The returned value should match either of the following:
- *
- * * public
- * * protected
- * * private
- *
- * If a method has no visibility set in the class definition this method
- * will return 'public'.
- *
- * @return string
- */
- public function getVisibility()
- {
- if ($this->property->type & \PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED) {
- return 'protected';
- }
- if ($this->property->type & \PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE) {
- return 'private';
- }
-
- return 'public';
- }
-
- /**
- * Returns whether this property is static.
- *
- * @return bool
- */
- public function isStatic()
- {
- return (bool) ($this->property->type & \PhpParser\Node\Stmt\Class_::MODIFIER_STATIC);
- }
-
- /**
- * Returns the parsed DocBlock.
- *
- * @return DocBlock|null
- */
- public function getDocBlock()
- {
- return $this->extractDocBlock($this->property);
- }
-}
diff --git a/src/phpDocumentor/Reflection/ConstantReflector.php b/src/phpDocumentor/Reflection/ConstantReflector.php
deleted file mode 100644
index cd106c19..00000000
--- a/src/phpDocumentor/Reflection/ConstantReflector.php
+++ /dev/null
@@ -1,69 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use phpDocumentor\Reflection\DocBlock;
-use phpDocumentor\Reflection\DocBlock\Context;
-use PhpParser\Node\Const_;
-use PhpParser\Node\Stmt\Const_ as ConstStmt;
-
-/**
- * Provides Static Reflection for file-level constants.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class ConstantReflector extends BaseReflector
-{
- /** @var ConstStmt */
- protected $constant;
-
- /** @var Const_ */
- protected $node;
-
- /**
- * Registers the Constant Statement and Node with this reflector.
- *
- * @param ConstStmt $stmt
- * @param Const_ $node
- */
- public function __construct(
- ConstStmt $stmt,
- Context $context,
- Const_ $node
- ) {
- parent::__construct($node, $context);
- $this->constant = $stmt;
- }
-
- /**
- * Returns the value contained in this Constant.
- *
- * @return string
- */
- public function getValue()
- {
- return $this->getRepresentationOfValue($this->node->value);
- }
-
- /**
- * Returns the parsed DocBlock.
- *
- * @return DocBlock|null
- */
- public function getDocBlock()
- {
- return $this->extractDocBlock($this->constant);
- }
-}
diff --git a/src/phpDocumentor/Reflection/Event/ExportDocBlockTagEvent.php b/src/phpDocumentor/Reflection/Event/ExportDocBlockTagEvent.php
deleted file mode 100644
index f6b754fb..00000000
--- a/src/phpDocumentor/Reflection/Event/ExportDocBlockTagEvent.php
+++ /dev/null
@@ -1,55 +0,0 @@
-xml;
- }
-
- /**
- * @return Tag|null
- */
- public function getObject()
- {
- return $this->object;
- }
-
- /**
- * @param Tag $object
- *
- * @return ExportDocBlockTagEvent
- */
- public function setObject(Tag $object = null)
- {
- $this->object = $object;
-
- return $this;
- }
-
- /**
- * @param DOMNode $xml
- *
- * @return ExportDocBlockTagEvent
- */
- public function setXml(DOMNode $xml = null)
- {
- $this->xml = $xml;
-
- return $this;
- }
-}
diff --git a/src/phpDocumentor/Reflection/Event/PostDocBlockExtractionEvent.php b/src/phpDocumentor/Reflection/Event/PostDocBlockExtractionEvent.php
deleted file mode 100644
index 897918ff..00000000
--- a/src/phpDocumentor/Reflection/Event/PostDocBlockExtractionEvent.php
+++ /dev/null
@@ -1,31 +0,0 @@
-docblock = $docblock;
-
- return $this;
- }
-
- /**
- * @return DocBlock|null
- */
- public function getDocblock()
- {
- return $this->docblock;
- }
-}
diff --git a/src/phpDocumentor/Reflection/Exception.php b/src/phpDocumentor/Reflection/Exception.php
index 9d9f78ec..6ef2403a 100644
--- a/src/phpDocumentor/Reflection/Exception.php
+++ b/src/phpDocumentor/Reflection/Exception.php
@@ -1,22 +1,24 @@
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
+ * @link http://phpdoc.org
*/
+
namespace phpDocumentor\Reflection;
/**
* An exception specifically originating from the Reflection component.
*
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
+ *
+ * @api
*/
class Exception extends \Exception
{
diff --git a/src/phpDocumentor/Reflection/Exception/UnparsableFile.php b/src/phpDocumentor/Reflection/Exception/UnparsableFile.php
deleted file mode 100644
index 854de95b..00000000
--- a/src/phpDocumentor/Reflection/Exception/UnparsableFile.php
+++ /dev/null
@@ -1,25 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection\Exception;
-
-/**
- * Exception representing any situation where the file is not parsable.
- *
- * @author Mike van Riel
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class UnparsableFile extends \phpDocumentor\Reflection\Exception
-{
-}
diff --git a/src/phpDocumentor/Reflection/Exception/UnreadableFile.php b/src/phpDocumentor/Reflection/Exception/UnreadableFile.php
deleted file mode 100644
index 93d61c5c..00000000
--- a/src/phpDocumentor/Reflection/Exception/UnreadableFile.php
+++ /dev/null
@@ -1,26 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection\Exception;
-
-/**
- * Exception representing any error in the Reflection library due to a file not
- * being readable or accessible.
- *
- * @author Mike van Riel
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class UnreadableFile extends \phpDocumentor\Reflection\Exception
-{
-}
diff --git a/src/phpDocumentor/Reflection/File/LocalFile.php b/src/phpDocumentor/Reflection/File/LocalFile.php
new file mode 100644
index 00000000..7ac4b743
--- /dev/null
+++ b/src/phpDocumentor/Reflection/File/LocalFile.php
@@ -0,0 +1,70 @@
+path = $path;
+ }
+
+ /**
+ * Returns the content of the file as a string.
+ */
+ #[Override]
+ public function getContents(): string
+ {
+ return (string) file_get_contents($this->path);
+ }
+
+ /**
+ * Returns md5 hash of the file.
+ */
+ #[Override]
+ public function md5(): string
+ {
+ return md5_file($this->path);
+ }
+
+ /**
+ * Returns a relative path to the file.
+ */
+ #[Override]
+ public function path(): string
+ {
+ return $this->path;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/FileReflector.php b/src/phpDocumentor/Reflection/FileReflector.php
deleted file mode 100644
index 91ff73af..00000000
--- a/src/phpDocumentor/Reflection/FileReflector.php
+++ /dev/null
@@ -1,563 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use phpDocumentor\Event\Dispatcher;
-use phpDocumentor\Parser\Event\LogEvent;
-use phpDocumentor\Reflection\DocBlock;
-use phpDocumentor\Reflection\DocBlock\Context;
-use phpDocumentor\Reflection\DocBlock\Location;
-use phpDocumentor\Reflection\Event\PostDocBlockExtractionEvent;
-use phpDocumentor\Reflection\Exception;
-use PhpParser\Node\Stmt\ClassMethod;
-use Psr\Log\LogLevel;
-use PhpParser\Comment\Doc;
-use PhpParser\Node;
-use PhpParser\Node\Const_;
-use PhpParser\Node\Expr\FuncCall;
-use PhpParser\Node\Expr\Include_;
-use PhpParser\Node\Name;
-use PhpParser\Node\Stmt\Class_;
-use PhpParser\Node\Stmt\ClassConst;
-use PhpParser\Node\Stmt\Const_ as ConstStmt;
-use PhpParser\Node\Stmt\Function_;
-use PhpParser\Node\Stmt\InlineHTML;
-use PhpParser\Node\Stmt\Interface_;
-use PhpParser\Node\Stmt\Property;
-use PhpParser\Node\Stmt\PropertyProperty;
-use PhpParser\Node\Stmt\Trait_;
-use PhpParser\Node\Stmt\UseUse;
-use PhpParser\NodeVisitor;
-
-/**
- * Reflection class for a full file.
- *
- * @author Mike van Riel
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class FileReflector extends ReflectionAbstract implements NodeVisitor
-{
- /** @var string An MD5 hashed representation of the contents of this file */
- protected $hash;
-
- /** @var string The contents of this file. */
- protected $contents = '';
-
- /** @var IncludeReflector[] */
- protected $includes = array();
-
- /** @var ConstantReflector[] */
- protected $constants = array();
-
- /** @var ClassReflector[] */
- protected $classes = array();
-
- /** @var TraitReflector[] */
- protected $traits = array();
-
- /** @var InterfaceReflector[] */
- protected $interfaces = array();
-
- /** @var FunctionReflector[] */
- protected $functions = array();
-
- /** @var string The name of the file associated with this reflection object. */
- protected $filename = '';
-
- /** @var DocBlock */
- protected $doc_block;
-
- /** @var string The package name that should be used if none is present in the file */
- protected $default_package_name = 'Default';
-
- /** @var string[] A list of markers contained in this file. */
- protected $markers = array();
-
- /** @var string[] A list of errors during processing */
- protected $parse_markers = array();
-
- /** @var string[] A list of all marker types to search for in this file. */
- protected $marker_terms = array('TODO', 'FIXME');
-
- /** @var Context */
- protected $context;
-
- /**
- * Opens the file and retrieves its contents.
- *
- * During construction the given file is checked whether it is readable and
- * if the $validate argument is true a PHP Lint action is executed to
- * check whether the there are no parse errors.
- *
- * By default the Lint check is disabled because of the performance hit
- * introduced by this action.
- *
- * If the validation checks out, the file's contents are read, converted to
- * UTF-8 and the object is created from those contents.
- *
- * @param string $file Name of the file.
- * @param boolean $validate Whether to check the file using PHP Lint.
- * @param string $encoding The encoding of the file.
- *
- * @throws Exception\UnreadableFile If the filename is incorrect or
- * the file cannot be opened
- * @throws Exception\UnparsableFile If the file fails PHP lint checking
- * (this can only happen when $validate is set to true)
- */
- public function __construct($file, $validate = false, $encoding = 'utf-8')
- {
- if (!is_string($file) || (!is_readable($file))) {
- throw new Exception\UnreadableFile(
- 'The given file should be a string, should exist on the filesystem and should be readable'
- );
- }
-
- if ($validate) {
- exec('php -l ' . escapeshellarg($file), $output, $result);
- if ($result != 0) {
- throw new Exception\UnparsableFile(
- 'The given file could not be interpreted as it contains errors: '
- . implode(PHP_EOL, $output)
- );
- }
- }
-
- $this->filename = $file;
- $this->contents = file_get_contents($file);
- $this->context = new Context();
-
- if (strtolower($encoding) !== 'utf-8' && extension_loaded('iconv')) {
- $this->contents = iconv(
- strtolower($encoding),
- 'utf-8//IGNORE//TRANSLIT',
- $this->contents
- );
- }
-
- // filemtime($file) is sometimes between 0.00001 and 0.00005 seconds
- // faster but md5 is more accurate. It can also result in false
- // positives or false negatives after copying or checking out a codebase.
- $this->hash = md5($this->contents);
- }
-
- public function process()
- {
- // with big fluent interfaces it can happen that PHP-Parser's Traverser
- // exceeds the 100 recursions limit; we set it to 10000 to be sure.
- ini_set('xdebug.max_nesting_level', 10000);
-
- $traverser = new Traverser();
- $traverser->addVisitor($this);
- $traverser->traverse($this->contents);
-
- $this->scanForMarkers();
- }
-
- /**
- * @return ClassReflector[]
- */
- public function getClasses()
- {
- return $this->classes;
- }
-
- /**
- * @return TraitReflector[]
- */
- public function getTraits()
- {
- return $this->traits;
- }
-
- /**
- * @return ConstantReflector[]
- */
- public function getConstants()
- {
- return $this->constants;
- }
-
- /**
- * @return FunctionReflector[]
- */
- public function getFunctions()
- {
- return $this->functions;
- }
-
- /**
- * @return IncludeReflector[]
- */
- public function getIncludes()
- {
- return $this->includes;
- }
-
- /**
- * @return InterfaceReflector[]
- */
- public function getInterfaces()
- {
- return $this->interfaces;
- }
-
- public function beforeTraverse(array $nodes)
- {
- $node = null;
- $key = 0;
- foreach ($nodes as $k => $n) {
- if (!$n instanceof InlineHTML) {
- $node = $n;
- $key = $k;
- break;
- }
- }
-
- if ($node) {
- $comments = (array) $node->getAttribute('comments');
-
- // remove non-DocBlock comments
- $comments = array_values(
- array_filter(
- $comments,
- function ($comment) {
- return $comment instanceof Doc;
- }
- )
- );
-
- if (!empty($comments)) {
- try {
- $docblock = new DocBlock(
- (string) $comments[0],
- null,
- new Location($comments[0]->getLine())
- );
-
- // the first DocBlock in a file documents the file if
- // * it precedes another DocBlock or
- // * it contains a @package tag and doesn't precede a class
- // declaration or
- // * it precedes a non-documentable element (thus no include,
- // require, class, function, define, const)
- if (count($comments) > 1
- || (!$node instanceof Class_
- && !$node instanceof Interface_
- && $docblock->hasTag('package'))
- || !$this->isNodeDocumentable($node)
- ) {
- $this->doc_block = $docblock;
-
- // remove the file level DocBlock from the node's comments
- array_shift($comments);
- }
- } catch (\Exception $e) {
- $this->log($e->getMessage(), LogLevel::CRITICAL);
- }
- }
-
- // always update the comments attribute so that standard comments
- // do not stop DocBlock from being attached to an element
- $node->setAttribute('comments', $comments);
- $nodes[$key] = $node;
- }
-
- if (class_exists('phpDocumentor\Event\Dispatcher')) {
- Dispatcher::getInstance()->dispatch(
- 'reflection.docblock-extraction.post',
- PostDocBlockExtractionEvent
- ::createInstance($this)->setDocblock($this->doc_block)
- );
- }
-
- return $nodes;
- }
-
- /**
- * Checks whether the given node is recogized by phpDocumentor as a
- * documentable element.
- *
- * The following elements are recognized:
- *
- * - Trait
- * - Class
- * - Interface
- * - Class constant
- * - Class method
- * - Property
- * - Include/Require
- * - Constant, both const and define
- * - Function
- *
- * @param Node $node
- *
- * @return bool
- */
- protected function isNodeDocumentable(Node $node)
- {
- return ($node instanceof Class_)
- || ($node instanceof Interface_)
- || ($node instanceof ClassConst)
- || ($node instanceof ClassMethod)
- || ($node instanceof ConstStmt)
- || ($node instanceof Function_)
- || ($node instanceof Property)
- || ($node instanceof PropertyProperty)
- || ($node instanceof Trait_)
- || ($node instanceof Include_)
- || ($node instanceof FuncCall
- && ($node->name instanceof Name)
- && $node->name == 'define');
- }
-
- public function enterNode(Node $node)
- {
- }
-
- public function getName()
- {
- return $this->filename;
- }
-
- public function getFilename()
- {
- return $this->filename;
- }
-
- public function getHash()
- {
- return $this->hash;
- }
-
- public function getDocBlock()
- {
- return $this->doc_block;
- }
-
- public function getLineNumber()
- {
- return 0;
- }
-
- public function getDefaultPackageName()
- {
- return $this->default_package_name;
- }
-
- /**
- * Adds a marker to scan the contents of this file for.
- *
- * @param string $name The Marker term, e.g. FIXME or TODO.
- *
- * @return void
- */
- public function addMarker($name)
- {
- $this->marker_terms[] = $name;
- }
-
- /**
- * Sets a list of markers to search for.
- *
- * @param string[] $markers A list of marker terms to scan for.
- *
- * @see phpDocumentor\Reflection\FileReflector::addMarker()
- *
- * @return void
- */
- public function setMarkers(array $markers)
- {
- $this->marker_terms = array();
-
- foreach ($markers as $marker) {
- $this->addMarker($marker);
- }
- }
-
- public function getMarkers()
- {
- return $this->markers;
- }
-
- /**
- * Adds a parse error to the system
- *
- * @param LogEvent $data Contains the type,
- * message, line and code element.
- *
- * @return void
- */
- public function addParserMarker($data)
- {
- $this->parse_markers[] = array(
- $data->getType(),
- $data->getMessage(),
- $data->getLine(),
- $data->getCode()
- );
- }
-
- /**
- * Scans the file for markers and records them in the markers property.
- *
- * @see getMarkers()
- *
- * @todo this method may incur a performance penalty while the AST also
- * contains the comments. This method should be replaced by a piece of
- * code that interprets the comments in the AST.
- * This has not been done since that may be an extensive refactoring (each
- * PhpParser\Node* contains a 'comments' attribute and must thus recursively
- * be discovered)
- *
- * @return void
- */
- public function scanForMarkers()
- {
- // find all markers, get the entire file and check for marker terms.
- $marker_data = array();
- foreach (explode("\n", $this->contents) as $line_number => $line) {
- preg_match_all(
- '~//[\s]*(' . implode('|', $this->marker_terms) . ')\:?[\s]*(.*)~',
- $line,
- $matches,
- PREG_SET_ORDER
- );
- foreach ($matches as &$match) {
- $match[3] = $line_number + 1;
- }
- $marker_data = array_merge($marker_data, $matches);
- }
-
- // store marker results and remove first entry (entire match),
- // this results in an array with 2 entries:
- // marker name and content
- $this->markers = $marker_data;
- foreach ($this->markers as &$marker) {
- array_shift($marker);
- }
- }
-
- public function getParseErrors()
- {
- return $this->parse_markers;
- }
-
- public function getNamespace()
- {
- return $this->context->getNamespace();
- }
-
- public function getNamespaceAliases()
- {
- return $this->context->getNamespaceAliases();
- }
-
- public function getContents()
- {
- return $this->contents;
- }
-
- public function setDefaultPackageName($default_package_name)
- {
- $this->default_package_name = $default_package_name;
- }
-
- public function setFilename($filename)
- {
- $this->filename = $filename;
- }
-
- public function leaveNode(Node $node)
- {
- $prettyPrinter = new PrettyPrinter;
-
- switch (get_class($node)) {
- case 'PhpParser\Node\Stmt\Use_':
- /** @var \PhpParser\Node\Stmt\UseUse $use */
- foreach ($node->uses as $use) {
- $this->context->setNamespaceAlias(
- $use->alias,
- implode('\\', $use->name->parts)
- );
- }
- break;
- case 'PhpParser\Node\Stmt\Namespace_':
- $this->context->setNamespace(
- isset($node->name) && ($node->name) ? implode('\\', $node->name->parts) : ''
- );
- break;
- case 'PhpParser\Node\Stmt\Class_':
- $class = new ClassReflector($node, $this->context);
- $class->parseSubElements();
- $this->classes[] = $class;
- break;
- case 'PhpParser\Node\Stmt\Trait_':
- $trait = new TraitReflector($node, $this->context);
- $trait->parseSubElements();
- $this->traits[] = $trait;
- break;
- case 'PhpParser\Node\Stmt\Interface_':
- $interface = new InterfaceReflector($node, $this->context);
- $interface->parseSubElements();
- $this->interfaces[] = $interface;
- break;
- case 'PhpParser\Node\Stmt\Function_':
- $function = new FunctionReflector($node, $this->context);
- $this->functions[] = $function;
- break;
- case 'PhpParser\Node\Stmt\Const_':
- foreach ($node->consts as $constant) {
- $reflector = new ConstantReflector(
- $node,
- $this->context,
- $constant
- );
- $this->constants[] = $reflector;
- }
- break;
- case 'PhpParser\Node\Expr\FuncCall':
- if (($node->name instanceof Name)
- && ($node->name == 'define')
- && isset($node->args[0])
- && isset($node->args[1])
- ) {
- // transform the first argument of the define function call into a constant name
- $name = str_replace(
- array('\\\\', '"', "'"),
- array('\\', '', ''),
- trim($prettyPrinter->prettyPrintExpr($node->args[0]->value), '\'')
- );
- $nameParts = explode('\\', $name);
- $shortName = end($nameParts);
-
- $constant = new Const_($shortName, $node->args[1]->value, $node->getAttributes());
- $constant->namespacedName = new Name($name);
-
- $constant_statement = new ConstStmt(array($constant));
- $constant_statement->setAttribute('comments', array($node->getDocComment()));
- $this->constants[] = new ConstantReflector($constant_statement, $this->context, $constant);
- }
- break;
- case 'PhpParser\Node\Expr\Include_':
- $include = new IncludeReflector($node, $this->context);
- $this->includes[] = $include;
- break;
- }
- }
-
- public function afterTraverse(array $nodes)
- {
- }
-}
diff --git a/src/phpDocumentor/Reflection/FunctionReflector.php b/src/phpDocumentor/Reflection/FunctionReflector.php
deleted file mode 100644
index 95a673a7..00000000
--- a/src/phpDocumentor/Reflection/FunctionReflector.php
+++ /dev/null
@@ -1,76 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use phpDocumentor\Reflection\BaseReflector;
-use phpDocumentor\Reflection\DocBlock\Context;
-use PhpParser\Node\Param;
-use PhpParser\Node\Stmt;
-use PhpParser\Node\Stmt\Function_;
-
-/**
- * Provides Static Reflection for functions.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class FunctionReflector extends BaseReflector
-{
- /** @var \PhpParser\Node\Stmt\Function_ */
- protected $node;
-
- /** @var FunctionReflector\ArgumentReflector[] */
- protected $arguments = array();
-
- /**
- * Initializes the reflector using the function statement object of
- * PHP-Parser.
- *
- * @param \PhpParser\Node\Stmt $node Function object coming from PHP-Parser.
- * @param Context $context The context in which the node occurs.
- */
- public function __construct(\PhpParser\Node\Stmt $node, Context $context)
- {
- parent::__construct($node, $context);
-
- /** @var \PhpParser\Node\Param $param */
- foreach ($node->params as $param) {
- $reflector = new FunctionReflector\ArgumentReflector(
- $param,
- $context
- );
- $this->arguments[$reflector->getName()] = $reflector;
- }
- }
-
- /**
- * Checks whether the function returns a value by reference.
- *
- * @return bool TRUE if the return value is by reference, FALSE otherwise.
- */
- public function isByRef()
- {
- return $this->node->byRef;
- }
-
- /**
- * Returns a list of Argument objects.
- *
- * @return FunctionReflector\ArgumentReflector[]
- */
- public function getArguments()
- {
- return $this->arguments;
- }
-}
diff --git a/src/phpDocumentor/Reflection/FunctionReflector/ArgumentReflector.php b/src/phpDocumentor/Reflection/FunctionReflector/ArgumentReflector.php
deleted file mode 100644
index 8b338711..00000000
--- a/src/phpDocumentor/Reflection/FunctionReflector/ArgumentReflector.php
+++ /dev/null
@@ -1,71 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection\FunctionReflector;
-
-use phpDocumentor\Reflection\BaseReflector;
-use PhpParser\Node\Param;
-
-class ArgumentReflector extends BaseReflector
-{
- /** @var Param */
- protected $node;
-
- /**
- * Checks whether the argument is passed by reference.
- *
- * @return bool TRUE if the argument is by reference, FALSE otherwise.
- */
- public function isByRef()
- {
- return $this->node->byRef;
- }
-
- /**
- * Returns the default value or null is none is set.
- *
- * @return string|null
- */
- public function getDefault()
- {
- $result = null;
- if ($this->node->default) {
- $result = $this->getRepresentationOfValue($this->node->default);
- }
-
- return $result;
- }
-
- /**
- * Returns the typehint, or null if none is set.
- *
- * @return string|null
- */
- public function getType()
- {
- $type = (string) $this->node->type;
-
- // in case of the callable of array keyword; do not prefix with a \
- if ($type == 'callable' || $type == 'array'
- || $type == 'self' || $type == '$this'
- ) {
- return $type;
- }
-
- return $type ? '\\'.$type : '';
- }
-
- public function getName()
- {
- return '$'.parent::getName();
- }
-}
diff --git a/src/phpDocumentor/Reflection/IncludeReflector.php b/src/phpDocumentor/Reflection/IncludeReflector.php
deleted file mode 100644
index 8b2eb5ca..00000000
--- a/src/phpDocumentor/Reflection/IncludeReflector.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use Exception;
-use PhpParser\Node\Expr\Include_;
-
-class IncludeReflector extends BaseReflector
-{
- /** @var Include_ */
- protected $node;
-
- /**
- * Returns the type of this include.
- *
- * Valid types are:
- * - Include
- * - Include Once
- * - Require
- * - Require Once
- *
- * @throws Exception if the include is of an unknown type
- *
- * @return string
- */
- public function getType()
- {
- switch ($this->node->type) {
- case Include_::TYPE_INCLUDE:
- return 'Include';
- case Include_::TYPE_INCLUDE_ONCE:
- return 'Include Once';
- case Include_::TYPE_REQUIRE:
- return 'Require';
- case Include_::TYPE_REQUIRE_ONCE:
- return 'Require Once';
- default:
- throw new Exception(
- 'Unknown include type detected: '.$this->node->type
- );
- }
- }
-
- public function getShortName()
- {
- return (string) $this->node->expr->value;
- }
-}
diff --git a/src/phpDocumentor/Reflection/InterfaceReflector.php b/src/phpDocumentor/Reflection/InterfaceReflector.php
deleted file mode 100644
index e32bdc0b..00000000
--- a/src/phpDocumentor/Reflection/InterfaceReflector.php
+++ /dev/null
@@ -1,118 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Name;
-use PhpParser\Node\Stmt\Class_;
-use PhpParser\Node\Stmt\Interface_;
-
-class InterfaceReflector extends BaseReflector
-{
- /** @var Interface_|\PhpParser\Node\Stmt\Class_ */
- protected $node;
-
- /**
- * @var ClassReflector\ConstantReflector[]
- */
- protected $constants = array();
- /**
- * @var ClassReflector\PropertyReflector[]
- */
- protected $properties = array();
- /**
- * @var ClassReflector\MethodReflector[]
- */
- protected $methods = array();
-
- public function parseSubElements()
- {
- foreach ($this->node->stmts as $stmt) {
- switch (get_class($stmt)) {
- case 'PhpParser\Node\Stmt\Property':
- foreach ($stmt->props as $property) {
- $this->properties[] = new ClassReflector\PropertyReflector(
- $stmt,
- $this->context,
- $property
- );
- }
- break;
- case 'PhpParser\Node\Stmt\ClassMethod':
- $this->methods[strtolower($stmt->name)] = new ClassReflector\MethodReflector(
- $stmt,
- $this->context
- );
- break;
- case 'PhpParser\Node\Stmt\ClassConst':
- foreach ($stmt->consts as $constant) {
- $this->constants[] = new ClassReflector\ConstantReflector(
- $stmt,
- $this->context,
- $constant
- );
- }
- break;
- }
- }
- }
-
- public function getParentInterfaces()
- {
- $names = array();
- if ($this->node instanceof Interface_
- && $this->node->extends
- ) {
- /** @var Name */
- foreach ($this->node->extends as $node) {
- $names[] = '\\'.(string) $node;
- }
- }
-
- return $names;
- }
-
- /**
- * @return ClassReflector\ConstantReflector[]
- */
- public function getConstants()
- {
- return $this->constants;
- }
-
- /**
- * @return ClassReflector\PropertyReflector[]
- */
- public function getProperties()
- {
- return $this->properties;
- }
-
- /**
- * @return ClassReflector\MethodReflector[]
- */
- public function getMethods()
- {
- return $this->methods;
- }
-
- /**
- * @param string $name the method name
- * @return ClassReflector\MethodReflector|null
- */
- public function getMethod($name)
- {
- $name = strtolower($name);
-
- return isset($this->methods[$name]) ? $this->methods[$name] : null;
- }
-}
diff --git a/src/phpDocumentor/Reflection/Lexer.php b/src/phpDocumentor/Reflection/Lexer.php
deleted file mode 100644
index 09a4ff0b..00000000
--- a/src/phpDocumentor/Reflection/Lexer.php
+++ /dev/null
@@ -1,74 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Lexer as BaseLexer;
-use PhpParser\Parser;
-
-/**
- * Custom lexer for phpDocumentor.
- *
- * phpDocumentor has a custom Lexer for PHP-Parser because it needs
- * unmodified value for Scalar variables instead of an interpreted version.
- *
- * If the interpreted version was to be used then the XML interpretation would
- * fail because of special characters.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class Lexer extends BaseLexer
-{
- /**
- * Retrieves the next token and determines the associated attributes and
- * returns the token id.
- *
- * @param string $value
- * @param string[] $startAttributes
- * @param string[] $endAttributes
- *
- * @return int
- */
- public function getNextToken(
- &$value = null,
- &$startAttributes = null,
- &$endAttributes = null
- ) {
- $tokenId = parent::getNextToken($value, $startAttributes, $endAttributes);
-
- if ($this->isTokenScalar($tokenId)) {
- // store original value because the value itself will be interpreted
- // by PHP_Parser and we want the unformatted value
- $endAttributes['originalValue'] = $value;
- }
-
- return $tokenId;
- }
-
- /**
- * Returns whether the given token id is a scalar that will be interpreted
- * by PHP-Parser.
- *
- * @param int $tokenId The id to check, must match a \PhpParser_Parser::T_*
- * constant.
- *
- * @return bool
- */
- protected function isTokenScalar($tokenId)
- {
- return $tokenId == Parser::T_CONSTANT_ENCAPSED_STRING
- || $tokenId == Parser::T_LNUMBER
- || $tokenId == Parser::T_DNUMBER;
- }
-}
diff --git a/src/phpDocumentor/Reflection/Metadata/MetaDataContainer.php b/src/phpDocumentor/Reflection/Metadata/MetaDataContainer.php
new file mode 100644
index 00000000..daad021f
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Metadata/MetaDataContainer.php
@@ -0,0 +1,22 @@
+ $middleware->execute($command, $lastCallable);
+ }
+
+ return $lastCallable;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Middleware/Command.php b/src/phpDocumentor/Reflection/Middleware/Command.php
new file mode 100644
index 00000000..233f0feb
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Middleware/Command.php
@@ -0,0 +1,21 @@
+ */
+ private SplDoublyLinkedList $parts;
+
+ public function __construct()
+ {
+ $this->resetState('\\');
+ }
+
+ /**
+ * Resets the object to a known state before start processing.
+ *
+ * @inheritDoc
+ */
+ #[Override]
+ public function beforeTraverse(array $nodes)
+ {
+ $this->resetState('\\');
+
+ return null;
+ }
+
+ /**
+ * Performs a reset of the added element when needed.
+ *
+ * @inheritDoc
+ */
+ #[Override]
+ public function leaveNode(Node $node)
+ {
+ switch ($node::class) {
+ case Namespace_::class:
+ case Class_::class:
+ case Enum_::class:
+ case EnumCase::class:
+ case ClassMethod::class:
+ case Trait_::class:
+ case PropertyProperty::class:
+ case Node\PropertyItem::class:
+ case ClassConst::class:
+ case Const_::class:
+ case Interface_::class:
+ case Function_::class:
+ if (!$this->parts->isEmpty()) {
+ $this->parts->pop();
+ }
+
+ break;
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds fqsen property to a node when applicable.
+ */
+ #[Override]
+ public function enterNode(Node $node): int|null
+ {
+ switch ($node::class) {
+ case Namespace_::class:
+ if ($node->name === null) {
+ break;
+ }
+
+ $this->resetState('\\' . $node->name . '\\');
+ $this->setFqsen($node);
+ break;
+ case Class_::class:
+ case Trait_::class:
+ case Interface_::class:
+ case Enum_::class:
+ if (empty($node->name)) {
+ return NodeTraverser::DONT_TRAVERSE_CHILDREN;
+ }
+
+ $this->parts->push((string) $node->name);
+ $this->setFqsen($node);
+ break;
+ case Function_::class:
+ $this->parts->push($node->name . '()');
+ $this->setFqsen($node);
+
+ return NodeTraverser::DONT_TRAVERSE_CHILDREN;
+
+ case ClassMethod::class:
+ $this->parts->push('::' . $node->name . '()');
+ $this->setFqsen($node);
+
+ return NodeTraverser::DONT_TRAVERSE_CHILDREN;
+
+ case ClassConst::class:
+ $this->parts->push('::');
+ break;
+ case Const_::class:
+ $this->parts->push($node->name);
+ $this->setFqsen($node);
+ break;
+ case Node\PropertyItem::class:
+ case PropertyProperty::class:
+ $this->parts->push('::$' . $node->name);
+ $this->setFqsen($node);
+ break;
+ case EnumCase::class:
+ $this->parts->push('::' . $node->name);
+ $this->setFqsen($node);
+ break;
+ }
+
+ return null;
+ }
+
+ /**
+ * Resets the state of the object to an empty state.
+ */
+ private function resetState(string|null $namespace = null): void
+ {
+ $this->parts = new SplDoublyLinkedList();
+ $this->parts->push($namespace);
+ }
+
+ /**
+ * Builds the name of the current node using the parts that are pushed to the parts list.
+ */
+ private function buildName(): string
+ {
+ $name = '';
+ foreach ($this->parts as $part) {
+ $name .= $part;
+ }
+
+ return rtrim($name, '\\');
+ }
+
+ private function setFqsen(Node $node): void
+ {
+ $fqsen = new Fqsen($this->buildName());
+ $node->setAttribute('fqsen', $fqsen);
+ }
+}
diff --git a/src/phpDocumentor/Reflection/NodeVisitor/FindingVisitor.php b/src/phpDocumentor/Reflection/NodeVisitor/FindingVisitor.php
new file mode 100644
index 00000000..9bc17ab1
--- /dev/null
+++ b/src/phpDocumentor/Reflection/NodeVisitor/FindingVisitor.php
@@ -0,0 +1,17 @@
+foundNode = null;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Argument.php b/src/phpDocumentor/Reflection/Php/Argument.php
new file mode 100644
index 00000000..25fb6010
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Argument.php
@@ -0,0 +1,77 @@
+type = $type;
+ }
+
+ /**
+ * Returns the name of this argument.
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function getType(): Type|null
+ {
+ return $this->type;
+ }
+
+ public function getDefault(): string|null
+ {
+ return $this->default;
+ }
+
+ public function isByReference(): bool
+ {
+ return $this->byReference;
+ }
+
+ public function isVariadic(): bool
+ {
+ return $this->isVariadic;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/AsymmetricVisibility.php b/src/phpDocumentor/Reflection/Php/AsymmetricVisibility.php
new file mode 100644
index 00000000..1877f398
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/AsymmetricVisibility.php
@@ -0,0 +1,26 @@
+readVisibility;
+ }
+
+ public function getWriteVisibility(): Visibility
+ {
+ return $this->writeVisibility;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Attribute.php b/src/phpDocumentor/Reflection/Php/Attribute.php
new file mode 100644
index 00000000..19071316
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Attribute.php
@@ -0,0 +1,36 @@
+fqsen;
+ }
+
+ /** @return CallArgument[] */
+ public function getArguments(): array
+ {
+ return $this->arguments;
+ }
+
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/AttributeContainer.php b/src/phpDocumentor/Reflection/Php/AttributeContainer.php
new file mode 100644
index 00000000..7d501bfb
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/AttributeContainer.php
@@ -0,0 +1,13 @@
+value;
+ }
+
+ public function getName(): string|null
+ {
+ return $this->name;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Class_.php b/src/phpDocumentor/Reflection/Php/Class_.php
new file mode 100644
index 00000000..654b050e
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Class_.php
@@ -0,0 +1,237 @@
+location = $location;
+ $this->endLocation = $endLocation;
+ }
+
+ /**
+ * Returns true when this class is final. Otherwise returns false.
+ */
+ public function isFinal(): bool
+ {
+ return $this->final;
+ }
+
+ /**
+ * Returns true when this class is abstract. Otherwise returns false.
+ */
+ public function isAbstract(): bool
+ {
+ return $this->abstract;
+ }
+
+ /**
+ * Returns true when this class is read-only. Otherwise returns false.
+ */
+ public function isReadOnly(): bool
+ {
+ return $this->readOnly;
+ }
+
+ /**
+ * Returns the superclass this class is extending if available.
+ */
+ public function getParent(): Fqsen|null
+ {
+ return $this->parent;
+ }
+
+ /**
+ * Returns the interfaces this class is implementing.
+ *
+ * @return Fqsen[]
+ */
+ public function getInterfaces(): array
+ {
+ return $this->implements;
+ }
+
+ /**
+ * Add a interface Fqsen this class is implementing.
+ */
+ public function addInterface(Fqsen $interface): void
+ {
+ $this->implements[(string) $interface] = $interface;
+ }
+
+ /**
+ * Returns the constants of this class.
+ *
+ * @return Constant[]
+ */
+ public function getConstants(): array
+ {
+ return $this->constants;
+ }
+
+ /**
+ * Add Constant to this class.
+ */
+ public function addConstant(Constant $constant): void
+ {
+ $this->constants[(string) $constant->getFqsen()] = $constant;
+ }
+
+ /**
+ * Returns the methods of this class.
+ *
+ * @return Method[]
+ */
+ public function getMethods(): array
+ {
+ return $this->methods;
+ }
+
+ /**
+ * Add a method to this class.
+ */
+ public function addMethod(Method $method): void
+ {
+ $this->methods[(string) $method->getFqsen()] = $method;
+ }
+
+ /**
+ * Returns the properties of this class.
+ *
+ * @return Property[]
+ */
+ public function getProperties(): array
+ {
+ return $this->properties;
+ }
+
+ /**
+ * Add a property to this class.
+ */
+ public function addProperty(Property $property): void
+ {
+ $this->properties[(string) $property->getFqsen()] = $property;
+ }
+
+ /**
+ * Returns the traits used by this class.
+ *
+ * @return Fqsen[]
+ */
+ public function getUsedTraits(): array
+ {
+ return $this->usedTraits;
+ }
+
+ /**
+ * Add trait fqsen used by this class.
+ */
+ public function addUsedTrait(Fqsen $fqsen): void
+ {
+ $this->usedTraits[(string) $fqsen] = $fqsen;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Constant.php b/src/phpDocumentor/Reflection/Php/Constant.php
new file mode 100644
index 00000000..3d9bf700
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Constant.php
@@ -0,0 +1,109 @@
+location = $location ?: new Location(-1);
+ $this->endLocation = $endLocation ?: new Location(-1);
+ $this->visibility = $visibility ?: new Visibility(Visibility::PUBLIC_);
+ }
+
+ /**
+ * Returns the value of this constant.
+ */
+ public function getValue(): string|null
+ {
+ return $this->value;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ /**
+ * Returns DocBlock of this constant if available.
+ */
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+
+ public function getVisibility(): Visibility
+ {
+ return $this->visibility;
+ }
+
+ public function isFinal(): bool
+ {
+ return $this->final;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/EnumCase.php b/src/phpDocumentor/Reflection/Php/EnumCase.php
new file mode 100644
index 00000000..1d30fc36
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/EnumCase.php
@@ -0,0 +1,78 @@
+location = $location;
+ $this->endLocation = $endLocation;
+ }
+
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+
+ public function getValue(): string|null
+ {
+ return $this->value;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Enum_.php b/src/phpDocumentor/Reflection/Php/Enum_.php
new file mode 100644
index 00000000..511cdc16
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Enum_.php
@@ -0,0 +1,187 @@
+ */
+ private array $implements = [];
+
+ /** @var Constant[] References to constants defined in this enum. */
+ private array $constants = [];
+
+ /** @var array */
+ private array $methods = [];
+
+ /** @var array */
+ private array $usedTraits = [];
+
+ public function __construct(
+ /** @var Fqsen Full Qualified Structural Element Name */
+ private readonly Fqsen $fqsen,
+ private readonly Type|null $backedType,
+ private readonly DocBlock|null $docBlock = null,
+ Location|null $location = null,
+ Location|null $endLocation = null,
+ ) {
+ if ($location === null) {
+ $location = new Location(-1);
+ }
+
+ if ($endLocation === null) {
+ $endLocation = new Location(-1);
+ }
+
+ $this->location = $location;
+ $this->endLocation = $endLocation;
+ }
+
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+
+ public function addCase(EnumCase $case): void
+ {
+ $this->cases[(string) $case->getFqsen()] = $case;
+ }
+
+ /** @return EnumCase[] */
+ public function getCases(): array
+ {
+ return $this->cases;
+ }
+
+ /**
+ * Returns the interfaces this enum is implementing.
+ *
+ * @return Fqsen[]
+ */
+ public function getInterfaces(): array
+ {
+ return $this->implements;
+ }
+
+ /**
+ * Add an interface Fqsen this enum is implementing.
+ */
+ public function addInterface(Fqsen $interface): void
+ {
+ $this->implements[(string) $interface] = $interface;
+ }
+
+ /**
+ * Returns the constants of this enum.
+ *
+ * @return Constant[]
+ */
+ public function getConstants(): array
+ {
+ return $this->constants;
+ }
+
+ /**
+ * Add Constant to this enum.
+ */
+ public function addConstant(Constant $constant): void
+ {
+ $this->constants[(string) $constant->getFqsen()] = $constant;
+ }
+
+ /**
+ * Returns the methods of this enum.
+ *
+ * @return Method[]
+ */
+ public function getMethods(): array
+ {
+ return $this->methods;
+ }
+
+ /**
+ * Add a method to this enum.
+ */
+ public function addMethod(Method $method): void
+ {
+ $this->methods[(string) $method->getFqsen()] = $method;
+ }
+
+ /**
+ * Returns the traits used by this enum.
+ *
+ * @return Fqsen[]
+ */
+ public function getUsedTraits(): array
+ {
+ return $this->usedTraits;
+ }
+
+ /**
+ * Add trait fqsen used by this enum.
+ */
+ public function addUsedTrait(Fqsen $fqsen): void
+ {
+ $this->usedTraits[(string) $fqsen] = $fqsen;
+ }
+
+ public function getBackedType(): Type|null
+ {
+ return $this->backedType;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/AbstractFactory.php b/src/phpDocumentor/Reflection/Php/Factory/AbstractFactory.php
new file mode 100644
index 00000000..95b1a7bd
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/AbstractFactory.php
@@ -0,0 +1,84 @@
+ $reducers */
+ public function __construct(
+ protected readonly DocBlockFactoryInterface $docBlockFactory,
+ protected readonly iterable $reducers = [],
+ ) {
+ }
+
+ /**
+ * Returns true when the strategy is able to handle the object.
+ *
+ * @param object $object object to check.
+ */
+ #[Override]
+ abstract public function matches(ContextStack $context, object $object): bool;
+
+ #[Override]
+ public function create(ContextStack $context, object $object, StrategyContainer $strategies): void
+ {
+ if (!$this->matches($context, $object)) {
+ throw new InvalidArgumentException(
+ sprintf(
+ '%s cannot handle objects with the type %s',
+ self::class,
+ get_debug_type($object),
+ ),
+ );
+ }
+
+ $element = $this->doCreate($context, $object, $strategies);
+ foreach ($this->reducers as $reducer) {
+ $element = $reducer->reduce($context, $object, $strategies, $element);
+ }
+ }
+
+ /**
+ * Creates an Element out of the given object.
+ *
+ * Since an object might contain other objects that need to be converted the $factory is passed so it can be
+ * used to create nested Elements.
+ *
+ * @param NodeAbstract|object $object object to convert to an Element
+ */
+ abstract protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): object|null;
+
+ protected function createDocBlock(Doc|null $docBlock = null, Context|null $context = null): DocBlock|null
+ {
+ if ($docBlock === null) {
+ return null;
+ }
+
+ return $this->docBlockFactory->create($docBlock->getText(), $context);
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/ClassConstant.php b/src/phpDocumentor/Reflection/Php/Factory/ClassConstant.php
new file mode 100644
index 00000000..9c594109
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/ClassConstant.php
@@ -0,0 +1,123 @@
+ $reducers */
+ public function __construct(
+ DocBlockFactoryInterface $blockFactory,
+ private readonly PrettyPrinter $valueConverter,
+ iterable $reducers = [],
+ ) {
+ parent::__construct($blockFactory, $reducers);
+ }
+
+ #[Override]
+ public function matches(ContextStack $context, object $object): bool
+ {
+ return $object instanceof ClassConst;
+ }
+
+ /**
+ * Creates an Constant out of the given object.
+ *
+ * Since an object might contain other objects that need to be converted the $factory is passed so it can be
+ * used to create nested Elements.
+ *
+ * @param ContextStack $context of the created object
+ * @param ClassConst $object object to convert to an Element
+ * @param StrategyContainer $strategies used to convert nested objects.
+ */
+ #[Override]
+ protected function doCreate(
+ ContextStack $context,
+ object $object,
+ StrategyContainer $strategies,
+ ): object|null {
+ $constantContainer = $context->peek();
+ Assert::isInstanceOfAny(
+ $constantContainer,
+ [
+ Class_::class,
+ Enum_::class,
+ Interface_::class,
+ Trait_::class,
+ ],
+ );
+
+ $constants = new ClassConstantIterator($object);
+
+ foreach ($constants as $const) {
+ $constant = new ConstantElement(
+ $const->getFqsen(),
+ $this->createDocBlock($const->getDocComment(), $context->getTypeContext()),
+ $const->getValue() !== null ? $this->valueConverter->prettyPrintExpr($const->getValue()) : null,
+ new Location($const->getLine()),
+ new Location($const->getEndLine()),
+ $this->buildVisibility($const),
+ $const->isFinal(),
+ );
+
+ foreach ($this->reducers as $reducer) {
+ $constant = $reducer->reduce($context, $const, $strategies, $constant);
+ }
+
+ if ($constant === null) {
+ continue;
+ }
+
+ $constantContainer->addConstant($constant);
+ }
+
+ return null;
+ }
+
+ /**
+ * Converts the visibility of the constant to a valid Visibility object.
+ */
+ private function buildVisibility(ClassConstantIterator $node): Visibility
+ {
+ if ($node->isPrivate()) {
+ return new Visibility(Visibility::PRIVATE_);
+ }
+
+ if ($node->isProtected()) {
+ return new Visibility(Visibility::PROTECTED_);
+ }
+
+ return new Visibility(Visibility::PUBLIC_);
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/ClassConstantIterator.php b/src/phpDocumentor/Reflection/Php/Factory/ClassConstantIterator.php
new file mode 100644
index 00000000..93e1f23c
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/ClassConstantIterator.php
@@ -0,0 +1,160 @@
+
+ */
+final class ClassConstantIterator implements Iterator
+{
+ /** @var int index of the current ClassConst to use */
+ private int $index = 0;
+
+ /**
+ * Initializes the class with source data.
+ */
+ public function __construct(private readonly ClassConst $classConstants)
+ {
+ }
+
+ /**
+ * Gets line the node started in.
+ *
+ * @return int Line
+ */
+ public function getLine(): int
+ {
+ return $this->classConstants->getLine();
+ }
+
+ /**
+ * Gets line the node ended in.
+ *
+ * @return int Line
+ */
+ public function getEndLine(): int
+ {
+ return $this->classConstants->getEndLine();
+ }
+
+ /**
+ * Returns the name of the current constant.
+ */
+ public function getName(): string
+ {
+ return (string) $this->classConstants->consts[$this->index]->name;
+ }
+
+ /**
+ * Returns the fqsen of the current constant.
+ */
+ public function getFqsen(): Fqsen
+ {
+ return $this->classConstants->consts[$this->index]->getAttribute('fqsen');
+ }
+
+ /**
+ * returns true when the current property is public.
+ */
+ public function isPublic(): bool
+ {
+ return $this->classConstants->isPublic();
+ }
+
+ /**
+ * returns true when the current property is protected.
+ */
+ public function isProtected(): bool
+ {
+ return $this->classConstants->isProtected();
+ }
+
+ /**
+ * returns true when the current property is private.
+ */
+ public function isPrivate(): bool
+ {
+ return $this->classConstants->isPrivate();
+ }
+
+ /**
+ * Gets the doc comment of the node.
+ *
+ * The doc comment has to be the last comment associated with the node.
+ */
+ public function getDocComment(): Doc|null
+ {
+ $docComment = $this->classConstants->consts[$this->index]->getDocComment();
+ if ($docComment === null) {
+ $docComment = $this->classConstants->getDocComment();
+ }
+
+ return $docComment;
+ }
+
+ public function getValue(): Expr
+ {
+ return $this->classConstants->consts[$this->index]->value;
+ }
+
+ public function isFinal(): bool
+ {
+ return $this->classConstants->isFinal();
+ }
+
+ /** @link http://php.net/manual/en/iterator.current.php */
+ #[Override]
+ public function current(): self
+ {
+ return $this;
+ }
+
+ /** @link http://php.net/manual/en/iterator.next.php */
+ #[Override]
+ public function next(): void
+ {
+ ++$this->index;
+ }
+
+ /** @link http://php.net/manual/en/iterator.key.php */
+ #[Override]
+ public function key(): int|null
+ {
+ return $this->index;
+ }
+
+ /** @link http://php.net/manual/en/iterator.valid.php */
+ #[Override]
+ public function valid(): bool
+ {
+ return isset($this->classConstants->consts[$this->index]);
+ }
+
+ /** @link http://php.net/manual/en/iterator.rewind.php */
+ #[Override]
+ public function rewind(): void
+ {
+ $this->index = 0;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Class_.php b/src/phpDocumentor/Reflection/Php/Factory/Class_.php
new file mode 100644
index 00000000..9c0f825e
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Class_.php
@@ -0,0 +1,80 @@
+createDocBlock($object->getDocComment(), $context->getTypeContext());
+
+ $classElement = new ClassElement(
+ $object->getAttribute('fqsen'),
+ $docBlock,
+ isset($object->extends) ? new Fqsen('\\' . $object->extends) : null,
+ $object->isAbstract(),
+ $object->isFinal(),
+ new Location($object->getLine()),
+ new Location($object->getEndLine()),
+ $object->isReadonly(),
+ );
+
+ foreach ($object->implements as $interfaceClassName) {
+ $classElement->addInterface(
+ new Fqsen('\\' . $interfaceClassName->toString()),
+ );
+ }
+
+ $file = $context->peek();
+ assert($file instanceof FileElement);
+ $file->addClass($classElement);
+
+ foreach ($object->stmts as $stmt) {
+ $thisContext = $context->push($classElement);
+ $strategy = $strategies->findMatching($thisContext, $stmt);
+ $strategy->create($thisContext, $stmt, $strategies);
+ }
+
+ return $classElement;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/ConstructorPromotion.php b/src/phpDocumentor/Reflection/Php/Factory/ConstructorPromotion.php
new file mode 100644
index 00000000..ef8754e8
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/ConstructorPromotion.php
@@ -0,0 +1,102 @@
+ $reducers */
+ public function __construct(
+ private readonly ProjectFactoryStrategy $methodStrategy,
+ DocBlockFactoryInterface $docBlockFactory,
+ private readonly PrettyPrinter $valueConverter,
+ iterable $reducers = [],
+ ) {
+ parent::__construct($docBlockFactory, $reducers);
+ }
+
+ #[Override]
+ public function matches(ContextStack $context, object $object): bool
+ {
+ try {
+ return $context->peek() instanceof ClassElement &&
+ $object instanceof ClassMethod &&
+ (string) ($object->name) === '__construct';
+ } catch (OutOfBoundsException) {
+ return false;
+ }
+ }
+
+ /** @param ClassMethod $object */
+ #[Override]
+ protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): object|null
+ {
+ $this->methodStrategy->create($context, $object, $strategies);
+
+ foreach ($object->params as $param) {
+ if ($param->flags === 0) {
+ continue;
+ }
+
+ $this->promoteParameterToProperty($context, $strategies, $param);
+ }
+
+ return $context->peek();
+ }
+
+ private function promoteParameterToProperty(ContextStack $context, StrategyContainer $strategies, Param $param): void
+ {
+ $methodContainer = $context->peek();
+ Assert::isInstanceOf($methodContainer, ClassElement::class);
+ Assert::isInstanceOf($param->var, Variable::class);
+
+ $property = PropertyBuilder::create(
+ $this->valueConverter,
+ $this->docBlockFactory,
+ $strategies,
+ $this->reducers,
+ )->fqsen(new Fqsen($methodContainer->getFqsen() . '::$' . (string) $param->var->name))
+ ->visibility($param)
+ ->type($param->type)
+ ->docblock($param->getDocComment())
+ ->default($param->default)
+ ->readOnly($this->readOnly($param->flags))
+ ->static(false)
+ ->startLocation(new Location($param->getLine(), $param->getStartFilePos()))
+ ->endLocation(new Location($param->getEndLine(), $param->getEndFilePos()))
+ ->hooks($param->hooks ?? [])
+ ->build($context);
+
+ foreach ($this->reducers as $reducer) {
+ $property = $reducer->reduce($context, $param, $strategies, $property);
+ }
+
+ if ($property === null) {
+ return;
+ }
+
+ $methodContainer->addProperty($property);
+ }
+
+ private function readOnly(int $flags): bool
+ {
+ return (bool) ($flags & Modifiers::READONLY) === true;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/ContextStack.php b/src/phpDocumentor/Reflection/Php/Factory/ContextStack.php
new file mode 100644
index 00000000..1d5878d4
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/ContextStack.php
@@ -0,0 +1,87 @@
+elements = $elements;
+
+ return $self;
+ }
+
+ public function push(Element|FileElement|PropertyHook $element): self
+ {
+ $elements = $this->elements;
+ $elements[] = $element;
+
+ return self::createFromSelf($this->project, $this->typeContext, $elements);
+ }
+
+ public function withTypeContext(TypeContext $typeContext): ContextStack
+ {
+ return self::createFromSelf($this->project, $typeContext, $this->elements);
+ }
+
+ public function getTypeContext(): TypeContext|null
+ {
+ return $this->typeContext;
+ }
+
+ public function getProject(): Project
+ {
+ return $this->project;
+ }
+
+ public function peek(): Element|FileElement|PropertyHook
+ {
+ $element = end($this->elements);
+ if ($element === false) {
+ throw new OutOfBoundsException('Stack is empty');
+ }
+
+ return $element;
+ }
+
+ /**
+ * Returns the first element of type.
+ *
+ * Will reverse search the stack for an element matching $type. Will return null when the element type is not
+ * in the current stack.
+ *
+ * @param class-string $type
+ */
+ public function search(string $type): Element|FileElement|PropertyHook|null
+ {
+ $reverseElements = array_reverse($this->elements);
+ foreach ($reverseElements as $element) {
+ if ($element instanceof $type) {
+ return $element;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Define.php b/src/phpDocumentor/Reflection/Php/Factory/Define.php
new file mode 100644
index 00000000..28a4fe36
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Define.php
@@ -0,0 +1,153 @@
+expr;
+ if (!$expression instanceof FuncCall) {
+ return false;
+ }
+
+ if (!$expression->name instanceof Name) {
+ return false;
+ }
+
+ return (string) $expression->name === 'define';
+ }
+
+ /**
+ * Creates an Constant out of the given object.
+ *
+ * Since an object might contain other objects that need to be converted the $factory is passed so it can be
+ * used to create nested Elements.
+ *
+ * @param Expression $object object to convert to an Element
+ * @param StrategyContainer $strategies used to convert nested objects.
+ */
+ #[Override]
+ protected function doCreate(
+ ContextStack $context,
+ object $object,
+ StrategyContainer $strategies,
+ ): object|null {
+ $expression = $object->expr;
+ assert($expression instanceof FuncCall);
+
+ [$name, $value] = $expression->args;
+
+ //We cannot calculate the name of a variadic consuming define.
+ if ($name instanceof VariadicPlaceholder || $value instanceof VariadicPlaceholder) {
+ return null;
+ }
+
+ $file = $context->search(FileElement::class);
+ assert($file instanceof FileElement);
+
+ $fqsen = $this->determineFqsen($name, $context);
+ if ($fqsen === null) {
+ return null;
+ }
+
+ $constant = new ConstantElement(
+ $fqsen,
+ $this->createDocBlock($object->getDocComment(), $context->getTypeContext()),
+ $this->determineValue($value),
+ new Location($object->getLine()),
+ new Location($object->getEndLine()),
+ );
+
+ $file->addConstant($constant);
+
+ return $constant;
+ }
+
+ private function determineValue(Arg|null $value): string|null
+ {
+ if ($value === null) {
+ return null;
+ }
+
+ return $this->valueConverter->prettyPrintExpr($value->value);
+ }
+
+ private function determineFqsen(Arg $name, ContextStack $context): Fqsen|null
+ {
+ return $this->fqsenFromExpression($name->value, $context);
+ }
+
+ private function fqsenFromExpression(Expr $nameString, ContextStack $context): Fqsen|null
+ {
+ try {
+ return $this->fqsenFromString($this->constantEvaluator->evaluate($nameString, $context));
+ } catch (ConstExprEvaluationException) {
+ //Ignore any errors as we cannot evaluate all expressions
+ return null;
+ }
+ }
+
+ private function fqsenFromString(string $nameString): Fqsen
+ {
+ if (str_starts_with($nameString, '\\') === false) {
+ return new Fqsen(sprintf('\\%s', $nameString));
+ }
+
+ return new Fqsen(sprintf('%s', $nameString));
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/EnumCase.php b/src/phpDocumentor/Reflection/Php/Factory/EnumCase.php
new file mode 100644
index 00000000..14338d94
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/EnumCase.php
@@ -0,0 +1,56 @@
+ $reducers */
+ public function __construct(
+ DocBlockFactoryInterface $docBlockFactory,
+ private readonly PrettyPrinter $prettyPrinter,
+ iterable $reducers = [],
+ ) {
+ parent::__construct($docBlockFactory, $reducers);
+ }
+
+ #[Override]
+ public function matches(ContextStack $context, object $object): bool
+ {
+ return $object instanceof EnumCaseNode;
+ }
+
+ /** @param EnumCaseNode $object */
+ #[Override]
+ protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): object|null
+ {
+ $docBlock = $this->createDocBlock($object->getDocComment(), $context->getTypeContext());
+ $enum = $context->peek();
+ assert($enum instanceof EnumElement);
+
+ $case = new EnumCaseElement(
+ $object->getAttribute('fqsen'),
+ $docBlock,
+ new Location($object->getLine()),
+ new Location($object->getEndLine()),
+ $object->expr !== null ? $this->prettyPrinter->prettyPrintExpr($object->expr) : null,
+ );
+
+ $enum->addCase($case);
+
+ return $case;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Enum_.php b/src/phpDocumentor/Reflection/Php/Factory/Enum_.php
new file mode 100644
index 00000000..4a8d7b46
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Enum_.php
@@ -0,0 +1,65 @@
+createDocBlock($object->getDocComment(), $context->getTypeContext());
+
+ $enum = new \phpDocumentor\Reflection\Php\Enum_(
+ $object->getAttribute('fqsen'),
+ (new Type())->fromPhpParser($object->scalarType),
+ $docBlock,
+ new Location($object->getLine()),
+ new Location($object->getEndLine()),
+ );
+
+ foreach ($object->implements as $interfaceClassName) {
+ $enum->addInterface(
+ new Fqsen('\\' . $interfaceClassName->toString()),
+ );
+ }
+
+ $file = $context->peek();
+ assert($file instanceof FileElement);
+ $file->addEnum($enum);
+
+ foreach ($object->stmts as $stmt) {
+ $thisContext = $context->push($enum);
+ $strategy = $strategies->findMatching($thisContext, $stmt);
+ $strategy->create($thisContext, $stmt, $strategies);
+ }
+
+ return $enum;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/File.php b/src/phpDocumentor/Reflection/Php/Factory/File.php
new file mode 100644
index 00000000..46fba96d
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/File.php
@@ -0,0 +1,194 @@
+ $this->createFile($command);
+
+ $this->middlewareChain = ChainFactory::createExecutionChain($middleware, $lastCallable);
+ }
+
+ #[Override]
+ public function matches(ContextStack $context, object $object): bool
+ {
+ return $object instanceof FileSystemFile;
+ }
+
+ /**
+ * Creates an File out of the given object.
+ *
+ * Since an object might contain other objects that need to be converted the $factory is passed so it can be
+ * used to create nested Elements.
+ *
+ * @param ContextStack $context used to convert nested objects.
+ * @param FileSystemFile $object path to the file to convert to an File object.
+ * @param StrategyContainer $strategies used to convert nested objects.
+ */
+ #[Override]
+ protected function doCreate(ContextStack $context, object $object, StrategyContainer $strategies): object|null
+ {
+ $command = new CreateCommand($context, $object, $strategies);
+ $middlewareChain = $this->middlewareChain;
+
+ $file = $middlewareChain($command);
+ if ($file === null) {
+ return null;
+ }
+
+ $context->getProject()->addFile($file);
+
+ return $file;
+ }
+
+ private function createFile(CreateCommand $command): FileElement
+ {
+ $file = $command->getFile();
+ $code = $file->getContents();
+ $nodes = $this->nodesFactory->create($code);
+
+ $docBlock = $this->createFileDocBlock(null, $nodes);
+
+ $result = new FileElement(
+ $file->md5(),
+ $file->path(),
+ $code,
+ $docBlock,
+ );
+
+ $this->createElements($command->getContext()->push($result), $nodes, $command->getStrategies());
+
+ return $result;
+ }
+
+ /** @param Node[] $nodes */
+ private function createElements(
+ ContextStack $contextStack,
+ array $nodes,
+ StrategyContainer $strategies,
+ ): void {
+ foreach ($nodes as $node) {
+ $strategy = $strategies->findMatching($contextStack, $node);
+ $strategy->create($contextStack, $node, $strategies);
+ }
+ }
+
+ /** @param Node[] $nodes */
+ protected function createFileDocBlock(
+ Context|null $context = null,
+ array $nodes = [],
+ ): DocBlockInstance|null {
+ $node = null;
+ $comments = [];
+ foreach ($nodes as $n) {
+ if (!in_array($n::class, self::SKIPPED_NODE_TYPES)) {
+ $node = $n;
+ break;
+ }
+
+ $comments = array_merge($comments, $n->getComments());
+ }
+
+ if (!$node instanceof Node) {
+ return null;
+ }
+
+ $comments = array_merge($comments, $node->getComments());
+ if (empty($comments)) {
+ return null;
+ }
+
+ $found = 0;
+ $firstDocBlock = null;
+ foreach ($comments as $comment) {
+ if (!$comment instanceof Doc) {
+ continue;
+ }
+
+ // If current node cannot have a docblock return the first comment as docblock for the file.
+ if (
+ !(
+ $node instanceof ConstantNode ||
+ $node instanceof ClassNode ||
+ $node instanceof FunctionNode ||
+ $node instanceof InterfaceNode ||
+ $node instanceof TraitNode
+ )
+ ) {
+ return $this->createDocBlock($comment, $context);
+ }
+
+ ++$found;
+ if ($firstDocBlock === null) {
+ $firstDocBlock = $comment;
+ } elseif ($found > 2) {
+ break;
+ }
+ }
+
+ if ($found === 2) {
+ return $this->createDocBlock($firstDocBlock, $context);
+ }
+
+ return null;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/File/CreateCommand.php b/src/phpDocumentor/Reflection/Php/Factory/File/CreateCommand.php
new file mode 100644
index 00000000..10ef2e3f
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/File/CreateCommand.php
@@ -0,0 +1,51 @@
+strategies;
+ }
+
+ public function getFile(): File
+ {
+ return $this->file;
+ }
+
+ public function getContext(): ContextStack
+ {
+ return $this->context;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Function_.php b/src/phpDocumentor/Reflection/Php/Factory/Function_.php
new file mode 100644
index 00000000..fc771552
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Function_.php
@@ -0,0 +1,73 @@
+peek() instanceof FileElement;
+ }
+
+ /**
+ * Creates a FunctionDescriptor out of the given object including its child elements.
+ *
+ * @param ContextStack $context of the created object
+ * @param FunctionNode $object
+ */
+ #[Override]
+ protected function doCreate(
+ ContextStack $context,
+ object $object,
+ StrategyContainer $strategies,
+ ): object|null {
+ $file = $context->peek();
+ Assert::isInstanceOf($file, FileElement::class);
+
+ $function = new FunctionDescriptor(
+ $object->getAttribute('fqsen'),
+ $this->createDocBlock($object->getDocComment(), $context->getTypeContext()),
+ new Location($object->getLine()),
+ new Location($object->getEndLine()),
+ (new Type())->fromPhpParser($object->getReturnType()),
+ $object->byRef ?: false,
+ );
+
+ $file->addFunction($function);
+
+ $thisContext = $context->push($function);
+ foreach ($object->stmts as $stmt) {
+ $strategy = $strategies->findMatching($thisContext, $stmt);
+ $strategy->create($thisContext, $stmt, $strategies);
+ }
+
+ return $function;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/GlobalConstant.php b/src/phpDocumentor/Reflection/Php/Factory/GlobalConstant.php
new file mode 100644
index 00000000..fc1e04ac
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/GlobalConstant.php
@@ -0,0 +1,82 @@
+peek();
+ Assert::isInstanceOf($file, FileElement::class);
+
+ foreach ($constants as $const) {
+ $file->addConstant(
+ new ConstantElement(
+ $const->getFqsen(),
+ $this->createDocBlock($const->getDocComment(), $context->getTypeContext()),
+ $const->getValue() !== null ? $this->valueConverter->prettyPrintExpr($const->getValue()) : null,
+ new Location($const->getLine()),
+ new Location($const->getEndLine()),
+ ),
+ );
+ }
+
+ return null;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/GlobalConstantIterator.php b/src/phpDocumentor/Reflection/Php/Factory/GlobalConstantIterator.php
new file mode 100644
index 00000000..ccbf11c8
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/GlobalConstantIterator.php
@@ -0,0 +1,126 @@
+ */
+final class GlobalConstantIterator implements Iterator
+{
+ /** @var int index of the current constant to use */
+ private int $index = 0;
+
+ /**
+ * Initializes the class with source data.
+ */
+ public function __construct(private readonly Const_ $constant)
+ {
+ }
+
+ /**
+ * Gets line the node started in.
+ *
+ * @return int Line
+ */
+ public function getLine(): int
+ {
+ return $this->constant->getLine();
+ }
+
+ /**
+ * Gets line the node ended in.
+ *
+ * @return int Line
+ */
+ public function getEndLine(): int
+ {
+ return $this->constant->getEndLine();
+ }
+
+ /**
+ * Returns the name of the current constant.
+ */
+ public function getName(): string
+ {
+ return (string) $this->constant->consts[$this->index]->name;
+ }
+
+ /**
+ * Returns the fqsen of the current constant.
+ */
+ public function getFqsen(): Fqsen
+ {
+ return $this->constant->consts[$this->index]->getAttribute('fqsen');
+ }
+
+ /**
+ * Gets the doc comment of the node.
+ *
+ * The doc comment has to be the last comment associated with the node.
+ */
+ public function getDocComment(): Doc|null
+ {
+ $docComment = $this->constant->consts[$this->index]->getDocComment();
+ if ($docComment === null) {
+ $docComment = $this->constant->getDocComment();
+ }
+
+ return $docComment;
+ }
+
+ public function getValue(): Expr
+ {
+ return $this->constant->consts[$this->index]->value;
+ }
+
+ /** @link http://php.net/manual/en/iterator.current.php */
+ #[Override]
+ public function current(): self
+ {
+ return $this;
+ }
+
+ /** @link http://php.net/manual/en/iterator.next.php */
+ #[Override]
+ public function next(): void
+ {
+ ++$this->index;
+ }
+
+ /** @link http://php.net/manual/en/iterator.key.php */
+ #[Override]
+ public function key(): int|null
+ {
+ return $this->index;
+ }
+
+ /** @link http://php.net/manual/en/iterator.valid.php */
+ #[Override]
+ public function valid(): bool
+ {
+ return isset($this->constant->consts[$this->index]);
+ }
+
+ /** @link http://php.net/manual/en/iterator.rewind.php */
+ #[Override]
+ public function rewind(): void
+ {
+ $this->index = 0;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/IfStatement.php b/src/phpDocumentor/Reflection/Php/Factory/IfStatement.php
new file mode 100644
index 00000000..1ce43476
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/IfStatement.php
@@ -0,0 +1,43 @@
+stmts as $stmt) {
+ $strategies->findMatching($context, $stmt)->create($context, $stmt, $strategies);
+ }
+
+ foreach ($object->elseifs as $elseIf) {
+ foreach ($elseIf->stmts as $stmt) {
+ $strategies->findMatching($context, $stmt)->create($context, $stmt, $strategies);
+ }
+ }
+
+ if (!($object->else instanceof Else_)) {
+ return;
+ }
+
+ foreach ($object->else->stmts as $stmt) {
+ $strategies->findMatching($context, $stmt)->create($context, $stmt, $strategies);
+ }
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Interface_.php b/src/phpDocumentor/Reflection/Php/Factory/Interface_.php
new file mode 100644
index 00000000..bc4fbc0a
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Interface_.php
@@ -0,0 +1,78 @@
+createDocBlock($object->getDocComment(), $context->getTypeContext());
+ $parents = [];
+ foreach ($object->extends as $extend) {
+ $parents['\\' . (string) $extend] = new Fqsen('\\' . (string) $extend);
+ }
+
+ $interface = new InterfaceElement(
+ $object->getAttribute('fqsen'),
+ $parents,
+ $docBlock,
+ new Location($object->getLine()),
+ new Location($object->getEndLine()),
+ );
+ $file = $context->peek();
+ Assert::isInstanceOf($file, FileElement::class);
+ $file->addInterface($interface);
+
+ foreach ($object->stmts as $stmt) {
+ $thisContext = $context->push($interface);
+ $strategy = $strategies->findMatching($thisContext, $stmt);
+ $strategy->create($thisContext, $stmt, $strategies);
+ }
+
+ return $interface;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Method.php b/src/phpDocumentor/Reflection/Php/Factory/Method.php
new file mode 100644
index 00000000..2ffe0a8b
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Method.php
@@ -0,0 +1,106 @@
+peek();
+ Assert::isInstanceOfAny(
+ $methodContainer,
+ [
+ Class_::class,
+ Interface_::class,
+ Trait_::class,
+ Enum_::class,
+ ],
+ );
+
+ $method = new MethodDescriptor(
+ $object->getAttribute('fqsen'),
+ $this->buildVisibility($object),
+ $this->createDocBlock($object->getDocComment(), $context->getTypeContext()),
+ $object->isAbstract(),
+ $object->isStatic(),
+ $object->isFinal(),
+ new Location($object->getLine(), $object->getStartFilePos()),
+ new Location($object->getEndLine(), $object->getEndFilePos()),
+ (new Type())->fromPhpParser($object->getReturnType()),
+ $object->byRef ?: false,
+ );
+ $methodContainer->addMethod($method);
+
+ if (!is_array($object->stmts)) {
+ return $method;
+ }
+
+ $thisContext = $context->push($method);
+ foreach ($object->stmts as $stmt) {
+ $strategy = $strategies->findMatching($thisContext, $stmt);
+ $strategy->create($thisContext, $stmt, $strategies);
+ }
+
+ return $method;
+ }
+
+ /**
+ * Converts the visibility of the method to a valid Visibility object.
+ */
+ private function buildVisibility(ClassMethod $node): Visibility
+ {
+ if ($node->isPrivate()) {
+ return new Visibility(Visibility::PRIVATE_);
+ }
+
+ if ($node->isProtected()) {
+ return new Visibility(Visibility::PROTECTED_);
+ }
+
+ return new Visibility(Visibility::PUBLIC_);
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Namespace_.php b/src/phpDocumentor/Reflection/Php/Factory/Namespace_.php
new file mode 100644
index 00000000..788b5b42
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Namespace_.php
@@ -0,0 +1,51 @@
+matches($context, $object)) {
+ throw new InvalidArgumentException(
+ sprintf(
+ '%s cannot handle objects with the type %s',
+ self::class,
+ get_debug_type($object),
+ ),
+ );
+ }
+
+ $file = $context->peek();
+ Assert::isInstanceOf($file, FileElement::class);
+ $file->addNamespace($object->getAttribute('fqsen') ?? new Fqsen('\\'));
+ $typeContext = (new NamespaceNodeToContext())($object);
+ foreach ($object->stmts as $stmt) {
+ $strategy = $strategies->findMatching($context, $stmt);
+ $strategy->create($context->withTypeContext($typeContext), $stmt, $strategies);
+ }
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Noop.php b/src/phpDocumentor/Reflection/Php/Factory/Noop.php
new file mode 100644
index 00000000..c59d2cb6
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Noop.php
@@ -0,0 +1,23 @@
+ $reducers */
+ public function __construct(
+ DocBlockFactoryInterface $docBlockFactory,
+ private readonly PrettyPrinter $valueConverter,
+ iterable $reducers = [],
+ ) {
+ parent::__construct($docBlockFactory, $reducers);
+ }
+
+ #[Override]
+ public function matches(ContextStack $context, object $object): bool
+ {
+ return $object instanceof PropertyNode;
+ }
+
+ /**
+ * Creates an PropertyDescriptor out of the given object.
+ *
+ * Since an object might contain other objects that need to be converted the $factory is passed so it can be
+ * used to create nested Elements.
+ *
+ * @param ContextStack $context used to convert nested objects.
+ * @param PropertyNode $object
+ */
+ #[Override]
+ protected function doCreate(
+ ContextStack $context,
+ object $object,
+ StrategyContainer $strategies,
+ ): object|null {
+ $propertyContainer = $context->peek();
+ Assert::isInstanceOfAny(
+ $propertyContainer,
+ [
+ Class_::class,
+ Trait_::class,
+ ],
+ );
+
+ $iterator = new PropertyIterator($object);
+ foreach ($iterator as $stmt) {
+ $property = PropertyBuilder::create(
+ $this->valueConverter,
+ $this->docBlockFactory,
+ $strategies,
+ $this->reducers,
+ )
+ ->fqsen($stmt->getFqsen())
+ ->visibility($stmt)
+ ->type($stmt->getType())
+ ->docblock($stmt->getDocComment())
+ ->default($iterator->getDefault())
+ ->static($stmt->isStatic())
+ ->startLocation(new Location($stmt->getLine()))
+ ->endLocation(new Location($stmt->getEndLine()))
+ ->readOnly($stmt->isReadonly())
+ ->hooks($stmt->getHooks())
+ ->build($context);
+
+ foreach ($this->reducers as $reducer) {
+ $property = $reducer->reduce($context, $object, $strategies, $property);
+ }
+
+ if ($property === null) {
+ continue;
+ }
+
+ $propertyContainer->addProperty($property);
+ }
+
+ return null;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/PropertyBuilder.php b/src/phpDocumentor/Reflection/Php/Factory/PropertyBuilder.php
new file mode 100644
index 00000000..db1a2776
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/PropertyBuilder.php
@@ -0,0 +1,344 @@
+ $reducers */
+ private function __construct(
+ private PrettyPrinter $valueConverter,
+ private DocBlockFactoryInterface $docBlockFactory,
+ private StrategyContainer $strategies,
+ private iterable $reducers,
+ ) {
+ $this->visibility = new Visibility(Visibility::PUBLIC_);
+ }
+
+ /** @param iterable $reducers */
+ public static function create(
+ PrettyPrinter $valueConverter,
+ DocBlockFactoryInterface $docBlockFactory,
+ StrategyContainer $strategies,
+ iterable $reducers,
+ ): self {
+ return new self($valueConverter, $docBlockFactory, $strategies, $reducers);
+ }
+
+ public function fqsen(Fqsen $fqsen): self
+ {
+ $this->fqsen = $fqsen;
+
+ return $this;
+ }
+
+ public function visibility(Param|PropertyIterator $node): self
+ {
+ $this->visibility = $this->buildVisibility($node);
+
+ return $this;
+ }
+
+ public function type(Identifier|Name|ComplexType|null $type): self
+ {
+ $this->type = $type;
+
+ return $this;
+ }
+
+ public function readOnly(bool $readOnly): self
+ {
+ $this->readOnly = $readOnly;
+
+ return $this;
+ }
+
+ public function docblock(Doc|null $docblock): self
+ {
+ $this->docblock = $docblock;
+
+ return $this;
+ }
+
+ public function default(Expr|null $default): self
+ {
+ $this->default = $default;
+
+ return $this;
+ }
+
+ public function static(bool $static): self
+ {
+ $this->static = $static;
+
+ return $this;
+ }
+
+ public function startLocation(Location $startLocation): self
+ {
+ $this->startLocation = $startLocation;
+
+ return $this;
+ }
+
+ public function endLocation(Location $endLocation): self
+ {
+ $this->endLocation = $endLocation;
+
+ return $this;
+ }
+
+ /** @param PropertyHookNode[] $hooks */
+ public function hooks(array $hooks): self
+ {
+ $this->hooks = $hooks;
+
+ return $this;
+ }
+
+ public function build(ContextStack $context): PropertyElement
+ {
+ $hooks = array_filter(array_map(
+ fn (PropertyHookNode $hook) => $this->buildHook($hook, $context, $this->visibility),
+ $this->hooks,
+ ));
+
+ // Check if this is a virtual property by examining all hooks
+ $isVirtual = $this->isVirtualProperty($this->hooks, $this->fqsen->getName());
+
+ return new PropertyElement(
+ $this->fqsen,
+ $this->visibility,
+ $this->docblock !== null ? $this->docBlockFactory->create($this->docblock->getText(), $context->getTypeContext()) : null,
+ $this->default !== null ? $this->valueConverter->prettyPrintExpr($this->default) : null,
+ $this->static,
+ $this->startLocation,
+ $this->endLocation,
+ (new Type())->fromPhpParser($this->type),
+ $this->readOnly,
+ $hooks,
+ $isVirtual,
+ );
+ }
+
+ /**
+ * Returns true when current property has asymmetric accessors.
+ *
+ * This method will always return false when your phpparser version is < 5.2
+ */
+ private function isAsymmetric(Param|PropertyIterator $node): bool
+ {
+ if (method_exists($node, 'isPrivateSet') === false) {
+ return false;
+ }
+
+ return $node->isPublicSet() || $node->isProtectedSet() || $node->isPrivateSet();
+ }
+
+ private function buildVisibility(Param|PropertyIterator $node): Visibility
+ {
+ if ($this->isAsymmetric($node) === false) {
+ return $this->buildReadVisibility($node);
+ }
+
+ $readVisibility = $this->buildReadVisibility($node);
+ $writeVisibility = $this->buildWriteVisibility($node);
+
+ if ((string) $writeVisibility === (string) $readVisibility) {
+ return $readVisibility;
+ }
+
+ return new AsymmetricVisibility(
+ $readVisibility,
+ $writeVisibility,
+ );
+ }
+
+ private function buildReadVisibility(Param|PropertyIterator $node): Visibility
+ {
+ if ($node instanceof Param && method_exists($node, 'isPublic') === false) {
+ return $this->buildVisibilityFromFlags($node->flags);
+ }
+
+ if ($node->isPrivate()) {
+ return new Visibility(Visibility::PRIVATE_);
+ }
+
+ if ($node->isProtected()) {
+ return new Visibility(Visibility::PROTECTED_);
+ }
+
+ return new Visibility(Visibility::PUBLIC_);
+ }
+
+ private function buildVisibilityFromFlags(int $flags): Visibility
+ {
+ if ((bool) ($flags & Modifiers::PRIVATE) === true) {
+ return new Visibility(Visibility::PRIVATE_);
+ }
+
+ if ((bool) ($flags & Modifiers::PROTECTED) === true) {
+ return new Visibility(Visibility::PROTECTED_);
+ }
+
+ return new Visibility(Visibility::PUBLIC_);
+ }
+
+ private function buildWriteVisibility(Param|PropertyIterator $node): Visibility
+ {
+ if ($node->isPrivateSet()) {
+ return new Visibility(Visibility::PRIVATE_);
+ }
+
+ if ($node->isProtectedSet()) {
+ return new Visibility(Visibility::PROTECTED_);
+ }
+
+ return new Visibility(Visibility::PUBLIC_);
+ }
+
+ private function buildHook(PropertyHookNode $hook, ContextStack $context, Visibility $propertyVisibility): PropertyHook|null
+ {
+ $doc = $hook->getDocComment();
+
+ $result = new PropertyHook(
+ $hook->name->toString(),
+ $this->buildHookVisibility($hook->name->toString(), $propertyVisibility),
+ $doc !== null ? $this->docBlockFactory->create($doc->getText(), $context->getTypeContext()) : null,
+ $hook->isFinal(),
+ new Location($hook->getStartLine()),
+ new Location($hook->getEndLine()),
+ );
+
+ foreach ($this->reducers as $reducer) {
+ $result = $reducer->reduce($context, $hook, $this->strategies, $result);
+ }
+
+ if ($result === null) {
+ return $result;
+ }
+
+ $thisContext = $context->push($result);
+ foreach ($hook->getStmts() ?? [] as $stmt) {
+ $strategy = $this->strategies->findMatching($thisContext, $stmt);
+ $strategy->create($thisContext, $stmt, $this->strategies);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Detects if a property is virtual by checking if any of its hooks reference the property itself.
+ *
+ * A virtual property is one where no defined hook references the property itself.
+ * For example, in the 'get' hook, it doesn't use $this->propertyName.
+ *
+ * @param PropertyHookNode[] $hooks The property hooks to check
+ * @param string $propertyName The name of the property
+ *
+ * @return bool True if the property is virtual, false otherwise
+ */
+ private function isVirtualProperty(array $hooks, string $propertyName): bool
+ {
+ if (empty($hooks)) {
+ return false;
+ }
+
+ foreach ($hooks as $hook) {
+ $stmts = $hook->getStmts();
+
+ if ($stmts === null || count($stmts) === 0) {
+ continue;
+ }
+
+ $finder = new FindingVisitor(
+ static function (Node $node) use ($propertyName) {
+ // Check if the node is a property fetch that references the property
+ return $node instanceof PropertyFetch && $node->name instanceof Identifier &&
+ $node->name->toString() === $propertyName &&
+ $node->var instanceof Variable &&
+ $node->var->name === 'this';
+ },
+ );
+
+ $traverser = new NodeTraverser($finder);
+ $traverser->traverse($stmts);
+
+ if ($finder->getFoundNode() !== null) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Builds the hook visibility based on the hook name and property visibility.
+ *
+ * @param string $hookName The name of the hook ('get' or 'set')
+ * @param Visibility $propertyVisibility The visibility of the property
+ *
+ * @return Visibility The appropriate visibility for the hook
+ */
+ private function buildHookVisibility(string $hookName, Visibility $propertyVisibility): Visibility
+ {
+ if ($propertyVisibility instanceof AsymmetricVisibility === false) {
+ return $propertyVisibility;
+ }
+
+ return match ($hookName) {
+ 'get' => $propertyVisibility->getReadVisibility(),
+ 'set' => $propertyVisibility->getWriteVisibility(),
+ default => $propertyVisibility,
+ };
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/PropertyIterator.php b/src/phpDocumentor/Reflection/Php/Factory/PropertyIterator.php
new file mode 100644
index 00000000..ea0c5cac
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/PropertyIterator.php
@@ -0,0 +1,251 @@
+
+ */
+final class PropertyIterator implements Iterator
+{
+ /** @var int index of the current propertyProperty to use */
+ private int $index = 0;
+
+ /**
+ * Instantiates this iterator with the propertyNode to iterate.
+ */
+ public function __construct(private readonly PropertyNode $property)
+ {
+ }
+
+ /**
+ * returns true when the current property is public.
+ */
+ public function isPublic(): bool
+ {
+ return $this->property->isPublic();
+ }
+
+ /**
+ * Returns asymmetric accessor value for current property.
+ *
+ * This method will return the same value as {@see self::isPublic()} when your phpparser version is < 5.2
+ */
+ public function isPublicSet(): bool
+ {
+ if ($this->isAsymmetric() === false) {
+ return $this->isPublic();
+ }
+
+ return $this->property->isPublic();
+ }
+
+ /**
+ * returns true when the current property is protected.
+ */
+ public function isProtected(): bool
+ {
+ return $this->property->isProtected();
+ }
+
+ /**
+ * Returns asymetric accessor value for current property.
+ *
+ * This method will return the same value as {@see self::isProtected()} when your phpparser version is < 5.2
+ */
+ public function isProtectedSet(): bool
+ {
+ if ($this->isAsymmetric() === false) {
+ return $this->isProtected();
+ }
+
+ return $this->property->isProtectedSet();
+ }
+
+ /**
+ * returns true when the current property is private.
+ */
+ public function isPrivate(): bool
+ {
+ return $this->property->isPrivate();
+ }
+
+ /**
+ * Returns asymetric accessor value for current property.
+ *
+ * This method will return the same value as {@see self::isPrivate()} when your phpparser version is < 5.2
+ */
+ public function isPrivateSet(): bool
+ {
+ if ($this->isAsymmetric() === false) {
+ return $this->isPrivate();
+ }
+
+ return $this->property->isPrivateSet();
+ }
+
+ /**
+ * Returns true when current property has asymetric accessors.
+ *
+ * This method will always return false when your phpparser version is < 5.2
+ */
+ public function isAsymmetric(): bool
+ {
+ if (method_exists($this->property, 'isPrivateSet') === false) {
+ return false;
+ }
+
+ return $this->property->isPublicSet() || $this->property->isProtectedSet() || $this->property->isPrivateSet();
+ }
+
+ /**
+ * returns true when the current property is static.
+ */
+ public function isStatic(): bool
+ {
+ return $this->property->isStatic();
+ }
+
+ /**
+ * returns true when the current property is readonly.
+ */
+ public function isReadOnly(): bool
+ {
+ return $this->property->isReadOnly();
+ }
+
+ /**
+ * Gets line the node started in.
+ */
+ public function getLine(): int
+ {
+ return $this->property->getLine();
+ }
+
+ /**
+ * Gets line the node started in.
+ */
+ public function getEndLine(): int
+ {
+ return $this->property->getEndLine();
+ }
+
+ /**
+ * Gets the type of the property.
+ */
+ public function getType(): Identifier|Name|ComplexType|null
+ {
+ return $this->property->type;
+ }
+
+ /**
+ * Gets the doc comment of the node.
+ *
+ * The doc comment has to be the last comment associated with the node.
+ */
+ public function getDocComment(): Doc|null
+ {
+ $docComment = $this->property->props[$this->index]->getDocComment();
+ if ($docComment === null) {
+ $docComment = $this->property->getDocComment();
+ }
+
+ return $docComment;
+ }
+
+ /**
+ * returns the name of the current property.
+ */
+ public function getName(): string
+ {
+ return (string) $this->property->props[$this->index]->name;
+ }
+
+ /**
+ * returns the default value of the current property.
+ */
+ public function getDefault(): Expr|null
+ {
+ return $this->property->props[$this->index]->default;
+ }
+
+ /**
+ * Returns the fqsen of the current property.
+ */
+ public function getFqsen(): Fqsen
+ {
+ return $this->property->props[$this->index]->getAttribute('fqsen');
+ }
+
+ /** @return PropertyHook[] */
+ public function getHooks(): array
+ {
+ if (property_exists($this->property, 'hooks') === false) {
+ return [];
+ }
+
+ return $this->property->hooks;
+ }
+
+ /** @link http://php.net/manual/en/iterator.current.php */
+ #[Override]
+ public function current(): self
+ {
+ return $this;
+ }
+
+ /** @link http://php.net/manual/en/iterator.next.php */
+ #[Override]
+ public function next(): void
+ {
+ ++$this->index;
+ }
+
+ /** @link http://php.net/manual/en/iterator.key.php */
+ #[Override]
+ public function key(): int|null
+ {
+ return $this->index;
+ }
+
+ /** @link http://php.net/manual/en/iterator.valid.php */
+ #[Override]
+ public function valid(): bool
+ {
+ return isset($this->property->props[$this->index]);
+ }
+
+ /** @link http://php.net/manual/en/iterator.rewind.php */
+ #[Override]
+ public function rewind(): void
+ {
+ $this->index = 0;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Reducer/Attribute.php b/src/phpDocumentor/Reflection/Php/Factory/Reducer/Attribute.php
new file mode 100644
index 00000000..ed448610
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Reducer/Attribute.php
@@ -0,0 +1,73 @@
+printer = new Standard();
+ }
+
+ #[Override]
+ public function reduce(
+ ContextStack $context,
+ object $object,
+ StrategyContainer $strategies,
+ object|null $carry,
+ ): object|null {
+ if ($carry === null) {
+ return null;
+ }
+
+ if (property_exists($object, 'attrGroups') === false || isset($object->attrGroups) === false) {
+ return $carry;
+ }
+
+ if ($carry instanceof AttributeContainer === false) {
+ throw new InvalidArgumentException(sprintf('Attribute can not be added on %s', $carry::class));
+ }
+
+ foreach ($object->attrGroups as $attrGroup) {
+ assert($attrGroup instanceof AttributeGroup);
+ foreach ($attrGroup->attrs as $attr) {
+ $carry->addAttribute(
+ new \phpDocumentor\Reflection\Php\Attribute(
+ new Fqsen('\\' . $attr->name->toString()),
+ array_map($this->buildCallArgument(...), $attr->args),
+ ),
+ );
+ }
+ }
+
+ return $carry;
+ }
+
+ private function buildCallArgument(Arg $arg): CallArgument
+ {
+ return new CallArgument(
+ $this->printer->prettyPrintExpr($arg->value),
+ $arg->name?->toString(),
+ );
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Reducer/Parameter.php b/src/phpDocumentor/Reflection/Php/Factory/Reducer/Parameter.php
new file mode 100644
index 00000000..074defc1
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Reducer/Parameter.php
@@ -0,0 +1,59 @@
+getParams() as $param) {
+ Assert::isInstanceOf($param->var, Variable::class);
+
+ $carry->addArgument(
+ new ArgumentDescriptor(
+ is_string($param->var->name) ? $param->var->name : $this->valueConverter->prettyPrintExpr($param->var->name),
+ (new Type())->fromPhpParser($param->type),
+ $param->default !== null ? $this->valueConverter->prettyPrintExpr($param->default) : null,
+ $param->byRef,
+ $param->variadic,
+ ),
+ );
+ }
+
+ return $carry;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Reducer/Reducer.php b/src/phpDocumentor/Reflection/Php/Factory/Reducer/Reducer.php
new file mode 100644
index 00000000..a1e10a10
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Reducer/Reducer.php
@@ -0,0 +1,25 @@
+matches($context, $object) === false) {
+ throw new InvalidArgumentException('Does not match expected node');
+ }
+
+ $class = $context->peek();
+
+ if (
+ $class instanceof Class_ === false
+ && $class instanceof Trait_ === false
+ && $class instanceof Enum_ === false
+ ) {
+ throw new InvalidArgumentException('Traits can only be used in classes, enums or other traits');
+ }
+
+ foreach ($object->traits as $trait) {
+ $class->addUsedTrait(new Fqsen($trait->toCodeString()));
+ }
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Trait_.php b/src/phpDocumentor/Reflection/Php/Factory/Trait_.php
new file mode 100644
index 00000000..41245877
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Trait_.php
@@ -0,0 +1,63 @@
+getAttribute('fqsen'),
+ $this->createDocBlock($object->getDocComment(), $context->getTypeContext()),
+ new Location($object->getLine()),
+ new Location($object->getEndLine()),
+ );
+
+ $file = $context->peek();
+ Assert::isInstanceOf($file, FileElement::class);
+ $file->addTrait($trait);
+
+ foreach ($object->stmts as $stmt) {
+ $thisContext = $context->push($trait);
+ $strategy = $strategies->findMatching($thisContext, $stmt);
+ $strategy->create($thisContext, $stmt, $strategies);
+ }
+
+ return $trait;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Factory/Type.php b/src/phpDocumentor/Reflection/Php/Factory/Type.php
new file mode 100644
index 00000000..160f02c6
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Factory/Type.php
@@ -0,0 +1,83 @@
+resolve($this->convertPhpParserTypeToString($type), $context);
+ }
+
+ private function convertPhpParserTypeToString(NodeAbstract|string $type): string
+ {
+ if (is_string($type)) {
+ return $type;
+ }
+
+ if ($type instanceof Identifier) {
+ return $type->toString();
+ }
+
+ if ($type instanceof Name) {
+ return $type->toString();
+ }
+
+ if ($type instanceof NullableType) {
+ return '?' . $this->convertPhpParserTypeToString($type->type);
+ }
+
+ if ($type instanceof UnionType) {
+ $typesAsStrings = array_map(
+ fn ($typeObject): string => $this->convertPhpParserTypeToString($typeObject),
+ $type->types,
+ );
+
+ return implode('|', $typesAsStrings);
+ }
+
+ if ($type instanceof IntersectionType) {
+ $typesAsStrings = array_map(
+ fn ($typeObject): string => $this->convertPhpParserTypeToString($typeObject),
+ $type->types,
+ );
+
+ return implode('&', $typesAsStrings);
+ }
+
+ throw new InvalidArgumentException(sprintf('Unsupported complex type %s', $type::class));
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/File.php b/src/phpDocumentor/Reflection/Php/File.php
new file mode 100644
index 00000000..bd9cc2ae
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/File.php
@@ -0,0 +1,244 @@
+name = basename($path);
+ }
+
+ /**
+ * Returns the hash of the contents for this file.
+ */
+ public function getHash(): string
+ {
+ return $this->hash;
+ }
+
+ /**
+ * Retrieves the contents of this file.
+ */
+ public function getSource(): string
+ {
+ return $this->source;
+ }
+
+ /**
+ * Returns the namespace fqsens that have been defined in this file.
+ *
+ * @return Fqsen[]
+ */
+ public function getNamespaces(): array
+ {
+ return $this->namespaces;
+ }
+
+ /**
+ * Add namespace to file
+ */
+ public function addNamespace(Fqsen $fqsen): void
+ {
+ $this->namespaces[(string) $fqsen] = $fqsen;
+ }
+
+ /**
+ * Returns a list of all includes that have been declared in this file.
+ *
+ * @return string[]
+ */
+ public function getIncludes(): array
+ {
+ return $this->includes;
+ }
+
+ public function addInclude(string $include): void
+ {
+ $this->includes[$include] = $include;
+ }
+
+ /**
+ * Returns a list of constant descriptors contained in this file.
+ *
+ * @return Constant[]
+ */
+ public function getConstants(): array
+ {
+ return $this->constants;
+ }
+
+ /**
+ * Add constant to this file.
+ */
+ public function addConstant(Constant $constant): void
+ {
+ $this->constants[(string) $constant->getFqsen()] = $constant;
+ }
+
+ /**
+ * Returns a list of function descriptors contained in this file.
+ *
+ * @return Function_[]
+ */
+ public function getFunctions(): array
+ {
+ return $this->functions;
+ }
+
+ /**
+ * Add function to this file.
+ */
+ public function addFunction(Function_ $function): void
+ {
+ $this->functions[(string) $function->getFqsen()] = $function;
+ }
+
+ /**
+ * Returns a list of class descriptors contained in this file.
+ *
+ * @return Class_[]
+ */
+ public function getClasses(): array
+ {
+ return $this->classes;
+ }
+
+ /**
+ * Add Class to this file.
+ */
+ public function addClass(Class_ $class): void
+ {
+ $this->classes[(string) $class->getFqsen()] = $class;
+ }
+
+ /**
+ * Returns a list of interface descriptors contained in this file.
+ *
+ * @return Interface_[]
+ */
+ public function getInterfaces(): array
+ {
+ return $this->interfaces;
+ }
+
+ /**
+ * Add interface to this file.
+ */
+ public function addInterface(Interface_ $interface): void
+ {
+ $this->interfaces[(string) $interface->getFqsen()] = $interface;
+ }
+
+ /**
+ * Returns a list of trait descriptors contained in this file.
+ *
+ * @return Trait_[]
+ */
+ public function getTraits(): array
+ {
+ return $this->traits;
+ }
+
+ /**
+ * Add trait to this file.
+ */
+ public function addTrait(Trait_ $trait): void
+ {
+ $this->traits[(string) $trait->getFqsen()] = $trait;
+ }
+
+ public function addEnum(Enum_ $enum): void
+ {
+ $this->enums[(string) $enum->getFqsen()] = $enum;
+ }
+
+ /**
+ * Returns a list of enum descriptors contained in this file.
+ *
+ * @return Enum_[]
+ */
+ public function getEnums(): array
+ {
+ return $this->enums;
+ }
+
+ /**
+ * Returns the file path relative to the project's root.
+ */
+ public function getPath(): string
+ {
+ return $this->path;
+ }
+
+ /**
+ * Returns the DocBlock of the element if available
+ */
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ /**
+ * Returns the full name of this file
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Function_.php b/src/phpDocumentor/Reflection/Php/Function_.php
new file mode 100644
index 00000000..5b5034ea
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Function_.php
@@ -0,0 +1,138 @@
+location = $location;
+ $this->endLocation = $endLocation;
+ $this->returnType = $returnType;
+ }
+
+ /**
+ * Returns the arguments of this function.
+ *
+ * @return Argument[]
+ */
+ public function getArguments(): array
+ {
+ return $this->arguments;
+ }
+
+ /**
+ * Add an argument to the function.
+ */
+ public function addArgument(Argument $argument): void
+ {
+ $this->arguments[] = $argument;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ /**
+ * Returns the DocBlock of the element if available
+ */
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+
+ public function getReturnType(): Type
+ {
+ return $this->returnType;
+ }
+
+ public function getHasReturnByReference(): bool
+ {
+ return $this->hasReturnByReference;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/HasAttributes.php b/src/phpDocumentor/Reflection/Php/HasAttributes.php
new file mode 100644
index 00000000..413f64b6
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/HasAttributes.php
@@ -0,0 +1,22 @@
+attributes[] = $attribute;
+ }
+
+ /** @return Attribute[] */
+ public function getAttributes(): array
+ {
+ return $this->attributes;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Interface_.php b/src/phpDocumentor/Reflection/Php/Interface_.php
new file mode 100644
index 00000000..a38ca067
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Interface_.php
@@ -0,0 +1,143 @@
+location = $location ?: new Location(-1);
+ $this->endLocation = $endLocation ?: new Location(-1);
+ }
+
+ /**
+ * Returns the constants of this interface.
+ *
+ * @return Constant[]
+ */
+ public function getConstants(): array
+ {
+ return $this->constants;
+ }
+
+ /**
+ * Add constant to this interface.
+ */
+ public function addConstant(Constant $constant): void
+ {
+ $this->constants[(string) $constant->getFqsen()] = $constant;
+ }
+
+ /**
+ * Returns the methods in this interface.
+ *
+ * @return Method[]
+ */
+ public function getMethods(): array
+ {
+ return $this->methods;
+ }
+
+ /**
+ * Add method to this interface.
+ */
+ public function addMethod(Method $method): void
+ {
+ $this->methods[(string) $method->getFqsen()] = $method;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ /**
+ * Returns the DocBlock of this interface if available.
+ */
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ /**
+ * Returns the Fqsen of the interfaces this interface is extending.
+ *
+ * @return Fqsen[]
+ */
+ public function getParents(): array
+ {
+ return $this->parents;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/MetadataContainer.php b/src/phpDocumentor/Reflection/Php/MetadataContainer.php
new file mode 100644
index 00000000..e0f624e2
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/MetadataContainer.php
@@ -0,0 +1,43 @@
+key(), $this->metadata)) {
+ throw new Exception(sprintf('Metadata with key "%s" already exists', $metadata->key()));
+ }
+
+ $this->metadata[$metadata->key()] = $metadata;
+ }
+
+ /** @return Metadata[] */
+ public function getMetadata(): array
+ {
+ return $this->metadata;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Method.php b/src/phpDocumentor/Reflection/Php/Method.php
new file mode 100644
index 00000000..d0b2774a
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Method.php
@@ -0,0 +1,186 @@
+visibility === null) {
+ $this->visibility = new Visibility('public');
+ }
+
+ if ($location === null) {
+ $location = new Location(-1);
+ }
+
+ if ($endLocation === null) {
+ $endLocation = new Location(-1);
+ }
+
+ if ($returnType === null) {
+ $returnType = new Mixed_();
+ }
+
+ $this->location = $location;
+ $this->endLocation = $endLocation;
+ $this->returnType = $returnType;
+ }
+
+ /**
+ * Returns true when this method is abstract. Otherwise returns false.
+ */
+ public function isAbstract(): bool
+ {
+ return $this->abstract;
+ }
+
+ /**
+ * Returns true when this method is final. Otherwise returns false.
+ */
+ public function isFinal(): bool
+ {
+ return $this->final;
+ }
+
+ /**
+ * Returns true when this method is static. Otherwise returns false.
+ */
+ public function isStatic(): bool
+ {
+ return $this->static;
+ }
+
+ /**
+ * Returns the Visibility of this method.
+ */
+ public function getVisibility(): Visibility|null
+ {
+ return $this->visibility;
+ }
+
+ /**
+ * Returns the arguments of this method.
+ *
+ * @return Argument[]
+ */
+ public function getArguments(): array
+ {
+ return $this->arguments;
+ }
+
+ /**
+ * Add new argument to this method.
+ */
+ public function addArgument(Argument $argument): void
+ {
+ $this->arguments[] = $argument;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ /**
+ * Returns the DocBlock of this method if available.
+ */
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+
+ /**
+ * Returns the in code defined return type.
+ *
+ * Return types are introduced in php 7.0 when your could doesn't have a
+ * return type defined this method will return Mixed_ by default. The return value of this
+ * method is not affected by the return tag in your docblock.
+ */
+ public function getReturnType(): Type
+ {
+ return $this->returnType;
+ }
+
+ public function getHasReturnByReference(): bool
+ {
+ return $this->hasReturnByReference;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Namespace_.php b/src/phpDocumentor/Reflection/Php/Namespace_.php
new file mode 100644
index 00000000..0e8900c8
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Namespace_.php
@@ -0,0 +1,175 @@
+classes;
+ }
+
+ /**
+ * Add a class to this namespace.
+ */
+ public function addClass(Fqsen $class): void
+ {
+ $this->classes[(string) $class] = $class;
+ }
+
+ /**
+ * Returns a list of all constants in this namespace.
+ *
+ * @return Fqsen[]
+ */
+ public function getConstants(): array
+ {
+ return $this->constants;
+ }
+
+ /**
+ * Add a Constant to this Namespace.
+ */
+ public function addConstant(Fqsen $contant): void
+ {
+ $this->constants[(string) $contant] = $contant;
+ }
+
+ /**
+ * Returns a list of all functions in this namespace.
+ *
+ * @return Fqsen[]
+ */
+ public function getFunctions(): array
+ {
+ return $this->functions;
+ }
+
+ /**
+ * Add a function to this namespace.
+ */
+ public function addFunction(Fqsen $function): void
+ {
+ $this->functions[(string) $function] = $function;
+ }
+
+ /**
+ * Returns a list of all interfaces in this namespace.
+ *
+ * @return Fqsen[]
+ */
+ public function getInterfaces(): array
+ {
+ return $this->interfaces;
+ }
+
+ /**
+ * Add an interface the this namespace.
+ */
+ public function addInterface(Fqsen $interface): void
+ {
+ $this->interfaces[(string) $interface] = $interface;
+ }
+
+ /**
+ * Returns a list of all traits in this namespace.
+ *
+ * @return Fqsen[]
+ */
+ public function getTraits(): array
+ {
+ return $this->traits;
+ }
+
+ /**
+ * Add a trait to this namespace.
+ */
+ public function addTrait(Fqsen $trait): void
+ {
+ $this->traits[(string) $trait] = $trait;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[\Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[\Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/NodesFactory.php b/src/phpDocumentor/Reflection/Php/NodesFactory.php
new file mode 100644
index 00000000..cb877df6
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/NodesFactory.php
@@ -0,0 +1,72 @@
+createForNewestSupportedVersion();
+ $traverser = new NodeTraverser();
+ $traverser->addVisitor(new NameResolver());
+ $traverser->addVisitor(new ElementNameResolver());
+
+ return new static($parser, $traverser);
+ }
+
+ /**
+ * Will convert the provided code to nodes.
+ *
+ * @param string $code code to process.
+ *
+ * @return Node[]
+ */
+ public function create(string $code): array
+ {
+ $nodes = $this->parser->parse($code);
+ Assert::isArray($nodes);
+
+ return $this->traverser->traverse($nodes);
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Project.php b/src/phpDocumentor/Reflection/Php/Project.php
new file mode 100644
index 00000000..10041035
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Project.php
@@ -0,0 +1,100 @@
+rootNamespace !== null) {
+ return;
+ }
+
+ $this->rootNamespace = new Namespace_(new Fqsen('\\'));
+ }
+
+ /**
+ * Returns the name of this project.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * Returns all files with their sub-elements.
+ *
+ * @return File[]
+ */
+ public function getFiles(): array
+ {
+ return $this->files;
+ }
+
+ /**
+ * Add a file to this project.
+ */
+ public function addFile(File $file): void
+ {
+ $this->files[$file->getPath()] = $file;
+ }
+
+ /**
+ * Returns all namespaces with their sub-elements.
+ *
+ * @return Namespace_[]
+ */
+ public function getNamespaces(): array
+ {
+ return $this->namespaces;
+ }
+
+ /**
+ * Add a namespace to the project.
+ */
+ public function addNamespace(Namespace_ $namespace): void
+ {
+ $this->namespaces[(string) $namespace->getFqsen()] = $namespace;
+ }
+
+ /**
+ * Returns the root (global) namespace.
+ */
+ public function getRootNamespace(): Namespace_|null
+ {
+ return $this->rootNamespace;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/ProjectFactory.php b/src/phpDocumentor/Reflection/Php/ProjectFactory.php
new file mode 100644
index 00000000..dd461b76
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/ProjectFactory.php
@@ -0,0 +1,214 @@
+strategies = is_array($strategies) ? new ProjectFactoryStrategies($strategies) : $strategies;
+ }
+
+ /**
+ * Creates a new instance of this factory. With all default strategies.
+ */
+ public static function createInstance(): self
+ {
+ $docblockFactory = DocBlockFactory::createInstance();
+
+ $attributeReducer = new Attribute();
+ $parameterReducer = new Parameter(new PrettyPrinter());
+
+ $methodStrategy = new Method($docblockFactory, [$attributeReducer, $parameterReducer]);
+
+ $strategies = new ProjectFactoryStrategies(
+ [
+ new \phpDocumentor\Reflection\Php\Factory\Namespace_(),
+ new Class_($docblockFactory, [$attributeReducer]),
+ new Enum_($docblockFactory, [$attributeReducer]),
+ new EnumCase($docblockFactory, new PrettyPrinter(), [$attributeReducer]),
+ new Define($docblockFactory, new PrettyPrinter()),
+ new GlobalConstant($docblockFactory, new PrettyPrinter()),
+ new ClassConstant($docblockFactory, new PrettyPrinter(), [$attributeReducer]),
+ new Factory\File($docblockFactory, NodesFactory::createInstance()),
+ new Function_($docblockFactory, [$attributeReducer, $parameterReducer]),
+ new Interface_($docblockFactory, [$attributeReducer]),
+ $methodStrategy,
+ new Property($docblockFactory, new PrettyPrinter(), [$attributeReducer, $parameterReducer]),
+ new Trait_($docblockFactory, [$attributeReducer]),
+
+ new IfStatement(),
+ new TraitUse(),
+ ],
+ );
+
+ $strategies->addStrategy(
+ new ConstructorPromotion($methodStrategy, $docblockFactory, new PrettyPrinter(), [$attributeReducer, $parameterReducer]),
+ 1100,
+ );
+ $strategies->addStrategy(new Noop(), -PHP_INT_MAX);
+
+ return new self($strategies);
+ }
+
+ public function addStrategy(
+ ProjectFactoryStrategy $strategy,
+ int $priority = ProjectFactoryStrategies::DEFAULT_PRIORITY,
+ ): void {
+ $this->strategies->addStrategy($strategy, $priority);
+ }
+
+ /**
+ * Creates a project from the set of files.
+ *
+ * @param SourceFile[] $files
+ *
+ * @throws Exception When no matching strategy was found.
+ */
+ #[Override]
+ public function create(string $name, array $files): ProjectInterface
+ {
+ $contextStack = new ContextStack(new Project($name), null);
+
+ foreach ($files as $filePath) {
+ $strategy = $this->strategies->findMatching($contextStack, $filePath);
+ $strategy->create($contextStack, $filePath, $this->strategies);
+ }
+
+ $project = $contextStack->getProject();
+ $this->buildNamespaces($project);
+
+ return $project;
+ }
+
+ /**
+ * Builds the namespace tree with all elements in the project.
+ */
+ private function buildNamespaces(Project $project): void
+ {
+ foreach ($project->getFiles() as $file) {
+ foreach ($file->getNamespaces() as $namespaceFqsen) {
+ $namespace = $this->getNamespaceByName($project, (string) $namespaceFqsen);
+ $this->buildNamespace($file, $namespace);
+ }
+ }
+ }
+
+ /**
+ * Gets Namespace from the project if it exists, otherwise returns a new namepace
+ */
+ private function getNamespaceByName(Project $project, string $name): Namespace_
+ {
+ $existingNamespaces = $project->getNamespaces();
+
+ if (isset($existingNamespaces[$name])) {
+ return $existingNamespaces[$name];
+ }
+
+ $namespace = new Namespace_(new Fqsen($name));
+ $project->addNamespace($namespace);
+
+ return $namespace;
+ }
+
+ /**
+ * Adds all elements belonging to the namespace to the namespace.
+ */
+ private function buildNamespace(File $file, Namespace_ $namespace): void
+ {
+ foreach ($file->getClasses() as $class) {
+ if ($namespace->getFqsen() . '\\' . $class->getName() !== (string) $class->getFqsen()) {
+ continue;
+ }
+
+ $namespace->addClass($class->getFqsen());
+ }
+
+ foreach ($file->getInterfaces() as $interface) {
+ if ($namespace->getFqsen() . '\\' . $interface->getName() !== (string) $interface->getFqsen()) {
+ continue;
+ }
+
+ $namespace->addInterface($interface->getFqsen());
+ }
+
+ foreach ($file->getFunctions() as $function) {
+ if ($namespace->getFqsen() . '\\' . $function->getName() . '()' !== (string) $function->getFqsen()) {
+ continue;
+ }
+
+ $namespace->addFunction($function->getFqsen());
+ }
+
+ foreach ($file->getConstants() as $constant) {
+ if (
+ $namespace->getFqsen() . '::' . $constant->getName() !== (string) $constant->getFqsen() &&
+ $namespace->getFqsen() . '\\' . $constant->getName() !== (string) $constant->getFqsen()
+ ) {
+ continue;
+ }
+
+ $namespace->addConstant($constant->getFqsen());
+ }
+
+ foreach ($file->getTraits() as $trait) {
+ if ($namespace->getFqsen() . '\\' . $trait->getName() !== (string) $trait->getFqsen()) {
+ continue;
+ }
+
+ $namespace->addTrait($trait->getFqsen());
+ }
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategies.php b/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategies.php
new file mode 100644
index 00000000..00c3af7c
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategies.php
@@ -0,0 +1,73 @@
+ */
+ private readonly SplPriorityQueue $strategies;
+
+ /**
+ * Initializes the factory with a number of strategies.
+ *
+ * @param ProjectFactoryStrategy[] $strategies
+ */
+ public function __construct(array $strategies)
+ {
+ $this->strategies = new SplPriorityQueue();
+ foreach ($strategies as $strategy) {
+ $this->addStrategy($strategy);
+ }
+ }
+
+ /**
+ * Find the ProjectFactoryStrategy that matches $object.
+ *
+ * @throws OutOfBoundsException When no matching strategy was found.
+ */
+ #[Override]
+ public function findMatching(ContextStack $context, mixed $object): ProjectFactoryStrategy
+ {
+ foreach (clone $this->strategies as $strategy) {
+ if ($strategy->matches($context, $object)) {
+ return $strategy;
+ }
+ }
+
+ throw new OutOfBoundsException(
+ sprintf(
+ 'No matching factory found for %s',
+ get_debug_type($object),
+ ),
+ );
+ }
+
+ /**
+ * Add a strategy to this container.
+ */
+ public function addStrategy(ProjectFactoryStrategy $strategy, int $priority = self::DEFAULT_PRIORITY): void
+ {
+ $this->strategies->insert($strategy, $priority);
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategy.php b/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategy.php
new file mode 100644
index 00000000..1fa39583
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/ProjectFactoryStrategy.php
@@ -0,0 +1,41 @@
+visibility = $visibility ?: new Visibility('public');
+ $this->location = $location ?: new Location(-1);
+ $this->endLocation = $endLocation ?: new Location(-1);
+ }
+
+ /**
+ * returns the default value of this property.
+ */
+ public function getDefault(): string|null
+ {
+ return $this->default;
+ }
+
+ /**
+ * Returns true when this method is static. Otherwise returns false.
+ */
+ public function isStatic(): bool
+ {
+ return $this->static;
+ }
+
+ /**
+ * Returns the types of this property.
+ *
+ * @return string[]
+ */
+ public function getTypes(): array
+ {
+ return $this->types;
+ }
+
+ /**
+ * Add a type to this property
+ */
+ public function addType(string $type): void
+ {
+ $this->types[] = $type;
+ }
+
+ /**
+ * Return visibility of the property.
+ */
+ public function getVisibility(): Visibility|null
+ {
+ return $this->visibility;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ /**
+ * Returns the DocBlock of this property.
+ */
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+
+ public function getType(): Type|null
+ {
+ return $this->type;
+ }
+
+ public function isReadOnly(): bool
+ {
+ return $this->readOnly;
+ }
+
+ /** @return PropertyHook[] */
+ public function getHooks(): array
+ {
+ return $this->hooks;
+ }
+
+ /**
+ * Returns true when this property is virtual (not explicitly backed).
+ *
+ * A virtual property is one where no defined hook references the property itself.
+ */
+ public function isVirtual(): bool
+ {
+ return $this->virtual;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/PropertyHook.php b/src/phpDocumentor/Reflection/Php/PropertyHook.php
new file mode 100644
index 00000000..9def360b
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/PropertyHook.php
@@ -0,0 +1,95 @@
+location = $location ?? new Location(-1);
+ $this->endLocation = $endLocation ?? new Location(-1);
+ }
+
+ /**
+ * Returns true when this hook is final. Otherwise, returns false.
+ */
+ public function isFinal(): bool
+ {
+ return $this->final;
+ }
+
+ /**
+ * Returns the Visibility of this hook.
+ */
+ public function getVisibility(): Visibility|null
+ {
+ return $this->visibility;
+ }
+
+ /**
+ * Returns the arguments of this hook.
+ *
+ * @return Argument[]
+ */
+ public function getArguments(): array
+ {
+ return $this->arguments;
+ }
+
+ /**
+ * Add new argument to this hook.
+ */
+ public function addArgument(Argument $argument): void
+ {
+ $this->arguments[] = $argument;
+ }
+
+ /**
+ * Returns the name of this hook.
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * Returns the DocBlock of this method if available.
+ */
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/StrategyContainer.php b/src/phpDocumentor/Reflection/Php/StrategyContainer.php
new file mode 100644
index 00000000..5ca89ed3
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/StrategyContainer.php
@@ -0,0 +1,30 @@
+location = $location;
+ $this->endLocation = $endLocation;
+ }
+
+ /**
+ * Returns the methods of this Trait.
+ *
+ * @return Method[]
+ */
+ public function getMethods(): array
+ {
+ return $this->methods;
+ }
+
+ /**
+ * Add a method to this Trait
+ */
+ public function addMethod(Method $method): void
+ {
+ $this->methods[(string) $method->getFqsen()] = $method;
+ }
+
+ /**
+ * Returns the properties of this trait.
+ *
+ * @return Property[]
+ */
+ public function getProperties(): array
+ {
+ return $this->properties;
+ }
+
+ /**
+ * Add a property to this Trait.
+ */
+ public function addProperty(Property $property): void
+ {
+ $this->properties[(string) $property->getFqsen()] = $property;
+ }
+
+ /**
+ * Returns the Fqsen of the element.
+ */
+ #[Override]
+ public function getFqsen(): Fqsen
+ {
+ return $this->fqsen;
+ }
+
+ /**
+ * Returns the name of the element.
+ */
+ #[Override]
+ public function getName(): string
+ {
+ return $this->fqsen->getName();
+ }
+
+ public function getDocBlock(): DocBlock|null
+ {
+ return $this->docBlock;
+ }
+
+ /**
+ * Returns fqsen of all traits used by this trait.
+ *
+ * @return Fqsen[]
+ */
+ public function getUsedTraits(): array
+ {
+ return $this->usedTraits;
+ }
+
+ /**
+ * Add reference to trait used by this trait.
+ */
+ public function addUsedTrait(Fqsen $fqsen): void
+ {
+ $this->usedTraits[(string) $fqsen] = $fqsen;
+ }
+
+ public function getLocation(): Location
+ {
+ return $this->location;
+ }
+
+ public function getEndLocation(): Location
+ {
+ return $this->endLocation;
+ }
+
+ /**
+ * Returns the constants of this class.
+ *
+ * @return Constant[]
+ */
+ public function getConstants(): array
+ {
+ return $this->constants;
+ }
+
+ /**
+ * Add Constant to this class.
+ */
+ public function addConstant(Constant $constant): void
+ {
+ $this->constants[(string) $constant->getFqsen()] = $constant;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/ValueEvaluator/ConstantEvaluator.php b/src/phpDocumentor/Reflection/Php/ValueEvaluator/ConstantEvaluator.php
new file mode 100644
index 00000000..02834ea1
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/ValueEvaluator/ConstantEvaluator.php
@@ -0,0 +1,46 @@
+ $this->evaluateFallback($expr, $contextStack));
+
+ return $evaluator->evaluateSilently($expr);
+ // @codeCoverageIgnoreEnd
+ }
+
+ /** @throws ConstExprEvaluationException */
+ private function evaluateFallback(Expr $expr, ContextStack $contextStack): string
+ {
+ $typeContext = $contextStack->getTypeContext();
+ if ($typeContext === null) {
+ throw new ConstExprEvaluationException(
+ sprintf('Expression of type %s cannot be evaluated', $expr->getType()),
+ );
+ }
+
+ if ($expr instanceof Namespace_) {
+ return $typeContext->getNamespace();
+ }
+
+ throw new ConstExprEvaluationException(
+ sprintf('Expression of type %s cannot be evaluated', $expr->getType()),
+ );
+ }
+}
diff --git a/src/phpDocumentor/Reflection/Php/Visibility.php b/src/phpDocumentor/Reflection/Php/Visibility.php
new file mode 100644
index 00000000..59d7c523
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Php/Visibility.php
@@ -0,0 +1,74 @@
+visibility = $visibility;
+ }
+
+ /**
+ * Will return a string representation of visibility.
+ */
+ #[Override]
+ public function __toString(): string
+ {
+ return $this->visibility;
+ }
+}
diff --git a/src/phpDocumentor/Reflection/PrettyPrinter.php b/src/phpDocumentor/Reflection/PrettyPrinter.php
deleted file mode 100644
index 0d9d0704..00000000
--- a/src/phpDocumentor/Reflection/PrettyPrinter.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Scalar\String_;
-use PhpParser\PrettyPrinter\Standard;
-
-/**
- * Custom PrettyPrinter for phpDocumentor.
- *
- * phpDocumentor has a custom PrettyPrinter for PHP-Parser because it needs the
- * unmodified value for Scalar variables instead of an interpreted version.
- *
- * If the interpreted version was to be used then the XML interpretation would
- * fail because of special characters.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class PrettyPrinter extends Standard
-{
- /**
- * Converts the string into it's original representation without converting
- * the special character combinations.
- *
- * This method is overridden from the original Zend Pretty Printer because
- * the original returns the strings as interpreted by PHP-Parser.
- * Since we do not want such conversions we take the original that is
- * injected by our own custom Lexer.
- *
- * @param String $node The node to return a string
- * representation of.
- *
- * @see Lexer where the originalValue is injected.
- *
- * @return string
- */
- public function pScalar_String(String_ $node)
- {
- if (method_exists($this, 'pSafe')) {
- return $this->pSafe($node->getAttribute('originalValue'));
- }
-
- return $this->pNoIndent($node->getAttribute('originalValue'));
- }
-
-}
diff --git a/src/phpDocumentor/Reflection/ReflectionAbstract.php b/src/phpDocumentor/Reflection/ReflectionAbstract.php
deleted file mode 100644
index 871ed3a4..00000000
--- a/src/phpDocumentor/Reflection/ReflectionAbstract.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- * @copyright 2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use phpDocumentor\Event\DebugEvent;
-use phpDocumentor\Event\Dispatcher;
-use phpDocumentor\Event\LogEvent;
-
-/**
- * Provides basic event logging and dispatching for every reflection class.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-abstract class ReflectionAbstract
-{
- /**
- * The context (namespace, aliases) for the reflection.
- *
- * @var \phpDocumentor\Reflection\DocBlock\Context
- */
- protected $context = null;
-
- /**
- * Dispatches a logging request.
- *
- * @param string $message The message to log.
- * @param int $priority The logging priority, the lower,
- * the more important. Ranges from 1 to 7
- *
- * @return void
- */
- public function log($message, $priority = 6)
- {
- if (class_exists('phpDocumentor\Event\Dispatcher')) {
- Dispatcher::getInstance()->dispatch(
- 'system.log',
- LogEvent::createInstance($this)
- ->setMessage($message)->setPriority($priority)
- );
- }
- }
-
- /**
- * Dispatches a logging request to log a debug message.
- *
- * @param string $message The message to log.
- *
- * @return void
- */
- public function debug($message)
- {
- if (class_exists('phpDocumentor\Event\Dispatcher')) {
- Dispatcher::getInstance()->dispatch(
- 'system.debug',
- DebugEvent::createInstance($this)
- ->setMessage($message)
- );
- }
- }
-}
diff --git a/src/phpDocumentor/Reflection/TraitReflector.php b/src/phpDocumentor/Reflection/TraitReflector.php
deleted file mode 100644
index 1c5a46cb..00000000
--- a/src/phpDocumentor/Reflection/TraitReflector.php
+++ /dev/null
@@ -1,19 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-class TraitReflector extends ClassReflector
-{
- /** @var \PhpParser\Node\Stmt\Trait_ */
- protected $node;
-}
diff --git a/src/phpDocumentor/Reflection/Traverser.php b/src/phpDocumentor/Reflection/Traverser.php
deleted file mode 100644
index 31c3890b..00000000
--- a/src/phpDocumentor/Reflection/Traverser.php
+++ /dev/null
@@ -1,100 +0,0 @@
-
- * @copyright 2010-2012 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Error;
-use PhpParser\NodeVisitor\NameResolver;
-use PhpParser\Parser;
-use PhpParser\NodeTraverser;
-use PhpParser\NodeVisitor;
-use PhpParser\NodeVisitorAbstract;
-
-/**
- * The source code traverser that scans the given source code and transforms
- * it into tokens.
- *
- * @author Mike van Riel
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class Traverser
-{
- /**
- * List of visitors to apply upon traversing.
- *
- * @see traverse()
- *
- * @var \PhpParser\NodeVisitorAbstract[]
- */
- public $visitors = array();
-
- /**
- * Traverses the given contents and builds an AST.
- *
- * @param string $contents The source code of the file that is to be scanned
- *
- * @return void
- */
- public function traverse($contents)
- {
- try {
- $this->createTraverser()->traverse(
- $this->createParser()->parse($contents)
- );
- } catch (Error $e) {
- echo 'Parse Error: ', $e->getMessage();
- }
- }
-
- /**
- * Adds a visitor object to the traversal process.
- *
- * With visitors it is possible to extend the traversal process and
- * modify the found tokens.
- *
- * @param \PhpParser\NodeVisitor $visitor
- *
- * @return void
- */
- public function addVisitor(\PhpParser\NodeVisitor $visitor)
- {
- $this->visitors[] = $visitor;
- }
-
- /**
- * Creates a parser object using our own Lexer.
- *
- * @return Parser
- */
- protected function createParser()
- {
- return new Parser(new Lexer());
- }
-
- /**
- * Creates a new traverser object and adds visitors.
- *
- * @return NodeTraverser
- */
- protected function createTraverser()
- {
- $node_traverser = new NodeTraverser();
- $node_traverser->addVisitor(new NameResolver());
-
- foreach ($this->visitors as $visitor) {
- $node_traverser->addVisitor($visitor);
- }
-
- return $node_traverser;
- }
-}
diff --git a/src/phpDocumentor/Reflection/Types/NamespaceNodeToContext.php b/src/phpDocumentor/Reflection/Types/NamespaceNodeToContext.php
new file mode 100644
index 00000000..a79a6934
--- /dev/null
+++ b/src/phpDocumentor/Reflection/Types/NamespaceNodeToContext.php
@@ -0,0 +1,64 @@
+name ? $namespace->name->toString() : '',
+ $this->aliasesToFullyQualifiedNames($namespace),
+ );
+ }
+
+ /** @return string[] indexed by alias */
+ private function aliasesToFullyQualifiedNames(Namespace_ $namespace): array
+ {
+ // flatten(flatten(map(stuff)))
+ return array_merge([], ...array_merge([], ...array_map(
+ static fn ($use): array => array_map(
+ static function (Node\UseItem|UseUse $useUse) use ($use): array {
+ if ($use instanceof GroupUse) {
+ return [
+ (string) $useUse->getAlias() => $use->prefix->toString() . '\\' . $useUse->name->toString(),
+ ];
+ }
+
+ return [(string) $useUse->getAlias() => $useUse->name->toString()];
+ },
+ $use->uses,
+ ),
+ $this->classAlikeUses($namespace),
+ )));
+ }
+
+ /** @return Use_[]|GroupUse[] */
+ private function classAlikeUses(Namespace_ $namespace): array
+ {
+ return array_filter(
+ $namespace->stmts,
+ static fn (Node $node): bool => (
+ $node instanceof Use_
+ || $node instanceof GroupUse
+ ) && in_array($node->type, [Use_::TYPE_UNKNOWN, Use_::TYPE_NORMAL], true),
+ );
+ }
+}
diff --git a/tests/assets/phpunit_assert.php b/tests/assets/phpunit_assert.php
new file mode 100644
index 00000000..2dff5c52
--- /dev/null
+++ b/tests/assets/phpunit_assert.php
@@ -0,0 +1,3255 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ *
+ * This file is added for benchmark purposes only!!!
+ */
+namespace PHPUnit\Framework;
+
+use ArrayAccess;
+use Countable;
+use DOMDocument;
+use DOMElement;
+use PHPUnit\Framework\Constraint\ArrayHasKey;
+use PHPUnit\Framework\Constraint\ArraySubset;
+use PHPUnit\Framework\Constraint\Attribute;
+use PHPUnit\Framework\Constraint\Callback;
+use PHPUnit\Framework\Constraint\ClassHasAttribute;
+use PHPUnit\Framework\Constraint\ClassHasStaticAttribute;
+use PHPUnit\Framework\Constraint\Constraint;
+use PHPUnit\Framework\Constraint\Count;
+use PHPUnit\Framework\Constraint\DirectoryExists;
+use PHPUnit\Framework\Constraint\FileExists;
+use PHPUnit\Framework\Constraint\GreaterThan;
+use PHPUnit\Framework\Constraint\IsAnything;
+use PHPUnit\Framework\Constraint\IsEmpty;
+use PHPUnit\Framework\Constraint\IsEqual;
+use PHPUnit\Framework\Constraint\IsFalse;
+use PHPUnit\Framework\Constraint\IsFinite;
+use PHPUnit\Framework\Constraint\IsIdentical;
+use PHPUnit\Framework\Constraint\IsInfinite;
+use PHPUnit\Framework\Constraint\IsInstanceOf;
+use PHPUnit\Framework\Constraint\IsJson;
+use PHPUnit\Framework\Constraint\IsNan;
+use PHPUnit\Framework\Constraint\IsNull;
+use PHPUnit\Framework\Constraint\IsReadable;
+use PHPUnit\Framework\Constraint\IsTrue;
+use PHPUnit\Framework\Constraint\IsType;
+use PHPUnit\Framework\Constraint\IsWritable;
+use PHPUnit\Framework\Constraint\JsonMatches;
+use PHPUnit\Framework\Constraint\LessThan;
+use PHPUnit\Framework\Constraint\LogicalAnd;
+use PHPUnit\Framework\Constraint\LogicalNot;
+use PHPUnit\Framework\Constraint\LogicalOr;
+use PHPUnit\Framework\Constraint\LogicalXor;
+use PHPUnit\Framework\Constraint\ObjectHasAttribute;
+use PHPUnit\Framework\Constraint\RegularExpression;
+use PHPUnit\Framework\Constraint\SameSize;
+use PHPUnit\Framework\Constraint\StringContains;
+use PHPUnit\Framework\Constraint\StringEndsWith;
+use PHPUnit\Framework\Constraint\StringMatchesFormatDescription;
+use PHPUnit\Framework\Constraint\StringStartsWith;
+use PHPUnit\Framework\Constraint\TraversableContains;
+use PHPUnit\Framework\Constraint\TraversableContainsOnly;
+use PHPUnit\Util\InvalidArgumentHelper;
+use PHPUnit\Util\Type;
+use PHPUnit\Util\Xml;
+use ReflectionClass;
+use ReflectionException;
+use ReflectionObject;
+use Traversable;
+
+/**
+ * A set of assertion methods.
+ */
+abstract class Assert
+{
+ /**
+ * @var int
+ */
+ private static $count = 0;
+
+ /**
+ * Asserts that an array has a specified key.
+ *
+ * @param int|string $key
+ * @param array|ArrayAccess $array
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertArrayHasKey($key, $array, string $message = ''): void
+ {
+ if (!(\is_int($key) || \is_string($key))) {
+ throw InvalidArgumentHelper::factory(
+ 1,
+ 'integer or string'
+ );
+ }
+
+ if (!(\is_array($array) || $array instanceof ArrayAccess)) {
+ throw InvalidArgumentHelper::factory(
+ 2,
+ 'array or ArrayAccess'
+ );
+ }
+
+ $constraint = new ArrayHasKey($key);
+
+ static::assertThat($array, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an array has a specified subset.
+ *
+ * @param array|ArrayAccess $subset
+ * @param array|ArrayAccess $array
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ *
+ * @codeCoverageIgnore
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3494
+ */
+ public static function assertArraySubset($subset, $array, bool $checkForObjectIdentity = false, string $message = ''): void
+ {
+ self::createWarning('assertArraySubset() is deprecated and will be removed in PHPUnit 9.');
+
+ if (!(\is_array($subset) || $subset instanceof ArrayAccess)) {
+ throw InvalidArgumentHelper::factory(
+ 1,
+ 'array or ArrayAccess'
+ );
+ }
+
+ if (!(\is_array($array) || $array instanceof ArrayAccess)) {
+ throw InvalidArgumentHelper::factory(
+ 2,
+ 'array or ArrayAccess'
+ );
+ }
+
+ $constraint = new ArraySubset($subset, $checkForObjectIdentity);
+
+ static::assertThat($array, $constraint, $message);
+ }
+
+ /**
+ * Asserts that an array does not have a specified key.
+ *
+ * @param int|string $key
+ * @param array|ArrayAccess $array
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertArrayNotHasKey($key, $array, string $message = ''): void
+ {
+ if (!(\is_int($key) || \is_string($key))) {
+ throw InvalidArgumentHelper::factory(
+ 1,
+ 'integer or string'
+ );
+ }
+
+ if (!(\is_array($array) || $array instanceof ArrayAccess)) {
+ throw InvalidArgumentHelper::factory(
+ 2,
+ 'array or ArrayAccess'
+ );
+ }
+
+ $constraint = new LogicalNot(
+ new ArrayHasKey($key)
+ );
+
+ static::assertThat($array, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a haystack contains a needle.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
+ {
+ // @codeCoverageIgnoreStart
+ if (\is_string($haystack)) {
+ self::createWarning('Using assertContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringContainsString() or assertStringContainsStringIgnoringCase() instead.');
+ }
+
+ if (!$checkForObjectIdentity) {
+ self::createWarning('The optional $checkForObjectIdentity parameter of assertContains() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertContainsEquals() instead.');
+ }
+
+ if ($checkForNonObjectIdentity) {
+ self::createWarning('The optional $checkForNonObjectIdentity parameter of assertContains() is deprecated and will be removed in PHPUnit 9.');
+ }
+
+ if ($ignoreCase) {
+ self::createWarning('The optional $ignoreCase parameter of assertContains() is deprecated and will be removed in PHPUnit 9.');
+ }
+ // @codeCoverageIgnoreEnd
+
+ if (\is_array($haystack) ||
+ (\is_object($haystack) && $haystack instanceof Traversable)) {
+ $constraint = new TraversableContains(
+ $needle,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ );
+ } elseif (\is_string($haystack)) {
+ if (!\is_string($needle)) {
+ throw InvalidArgumentHelper::factory(
+ 1,
+ 'string'
+ );
+ }
+
+ $constraint = new StringContains(
+ $needle,
+ $ignoreCase
+ );
+ } else {
+ throw InvalidArgumentHelper::factory(
+ 2,
+ 'array, traversable or string'
+ );
+ }
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ public static function assertContainsEquals($needle, iterable $haystack, string $message = ''): void
+ {
+ $constraint = new TraversableContains($needle, false, false);
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object contains a needle.
+ *
+ * @param object|string $haystackClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
+ {
+ self::createWarning('assertAttributeContains() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertContains(
+ $needle,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message,
+ $ignoreCase,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ );
+ }
+
+ /**
+ * Asserts that a haystack does not contain a needle.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertNotContains($needle, $haystack, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
+ {
+ // @codeCoverageIgnoreStart
+ if (\is_string($haystack)) {
+ self::createWarning('Using assertNotContains() with string haystacks is deprecated and will not be supported in PHPUnit 9. Refactor your test to use assertStringNotContainsString() or assertStringNotContainsStringIgnoringCase() instead.');
+ }
+
+ if (!$checkForObjectIdentity) {
+ self::createWarning('The optional $checkForObjectIdentity parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotContainsEquals() instead.');
+ }
+
+ if ($checkForNonObjectIdentity) {
+ self::createWarning('The optional $checkForNonObjectIdentity parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9.');
+ }
+
+ if ($ignoreCase) {
+ self::createWarning('The optional $ignoreCase parameter of assertNotContains() is deprecated and will be removed in PHPUnit 9.');
+ }
+ // @codeCoverageIgnoreEnd
+
+ if (\is_array($haystack) ||
+ (\is_object($haystack) && $haystack instanceof Traversable)) {
+ $constraint = new LogicalNot(
+ new TraversableContains(
+ $needle,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ )
+ );
+ } elseif (\is_string($haystack)) {
+ if (!\is_string($needle)) {
+ throw InvalidArgumentHelper::factory(
+ 1,
+ 'string'
+ );
+ }
+
+ $constraint = new LogicalNot(
+ new StringContains(
+ $needle,
+ $ignoreCase
+ )
+ );
+ } else {
+ throw InvalidArgumentHelper::factory(
+ 2,
+ 'array, traversable or string'
+ );
+ }
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ public static function assertNotContainsEquals($needle, iterable $haystack, string $message = ''): void
+ {
+ $constraint = new LogicalNot(new TraversableContains($needle, false, false));
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object does not contain a needle.
+ *
+ * @param object|string $haystackClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotContains($needle, string $haystackAttributeName, $haystackClassOrObject, string $message = '', bool $ignoreCase = false, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): void
+ {
+ self::createWarning('assertAttributeNotContains() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotContains(
+ $needle,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message,
+ $ignoreCase,
+ $checkForObjectIdentity,
+ $checkForNonObjectIdentity
+ );
+ }
+
+ /**
+ * Asserts that a haystack contains only values of a given type.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
+ {
+ if ($isNativeType === null) {
+ $isNativeType = Type::isType($type);
+ }
+
+ static::assertThat(
+ $haystack,
+ new TraversableContainsOnly(
+ $type,
+ $isNativeType
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack contains only instances of a given class name.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertContainsOnlyInstancesOf(string $className, iterable $haystack, string $message = ''): void
+ {
+ static::assertThat(
+ $haystack,
+ new TraversableContainsOnly(
+ $className,
+ false
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object contains only values of a given type.
+ *
+ * @param object|string $haystackClassOrObject
+ * @param bool $isNativeType
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void
+ {
+ self::createWarning('assertAttributeContainsOnly() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertContainsOnly(
+ $type,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $isNativeType,
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack does not contain only values of a given type.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotContainsOnly(string $type, iterable $haystack, ?bool $isNativeType = null, string $message = ''): void
+ {
+ if ($isNativeType === null) {
+ $isNativeType = Type::isType($type);
+ }
+
+ static::assertThat(
+ $haystack,
+ new LogicalNot(
+ new TraversableContainsOnly(
+ $type,
+ $isNativeType
+ )
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a haystack that is stored in a static attribute of a class
+ * or an attribute of an object does not contain only values of a given
+ * type.
+ *
+ * @param object|string $haystackClassOrObject
+ * @param bool $isNativeType
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotContainsOnly(string $type, string $haystackAttributeName, $haystackClassOrObject, ?bool $isNativeType = null, string $message = ''): void
+ {
+ self::createWarning('assertAttributeNotContainsOnly() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotContainsOnly(
+ $type,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $isNativeType,
+ $message
+ );
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable.
+ *
+ * @param Countable|iterable $haystack
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertCount(int $expectedCount, $haystack, string $message = ''): void
+ {
+ if (!$haystack instanceof Countable && !\is_iterable($haystack)) {
+ throw InvalidArgumentHelper::factory(2, 'countable or iterable');
+ }
+
+ static::assertThat(
+ $haystack,
+ new Count($expectedCount),
+ $message
+ );
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable
+ * that is stored in an attribute.
+ *
+ * @param object|string $haystackClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeCount() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertCount(
+ $expectedCount,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable.
+ *
+ * @param Countable|iterable $haystack
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertNotCount(int $expectedCount, $haystack, string $message = ''): void
+ {
+ if (!$haystack instanceof Countable && !\is_iterable($haystack)) {
+ throw InvalidArgumentHelper::factory(2, 'countable or iterable');
+ }
+
+ $constraint = new LogicalNot(
+ new Count($expectedCount)
+ );
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * Asserts the number of elements of an array, Countable or Traversable
+ * that is stored in an attribute.
+ *
+ * @param object|string $haystackClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotCount(int $expectedCount, string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeNotCount() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotCount(
+ $expectedCount,
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that two variables are equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertEquals($expected, $actual, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
+ {
+ // @codeCoverageIgnoreStart
+ if ($delta !== 0.0) {
+ self::createWarning('The optional $delta parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsWithDelta() instead.');
+ }
+
+ if ($maxDepth !== 10) {
+ self::createWarning('The optional $maxDepth parameter of assertEquals() is deprecated and will be removed in PHPUnit 9.');
+ }
+
+ if ($canonicalize) {
+ self::createWarning('The optional $canonicalize parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsCanonicalizing() instead.');
+ }
+
+ if ($ignoreCase) {
+ self::createWarning('The optional $ignoreCase parameter of assertEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertEqualsIgnoringCase() instead.');
+ }
+ // @codeCoverageIgnoreEnd
+
+ $constraint = new IsEqual(
+ $expected,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two variables are equal (canonicalizing).
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void
+ {
+ $constraint = new IsEqual(
+ $expected,
+ 0.0,
+ 10,
+ true,
+ false
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two variables are equal (ignoring case).
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void
+ {
+ $constraint = new IsEqual(
+ $expected,
+ 0.0,
+ 10,
+ false,
+ true
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two variables are equal (with delta).
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
+ {
+ $constraint = new IsEqual(
+ $expected,
+ $delta
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a variable is equal to an attribute of an object.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
+ {
+ self::createWarning('assertAttributeEquals() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertEquals(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that two variables are not equal.
+ *
+ * @param float $delta
+ * @param int $maxDepth
+ * @param bool $canonicalize
+ * @param bool $ignoreCase
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotEquals($expected, $actual, string $message = '', $delta = 0.0, $maxDepth = 10, $canonicalize = false, $ignoreCase = false): void
+ {
+ // @codeCoverageIgnoreStart
+ if ($delta !== 0.0) {
+ self::createWarning('The optional $delta parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotEqualsWithDelta() instead.');
+ }
+
+ if ($maxDepth !== 10) {
+ self::createWarning('The optional $maxDepth parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9.');
+ }
+
+ if ($canonicalize) {
+ self::createWarning('The optional $canonicalize parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotEqualsCanonicalizing() instead.');
+ }
+
+ if ($ignoreCase) {
+ self::createWarning('The optional $ignoreCase parameter of assertNotEquals() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertNotEqualsIgnoringCase() instead.');
+ }
+ // @codeCoverageIgnoreEnd
+
+ $constraint = new LogicalNot(
+ new IsEqual(
+ $expected,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ )
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two variables are not equal (canonicalizing).
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void
+ {
+ $constraint = new LogicalNot(
+ new IsEqual(
+ $expected,
+ 0.0,
+ 10,
+ true,
+ false
+ )
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two variables are not equal (ignoring case).
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void
+ {
+ $constraint = new LogicalNot(
+ new IsEqual(
+ $expected,
+ 0.0,
+ 10,
+ false,
+ true
+ )
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that two variables are not equal (with delta).
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void
+ {
+ $constraint = new LogicalNot(
+ new IsEqual(
+ $expected,
+ $delta
+ )
+ );
+
+ static::assertThat($actual, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a variable is not equal to an attribute of an object.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotEquals($expected, string $actualAttributeName, $actualClassOrObject, string $message = '', float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): void
+ {
+ self::createWarning('assertAttributeNotEquals() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotEquals(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ );
+ }
+
+ /**
+ * Asserts that a variable is empty.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertEmpty($actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::isEmpty(), $message);
+ }
+
+ /**
+ * Asserts that a static attribute of a class or an attribute of an object
+ * is empty.
+ *
+ * @param object|string $haystackClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeEmpty() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertEmpty(
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not empty.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotEmpty($actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::logicalNot(static::isEmpty()), $message);
+ }
+
+ /**
+ * Asserts that a static attribute of a class or an attribute of an object
+ * is not empty.
+ *
+ * @param object|string $haystackClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotEmpty(string $haystackAttributeName, $haystackClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeNotEmpty() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotEmpty(
+ static::readAttribute($haystackClassOrObject, $haystackAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is greater than another value.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertGreaterThan($expected, $actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::greaterThan($expected), $message);
+ }
+
+ /**
+ * Asserts that an attribute is greater than another value.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeGreaterThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeGreaterThan() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertGreaterThan(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is greater than or equal to another value.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ static::greaterThanOrEqual($expected),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an attribute is greater than or equal to another value.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeGreaterThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeGreaterThanOrEqual() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertGreaterThanOrEqual(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is smaller than another value.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertLessThan($expected, $actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::lessThan($expected), $message);
+ }
+
+ /**
+ * Asserts that an attribute is smaller than another value.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeLessThan($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeLessThan() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertLessThan(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a value is smaller than or equal to another value.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertLessThanOrEqual($expected, $actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::lessThanOrEqual($expected), $message);
+ }
+
+ /**
+ * Asserts that an attribute is smaller than or equal to another value.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeLessThanOrEqual($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeLessThanOrEqual() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertLessThanOrEqual(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that the contents of one file is equal to the contents of another
+ * file.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
+ {
+ static::assertFileExists($expected, $message);
+ static::assertFileExists($actual, $message);
+
+ $constraint = new IsEqual(
+ \file_get_contents($expected),
+ 0.0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ );
+
+ static::assertThat(\file_get_contents($actual), $constraint, $message);
+ }
+
+ /**
+ * Asserts that the contents of one file is not equal to the contents of
+ * another file.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileNotEquals(string $expected, string $actual, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
+ {
+ static::assertFileExists($expected, $message);
+ static::assertFileExists($actual, $message);
+
+ $constraint = new LogicalNot(
+ new IsEqual(
+ \file_get_contents($expected),
+ 0.0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ )
+ );
+
+ static::assertThat(\file_get_contents($actual), $constraint, $message);
+ }
+
+ /**
+ * Asserts that the contents of a string is equal
+ * to the contents of a file.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
+ {
+ static::assertFileExists($expectedFile, $message);
+
+ $constraint = new IsEqual(
+ \file_get_contents($expectedFile),
+ 0.0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ );
+
+ static::assertThat($actualString, $constraint, $message);
+ }
+
+ /**
+ * Asserts that the contents of a string is not equal
+ * to the contents of a file.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = '', bool $canonicalize = false, bool $ignoreCase = false): void
+ {
+ static::assertFileExists($expectedFile, $message);
+
+ $constraint = new LogicalNot(
+ new IsEqual(
+ \file_get_contents($expectedFile),
+ 0.0,
+ 10,
+ $canonicalize,
+ $ignoreCase
+ )
+ );
+
+ static::assertThat($actualString, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a file/dir is readable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsReadable(string $filename, string $message = ''): void
+ {
+ static::assertThat($filename, new IsReadable, $message);
+ }
+
+ /**
+ * Asserts that a file/dir exists and is not readable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotIsReadable(string $filename, string $message = ''): void
+ {
+ static::assertThat($filename, new LogicalNot(new IsReadable), $message);
+ }
+
+ /**
+ * Asserts that a file/dir exists and is writable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsWritable(string $filename, string $message = ''): void
+ {
+ static::assertThat($filename, new IsWritable, $message);
+ }
+
+ /**
+ * Asserts that a file/dir exists and is not writable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotIsWritable(string $filename, string $message = ''): void
+ {
+ static::assertThat($filename, new LogicalNot(new IsWritable), $message);
+ }
+
+ /**
+ * Asserts that a directory exists.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertDirectoryExists(string $directory, string $message = ''): void
+ {
+ static::assertThat($directory, new DirectoryExists, $message);
+ }
+
+ /**
+ * Asserts that a directory does not exist.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertDirectoryNotExists(string $directory, string $message = ''): void
+ {
+ static::assertThat($directory, new LogicalNot(new DirectoryExists), $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is readable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertDirectoryIsReadable(string $directory, string $message = ''): void
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertIsReadable($directory, $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is not readable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertDirectoryNotIsReadable(string $directory, string $message = ''): void
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertNotIsReadable($directory, $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is writable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertDirectoryIsWritable(string $directory, string $message = ''): void
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertIsWritable($directory, $message);
+ }
+
+ /**
+ * Asserts that a directory exists and is not writable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertDirectoryNotIsWritable(string $directory, string $message = ''): void
+ {
+ self::assertDirectoryExists($directory, $message);
+ self::assertNotIsWritable($directory, $message);
+ }
+
+ /**
+ * Asserts that a file exists.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileExists(string $filename, string $message = ''): void
+ {
+ static::assertThat($filename, new FileExists, $message);
+ }
+
+ /**
+ * Asserts that a file does not exist.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileNotExists(string $filename, string $message = ''): void
+ {
+ static::assertThat($filename, new LogicalNot(new FileExists), $message);
+ }
+
+ /**
+ * Asserts that a file exists and is readable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileIsReadable(string $file, string $message = ''): void
+ {
+ self::assertFileExists($file, $message);
+ self::assertIsReadable($file, $message);
+ }
+
+ /**
+ * Asserts that a file exists and is not readable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileNotIsReadable(string $file, string $message = ''): void
+ {
+ self::assertFileExists($file, $message);
+ self::assertNotIsReadable($file, $message);
+ }
+
+ /**
+ * Asserts that a file exists and is writable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileIsWritable(string $file, string $message = ''): void
+ {
+ self::assertFileExists($file, $message);
+ self::assertIsWritable($file, $message);
+ }
+
+ /**
+ * Asserts that a file exists and is not writable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFileNotIsWritable(string $file, string $message = ''): void
+ {
+ self::assertFileExists($file, $message);
+ self::assertNotIsWritable($file, $message);
+ }
+
+ /**
+ * Asserts that a condition is true.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertTrue($condition, string $message = ''): void
+ {
+ static::assertThat($condition, static::isTrue(), $message);
+ }
+
+ /**
+ * Asserts that a condition is not true.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotTrue($condition, string $message = ''): void
+ {
+ static::assertThat($condition, static::logicalNot(static::isTrue()), $message);
+ }
+
+ /**
+ * Asserts that a condition is false.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFalse($condition, string $message = ''): void
+ {
+ static::assertThat($condition, static::isFalse(), $message);
+ }
+
+ /**
+ * Asserts that a condition is not false.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotFalse($condition, string $message = ''): void
+ {
+ static::assertThat($condition, static::logicalNot(static::isFalse()), $message);
+ }
+
+ /**
+ * Asserts that a variable is null.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNull($actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::isNull(), $message);
+ }
+
+ /**
+ * Asserts that a variable is not null.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotNull($actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::logicalNot(static::isNull()), $message);
+ }
+
+ /**
+ * Asserts that a variable is finite.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertFinite($actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::isFinite(), $message);
+ }
+
+ /**
+ * Asserts that a variable is infinite.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertInfinite($actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::isInfinite(), $message);
+ }
+
+ /**
+ * Asserts that a variable is nan.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNan($actual, string $message = ''): void
+ {
+ static::assertThat($actual, static::isNan(), $message);
+ }
+
+ /**
+ * Asserts that a class has a specified attribute.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void
+ {
+ if (!self::isValidClassAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!\class_exists($className)) {
+ throw InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ static::assertThat($className, new ClassHasAttribute($attributeName), $message);
+ }
+
+ /**
+ * Asserts that a class does not have a specified attribute.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void
+ {
+ if (!self::isValidClassAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!\class_exists($className)) {
+ throw InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ static::assertThat(
+ $className,
+ new LogicalNot(
+ new ClassHasAttribute($attributeName)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a class has a specified static attribute.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
+ {
+ if (!self::isValidClassAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!\class_exists($className)) {
+ throw InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ static::assertThat(
+ $className,
+ new ClassHasStaticAttribute($attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a class does not have a specified static attribute.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void
+ {
+ if (!self::isValidClassAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!\class_exists($className)) {
+ throw InvalidArgumentHelper::factory(2, 'class name', $className);
+ }
+
+ static::assertThat(
+ $className,
+ new LogicalNot(
+ new ClassHasStaticAttribute($attributeName)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an object has a specified attribute.
+ *
+ * @param object $object
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void
+ {
+ if (!self::isValidObjectAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!\is_object($object)) {
+ throw InvalidArgumentHelper::factory(2, 'object');
+ }
+
+ static::assertThat(
+ $object,
+ new ObjectHasAttribute($attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an object does not have a specified attribute.
+ *
+ * @param object $object
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void
+ {
+ if (!self::isValidObjectAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(1, 'valid attribute name');
+ }
+
+ if (!\is_object($object)) {
+ throw InvalidArgumentHelper::factory(2, 'object');
+ }
+
+ static::assertThat(
+ $object,
+ new LogicalNot(
+ new ObjectHasAttribute($attributeName)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that two variables have the same type and value.
+ * Used on objects, it asserts that two variables reference
+ * the same object.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertSame($expected, $actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsIdentical($expected),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable and an attribute of an object have the same type
+ * and value.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeSame() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertSame(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that two variables do not have the same type and value.
+ * Used on objects, it asserts that two variables do not reference
+ * the same object.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotSame($expected, $actual, string $message = ''): void
+ {
+ if (\is_bool($expected) && \is_bool($actual)) {
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ static::assertThat(
+ $actual,
+ new LogicalNot(
+ new IsIdentical($expected)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable and an attribute of an object do not have the
+ * same type and value.
+ *
+ * @param object|string $actualClassOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotSame($expected, string $actualAttributeName, $actualClassOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeNotSame() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotSame(
+ $expected,
+ static::readAttribute($actualClassOrObject, $actualAttributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of a given type.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertInstanceOf(string $expected, $actual, string $message = ''): void
+ {
+ if (!\class_exists($expected) && !\interface_exists($expected)) {
+ throw InvalidArgumentHelper::factory(1, 'class or interface name');
+ }
+
+ static::assertThat(
+ $actual,
+ new IsInstanceOf($expected),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param object|string $classOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeInstanceOf() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertInstanceOf(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of a given type.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertNotInstanceOf(string $expected, $actual, string $message = ''): void
+ {
+ if (!\class_exists($expected) && !\interface_exists($expected)) {
+ throw InvalidArgumentHelper::factory(1, 'class or interface name');
+ }
+
+ static::assertThat(
+ $actual,
+ new LogicalNot(
+ new IsInstanceOf($expected)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param object|string $classOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotInstanceOf(string $expected, string $attributeName, $classOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeNotInstanceOf() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotInstanceOf(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of a given type.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369
+ * @codeCoverageIgnore
+ */
+ public static function assertInternalType(string $expected, $actual, string $message = ''): void
+ {
+ self::createWarning('assertInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsArray(), assertIsBool(), assertIsFloat(), assertIsInt(), assertIsNumeric(), assertIsObject(), assertIsResource(), assertIsString(), assertIsScalar(), assertIsCallable(), or assertIsIterable() instead.');
+
+ static::assertThat(
+ $actual,
+ new IsType($expected),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param object|string $classOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeInternalType() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertInternalType(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type array.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsArray($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_ARRAY),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type bool.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsBool($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_BOOL),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type float.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsFloat($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_FLOAT),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type int.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsInt($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_INT),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type numeric.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNumeric($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_NUMERIC),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type object.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsObject($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_OBJECT),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type resource.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsResource($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_RESOURCE),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type string.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsString($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_STRING),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type scalar.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsScalar($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_SCALAR),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type callable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsCallable($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_CALLABLE),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is of type iterable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsIterable($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new IsType(IsType::TYPE_ITERABLE),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of a given type.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3369
+ * @codeCoverageIgnore
+ */
+ public static function assertNotInternalType(string $expected, $actual, string $message = ''): void
+ {
+ self::createWarning('assertNotInternalType() is deprecated and will be removed in PHPUnit 9. Refactor your test to use assertIsNotArray(), assertIsNotBool(), assertIsNotFloat(), assertIsNotInt(), assertIsNotNumeric(), assertIsNotObject(), assertIsNotResource(), assertIsNotString(), assertIsNotScalar(), assertIsNotCallable(), or assertIsNotIterable() instead.');
+
+ static::assertThat(
+ $actual,
+ new LogicalNot(
+ new IsType($expected)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type array.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotArray($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_ARRAY)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type bool.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotBool($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_BOOL)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type float.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotFloat($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_FLOAT)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type int.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotInt($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_INT)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type numeric.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotNumeric($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_NUMERIC)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type object.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotObject($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_OBJECT)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type resource.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotResource($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_RESOURCE)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type string.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotString($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_STRING)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type scalar.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotScalar($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_SCALAR)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type callable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotCallable($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_CALLABLE)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a variable is not of type iterable.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertIsNotIterable($actual, string $message = ''): void
+ {
+ static::assertThat(
+ $actual,
+ new LogicalNot(new IsType(IsType::TYPE_ITERABLE)),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that an attribute is of a given type.
+ *
+ * @param object|string $classOrObject
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws ReflectionException
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function assertAttributeNotInternalType(string $expected, string $attributeName, $classOrObject, string $message = ''): void
+ {
+ self::createWarning('assertAttributeNotInternalType() is deprecated and will be removed in PHPUnit 9.');
+
+ static::assertNotInternalType(
+ $expected,
+ static::readAttribute($classOrObject, $attributeName),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a string matches a given regular expression.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertRegExp(string $pattern, string $string, string $message = ''): void
+ {
+ static::assertThat($string, new RegularExpression($pattern), $message);
+ }
+
+ /**
+ * Asserts that a string does not match a given regular expression.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertNotRegExp(string $pattern, string $string, string $message = ''): void
+ {
+ static::assertThat(
+ $string,
+ new LogicalNot(
+ new RegularExpression($pattern)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
+ * is the same.
+ *
+ * @param Countable|iterable $expected
+ * @param Countable|iterable $actual
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertSameSize($expected, $actual, string $message = ''): void
+ {
+ if (!$expected instanceof Countable && !\is_iterable($expected)) {
+ throw InvalidArgumentHelper::factory(1, 'countable or iterable');
+ }
+
+ if (!$actual instanceof Countable && !\is_iterable($actual)) {
+ throw InvalidArgumentHelper::factory(2, 'countable or iterable');
+ }
+
+ static::assertThat(
+ $actual,
+ new SameSize($expected),
+ $message
+ );
+ }
+
+ /**
+ * Assert that the size of two arrays (or `Countable` or `Traversable` objects)
+ * is not the same.
+ *
+ * @param Countable|iterable $expected
+ * @param Countable|iterable $actual
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertNotSameSize($expected, $actual, string $message = ''): void
+ {
+ if (!$expected instanceof Countable && !\is_iterable($expected)) {
+ throw InvalidArgumentHelper::factory(1, 'countable or iterable');
+ }
+
+ if (!$actual instanceof Countable && !\is_iterable($actual)) {
+ throw InvalidArgumentHelper::factory(2, 'countable or iterable');
+ }
+
+ static::assertThat(
+ $actual,
+ new LogicalNot(
+ new SameSize($expected)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a string matches a given format string.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringMatchesFormat(string $format, string $string, string $message = ''): void
+ {
+ static::assertThat($string, new StringMatchesFormatDescription($format), $message);
+ }
+
+ /**
+ * Asserts that a string does not match a given format string.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void
+ {
+ static::assertThat(
+ $string,
+ new LogicalNot(
+ new StringMatchesFormatDescription($format)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a string matches a given format file.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
+ {
+ static::assertFileExists($formatFile, $message);
+
+ static::assertThat(
+ $string,
+ new StringMatchesFormatDescription(
+ \file_get_contents($formatFile)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a string does not match a given format string.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void
+ {
+ static::assertFileExists($formatFile, $message);
+
+ static::assertThat(
+ $string,
+ new LogicalNot(
+ new StringMatchesFormatDescription(
+ \file_get_contents($formatFile)
+ )
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that a string starts with a given prefix.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringStartsWith(string $prefix, string $string, string $message = ''): void
+ {
+ static::assertThat($string, new StringStartsWith($prefix), $message);
+ }
+
+ /**
+ * Asserts that a string starts not with a given prefix.
+ *
+ * @param string $prefix
+ * @param string $string
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringStartsNotWith($prefix, $string, string $message = ''): void
+ {
+ static::assertThat(
+ $string,
+ new LogicalNot(
+ new StringStartsWith($prefix)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringContainsString(string $needle, string $haystack, string $message = ''): void
+ {
+ $constraint = new StringContains($needle, false);
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
+ {
+ $constraint = new StringContains($needle, true);
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void
+ {
+ $constraint = new LogicalNot(new StringContains($needle));
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void
+ {
+ $constraint = new LogicalNot(new StringContains($needle, true));
+
+ static::assertThat($haystack, $constraint, $message);
+ }
+
+ /**
+ * Asserts that a string ends with a given suffix.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringEndsWith(string $suffix, string $string, string $message = ''): void
+ {
+ static::assertThat($string, new StringEndsWith($suffix), $message);
+ }
+
+ /**
+ * Asserts that a string ends not with a given suffix.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void
+ {
+ static::assertThat(
+ $string,
+ new LogicalNot(
+ new StringEndsWith($suffix)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that two XML files are equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
+ {
+ $expected = Xml::loadFile($expectedFile);
+ $actual = Xml::loadFile($actualFile);
+
+ static::assertEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML files are not equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void
+ {
+ $expected = Xml::loadFile($expectedFile);
+ $actual = Xml::loadFile($actualFile);
+
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are equal.
+ *
+ * @param DOMDocument|string $actualXml
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
+ {
+ $expected = Xml::loadFile($expectedFile);
+ $actual = Xml::load($actualXml);
+
+ static::assertEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are not equal.
+ *
+ * @param DOMDocument|string $actualXml
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void
+ {
+ $expected = Xml::loadFile($expectedFile);
+ $actual = Xml::load($actualXml);
+
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are equal.
+ *
+ * @param DOMDocument|string $expectedXml
+ * @param DOMDocument|string $actualXml
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
+ {
+ $expected = Xml::load($expectedXml);
+ $actual = Xml::load($actualXml);
+
+ static::assertEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that two XML documents are not equal.
+ *
+ * @param DOMDocument|string $expectedXml
+ * @param DOMDocument|string $actualXml
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ * @throws Exception
+ */
+ public static function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void
+ {
+ $expected = Xml::load($expectedXml);
+ $actual = Xml::load($actualXml);
+
+ static::assertNotEquals($expected, $actual, $message);
+ }
+
+ /**
+ * Asserts that a hierarchy of DOMElements matches.
+ *
+ * @throws AssertionFailedError
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void
+ {
+ $expectedElement = Xml::import($expectedElement);
+ $actualElement = Xml::import($actualElement);
+
+ static::assertSame(
+ $expectedElement->tagName,
+ $actualElement->tagName,
+ $message
+ );
+
+ if ($checkAttributes) {
+ static::assertSame(
+ $expectedElement->attributes->length,
+ $actualElement->attributes->length,
+ \sprintf(
+ '%s%sNumber of attributes on node "%s" does not match',
+ $message,
+ !empty($message) ? "\n" : '',
+ $expectedElement->tagName
+ )
+ );
+
+ for ($i = 0; $i < $expectedElement->attributes->length; $i++) {
+ $expectedAttribute = $expectedElement->attributes->item($i);
+ $actualAttribute = $actualElement->attributes->getNamedItem($expectedAttribute->name);
+
+ \assert($expectedAttribute instanceof \DOMAttr);
+
+ if (!$actualAttribute) {
+ static::fail(
+ \sprintf(
+ '%s%sCould not find attribute "%s" on node "%s"',
+ $message,
+ !empty($message) ? "\n" : '',
+ $expectedAttribute->name,
+ $expectedElement->tagName
+ )
+ );
+ }
+ }
+ }
+
+ Xml::removeCharacterDataNodes($expectedElement);
+ Xml::removeCharacterDataNodes($actualElement);
+
+ static::assertSame(
+ $expectedElement->childNodes->length,
+ $actualElement->childNodes->length,
+ \sprintf(
+ '%s%sNumber of child nodes of "%s" differs',
+ $message,
+ !empty($message) ? "\n" : '',
+ $expectedElement->tagName
+ )
+ );
+
+ for ($i = 0; $i < $expectedElement->childNodes->length; $i++) {
+ static::assertEqualXMLStructure(
+ $expectedElement->childNodes->item($i),
+ $actualElement->childNodes->item($i),
+ $checkAttributes,
+ $message
+ );
+ }
+ }
+
+ /**
+ * Evaluates a PHPUnit\Framework\Constraint matcher object.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertThat($value, Constraint $constraint, string $message = ''): void
+ {
+ self::$count += \count($constraint);
+
+ $constraint->evaluate($value, $message);
+ }
+
+ /**
+ * Asserts that a string is a valid JSON string.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertJson(string $actualJson, string $message = ''): void
+ {
+ static::assertThat($actualJson, static::isJson(), $message);
+ }
+
+ /**
+ * Asserts that two given JSON encoded objects or arrays are equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void
+ {
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ static::assertThat($actualJson, new JsonMatches($expectedJson), $message);
+ }
+
+ /**
+ * Asserts that two given JSON encoded objects or arrays are not equal.
+ *
+ * @param string $expectedJson
+ * @param string $actualJson
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertJsonStringNotEqualsJsonString($expectedJson, $actualJson, string $message = ''): void
+ {
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ static::assertThat(
+ $actualJson,
+ new LogicalNot(
+ new JsonMatches($expectedJson)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that the generated JSON encoded object and the content of the given file are equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
+ {
+ static::assertFileExists($expectedFile, $message);
+ $expectedJson = \file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ static::assertThat($actualJson, new JsonMatches($expectedJson), $message);
+ }
+
+ /**
+ * Asserts that the generated JSON encoded object and the content of the given file are not equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void
+ {
+ static::assertFileExists($expectedFile, $message);
+ $expectedJson = \file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ static::assertThat(
+ $actualJson,
+ new LogicalNot(
+ new JsonMatches($expectedJson)
+ ),
+ $message
+ );
+ }
+
+ /**
+ * Asserts that two JSON files are equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
+ {
+ static::assertFileExists($expectedFile, $message);
+ static::assertFileExists($actualFile, $message);
+
+ $actualJson = \file_get_contents($actualFile);
+ $expectedJson = \file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ $constraintExpected = new JsonMatches(
+ $expectedJson
+ );
+
+ $constraintActual = new JsonMatches($actualJson);
+
+ static::assertThat($expectedJson, $constraintActual, $message);
+ static::assertThat($actualJson, $constraintExpected, $message);
+ }
+
+ /**
+ * Asserts that two JSON files are not equal.
+ *
+ * @throws ExpectationFailedException
+ * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
+ */
+ public static function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void
+ {
+ static::assertFileExists($expectedFile, $message);
+ static::assertFileExists($actualFile, $message);
+
+ $actualJson = \file_get_contents($actualFile);
+ $expectedJson = \file_get_contents($expectedFile);
+
+ static::assertJson($expectedJson, $message);
+ static::assertJson($actualJson, $message);
+
+ $constraintExpected = new JsonMatches(
+ $expectedJson
+ );
+
+ $constraintActual = new JsonMatches($actualJson);
+
+ static::assertThat($expectedJson, new LogicalNot($constraintActual), $message);
+ static::assertThat($actualJson, new LogicalNot($constraintExpected), $message);
+ }
+
+ /**
+ * @throws Exception
+ */
+ public static function logicalAnd(): LogicalAnd
+ {
+ $constraints = \func_get_args();
+
+ $constraint = new LogicalAnd;
+ $constraint->setConstraints($constraints);
+
+ return $constraint;
+ }
+
+ public static function logicalOr(): LogicalOr
+ {
+ $constraints = \func_get_args();
+
+ $constraint = new LogicalOr;
+ $constraint->setConstraints($constraints);
+
+ return $constraint;
+ }
+
+ public static function logicalNot(Constraint $constraint): LogicalNot
+ {
+ return new LogicalNot($constraint);
+ }
+
+ public static function logicalXor(): LogicalXor
+ {
+ $constraints = \func_get_args();
+
+ $constraint = new LogicalXor;
+ $constraint->setConstraints($constraints);
+
+ return $constraint;
+ }
+
+ public static function anything(): IsAnything
+ {
+ return new IsAnything;
+ }
+
+ public static function isTrue(): IsTrue
+ {
+ return new IsTrue;
+ }
+
+ public static function callback(callable $callback): Callback
+ {
+ return new Callback($callback);
+ }
+
+ public static function isFalse(): IsFalse
+ {
+ return new IsFalse;
+ }
+
+ public static function isJson(): IsJson
+ {
+ return new IsJson;
+ }
+
+ public static function isNull(): IsNull
+ {
+ return new IsNull;
+ }
+
+ public static function isFinite(): IsFinite
+ {
+ return new IsFinite;
+ }
+
+ public static function isInfinite(): IsInfinite
+ {
+ return new IsInfinite;
+ }
+
+ public static function isNan(): IsNan
+ {
+ return new IsNan;
+ }
+
+ /**
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function attribute(Constraint $constraint, string $attributeName): Attribute
+ {
+ self::createWarning('attribute() is deprecated and will be removed in PHPUnit 9.');
+
+ return new Attribute($constraint, $attributeName);
+ }
+
+ public static function contains($value, bool $checkForObjectIdentity = true, bool $checkForNonObjectIdentity = false): TraversableContains
+ {
+ return new TraversableContains($value, $checkForObjectIdentity, $checkForNonObjectIdentity);
+ }
+
+ public static function containsOnly(string $type): TraversableContainsOnly
+ {
+ return new TraversableContainsOnly($type);
+ }
+
+ public static function containsOnlyInstancesOf(string $className): TraversableContainsOnly
+ {
+ return new TraversableContainsOnly($className, false);
+ }
+
+ /**
+ * @param int|string $key
+ */
+ public static function arrayHasKey($key): ArrayHasKey
+ {
+ return new ArrayHasKey($key);
+ }
+
+ public static function equalTo($value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): IsEqual
+ {
+ return new IsEqual($value, $delta, $maxDepth, $canonicalize, $ignoreCase);
+ }
+
+ /**
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function attributeEqualTo(string $attributeName, $value, float $delta = 0.0, int $maxDepth = 10, bool $canonicalize = false, bool $ignoreCase = false): Attribute
+ {
+ self::createWarning('attributeEqualTo() is deprecated and will be removed in PHPUnit 9.');
+
+ return static::attribute(
+ static::equalTo(
+ $value,
+ $delta,
+ $maxDepth,
+ $canonicalize,
+ $ignoreCase
+ ),
+ $attributeName
+ );
+ }
+
+ public static function isEmpty(): IsEmpty
+ {
+ return new IsEmpty;
+ }
+
+ public static function isWritable(): IsWritable
+ {
+ return new IsWritable;
+ }
+
+ public static function isReadable(): IsReadable
+ {
+ return new IsReadable;
+ }
+
+ public static function directoryExists(): DirectoryExists
+ {
+ return new DirectoryExists;
+ }
+
+ public static function fileExists(): FileExists
+ {
+ return new FileExists;
+ }
+
+ public static function greaterThan($value): GreaterThan
+ {
+ return new GreaterThan($value);
+ }
+
+ public static function greaterThanOrEqual($value): LogicalOr
+ {
+ return static::logicalOr(
+ new IsEqual($value),
+ new GreaterThan($value)
+ );
+ }
+
+ public static function classHasAttribute(string $attributeName): ClassHasAttribute
+ {
+ return new ClassHasAttribute($attributeName);
+ }
+
+ public static function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute
+ {
+ return new ClassHasStaticAttribute($attributeName);
+ }
+
+ public static function objectHasAttribute($attributeName): ObjectHasAttribute
+ {
+ return new ObjectHasAttribute($attributeName);
+ }
+
+ public static function identicalTo($value): IsIdentical
+ {
+ return new IsIdentical($value);
+ }
+
+ public static function isInstanceOf(string $className): IsInstanceOf
+ {
+ return new IsInstanceOf($className);
+ }
+
+ public static function isType(string $type): IsType
+ {
+ return new IsType($type);
+ }
+
+ public static function lessThan($value): LessThan
+ {
+ return new LessThan($value);
+ }
+
+ public static function lessThanOrEqual($value): LogicalOr
+ {
+ return static::logicalOr(
+ new IsEqual($value),
+ new LessThan($value)
+ );
+ }
+
+ public static function matchesRegularExpression(string $pattern): RegularExpression
+ {
+ return new RegularExpression($pattern);
+ }
+
+ public static function matches(string $string): StringMatchesFormatDescription
+ {
+ return new StringMatchesFormatDescription($string);
+ }
+
+ public static function stringStartsWith($prefix): StringStartsWith
+ {
+ return new StringStartsWith($prefix);
+ }
+
+ public static function stringContains(string $string, bool $case = true): StringContains
+ {
+ return new StringContains($string, $case);
+ }
+
+ public static function stringEndsWith(string $suffix): StringEndsWith
+ {
+ return new StringEndsWith($suffix);
+ }
+
+ public static function countOf(int $count): Count
+ {
+ return new Count($count);
+ }
+
+ /**
+ * Fails a test with the given message.
+ *
+ * @throws AssertionFailedError
+ */
+ public static function fail(string $message = ''): void
+ {
+ self::$count++;
+
+ throw new AssertionFailedError($message);
+ }
+
+ /**
+ * Returns the value of an attribute of a class or an object.
+ * This also works for attributes that are declared protected or private.
+ *
+ * @param object|string $classOrObject
+ *
+ * @throws Exception
+ * @throws ReflectionException
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function readAttribute($classOrObject, string $attributeName)
+ {
+ self::createWarning('readAttribute() is deprecated and will be removed in PHPUnit 9.');
+
+ if (!self::isValidClassAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(2, 'valid attribute name');
+ }
+
+ if (\is_string($classOrObject)) {
+ if (!\class_exists($classOrObject)) {
+ throw InvalidArgumentHelper::factory(
+ 1,
+ 'class name'
+ );
+ }
+
+ return static::getStaticAttribute(
+ $classOrObject,
+ $attributeName
+ );
+ }
+
+ if (\is_object($classOrObject)) {
+ return static::getObjectAttribute(
+ $classOrObject,
+ $attributeName
+ );
+ }
+
+ throw InvalidArgumentHelper::factory(
+ 1,
+ 'class name or object'
+ );
+ }
+
+ /**
+ * Returns the value of a static attribute.
+ * This also works for attributes that are declared protected or private.
+ *
+ * @throws Exception
+ * @throws ReflectionException
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function getStaticAttribute(string $className, string $attributeName)
+ {
+ self::createWarning('getStaticAttribute() is deprecated and will be removed in PHPUnit 9.');
+
+ if (!\class_exists($className)) {
+ throw InvalidArgumentHelper::factory(1, 'class name');
+ }
+
+ if (!self::isValidClassAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(2, 'valid attribute name');
+ }
+
+ $class = new ReflectionClass($className);
+
+ while ($class) {
+ $attributes = $class->getStaticProperties();
+
+ if (\array_key_exists($attributeName, $attributes)) {
+ return $attributes[$attributeName];
+ }
+
+ $class = $class->getParentClass();
+ }
+
+ throw new Exception(
+ \sprintf(
+ 'Attribute "%s" not found in class.',
+ $attributeName
+ )
+ );
+ }
+
+ /**
+ * Returns the value of an object's attribute.
+ * This also works for attributes that are declared protected or private.
+ *
+ * @param object $object
+ *
+ * @throws Exception
+ *
+ * @deprecated https://github.com/sebastianbergmann/phpunit/issues/3338
+ * @codeCoverageIgnore
+ */
+ public static function getObjectAttribute($object, string $attributeName)
+ {
+ self::createWarning('getObjectAttribute() is deprecated and will be removed in PHPUnit 9.');
+
+ if (!\is_object($object)) {
+ throw InvalidArgumentHelper::factory(1, 'object');
+ }
+
+ if (!self::isValidClassAttributeName($attributeName)) {
+ throw InvalidArgumentHelper::factory(2, 'valid attribute name');
+ }
+
+ try {
+ $reflector = new ReflectionObject($object);
+
+ do {
+ try {
+ $attribute = $reflector->getProperty($attributeName);
+
+ if (!$attribute || $attribute->isPublic()) {
+ return $object->$attributeName;
+ }
+
+ $attribute->setAccessible(true);
+ $value = $attribute->getValue($object);
+ $attribute->setAccessible(false);
+
+ return $value;
+ } catch (ReflectionException $e) {
+ }
+ } while ($reflector = $reflector->getParentClass());
+ } catch (ReflectionException $e) {
+ }
+
+ throw new Exception(
+ \sprintf(
+ 'Attribute "%s" not found in object.',
+ $attributeName
+ )
+ );
+ }
+
+ /**
+ * Mark the test as incomplete.
+ *
+ * @throws IncompleteTestError
+ */
+ public static function markTestIncomplete(string $message = ''): void
+ {
+ throw new IncompleteTestError($message);
+ }
+
+ /**
+ * Mark the test as skipped.
+ *
+ * @throws SkippedTestError
+ * @throws SyntheticSkippedError
+ */
+ public static function markTestSkipped(string $message = ''): void
+ {
+ if ($hint = self::detectLocationHint($message)) {
+ $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
+ \array_unshift($trace, $hint);
+
+ throw new SyntheticSkippedError($hint['message'], 0, $hint['file'], (int) $hint['line'], $trace);
+ }
+
+ throw new SkippedTestError($message);
+ }
+
+ /**
+ * Return the current assertion count.
+ */
+ public static function getCount(): int
+ {
+ return self::$count;
+ }
+
+ /**
+ * Reset the assertion counter.
+ */
+ public static function resetCount(): void
+ {
+ self::$count = 0;
+ }
+
+ private static function detectLocationHint(string $message): ?array
+ {
+ $hint = null;
+ $lines = \preg_split('/\r\n|\r|\n/', $message);
+
+ while (\strpos($lines[0], '__OFFSET') !== false) {
+ $offset = \explode('=', \array_shift($lines));
+
+ if ($offset[0] === '__OFFSET_FILE') {
+ $hint['file'] = $offset[1];
+ }
+
+ if ($offset[0] === '__OFFSET_LINE') {
+ $hint['line'] = $offset[1];
+ }
+ }
+
+ if ($hint) {
+ $hint['message'] = \implode(\PHP_EOL, $lines);
+ }
+
+ return $hint;
+ }
+
+ private static function isValidObjectAttributeName(string $attributeName): bool
+ {
+ return (bool) \preg_match('/[^\x00-\x1f\x7f-\x9f]+/', $attributeName);
+ }
+
+ private static function isValidClassAttributeName(string $attributeName): bool
+ {
+ return (bool) \preg_match('/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/', $attributeName);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ private static function createWarning(string $warning): void
+ {
+ foreach (\debug_backtrace() as $step) {
+ if (isset($step['object']) && $step['object'] instanceof TestCase) {
+ \assert($step['object'] instanceof TestCase);
+
+ $step['object']->addWarning($warning);
+
+ break;
+ }
+ }
+ }
+}
diff --git a/tests/bench/ProjectFactoryBench.php b/tests/bench/ProjectFactoryBench.php
new file mode 100644
index 00000000..73274eae
--- /dev/null
+++ b/tests/bench/ProjectFactoryBench.php
@@ -0,0 +1,30 @@
+factory = \phpDocumentor\Reflection\Php\ProjectFactory::createInstance();
+ }
+
+ /**
+ * @Revs({1, 8, 64, 1024})
+ */
+ public function benchCreateSingleFileProject()
+ {
+ $this->factory->create('myProject', [new LocalFile(__DIR__ . '/../assets/phpunit_assert.php')]);
+ }
+}
diff --git a/tests/coverage-checker.php b/tests/coverage-checker.php
new file mode 100755
index 00000000..5ba8bf72
--- /dev/null
+++ b/tests/coverage-checker.php
@@ -0,0 +1,31 @@
+xpath('//metrics');
+$totalElements = 0;
+$checkedElements = 0;
+
+foreach ($metrics as $metric) {
+ $totalElements += (int) $metric['elements'];
+ $checkedElements += (int) $metric['coveredelements'];
+}
+
+$coverage = ($checkedElements / $totalElements) * 100;
+
+if ($coverage < $percentage) {
+ echo 'Code coverage is ' . $coverage . '%, which is below the accepted ' . $percentage . '%' . PHP_EOL;
+ exit(1);
+}
+
+echo 'Code coverage is ' . $coverage . '% - OK!' . PHP_EOL;
diff --git a/tests/example.file.php b/tests/example.file.php
new file mode 100644
index 00000000..2bac0180
--- /dev/null
+++ b/tests/example.file.php
@@ -0,0 +1,275 @@
+
+ */
+
+namespace Luigi\Pizza
+{
+ /**
+ * The high VAT percentage.
+ *
+ * This describes the VAT percentage for all non-food items.
+ *
+ * @var integer
+ */
+ const VAT_HIGH = 21;
+
+ /**
+ * The low VAT percentage.
+ *
+ * This describes the VAT percentage for all non-food items.
+ *
+ * @var integer
+ */
+ define('Luigi\Pizza\VAT_LOW', 6);
+
+ /**
+ * @var integer SIZE_5CM A 5 centimeter pizza size.
+ * @var integer SIZE_10CM A 10 centimeter pizza size.
+ * @var integer SIZE_15CM A 15 centimeter pizza size.
+ * @var integer SIZE_20CM A 20 centimeter pizza size.
+ * @var integer DEFAULT_SIZE The default Pizza size if you don't provide your own.
+ */
+ const SIZE_5CM = 5, SIZE_10CM = 10, SIZE_15CM = 15, SIZE_20CM = 20, DEFAULT_SIZE = SIZE_20CM;
+
+ trait ExampleNestedTrait
+ {
+ private function exampleTraitMethod()
+ {
+ }
+ }
+
+ /**
+ * A single item with a value
+ *
+ * Represents something with a price.
+ */
+ trait HasPrice
+ {
+ use ExampleNestedTrait;
+
+ private $temporaryPrice = 1;
+
+ public function getPrice()
+ {
+ return $this->price;
+ }
+ }
+
+ /**
+ * Any class implementing this interface has an associated price.
+ *
+ * Using this interface we can easily add the price of all components in a pizza by checking for this interface and
+ * adding the prices together for all components.
+ */
+ interface Valued
+ {
+ const BASE_PRICE = 1;
+
+ function getPrice();
+ }
+
+ interface Series
+ {
+ }
+
+ interface Style extends Valued
+ {
+ }
+
+ interface Sauce extends Valued
+ {
+ }
+
+ interface Topping extends Valued, \Serializable
+ {
+ }
+
+ abstract class PizzaComponentFactory implements \Traversable, Valued
+ {
+ public function add()
+ {
+ }
+
+ /**
+ * Calculates the price for this specific component.
+ *
+ * @param float[] $...additionalPrices Additional costs may be passed
+ *
+ * @return float
+ */
+ abstract protected function calculatePrice();
+ }
+
+ final class StyleFactory extends PizzaComponentFactory
+ {
+ public function getPrice()
+ {
+ }
+
+ protected function calculatePrice()
+ {
+ }
+ }
+
+ final class SauceFactory extends PizzaComponentFactory
+ {
+ public function getPrice()
+ {
+ }
+
+ protected function calculatePrice()
+ {
+ }
+ }
+
+ final class ToppingFactory extends PizzaComponentFactory
+ {
+ public function getPrice()
+ {
+ }
+
+ protected function calculatePrice()
+ {
+ }
+ }
+
+ final class ItalianStyle implements Style
+ {
+ use HasPrice;
+
+ private $price = 2.0;
+ }
+
+ final class AmericanStyle implements Style
+ {
+ use HasPrice;
+
+ private $price = 1.5;
+ }
+
+ final class TomatoSauce implements Sauce
+ {
+ use HasPrice;
+
+ private $price = 1.5;
+ }
+
+ final class CheeseTopping implements Topping
+ {
+ use HasPrice;
+
+ private $price = 1.5;
+
+ public function serialize()
+ {
+ }
+
+ public function unserialize($serialized)
+ {
+ }
+ }
+}
+
+namespace Luigi
+{
+ /**
+ * Class representing a single Pizza.
+ *
+ * This is Luigi's famous Pizza.
+ *
+ * @package Luigi\Pizza
+ */
+ class Pizza implements Pizza\Valued
+ {
+ /**
+ * The packaging method used to transport the pizza.
+ */
+ const PACKAGING = 'box';
+
+ const
+ /** @var string DELIVERY designates that the delivery method is to deliver the pizza to the customer. */
+ DELIVERY = 'delivery',
+ /** @var string PICKUP designates that the delivery method is that the customer picks the pizza up. */
+ PICKUP = 'pickup';
+
+ /** @var static contains the active instance for this Pizza. */
+ static private $instance;
+
+ /**
+ * @var Pizza\Style $style
+ * @var Pizza\Sauce|null $sauce
+ * @var Pizza\Topping[] $toppings
+ */
+ private $style, $sauce, $toppings;
+
+ /**
+ * The size of the pizza in centimeters, defaults to 20cm.
+ *
+ * @var int
+ */
+ public $size = \Luigi\Pizza\SIZE_20CM;
+
+ var $legacy; // don't use this anymore!
+
+ protected
+ /** @var string $packaging The type of packaging for this Pizza */
+ $packaging = self::PACKAGING,
+ /** @var string $deliveryMethod Is the customer picking this pizza up or must it be delivered? */
+ $deliveryMethod;
+
+ private function __construct(Pizza\Style $style)
+ {
+ $this->style = $style;
+ }
+
+ /**
+ * Creates a new instance of a Pizza.
+ *
+ * This method can be used to instantiate a new object of this class which can then be retrieved using
+ * {@see self::getInstance()}.
+ *
+ * @param Pizza\Style $style
+ *
+ * @see self::getInstance to retrieve the pizza object.
+ *
+ * @return void
+ */
+ public static function createInstance(Pizza\Style $style)
+ {
+ self::$instance = new static($style);
+ }
+
+ /**
+ * @return self
+ */
+ static function getInstance()
+ {
+ return self::$instance;
+ }
+
+ final public function setSauce(Pizza\Sauce $sauce)
+ {
+ $this->sauce = $sauce;
+ }
+
+ final public function addTopping(Pizza\Topping $topping)
+ {
+ $this->toppings[] = $topping;
+ }
+
+ public function setSize(&$size = \Luigi\Pizza\SIZE_20CM)
+ {
+ }
+
+ public function getPrice()
+ {
+ }
+ }
+}
diff --git a/tests/integration/AsymmetricAccessorTest.php b/tests/integration/AsymmetricAccessorTest.php
new file mode 100644
index 00000000..ee6dfc4f
--- /dev/null
+++ b/tests/integration/AsymmetricAccessorTest.php
@@ -0,0 +1,52 @@
+= 5.2')]
+final class AsymmetricAccessorTest extends TestCase
+{
+ public function testAsymmetricAccessor(): void
+ {
+ $file = __DIR__ . '/data/PHP84/AsymmetricAccessor.php';
+ $projectFactory = ProjectFactory::createInstance();
+ $project = $projectFactory->create('My project', [new LocalFile($file)]);
+
+ $class = $project->getFiles()[$file]->getClasses()['\AsymmetricAccessor'];
+
+ self::assertEquals(
+ 'public',
+ $class->getProperties()['\AsymmetricAccessor::$pizza']->getVisibility()->getReadVisibility(),
+ );
+ self::assertEquals(
+ 'private',
+ $class->getProperties()['\AsymmetricAccessor::$pizza']->getVisibility()->getWriteVisibility(),
+ );
+ }
+
+ public function testAsyncPropertyPromotion(): void
+ {
+ $file = __DIR__ . '/data/PHP84/AsymmetricPropertyPromotion.php';
+ $projectFactory = ProjectFactory::createInstance();
+ $project = $projectFactory->create('My project', [new LocalFile($file)]);
+
+
+ $class = $project->getFiles()[$file]->getClasses()['\AsymmetricPropertyPromotion'];
+
+ self::assertEquals(
+ 'public',
+ $class->getProperties()['\AsymmetricPropertyPromotion::$pizza']->getVisibility()->getReadVisibility(),
+ );
+ self::assertEquals(
+ 'protected',
+ $class->getProperties()['\AsymmetricPropertyPromotion::$pizza']->getVisibility()->getWriteVisibility(),
+ );
+ }
+}
diff --git a/tests/integration/ClassesTest.php b/tests/integration/ClassesTest.php
new file mode 100644
index 00000000..cf310e0e
--- /dev/null
+++ b/tests/integration/ClassesTest.php
@@ -0,0 +1,168 @@
+fixture = ProjectFactory::createInstance();
+ $this->project = $this->fixture->create(
+ 'MyProject',
+ [
+ new LocalFile(self::FILE_PIZZA),
+ new LocalFile(self::FILE_LUIGI_PIZZA),
+ ]
+ );
+ }
+
+ public function testItHasAllConstants() : void
+ {
+ $file = $this->project->getFiles()[self::FILE_PIZZA];
+
+ $className = '\\Pizza';
+ $constantName = '\\Pizza::PACKAGING';
+
+ $class = $this->fetchClassFromFile($className, $file);
+
+ $this->assertArrayHasKey($constantName, $class->getConstants());
+ $constant = $class->getConstants()[$constantName];
+
+ $this->assertInstanceOf(Constant::class, $constant);
+
+ $this->assertArrayHasKey('\\OVEN_TEMPERATURE', $file->getConstants());
+ $this->assertArrayHasKey('\\MAX_OVEN_TEMPERATURE', $file->getConstants());
+ }
+
+ public function testTypedPropertiesReturnTheirType() : void
+ {
+ $fileName = self::FILE_LUIGI_PIZZA;
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ /** @var Class_ $pizzaClass */
+ $pizzaClass = $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza'];
+ $this->assertArrayHasKey('\\Luigi\\Pizza::$size', $pizzaClass->getProperties());
+ $this->assertEquals(new Integer(), $pizzaClass->getProperties()['\\Luigi\\Pizza::$size']->getType());
+ }
+
+ public function testUsedTraitsAreIncludedInClass() : void
+ {
+ $fileName = self::FILE_LUIGI_PIZZA;
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ /** @var Class_ $pizzaClass */
+ $pizzaClass = $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza'];
+ $this->assertEquals(['\\Luigi\\ExampleNestedTrait' => new Fqsen('\\Luigi\\ExampleNestedTrait')], $pizzaClass->getUsedTraits());
+ }
+
+ public function testWithNamespacedClass() : void
+ {
+ $fileName = self::FILE_LUIGI_PIZZA;
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertArrayHasKey('\\Luigi\\Pizza', $project->getFiles()[$fileName]->getClasses());
+ $this->assertEquals('\Pizza', $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza']->getParent());
+ $this->assertArrayHasKey(
+ '\\Luigi\\Pizza::$instance',
+ $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza']->getProperties()
+ );
+
+ $methods = $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza']->getMethods();
+ $this->assertArrayHasKey(
+ '\\Luigi\\Pizza::__construct()',
+ $methods
+ );
+
+ $this->assertEquals('style', $methods['\\Luigi\\Pizza::__construct()']->getArguments()[0]->getName());
+ $this->assertEquals(
+ new Object_(new Fqsen('\\Luigi\\Pizza\Style')),
+ $methods['\\Luigi\\Pizza::__construct()']->getArguments()[0]->getType()
+ );
+ }
+
+ public function testWithUsedParent() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/StyleFactory.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertArrayHasKey('\\Luigi\\StyleFactory', $project->getFiles()[$fileName]->getClasses());
+ $this->assertEquals(
+ '\\Luigi\\Pizza\\PizzaComponentFactory',
+ $project->getFiles()[$fileName]->getClasses()['\\Luigi\\StyleFactory']->getParent()
+ );
+ }
+
+ public function testWithInterface() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/Valued.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey('\\Luigi\\Valued', $project->getFiles()[$fileName]->getInterfaces());
+ }
+
+ public function testWithTrait() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/ExampleNestedTrait.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey('\\Luigi\\ExampleNestedTrait', $project->getFiles()[$fileName]->getTraits());
+ }
+
+ private function fetchClassFromFile(string $className, PhpFile $file)
+ {
+ $this->assertArrayHasKey($className, $file->getClasses());
+
+ return $file->getClasses()[$className];
+ }
+}
diff --git a/tests/integration/EnumTest.php b/tests/integration/EnumTest.php
new file mode 100644
index 00000000..8d42d1e4
--- /dev/null
+++ b/tests/integration/EnumTest.php
@@ -0,0 +1,124 @@
+fixture = ProjectFactory::createInstance();
+ $this->project = $this->fixture->create(
+ 'Enums',
+ [
+ new LocalFile(self::FILE),
+ new LocalFile(self::BACKED_ENUM),
+ new LocalFile(self::ENUM_WITH_CONSTANT),
+ new LocalFile(self::ENUM_CONSUMER),
+ ]
+ );
+ }
+
+ public function testFileHasEnum(): void
+ {
+ $file = $this->project->getFiles()[self::FILE];
+
+ $enum = $file->getEnums()['\MyNamespace\MyEnum'];
+ self::assertInstanceOf(Enum_::class, $enum);
+ self::assertCount(2, $enum->getCases());
+ self::assertNull($enum->getBackedType());
+ self::assertArrayHasKey('\MyNamespace\MyEnum::VALUE1', $enum->getCases());
+ self::assertArrayHasKey('\MyNamespace\MyEnum::VALUE2', $enum->getCases());
+ }
+
+ public function testEnumWithConstant(): void
+ {
+ $file = $this->project->getFiles()[self::ENUM_WITH_CONSTANT];
+
+ $enum = $file->getEnums()['\MyNamespace\MyEnumWithConstant'];
+ self::assertInstanceOf(Enum_::class, $enum);
+ self::assertCount(1, $enum->getConstants());
+ self::assertArrayHasKey('\MyNamespace\MyEnumWithConstant::MYCONST', $enum->getConstants());
+ self::assertSame("'MyConstValue'", $enum->getConstants()['\MyNamespace\MyEnumWithConstant::MYCONST']->getValue());
+ }
+
+ public function testBackedEnum(): void
+ {
+ $file = $this->project->getFiles()[self::BACKED_ENUM];
+
+ $enum = $file->getEnums()['\MyNamespace\MyBackedEnum'];
+ self::assertInstanceOf(Enum_::class, $enum);
+ self::assertCount(2, $enum->getCases());
+ self::assertEquals(new String_(), $enum->getBackedType());
+ self::assertArrayHasKey('\MyNamespace\MyBackedEnum::VALUE1', $enum->getCases());
+ self::assertArrayHasKey('\MyNamespace\MyBackedEnum::VALUE2', $enum->getCases());
+
+ self::assertSame("'this is value1'", $enum->getCases()['\MyNamespace\MyBackedEnum::VALUE1']->getValue());
+ self::assertSame("'this is value2'", $enum->getCases()['\MyNamespace\MyBackedEnum::VALUE2']->getValue());
+ }
+
+ public function testEnumSupportInProperty(): void
+ {
+ $file = $this->project->getFiles()[self::ENUM_CONSUMER];
+
+ $class = $file->getClasses()['\MyNamespace\EnumConsumer'];
+
+ self::assertEquals(
+ '\MyNamespace\MyEnum::VALUE1',
+ $class->getProperties()['\MyNamespace\EnumConsumer::$myEnum']->getDefault()
+ );
+
+ self::assertEquals(
+ new Object_(new Fqsen('\MyNamespace\MyEnum')),
+ $class->getProperties()['\MyNamespace\EnumConsumer::$myEnum']->getType()
+ );
+ }
+
+ public function testEnumSupportInMethod(): void
+ {
+ $file = $this->project->getFiles()[self::ENUM_CONSUMER];
+
+ $class = $file->getClasses()['\MyNamespace\EnumConsumer'];
+ $method = $class->getMethods()['\MyNamespace\EnumConsumer::consume()'];
+
+ self::assertEquals(
+ new Object_(new Fqsen('\MyNamespace\MyEnum')),
+ $method->getReturnType()
+ );
+
+ self::assertEquals(
+ new Object_(new Fqsen('\MyNamespace\MyEnum')),
+ $method->getArguments()[0]->getType()
+ );
+
+ //This should be fixed in #219
+// self::assertEquals(
+// '\MyNamespace\MyEnum::VALUE1',
+// $method->getArguments()[0]->getDefault()
+// );
+ }
+}
diff --git a/tests/integration/FileDocblockTest.php b/tests/integration/FileDocblockTest.php
new file mode 100644
index 00000000..d6fa98d6
--- /dev/null
+++ b/tests/integration/FileDocblockTest.php
@@ -0,0 +1,94 @@
+fixture = ProjectFactory::createInstance();
+ }
+
+ /**
+ * @dataProvider fileProvider
+ */
+ public function testFileDocblock(string $fileName) : void
+ {
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertEquals(
+ 'This file is part of phpDocumentor.',
+ $project->getFiles()[$fileName]->getDocBlock()->getSummary()
+ );
+ }
+
+ public static function fileProvider() : array
+ {
+ return [
+ [ __DIR__ . '/data/GlobalFiles/empty.php' ],
+ [ __DIR__ . '/data/GlobalFiles/empty_with_declare.php' ],
+ [ __DIR__ . '/data/GlobalFiles/empty_shebang.php' ],
+ [ __DIR__ . '/data/GlobalFiles/psr12.php' ],
+ [ __DIR__ . '/data/GlobalFiles/docblock_followed_by_html.php' ],
+ ];
+ }
+
+ public function testConditionalFunctionDefine() : void
+ {
+ $fileName = __DIR__ . '/data/GlobalFiles/conditional_function.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertCount(
+ 4,
+ $project->getFiles()[$fileName]->getFunctions()
+ );
+ }
+
+ public function testGlobalNamespacedFunctionDefine() : void
+ {
+ $fileName = __DIR__ . '/data/GlobalFiles/global_namspaced_function.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertCount(
+ 1,
+ $project->getFiles()[$fileName]->getFunctions()
+ );
+ }
+
+ public function testFileWithInlineFunction() : void
+ {
+ $fileName = __DIR__ . '/data/GlobalFiles/inline_function.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertCount(
+ 1,
+ $project->getFiles()[$fileName]->getClasses()
+ );
+ }
+}
diff --git a/tests/integration/Metadata/Hook.php b/tests/integration/Metadata/Hook.php
new file mode 100644
index 00000000..7ba23274
--- /dev/null
+++ b/tests/integration/Metadata/Hook.php
@@ -0,0 +1,25 @@
+hook = $hook;
+ }
+
+ public function key(): string
+ {
+ return "project-metadata";
+ }
+
+ public function hook(): string
+ {
+ return $this->hook;
+ }
+}
diff --git a/tests/integration/Metadata/HookStrategy.php b/tests/integration/Metadata/HookStrategy.php
new file mode 100644
index 00000000..afcaebcd
--- /dev/null
+++ b/tests/integration/Metadata/HookStrategy.php
@@ -0,0 +1,29 @@
+expr instanceof FuncCall && ((string)$object->expr->name) === 'hook';
+ }
+
+ public function create(ContextStack $context, object $object, StrategyContainer $strategies): void
+ {
+ $method = $context->peek();
+ $method->addMetadata(new Hook($object->expr->args[0]->value->value));
+ }
+}
diff --git a/tests/integration/Metadata/example.php b/tests/integration/Metadata/example.php
new file mode 100644
index 00000000..d25fd818
--- /dev/null
+++ b/tests/integration/Metadata/example.php
@@ -0,0 +1,10 @@
+addStrategy(new HookStrategy());
+
+ /** @var Project $project */
+ $project = $projectFactory->create('My project', [new LocalFile(self::FILE)]);
+ $class = $project->getFiles()[self::FILE]->getClasses()['\myHookUsingClass'];
+
+ self::assertArrayHasKey('project-metadata', $class->getMethods()['\myHookUsingClass::test()']->getMetadata());
+ }
+}
diff --git a/tests/integration/PHP8/ConstructorPromotionTest.php b/tests/integration/PHP8/ConstructorPromotionTest.php
new file mode 100644
index 00000000..46e3b84f
--- /dev/null
+++ b/tests/integration/PHP8/ConstructorPromotionTest.php
@@ -0,0 +1,146 @@
+fixture = ProjectFactory::createInstance();
+ $this->project = $this->fixture->create(
+ 'PHP8',
+ [
+ new LocalFile(self::FILE),
+ ]
+ );
+ }
+
+ public function testPropertiesAreCreated() : void
+ {
+ $file = $this->project->getFiles()[self::FILE];
+ $class = $file->getClasses()['\\PHP8\\ConstructorPromotion'];
+
+ $constructor = $this->expectedContructorMethod();
+ $constructor->addArgument(new Argument('name', new String_(), "'default name'"));
+ $constructor->addArgument(new Argument('email', new Object_(new Fqsen('\\PHP8\\Email'))));
+ $constructor->addArgument(new Argument('birth_date', new Object_(new Fqsen('\\' . \DateTimeImmutable::class))));
+
+ self::assertEquals($constructor, $class->getMethods()['\PHP8\ConstructorPromotion::__construct()']);
+ self::assertEquals(
+ [
+ '\PHP8\ConstructorPromotion::$name' => $this->expectedNameProperty(),
+ '\PHP8\ConstructorPromotion::$email' => $this->expectedEmailProperty(),
+ '\PHP8\ConstructorPromotion::$birth_date' => $this->expectedBirthDateProperty()
+ ],
+ $class->getProperties()
+ );
+ }
+
+ private function expectedContructorMethod(): Method
+ {
+ $constructor = new Method(
+ new Fqsen('\PHP8\ConstructorPromotion::__construct()'),
+ new Visibility(Visibility::PUBLIC_),
+ new DocBlock(
+ 'Constructor with promoted properties',
+ null,
+ [
+ new Param(
+ 'name',
+ new String_(),
+ false,
+ new DocBlock\Description('my docblock name')
+ )
+ ],
+ new Context('PHP8', ['DateTimeImmutable' => 'DateTimeImmutable'])
+ ),
+ false,
+ false,
+ false,
+ new Location(16, 218),
+ new Location(27, 517)
+ );
+ return $constructor;
+ }
+
+ private function expectedNameProperty(): Property
+ {
+ $name = new Property(
+ new Fqsen('\PHP8\ConstructorPromotion::$name'),
+ new Visibility(Visibility::PUBLIC_),
+ new DocBlock(
+ 'Summary',
+ new DocBlock\Description('Description'),
+ [
+ new Var_('name', new String_(), new DocBlock\Description('property description'))
+ ],
+ new Context('PHP8', ['DateTimeImmutable' => 'DateTimeImmutable'])
+ ),
+ "'default name'",
+ false,
+ new Location(24, 393),
+ new Location(24, 428),
+ new String_()
+ );
+ return $name;
+ }
+
+ private function expectedEmailProperty(): Property
+ {
+ $email = new Property(
+ new Fqsen('\PHP8\ConstructorPromotion::$email'),
+ new Visibility(Visibility::PROTECTED_),
+ null,
+ null,
+ false,
+ new Location(25, 439),
+ new Location(25, 460),
+ new Object_(new Fqsen('\\PHP8\\Email'))
+ );
+ return $email;
+ }
+
+ private function expectedBirthDateProperty(): Property
+ {
+ $birthDate = new Property(
+ new Fqsen('\PHP8\ConstructorPromotion::$birth_date'),
+ new Visibility(Visibility::PRIVATE_),
+ null,
+ null,
+ false,
+ new Location(26, 471),
+ new Location(26, 507),
+ new Object_(new Fqsen('\\' . \DateTimeImmutable::class))
+ );
+ return $birthDate;
+ }
+}
diff --git a/tests/integration/PHP8/MixedTypeTest.php b/tests/integration/PHP8/MixedTypeTest.php
new file mode 100644
index 00000000..dbe16bb0
--- /dev/null
+++ b/tests/integration/PHP8/MixedTypeTest.php
@@ -0,0 +1,42 @@
+fixture = ProjectFactory::createInstance();
+
+ /** @var Project $project */
+ $project = $this->fixture->create(
+ 'PHP8',
+ [
+ new LocalFile(self::FILE),
+ ]
+ );
+
+ $file = $project->getFiles()[self::FILE];
+
+ $class = $file->getClasses()['\PHP8\MixedType'];
+
+ self::assertEquals(new Mixed_(), $class->getMethods()['\PHP8\MixedType::getProperty()']->getReturnType());
+ self::assertEquals(new Mixed_(), $class->getMethods()['\PHP8\MixedType::setProperty()']->getArguments()[0]->getType());
+ self::assertEquals(new Mixed_(), $class->getProperties()['\PHP8\MixedType::$property']->getType());
+ }
+}
diff --git a/tests/integration/PHP8/StaticTypeTest.php b/tests/integration/PHP8/StaticTypeTest.php
new file mode 100644
index 00000000..b00533f5
--- /dev/null
+++ b/tests/integration/PHP8/StaticTypeTest.php
@@ -0,0 +1,44 @@
+fixture = ProjectFactory::createInstance();
+
+ /** @var Project $project */
+ $project = $this->fixture->create(
+ 'PHP8',
+ [
+ new LocalFile(self::FILE),
+ ]
+ );
+
+ $file = $project->getFiles()[self::FILE];
+
+ $class = $file->getClasses()['\PHP8\StaticType'];
+
+ self::assertEquals(new Static_(), $class->getMethods()['\PHP8\StaticType::getProperty()']->getReturnType());
+ self::assertEquals(new Mixed_(), $class->getMethods()['\PHP8\StaticType::setProperty()']->getArguments()[0]->getType());
+ self::assertEquals(null, $class->getProperties()['\PHP8\StaticType::$property']->getType());
+ self::assertTrue($class->getProperties()['\PHP8\StaticType::$property']->isStatic());
+ }
+}
diff --git a/tests/integration/PHP8/UnionTypesTest.php b/tests/integration/PHP8/UnionTypesTest.php
new file mode 100644
index 00000000..bebfae04
--- /dev/null
+++ b/tests/integration/PHP8/UnionTypesTest.php
@@ -0,0 +1,50 @@
+fixture = ProjectFactory::createInstance();
+
+ /** @var Project $project */
+ $project = $this->fixture->create(
+ 'PHP8',
+ [
+ new LocalFile(self::FILE),
+ ]
+ );
+
+ $file = $project->getFiles()[self::FILE];
+
+ $class = $file->getClasses()['\PHP8\UnionTypes'];
+
+ self::assertEquals(new Compound([new String_(), new Null_(), new Object_(new Fqsen('\Foo\Date'))]), $class->getMethods()['\PHP8\UnionTypes::union()']->getReturnType());
+ self::assertEquals(new Compound([new Integer(), new False_()]), $class->getMethods()['\PHP8\UnionTypes::union()']->getArguments()[0]->getType());
+ self::assertEquals(new Compound([new String_(), new Null_(), new False_()]), $class->getProperties()['\PHP8\UnionTypes::$property']->getType());
+ }
+}
diff --git a/tests/integration/ProjectCreationTest.php b/tests/integration/ProjectCreationTest.php
new file mode 100644
index 00000000..fe545d35
--- /dev/null
+++ b/tests/integration/ProjectCreationTest.php
@@ -0,0 +1,239 @@
+fixture = ProjectFactory::createInstance();
+ }
+
+ public function testCreateProjectWithFunctions() : void
+ {
+ $fileName = __DIR__ . '/data/simpleFunction.php';
+
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $file = $project->getFiles()[$fileName];
+ $this->assertArrayHasKey('\simpleFunction()', $file->getFunctions());
+
+ /** @var Function_ $function */
+ $function = $file->getFunctions()['\simpleFunction()'];
+ $this->assertSame('\simpleFunction()', (string) $function->getFqsen());
+ $this->assertCount(1, $function->getArguments());
+ }
+
+ public function testCreateProjectWithClass() : void
+ {
+ $fileName = __DIR__ . '/data/Pizza.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertArrayHasKey('\\Pizza', $project->getFiles()[$fileName]->getClasses());
+ $this->assertArrayHasKey(
+ '\\Pizza::PACKAGING',
+ $project->getFiles()[$fileName]->getClasses()['\\Pizza']->getConstants()
+ );
+ $constant = $project->getFiles()[$fileName]->getClasses()['\\Pizza']->getConstants()['\\Pizza::PACKAGING'];
+
+ $this->assertEquals('\'box\'', $constant->getValue());
+ }
+
+ public function testTypedPropertiesReturnTheirType() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/Pizza.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ /** @var Class_ $pizzaClass */
+ $pizzaClass = $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza'];
+ $this->assertArrayHasKey('\\Luigi\\Pizza::$size', $pizzaClass->getProperties());
+ $this->assertEquals(new Integer(), $pizzaClass->getProperties()['\\Luigi\\Pizza::$size']->getType());
+ }
+
+ public function testFileWithDocBlock() : void
+ {
+ $fileName = __DIR__ . '/data/Pizza.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertInstanceOf(Docblock::class, $project->getFiles()[$fileName]->getDocBlock());
+ }
+
+ public function testWithNamespacedClass() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/Pizza.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertArrayHasKey('\\Luigi\\Pizza', $project->getFiles()[$fileName]->getClasses());
+ $this->assertEquals('\Pizza', $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza']->getParent());
+ $this->assertArrayHasKey(
+ '\\Luigi\\Pizza::$instance',
+ $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza']->getProperties()
+ );
+
+ $methods = $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza']->getMethods();
+ $this->assertArrayHasKey(
+ '\\Luigi\\Pizza::__construct()',
+ $methods
+ );
+
+ $this->assertEquals('style', $methods['\\Luigi\\Pizza::__construct()']->getArguments()[0]->getName());
+ $this->assertEquals(
+ new Object_(new Fqsen('\\Luigi\\Pizza\Style')),
+ $methods['\\Luigi\\Pizza::__construct()']->getArguments()[0]->getType()
+ );
+ }
+
+ public function testDocblockOfMethodIsProcessed() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/Pizza.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+
+ $methods = $project->getFiles()[$fileName]->getClasses()['\\Luigi\\Pizza']->getMethods();
+
+ $createInstanceMethod = $methods['\\Luigi\\Pizza::createInstance()'];
+
+ $this->assertInstanceOf(DocBlock::class, $createInstanceMethod->getDocBlock());
+
+ $docblock = $createInstanceMethod->getDocBlock();
+ /** @var Param[] $params */
+ $params = $docblock->getTagsByName('param');
+
+ /** @var Object_ $objectType */
+ $objectType = $params[0]->getType();
+
+ $this->assertEquals(new Fqsen('\Luigi\Pizza\Style'), $objectType->getFqsen());
+ }
+
+ public function testWithUsedParent() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/StyleFactory.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertArrayHasKey('\\Luigi\\StyleFactory', $project->getFiles()[$fileName]->getClasses());
+ $this->assertEquals(
+ '\\Luigi\\Pizza\\PizzaComponentFactory',
+ $project->getFiles()[$fileName]->getClasses()['\\Luigi\\StyleFactory']->getParent()
+ );
+ }
+
+ public function testWithInterface() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/Valued.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey('\\Luigi\\Valued', $project->getFiles()[$fileName]->getInterfaces());
+ }
+
+ public function testWithTrait() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/ExampleNestedTrait.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey('\\Luigi\\ExampleNestedTrait', $project->getFiles()[$fileName]->getTraits());
+ }
+
+ public function testWithGlobalConstants() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/constants.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey('\\Luigi\\OVEN_TEMPERATURE', $project->getFiles()[$fileName]->getConstants());
+ $this->assertArrayHasKey('\\Luigi\\MAX_OVEN_TEMPERATURE', $project->getFiles()[$fileName]->getConstants());
+ $this->assertArrayHasKey('\\OUTSIDE_OVEN_TEMPERATURE', $project->getFiles()[$fileName]->getConstants());
+ $this->assertArrayHasKey('\\LuigiFoo\\_OUTSIDE_OVEN_TEMPERATURE', $project->getFiles()[$fileName]->getConstants());
+ }
+
+ public function testInterfaceExtends() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/Packing.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey('\\Luigi\\Packing', $project->getFiles()[$fileName]->getInterfaces());
+ $interface = current($project->getFiles()[$fileName]->getInterfaces());
+
+ $this->assertEquals(['\\Packing' => new Fqsen('\\Packing')], $interface->getParents());
+ }
+
+ public function testMethodReturnType() : void
+ {
+ $fileName = __DIR__ . '/data/Packing.php';
+ $project = $this->fixture->create(
+ 'MyProject',
+ [new LocalFile($fileName)]
+ );
+
+ $this->assertArrayHasKey('\\Packing', $project->getFiles()[$fileName]->getInterfaces());
+ $interface = current($project->getFiles()[$fileName]->getInterfaces());
+
+ $this->assertEquals(new String_(), $interface->getMethods()['\Packing::getName()']->getReturnType());
+ }
+}
diff --git a/tests/integration/ProjectNamespaceTest.php b/tests/integration/ProjectNamespaceTest.php
new file mode 100644
index 00000000..cc4e36df
--- /dev/null
+++ b/tests/integration/ProjectNamespaceTest.php
@@ -0,0 +1,71 @@
+fixture = $this->fixture = ProjectFactory::createInstance();
+ }
+
+ public function testWithNamespacedClass() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/Pizza.php';
+ $project = $this->fixture->create(
+ 'My Project',
+ [ new LocalFile($fileName) ]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertArrayHasKey('\\Luigi', $project->getNamespaces());
+ $this->assertEquals(
+ ['\\Luigi\\Pizza' => new Fqsen('\\Luigi\\Pizza')],
+ $project->getNamespaces()['\\Luigi']->getClasses()
+ );
+ }
+
+ public function testWithNamespacedConstant() : void
+ {
+ $fileName = __DIR__ . '/data/Luigi/constants.php';
+ $project = $this->fixture->create(
+ 'My Project',
+ [ new LocalFile($fileName) ]
+ );
+
+ $this->assertArrayHasKey($fileName, $project->getFiles());
+ $this->assertArrayHasKey('\\Luigi', $project->getNamespaces());
+ $this->assertEquals(
+ [
+ '\\Luigi\\OVEN_TEMPERATURE' => new Fqsen('\\Luigi\\OVEN_TEMPERATURE'),
+ '\\Luigi\\MAX_OVEN_TEMPERATURE' => new Fqsen('\\Luigi\\MAX_OVEN_TEMPERATURE'),
+ ],
+ $project->getNamespaces()['\\Luigi']->getConstants()
+ );
+ }
+}
diff --git a/tests/integration/PropertyHookTest.php b/tests/integration/PropertyHookTest.php
new file mode 100644
index 00000000..2691595d
--- /dev/null
+++ b/tests/integration/PropertyHookTest.php
@@ -0,0 +1,131 @@
+= 5.2')]
+#[CoversNothing]
+final class PropertyHookTest extends TestCase
+{
+ public function testPropertyHookWithDocblocks(): void
+ {
+ $file = __DIR__ . '/data/PHP84/PropertyHook.php';
+ $projectFactory = ProjectFactory::createInstance();
+ $project = $projectFactory->create('My project', [new LocalFile($file)]);
+
+ $class = $project->getFiles()[$file]->getClasses()['\PropertyHook'];
+ $hooks = $class->getProperties()['\PropertyHook::$example']->getHooks();
+
+ $this->assertTrue($class->getProperties()['\PropertyHook::$example']->isVirtual());
+ $this->assertCount(2, $hooks);
+ $this->assertEquals('get', $hooks[0]->getName());
+ $this->assertEquals(new Visibility(Visibility::PUBLIC_), $hooks[0]->getVisibility());
+ $this->assertCount(1, $hooks[0]->getAttributes());
+ $this->assertCount(0, $hooks[0]->getArguments());
+ $this->assertSame('Not sure this works, but it gets', $hooks[0]->getDocBlock()->getSummary());
+
+ $this->assertEquals('set', $hooks[1]->getName());
+ $this->assertEquals(new Visibility(Visibility::PUBLIC_), $hooks[1]->getVisibility());
+ $this->assertCount(1, $hooks[1]->getAttributes());
+ $this->assertCount(1, $hooks[1]->getArguments());
+ $this->assertEquals(new Argument(
+ 'value',
+ new Compound(
+ [
+ new String_(),
+ new Integer()
+ ]
+ ),
+ ), $hooks[1]->getArguments()[0]);
+ $this->assertSame('Not sure this works, but it gets', $hooks[0]->getDocBlock()->getSummary());
+ }
+
+ public function testPropertyHookAsymmetric(): void
+ {
+ $file = __DIR__ . '/data/PHP84/PropertyHookAsymmetric.php';
+ $projectFactory = ProjectFactory::createInstance();
+ $project = $projectFactory->create('My project', [new LocalFile($file)]);
+
+ $class = $project->getFiles()[$file]->getClasses()['\PropertyHook'];
+ $hooks = $class->getProperties()['\PropertyHook::$example']->getHooks();
+
+
+ $this->assertEquals(
+ new AsymmetricVisibility(
+ new Visibility(Visibility::PUBLIC_),
+ new Visibility(Visibility::PRIVATE_)
+ ),
+ $class->getProperties()['\PropertyHook::$example']->getVisibility()
+ );
+ $this->assertTrue($class->getProperties()['\PropertyHook::$example']->isVirtual());
+ $this->assertCount(2, $hooks);
+ $this->assertEquals('get', $hooks[0]->getName());
+ $this->assertEquals(new Visibility(Visibility::PUBLIC_), $hooks[0]->getVisibility());
+ $this->assertCount(0, $hooks[0]->getArguments());
+
+ $this->assertEquals('set', $hooks[1]->getName());
+ $this->assertEquals(new Visibility(Visibility::PRIVATE_), $hooks[1]->getVisibility());
+ $this->assertCount(1, $hooks[1]->getArguments());
+ $this->assertEquals(new Argument(
+ 'value',
+ new Compound(
+ [
+ new String_(),
+ new Integer()
+ ]
+ ),
+ ), $hooks[1]->getArguments()[0]);
+ }
+
+ public function testVirtualProperty(): void
+ {
+ $file = __DIR__ . '/data/PHP84/PropertyHookVirtual.php';
+ $projectFactory = ProjectFactory::createInstance();
+ $project = $projectFactory->create('My project', [new LocalFile($file)]);
+
+ $class = $project->getFiles()[$file]->getClasses()['\PropertyHookVirtual'];
+
+ // Test get-only virtual property
+ $fullNameProperty = $class->getProperties()['\PropertyHookVirtual::$fullName'];
+ $this->assertTrue($fullNameProperty->isVirtual(), 'Property with getter that doesn\'t reference itself should be virtual');
+ $this->assertCount(1, $fullNameProperty->getHooks());
+ $this->assertEquals('get', $fullNameProperty->getHooks()[0]->getName());
+
+ // Test set-only virtual property
+ $compositeNameProperty = $class->getProperties()['\PropertyHookVirtual::$compositeName'];
+ $this->assertTrue($compositeNameProperty->isVirtual(), 'Property with setter that doesn\'t reference itself should be virtual');
+ $this->assertCount(1, $compositeNameProperty->getHooks());
+ $this->assertEquals('set', $compositeNameProperty->getHooks()[0]->getName());
+
+ // Test property with both get and set hooks that doesn't reference itself
+ $completeFullNameProperty = $class->getProperties()['\PropertyHookVirtual::$completeFullName'];
+ $this->assertTrue($completeFullNameProperty->isVirtual(), 'Property with getter and setter that don\'t reference itself should be virtual');
+ $this->assertCount(2, $completeFullNameProperty->getHooks());
+
+ $nonVirtualPropertyWithoutHooks = $class->getProperties()['\PropertyHookVirtual::$firstName'];
+ $this->assertFalse($nonVirtualPropertyWithoutHooks->isVirtual(), 'Property without hooks should not be virtual');
+ $this->assertCount(0, $nonVirtualPropertyWithoutHooks->getHooks());
+
+ // Test non-virtual property that references itself
+ $nonVirtualNameProperty = $class->getProperties()['\PropertyHookVirtual::$nonVirtualName'];
+ $this->assertFalse($nonVirtualNameProperty->isVirtual(), 'Property with hooks that reference itself should not be virtual');
+ $this->assertCount(2, $nonVirtualNameProperty->getHooks());
+ }
+}
diff --git a/tests/integration/data/Enums/EnumConsumer.php b/tests/integration/data/Enums/EnumConsumer.php
new file mode 100644
index 00000000..c6944f5e
--- /dev/null
+++ b/tests/integration/data/Enums/EnumConsumer.php
@@ -0,0 +1,15 @@
+myEnum = $enum;
+ }
+}
diff --git a/tests/integration/data/Enums/backedEnum.php b/tests/integration/data/Enums/backedEnum.php
new file mode 100644
index 00000000..90e249fd
--- /dev/null
+++ b/tests/integration/data/Enums/backedEnum.php
@@ -0,0 +1,11 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+?>
+Test
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+require 'Pizza.php';
diff --git a/tests/integration/data/GlobalFiles/empty_shebang.php b/tests/integration/data/GlobalFiles/empty_shebang.php
new file mode 100644
index 00000000..eaff36c6
--- /dev/null
+++ b/tests/integration/data/GlobalFiles/empty_shebang.php
@@ -0,0 +1,15 @@
+#!/bin/php
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+require 'Pizza.php';
diff --git a/tests/integration/data/GlobalFiles/empty_with_declare.php b/tests/integration/data/GlobalFiles/empty_with_declare.php
new file mode 100644
index 00000000..956d42d0
--- /dev/null
+++ b/tests/integration/data/GlobalFiles/empty_with_declare.php
@@ -0,0 +1,14 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+require 'Pizza.php';
diff --git a/tests/integration/data/GlobalFiles/empty_with_html.php b/tests/integration/data/GlobalFiles/empty_with_html.php
new file mode 100644
index 00000000..574f8741
--- /dev/null
+++ b/tests/integration/data/GlobalFiles/empty_with_html.php
@@ -0,0 +1,14 @@
+Test
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+require 'Pizza.php';
diff --git a/tests/integration/data/GlobalFiles/global_namspaced_function.php b/tests/integration/data/GlobalFiles/global_namspaced_function.php
new file mode 100644
index 00000000..265c6384
--- /dev/null
+++ b/tests/integration/data/GlobalFiles/global_namspaced_function.php
@@ -0,0 +1,23 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+declare(strict_types=1);
+
+require 'Pizza.php';
diff --git a/tests/integration/data/Luigi/ExampleNestedTrait.php b/tests/integration/data/Luigi/ExampleNestedTrait.php
new file mode 100644
index 00000000..4539e71c
--- /dev/null
+++ b/tests/integration/data/Luigi/ExampleNestedTrait.php
@@ -0,0 +1,21 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace Luigi {
+
+ trait ExampleNestedTrait
+ {
+ private function exampleTraitMethod()
+ {
+ }
+ }
+}
diff --git a/tests/integration/data/Luigi/Packing.php b/tests/integration/data/Luigi/Packing.php
new file mode 100644
index 00000000..cdd0ea40
--- /dev/null
+++ b/tests/integration/data/Luigi/Packing.php
@@ -0,0 +1,17 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace Luigi;
+
+interface Packing extends \Packing
+{
+}
diff --git a/tests/integration/data/Luigi/Pizza.php b/tests/integration/data/Luigi/Pizza.php
new file mode 100644
index 00000000..0c7ab328
--- /dev/null
+++ b/tests/integration/data/Luigi/Pizza.php
@@ -0,0 +1,96 @@
+style = $style;
+ }
+
+ /**
+ * Creates a new instance of a Pizza.
+ *
+ * This method can be used to instantiate a new object of this class which can then be retrieved using
+ * {@see self::getInstance()}.
+ *
+ * @param Pizza\Style $style
+ *
+ * @see self::getInstance to retrieve the pizza object.
+ *
+ * @return void
+ */
+ public static function createInstance(Pizza\Style $style)
+ {
+ self::$instance = new static($style);
+ }
+
+ /**
+ * @return self
+ */
+ static function getInstance()
+ {
+ return self::$instance;
+ }
+
+ final public function setSauce(Pizza\Sauce $sauce)
+ {
+ $this->sauce = $sauce;
+ }
+
+ final public function addTopping(Pizza\Topping $topping)
+ {
+ $this->toppings[] = $topping;
+ }
+
+ public function setSize(&$size = \Luigi\Pizza\SIZE_20CM)
+ {
+ }
+
+ public function getPrice()
+ {
+ }
+}
diff --git a/tests/integration/data/Luigi/StyleFactory.php b/tests/integration/data/Luigi/StyleFactory.php
new file mode 100644
index 00000000..1c80e7ac
--- /dev/null
+++ b/tests/integration/data/Luigi/StyleFactory.php
@@ -0,0 +1,26 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace Luigi;
+
+use Luigi\Pizza\PizzaComponentFactory;
+
+final class StyleFactory extends PizzaComponentFactory
+{
+ public function getPrice()
+ {
+ }
+
+ protected function calculatePrice()
+ {
+ }
+}
diff --git a/tests/integration/data/Luigi/Valued.php b/tests/integration/data/Luigi/Valued.php
new file mode 100644
index 00000000..e989e894
--- /dev/null
+++ b/tests/integration/data/Luigi/Valued.php
@@ -0,0 +1,27 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+namespace Luigi;
+
+/**
+ * Any class implementing this interface has an associated price.
+ *
+ * Using this interface we can easily add the price of all components in a pizza by checking for this interface and
+ * adding the prices together for all components.
+ */
+interface Valued
+{
+ const BASE_PRICE = 1;
+
+ function getPrice();
+}
diff --git a/tests/integration/data/Luigi/constants.php b/tests/integration/data/Luigi/constants.php
new file mode 100644
index 00000000..820fa1bf
--- /dev/null
+++ b/tests/integration/data/Luigi/constants.php
@@ -0,0 +1,14 @@
+property;
+ }
+
+ public function setProperty(mixed $value): void
+ {
+ $this->property = $value;
+ }
+}
diff --git a/tests/integration/data/PHP8/StaticType.php b/tests/integration/data/PHP8/StaticType.php
new file mode 100644
index 00000000..3b5e9ef1
--- /dev/null
+++ b/tests/integration/data/PHP8/StaticType.php
@@ -0,0 +1,21 @@
+property;
+ }
+
+ public function setProperty($value): void
+ {
+ $this->property = $value;
+ }
+}
diff --git a/tests/integration/data/PHP8/UnionTypes.php b/tests/integration/data/PHP8/UnionTypes.php
new file mode 100644
index 00000000..299fe747
--- /dev/null
+++ b/tests/integration/data/PHP8/UnionTypes.php
@@ -0,0 +1,17 @@
+modified) {
+ return $this->foo . ' (modified)';
+ }
+ return $this->foo;
+ }
+ /** Not sure this works, but it sets */
+ #[Setter(new DateTimeImmutable())]
+ set(string|int $value) {
+ $this->foo = strtolower($value);
+ $this->modified = true;
+ }
+ }
+}
diff --git a/tests/integration/data/PHP84/PropertyHookAsymmetric.php b/tests/integration/data/PHP84/PropertyHookAsymmetric.php
new file mode 100644
index 00000000..85253613
--- /dev/null
+++ b/tests/integration/data/PHP84/PropertyHookAsymmetric.php
@@ -0,0 +1,23 @@
+modified) {
+ return $this->foo . ' (modified)';
+ }
+ return $this->foo;
+ }
+ set(string|int $value) {
+ $this->foo = strtolower($value);
+ $this->modified = true;
+ }
+ }
+}
diff --git a/tests/integration/data/PHP84/PropertyHookPromotion.php b/tests/integration/data/PHP84/PropertyHookPromotion.php
new file mode 100644
index 00000000..7c933750
--- /dev/null
+++ b/tests/integration/data/PHP84/PropertyHookPromotion.php
@@ -0,0 +1,32 @@
+modified) {
+ return $this->foo . ' (modified)';
+ }
+ return $this->foo;
+ }
+ /** Not sure this works, but it sets */
+ #[Setter(new DateTimeImmutable())]
+ set(string|int $value) {
+ $this->foo = strtolower($value);
+ $this->modified = true;
+ }
+ }
+ )
+ {
+ }
+}
diff --git a/tests/integration/data/PHP84/PropertyHookVirtual.php b/tests/integration/data/PHP84/PropertyHookVirtual.php
new file mode 100644
index 00000000..7e6f5ec3
--- /dev/null
+++ b/tests/integration/data/PHP84/PropertyHookVirtual.php
@@ -0,0 +1,60 @@
+fullName
+ get {
+ return $this->firstName . ' ' . $this->lastName;
+ }
+ }
+
+ /**
+ * A virtual property that decomposes a full name into first and last name
+ */
+ public string $compositeName {
+ // This is a virtual property with a setter
+ // It doesn't reference $this->compositeName
+ set(string $value) {
+ [$this->firstName, $this->lastName] = explode(' ', $value, 2);
+ }
+ }
+
+ /**
+ * A virtual property with both getter and setter
+ */
+ public string $completeFullName {
+ // Getter doesn't reference $this->completeFullName
+ get {
+ return $this->firstName . ' ' . $this->lastName;
+ }
+ // Setter doesn't reference $this->completeFullName
+ set(string $value) {
+ [$this->firstName, $this->lastName] = explode(' ', $value, 2);
+ }
+ }
+
+ /**
+ * A non-virtual property that references itself in its hook
+ */
+ public string $nonVirtualName {
+ get {
+ return $this->nonVirtualName ?? $this->firstName;
+ }
+ set(string $value) {
+ $this->nonVirtualName = $value;
+ }
+ }
+
+ public function __construct(
+ private string $firstName = 'John',
+ private string $lastName = 'Doe'
+ ) {
+ }
+}
diff --git a/tests/integration/data/Packing.php b/tests/integration/data/Packing.php
new file mode 100644
index 00000000..17891a05
--- /dev/null
+++ b/tests/integration/data/Packing.php
@@ -0,0 +1,16 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+interface Packing
+{
+ public function getName(): string;
+}
diff --git a/tests/integration/data/Pizza.php b/tests/integration/data/Pizza.php
new file mode 100644
index 00000000..05818e1e
--- /dev/null
+++ b/tests/integration/data/Pizza.php
@@ -0,0 +1,29 @@
+
+ * @license http://www.opensource.org/licenses/mit-license.php MIT
+ * @link http://phpdoc.org
+ */
+
+/**
+ * This needs a docblock to separate from
+ * file docblock
+ */
+const OVEN_TEMPERATURE = 9001;
+define('MAX_OVEN_TEMPERATURE', 9002);
+
+/**
+ * Pizza base class
+ */
+class Pizza
+{
+ /**
+ * The packaging method used to transport the pizza.
+ */
+ const PACKAGING = 'box';
+}
diff --git a/tests/integration/data/simpleFunction.php b/tests/integration/data/simpleFunction.php
new file mode 100644
index 00000000..ec0da6da
--- /dev/null
+++ b/tests/integration/data/simpleFunction.php
@@ -0,0 +1,8 @@
+
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Expr;
-
-/**
- * Class for testing base reflector.
- *
- * Extends the baseReflector so properties and abstract methods can be mocked,
- * and therefore tested.
- *
- * @author Erik Baars
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class BaseReflectorMock extends BaseReflector
-{
- /**
- * Overload method so we can test the protected method
- *
- * @param Expr $value
- *
- * @return string
- */
- public function getRepresentationOfValueMock(
- Expr $value = null
- ) {
- return parent::getRepresentationOfValue($value);
- }
-
- /**
- * @param $val
- *
- * @return void
- */
- public function setPrettyPrinter($val)
- {
- self::$prettyPrinter = $val;
- }
-}
diff --git a/tests/mocks/phpDocumentor/Reflection/ClassReflectorMock.php b/tests/mocks/phpDocumentor/Reflection/ClassReflectorMock.php
deleted file mode 100644
index a44314b9..00000000
--- a/tests/mocks/phpDocumentor/Reflection/ClassReflectorMock.php
+++ /dev/null
@@ -1,31 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-/**
- * Class for testing ClassReflector.
- *
- * Extends the ClassReflector so properties and abstract methods can be mocked,
- * and therefore tested.
- *
- * @author Erik Baars
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class ClassReflectorMock extends ClassReflector
-{
- public function setTraits(array $val)
- {
- $this->traits = $val;
- }
-}
diff --git a/tests/mocks/phpDocumentor/Reflection/NodeExprMock.php b/tests/mocks/phpDocumentor/Reflection/NodeExprMock.php
deleted file mode 100644
index 777ec563..00000000
--- a/tests/mocks/phpDocumentor/Reflection/NodeExprMock.php
+++ /dev/null
@@ -1,29 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Expr;
-
-/**
- * Class for testing PhpParser_Node_Expr.
- *
- * Extends the PhpParser_Node_Expr so properties and abstract methods can be mocked,
- * and therefore tested.
- *
- * @author Vasil Rangelov
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class NodeExprMock extends Expr
-{
-}
diff --git a/tests/mocks/phpDocumentor/Reflection/NodeStmtMock.php b/tests/mocks/phpDocumentor/Reflection/NodeStmtMock.php
deleted file mode 100644
index c0857415..00000000
--- a/tests/mocks/phpDocumentor/Reflection/NodeStmtMock.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Stmt;
-
-/**
- * Class for testing PhpParser_Node_Stmt.
- *
- * Extends the PhpParser_Node_Stmt so properties and abstract methods can be mocked,
- * and therefore tested.
- *
- * @author Erik Baars
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class NodeStmtMock extends \PhpParser\Node\Stmt
-{
- public $name = null;
-
- public function setName($val)
- {
- $this->name = $val;
- }
-
- public function __toString()
- {
- return 'testNodeMock';
- }
-}
diff --git a/tests/mocks/phpDocumentor/Reflection/NodeStmtMock2.php b/tests/mocks/phpDocumentor/Reflection/NodeStmtMock2.php
deleted file mode 100644
index 7a32cec8..00000000
--- a/tests/mocks/phpDocumentor/Reflection/NodeStmtMock2.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Stmt;
-
-/**
- * Class for testing PhpParser_Node_Stmt.
- *
- * Extends the PhpParser_Node_Stmt so properties and abstract methods can be mocked,
- * and therefore tested.
- *
- * @author Erik Baars
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class NodeStmtMock2 extends Stmt
-{
- public $type = null;
-
- public $implements = array();
-
- public $extends = null;
-}
diff --git a/tests/unit/phpDocumentor/Reflection/BaseReflectorTest.php b/tests/unit/phpDocumentor/Reflection/BaseReflectorTest.php
deleted file mode 100644
index abe2ad28..00000000
--- a/tests/unit/phpDocumentor/Reflection/BaseReflectorTest.php
+++ /dev/null
@@ -1,272 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-use phpDocumentor\Reflection\DocBlock\Context;
-use PHPUnit_Framework_TestCase;
-
-/**
- * Class for testing base reflector.
- *
- * @author Erik Baars
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class BaseReflectorTest extends PHPUnit_Framework_TestCase
-{
- /**
- * Tests the setNameSpace method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::setNameSpace
- * @covers \phpDocumentor\Reflection\BaseReflector::getNameSpace
- *
- * @return void
- */
- public function testSetNameSpace()
- {
- /** @var BaseReflector $base_reflector */
- $base_reflector = new BaseReflectorMock(
- $this->getMockBuilder('\PhpParser\Node\Stmt')->disableOriginalConstructor()->getMock(),
- new Context()
- );
- $base_reflector->setNamespace('namespace_name');
-
- $this->assertEquals('namespace_name', $base_reflector->getNameSpace());
- }
-
- /**
- * Tests the setNameSpace method when an invalid argument is passed
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::setNameSpace
- *
- * @expectedException \InvalidArgumentException
- *
- * @return void
- */
- public function testSetNameSpaceInvalidArgument()
- {
- /** @var BaseReflector $base_reflector */
- $base_reflector = new BaseReflectorMock(
- $this->getMockBuilder('\PhpParser\Node\Stmt')->disableOriginalConstructor()->getMock(),
- new Context()
- );
- $base_reflector->setNamespace(null);
- }
-
- /**
- * Tests the getDocblock method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::getDocBlock
- *
- * @return void
- */
- public function testGetDocBlock()
- {
- $this->markTestIncomplete();
- }
-
- /**
- * Tests the getName method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::getName
- *
- * @return void
- */
- public function testGetName()
- {
- $this->markTestIncomplete();
- }
-
- /**
- * Tests the getShortName method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::getShortName
- *
- * @return void
- */
- public function testGetShortName()
- {
- $node = new NodeStmtMock();
- $base_reflector = new BaseReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertEquals($node->__toString(), $base_reflector->getShortName());
-
- $node->setName('test_name');
-
- $this->assertEquals('test_name', $base_reflector->getShortName());
- }
-
- /**
- * Tests the getNameSpaceAlias method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::getNamespaceAliases
- * @covers \phpDocumentor\Reflection\BaseReflector::setNamespaceAliases
- *
- * @return void
- */
- public function testGetNamespaceAliases()
- {
- $node = new NodeStmtMock();
- $base_reflector = new BaseReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertEquals(array(), $base_reflector->getNamespaceAliases());
-
- $base_reflector->setNamespaceAliases(
- array('test_namespace', 'test_namespace_2')
- );
-
- $this->assertCount(2, $base_reflector->getNamespaceAliases());
- $this->assertEquals(
- array('\test_namespace', '\test_namespace_2'),
- $base_reflector->getNamespaceAliases()
- );
- }
-
- /**
- * Tests the getNameSpaceAlias method
- *
- * Tests the following scenarios:
- * - no namespace aliases set yet
- * - overwrite the current namespace alias
- * - add another namespace alias without overwriting the already set alias
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::getNamespaceAliases
- * @covers \phpDocumentor\Reflection\BaseReflector::setNamespaceAlias
- *
- * @return void
- */
- public function testsetNamespaceAlias()
- {
- $node = new NodeStmtMock();
-
- $base_reflector = new BaseReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertEquals(array(), $base_reflector->getNamespaceAliases());
-
- $base_reflector->setNamespaceAlias('test_alias', 'test_namespace');
-
- $namespace_aliases = $base_reflector->getNamespaceAliases();
- $this->assertCount(1, $namespace_aliases);
- $this->assertArrayHasKey('test_alias', $namespace_aliases);
- $this->assertEquals('\test_namespace', $namespace_aliases['test_alias']);
-
- $base_reflector->setNamespaceAlias('test_alias', 'test_namespace_2');
-
- $namespace_aliases = $base_reflector->getNamespaceAliases();
- $this->assertCount(1, $namespace_aliases);
- $this->assertArrayHasKey('test_alias', $namespace_aliases);
- $this->assertEquals('\test_namespace_2', $namespace_aliases['test_alias']);
-
- $base_reflector->setNamespaceAlias('test_alias2', 'test_namespace');
-
- $namespace_aliases = $base_reflector->getNamespaceAliases();
- $this->assertCount(2, $namespace_aliases);
- $this->assertArrayHasKey('test_alias', $namespace_aliases);
- $this->assertArrayHasKey('test_alias2', $namespace_aliases);
- $this->assertEquals('\test_namespace_2', $namespace_aliases['test_alias']);
- $this->assertEquals('\test_namespace', $namespace_aliases['test_alias2']);
- }
-
- /**
- * Tests the getLinenumber method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::getLinenumber
- *
- * @return void
- */
- public function testGetLinenumber()
- {
- $node = new NodeStmtMock();
-
- $base_reflector = new BaseReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertEquals($node->getLine(), $base_reflector->getLinenumber());
-
- $node->setLine(123);
-
- $this->assertEquals(123, $base_reflector->getLinenumber());
- }
-
- /**
- * Tests the setDefaultPackageName method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::setDefaultPackageName
- * @covers \phpDocumentor\Reflection\BaseReflector::getDefaultPackageName
- *
- * @return void
- */
- public function testSetDefaultPackageName()
- {
- $node = new NodeStmtMock();
-
- $base_reflector = new BaseReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertInternalType(
- 'string',
- $base_reflector->getDefaultPackageName()
- );
- $this->assertEquals('', $base_reflector->getDefaultPackageName());
-
- $base_reflector->setDefaultPackageName('test_name');
-
- $this->assertEquals('test_name', $base_reflector->getDefaultPackageName());
- }
-
- /**
- * Tests the setDefaultPackageName method
- *
- * @covers \phpDocumentor\Reflection\BaseReflector::getRepresentationOfValue
- *
- * @return void
- */
- public function testGetRepresentationOfValue()
- {
- $node = new NodeStmtMock();
- $base_reflector = new BaseReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertEquals('', $base_reflector->getRepresentationOfValueMock(null));
-
- $pretty_printer = $this->getMock(
- '\phpDocumentor\Reflection\PrettyPrinter',
- array('prettyPrintExpr')
- );
- $base_reflector->setPrettyPrinter($pretty_printer);
- $pretty_printer
- ->expects($this->once())
- ->method('prettyPrintExpr')
- ->will($this->returnValue('test_output'));
-
- $this->assertEquals(
- 'test_output',
- $base_reflector->getRepresentationOfValueMock(new NodeExprMock())
- );
- }
-}
diff --git a/tests/unit/phpDocumentor/Reflection/ClassReflectorTest.php b/tests/unit/phpDocumentor/Reflection/ClassReflectorTest.php
deleted file mode 100644
index 76b9b2bf..00000000
--- a/tests/unit/phpDocumentor/Reflection/ClassReflectorTest.php
+++ /dev/null
@@ -1,182 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-use phpDocumentor\Reflection\DocBlock\Context;
-use PhpParser\Node\Stmt\Class_;
-use PhpParser\Node\Stmt\ClassMethod;
-use PHPUnit_Framework_TestCase;
-
-/**
- * Class for testing ClassReflector.
- *
- * @author Erik Baars
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class ClassReflectorTest extends PHPUnit_Framework_TestCase
-{
- /**
- * Tests the parseSubElements method
- *
- * @covers \phpDocumentor\Reflection\ClassReflector::parseSubElements
- *
- * @return void
- */
- public function testParseSubElements()
- {
- $this->markTestIncomplete();
- }
-
- /**
- * Tests the parseSubElements method
- *
- * @covers \phpDocumentor\Reflection\ClassReflector::isAbstract
- *
- * @return void
- */
- public function testIsAbstract()
- {
- //$this->markTestSkipped();
- $node = new NodeStmtMock2();
- $class_reflector = new ClassReflector(
- $node,
- new Context()
- );
-
- $this->assertFalse($class_reflector->isAbstract());
-
- $node->type = Class_::MODIFIER_ABSTRACT;
- $this->assertTrue($class_reflector->isAbstract());
- }
-
- /**
- * Tests the parseSubElements method
- *
- * @covers \phpDocumentor\Reflection\ClassReflector::isFinal
- *
- * @return void
- */
- public function testIsFinal()
- {
- $node = new NodeStmtMock2();
- $class_reflector = new ClassReflector(
- $node,
- new Context()
- );
-
- $this->assertFalse($class_reflector->isFinal());
-
- $node->type = Class_::MODIFIER_FINAL;
- $this->assertTrue($class_reflector->isFinal());
- }
-
- /**
- * Tests the parseSubElements method
- *
- * @covers \phpDocumentor\Reflection\ClassReflector::getTraits
- *
- * @return void
- */
- public function testGetTraits()
- {
- $node = new NodeStmtMock();
- $class_reflector = new ClassReflectorMock(
- $node,
- new Context()
- );
-
- $traits = $class_reflector->getTraits();
- $this->assertInternalType('array', $traits);
- $this->assertEmpty($traits);
-
- $class_reflector->setTraits(array('trait1', 'trait2'));
- $traits = $class_reflector->getTraits();
-
- $this->assertCount(2, $traits);
- $this->assertEquals('trait1', reset($traits));
- }
-
- /**
- * Tests the parseSubElements method
- *
- * @covers \phpDocumentor\Reflection\ClassReflector::getParentClass
- *
- * @return void
- */
- public function testGetParentClass()
- {
- $node = new NodeStmtMock();
- $class_reflector = new ClassReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertEquals('', $class_reflector->getParentClass());
-
- $node->extends = 'dummy';
-
- $this->assertEquals('\dummy', $class_reflector->getParentClass());
- }
-
- /**
- * Tests the parseSubElements method
- *
- * @covers \phpDocumentor\Reflection\ClassReflector::getInterfaces
- *
- * @return void
- */
- public function testGetInterfaces()
- {
- $node = new NodeStmtMock();
- $class_reflector = new ClassReflectorMock(
- $node,
- new Context()
- );
-
- $this->assertEquals(array(), $class_reflector->getInterfaces());
-
- $node->implements = array('dummy');
-
- $this->assertEquals(array('\dummy'), $class_reflector->getInterfaces());
- }
-
- /**
- * Tests the getMethod method
- *
- * @covers \phpDocumentor\Reflection\ClassReflector::getMethod
- *
- * @return void
- */
- public function testGetMethod()
- {
- $node = new NodeStmtMock();
- $node->stmts = array(new ClassMethod('someMethod'));
- $class_reflector = new ClassReflectorMock(
- $node,
- new Context()
- );
-
- // Before parseSubElements
- $this->assertNull($class_reflector->getMethod('someMethod'));
-
- $class_reflector->parseSubElements();
-
- // After parseSubElements
- $this->assertInstanceOf(
- '\phpDocumentor\Reflection\ClassReflector\MethodReflector',
- $class_reflector->getMethod('someMethod')
- );
- $this->assertNull($class_reflector->getMethod('someOtherMethod'));
- }
-}
diff --git a/tests/unit/phpDocumentor/Reflection/File/LocalFileTest.php b/tests/unit/phpDocumentor/Reflection/File/LocalFileTest.php
new file mode 100644
index 00000000..acfe40cc
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/File/LocalFileTest.php
@@ -0,0 +1,48 @@
+assertStringEqualsFile(__FILE__, $file->getContents());
+ }
+
+ public function testMd5(): void
+ {
+ $file = new LocalFile(__FILE__);
+ $this->assertEquals(md5_file(__FILE__), $file->md5());
+ }
+
+ public function testNotExistingFileThrowsException(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ new LocalFile('aa');
+ }
+
+ public function testPath(): void
+ {
+ $file = new LocalFile(__FILE__);
+ $this->assertEquals(__FILE__, $file->path());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Middleware/ChainFactoryTest.php b/tests/unit/phpDocumentor/Reflection/Middleware/ChainFactoryTest.php
new file mode 100644
index 00000000..e5afad9c
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Middleware/ChainFactoryTest.php
@@ -0,0 +1,76 @@
+givenAMiddleware('c');
+ $middleware2 = $this->givenAMiddleware('b');
+
+ $chain = ChainFactory::createExecutionChain(
+ [$middleware1, $middleware2],
+ static function (): stdClass {
+ $result = new stdClass();
+ $result->counter = 'a';
+
+ return $result;
+ },
+ );
+
+ $this->assertInstanceOf(stdClass::class, $chain(new $exampleCommand()));
+ $this->assertSame('abc', $chain(new $exampleCommand())->counter);
+ }
+
+ public function testItThrowsAnExceptionIfAnythingOtherThanAMiddlewareIsPassed(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->expectExceptionMessage(
+ 'Middleware must be an instance of phpDocumentor\Reflection\Middleware\Middleware but string was given',
+ );
+ $middleware = '1';
+
+ ChainFactory::createExecutionChain(
+ [$middleware],
+ static fn (): stdClass => new stdClass(),
+ );
+ }
+
+ private function givenAMiddleware(string $exampleValue): Middleware
+ {
+ return new class ($exampleValue) implements Middleware {
+ public function __construct(private readonly string $exampleAddedValue)
+ {
+ }
+
+ public function execute(Command $command, callable $next): object
+ {
+ $result = $next($command);
+ $result->counter .= $this->exampleAddedValue;
+
+ return $result;
+ }
+ };
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/NodeVisitor/ElementNameResolverTest.php b/tests/unit/phpDocumentor/Reflection/NodeVisitor/ElementNameResolverTest.php
new file mode 100644
index 00000000..18ceccda
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/NodeVisitor/ElementNameResolverTest.php
@@ -0,0 +1,159 @@
+fixture = new ElementNameResolver();
+ $this->fixture->beforeTraverse([]);
+ }
+
+ public function testFunctionWithoutNamespace(): void
+ {
+ $function = new Function_('myFunction');
+ $this->fixture->enterNode($function);
+
+ $this->assertEquals('\myFunction()', (string) $function->getAttribute('fqsen'));
+ }
+
+ public function testWithClass(): void
+ {
+ $class = new Class_('myClass');
+ $this->fixture->enterNode($class);
+
+ $this->assertEquals('\myClass', (string) $class->getAttribute('fqsen'));
+ }
+
+ public function testWithClassMethod(): void
+ {
+ $class = new Class_('myClass');
+ $this->fixture->enterNode($class);
+
+ $method = new ClassMethod('method');
+ $this->fixture->enterNode($method);
+
+ $this->assertEquals('\myClass::method()', (string) $method->getAttribute('fqsen'));
+ }
+
+ public function testWithClassProperty(): void
+ {
+ $class = new Class_('myClass');
+ $this->fixture->enterNode($class);
+
+ $method = new PropertyProperty('name');
+ $this->fixture->enterNode($method);
+
+ $this->assertEquals('\myClass::$name', (string) $method->getAttribute('fqsen'));
+ }
+
+ /**
+ * If anonymous classes were processed, we would obtain a
+ * InvalidArgumentException for an invalid Fqsen.
+ */
+ public function testDoesNotEnterAnonymousClass(): void
+ {
+ $class = new Class_(null);
+ $this->assertEquals(
+ NodeTraverser::DONT_TRAVERSE_CHILDREN,
+ $this->fixture->enterNode($class),
+ );
+ }
+
+ /** @link https://github.com/phpDocumentor/Reflection/issues/103 */
+ public function testAnonymousClassDoesNotPopParts(): void
+ {
+ $anonymousClass = new Class_(null);
+
+ $new = new New_($anonymousClass);
+
+ $namespace = new Namespace_(new Name('ANamespace'), [new Return_($new)]);
+
+ $this->fixture->enterNode($namespace);
+ $this->fixture->enterNode($new);
+ $this->fixture->enterNode($anonymousClass);
+ $this->fixture->leaveNode($anonymousClass);
+ $this->fixture->leaveNode($new);
+ $this->fixture->leaveNode($namespace);
+
+ $this->assertTrue(true);
+ }
+
+ public function testClassConstant(): void
+ {
+ $const = new Const_('MY_CLASS', new String_('value'));
+ $classConst = new ClassConst([$const]);
+ $class = new Class_('myClass');
+
+ $this->fixture->enterNode($class);
+ $this->fixture->enterNode($classConst);
+ $this->fixture->enterNode($const);
+
+ $this->assertEquals('\\myClass::MY_CLASS', (string) $const->getAttribute('fqsen'));
+ }
+
+ public function testNamespacedConstant(): void
+ {
+ $const = new Const_('MY_CLASS', new String_('value'));
+ $namespace = new Namespace_(new Name('name'));
+
+ $this->fixture->enterNode($namespace);
+ $this->fixture->enterNode($const);
+
+ $this->assertEquals('\\name\\MY_CLASS', (string) $const->getAttribute('fqsen'));
+ }
+
+ public function testNoNameNamespace(): void
+ {
+ $const = new Const_('MY_CLASS', new String_('value'));
+ $namespace = new Namespace_(null);
+
+ $this->fixture->enterNode($namespace);
+ $this->fixture->enterNode($const);
+
+ $this->assertEquals('\\MY_CLASS', (string) $const->getAttribute('fqsen'));
+ }
+
+ public function testWithEnumWithCase(): void
+ {
+ $enum = new Enum_('myEnum');
+ $this->fixture->enterNode($enum);
+
+ $case = new EnumCase('VALUE1');
+ $this->fixture->enterNode($case);
+
+ $this->assertEquals('\myEnum::VALUE1', (string) $case->getAttribute('fqsen'));
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/ArgumentTest.php b/tests/unit/phpDocumentor/Reflection/Php/ArgumentTest.php
new file mode 100644
index 00000000..a07de699
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/ArgumentTest.php
@@ -0,0 +1,74 @@
+assertInstanceOf(Mixed_::class, $argument->getType());
+
+ $argument = new Argument(
+ 'myArgument',
+ new String_(),
+ 'myDefaultValue',
+ true,
+ true,
+ );
+ $this->assertEquals(new String_(), $argument->getType());
+ }
+
+ public function testGetName(): void
+ {
+ $argument = new Argument('myArgument', null, 'myDefault', true, true);
+ $this->assertEquals('myArgument', $argument->getName());
+ }
+
+ public function testGetDefault(): void
+ {
+ $argument = new Argument('myArgument', null, 'myDefaultValue', true, true);
+ $this->assertEquals('myDefaultValue', $argument->getDefault());
+
+ $argument = new Argument('myArgument', null, null, true, true);
+ $this->assertNull($argument->getDefault());
+ }
+
+ public function testGetWhetherArgumentIsPassedByReference(): void
+ {
+ $argument = new Argument('myArgument', null, 'myDefaultValue', true, true);
+ $this->assertTrue($argument->isByReference());
+
+ $argument = new Argument('myArgument', null, null, false, true);
+ $this->assertFalse($argument->isByReference());
+ }
+
+ public function testGetWhetherArgumentisVariadic(): void
+ {
+ $argument = new Argument('myArgument', null, 'myDefaultValue', true, true);
+ $this->assertTrue($argument->isVariadic());
+
+ $argument = new Argument('myArgument', null, 'myDefaultValue', true, false);
+ $this->assertFalse($argument->isVariadic());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Class_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Class_Test.php
new file mode 100644
index 00000000..c3764ef7
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Class_Test.php
@@ -0,0 +1,184 @@
+parent = new Fqsen('\MyParentClass');
+ $this->fqsen = new Fqsen('\MyClass');
+ $this->docBlock = new DocBlock('');
+
+ $this->fixture = new Class_($this->fqsen, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGettingName(): void
+ {
+ $this->assertSame($this->fqsen->getName(), $this->fixture->getName());
+ }
+
+ public function testGettingFqsen(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ }
+
+ public function testGettingDocBlock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testGettingParent(): void
+ {
+ $class = new Class_($this->fqsen, $this->docBlock);
+ $this->assertNull($class->getParent());
+
+ $class = new Class_($this->fqsen, $this->docBlock, $this->parent);
+ $this->assertSame($this->parent, $class->getParent());
+ }
+
+ public function testAddAndGettingInterfaces(): void
+ {
+ $this->assertEmpty($this->fixture->getInterfaces());
+
+ $interface = new Fqsen('\MyInterface');
+
+ $this->fixture->addInterface($interface);
+
+ $this->assertSame(['\MyInterface' => $interface], $this->fixture->getInterfaces());
+ }
+
+ public function testAddAndGettingConstants(): void
+ {
+ $this->assertEmpty($this->fixture->getConstants());
+
+ $constant = new Constant(new Fqsen('\MyClass::MY_CONSTANT'));
+
+ $this->fixture->addConstant($constant);
+
+ $this->assertSame(['\MyClass::MY_CONSTANT' => $constant], $this->fixture->getConstants());
+ }
+
+ public function testAddAndGettingProperties(): void
+ {
+ $this->assertEmpty($this->fixture->getProperties());
+
+ $property = new Property(new Fqsen('\MyClass::$myProperty'));
+
+ $this->fixture->addProperty($property);
+
+ $this->assertSame(['\MyClass::$myProperty' => $property], $this->fixture->getProperties());
+ }
+
+ public function testAddAndGettingMethods(): void
+ {
+ $this->assertEmpty($this->fixture->getMethods());
+
+ $method = new Method(new Fqsen('\MyClass::myMethod()'));
+
+ $this->fixture->addMethod($method);
+
+ $this->assertSame(['\MyClass::myMethod()' => $method], $this->fixture->getMethods());
+ }
+
+ public function testAddAndGettingUsedTrait(): void
+ {
+ $this->assertEmpty($this->fixture->getUsedTraits());
+
+ $trait = new Fqsen('\MyTrait');
+
+ $this->fixture->addUsedTrait($trait);
+
+ $this->assertSame(['\MyTrait' => $trait], $this->fixture->getUsedTraits());
+ }
+
+ public function testGettingWhetherClassIsAbstract(): void
+ {
+ $class = new Class_($this->fqsen, $this->docBlock);
+ $this->assertFalse($class->isAbstract());
+
+ $class = new Class_($this->fqsen, $this->docBlock, null, true);
+ $this->assertTrue($class->isAbstract());
+ }
+
+ public function testGettingWhetherClassIsFinal(): void
+ {
+ $class = new Class_($this->fqsen, $this->docBlock);
+ $this->assertFalse($class->isFinal());
+
+ $class = new Class_($this->fqsen, $this->docBlock, null, false, true);
+ $this->assertTrue($class->isFinal());
+ }
+
+ public function testGettingWhetherClassIsReadOnly(): void
+ {
+ $class = new Class_($this->fqsen, $this->docBlock);
+ $this->assertFalse($class->isReadOnly());
+
+ $class = new Class_(
+ $this->fqsen,
+ $this->docBlock,
+ null,
+ false,
+ false,
+ null,
+ null,
+ true,
+ );
+ $this->assertTrue($class->isReadOnly());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Class_(
+ $this->fqsen,
+ $this->docBlock,
+ null,
+ false,
+ false,
+ new Location(100, 20),
+ new Location(101, 20),
+ );
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/ConstantTest.php b/tests/unit/phpDocumentor/Reflection/Php/ConstantTest.php
new file mode 100644
index 00000000..0fbed684
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/ConstantTest.php
@@ -0,0 +1,84 @@
+fqsen = new Fqsen('\MySpace\CONSTANT');
+ $this->docBlock = new DocBlock('');
+ $this->fixture = new Constant($this->fqsen, $this->docBlock, $this->value);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGetValue(): void
+ {
+ $this->assertSame($this->value, $this->fixture->getValue());
+ }
+
+ public function testIsFinal(): void
+ {
+ $this->assertFalse($this->fixture->isFinal());
+ }
+
+ public function testGetFqsen(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ $this->assertSame($this->fqsen->getName(), $this->fixture->getName());
+ }
+
+ public function testGetDocblock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testGetVisibility(): void
+ {
+ $this->assertEquals(new Visibility(Visibility::PUBLIC_), $this->fixture->getVisibility());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Constant($this->fqsen, $this->docBlock, null, new Location(100, 20), new Location(101, 20));
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/EnumCaseTest.php b/tests/unit/phpDocumentor/Reflection/Php/EnumCaseTest.php
new file mode 100644
index 00000000..11267ac2
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/EnumCaseTest.php
@@ -0,0 +1,74 @@
+fqsen = new Fqsen('\Enum::VALUE');
+ $this->docBlock = new DocBlock('');
+
+ $this->fixture = new EnumCase($this->fqsen, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGettingName(): void
+ {
+ $this->assertSame($this->fqsen->getName(), $this->fixture->getName());
+ }
+
+ public function testGettingFqsen(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ }
+
+ public function testGettingDocBlock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testGetValue(): void
+ {
+ $this->assertNull($this->fixture->getValue());
+ }
+
+ public function testGetLocationReturnsDefault(): void
+ {
+ self::assertEquals(new Location(-1), $this->fixture->getLocation());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Enum_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Enum_Test.php
new file mode 100644
index 00000000..6ed91334
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Enum_Test.php
@@ -0,0 +1,134 @@
+parent = new Fqsen('\MyParentEnum');
+ $this->fqsen = new Fqsen('\Enum');
+ $this->docBlock = new DocBlock('');
+
+ $this->fixture = new Enum_($this->fqsen, null, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGettingName(): void
+ {
+ $this->assertSame($this->fqsen->getName(), $this->fixture->getName());
+ }
+
+ public function testGetBackedWithOutType(): void
+ {
+ $this->assertNull($this->fixture->getBackedType());
+ }
+
+ public function testGettingFqsen(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ }
+
+ public function testGettingDocBlock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testAddAndGettingInterfaces(): void
+ {
+ $this->assertEmpty($this->fixture->getInterfaces());
+
+ $interface = new Fqsen('\MyInterface');
+
+ $this->fixture->addInterface($interface);
+
+ $this->assertSame(['\MyInterface' => $interface], $this->fixture->getInterfaces());
+ }
+
+ public function testAddAndGettingConstants(): void
+ {
+ $this->assertEmpty($this->fixture->getConstants());
+
+ $constant = new Constant(new Fqsen('\MyClass::MYCONST'));
+
+ $this->fixture->addConstant($constant);
+
+ $this->assertSame(['\MyClass::MYCONST' => $constant], $this->fixture->getConstants());
+ }
+
+ public function testAddAndGettingMethods(): void
+ {
+ $this->assertEmpty($this->fixture->getMethods());
+
+ $method = new Method(new Fqsen('\MyClass::myMethod()'));
+
+ $this->fixture->addMethod($method);
+
+ $this->assertSame(['\MyClass::myMethod()' => $method], $this->fixture->getMethods());
+ }
+
+ public function testAddAndGettingUsedTrait(): void
+ {
+ $this->assertEmpty($this->fixture->getUsedTraits());
+
+ $trait = new Fqsen('\MyTrait');
+
+ $this->fixture->addUsedTrait($trait);
+
+ $this->assertSame(['\MyTrait' => $trait], $this->fixture->getUsedTraits());
+ }
+
+ public function testAddAndGettingCases(): void
+ {
+ $this->assertEmpty($this->fixture->getCases());
+
+ $case = new EnumCase(new Fqsen('\MyEnum::VALUE'), null);
+
+ $this->fixture->addCase($case);
+
+ $this->assertSame(['\MyEnum::VALUE' => $case], $this->fixture->getCases());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Enum_($this->fqsen, null, $this->docBlock, new Location(100, 20), new Location(101, 20));
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php
new file mode 100644
index 00000000..61f5cf7c
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php
@@ -0,0 +1,94 @@
+setAttribute('fqsen', new Fqsen((string) $const1->name));
+ $const2 = new Const_('\Space\MyClass::MY_CONST2', new Variable('2'));
+ $const2->setAttribute('fqsen', new Fqsen((string) $const2->name));
+
+ $classConstantNode = new ClassConst([$const1, $const2]);
+
+ $i = 1;
+ foreach (new ClassConstantIterator($classConstantNode) as $constant) {
+ $this->assertEquals('\Space\MyClass::MY_CONST' . $i, $constant->getName());
+ $this->assertEquals('\Space\MyClass::MY_CONST' . $i, (string) $constant->getFqsen());
+ $this->assertEquals($i, $constant->getValue()->name);
+ ++$i;
+ }
+ }
+
+ public function testKey(): void
+ {
+ $constantMock = m::mock(ClassConst::class);
+
+ $fixture = new ClassConstantIterator($constantMock);
+
+ $this->assertEquals(0, $fixture->key());
+ $fixture->next();
+ $this->assertEquals(1, $fixture->key());
+ }
+
+ public function testProxyMethods(): void
+ {
+ $constantMock = m::mock(ClassConst::class);
+ $constantMock->shouldReceive('getLine')->once()->andReturn(10);
+
+ $fixture = new ClassConstantIterator($constantMock);
+
+ $this->assertEquals(10, $fixture->getLine());
+ }
+
+ public function testGetDocCommentPropFirst(): void
+ {
+ $const = m::mock(Const_::class);
+ $classConstants = m::mock(ClassConst::class);
+ $classConstants->consts = [$const];
+
+ $const->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
+ $classConstants->shouldReceive('getDocComment')->never();
+
+ $fixture = new ClassConstantIterator($classConstants);
+
+ $this->assertEquals('test', $fixture->getDocComment()->getText());
+ }
+
+ public function testGetDocComment(): void
+ {
+ $const = m::mock(Const_::class);
+ $classConstants = m::mock(ClassConst::class);
+ $classConstants->consts = [$const];
+
+ $const->shouldReceive('getDocComment')->once()->andReturnNull();
+ $classConstants->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
+
+ $fixture = new ClassConstantIterator($classConstants);
+
+ $this->assertEquals('test', $fixture->getDocComment()->getText());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantTest.php
new file mode 100644
index 00000000..dec052dc
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantTest.php
@@ -0,0 +1,189 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new ClassConstant(
+ $this->docBlockFactory->reveal(),
+ new PrettyPrinter(),
+ );
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), $this->buildConstantIteratorStub()));
+ }
+
+ #[DataProvider('visibilityProvider')]
+ public function testCreateWithVisibility(int $input, string $expectedVisibility, bool $isFinal = false): void
+ {
+ $constantStub = $this->buildConstantIteratorStub($input);
+
+ $class = $this->performCreate($constantStub);
+
+ $constant = current($class->getConstants());
+ $this->assertConstant($constant, $expectedVisibility);
+ $this->assertSame($isFinal, $constant->isFinal());
+ }
+
+ /** @return array */
+ public static function visibilityProvider(): array
+ {
+ return [
+ [
+ ClassNode::MODIFIER_PUBLIC,
+ 'public',
+ ],
+ [
+ ClassNode::MODIFIER_PROTECTED,
+ 'protected',
+ ],
+ [
+ ClassNode::MODIFIER_PRIVATE,
+ 'private',
+ ],
+ [
+ ClassNode::MODIFIER_PRIVATE | ClassNode::MODIFIER_FINAL,
+ 'private',
+ true,
+ ],
+ ];
+ }
+
+ public function testCreateForInterface(): void
+ {
+ $interface = new InterfaceElement(new Fqsen('\myInterface'));
+ $const = new Const_('\Space\MyInterface::MY_CONST1', new String_('a'));
+ $const->setAttribute('fqsen', new Fqsen((string) $const->name));
+ $constantStub = new ClassConst([$const], ClassNode::MODIFIER_PUBLIC);
+
+ $result = $this->performCreateWith($constantStub, $interface);
+
+ self::assertInstanceOf(ConstantDescriptor::class, current($result->getConstants()));
+ }
+
+ public function testCreateForTrait(): void
+ {
+ $trait = new TraitElement(new Fqsen('\myTrait'));
+ $const = new Const_('\Space\MyTrait::MY_CONST1', new String_('a'));
+ $const->setAttribute('fqsen', new Fqsen((string) $const->name));
+ $constantStub = new ClassConst([$const], ClassNode::MODIFIER_PUBLIC);
+
+ $result = $this->performCreateWith($constantStub, $trait);
+
+ self::assertInstanceOf(ConstantDescriptor::class, current($result->getConstants()));
+ }
+
+ public function testCreateForEnum(): void
+ {
+ $enum = new EnumElement(new Fqsen('\myEnum'), new Null_());
+ $const = new Const_('\Space\MyEnum::MY_CONST1', new String_('a'));
+ $const->setAttribute('fqsen', new Fqsen((string) $const->name));
+ $constantStub = new ClassConst([$const], ClassNode::MODIFIER_PUBLIC);
+
+ $result = $this->performCreateWith($constantStub, $enum);
+
+ self::assertInstanceOf(ConstantDescriptor::class, current($result->getConstants()));
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('text');
+ $docBlock = new DocBlockDescriptor('text');
+ $this->docBlockFactory->create('text', null)->willReturn($docBlock);
+
+ $const = new Const_('\Space\MyClass::MY_CONST1', new String_('a'), ['comments' => [$doc]]);
+ $const->setAttribute('fqsen', new Fqsen((string) $const->name));
+ $constantStub = new ClassConst([$const], ClassNode::MODIFIER_PUBLIC);
+
+ $class = $this->performCreate($constantStub);
+
+ $constant = current($class->getConstants());
+ $this->assertConstant($constant, 'public');
+ $this->assertSame($docBlock, $constant->getDocBlock());
+ }
+
+ private function buildConstantIteratorStub(int $modifier = ClassNode::MODIFIER_PUBLIC): ClassConst
+ {
+ $const = new Const_('\Space\MyClass::MY_CONST1', new String_('a'));
+ $const->setAttribute('fqsen', new Fqsen((string) $const->name));
+
+ return new ClassConst([$const], $modifier);
+ }
+
+ private function assertConstant(ConstantDescriptor $constant, string $visibility): void
+ {
+ $this->assertInstanceOf(ConstantDescriptor::class, $constant);
+ $this->assertEquals('\Space\MyClass::MY_CONST1', (string) $constant->getFqsen());
+ $this->assertEquals('\'a\'', $constant->getValue());
+ $this->assertEquals($visibility, (string) $constant->getVisibility());
+ }
+
+ private function performCreate(ClassConst $constantStub): ClassElement
+ {
+ $class = new ClassElement(new Fqsen('\myClass'));
+ $this->performCreateWith($constantStub, $class);
+
+ return $class;
+ }
+
+ private function performCreateWith(ClassConst $constantStub, Element $parent): Element
+ {
+ $factory = new ProjectFactoryStrategies([]);
+ $this->fixture->create(self::createContext(null)->push($parent), $constantStub, $factory);
+
+ return $parent;
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/Class_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/Class_Test.php
new file mode 100644
index 00000000..82da37ef
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/Class_Test.php
@@ -0,0 +1,187 @@
+docblockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Class_($this->docblockFactory->reveal());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue(
+ $this->fixture->matches(
+ self::createContext(null),
+ $this->prophesize(ClassNode::class)->reveal(),
+ ),
+ );
+ }
+
+ public function testSimpleCreate(): void
+ {
+ $containerMock = m::mock(StrategyContainer::class);
+ $classMock = $this->buildClassMock();
+ $classMock->shouldReceive('getDocComment')->andReturnNull();
+
+ $class = $this->performCreate($classMock, $containerMock);
+
+ $this->assertInstanceOf(ClassElement::class, $class);
+ $this->assertEquals('\Space\MyClass', (string) $class->getFqsen());
+ $this->assertNull($class->getParent());
+ $this->assertTrue($class->isFinal());
+ $this->assertTrue($class->isAbstract());
+ }
+
+ public function testClassWithParent(): void
+ {
+ $containerMock = m::mock(StrategyContainer::class);
+ $classMock = $this->buildClassMock();
+ $classMock->shouldReceive('getDocComment')->andReturnNull();
+ $classMock->extends = new Name('Space\MyParent');
+
+ $class = $this->performCreate($classMock, $containerMock);
+
+ $this->assertInstanceOf(ClassElement::class, $class);
+ $this->assertEquals('\Space\MyClass', (string) $class->getFqsen());
+ $this->assertEquals('\Space\MyParent', (string) $class->getParent());
+ }
+
+ public function testClassImplementingInterface(): void
+ {
+ $containerMock = m::mock(StrategyContainer::class);
+ $classMock = $this->buildClassMock();
+ $classMock->shouldReceive('getDocComment')->andReturnNull();
+ $classMock->extends = new Name('Space\MyParent');
+ $classMock->implements = [
+ new Name('MyInterface'),
+ ];
+
+ $class = $this->performCreate($classMock, $containerMock);
+
+ $this->assertInstanceOf(ClassElement::class, $class);
+ $this->assertEquals('\Space\MyClass', (string) $class->getFqsen());
+
+ $this->assertEquals(
+ ['\MyInterface' => new Fqsen('\MyInterface')],
+ $class->getInterfaces(),
+ );
+ }
+
+ public function testIteratesStatements(): void
+ {
+ $method1 = new ClassMethod('MyClass::method1');
+ $method1Descriptor = new MethodElement(new Fqsen('\MyClass::method1'));
+ $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $classMock = $this->buildClassMock();
+ $classMock->shouldReceive('getDocComment')->andReturnNull();
+ $classMock->stmts = [$method1];
+
+ $strategyMock->create(Argument::type(ContextStack::class), $method1, $containerMock)
+ ->will(function ($args) use ($method1Descriptor): void {
+ $args[0]->peek()->addMethod($method1Descriptor);
+ })
+ ->shouldBeCalled();
+
+ $containerMock->findMatching(
+ Argument::type(ContextStack::class),
+ $method1,
+ )->willReturn($strategyMock->reveal());
+
+ $class = $this->performCreate($classMock, $containerMock->reveal());
+
+ $this->assertInstanceOf(ClassElement::class, $class);
+ $this->assertEquals('\Space\MyClass', (string) $class->getFqsen());
+ $this->assertEquals(
+ ['\MyClass::method1' => $method1Descriptor],
+ $class->getMethods(),
+ );
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $classMock = $this->buildClassMock();
+ $classMock->shouldReceive('getDocComment')->andReturn($doc);
+ $docBlock = new DocBlockElement('');
+ $this->docblockFactory->create('Text', null)->willReturn($docBlock);
+ $containerMock = m::mock(StrategyContainer::class);
+
+ $class = $this->performCreate($classMock, $containerMock);
+
+ $this->assertSame($docBlock, $class->getDocBlock());
+ }
+
+ private function buildClassMock(): m\MockInterface|ClassNode
+ {
+ $classMock = m::mock(ClassNode::class);
+ $classMock->shouldReceive('getAttribute')->andReturn(new Fqsen('\Space\MyClass'));
+ $classMock->implements = [];
+ $classMock->stmts = [];
+ $classMock->shouldReceive('isFinal')->andReturn(true);
+ $classMock->shouldReceive('isAbstract')->andReturn(true);
+ $classMock->shouldReceive('isReadonly')->andReturn(true);
+ $classMock->shouldReceive('getLine')->andReturn(1);
+ $classMock->shouldReceive('getEndLine')->andReturn(2);
+
+ return $classMock;
+ }
+
+ private function performCreate(ClassNode $classMock, StrategyContainer $containerMock): ClassElement
+ {
+ $file = new File('hash', 'path');
+ $this->fixture->create(self::createContext(null)->push($file), $classMock, $containerMock);
+
+ return current($file->getClasses());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/ConstructorPromotionTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/ConstructorPromotionTest.php
new file mode 100644
index 00000000..bb63942d
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/ConstructorPromotionTest.php
@@ -0,0 +1,158 @@
+strategy = $this->prophesize(ProjectFactoryStrategy::class);
+ $this->docblockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $printer = $this->prophesize(Standard::class);
+ $printer->prettyPrintExpr(Argument::any())->willReturn('myType');
+
+ $this->fixture = new ConstructorPromotion(
+ $this->strategy->reveal(),
+ $this->docblockFactory->reveal(),
+ $printer->reveal(),
+ );
+ }
+
+ #[DataProvider('objectProvider')]
+ public function testMatches(ContextStack $context, object $object, bool $expected): void
+ {
+ self::assertEquals($expected, $this->fixture->matches($context, $object));
+ }
+
+ /** @return mixed[][] */
+ public static function objectProvider(): array
+ {
+ $context = new ContextStack(new Project('test'));
+
+ return [
+ 'emptyContext' => [
+ $context,
+ new stdClass(),
+ false,
+ ],
+ 'invalid stack type' => [
+ $context->push(new InterfaceElement(new Fqsen('\MyInterface'))),
+ new ClassMethod('foo'),
+ false,
+ ],
+ 'with class but not constructor' => [
+ $context->push(new ClassElement(new Fqsen('\MyInterface'))),
+ new ClassMethod('foo'),
+ false,
+ ],
+ 'with class but and is constructor' => [
+ $context->push(new ClassElement(new Fqsen('\MyInterface'))),
+ new ClassMethod('__construct'),
+ true,
+ ],
+ ];
+ }
+
+ #[DataProvider('visibilityProvider')]
+ public function testCreateWithProperty(int $flags, string $visibility, bool $readOnly = false): void
+ {
+ $methodNode = new ClassMethod('__construct');
+ $methodNode->params = [
+ new Param(
+ new Variable('myArgument'),
+ new String_('MyDefault'),
+ new Identifier('string'),
+ false,
+ false,
+ [
+ 'comments' => [
+ new Doc('text'),
+ ],
+ ],
+ $flags,
+ ),
+ ];
+
+ $docBlock = new DocBlock('Test');
+ $class = new ClassElement(new Fqsen('\MyClass'));
+ $context = self::createContext()->push($class);
+
+ $this->docblockFactory->create('text', null)->willReturn($docBlock);
+ $this->strategy->create($context, $methodNode, Argument::type(StrategyContainer::class))
+ ->shouldBeCalled();
+
+ $this->fixture->create(
+ $context,
+ $methodNode,
+ $this->prophesize(StrategyContainer::class)->reveal(),
+ );
+
+ $property = current($class->getProperties());
+
+ self::assertInstanceOf(PropertyElement::class, $property);
+ self::assertEquals($visibility, $property->getVisibility());
+ self::assertSame($docBlock, $property->getDocBlock());
+ self::assertSame('myType', $property->getDefault());
+ self::assertEquals('\MyClass::$myArgument', $property->getFqsen());
+ self::assertSame($readOnly, $property->isReadOnly());
+ }
+
+ /** @return mixed[][] */
+ public static function visibilityProvider(): array
+ {
+ return [
+ [
+ ClassNode::MODIFIER_PUBLIC,
+ Visibility::PUBLIC_,
+ ],
+ [
+ ClassNode::MODIFIER_PROTECTED,
+ Visibility::PROTECTED_,
+ ],
+ [
+ ClassNode::MODIFIER_PRIVATE,
+ Visibility::PRIVATE_,
+ ],
+ [
+ ClassNode::MODIFIER_PRIVATE | ClassNode::MODIFIER_READONLY,
+ Visibility::PRIVATE_,
+ true,
+ ],
+ ];
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/ContextStackTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/ContextStackTest.php
new file mode 100644
index 00000000..68af0687
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/ContextStackTest.php
@@ -0,0 +1,87 @@
+getProject());
+ self::assertSame($typeContext, $context->getTypeContext());
+ }
+
+ public function testPeekThowsWhenEmpty(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+ $project = new Project('myProject');
+ $typeContext = new Context('myNamespace');
+ $context = new ContextStack($project, $typeContext);
+
+ $context->peek();
+ }
+
+ public function testPeekReturnsTopOfStack(): void
+ {
+ $class = new ClassElement(new Fqsen('\MyClass'));
+
+ $project = new Project('myProject');
+ $typeContext = new Context('myNamespace');
+ $context = new ContextStack($project, $typeContext);
+ $context = $context->push($class);
+
+ self::assertSame($class, $context->peek());
+ self::assertSame($project, $context->getProject());
+ self::assertSame($typeContext, $context->getTypeContext());
+ }
+
+ public function testCreateWithTypeContext(): void
+ {
+ $class = new ClassElement(new Fqsen('\MyClass'));
+
+ $project = new Project('myProject');
+ $typeContext = new Context('myNamespace');
+ $context = new ContextStack($project);
+ $context = $context->push($class)->withTypeContext($typeContext);
+
+ self::assertSame($class, $context->peek());
+ self::assertSame($project, $context->getProject());
+ self::assertSame($typeContext, $context->getTypeContext());
+ }
+
+ public function testSearchEmptyStackResultsInNull(): void
+ {
+ $project = new Project('myProject');
+ $context = new ContextStack($project);
+
+ self::assertNull($context->search(ClassElement::class));
+ }
+
+ public function testSearchStackForExistingElementTypeWillReturnTheFirstHit(): void
+ {
+ $class = new ClassElement(new Fqsen('\MyClass'));
+ $project = new Project('myProject');
+ $context = new ContextStack($project);
+ $context = $context
+ ->push(new ClassElement(new Fqsen('\OtherClass')))
+ ->push($class)
+ ->push(new Method(new Fqsen('\MyClass::method()')));
+
+ self::assertSame($class, $context->search(ClassElement::class));
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/DefineTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/DefineTest.php
new file mode 100644
index 00000000..c0668641
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/DefineTest.php
@@ -0,0 +1,154 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Define($this->docBlockFactory->reveal(), new PrettyPrinter());
+ }
+
+ public function testMatches(): void
+ {
+ $invalidExpressionType = new Expression(new Exit_());
+ $invalidFunctionCall = new Expression(new FuncCall(new Name('print')));
+
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertFalse($this->fixture->matches(self::createContext(null), $invalidExpressionType));
+ $this->assertFalse($this->fixture->matches(self::createContext(null), $invalidFunctionCall));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), $this->buildDefineStub()));
+ }
+
+ public function testCreate(): void
+ {
+ $constantStub = $this->buildDefineStub();
+ $file = new FileElement('hash', 'path');
+ $contextStack = self::createContext(new Context('Space\\MyClass'))->push($file);
+
+ $this->fixture->create($contextStack, $constantStub, new ProjectFactoryStrategies([]));
+
+ $constant = current($file->getConstants());
+
+ $this->assertConstant($constant, '');
+ }
+
+ public function testCreateNamespace(): void
+ {
+ $constantStub = $this->buildDefineStub('\\OtherSpace\\MyClass');
+ $file = new FileElement('hash', 'path');
+ $contextStack = self::createContext(new Context('Space\\MyClass'))->push($file);
+
+ $this->fixture->create($contextStack, $constantStub, new ProjectFactoryStrategies([]));
+
+ $constant = current($file->getConstants());
+
+ $this->assertConstant($constant, '\\OtherSpace\\MyClass');
+ }
+
+ public function testCreateGlobal(): void
+ {
+ $constantStub = $this->buildDefineStub();
+ $file = new FileElement('hash', 'path');
+ $contextStack = self::createContext()->push($file);
+
+ $this->fixture->create($contextStack, $constantStub, new ProjectFactoryStrategies([]));
+
+ $constant = current($file->getConstants());
+
+ $this->assertConstant($constant, '');
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $docBlock = new DocBlockDescriptor('');
+
+ $constantStub = new Expression(
+ new FuncCall(
+ new Name('define'),
+ [
+ new Arg(new String_('MY_CONST1')),
+ new Arg(new String_('a')),
+ ],
+ ),
+ ['comments' => [$doc]],
+ );
+ $typeContext = new Context('Space\\MyClass');
+
+ $this->docBlockFactory->create('Text', $typeContext)->willReturn($docBlock);
+
+ $file = new FileElement('hash', 'path');
+ $contextStack = self::createContext($typeContext)->push($file);
+
+ $this->fixture->create($contextStack, $constantStub, new ProjectFactoryStrategies([]));
+
+ $constant = current($file->getConstants());
+ $this->assertConstant($constant, '');
+ $this->assertSame($docBlock, $constant->getDocBlock());
+ }
+
+ private function buildDefineStub(string $namespace = ''): Expression
+ {
+ return new Expression(
+ new FuncCall(
+ new Name('define'),
+ [
+ new Arg(new String_($namespace ? $namespace . '\\MY_CONST1' : 'MY_CONST1')),
+ new Arg(new String_('a')),
+ ],
+ ),
+ );
+ }
+
+ private function assertConstant(ConstantDescriptor $constant, string $namespace): void
+ {
+ $this->assertInstanceOf(ConstantDescriptor::class, $constant);
+ $this->assertEquals($namespace . '\\MY_CONST1', (string) $constant->getFqsen());
+ $this->assertEquals('\'a\'', $constant->getValue());
+ $this->assertEquals('public', (string) $constant->getVisibility());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/DummyFactoryStrategy.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/DummyFactoryStrategy.php
new file mode 100644
index 00000000..0d01406b
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/DummyFactoryStrategy.php
@@ -0,0 +1,47 @@
+docblockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new EnumCase($this->docblockFactory->reveal(), new Standard());
+ }
+
+ public function testMatches(): void
+ {
+ self::assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ self::assertTrue(
+ $this->fixture->matches(
+ self::createContext(null),
+ $this->prophesize(EnumCaseNode::class)->reveal(),
+ ),
+ );
+ }
+
+ public function testSimpleCreate(): void
+ {
+ $containerMock = $this->prophesize(StrategyContainer::class)->reveal();
+ $enumMock = $this->buildEnumCaseMock();
+ $enumMock->getDocComment()->willReturn(null);
+
+ $result = $this->performCreate($enumMock->reveal());
+
+ self::assertInstanceOf(EnumElement::class, $result);
+ self::assertEquals(
+ [
+ '\Space\MyEnum::VALUE' => new EnumCaseElement(
+ new Fqsen('\Space\MyEnum::VALUE'),
+ null,
+ new Location(1),
+ new Location(2),
+ ),
+ ],
+ $result->getCases(),
+ );
+ }
+
+ private function performCreate(EnumCaseNode $enumCase): EnumElement
+ {
+ $factory = new ProjectFactoryStrategies([]);
+ $enum = new EnumElement(new Fqsen('\myEnum'), null);
+ $this->fixture->create(self::createContext(null)->push($enum), $enumCase, $factory);
+
+ return $enum;
+ }
+
+ private function buildEnumCaseMock(): ObjectProphecy
+ {
+ $enumMock = $this->prophesize(EnumCaseNode::class);
+ $enumMock->expr = null;
+ $enumMock->getAttribute('fqsen')->willReturn(new Fqsen('\Space\MyEnum::VALUE'));
+ $enumMock->getLine()->willReturn(1);
+ $enumMock->getEndLine()->willReturn(2);
+
+ return $enumMock;
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/Enum_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/Enum_Test.php
new file mode 100644
index 00000000..6a66d257
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/Enum_Test.php
@@ -0,0 +1,181 @@
+docblockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Enum_($this->docblockFactory->reveal());
+ }
+
+ public function testMatches(): void
+ {
+ self::assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ self::assertTrue(
+ $this->fixture->matches(
+ self::createContext(null),
+ $this->prophesize(EnumNode::class)->reveal(),
+ ),
+ );
+ }
+
+ public function testSimpleCreate(): void
+ {
+ $containerMock = m::mock(StrategyContainer::class);
+ $enumMock = $this->buildEnumMock();
+ $enumMock->shouldReceive('getDocComment')->andReturnNull();
+
+ $result = $this->performCreate($enumMock, $containerMock);
+
+ self::assertInstanceOf(EnumElement::class, $result);
+ self::assertEquals('\Space\MyEnum', (string) $result->getFqsen());
+ }
+
+ public function testBackedEnumTypeIsSet(): void
+ {
+ $containerMock = m::mock(StrategyContainer::class);
+ $enumMock = $this->buildEnumMock();
+ $enumMock->shouldReceive('getDocComment')->andReturnNull();
+ $enumMock->scalarType = new Identifier('string');
+
+ $result = $this->performCreate($enumMock, $containerMock);
+
+ self::assertInstanceOf(EnumElement::class, $result);
+ self::assertEquals('\Space\MyEnum', (string) $result->getFqsen());
+ self::assertEquals(new String_(), $result->getBackedType());
+ }
+
+ public function testClassImplementingInterface(): void
+ {
+ $containerMock = m::mock(StrategyContainer::class);
+ $enumMock = $this->buildEnumMock();
+ $enumMock->shouldReceive('getDocComment')->andReturnNull();
+ $enumMock->extends = 'Space\MyParent';
+ $enumMock->implements = [
+ new Name('MyInterface'),
+ ];
+
+ $result = $this->performCreate($enumMock, $containerMock);
+
+ self::assertInstanceOf(EnumElement::class, $result);
+ self::assertEquals('\Space\MyEnum', (string) $result->getFqsen());
+
+ self::assertEquals(
+ ['\MyInterface' => new Fqsen('\MyInterface')],
+ $result->getInterfaces(),
+ );
+ }
+
+ public function testIteratesStatements(): void
+ {
+ $method1 = new ClassMethod('MyEnum::method1');
+ $method1Descriptor = new MethodElement(new Fqsen('\MyEnum::method1'));
+ $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $enumMock = $this->buildEnumMock();
+ $enumMock->shouldReceive('getDocComment')->andReturnNull();
+ $enumMock->stmts = [$method1];
+
+ $strategyMock->create(Argument::type(ContextStack::class), $method1, $containerMock)
+ ->will(function ($args) use ($method1Descriptor): void {
+ $args[0]->peek()->addMethod($method1Descriptor);
+ })
+ ->shouldBeCalled();
+
+ $containerMock->findMatching(
+ Argument::type(ContextStack::class),
+ $method1,
+ )->willReturn($strategyMock->reveal());
+
+ $result = $this->performCreate($enumMock, $containerMock->reveal());
+
+ self::assertInstanceOf(EnumElement::class, $result);
+ self::assertEquals('\Space\MyEnum', (string) $result->getFqsen());
+ self::assertEquals(
+ ['\MyEnum::method1' => $method1Descriptor],
+ $result->getMethods(),
+ );
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $enumMock = $this->buildEnumMock();
+ $enumMock->shouldReceive('getDocComment')->andReturn($doc);
+ $docBlock = new DocBlockElement('');
+ $this->docblockFactory->create('Text', null)->willReturn($docBlock);
+ $containerMock = m::mock(StrategyContainer::class);
+
+ $result = $this->performCreate($enumMock, $containerMock);
+
+ self::assertSame($docBlock, $result->getDocBlock());
+ }
+
+ private function buildEnumMock(): m\MockInterface|ClassNode
+ {
+ $enumMock = m::mock(EnumNode::class);
+ $enumMock->scalarType = null;
+ $enumMock->shouldReceive('getAttribute')->andReturn(new Fqsen('\Space\MyEnum'));
+ $enumMock->implements = [];
+ $enumMock->stmts = [];
+ $enumMock->shouldReceive('getLine')->andReturn(1);
+ $enumMock->shouldReceive('getEndLine')->andReturn(2);
+
+ return $enumMock;
+ }
+
+ private function performCreate(EnumNode $enumMock, StrategyContainer $containerMock): EnumElement
+ {
+ $file = new File('hash', 'path');
+ $this->fixture->create(self::createContext(null)->push($file), $enumMock, $containerMock);
+
+ return current($file->getEnums());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/File/CreateCommandTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/File/CreateCommandTest.php
new file mode 100644
index 00000000..d6df9cdd
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/File/CreateCommandTest.php
@@ -0,0 +1,55 @@
+file = new LocalFile(__FILE__);
+ $this->strategies = new ProjectFactoryStrategies([]);
+ $this->fixture = new CreateCommand(
+ new ContextStack(new Project('test')),
+ $this->file,
+ $this->strategies,
+ );
+ }
+
+ public function testGetFile(): void
+ {
+ $this->assertSame($this->file, $this->fixture->getFile());
+ }
+
+ public function testGetStrategies(): void
+ {
+ $this->assertSame($this->strategies, $this->fixture->getStrategies());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/FileTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/FileTest.php
new file mode 100644
index 00000000..7e7fdd94
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/FileTest.php
@@ -0,0 +1,155 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->nodesFactoryMock = $this->prophesize(NodesFactory::class);
+ $this->fixture = new File($this->docBlockFactory->reveal(), $this->nodesFactoryMock->reveal());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), m::mock(SourceFile::class)));
+ }
+
+ public function testMiddlewareIsExecuted(): void
+ {
+ $file = new FileElement('aa', __FILE__);
+ $this->nodesFactoryMock->create(file_get_contents(__FILE__))->willReturn([]);
+ $middleware = $this->prophesize(Middleware::class);
+ $middleware->execute(Argument::any(), Argument::any())->shouldBeCalled()->willReturn($file);
+ $fixture = new File(
+ $this->docBlockFactory->reveal(),
+ $this->nodesFactoryMock->reveal(),
+ [$middleware->reveal()],
+ );
+ $context = self::createContext();
+ $containerMock = $this->prophesize(StrategyContainer::class);
+
+ $fixture->create($context, new SourceFile\LocalFile(__FILE__), $containerMock->reveal());
+
+ $result = current($context->getProject()->getFiles());
+ $this->assertSame($result, $file);
+ }
+
+ public function testMiddlewareIsChecked(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ new File($this->docBlockFactory->reveal(), $this->nodesFactoryMock->reveal(), [new stdClass()]);
+ }
+
+ #[DataProvider('nodeProvider')]
+ public function testFileGetsCommentFromFirstNode(Node $node, DocBlockDescriptor $docblock): void
+ {
+ $this->nodesFactoryMock->create(file_get_contents(__FILE__))->willReturn([$node]);
+ $this->docBlockFactory->create('Text', null)->willReturn($docblock);
+
+ $strategies = $this->prophesize(StrategyContainer::class);
+ $strategies->findMatching(Argument::type(ContextStack::class), $node)->willReturn(
+ $this->prophesize(ProjectFactoryStrategy::class)->reveal(),
+ );
+
+ $context = self::createContext();
+
+ $this->fixture->create($context, new SourceFile\LocalFile(__FILE__), $strategies->reveal());
+
+ $file = current($context->getProject()->getFiles());
+ $this->assertInstanceOf(FileElement::class, $file);
+ $this->assertSame($docblock, $file->getDocBlock());
+ }
+
+ /** @return array */
+ public static function nodeProvider(): array
+ {
+ $docBlockNode = new DocBlockNode('Text');
+ $namespaceNode = new NamespaceNode(new Name('mySpace'));
+ $namespaceNode->getAttribute('fsqen', new Fqsen('\mySpace'));
+ $namespaceNode->setAttribute('comments', [$docBlockNode]);
+
+ $classNode = new ClassNode('myClass');
+ $classNode->setAttribute('comments', [$docBlockNode, new DocBlockNode('')]);
+
+ $namespaceNode2 = new NamespaceNode(new Name('mySpace'));
+ $namespaceNode2->getAttribute('fsqen', new Fqsen('\mySpace'));
+ $namespaceNode2->setAttribute('comments', [new CommentNode('@codingStandardsIgnoreStart'), $docBlockNode]);
+
+ return [
+ 'With namespace' => [
+ $namespaceNode,
+ new DocBlockDescriptor(''),
+ ],
+ 'With class' => [
+ $classNode,
+ new DocBlockDescriptor(''),
+ ],
+ 'With comments' => [
+ $namespaceNode2,
+ new DocBlockDescriptor(''),
+ ],
+ ];
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/Function_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/Function_Test.php
new file mode 100644
index 00000000..cd41c748
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/Function_Test.php
@@ -0,0 +1,139 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Function_($this->docBlockFactory->reveal());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(
+ self::createContext(null)->push(new File('hash', 'path')),
+ $this->prophesize(\PhpParser\Node\Stmt\Function_::class)->reveal(),
+ ));
+ }
+
+ public function testCreateWithoutParameters(): void
+ {
+ $functionMock = $this->prophesize(\PhpParser\Node\Stmt\Function_::class);
+ $functionMock->byRef = false;
+ $functionMock->stmts = [];
+ $functionMock->getAttribute('fqsen')->willReturn(new Fqsen('\SomeSpace::function()'));
+ $functionMock->params = [];
+ $functionMock->getDocComment()->willReturn(null);
+ $functionMock->getLine()->willReturn(1);
+ $functionMock->getEndLine()->willReturn(2);
+ $functionMock->getReturnType()->willReturn(null);
+
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $file = new File('hash', 'path');
+
+ $this->fixture->create(self::createContext()->push($file), $functionMock->reveal(), $containerMock->reveal());
+
+ $function = current($file->getFunctions());
+ $this->assertInstanceOf(FunctionDescriptor::class, $function);
+ $this->assertEquals('\SomeSpace::function()', (string) $function->getFqsen());
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $functionMock = $this->prophesize(\PhpParser\Node\Stmt\Function_::class);
+ $functionMock->byRef = false;
+ $functionMock->stmts = [];
+ $functionMock->getAttribute('fqsen')->willReturn(new Fqsen('\SomeSpace::function()'));
+ $functionMock->params = [];
+ $functionMock->getDocComment()->willReturn($doc);
+ $functionMock->getLine()->willReturn(1);
+ $functionMock->getEndLine()->willReturn(2);
+ $functionMock->getReturnType()->willReturn(null);
+
+ $docBlock = new DocBlockDescriptor('');
+ $this->docBlockFactory->create('Text', null)->willReturn($docBlock);
+
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $file = new File('hash', 'path');
+ $this->fixture->create(self::createContext()->push($file), $functionMock->reveal(), $containerMock->reveal());
+
+ $function = current($file->getFunctions());
+
+ $this->assertEquals('\SomeSpace::function()', (string) $function->getFqsen());
+ $this->assertSame($docBlock, $function->getDocBlock());
+ }
+
+ public function testIteratesStatements(): void
+ {
+ $doc = new Doc('Text');
+ $functionMock = $this->prophesize(\PhpParser\Node\Stmt\Function_::class);
+ $functionMock->byRef = false;
+ $functionMock->stmts = [];
+ $functionMock->getAttribute('fqsen')->willReturn(new Fqsen('\SomeSpace::function()'));
+ $functionMock->params = [];
+ $functionMock->getDocComment()->willReturn(null);
+ $functionMock->getLine()->willReturn(1);
+ $functionMock->getEndLine()->willReturn(2);
+ $functionMock->getReturnType()->willReturn(null);
+ $functionMock->stmts = [new Expression(new FuncCall(new Name('hook')))];
+
+ $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $containerMock->findMatching(
+ Argument::type(ContextStack::class),
+ Argument::type(Expression::class),
+ )->willReturn($strategyMock->reveal())->shouldBeCalledOnce();
+
+ $file = new File('hash', 'path');
+ $this->fixture->create(
+ self::createContext(null)->push($file),
+ $functionMock->reveal(),
+ $containerMock->reveal(),
+ );
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/GlobalConstantIteratorTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/GlobalConstantIteratorTest.php
new file mode 100644
index 00000000..971a6054
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/GlobalConstantIteratorTest.php
@@ -0,0 +1,93 @@
+setAttribute('fqsen', new Fqsen((string) $const1->name));
+ $const2 = new Const_('\Space\MY_CONST2', new Variable('b'));
+ $const2->setAttribute('fqsen', new Fqsen((string) $const2->name));
+
+ $globalConstantNode = new ConstStatement([$const1, $const2]);
+
+ $i = 1;
+ foreach (new GlobalConstantIterator($globalConstantNode) as $constant) {
+ $this->assertEquals('\Space\MY_CONST' . $i, $constant->getName());
+ $this->assertEquals('\Space\MY_CONST' . $i, (string) $constant->getFqsen());
+
+ ++$i;
+ }
+ }
+
+ public function testKey(): void
+ {
+ $constant = m::mock(ConstStatement::class);
+
+ $fixture = new GlobalConstantIterator($constant);
+
+ $this->assertEquals(0, $fixture->key());
+ $fixture->next();
+ $this->assertEquals(1, $fixture->key());
+ }
+
+ public function testProxyMethods(): void
+ {
+ $constant = m::mock(ConstStatement::class);
+ $constant->shouldReceive('getLine')->once()->andReturn(10);
+
+ $fixture = new GlobalConstantIterator($constant);
+
+ $this->assertEquals(10, $fixture->getLine());
+ }
+
+ public function testGetDocCommentPropFirst(): void
+ {
+ $const = m::mock(Const_::class);
+ $constants = m::mock(ConstStatement::class);
+ $constants->consts = [$const];
+
+ $const->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
+ $constants->shouldReceive('getDocComment')->never();
+
+ $fixture = new GlobalConstantIterator($constants);
+
+ $this->assertEquals('test', $fixture->getDocComment()->getText());
+ }
+
+ public function testGetDocComment(): void
+ {
+ $const = m::mock(Const_::class);
+ $constants = m::mock(ConstStatement::class);
+ $constants->consts = [$const];
+
+ $const->shouldReceive('getDocComment')->once()->andReturnNull();
+ $constants->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
+
+ $fixture = new GlobalConstantIterator($constants);
+
+ $this->assertEquals('test', $fixture->getDocComment()->getText());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/GlobalConstantTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/GlobalConstantTest.php
new file mode 100644
index 00000000..533c18f6
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/GlobalConstantTest.php
@@ -0,0 +1,109 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new GlobalConstant($this->docBlockFactory->reveal(), new PrettyPrinter());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), $this->buildConstantIteratorStub()));
+ }
+
+ public function testCreate(): void
+ {
+ $factory = new ProjectFactoryStrategies([]);
+
+ $constantStub = $this->buildConstantIteratorStub();
+
+ $file = new FileElement('hash', 'path');
+ $this->fixture->create(self::createContext(null)->push($file), $constantStub, $factory);
+ $constant = current($file->getConstants());
+
+ $this->assertConstant($constant);
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $docBlock = new DocBlockDescriptor('');
+
+ $const = new Const_('\Space\MyClass\MY_CONST1', new String_('a'), ['comments' => [$doc]]);
+ $const->setAttribute('fqsen', new Fqsen((string) $const->name));
+
+ $constantStub = new ConstStatement([$const]);
+ $containerMock = m::mock(StrategyContainer::class);
+ $this->docBlockFactory->create('Text', null)->willReturn($docBlock);
+
+ $file = new FileElement('hash', 'path');
+ $this->fixture->create(self::createContext(null)->push($file), $constantStub, $containerMock);
+ $constant = current($file->getConstants());
+
+ $this->assertConstant($constant);
+ $this->assertSame($docBlock, $constant->getDocBlock());
+ }
+
+ private function buildConstantIteratorStub(): ConstStatement
+ {
+ $const = new Const_('\Space\MyClass\MY_CONST1', new String_('a'));
+ $const->setAttribute('fqsen', new Fqsen((string) $const->name));
+
+ return new ConstStatement([$const]);
+ }
+
+ private function assertConstant(ConstantDescriptor $constant): void
+ {
+ $this->assertInstanceOf(ConstantDescriptor::class, $constant);
+ $this->assertEquals('\Space\MyClass\MY_CONST1', (string) $constant->getFqsen());
+ $this->assertEquals('\'a\'', $constant->getValue());
+ $this->assertEquals('public', (string) $constant->getVisibility());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/Interface_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/Interface_Test.php
new file mode 100644
index 00000000..91d40920
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/Interface_Test.php
@@ -0,0 +1,143 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Interface_($this->docBlockFactory->reveal());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), m::mock(InterfaceNode::class)));
+ }
+
+ public function testSimpleCreate(): void
+ {
+ $interfaceMock = $this->buildClassMock();
+ $interfaceMock->shouldReceive('getDocComment')->andReturnNull();
+ $containerMock = $this->prophesize(StrategyContainer::class);
+
+ $interface = $this->performCreate($interfaceMock, $containerMock->reveal());
+
+ $this->assertInstanceOf(InterfaceElement::class, $interface);
+ $this->assertEquals('\Space\MyInterface', (string) $interface->getFqsen());
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $docBlock = new DocBlockElement('');
+ $this->docBlockFactory->create('Text', null)->willReturn($docBlock);
+ $containerMock = $this->prophesize(StrategyContainer::class);
+
+ $interfaceMock = $this->buildClassMock();
+ $interfaceMock->shouldReceive('getDocComment')->andReturn($doc);
+
+ $interface = $this->performCreate($interfaceMock, $containerMock->reveal());
+
+ $this->assertSame($docBlock, $interface->getDocBlock());
+ }
+
+ public function testIteratesStatements(): void
+ {
+ $method1 = new ClassMethod('MyClass::method1');
+ $method1Descriptor = new MethodElement(new Fqsen('\MyClass::method1'));
+ $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $classMock = $this->buildClassMock();
+ $classMock->shouldReceive('getDocComment')->andReturnNull();
+ $classMock->stmts = [$method1];
+
+ $strategyMock->create(Argument::type(ContextStack::class), $method1, $containerMock)
+ ->will(function ($args) use ($method1Descriptor): void {
+ $args[0]->peek()->addMethod($method1Descriptor);
+ })
+ ->shouldBeCalled();
+
+ $containerMock->findMatching(
+ Argument::type(ContextStack::class),
+ $method1,
+ )->willReturn($strategyMock->reveal());
+
+ $class = $this->performCreate($classMock, $containerMock->reveal());
+
+ $this->assertInstanceOf(InterfaceElement::class, $class);
+ $this->assertEquals('\Space\MyInterface', (string) $class->getFqsen());
+ $this->assertEquals(
+ ['\MyClass::method1' => $method1Descriptor],
+ $class->getMethods(),
+ );
+ }
+
+ private function buildClassMock(): m\MockInterface|InterfaceNode
+ {
+ $interfaceMock = m::mock(InterfaceNode::class);
+ $interfaceMock->extends = [];
+ $interfaceMock->stmts = [];
+ $interfaceMock->shouldReceive('getAttribute')->andReturn(new Fqsen('\Space\MyInterface'));
+ $interfaceMock->shouldReceive('getLine')->andReturn(1);
+ $interfaceMock->shouldReceive('getEndLine')->andReturn(2);
+
+ return $interfaceMock;
+ }
+
+ private function performCreate(m\MockInterface $interfaceMock, StrategyContainer $containerMock): InterfaceElement
+ {
+ $file = new FileElement('hash', 'path');
+ $this->fixture->create(
+ self::createContext(null)->push($file),
+ $interfaceMock,
+ $containerMock,
+ );
+
+ return current($file->getInterfaces());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/MethodTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/MethodTest.php
new file mode 100644
index 00000000..2e6ab447
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/MethodTest.php
@@ -0,0 +1,168 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Method($this->docBlockFactory->reveal());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), m::mock(ClassMethod::class)));
+ }
+
+ public function testCreateWithoutParameters(): void
+ {
+ $classMethodMock = $this->buildClassMethodMock();
+ $classMethodMock->params = [];
+ $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(false);
+ $classMethodMock->shouldReceive('isProtected')->once()->andReturn(false);
+ $classMethodMock->shouldReceive('getDocComment')->once()->andReturnNull();
+ $classMethodMock->shouldReceive('getReturnType')->once()->andReturn(null);
+
+ $containerMock = m::mock(StrategyContainer::class);
+ $containerMock->shouldReceive('findMatching')->never();
+
+ $class = new ClassElement(new Fqsen('\\MyClass'));
+ $this->fixture->create(self::createContext(null)->push($class), $classMethodMock, $containerMock);
+
+ $method = current($class->getMethods());
+ $this->assertInstanceOf(MethodDescriptor::class, $method);
+ $this->assertEquals('\SomeSpace\Class::function()', (string) $method->getFqsen());
+ $this->assertEquals('public', (string) $method->getVisibility());
+ }
+
+ public function testCreateProtectedMethod(): void
+ {
+ $classMethodMock = $this->buildClassMethodMock();
+ $classMethodMock->params = [];
+ $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(false);
+ $classMethodMock->shouldReceive('isProtected')->once()->andReturn(true);
+ $classMethodMock->shouldReceive('getDocComment')->once()->andReturnNull();
+ $classMethodMock->shouldReceive('getReturnType')->once()->andReturn(null);
+
+ $containerMock = m::mock(StrategyContainer::class);
+ $containerMock->shouldReceive('findMatching')->never();
+
+ $class = new ClassElement(new Fqsen('\\MyClass'));
+ $this->fixture->create(self::createContext(null)->push($class), $classMethodMock, $containerMock);
+
+ $method = current($class->getMethods());
+ $this->assertInstanceOf(MethodDescriptor::class, $method);
+ $this->assertEquals('\SomeSpace\Class::function()', (string) $method->getFqsen());
+ $this->assertEquals('protected', (string) $method->getVisibility());
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $classMethodMock = $this->buildClassMethodMock();
+ $classMethodMock->params = [];
+ $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(true);
+ $classMethodMock->shouldReceive('getDocComment')->andReturn($doc);
+ $classMethodMock->shouldReceive('getReturnType')->once()->andReturn(null);
+
+ $docBlock = new DocBlockDescriptor('');
+ $this->docBlockFactory->create('Text', null)->willReturn($docBlock);
+ $containerMock = $this->prophesize(StrategyContainer::class);
+
+ $class = new ClassElement(new Fqsen('\\MyClass'));
+ $this->fixture->create(self::createContext(null)->push($class), $classMethodMock, $containerMock->reveal());
+
+ $method = current($class->getMethods());
+ $this->assertInstanceOf(MethodDescriptor::class, $method);
+ $this->assertEquals('\SomeSpace\Class::function()', (string) $method->getFqsen());
+ $this->assertSame($docBlock, $method->getDocBlock());
+ }
+
+ /** @return MockInterface|ClassMethod */
+ private function buildClassMethodMock(): MockInterface
+ {
+ $methodMock = m::mock(ClassMethod::class);
+ $methodMock->name = new Identifier('function');
+ $methodMock->byRef = false;
+ $methodMock->stmts = [];
+ $methodMock->shouldReceive('getAttribute')->andReturn(new Fqsen('\SomeSpace\Class::function()'));
+ $methodMock->params = [];
+
+ $methodMock->shouldReceive('isStatic')->once()->andReturn(true);
+ $methodMock->shouldReceive('isFinal')->once()->andReturn(true);
+ $methodMock->shouldReceive('isAbstract')->once()->andReturn(true);
+ $methodMock->shouldReceive('getLine')->once()->andReturn(1);
+ $methodMock->shouldReceive('getStartFilePos')->once()->andReturn(10);
+ $methodMock->shouldReceive('getEndLine')->once()->andReturn(2);
+ $methodMock->shouldReceive('getEndFilePos')->once()->andReturn(20);
+
+ return $methodMock;
+ }
+
+ public function testIteratesStatements(): void
+ {
+ $method1 = $this->buildClassMethodMock();
+ $method1->shouldReceive('isPrivate')->once()->andReturn(true);
+ $method1->shouldReceive('getDocComment')->andReturn(null);
+ $method1->shouldReceive('getReturnType')->once()->andReturn(null);
+ $method1->stmts = [new Expression(new FuncCall(new Name('hook')))];
+
+ $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $containerMock->findMatching(
+ Argument::type(ContextStack::class),
+ Argument::type(Expression::class),
+ )->willReturn($strategyMock->reveal())->shouldBeCalledOnce();
+
+ $class = new ClassElement(new Fqsen('\\MyClass'));
+ $this->fixture->create(self::createContext(null)->push($class), $method1, $containerMock->reveal());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/Namespace_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/Namespace_Test.php
new file mode 100644
index 00000000..2e45e06c
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/Namespace_Test.php
@@ -0,0 +1,82 @@
+fixture = new Namespace_();
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(
+ self::createContext(null),
+ $this->prophesize(NamespaceNode::class)->reveal(),
+ ));
+ }
+
+ public function testCreateThrowsException(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ $this->fixture->create(
+ self::createContext(null),
+ new stdClass(),
+ $this->prophesize(StrategyContainer::class)->reveal(),
+ );
+ }
+
+ public function testIteratesStatements(): void
+ {
+ $class = new ClassNode('\MyClass');
+ $classElement = new ClassElement(new Fqsen('\MyClass'));
+ $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $namespace = new NamespaceNode(new Name('MyNamespace'));
+ $namespace->setAttribute('fqsen', new Fqsen('\MyNamespace'));
+ $namespace->stmts = [$class];
+
+ $strategyMock->create(Argument::type(ContextStack::class), $class, $containerMock)
+ ->will(function ($args) use ($classElement): void {
+ $args[0]->peek()->addClass($classElement);
+ })
+ ->shouldBeCalled();
+
+ $containerMock->findMatching(
+ Argument::type(ContextStack::class),
+ $class,
+ )->willReturn($strategyMock->reveal());
+
+ $file = new File('hash', 'path');
+ $this->fixture->create(self::createContext(null)->push($file), $namespace, $containerMock->reveal());
+ $class = current($file->getClasses());
+ $fqsen = current($file->getNamespaces());
+
+ $this->assertInstanceOf(ClassElement::class, $class);
+ $this->assertEquals('\MyClass', (string) $class->getFqsen());
+ $this->assertEquals(new Fqsen('\MyNamespace'), $fqsen);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyBuilderTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyBuilderTest.php
new file mode 100644
index 00000000..297734a1
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyBuilderTest.php
@@ -0,0 +1,62 @@
+createMock(DocBlockFactoryInterface::class);
+ $valueConverter = $this->createMock(PrettyPrinter\Standard::class);
+ $strategies = $this->createMock(StrategyContainer::class);
+ $reducers = [];
+
+ $prop1 = new PropertyProperty('prop1');
+ $propertyNode = new PropertyNode(1, [$prop1]);
+ $properties = new PropertyIterator($propertyNode);
+
+ $builder = PropertyBuilder::create($valueConverter, $docBlockFactory, $strategies, $reducers);
+ $builder->fqsen($fqsen)
+ ->visibility($properties)
+ ->docblock($properties->getDocComment())
+ ->default($properties->getDefault())
+ ->static(true)
+ ->startLocation($startLocation)
+ ->endLocation($endLocation)
+ ->type($properties->getType())
+ ->readOnly(true)
+ ->hooks($properties->getHooks());
+
+ $context = \phpDocumentor\Reflection\Php\Factory\TestCase::createContext();
+ $property = $builder->build($context);
+
+ $this->assertSame($fqsen, $property->getFqsen());
+ $this->assertEquals($visibility, $property->getVisibility());
+ $this->assertNull($property->getDocBlock());
+ $this->assertNull($property->getDefault());
+ $this->assertTrue($property->isStatic());
+ $this->assertSame($startLocation, $property->getLocation());
+ $this->assertSame($endLocation, $property->getEndLocation());
+ $this->assertNull($property->getType());
+ $this->assertTrue($property->isReadOnly());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyIteratorTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyIteratorTest.php
new file mode 100644
index 00000000..a97feaa8
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyIteratorTest.php
@@ -0,0 +1,110 @@
+assertEquals('prop' . $i, $property->getName());
+ ++$i;
+ }
+ }
+
+ public function testKey(): void
+ {
+ $propertyMock = m::mock(PropertyNode::class);
+
+ $fixture = new PropertyIterator($propertyMock);
+
+ $this->assertEquals(0, $fixture->key());
+ $fixture->next();
+ $this->assertEquals(1, $fixture->key());
+ }
+
+ public function testProxyMethods(): void
+ {
+ $propertyMock = m::mock(PropertyNode::class);
+ $propertyMock->shouldReceive('isPublic')->once()->andReturn(true);
+ $propertyMock->shouldReceive('isProtected')->once()->andReturn(true);
+ $propertyMock->shouldReceive('isPrivate')->once()->andReturn(true);
+ $propertyMock->shouldReceive('isStatic')->once()->andReturn(true);
+ $propertyMock->shouldReceive('isReadOnly')->once()->andReturn(true);
+ $propertyMock->shouldReceive('getLine')->once()->andReturn(10);
+
+ $fixture = new PropertyIterator($propertyMock);
+
+ $this->assertTrue($fixture->isStatic());
+ $this->assertTrue($fixture->isReadOnly());
+ $this->assertTrue($fixture->isPrivate());
+ $this->assertTrue($fixture->isProtected());
+ $this->assertTrue($fixture->isPublic());
+ $this->assertEquals(10, $fixture->getLine());
+ }
+
+ public function testGetDefault(): void
+ {
+ $prop = m::mock(PropertyProperty::class);
+ $prop->default = new String_('myDefault');
+ $property = new PropertyNode(1, [$prop]);
+
+ $fixture = new PropertyIterator($property);
+
+ $this->assertEquals(new String_('myDefault'), $fixture->getDefault());
+ }
+
+ public function testGetDocCommentPropFirst(): void
+ {
+ $prop = m::mock(PropertyProperty::class);
+ $propertyNode = m::mock(PropertyNode::class);
+ $propertyNode->props = [$prop];
+
+ $prop->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
+ $propertyNode->shouldReceive('getDocComment')->never();
+
+ $fixture = new PropertyIterator($propertyNode);
+
+ $this->assertEquals('test', $fixture->getDocComment()->getText());
+ }
+
+ public function testGetDocComment(): void
+ {
+ $prop = m::mock(PropertyProperty::class);
+ $propertyNode = m::mock(PropertyNode::class);
+ $propertyNode->props = [$prop];
+
+ $prop->shouldReceive('getDocComment')->once()->andReturnNull();
+ $propertyNode->shouldReceive('getDocComment')->once()->andReturn(new Doc('test'));
+
+ $fixture = new PropertyIterator($propertyNode);
+
+ $this->assertEquals('test', $fixture->getDocComment()->getText());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyTest.php
new file mode 100644
index 00000000..42c5a722
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/PropertyTest.php
@@ -0,0 +1,159 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Property($this->docBlockFactory->reveal(), new PrettyPrinter());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), new PropertyNode(1, [])));
+ }
+
+ #[DataProvider('visibilityProvider')]
+ public function testCreateWithVisibility(int $input, string $expectedVisibility): void
+ {
+ $constantStub = $this->buildPropertyMock($input);
+
+ $class = $this->performCreate($constantStub);
+
+ $property = current($class->getProperties());
+ $this->assertProperty($property, $expectedVisibility);
+ }
+
+ /** @return array */
+ public static function visibilityProvider(): array
+ {
+ return [
+ [
+ ClassNode::MODIFIER_PUBLIC,
+ 'public',
+ ],
+ [
+ ClassNode::MODIFIER_PROTECTED,
+ 'protected',
+ ],
+ [
+ ClassNode::MODIFIER_PRIVATE,
+ 'private',
+ ],
+ ];
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('text');
+ $docBlock = new DocBlockDescriptor('text');
+ $this->docBlockFactory->create('text', null)->willReturn($docBlock);
+
+ $property = new PropertyProperty('property', new String_('MyDefault'), ['comments' => [$doc]]);
+ $property->setAttribute('fqsen', new Fqsen('\myClass::$property'));
+ $node = new PropertyNode(ClassNode::MODIFIER_PRIVATE | ClassNode::MODIFIER_STATIC, [$property]);
+ $class = $this->performCreate($node);
+ $property = current($class->getProperties());
+
+ $this->assertProperty($property, 'private');
+ $this->assertSame($docBlock, $property->getDocBlock());
+ }
+
+ public function testCreateMultipleInOneStatement(): void
+ {
+ $property1 = new PropertyProperty('property1', new String_('MyDefault1'));
+ $property1->setAttribute('fqsen', new Fqsen('\myClass::$property1'));
+ $property2 = new PropertyProperty('property2', new String_('MyDefault2'));
+ $property2->setAttribute('fqsen', new Fqsen('\myClass::$property2'));
+ $node = new PropertyNode(
+ ClassNode::MODIFIER_PRIVATE | ClassNode::MODIFIER_STATIC,
+ [$property1, $property2],
+ );
+
+ $class = $this->performCreate($node);
+ $properties = $class->getProperties();
+ $property1 = current($properties);
+ next($properties);
+ $property2 = current($properties);
+
+ $this->assertProperty($property1, 'private', 'property1', '\'MyDefault1\'');
+ $this->assertProperty($property2, 'private', 'property2', '\'MyDefault2\'');
+ }
+
+ private function buildPropertyMock(int $modifier): PropertyNode
+ {
+ $property = new PropertyProperty('property', new String_('MyDefault'));
+ $property->setAttribute('fqsen', new Fqsen('\myClass::$property'));
+
+ return new PropertyNode($modifier | ClassNode::MODIFIER_STATIC, [$property]);
+ }
+
+ private function assertProperty(
+ PropertyDescriptor $property,
+ string $visibility,
+ string $name = 'property',
+ string|null $default = '\'MyDefault\'',
+ ): void {
+ $this->assertInstanceOf(PropertyDescriptor::class, $property);
+ $this->assertEquals('\myClass::$' . $name, (string) $property->getFqsen());
+ $this->assertTrue($property->isStatic());
+ $this->assertEquals($default, $property->getDefault());
+ $this->assertEquals($visibility, (string) $property->getVisibility());
+ }
+
+ private function performCreate(PropertyNode $property): ClassElement
+ {
+ $factory = new ProjectFactoryStrategies([]);
+ $class = new ClassElement(new Fqsen('\myClass'));
+ $this->fixture->create(self::createContext(null)->push($class), $property, $factory);
+
+ return $class;
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/TestCase.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/TestCase.php
new file mode 100644
index 00000000..36acc0a4
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/TestCase.php
@@ -0,0 +1,47 @@
+expectException(InvalidArgumentException::class);
+ $this->fixture->create(self::createContext(null), new stdClass(), m::mock(StrategyContainer::class));
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/TraitUseTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/TraitUseTest.php
new file mode 100644
index 00000000..78414df2
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/TraitUseTest.php
@@ -0,0 +1,71 @@
+fixture = new TraitUse();
+ }
+
+ public function testMatchesOnlyTraitUseNode(): void
+ {
+ self::assertTrue(
+ $this->fixture->matches(
+ self::createContext(),
+ $this->givenTraitUse(),
+ ),
+ );
+ }
+
+ public function testCreateThrowsExceptionWhenStackDoesNotContainClass(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+
+ $context = self::createContext()->push(new Interface_(new Fqsen('\Interface')));
+ $this->fixture->create($context, $this->givenTraitUse(), new ProjectFactoryStrategies([]));
+ }
+
+ /** @param Class_Element|Trait_Element $traitConsumer */
+ #[DataProvider('consumerProvider')]
+ public function testCreateWillAddUsedTraitToContextTop(Element $traitConsumer): void
+ {
+ $context = self::createContext()->push($traitConsumer);
+ $this->fixture->create($context, $this->givenTraitUse(), new ProjectFactoryStrategies([]));
+
+ self::assertEquals(['\Foo' => new Fqsen('\Foo')], $traitConsumer->getUsedTraits());
+ }
+
+ private function givenTraitUse(): TraitUseNode
+ {
+ return new TraitUseNode([new FullyQualified('Foo')]);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/Trait_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/Trait_Test.php
new file mode 100644
index 00000000..89cc74f8
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/Trait_Test.php
@@ -0,0 +1,137 @@
+docBlockFactory = $this->prophesize(DocBlockFactoryInterface::class);
+ $this->fixture = new Trait_($this->docBlockFactory->reveal());
+ }
+
+ public function testMatches(): void
+ {
+ $this->assertFalse($this->fixture->matches(self::createContext(null), new stdClass()));
+ $this->assertTrue($this->fixture->matches(self::createContext(null), m::mock(TraitNode::class)));
+ }
+
+ public function testSimpleCreate(): void
+ {
+ $containerMock = m::mock(StrategyContainer::class);
+ $interfaceMock = $this->buildTraitMock();
+ $interfaceMock->shouldReceive('getDocComment')->andReturnNull();
+
+ $trait = $this->performCreate($interfaceMock, $containerMock);
+
+ $this->assertInstanceOf(TraitElement::class, $trait);
+ $this->assertEquals('\Space\MyTrait', (string) $trait->getFqsen());
+ }
+
+ public function testIteratesStatements(): void
+ {
+ $method1 = new ClassMethod('\Space\MyTrait::method1');
+ $method1Descriptor = new MethodElement(new Fqsen('\Space\MyTrait::method1'));
+ $strategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+ $containerMock = $this->prophesize(StrategyContainer::class);
+ $classMock = $this->buildTraitMock();
+ $classMock->shouldReceive('getDocComment')->andReturnNull();
+ $classMock->stmts = [$method1];
+
+ $strategyMock->create(Argument::type(ContextStack::class), $method1, $containerMock)
+ ->will(function ($args) use ($method1Descriptor): void {
+ $args[0]->peek()->addMethod($method1Descriptor);
+ })
+ ->shouldBeCalled();
+
+ $containerMock->findMatching(
+ Argument::type(ContextStack::class),
+ $method1,
+ )->willReturn($strategyMock->reveal());
+
+ $trait = $this->performCreate($classMock, $containerMock->reveal());
+
+ $this->assertEquals('\Space\MyTrait', (string) $trait->getFqsen());
+ $this->assertEquals(
+ ['\Space\MyTrait::method1' => $method1Descriptor],
+ $trait->getMethods(),
+ );
+ }
+
+ public function testCreateWithDocBlock(): void
+ {
+ $doc = new Doc('Text');
+ $traitMock = $this->buildTraitMock();
+ $traitMock->shouldReceive('getDocComment')->andReturn($doc);
+ $docBlock = new DocBlockElement('');
+ $this->docBlockFactory->create('Text', null)->willReturn($docBlock);
+ $containerMock = m::mock(StrategyContainer::class);
+
+ $trait = $this->performCreate($traitMock, $containerMock);
+
+ $this->assertSame($docBlock, $trait->getDocBlock());
+ }
+
+ private function buildTraitMock(): m\MockInterface|TraitNode
+ {
+ $mock = m::mock(TraitNode::class);
+ $mock->shouldReceive('getAttribute')->andReturn(new Fqsen('\Space\MyTrait'));
+ $mock->stmts = [];
+ $mock->shouldReceive('getLine')->andReturn(1);
+ $mock->shouldReceive('getEndLine')->andReturn(2);
+
+ return $mock;
+ }
+
+ private function performCreate(TraitNode $traitNode, StrategyContainer $containerMock): TraitElement
+ {
+ $file = new File('hash', 'path');
+ $this->fixture->create(self::createContext(null)->push($file), $traitNode, $containerMock);
+
+ return current($file->getTraits());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/TypeTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/TypeTest.php
new file mode 100644
index 00000000..be914b6d
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/TypeTest.php
@@ -0,0 +1,96 @@
+fromPhpParser(null);
+
+ $this->assertNull($result);
+ }
+
+ public function testReturnsReflectedType(): void
+ {
+ $factory = new Type();
+ $given = new Name('integer');
+ $expected = new Integer();
+
+ $result = $factory->fromPhpParser($given);
+
+ $this->assertEquals($expected, $result);
+ }
+
+ public function testReturnsNullableTypeWhenPassedAPhpParserNullable(): void
+ {
+ $factory = new Type();
+ $given = new NullableType(new Identifier('integer'));
+ $expected = new Nullable(new Integer());
+
+ $result = $factory->fromPhpParser($given);
+
+ $this->assertEquals($expected, $result);
+ }
+
+ public function testReturnsUnion(): void
+ {
+ $factory = new Type();
+ $given = new UnionType([new Identifier('integer'), new Identifier('string')]);
+ $expected = new Compound([new Integer(), new String_()]);
+
+ $result = $factory->fromPhpParser($given);
+
+ $this->assertEquals($expected, $result);
+ }
+
+ public function testReturnsUnionGivenVariousTypes(): void
+ {
+ $factory = new Type();
+ $given = new UnionType(['integer', new Name('string'), new Identifier('float')]);
+ $expected = new Compound([new Integer(), new String_(), new Float_()]);
+
+ $result = $factory->fromPhpParser($given);
+
+ $this->assertEquals($expected, $result);
+ }
+
+ public function testReturnsInterseptionType(): void
+ {
+ $factory = new Type();
+ $given = new IntersectionType(['integer', new Name('string')]);
+ $expected = new Intersection([new Integer(), new String_()]);
+
+ $result = $factory->fromPhpParser($given);
+
+ self::assertEquals($expected, $result);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/FileTest.php b/tests/unit/phpDocumentor/Reflection/Php/FileTest.php
new file mode 100644
index 00000000..9cdf0d3a
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/FileTest.php
@@ -0,0 +1,165 @@
+docBlock = new DocBlock('');
+
+ $this->fixture = new File(self::EXAMPLE_HASH, self::EXAMPLE_PATH, self::EXAMPLE_SOURCE, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testAddAndGetClasses(): void
+ {
+ $this->assertEmpty($this->fixture->getClasses());
+
+ $class = new Class_(new Fqsen('\MySpace\MyClass'));
+ $this->fixture->addClass($class);
+
+ $this->assertEquals(['\MySpace\MyClass' => $class], $this->fixture->getClasses());
+ }
+
+ public function testAddAndGetConstants(): void
+ {
+ $this->assertEmpty($this->fixture->getConstants());
+
+ $constant = new Constant(new Fqsen('\MySpace::MY_CONSTANT'));
+ $this->fixture->addConstant($constant);
+
+ $this->assertEquals(['\MySpace::MY_CONSTANT' => $constant], $this->fixture->getConstants());
+ }
+
+ public function testAddAndGetFunctions(): void
+ {
+ $this->assertEmpty($this->fixture->getFunctions());
+
+ $function = new Function_(new Fqsen('\MySpace::MyFunction()'));
+ $this->fixture->addFunction($function);
+
+ $this->assertEquals(['\MySpace::MyFunction()' => $function], $this->fixture->getFunctions());
+ }
+
+ public function testAddAndGetInterfaces(): void
+ {
+ $this->assertEmpty($this->fixture->getInterfaces());
+
+ $interface = new Interface_(new Fqsen('\MySpace\MyInterface'), []);
+ $this->fixture->addInterface($interface);
+
+ $this->assertEquals(['\MySpace\MyInterface' => $interface], $this->fixture->getInterfaces());
+ }
+
+ public function testAddAndGetTraits(): void
+ {
+ $this->assertEmpty($this->fixture->getTraits());
+
+ $trait = new Trait_(new Fqsen('\MySpace\MyTrait'));
+ $this->fixture->addTrait($trait);
+
+ $this->assertEquals(['\MySpace\MyTrait' => $trait], $this->fixture->getTraits());
+ }
+
+ public function testAddAndGetEnums(): void
+ {
+ $this->assertEmpty($this->fixture->getEnums());
+
+ $enum = new Enum_(new Fqsen('\MySpace\MyEnum'), null);
+ $this->fixture->addEnum($enum);
+
+ $this->assertEquals(['\MySpace\MyEnum' => $enum], $this->fixture->getEnums());
+ }
+
+ public function testGetDocBlock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testGetHash(): void
+ {
+ $this->assertSame(self::EXAMPLE_HASH, $this->fixture->getHash());
+ }
+
+ public function testGetName(): void
+ {
+ $this->assertSame(self::EXAMPLE_NAME, $this->fixture->getName());
+ }
+
+ public function testSetAndGetPath(): void
+ {
+ $this->assertSame(self::EXAMPLE_PATH, $this->fixture->getPath());
+ }
+
+ public function testSetAndGetSource(): void
+ {
+ $this->assertSame(self::EXAMPLE_SOURCE, $this->fixture->getSource());
+ }
+
+ public function testSetAndGetNamespaceAliases(): void
+ {
+ $this->assertEmpty($this->fixture->getNamespaces());
+
+ $this->fixture->addNamespace(new Fqsen('\MyNamepace\Foo'));
+
+ $this->assertEquals(['\MyNamepace\Foo' => new Fqsen('\MyNamepace\Foo')], $this->fixture->getNamespaces());
+ }
+
+ public function testAddAndGetIncludes(): void
+ {
+ $this->assertEmpty($this->fixture->getIncludes());
+
+ $include = self::EXAMPLE_PATH;
+ $this->fixture->addInclude($include);
+
+ $this->assertSame([self::EXAMPLE_PATH => self::EXAMPLE_PATH], $this->fixture->getIncludes());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Function_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Function_Test.php
new file mode 100644
index 00000000..fbbdf7d0
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Function_Test.php
@@ -0,0 +1,107 @@
+fqsen = new Fqsen('\space\MyFunction()');
+ $this->docBlock = new DocBlock('aa');
+ $this->fixture = new Function_($this->fqsen, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGetName(): void
+ {
+ $this->assertEquals('MyFunction', $this->fixture->getName());
+ }
+
+ public function testAddAndGetArguments(): void
+ {
+ $argument = new Argument('firstArgument');
+ $this->fixture->addArgument($argument);
+
+ $this->assertEquals([$argument], $this->fixture->getArguments());
+ }
+
+ public function testGetFqsen(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ }
+
+ public function testGetDocblock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testGetDefaultReturnType(): void
+ {
+ $function = new Function_($this->fqsen);
+ $this->assertEquals(new Mixed_(), $function->getReturnType());
+ }
+
+ public function testGetReturnTypeFromConstructor(): void
+ {
+ $returnType = new String_();
+ $function = new Function_($this->fqsen, null, null, null, $returnType);
+
+ $this->assertSame($returnType, $function->getReturnType());
+ }
+
+ public function testGetHasReturnByReference(): void
+ {
+ $function = new Function_($this->fqsen);
+ $this->assertSame(false, $function->getHasReturnByReference());
+ }
+
+ public function testGetHasReturnByReferenceFromConstructor(): void
+ {
+ $function = new Function_($this->fqsen, null, null, null, null, true);
+ $this->assertSame(true, $function->getHasReturnByReference());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Function_($this->fqsen, $this->docBlock, new Location(100, 20), new Location(101, 20));
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php
new file mode 100644
index 00000000..2d63f04f
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Interface_Test.php
@@ -0,0 +1,116 @@
+exampleParents = [
+ new Fqsen('\MySpace\MyParent'),
+ new Fqsen('\MySpace\MyOtherParent'),
+ ];
+
+ $this->fqsen = new Fqsen('\MySpace\MyInterface');
+ $this->docBlock = new DocBlock('');
+ $this->fixture = new Interface_($this->fqsen, $this->exampleParents, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGetName(): void
+ {
+ $this->assertSame($this->fqsen->getName(), $this->fixture->getName());
+ }
+
+ public function testGetFqsen(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ }
+
+ public function testGetDocblock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testSettingAndGettingConstants(): void
+ {
+ $this->assertEquals([], $this->fixture->getConstants());
+
+ $constant = new Constant(new Fqsen('\MySpace\MyInterface::MY_CONSTANT'));
+
+ $this->fixture->addConstant($constant);
+
+ $this->assertEquals(['\MySpace\MyInterface::MY_CONSTANT' => $constant], $this->fixture->getConstants());
+ }
+
+ public function testSettingAndGettingMethods(): void
+ {
+ $this->assertEquals([], $this->fixture->getMethods());
+
+ $method = new Method(new Fqsen('\MySpace\MyInterface::myMethod()'));
+
+ $this->fixture->addMethod($method);
+
+ $this->assertEquals(['\MySpace\MyInterface::myMethod()' => $method], $this->fixture->getMethods());
+ }
+
+ public function testReturningTheParentsOfThisInterface(): void
+ {
+ $this->assertSame($this->exampleParents, $this->fixture->getParents());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Interface_($this->fqsen, [], $this->docBlock, new Location(100, 20), new Location(101, 20));
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+
+ public function testArrayWithParentsMustBeFqsenObjects(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+
+ new Interface_(new Fqsen('\MyInterface'), ['InvalidInterface']);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/MetadataContainerTestHelper.php b/tests/unit/phpDocumentor/Reflection/Php/MetadataContainerTestHelper.php
new file mode 100644
index 00000000..fc444117
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/MetadataContainerTestHelper.php
@@ -0,0 +1,32 @@
+getFixture()->addMetadata($stub);
+
+ self::assertSame(['stub' => $stub], $this->getFixture()->getMetadata());
+ }
+
+ public function testSetMetaDataWithExistingKeyThrows(): void
+ {
+ self::expectException(Exception::class);
+
+ $stub = new MetadataStub('stub');
+
+ $this->getFixture()->addMetadata($stub);
+ $this->getFixture()->addMetadata($stub);
+ }
+
+ abstract public function getFixture(): MetaDataContainerInterface;
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/MetadataStub.php b/tests/unit/phpDocumentor/Reflection/Php/MetadataStub.php
new file mode 100644
index 00000000..86618437
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/MetadataStub.php
@@ -0,0 +1,19 @@
+key;
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/MethodTest.php b/tests/unit/phpDocumentor/Reflection/Php/MethodTest.php
new file mode 100644
index 00000000..c72b5fc3
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/MethodTest.php
@@ -0,0 +1,167 @@
+fqsen = new Fqsen('\My\Space::MyMethod()');
+ $this->visibility = new Visibility('private');
+ $this->docblock = new DocBlock('');
+ $this->fixture = new Method($this->fqsen);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGetFqsenAndGetName(): void
+ {
+ $method = new Method($this->fqsen);
+
+ $this->assertSame($this->fqsen, $method->getFqsen());
+ $this->assertEquals($this->fqsen->getName(), $method->getName());
+ }
+
+ public function testGetDocBlock(): void
+ {
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock);
+
+ $this->assertSame($this->docblock, $method->getDocBlock());
+ }
+
+ public function testAddingAndGettingArguments(): void
+ {
+ $method = new Method($this->fqsen);
+ $this->assertEquals([], $method->getArguments());
+
+ $argument = new Argument('myArgument');
+ $method->addArgument($argument);
+
+ $this->assertEquals([$argument], $method->getArguments());
+ }
+
+ public function testGettingWhetherMethodIsAbstract(): void
+ {
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock, false);
+ $this->assertFalse($method->isAbstract());
+
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock, true);
+ $this->assertTrue($method->isAbstract());
+ }
+
+ public function testGettingWhetherMethodIsFinal(): void
+ {
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock, false, false, false);
+ $this->assertFalse($method->isFinal());
+
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock, false, false, true);
+ $this->assertTrue($method->isFinal());
+ }
+
+ public function testGettingWhetherMethodIsStatic(): void
+ {
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock, false, false, false);
+ $this->assertFalse($method->isStatic());
+
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock, false, true, false);
+ $this->assertTrue($method->isStatic());
+ }
+
+ public function testGettingVisibility(): void
+ {
+ $method = new Method($this->fqsen, $this->visibility, $this->docblock, false, false, false);
+ $this->assertSame($this->visibility, $method->getVisibility());
+ }
+
+ public function testGetDefaultVisibility(): void
+ {
+ $method = new Method($this->fqsen);
+ $this->assertEquals(new Visibility('public'), $method->getVisibility());
+ }
+
+ public function testGetDefaultReturnType(): void
+ {
+ $method = new Method($this->fqsen);
+ $this->assertEquals(new Mixed_(), $method->getReturnType());
+ }
+
+ public function testGetReturnTypeFromConstructor(): void
+ {
+ $returnType = new String_();
+ $method = new Method(
+ $this->fqsen,
+ new Visibility('public'),
+ null,
+ false,
+ false,
+ false,
+ null,
+ null,
+ $returnType,
+ );
+
+ $this->assertSame($returnType, $method->getReturnType());
+ }
+
+ public function testGetHasReturnByReference(): void
+ {
+ $method = new Method($this->fqsen);
+ $this->assertSame(false, $method->getHasReturnByReference());
+ }
+
+ public function testGetHasReturnByReferenceFromConstructor(): void
+ {
+ $method = new Method($this->fqsen, null, null, false, false, false, null, null, null, true);
+ $this->assertSame(true, $method->getHasReturnByReference());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Method(
+ $this->fqsen,
+ null,
+ null,
+ false,
+ false,
+ false,
+ new Location(100, 20),
+ new Location(101, 20),
+ );
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Namespace_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Namespace_Test.php
new file mode 100644
index 00000000..3ed7d211
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Namespace_Test.php
@@ -0,0 +1,109 @@
+fqsen = new Fqsen('\MySpace');
+ $this->docBlock = new DocBlock('');
+
+ $this->fixture = new Namespace_($this->fqsen, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testAddAndGetClasses(): void
+ {
+ $this->assertEmpty($this->fixture->getClasses());
+
+ $class = new Fqsen('\MySpace\MyClass');
+ $this->fixture->addClass($class);
+
+ $this->assertEquals(['\MySpace\MyClass' => $class], $this->fixture->getClasses());
+ }
+
+ public function testAddAndGetConstants(): void
+ {
+ $this->assertEmpty($this->fixture->getConstants());
+
+ $constant = new Fqsen('\MySpace::MY_CONSTANT');
+ $this->fixture->addConstant($constant);
+
+ $this->assertEquals(['\MySpace::MY_CONSTANT' => $constant], $this->fixture->getConstants());
+ }
+
+ public function testAddAndGetFunctions(): void
+ {
+ $this->assertEmpty($this->fixture->getFunctions());
+
+ $function = new Fqsen('\MySpace\MyFunction()');
+ $this->fixture->addFunction($function);
+
+ $this->assertEquals(['\MySpace\MyFunction()' => $function], $this->fixture->getFunctions());
+ }
+
+ public function testAddAndGetInterfaces(): void
+ {
+ $this->assertEmpty($this->fixture->getInterfaces());
+
+ $interface = new Fqsen('\MySpace\MyInterface');
+ $this->fixture->addInterface($interface);
+
+ $this->assertEquals(['\MySpace\MyInterface' => $interface], $this->fixture->getInterfaces());
+ }
+
+ public function testAddAndGetTraits(): void
+ {
+ $this->assertEmpty($this->fixture->getTraits());
+
+ $trait = new Fqsen('\MySpace\MyTrait');
+ $this->fixture->addTrait($trait);
+
+ $this->assertEquals(['\MySpace\MyTrait' => $trait], $this->fixture->getTraits());
+ }
+
+ public function testGetFqsen(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ $this->assertEquals($this->fqsen->getName(), $this->fixture->getName());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/NodesFactoryTest.php b/tests/unit/phpDocumentor/Reflection/Php/NodesFactoryTest.php
new file mode 100644
index 00000000..1854ff0d
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/NodesFactoryTest.php
@@ -0,0 +1,69 @@
+assertInstanceOf(NodesFactory::class, $factory);
+ $this->assertEquals($this->givenTheExpectedDefaultNodesFactory(), $factory);
+ }
+
+ public function testThatCodeGetsConvertedIntoNodes(): void
+ {
+ $parser = $this->prophesize(Parser::class);
+ $parser->parse('this is my code')->willReturn(['parsed code']);
+
+ $nodeTraverser = $this->prophesize(NodeTraverserInterface::class);
+ $nodeTraverser->traverse(['parsed code'])->willReturn(['traversed code']);
+
+ $factory = new NodesFactory($parser->reveal(), $nodeTraverser->reveal());
+
+ $result = $factory->create('this is my code');
+
+ $this->assertSame(['traversed code'], $result);
+ }
+
+ private function givenTheExpectedDefaultNodesFactory(): NodesFactory
+ {
+ $parser = (new ParserFactory())->createForNewestSupportedVersion();
+ $traverser = new NodeTraverser();
+ $traverser->addVisitor(new NameResolver());
+ $traverser->addVisitor(new ElementNameResolver());
+
+ return new NodesFactory($parser, $traverser);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/ProjectFactoryStrategiesTest.php b/tests/unit/phpDocumentor/Reflection/Php/ProjectFactoryStrategiesTest.php
new file mode 100644
index 00000000..e8a332d0
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/ProjectFactoryStrategiesTest.php
@@ -0,0 +1,57 @@
+assertTrue(true);
+ }
+
+ public function testFindMatching(): void
+ {
+ $strategy = new DummyFactoryStrategy();
+ $container = new ProjectFactoryStrategies([$strategy]);
+ $actual = $container->findMatching(
+ new ContextStack(new Project('name'), new Context('global')),
+ new stdClass(),
+ );
+
+ $this->assertSame($strategy, $actual);
+ }
+
+ public function testCreateThrowsExceptionWhenStrategyNotFound(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+ $container = new ProjectFactoryStrategies([]);
+ $container->findMatching(
+ new ContextStack(new Project('name'), new Context('global')),
+ new stdClass(),
+ );
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/ProjectFactoryTest.php b/tests/unit/phpDocumentor/Reflection/Php/ProjectFactoryTest.php
new file mode 100644
index 00000000..67da45f4
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/ProjectFactoryTest.php
@@ -0,0 +1,269 @@
+assertInstanceOf(ProjectFactory::class, ProjectFactory::createInstance());
+ }
+
+ public function testCreate(): void
+ {
+ $expected = ['some/file.php', 'some/other.php'];
+ $calls = 0;
+ $someOtherStrategy = $this->prophesize(ProjectFactoryStrategy::class);
+ $someOtherStrategy->matches(
+ ProphesizeArgument::type(ContextStack::class),
+ ProphesizeArgument::any(),
+ )->willReturn(false);
+
+ $someOtherStrategy->create(
+ ProphesizeArgument::any(),
+ ProphesizeArgument::any(),
+ ProphesizeArgument::any(),
+ )->shouldNotBeCalled();
+
+ $fileStrategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+ $fileStrategyMock->matches(
+ ProphesizeArgument::type(ContextStack::class),
+ ProphesizeArgument::any(),
+ )->willReturn(true);
+
+ $fileStrategyMock->create(
+ ProphesizeArgument::type(ContextStack::class),
+ ProphesizeArgument::type(LocalFile::class),
+ ProphesizeArgument::any(),
+ )->will(function ($args) use (&$calls, $expected): void {
+ $context = $args[0];
+ assert($context instanceof ContextStack);
+
+ $file = $args[1];
+ assert($file instanceof LocalFile);
+ $context->getProject()->addFile(new File($file->md5(), $expected[$calls++]));
+ });
+
+ $projectFactory = new ProjectFactory([$someOtherStrategy->reveal(), $fileStrategyMock->reveal()]);
+
+ $files = [new LocalFile(__FILE__), new LocalFile(__FILE__)];
+ $project = $projectFactory->create('MyProject', $files);
+
+ $this->assertInstanceOf(Project::class, $project);
+
+ $projectFilePaths = array_keys($project->getFiles());
+ $this->assertEquals(['some/file.php', 'some/other.php'], $projectFilePaths);
+ }
+
+ public function testCreateThrowsExceptionWhenStrategyNotFound(): void
+ {
+ $this->expectException(OutOfBoundsException::class);
+ $projectFactory = new ProjectFactory([]);
+ $projectFactory->create('MyProject', ['aa']);
+ }
+
+ public function testCreateProjectFromFileWithNamespacedClass(): void
+ {
+ $file = new File(md5('some/file.php'), 'some/file.php');
+ $file->addNamespace(new Fqsen('\mySpace'));
+ $file->addClass(new Class_(new Fqsen('\mySpace\MyClass')));
+
+ $namespaces = $this->fetchNamespacesFromSingleFile($file);
+
+ $this->assertEquals('\mySpace', key($namespaces));
+
+ $mySpace = current($namespaces);
+
+ $this->assertInstanceOf(Namespace_::class, $mySpace);
+ $this->assertEquals('\mySpace\MyClass', key($mySpace->getClasses()));
+ }
+
+ public function testWithNamespacedInterface(): void
+ {
+ $file = new File(md5('some/file.php'), 'some/file.php');
+ $file->addNamespace(new Fqsen('\mySpace'));
+ $file->addInterface(new Interface_(new Fqsen('\mySpace\MyInterface')));
+
+ $namespaces = $this->fetchNamespacesFromSingleFile($file);
+
+ $mySpace = current($namespaces);
+
+ $this->assertInstanceOf(Namespace_::class, $mySpace);
+ $this->assertEquals('\mySpace\MyInterface', key($mySpace->getInterfaces()));
+ }
+
+ public function testWithNamespacedFunction(): void
+ {
+ $file = new File(md5('some/file.php'), 'some/file.php');
+ $file->addNamespace(new Fqsen('\mySpace'));
+ $file->addFunction(new Function_(new Fqsen('\mySpace\function()')));
+
+ $namespaces = $this->fetchNamespacesFromSingleFile($file);
+
+ $mySpace = current($namespaces);
+
+ $this->assertInstanceOf(Namespace_::class, $mySpace);
+ $this->assertEquals('\mySpace\function()', key($mySpace->getFunctions()));
+ }
+
+ public function testWithNamespacedConstant(): void
+ {
+ $file = new File(md5('some/file.php'), 'some/file.php');
+ $file->addNamespace(new Fqsen('\mySpace'));
+ $file->addConstant(new Constant(new Fqsen('\mySpace::MY_CONST')));
+
+ $namespaces = $this->fetchNamespacesFromSingleFile($file);
+
+ $mySpace = current($namespaces);
+
+ $this->assertInstanceOf(Namespace_::class, $mySpace);
+ $this->assertEquals('\mySpace::MY_CONST', key($mySpace->getConstants()));
+ }
+
+ public function testWithNamespacedTrait(): void
+ {
+ $file = new File(md5('some/file.php'), 'some/file.php');
+ $file->addNamespace(new Fqsen('\mySpace'));
+ $file->addTrait(new Trait_(new Fqsen('\mySpace\MyTrait')));
+
+ $namespaces = $this->fetchNamespacesFromSingleFile($file);
+
+ $mySpace = current($namespaces);
+
+ $this->assertInstanceOf(Namespace_::class, $mySpace);
+ $this->assertEquals('\mySpace\MyTrait', key($mySpace->getTraits()));
+ }
+
+ public function testNamespaceSpreadOverMultipleFiles(): void
+ {
+ $someFile = new File(md5('some/file.php'), 'some/file.php');
+ $someFile->addNamespace(new Fqsen('\mySpace'));
+ $someFile->addClass(new Class_(new Fqsen('\mySpace\MyClass')));
+
+ $otherFile = new File(md5('some/other.php'), 'some/other.php');
+ $otherFile->addNamespace(new Fqsen('\mySpace'));
+ $otherFile->addClass(new Class_(new Fqsen('\mySpace\OtherClass')));
+
+ $namespaces = $this->fetchNamespacesFromMultipleFiles([$otherFile, $someFile]);
+
+ $this->assertCount(1, $namespaces);
+ $this->assertCount(2, current($namespaces)->getClasses());
+ }
+
+ public function testSingleFileMultipleNamespaces(): void
+ {
+ $someFile = new File(md5('some/file.php'), 'some/file.php');
+ $someFile->addNamespace(new Fqsen('\mySpace'));
+ $someFile->addClass(new Class_(new Fqsen('\mySpace\MyClass')));
+ $someFile->addNamespace(new Fqsen('\mySpace\SubSpace'));
+ $someFile->addClass(new Class_(new Fqsen('\mySpace\SubSpace\MyClass')));
+
+ $namespaces = $this->fetchNamespacesFromSingleFile($someFile);
+
+ $this->assertCount(2, $namespaces);
+ $this->assertArrayHasKey('\mySpace', $namespaces);
+ $this->assertArrayHasKey('\mySpace\SubSpace', $namespaces);
+
+ $this->assertCount(1, $namespaces['\mySpace']->getClasses());
+ }
+
+ /**
+ * Uses the ProjectFactory to create a Project and returns the namespaces created by the factory.
+ *
+ * @return Namespace_[] Namespaces of the project
+ *
+ * @throws Exception
+ */
+ private function fetchNamespacesFromSingleFile(File $file): array
+ {
+ return $this->fetchNamespacesFromMultipleFiles([$file]);
+ }
+
+ /**
+ * Uses the ProjectFactory to create a Project and returns the namespaces created by the factory.
+ *
+ * @param File[] $files
+ *
+ * @return Namespace_[] Namespaces of the project
+ *
+ * @throws Exception
+ */
+ private function fetchNamespacesFromMultipleFiles(array $files): array
+ {
+ $fileStrategyMock = $this->prophesize(ProjectFactoryStrategy::class);
+ $fileStrategyMock->matches(
+ ProphesizeArgument::type(ContextStack::class),
+ ProphesizeArgument::any(),
+ )->willReturn(true);
+
+ $fileStrategyMock->create(
+ ProphesizeArgument::type(ContextStack::class),
+ ProphesizeArgument::type(File::class),
+ ProphesizeArgument::any(),
+ )->will(function ($args): void {
+ $context = $args[0];
+ assert($context instanceof ContextStack);
+
+ $file = $args[1];
+ assert($file instanceof File);
+ $context->getProject()->addFile($file);
+ });
+
+ $projectFactory = new ProjectFactory([$fileStrategyMock->reveal()]);
+ $project = $projectFactory->create('My Project', $files);
+
+ return $project->getNamespaces();
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/ProjectTest.php b/tests/unit/phpDocumentor/Reflection/Php/ProjectTest.php
new file mode 100644
index 00000000..724a996c
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/ProjectTest.php
@@ -0,0 +1,75 @@
+fixture = new Project(self::EXAMPLE_NAME);
+ }
+
+ public function testGetSetName(): void
+ {
+ $this->assertEquals(self::EXAMPLE_NAME, $this->fixture->getName());
+ }
+
+ public function testGetAddFiles(): void
+ {
+ $this->assertEmpty($this->fixture->getFiles());
+
+ $include = new File('foo-bar', 'foo/bar');
+ $this->fixture->addFile($include);
+
+ $this->assertSame(['foo/bar' => $include], $this->fixture->getFiles());
+ }
+
+ public function testGetRootNamespace(): void
+ {
+ $this->assertInstanceOf(Namespace_::class, $this->fixture->getRootNamespace());
+
+ $namespaceDescriptor = new Namespace_(new Fqsen('\MySpace'));
+ $project = new Project(self::EXAMPLE_NAME, $namespaceDescriptor);
+
+ $this->assertSame($namespaceDescriptor, $project->getRootNamespace());
+ }
+
+ public function testGetAddNamespace(): void
+ {
+ $this->assertEmpty($this->fixture->getNamespaces());
+
+ $namespace = new Namespace_(new Fqsen('\MySpace'));
+ $this->fixture->addNamespace($namespace);
+
+ $this->assertSame(['\MySpace' => $namespace], $this->fixture->getNamespaces());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/PropertyTest.php b/tests/unit/phpDocumentor/Reflection/Php/PropertyTest.php
new file mode 100644
index 00000000..44a7d5ef
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/PropertyTest.php
@@ -0,0 +1,148 @@
+fqsen = new Fqsen('\My\Class::$property');
+ $this->visibility = new Visibility('private');
+ $this->docBlock = new DocBlock('');
+ $this->fixture = new Property($this->fqsen);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGetFqsenAndGetName(): void
+ {
+ $property = new Property($this->fqsen);
+
+ $this->assertSame($this->fqsen, $property->getFqsen());
+ $this->assertEquals($this->fqsen->getName(), $property->getName());
+ }
+
+ public function testGettingWhetherPropertyIsStatic(): void
+ {
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, null, false);
+ $this->assertFalse($property->isStatic());
+
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, null, true);
+ $this->assertTrue($property->isStatic());
+ }
+
+ public function testGettingWhetherPropertyIsReadOnly(): void
+ {
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, null);
+ $this->assertFalse($property->isReadOnly());
+
+ $property = new Property(
+ $this->fqsen,
+ $this->visibility,
+ $this->docBlock,
+ null,
+ true,
+ null,
+ null,
+ null,
+ true,
+ );
+
+ $this->assertTrue($property->isReadOnly());
+ }
+
+ public function testGettingVisibility(): void
+ {
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, null, true);
+
+ $this->assertSame($this->visibility, $property->getVisibility());
+ }
+
+ public function testSetAndGetTypes(): void
+ {
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, null, true);
+ $this->assertEquals([], $property->getTypes());
+
+ $property->addType('a');
+ $this->assertEquals(['a'], $property->getTypes());
+ }
+
+ public function testGetDefault(): void
+ {
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, null, false);
+ $this->assertNull($property->getDefault());
+
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, 'a', true);
+ $this->assertEquals('a', $property->getDefault());
+ }
+
+ public function testGetDocBlock(): void
+ {
+ $property = new Property($this->fqsen, $this->visibility, $this->docBlock, null, false);
+ $this->assertSame($this->docBlock, $property->getDocBlock());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Property($this->fqsen, null, null, null, false, new Location(100, 20), new Location(101, 20));
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+
+ public function testGetType(): void
+ {
+ $type = new Integer();
+ $fixture = new Property(
+ $this->fqsen,
+ null,
+ null,
+ null,
+ false,
+ null,
+ null,
+ $type,
+ );
+
+ $this->assertSame($type, $fixture->getType());
+
+ $fixture = new Property($this->fqsen);
+ $this->assertNull($fixture->getType());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/TestCase.php b/tests/unit/phpDocumentor/Reflection/Php/TestCase.php
new file mode 100644
index 00000000..75712494
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/TestCase.php
@@ -0,0 +1,53 @@
+assertSame(-1, $this->fixture->getLocation()->getLineNumber());
+ $this->assertSame(0, $this->fixture->getLocation()->getColumnNumber());
+
+ $this->assertSame(-1, $this->fixture->getEndLocation()->getLineNumber());
+ $this->assertSame(0, $this->fixture->getEndLocation()->getColumnNumber());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ }
+
+ protected function assertLineAndColumnNumberIsReturnedWhenALocationIsProvided(Element|MetaDataContainerInterface $fixture): void
+ {
+ $this->assertSame(100, $fixture->getLocation()->getLineNumber());
+ $this->assertSame(20, $fixture->getLocation()->getColumnNumber());
+
+ $this->assertSame(101, $fixture->getEndLocation()->getLineNumber());
+ $this->assertSame(20, $fixture->getEndLocation()->getColumnNumber());
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/Trait_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Trait_Test.php
new file mode 100644
index 00000000..afddb001
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/Trait_Test.php
@@ -0,0 +1,111 @@
+fqsen = new Fqsen('\MyTrait');
+ $this->docBlock = new DocBlock('');
+ $this->fixture = new Trait_($this->fqsen, $this->docBlock);
+ }
+
+ private function getFixture(): MetaDataContainerInterface
+ {
+ return $this->fixture;
+ }
+
+ public function testGetFqsenAndGetName(): void
+ {
+ $this->assertSame($this->fqsen, $this->fixture->getFqsen());
+ $this->assertEquals($this->fqsen->getName(), $this->fixture->getName());
+ }
+
+ public function testAddAndGettingProperties(): void
+ {
+ $this->assertEquals([], $this->fixture->getProperties());
+
+ $property = new Property(new Fqsen('\MyTrait::$myProperty'));
+
+ $this->fixture->addProperty($property);
+
+ $this->assertEquals(['\MyTrait::$myProperty' => $property], $this->fixture->getProperties());
+ }
+
+ public function testAddAndGettingMethods(): void
+ {
+ $this->assertEquals([], $this->fixture->getMethods());
+
+ $method = new Method(new Fqsen('\MyTrait::myMethod()'));
+
+ $this->fixture->addMethod($method);
+
+ $this->assertEquals(['\MyTrait::myMethod()' => $method], $this->fixture->getMethods());
+ }
+
+ public function testAddAndGettingUsedTrait(): void
+ {
+ $this->assertEmpty($this->fixture->getUsedTraits());
+
+ $trait = new Fqsen('\MyTrait');
+
+ $this->fixture->addUsedTrait($trait);
+
+ $this->assertSame(['\MyTrait' => $trait], $this->fixture->getUsedTraits());
+ }
+
+ public function testAddAndGettingConstants(): void
+ {
+ $this->assertEmpty($this->fixture->getConstants());
+
+ $constant = new Constant(new Fqsen('\MyClass::MY_CONSTANT'));
+
+ $this->fixture->addConstant($constant);
+
+ $this->assertSame(['\MyClass::MY_CONSTANT' => $constant], $this->fixture->getConstants());
+ }
+
+ public function testGetDocblock(): void
+ {
+ $this->assertSame($this->docBlock, $this->fixture->getDocBlock());
+ }
+
+ public function testLineAndColumnNumberIsReturnedWhenALocationIsProvided(): void
+ {
+ $fixture = new Trait_($this->fqsen, $this->docBlock, new Location(100, 20), new Location(101, 20));
+ $this->assertLineAndColumnNumberIsReturnedWhenALocationIsProvided($fixture);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/ValueEvaluator/ConstantEvaluatorTest.php b/tests/unit/phpDocumentor/Reflection/Php/ValueEvaluator/ConstantEvaluatorTest.php
new file mode 100644
index 00000000..7c5754d0
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/ValueEvaluator/ConstantEvaluatorTest.php
@@ -0,0 +1,42 @@
+expectException(ConstExprEvaluationException::class);
+
+ $evaluator = new ConstantEvaluator();
+ $evaluator->evaluate(new Namespace_(), new ContextStack(new Project('test')));
+ }
+
+ public function testEvaluateThrowsOnUnknownExpression(): void
+ {
+ $this->expectException(ConstExprEvaluationException::class);
+
+ $evaluator = new ConstantEvaluator();
+ $result = $evaluator->evaluate(new ShellExec([]), new ContextStack(new Project('test'), new Context('Test')));
+ }
+
+ public function testEvaluateReturnsNamespaceFromContext(): void
+ {
+ $evaluator = new ConstantEvaluator();
+ $result = $evaluator->evaluate(new Namespace_(), new ContextStack(new Project('test'), new Context('Test')));
+
+ self::assertSame('Test', $result);
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/Php/VisibilityTest.php b/tests/unit/phpDocumentor/Reflection/Php/VisibilityTest.php
new file mode 100644
index 00000000..17cf1e34
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Php/VisibilityTest.php
@@ -0,0 +1,51 @@
+assertEquals($expected, (string) $visibility);
+ }
+
+ /** @return string[][] */
+ public static function visibilityProvider(): array
+ {
+ return [
+ ['public', 'public'],
+ ['protected', 'protected'],
+ ['private', 'private'],
+ ['PrIvate', 'private'],
+ ];
+ }
+
+ public function testVisibilityChecksInput(): void
+ {
+ $this->expectException(InvalidArgumentException::class);
+ new Visibility('fooBar');
+ }
+}
diff --git a/tests/unit/phpDocumentor/Reflection/PrettyPrinterTest.php b/tests/unit/phpDocumentor/Reflection/PrettyPrinterTest.php
deleted file mode 100644
index 434be7ca..00000000
--- a/tests/unit/phpDocumentor/Reflection/PrettyPrinterTest.php
+++ /dev/null
@@ -1,45 +0,0 @@
-
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-namespace phpDocumentor\Reflection;
-
-use PhpParser\Node\Scalar\String_;
-use PHPUnit_Framework_TestCase;
-
-/**
- * Class for testing the PrettyPrinter.
- *
- * @author Vasil Rangelov
- * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
- * @license http://www.opensource.org/licenses/mit-license.php MIT
- * @link http://phpdoc.org
- */
-class PrettyPrinterTest extends PHPUnit_Framework_TestCase
-{
- /**
- * @covers \phpDocumentor\Reflection\PrettyPrinter::pScalar_String
- *
- * @return void
- */
- public function testScalarStringPrinting()
- {
- $object = new PrettyPrinter();
- $this->assertEquals(
- 'Another value',
- $object->pScalar_String(
- new String_(
- 'Value',
- array('originalValue' => 'Another value')
- )
- )
- );
- }
-}
diff --git a/tests/unit/phpDocumentor/Reflection/Types/NamespaceNodeToContextTest.php b/tests/unit/phpDocumentor/Reflection/Types/NamespaceNodeToContextTest.php
new file mode 100644
index 00000000..dd058a31
--- /dev/null
+++ b/tests/unit/phpDocumentor/Reflection/Types/NamespaceNodeToContextTest.php
@@ -0,0 +1,93 @@
+assertEquals($expectedContext, (new NamespaceNodeToContext())->__invoke($namespace));
+ }
+
+ /** @return (Namespace|Context|null)[][] */
+ public static function expectedContextsProvider(): array
+ {
+ $namespaceWithImports = new Namespace_(
+ new Name('With\\Imports'),
+ [
+ (new Use_('ClassName', UseStatement::TYPE_NORMAL))->getNode(),
+ (new Use_('ConstantName', UseStatement::TYPE_CONSTANT))->getNode(),
+ (new Use_('FunctionName', UseStatement::TYPE_FUNCTION))->getNode(),
+ (new Use_('UnknownName', UseStatement::TYPE_UNKNOWN))->getNode(),
+ (new Use_('AAA\\BBB', UseStatement::TYPE_NORMAL))->getNode(),
+ (new Use_('BBB\\CCC\\DDD', UseStatement::TYPE_NORMAL))->getNode(),
+ (new Use_('Foo\\EEE\\FFF', UseStatement::TYPE_NORMAL))->getNode(),
+ (new Use_('Foo', UseStatement::TYPE_NORMAL))->getNode(),
+ (new Use_('GGG', UseStatement::TYPE_NORMAL))->as('HHH')->getNode(),
+ (new Use_('III', UseStatement::TYPE_NORMAL))->as('JJJ')->getNode(),
+ new GroupUse(
+ new Name('LLL'),
+ [
+ new UseUse(new Name('MMM')),
+ new UseUse(new Name('NNN'), 'OOO'),
+ ],
+ ),
+ (new Use_('\\PPP', UseStatement::TYPE_NORMAL))->getNode(),
+ new Class_('ClassNode'), // class node, should be ignored
+ ],
+ );
+
+ return [
+ 'No namespace' => [
+ null,
+ new Context(''),
+ ],
+ 'Empty namespace' => [
+ new Namespace_(),
+ new Context(''),
+ ],
+ 'Actual namespace' => [
+ new Namespace_(new Name('Foo\\Bar')),
+ new Context('Foo\\Bar'),
+ ],
+ 'Actual namespace prefixed with \\' => [
+ new Namespace_(new Name('\\Foo\\Bar')),
+ new Context('Foo\\Bar'),
+ ],
+ 'Complex use statement' => [
+ $namespaceWithImports,
+ new Context(
+ 'With\\Imports',
+ [
+ 'ClassName' => 'ClassName',
+ 'UnknownName' => 'UnknownName',
+ 'BBB' => 'AAA\\BBB',
+ 'DDD' => 'BBB\\CCC\\DDD',
+ 'FFF' => 'Foo\\EEE\\FFF',
+ 'Foo' => 'Foo',
+ 'HHH' => 'GGG',
+ 'JJJ' => 'III',
+ 'MMM' => 'LLL\\MMM',
+ 'OOO' => 'LLL\\NNN',
+ 'PPP' => 'PPP',
+ ],
+ ),
+ ],
+ ];
+ }
+}