updated-packages

This commit is contained in:
RafficMohammed
2023-01-08 00:13:22 +05:30
parent 3ff7df7487
commit da241bacb6
12659 changed files with 563377 additions and 510538 deletions

View File

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

View File

@@ -0,0 +1,223 @@
on:
push:
branches:
- 2.x
pull_request:
name: Qa workflow
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- 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: Install phive
run: make install-phive
- name: Install PHAR dependencies
run: tools/phive.phar --no-progress install --copy --trust-gpg-keys 4AA394086372C20A,8A03EA3B385DBAA1 --force-accept-unsigned
phpunit-with-coverage:
runs-on: ubuntu-latest
name: Unit tests
needs: setup
steps:
- uses: actions/checkout@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.2
ini-values: memory_limit=2G, display_errors=On, error_reporting=-1
coverage: pcov
- 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 composer dependencies
uses: actions/cache@v1
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ubuntu-latest-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ubuntu-latest-composer-
- name: Install Composer dependencies
run: |
composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader
- 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', '8.0']
name: Unit tests for PHP version ${{ matrix.php-versions }} on ${{ matrix.operating-system }}
needs:
- setup
- phpunit-with-coverage
steps:
- uses: actions/checkout@v2
- 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@v2
with:
php-version: ${{ matrix.php-versions }}
ini-values: memory_limit=2G, display_errors=On, error_reporting=-1
coverage: none
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer 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 Composer dependencies
run: |
composer install --no-progress --no-suggest --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@v2
- 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@latest
with:
args: -s
phpstan:
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@v2
- 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@latest
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@v2
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.2
ini-values: memory_limit=2G, display_errors=On, error_reporting=-1
tools: psalm
coverage: none
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache composer 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 Composer dependencies
run: |
composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader
- name: Psalm
run: psalm --output-format=github
bc_check:
name: BC Check
runs-on: ubuntu-latest
needs: [setup, phpunit]
steps:
- uses: actions/checkout@v2
- 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,35 +0,0 @@
language: php
php:
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm
- nightly
matrix:
allow_failures:
- php:
- hhvm
- nightly
cache:
directories:
- $HOME/.composer/cache
script:
- vendor/bin/phpunit --coverage-clover=coverage.clover -v
- composer update --no-interaction --prefer-source
- vendor/bin/phpunit -v
before_script:
- composer install --no-interaction
after_script:
- if [ $TRAVIS_PHP_VERSION = '5.6' ]; then wget https://scrutinizer-ci.com/ocular.phar; php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi
notifications:
irc: "irc.freenode.org#phpdocumentor"
email:
- me@mikevanriel.com
- ashnazg@php.net

View File

@@ -1,2 +1,11 @@
# ReflectionCommon
[![Build Status](https://travis-ci.org/phpDocumentor/ReflectionCommon.svg?branch=master)](https://travis-ci.org/phpDocumentor/ReflectionCommon)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![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)
[![Stable Version](https://img.shields.io/packagist/v/phpDocumentor/Reflection-Common.svg)](https://packagist.org/packages/phpDocumentor/Reflection-Common)
[![Unstable Version](https://img.shields.io/packagist/vpre/phpDocumentor/Reflection-Common.svg)](https://packagist.org/packages/phpDocumentor/Reflection-Common)
ReflectionCommon
================

View File

@@ -11,19 +11,18 @@
}
],
"require": {
"php": ">=5.5"
"php": "^7.2 || ^8.0"
},
"autoload" : {
"psr-4" : {
"phpDocumentor\\Reflection\\": ["src"]
"phpDocumentor\\Reflection\\": "src/"
}
},
"require-dev": {
"phpunit/phpunit": "^4.6"
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-2.x": "2.x-dev"
}
}
}

View File

@@ -1,11 +1,13 @@
<?php
declare(strict_types=1);
/**
* phpDocumentor
*
* PHP Version 5.5
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -18,15 +20,11 @@ interface Element
{
/**
* Returns the Fqsen of the element.
*
* @return Fqsen
*/
public function getFqsen();
public function getFqsen() : Fqsen;
/**
* Returns the name of the element.
*
* @return string
*/
public function getName();
}
public function getName() : string;
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -19,22 +20,16 @@ interface File
{
/**
* Returns the content of the file as a string.
*
* @return string
*/
public function getContents();
public function getContents() : string;
/**
* Returns md5 hash of the file.
*
* @return string
*/
public function md5();
public function md5() : string;
/**
* Returns an relative path to the file.
*
* @return string
*/
public function path();
public function path() : string;
}

View File

@@ -1,51 +1,60 @@
<?php
declare(strict_types=1);
/**
* phpDocumentor
*
* PHP Version 5.5
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
use InvalidArgumentException;
use function assert;
use function end;
use function explode;
use function is_string;
use function preg_match;
use function sprintf;
use function trim;
/**
* Value Object for Fqsen.
*
* @link https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc-meta.md
*
* @psalm-immutable
*/
final class Fqsen
{
/**
* @var string full quallified class name
*/
/** @var string full quallified class name */
private $fqsen;
/**
* @var string name of the element without path.
*/
/** @var string name of the element without path. */
private $name;
/**
* Initializes the object.
*
* @param string $fqsen
*
* @throws \InvalidArgumentException when $fqsen is not matching the format.
* @throws InvalidArgumentException when $fqsen is not matching the format.
*/
public function __construct($fqsen)
public function __construct(string $fqsen)
{
$matches = array();
$matches = [];
$result = preg_match(
//phpcs:ignore Generic.Files.LineLength.TooLong
'/^\\\\([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff\\\\]*)?(?:[:]{2}\\$?([a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*))?(?:\\(\\))?$/',
$fqsen,
$matches
$fqsen,
$matches
);
if ($result === 0) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf('"%s" is not a valid Fqsen.', $fqsen)
);
}
@@ -56,26 +65,24 @@ final class Fqsen
$this->name = $matches[2];
} else {
$matches = explode('\\', $fqsen);
$this->name = trim(end($matches), '()');
$name = end($matches);
assert(is_string($name));
$this->name = trim($name, '()');
}
}
/**
* converts this class to string.
*
* @return string
*/
public function __toString()
public function __toString() : string
{
return $this->fqsen;
}
/**
* Returns the name of the element without path.
*
* @return string
*/
public function getName()
public function getName() : string
{
return $this->name;
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -14,10 +15,12 @@ namespace phpDocumentor\Reflection;
/**
* The location where an element occurs within a file.
*
* @psalm-immutable
*/
final class Location
{
/** @var int */
/** @var int */
private $lineNumber = 0;
/** @var int */
@@ -25,32 +28,25 @@ final class Location
/**
* Initializes the location for an element using its line number in the file and optionally the column number.
*
* @param int $lineNumber
* @param int $columnNumber
*/
public function __construct($lineNumber, $columnNumber = 0)
public function __construct(int $lineNumber, int $columnNumber = 0)
{
$this->lineNumber = $lineNumber;
$this->lineNumber = $lineNumber;
$this->columnNumber = $columnNumber;
}
/**
* Returns the line number that is covered by this location.
*
* @return integer
*/
public function getLineNumber()
public function getLineNumber() : int
{
return $this->lineNumber;
}
/**
* Returns the column number (character position on a line) for this location object.
*
* @return integer
*/
public function getColumnNumber()
public function getColumnNumber() : int
{
return $this->columnNumber;
}

View File

@@ -1,11 +1,13 @@
<?php
declare(strict_types=1);
/**
* phpDocumentor
*
* PHP Version 5.5
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -18,8 +20,6 @@ interface Project
{
/**
* Returns the name of the project.
*
* @return string
*/
public function getName();
public function getName() : string;
}

View File

@@ -1,13 +1,16 @@
<?php
declare(strict_types=1);
/**
* phpDocumentor
*
* PHP Version 5.5
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel / Naenius (http://www.naenius.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
/**
@@ -19,9 +22,7 @@ interface ProjectFactory
/**
* Creates a project from the set of files.
*
* @param string $name
* @param File[] $files
* @return Project
*/
public function create($name, array $files);
public function create(string $name, array $files) : Project;
}

View File

@@ -1,3 +0,0 @@
service_name: travis-ci
coverage_clover: coverage.xml
json_path: coverage.json

View File

@@ -1,5 +1,13 @@
The ReflectionDocBlock Component [![Build Status](https://secure.travis-ci.org/phpDocumentor/ReflectionDocBlock.png)](https://travis-ci.org/phpDocumentor/ReflectionDocBlock)
================================
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
![Qa workflow](https://github.com/phpDocumentor/ReflectionDocBlock/workflows/Qa%20workflow/badge.svg)
[![Coveralls Coverage](https://img.shields.io/coveralls/github/phpDocumentor/ReflectionDocBlock.svg)](https://coveralls.io/github/phpDocumentor/ReflectionDocBlock?branch=master)
[![Scrutinizer Code Coverage](https://img.shields.io/scrutinizer/coverage/g/phpDocumentor/ReflectionDocBlock.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionDocBlock/?branch=master)
[![Scrutinizer Code Quality](https://img.shields.io/scrutinizer/g/phpDocumentor/ReflectionDocBlock.svg)](https://scrutinizer-ci.com/g/phpDocumentor/ReflectionDocBlock/?branch=master)
[![Stable Version](https://img.shields.io/packagist/v/phpdocumentor/reflection-docblock.svg)](https://packagist.org/packages/phpdocumentor/reflection-docblock)
[![Unstable Version](https://img.shields.io/packagist/vpre/phpdocumentor/reflection-docblock.svg)](https://packagist.org/packages/phpdocumentor/reflection-docblock)
ReflectionDocBlock
==================
Introduction
------------
@@ -27,8 +35,8 @@ instantiated using its `createInstance` factory method like this:
$factory = \phpDocumentor\Reflection\DocBlockFactory::createInstance();
```
Then we can use the `create` method of the factory to interpret the DocBlock.
Please note that it is also possible to provide a class that has the
Then we can use the `create` method of the factory to interpret the DocBlock.
Please note that it is also possible to provide a class that has the
`getDocComment()` method, such as an object of type `ReflectionClass`, the
create method will read that if it exists.

View File

@@ -1,34 +1,42 @@
{
"name": "phpdocumentor/reflection-docblock",
"name": "phpdocumentor/reflection-docblock",
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"type": "library",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
},
{
"name": "Jaap van Otterdijk",
"email": "account@ijaap.nl"
}
],
"require": {
"php": "^7.0",
"phpdocumentor/reflection-common": "^1.0.0",
"phpdocumentor/type-resolver": "^0.4.0",
"webmozart/assert": "^1.0"
},
"autoload": {
"psr-4": {"phpDocumentor\\Reflection\\": ["src/"]}
},
"autoload-dev": {
"psr-4": {"phpDocumentor\\Reflection\\": ["tests/unit"]}
"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.0",
"phpunit/phpunit": "^6.4",
"doctrine/instantiator": "~1.0.5"
"mockery/mockery": "~1.3.2",
"psalm/phar": "^4.8"
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"phpDocumentor\\Reflection\\": ["tests/unit", "tests/integration"]
}
},
"extra": {
"branch-alias": {
"dev-master": "4.x-dev"
"dev-master": "5.x-dev"
}
}
}

View File

@@ -1,31 +0,0 @@
includes:
- temp/ecs/config/clean-code.neon
- temp/ecs/config/psr2-checkers.neon
- temp/ecs/config/spaces.neon
- temp/ecs/config/common.neon
checkers:
PhpCsFixer\Fixer\Operator\ConcatSpaceFixer:
spacing: one
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:
SlevomatCodingStandard\Sniffs\Classes\UnusedPrivateElementsSniff:
# WIP code
- src/DocBlock/StandardTagFactory.php
PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\EmptyStatementSniff:
# WIP code
- src/DocBlock/StandardTagFactory.php
PHP_CodeSniffer\Standards\Squiz\Sniffs\Classes\ValidClassNameSniff:
- src/DocBlock/Tags/Return_.php
- src/DocBlock/Tags/Var_.php
PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff:
- */tests/**

View File

@@ -1,111 +1,96 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\TagWithType;
use Webmozart\Assert\Assert;
final class DocBlock
{
/** @var string The opening line for this docblock. */
private $summary = '';
private $summary;
/** @var DocBlock\Description The actual description for this docblock. */
private $description = null;
private $description;
/** @var Tag[] An array containing all the tags in this docblock; except inline. */
private $tags = [];
/** @var Types\Context Information about the context of this DocBlock. */
private $context = null;
/** @var Types\Context|null Information about the context of this DocBlock. */
private $context;
/** @var Location Information about the location of this DocBlock. */
private $location = null;
/** @var Location|null Information about the location of this DocBlock. */
private $location;
/** @var bool Is this DocBlock (the start of) a template? */
private $isTemplateStart = false;
private $isTemplateStart;
/** @var bool Does this DocBlock signify the end of a DocBlock template? */
private $isTemplateEnd = false;
private $isTemplateEnd;
/**
* @param string $summary
* @param DocBlock\Description $description
* @param DocBlock\Tag[] $tags
* @param Types\Context $context The context in which the DocBlock occurs.
* @param Location $location The location within the file that this DocBlock occurs in.
* @param bool $isTemplateStart
* @param bool $isTemplateEnd
* @param Types\Context $context The context in which the DocBlock occurs.
* @param Location $location The location within the file that this DocBlock occurs in.
*/
public function __construct(
$summary = '',
DocBlock\Description $description = null,
string $summary = '',
?DocBlock\Description $description = null,
array $tags = [],
Types\Context $context = null,
Location $location = null,
$isTemplateStart = false,
$isTemplateEnd = false
?Types\Context $context = null,
?Location $location = null,
bool $isTemplateStart = false,
bool $isTemplateEnd = false
) {
Assert::string($summary);
Assert::boolean($isTemplateStart);
Assert::boolean($isTemplateEnd);
Assert::allIsInstanceOf($tags, Tag::class);
$this->summary = $summary;
$this->summary = $summary;
$this->description = $description ?: new DocBlock\Description('');
foreach ($tags as $tag) {
$this->addTag($tag);
}
$this->context = $context;
$this->context = $context;
$this->location = $location;
$this->isTemplateEnd = $isTemplateEnd;
$this->isTemplateEnd = $isTemplateEnd;
$this->isTemplateStart = $isTemplateStart;
}
/**
* @return string
*/
public function getSummary()
public function getSummary(): string
{
return $this->summary;
}
/**
* @return DocBlock\Description
*/
public function getDescription()
public function getDescription(): DocBlock\Description
{
return $this->description;
}
/**
* Returns the current context.
*
* @return Types\Context
*/
public function getContext()
public function getContext(): ?Types\Context
{
return $this->context;
}
/**
* Returns the current location.
*
* @return Location
*/
public function getLocation()
public function getLocation(): ?Location
{
return $this->location;
}
@@ -128,10 +113,8 @@ final class DocBlock
* elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
*
* @see self::isTemplateEnd() for the check whether a closing marker was provided.
*
* @return boolean
*/
public function isTemplateStart()
public function isTemplateStart(): bool
{
return $this->isTemplateStart;
}
@@ -140,10 +123,8 @@ final class DocBlock
* Returns whether this DocBlock is the end of a Template section.
*
* @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
*
* @return boolean
*/
public function isTemplateEnd()
public function isTemplateEnd(): bool
{
return $this->isTemplateEnd;
}
@@ -153,7 +134,7 @@ final class DocBlock
*
* @return Tag[]
*/
public function getTags()
public function getTags(): array
{
return $this->tags;
}
@@ -166,13 +147,10 @@ final class DocBlock
*
* @return Tag[]
*/
public function getTagsByName($name)
public function getTagsByName(string $name): array
{
Assert::string($name);
$result = [];
/** @var Tag $tag */
foreach ($this->getTags() as $tag) {
if ($tag->getName() !== $name) {
continue;
@@ -184,18 +162,36 @@ final class DocBlock
return $result;
}
/**
* Returns an array of tags with type matching the given name. If no tags are found
* an empty array is returned.
*
* @param string $name String to search by.
*
* @return TagWithType[]
*/
public function getTagsWithTypeByName(string $name): array
{
$result = [];
foreach ($this->getTagsByName($name) as $tag) {
if (!$tag instanceof TagWithType) {
continue;
}
$result[] = $tag;
}
return $result;
}
/**
* Checks if a tag of a certain type is present in this DocBlock.
*
* @param string $name Tag name to check for.
*
* @return bool
*/
public function hasTag($name)
public function hasTag(string $name): bool
{
Assert::string($name);
/** @var Tag $tag */
foreach ($this->getTags() as $tag) {
if ($tag->getName() === $name) {
return true;
@@ -208,11 +204,9 @@ final class DocBlock
/**
* Remove a tag from this DocBlock.
*
* @param Tag $tag The tag to remove.
*
* @return void
* @param Tag $tagToRemove The tag to remove.
*/
public function removeTag(Tag $tagToRemove)
public function removeTag(Tag $tagToRemove): void
{
foreach ($this->tags as $key => $tag) {
if ($tag === $tagToRemove) {
@@ -226,10 +220,8 @@ final class DocBlock
* Adds a tag to this DocBlock.
*
* @param Tag $tag The tag to add.
*
* @return void
*/
private function addTag(Tag $tag)
private function addTag(Tag $tag): void
{
$this->tags[] = $tag;
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -14,7 +15,8 @@ namespace phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter;
use Webmozart\Assert\Assert;
use function vsprintf;
/**
* Object representing to description for a DocBlock.
@@ -59,15 +61,20 @@ class Description
/**
* Initializes a Description with its body (template) and a listing of the tags used in the body template.
*
* @param string $bodyTemplate
* @param Tag[] $tags
*/
public function __construct($bodyTemplate, array $tags = [])
public function __construct(string $bodyTemplate, array $tags = [])
{
Assert::string($bodyTemplate);
$this->bodyTemplate = $bodyTemplate;
$this->tags = $tags;
$this->tags = $tags;
}
/**
* Returns the body template.
*/
public function getBodyTemplate(): string
{
return $this->bodyTemplate;
}
/**
@@ -75,7 +82,7 @@ class Description
*
* @return Tag[]
*/
public function getTags()
public function getTags(): array
{
return $this->tags;
}
@@ -83,12 +90,8 @@ class Description
/**
* Renders this description as a string where the provided formatter will format the tags in the expected string
* format.
*
* @param Formatter|null $formatter
*
* @return string
*/
public function render(Formatter $formatter = null)
public function render(?Formatter $formatter = null): string
{
if ($formatter === null) {
$formatter = new PassthroughFormatter();
@@ -104,10 +107,8 @@ class Description
/**
* Returns a plain string representation of this description.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->render();
}

View File

@@ -1,18 +1,32 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use function count;
use function implode;
use function ltrim;
use function min;
use function str_replace;
use function strlen;
use function strpos;
use function substr;
use function trim;
use const PREG_SPLIT_DELIM_CAPTURE;
/**
* Creates a new Description object given a body of text.
@@ -38,8 +52,6 @@ class DescriptionFactory
/**
* Initializes this factory with the means to construct (inline) tags.
*
* @param TagFactory $tagFactory
*/
public function __construct(TagFactory $tagFactory)
{
@@ -48,27 +60,36 @@ class DescriptionFactory
/**
* Returns the parsed text of this description.
*
* @param string $contents
* @param TypeContext $context
*
* @return Description
*/
public function create($contents, TypeContext $context = null)
public function create(string $contents, ?TypeContext $context = null): Description
{
list($text, $tags) = $this->parse($this->lex($contents), $context);
$tokens = $this->lex($contents);
$count = count($tokens);
$tagCount = 0;
$tags = [];
return new Description($text, $tags);
for ($i = 1; $i < $count; $i += 2) {
$tags[] = $this->tagFactory->create($tokens[$i], $context);
$tokens[$i] = '%' . ++$tagCount . '$s';
}
//In order to allow "literal" inline tags, the otherwise invalid
//sequence "{@}" is changed to "@", and "{}" is changed to "}".
//"%" is escaped to "%%" because of vsprintf.
//See unit tests for examples.
for ($i = 0; $i < $count; $i += 2) {
$tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]);
}
return new Description(implode('', $tokens), $tags);
}
/**
* Strips the contents from superfluous whitespace and splits the description into a series of tokens.
*
* @param string $contents
*
* @return string[] A series of tokens of which the description text is composed.
*/
private function lex($contents)
private function lex(string $contents): array
{
$contents = $this->removeSuperfluousStartingWhitespace($contents);
@@ -77,7 +98,7 @@ class DescriptionFactory
return [$contents];
}
return 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.
(?!@\})
@@ -103,41 +124,11 @@ class DescriptionFactory
)
\}/Sux',
$contents,
null,
0,
PREG_SPLIT_DELIM_CAPTURE
);
}
/**
* Parses the stream of tokens in to a new set of tokens containing Tags.
*
* @param string[] $tokens
* @param TypeContext $context
*
* @return string[]|Tag[]
*/
private function parse($tokens, TypeContext $context)
{
$count = count($tokens);
$tagCount = 0;
$tags = [];
for ($i = 1; $i < $count; $i += 2) {
$tags[] = $this->tagFactory->create($tokens[$i], $context);
$tokens[$i] = '%' . ++$tagCount . '$s';
}
//In order to allow "literal" inline tags, the otherwise invalid
//sequence "{@}" is changed to "@", and "{}" is changed to "}".
//"%" is escaped to "%%" because of vsprintf.
//See unit tests for examples.
for ($i = 0; $i < $count; $i += 2) {
$tokens[$i] = str_replace(['{@}', '{}', '%'], ['@', '}', '%%'], $tokens[$i]);
}
return [implode('', $tokens), $tags];
}
/**
* Removes the superfluous from a multi-line description.
*
@@ -151,14 +142,10 @@ class DescriptionFactory
*
* If we do not normalize the indentation then we have superfluous whitespace on the second and subsequent
* lines and this may cause rendering issues when, for example, using a Markdown converter.
*
* @param string $contents
*
* @return string
*/
private function removeSuperfluousStartingWhitespace($contents)
private function removeSuperfluousStartingWhitespace(string $contents): string
{
$lines = explode("\n", $contents);
$lines = Utils::pregSplit("/\r\n?|\n/", $contents);
// if there is only one line then we don't have lines with superfluous whitespace and
// can use the contents as-is
@@ -168,9 +155,9 @@ class DescriptionFactory
// determine how many whitespace characters need to be stripped
$startingSpaceCount = 9999999;
for ($i = 1; $i < count($lines); $i++) {
for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) {
// lines with a no length do not count as they are not indented at all
if (strlen(trim($lines[$i])) === 0) {
if (trim($lines[$i]) === '') {
continue;
}
@@ -181,7 +168,7 @@ class DescriptionFactory
// strip the number of spaces from each line
if ($startingSpaceCount > 0) {
for ($i = 1; $i < count($lines); $i++) {
for ($i = 1, $iMax = count($lines); $i < $iMax; ++$i) {
$lines[$i] = substr($lines[$i], $startingSpaceCount);
}
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -14,6 +15,17 @@ namespace phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Tags\Example;
use function array_slice;
use function file;
use function getcwd;
use function implode;
use function is_readable;
use function rtrim;
use function sprintf;
use function trim;
use const DIRECTORY_SEPARATOR;
/**
* Class used to find an example file's location based on a given ExampleDescriptor.
*/
@@ -27,18 +39,14 @@ class ExampleFinder
/**
* Attempts to find the example contents for the given descriptor.
*
* @param Example $example
*
* @return string
*/
public function find(Example $example)
public function find(Example $example): string
{
$filename = $example->getFilePath();
$file = $this->getExampleFileContents($filename);
if (!$file) {
return "** File not found : {$filename} **";
return sprintf('** File not found : %s **', $filename);
}
return implode('', array_slice($file, $example->getStartingLine() - 1, $example->getLineCount()));
@@ -46,22 +54,16 @@ class ExampleFinder
/**
* Registers the project's root directory where an 'examples' folder can be expected.
*
* @param string $directory
*
* @return void
*/
public function setSourceDirectory($directory = '')
public function setSourceDirectory(string $directory = ''): void
{
$this->sourceDirectory = $directory;
}
/**
* Returns the project's root directory where an 'examples' folder can be expected.
*
* @return string
*/
public function getSourceDirectory()
public function getSourceDirectory(): string
{
return $this->sourceDirectory;
}
@@ -71,7 +73,7 @@ class ExampleFinder
*
* @param string[] $directories
*/
public function setExampleDirectories(array $directories)
public function setExampleDirectories(array $directories): void
{
$this->exampleDirectories = $directories;
}
@@ -81,7 +83,7 @@ class ExampleFinder
*
* @return string[]
*/
public function getExampleDirectories()
public function getExampleDirectories(): array
{
return $this->exampleDirectories;
}
@@ -97,11 +99,9 @@ class ExampleFinder
* 3. Checks the 'examples' folder in the current working directory for examples
* 4. Checks the path relative to the current working directory for the given filename
*
* @param string $filename
*
* @return string|null
* @return string[] all lines of the example file
*/
private function getExampleFileContents($filename)
private function getExampleFileContents(string $filename): ?array
{
$normalizedPath = null;
@@ -123,42 +123,31 @@ class ExampleFinder
}
}
return $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : null;
$lines = $normalizedPath && is_readable($normalizedPath) ? file($normalizedPath) : false;
return $lines !== false ? $lines : null;
}
/**
* Get example filepath based on the example directory inside your project.
*
* @param string $file
*
* @return string
*/
private function getExamplePathFromExampleDirectory($file)
private function getExamplePathFromExampleDirectory(string $file): string
{
return getcwd() . DIRECTORY_SEPARATOR . 'examples' . DIRECTORY_SEPARATOR . $file;
}
/**
* Returns a path to the example file in the given directory..
*
* @param string $directory
* @param string $file
*
* @return string
*/
private function constructExamplePath($directory, $file)
private function constructExamplePath(string $directory, string $file): string
{
return rtrim($directory, '\\/') . DIRECTORY_SEPARATOR . $file;
}
/**
* Get example filepath based on sourcecode.
*
* @param string $file
*
* @return string
*/
private function getExamplePathFromSource($file)
private function getExamplePathFromSource(string $file): string
{
return sprintf(
'%s%s%s',

View File

@@ -1,19 +1,27 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock;
use Webmozart\Assert\Assert;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter;
use function sprintf;
use function str_repeat;
use function str_replace;
use function strlen;
use function wordwrap;
/**
* Converts a DocBlock back from an object to a complete DocComment including Asterisks.
@@ -30,33 +38,37 @@ class Serializer
protected $isFirstLineIndented = true;
/** @var int|null The max length of a line. */
protected $lineLength = null;
protected $lineLength;
/** @var DocBlock\Tags\Formatter A custom tag formatter. */
protected $tagFormatter = null;
/** @var Formatter A custom tag formatter. */
protected $tagFormatter;
/** @var string */
private $lineEnding;
/**
* Create a Serializer instance.
*
* @param int $indent The number of times the indent string is repeated.
* @param string $indentString The string to indent the comment with.
* @param bool $indentFirstLine Whether to indent the first line.
* @param int|null $lineLength The max length of a line or NULL to disable line wrapping.
* @param DocBlock\Tags\Formatter $tagFormatter A custom tag formatter, defaults to PassthroughFormatter.
* @param int $indent The number of times the indent string is repeated.
* @param string $indentString The string to indent the comment with.
* @param bool $indentFirstLine Whether to indent the first line.
* @param int|null $lineLength The max length of a line or NULL to disable line wrapping.
* @param Formatter $tagFormatter A custom tag formatter, defaults to PassthroughFormatter.
* @param string $lineEnding Line ending used in the output, by default \n is used.
*/
public function __construct($indent = 0, $indentString = ' ', $indentFirstLine = true, $lineLength = null, $tagFormatter = null)
{
Assert::integer($indent);
Assert::string($indentString);
Assert::boolean($indentFirstLine);
Assert::nullOrInteger($lineLength);
Assert::nullOrIsInstanceOf($tagFormatter, 'phpDocumentor\Reflection\DocBlock\Tags\Formatter');
$this->indent = $indent;
$this->indentString = $indentString;
public function __construct(
int $indent = 0,
string $indentString = ' ',
bool $indentFirstLine = true,
?int $lineLength = null,
?Formatter $tagFormatter = null,
string $lineEnding = "\n"
) {
$this->indent = $indent;
$this->indentString = $indentString;
$this->isFirstLineIndented = $indentFirstLine;
$this->lineLength = $lineLength;
$this->tagFormatter = $tagFormatter ?: new DocBlock\Tags\Formatter\PassthroughFormatter();
$this->lineLength = $lineLength;
$this->tagFormatter = $tagFormatter ?: new PassthroughFormatter();
$this->lineEnding = $lineEnding;
}
/**
@@ -66,9 +78,9 @@ class Serializer
*
* @return string The serialized doc block.
*/
public function getDocComment(DocBlock $docblock)
public function getDocComment(DocBlock $docblock): string
{
$indent = str_repeat($this->indentString, $this->indent);
$indent = str_repeat($this->indentString, $this->indent);
$firstIndent = $this->isFirstLineIndented ? $indent : '';
// 3 === strlen(' * ')
$wrapLength = $this->lineLength ? $this->lineLength - strlen($indent) - 3 : null;
@@ -81,63 +93,49 @@ class Serializer
)
);
$comment = "{$firstIndent}/**\n";
$comment = $firstIndent . "/**\n";
if ($text) {
$comment .= "{$indent} * {$text}\n";
$comment .= "{$indent} *\n";
$comment .= $indent . ' * ' . $text . "\n";
$comment .= $indent . " *\n";
}
$comment = $this->addTagBlock($docblock, $wrapLength, $indent, $comment);
$comment .= $indent . ' */';
return $comment;
return str_replace("\n", $this->lineEnding, $comment . $indent . ' */');
}
/**
* @param $indent
* @param $text
* @return mixed
*/
private function removeTrailingSpaces($indent, $text)
private function removeTrailingSpaces(string $indent, string $text): string
{
return str_replace("\n{$indent} * \n", "\n{$indent} *\n", $text);
return str_replace(
sprintf("\n%s * \n", $indent),
sprintf("\n%s *\n", $indent),
$text
);
}
/**
* @param $indent
* @param $text
* @return mixed
*/
private function addAsterisksForEachLine($indent, $text)
private function addAsterisksForEachLine(string $indent, string $text): string
{
return str_replace("\n", "\n{$indent} * ", $text);
return str_replace(
"\n",
sprintf("\n%s * ", $indent),
$text
);
}
/**
* @param DocBlock $docblock
* @param $wrapLength
* @return string
*/
private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength)
private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, ?int $wrapLength): string
{
$text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription()
$text = $docblock->getSummary() . ((string) $docblock->getDescription() ? "\n\n" . $docblock->getDescription()
: '');
if ($wrapLength !== null) {
$text = wordwrap($text, $wrapLength);
return $text;
}
return $text;
}
/**
* @param DocBlock $docblock
* @param $wrapLength
* @param $indent
* @param $comment
* @return string
*/
private function addTagBlock(DocBlock $docblock, $wrapLength, $indent, $comment)
private function addTagBlock(DocBlock $docblock, ?int $wrapLength, string $indent, string $comment): string
{
foreach ($docblock->getTags() as $tag) {
$tagText = $this->tagFormatter->format($tag);
@@ -145,9 +143,13 @@ class Serializer
$tagText = wordwrap($tagText, $wrapLength);
}
$tagText = str_replace("\n", "\n{$indent} * ", $tagText);
$tagText = str_replace(
"\n",
sprintf("\n%s * ", $indent),
$tagText
);
$comment .= "{$indent} * {$tagText}\n";
$comment .= sprintf("%s * %s\n", $indent, $tagText);
}
return $comment;

View File

@@ -1,23 +1,54 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
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\Generic;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag;
use phpDocumentor\Reflection\DocBlock\Tags\Method;
use phpDocumentor\Reflection\DocBlock\Tags\Param;
use phpDocumentor\Reflection\DocBlock\Tags\Property;
use phpDocumentor\Reflection\DocBlock\Tags\PropertyRead;
use phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite;
use phpDocumentor\Reflection\DocBlock\Tags\Return_;
use phpDocumentor\Reflection\DocBlock\Tags\See as SeeTag;
use phpDocumentor\Reflection\DocBlock\Tags\Since;
use phpDocumentor\Reflection\DocBlock\Tags\Source;
use phpDocumentor\Reflection\DocBlock\Tags\Throws;
use phpDocumentor\Reflection\DocBlock\Tags\Uses;
use phpDocumentor\Reflection\DocBlock\Tags\Var_;
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;
use function array_slice;
use function call_user_func_array;
use function count;
use function get_class;
use function preg_match;
use function strpos;
use function trim;
/**
* Creates a Tag object given the contents of a tag.
*
@@ -38,41 +69,47 @@ use Webmozart\Assert\Assert;
final class StandardTagFactory implements TagFactory
{
/** PCRE regular expression matching a tag name. */
const REGEX_TAGNAME = '[\w\-\_\\\\]+';
public const REGEX_TAGNAME = '[\w\-\_\\\\:]+';
/**
* @var string[] An array with a tag as a key, and an FQCN to a class that handles it as an array value.
* @var array<class-string<Tag>> An array with a tag as a key, and an
* FQCN to a class that handles it as an array value.
*/
private $tagHandlerMappings = [
'author' => '\phpDocumentor\Reflection\DocBlock\Tags\Author',
'covers' => '\phpDocumentor\Reflection\DocBlock\Tags\Covers',
'deprecated' => '\phpDocumentor\Reflection\DocBlock\Tags\Deprecated',
'author' => Author::class,
'covers' => Covers::class,
'deprecated' => Deprecated::class,
// 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example',
'link' => '\phpDocumentor\Reflection\DocBlock\Tags\Link',
'method' => '\phpDocumentor\Reflection\DocBlock\Tags\Method',
'param' => '\phpDocumentor\Reflection\DocBlock\Tags\Param',
'property-read' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyRead',
'property' => '\phpDocumentor\Reflection\DocBlock\Tags\Property',
'property-write' => '\phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite',
'return' => '\phpDocumentor\Reflection\DocBlock\Tags\Return_',
'see' => '\phpDocumentor\Reflection\DocBlock\Tags\See',
'since' => '\phpDocumentor\Reflection\DocBlock\Tags\Since',
'source' => '\phpDocumentor\Reflection\DocBlock\Tags\Source',
'throw' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
'throws' => '\phpDocumentor\Reflection\DocBlock\Tags\Throws',
'uses' => '\phpDocumentor\Reflection\DocBlock\Tags\Uses',
'var' => '\phpDocumentor\Reflection\DocBlock\Tags\Var_',
'version' => '\phpDocumentor\Reflection\DocBlock\Tags\Version'
'link' => LinkTag::class,
'method' => Method::class,
'param' => Param::class,
'property-read' => PropertyRead::class,
'property' => Property::class,
'property-write' => PropertyWrite::class,
'return' => Return_::class,
'see' => SeeTag::class,
'since' => Since::class,
'source' => Source::class,
'throw' => Throws::class,
'throws' => Throws::class,
'uses' => Uses::class,
'var' => Var_::class,
'version' => Version::class,
];
/**
* @var \ReflectionParameter[][] a lazy-loading cache containing parameters for each tagHandler that has been used.
* @var array<class-string<Tag>> An array with a anotation s a key, and an
* FQCN to a class that handles it as an array value.
*/
private $annotationMappings = [];
/**
* @var ReflectionParameter[][] a lazy-loading cache containing parameters
* for each tagHandler that has been used.
*/
private $tagHandlerParameterCache = [];
/**
* @var FqsenResolver
*/
/** @var FqsenResolver */
private $fqsenResolver;
/**
@@ -87,12 +124,11 @@ final class StandardTagFactory implements TagFactory
* If no tag handlers are provided than the default list in the {@see self::$tagHandlerMappings} property
* is used.
*
* @param FqsenResolver $fqsenResolver
* @param string[] $tagHandlers
*
* @see self::registerTagHandler() to add a new tag handler to the existing default list.
*
* @param array<class-string<Tag>> $tagHandlers
*/
public function __construct(FqsenResolver $fqsenResolver, array $tagHandlers = null)
public function __construct(FqsenResolver $fqsenResolver, ?array $tagHandlers = null)
{
$this->fqsenResolver = $fqsenResolver;
if ($tagHandlers !== null) {
@@ -102,54 +138,38 @@ final class StandardTagFactory implements TagFactory
$this->addService($fqsenResolver, FqsenResolver::class);
}
/**
* {@inheritDoc}
*/
public function create($tagLine, TypeContext $context = null)
public function create(string $tagLine, ?TypeContext $context = null): Tag
{
if (! $context) {
if (!$context) {
$context = new TypeContext('');
}
list($tagName, $tagBody) = $this->extractTagParts($tagLine);
[$tagName, $tagBody] = $this->extractTagParts($tagLine);
if ($tagBody !== '' && $tagBody[0] === '[') {
throw new \InvalidArgumentException(
'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
);
}
return $this->createTag($tagBody, $tagName, $context);
return $this->createTag(trim($tagBody), $tagName, $context);
}
/**
* {@inheritDoc}
* @param mixed $value
*/
public function addParameter($name, $value)
public function addParameter(string $name, $value): void
{
$this->serviceLocator[$name] = $value;
}
/**
* {@inheritDoc}
*/
public function addService($service, $alias = null)
public function addService(object $service, ?string $alias = null): void
{
$this->serviceLocator[$alias ?: get_class($service)] = $service;
}
/**
* {@inheritDoc}
*/
public function registerTagHandler($tagName, $handler)
public function registerTagHandler(string $tagName, string $handler): void
{
Assert::stringNotEmpty($tagName);
Assert::stringNotEmpty($handler);
Assert::classExists($handler);
Assert::implementsInterface($handler, StaticMethod::class);
Assert::implementsInterface($handler, Tag::class);
if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'A namespaced tag must have a leading backslash as it must be fully qualified'
);
}
@@ -160,15 +180,13 @@ final class StandardTagFactory implements TagFactory
/**
* Extracts all components for a tag.
*
* @param string $tagLine
*
* @return string[]
*/
private function extractTagParts($tagLine)
private function extractTagParts(string $tagLine): array
{
$matches = [];
if (! preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)/us', $tagLine, $matches)) {
throw new \InvalidArgumentException(
if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\s\(\{])\s*([^\s].*)|$)/us', $tagLine, $matches)) {
throw new InvalidArgumentException(
'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
);
}
@@ -183,14 +201,8 @@ final class StandardTagFactory implements TagFactory
/**
* Creates a new tag object with the given name and body or returns null if the tag name was recognized but the
* body was invalid.
*
* @param string $body
* @param string $name
* @param TypeContext $context
*
* @return Tag|null
*/
private function createTag($body, $name, TypeContext $context)
private function createTag(string $body, string $name, TypeContext $context): Tag
{
$handlerClassName = $this->findHandlerClassName($name, $context);
$arguments = $this->getArgumentsForParametersFromWiring(
@@ -198,28 +210,34 @@ final class StandardTagFactory implements TagFactory
$this->getServiceLocatorWithDynamicParameters($context, $name, $body)
);
return call_user_func_array([$handlerClassName, 'create'], $arguments);
try {
$callable = [$handlerClassName, 'create'];
Assert::isCallable($callable);
/** @phpstan-var callable(string): ?Tag $callable */
$tag = call_user_func_array($callable, $arguments);
return $tag ?? InvalidTag::create($body, $name);
} catch (InvalidArgumentException $e) {
return InvalidTag::create($body, $name)->withError($e);
}
}
/**
* Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
*
* @param string $tagName
* @param TypeContext $context
*
* @return string
* @return class-string<Tag>
*/
private function findHandlerClassName($tagName, TypeContext $context)
private function findHandlerClassName(string $tagName, TypeContext $context): string
{
$handlerClassName = Generic::class;
if (isset($this->tagHandlerMappings[$tagName])) {
$handlerClassName = $this->tagHandlerMappings[$tagName];
} elseif ($this->isAnnotation($tagName)) {
// TODO: Annotation support is planned for a later stage and as such is disabled for now
// $tagName = (string)$this->fqsenResolver->resolve($tagName, $context);
// if (isset($this->annotationMappings[$tagName])) {
// $handlerClassName = $this->annotationMappings[$tagName];
// }
$tagName = (string) $this->fqsenResolver->resolve($tagName, $context);
if (isset($this->annotationMappings[$tagName])) {
$handlerClassName = $this->annotationMappings[$tagName];
}
}
return $handlerClassName;
@@ -228,17 +246,28 @@ final class StandardTagFactory implements TagFactory
/**
* Retrieves the arguments that need to be passed to the Factory Method with the given Parameters.
*
* @param \ReflectionParameter[] $parameters
* @param mixed[] $locator
* @param ReflectionParameter[] $parameters
* @param mixed[] $locator
*
* @return mixed[] A series of values that can be passed to the Factory Method of the tag whose parameters
* is provided with this method.
*/
private function getArgumentsForParametersFromWiring($parameters, $locator)
private function getArgumentsForParametersFromWiring(array $parameters, array $locator): array
{
$arguments = [];
foreach ($parameters as $index => $parameter) {
$typeHint = $parameter->getClass() ? $parameter->getClass()->getName() : null;
foreach ($parameters as $parameter) {
$type = $parameter->getType();
$typeHint = null;
if ($type instanceof ReflectionNamedType) {
$typeHint = $type->getName();
if ($typeHint === 'self') {
$declaringClass = $parameter->getDeclaringClass();
if ($declaringClass !== null) {
$typeHint = $declaringClass->getName();
}
}
}
if (isset($locator[$typeHint])) {
$arguments[] = $locator[$typeHint];
continue;
@@ -260,14 +289,14 @@ final class StandardTagFactory implements TagFactory
* Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
* tag handler class name.
*
* @param string $handlerClassName
* @param class-string $handlerClassName
*
* @return \ReflectionParameter[]
* @return ReflectionParameter[]
*/
private function fetchParametersForHandlerFactoryMethod($handlerClassName)
private function fetchParametersForHandlerFactoryMethod(string $handlerClassName): array
{
if (! isset($this->tagHandlerParameterCache[$handlerClassName])) {
$methodReflection = new \ReflectionMethod($handlerClassName, 'create');
if (!isset($this->tagHandlerParameterCache[$handlerClassName])) {
$methodReflection = new ReflectionMethod($handlerClassName, 'create');
$this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
}
@@ -275,39 +304,39 @@ final class StandardTagFactory implements TagFactory
}
/**
* Returns a copy of this class' Service Locator with added dynamic parameters, such as the tag's name, body and
* Context.
* Returns a copy of this class' Service Locator with added dynamic parameters,
* such as the tag's name, body and Context.
*
* @param TypeContext $context The Context (namespace and aliasses) that may be passed and is used to resolve FQSENs.
* @param string $tagName The name of the tag that may be passed onto the factory method of the Tag class.
* @param string $tagBody The body of the tag that may be passed onto the factory method of the Tag class.
* @param TypeContext $context The Context (namespace and aliasses) that may be
* passed and is used to resolve FQSENs.
* @param string $tagName The name of the tag that may be
* passed onto the factory method of the Tag class.
* @param string $tagBody The body of the tag that may be
* passed onto the factory method of the Tag class.
*
* @return mixed[]
*/
private function getServiceLocatorWithDynamicParameters(TypeContext $context, $tagName, $tagBody)
{
$locator = array_merge(
private function getServiceLocatorWithDynamicParameters(
TypeContext $context,
string $tagName,
string $tagBody
): array {
return array_merge(
$this->serviceLocator,
[
'name' => $tagName,
'body' => $tagBody,
TypeContext::class => $context
'name' => $tagName,
'body' => $tagBody,
TypeContext::class => $context,
]
);
return $locator;
}
/**
* Returns whether the given tag belongs to an annotation.
*
* @param string $tagContent
*
* @todo this method should be populated once we implement Annotation notation support.
*
* @return bool
*/
private function isAnnotation($tagContent)
private function isAnnotation(string $tagContent): bool
{
// 1. Contains a namespace separator
// 2. Contains parenthesis

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,11 +17,15 @@ use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
interface Tag
{
public function getName();
public function getName(): string;
public static function create($body);
/**
* @return Tag|mixed Class that implements Tag
* @phpstan-return ?Tag
*/
public static function create(string $body);
public function render(Formatter $formatter = null);
public function render(?Formatter $formatter = null): string;
public function __toString();
public function __toString(): string;
}

View File

@@ -1,17 +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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock;
use InvalidArgumentException;
use phpDocumentor\Reflection\Types\Context as TypeContext;
interface TagFactory
@@ -34,12 +36,20 @@ interface TagFactory
*
* These parameters are injected at the last moment and will override any existing parameter with those names.
*
* @param string $name
* @param mixed $value
*
* @return void
* @param mixed $value
*/
public function addParameter($name, $value);
public function addParameter(string $name, $value): void;
/**
* Factory method responsible for instantiating the correct sub type.
*
* @param string $tagLine The text for this tag, including description.
*
* @return Tag A new tag object.
*
* @throws InvalidArgumentException If an invalid tag line was presented.
*/
public function create(string $tagLine, ?TypeContext $context = null): Tag;
/**
* Registers a service with the Service Locator using the FQCN of the class or the alias, if provided.
@@ -49,45 +59,26 @@ interface TagFactory
*
* Because interfaces are regularly used as type-hints this method provides an alias parameter; if the FQCN of the
* interface is passed as alias then every time that interface is requested the provided service will be returned.
*
* @param object $service
* @param string $alias
*
* @return void
*/
public function addService($service);
/**
* Factory method responsible for instantiating the correct sub type.
*
* @param string $tagLine The text for this tag, including description.
* @param TypeContext $context
*
* @throws \InvalidArgumentException if an invalid tag line was presented.
*
* @return Tag A new tag object.
*/
public function create($tagLine, TypeContext $context = null);
public function addService(object $service): void;
/**
* Registers a handler for tags.
*
* If you want to use your own tags then you can use this method to instruct the TagFactory to register the name
* of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement the {@see Tag} interface (and thus
* the create method).
* If you want to use your own tags then you can use this method to instruct the TagFactory
* to register the name of a tag with the FQCN of a 'Tag Handler'. The Tag handler should implement
* the {@see Tag} interface (and thus the create method).
*
* @param string $tagName Name of tag to register a handler for. When registering a namespaced tag, the full
* name, along with a prefixing slash MUST be provided.
* @param string $handler FQCN of handler.
* @param string $tagName Name of tag to register a handler for. When registering a namespaced
* tag, the full name, along with a prefixing slash MUST be provided.
* @param class-string<Tag> $handler FQCN of handler.
*
* @throws \InvalidArgumentException if the tag name is not a string
* @throws \InvalidArgumentException if the tag name is namespaced (contains backslashes) but does not start with
* a backslash
* @throws \InvalidArgumentException if the handler is not a string
* @throws \InvalidArgumentException if the handler is not an existing class
* @throws \InvalidArgumentException if the handler does not implement the {@see Tag} interface
*
* @return void
* @throws InvalidArgumentException If the tag name is not a string.
* @throws InvalidArgumentException If the tag name is namespaced (contains backslashes) but
* does not start with a backslash.
* @throws InvalidArgumentException If the handler is not a string.
* @throws InvalidArgumentException If the handler is not an existing class.
* @throws InvalidArgumentException If the handler does not implement the {@see Tag} interface.
*/
public function registerTagHandler($tagName, $handler);
public function registerTagHandler(string $tagName, string $handler): void;
}

View File

@@ -1,18 +1,25 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
use Webmozart\Assert\Assert;
use InvalidArgumentException;
use function filter_var;
use function preg_match;
use function trim;
use const FILTER_VALIDATE_EMAIL;
/**
* Reflection class for an {@}author tag in a Docblock.
@@ -23,23 +30,18 @@ final class Author extends BaseTag implements Factory\StaticMethod
protected $name = 'author';
/** @var string The name of the author */
private $authorName = '';
private $authorName;
/** @var string The email of the author */
private $authorEmail = '';
private $authorEmail;
/**
* Initializes this tag with the author name and e-mail.
*
* @param string $authorName
* @param string $authorEmail
*/
public function __construct($authorName, $authorEmail)
public function __construct(string $authorName, string $authorEmail)
{
Assert::string($authorName);
Assert::string($authorEmail);
if ($authorEmail && !filter_var($authorEmail, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('The author tag does not have a valid e-mail address');
throw new InvalidArgumentException('The author tag does not have a valid e-mail address');
}
$this->authorName = $authorName;
@@ -51,7 +53,7 @@ final class Author extends BaseTag implements Factory\StaticMethod
*
* @return string The author's name.
*/
public function getAuthorName()
public function getAuthorName(): string
{
return $this->authorName;
}
@@ -61,39 +63,39 @@ final class Author extends BaseTag implements Factory\StaticMethod
*
* @return string The author's email.
*/
public function getEmail()
public function getEmail(): string
{
return $this->authorEmail;
}
/**
* Returns this tag in string form.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->authorName . (strlen($this->authorEmail) ? ' <' . $this->authorEmail . '>' : '');
if ($this->authorEmail) {
$authorEmail = '<' . $this->authorEmail . '>';
} else {
$authorEmail = '';
}
$authorName = $this->authorName;
return $authorName . ($authorEmail !== '' ? ($authorName !== '' ? ' ' : '') . $authorEmail : '');
}
/**
* Attempts to create a new Author object based on he tag body.
*
* @param string $body
*
* @return static
* Attempts to create a new Author object based on the tag body.
*/
public static function create($body)
public static function create(string $body): ?self
{
Assert::string($body);
$splitTagContent = preg_match('/^([^\<]*)(?:\<([^\>]*)\>)?$/u', $body, $matches);
if (!$splitTagContent) {
return null;
}
$authorName = trim($matches[1]);
$email = isset($matches[2]) ? trim($matches[2]) : '';
$email = isset($matches[2]) ? trim($matches[2]) : '';
return new static($authorName, $email);
}

View File

@@ -1,13 +1,14 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
@@ -31,17 +32,17 @@ abstract class BaseTag implements DocBlock\Tag
*
* @return string The name of this tag.
*/
public function getName()
public function getName(): string
{
return $this->name;
}
public function getDescription()
public function getDescription(): ?Description
{
return $this->description;
}
public function render(Formatter $formatter = null)
public function render(?Formatter $formatter = null): string
{
if ($formatter === null) {
$formatter = new Formatter\PassthroughFormatter();

View File

@@ -1,13 +1,14 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
@@ -17,67 +18,84 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_key_exists;
use function explode;
/**
* Reflection class for a @covers tag in a Docblock.
*/
final class Covers extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'covers';
/** @var Fqsen */
private $refers = null;
private $refers;
/**
* Initializes this tag.
*
* @param Fqsen $refers
* @param Description $description
*/
public function __construct(Fqsen $refers, Description $description = null)
public function __construct(Fqsen $refers, ?Description $description = null)
{
$this->refers = $refers;
$this->refers = $refers;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
DescriptionFactory $descriptionFactory = null,
FqsenResolver $resolver = null,
TypeContext $context = null
) {
Assert::string($body);
Assert::notEmpty($body);
string $body,
?DescriptionFactory $descriptionFactory = null,
?FqsenResolver $resolver = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($body);
Assert::notNull($descriptionFactory);
Assert::notNull($resolver);
$parts = preg_split('/\s+/Su', $body, 2);
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
return new static(
$resolver->resolve($parts[0], $context),
$descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $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.
*
* @return Fqsen
*/
public function getReference()
public function getReference(): Fqsen
{
return $this->refers;
}
/**
* Returns a string representation of this tag.
*
* @return string
*/
public function __toString()
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

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,18 +18,21 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;
use function preg_match;
/**
* Reflection class for a {@}deprecated tag in a Docblock.
*/
final class Deprecated extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'deprecated';
/**
* PCRE regular expression matching a version vector.
* Assumes the "x" modifier.
*/
const REGEX_VECTOR = '(?:
public const REGEX_VECTOR = '(?:
# Normal release vectors.
\d\S*
|
@@ -40,23 +44,25 @@ final class Deprecated extends BaseTag implements Factory\StaticMethod
[^\s\:]+\:\s*\$[^\$]+\$
)';
/** @var string The version vector. */
private $version = '';
/** @var string|null The version vector. */
private $version;
public function __construct($version = null, Description $description = null)
public function __construct(?string $version = null, ?Description $description = null)
{
Assert::nullOrStringNotEmpty($version);
Assert::nullOrNotEmpty($version);
$this->version = $version;
$this->version = $version;
$this->description = $description;
}
/**
* @return static
*/
public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
{
Assert::nullOrString($body);
public static function create(
?string $body,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
if (empty($body)) {
return new static();
}
@@ -65,33 +71,39 @@ final class Deprecated extends BaseTag implements Factory\StaticMethod
if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
return new static(
null,
null !== $descriptionFactory ? $descriptionFactory->create($body, $context) : null
$descriptionFactory !== null ? $descriptionFactory->create($body, $context) : null
);
}
Assert::notNull($descriptionFactory);
return new static(
$matches[1],
$descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
$descriptionFactory->create($matches[2] ?? '', $context)
);
}
/**
* Gets the version section of the tag.
*
* @return string
*/
public function getVersion()
public function getVersion(): ?string
{
return $this->version;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
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

@@ -1,103 +1,110 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\Tag;
use Webmozart\Assert\Assert;
use function array_key_exists;
use function preg_match;
use function rawurlencode;
use function str_replace;
use function strpos;
use function trim;
/**
* Reflection class for a {@}example tag in a Docblock.
*/
final class Example extends BaseTag
final class Example implements Tag, Factory\StaticMethod
{
/**
* @var string Path to a file to use as an example. May also be an absolute URI.
*/
/** @var string Path to a file to use as an example. May also be an absolute URI. */
private $filePath;
/**
* @var bool Whether the file path component represents an URI. This determines how the file portion
* appears at {@link getContent()}.
*/
private $isURI = false;
private $isURI;
/**
* @var int
*/
/** @var int */
private $startingLine;
/**
* @var int
*/
/** @var int */
private $lineCount;
public function __construct($filePath, $isURI, $startingLine, $lineCount, $description)
{
Assert::notEmpty($filePath);
Assert::integer($startingLine);
Assert::greaterThanEq($startingLine, 0);
/** @var string|null */
private $content;
$this->filePath = $filePath;
public function __construct(
string $filePath,
bool $isURI,
int $startingLine,
int $lineCount,
?string $content
) {
Assert::stringNotEmpty($filePath);
Assert::greaterThanEq($startingLine, 1);
Assert::greaterThanEq($lineCount, 0);
$this->filePath = $filePath;
$this->startingLine = $startingLine;
$this->lineCount = $lineCount;
$this->name = 'example';
if ($description !== null) {
$this->description = trim($description);
$this->lineCount = $lineCount;
if ($content !== null) {
$this->content = trim($content);
}
$this->isURI = $isURI;
}
/**
* {@inheritdoc}
*/
public function getContent()
public function getContent(): string
{
if (null === $this->description) {
$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))
:$this->filePath;
: $this->filePath;
}
return trim($filePath . ' ' . parent::getDescription());
return trim($filePath);
}
return $this->description;
return $this->content;
}
/**
* {@inheritdoc}
*/
public static function create($body)
public function getDescription(): ?string
{
return $this->content;
}
public static function create(string $body): ?Tag
{
// File component: File path in quotes or File URI / Source information
if (! preg_match('/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
if (!preg_match('/^\s*(?:(\"[^\"]+\")|(\S+))(?:\s+(.*))?$/sux', $body, $matches)) {
return null;
}
$filePath = null;
$fileUri = null;
if ('' !== $matches[1]) {
if ($matches[1] !== '') {
$filePath = $matches[1];
} else {
$fileUri = $matches[2];
}
$startingLine = 1;
$lineCount = null;
$lineCount = 0;
$description = null;
if (array_key_exists(3, $matches)) {
@@ -105,9 +112,9 @@ final class Example extends BaseTag
// 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] !== '') {
$lineCount = (int)$contentMatches[2];
$startingLine = (int) $contentMatches[1];
if (isset($contentMatches[2])) {
$lineCount = (int) $contentMatches[2];
}
if (array_key_exists(3, $contentMatches)) {
@@ -117,7 +124,7 @@ final class Example extends BaseTag
}
return new static(
$filePath !== null?$filePath:$fileUri,
$filePath ?? ($fileUri ?? ''),
$fileUri !== null,
$startingLine,
$lineCount,
@@ -131,46 +138,63 @@ final class Example extends BaseTag
* @return string Path to a file to use as an example.
* May also be an absolute URI.
*/
public function getFilePath()
public function getFilePath(): string
{
return $this->filePath;
return trim($this->filePath, '"');
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->filePath . ($this->description ? ' ' . $this->description : '');
$filePath = $this->filePath;
$isDefaultLine = $this->startingLine === 1 && $this->lineCount === 0;
$startingLine = !$isDefaultLine ? (string) $this->startingLine : '';
$lineCount = !$isDefaultLine ? (string) $this->lineCount : '';
$content = (string) $this->content;
return $filePath
. ($startingLine !== ''
? ($filePath !== '' ? ' ' : '') . $startingLine
: '')
. ($lineCount !== ''
? ($filePath !== '' || $startingLine !== '' ? ' ' : '') . $lineCount
: '')
. ($content !== ''
? ($filePath !== '' || $startingLine !== '' || $lineCount !== '' ? ' ' : '') . $content
: '');
}
/**
* Returns true if the provided URI is relative or contains a complete scheme (and thus is absolute).
*
* @param string $uri
*
* @return bool
*/
private function isUriRelative($uri)
private function isUriRelative(string $uri): bool
{
return false === strpos($uri, ':');
return strpos($uri, ':') === false;
}
/**
* @return int
*/
public function getStartingLine()
public function getStartingLine(): int
{
return $this->startingLine;
}
/**
* @return int
*/
public function getLineCount()
public function getLineCount(): int
{
return $this->lineCount;
}
public function getName(): string
{
return 'example';
}
public function render(?Formatter $formatter = null): string
{
if ($formatter === null) {
$formatter = new Formatter\PassthroughFormatter();
}
return $formatter->format($this);
}
}

View File

@@ -1,18 +1,25 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
/**
* @deprecated This contract is totally covered by Tag contract. Every class using StaticMethod also use Tag
*/
interface StaticMethod
{
public static function create($body);
/**
* @return mixed
*/
public static function create(string $body);
}

View File

@@ -1,18 +0,0 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
interface Strategy
{
public function create($body);
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -18,10 +19,6 @@ interface Formatter
{
/**
* Formats a tag into a string representation according to a specific format, such as Markdown.
*
* @param Tag $tag
*
* @return string
*/
public function format(Tag $tag);
public function format(Tag $tag): string;
}

View File

@@ -1,13 +1,13 @@
<?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.
*
* @author Jan Schneider <jan@horde.org>
* @copyright 2017 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,14 +16,16 @@ namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter;
use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
use function max;
use function str_repeat;
use function strlen;
class AlignFormatter implements Formatter
{
/** @var int The maximum tag name length. */
protected $maxLen = 0;
/**
* Constructor.
*
* @param Tag[] $tags All tags that should later be aligned with the formatter.
*/
public function __construct(array $tags)
@@ -35,13 +37,14 @@ class AlignFormatter implements Formatter
/**
* Formats the given tag to return a simple plain text version.
*
* @param Tag $tag
*
* @return string
*/
public function format(Tag $tag)
public function format(Tag $tag): string
{
return '@' . $tag->getName() . str_repeat(' ', $this->maxLen - strlen($tag->getName()) + 1) . (string)$tag;
return '@' . $tag->getName() .
str_repeat(
' ',
$this->maxLen - strlen($tag->getName()) + 1
) .
$tag;
}
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -15,17 +16,15 @@ namespace phpDocumentor\Reflection\DocBlock\Tags\Formatter;
use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
use function trim;
class PassthroughFormatter implements Formatter
{
/**
* Formats the given tag to return a simple plain text version.
*
* @param Tag $tag
*
* @return string
*/
public function format(Tag $tag)
public function format(Tag $tag): string
{
return trim('@' . $tag->getName() . ' ' . (string)$tag);
return trim('@' . $tag->getName() . ' ' . $tag);
}
}

View File

@@ -1,88 +1,86 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
use InvalidArgumentException;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;
use function preg_match;
/**
* Parses a tag definition for a DocBlock.
*/
class Generic extends BaseTag implements Factory\StaticMethod
final class Generic extends BaseTag implements Factory\StaticMethod
{
/**
* Parses a tag and populates the member variables.
*
* @param string $name Name of the tag.
* @param string $name Name of the tag.
* @param Description $description The contents of the given tag.
*/
public function __construct($name, Description $description = null)
public function __construct(string $name, ?Description $description = null)
{
$this->validateTagName($name);
$this->name = $name;
$this->name = $name;
$this->description = $description;
}
/**
* Creates a new tag that represents any unknown tag type.
*
* @param string $body
* @param string $name
* @param DescriptionFactory $descriptionFactory
* @param TypeContext $context
*
* @return static
*/
public static function create(
$body,
$name = '',
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
Assert::string($body);
string $body,
string $name = '',
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($name);
Assert::notNull($descriptionFactory);
$description = $descriptionFactory && $body ? $descriptionFactory->create($body, $context) : null;
$description = $body !== '' ? $descriptionFactory->create($body, $context) : null;
return new static($name, $description);
}
/**
* Returns the tag as a serialized string
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return ($this->description ? $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
return $description;
}
/**
* Validates if the tag name matches the expected format, otherwise throws an exception.
*
* @param string $name
*
* @return void
*/
private function validateTagName($name)
private function validateTagName(string $name): void
{
if (! preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) {
throw new \InvalidArgumentException(
if (!preg_match('/^' . StandardTagFactory::REGEX_TAGNAME . '$/u', $name)) {
throw new InvalidArgumentException(
'The tag name "' . $name . '" is not wellformed. Tags may only consist of letters, underscores, '
. 'hyphens and backslashes.'
);

View File

@@ -0,0 +1,145 @@
<?php
declare(strict_types=1);
namespace phpDocumentor\Reflection\DocBlock\Tags;
use Closure;
use Exception;
use phpDocumentor\Reflection\DocBlock\Tag;
use ReflectionClass;
use ReflectionException;
use ReflectionFunction;
use Throwable;
use function array_map;
use function get_class;
use function get_resource_type;
use function is_array;
use function is_object;
use function is_resource;
use function sprintf;
/**
* This class represents an exception during the tag creation
*
* Since the internals of the library are relaying on the correct syntax of a docblock
* we cannot simply throw exceptions at all time because the exceptions will break the creation of a
* docklock. Just silently ignore the exceptions is not an option because the user as an issue to fix.
*
* This tag holds that error information until a using application is able to display it. The object wil just behave
* like any normal tag. So the normal application flow will not break.
*/
final class InvalidTag implements Tag
{
/** @var string */
private $name;
/** @var string */
private $body;
/** @var Throwable|null */
private $throwable;
private function __construct(string $name, string $body)
{
$this->name = $name;
$this->body = $body;
}
public function getException(): ?Throwable
{
return $this->throwable;
}
public function getName(): string
{
return $this->name;
}
public static function create(string $body, string $name = ''): self
{
return new self($name, $body);
}
public function withError(Throwable $exception): self
{
$this->flattenExceptionBacktrace($exception);
$tag = new self($this->name, $this->body);
$tag->throwable = $exception;
return $tag;
}
/**
* Removes all complex types from backtrace
*
* Not all objects are serializable. So we need to remove them from the
* stored exception to be sure that we do not break existing library usage.
*/
private function flattenExceptionBacktrace(Throwable $exception): void
{
$traceProperty = (new ReflectionClass(Exception::class))->getProperty('trace');
$traceProperty->setAccessible(true);
do {
$trace = $exception->getTrace();
if (isset($trace[0]['args'])) {
$trace = array_map(
function (array $call): array {
$call['args'] = array_map([$this, 'flattenArguments'], $call['args'] ?? []);
return $call;
},
$trace
);
}
$traceProperty->setValue($exception, $trace);
$exception = $exception->getPrevious();
} while ($exception !== null);
$traceProperty->setAccessible(false);
}
/**
* @param mixed $value
*
* @return mixed
*
* @throws ReflectionException
*/
private function flattenArguments($value)
{
if ($value instanceof Closure) {
$closureReflection = new ReflectionFunction($value);
$value = sprintf(
'(Closure at %s:%s)',
$closureReflection->getFileName(),
$closureReflection->getStartLine()
);
} elseif (is_object($value)) {
$value = sprintf('object(%s)', get_class($value));
} elseif (is_resource($value)) {
$value = sprintf('resource(%s)', get_resource_type($value));
} elseif (is_array($value)) {
$value = array_map([$this, 'flattenArguments'], $value);
}
return $value;
}
public function render(?Formatter $formatter = null): string
{
if ($formatter === null) {
$formatter = new Formatter\PassthroughFormatter();
}
return $formatter->format($this);
}
public function __toString(): string
{
return $this->body;
}
}

View File

@@ -1,13 +1,14 @@
<?php
declare(strict_types=1);
/**
* phpDocumentor
* This file is part of phpDocumentor.
*
* PHP Version 5.3
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Ben Selby <benmatselby@gmail.com>
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
@@ -15,63 +16,63 @@ namespace phpDocumentor\Reflection\DocBlock\Tags;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
/**
* Reflection class for a @link tag in a Docblock.
* Reflection class for a {@}link tag in a Docblock.
*/
final class Link extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'link';
/** @var string */
private $link = '';
private $link;
/**
* Initializes a link to a URL.
*
* @param string $link
* @param Description $description
*/
public function __construct($link, Description $description = null)
public function __construct(string $link, ?Description $description = null)
{
Assert::string($link);
$this->link = $link;
$this->link = $link;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
{
Assert::string($body);
public static function create(
string $body,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
$description = isset($parts[1]) ? $descriptionFactory->create($parts[1], $context) : null;
return new static($parts[0], $description);
}
/**
* Gets the link
*
* @return string
*/
public function getLink()
* Gets the link
*/
public function getLink(): string
{
return $this->link;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->link . ($this->description ? ' ' . $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$link = $this->link;
return $link . ($description !== '' ? ($link !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -1,53 +1,74 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
use InvalidArgumentException;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\TypeResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Types\Mixed_;
use phpDocumentor\Reflection\Types\Void_;
use Webmozart\Assert\Assert;
use function array_keys;
use function explode;
use function implode;
use function is_string;
use function preg_match;
use function sort;
use function strpos;
use function substr;
use function trim;
use function var_export;
/**
* Reflection class for an {@}method in a Docblock.
*/
final class Method extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'method';
/** @var string */
private $methodName = '';
private $methodName;
/** @var string[] */
private $arguments = [];
/**
* @phpstan-var array<int, array{name: string, type: Type}>
* @var array<int, array<string, Type|string>>
*/
private $arguments;
/** @var bool */
private $isStatic = false;
private $isStatic;
/** @var Type */
private $returnType;
/**
* @param array<int, array<string, Type|string>> $arguments
* @phpstan-param array<int, array{name: string, type: Type}|string> $arguments
*/
public function __construct(
$methodName,
string $methodName,
array $arguments = [],
Type $returnType = null,
$static = false,
Description $description = null
?Type $returnType = null,
bool $static = false,
?Description $description = null
) {
Assert::stringNotEmpty($methodName);
Assert::boolean($static);
if ($returnType === null) {
$returnType = new Void_();
@@ -60,17 +81,15 @@ final class Method extends BaseTag implements Factory\StaticMethod
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): ?self {
Assert::stringNotEmpty($body);
Assert::allNotNull([ $typeResolver, $descriptionFactory ]);
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
// 1. none or more whitespace
// 2. optionally the keyword "static" followed by whitespace
@@ -81,8 +100,9 @@ final class Method extends BaseTag implements Factory\StaticMethod
// 5. then a word with underscores, followed by ( and any character
// until a ) and whitespace : as method name with signature
// 6. any remaining text : as description
if (!preg_match(
'/^
if (
!preg_match(
'/^
# Static keyword
# Declares a static method ONLY if type is also present
(?:
@@ -91,23 +111,19 @@ final class Method extends BaseTag implements Factory\StaticMethod
)?
# Return type
(?:
(
(
(?:[\w\|_\\\\]*\$this[\w\|_\\\\]*)
|
(?:
(?:[\w\|_\\\\]+)
# array notation
# array notation
(?:\[\])*
)*
)*+
)
\s+
)?
# Legacy method name (not captured)
(?:
[\w_]+\(\)\s+
)?
# Method name
([\w\|_\\\\]+)
([\w_]+)
# Arguments
(?:
\(([^\)]*)\)
@@ -116,15 +132,16 @@ final class Method extends BaseTag implements Factory\StaticMethod
# Description
(.*)
$/sux',
$body,
$matches
)) {
$body,
$matches
)
) {
return null;
}
list(, $static, $returnType, $methodName, $arguments, $description) = $matches;
[, $static, $returnType, $methodName, $argumentLines, $description] = $matches;
$static = $static === 'static';
$static = $static === 'static';
if ($returnType === '') {
$returnType = 'void';
@@ -133,26 +150,26 @@ final class Method extends BaseTag implements Factory\StaticMethod
$returnType = $typeResolver->resolve($returnType, $context);
$description = $descriptionFactory->create($description, $context);
if (is_string($arguments) && strlen($arguments) > 0) {
$arguments = explode(',', $arguments);
foreach ($arguments as &$argument) {
/** @phpstan-var array<int, array{name: string, type: Type}> $arguments */
$arguments = [];
if ($argumentLines !== '') {
$argumentsExploded = explode(',', $argumentLines);
foreach ($argumentsExploded as $argument) {
$argument = explode(' ', self::stripRestArg(trim($argument)), 2);
if ($argument[0][0] === '$') {
if (strpos($argument[0], '$') === 0) {
$argumentName = substr($argument[0], 1);
$argumentType = new Void_();
$argumentType = new Mixed_();
} else {
$argumentType = $typeResolver->resolve($argument[0], $context);
$argumentName = '';
if (isset($argument[1])) {
$argument[1] = self::stripRestArg($argument[1]);
$argument[1] = self::stripRestArg($argument[1]);
$argumentName = substr($argument[1], 1);
}
}
$argument = [ 'name' => $argumentName, 'type' => $argumentType];
$arguments[] = ['name' => $argumentName, 'type' => $argumentType];
}
} else {
$arguments = [];
}
return new static($methodName, $arguments, $returnType, $static, $description);
@@ -160,18 +177,17 @@ final class Method extends BaseTag implements Factory\StaticMethod
/**
* Retrieves the method name.
*
* @return string
*/
public function getMethodName()
public function getMethodName(): string
{
return $this->methodName;
}
/**
* @return string[]
* @return array<int, array<string, Type|string>>
* @phpstan-return array<int, array{name: string, type: Type}>
*/
public function getArguments()
public function getArguments(): array
{
return $this->arguments;
}
@@ -181,57 +197,78 @@ final class Method extends BaseTag implements Factory\StaticMethod
*
* @return bool TRUE if the method declaration is for a static method, FALSE otherwise.
*/
public function isStatic()
public function isStatic(): bool
{
return $this->isStatic;
}
/**
* @return Type
*/
public function getReturnType()
public function getReturnType(): Type
{
return $this->returnType;
}
public function __toString()
public function __toString(): string
{
$arguments = [];
foreach ($this->arguments as $argument) {
$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 = $this->methodName;
return $static
. ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '')
. ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $methodName : '')
. $argumentStr
. ($description !== '' ? ' ' . $description : '');
}
private function filterArguments($arguments)
/**
* @param mixed[][]|string[] $arguments
* @phpstan-param array<int, array{name: string, type: Type}|string> $arguments
*
* @return mixed[][]
* @phpstan-return array<int, array{name: string, type: Type}>
*/
private function filterArguments(array $arguments = []): array
{
foreach ($arguments as &$argument) {
$result = [];
foreach ($arguments as $argument) {
if (is_string($argument)) {
$argument = [ 'name' => $argument ];
$argument = ['name' => $argument];
}
if (! isset($argument['type'])) {
$argument['type'] = new Void_();
if (!isset($argument['type'])) {
$argument['type'] = new Mixed_();
}
$keys = array_keys($argument);
sort($keys);
if ($keys !== [ 'name', 'type' ]) {
throw new \InvalidArgumentException(
if ($keys !== ['name', 'type']) {
throw new InvalidArgumentException(
'Arguments can only have the "name" and "type" fields, found: ' . var_export($keys, true)
);
}
$result[] = $argument;
}
return $arguments;
return $result;
}
private static function stripRestArg($argument)
private static function stripRestArg(string $argument): string
{
if (strpos($argument, '...') === 0) {
$argument = trim(substr($argument, 3));

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,125 +18,157 @@ 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 strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
/**
* Reflection class for the {@}param tag in a Docblock.
*/
final class Param extends BaseTag implements Factory\StaticMethod
final class Param extends TagWithType implements Factory\StaticMethod
{
/** @var string */
protected $name = 'param';
/** @var Type */
private $type;
/** @var string */
private $variableName = '';
/** @var string|null */
private $variableName;
/** @var bool determines whether this is a variadic argument */
private $isVariadic = false;
private $isVariadic;
/**
* @param string $variableName
* @param Type $type
* @param bool $isVariadic
* @param Description $description
*/
public function __construct($variableName, Type $type = null, $isVariadic = false, Description $description = null)
{
Assert::string($variableName);
Assert::boolean($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,
bool $isReference = false
) {
$this->name = 'param';
$this->variableName = $variableName;
$this->type = $type;
$this->isVariadic = $isVariadic;
$this->description = $description;
$this->type = $type;
$this->isVariadic = $isVariadic;
$this->description = $description;
$this->isReference = $isReference;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($body);
Assert::allNotNull([$typeResolver, $descriptionFactory]);
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
$type = null;
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
$variableName = '';
$isVariadic = false;
$isVariadic = false;
$isReference = false;
// if the first item that is encountered is not a variable; it is a type
if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
$type = $typeResolver->resolve(array_shift($parts), $context);
array_shift($parts);
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]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$' || substr($parts[0], 0, 4) === '...$')) {
// 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 (substr($variableName, 0, 3) === '...') {
$isVariadic = true;
$variableName = substr($variableName, 3);
if ($type) {
array_shift($parts);
}
if (substr($variableName, 0, 1) === '$') {
Assert::notNull($variableName);
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);
}
/**
* Returns the variable's name.
*
* @return string
*/
public function getVariableName()
public function getVariableName(): ?string
{
return $this->variableName;
}
/**
* Returns the variable's type or null if unknown.
*
* @return Type|null
*/
public function getType()
{
return $this->type;
}
/**
* Returns whether this tag is variadic.
*
* @return boolean
*/
public function isVariadic()
public function isVariadic(): bool
{
return $this->isVariadic;
}
/**
* Returns a string representation for this tag.
*
* @return string
* Returns whether this tag is passed by reference.
*/
public function __toString()
public function isReference(): bool
{
return ($this->type ? $this->type . ' ' : '')
. ($this->isVariadic() ? '...' : '')
. '$' . $this->variableName
. ($this->description ? ' ' . $this->description : '');
return $this->isReference;
}
/**
* Returns a string representation for this tag.
*/
public function __toString(): string
{
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

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,66 +18,68 @@ 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 strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
/**
* Reflection class for a {@}property tag in a Docblock.
*/
class Property extends BaseTag implements Factory\StaticMethod
final class Property extends TagWithType implements Factory\StaticMethod
{
/** @var string */
protected $name = 'property';
/** @var string|null */
protected $variableName;
/** @var Type */
private $type;
/** @var string */
protected $variableName = '';
/**
* @param string $variableName
* @param Type $type
* @param Description $description
*/
public function __construct($variableName, Type $type = null, Description $description = null)
public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
{
Assert::string($variableName);
$this->name = 'property';
$this->variableName = $variableName;
$this->type = $type;
$this->description = $description;
$this->type = $type;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($body);
Assert::allNotNull([$typeResolver, $descriptionFactory]);
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
$type = null;
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$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
if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
$type = $typeResolver->resolve(array_shift($parts), $context);
array_shift($parts);
if ($firstPart && $firstPart[0] !== '$') {
$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]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) {
// 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 (substr($variableName, 0, 1) === '$') {
$variableName = substr($variableName, 1);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
$variableName = substr($variableName, 1);
}
$description = $descriptionFactory->create(implode('', $parts), $context);
@@ -86,33 +89,33 @@ class Property extends BaseTag implements Factory\StaticMethod
/**
* Returns the variable's name.
*
* @return string
*/
public function getVariableName()
public function getVariableName(): ?string
{
return $this->variableName;
}
/**
* Returns the variable's type or null if unknown.
*
* @return Type|null
*/
public function getType()
{
return $this->type;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return ($this->type ? $this->type . ' ' : '')
. '$' . $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

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,66 +18,68 @@ 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 strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
/**
* Reflection class for a {@}property-read tag in a Docblock.
*/
class PropertyRead extends BaseTag implements Factory\StaticMethod
final class PropertyRead extends TagWithType implements Factory\StaticMethod
{
/** @var string */
protected $name = 'property-read';
/** @var string|null */
protected $variableName;
/** @var Type */
private $type;
/** @var string */
protected $variableName = '';
/**
* @param string $variableName
* @param Type $type
* @param Description $description
*/
public function __construct($variableName, Type $type = null, Description $description = null)
public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
{
Assert::string($variableName);
$this->name = 'property-read';
$this->variableName = $variableName;
$this->type = $type;
$this->description = $description;
$this->type = $type;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($body);
Assert::allNotNull([$typeResolver, $descriptionFactory]);
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
$type = null;
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$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
if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
$type = $typeResolver->resolve(array_shift($parts), $context);
array_shift($parts);
if ($firstPart && $firstPart[0] !== '$') {
$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]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) {
// 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 (substr($variableName, 0, 1) === '$') {
$variableName = substr($variableName, 1);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
$variableName = substr($variableName, 1);
}
$description = $descriptionFactory->create(implode('', $parts), $context);
@@ -86,33 +89,33 @@ class PropertyRead extends BaseTag implements Factory\StaticMethod
/**
* Returns the variable's name.
*
* @return string
*/
public function getVariableName()
public function getVariableName(): ?string
{
return $this->variableName;
}
/**
* Returns the variable's type or null if unknown.
*
* @return Type|null
*/
public function getType()
{
return $this->type;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return ($this->type ? $this->type . ' ' : '')
. '$' . $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

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,66 +18,68 @@ 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 strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
/**
* Reflection class for a {@}property-write tag in a Docblock.
*/
class PropertyWrite extends BaseTag implements Factory\StaticMethod
final class PropertyWrite extends TagWithType implements Factory\StaticMethod
{
/** @var string */
protected $name = 'property-write';
protected $variableName;
/** @var Type */
private $type;
/** @var string */
protected $variableName = '';
/**
* @param string $variableName
* @param Type $type
* @param Description $description
*/
public function __construct($variableName, Type $type = null, Description $description = null)
public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
{
Assert::string($variableName);
$this->name = 'property-write';
$this->variableName = $variableName;
$this->type = $type;
$this->description = $description;
$this->type = $type;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($body);
Assert::allNotNull([$typeResolver, $descriptionFactory]);
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
$type = null;
[$firstPart, $body] = self::extractTypeFromBody($body);
$type = null;
$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
if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
$type = $typeResolver->resolve(array_shift($parts), $context);
array_shift($parts);
if ($firstPart && $firstPart[0] !== '$') {
$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]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) {
// 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 (substr($variableName, 0, 1) === '$') {
$variableName = substr($variableName, 1);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
$variableName = substr($variableName, 1);
}
$description = $descriptionFactory->create(implode('', $parts), $context);
@@ -86,33 +89,33 @@ class PropertyWrite extends BaseTag implements Factory\StaticMethod
/**
* Returns the variable's name.
*
* @return string
*/
public function getVariableName()
public function getVariableName(): ?string
{
return $this->variableName;
}
/**
* Returns the variable's type or null if unknown.
*
* @return Type|null
*/
public function getType()
{
return $this->type;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return ($this->type ? $this->type . ' ' : '')
. '$' . $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

@@ -1,13 +1,14 @@
<?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.
*
* @copyright 2010-2017 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags\Reference;
@@ -15,18 +16,13 @@ namespace phpDocumentor\Reflection\DocBlock\Tags\Reference;
use phpDocumentor\Reflection\Fqsen as RealFqsen;
/**
* Fqsen reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See}
* Fqsen reference used by {@see \phpDocumentor\Reflection\DocBlock\Tags\See}
*/
final class Fqsen implements Reference
{
/**
* @var RealFqsen
*/
/** @var RealFqsen */
private $fqsen;
/**
* Fqsen constructor.
*/
public function __construct(RealFqsen $fqsen)
{
$this->fqsen = $fqsen;
@@ -35,8 +31,8 @@ final class Fqsen implements Reference
/**
* @return string string representation of the referenced fqsen
*/
public function __toString()
public function __toString(): string
{
return (string)$this->fqsen;
return (string) $this->fqsen;
}
}

View File

@@ -1,21 +1,22 @@
<?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.
*
* @copyright 2010-2017 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags\Reference;
/**
* Interface for references in {@see phpDocumentor\Reflection\DocBlock\Tags\See}
* Interface for references in {@see \phpDocumentor\Reflection\DocBlock\Tags\See}
*/
interface Reference
{
public function __toString();
public function __toString(): string;
}

View File

@@ -1,13 +1,14 @@
<?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.
*
* @copyright 2010-2017 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags\Reference;
@@ -15,25 +16,20 @@ namespace phpDocumentor\Reflection\DocBlock\Tags\Reference;
use Webmozart\Assert\Assert;
/**
* Url reference used by {@see phpDocumentor\Reflection\DocBlock\Tags\See}
* Url reference used by {@see \phpDocumentor\Reflection\DocBlock\Tags\See}
*/
final class Url implements Reference
{
/**
* @var string
*/
/** @var string */
private $uri;
/**
* Url constructor.
*/
public function __construct($uri)
public function __construct(string $uri)
{
Assert::stringNotEmpty($uri);
$this->uri = $uri;
}
public function __toString()
public function __toString(): string
{
return $this->uri;
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -22,51 +23,42 @@ use Webmozart\Assert\Assert;
/**
* Reflection class for a {@}return tag in a Docblock.
*/
final class Return_ extends BaseTag implements Factory\StaticMethod
final class Return_ extends TagWithType implements Factory\StaticMethod
{
protected $name = 'return';
/** @var Type */
private $type;
public function __construct(Type $type, Description $description = null)
public function __construct(Type $type, ?Description $description = null)
{
$this->type = $type;
$this->name = 'return';
$this->type = $type;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
Assert::string($body);
Assert::allNotNull([$typeResolver, $descriptionFactory]);
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
[$type, $description] = self::extractTypeFromBody($body);
$type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context);
$description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context);
$type = $typeResolver->resolve($type, $context);
$description = $descriptionFactory->create($description, $context);
return new static($type, $description);
}
/**
* Returns the type section of the variable.
*
* @return Type
*/
public function getType()
public function __toString(): string
{
return $this->type;
}
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
public function __toString()
{
return $this->type . ' ' . $this->description;
$type = $this->type ? '' . $this->type : 'mixed';
return $type . ($description !== '' ? ' ' . $description : '');
}
}

View File

@@ -1,13 +1,14 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
@@ -17,72 +18,89 @@ 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;
/**
* Reflection class for an {@}see tag in a Docblock.
*/
class See extends BaseTag implements Factory\StaticMethod
final class See extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'see';
/** @var Reference */
protected $refers = null;
protected $refers;
/**
* Initializes this tag.
*
* @param Reference $refers
* @param Description $description
*/
public function __construct(Reference $refers, Description $description = null)
public function __construct(Reference $refers, ?Description $description = null)
{
$this->refers = $refers;
$this->refers = $refers;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
FqsenResolver $resolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
Assert::string($body);
Assert::allNotNull([$resolver, $descriptionFactory]);
string $body,
?FqsenResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
$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
if (preg_match('/\w:\/\/\w/i', $parts[0])) {
if (preg_match('#\w://\w#', $parts[0])) {
return new static(new Url($parts[0]), $description);
}
return new static(new FqsenRef($resolver->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]);
}
/**
* Returns the ref of this tag.
*
* @return Reference
*/
public function getReference()
public function getReference(): Reference
{
return $this->refers;
}
/**
* Returns a string representation of this tag.
*
* @return string
*/
public function __toString()
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

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,18 +18,21 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;
use function preg_match;
/**
* Reflection class for a {@}since tag in a Docblock.
*/
final class Since extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'since';
/**
* PCRE regular expression matching a version vector.
* Assumes the "x" modifier.
*/
const REGEX_VECTOR = '(?:
public const REGEX_VECTOR = '(?:
# Normal release vectors.
\d\S*
|
@@ -40,55 +44,60 @@ final class Since extends BaseTag implements Factory\StaticMethod
[^\s\:]+\:\s*\$[^\$]+\$
)';
/** @var string The version vector. */
private $version = '';
/** @var string|null The version vector. */
private $version;
public function __construct($version = null, Description $description = null)
public function __construct(?string $version = null, ?Description $description = null)
{
Assert::nullOrStringNotEmpty($version);
Assert::nullOrNotEmpty($version);
$this->version = $version;
$this->description = $description;
}
/**
* @return static
*/
public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
{
Assert::nullOrString($body);
public static function create(
?string $body,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): ?self {
if (empty($body)) {
return new static();
}
$matches = [];
if (! preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
if (!preg_match('/^(' . self::REGEX_VECTOR . ')\s*(.+)?$/sux', $body, $matches)) {
return null;
}
Assert::notNull($descriptionFactory);
return new static(
$matches[1],
$descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
$descriptionFactory->create($matches[2] ?? '', $context)
);
}
/**
* Gets the version section of the tag.
*
* @return string
*/
public function getVersion()
public function getVersion(): ?string
{
return $this->version;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
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

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,6 +18,8 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;
use function preg_match;
/**
* Reflection class for a {@}source tag in a Docblock.
*/
@@ -26,26 +29,30 @@ final class Source extends BaseTag implements Factory\StaticMethod
protected $name = 'source';
/** @var int The starting line, relative to the structural element's location. */
private $startingLine = 1;
private $startingLine;
/** @var int|null The number of lines, relative to the starting line. NULL means "to the end". */
private $lineCount = null;
private $lineCount;
public function __construct($startingLine, $lineCount = null, Description $description = null)
/**
* @param int|string $startingLine should be a to int convertible value
* @param int|string|null $lineCount should be a to int convertible value
*/
public function __construct($startingLine, $lineCount = null, ?Description $description = null)
{
Assert::integerish($startingLine);
Assert::nullOrIntegerish($lineCount);
$this->startingLine = (int)$startingLine;
$this->lineCount = $lineCount !== null ? (int)$lineCount : null;
$this->startingLine = (int) $startingLine;
$this->lineCount = $lineCount !== null ? (int) $lineCount : null;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
{
public static function create(
string $body,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($body);
Assert::notNull($descriptionFactory);
@@ -55,15 +62,15 @@ final class Source extends BaseTag implements Factory\StaticMethod
// Starting line / Number of lines / Description
if (preg_match('/^([1-9]\d*)\s*(?:((?1))\s+)?(.*)$/sux', $body, $matches)) {
$startingLine = (int)$matches[1];
$startingLine = (int) $matches[1];
if (isset($matches[2]) && $matches[2] !== '') {
$lineCount = (int)$matches[2];
$lineCount = (int) $matches[2];
}
$description = $matches[3];
}
return new static($startingLine, $lineCount, $descriptionFactory->create($description, $context));
return new static($startingLine, $lineCount, $descriptionFactory->create($description ?? '', $context));
}
/**
@@ -72,7 +79,7 @@ final class Source extends BaseTag implements Factory\StaticMethod
* @return int The starting line, relative to the structural element's
* location.
*/
public function getStartingLine()
public function getStartingLine(): int
{
return $this->startingLine;
}
@@ -83,15 +90,27 @@ final class Source extends BaseTag implements Factory\StaticMethod
* @return int|null The number of lines, relative to the starting line. NULL
* means "to the end".
*/
public function getLineCount()
public function getLineCount(): ?int
{
return $this->lineCount;
}
public function __toString()
public function __toString(): string
{
return $this->startingLine
. ($this->lineCount !== null ? ' ' . $this->lineCount : '')
. ($this->description ? ' ' . $this->description->render() : '');
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$startingLine = (string) $this->startingLine;
$lineCount = $this->lineCount !== null ? ' ' . $this->lineCount : '';
return $startingLine
. $lineCount
. ($description !== ''
? ' ' . $description
: '');
}
}

View File

@@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
use phpDocumentor\Reflection\Type;
use function in_array;
use function strlen;
use function substr;
use function trim;
abstract class TagWithType extends BaseTag
{
/** @var ?Type */
protected $type;
/**
* Returns the type section of the variable.
*/
public function getType(): ?Type
{
return $this->type;
}
/**
* @return string[]
*/
protected static function extractTypeFromBody(string $body): array
{
$type = '';
$nestingLevel = 0;
for ($i = 0, $iMax = strlen($body); $i < $iMax; $i++) {
$character = $body[$i];
if ($nestingLevel === 0 && trim($character) === '') {
break;
}
$type .= $character;
if (in_array($character, ['<', '(', '[', '{'])) {
$nestingLevel++;
continue;
}
if (in_array($character, ['>', ')', ']', '}'])) {
$nestingLevel--;
continue;
}
}
$description = trim(substr($body, strlen($type)));
return [$type, $description];
}
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -22,51 +23,42 @@ use Webmozart\Assert\Assert;
/**
* Reflection class for a {@}throws tag in a Docblock.
*/
final class Throws extends BaseTag implements Factory\StaticMethod
final class Throws extends TagWithType implements Factory\StaticMethod
{
protected $name = 'throws';
/** @var Type */
private $type;
public function __construct(Type $type, Description $description = null)
public function __construct(Type $type, ?Description $description = null)
{
$this->name = 'throws';
$this->type = $type;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
Assert::string($body);
Assert::allNotNull([$typeResolver, $descriptionFactory]);
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
[$type, $description] = self::extractTypeFromBody($body);
$type = $typeResolver->resolve(isset($parts[0]) ? $parts[0] : '', $context);
$description = $descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $context);
$type = $typeResolver->resolve($type, $context);
$description = $descriptionFactory->create($description, $context);
return new static($type, $description);
}
/**
* Returns the type section of the variable.
*
* @return Type
*/
public function getType()
public function __toString(): string
{
return $this->type;
}
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
public function __toString()
{
return $this->type . ' ' . $this->description;
$type = (string) $this->type;
return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -1,13 +1,14 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
@@ -17,67 +18,83 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;
use Webmozart\Assert\Assert;
use function array_key_exists;
use function explode;
/**
* Reflection class for a {@}uses tag in a Docblock.
*/
final class Uses extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'uses';
/** @var Fqsen */
protected $refers = null;
protected $refers;
/**
* Initializes this tag.
*
* @param Fqsen $refers
* @param Description $description
*/
public function __construct(Fqsen $refers, Description $description = null)
public function __construct(Fqsen $refers, ?Description $description = null)
{
$this->refers = $refers;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
FqsenResolver $resolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
Assert::string($body);
Assert::allNotNull([$resolver, $descriptionFactory]);
string $body,
?FqsenResolver $resolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::notNull($resolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/\s+/Su', $body, 2);
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
return new static(
$resolver->resolve($parts[0], $context),
$descriptionFactory->create(isset($parts[1]) ? $parts[1] : '', $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.
*
* @return Fqsen
*/
public function getReference()
public function getReference(): Fqsen
{
return $this->refers;
}
/**
* Returns a string representation of this tag.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return $this->refers . ' ' . $this->description->render();
if ($this->description) {
$description = $this->description->render();
} else {
$description = '';
}
$refers = (string) $this->refers;
return $refers . ($description !== '' ? ($refers !== '' ? ' ' : '') . $description : '');
}
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -17,66 +18,69 @@ 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 strpos;
use function substr;
use const PREG_SPLIT_DELIM_CAPTURE;
/**
* Reflection class for a {@}var tag in a Docblock.
*/
class Var_ extends BaseTag implements Factory\StaticMethod
final class Var_ extends TagWithType implements Factory\StaticMethod
{
/** @var string */
protected $name = 'var';
/** @var Type */
private $type;
/** @var string */
/** @var string|null */
protected $variableName = '';
/**
* @param string $variableName
* @param Type $type
* @param Description $description
*/
public function __construct($variableName, Type $type = null, Description $description = null)
public function __construct(?string $variableName, ?Type $type = null, ?Description $description = null)
{
Assert::string($variableName);
$this->name = 'var';
$this->variableName = $variableName;
$this->type = $type;
$this->description = $description;
}
/**
* {@inheritdoc}
*/
public static function create(
$body,
TypeResolver $typeResolver = null,
DescriptionFactory $descriptionFactory = null,
TypeContext $context = null
) {
string $body,
?TypeResolver $typeResolver = null,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): self {
Assert::stringNotEmpty($body);
Assert::allNotNull([$typeResolver, $descriptionFactory]);
Assert::notNull($typeResolver);
Assert::notNull($descriptionFactory);
$parts = preg_split('/(\s+)/Su', $body, 3, PREG_SPLIT_DELIM_CAPTURE);
[$firstPart, $body] = self::extractTypeFromBody($body);
$parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE);
$type = null;
$variableName = '';
// if the first item that is encountered is not a variable; it is a type
if (isset($parts[0]) && (strlen($parts[0]) > 0) && ($parts[0][0] !== '$')) {
$type = $typeResolver->resolve(array_shift($parts), $context);
array_shift($parts);
if ($firstPart && $firstPart[0] !== '$') {
$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]) && (strlen($parts[0]) > 0) && ($parts[0][0] === '$')) {
// 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 (substr($variableName, 0, 1) === '$') {
$variableName = substr($variableName, 1);
if ($type) {
array_shift($parts);
}
Assert::notNull($variableName);
$variableName = substr($variableName, 1);
}
$description = $descriptionFactory->create(implode('', $parts), $context);
@@ -86,33 +90,33 @@ class Var_ extends BaseTag implements Factory\StaticMethod
/**
* Returns the variable's name.
*
* @return string
*/
public function getVariableName()
public function getVariableName(): ?string
{
return $this->variableName;
}
/**
* Returns the variable's type or null if unknown.
*
* @return Type|null
*/
public function getType()
{
return $this->type;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return ($this->type ? $this->type . ' ' : '')
. (empty($this->variableName) ? null : ('$' . $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

@@ -1,13 +1,14 @@
<?php
declare(strict_types=1);
/**
* phpDocumentor
* This file is part of phpDocumentor.
*
* PHP Version 5.3
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @author Vasil Rangelov <boen.robot@gmail.com>
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\DocBlock\Tags;
@@ -17,18 +18,21 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use Webmozart\Assert\Assert;
use function preg_match;
/**
* Reflection class for a {@}version tag in a Docblock.
*/
final class Version extends BaseTag implements Factory\StaticMethod
{
/** @var string */
protected $name = 'version';
/**
* PCRE regular expression matching a version vector.
* Assumes the "x" modifier.
*/
const REGEX_VECTOR = '(?:
public const REGEX_VECTOR = '(?:
# Normal release vectors.
\d\S*
|
@@ -40,23 +44,22 @@ final class Version extends BaseTag implements Factory\StaticMethod
[^\s\:]+\:\s*\$[^\$]+\$
)';
/** @var string The version vector. */
private $version = '';
/** @var string|null The version vector. */
private $version;
public function __construct($version = null, Description $description = null)
public function __construct(?string $version = null, ?Description $description = null)
{
Assert::nullOrStringNotEmpty($version);
$this->version = $version;
$this->version = $version;
$this->description = $description;
}
/**
* @return static
*/
public static function create($body, DescriptionFactory $descriptionFactory = null, TypeContext $context = null)
{
Assert::nullOrString($body);
public static function create(
?string $body,
?DescriptionFactory $descriptionFactory = null,
?TypeContext $context = null
): ?self {
if (empty($body)) {
return new static();
}
@@ -66,29 +69,38 @@ final class Version extends BaseTag implements Factory\StaticMethod
return null;
}
$description = null;
if ($descriptionFactory !== null) {
$description = $descriptionFactory->create($matches[2] ?? '', $context);
}
return new static(
$matches[1],
$descriptionFactory->create(isset($matches[2]) ? $matches[2] : '', $context)
$description
);
}
/**
* Gets the version section of the tag.
*
* @return string
*/
public function getVersion()
public function getVersion(): ?string
{
return $this->version;
}
/**
* Returns a string representation for this tag.
*
* @return string
*/
public function __toString()
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

@@ -1,23 +1,38 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
use InvalidArgumentException;
use LogicException;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
use phpDocumentor\Reflection\DocBlock\Tag;
use phpDocumentor\Reflection\DocBlock\TagFactory;
use Webmozart\Assert\Assert;
use function array_shift;
use function count;
use function explode;
use function is_object;
use function method_exists;
use function preg_match;
use function preg_replace;
use function str_replace;
use function strpos;
use function substr;
use function trim;
final class DocBlockFactory implements DocBlockFactoryInterface
{
/** @var DocBlock\DescriptionFactory */
@@ -28,27 +43,22 @@ final class DocBlockFactory implements DocBlockFactoryInterface
/**
* Initializes this factory with the required subcontractors.
*
* @param DescriptionFactory $descriptionFactory
* @param TagFactory $tagFactory
*/
public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory)
{
$this->descriptionFactory = $descriptionFactory;
$this->tagFactory = $tagFactory;
$this->tagFactory = $tagFactory;
}
/**
* Factory method for easy instantiation.
*
* @param string[] $additionalTags
*
* @return DocBlockFactory
* @param array<string, class-string<Tag>> $additionalTags
*/
public static function createInstance(array $additionalTags = [])
public static function createInstance(array $additionalTags = []): self
{
$fqsenResolver = new FqsenResolver();
$tagFactory = new StandardTagFactory($fqsenResolver);
$fqsenResolver = new FqsenResolver();
$tagFactory = new StandardTagFactory($fqsenResolver);
$descriptionFactory = new DescriptionFactory($tagFactory);
$tagFactory->addService($descriptionFactory);
@@ -65,20 +75,18 @@ final class DocBlockFactory implements DocBlockFactoryInterface
/**
* @param object|string $docblock A string containing the DocBlock to parse or an object supporting the
* getDocComment method (such as a ReflectionClass object).
* @param Types\Context $context
* @param Location $location
*
* @return DocBlock
*/
public function create($docblock, Types\Context $context = null, Location $location = null)
public function create($docblock, ?Types\Context $context = null, ?Location $location = null): DocBlock
{
if (is_object($docblock)) {
if (!method_exists($docblock, 'getDocComment')) {
$exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method';
throw new \InvalidArgumentException($exceptionMessage);
throw new InvalidArgumentException($exceptionMessage);
}
$docblock = $docblock->getDocComment();
Assert::string($docblock);
}
Assert::stringNotEmpty($docblock);
@@ -88,14 +96,13 @@ final class DocBlockFactory implements DocBlockFactoryInterface
}
$parts = $this->splitDocBlock($this->stripDocComment($docblock));
list($templateMarker, $summary, $description, $tags) = $parts;
[$templateMarker, $summary, $description, $tags] = $parts;
return new DocBlock(
$summary,
$description ? $this->descriptionFactory->create($description, $context) : null,
array_filter($this->parseTagBlock($tags, $context), function ($tag) {
return $tag instanceof Tag;
}),
$this->parseTagBlock($tags, $context),
$context,
$location,
$templateMarker === '#@+',
@@ -103,7 +110,10 @@ final class DocBlockFactory implements DocBlockFactoryInterface
);
}
public function registerTagHandler($tagName, $handler)
/**
* @param class-string<Tag> $handler
*/
public function registerTagHandler(string $tagName, string $handler): void
{
$this->tagFactory->registerTagHandler($tagName, $handler);
}
@@ -112,12 +122,12 @@ final class DocBlockFactory implements DocBlockFactoryInterface
* Strips the asterisks from the DocBlock comment.
*
* @param string $comment String containing the comment text.
*
* @return string
*/
private function stripDocComment($comment)
private function stripDocComment(string $comment): string
{
$comment = trim(preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment));
$comment = preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]?(.*)?#u', '$1', $comment);
Assert::string($comment);
$comment = trim($comment);
// reg ex above is not able to remove */ from a single line docblock
if (substr($comment, -2) === '*/') {
@@ -127,18 +137,21 @@ final class DocBlockFactory implements DocBlockFactoryInterface
return str_replace(["\r\n", "\r"], "\n", $comment);
}
// phpcs:disable
/**
* Splits the DocBlock into a template marker, summary, description and block of tags.
*
* @param string $comment Comment to split into the sub-parts.
*
* @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
* @return string[] containing the template marker (if any), summary, description and a string containing the tags.
*
* @author Mike van Riel <me@mikevanriel.com> for extending the regex with template marker support.
*
* @return string[] containing the template marker (if any), summary, description and a string containing the tags.
* @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
*/
private function splitDocBlock($comment)
private function splitDocBlock(string $comment) : array
{
// phpcs:enable
// Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
// method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
// performance impact of running a regular expression
@@ -148,7 +161,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
// clears all extra horizontal whitespace from the line endings to prevent parsing issues
$comment = preg_replace('/\h*$/Sum', '', $comment);
Assert::string($comment);
/*
* Splits the docblock into a template marker, summary, description and tags section.
*
@@ -176,7 +189,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface
[^\n.]+
(?:
(?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines
[\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
[\n.]* (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
[^\n.]+ # Include anything else
)*
\.?
@@ -214,20 +227,21 @@ final class DocBlockFactory implements DocBlockFactoryInterface
/**
* Creates the tag objects.
*
* @param string $tags Tag block to parse.
* @param string $tags Tag block to parse.
* @param Types\Context $context Context of the parsed Tag
*
* @return DocBlock\Tag[]
*/
private function parseTagBlock($tags, Types\Context $context)
private function parseTagBlock(string $tags, Types\Context $context): array
{
$tags = $this->filterTagBlock($tags);
if (!$tags) {
if ($tags === null) {
return [];
}
$result = $this->splitTagBlockIntoTagLines($tags);
foreach ($result as $key => $tagLine) {
$result = [];
$lines = $this->splitTagBlockIntoTagLines($tags);
foreach ($lines as $key => $tagLine) {
$result[$key] = $this->tagFactory->create(trim($tagLine), $context);
}
@@ -235,40 +249,36 @@ final class DocBlockFactory implements DocBlockFactoryInterface
}
/**
* @param string $tags
*
* @return string[]
*/
private function splitTagBlockIntoTagLines($tags)
private function splitTagBlockIntoTagLines(string $tags): array
{
$result = [];
foreach (explode("\n", $tags) as $tag_line) {
if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
$result[] = $tag_line;
foreach (explode("\n", $tags) as $tagLine) {
if ($tagLine !== '' && strpos($tagLine, '@') === 0) {
$result[] = $tagLine;
} else {
$result[count($result) - 1] .= "\n" . $tag_line;
$result[count($result) - 1] .= "\n" . $tagLine;
}
}
return $result;
}
/**
* @param $tags
* @return string
*/
private function filterTagBlock($tags)
private function filterTagBlock(string $tags): ?string
{
$tags = trim($tags);
if (!$tags) {
return null;
}
if ('@' !== $tags[0]) {
if ($tags[0] !== '@') {
// @codeCoverageIgnoreStart
// Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that
// we didn't foresee.
throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags);
throw new LogicException('A tag block started with text instead of an at-sign(@): ' . $tags);
// @codeCoverageIgnoreEnd
}

View File

@@ -1,23 +1,23 @@
<?php
declare(strict_types=1);
namespace phpDocumentor\Reflection;
use phpDocumentor\Reflection\DocBlock\Tag;
// phpcs:ignore SlevomatCodingStandard.Classes.SuperfluousInterfaceNaming.SuperfluousSuffix
interface DocBlockFactoryInterface
{
/**
* Factory method for easy instantiation.
*
* @param string[] $additionalTags
*
* @return DocBlockFactory
* @param array<string, class-string<Tag>> $additionalTags
*/
public static function createInstance(array $additionalTags = []);
public static function createInstance(array $additionalTags = []): DocBlockFactory;
/**
* @param string $docblock
* @param Types\Context $context
* @param Location $location
*
* @return DocBlock
* @param string|object $docblock
*/
public function create($docblock, Types\Context $context = null, Location $location = null);
public function create($docblock, ?Types\Context $context = null, ?Location $location = null): DocBlock;
}

View File

@@ -0,0 +1,44 @@
<?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,62 @@
<?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 Webmozart\Assert\Assert;
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 $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());
}
Assert::allString($parts);
return $parts;
}
}

View File

@@ -1,3 +1,11 @@
[![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=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)
TypeResolver and FqsenResolver
==============================
@@ -22,8 +30,7 @@ The easiest way to install this library is with [Composer](https://getcomposer.o
## Examples
Ready to dive in and don't want to read through all that text below? Just consult the [examples](examples) folder and
check which type of action that your want to accomplish.
Ready to dive in and don't want to read through all that text below? Just consult the [examples](examples) folder and check which type of action that your want to accomplish.
## On Types and Element Names
@@ -39,6 +46,7 @@ The TypeResolver can resolve:
- a php primitive or pseudo-primitive such as a string or void (`@var string` or `@return void`).
- a composite such as an array of string (`@var string[]`).
- a compound such as a string or integer (`@var string|integer`).
- an array expression (`@var (string|TypeResolver)[]`)
- an object or interface such as the TypeResolver class (`@var TypeResolver`
or `@var \phpDocumentor\Reflection\TypeResolver`)
@@ -58,8 +66,7 @@ Where the FqsenResolver can resolve:
## Resolving a type
In order to resolve a type you will have to instantiate the class `\phpDocumentor\Reflection\TypeResolver`
and call its `resolve` method like this:
In order to resolve a type you will have to instantiate the class `\phpDocumentor\Reflection\TypeResolver` and call its `resolve` method like this:
```php
$typeResolver = new \phpDocumentor\Reflection\TypeResolver();
@@ -70,31 +77,30 @@ In this example you will receive a Value Object of class `\phpDocumentor\Reflect
elements, one of type `\phpDocumentor\Reflection\Types\String_` and one of type
`\phpDocumentor\Reflection\Types\Integer`.
The real power of this resolver is in its capability to expand partial class names into fully qualified class names; but
in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will inform the resolver
in which namespace the given expression occurs and which namespace aliases (or imports) apply.
The real power of this resolver is in its capability to expand partial class names into fully qualified class names; but in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.
### Resolving nullable types
Php 7.1 introduced nullable types e.g. `?string`. Type resolver will resolve the original type without the nullable notation `?`
just like it would do without the `?`. After that the type is wrapped in a `\phpDocumentor\Reflection\Types\Nullable` object.
The `Nullable` type has a method to fetch the actual type.
## Resolving an FQSEN
A Fully Qualified Structural Element Name is a reference to another element in your code bases and can be resolved using
the `\phpDocumentor\Reflection\FqsenResolver` class' `resolve` method, like this:
A Fully Qualified Structural Element Name is a reference to another element in your code bases and can be resolved using the `\phpDocumentor\Reflection\FqsenResolver` class' `resolve` method, like this:
```php
$fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver();
$fqsen = $fqsenResolver->resolve('\phpDocumentor\Reflection\FqsenResolver::resolve()');
```
In this example we resolve a Fully Qualified Structural Element Name (meaning that it includes the full namespace, class
name and element name) and receive a Value Object of type `\phpDocumentor\Reflection\Fqsen`.
In this example we resolve a Fully Qualified Structural Element Name (meaning that it includes the full namespace, class name and element name) and receive a Value Object of type `\phpDocumentor\Reflection\Fqsen`.
The real power of this resolver is in its capability to expand partial element names into Fully Qualified Structural
Element Names; but in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will
inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.
The real power of this resolver is in its capability to expand partial element names into Fully Qualified Structural Element Names; but in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.
## Resolving partial Classes and Structural Element Names
Perhaps the best feature of this library is that it knows how to resolve partial class names into fully qualified class
names.
Perhaps the best feature of this library is that it knows how to resolve partial class names into fully qualified class names.
For example, you have this file:
@@ -116,9 +122,8 @@ class Classy
```
Suppose that you would want to resolve (and expand) the type in the `@var` tag and the element name in the `@see` tag.
For the resolvers to know how to expand partial names you have to provide a bit of _Context_ for them by instantiating
a new class named `\phpDocumentor\Reflection\Types\Context` with the name of the namespace and the aliases that are in
play.
For the resolvers to know how to expand partial names you have to provide a bit of _Context_ for them by instantiating a new class named `\phpDocumentor\Reflection\Types\Context` with the name of the namespace and the aliases that are in play.
### Creating a Context
@@ -131,9 +136,7 @@ $context = new \phpDocumentor\Reflection\Types\Context(
);
```
Or by using the `\phpDocumentor\Reflection\Types\ContextFactory` to instantiate a new context based on a Reflector
object or by providing the namespace that you'd like to extract and the source code of the file in which the given
type expression occurs.
Or by using the `\phpDocumentor\Reflection\Types\ContextFactory` to instantiate a new context based on a Reflector object or by providing the namespace that you'd like to extract and the source code of the file in which the given type expression occurs.
```php
$contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
@@ -149,8 +152,7 @@ $context = $contextFactory->createForNamespace('\My\Example', file_get_contents(
### Using the Context
After you have obtained a Context it is just a matter of passing it along with the `resolve` method of either Resolver
class as second argument and the Resolvers will take this into account when resolving partial names.
After you have obtained a Context it is just a matter of passing it along with the `resolve` method of either Resolver class as second argument and the Resolvers will take this into account when resolving partial names.
To obtain the resolved class name for the `@var` tag in the example above you can do:
@@ -159,24 +161,17 @@ $typeResolver = new \phpDocumentor\Reflection\TypeResolver();
$type = $typeResolver->resolve('Types\Context', $context);
```
When you do this you will receive an object of class `\phpDocumentor\Reflection\Types\Object_` for which you can call
the `getFqsen` method to receive a Value Object that represents the complete FQSEN. So that would be
`phpDocumentor\Reflection\Types\Context`.
When you do this you will receive an object of class `\phpDocumentor\Reflection\Types\Object_` for which you can call the `getFqsen` method to receive a Value Object that represents the complete FQSEN. So that would be `phpDocumentor\Reflection\Types\Context`.
> Why is the FQSEN wrapped in another object `Object_`?
>
> The resolve method of the TypeResolver only returns object with the interface `Type` and the FQSEN is a common
> type that does not represent a Type. Also: in some cases a type can represent an "Untyped Object", meaning that it
> is an object (signified by the `object` keyword) but does not refer to a specific element using an FQSEN.
> The resolve method of the TypeResolver only returns object with the interface `Type` and the FQSEN is a common type that does not represent a Type. Also: in some cases a type can represent an "Untyped Object", meaning that it is an object (signified by the `object` keyword) but does not refer to a specific element using an FQSEN.
Another example is on how to resolve the FQSEN of a method as can be seen with the `@see` tag in the example above. To
resolve that you can do the following:
Another example is on how to resolve the FQSEN of a method as can be seen with the `@see` tag in the example above. To resolve that you can do the following:
```php
$fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver();
$type = $fqsenResolver->resolve('Classy::otherFunction()', $context);
```
Because Classy is a Class in the current namespace its FQSEN will have the `My\Example` namespace and by calling the
`resolve` method of the FQSEN Resolver you will receive an `Fqsen` object that refers to
`\My\Example\Classy::otherFunction()`.
Because Classy is a Class in the current namespace its FQSEN will have the `My\Example` namespace and by calling the `resolve` method of the FQSEN Resolver you will receive an `Fqsen` object that refers to `\My\Example\Classy::otherFunction()`.

View File

@@ -1,27 +1,35 @@
{
"name": "phpdocumentor/type-resolver",
"type": "library",
"name": "phpdocumentor/type-resolver",
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"type": "library",
"license": "MIT",
"authors": [
{"name": "Mike van Riel", "email": "me@mikevanriel.com"}
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
}
],
"require": {
"php": "^5.5 || ^7.0",
"phpdocumentor/reflection-common": "^1.0"
},
"autoload": {
"psr-4": {"phpDocumentor\\Reflection\\": ["src/"]}
},
"autoload-dev": {
"psr-4": {"phpDocumentor\\Reflection\\": ["tests/unit"]}
"php": "^7.2 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^5.2||^4.8.24",
"mockery/mockery": "^0.9.4"
"ext-tokenizer": "*",
"psalm/phar": "^4.8"
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src"
}
},
"autoload-dev": {
"psr-4": {
"phpDocumentor\\Reflection\\": ["tests/unit", "tests/benchmark"]
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
"dev-1.x": "1.x-dev"
}
}
}

View File

@@ -1,25 +1,36 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
use InvalidArgumentException;
use phpDocumentor\Reflection\Types\Context;
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 */
const OPERATOR_NAMESPACE = '\\';
private const OPERATOR_NAMESPACE = '\\';
public function resolve($fqsen, Context $context = null)
public function resolve(string $fqsen, ?Context $context = null): Fqsen
{
if ($context === null) {
$context = new Context('');
@@ -34,12 +45,8 @@ class FqsenResolver
/**
* Tests whether the given type is a Fully Qualified Structural Element Name.
*
* @param string $type
*
* @return bool
*/
private function isFqsen($type)
private function isFqsen(string $type): bool
{
return strpos($type, self::OPERATOR_NAMESPACE) === 0;
}
@@ -48,13 +55,9 @@ class FqsenResolver
* Resolves a partial Structural Element Name (i.e. `Reflection\DocBlock`) to its FQSEN representation
* (i.e. `\phpDocumentor\Reflection\DocBlock`) based on the Namespace and aliases mentioned in the Context.
*
* @param string $type
* @param Context $context
*
* @return Fqsen
* @throws \InvalidArgumentException when type is not a valid FQSEN.
* @throws InvalidArgumentException When type is not a valid FQSEN.
*/
private function resolvePartialStructuralElementName($type, Context $context)
private function resolvePartialStructuralElementName(string $type, Context $context): Fqsen
{
$typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2);
@@ -63,7 +66,7 @@ class FqsenResolver
// if the first segment is not an alias; prepend namespace name and return
if (!isset($namespaceAliases[$typeParts[0]])) {
$namespace = $context->getNamespace();
if ('' !== $namespace) {
if ($namespace !== '') {
$namespace .= self::OPERATOR_NAMESPACE;
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class CallableString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'callable-string';
}
}

View File

@@ -0,0 +1,40 @@
<?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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class HtmlEscapedString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'html-escaped-string';
}
}

View File

@@ -0,0 +1,61 @@
<?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\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Integer;
/**
* Value Object representing the type 'int'.
*
* @psalm-immutable
*/
final class IntegerRange extends Integer implements PseudoType
{
/** @var string */
private $minValue;
/** @var string */
private $maxValue;
public function __construct(string $minValue, string $maxValue)
{
$this->minValue = $minValue;
$this->maxValue = $maxValue;
}
public function underlyingType(): Type
{
return new Integer();
}
public function getMinValue(): string
{
return $this->minValue;
}
public function getMaxValue(): string
{
return $this->maxValue;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'int<' . $this->minValue . ', ' . $this->maxValue . '>';
}
}

View File

@@ -0,0 +1,50 @@
<?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\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Integer;
use phpDocumentor\Reflection\Types\Mixed_;
/**
* Value Object representing the type 'list'.
*
* @psalm-immutable
*/
final class List_ extends Array_ implements PseudoType
{
public function underlyingType(): Type
{
return new Array_();
}
public function __construct(?Type $valueType = null)
{
parent::__construct($valueType, new Integer());
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
if ($this->valueType instanceof Mixed_) {
return 'list';
}
return 'list<' . $this->valueType . '>';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class LiteralString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'literal-string';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class LowercaseString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'lowercase-string';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Integer;
/**
* Value Object representing the type 'int'.
*
* @psalm-immutable
*/
final class NegativeInteger extends Integer implements PseudoType
{
public function underlyingType(): Type
{
return new Integer();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'negative-int';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class NonEmptyLowercaseString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'non-empty-lowercase-string';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class NonEmptyString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'non-empty-string';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class NumericString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'numeric-string';
}
}

View File

@@ -0,0 +1,47 @@
<?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\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\AggregatedType;
use phpDocumentor\Reflection\Types\Compound;
use phpDocumentor\Reflection\Types\Float_;
use phpDocumentor\Reflection\Types\Integer;
/**
* Value Object representing the 'numeric' pseudo-type, which is either a numeric-string, integer or float.
*
* @psalm-immutable
*/
final class Numeric_ extends AggregatedType implements PseudoType
{
public function __construct()
{
AggregatedType::__construct([new NumericString(), new Integer(), new Float_()], '|');
}
public function underlyingType(): Type
{
return new Compound([new NumericString(), new Integer(), new Float_()]);
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'numeric';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Integer;
/**
* Value Object representing the type 'int'.
*
* @psalm-immutable
*/
final class PositiveInteger extends Integer implements PseudoType
{
public function underlyingType(): Type
{
return new Integer();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'positive-int';
}
}

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 http://phpdoc.org
*/
namespace phpDocumentor\Reflection\PseudoTypes;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\String_;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class TraitString extends String_ implements PseudoType
{
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'trait-string';
}
}

View File

@@ -0,0 +1,40 @@
<?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

@@ -1,18 +1,25 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
/**
* @psalm-immutable
*/
interface Type
{
public function __toString();
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string;
}

View File

@@ -1,68 +1,134 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection;
use ArrayIterator;
use InvalidArgumentException;
use phpDocumentor\Reflection\PseudoTypes\IntegerRange;
use phpDocumentor\Reflection\PseudoTypes\List_;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\ArrayKey;
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\InterfaceString;
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_key_exists;
use function array_pop;
use function array_values;
use function class_exists;
use function class_implements;
use function count;
use function current;
use function end;
use function in_array;
use function is_numeric;
use function key;
use function preg_split;
use function strpos;
use function strtolower;
use function trim;
use const PREG_SPLIT_DELIM_CAPTURE;
use const PREG_SPLIT_NO_EMPTY;
final class TypeResolver
{
/** @var string Definition of the ARRAY operator for types */
const OPERATOR_ARRAY = '[]';
private const OPERATOR_ARRAY = '[]';
/** @var string Definition of the NAMESPACE operator in PHP */
const OPERATOR_NAMESPACE = '\\';
private const OPERATOR_NAMESPACE = '\\';
/** @var string[] List of recognized keywords and unto which Value Object they map */
private $keywords = array(
/** @var int the iterator parser is inside a compound context */
private const PARSER_IN_COMPOUND = 0;
/** @var int the iterator parser is inside a nullable expression context */
private const PARSER_IN_NULLABLE = 1;
/** @var int the iterator parser is inside an array expression context */
private const PARSER_IN_ARRAY_EXPRESSION = 2;
/** @var int the iterator parser is inside a collection expression context */
private const PARSER_IN_COLLECTION_EXPRESSION = 3;
/**
* @var array<string, string> List of recognized keywords and unto which Value Object they map
* @psalm-var array<string, class-string<Type>>
*/
private $keywords = [
'string' => Types\String_::class,
'class-string' => Types\ClassString::class,
'interface-string' => Types\InterfaceString::class,
'html-escaped-string' => PseudoTypes\HtmlEscapedString::class,
'lowercase-string' => PseudoTypes\LowercaseString::class,
'non-empty-lowercase-string' => PseudoTypes\NonEmptyLowercaseString::class,
'non-empty-string' => PseudoTypes\NonEmptyString::class,
'numeric-string' => PseudoTypes\NumericString::class,
'numeric' => PseudoTypes\Numeric_::class,
'trait-string' => PseudoTypes\TraitString::class,
'int' => Types\Integer::class,
'integer' => Types\Integer::class,
'positive-int' => PseudoTypes\PositiveInteger::class,
'negative-int' => PseudoTypes\NegativeInteger::class,
'bool' => Types\Boolean::class,
'boolean' => Types\Boolean::class,
'real' => Types\Float_::class,
'float' => Types\Float_::class,
'double' => Types\Float_::class,
'object' => Object_::class,
'object' => Types\Object_::class,
'mixed' => Types\Mixed_::class,
'array' => Array_::class,
'array' => Types\Array_::class,
'array-key' => Types\ArrayKey::class,
'resource' => Types\Resource_::class,
'void' => Types\Void_::class,
'null' => Types\Null_::class,
'scalar' => Types\Scalar::class,
'callback' => Types\Callable_::class,
'callable' => Types\Callable_::class,
'false' => Types\Boolean::class,
'true' => Types\Boolean::class,
'callable-string' => PseudoTypes\CallableString::class,
'false' => PseudoTypes\False_::class,
'true' => PseudoTypes\True_::class,
'literal-string' => PseudoTypes\LiteralString::class,
'self' => Types\Self_::class,
'$this' => Types\This::class,
'static' => Types\Static_::class,
'parent' => Types\Parent_::class,
'iterable' => Iterable_::class,
);
'iterable' => Types\Iterable_::class,
'never' => Types\Never_::class,
'list' => PseudoTypes\List_::class,
];
/** @var FqsenResolver */
/**
* @var FqsenResolver
* @psalm-readonly
*/
private $fqsenResolver;
/**
* Initializes this TypeResolver with the means to create and resolve Fqsen objects.
*
* @param FqsenResolver $fqsenResolver
*/
public function __construct(FqsenResolver $fqsenResolver = null)
public function __construct(?FqsenResolver $fqsenResolver = null)
{
$this->fqsenResolver = $fqsenResolver ?: new FqsenResolver();
}
@@ -77,74 +143,257 @@ final class TypeResolver
* This method only works as expected if the namespace and aliases are set;
* no dynamic reflection is being performed here.
*
* @param string $type The relative or absolute type.
* @param Context $context
*
* @uses Context::getNamespace() to determine with what to prefix the type name.
* @uses Context::getNamespaceAliases() to check whether the first part of the relative type name should not be
* replaced with another namespace.
* replaced with another namespace.
* @uses Context::getNamespace() to determine with what to prefix the type name.
*
* @return Type|null
* @param string $type The relative or absolute type.
*/
public function resolve($type, Context $context = null)
public function resolve(string $type, ?Context $context = null): Type
{
if (!is_string($type)) {
throw new \InvalidArgumentException(
'Attempted to resolve type but it appeared not to be a string, received: ' . var_export($type, true)
);
}
$type = trim($type);
if (!$type) {
throw new \InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty');
throw new InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty');
}
if ($context === null) {
$context = new Context('');
}
// split the type string into tokens `|`, `?`, `<`, `>`, `,`, `(`, `)`, `[]`, '<', '>' and type names
$tokens = preg_split(
'/(\\||\\?|<|>|&|, ?|\\(|\\)|\\[\\]+)/',
$type,
-1,
PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
);
if ($tokens === false) {
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);
}
/**
* Analyse each tokens and creates types
*
* @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 === '|' || $token === '&') {
if (count($types) === 0) {
throw new RuntimeException(
'A type is missing before a type separator'
);
}
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 (
!in_array($parserContext, [
self::PARSER_IN_COMPOUND,
self::PARSER_IN_ARRAY_EXPRESSION,
self::PARSER_IN_COLLECTION_EXPRESSION,
], true)
) {
throw new RuntimeException(
'Unexpected nullable character'
);
}
$tokens->next();
$type = $this->parseTypes($tokens, $context, self::PARSER_IN_NULLABLE);
$types[] = new Nullable($type);
} elseif ($token === '(') {
$tokens->next();
$type = $this->parseTypes($tokens, $context, self::PARSER_IN_ARRAY_EXPRESSION);
$token = $tokens->current();
if ($token === null) { // Someone did not properly close their array expression ..
break;
}
$tokens->next();
$resolvedType = new Expression($type);
$types[] = $resolvedType;
} elseif ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION && isset($token[0]) && $token[0] === ')') {
break;
} elseif ($token === '<') {
if (count($types) === 0) {
throw new RuntimeException(
'Unexpected collection operator "<", class name is missing'
);
}
$classType = array_pop($types);
if ($classType !== null) {
if ((string) $classType === 'class-string') {
$types[] = $this->resolveClassString($tokens, $context);
} elseif ((string) $classType === 'int') {
$types[] = $this->resolveIntRange($tokens);
} elseif ((string) $classType === 'interface-string') {
$types[] = $this->resolveInterfaceString($tokens, $context);
} else {
$types[] = $this->resolveCollection($tokens, $classType, $context);
}
}
$tokens->next();
} elseif (
$parserContext === self::PARSER_IN_COLLECTION_EXPRESSION
&& ($token === '>' || trim($token) === ',')
) {
break;
} elseif ($token === self::OPERATOR_ARRAY) {
end($types);
$last = key($types);
if ($last === null) {
throw new InvalidArgumentException('Unexpected array operator');
}
$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();
if ($parserContext === self::PARSER_IN_NULLABLE) {
return $type;
}
$types[] = $type;
}
}
if ($token === '|' || $token === '&') {
throw new RuntimeException(
'A type is missing after a type separator'
);
}
if (count($types) === 0) {
if ($parserContext === self::PARSER_IN_NULLABLE) {
throw new RuntimeException(
'A type is missing after a nullable character'
);
}
if ($parserContext === self::PARSER_IN_ARRAY_EXPRESSION) {
throw new RuntimeException(
'A type is missing in an array expression'
);
}
if ($parserContext === self::PARSER_IN_COLLECTION_EXPRESSION) {
throw new RuntimeException(
'A type is missing in a collection expression'
);
}
} elseif (count($types) === 1) {
return current($types);
}
if ($compoundToken === '|') {
return new Compound(array_values($types));
}
return new Intersection(array_values($types));
}
/**
* resolve the given type into a type object
*
* @param string $type the type string, representing a single type
*
* @return Type|Array_|Object_
*
* @psalm-mutation-free
*/
private function resolveSingleType(string $type, Context $context): object
{
switch (true) {
case $this->isNullableType($type):
return $this->resolveNullableType($type, $context);
case $this->isKeyword($type):
return $this->resolveKeyword($type);
case ($this->isCompoundType($type)):
return $this->resolveCompoundType($type, $context);
case $this->isTypedArray($type):
return $this->resolveTypedArray($type, $context);
case $this->isFqsen($type):
return $this->resolveTypedObject($type);
case $this->isPartialStructuralElementName($type):
return $this->resolveTypedObject($type, $context);
// @codeCoverageIgnoreStart
default:
// I haven't got the foggiest how the logic would come here but added this as a defense.
throw new \RuntimeException(
throw new RuntimeException(
'Unable to resolve type "' . $type . '", there is no known method to resolve it'
);
}
// @codeCoverageIgnoreEnd
}
/**
* Adds a keyword to the list of Keywords and associates it with a specific Value Object.
*
* @param string $keyword
* @param string $typeClassName
*
* @return void
* @psalm-param class-string<Type> $typeClassName
*/
public function addKeyword($keyword, $typeClassName)
public function addKeyword(string $keyword, string $typeClassName): void
{
if (!class_exists($typeClassName)) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class'
. ' but we could not find the class ' . $typeClassName
);
}
if (!in_array(Type::class, class_implements($typeClassName))) {
throw new \InvalidArgumentException(
$interfaces = class_implements($typeClassName);
if ($interfaces === false) {
throw new InvalidArgumentException(
'The Value Object that needs to be created with a keyword "' . $keyword . '" must be an existing class'
. ' but we could not find the class ' . $typeClassName
);
}
if (!in_array(Type::class, $interfaces, true)) {
throw new InvalidArgumentException(
'The class "' . $typeClassName . '" must implement the interface "phpDocumentor\Reflection\Type"'
);
}
@@ -152,28 +401,16 @@ 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.
*
* @return bool
*/
private function isTypedArray($type)
{
return substr($type, -2) === self::OPERATOR_ARRAY;
}
/**
* Detects whether the given type represents a PHPDoc keyword.
*
* @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
*
* @return bool
* @psalm-mutation-free
*/
private function isKeyword($type)
private function isKeyword(string $type): bool
{
return in_array(strtolower($type), array_keys($this->keywords), true);
return array_key_exists(strtolower($type), $this->keywords);
}
/**
@@ -181,70 +418,29 @@ final class TypeResolver
*
* @param string $type A relative or absolute type as defined in the phpDocumentor documentation.
*
* @return bool
* @psalm-mutation-free
*/
private function isPartialStructuralElementName($type)
private function isPartialStructuralElementName(string $type): bool
{
return ($type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type);
return (isset($type[0]) && $type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type);
}
/**
* Tests whether the given type is a Fully Qualified Structural Element Name.
*
* @param string $type
*
* @return bool
* @psalm-mutation-free
*/
private function isFqsen($type)
private function isFqsen(string $type): bool
{
return strpos($type, self::OPERATOR_NAMESPACE) === 0;
}
/**
* Tests whether the given type is a compound type (i.e. `string|int`).
*
* @param string $type
*
* @return bool
*/
private function isCompoundType($type)
{
return strpos($type, '|') !== false;
}
/**
* Test whether the given type is a nullable type (i.e. `?string`)
*
* @param string $type
*
* @return bool
*/
private function isNullableType($type)
{
return $type[0] === '?';
}
/**
* Resolves the given typed array string (i.e. `string[]`) into an Array object with the right types set.
*
* @param string $type
* @param Context $context
*
* @return Array_
*/
private function resolveTypedArray($type, Context $context)
{
return new Array_($this->resolve(substr($type, 0, -2), $context));
}
/**
* Resolves the given keyword (such as `string`) into a Type object representing that keyword.
*
* @param string $type
*
* @return Type
* @psalm-mutation-free
*/
private function resolveKeyword($type)
private function resolveKeyword(string $type): Type
{
$className = $this->keywords[strtolower($type)];
@@ -254,45 +450,251 @@ final class TypeResolver
/**
* Resolves the given FQSEN string into an FQSEN object.
*
* @param string $type
* @param Context|null $context
*
* @return Object_
* @psalm-mutation-free
*/
private function resolveTypedObject($type, Context $context = null)
private function resolveTypedObject(string $type, ?Context $context = null): Object_
{
return new Object_($this->fqsenResolver->resolve($type, $context));
}
/**
* Resolves a compound type (i.e. `string|int`) into the appropriate Type objects or FQSEN.
* Resolves class string
*
* @param string $type
* @param Context $context
*
* @return Compound
* @param ArrayIterator<int, (string|null)> $tokens
*/
private function resolveCompoundType($type, Context $context)
private function resolveClassString(ArrayIterator $tokens, Context $context): Type
{
$types = [];
$tokens->next();
foreach (explode('|', $type) as $part) {
$types[] = $this->resolve($part, $context);
$classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
if (!$classType instanceof Object_ || $classType->getFqsen() === null) {
throw new RuntimeException(
$classType . ' is not a class string'
);
}
return new Compound($types);
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'class-string: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
return new ClassString($classType->getFqsen());
}
/**
* Resolve nullable types (i.e. `?string`) into a Nullable type wrapper
* Resolves integer ranges
*
* @param string $type
* @param Context $context
*
* @return Nullable
* @param ArrayIterator<int, (string|null)> $tokens
*/
private function resolveNullableType($type, Context $context)
private function resolveIntRange(ArrayIterator $tokens): Type
{
return new Nullable($this->resolve(ltrim($type, '?'), $context));
$tokens->next();
$token = '';
$minValue = null;
$maxValue = null;
$commaFound = false;
$tokenCounter = 0;
while ($tokens->valid()) {
$tokenCounter++;
$token = $tokens->current();
if ($token === null) {
throw new RuntimeException(
'Unexpected nullable character'
);
}
$token = trim($token);
if ($token === '>') {
break;
}
if ($token === ',') {
$commaFound = true;
}
if ($commaFound === false && $minValue === null) {
if (is_numeric($token) || $token === 'max' || $token === 'min') {
$minValue = $token;
}
}
if ($commaFound === true && $maxValue === null) {
if (is_numeric($token) || $token === 'max' || $token === 'min') {
$maxValue = $token;
}
}
$tokens->next();
}
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'interface-string: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
if ($minValue === null || $maxValue === null || $tokenCounter > 4) {
throw new RuntimeException(
'int<min,max> has not the correct format'
);
}
return new IntegerRange($minValue, $maxValue);
}
/**
* Resolves class string
*
* @param ArrayIterator<int, (string|null)> $tokens
*/
private function resolveInterfaceString(ArrayIterator $tokens, Context $context): Type
{
$tokens->next();
$classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
if (!$classType instanceof Object_ || $classType->getFqsen() === null) {
throw new RuntimeException(
$classType . ' is not a interface string'
);
}
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'interface-string: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
return new InterfaceString($classType->getFqsen());
}
/**
* 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
{
$isArray = ((string) $classType === 'array');
$isIterable = ((string) $classType === 'iterable');
$isList = ((string) $classType === 'list');
// allow only "array", "iterable" or class name before "<"
if (
!$isArray && !$isIterable && !$isList
&& (!$classType instanceof Object_ || $classType->getFqsen() === null)
) {
throw new RuntimeException(
$classType . ' is not a collection'
);
}
$tokens->next();
$valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
$keyType = null;
$token = $tokens->current();
if ($token !== null && trim($token) === ',' && !$isList) {
// if we have a comma, then we just parsed the key type, not the value type
$keyType = $valueType;
if ($isArray) {
// check the key type for an "array" collection. We allow only
// strings or integers.
if (
!$keyType instanceof ArrayKey &&
!$keyType instanceof String_ &&
!$keyType instanceof Integer &&
!$keyType instanceof Compound
) {
throw new RuntimeException(
'An array can have only integers or strings as keys'
);
}
if ($keyType instanceof Compound) {
foreach ($keyType->getIterator() as $item) {
if (
!$item instanceof ArrayKey &&
!$item instanceof String_ &&
!$item instanceof Integer
) {
throw new RuntimeException(
'An array can have only integers or strings as keys'
);
}
}
}
}
$tokens->next();
// now let's parse the value type
$valueType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
}
$token = $tokens->current();
if ($token !== '>') {
if (empty($token)) {
throw new RuntimeException(
'Collection: ">" is missing'
);
}
throw new RuntimeException(
'Unexpected character "' . $token . '", ">" is missing'
);
}
if ($isArray) {
return new Array_($valueType, $keyType);
}
if ($isIterable) {
return new Iterable_($valueType, $keyType);
}
if ($isList) {
return new List_($valueType);
}
if ($classType instanceof Object_) {
return $this->makeCollectionFromObject($classType, $valueType, $keyType);
}
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

@@ -0,0 +1,83 @@
<?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 a list of values. This is an abstract class for Array_ and Collection.
*
* @psalm-immutable
*/
abstract class AbstractList implements Type
{
/** @var Type */
protected $valueType;
/** @var Type|null */
protected $keyType;
/** @var Type */
protected $defaultKeyType;
/**
* Initializes this representation of an array with the given Type.
*/
public function __construct(?Type $valueType = null, ?Type $keyType = null)
{
if ($valueType === null) {
$valueType = new Mixed_();
}
$this->valueType = $valueType;
$this->defaultKeyType = new Compound([new String_(), new Integer()]);
$this->keyType = $keyType;
}
/**
* Returns the type for the keys of this array.
*/
public function getKeyType(): Type
{
return $this->keyType ?? $this->defaultKeyType;
}
/**
* 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
{
if ($this->keyType) {
return 'array<' . $this->keyType . ',' . $this->valueType . '>';
}
if ($this->valueType instanceof Mixed_) {
return 'array';
}
if ($this->valueType instanceof Compound) {
return '(' . $this->valueType . ')[]';
}
return $this->valueType . '[]';
}
}

View File

@@ -0,0 +1,125 @@
<?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

@@ -0,0 +1,42 @@
<?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\PseudoType;
use phpDocumentor\Reflection\Type;
/**
* Value Object representing a array-key Type.
*
* A array-key Type is the supertype (but not a union) of int and string.
*
* @psalm-immutable
*/
final class ArrayKey extends AggregatedType implements PseudoType
{
public function __construct()
{
parent::__construct([new String_(), new Integer()], '|');
}
public function underlyingType(): Type
{
return new Compound([new String_(), new Integer()]);
}
public function __toString(): string
{
return 'array-key';
}
}

View File

@@ -1,19 +1,18 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\Types;
use phpDocumentor\Reflection\Type;
/**
* Represents an array type as described in the PSR-5, the PHPDoc Standard.
*
@@ -22,65 +21,9 @@ use phpDocumentor\Reflection\Type;
* 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_ implements Type
class Array_ extends AbstractList
{
/** @var Type */
private $valueType;
/** @var Type */
private $keyType;
/**
* Initializes this representation of an array with the given Type or Fqsen.
*
* @param Type $valueType
* @param Type $keyType
*/
public function __construct(Type $valueType = null, Type $keyType = null)
{
if ($keyType === null) {
$keyType = new Compound([ new String_(), new Integer() ]);
}
if ($valueType === null) {
$valueType = new Mixed_();
}
$this->valueType = $valueType;
$this->keyType = $keyType;
}
/**
* Returns the type for the keys of this array.
*
* @return Type
*/
public function getKeyType()
{
return $this->keyType;
}
/**
* Returns the value for the keys of this array.
*
* @return Type
*/
public function getValueType()
{
return $this->valueType;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
{
if ($this->valueType instanceof Mixed_) {
return 'array';
}
return $this->valueType . '[]';
}
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,15 +17,15 @@ 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.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'bool';
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,15 +17,15 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a Callable type.
*
* @psalm-immutable
*/
final class Callable_ implements Type
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'callable';
}

View File

@@ -0,0 +1,62 @@
<?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\Fqsen;
use phpDocumentor\Reflection\PseudoType;
use phpDocumentor\Reflection\Type;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class ClassString extends String_ implements PseudoType
{
/** @var Fqsen|null */
private $fqsen;
/**
* Initializes this representation of a class string with the given Fqsen.
*/
public function __construct(?Fqsen $fqsen = null)
{
$this->fqsen = $fqsen;
}
public function underlyingType(): Type
{
return new String_();
}
/**
* Returns the FQSEN associated with this object.
*/
public function getFqsen(): ?Fqsen
{
return $this->fqsen;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
if ($this->fqsen === null) {
return 'class-string';
}
return 'class-string<' . (string) $this->fqsen . '>';
}
}

View File

@@ -0,0 +1,68 @@
<?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\Fqsen;
use phpDocumentor\Reflection\Type;
/**
* Represents a collection type as described in the PSR-5, the PHPDoc Standard.
*
* A collection can be represented in two forms:
*
* 1. `ACollectionObject<aValueType>`
* 2. `ACollectionObject<aValueType,aKeyType>`
*
* - 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
{
/** @var Fqsen|null */
private $fqsen;
/**
* Initializes this representation of an array with the given Type or Fqsen.
*/
public function __construct(?Fqsen $fqsen, Type $valueType, ?Type $keyType = null)
{
parent::__construct($valueType, $keyType);
$this->fqsen = $fqsen;
}
/**
* Returns the FQSEN associated with this object.
*/
public function getFqsen(): ?Fqsen
{
return $this->fqsen;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
$objectType = (string) ($this->fqsen ?? 'object');
if ($this->keyType === null) {
return $objectType . '<' . $this->valueType . '>';
}
return $objectType . '<' . $this->keyType . ',' . $this->valueType . '>';
}
}

View File

@@ -1,19 +1,18 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\Types;
use ArrayIterator;
use IteratorAggregate;
use phpDocumentor\Reflection\Type;
/**
@@ -22,72 +21,18 @@ use phpDocumentor\Reflection\Type;
* 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
* @throws \InvalidArgumentException when types are not all instance of Type
* @param array<Type> $types
*/
public function __construct(array $types)
{
foreach ($types as $type) {
if (!$type instanceof Type) {
throw new \InvalidArgumentException('A compound type can only have other types as elements');
}
}
$this->types = $types;
}
/**
* Returns the type at the given index.
*
* @param integer $index
*
* @return Type|null
*/
public function get($index)
{
if (!$this->has($index)) {
return null;
}
return $this->types[$index];
}
/**
* Tests if this compound type has a type with the given index.
*
* @param integer $index
*
* @return bool
*/
public function has($index)
{
return isset($this->types[$index]);
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
{
return implode('|', $this->types);
}
/**
* {@inheritdoc}
*/
public function getIterator()
{
return new ArrayIterator($this->types);
parent::__construct($types, '|');
}
}

View File

@@ -1,17 +1,22 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\Types;
use function strlen;
use function substr;
use function trim;
/**
* Provides information about the Context in which the DocBlock occurs that receives this context.
*
@@ -25,32 +30,39 @@ namespace phpDocumentor\Reflection\Types;
*
* @see ContextFactory::createFromClassReflector()
* @see ContextFactory::createForNamespace()
*
* @psalm-immutable
*/
final class Context
{
/** @var string The current namespace. */
private $namespace;
/** @var array List of namespace aliases => Fully Qualified Namespace. */
/**
* @var string[] List of namespace aliases => Fully Qualified Namespace.
* @psalm-var array<string, string>
*/
private $namespaceAliases;
/**
* Initializes the new context and normalizes all passed namespaces to be in Qualified Namespace Name (QNN)
* format (without a preceding `\`).
*
* @param string $namespace The namespace where this DocBlock resides in.
* @param array $namespaceAliases List of namespace aliases => Fully Qualified Namespace.
* @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($namespace, array $namespaceAliases = [])
public function __construct(string $namespace, array $namespaceAliases = [])
{
$this->namespace = ('global' !== $namespace && 'default' !== $namespace)
? trim((string)$namespace, '\\')
$this->namespace = $namespace !== 'global' && $namespace !== 'default'
? trim($namespace, '\\')
: '';
foreach ($namespaceAliases as $alias => $fqnn) {
if ($fqnn[0] === '\\') {
$fqnn = substr($fqnn, 1);
}
if ($fqnn[strlen($fqnn) - 1] === '\\') {
$fqnn = substr($fqnn, 0, -1);
}
@@ -63,10 +75,8 @@ final class Context
/**
* Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in.
*
* @return string
*/
public function getNamespace()
public function getNamespace(): string
{
return $this->namespace;
}
@@ -76,8 +86,9 @@ final class Context
* the alias for the imported Namespace.
*
* @return string[]
* @psalm-return array<string, string>
*/
public function getNamespaceAliases()
public function getNamespaceAliases(): array
{
return $this->namespaceAliases;
}

View File

@@ -1,17 +1,60 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\Types;
use ArrayIterator;
use InvalidArgumentException;
use ReflectionClass;
use ReflectionClassConstant;
use ReflectionMethod;
use ReflectionParameter;
use ReflectionProperty;
use Reflector;
use RuntimeException;
use UnexpectedValueException;
use function define;
use function defined;
use function file_exists;
use function file_get_contents;
use function get_class;
use function in_array;
use function is_string;
use function strrpos;
use function substr;
use function token_get_all;
use function trim;
use const T_AS;
use const T_CLASS;
use const T_CURLY_OPEN;
use const T_DOLLAR_OPEN_CURLY_BRACES;
use const T_NAME_FULLY_QUALIFIED;
use const T_NAME_QUALIFIED;
use const T_NAMESPACE;
use const T_NS_SEPARATOR;
use const T_STRING;
use const T_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.
*
@@ -24,31 +67,92 @@ namespace phpDocumentor\Reflection\Types;
final class ContextFactory
{
/** The literal used at the end of a use statement. */
const T_LITERAL_END_OF_USE = ';';
private const T_LITERAL_END_OF_USE = ';';
/** The literal used between sets of use statements */
const T_LITERAL_USE_SEPARATOR = ',';
private const T_LITERAL_USE_SEPARATOR = ',';
/**
* Build a Context given a Class Reflection.
*
* @param \Reflector $reflector
*
* @see Context for more information on Contexts.
*
* @return Context
*/
public function createFromReflector(\Reflector $reflector)
public function createFromReflector(Reflector $reflector): Context
{
if (method_exists($reflector, 'getDeclaringClass')) {
$reflector = $reflector->getDeclaringClass();
if ($reflector instanceof ReflectionClass) {
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @var ReflectionClass<object> $reflector */
return $this->createFromReflectionClass($reflector);
}
$fileName = $reflector->getFileName();
$namespace = $reflector->getNamespaceName();
if ($reflector instanceof ReflectionParameter) {
return $this->createFromReflectionParameter($reflector);
}
if (file_exists($fileName)) {
return $this->createForNamespace($namespace, file_get_contents($fileName));
if ($reflector instanceof ReflectionMethod) {
return $this->createFromReflectionMethod($reflector);
}
if ($reflector instanceof ReflectionProperty) {
return $this->createFromReflectionProperty($reflector);
}
if ($reflector instanceof ReflectionClassConstant) {
return $this->createFromReflectionClassConstant($reflector);
}
throw new UnexpectedValueException('Unhandled \Reflector instance given: ' . get_class($reflector));
}
private function createFromReflectionParameter(ReflectionParameter $parameter): Context
{
$class = $parameter->getDeclaringClass();
if (!$class) {
throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName());
}
return $this->createFromReflectionClass($class);
}
private function createFromReflectionMethod(ReflectionMethod $method): Context
{
$class = $method->getDeclaringClass();
return $this->createFromReflectionClass($class);
}
private function createFromReflectionProperty(ReflectionProperty $property): Context
{
$class = $property->getDeclaringClass();
return $this->createFromReflectionClass($class);
}
private function createFromReflectionClassConstant(ReflectionClassConstant $constant): Context
{
//phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable
/** @phpstan-var ReflectionClass<object> $class */
$class = $constant->getDeclaringClass();
return $this->createFromReflectionClass($class);
}
/**
* @phpstan-param ReflectionClass<object> $class
*/
private function createFromReflectionClass(ReflectionClass $class): Context
{
$fileName = $class->getFileName();
$namespace = $class->getNamespaceName();
if (is_string($fileName) && file_exists($fileName)) {
$contents = file_get_contents($fileName);
if ($contents === false) {
throw new RuntimeException('Unable to read file "' . $fileName . '"');
}
return $this->createForNamespace($namespace, $contents);
}
return new Context($namespace, []);
@@ -57,22 +161,22 @@ final class ContextFactory
/**
* Build a Context for a namespace in the provided file contents.
*
* @param string $namespace It does not matter if a `\` precedes the namespace name, this method first normalizes.
* @param string $fileContents the file's contents to retrieve the aliases from with the given namespace.
*
* @see Context for more information on Contexts.
*
* @return Context
* @param string $namespace It does not matter if a `\` precedes the namespace name,
* this method first normalizes.
* @param string $fileContents The file's contents to retrieve the aliases from with the given namespace.
*/
public function createForNamespace($namespace, $fileContents)
public function createForNamespace(string $namespace, string $fileContents): Context
{
$namespace = trim($namespace, '\\');
$useStatements = [];
$namespace = trim($namespace, '\\');
$useStatements = [];
$currentNamespace = '';
$tokens = new \ArrayIterator(token_get_all($fileContents));
$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;
@@ -80,30 +184,37 @@ final class ContextFactory
// Fast-forward the iterator through the class so that any
// T_USE tokens found within are skipped - these are not
// valid namespace use statements so should be ignored.
$braceLevel = 0;
$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;
}
$braceLevel++;
++$braceLevel;
}
if ($tokens->current() === '}') {
$braceLevel--;
if ($currentToken === '}') {
--$braceLevel;
}
$tokens->next();
}
break;
case T_USE:
if ($currentNamespace === $namespace) {
$useStatements = array_merge($useStatements, $this->parseUseStatement($tokens));
$useStatements += $this->parseUseStatement($tokens);
}
break;
}
$tokens->next();
}
@@ -113,18 +224,16 @@ final class ContextFactory
/**
* Deduce the name from tokens when we are at the T_NAMESPACE token.
*
* @param \ArrayIterator $tokens
*
* @return string
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*/
private function parseNamespace(\ArrayIterator $tokens)
private function parseNamespace(ArrayIterator $tokens): string
{
// skip to the first string or namespace separator
$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();
}
@@ -135,22 +244,22 @@ final class ContextFactory
/**
* Deduce the names of all imports when we are at the T_USE token.
*
* @param \ArrayIterator $tokens
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*
* @return string[]
* @psalm-return array<string, string>
*/
private function parseUseStatement(\ArrayIterator $tokens)
private function parseUseStatement(ArrayIterator $tokens): array
{
$uses = [];
$continue = true;
while ($continue) {
while ($tokens->valid()) {
$this->skipToNextStringOrNamespaceSeparator($tokens);
list($alias, $fqnn) = $this->extractUseStatement($tokens);
$uses[$alias] = $fqnn;
if ($tokens->current()[0] === self::T_LITERAL_END_OF_USE) {
$continue = false;
$uses += $this->extractUseStatements($tokens);
$currentToken = $tokens->current();
if ($currentToken[0] === self::T_LITERAL_END_OF_USE) {
return $uses;
}
}
@@ -160,51 +269,152 @@ final class ContextFactory
/**
* Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token.
*
* @param \ArrayIterator $tokens
*
* @return void
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*/
private function skipToNextStringOrNamespaceSeparator(\ArrayIterator $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();
}
}
/**
* 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.
* a USE statement yet. This will return a key/value array of the alias => namespace.
*
* @param \ArrayIterator $tokens
* @param ArrayIterator<int, string|array{0:int,1:string,2:int}> $tokens
*
* @return string
* @return string[]
* @psalm-return array<string, string>
*
* @psalm-suppress TypeDoesNotContainType
*/
private function extractUseStatement(\ArrayIterator $tokens)
private function extractUseStatements(ArrayIterator $tokens): array
{
$result = [''];
while ($tokens->valid()
&& ($tokens->current()[0] !== self::T_LITERAL_USE_SEPARATOR)
&& ($tokens->current()[0] !== self::T_LITERAL_END_OF_USE)
) {
if ($tokens->current()[0] === T_AS) {
$result[] = '';
$extractedUseStatements = [];
$groupedNs = '';
$currentNs = '';
$currentAlias = '';
$state = 'start';
while ($tokens->valid()) {
$currentToken = $tokens->current();
$tokenId = is_string($currentToken) ? $currentToken : $currentToken[0];
$tokenValue = is_string($currentToken) ? null : $currentToken[1];
switch ($state) {
case 'start':
switch ($tokenId) {
case T_STRING:
case T_NS_SEPARATOR:
$currentNs .= (string) $tokenValue;
$currentAlias = $tokenValue;
break;
case T_NAME_QUALIFIED:
case T_NAME_FULLY_QUALIFIED:
$currentNs .= (string) $tokenValue;
$currentAlias = substr(
(string) $tokenValue,
(int) (strrpos((string) $tokenValue, '\\')) + 1
);
break;
case T_CURLY_OPEN:
case '{':
$state = 'grouped';
$groupedNs = $currentNs;
break;
case T_AS:
$state = 'start-alias';
break;
case self::T_LITERAL_USE_SEPARATOR:
case self::T_LITERAL_END_OF_USE:
$state = 'end';
break;
default:
break;
}
break;
case 'start-alias':
switch ($tokenId) {
case T_STRING:
$currentAlias = $tokenValue;
break;
case self::T_LITERAL_USE_SEPARATOR:
case self::T_LITERAL_END_OF_USE:
$state = 'end';
break;
default:
break;
}
break;
case 'grouped':
switch ($tokenId) {
case T_STRING:
case T_NS_SEPARATOR:
$currentNs .= (string) $tokenValue;
$currentAlias = $tokenValue;
break;
case T_AS:
$state = 'grouped-alias';
break;
case self::T_LITERAL_USE_SEPARATOR:
$state = 'grouped';
$extractedUseStatements[(string) $currentAlias] = $currentNs;
$currentNs = $groupedNs;
$currentAlias = '';
break;
case self::T_LITERAL_END_OF_USE:
$state = 'end';
break;
default:
break;
}
break;
case 'grouped-alias':
switch ($tokenId) {
case T_STRING:
$currentAlias = $tokenValue;
break;
case self::T_LITERAL_USE_SEPARATOR:
$state = 'grouped';
$extractedUseStatements[(string) $currentAlias] = $currentNs;
$currentNs = $groupedNs;
$currentAlias = '';
break;
case self::T_LITERAL_END_OF_USE:
$state = 'end';
break;
default:
break;
}
}
if ($tokens->current()[0] === T_STRING || $tokens->current()[0] === T_NS_SEPARATOR) {
$result[count($result) - 1] .= $tokens->current()[1];
if ($state === 'end') {
break;
}
$tokens->next();
}
if (count($result) == 1) {
$backslashPos = strrpos($result[0], '\\');
if (false !== $backslashPos) {
$result[] = substr($result[0], $backslashPos + 1);
} else {
$result[] = $result[0];
}
if ($groupedNs !== $currentNs) {
$extractedUseStatements[(string) $currentAlias] = $currentNs;
}
return array_reverse($result);
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

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,15 +17,15 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a Float.
*
* @psalm-immutable
*/
final class Float_ implements Type
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'float';
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -14,14 +15,17 @@ namespace phpDocumentor\Reflection\Types;
use phpDocumentor\Reflection\Type;
final class Integer implements Type
/**
* Value object representing Integer type
*
* @psalm-immutable
*/
class Integer implements Type
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'int';
}

View File

@@ -0,0 +1,56 @@
<?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\Fqsen;
use phpDocumentor\Reflection\Type;
/**
* Value Object representing the type 'string'.
*
* @psalm-immutable
*/
final class InterfaceString implements Type
{
/** @var Fqsen|null */
private $fqsen;
/**
* Initializes this representation of a class string with the given Fqsen.
*/
public function __construct(?Fqsen $fqsen = null)
{
$this->fqsen = $fqsen;
}
/**
* Returns the FQSEN associated with this object.
*/
public function getFqsen(): ?Fqsen
{
return $this->fqsen;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
if ($this->fqsen === null) {
return 'interface-string';
}
return 'interface-string<' . (string) $this->fqsen . '>';
}
}

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

@@ -1,31 +1,38 @@
<?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.
*
* @copyright 2010-2017 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\Types;
use phpDocumentor\Reflection\Type;
/**
* Value Object representing iterable type
*
* @psalm-immutable
*/
final class Iterable_ implements Type
final class Iterable_ extends AbstractList
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'iterable';
if ($this->keyType) {
return 'iterable<' . $this->keyType . ',' . $this->valueType . '>';
}
if ($this->valueType instanceof Mixed_) {
return 'iterable';
}
return 'iterable<' . $this->valueType . '>';
}
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,15 +17,15 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing an unknown, or mixed, type.
*
* @psalm-immutable
*/
final class Mixed_ implements Type
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'mixed';
}

View File

@@ -0,0 +1,35 @@
<?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;
/**
* Value Object representing the return-type 'never'.
*
* Never is generally only used when working with return types as it signifies that the method that only
* ever throw or exit.
*
* @psalm-immutable
*/
final class Never_ implements Type
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
return 'never';
}
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,15 +17,15 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a null value or type.
*
* @psalm-immutable
*/
final class Null_ implements Type
{
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return 'null';
}

View File

@@ -1,12 +1,13 @@
<?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.
*
* @copyright 2010-2017 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
@@ -16,18 +17,16 @@ use phpDocumentor\Reflection\Type;
/**
* Value Object representing a nullable type. The real type is wrapped.
*
* @psalm-immutable
*/
final class Nullable implements Type
{
/**
* @var Type
*/
/** @var Type The actual type that is wrapped */
private $realType;
/**
* Initialises this nullable type using the real type embedded
*
* @param Type $realType
*/
public function __construct(Type $realType)
{
@@ -36,20 +35,16 @@ final class Nullable implements Type
/**
* Provide access to the actual type directly, if needed.
*
* @return Type
*/
public function getActualType()
public function getActualType(): Type
{
return $this->realType;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
return '?' . $this->realType->__toString();
}

View File

@@ -1,26 +1,32 @@
<?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.
*
* @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link http://phpdoc.org
*/
namespace phpDocumentor\Reflection\Types;
use InvalidArgumentException;
use phpDocumentor\Reflection\Fqsen;
use phpDocumentor\Reflection\Type;
use function strpos;
/**
* Value Object representing an object.
*
* 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
{
@@ -30,15 +36,14 @@ final class Object_ implements Type
/**
* Initializes this object with an optional FQSEN, if not provided this object is considered 'untyped'.
*
* @param Fqsen $fqsen
* @throws \InvalidArgumentException when provided $fqsen is not a valid type.
* @throws InvalidArgumentException When provided $fqsen is not a valid type.
*/
public function __construct(Fqsen $fqsen = null)
public function __construct(?Fqsen $fqsen = null)
{
if (strpos((string)$fqsen, '::') !== false || strpos((string)$fqsen, '()') !== false) {
throw new \InvalidArgumentException(
if (strpos((string) $fqsen, '::') !== false || strpos((string) $fqsen, '()') !== false) {
throw new InvalidArgumentException(
'Object types can only refer to a class, interface or trait but a method, function, constant or '
. 'property was received: ' . (string)$fqsen
. 'property was received: ' . (string) $fqsen
);
}
@@ -47,23 +52,16 @@ final class Object_ implements Type
/**
* Returns the FQSEN associated with this object.
*
* @return Fqsen|null
*/
public function getFqsen()
public function getFqsen(): ?Fqsen
{
return $this->fqsen;
}
/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*
* @return string
*/
public function __toString()
public function __toString(): string
{
if ($this->fqsen) {
return (string)$this->fqsen;
return (string) $this->fqsen;
}
return 'object';

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