Skip to content

Commit

Permalink
Merge pull request #343 from phpDocumentor/poc-phpstan-integration
Browse files Browse the repository at this point in the history
PhpStan based tag parsing
  • Loading branch information
jaapio authored Nov 11, 2022
2 parents 203354b + 1f95f3b commit b5dc333
Show file tree
Hide file tree
Showing 38 changed files with 1,420 additions and 76 deletions.
5 changes: 3 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
],
"require": {
"php": "^7.2 || ^8.0",
"phpdocumentor/type-resolver": "^1.3",
"phpdocumentor/type-resolver": "1.x-dev@dev",
"webmozart/assert": "^1.9.1",
"phpdocumentor/reflection-common": "^2.2",
"ext-filter": "*"
"ext-filter": "*",
"phpstan/phpdoc-parser": "^1.7"
},
"require-dev": {
"mockery/mockery": "~1.3.5",
Expand Down
77 changes: 66 additions & 11 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,10 @@

<!-- Set the minimum PHP version for PHPCompatibility.
This should be kept in sync with the requirements in the composer.json file. -->
<config name="testVersion" value="7.2-"/>
<config name="testVersion" value="7.4-"/>

<rule ref="phpDocumentor">
<exclude name="SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException" />

<!-- Property type declarations are a PHP 7.4 feature. -->
<exclude name="SlevomatCodingStandard.TypeHints.PropertyTypeHint.MissingNativeTypeHint"/>
</rule>

<rule ref="SlevomatCodingStandard.Classes.SuperfluousAbstractClassNaming.SuperfluousPrefix">
Expand Down
4 changes: 2 additions & 2 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd"
colors="true"
convertDeprecationsToExceptions="true"
beStrictAboutOutputDuringTests="true"
convertDeprecationsToExceptions="false"
beStrictAboutOutputDuringTests="false"
forceCoversAnnotation="true"
verbose="true"
bootstrap="vendor/autoload.php"
Expand Down
5 changes: 3 additions & 2 deletions src/DocBlock/DescriptionFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace phpDocumentor\Reflection\DocBlock;

use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\Utils;

Expand Down Expand Up @@ -47,13 +48,13 @@
*/
class DescriptionFactory
{
/** @var TagFactory */
/** @var Factory */
private $tagFactory;

/**
* Initializes this factory with the means to construct (inline) tags.
*/
public function __construct(TagFactory $tagFactory)
public function __construct(Factory $tagFactory)
{
$this->tagFactory = $tagFactory;
}
Expand Down
43 changes: 30 additions & 13 deletions src/DocBlock/StandardTagFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use phpDocumentor\Reflection\DocBlock\Tags\Author;
use phpDocumentor\Reflection\DocBlock\Tags\Covers;
use phpDocumentor\Reflection\DocBlock\Tags\Deprecated;
use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag;
Expand All @@ -40,12 +41,15 @@
use ReflectionParameter;
use Webmozart\Assert\Assert;

use function array_key_exists;
use function array_merge;
use function array_slice;
use function call_user_func_array;
use function count;
use function get_class;
use function is_object;
use function preg_match;
use function sprintf;
use function strpos;
use function trim;

Expand All @@ -72,7 +76,7 @@ final class StandardTagFactory implements TagFactory
public const REGEX_TAGNAME = '[\w\-\_\\\\:]+';

/**
* @var array<class-string<Tag>> An array with a tag as a key, and an
* @var array<class-string<Tag>|Factory> An array with a tag as a key, and an
* FQCN to a class that handles it as an array value.
*/
private $tagHandlerMappings = [
Expand Down Expand Up @@ -162,18 +166,25 @@ public function addService(object $service, ?string $alias = null): void
$this->serviceLocator[$alias ?: get_class($service)] = $service;
}

public function registerTagHandler(string $tagName, string $handler): void
/** {@inheritDoc} */
public function registerTagHandler(string $tagName, $handler): void
{
Assert::stringNotEmpty($tagName);
Assert::classExists($handler);
Assert::implementsInterface($handler, Tag::class);

if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
throw new InvalidArgumentException(
'A namespaced tag must have a leading backslash as it must be fully qualified'
);
}

if (is_object($handler)) {
Assert::isInstanceOf($handler, Factory::class);
$this->tagHandlerMappings[$tagName] = $handler;

return;
}

Assert::classExists($handler);
Assert::implementsInterface($handler, Tag::class);
$this->tagHandlerMappings[$tagName] = $handler;
}

Expand Down Expand Up @@ -210,6 +221,10 @@ private function createTag(string $body, string $name, TypeContext $context): Ta
$this->getServiceLocatorWithDynamicParameters($context, $name, $body)
);

if (array_key_exists('tagLine', $arguments)) {
$arguments['tagLine'] = sprintf('@%s %s', $name, $body);
}

try {
$callable = [$handlerClassName, 'create'];
Assert::isCallable($callable);
Expand All @@ -225,9 +240,9 @@ private function createTag(string $body, string $name, TypeContext $context): Ta
/**
* Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
*
* @return class-string<Tag>
* @return class-string<Tag>|Factory
*/
private function findHandlerClassName(string $tagName, TypeContext $context): string
private function findHandlerClassName(string $tagName, TypeContext $context)
{
$handlerClassName = Generic::class;
if (isset($this->tagHandlerMappings[$tagName])) {
Expand Down Expand Up @@ -268,18 +283,18 @@ private function getArgumentsForParametersFromWiring(array $parameters, array $l
}
}

$parameterName = $parameter->getName();
if (isset($locator[$typeHint])) {
$arguments[] = $locator[$typeHint];
$arguments[$parameterName] = $locator[$typeHint];
continue;
}

$parameterName = $parameter->getName();
if (isset($locator[$parameterName])) {
$arguments[] = $locator[$parameterName];
$arguments[$parameterName] = $locator[$parameterName];
continue;
}

$arguments[] = null;
$arguments[$parameterName] = null;
}

return $arguments;
Expand All @@ -289,12 +304,14 @@ private function getArgumentsForParametersFromWiring(array $parameters, array $l
* Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
* tag handler class name.
*
* @param class-string $handlerClassName
* @param class-string|Factory $handler
*
* @return ReflectionParameter[]
*/
private function fetchParametersForHandlerFactoryMethod(string $handlerClassName): array
private function fetchParametersForHandlerFactoryMethod($handler): array
{
$handlerClassName = is_object($handler) ? get_class($handler) : $handler;

if (!isset($this->tagHandlerParameterCache[$handlerClassName])) {
$methodReflection = new ReflectionMethod($handlerClassName, 'create');
$this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
Expand Down
19 changes: 4 additions & 15 deletions src/DocBlock/TagFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
namespace phpDocumentor\Reflection\DocBlock;

use InvalidArgumentException;
use phpDocumentor\Reflection\Types\Context as TypeContext;
use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory;

interface TagFactory
interface TagFactory extends Factory
{
/**
* Adds a parameter to the service locator that can be injected in a tag's factory method.
Expand All @@ -40,17 +40,6 @@ interface TagFactory
*/
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.
*
Expand All @@ -71,7 +60,7 @@ public function addService(object $service): void;
*
* @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.
* @param class-string<Tag>|Factory $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
Expand All @@ -80,5 +69,5 @@ public function addService(object $service): void;
* @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(string $tagName, string $handler): void;
public function registerTagHandler(string $tagName, $handler): void;
}
Loading

0 comments on commit b5dc333

Please sign in to comment.