Skip to content

Commit d6c050a

Browse files
committed
WIP
1 parent 7192e67 commit d6c050a

16 files changed

+734
-272
lines changed

composer.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/DocBlock/StandardTagFactory.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
use function call_user_func_array;
4646
use function count;
4747
use function get_class;
48+
use function is_object;
4849
use function preg_match;
4950
use function strpos;
5051
use function trim;
@@ -162,18 +163,23 @@ public function addService(object $service, ?string $alias = null): void
162163
$this->serviceLocator[$alias ?: get_class($service)] = $service;
163164
}
164165

165-
public function registerTagHandler(string $tagName, string $handler): void
166+
public function registerTagHandler(string $tagName, $handler): void
166167
{
167168
Assert::stringNotEmpty($tagName);
168-
Assert::classExists($handler);
169-
Assert::implementsInterface($handler, Tag::class);
170-
171169
if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
172170
throw new InvalidArgumentException(
173171
'A namespaced tag must have a leading backslash as it must be fully qualified'
174172
);
175173
}
176174

175+
if (is_object($handler)) {
176+
Assert::implementsInterface($handler, TagFactory::class);
177+
$this->tagHandlerMappings[$tagName] = $handler;
178+
return;
179+
}
180+
181+
Assert::classExists($handler);
182+
Assert::implementsInterface($handler, Tag::class);
177183
$this->tagHandlerMappings[$tagName] = $handler;
178184
}
179185

@@ -210,6 +216,8 @@ private function createTag(string $body, string $name, TypeContext $context): Ta
210216
$this->getServiceLocatorWithDynamicParameters($context, $name, $body)
211217
);
212218

219+
$arguments['tagLine'] = sprintf('@%s %s', $name, $body);
220+
213221
try {
214222
$callable = [$handlerClassName, 'create'];
215223
Assert::isCallable($callable);
@@ -225,9 +233,9 @@ private function createTag(string $body, string $name, TypeContext $context): Ta
225233
/**
226234
* Determines the Fully Qualified Class Name of the Factory or Tag (containing a Factory Method `create`).
227235
*
228-
* @return class-string<Tag>
236+
* @return class-string<Tag>|TagFactory
229237
*/
230-
private function findHandlerClassName(string $tagName, TypeContext $context): string
238+
private function findHandlerClassName(string $tagName, TypeContext $context)
231239
{
232240
$handlerClassName = Generic::class;
233241
if (isset($this->tagHandlerMappings[$tagName])) {
@@ -275,11 +283,11 @@ private function getArgumentsForParametersFromWiring(array $parameters, array $l
275283

276284
$parameterName = $parameter->getName();
277285
if (isset($locator[$parameterName])) {
278-
$arguments[] = $locator[$parameterName];
286+
$arguments[$parameterName] = $locator[$parameterName];
279287
continue;
280288
}
281289

282-
$arguments[] = null;
290+
$arguments[$parameterName] = null;
283291
}
284292

285293
return $arguments;
@@ -289,12 +297,14 @@ private function getArgumentsForParametersFromWiring(array $parameters, array $l
289297
* Retrieves a series of ReflectionParameter objects for the static 'create' method of the given
290298
* tag handler class name.
291299
*
292-
* @param class-string $handlerClassName
300+
* @param class-string|TagFactory $handler
293301
*
294302
* @return ReflectionParameter[]
295303
*/
296-
private function fetchParametersForHandlerFactoryMethod(string $handlerClassName): array
304+
private function fetchParametersForHandlerFactoryMethod($handler): array
297305
{
306+
$handlerClassName = is_object($handler) ? get_class($handler) : $handler;
307+
298308
if (!isset($this->tagHandlerParameterCache[$handlerClassName])) {
299309
$methodReflection = new ReflectionMethod($handlerClassName, 'create');
300310
$this->tagHandlerParameterCache[$handlerClassName] = $methodReflection->getParameters();
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
/*
3+
* This file is part of phpDocumentor.
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*
8+
* @link http://phpdoc.org
9+
*
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
15+
16+
use phpDocumentor\Reflection\DocBlock\Tag;
17+
use phpDocumentor\Reflection\DocBlock\TagFactory;
18+
use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag;
19+
use phpDocumentor\Reflection\Types\Context as TypeContext;
20+
use PHPStan\PhpDocParser\Lexer\Lexer;
21+
use PHPStan\PhpDocParser\Parser\ConstExprParser;
22+
use PHPStan\PhpDocParser\Parser\PhpDocParser;
23+
use PHPStan\PhpDocParser\Parser\TokenIterator;
24+
use PHPStan\PhpDocParser\Parser\TypeParser;
25+
26+
/**
27+
* Factory class creating tags using phpstan's parser
28+
*
29+
* This class uses {@see PHPStanFactory} implementations to create tags
30+
* from the ast of the phpstan docblock parser.
31+
*
32+
* @internal This class is not part of the BC promise of this library.
33+
*/
34+
class AbstractPHPStanFactory implements TagFactory
35+
{
36+
private PhpDocParser $parser;
37+
private Lexer $lexer;
38+
private array $factories;
39+
40+
public function __construct(PHPStanFactory ...$factories)
41+
{
42+
$this->lexer = new Lexer();
43+
$constParser = new ConstExprParser();
44+
$this->parser = new PhpDocParser(new TypeParser($constParser), $constParser);
45+
$this->factories = $factories;
46+
}
47+
48+
public function addParameter(string $name, $value): void
49+
{
50+
// TODO: Implement addParameter() method.
51+
}
52+
53+
public function create(string $tagLine, ?TypeContext $context = null): Tag
54+
{
55+
$tokens = $this->lexer->tokenize($tagLine);
56+
$ast = $this->parser->parseTag(new TokenIterator($tokens));
57+
58+
foreach ($this->factories as $factory) {
59+
if ($factory->supports($ast, $context)) {
60+
return $factory->create($ast, $context);
61+
}
62+
}
63+
64+
return InvalidTag::create(
65+
$ast->name,
66+
(string) $ast->value
67+
);
68+
}
69+
70+
public function addService(object $service): void
71+
{
72+
// TODO: Implement addService() method.
73+
}
74+
75+
public function registerTagHandler(string $tagName, string $handler): void
76+
{
77+
// TODO: Implement registerTagHandler() method.
78+
}
79+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
6+
7+
use phpDocumentor\Reflection\DocBlock\Tag;
8+
use phpDocumentor\Reflection\Types\Context;
9+
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
10+
11+
interface PHPStanFactory
12+
{
13+
public function create(PhpDocTagNode $node, Context $context): Tag;
14+
15+
public function supports(PhpDocTagNode $node, ?Context $context): bool;
16+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
6+
7+
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
8+
use phpDocumentor\Reflection\DocBlock\Tag;
9+
use phpDocumentor\Reflection\DocBlock\Tags\Param;
10+
use phpDocumentor\Reflection\Types\Context;
11+
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
12+
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
13+
use Webmozart\Assert\Assert;
14+
15+
use function trim;
16+
17+
/**
18+
* @internal This class is not part of the BC promise of this library.
19+
*/
20+
final class ParamFactory implements PHPStanFactory
21+
{
22+
private TypeFactory $typeFactory;
23+
private DescriptionFactory $descriptionFactory;
24+
25+
public function __construct(TypeFactory $typeFactory, DescriptionFactory $descriptionFactory)
26+
{
27+
$this->typeFactory = $typeFactory;
28+
$this->descriptionFactory = $descriptionFactory;
29+
}
30+
31+
public function create(PhpDocTagNode $node, Context $context): Tag
32+
{
33+
$tagValue = $node->value;
34+
Assert::isInstanceOf($tagValue, ParamTagValueNode::class);
35+
36+
return new Param(
37+
trim($tagValue->parameterName, '$'),
38+
$this->typeFactory->createType($tagValue->type, $context),
39+
$tagValue->isVariadic,
40+
$this->descriptionFactory->create($tagValue->description, $context),
41+
$tagValue->isReference
42+
);
43+
}
44+
45+
public function supports(PhpDocTagNode $node, ?Context $context): bool
46+
{
47+
return $node->value instanceof ParamTagValueNode;
48+
}
49+
}

0 commit comments

Comments
 (0)