Skip to content

Commit d1849f6

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

File tree

12 files changed

+269
-32
lines changed

12 files changed

+269
-32
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: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,22 @@
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)
2223
{
2324
}
2425

25-
/** @param (Element|FileElement)[] $elements */
26+
/** @param (Element|FileElement|PropertyHook)[] $elements */
2627
private static function createFromSelf(Project $project, TypeContext|null $typeContext, array $elements): self
2728
{
2829
$self = new self($project, $typeContext);
@@ -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) {
@@ -72,7 +73,7 @@ public function peek(): Element|FileElement
7273
*
7374
* @param class-string $type
7475
*/
75-
public function search(string $type): Element|FileElement|null
76+
public function search(string $type): Element|FileElement|PropertyHook|null
7677
{
7778
$reverseElements = array_reverse($this->elements);
7879
foreach ($reverseElements as $element) {

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,21 @@ 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+
->hooks($stmt->getHooks())
90+
->build($context);
8791

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

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

Lines changed: 80 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,36 @@ 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 iterable<Reducer> $reducers */
52+
private function __construct(
53+
private PrettyPrinter $valueConverter,
54+
private DocBlockFactoryInterface $docBlockFactory,
55+
private StrategyContainer $strategies,
56+
private iterable $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+
/**
62+
* @param iterable<Reducer> $reducers
63+
*/
64+
public static function create(
65+
PrettyPrinter $valueConverter,
66+
DocBlockFactoryInterface $docBlockFactory,
67+
StrategyContainer $strategies,
68+
iterable $reducers,
69+
): self {
70+
return new self($valueConverter, $docBlockFactory, $strategies, $reducers);
5171
}
5272

5373
public function fqsen(Fqsen $fqsen): self
@@ -78,7 +98,7 @@ public function readOnly(bool $readOnly): self
7898
return $this;
7999
}
80100

81-
public function docblock(DocBlock|null $docblock): self
101+
public function docblock(Doc|null $docblock): self
82102
{
83103
$this->docblock = $docblock;
84104

@@ -113,18 +133,30 @@ public function endLocation(Location $endLocation): self
113133
return $this;
114134
}
115135

116-
public function build(): PropertyElement
136+
/** @param PropertyHookNode[] $hooks */
137+
public function hooks(array $hooks): self
138+
{
139+
$this->hooks = $hooks;
140+
141+
return $this;
142+
}
143+
144+
public function build(ContextStack $context): PropertyElement
117145
{
118146
return new PropertyElement(
119147
$this->fqsen,
120148
$this->visibility,
121-
$this->docblock,
149+
$this->docblock !== null ? $this->docBlockFactory->create($this->docblock->getText(), $context->getTypeContext()) : null,
122150
$this->default !== null ? $this->valueConverter->prettyPrintExpr($this->default) : null,
123151
$this->static,
124152
$this->startLocation,
125153
$this->endLocation,
126154
(new Type())->fromPhpParser($this->type),
127155
$this->readOnly,
156+
array_filter(array_map(
157+
fn (PropertyHookNode $hook) => $this->buildHook($hook, $context),
158+
$this->hooks,
159+
)),
128160
);
129161
}
130162

@@ -160,7 +192,7 @@ private function buildVisibility(Param|PropertyIterator $node): Visibility
160192
private function buildReadVisibility(Param|PropertyIterator $node): Visibility
161193
{
162194
if ($node instanceof Param && method_exists($node, 'isPublic') === false) {
163-
return $this->buildPromotedPropertyReadVisibility($node->flags);
195+
return $this->buildVisibilityFromFlags($node->flags);
164196
}
165197

166198
if ($node->isPrivate()) {
@@ -174,7 +206,7 @@ private function buildReadVisibility(Param|PropertyIterator $node): Visibility
174206
return new Visibility(Visibility::PUBLIC_);
175207
}
176208

177-
private function buildPromotedPropertyReadVisibility(int $flags): Visibility
209+
private function buildVisibilityFromFlags(int $flags): Visibility
178210
{
179211
if ((bool) ($flags & Modifiers::PRIVATE) === true) {
180212
return new Visibility(Visibility::PRIVATE_);
@@ -199,4 +231,34 @@ private function buildWriteVisibility(Param|PropertyIterator $node): Visibility
199231

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

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)