composer update

This commit is contained in:
2020-12-06 10:28:55 +00:00
parent 69d92960d9
commit 09413522bb
1596 changed files with 60456 additions and 39587 deletions

View File

@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: composer
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 10

View File

@@ -1,14 +1,14 @@
on:
push:
branches:
- master
- 2.x
pull_request:
name: Qa workflow
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Restore/cache vendor folder
uses: actions/cache@v1
@@ -46,7 +46,7 @@ jobs:
name: Unit tests
needs: setup
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -90,13 +90,13 @@ jobs:
- ubuntu-latest
- windows-latest
- macOS-latest
php-versions: ['7.2', '7.3', '7.4']
php-versions: ['7.2', '7.3', '7.4', '8.0']
name: Unit tests for PHP version ${{ matrix.php-versions }} on ${{ matrix.operating-system }}
needs:
- setup
- phpunit-with-coverage
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Restore/cache tools folder
uses: actions/cache@v1
@@ -137,7 +137,7 @@ jobs:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
@@ -147,7 +147,7 @@ jobs:
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Code style check
uses: phpDocumentor/coding-standard@master
uses: phpDocumentor/coding-standard@latest
with:
args: -s
@@ -155,7 +155,7 @@ jobs:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
@@ -165,7 +165,7 @@ jobs:
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: PHPStan
uses: phpDocumentor/phpstan-ga@master
uses: phpDocumentor/phpstan-ga@latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@@ -175,7 +175,7 @@ jobs:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -208,7 +208,7 @@ jobs:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- uses: actions/checkout@v2
- name: fetch tags
run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Restore/cache vendor folder

View File

@@ -1,48 +0,0 @@
before_commands:
- "composer install --no-dev --prefer-source"
checks:
php:
excluded_dependencies:
- phpstan/phpstan
tools:
external_code_coverage:
enabled: true
timeout: 300
filter:
excluded_paths: ["tests", "vendor"]
php_code_sniffer:
enabled: true
config:
standard: PSR2
filter:
paths: ["src/*", "tests/*"]
excluded_paths: []
php_cpd:
enabled: true
excluded_dirs: ["tests", "vendor"]
php_cs_fixer:
enabled: true
config:
level: all
filter:
paths: ["src/*", "tests/*"]
php_loc:
enabled: true
excluded_dirs: ["tests", "vendor"]
php_mess_detector:
enabled: true
config:
ruleset: phpmd.xml.dist
design_rules: { eval_expression: false }
filter:
paths: ["src/*"]
php_pdepend:
enabled: true
excluded_dirs: ["tests", "vendor"]
php_analyzer:
enabled: true
filter:
paths: ["src/*", "tests/*"]
sensiolabs_security_checker: true

View File

@@ -1,49 +0,0 @@
language: php
php: [ 7.1, 7.2, 7.3, 7.4 ]
matrix:
fast_finish: true
install:
- travis_retry composer install --no-interaction --prefer-dist --optimize-autoloader
script:
- ./vendor/bin/phpunit --no-coverage
jobs:
include:
- stage: analysis
php: 7.1
script:
- ./vendor/bin/phpunit
after_script:
- travis_retry wget --no-verbose https://phar.io/releases/phive.phar
- travis_retry php phive.phar --no-progress install --trust-gpg-keys E82B2FB314E9906E php-coveralls/php-coveralls && ./tools/php-coveralls --verbose
- travis_retry wget --no-verbose https://scrutinizer-ci.com/ocular.phar && php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml
- stage: analysis
php: 7.1
before_script:
- travis_retry wget --no-verbose https://phar.io/releases/phive.phar
- travis_retry php phive.phar --no-progress install --trust-gpg-keys CF1A108D0E7AE720 phpstan
script:
- ./tools/phpstan analyse src --level max --configuration phpstan.neon
- stage: analysis
php: 7.1
script:
- composer create-project symplify/easy-coding-standard temp/ecs ^3 && temp/ecs/bin/ecs check src tests
cache:
directories:
- $HOME/.composer
- $HOME/.phive
notifications:
irc: "irc.freenode.org#phpdocumentor"
slack:
secure: "fjumM0h+4w3EYM4dpgqvpiCug7m4sSIC5+HATgwga/Nrc6IjlbWvGOv3JPgD3kQUhi18VmZfUYPmCv916SIbMnv8JWcrSaJXnPCgmxidvYkuzQDIw1HDJbVppGnkmwQA/qjIrM3sIEMfnu/arLRJQLI363aStZzGPxwIa4PDKcg="
email:
- me@mikevanriel.com
- ashnazg@php.net

View File

@@ -1,35 +0,0 @@
.PHONY: install-phive
install-phive:
mkdir tools; \
wget -O tools/phive.phar https://github.com/phar-io/phive/releases/download/0.13.2/phive-0.13.2.phar; \
wget -O tools/phive.phar.asc https://github.com/phar-io/phive/releases/download/0.13.2/phive-0.13.2.phar.asc; \
gpg --keyserver pool.sks-keyservers.net --recv-keys 0x9D8A98B29B2D5D79; \
gpg --verify tools/phive.phar.asc tools/phive.phar; \
chmod +x tools/phive.phar
.PHONY: setup
setup: install-phive
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phar-ga:latest php tools/phive.phar install --force-accept-unsigned
.PHONY: phpcs
phpcs:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest -s
.PHONY: phpcbf
phpcbf:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:latest phpcbf
.PHONY: phpstan
phpstan:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpstan-ga:latest analyse src --no-progress --configuration phpstan.neon
.PHONY: psalm
psalm:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.2 tools/psalm --show-info=true
.PHONY: test
test:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.2 tools/phpunit
.PHONY: pre-commit-test
pre-commit-test: test phpcs phpstan psalm

View File

@@ -1,6 +1,5 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Travis Status](https://img.shields.io/travis/phpDocumentor/ReflectionCommon.svg?label=Linux)](https://travis-ci.org/phpDocumentor/ReflectionCommon)
[![Appveyor Status](https://img.shields.io/appveyor/ci/phpDocumentor/ReflectionCommon.svg?label=Windows)](https://ci.appveyor.com/project/phpDocumentor/ReflectionCommon/branch/master)
![Qa workflow](https://github.com/phpDocumentor/ReflectionCommon/workflows/Qa%20workflow/badge.svg)
[![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/ReflectionCommon.svg)](https://coveralls.io/github/phpDocumentor/ReflectionCommon?branch=master)
[![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/ReflectionCommon.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionCommon/?branch=master)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/ReflectionCommon.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionCommon/?branch=master)

View File

@@ -1,52 +0,0 @@
build: false
clone_folder: c:\reflectioncommon
max_jobs: 3
platform: x86
pull_requests:
do_not_increment_build_number: true
version: '{build}.{branch}'
skip_tags: true
branches:
only:
- master
environment:
matrix:
- php_ver_target: 7.1
- php_ver_target: 7.2
matrix:
fast_finish: false
cache:
- c:\php -> appveyor.yml
- '%LOCALAPPDATA%\Composer\files'
init:
- SET PATH=C:\Program Files\OpenSSL;c:\tools\php;%PATH%
- SET COMPOSER_NO_INTERACTION=1
- SET PHP=1
- SET ANSICON=121x90 (121x90)
install:
- IF EXIST c:\tools\php (SET PHP=0)
- ps: appveyor-retry cinst --params '""/InstallDir:C:\tools\php""' --ignore-checksums -y php --version ((choco search php --exact --all-versions -r | select-string -pattern $env:php_ver_target | sort { [version]($_ -split '\|' | select -last 1) } -Descending | Select-Object -first 1) -replace '[php|]','')
- cd c:\tools\php
- IF %PHP%==1 copy /Y php.ini-development php.ini
- IF %PHP%==1 echo max_execution_time=1200 >> php.ini
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini
- IF %PHP%==1 echo extension_dir=ext >> php.ini
- IF %PHP%==1 echo extension=php_curl.dll >> php.ini
- IF %PHP%==1 echo extension=php_openssl.dll >> php.ini
- IF %PHP%==1 echo extension=php_mbstring.dll >> php.ini
- IF %PHP%==1 echo extension=php_fileinfo.dll >> php.ini
- IF %PHP%==1 echo zend.assertions=1 >> php.ini
- IF %PHP%==1 echo assert.exception=On >> php.ini
- IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
- appveyor-retry appveyor DownloadFile https://getcomposer.org/composer.phar
- cd c:\reflectioncommon
- composer install --no-interaction --prefer-dist --no-progress
test_script:
- cd c:\reflectioncommon
- vendor\bin\phpunit --no-coverage

View File

@@ -11,7 +11,7 @@
}
],
"require": {
"php": ">=7.1"
"php": "^7.2 || ^8.0"
},
"autoload" : {
"psr-4" : {
@@ -22,7 +22,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
"dev-2.x": "2.x-dev"
}
}
}

View File

@@ -1,17 +0,0 @@
includes:
- temp/ecs/config/clean-code.neon
- temp/ecs/config/psr2.neon
- temp/ecs/config/common.neon
parameters:
exclude_checkers:
# from temp/ecs/config/common.neon
- PhpCsFixer\Fixer\ClassNotation\OrderedClassElementsFixer
- PhpCsFixer\Fixer\PhpUnit\PhpUnitStrictFixer
- PhpCsFixer\Fixer\ControlStructure\YodaStyleFixer
# from temp/ecs/config/spaces.neon
- PhpCsFixer\Fixer\Operator\NotOperatorWithSuccessorSpaceFixer
skip:
PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff:
- */tests/**

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^8.4" installed="8.4.3" location="./tools/phpunit" copy="true"/>
<phar name="psalm" version="^3.7.2" installed="3.11.2" location="./tools/psalm" copy="true"/>
</phive>

View File

@@ -1,11 +0,0 @@
<?xml version="1.0"?>
<ruleset name="phpDocumentor">
<description>The coding standard for phpDocumentor.</description>
<file>src</file>
<file>tests/unit</file>
<arg value="p"/>
<rule ref="phpDocumentor">
</rule>
</ruleset>

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<ruleset
name="ProxyManager rules"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"
>
<rule ref="rulesets/codesize.xml"/>
<rule ref="rulesets/unusedcode.xml"/>
<rule ref="rulesets/design.xml">
<!-- eval is needed to generate runtime classes -->
<exclude name="EvalExpression"/>
</rule>
<rule ref="rulesets/naming.xml">
<exclude name="LongVariable"/>
</rule>
<rule ref="rulesets/naming.xml/LongVariable">
<properties>
<property name="minimum">40</property>
</properties>
</rule>
</ruleset>

View File

@@ -1,2 +0,0 @@
parameters:
level: max

View File

@@ -1,14 +0,0 @@
<?xml version="1.0"?>
<psalm
totallyTyped="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config file:///composer/vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>

View File

@@ -1,12 +0,0 @@
version: 1
update_configs:
- package_manager: "php:composer"
directory: "/"
update_schedule: "weekly"
automerged_updates:
- match:
dependency_type: "development"
update_type: "all"
- match:
dependency_type: "production"
update_type: "semver:patch"

View File

@@ -1,250 +0,0 @@
on:
push:
branches:
- master
pull_request:
name: Qa workflow
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Restore/cache tools folder
uses: actions/cache@v1
with:
path: tools
key: all-tools-${{ github.sha }}
restore-keys: |
all-tools-${{ github.sha }}-
all-tools-
- name: composer
uses: docker://composer
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: install --no-interaction --prefer-dist --optimize-autoloader
- name: composer-require-checker
uses: docker://phpga/composer-require-checker-ga
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: check --config-file ./composer-require-config.json composer.json
- name: Install phive
run: make install-phive
- name: Install PHAR dependencies
run: tools/phive.phar --no-progress install --copy --trust-gpg-keys 4AA394086372C20A,D2CCAC42F6295E7D,E82B2FB314E9906E,8E730BA25823D8B5 --force-accept-unsigned
phpunit-with-coverage:
runs-on: ubuntu-latest
name: Unit tests
needs: setup
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Restore/cache tools folder
uses: actions/cache@v1
with:
path: tools
key: all-tools-${{ github.sha }}
restore-keys: |
all-tools-${{ github.sha }}-
all-tools-
- name: PHPUnit
uses: docker://phpdoc/phpunit-ga:latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Quick check code coverage level
run: php tests/coverage-checker.php 89
phpunit:
name: Unit tests for PHP version ${{ matrix.php-versions }} on ${{ matrix.operating-system }}
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system:
- ubuntu-latest
- windows-latest
- macOS-latest
php-versions: ['7.2', '7.3', '7.4']
env:
extensions: mbstring
key: cache-v1 # can be any string, change to clear the extension cache.
needs:
- setup
- phpunit-with-coverage
steps:
- uses: actions/checkout@master
- name: Setup cache environment
id: cache-env
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.php-versions }}
extensions: ${{ env.extensions }}
key: ${{ env.key }}
- name: Cache extensions
uses: actions/cache@v1
with:
path: ${{ steps.cache-env.outputs.dir }}
key: ${{ steps.cache-env.outputs.key }}
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: ${{ matrix.php-versions }}
extension: ${{ env.extensions }}
ini-values: memory_limit=2G, display_errors=On, error_reporting=-1
- name: Restore/cache tools folder
uses: actions/cache@v1
with:
path: tools
key: all-tools-${{ github.sha }}
restore-keys: |
all-tools-${{ github.sha }}-
all-tools-
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v1
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Run PHPUnit
continue-on-error: true
run: php tools/phpunit
codestyle:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Code style check
uses: phpDocumentor/coding-standard@master
with:
args: -s
phpstan:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: PHPStan
uses: phpDocumentor/phpstan-ga@0.12.3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: analyse src --configuration phpstan.neon
psalm:
name: Psalm
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system:
- ubuntu-latest
php-versions: ['7.2']
env:
extensions: mbstring
key: cache-v1 # can be any string, change to clear the extension cache.
needs:
- setup
- phpunit
steps:
- uses: actions/checkout@master
- name: Setup cache environment
id: cache-env
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.php-versions }}
extensions: ${{ env.extensions }}
key: ${{ env.key }}
- name: Cache extensions
uses: actions/cache@v1
with:
path: ${{ steps.cache-env.outputs.dir }}
key: ${{ steps.cache-env.outputs.key }}
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: ${{ matrix.php-versions }}
extension: ${{ env.extensions }}
tools: psalm
ini-values: memory_limit=2G, display_errors=On, error_reporting=-1
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v1
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-interaction --prefer-dist --optimize-autoloader
- name: Run psalm
run: psalm --output-format=github
bc_check:
name: BC Check
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- name: fetch tags
run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: BC Check
uses: docker://nyholm/roave-bc-check-ga

View File

@@ -1,37 +0,0 @@
.PHONY: install-phive
install-phive:
mkdir tools; \
wget -O tools/phive.phar https://phar.io/releases/phive.phar; \
wget -O tools/phive.phar.asc https://phar.io/releases/phive.phar.asc; \
gpg --keyserver pool.sks-keyservers.net --recv-keys 0x9D8A98B29B2D5D79; \
gpg --verify tools/phive.phar.asc tools/phive.phar; \
chmod +x tools/phive.phar
.PHONY: setup
setup: install-phive
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phar-ga:latest php tools/phive.phar install --force-accept-unsigned
.PHONY: phpcs
phpcs:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:v1.0.0 -s
.PHONY: phpcbf
phpcbf:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:v1.0.0 phpcbf
.PHONY: phpstan
phpstan:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpstan-ga:latest analyse src --no-progress --configuration phpstan.neon
.PHONY: psalm
psalm:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project mickaelandrieu/psalm-ga
.PHONY: test
test:
docker run -it --rm -v${CURDIR}:/github/workspace phpdoc/phpunit-ga
docker run -it --rm -v${CURDIR}:/data -w /data php:7.2 -f ./tests/coverage-checker.php 89
.PHONY: pre-commit-test
pre-commit-test: test phpcs phpstan psalm

View File

@@ -1,15 +0,0 @@
{
"symbol-whitelist" : [
"null", "true", "false",
"static", "self", "parent",
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "XSLTProcessor"
],
"php-core-extensions" : [
"Core",
"pcre",
"Reflection",
"tokenizer",
"SPL",
"standard"
]
}

View File

@@ -14,15 +14,14 @@
}
],
"require": {
"php": "^7.2",
"phpdocumentor/type-resolver": "^1.0",
"webmozart/assert": "^1",
"phpdocumentor/reflection-common": "^2.0",
"ext-filter": "^7.1"
"php": "^7.2 || ^8.0",
"phpdocumentor/type-resolver": "^1.3",
"webmozart/assert": "^1.9.1",
"phpdocumentor/reflection-common": "^2.2",
"ext-filter": "*"
},
"require-dev": {
"mockery/mockery": "^1",
"doctrine/instantiator": "^1"
"mockery/mockery": "~1.3.2"
},
"autoload": {
"psr-4": {

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^8.4.3" installed="8.4.3" location="./tools/phpunit" copy="true"/>
<phar name="phpstan" version="^0.9.1" installed="0.12.2" location="./tools/phpstan" copy="true"/>
</phive>

View File

@@ -1,17 +0,0 @@
<?xml version="1.0"?>
<ruleset name="phpDocumentor">
<description>The coding standard for phpDocumentor.</description>
<file>src</file>
<file>tests/unit</file>
<exclude-pattern>*/tests/unit/Types/ContextFactoryTest.php</exclude-pattern>
<arg value="p"/>
<rule ref="phpDocumentor">
<exclude name="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException" />
</rule>
<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix">
<exclude-pattern>*/src/*/Abstract*.php</exclude-pattern>
</rule>
</ruleset>

View File

@@ -1,8 +0,0 @@
includes:
- /composer/vendor/phpstan/phpstan-mockery/extension.neon
- /composer/vendor/phpstan/phpstan-webmozart-assert/extension.neon
parameters:
level: max
ignoreErrors:
- '#Call to static method Webmozart\\Assert\\Assert::implementsInterface\(\) with class-string#'

View File

@@ -1,45 +0,0 @@
<?xml version="1.0"?>
<psalm
totallyTyped="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config file:///composer/vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<issueHandlers>
<RedundantConditionGivenDocblockType>
<errorLevel type="info">
<!-- Psalm is very strict and believe that because we documented a type, it is redundant to assert it -->
<file name="src/DocBlock/StandardTagFactory.php"/>
</errorLevel>
</RedundantConditionGivenDocblockType>
<PossiblyNullArrayOffset>
<errorLevel type="info">
<!-- Psalm forbid accessing an array with a null offset but it's still working code without notice -->
<file name="src/DocBlock/StandardTagFactory.php"/>
</errorLevel>
</PossiblyNullArrayOffset>
<DeprecatedInterface>
<errorLevel type="info">
<!-- Will be removed in 6.0.0 issues/211 -->
<referencedClass name="phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod"/>
</errorLevel>
</DeprecatedInterface>
<RedundantConditionGivenDocblockType>
<errorLevel type="info">
<!-- Psalm manage to infer a more precise type than PHPStan. notNull assert is needed for PHPStan but
Psalm sees it as redundant -->
<directory name="src/DocBlock/Tags/"/>
</errorLevel>
</RedundantConditionGivenDocblockType>
</issueHandlers>
</psalm>

View File

@@ -14,13 +14,12 @@ declare(strict_types=1);
namespace phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;
use phpDocumentor\Reflection\Utils;
use function count;
use function explode;
use function implode;
use function ltrim;
use function min;
use function preg_split;
use function str_replace;
use function strlen;
use function strpos;
@@ -98,7 +97,7 @@ class DescriptionFactory
return [$contents];
}
$parts = preg_split(
return Utils::pregSplit(
'/\{
# "{@}" is not a valid inline tag. This ensures that we do not treat it as one, but treat it literally.
(?!@\})
@@ -127,9 +126,6 @@ class DescriptionFactory
0,
PREG_SPLIT_DELIM_CAPTURE
);
Assert::isArray($parts);
return $parts;
}
/**

View File

@@ -17,7 +17,6 @@ use InvalidArgumentException;
use phpDocumentor\Reflection\DocBlock\Tags\Author;
use phpDocumentor\Reflection\DocBlock\Tags\Covers;
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag;
@@ -37,6 +36,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Version;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionParameter;
use Webmozart\Assert\Assert;
use function array_merge;
@@ -165,7 +165,7 @@ final class StandardTagFactory implements TagFactory
{
Assert::stringNotEmpty($tagName);
Assert::classExists($handler);
Assert::implementsInterface($handler, StaticMethod::class);
Assert::implementsInterface($handler, Tag::class);
if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
throw new InvalidArgumentException(
@@ -255,10 +255,16 @@ final class StandardTagFactory implements TagFactory
{
$arguments = [];
foreach ($parameters as $parameter) {
$class = $parameter->getClass();
$type = $parameter->getType();
$typeHint = null;
if ($class !== null) {
$typeHint = $class->getName();
if ($type instanceof ReflectionNamedType) {
$typeHint = $type->getName();
if ($typeHint === 'self') {
$declaringClass = $parameter->getDeclaringClass();
if ($declaringClass !== null) {
$typeHint = $declaringClass->getName();
}
}
}
if (isset($locator[$typeHint])) {
@@ -282,6 +288,8 @@ final class StandardTagFactory implements TagFactory
* Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
* tag handler class name.
*
* @param class-string $handlerClassName
*
* @return ReflectionParameter[]
*/
private function fetchParametersForHandlerFactoryMethod(string $handlerClassName) : array

View File

@@ -71,7 +71,15 @@ final class Author extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return $this->authorName . ($this->authorEmail !== '' ? ' <' . $this->authorEmail . '>' : '');
if ($this->authorEmail) {
$authorEmail = '<' . $this->authorEmail . '>';
} else {
$authorEmail = '';
}
$authorName = (string) $this->authorName;
return $authorName . ($authorEmail !== '' ? ($authorName !== '' ? ' ' : '') . $authorEmail : '');
}
/**

View File

@@ -18,8 +18,10 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function preg_split;
use function array_key_exists;
use function explode;
/**
* Reflection class for a @covers tag in a Docblock.
@@ -47,19 +49,31 @@ final class Covers extends BaseTag implements Factory\StaticMethod
?FqsenResolver $resolver = null,
?TypeContext $context = null
) : self {
Assert::notEmpty($body);
Assert::stringNotEmpty($body);
Assert::notNull($descriptionFactory);
Assert::notNull($resolver);
$parts = preg_split('/\s+/Su', $body, 2);
Assert::isArray($parts);
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
return new static(
$resolver->resolve($parts[0], $context),
self::resolveFqsen($parts[0], $resolver, $context),
$descriptionFactory->create($parts[1] ?? '', $context)
);
}
private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen
{
Assert::notNull($fqsenResolver);
$fqsenParts = explode('::', $parts);
$resolved = $fqsenResolver->resolve($fqsenParts[0], $context);
if (!array_key_exists(1, $fqsenParts)) {
return $resolved;
}
return new Fqsen($resolved . '::' . $fqsenParts[1]);
}
/**
* Returns the structural element this tag refers to.
*/
@@ -73,6 +87,14 @@ final class Covers extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return $this->refers . ($this->description ? ' ' . $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$refers = (string) $this->refers;
return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -48,7 +48,7 @@ final class Deprecated extends BaseTag implements Factory\StaticMethod
public function __construct(?string $version = null, ?Description $description = null)
{
Assert::nullOrStringNotEmpty($version);
Assert::nullOrNotEmpty($version);
$this->version = $version;
$this->description = $description;
@@ -95,6 +95,14 @@ final class Deprecated extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return ($this->version ?? '') . ($this->description ? ' ' . $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$version = (string) $this->version;
return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -45,10 +45,15 @@ final class Example implements Tag, Factory\StaticMethod
/** @var string|null */
private $content;
public function __construct(string $filePath, bool $isURI, int $startingLine, int $lineCount, ?string $content)
{
Assert::notEmpty($filePath);
Assert::greaterThanEq($startingLine, 0);
public function __construct(
string $filePath,
bool $isURI,
int $startingLine,
int $lineCount,
?string $content
) {
Assert::stringNotEmpty($filePath);
Assert::greaterThanEq($startingLine, 1);
Assert::greaterThanEq($lineCount, 0);
$this->filePath = $filePath;
@@ -63,8 +68,8 @@ final class Example implements Tag, Factory\StaticMethod
public function getContent() : string
{
if ($this->content === null) {
$filePath = '"' . $this->filePath . '"';
if ($this->content === null || $this->content === '') {
$filePath = $this->filePath;
if ($this->isURI) {
$filePath = $this->isUriRelative($this->filePath)
? str_replace('%2F', '/', rawurlencode($this->filePath))
@@ -85,7 +90,7 @@ final class Example implements Tag, Factory\StaticMethod
public static function create(string $body) : ?Tag
{
// File component: File path in quotes or File URI / Source information
if (!preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
if (!preg_match('/^\s*(?:(\"[^\"]+\")|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
return null;
}
@@ -107,7 +112,7 @@ final class Example implements Tag, Factory\StaticMethod
// Starting line / Number of lines / Description
if (preg_match('/^([1-9]\d*)(?:\s+((?1))\s*)?(.*)$/sux', $matches[3], $contentMatches)) {
$startingLine = (int) $contentMatches[1];
if (isset($contentMatches[2]) && $contentMatches[2] !== '') {
if (isset($contentMatches[2])) {
$lineCount = (int) $contentMatches[2];
}
@@ -134,7 +139,7 @@ final class Example implements Tag, Factory\StaticMethod
*/
public function getFilePath() : string
{
return $this->filePath;
return trim($this->filePath, '"');
}
/**
@@ -142,7 +147,22 @@ final class Example implements Tag, Factory\StaticMethod
*/
public function __toString() : string
{
return $this->filePath . ($this->content ? ' ' . $this->content : '');
$filePath = (string) $this->filePath;
$isDefaultLine = $this->startingLine === 1 && $this->lineCount === 0;
$startingLine = !$isDefaultLine ? (string) $this->startingLine : '';
$lineCount = !$isDefaultLine ? (string) $this->lineCount : '';
$content = (string) $this->content;
return $filePath
. ($startingLine !== ''
? ($filePath !== '' ? ' ' : '') . $startingLine
: '')
. ($lineCount !== ''
? ($filePath !== '' || $startingLine !== '' ? ' ' : '') . $lineCount
: '')
. ($content !== ''
? ($filePath !== '' || $startingLine !== '' || $lineCount !== '' ? ' ' : '') . $content
: '');
}
/**

View File

@@ -64,7 +64,13 @@ final class Generic extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return $this->description ? $this->description->render() : '';
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
return $description;
}
/**

View File

@@ -8,12 +8,13 @@ use Closure;
use Exception;
use phpDocumentor\Reflection\DocBlock\Tag;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use Throwable;
use function array_map;
use function array_walk_recursive;
use function get_class;
use function get_resource_type;
use function is_array;
use function is_object;
use function is_resource;
use function sprintf;
@@ -80,29 +81,12 @@ final class InvalidTag implements Tag
$traceProperty = (new ReflectionClass(Exception::class))->getProperty('trace');
$traceProperty->setAccessible(true);
$flatten =
/** @param mixed $value */
static function (&$value) : void {
if ($value instanceof Closure) {
$closureReflection = new ReflectionFunction($value);
$value = sprintf(
'(Closure at %s:%s)',
$closureReflection->getFileName(),
$closureReflection->getStartLine()
);
} elseif (is_object($value)) {
$value = sprintf('object(%s)', get_class($value));
} elseif (is_resource($value)) {
$value = sprintf('resource(%s)', get_resource_type($value));
}
};
do {
$trace = $exception->getTrace();
if (isset($trace[0]['args'])) {
$trace = array_map(
static function (array $call) use ($flatten) : array {
array_walk_recursive($call['args'], $flatten);
function (array $call) : array {
$call['args'] = array_map([$this, 'flattenArguments'], $call['args']);
return $call;
},
@@ -117,6 +101,33 @@ final class InvalidTag implements Tag
$traceProperty->setAccessible(false);
}
/**
* @param mixed $value
*
* @return mixed
*
* @throws ReflectionException
*/
private function flattenArguments($value)
{
if ($value instanceof Closure) {
$closureReflection = new ReflectionFunction($value);
$value = sprintf(
'(Closure at %s:%s)',
$closureReflection->getFileName(),
$closureReflection->getStartLine()
);
} elseif (is_object($value)) {
$value = sprintf('object(%s)', get_class($value));
} elseif (is_resource($value)) {
$value = sprintf('resource(%s)', get_resource_type($value));
} elseif (is_array($value)) {
$value = array_map([$this, 'flattenArguments'], $value);
}
return $value;
}
public function render(?Formatter $formatter = null) : string
{
if ($formatter === null) {

View File

@@ -16,11 +16,11 @@ namespace phpDocumentor\Reflection\DocBlock\Tags;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function preg_split;
/**
* Reflection class for a @link tag in a Docblock.
* Reflection class for a {@}link tag in a Docblock.
*/
final class Link extends BaseTag implements Factory\StaticMethod
{
@@ -46,8 +46,7 @@ final class Link extends BaseTag implements Factory\StaticMethod
) : self {
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
Assert::isArray($parts);
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
$description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
return new static($parts[0], $description);
@@ -66,6 +65,14 @@ final class Link extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return $this->link . ($this->description ? ' ' . $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$link = (string) $this->link;
return $link . ($description !== '' ? ($link !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -213,11 +213,25 @@ final class Method extends BaseTag implements Factory\StaticMethod
$arguments[] = $argument['type'] . ' $' . $argument['name'];
}
return trim(($this->isStatic() ? 'static ' : '')
. (string) $this->returnType . ' '
. $this->methodName
. '(' . implode(', ', $arguments) . ')'
. ($this->description ? ' ' . $this->description->render() : ''));
$argumentStr = '(' . implode(', ', $arguments) . ')';
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$static = $this->isStatic ? 'static' : '';
$returnType = (string) $this->returnType;
$methodName = (string) $this->methodName;
return $static
. ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '')
. ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $methodName : '')
. $argumentStr
. ($description !== '' ? ' ' . $description : '');
}
/**

View File

@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_shift;
use function array_unshift;
use function implode;
use function preg_split;
use function strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
@@ -38,17 +38,22 @@ final class Param extends TagWithType implements Factory\StaticMethod
/** @var bool determines whether this is a variadic argument */
private $isVariadic;
/** @var bool determines whether this is passed by reference */
private $isReference;
public function __construct(
?string $variableName,
?Type $type = null,
bool $isVariadic = false,
?Description $description = null
?Description $description = null,
bool $isReference = false
) {
$this->name = 'param';
$this->variableName = $variableName;
$this->type = $type;
$this->isVariadic = $isVariadic;
$this->description = $description;
$this->isReference = $isReference;
}
public static function create(
@@ -64,39 +69,46 @@ final class Param extends TagWithType implements Factory\StaticMethod
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
Assert::isArray($parts);
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
$variableName = '';
$isVariadic = false;
$isReference = false;
// if the first item that is encountered is not a variable; it is a type
if ($firstPart && $firstPart[0] !== '$') {
if ($firstPart && !self::strStartsWithVariable($firstPart)) {
$type = $typeResolver->resolve($firstPart, $context);
} else {
// first part is not a type; we should prepend it to the parts array for further processing
array_unshift($parts, $firstPart);
}
// if the next item starts with a $ or ...$ it must be the variable name
if (isset($parts[0]) && (strpos($parts[0], '$') === 0 || strpos($parts[0], '...$') === 0)) {
// if the next item starts with a $ or ...$ or &$ or &...$ it must be the variable name
if (isset($parts[0]) && self::strStartsWithVariable($parts[0])) {
$variableName = array_shift($parts);
array_shift($parts);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
if (strpos($variableName, '...') === 0) {
$isVariadic = true;
$variableName = substr($variableName, 3);
}
if (strpos($variableName, '$') === 0) {
$variableName = substr($variableName, 1);
} elseif (strpos($variableName, '&$') === 0) {
$isReference = true;
$variableName = substr($variableName, 2);
} elseif (strpos($variableName, '...$') === 0) {
$isVariadic = true;
$variableName = substr($variableName, 4);
} elseif (strpos($variableName, '&...$') === 0) {
$isVariadic = true;
$isReference = true;
$variableName = substr($variableName, 5);
}
}
$description = $descriptionFactory->create(implode('', $parts), $context);
return new static($variableName, $type, $isVariadic, $description);
return new static($variableName, $type, $isVariadic, $description, $isReference);
}
/**
@@ -115,14 +127,46 @@ final class Param extends TagWithType implements Factory\StaticMethod
return $this->isVariadic;
}
/**
* Returns whether this tag is passed by reference.
*/
public function isReference() : bool
{
return $this->isReference;
}
/**
* Returns a string representation for this tag.
*/
public function __toString() : string
{
return ($this->type ? $this->type . ' ' : '')
. ($this->isVariadic() ? '...' : '')
. ($this->variableName !== null ? '$' . $this->variableName : '')
. ($this->description ? ' ' . $this->description : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$variableName = '';
if ($this->variableName) {
$variableName .= ($this->isReference ? '&' : '') . ($this->isVariadic ? '...' : '');
$variableName .= '$' . $this->variableName;
}
$type = (string) $this->type;
return $type
. ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '')
. ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : '');
}
private static function strStartsWithVariable(string $str) : bool
{
return strpos($str, '$') === 0
||
strpos($str, '...$') === 0
||
strpos($str, '&$') === 0
||
strpos($str, '&...$') === 0;
}
}

View File

@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_shift;
use function array_unshift;
use function implode;
use function preg_split;
use function strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
@@ -57,8 +57,7 @@ final class Property extends TagWithType implements Factory\StaticMethod
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
Assert::isArray($parts);
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
$variableName = '';
// if the first item that is encountered is not a variable; it is a type
@@ -69,10 +68,12 @@ final class Property extends TagWithType implements Factory\StaticMethod
array_unshift($parts, $firstPart);
}
// if the next item starts with a $ or ...$ it must be the variable name
// if the next item starts with a $ it must be the variable name
if (isset($parts[0]) && strpos($parts[0], '$') === 0) {
$variableName = array_shift($parts);
array_shift($parts);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
@@ -97,8 +98,22 @@ final class Property extends TagWithType implements Factory\StaticMethod
*/
public function __toString() : string
{
return ($this->type ? $this->type . ' ' : '')
. ($this->variableName ? '$' . $this->variableName : '')
. ($this->description ? ' ' . $this->description : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
if ($this->variableName) {
$variableName = '$' . $this->variableName;
} else {
$variableName = '';
}
$type = (string) $this->type;
return $type
. ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '')
. ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_shift;
use function array_unshift;
use function implode;
use function preg_split;
use function strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
@@ -57,8 +57,7 @@ final class PropertyRead extends TagWithType implements Factory\StaticMethod
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
Assert::isArray($parts);
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
$variableName = '';
// if the first item that is encountered is not a variable; it is a type
@@ -69,10 +68,12 @@ final class PropertyRead extends TagWithType implements Factory\StaticMethod
array_unshift($parts, $firstPart);
}
// if the next item starts with a $ or ...$ it must be the variable name
// if the next item starts with a $ it must be the variable name
if (isset($parts[0]) && strpos($parts[0], '$') === 0) {
$variableName = array_shift($parts);
array_shift($parts);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
@@ -97,8 +98,22 @@ final class PropertyRead extends TagWithType implements Factory\StaticMethod
*/
public function __toString() : string
{
return ($this->type ? $this->type . ' ' : '')
. ($this->variableName ? '$' . $this->variableName : '')
. ($this->description ? ' ' . $this->description : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
if ($this->variableName) {
$variableName = '$' . $this->variableName;
} else {
$variableName = '';
}
$type = (string) $this->type;
return $type
. ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '')
. ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_shift;
use function array_unshift;
use function implode;
use function preg_split;
use function strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
@@ -57,8 +57,7 @@ final class PropertyWrite extends TagWithType implements Factory\StaticMethod
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
Assert::isArray($parts);
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
$variableName = '';
// if the first item that is encountered is not a variable; it is a type
@@ -69,10 +68,12 @@ final class PropertyWrite extends TagWithType implements Factory\StaticMethod
array_unshift($parts, $firstPart);
}
// if the next item starts with a $ or ...$ it must be the variable name
// if the next item starts with a $ it must be the variable name
if (isset($parts[0]) && strpos($parts[0], '$') === 0) {
$variableName = array_shift($parts);
array_shift($parts);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
@@ -97,8 +98,22 @@ final class PropertyWrite extends TagWithType implements Factory\StaticMethod
*/
public function __toString() : string
{
return ($this->type ? $this->type . ' ' : '')
. ($this->variableName ? '$' . $this->variableName : '')
. ($this->description ? ' ' . $this->description : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
if ($this->variableName) {
$variableName = '$' . $this->variableName;
} else {
$variableName = '';
}
$type = (string) $this->type;
return $type
. ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '')
. ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -51,6 +51,14 @@ final class Return_ extends TagWithType implements Factory\StaticMethod
public function __toString() : string
{
return ($this->type ?: 'mixed') . ' ' . (string) $this->description;
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$type = $this->type ? '' . $this->type : 'mixed';
return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -18,11 +18,14 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef;
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference;
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_key_exists;
use function explode;
use function preg_match;
use function preg_split;
/**
* Reflection class for an {@}see tag in a Docblock.
@@ -50,11 +53,9 @@ final class See extends BaseTag implements Factory\StaticMethod
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
) : self {
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
Assert::isArray($parts);
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
$description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
// https://tools.ietf.org/html/rfc2396#section-3
@@ -62,7 +63,20 @@ final class See extends BaseTag implements Factory\StaticMethod
return new static(new Url($parts[0]), $description);
}
return new static(new FqsenRef($typeResolver->resolve($parts[0], $context)), $description);
return new static(new FqsenRef(self::resolveFqsen($parts[0], $typeResolver, $context)), $description);
}
private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen
{
Assert::notNull($fqsenResolver);
$fqsenParts = explode('::', $parts);
$resolved = $fqsenResolver->resolve($fqsenParts[0], $context);
if (!array_key_exists(1, $fqsenParts)) {
return $resolved;
}
return new Fqsen($resolved . '::' . $fqsenParts[1]);
}
/**
@@ -78,6 +92,14 @@ final class See extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return $this->refers . ($this->description ? ' ' . $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$refers = (string) $this->refers;
return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -48,7 +48,7 @@ final class Since extends BaseTag implements Factory\StaticMethod
public function __construct(?string $version = null, ?Description $description = null)
{
Assert::nullOrStringNotEmpty($version);
Assert::nullOrNotEmpty($version);
$this->version = $version;
$this->description = $description;
@@ -89,6 +89,14 @@ final class Since extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return (string) $this->version . ($this->description ? ' ' . (string) $this->description : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$version = (string) $this->version;
return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -96,8 +96,22 @@ final class Source extends BaseTag implements Factory\StaticMethod
public function __toString() : string
{
return $this->startingLine
. ($this->lineCount !== null ? ' ' . $this->lineCount : '')
. ($this->description ? ' ' . (string) $this->description : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$startingLine = (string) $this->startingLine;
$lineCount = $this->lineCount !== null ? '' . $this->lineCount : '';
return $startingLine
. ($lineCount !== ''
? ($startingLine || $startingLine === '0' ? ' ' : '') . $lineCount
: '')
. ($description !== ''
? ($startingLine || $startingLine === '0' || $lineCount !== '' ? ' ' : '') . $description
: '');
}
}

View File

@@ -51,6 +51,14 @@ final class Throws extends TagWithType implements Factory\StaticMethod
public function __toString() : string
{
return (string) $this->type . ' ' . (string) $this->description;
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$type = (string) $this->type;
return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -18,8 +18,10 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function preg_split;
use function array_key_exists;
use function explode;
/**
* Reflection class for a {@}uses tag in a Docblock.
@@ -50,16 +52,27 @@ final class Uses extends BaseTag implements Factory\StaticMethod
Assert::notNull($resolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
Assert::isArray($parts);
Assert::allString($parts);
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
return new static(
$resolver->resolve($parts[0], $context),
self::resolveFqsen($parts[0], $resolver, $context),
$descriptionFactory->create($parts[1] ?? '', $context)
);
}
private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen
{
Assert::notNull($fqsenResolver);
$fqsenParts = explode('::', $parts);
$resolved = $fqsenResolver->resolve($fqsenParts[0], $context);
if (!array_key_exists(1, $fqsenParts)) {
return $resolved;
}
return new Fqsen($resolved . '::' . $fqsenParts[1]);
}
/**
* Returns the structural element this tag refers to.
*/
@@ -73,6 +86,14 @@ final class Uses extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return $this->refers . ' ' . (string) $this->description;
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$refers = (string) $this->refers;
return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -18,11 +18,11 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_shift;
use function array_unshift;
use function implode;
use function preg_split;
use function strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
@@ -57,8 +57,7 @@ final class Var_ extends TagWithType implements Factory\StaticMethod
[$firstPart, $body] = self::extractTypeFromBody($body);
$parts = preg_split('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
Assert::isArray($parts);
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
$type = null;
$variableName = '';
@@ -70,10 +69,12 @@ final class Var_ extends TagWithType implements Factory\StaticMethod
array_unshift($parts, $firstPart);
}
// if the next item starts with a $ or ...$ it must be the variable name
// if the next item starts with a $ it must be the variable name
if (isset($parts[0]) && strpos($parts[0], '$') === 0) {
$variableName = array_shift($parts);
array_shift($parts);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
@@ -98,8 +99,22 @@ final class Var_ extends TagWithType implements Factory\StaticMethod
*/
public function __toString() : string
{
return ($this->type ? $this->type . ' ' : '')
. (empty($this->variableName) ? '' : '$' . $this->variableName)
. ($this->description ? ' ' . $this->description : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
if ($this->variableName) {
$variableName = '$' . $this->variableName;
} else {
$variableName = '';
}
$type = (string) $this->type;
return $type
. ($variableName !== '' ? ($type !== '' ? ' ' : '') . $variableName : '')
. ($description !== '' ? ($type !== '' || $variableName !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -92,7 +92,14 @@ final class Version extends BaseTag implements Factory\StaticMethod
*/
public function __toString() : string
{
return ((string) $this->version) .
($this->description instanceof Description ? ' ' . $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$version = (string) $this->version;
return $version . ($description !== '' ? ($version !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace phpDocumentor\Reflection\Exception;
use InvalidArgumentException;
use const PREG_BACKTRACK_LIMIT_ERROR;
use const PREG_BAD_UTF8_ERROR;
use const PREG_BAD_UTF8_OFFSET_ERROR;
use const PREG_INTERNAL_ERROR;
use const PREG_JIT_STACKLIMIT_ERROR;
use const PREG_NO_ERROR;
use const PREG_RECURSION_LIMIT_ERROR;
final class PcreException extends InvalidArgumentException
{
public static function createFromPhpError(int $errorCode) : self
{
switch ($errorCode) {
case PREG_BACKTRACK_LIMIT_ERROR:
return new self('Backtrack limit error');
case PREG_RECURSION_LIMIT_ERROR:
return new self('Recursion limit error');
case PREG_BAD_UTF8_ERROR:
return new self('Bad UTF8 error');
case PREG_BAD_UTF8_OFFSET_ERROR:
return new self('Bad UTF8 offset error');
case PREG_JIT_STACKLIMIT_ERROR:
return new self('Jit stacklimit error');
case PREG_NO_ERROR:
case PREG_INTERNAL_ERROR:
default:
}
return new self('Unknown Pcre error');
}
}

View File

@@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
use phpDocumentor\Reflection\Exception\PcreException;
use function preg_last_error;
use function preg_split as php_preg_split;
abstract class Utils
{
/**
* Wrapper function for phps preg_split
*
* This function is inspired by {@link https://github.com/thecodingmachine/safe/blob/master/generated/pcre.php}. But
* since this library is all about performance we decided to strip everything we don't need. Reducing the amount
* of files that have to be loaded, ect.
*
* @param string $pattern The pattern to search for, as a string.
* @param string $subject The input string.
* @param int|null $limit If specified, then only substrings up to limit are returned with the
* rest of the string being placed in the last substring. A limit of -1 or 0 means "no limit".
* @param int $flags flags can be any combination of the following flags (combined with the | bitwise operator):
* *PREG_SPLIT_NO_EMPTY*
* If this flag is set, only non-empty pieces will be returned by preg_split().
* *PREG_SPLIT_DELIM_CAPTURE*
* If this flag is set, parenthesized expression in the delimiter pattern will be captured
* and returned as well.
* *PREG_SPLIT_OFFSET_CAPTURE*
* If this flag is set, for every occurring match the appendant string offset will also be returned.
* Note that this changes the return value in an array where every element is an array consisting of the
* matched string at offset 0 and its string offset into subject at offset 1.
*
* @return string[] Returns an array containing substrings of subject split along boundaries matched by pattern
*
* @throws PcreException
*/
public static function pregSplit(string $pattern, string $subject, ?int $limit = -1, int $flags = 0) : array
{
$parts = php_preg_split($pattern, $subject, $limit, $flags);
if ($parts === false) {
throw PcreException::createFromPhpError(preg_last_error());
}
return $parts;
}
}

View File

@@ -1,201 +0,0 @@
on:
push:
branches:
- master
pull_request:
name: Qa workflow
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Restore/cache tools folder
uses: actions/cache@v1
with:
path: tools
key: all-tools-${{ github.sha }}
restore-keys: |
all-tools-${{ github.sha }}-
all-tools-
- name: composer
uses: docker://composer
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: install --no-interaction --prefer-dist --optimize-autoloader
- name: composer-require-checker
uses: docker://phpga/composer-require-checker-ga
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: check --config-file ./composer-require-config.json composer.json
- name: Install phive
run: make install-phive
- name: Install PHAR dependencies
run: tools/phive.phar --no-progress install --copy --trust-gpg-keys 4AA394086372C20A,D2CCAC42F6295E7D,E82B2FB314E9906E,8E730BA25823D8B5 --force-accept-unsigned
phpunit-with-coverage:
runs-on: ubuntu-latest
name: Unit tests
needs: setup
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Restore/cache tools folder
uses: actions/cache@v1
with:
path: tools
key: all-tools-${{ github.sha }}
restore-keys: |
all-tools-${{ github.sha }}-
all-tools-
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: 7.2
extension-csv: mbstring, intl, iconv, libxml, dom, json, simplexml, zlib
ini-values-csv: memory_limit=2G, display_errors=On, error_reporting=-1
coverage: xdebug
pecl: false
- name: Run PHPUnit
run: php tools/phpunit
phpunit:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system:
- ubuntu-latest
- windows-latest
- macOS-latest
php-versions: ['7.2', '7.3', '7.4']
name: Unit tests for PHP version ${{ matrix.php-versions }} on ${{ matrix.operating-system }}
needs:
- setup
- phpunit-with-coverage
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Restore/cache tools folder
uses: actions/cache@v1
with:
path: tools
key: all-tools-${{ github.sha }}
restore-keys: |
all-tools-${{ github.sha }}-
all-tools-
- name: Setup PHP
uses: shivammathur/setup-php@master
with:
php-version: ${{ matrix.php-versions }}
extension-csv: mbstring, intl, iconv, libxml, dom, json, simplexml, zlib
ini-values-csv: memory_limit=2G, display_errors=On, error_reporting=-1
pecl: false
- name: Run PHPUnit
continue-on-error: true
run: php tools/phpunit
codestyle:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Code style check
uses: phpDocumentor/coding-standard@master
with:
args: -s
phpstan:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: PHPStan
uses: phpDocumentor/phpstan-ga@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
args: analyse src --configuration phpstan.neon
psalm:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Restore/cache tools folder
uses: actions/cache@v1
with:
path: tools
key: all-tools-${{ github.sha }}
restore-keys: |
all-tools-${{ github.sha }}-
all-tools-
- name: Psalm
uses: docker://mickaelandrieu/psalm-ga
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
bc_check:
name: BC Check
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@master
- name: fetch tags
run: git fetch --depth=1 origin +refs/tags/*:refs/tags/*
- name: Restore/cache vendor folder
uses: actions/cache@v1
with:
path: vendor
key: all-build-${{ hashFiles('**/composer.lock') }}
restore-keys: |
all-build-${{ hashFiles('**/composer.lock') }}
all-build-
- name: Roave BC Check
uses: docker://nyholm/roave-bc-check-ga

View File

@@ -1,32 +0,0 @@
.PHONY: install-phive
install-phive:
mkdir tools; \
wget -O tools/phive.phar https://phar.io/releases/phive.phar; \
wget -O tools/phive.phar.asc https://phar.io/releases/phive.phar.asc; \
gpg --keyserver pool.sks-keyservers.net --recv-keys 0x9D8A98B29B2D5D79; \
gpg --verify tools/phive.phar.asc tools/phive.phar; \
chmod +x tools/phive.phar
.PHONY: setup
setup: install-phive
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phar-ga:latest php tools/phive.phar install --force-accept-unsigned
.PHONY: phpcs
phpcs:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpcs-ga:v1.0.0 -s
.PHONY: phpstan
phpstan:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project phpdoc/phpstan-ga:latest analyse src --no-progress --configuration phpstan.neon
.PHONY: psaml
psalm:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project mickaelandrieu/psalm-ga
.PHONY: test
test:
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.2 tools/phpunit
.PHONY: pre-commit-test
pre-commit-test: test phpcs phpstan psalm

View File

@@ -1,8 +1,8 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![](https://github.com/phpdocumentor/typeresolver/workflows/Qa%20workflow/badge.svg?branch=master)
[![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/TypeResolver.svg)](https://coveralls.io/github/phpDocumentor/TypeResolver?branch=master)
[![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/TypeResolver.svg)](https://scrutinizer-ci.com/g/phpDocumentor/TypeResolver/?branch=master)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/TypeResolver.svg)](https://scrutinizer-ci.com/g/phpDocumentor/TypeResolver/?branch=master)
![](https://github.com/phpdocumentor/typeresolver/workflows/Qa%20workflow/badge.svg?branch=1.x)
[![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/TypeResolver.svg)](https://coveralls.io/github/phpDocumentor/TypeResolver?branch=1.x)
[![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/TypeResolver.svg)](https://scrutinizer-ci.com/g/phpDocumentor/TypeResolver/?branch=1.x)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/TypeResolver.svg)](https://scrutinizer-ci.com/g/phpDocumentor/TypeResolver/?branch=1.x)
![Packagist Version](https://img.shields.io/packagist/v/phpdocumentor/type-resolver?label=Packagist%20stable)
![Packagist Version](https://img.shields.io/packagist/vpre/phpdocumentor/type-resolver?label=Packagist%20unstable)

View File

@@ -1,15 +0,0 @@
{
"symbol-whitelist" : [
"null", "true", "false",
"static", "self", "parent",
"array", "string", "int", "float", "bool", "iterable", "callable", "void", "object", "XSLTProcessor"
],
"php-core-extensions" : [
"Core",
"pcre",
"Reflection",
"tokenizer",
"SPL",
"standard"
]
}

View File

@@ -10,12 +10,11 @@
}
],
"require": {
"php": "^7.2",
"php": "^7.2 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
"mockery/mockery": "~1",
"ext-tokenizer": "^7.2"
"ext-tokenizer": "*"
},
"autoload": {
"psr-4": {
@@ -24,12 +23,12 @@
},
"autoload-dev": {
"psr-4": {
"phpDocumentor\\Reflection\\": "tests/unit"
"phpDocumentor\\Reflection\\": ["tests/unit", "tests/benchmark"]
}
},
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
"dev-1.x": "1.x-dev"
}
}
}

View File

@@ -4,32 +4,29 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2442731855d696520d47913fd007c143",
"content-hash": "ee8aea1f755e1772266bc7e041d8ee5b",
"packages": [
{
"name": "phpdocumentor/reflection-common",
"version": "2.0.0",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a"
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a",
"reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "~6"
"php": "^7.2 || ^8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.x-dev"
"dev-2.x": "2.x-dev"
}
},
"autoload": {
@@ -56,374 +53,19 @@
"reflection",
"static analysis"
],
"time": "2018-08-07T13:53:10+00:00"
}
],
"packages-dev": [
{
"name": "hamcrest/hamcrest-php",
"version": "v2.0.0",
"source": {
"type": "git",
"url": "https://github.com/hamcrest/hamcrest-php.git",
"reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad",
"reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad",
"shasum": ""
},
"require": {
"php": "^5.3|^7.0"
},
"replace": {
"cordoval/hamcrest-php": "*",
"davedevelopment/hamcrest-php": "*",
"kodova/hamcrest-php": "*"
},
"require-dev": {
"phpunit/php-file-iterator": "1.3.3",
"phpunit/phpunit": "~4.0",
"satooshi/php-coveralls": "^1.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
}
},
"autoload": {
"classmap": [
"hamcrest"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD"
],
"description": "This is the PHP port of Hamcrest Matchers",
"keywords": [
"test"
],
"time": "2016-01-20T08:20:44+00:00"
},
{
"name": "mockery/mockery",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "5571962a4f733fbb57bede39778f71647fae8e66"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/5571962a4f733fbb57bede39778f71647fae8e66",
"reference": "5571962a4f733fbb57bede39778f71647fae8e66",
"shasum": ""
},
"require": {
"hamcrest/hamcrest-php": "~2.0",
"lib-pcre": ">=7.0",
"php": ">=5.6.0",
"sebastian/comparator": "^1.2.4|^3.0"
},
"require-dev": {
"phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.2.x-dev"
}
},
"autoload": {
"psr-0": {
"Mockery": "library/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Pádraic Brady",
"email": "padraic.brady@gmail.com",
"homepage": "http://blog.astrumfutura.com"
},
{
"name": "Dave Marshall",
"email": "dave.marshall@atstsolutions.co.uk",
"homepage": "http://davedevelopment.co.uk"
}
],
"description": "Mockery is a simple yet flexible PHP mock object framework",
"homepage": "https://github.com/mockery/mockery",
"keywords": [
"BDD",
"TDD",
"library",
"mock",
"mock objects",
"mockery",
"stub",
"test",
"test double",
"testing"
],
"time": "2019-11-24T07:54:50+00:00"
},
{
"name": "sebastian/comparator",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
"reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
"shasum": ""
},
"require": {
"php": "^7.1",
"sebastian/diff": "^3.0",
"sebastian/exporter": "^3.1"
},
"require-dev": {
"phpunit/phpunit": "^7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@2bepublished.at"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Provides the functionality to compare PHP values for equality",
"homepage": "https://github.com/sebastianbergmann/comparator",
"keywords": [
"comparator",
"compare",
"equality"
],
"time": "2018-07-12T15:12:46+00:00"
},
{
"name": "sebastian/diff",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
"reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
"shasum": ""
},
"require": {
"php": "^7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5 || ^8.0",
"symfony/process": "^2 || ^3.3 || ^4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Kore Nordmann",
"email": "mail@kore-nordmann.de"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
}
],
"description": "Diff implementation",
"homepage": "https://github.com/sebastianbergmann/diff",
"keywords": [
"diff",
"udiff",
"unidiff",
"unified diff"
],
"time": "2019-02-04T06:01:07+00:00"
},
{
"name": "sebastian/exporter",
"version": "3.1.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
"reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
"shasum": ""
},
"require": {
"php": "^7.0",
"sebastian/recursion-context": "^3.0"
},
"require-dev": {
"ext-mbstring": "*",
"phpunit/phpunit": "^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.1.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Volker Dusch",
"email": "github@wallbash.com"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
},
{
"name": "Bernhard Schussek",
"email": "bschussek@gmail.com"
}
],
"description": "Provides the functionality to export PHP variables for visualization",
"homepage": "http://www.github.com/sebastianbergmann/exporter",
"keywords": [
"export",
"exporter"
],
"time": "2019-09-14T09:02:43+00:00"
},
{
"name": "sebastian/recursion-context",
"version": "3.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
"reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
"shasum": ""
},
"require": {
"php": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^6.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
},
"autoload": {
"classmap": [
"src/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Jeff Welch",
"email": "whatthejeff@gmail.com"
},
{
"name": "Sebastian Bergmann",
"email": "sebastian@phpunit.de"
},
{
"name": "Adam Harvey",
"email": "aharvey@php.net"
}
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2017-03-03T06:23:57+00:00"
"time": "2020-06-27T09:03:43+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "^7.2"
"php": "^7.2 || ^8.0"
},
"platform-dev": {
"ext-tokenizer": "^7.2"
"ext-tokenizer": "*"
}
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="phpunit" version="^8.4" installed="8.4.3" location="./tools/phpunit" copy="true"/>
</phive>

View File

@@ -0,0 +1,10 @@
{
"bootstrap": "vendor/autoload.php",
"path": "tests/benchmark",
"extensions": [
"Jaapio\\Blackfire\\Extension"
],
"blackfire" : {
"env": "c12030d0-c177-47e2-b466-4994c40dc993"
}
}

View File

@@ -1,16 +0,0 @@
<?xml version="1.0"?>
<ruleset name="phpDocumentor">
<description>The coding standard for phpDocumentor.</description>
<file>src</file>
<file>tests/unit</file>
<exclude-pattern>*/tests/unit/Types/ContextFactoryTest.php</exclude-pattern>
<arg value="p"/>
<rule ref="phpDocumentor">
</rule>
<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix">
<exclude-pattern>*/src/*/Abstract*.php</exclude-pattern>
</rule>
</ruleset>

View File

@@ -1,4 +0,0 @@
parameters:
level: max
checkGenericClassInNonGenericObjectType: false
checkMissingIterableValueType: false

View File

@@ -1,18 +0,0 @@
<?xml version="1.0"?>
<psalm
totallyTyped="false"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config file:///composer/vendor/vimeo/psalm/config.xsd"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
<issueHandlers>
<LessSpecificReturnType errorLevel="info" />
</issueHandlers>
</psalm>

View File

@@ -19,6 +19,11 @@ use function explode;
use function implode;
use function strpos;
/**
* Resolver for Fqsen using Context information
*
* @psalm-immutable
*/
class FqsenResolver
{
/** @var string Definition of the NAMESPACE operator in PHP */

View File

@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
interface PseudoType extends Type
{
public function underlyingType() : Type;
}

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Boolean;
use function class_alias;
/**
* Value Object representing the PseudoType 'False', which is a Boolean type.
*
* @psalm-immutable
*/
final class False_ extends Boolean implements PseudoType
{
public function underlyingType() : Type
{
return new Boolean();
}
public function __toString() : string
{
return 'false';
}
}
class_alias('\phpDocumentor\Reflection\PseudoTypes\False_', 'phpDocumentor\Reflection\Types\False_', false);

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link https://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Boolean;
use function class_alias;
/**
* Value Object representing the PseudoType 'False', which is a Boolean type.
*
* @psalm-immutable
*/
final class True_ extends Boolean implements PseudoType
{
public function underlyingType() : Type
{
return new Boolean();
}
public function __toString() : string
{
return 'true';
}
}
class_alias('\phpDocumentor\Reflection\PseudoTypes\True_', 'phpDocumentor\Reflection\Types\True_', false);

View File

@@ -13,6 +13,9 @@ declare(strict_types=1);
namespace phpDocumentor\Reflection;
/**
* @psalm-immutable
*/
interface Type
{
/**

View File

@@ -20,23 +20,26 @@ use phpDocumentor\Reflection\Types\ClassString;
use phpDocumentor\Reflection\Types\Collection;
use phpDocumentor\Reflection\Types\Compound;
use phpDocumentor\Reflection\Types\Context;
use phpDocumentor\Reflection\Types\Expression;
use phpDocumentor\Reflection\Types\Integer;
use phpDocumentor\Reflection\Types\Intersection;
use phpDocumentor\Reflection\Types\Iterable_;
use phpDocumentor\Reflection\Types\Nullable;
use phpDocumentor\Reflection\Types\Object_;
use phpDocumentor\Reflection\Types\String_;
use RuntimeException;
use function array_keys;
use function array_key_exists;
use function array_pop;
use function array_values;
use function class_exists;
use function class_implements;
use function count;
use function end;
use function in_array;
use function key;
use function preg_split;
use function strlen;
use function strpos;
use function strtolower;
use function substr;
use function trim;
use const PREG_SPLIT_DELIM_CAPTURE;
use const PREG_SPLIT_NO_EMPTY;
@@ -84,8 +87,8 @@ final class TypeResolver
'scalar' => Types\Scalar::class,
'callback' => Types\Callable_::class,
'callable' => Types\Callable_::class,
'false' => Types\Boolean::class,
'true' => Types\Boolean::class,
'false' => PseudoTypes\False_::class,
'true' => PseudoTypes\True_::class,
'self' => Types\Self_::class,
'$this' => Types\This::class,
'static' => Types\Static_::class,
@@ -93,7 +96,10 @@ final class TypeResolver
'iterable' => Iterable_::class,
];
/** @var FqsenResolver */
/**
* @var FqsenResolver
* @psalm-readonly
*/
private $fqsenResolver;
/**
@@ -131,9 +137,9 @@ final class TypeResolver
$context = new Context('');
}
// split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)[]`, '<', '>' and type names
// split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)`, `[]`, '<', '>' and type names
$tokens = preg_split(
'/(\\||\\?|<|>|, ?|\\(|\\)(?:\\[\\])+)/',
'/(\\||\\?|<|>|&|, ?|\\(|\\)|\\[\\]+)/',
$type,
-1,
PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
@@ -143,6 +149,7 @@ final class TypeResolver
throw new InvalidArgumentException('Unable to split the type string "' . $type . '" into tokens');
}
/** @var ArrayIterator<int, string|null> $tokenIterator */
$tokenIterator = new ArrayIterator($tokens);
return $this->parseTypes($tokenIterator, $context, self::PARSER_IN_COMPOUND);
@@ -151,38 +158,49 @@ final class TypeResolver
/**
* Analyse each tokens and creates types
*
* @param ArrayIterator $tokens the iterator on tokens
* @param int $parserContext on of self::PARSER_* constants, indicating
* @param ArrayIterator<int, string|null> $tokens the iterator on tokens
* @param int $parserContext on of self::PARSER_* constants, indicating
* the context where we are in the parsing
*/
private function parseTypes(ArrayIterator $tokens, Context $context, int $parserContext) : Type
{
$types = [];
$token = '';
$compoundToken = '|';
while ($tokens->valid()) {
$token = $tokens->current();
if ($token === null) {
throw new RuntimeException(
'Unexpected nullable character'
);
}
if ($token === '|') {
if ($token === '|' || $token === '&') {
if (count($types) === 0) {
throw new RuntimeException(
'A type is missing before a type separator'
);
}
if ($parserContext !== self::PARSER_IN_COMPOUND
&& $parserContext !== self::PARSER_IN_ARRAY_EXPRESSION
&& $parserContext !== self::PARSER_IN_COLLECTION_EXPRESSION
if (!in_array($parserContext, [
self::PARSER_IN_COMPOUND,
self::PARSER_IN_ARRAY_EXPRESSION,
self::PARSER_IN_COLLECTION_EXPRESSION,
], true)
) {
throw new RuntimeException(
'Unexpected type separator'
);
}
$compoundToken = $token;
$tokens->next();
} elseif ($token === '?') {
if ($parserContext !== self::PARSER_IN_COMPOUND
&& $parserContext !== self::PARSER_IN_ARRAY_EXPRESSION
&& $parserContext !== self::PARSER_IN_COLLECTION_EXPRESSION
if (!in_array($parserContext, [
self::PARSER_IN_COMPOUND,
self::PARSER_IN_ARRAY_EXPRESSION,
self::PARSER_IN_COLLECTION_EXPRESSION,
], true)
) {
throw new RuntimeException(
'Unexpected nullable character'
@@ -196,22 +214,16 @@ final class TypeResolver
$tokens->next();
$type = $this->parseTypes($tokens, $context, self::PARSER_IN_ARRAY_EXPRESSION);
$resolvedType = new Array_($type);
$token = $tokens->current();
// Someone did not properly close their array expression ..
if ($token === null) {
if ($token === null) { // Someone did not properly close their array expression ..
break;
}
// we generate arrays corresponding to the number of '[]' after the ')'
$numberOfArrays = (strlen($token) - 1) / 2;
for ($i = 0; $i < $numberOfArrays - 1; ++$i) {
$resolvedType = new Array_($resolvedType);
}
$tokens->next();
$resolvedType = new Expression($type);
$types[] = $resolvedType;
$tokens->next();
} elseif ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION && $token[0] === ')') {
break;
} elseif ($token === '<') {
@@ -235,6 +247,17 @@ final class TypeResolver
&& ($token === '>' || trim($token) === ',')
) {
break;
} elseif ($token === self::OPERATOR_ARRAY) {
end($types);
$last = key($types);
$lastItem = $types[$last];
if ($lastItem instanceof Expression) {
$lastItem = $lastItem->getValueType();
}
$types[$last] = new Array_($lastItem);
$tokens->next();
} else {
$type = $this->resolveSingleType($token, $context);
$tokens->next();
@@ -246,7 +269,7 @@ final class TypeResolver
}
}
if ($token === '|') {
if ($token === '|' || $token === '&') {
throw new RuntimeException(
'A type is missing after a type separator'
);
@@ -274,7 +297,11 @@ final class TypeResolver
return $types[0];
}
return new Compound($types);
if ($compoundToken === '|') {
return new Compound(array_values($types));
}
return new Intersection(array_values($types));
}
/**
@@ -283,14 +310,14 @@ final class TypeResolver
* @param string $type the type string, representing a single type
*
* @return Type|Array_|Object_
*
* @psalm-mutation-free
*/
private function resolveSingleType(string $type, Context $context)
private function resolveSingleType(string $type, Context $context) : object
{
switch (true) {
case $this->isKeyword($type):
return $this->resolveKeyword($type);
case $this->isTypedArray($type):
return $this->resolveTypedArray($type, $context);
case $this->isFqsen($type):
return $this->resolveTypedObject($type);
case $this->isPartialStructuralElementName($type):
@@ -330,30 +357,24 @@ final class TypeResolver
$this->keywords[$keyword] = $typeClassName;
}
/**
* Detects whether the given type represents an array.
*
* @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
*/
private function isTypedArray(string $type) : bool
{
return substr($type, -2) === self::OPERATOR_ARRAY;
}
/**
* Detects whether the given type represents a PHPDoc keyword.
*
* @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
*
* @psalm-mutation-free
*/
private function isKeyword(string $type) : bool
{
return in_array(strtolower($type), array_keys($this->keywords), true);
return array_key_exists(strtolower($type), $this->keywords);
}
/**
* Detects whether the given type represents a relative structural element name.
*
* @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
*
* @psalm-mutation-free
*/
private function isPartialStructuralElementName(string $type) : bool
{
@@ -362,22 +383,18 @@ final class TypeResolver
/**
* Tests whether the given type is a Fully Qualified Structural Element Name.
*
* @psalm-mutation-free
*/
private function isFqsen(string $type) : bool
{
return strpos($type, self::OPERATOR_NAMESPACE) === 0;
}
/**
* Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set.
*/
private function resolveTypedArray(string $type, Context $context) : Array_
{
return new Array_($this->resolveSingleType(substr($type, 0, -2), $context));
}
/**
* Resolves the given keyword (such as `string`) into a Type object representing that keyword.
*
* @psalm-mutation-free
*/
private function resolveKeyword(string $type) : Type
{
@@ -388,6 +405,8 @@ final class TypeResolver
/**
* Resolves the given FQSEN string into an FQSEN object.
*
* @psalm-mutation-free
*/
private function resolveTypedObject(string $type, ?Context $context = null) : Object_
{
@@ -396,6 +415,8 @@ final class TypeResolver
/**
* Resolves class string
*
* @param ArrayIterator<int, (string|null)> $tokens
*/
private function resolveClassString(ArrayIterator $tokens, Context $context) : Type
{
@@ -409,15 +430,16 @@ final class TypeResolver
);
}
if ($tokens->current() !== '>') {
if (empty($tokens->current())) {
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'class-string: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $tokens->current() . '", ">" is missing'
'Unexpected character "' . $token . '", ">" is missing'
);
}
@@ -427,6 +449,8 @@ final class TypeResolver
/**
* Resolves the collection values and keys
*
* @param ArrayIterator<int, (string|null)> $tokens
*
* @return Array_|Iterable_|Collection
*/
private function resolveCollection(ArrayIterator $tokens, Type $classType, Context $context) : Type
@@ -447,7 +471,8 @@ final class TypeResolver
$valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
$keyType = null;
if ($tokens->current() !== null && trim($tokens->current()) === ',') {
$token = $tokens->current();
if ($token !== null && trim($token) === ',') {
// if we have a comma, then we just parsed the key type, not the value type
$keyType = $valueType;
if ($isArray) {
@@ -480,15 +505,16 @@ final class TypeResolver
$valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
}
if ($tokens->current() !== '>') {
if (empty($tokens->current())) {
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'Collection: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $tokens->current() . '", ">" is missing'
'Unexpected character "' . $token . '", ">" is missing'
);
}
@@ -500,7 +526,6 @@ final class TypeResolver
return new Iterable_($valueType, $keyType);
}
/** @psalm-suppress RedundantCondition */
if ($classType instanceof Object_) {
return $this->makeCollectionFromObject($classType, $valueType, $keyType);
}
@@ -508,6 +533,9 @@ final class TypeResolver
throw new RuntimeException('Invalid $classType provided');
}
/**
* @psalm-pure
*/
private function makeCollectionFromObject(Object_ $object, Type $valueType, ?Type $keyType = null) : Collection
{
return new Collection($object->getFqsen(), $valueType, $keyType);

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Represents a list of values. This is an abstract class for Array_ and Collection.
*
* @psalm-immutable
*/
abstract class AbstractList implements Type
{
@@ -48,11 +50,7 @@ abstract class AbstractList implements Type
*/
public function getKeyType() : Type
{
if ($this->keyType === null) {
return $this->defaultKeyType;
}
return $this->keyType;
return $this->keyType ?? $this->defaultKeyType;
}
/**

View File

@@ -0,0 +1,124 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\Types;
use ArrayIterator;
use IteratorAggregate;
use phpDocumentor\Reflection\Type;
use function array_key_exists;
use function implode;
/**
* Base class for aggregated types like Compound and Intersection
*
* A Aggregated Type is not so much a special keyword or object reference but is a series of Types that are separated
* using separator.
*
* @psalm-immutable
* @template-implements IteratorAggregate<int, Type>
*/
abstract class AggregatedType implements Type, IteratorAggregate
{
/**
* @psalm-allow-private-mutation
* @var array<int, Type>
*/
private $types = [];
/** @var string */
private $token;
/**
* @param array<Type> $types
*/
public function __construct(array $types, string $token)
{
foreach ($types as $type) {
$this->add($type);
}
$this->token = $token;
}
/**
* Returns the type at the given index.
*/
public function get(int $index) : ?Type
{
if (!$this->has($index)) {
return null;
}
return $this->types[$index];
}
/**
* Tests if this compound type has a type with the given index.
*/
public function has(int $index) : bool
{
return array_key_exists($index, $this->types);
}
/**
* Tests if this compound type contains the given type.
*/
public function contains(Type $type) : bool
{
foreach ($this->types as $typePart) {
// if the type is duplicate; do not add it
if ((string) $typePart === (string) $type) {
return true;
}
}
return false;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString() : string
{
return implode($this->token, $this->types);
}
/**
* @return ArrayIterator<int, Type>
*/
public function getIterator() : ArrayIterator
{
return new ArrayIterator($this->types);
}
/**
* @psalm-suppress ImpureMethodCall
*/
private function add(Type $type) : void
{
if ($type instanceof self) {
foreach ($type->getIterator() as $subType) {
$this->add($subType);
}
return;
}
// if the type is duplicate; do not add it
if ($this->contains($type)) {
return;
}
$this->types[] = $type;
}
}

View File

@@ -21,6 +21,8 @@ namespace phpDocumentor\Reflection\Types;
* 1. Untyped (`array`), where the key and value type is unknown and hence classified as 'Mixed_'.
* 2. Types (`string[]`), where the value type is provided by preceding an opening and closing square bracket with a
* type name.
*
* @psalm-immutable
*/
final class Array_ extends AbstractList
{

View File

@@ -17,8 +17,10 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a Boolean type.
*
* @psalm-immutable
*/
final class Boolean implements Type
class Boolean implements Type
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a Callable type.
*
* @psalm-immutable
*/
final class Callable_ implements Type
{

View File

@@ -18,6 +18,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class ClassString implements Type
{

View File

@@ -26,6 +26,8 @@ use phpDocumentor\Reflection\Type;
*
* - ACollectionObject can be 'array' or an object that can act as an array
* - aValueType and aKeyType can be any type expression
*
* @psalm-immutable
*/
final class Collection extends AbstractList
{

View File

@@ -13,10 +13,7 @@ declare(strict_types=1);
namespace phpDocumentor\Reflection\Types;
use ArrayIterator;
use IteratorAggregate;
use phpDocumentor\Reflection\Type;
use function implode;
/**
* Value Object representing a Compound Type.
@@ -24,82 +21,18 @@ use function implode;
* A Compound Type is not so much a special keyword or object reference but is a series of Types that are separated
* using an OR operator (`|`). This combination of types signifies that whatever is associated with this compound type
* may contain a value with any of the given types.
*
* @psalm-immutable
*/
final class Compound implements Type, IteratorAggregate
final class Compound extends AggregatedType
{
/** @var Type[] */
private $types = [];
/**
* Initializes a compound type (i.e. `string|int`) and tests if the provided types all implement the Type interface.
*
* @param Type[] $types
* @param array<Type> $types
*/
public function __construct(array $types)
{
foreach ($types as $type) {
$this->add($type);
}
}
/**
* Returns the type at the given index.
*/
public function get(int $index) : ?Type
{
if (!$this->has($index)) {
return null;
}
return $this->types[$index];
}
/**
* Tests if this compound type has a type with the given index.
*/
public function has(int $index) : bool
{
return isset($this->types[$index]);
}
/**
* Tests if this compound type contains the given type.
*/
public function contains(Type $type) : bool
{
foreach ($this->types as $typePart) {
// if the type is duplicate; do not add it
if ((string) $typePart === (string) $type) {
return true;
}
}
return false;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString() : string
{
return implode('|', $this->types);
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new ArrayIterator($this->types);
}
private function add(Type $type) : void
{
// if the type is duplicate; do not add it
if ($this->contains($type)) {
return;
}
$this->types[] = $type;
parent::__construct($types, '|');
}
}

View File

@@ -30,13 +30,18 @@ use function trim;
*
* @see ContextFactory::createFromClassReflector()
* @see ContextFactory::createForNamespace()
*
* @psalm-immutable
*/
final class Context
{
/** @var string The current namespace. */
private $namespace;
/** @var string[] List of namespace aliases => Fully Qualified Namespace. */
/**
* @var string[] List of namespace aliases => Fully Qualified Namespace.
* @psalm-var array<string, string>
*/
private $namespaceAliases;
/**
@@ -45,6 +50,8 @@ final class Context
*
* @param string $namespace The namespace where this DocBlock resides in.
* @param string[] $namespaceAliases List of namespace aliases => Fully Qualified Namespace.
*
* @psalm-param array<string, string> $namespaceAliases
*/
public function __construct(string $namespace, array $namespaceAliases = [])
{
@@ -80,6 +87,8 @@ final class Context
* the alias for the imported Namespace.
*
* @return string[]
*
* @psalm-return array<string, string>
*/
public function getNamespaceAliases() : array
{

View File

@@ -23,11 +23,15 @@ use ReflectionProperty;
use Reflector;
use RuntimeException;
use UnexpectedValueException;
use function array_merge;
use function define;
use function defined;
use function file_exists;
use function file_get_contents;
use function get_class;
use function in_array;
use function is_string;
use function strrpos;
use function substr;
use function token_get_all;
use function trim;
use const T_AS;
@@ -39,6 +43,14 @@ use const T_NS_SEPARATOR;
use const T_STRING;
use const T_USE;
if (!defined('T_NAME_QUALIFIED')) {
define('T_NAME_QUALIFIED', 'T_NAME_QUALIFIED');
}
if (!defined('T_NAME_FULLY_QUALIFIED')) {
define('T_NAME_FULLY_QUALIFIED', 'T_NAME_FULLY_QUALIFIED');
}
/**
* Convenience class to create a Context for DocBlocks when not using the Reflection Component of phpDocumentor.
*
@@ -64,6 +76,9 @@ final class ContextFactory
public function createFromReflector(Reflector $reflector) : Context
{
if ($reflector instanceof ReflectionClass) {
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @var ReflectionClass<object> $reflector */
return $this->createFromReflectionClass($reflector);
}
@@ -89,28 +104,46 @@ final class ContextFactory
private function createFromReflectionParameter(ReflectionParameter $parameter) : Context
{
$class = $parameter->getDeclaringClass();
if ($class) {
return $this->createFromReflectionClass($class);
if (!$class) {
throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName());
}
throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName());
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @var ReflectionClass<object> $class */
return $this->createFromReflectionClass($class);
}
private function createFromReflectionMethod(ReflectionMethod $method) : Context
{
return $this->createFromReflectionClass($method->getDeclaringClass());
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @var ReflectionClass<object> $class */
$class = $method->getDeclaringClass();
return $this->createFromReflectionClass($class);
}
private function createFromReflectionProperty(ReflectionProperty $property) : Context
{
return $this->createFromReflectionClass($property->getDeclaringClass());
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @var ReflectionClass<object> $class */
$class = $property->getDeclaringClass();
return $this->createFromReflectionClass($class);
}
private function createFromReflectionClassConstant(ReflectionClassConstant $constant) : Context
{
return $this->createFromReflectionClass($constant->getDeclaringClass());
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @var ReflectionClass<object> $class */
$class = $constant->getDeclaringClass();
return $this->createFromReflectionClass($class);
}
/**
* @param ReflectionClass<object> $class
*/
private function createFromReflectionClass(ReflectionClass $class) : Context
{
$fileName = $class->getFileName();
@@ -145,7 +178,8 @@ final class ContextFactory
$tokens = new ArrayIterator(token_get_all($fileContents));
while ($tokens->valid()) {
switch ($tokens->current()[0]) {
$currentToken = $tokens->current();
switch ($currentToken[0]) {
case T_NAMESPACE:
$currentNamespace = $this->parseNamespace($tokens);
break;
@@ -156,9 +190,9 @@ final class ContextFactory
$braceLevel = 0;
$firstBraceFound = false;
while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) {
if ($tokens->current() === '{'
|| $tokens->current()[0] === T_CURLY_OPEN
|| $tokens->current()[0] === T_DOLLAR_OPEN_CURLY_BRACES) {
$currentToken = $tokens->current();
if ($currentToken === '{'
|| in_array($currentToken[0], [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true)) {
if (!$firstBraceFound) {
$firstBraceFound = true;
}
@@ -166,7 +200,7 @@ final class ContextFactory
++$braceLevel;
}
if ($tokens->current() === '}') {
if ($currentToken === '}') {
--$braceLevel;
}
@@ -176,7 +210,7 @@ final class ContextFactory
break;
case T_USE:
if ($currentNamespace === $namespace) {
$useStatements = array_merge($useStatements, $this->parseUseStatement($tokens));
$useStatements += $this->parseUseStatement($tokens);
}
break;
@@ -190,6 +224,8 @@ final class ContextFactory
/**
* Deduce the name from tokens when we are at the T_NAMESPACE token.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*/
private function parseNamespace(ArrayIterator $tokens) : string
{
@@ -197,8 +233,8 @@ final class ContextFactory
$this->skipToNextStringOrNamespaceSeparator($tokens);
$name = '';
while ($tokens->valid() && ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR)
) {
$acceptedTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED];
while ($tokens->valid() && in_array($tokens->current()[0], $acceptedTokens, true)) {
$name .= $tokens->current()[1];
$tokens->next();
}
@@ -209,23 +245,24 @@ final class ContextFactory
/**
* Deduce the names of all imports when we are at the T_USE token.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*
* @return string[]
*
* @psalm-return array<string, string>
*/
private function parseUseStatement(ArrayIterator $tokens) : array
{
$uses = [];
while (true) {
while ($tokens->valid()) {
$this->skipToNextStringOrNamespaceSeparator($tokens);
$uses = array_merge($uses, $this->extractUseStatements($tokens));
if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) {
$uses += $this->extractUseStatements($tokens);
$currentToken = $tokens->current();
if ($currentToken[0] === self::T_LITERAL_END_OF_USE) {
return $uses;
}
if ($tokens->current() === false) {
break;
}
}
return $uses;
@@ -233,10 +270,25 @@ final class ContextFactory
/**
* Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*/
private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens) : void
{
while ($tokens->valid() && ($tokens->current()[0] !== T_STRING) && ($tokens->current()[0] !== T_NS_SEPARATOR)) {
while ($tokens->valid()) {
$currentToken = $tokens->current();
if (in_array($currentToken[0], [T_STRING, T_NS_SEPARATOR], true)) {
break;
}
if ($currentToken[0] === T_NAME_QUALIFIED) {
break;
}
if (defined('T_NAME_FULLY_QUALIFIED') && $currentToken[0] === T_NAME_FULLY_QUALIFIED) {
break;
}
$tokens->next();
}
}
@@ -245,9 +297,13 @@ final class ContextFactory
* Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of
* a USE statement yet. This will return a key/value array of the alias => namespace.
*
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*
* @return string[]
*
* @psalm-suppress TypeDoesNotContainType
*
* @psalm-return array<string, string>
*/
private function extractUseStatements(ArrayIterator $tokens) : array
{
@@ -266,9 +322,17 @@ final class ContextFactory
switch ($tokenId) {
case T_STRING:
case T_NS_SEPARATOR:
$currentNs .= $tokenValue;
$currentNs .= (string) $tokenValue;
$currentAlias = $tokenValue;
break;
case T_NAME_QUALIFIED:
case T_NAME_FULLY_QUALIFIED:
$currentNs .= (string) $tokenValue;
$currentAlias = substr(
(string) $tokenValue,
(int) (strrpos((string) $tokenValue, '\\')) + 1
);
break;
case T_CURLY_OPEN:
case '{':
$state = 'grouped';
@@ -304,17 +368,17 @@ final class ContextFactory
switch ($tokenId) {
case T_STRING:
case T_NS_SEPARATOR:
$currentNs .= $tokenValue;
$currentNs .= (string) $tokenValue;
$currentAlias = $tokenValue;
break;
case T_AS:
$state = 'grouped-alias';
break;
case self::T_LITERAL_USE_SEPARATOR:
$state = 'grouped';
$extractedUseStatements[$currentAlias] = $currentNs;
$currentNs = $groupedNs;
$currentAlias = '';
$state = 'grouped';
$extractedUseStatements[(string) $currentAlias] = $currentNs;
$currentNs = $groupedNs;
$currentAlias = '';
break;
case self::T_LITERAL_END_OF_USE:
$state = 'end';
@@ -330,10 +394,10 @@ final class ContextFactory
$currentAlias = $tokenValue;
break;
case self::T_LITERAL_USE_SEPARATOR:
$state = 'grouped';
$extractedUseStatements[$currentAlias] = $currentNs;
$currentNs = $groupedNs;
$currentAlias = '';
$state = 'grouped';
$extractedUseStatements[(string) $currentAlias] = $currentNs;
$currentNs = $groupedNs;
$currentAlias = '';
break;
case self::T_LITERAL_END_OF_USE:
$state = 'end';
@@ -351,7 +415,7 @@ final class ContextFactory
}
if ($groupedNs !== $currentNs) {
$extractedUseStatements[$currentAlias] = $currentNs;
$extractedUseStatements[(string) $currentAlias] = $currentNs;
}
return $extractedUseStatements;

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\Types;
use phpDocumentor\Reflection\Type;
/**
* Represents an expression type as described in the PSR-5, the PHPDoc Standard.
*
* @psalm-immutable
*/
final class Expression implements Type
{
/** @var Type */
protected $valueType;
/**
* Initializes this representation of an array with the given Type.
*/
public function __construct(Type $valueType)
{
$this->valueType = $valueType;
}
/**
* Returns the value for the keys of this array.
*/
public function getValueType() : Type
{
return $this->valueType;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString() : string
{
return '(' . $this->valueType . ')';
}
}

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a Float.
*
* @psalm-immutable
*/
final class Float_ implements Type
{

View File

@@ -15,6 +15,11 @@ namespace phpDocumentor\Reflection\Types;
use phpDocumentor\Reflection\Type;
/**
* Value object representing Integer type
*
* @psalm-immutable
*/
final class Integer implements Type
{
/**

View File

@@ -0,0 +1,37 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
declare(strict_types=1);
namespace phpDocumentor\Reflection\Types;
use phpDocumentor\Reflection\Type;
/**
* Value Object representing a Compound Type.
*
* A Intersection Type is not so much a special keyword or object reference but is a series of Types that are separated
* using an AND operator (`&`). This combination of types signifies that whatever is associated with this Intersection
* type may contain a value with any of the given types.
*
* @psalm-immutable
*/
final class Intersection extends AggregatedType
{
/**
* Initializes a intersection type (i.e. `\A&\B`) and tests if the provided types all implement the Type interface.
*
* @param array<Type> $types
*/
public function __construct(array $types)
{
parent::__construct($types, '&');
}
}

View File

@@ -15,6 +15,8 @@ namespace phpDocumentor\Reflection\Types;
/**
* Value Object representing iterable type
*
* @psalm-immutable
*/
final class Iterable_ extends AbstractList
{

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing an unknown, or mixed, type.
*
* @psalm-immutable
*/
final class Mixed_ implements Type
{

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a null value or type.
*
* @psalm-immutable
*/
final class Null_ implements Type
{

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a nullable type. The real type is wrapped.
*
* @psalm-immutable
*/
final class Nullable implements Type
{

View File

@@ -24,6 +24,8 @@ use function strpos;
* An object can be either typed or untyped. When an object is typed it means that it has an identifier, the FQSEN,
* pointing to an element in PHP. Object types that are untyped do not refer to a specific class but represent objects
* in general.
*
* @psalm-immutable
*/
final class Object_ implements Type
{

View File

@@ -19,6 +19,8 @@ use phpDocumentor\Reflection\Type;
* Value Object representing the 'parent' type.
*
* Parent, as a Type, represents the parent class of class in which the associated element was defined.
*
* @psalm-immutable
*/
final class Parent_ implements Type
{

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing the 'resource' Type.
*
* @psalm-immutable
*/
final class Resource_ implements Type
{

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing the 'scalar' pseudo-type, which is either a string, integer, float or boolean.
*
* @psalm-immutable
*/
final class Scalar implements Type
{

View File

@@ -19,6 +19,8 @@ use phpDocumentor\Reflection\Type;
* Value Object representing the 'self' type.
*
* Self, as a Type, represents the class in which the associated element was defined.
*
* @psalm-immutable
*/
final class Self_ implements Type
{

View File

@@ -24,6 +24,8 @@ use phpDocumentor\Reflection\Type;
*
* See the documentation on late static binding in the PHP Documentation for more information on the difference between
* static and self.
*
* @psalm-immutable
*/
final class Static_ implements Type
{

View File

@@ -17,6 +17,8 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class String_ implements Type
{

View File

@@ -20,6 +20,8 @@ use phpDocumentor\Reflection\Type;
*
* $this, as a Type, represents the instance of the class associated with the element as it was called. $this is
* commonly used when documenting fluent interfaces since it represents that the same object is returned.
*
* @psalm-immutable
*/
final class This implements Type
{

View File

@@ -20,6 +20,8 @@ use phpDocumentor\Reflection\Type;
*
* Void is generally only used when working with return types as it signifies that the method intentionally does not
* return any value.
*
* @psalm-immutable
*/
final class Void_ implements Type
{