Skip to content

Commit fc36142

Browse files
committed
Started reworking expression strings into Expression objects
Values of constants, named arguments, etc, should be parsed to include types and FQSENs that can be used in phpDocumentor to display links with. Since these are now simple strings, I have refactored this into a template string system with placeholder values that a consumer could replace with its own values.
1 parent 41cb981 commit fc36142

File tree

16 files changed

+326
-52
lines changed

16 files changed

+326
-52
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,16 @@ static-code-analysis: vendor ## Runs a static code analysis with phpstan/phpstan
1616
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/psalm
1717

1818
.PHONY: test
19-
test: test-unit test-functional ## Runs all test suites with phpunit/phpunit
19+
test: test-unit test-integration ## Runs all test suites with phpunit/phpunit
2020
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit
2121

2222
.PHONY: test-unit
2323
test-unit: ## Runs unit tests with phpunit/phpunit
2424
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit --testsuite=unit
2525

26-
.PHONY: test-functional
27-
test-functional: ## Runs unit tests with phpunit/phpunit
28-
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 vendor/bin/phpunit --testsuite=functional
26+
.PHONY: test-integration
27+
test-integration: ## Runs unit tests with phpunit/phpunit
28+
docker run -it --rm -v${PWD}:/opt/project -w /opt/project php:7.4 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/Argument.php

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ final class Argument
2727
/** @var Type a normalized type that should be in this Argument */
2828
private Type $type;
2929

30-
/** @var string|null the default value for an argument or null if none is provided */
31-
private ?string $default;
30+
/** @var Expression|null the default value for an argument or null if none is provided */
31+
private ?Expression $default;
3232

3333
/** @var bool whether the argument passes the parameter by reference instead of by value */
3434
private bool $byReference;
@@ -38,22 +38,33 @@ final class Argument
3838

3939
/**
4040
* Initializes the object.
41+
*
42+
* @param string|Expression|null $default
4143
*/
4244
public function __construct(
4345
string $name,
4446
?Type $type = null,
45-
?string $default = null,
47+
$default = null,
4648
bool $byReference = false,
4749
bool $isVariadic = false
4850
) {
4951
$this->name = $name;
50-
$this->default = $default;
5152
$this->byReference = $byReference;
5253
$this->isVariadic = $isVariadic;
5354
if ($type === null) {
5455
$type = new Mixed_();
5556
}
5657

58+
if (is_string($default)) {
59+
trigger_error(
60+
'Default values for arguments should be of type Expression, support for strings will be '
61+
. 'removed in 6.x',
62+
E_USER_DEPRECATED
63+
);
64+
$default = new Expression($default, []);
65+
}
66+
$this->default = $default;
67+
5768
$this->type = $type;
5869
}
5970

@@ -70,8 +81,20 @@ public function getType(): ?Type
7081
return $this->type;
7182
}
7283

73-
public function getDefault(): ?string
84+
/**
85+
* @return Expression|string|null
86+
*/
87+
public function getDefault(bool $asString = true)
7488
{
89+
if ($asString) {
90+
trigger_error(
91+
'The Default value will become of type Expression by default',
92+
E_USER_DEPRECATED
93+
);
94+
95+
return (string) $this->default;
96+
}
97+
7598
return $this->default;
7699
}
77100

src/phpDocumentor/Reflection/Php/Constant.php

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@ final class Constant implements Element, MetaDataContainerInterface
3030

3131
private ?DocBlock $docBlock;
3232

33-
private ?string $value;
33+
/**
34+
* @var string|Expression|null
35+
*/
36+
private $value;
3437

3538
private Location $location;
3639

@@ -46,26 +49,47 @@ final class Constant implements Element, MetaDataContainerInterface
4649
public function __construct(
4750
Fqsen $fqsen,
4851
?DocBlock $docBlock = null,
49-
?string $value = null,
52+
$value = null,
5053
?Location $location = null,
5154
?Location $endLocation = null,
5255
?Visibility $visibility = null,
5356
bool $final = false
5457
) {
5558
$this->fqsen = $fqsen;
5659
$this->docBlock = $docBlock;
57-
$this->value = $value;
5860
$this->location = $location ?: new Location(-1);
5961
$this->endLocation = $endLocation ?: new Location(-1);
6062
$this->visibility = $visibility ?: new Visibility(Visibility::PUBLIC_);
6163
$this->final = $final;
64+
65+
if (is_string($value)) {
66+
trigger_error(
67+
'Constant values should be of type Expression, support for strings will be '
68+
. 'removed in 6.x',
69+
E_USER_DEPRECATED
70+
);
71+
$value = new Expression($value, []);
72+
}
73+
$this->value = $value;
74+
6275
}
6376

6477
/**
65-
* Returns the value of this constant.
78+
* Returns the expression value for this constant.
79+
*
80+
* @return Expression|string|null
6681
*/
67-
public function getValue(): ?string
82+
public function getValue(bool $asString = true)
6883
{
84+
if ($asString) {
85+
trigger_error(
86+
'The expression value will become of type Expression by default',
87+
E_USER_DEPRECATED
88+
);
89+
90+
return (string) $this->value;
91+
}
92+
6993
return $this->value;
7094
}
7195

src/phpDocumentor/Reflection/Php/EnumCase.php

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,18 @@ final class EnumCase implements Element, MetaDataContainerInterface
2222

2323
private Location $endLocation;
2424

25-
private ?string $value;
25+
/** @var Expression|string|null */
26+
private $value;
2627

28+
/**
29+
* @param Expression|string|null $value
30+
*/
2731
public function __construct(
2832
Fqsen $fqsen,
2933
?DocBlock $docBlock,
3034
?Location $location = null,
3135
?Location $endLocation = null,
32-
?string $value = null
36+
$value = null
3337
) {
3438
if ($location === null) {
3539
$location = new Location(-1);
@@ -43,6 +47,14 @@ public function __construct(
4347
$this->docBlock = $docBlock;
4448
$this->location = $location;
4549
$this->endLocation = $endLocation;
50+
if (is_string($value)) {
51+
trigger_error(
52+
'Expression values for enum cases should be of type Expression, support for strings will be '
53+
. 'removed in 6.x',
54+
E_USER_DEPRECATED
55+
);
56+
$value = new Expression($value, []);
57+
}
4658
$this->value = $value;
4759
}
4860

@@ -71,8 +83,22 @@ public function getEndLocation(): Location
7183
return $this->endLocation;
7284
}
7385

74-
public function getValue(): ?string
86+
/**
87+
* Returns the value for this enum case.
88+
*
89+
* @return Expression|string|null
90+
*/
91+
public function getValue(bool $asString = true)
7592
{
93+
if ($asString) {
94+
trigger_error(
95+
'The enum case value will become of type Expression by default',
96+
E_USER_DEPRECATED
97+
);
98+
99+
return (string) $this->value;
100+
}
101+
76102
return $this->value;
77103
}
78104
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Php;
6+
7+
use phpDocumentor\Reflection\Fqsen;
8+
use phpDocumentor\Reflection\Type;
9+
10+
final class Expression
11+
{
12+
private string $expression;
13+
14+
/** @var array<string, Fqsen|Type> */
15+
private array $parts;
16+
17+
public function __construct(string $expression, array $parts)
18+
{
19+
$this->expression = $expression;
20+
$this->parts = $parts;
21+
}
22+
23+
public function getExpression(): string
24+
{
25+
return $this->expression;
26+
}
27+
28+
public function getParts(): array
29+
{
30+
return $this->parts;
31+
}
32+
33+
public function __toString(): string
34+
{
35+
$valuesAsStrings = array_map(
36+
static fn(object $part): string => (string)$part,
37+
$this->parts
38+
);
39+
40+
return str_replace(array_keys($this->parts), $valuesAsStrings, $this->expression);
41+
}
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Php\Expression;
6+
7+
use phpDocumentor\Reflection\Fqsen;
8+
use PhpParser\Node\Name;
9+
use PhpParser\PrettyPrinter\Standard;
10+
11+
final class ExpressionPrinter extends Standard
12+
{
13+
/**
14+
* @var array<Fqsen>
15+
*/
16+
private array $parts = [];
17+
18+
protected function resetState(): void
19+
{
20+
parent::resetState();
21+
22+
$this->parts = [];
23+
}
24+
25+
protected function pName(Name $node): string
26+
{
27+
$renderedName = parent::pName($node);
28+
$code = md5($renderedName);
29+
30+
$placeholder = '{{ PHPDOC' . $code . ' }}';
31+
$this->parts[$placeholder] = new Fqsen('\\' . $node);
32+
33+
return $placeholder;
34+
}
35+
36+
/**
37+
* @return array<Fqsen>
38+
*/
39+
public function getParts(): array
40+
{
41+
return $this->parts;
42+
}
43+
}

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
namespace phpDocumentor\Reflection\Php\Factory;
1515

1616
use phpDocumentor\Reflection\Php\Argument as ArgumentDescriptor;
17+
use phpDocumentor\Reflection\Php\Expression;
18+
use phpDocumentor\Reflection\Php\Expression\ExpressionPrinter;
1719
use phpDocumentor\Reflection\Php\Function_;
1820
use phpDocumentor\Reflection\Php\Method;
1921
use phpDocumentor\Reflection\Php\ProjectFactoryStrategy;
@@ -73,11 +75,16 @@ protected function doCreate(
7375
]
7476
);
7577

78+
$default = $object->default !== null ? $this->valueConverter->prettyPrintExpr($object->default) : null;
79+
if ($this->valueConverter instanceof ExpressionPrinter) {
80+
$default = new Expression($default, $this->valueConverter->getParts());
81+
}
82+
7683
$method->addArgument(
7784
new ArgumentDescriptor(
7885
(string) $object->var->name,
7986
(new Type())->fromPhpParser($object->type),
80-
$object->default !== null ? $this->valueConverter->prettyPrintExpr($object->default) : null,
87+
$default,
8188
$object->byRef,
8289
$object->variadic
8390
)

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use phpDocumentor\Reflection\Php\Class_;
1919
use phpDocumentor\Reflection\Php\Constant as ConstantElement;
2020
use phpDocumentor\Reflection\Php\Enum_;
21+
use phpDocumentor\Reflection\Php\Expression;
22+
use phpDocumentor\Reflection\Php\Expression\ExpressionPrinter;
2123
use phpDocumentor\Reflection\Php\Interface_;
2224
use phpDocumentor\Reflection\Php\StrategyContainer;
2325
use phpDocumentor\Reflection\Php\Trait_;
@@ -79,7 +81,7 @@ protected function doCreate(
7981
$constantContainer->addConstant(new ConstantElement(
8082
$const->getFqsen(),
8183
$this->createDocBlock($const->getDocComment(), $context->getTypeContext()),
82-
$const->getValue() !== null ? $this->valueConverter->prettyPrintExpr($const->getValue()) : null,
84+
$this->determineValue($const),
8385
new Location($const->getLine()),
8486
new Location($const->getEndLine()),
8587
$this->buildVisibility($const),
@@ -88,6 +90,19 @@ protected function doCreate(
8890
}
8991
}
9092

93+
private function determineValue(ClassConstantIterator $value): ?Expression
94+
{
95+
$expression = $value->getValue() !== null ? $this->valueConverter->prettyPrintExpr($value->getValue()) : null;
96+
if ($this->valueConverter instanceof ExpressionPrinter) {
97+
$expression = new Expression($expression, $this->valueConverter->getParts());
98+
}
99+
if (is_string($expression)) {
100+
$expression = new Expression($expression, []);
101+
}
102+
103+
return $expression;
104+
}
105+
91106
/**
92107
* Converts the visibility of the constant to a valid Visibility object.
93108
*/

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use phpDocumentor\Reflection\Fqsen;
1818
use phpDocumentor\Reflection\Location;
1919
use phpDocumentor\Reflection\Php\Constant as ConstantElement;
20+
use phpDocumentor\Reflection\Php\Expression as ValueExpression;
21+
use phpDocumentor\Reflection\Php\Expression\ExpressionPrinter;
2022
use phpDocumentor\Reflection\Php\File as FileElement;
2123
use phpDocumentor\Reflection\Php\StrategyContainer;
2224
use phpDocumentor\Reflection\Php\ValueEvaluator\ConstantEvaluator;
@@ -119,13 +121,21 @@ protected function doCreate(
119121
$file->addConstant($constant);
120122
}
121123

122-
private function determineValue(?Arg $value): ?string
124+
private function determineValue(?Arg $value): ?ValueExpression
123125
{
124126
if ($value === null) {
125127
return null;
126128
}
127129

128-
return $this->valueConverter->prettyPrintExpr($value->value);
130+
$expression = $value->value !== null ? $this->valueConverter->prettyPrintExpr($value->value) : null;
131+
if ($this->valueConverter instanceof ExpressionPrinter) {
132+
$expression = new ValueExpression($expression, $this->valueConverter->getParts());
133+
}
134+
if (is_string($expression)) {
135+
$expression = new ValueExpression($expression, []);
136+
}
137+
138+
return $expression;
129139
}
130140

131141
private function determineFqsen(Arg $name, ContextStack $context): ?Fqsen

0 commit comments

Comments
 (0)