Skip to content

Commit 68eaf8f

Browse files
committed
Centralize property creation.
As property creation is getting more complex, move all complexity into a sepatate builder class that handles everything.
1 parent 2e69fec commit 68eaf8f

File tree

7 files changed

+227
-132
lines changed

7 files changed

+227
-132
lines changed

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ fix-code-style:
1212

1313
.PHONY: static-code-analysis
1414
static-code-analysis: vendor ## Runs a static code analysis with phpstan/phpstan and vimeo/psalm
15-
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpstan --configuration=phpstan.neon
16-
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/psalm.phar
15+
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.2-cli vendor/bin/phpstan --configuration=phpstan.neon
16+
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.2-cli vendor/bin/psalm.phar
1717

1818
.PHONY: test
1919
test: test-unit test-functional ## Runs all test suites with phpunit/phpunit
@@ -25,7 +25,7 @@ test-unit: ## Runs unit tests with phpunit/phpunit
2525

2626
.PHONY: test-functional
2727
test-functional: ## Runs unit tests with phpunit/phpunit
28-
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit --testsuite=functional
28+
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:8.1-cli vendor/bin/phpunit --testsuite=integration
2929

3030
.PHONY: dependency-analysis
3131
dependency-analysis: vendor ## Runs a dependency analysis with maglnet/composer-require-checker

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

Lines changed: 12 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,10 @@
88
use phpDocumentor\Reflection\DocBlockFactoryInterface;
99
use phpDocumentor\Reflection\Fqsen;
1010
use phpDocumentor\Reflection\Location;
11-
use phpDocumentor\Reflection\Php\AsyncVisibility;
1211
use phpDocumentor\Reflection\Php\Class_ as ClassElement;
1312
use phpDocumentor\Reflection\Php\Factory\Reducer\Reducer;
1413
use phpDocumentor\Reflection\Php\ProjectFactoryStrategy;
15-
use phpDocumentor\Reflection\Php\Property;
1614
use phpDocumentor\Reflection\Php\StrategyContainer;
17-
use phpDocumentor\Reflection\Php\Visibility;
1815
use PhpParser\Modifiers;
1916
use PhpParser\Node\Expr\Variable;
2017
use PhpParser\Node\Param;
@@ -67,17 +64,18 @@ private function promoteParameterToProperty(ContextStack $context, StrategyConta
6764
Assert::isInstanceOf($methodContainer, ClassElement::class);
6865
Assert::isInstanceOf($param->var, Variable::class);
6966

70-
$property = new Property(
71-
new Fqsen($methodContainer->getFqsen() . '::$' . (string) $param->var->name),
72-
$this->buildPropertyVisibilty($param->flags),
73-
$this->createDocBlock($param->getDocComment(), $context->getTypeContext()),
74-
$param->default !== null ? $this->valueConverter->prettyPrintExpr($param->default) : null,
75-
false,
76-
new Location($param->getLine()),
77-
new Location($param->getEndLine()),
78-
(new Type())->fromPhpParser($param->type),
79-
$this->readOnly($param->flags),
80-
);
67+
$property = PropertyBuilder::create(
68+
$this->valueConverter,
69+
)->fqsen(new Fqsen($methodContainer->getFqsen() . '::$' . (string) $param->var->name))
70+
->visibility($param)
71+
->type($param->type)
72+
->docblock($this->createDocBlock($param->getDocComment(), $context->getTypeContext()))
73+
->default($param->default)
74+
->readOnly($this->readOnly($param->flags))
75+
->static(false)
76+
->startLocation(new Location($param->getLine(), $param->getStartFilePos()))
77+
->endLocation(new Location($param->getEndLine(), $param->getEndFilePos()))
78+
->build();
8179

8280
foreach ($this->reducers as $reducer) {
8381
$property = $reducer->reduce($context, $param, $strategies, $property);
@@ -90,44 +88,6 @@ private function promoteParameterToProperty(ContextStack $context, StrategyConta
9088
$methodContainer->addProperty($property);
9189
}
9290

93-
private function buildPropertyVisibilty(int $flags): Visibility
94-
{
95-
if ((bool) ($flags & Modifiers::VISIBILITY_SET_MASK) !== false) {
96-
return new AsyncVisibility(
97-
$this->buildReadVisibility($flags),
98-
$this->buildWriteVisibility($flags),
99-
);
100-
}
101-
102-
return $this->buildReadVisibility($flags);
103-
}
104-
105-
private function buildReadVisibility(int $flags): Visibility
106-
{
107-
if ((bool) ($flags & Modifiers::PRIVATE) === true) {
108-
return new Visibility(Visibility::PRIVATE_);
109-
}
110-
111-
if ((bool) ($flags & Modifiers::PROTECTED) === true) {
112-
return new Visibility(Visibility::PROTECTED_);
113-
}
114-
115-
return new Visibility(Visibility::PUBLIC_);
116-
}
117-
118-
private function buildWriteVisibility(int $flags): Visibility
119-
{
120-
if ((bool) ($flags & Modifiers::PRIVATE_SET) === true) {
121-
return new Visibility(Visibility::PRIVATE_);
122-
}
123-
124-
if ((bool) ($flags & Modifiers::PROTECTED_SET) === true) {
125-
return new Visibility(Visibility::PROTECTED_);
126-
}
127-
128-
return new Visibility(Visibility::PUBLIC_);
129-
}
130-
13191
private function readOnly(int $flags): bool
13292
{
13393
return (bool) ($flags & Modifiers::READONLY) === true;

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

Lines changed: 13 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,11 @@
1515

1616
use phpDocumentor\Reflection\DocBlockFactoryInterface;
1717
use phpDocumentor\Reflection\Location;
18-
use phpDocumentor\Reflection\Php\AsyncVisibility;
1918
use phpDocumentor\Reflection\Php\Class_;
2019
use phpDocumentor\Reflection\Php\Factory\Reducer\Reducer;
2120
use phpDocumentor\Reflection\Php\Property as PropertyDescriptor;
2221
use phpDocumentor\Reflection\Php\StrategyContainer;
2322
use phpDocumentor\Reflection\Php\Trait_;
24-
use phpDocumentor\Reflection\Php\Visibility;
2523
use PhpParser\Node\Stmt\Property as PropertyNode;
2624
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
2725
use Webmozart\Assert\Assert;
@@ -73,22 +71,19 @@ protected function doCreate(
7371

7472
$iterator = new PropertyIterator($object);
7573
foreach ($iterator as $stmt) {
76-
$default = $iterator->getDefault();
77-
if ($default !== null) {
78-
$default = $this->valueConverter->prettyPrintExpr($default);
79-
}
80-
81-
$property = new PropertyDescriptor(
82-
$stmt->getFqsen(),
83-
$this->buildVisibility($stmt),
84-
$this->createDocBlock($stmt->getDocComment(), $context->getTypeContext()),
85-
$default,
86-
$stmt->isStatic(),
87-
new Location($stmt->getLine()),
88-
new Location($stmt->getEndLine()),
89-
(new Type())->fromPhpParser($stmt->getType()),
90-
$stmt->isReadonly(),
91-
);
74+
$property = PropertyBuilder::create(
75+
$this->valueConverter,
76+
)
77+
->fqsen($stmt->getFqsen())
78+
->visibility($stmt)
79+
->type($stmt->getType())
80+
->docblock($this->createDocBlock($stmt->getDocComment(), $context->getTypeContext()))
81+
->default($iterator->getDefault())
82+
->static($stmt->isStatic())
83+
->startLocation(new Location($stmt->getLine()))
84+
->endLocation(new Location($stmt->getEndLine()))
85+
->readOnly($stmt->isReadonly())
86+
->build(null);
9287

9388
foreach ($this->reducers as $reducer) {
9489
$property = $reducer->reduce($context, $object, $strategies, $property);
@@ -103,48 +98,4 @@ protected function doCreate(
10398

10499
return null;
105100
}
106-
107-
/**
108-
* Converts the visibility of the property to a valid Visibility object.
109-
*/
110-
private function buildVisibility(PropertyIterator $node): Visibility
111-
{
112-
if ($node->isAsync() === false) {
113-
return $this->buildReadVisibility($node);
114-
}
115-
116-
$readVisibility = $this->buildReadVisibility($node);
117-
$writeVisibility = $this->buildWriteVisibility($node);
118-
119-
return new AsyncVisibility(
120-
$readVisibility,
121-
$writeVisibility,
122-
);
123-
}
124-
125-
private function buildReadVisibility(PropertyIterator $node): Visibility
126-
{
127-
if ($node->isPrivate()) {
128-
return new Visibility(Visibility::PRIVATE_);
129-
}
130-
131-
if ($node->isProtected()) {
132-
return new Visibility(Visibility::PROTECTED_);
133-
}
134-
135-
return new Visibility(Visibility::PUBLIC_);
136-
}
137-
138-
private function buildWriteVisibility(PropertyIterator $node): Visibility
139-
{
140-
if ($node->isPrivateSet()) {
141-
return new Visibility(Visibility::PRIVATE_);
142-
}
143-
144-
if ($node->isProtectedSet()) {
145-
return new Visibility(Visibility::PROTECTED_);
146-
}
147-
148-
return new Visibility(Visibility::PUBLIC_);
149-
}
150101
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Php\Factory;
6+
7+
use phpDocumentor\Reflection\DocBlock;
8+
use phpDocumentor\Reflection\Fqsen;
9+
use phpDocumentor\Reflection\Location;
10+
use phpDocumentor\Reflection\Php\AsyncVisibility;
11+
use phpDocumentor\Reflection\Php\Property as PropertyElement;
12+
use phpDocumentor\Reflection\Php\Visibility;
13+
use PhpParser\Node\ComplexType;
14+
use PhpParser\Node\Expr;
15+
use PhpParser\Node\Identifier;
16+
use PhpParser\Node\Name;
17+
use PhpParser\Node\Param;
18+
use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
19+
20+
use function method_exists;
21+
22+
/**
23+
* This class is responsible for building a property element from a PhpParser node.
24+
*
25+
* @internal
26+
*/
27+
final class PropertyBuilder
28+
{
29+
private Fqsen $fqsen;
30+
private Visibility $visibility;
31+
private bool $readOnly = false;
32+
private Identifier|Name|ComplexType|null $type;
33+
private DocBlock|null $docblock;
34+
private PrettyPrinter $valueConverter;
35+
private Expr|null $default;
36+
private bool $static;
37+
private Location $startLocation;
38+
private Location $endLocation;
39+
40+
private function __construct()
41+
{
42+
}
43+
44+
public static function create(PrettyPrinter $valueConverter): self
45+
{
46+
$instance = new self();
47+
$instance->valueConverter = $valueConverter;
48+
49+
return $instance;
50+
}
51+
52+
public function fqsen(Fqsen $fqsen): self
53+
{
54+
$this->fqsen = $fqsen;
55+
56+
return $this;
57+
}
58+
59+
public function visibility(Param|PropertyIterator $node): self
60+
{
61+
$this->visibility = $this->buildVisibility($node);
62+
63+
return $this;
64+
}
65+
66+
public function type(Identifier|Name|ComplexType|null $type): self
67+
{
68+
$this->type = $type;
69+
70+
return $this;
71+
}
72+
73+
public function readOnly(bool $readOnly): self
74+
{
75+
$this->readOnly = $readOnly;
76+
77+
return $this;
78+
}
79+
80+
public function docblock(DocBlock|null $docblock): self
81+
{
82+
$this->docblock = $docblock;
83+
84+
return $this;
85+
}
86+
87+
public function default(Expr|null $default): self
88+
{
89+
$this->default = $default;
90+
91+
return $this;
92+
}
93+
94+
public function static(bool $static): self
95+
{
96+
$this->static = $static;
97+
98+
return $this;
99+
}
100+
101+
public function startLocation(Location $startLocation): self
102+
{
103+
$this->startLocation = $startLocation;
104+
105+
return $this;
106+
}
107+
108+
public function endLocation(Location $endLocation): self
109+
{
110+
$this->endLocation = $endLocation;
111+
112+
return $this;
113+
}
114+
115+
public function build(): PropertyElement
116+
{
117+
return new PropertyElement(
118+
$this->fqsen,
119+
$this->visibility,
120+
$this->docblock,
121+
$this->default !== null ? $this->valueConverter->prettyPrintExpr($this->default) : null,
122+
$this->static,
123+
$this->startLocation,
124+
$this->endLocation,
125+
(new Type())->fromPhpParser($this->type),
126+
$this->readOnly,
127+
);
128+
}
129+
130+
/**
131+
* Returns true when current property has async accessors.
132+
*
133+
* This method will always return false when your phpparser version is < 5.2
134+
*/
135+
private function isAsync(Param|PropertyIterator $node): bool
136+
{
137+
if (method_exists($node, 'isPrivateSet') === false) {
138+
return false;
139+
}
140+
141+
return $node->isPublicSet() || $node->isProtectedSet() || $node->isPrivateSet();
142+
}
143+
144+
private function buildVisibility(Param|PropertyIterator $node): Visibility
145+
{
146+
if ($this->isAsync($node) === false) {
147+
return $this->buildReadVisibility($node);
148+
}
149+
150+
$readVisibility = $this->buildReadVisibility($node);
151+
$writeVisibility = $this->buildWriteVisibility($node);
152+
153+
return new AsyncVisibility(
154+
$readVisibility,
155+
$writeVisibility,
156+
);
157+
}
158+
159+
private function buildReadVisibility(Param|PropertyIterator $node): Visibility
160+
{
161+
if ($node->isPrivate()) {
162+
return new Visibility(Visibility::PRIVATE_);
163+
}
164+
165+
if ($node->isProtected()) {
166+
return new Visibility(Visibility::PROTECTED_);
167+
}
168+
169+
return new Visibility(Visibility::PUBLIC_);
170+
}
171+
172+
private function buildWriteVisibility(Param|PropertyIterator $node): Visibility
173+
{
174+
if ($node->isPrivateSet()) {
175+
return new Visibility(Visibility::PRIVATE_);
176+
}
177+
178+
if ($node->isProtectedSet()) {
179+
return new Visibility(Visibility::PROTECTED_);
180+
}
181+
182+
return new Visibility(Visibility::PUBLIC_);
183+
}
184+
}

0 commit comments

Comments
 (0)