🍲dfcv🏰dd⋉(● ∸ ●)⋊@% PNG %k25u25%fgd5n! PNG %k25u25%fgd5n!PKL_](n`,reflection-common/.github/workflows/push.ymlnu[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 PKL_]bί(reflection-common/.github/dependabot.ymlnu[version: 2 updates: - package-ecosystem: composer directory: "/" schedule: interval: daily open-pull-requests-limit: 10 PKL_].oreflection-common/src/File.phpnu[lineNumber = $lineNumber; $this->columnNumber = $columnNumber; } /** * Returns the line number that is covered by this location. */ public function getLineNumber() : int { return $this->lineNumber; } /** * Returns the column number (character position on a line) for this location object. */ public function getColumnNumber() : int { return $this->columnNumber; } } PKL_]079!reflection-common/src/Project.phpnu[fqsen = $fqsen; if (isset($matches[2])) { $this->name = $matches[2]; } else { $matches = explode('\\', $fqsen); $name = end($matches); assert(is_string($name)); $this->name = trim($name, '()'); } } /** * converts this class to string. */ public function __toString() : string { return $this->fqsen; } /** * Returns the name of the element without path. */ public function getName() : string { return $this->name; } } PKL_] һOOreflection-common/README.mdnu[[![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 ================ PKL_] ļreflection-common/composer.jsonnu[{ "name": "phpdocumentor/reflection-common", "keywords": ["phpdoc", "phpDocumentor", "reflection", "static analysis", "FQSEN"], "homepage": "http://www.phpdoc.org", "description": "Common reflection classes used by phpdocumentor to reflect the code structure", "license": "MIT", "authors": [ { "name": "Jaap van Otterdijk", "email": "opensource@ijaap.nl" } ], "require": { "php": "^7.2 || ^8.0" }, "autoload" : { "psr-4" : { "phpDocumentor\\Reflection\\": "src/" } }, "require-dev": { }, "extra": { "branch-alias": { "dev-2.x": "2.x-dev" } } } PKL_]*2Ȑ99reflection-common/LICENSEnu[The MIT License (MIT) Copyright (c) 2015 phpDocumentor Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PKL_]j=&type-resolver/docs/getting-started.rstnu[=============== Getting started =============== On this page you will find a brief introduction on how to use the TypeResolver in your project. Installation ============ The TypeResolver is available on Packagist and can be installed using Composer: .. code:: bash composer require phpdocumentor/type-resolver General usage =========== After you installed the TypeResolver you can use it in your project. This can be done by creating a new instance of the :php:class:`\phpDocumentor\Reflection\TypeResolver` class and calling :php:method:`\phpDocumentor\Reflection\TypeResolver::resolve()` with the type you want to resolve. .. code:: php $typeResolver = new \phpDocumentor\Reflection\TypeResolver(); $type = $typeResolver->resolve('string'); echo get_class($type); // phpDocumentor\Reflection\Types\String_ 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 :php:class:`\phpDocumentor\Reflection\Types\Context` class that will inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply. Read more about the Context class in the next section. PKL_]699type-resolver/docs/index.rstnu[============= Type resolver ============= This project part of the phpDocumentor project. It is capable of creating an object structure of the type specifications found in the PHPDoc blocks of a project. This can be useful for static analysis of a project or other behavior that requires knowledge of the types used in a project like automatically build forms. This project aims to cover all types that are available in PHPDoc and PHP itself. And is open for extension by third party developers. .. toctree:: :maxdepth: 2 :hidden: index getting-started PKL_]\4VV-type-resolver/.phpdoc/template/base.html.twignu[{% extends 'layout.html.twig' %} {% set topMenu = { "menu": [ { "name": "About", "url": "https://phpdoc.org/"}, { "name": "Components", "url": "https://phpdoc.org/components.html"}, { "name": "Documentation", "url": "https://docs.phpdoc.org/"}, ], "social": [ { "iconClass": "fab fa-mastodon", "url": "https://phpc.social/@phpdoc"}, { "iconClass": "fab fa-github", "url": "https://github.com/phpdocumentor/typeresolver"}, { "iconClass": "fas fa-envelope-open-text", "url": "https://github.com/orgs/phpDocumentor/discussions"} ] } %} PKL_])+type-resolver/src/Types/InterfaceString.phpnu[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 . '>'; } } PKL_]0[[#type-resolver/src/Types/Integer.phpnu[ Fully Qualified Namespace. * @psalm-var array */ 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 string[] $namespaceAliases List of namespace aliases => Fully Qualified Namespace. * @psalm-param array $namespaceAliases */ public function __construct(string $namespace, array $namespaceAliases = []) { $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); } $namespaceAliases[$alias] = $fqnn; } $this->namespaceAliases = $namespaceAliases; } /** * Returns the Qualified Namespace Name (thus without `\` in front) where the associated element is in. */ public function getNamespace(): string { return $this->namespace; } /** * Returns a list of Qualified Namespace Names (thus without `\` in front) that are imported, the keys represent * the alias for the imported Namespace. * * @return string[] * @psalm-return array */ public function getNamespaceAliases(): array { return $this->namespaceAliases; } } PKL_]|A-type-resolver/src/Types/CallableParameter.phpnu[type = $type; $this->isReference = $isReference; $this->isVariadic = $isVariadic; $this->isOptional = $isOptional; $this->name = $name; } public function getName(): ?string { return $this->name; } public function getType(): Type { return $this->type; } public function isReference(): bool { return $this->isReference; } public function isVariadic(): bool { return $this->isVariadic; } public function isOptional(): bool { return $this->isOptional; } } PKL_]Jii!type-resolver/src/Types/Null_.phpnu[realType = $realType; } /** * Provide access to the actual type directly, if needed. */ public function getActualType(): Type { return $this->realType; } /** * Returns a rendered output of the Type as it would be used in a DocBlock. */ public function __toString(): string { return '?' . $this->realType->__toString(); } } PKL_]*#type-resolver/src/Types/Parent_.phpnu[ */ abstract class AggregatedType implements Type, IteratorAggregate { /** * @psalm-allow-private-mutation * @var array */ private $types = []; /** @var string */ private $token; /** * @param array $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 */ public function getIterator(): ArrayIterator { return new ArrayIterator($this->types); } /** * @psalm-suppress ImpureMethodCall */ private function add(Type $type): void { if ($type instanceof static) { 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; } } PKL_]R&g__#type-resolver/src/Types/Boolean.phpnu[` * 2. `ACollectionObject` * * - 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 . '>'; } } PKL_]z(type-resolver/src/Types/Intersection.phpnu[ $types */ public function __construct(array $types) { parent::__construct($types, '&'); } } PKL_]9]@b"type-resolver/src/Types/Scalar.phpnu[parameters = $parameters; $this->returnType = $returnType; } /** @return CallableParameter[] */ public function getParameters(): array { return $this->parameters; } public function getReturnType(): ?Type { return $this->returnType; } /** * Returns a rendered output of the Type as it would be used in a DocBlock. */ public function __toString(): string { return 'callable'; } } PKL_]-S"type-resolver/src/Types/Never_.phpnu[fqsen = $fqsen; } /** * Returns the FQSEN associated with this object. */ public function getFqsen(): ?Fqsen { return $this->fqsen; } public function __toString(): string { if ($this->fqsen) { return (string) $this->fqsen; } return 'object'; } } PKL_] ݲ$type-resolver/src/Types/Compound.phpnu[ $types */ public function __construct(array $types) { parent::__construct($types, '|'); } } PKL_]pQ99%type-resolver/src/Types/Iterable_.phpnu[keyType) { return 'iterable<' . $this->keyType . ',' . $this->valueType . '>'; } if ($this->valueType instanceof Mixed_) { return 'iterable'; } return 'iterable<' . $this->valueType . '>'; } } PKL_]ʢzz(type-resolver/src/Types/AbstractList.phpnu[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 type for the values 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 . '[]'; } } PKL_]kqq"type-resolver/src/Types/Mixed_.phpnu[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 . ')'; } } PKL_]"type-resolver/src/Types/Array_.phpnu[ $reflector */ return $this->createFromReflectionClass($reflector); } if ($reflector instanceof ReflectionParameter) { return $this->createFromReflectionParameter($reflector); } if ($reflector instanceof ReflectionMethod) { return $this->createFromReflectionMethod($reflector); } if ($reflector instanceof ReflectionProperty) { return $this->createFromReflectionProperty($reflector); } if ($reflector instanceof ReflectionClassConstant) { return $this->createFromReflectionClassConstant($reflector); } throw new UnexpectedValueException('Unhandled \Reflector instance given: ' . get_class($reflector)); } private function createFromReflectionParameter(ReflectionParameter $parameter): Context { $class = $parameter->getDeclaringClass(); if (!$class) { throw new InvalidArgumentException('Unable to get class of ' . $parameter->getName()); } return $this->createFromReflectionClass($class); } private function createFromReflectionMethod(ReflectionMethod $method): Context { $class = $method->getDeclaringClass(); return $this->createFromReflectionClass($class); } private function createFromReflectionProperty(ReflectionProperty $property): Context { $class = $property->getDeclaringClass(); return $this->createFromReflectionClass($class); } private function createFromReflectionClassConstant(ReflectionClassConstant $constant): Context { //phpcs:ignore SlevomatCodingStandard.Commenting.InlineDocCommentDeclaration.MissingVariable /** @phpstan-var ReflectionClass $class */ $class = $constant->getDeclaringClass(); return $this->createFromReflectionClass($class); } /** * @phpstan-param ReflectionClass $class */ private function createFromReflectionClass(ReflectionClass $class): Context { $fileName = $class->getFileName(); $namespace = $class->getNamespaceName(); if (is_string($fileName) && file_exists($fileName)) { $contents = file_get_contents($fileName); if ($contents === false) { throw new RuntimeException('Unable to read file "' . $fileName . '"'); } return $this->createForNamespace($namespace, $contents); } return new Context($namespace, []); } /** * Build a Context for a namespace in the provided file contents. * * @see Context for more information on Contexts. * * @param string $namespace It does not matter if a `\` precedes the namespace name, * this method first normalizes. * @param string $fileContents The file's contents to retrieve the aliases from with the given namespace. */ public function createForNamespace(string $namespace, string $fileContents): Context { $namespace = trim($namespace, '\\'); $useStatements = []; $currentNamespace = ''; $tokens = new ArrayIterator(token_get_all($fileContents)); while ($tokens->valid()) { $currentToken = $tokens->current(); switch ($currentToken[0]) { case T_NAMESPACE: $currentNamespace = $this->parseNamespace($tokens); break; case T_CLASS: case T_TRAIT: // Fast-forward the iterator through the class so that any // T_USE tokens found within are skipped - these are not // valid namespace use statements so should be ignored. $braceLevel = 0; $firstBraceFound = false; while ($tokens->valid() && ($braceLevel > 0 || !$firstBraceFound)) { $currentToken = $tokens->current(); if ( $currentToken === '{' || in_array($currentToken[0], [T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES], true) ) { if (!$firstBraceFound) { $firstBraceFound = true; } ++$braceLevel; } if ($currentToken === '}') { --$braceLevel; } $tokens->next(); } break; case T_USE: if ($currentNamespace === $namespace) { $useStatements += $this->parseUseStatement($tokens); } break; } $tokens->next(); } return new Context($namespace, $useStatements); } /** * Deduce the name from tokens when we are at the T_NAMESPACE token. * * @param ArrayIterator $tokens */ private function parseNamespace(ArrayIterator $tokens): string { // skip to the first string or namespace separator $this->skipToNextStringOrNamespaceSeparator($tokens); $name = ''; $acceptedTokens = [T_STRING, T_NS_SEPARATOR, T_NAME_QUALIFIED]; while ($tokens->valid() && in_array($tokens->current()[0], $acceptedTokens, true)) { $name .= $tokens->current()[1]; $tokens->next(); } return $name; } /** * Deduce the names of all imports when we are at the T_USE token. * * @param ArrayIterator $tokens * * @return string[] * @psalm-return array */ private function parseUseStatement(ArrayIterator $tokens): array { $uses = []; while ($tokens->valid()) { $this->skipToNextStringOrNamespaceSeparator($tokens); $uses += $this->extractUseStatements($tokens); $currentToken = $tokens->current(); if ($currentToken[0] === self::T_LITERAL_END_OF_USE) { return $uses; } } return $uses; } /** * Fast-forwards the iterator as longs as we don't encounter a T_STRING or T_NS_SEPARATOR token. * * @param ArrayIterator $tokens */ private function skipToNextStringOrNamespaceSeparator(ArrayIterator $tokens): void { while ($tokens->valid()) { $currentToken = $tokens->current(); if (in_array($currentToken[0], [T_STRING, T_NS_SEPARATOR], true)) { break; } if ($currentToken[0] === T_NAME_QUALIFIED) { break; } if (defined('T_NAME_FULLY_QUALIFIED') && $currentToken[0] === T_NAME_FULLY_QUALIFIED) { break; } $tokens->next(); } } /** * Deduce the namespace name and alias of an import when we are at the T_USE token or have not reached the end of * a USE statement yet. This will return a key/value array of the alias => namespace. * * @param ArrayIterator $tokens * * @return string[] * @psalm-return array * * @psalm-suppress TypeDoesNotContainType */ private function extractUseStatements(ArrayIterator $tokens): array { $extractedUseStatements = []; $groupedNs = ''; $currentNs = ''; $currentAlias = ''; $state = 'start'; while ($tokens->valid()) { $currentToken = $tokens->current(); $tokenId = is_string($currentToken) ? $currentToken : $currentToken[0]; $tokenValue = is_string($currentToken) ? null : $currentToken[1]; switch ($state) { case 'start': switch ($tokenId) { case T_STRING: case T_NS_SEPARATOR: $currentNs .= (string) $tokenValue; $currentAlias = $tokenValue; break; case T_NAME_QUALIFIED: case T_NAME_FULLY_QUALIFIED: $currentNs .= (string) $tokenValue; $currentAlias = substr( (string) $tokenValue, (int) (strrpos((string) $tokenValue, '\\')) + 1 ); break; case T_CURLY_OPEN: case '{': $state = 'grouped'; $groupedNs = $currentNs; break; case T_AS: $state = 'start-alias'; break; case self::T_LITERAL_USE_SEPARATOR: case self::T_LITERAL_END_OF_USE: $state = 'end'; break; default: break; } break; case 'start-alias': switch ($tokenId) { case T_STRING: $currentAlias = $tokenValue; break; case self::T_LITERAL_USE_SEPARATOR: case self::T_LITERAL_END_OF_USE: $state = 'end'; break; default: break; } break; case 'grouped': switch ($tokenId) { case T_STRING: case T_NS_SEPARATOR: $currentNs .= (string) $tokenValue; $currentAlias = $tokenValue; break; case T_AS: $state = 'grouped-alias'; break; case self::T_LITERAL_USE_SEPARATOR: $state = 'grouped'; $extractedUseStatements[(string) $currentAlias] = $currentNs; $currentNs = $groupedNs; $currentAlias = ''; break; case self::T_LITERAL_END_OF_USE: $state = 'end'; break; default: break; } break; case 'grouped-alias': switch ($tokenId) { case T_STRING: $currentAlias = $tokenValue; break; case self::T_LITERAL_USE_SEPARATOR: $state = 'grouped'; $extractedUseStatements[(string) $currentAlias] = $currentNs; $currentNs = $groupedNs; $currentAlias = ''; break; case self::T_LITERAL_END_OF_USE: $state = 'end'; break; default: break; } } if ($state === 'end') { break; } $tokens->next(); } if ($groupedNs !== $currentNs) { $extractedUseStatements[(string) $currentAlias] = $currentNs; } return $extractedUseStatements; } } PKL_]Jpp%type-resolver/src/Types/Resource_.phpnu[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 . '>'; } } PKL_]j oo.type-resolver/src/PseudoTypes/IntegerValue.phpnu[value = $value; } public function getValue(): int { return $this->value; } public function underlyingType(): Type { return new Integer(); } public function __toString(): string { return (string) $this->value; } } PKL_]+1<<1type-resolver/src/PseudoTypes/NegativeInteger.phpnu[valueType, $this->keyType); } 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 'non-empty-list'; } return 'non-empty-list<' . $this->valueType . '>'; } } PKL_]nE??/type-resolver/src/PseudoTypes/LiteralString.phpnu[value = $value; } public function getValue(): float { return $this->value; } public function underlyingType(): Type { return new Float_(); } public function __toString(): string { return (string) $this->value; } } PKL_]_9xx1type-resolver/src/PseudoTypes/ConstExpression.phpnu[owner = $owner; $this->expression = $expression; } public function getOwner(): Type { return $this->owner; } public function getExpression(): string { return $this->expression; } public function underlyingType(): Type { return new Mixed_(); } public function __toString(): string { return sprintf('%s::%s', (string) $this->owner, $this->expression); } } PKL_]46MM'type-resolver/src/PseudoTypes/True_.phpnu[getItems()) . '}'; } } PKL_]X4CC1type-resolver/src/PseudoTypes/LowercaseString.phpnu[valueType instanceof Mixed_) { return 'list'; } return 'list<' . $this->valueType . '>'; } } PKL_]12-type-resolver/src/PseudoTypes/StringValue.phpnu[value = $value; } public function getValue(): string { return $this->value; } public function underlyingType(): Type { return new String_(); } public function __toString(): string { return sprintf('"%s"', $this->value); } } PKL_]G>QQ(type-resolver/src/PseudoTypes/False_.phpnu[key = $key; $this->value = $value ?? new Mixed_(); $this->optional = $optional; } public function getKey(): ?string { return $this->key; } public function getValue(): Type { return $this->value; } public function isOptional(): bool { return $this->optional; } public function __toString(): string { if ($this->key !== null) { return sprintf( '%s%s: %s', $this->key, $this->optional ? '?' : '', (string) $this->value ); } return (string) $this->value; } } PKL_] /type-resolver/src/PseudoTypes/ListShapeItem.phpnu[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 . '>'; } } PKL_]|+0-type-resolver/src/PseudoTypes/ObjectShape.phpnu[items = $items; } /** * @return ObjectShapeItem[] */ public function getItems(): array { return $this->items; } public function underlyingType(): Type { return new Object_(); } public function __toString(): string { return 'object{' . implode(', ', $this->items) . '}'; } } PKL_] TT,type-resolver/src/PseudoTypes/ArrayShape.phpnu[items = $items; } /** * @return ArrayShapeItem[] */ public function getItems(): array { return $this->items; } public function underlyingType(): Type { return new Array_(new Mixed_(), new ArrayKey()); } public function __toString(): string { return 'array{' . implode(', ', $this->items) . '}'; } } PKL_]Ȏ/type-resolver/src/PseudoTypes/NonEmptyArray.phpnu[valueType, $this->keyType); } /** * Returns a rendered output of the Type as it would be used in a DocBlock. */ public function __toString(): string { if ($this->keyType) { return 'non-empty-array<' . $this->keyType . ',' . $this->valueType . '>'; } if ($this->valueType instanceof Mixed_) { return 'non-empty-array'; } return 'non-empty-array<' . $this->valueType . '>'; } } PKL_]+UVV0type-resolver/src/PseudoTypes/ArrayShapeItem.phpnu[isFqsen($fqsen)) { return new Fqsen($fqsen); } return $this->resolvePartialStructuralElementName($fqsen, $context); } /** * Tests whether the given type is a Fully Qualified Structural Element Name. */ private function isFqsen(string $type): bool { return strpos($type, self::OPERATOR_NAMESPACE) === 0; } /** * 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. * * @throws InvalidArgumentException When type is not a valid FQSEN. */ private function resolvePartialStructuralElementName(string $type, Context $context): Fqsen { $typeParts = explode(self::OPERATOR_NAMESPACE, $type, 2); $namespaceAliases = $context->getNamespaceAliases(); // if the first segment is not an alias; prepend namespace name and return if (!isset($namespaceAliases[$typeParts[0]])) { $namespace = $context->getNamespace(); if ($namespace !== '') { $namespace .= self::OPERATOR_NAMESPACE; } return new Fqsen(self::OPERATOR_NAMESPACE . $namespace . $type); } $typeParts[0] = $namespaceAliases[$typeParts[0]]; return new Fqsen(self::OPERATOR_NAMESPACE . implode(self::OPERATOR_NAMESPACE, $typeParts)); } } PKL_]n dd"type-resolver/src/TypeResolver.phpnu[ List of recognized keywords and unto which Value Object they map * @psalm-var array> */ private $keywords = [ 'string' => String_::class, 'class-string' => ClassString::class, 'interface-string' => InterfaceString::class, 'html-escaped-string' => HtmlEscapedString::class, 'lowercase-string' => LowercaseString::class, 'non-empty-lowercase-string' => NonEmptyLowercaseString::class, 'non-empty-string' => NonEmptyString::class, 'numeric-string' => NumericString::class, 'numeric' => Numeric_::class, 'trait-string' => TraitString::class, 'int' => Integer::class, 'integer' => Integer::class, 'positive-int' => PositiveInteger::class, 'negative-int' => NegativeInteger::class, 'bool' => Boolean::class, 'boolean' => Boolean::class, 'real' => Float_::class, 'float' => Float_::class, 'double' => Float_::class, 'object' => Object_::class, 'mixed' => Mixed_::class, 'array' => Array_::class, 'array-key' => ArrayKey::class, 'non-empty-array' => NonEmptyArray::class, 'resource' => Resource_::class, 'void' => Void_::class, 'null' => Null_::class, 'scalar' => Scalar::class, 'callback' => Callable_::class, 'callable' => Callable_::class, 'callable-string' => CallableString::class, 'false' => False_::class, 'true' => True_::class, 'literal-string' => LiteralString::class, 'self' => Self_::class, '$this' => This::class, 'static' => Static_::class, 'parent' => Parent_::class, 'iterable' => Iterable_::class, 'never' => Never_::class, 'list' => List_::class, 'non-empty-list' => NonEmptyList::class, ]; /** * @psalm-readonly * @var FqsenResolver */ private $fqsenResolver; /** * @psalm-readonly * @var TypeParser */ private $typeParser; /** * @psalm-readonly * @var Lexer */ private $lexer; /** * Initializes this TypeResolver with the means to create and resolve Fqsen objects. */ public function __construct(?FqsenResolver $fqsenResolver = null) { $this->fqsenResolver = $fqsenResolver ?: new FqsenResolver(); if (class_exists(ParserConfig::class)) { $this->typeParser = new TypeParser(new ParserConfig([]), new ConstExprParser(new ParserConfig([]))); $this->lexer = new Lexer(new ParserConfig([])); } else { $this->typeParser = new TypeParser(new ConstExprParser()); $this->lexer = new Lexer(); } } /** * Analyzes the given type and returns the FQCN variant. * * When a type is provided this method checks whether it is not a keyword or * Fully Qualified Class Name. If so it will use the given namespace and * aliases to expand the type to a FQCN representation. * * This method only works as expected if the namespace and aliases are set; * no dynamic reflection is being performed here. * * @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. * * @param string $type The relative or absolute type. */ public function resolve(string $type, ?Context $context = null): Type { $type = trim($type); if (!$type) { throw new InvalidArgumentException('Attempted to resolve "' . $type . '" but it appears to be empty'); } if ($context === null) { $context = new Context(''); } $tokens = $this->lexer->tokenize($type); $tokenIterator = new TokenIterator($tokens); $ast = $this->parse($tokenIterator); $type = $this->createType($ast, $context); return $this->tryParseRemainingCompoundTypes($tokenIterator, $context, $type); } public function createType(?TypeNode $type, Context $context): Type { if ($type === null) { return new Mixed_(); } switch (get_class($type)) { case ArrayTypeNode::class: return new Array_( $this->createType($type->type, $context) ); case ArrayShapeNode::class: switch ($type->kind) { case ArrayShapeNode::KIND_ARRAY: return new ArrayShape( ...array_map( function (ArrayShapeItemNode $item) use ($context): ArrayShapeItem { return new ArrayShapeItem( (string) $item->keyName, $this->createType($item->valueType, $context), $item->optional ); }, $type->items ) ); case ArrayShapeNode::KIND_LIST: return new ListShape( ...array_map( function (ArrayShapeItemNode $item) use ($context): ListShapeItem { return new ListShapeItem( null, $this->createType($item->valueType, $context), $item->optional ); }, $type->items ) ); default: throw new RuntimeException('Unsupported array shape kind'); } case ObjectShapeNode::class: return new ObjectShape( ...array_map( function (ObjectShapeItemNode $item) use ($context): ObjectShapeItem { return new ObjectShapeItem( (string) $item->keyName, $this->createType($item->valueType, $context), $item->optional ); }, $type->items ) ); case CallableTypeNode::class: return $this->createFromCallable($type, $context); case ConstTypeNode::class: return $this->createFromConst($type, $context); case GenericTypeNode::class: return $this->createFromGeneric($type, $context); case IdentifierTypeNode::class: return $this->resolveSingleType($type->name, $context); case IntersectionTypeNode::class: return new Intersection( array_filter( array_map( function (TypeNode $nestedType) use ($context): Type { $type = $this->createType($nestedType, $context); if ($type instanceof AggregatedType) { return new Expression($type); } return $type; }, $type->types ) ) ); case NullableTypeNode::class: $nestedType = $this->createType($type->type, $context); return new Nullable($nestedType); case UnionTypeNode::class: return new Compound( array_filter( array_map( function (TypeNode $nestedType) use ($context): Type { $type = $this->createType($nestedType, $context); if ($type instanceof AggregatedType) { return new Expression($type); } return $type; }, $type->types ) ) ); case ThisTypeNode::class: return new This(); case ConditionalTypeNode::class: case ConditionalTypeForParameterNode::class: case OffsetAccessTypeNode::class: default: return new Mixed_(); } } private function createFromGeneric(GenericTypeNode $type, Context $context): Type { switch (strtolower($type->type->name)) { case 'array': return $this->createArray($type->genericTypes, $context); case 'class-string': $subType = $this->createType($type->genericTypes[0], $context); if (!$subType instanceof Object_ || $subType->getFqsen() === null) { throw new RuntimeException( $subType . ' is not a class string' ); } return new ClassString( $subType->getFqsen() ); case 'interface-string': $subType = $this->createType($type->genericTypes[0], $context); if (!$subType instanceof Object_ || $subType->getFqsen() === null) { throw new RuntimeException( $subType . ' is not a class string' ); } return new InterfaceString( $subType->getFqsen() ); case 'list': return new List_( $this->createType($type->genericTypes[0], $context) ); case 'non-empty-list': return new NonEmptyList( $this->createType($type->genericTypes[0], $context) ); case 'int': if (isset($type->genericTypes[1]) === false) { throw new RuntimeException('int has not the correct format'); } return new IntegerRange((string) $type->genericTypes[0], (string) $type->genericTypes[1]); case 'iterable': return new Iterable_( ...array_reverse( array_map( function (TypeNode $genericType) use ($context): Type { return $this->createType($genericType, $context); }, $type->genericTypes ) ) ); default: $collectionType = $this->createType($type->type, $context); if ($collectionType instanceof Object_ === false) { throw new RuntimeException(sprintf('%s is not a collection', (string) $collectionType)); } return new Collection( $collectionType->getFqsen(), ...array_reverse( array_map( function (TypeNode $genericType) use ($context): Type { return $this->createType($genericType, $context); }, $type->genericTypes ) ) ); } } private function createFromCallable(CallableTypeNode $type, Context $context): Callable_ { return new Callable_(array_map( function (CallableTypeParameterNode $param) use ($context): CallableParameter { return new CallableParameter( $this->createType($param->type, $context), $param->parameterName !== '' ? trim($param->parameterName, '$') : null, $param->isReference, $param->isVariadic, $param->isOptional ); }, $type->parameters ), $this->createType($type->returnType, $context)); } private function createFromConst(ConstTypeNode $type, Context $context): Type { switch (true) { case $type->constExpr instanceof ConstExprIntegerNode: return new IntegerValue((int) $type->constExpr->value); case $type->constExpr instanceof ConstExprFloatNode: return new FloatValue((float) $type->constExpr->value); case $type->constExpr instanceof ConstExprStringNode: return new StringValue($type->constExpr->value); case $type->constExpr instanceof ConstFetchNode: return new ConstExpression( $this->resolve($type->constExpr->className, $context), $type->constExpr->name ); default: throw new RuntimeException(sprintf('Unsupported constant type %s', get_class($type))); } } /** * 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->isKeyword($type): return $this->resolveKeyword($type); 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( '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. * * @psalm-param class-string $typeClassName */ public function addKeyword(string $keyword, string $typeClassName): void { if (!class_exists($typeClassName)) { 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 ); } $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"' ); } $this->keywords[$keyword] = $typeClassName; } /** * Detects whether the given type represents a PHPDoc keyword. * * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. * * @psalm-mutation-free */ private function isKeyword(string $type): bool { return array_key_exists(strtolower($type), $this->keywords); } /** * Detects whether the given type represents a relative structural element name. * * @param string $type A relative or absolute type as defined in the phpDocumentor documentation. * * @psalm-mutation-free */ private function isPartialStructuralElementName(string $type): bool { return (isset($type[0]) && $type[0] !== self::OPERATOR_NAMESPACE) && !$this->isKeyword($type); } /** * Tests whether the given type is a Fully Qualified Structural Element Name. * * @psalm-mutation-free */ private function isFqsen(string $type): bool { return strpos($type, self::OPERATOR_NAMESPACE) === 0; } /** * Resolves the given keyword (such as `string`) into a Type object representing that keyword. * * @psalm-mutation-free */ private function resolveKeyword(string $type): Type { $className = $this->keywords[strtolower($type)]; return new $className(); } /** * Resolves the given FQSEN string into an FQSEN object. * * @psalm-mutation-free */ private function resolveTypedObject(string $type, ?Context $context = null): Object_ { return new Object_($this->fqsenResolver->resolve($type, $context)); } /** @param TypeNode[] $typeNodes */ private function createArray(array $typeNodes, Context $context): Array_ { $types = array_reverse( array_map( function (TypeNode $node) use ($context): Type { return $this->createType($node, $context); }, $typeNodes ) ); if (isset($types[1]) === false) { return new Array_(...$types); } if ($this->validArrayKeyType($types[1]) || $types[1] instanceof ArrayKey) { return new Array_(...$types); } if ($types[1] instanceof Compound && $types[1]->getIterator()->count() === 2) { if ($this->validArrayKeyType($types[1]->get(0)) && $this->validArrayKeyType($types[1]->get(1))) { return new Array_(...$types); } } throw new RuntimeException('An array can have only integers or strings as keys'); } private function validArrayKeyType(?Type $type): bool { return $type instanceof String_ || $type instanceof Integer; } private function parse(TokenIterator $tokenIterator): TypeNode { try { $ast = $this->typeParser->parse($tokenIterator); } catch (ParserException $e) { throw new RuntimeException($e->getMessage(), 0, $e); } return $ast; } /** * Will try to parse unsupported type notations by phpstan * * The phpstan parser doesn't support the illegal nullable combinations like this library does. * This method will warn the user about those notations but for bc purposes we will still have it here. */ private function tryParseRemainingCompoundTypes(TokenIterator $tokenIterator, Context $context, Type $type): Type { if ( $tokenIterator->isCurrentTokenType(Lexer::TOKEN_UNION) || $tokenIterator->isCurrentTokenType(Lexer::TOKEN_INTERSECTION) ) { Deprecation::trigger( 'phpdocumentor/type-resolver', 'https://github.com/phpDocumentor/TypeResolver/issues/184', 'Legacy nullable type detected, please update your code as you are using nullable types in a docblock. support will be removed in v2.0.0' ); } $continue = true; while ($continue) { $continue = false; while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_UNION)) { $ast = $this->parse($tokenIterator); $type2 = $this->createType($ast, $context); $type = new Compound([$type, $type2]); $continue = true; } while ($tokenIterator->tryConsumeTokenType(Lexer::TOKEN_INTERSECTION)) { $ast = $this->typeParser->parse($tokenIterator); $type2 = $this->createType($ast, $context); $type = new Intersection([$type, $type2]); $continue = true; } } return $type; } } PKL_]~mm type-resolver/src/PseudoType.phpnu[ please note that if you want to pass partial class names that additional steps are necessary, see the > chapter `Resolving partial classes and FQSENs` for more information. Where the FqsenResolver can resolve: - Constant expressions (i.e. `@see \MyNamespace\MY_CONSTANT`) - Function expressions (i.e. `@see \MyNamespace\myFunction()`) - Class expressions (i.e. `@see \MyNamespace\MyClass`) - Interface expressions (i.e. `@see \MyNamespace\MyInterface`) - Trait expressions (i.e. `@see \MyNamespace\MyTrait`) - Class constant expressions (i.e. `@see \MyNamespace\MyClass::MY_CONSTANT`) - Property expressions (i.e. `@see \MyNamespace\MyClass::$myProperty`) - Method expressions (i.e. `@see \MyNamespace\MyClass::myMethod()`) ## 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: ```php $typeResolver = new \phpDocumentor\Reflection\TypeResolver(); $type = $typeResolver->resolve('string|integer'); ``` In this example you will receive a Value Object of class `\phpDocumentor\Reflection\Types\Compound` that has two 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. ### 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: ```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`. 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. For example, you have this file: ```php namespace My\Example; use phpDocumentor\Reflection\Types; class Classy { /** * @var Types\Context * @see Classy::otherFunction() */ public function __construct($context) {} public function otherFunction(){} } ``` 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. ### Creating a Context You can do this by manually creating a Context like this: ```php $context = new \phpDocumentor\Reflection\Types\Context( '\My\Example', [ 'Types' => '\phpDocumentor\Reflection\Types'] ); ``` 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(); $context = $contextFactory->createFromReflector(new ReflectionMethod('\My\Example\Classy', '__construct')); ``` or ```php $contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory(); $context = $contextFactory->createForNamespace('\My\Example', file_get_contents('My/Example/Classy.php')); ``` ### 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. To obtain the resolved class name for the `@var` tag in the example above you can do: ```php $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`. > 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. 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()`. PKL_]rV&&type-resolver/composer.jsonnu[{ "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" } ], "require": { "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", "phpstan/phpdoc-parser": "^1.18|^2.0", "doctrine/deprecations": "^1.0" }, "require-dev": { "ext-tokenizer": "*", "phpunit/phpunit": "^9.5", "phpstan/phpstan": "^1.8", "phpstan/phpstan-phpunit": "^1.1", "phpstan/extension-installer": "^1.1", "vimeo/psalm": "^4.25", "rector/rector": "^0.13.9", "phpbench/phpbench": "^1.2" }, "autoload": { "psr-4": { "phpDocumentor\\Reflection\\": "src" } }, "autoload-dev": { "psr-4": { "phpDocumentor\\Reflection\\": ["tests/unit", "tests/benchmark"] } }, "extra": { "branch-alias": { "dev-1.x": "1.x-dev" } }, "config": { "platform": { "php": "7.3.0" }, "allow-plugins": { "phpstan/extension-installer": true } } } PKL_]DAtype-resolver/phpdoc.dist.xmlnu[ Type Resolver build/docs latest src/ api php template template-extends template-implements extends implements phpDocumentor docs guides