Skip to content

Commit b0dff81

Browse files
committed
Add async visibility support
The async visibility class reflects the async accessors on properties.
1 parent 1f16892 commit b0dff81

File tree

10 files changed

+245
-4
lines changed

10 files changed

+245
-4
lines changed

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
"autoload-dev": {
1313
"psr-4": {
1414
"phpDocumentor\\": [
15-
"tests/integration/",
1615
"tests/unit/phpDocumentor",
1716
"tests/bench/"
17+
],
18+
"phpDocumentor\\Reflection\\": [
19+
"tests/integration"
1820
]
1921
}
2022
},
@@ -30,6 +32,7 @@
3032
"require-dev": {
3133
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
3234
"doctrine/coding-standard": "^13.0",
35+
"eliashaeussler/phpunit-attributes": "^1.7",
3336
"mikey179/vfsstream": "~1.2",
3437
"mockery/mockery": "~1.6.0",
3538
"phpspec/prophecy-phpunit": "^2.0",

composer.lock

Lines changed: 56 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

phpunit.xml.dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" colors="true" bootstrap="vendor/autoload.php" beStrictAboutOutputDuringTests="true" beStrictAboutChangesToGlobalState="true" cacheDirectory=".phpunit.cache" requireCoverageMetadata="true" beStrictAboutCoverageMetadata="false">
3+
<extensions>
4+
<bootstrap class="EliasHaeussler\PHPUnitAttributes\PHPUnitAttributesExtension" />
5+
</extensions>
36
<coverage>
47
<report>
58
<clover outputFile="build/logs/clover.xml"/>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection\Php;
6+
7+
final class AsyncVisibility extends Visibility
8+
{
9+
public function __construct(
10+
private Visibility $readVisibility,
11+
private Visibility $writeVisibility,
12+
) {
13+
parent::__construct((string) $readVisibility);
14+
}
15+
16+
public function getReadVisibility(): Visibility
17+
{
18+
return $this->readVisibility;
19+
}
20+
21+
public function getWriteVisibility(): Visibility
22+
{
23+
return $this->writeVisibility;
24+
}
25+
}

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use phpDocumentor\Reflection\DocBlockFactoryInterface;
1717
use phpDocumentor\Reflection\Location;
18+
use phpDocumentor\Reflection\Php\AsyncVisibility;
1819
use phpDocumentor\Reflection\Php\Class_;
1920
use phpDocumentor\Reflection\Php\Factory\Reducer\Reducer;
2021
use phpDocumentor\Reflection\Php\Property as PropertyDescriptor;
@@ -107,6 +108,21 @@ protected function doCreate(
107108
* Converts the visibility of the property to a valid Visibility object.
108109
*/
109110
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
110126
{
111127
if ($node->isPrivate()) {
112128
return new Visibility(Visibility::PRIVATE_);
@@ -118,4 +134,17 @@ private function buildVisibility(PropertyIterator $node): Visibility
118134

119135
return new Visibility(Visibility::PUBLIC_);
120136
}
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+
}
121150
}

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
use PhpParser\Node\Name;
2323
use PhpParser\Node\Stmt\Property as PropertyNode;
2424

25+
use function method_exists;
26+
2527
/**
2628
* This class acts like a combination of a PropertyNode and PropertyProperty to
2729
* be able to create property descriptors using a normal strategy.
@@ -48,6 +50,20 @@ public function isPublic(): bool
4850
return $this->property->isPublic();
4951
}
5052

53+
/**
54+
* Returns async accessor value for current property.
55+
*
56+
* This method will return the same value as {@see self::isPublic()} when your phpparser version is < 5.2
57+
*/
58+
public function isPublicSet(): bool
59+
{
60+
if ($this->isAsync() === false) {
61+
return $this->isPublic();
62+
}
63+
64+
return $this->property->isPublic();
65+
}
66+
5167
/**
5268
* returns true when the current property is protected.
5369
*/
@@ -56,6 +72,20 @@ public function isProtected(): bool
5672
return $this->property->isProtected();
5773
}
5874

75+
/**
76+
* Returns async accessor value for current property.
77+
*
78+
* This method will return the same value as {@see self::isProtected()} when your phpparser version is < 5.2
79+
*/
80+
public function isProtectedSet(): bool
81+
{
82+
if ($this->isAsync() === false) {
83+
return $this->isProtected();
84+
}
85+
86+
return $this->property->isProtectedSet();
87+
}
88+
5989
/**
6090
* returns true when the current property is private.
6191
*/
@@ -64,6 +94,34 @@ public function isPrivate(): bool
6494
return $this->property->isPrivate();
6595
}
6696

97+
/**
98+
* Returns async accessor value for current property.
99+
*
100+
* This method will return the same value as {@see self::isPrivate()} when your phpparser version is < 5.2
101+
*/
102+
public function isPrivateSet(): bool
103+
{
104+
if ($this->isAsync() === false) {
105+
return $this->isPrivate();
106+
}
107+
108+
return $this->property->isPrivateSet();
109+
}
110+
111+
/**
112+
* Returns true when current property has async accessors.
113+
*
114+
* This method will always return false when your phpparser version is < 5.2
115+
*/
116+
public function isAsync(): bool
117+
{
118+
if (method_exists($this->property, 'isPrivateSet') === false) {
119+
return false;
120+
}
121+
122+
return $this->property->isPublicSet() || $this->property->isProtected() || $this->property->isPrivateSet();
123+
}
124+
67125
/**
68126
* returns true when the current property is static.
69127
*/

src/phpDocumentor/Reflection/Php/Visibility.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/**
2323
* Value object for visibility values of classes, properties, ect.
2424
*/
25-
final class Visibility implements Stringable
25+
class Visibility implements Stringable
2626
{
2727
/**
2828
* constant for protected visibility
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection;
6+
7+
use EliasHaeussler\PHPUnitAttributes\Attribute\RequiresPackage;
8+
use phpDocumentor\Reflection\File\LocalFile;
9+
use phpDocumentor\Reflection\Php\ProjectFactory;
10+
use PHPUnit\Framework\TestCase;
11+
12+
/** @coversNothing */
13+
#[RequiresPackage('nikic/php-parser', '>= 5.2')]
14+
final class AsyncAccessorTest extends TestCase
15+
{
16+
public function testAsyncAccessor(): void
17+
{
18+
$file = __DIR__ . '/data/PHP84/AsyncAccessor.php';
19+
$projectFactory = ProjectFactory::createInstance();
20+
$project = $projectFactory->create('My project', [new LocalFile($file)]);
21+
22+
$class = $project->getFiles()[$file]->getClasses()['\AsyncAccessor'];
23+
24+
self::assertEquals(
25+
'public',
26+
$class->getProperties()['\AsyncAccessor::$pizza']->getVisibility()->getReadVisibility(),
27+
);
28+
self::assertEquals(
29+
'private',
30+
$class->getProperties()['\AsyncAccessor::$pizza']->getVisibility()->getWriteVisibility(),
31+
);
32+
}
33+
34+
public function testAsyncPropertyPromotion(): void
35+
{
36+
$file = __DIR__ . '/data/PHP84/AsyncPropertyPromotion.php';
37+
$projectFactory = ProjectFactory::createInstance();
38+
$project = $projectFactory->create('My project', [new LocalFile($file)]);
39+
40+
$class = $project->getFiles()[$file]->getClasses()['\AsyncPropertyPromotion'];
41+
42+
self::assertEquals(
43+
'public',
44+
$class->getProperties()['\AsyncPropertyPromotion::$pizza']->getVisibility()->getReadVisibility(),
45+
);
46+
self::assertEquals(
47+
'protected',
48+
$class->getProperties()['\AsyncPropertyPromotion::$pizza']->getVisibility()->getWriteVisibility(),
49+
);
50+
}
51+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
class AsyncAccessor
6+
{
7+
private(set) \Pizza $pizza;
8+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
class AsyncPropertyPromotion
6+
{
7+
public function __construct(
8+
protected(set) Pizza $pizza,
9+
) {}
10+
}

0 commit comments

Comments
 (0)