Skip to content

Commit d3a3fc3

Browse files
committed
First implementation of hooks
1 parent 283247b commit d3a3fc3

12 files changed

+259
-30
lines changed

src/phpDocumentor/Reflection/Php/Factory/AbstractFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ abstract class AbstractFactory implements ProjectFactoryStrategy
3030
{
3131
/** @param iterable<Reducer> $reducers */
3232
public function __construct(
33-
private readonly DocBlockFactoryInterface $docBlockFactory,
33+
protected readonly DocBlockFactoryInterface $docBlockFactory,
3434
protected readonly iterable $reducers = [],
3535
) {
3636
}

src/phpDocumentor/Reflection/Php/Factory/ConstructorPromotion.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,16 +66,20 @@ private function promoteParameterToProperty(ContextStack $context, StrategyConta
6666

6767
$property = PropertyBuilder::create(
6868
$this->valueConverter,
69+
$this->docBlockFactory,
70+
$strategies,
71+
$this->reducers,
6972
)->fqsen(new Fqsen($methodContainer->getFqsen() . '::$' . (string) $param->var->name))
7073
->visibility($param)
7174
->type($param->type)
72-
->docblock($this->createDocBlock($param->getDocComment(), $context->getTypeContext()))
75+
->docblock($param->getDocComment())
7376
->default($param->default)
7477
->readOnly($this->readOnly($param->flags))
7578
->static(false)
7679
->startLocation(new Location($param->getLine(), $param->getStartFilePos()))
7780
->endLocation(new Location($param->getEndLine(), $param->getEndFilePos()))
78-
->build();
81+
->hooks($param->hooks)
82+
->build($context);
7983

8084
foreach ($this->reducers as $reducer) {
8185
$property = $reducer->reduce($context, $param, $strategies, $property);

src/phpDocumentor/Reflection/Php/Factory/ContextStack.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@
88
use phpDocumentor\Reflection\Element;
99
use phpDocumentor\Reflection\Php\File as FileElement;
1010
use phpDocumentor\Reflection\Php\Project;
11+
use phpDocumentor\Reflection\Php\PropertyHook;
1112
use phpDocumentor\Reflection\Types\Context as TypeContext;
1213

1314
use function array_reverse;
1415
use function end;
1516

1617
final class ContextStack
1718
{
18-
/** @var (Element|FileElement)[] */
19+
/** @var (Element|FileElement|PropertyHook)[] */
1920
private array $elements = [];
2021

2122
public function __construct(private readonly Project $project, private readonly TypeContext|null $typeContext = null)
@@ -31,7 +32,7 @@ private static function createFromSelf(Project $project, TypeContext|null $typeC
3132
return $self;
3233
}
3334

34-
public function push(Element|FileElement $element): self
35+
public function push(Element|FileElement|PropertyHook $element): self
3536
{
3637
$elements = $this->elements;
3738
$elements[] = $element;
@@ -54,7 +55,7 @@ public function getProject(): Project
5455
return $this->project;
5556
}
5657

57-
public function peek(): Element|FileElement
58+
public function peek(): Element|FileElement|PropertyHook
5859
{
5960
$element = end($this->elements);
6061
if ($element === false) {

src/phpDocumentor/Reflection/Php/Factory/Property.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,20 @@ protected function doCreate(
7373
foreach ($iterator as $stmt) {
7474
$property = PropertyBuilder::create(
7575
$this->valueConverter,
76+
$this->docBlockFactory,
77+
$strategies,
78+
$this->reducers,
7679
)
7780
->fqsen($stmt->getFqsen())
7881
->visibility($stmt)
7982
->type($stmt->getType())
80-
->docblock($this->createDocBlock($stmt->getDocComment(), $context->getTypeContext()))
83+
->docblock($stmt->getDocComment())
8184
->default($iterator->getDefault())
8285
->static($stmt->isStatic())
8386
->startLocation(new Location($stmt->getLine()))
8487
->endLocation(new Location($stmt->getEndLine()))
8588
->readOnly($stmt->isReadonly())
86-
->build();
89+
->build($context);
8790

8891
foreach ($this->reducers as $reducer) {
8992
$property = $reducer->reduce($context, $object, $strategies, $property);

src/phpDocumentor/Reflection/Php/Factory/PropertyBuilder.php

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,27 @@
44

55
namespace phpDocumentor\Reflection\Php\Factory;
66

7-
use phpDocumentor\Reflection\DocBlock;
7+
use phpDocumentor\Reflection\DocBlockFactoryInterface;
88
use phpDocumentor\Reflection\Fqsen;
99
use phpDocumentor\Reflection\Location;
1010
use phpDocumentor\Reflection\Php\AsyncVisibility;
11+
use phpDocumentor\Reflection\Php\Factory\Reducer\Reducer;
1112
use phpDocumentor\Reflection\Php\Property as PropertyElement;
13+
use phpDocumentor\Reflection\Php\PropertyHook;
14+
use phpDocumentor\Reflection\Php\StrategyContainer;
1215
use phpDocumentor\Reflection\Php\Visibility;
16+
use PhpParser\Comment\Doc;
1317
use PhpParser\Modifiers;
1418
use PhpParser\Node\ComplexType;
1519
use PhpParser\Node\Expr;
1620
use PhpParser\Node\Identifier;
1721
use PhpParser\Node\Name;
1822
use PhpParser\Node\Param;
23+
use phpParser\Node\PropertyHook as PropertyHookNode;
1924
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
2025

26+
use function array_filter;
27+
use function array_map;
2128
use function method_exists;
2229

2330
/**
@@ -31,23 +38,33 @@ final class PropertyBuilder
3138
private Visibility $visibility;
3239
private bool $readOnly = false;
3340
private Identifier|Name|ComplexType|null $type;
34-
private DocBlock|null $docblock;
35-
private PrettyPrinter $valueConverter;
36-
private Expr|null $default;
37-
private bool $static;
41+
private Doc|null $docblock = null;
42+
43+
private Expr|null $default = null;
44+
private bool $static = false;
3845
private Location $startLocation;
3946
private Location $endLocation;
4047

41-
private function __construct()
42-
{
48+
/** @var PropertyHookNode */
49+
private array $hooks = [];
50+
51+
/** @param Reducer[] $reducers */
52+
private function __construct(
53+
private PrettyPrinter $valueConverter,
54+
private DocBlockFactoryInterface $docBlockFactory,
55+
private StrategyContainer $strategies,
56+
private array $reducers,
57+
) {
58+
$this->visibility = new Visibility(Visibility::PUBLIC_);
4359
}
4460

45-
public static function create(PrettyPrinter $valueConverter): self
46-
{
47-
$instance = new self();
48-
$instance->valueConverter = $valueConverter;
49-
50-
return $instance;
61+
public static function create(
62+
PrettyPrinter $valueConverter,
63+
DocBlockFactoryInterface $docBlockFactory,
64+
StrategyContainer $strategies,
65+
array $reducers,
66+
): self {
67+
return new self($valueConverter, $docBlockFactory, $strategies, $reducers);
5168
}
5269

5370
public function fqsen(Fqsen $fqsen): self
@@ -78,7 +95,7 @@ public function readOnly(bool $readOnly): self
7895
return $this;
7996
}
8097

81-
public function docblock(DocBlock|null $docblock): self
98+
public function docblock(Doc|null $docblock): self
8299
{
83100
$this->docblock = $docblock;
84101

@@ -113,18 +130,30 @@ public function endLocation(Location $endLocation): self
113130
return $this;
114131
}
115132

116-
public function build(): PropertyElement
133+
/** @param PropertyHookNode[] $hooks */
134+
public function hooks(array $hooks): self
135+
{
136+
$this->hooks = $hooks;
137+
138+
return $this;
139+
}
140+
141+
public function build(ContextStack|null $context): PropertyElement
117142
{
118143
return new PropertyElement(
119144
$this->fqsen,
120145
$this->visibility,
121-
$this->docblock,
146+
$this->docblock !== null ? $this->docBlockFactory->create($this->docblock->getText(), $context?->getTypeContext()) : null,
122147
$this->default !== null ? $this->valueConverter->prettyPrintExpr($this->default) : null,
123148
$this->static,
124149
$this->startLocation,
125150
$this->endLocation,
126151
(new Type())->fromPhpParser($this->type),
127152
$this->readOnly,
153+
array_filter(array_map(
154+
fn (PropertyHookNode $hook) => $this->buildHook($hook, $context),
155+
$this->hooks,
156+
)),
128157
);
129158
}
130159

@@ -160,7 +189,7 @@ private function buildVisibility(Param|PropertyIterator $node): Visibility
160189
private function buildReadVisibility(Param|PropertyIterator $node): Visibility
161190
{
162191
if ($node instanceof Param && method_exists($node, 'isPublic') === false) {
163-
return $this->buildPromotedPropertyReadVisibility($node->flags);
192+
return $this->buildVisibilityFromFlags($node->flags);
164193
}
165194

166195
if ($node->isPrivate()) {
@@ -174,7 +203,7 @@ private function buildReadVisibility(Param|PropertyIterator $node): Visibility
174203
return new Visibility(Visibility::PUBLIC_);
175204
}
176205

177-
private function buildPromotedPropertyReadVisibility(int $flags): Visibility
206+
private function buildVisibilityFromFlags(int $flags): Visibility
178207
{
179208
if ((bool) ($flags & Modifiers::PRIVATE) === true) {
180209
return new Visibility(Visibility::PRIVATE_);
@@ -199,4 +228,30 @@ private function buildWriteVisibility(Param|PropertyIterator $node): Visibility
199228

200229
return new Visibility(Visibility::PUBLIC_);
201230
}
231+
232+
private function buildHook(PropertyHookNode $hook, ContextStack $context): PropertyHook
233+
{
234+
$doc = $hook->getDocComment();
235+
236+
$result = new PropertyHook(
237+
$hook->name->toString(),
238+
$this->buildVisibilityFromFlags($hook->flags),
239+
$doc !== null ? $this->docBlockFactory->create($doc->getText(), $context->getTypeContext()) : null,
240+
$hook->isFinal(),
241+
new Location($hook->getStartLine(), $hook->getStartFilePos()),
242+
new Location($hook->getEndLine(), $hook->getEndFilePos()),
243+
);
244+
245+
foreach ($this->reducers as $reducer) {
246+
$result = $reducer->reduce($context, $hook, $this->strategies, $result);
247+
}
248+
249+
$thisContext = $context->push($result);
250+
foreach ($hook->getStmts() as $stmt) {
251+
$strategy = $this->strategies->findMatching($thisContext, $stmt);
252+
$strategy->create($thisContext, $stmt, $this->strategies);
253+
}
254+
255+
return $result;
256+
}
202257
}

src/phpDocumentor/Reflection/Php/Factory/PropertyIterator.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@
2020
use PhpParser\Node\Expr;
2121
use PhpParser\Node\Identifier;
2222
use PhpParser\Node\Name;
23+
use PhpParser\Node\PropertyHook;
2324
use PhpParser\Node\Stmt\Property as PropertyNode;
2425

2526
use function method_exists;
27+
use function property_exists;
2628

2729
/**
2830
* This class acts like a combination of a PropertyNode and PropertyProperty to
@@ -201,6 +203,16 @@ public function getFqsen(): Fqsen
201203
return $this->property->props[$this->index]->getAttribute('fqsen');
202204
}
203205

206+
/** @return PropertyHook[] */
207+
public function getHooks(): array
208+
{
209+
if (property_exists($this->property, 'hooks') === false) {
210+
return [];
211+
}
212+
213+
return $this->property->hooks;
214+
}
215+
204216
/** @link http://php.net/manual/en/iterator.current.php */
205217
public function current(): self
206218
{

src/phpDocumentor/Reflection/Php/Factory/Reducer/Parameter.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use phpDocumentor\Reflection\Php\Factory\Type;
1010
use phpDocumentor\Reflection\Php\Function_;
1111
use phpDocumentor\Reflection\Php\Method;
12+
use phpDocumentor\Reflection\Php\PropertyHook;
1213
use phpDocumentor\Reflection\Php\StrategyContainer;
1314
use PhpParser\Node\Expr\Variable;
1415
use PhpParser\Node\FunctionLike;
@@ -33,7 +34,7 @@ public function reduce(
3334
return $carry;
3435
}
3536

36-
if ($carry instanceof Method === false && $carry instanceof Function_ === false) {
37+
if ($carry instanceof Method === false && $carry instanceof Function_ === false && $carry instanceof PropertyHook === false) {
3738
return null;
3839
}
3940

src/phpDocumentor/Reflection/Php/ProjectFactory.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static function createInstance(): self
8585
new Function_($docblockFactory, [$attributeReducer, $parameterReducer]),
8686
new Interface_($docblockFactory, [$attributeReducer]),
8787
$methodStrategy,
88-
new Property($docblockFactory, new PrettyPrinter(), [$attributeReducer]),
88+
new Property($docblockFactory, new PrettyPrinter(), [$attributeReducer, $parameterReducer]),
8989
new Trait_($docblockFactory, [$attributeReducer]),
9090

9191
new IfStatement(),
@@ -94,7 +94,7 @@ public static function createInstance(): self
9494
);
9595

9696
$strategies->addStrategy(
97-
new ConstructorPromotion($methodStrategy, $docblockFactory, new PrettyPrinter(), [$attributeReducer]),
97+
new ConstructorPromotion($methodStrategy, $docblockFactory, new PrettyPrinter(), [$attributeReducer, $parameterReducer]),
9898
1100,
9999
);
100100
$strategies->addStrategy(new Noop(), -PHP_INT_MAX);

src/phpDocumentor/Reflection/Php/Property.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ final class Property implements Element, MetaDataContainerInterface, AttributeCo
3737

3838
private readonly Location $endLocation;
3939

40-
/** @param Visibility|null $visibility when null is provided a default 'public' is set. */
40+
/**
41+
* @param Visibility|null $visibility when null is provided a default 'public' is set.
42+
* @param PropertyHook[] $hooks
43+
*/
4144
public function __construct(
4245
private readonly Fqsen $fqsen,
4346
Visibility|null $visibility = null,
@@ -48,6 +51,7 @@ public function __construct(
4851
Location|null $endLocation = null,
4952
private readonly Type|null $type = null,
5053
private readonly bool $readOnly = false,
54+
private readonly array $hooks = [],
5155
) {
5256
$this->visibility = $visibility ?: new Visibility('public');
5357
$this->location = $location ?: new Location(-1);

0 commit comments

Comments
 (0)