Skip to content

Commit 7e5d3e4

Browse files
committed
Add support for constant visibility and globals constants with Define
1 parent 23b5112 commit 7e5d3e4

17 files changed

+587
-20
lines changed

src/phpDocumentor/Reflection/Php/Constant.php

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,24 @@ final class Constant implements Element
3535
/** @var Location */
3636
private $location;
3737

38+
/** @var Visibility */
39+
private $visibility;
40+
3841
/**
3942
* Initializes the object.
4043
*/
4144
public function __construct(
4245
Fqsen $fqsen,
4346
?DocBlock $docBlock = null,
4447
?string $value = null,
45-
?Location $location = null
48+
?Location $location = null,
49+
?Visibility $visibility = null
4650
) {
4751
$this->fqsen = $fqsen;
4852
$this->docBlock = $docBlock;
4953
$this->value = $value;
50-
51-
if ($location === null) {
52-
$location = new Location(-1);
53-
}
54-
55-
$this->location = $location;
54+
$this->location = $location ?: new Location(-1);
55+
$this->visibility = $visibility ?: new Visibility(Visibility::PUBLIC_);
5656
}
5757

5858
/**
@@ -91,4 +91,9 @@ public function getLocation() : Location
9191
{
9292
return $this->location;
9393
}
94+
95+
public function getVisibility() : Visibility
96+
{
97+
return $this->visibility;
98+
}
9499
}

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

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use phpDocumentor\Reflection\Location;
1717
use phpDocumentor\Reflection\Php\Constant as ConstantElement;
1818
use phpDocumentor\Reflection\Php\StrategyContainer;
19+
use phpDocumentor\Reflection\Php\Visibility;
1920
use phpDocumentor\Reflection\PrettyPrinter;
2021
use phpDocumentor\Reflection\Types\Context;
2122

@@ -63,6 +64,26 @@ protected function doCreate($object, StrategyContainer $strategies, ?Context $co
6364
$default = $this->valueConverter->prettyPrintExpr($object->getValue());
6465
}
6566

66-
return new ConstantElement($object->getFqsen(), $docBlock, $default, new Location($object->getLine()));
67+
return new ConstantElement(
68+
$object->getFqsen(),
69+
$docBlock,
70+
$default,
71+
new Location($object->getLine()),
72+
$this->buildVisibility($object)
73+
);
74+
}
75+
76+
/**
77+
* Converts the visibility of the constant to a valid Visibility object.
78+
*/
79+
private function buildVisibility(ClassConstantIterator $node) : Visibility
80+
{
81+
if ($node->isPrivate()) {
82+
return new Visibility(Visibility::PRIVATE_);
83+
} elseif ($node->isProtected()) {
84+
return new Visibility(Visibility::PROTECTED_);
85+
}
86+
87+
return new Visibility(Visibility::PUBLIC_);
6788
}
6889
}

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,30 @@ public function getFqsen() : Fqsen
6565
return $this->classConstants->consts[$this->index]->fqsen;
6666
}
6767

68+
/**
69+
* returns true when the current property is public.
70+
*/
71+
public function isPublic() : bool
72+
{
73+
return $this->classConstants->isPublic();
74+
}
75+
76+
/**
77+
* returns true when the current property is protected.
78+
*/
79+
public function isProtected() : bool
80+
{
81+
return $this->classConstants->isProtected();
82+
}
83+
84+
/**
85+
* returns true when the current property is private.
86+
*/
87+
public function isPrivate() : bool
88+
{
89+
return $this->classConstants->isPrivate();
90+
}
91+
6892
/**
6993
* Gets the doc comment of the node.
7094
*
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link http://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Reflection\Php\Factory;
15+
16+
use phpDocumentor\Reflection\Fqsen;
17+
use phpDocumentor\Reflection\Location;
18+
use phpDocumentor\Reflection\Php\Constant as ConstantElement;
19+
use phpDocumentor\Reflection\Php\StrategyContainer;
20+
use phpDocumentor\Reflection\PrettyPrinter;
21+
use phpDocumentor\Reflection\Types\Context;
22+
use PhpParser\Node\Arg;
23+
use PhpParser\Node\Expr\FuncCall;
24+
use PhpParser\Node\Name;
25+
use PhpParser\Node\Scalar\String_;
26+
use PhpParser\Node\Stmt\Expression;
27+
use RuntimeException;
28+
use function sprintf;
29+
30+
/**
31+
* Strategy to convert `define` expressions to ConstantElement
32+
*
33+
* @see ConstantElement
34+
* @see GlobalConstantIterator
35+
*/
36+
final class Define extends AbstractFactory
37+
{
38+
/** @var PrettyPrinter */
39+
private $valueConverter;
40+
41+
/**
42+
* Initializes the object.
43+
*/
44+
public function __construct(PrettyPrinter $prettyPrinter)
45+
{
46+
$this->valueConverter = $prettyPrinter;
47+
}
48+
49+
public function matches($object) : bool
50+
{
51+
if (!$object instanceof Expression) {
52+
return false;
53+
}
54+
55+
$expression = $object->expr;
56+
if (!$expression instanceof FuncCall) {
57+
return false;
58+
}
59+
60+
if (!$expression->name instanceof Name) {
61+
return false;
62+
}
63+
64+
if ((string) $expression->name !== 'define') {
65+
return false;
66+
}
67+
68+
return true;
69+
}
70+
71+
/**
72+
* Creates an Constant out of the given object.
73+
*
74+
* Since an object might contain other objects that need to be converted the $factory is passed so it can be
75+
* used to create nested Elements.
76+
*
77+
* @param Expression $object object to convert to an Element
78+
* @param StrategyContainer $strategies used to convert nested objects.
79+
* @param Context $context of the created object
80+
*
81+
* @return ConstantElement
82+
*/
83+
protected function doCreate($object, StrategyContainer $strategies, ?Context $context = null)
84+
{
85+
$expression = $object->expr;
86+
if (!$expression instanceof FuncCall) {
87+
throw new RuntimeException(
88+
'Provided expression is not a function call; this should not happen because the `create` method'
89+
. ' checks the given object again using `matches`'
90+
);
91+
}
92+
93+
[$name, $value] = $expression->args;
94+
95+
return new ConstantElement(
96+
$this->determineFqsen($context, $name),
97+
$this->createDocBlock($strategies, $object->getDocComment(), $context),
98+
$this->determineValue($value),
99+
new Location($object->getLine())
100+
);
101+
}
102+
103+
private function determineValue(?Arg $value) : ?string
104+
{
105+
if ($value === null) {
106+
return null;
107+
}
108+
109+
return $this->valueConverter->prettyPrintExpr($value->value);
110+
}
111+
112+
private function determineFqsen(?Context $context, Arg $name) : Fqsen
113+
{
114+
/** @var String_ $nameString */
115+
$nameString = $name->value;
116+
$namespace = $context ? $context->getNamespace() : '';
117+
118+
return new Fqsen(sprintf('\\%s\\%s', $namespace, $nameString->value));
119+
}
120+
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace phpDocumentor\Reflection\Php\Factory;
1515

16+
use OutOfBoundsException;
1617
use phpDocumentor\Reflection\DocBlock as DocBlockInstance;
1718
use phpDocumentor\Reflection\File as FileSystemFile;
1819
use phpDocumentor\Reflection\Middleware\ChainFactory;
@@ -122,6 +123,15 @@ private function createElements(
122123
) : void {
123124
foreach ($nodes as $node) {
124125
switch (get_class($node)) {
126+
case Node\Stmt\Expression::class:
127+
try {
128+
$strategy = $strategies->findMatching($node);
129+
$constant = $strategy->create($node, $strategies, $context);
130+
$file->addConstant($constant);
131+
} catch (OutOfBoundsException $exception) {
132+
// ignore, we are only interested when it is a define statement
133+
}
134+
break;
125135
case ClassNode::class:
126136
$strategy = $strategies->findMatching($node);
127137
$class = $strategy->create($node, $strategies, $context);

src/phpDocumentor/Reflection/Php/ProjectFactory.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public static function createInstance() : self
4848
[
4949
new Factory\Argument(new PrettyPrinter()),
5050
new Factory\Class_(),
51+
new Factory\Define(new PrettyPrinter()),
5152
new Factory\GlobalConstant(new PrettyPrinter()),
5253
new Factory\ClassConstant(new PrettyPrinter()),
5354
new Factory\DocBlock(DocBlockFactory::createInstance()),

tests/component/ProjectCreationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ public function testWithGlobalConstants() : void
187187
);
188188

189189
$this->assertArrayHasKey('\\Luigi\\OVEN_TEMPERATURE', $project->getFiles()[$fileName]->getConstants());
190+
$this->assertArrayHasKey('\\Luigi\\MAX_OVEN_TEMPERATURE', $project->getFiles()[$fileName]->getConstants());
190191
}
191192

192193
public function testInterfaceExtends() : void

tests/unit/phpDocumentor/Reflection/Php/ConstantTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
/**
2222
* @uses \phpDocumentor\Reflection\DocBlock
23+
* @uses \phpDocumentor\Reflection\Php\Visibility
2324
* @uses \phpDocumentor\Reflection\Fqsen
2425
*
2526
* @coversDefaultClass \phpDocumentor\Reflection\Php\Constant
@@ -77,6 +78,14 @@ public function testGetDocblock() : void
7778
$this->assertSame($this->docBlock, $this->fixture->getDocBlock());
7879
}
7980

81+
/**
82+
* @covers ::getVisibility
83+
*/
84+
public function testGetVisibility() : void
85+
{
86+
$this->assertEquals(new Visibility(Visibility::PUBLIC_), $this->fixture->getVisibility());
87+
}
88+
8089
/**
8190
* @covers ::getLocation
8291
*/

tests/unit/phpDocumentor/Reflection/Php/Factory/ClassConstantIteratorTest.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@ public function testIterateProps() : void
6060
*/
6161
public function testKey() : void
6262
{
63-
$propertyMock = m::mock(ClassConst::class);
63+
$constantMock = m::mock(ClassConst::class);
6464

65-
$fixture = new ClassConstantIterator($propertyMock);
65+
$fixture = new ClassConstantIterator($constantMock);
6666

6767
$this->assertEquals(0, $fixture->key());
6868
$fixture->next();
@@ -75,10 +75,10 @@ public function testKey() : void
7575
*/
7676
public function testProxyMethods() : void
7777
{
78-
$propertyMock = m::mock(ClassConst::class);
79-
$propertyMock->shouldReceive('getLine')->once()->andReturn(10);
78+
$constantMock = m::mock(ClassConst::class);
79+
$constantMock->shouldReceive('getLine')->once()->andReturn(10);
8080

81-
$fixture = new ClassConstantIterator($propertyMock);
81+
$fixture = new ClassConstantIterator($constantMock);
8282

8383
$this->assertEquals(10, $fixture->getLine());
8484
}

0 commit comments

Comments
 (0)