Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Step 6 Upgrade] Add PHP 8+ Attributes support #130

Merged
merged 53 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
ce1e4d4
[Step 6 Upgrade] Add PHP 8+ Attributes support
samsonasik Jan 24, 2024
eac85cd
add attribute in property
samsonasik Jan 24, 2024
1df960d
adding ReflectionAttribute class
samsonasik Jan 29, 2024
6763170
ref original usage
samsonasik Jan 29, 2024
6ea0eee
create AttributeResolverTrait call getAttributes() in various Reflect…
samsonasik Feb 1, 2024
b27428a
create AttributeResolverTrait call getAttributes() in various Reflect…
samsonasik Feb 1, 2024
7539a25
cs fix
samsonasik Feb 1, 2024
c26dd19
create attributes
samsonasik Feb 1, 2024
d30f29e
add getAttributes() method from Node
samsonasik Feb 1, 2024
525dd53
default value
samsonasik Feb 1, 2024
a705eac
loop not by name
samsonasik Feb 2, 2024
32c8bb6
param allow to have attribtu
samsonasik Feb 2, 2024
37d99e7
temporary ignore nitpick CI for src
samsonasik Feb 2, 2024
694753a
rework fixture
samsonasik Feb 2, 2024
6b94865
rework fixture
samsonasik Feb 2, 2024
6c895fe
fix attribute to string
samsonasik Feb 2, 2024
ff7b5ed
clean up unused use
samsonasik Feb 2, 2024
a171b37
fill Reflector
samsonasik Feb 2, 2024
209fe1e
fill reflector
samsonasik Feb 2, 2024
e74cc49
doc
samsonasik Feb 2, 2024
22afeaa
resolve args and target
samsonasik Feb 2, 2024
9add978
resolve attribtues
samsonasik Feb 2, 2024
235daf7
get from parent attributes
samsonasik Feb 2, 2024
84573c9
set is repeated
samsonasik Feb 2, 2024
0398c3f
test get attributes from params
samsonasik Feb 2, 2024
482f506
test get attributes from params
samsonasik Feb 2, 2024
8394aff
add __initialize on ReflectionClassConstant
samsonasik Feb 2, 2024
773ca7b
test for class const
samsonasik Feb 2, 2024
b967bef
test for property
samsonasik Feb 2, 2024
64034b7
add test for Class
samsonasik Feb 2, 2024
5062543
fix
samsonasik Feb 2, 2024
ac0a4f0
add test for class method
samsonasik Feb 2, 2024
e3559a8
fix start line
samsonasik Feb 2, 2024
2b5a782
add getNode() test
samsonasik Feb 2, 2024
266396d
test get constant node
samsonasik Feb 2, 2024
db4257b
failing test for ReflectionProperty
samsonasik Feb 2, 2024
ee70c54
fix PropertyProperty
samsonasik Feb 2, 2024
aab1cb1
fix PropertyProperty
samsonasik Feb 2, 2024
aa43eff
clean up
samsonasik Feb 2, 2024
2d3441b
clean up
samsonasik Feb 2, 2024
a06b0c7
solution for start line detection with attribute
samsonasik Feb 4, 2024
031c9db
add failing test case to use ComposerLocator
samsonasik Feb 4, 2024
9a2bb99
add handling by AST
samsonasik Feb 4, 2024
b567473
get line via AST
samsonasik Feb 4, 2024
987cf86
fix verify repeated attribute
samsonasik Feb 4, 2024
624a28b
typof ix
samsonasik Feb 4, 2024
8f09b87
fix
samsonasik Feb 4, 2024
9cec64c
remove __initialize() method on ReflectionParameter and ReflectionCla…
samsonasik Feb 4, 2024
eefee84
fix get node on Property
samsonasik Feb 4, 2024
799e69d
fix ReflectionParameter::getDefaultValue() return
samsonasik Feb 4, 2024
0f81b5e
resolve getNode()
samsonasik Feb 4, 2024
ece7e1f
final touch: resolve getNode()
samsonasik Feb 4, 2024
f62cbe6
final touch: ensure get attributes to correct value on ReflectionAttr…
samsonasik Feb 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nitpick.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"ignore": [
"src/*",
"tests/*",
"docs/*"
]
Expand Down
109 changes: 109 additions & 0 deletions src/ReflectionAttribute.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php

declare(strict_types=1);
/**
* Parser Reflection API
*
* @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/

namespace Go\ParserReflection;

use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use ReflectionAttribute as BaseReflectionAttribute;
use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\PropertyProperty;

/**
* ref original usage https://3v4l.org/duaQI
*/
class ReflectionAttribute extends BaseReflectionAttribute
{
public function __construct(
private string $attributeName,
private ReflectionClass|ReflectionMethod|ReflectionProperty|ReflectionClassConstant|ReflectionFunction|ReflectionParameter $reflector,
private array $arguments,
private bool $isRepeated,
) {
}

public function getNode(): Node\Attribute
{
/** @var Class_|ClassMethod|PropertyProperty|ClassConst|Function_|Param $node */
$node = $this->reflector->getNode();

// attrGroups only exists in Property Stmt
if ($node instanceof PropertyProperty) {
$node = $this->reflector->getTypeNode();
}

$nodeExpressionResolver = new NodeExpressionResolver($this);
foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
if ($attr->name->toString() !== $this->attributeName) {
continue;
}

$arguments = [];
foreach ($attr->args as $arg) {
$nodeExpressionResolver->process($arg->value);
$arguments[] = $nodeExpressionResolver->getValue();
}

if ($arguments !== $this->arguments) {
continue;
}

return $attr;
}
}

throw new ReflectionException('ReflectionAttribute should be initiated from Go\ParserReflection Reflection classes');
}

public function isRepeated(): bool
{
return $this->isRepeated;
}

/**
* {@inheritDoc}
*/
public function getArguments(): array
{
return $this->arguments;
}

/**
* {@inheritDoc}
*/
public function getName(): string
{
return $this->attributeName;
}

/**
* {@inheritDoc}
*/
public function getTarget(): int
{
throw new \RuntimeException(sprintf('cannot get target from %s', $this::class));
}

/**
* {@inheritDoc}
*/
public function newInstance(): object
{
throw new \RuntimeException(sprintf('cannot create new instance from %s', $this::class));
}
}
2 changes: 2 additions & 0 deletions src/ReflectionClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Go\ParserReflection;

use Go\ParserReflection\Traits\AttributeResolverTrait;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\Traits\ReflectionClassLikeTrait;
use PhpParser\Node\Name\FullyQualified;
Expand All @@ -27,6 +28,7 @@ class ReflectionClass extends InternalReflectionClass
{
use InternalPropertiesEmulationTrait;
use ReflectionClassLikeTrait;
use AttributeResolverTrait;

/**
* Initializes reflection instance
Expand Down
8 changes: 8 additions & 0 deletions src/ReflectionClassConstant.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@

namespace Go\ParserReflection;

use Go\ParserReflection\Traits\AttributeResolverTrait;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node;
use PhpParser\Node\Const_;
use PhpParser\Node\Stmt\ClassConst;
use PhpParser\Node\Stmt\ClassLike;
Expand All @@ -22,6 +24,7 @@
class ReflectionClassConstant extends BaseReflectionClassConstant
{
use InternalPropertiesEmulationTrait;
use AttributeResolverTrait;

/**
* Concrete class constant node
Expand Down Expand Up @@ -213,4 +216,9 @@ public function __toString(): string
(string) $value
);
}

public function getNode(): Node\Stmt\ClassConst
{
return $this->classConstantNode;
}
}
2 changes: 2 additions & 0 deletions src/ReflectionFunction.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
namespace Go\ParserReflection;

use Closure;
use Go\ParserReflection\Traits\AttributeResolverTrait;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\Traits\ReflectionFunctionLikeTrait;
use PhpParser\Node\Stmt\Function_;
Expand All @@ -24,6 +25,7 @@ class ReflectionFunction extends BaseReflectionFunction
{
use InternalPropertiesEmulationTrait;
use ReflectionFunctionLikeTrait;
use AttributeResolverTrait;

/**
* Initializes reflection instance for given AST-node
Expand Down
2 changes: 2 additions & 0 deletions src/ReflectionMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Go\ParserReflection;

use Go\ParserReflection\Traits\AttributeResolverTrait;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\Traits\ReflectionFunctionLikeTrait;
use PhpParser\Node\Stmt\ClassLike;
Expand All @@ -25,6 +26,7 @@ class ReflectionMethod extends BaseReflectionMethod
{
use InternalPropertiesEmulationTrait;
use ReflectionFunctionLikeTrait;
use AttributeResolverTrait;

/**
* Name of the class
Expand Down
4 changes: 3 additions & 1 deletion src/ReflectionParameter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Go\ParserReflection;

use Go\ParserReflection\Traits\AttributeResolverTrait;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
use PhpParser\Node\Expr;
Expand All @@ -32,6 +33,7 @@
class ReflectionParameter extends BaseReflectionParameter
{
use InternalPropertiesEmulationTrait;
use AttributeResolverTrait;

/**
* Reflection function or method
Expand Down Expand Up @@ -260,7 +262,7 @@ public function getDeclaringFunction(): ReflectionFunctionAbstract
/**
* {@inheritDoc}
*/
public function getDefaultValue()
public function getDefaultValue(): mixed
{
if (!$this->isDefaultValueAvailable()) {
throw new ReflectionException('Internal error: Failed to retrieve the default value');
Expand Down
2 changes: 2 additions & 0 deletions src/ReflectionProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Go\ParserReflection;

use Go\ParserReflection\Traits\AttributeResolverTrait;
use Go\ParserReflection\Traits\InitializationTrait;
use Go\ParserReflection\Traits\InternalPropertiesEmulationTrait;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;
Expand All @@ -27,6 +28,7 @@ class ReflectionProperty extends BaseReflectionProperty
{
use InitializationTrait;
use InternalPropertiesEmulationTrait;
use AttributeResolverTrait;

/**
* Type of property node
Expand Down
75 changes: 75 additions & 0 deletions src/Traits/AttributeResolverTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

declare(strict_types=1);
/**
* Parser Reflection API
*
* @copyright Copyright 2015, Lisachenko Alexander <[email protected]>
*
* This source file is subject to the license that is bundled
* with this source code in the file LICENSE.
*/

namespace Go\ParserReflection\Traits;

use Go\ParserReflection\ReflectionAttribute;
use Go\ParserReflection\ReflectionProperty;
use Go\ParserReflection\ValueResolver\NodeExpressionResolver;

trait AttributeResolverTrait
{
/**
* @return ReflectionAttribute[]
*/
public function getAttributes(?string $name = null, int $flags = 0): array
{
if ($this instanceof ReflectionProperty) {
$node = $this->getTypeNode();
} else {
$node = $this->getNode();
}

$node = $this->getNode();
$attributes = [];
$nodeExpressionResolver = new NodeExpressionResolver($this);

foreach ($node->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
$arguments = [];
foreach ($attr->args as $arg) {
$nodeExpressionResolver->process($arg->value);
$arguments[] = $nodeExpressionResolver->getValue();
}

if ($name === null) {
$attributes[] = new ReflectionAttribute($attr->name->toString(), $this, $arguments, $this->isAttributeRepeated($attr->name->toString(), $node->attrGroups));

continue;
}

if ($name !== $attr->name->toString()) {
continue;
}

$attributes[] = new ReflectionAttribute($name, $this, $arguments, $this->isAttributeRepeated($name, $node->attrGroups));
}
}

return $attributes;
}

private function isAttributeRepeated(string $attributeName, array $attrGroups): bool
{
$count = 0;

foreach ($attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) {
if ($attr->name->toString() === $attributeName) {
++$count;
}
}
}

return $count >= 2;
}
}
7 changes: 7 additions & 0 deletions src/Traits/ReflectionClassLikeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,13 @@ public function getShortName(): string

public function getStartLine(): int
{
if ($this->classLikeNode->attrGroups !== []) {
$attrGroups = $this->classLikeNode->attrGroups;
$lastAttrGroupsEndLine = end($attrGroups)->getAttribute('endLine');

return $lastAttrGroupsEndLine + 1;
}

return $this->classLikeNode->getAttribute('startLine');
}

Expand Down
7 changes: 7 additions & 0 deletions src/Traits/ReflectionFunctionLikeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,13 @@ public function getShortName(): string

public function getStartLine(): int
{
if ($this->functionLikeNode->attrGroups !== []) {
$attrGroups = $this->functionLikeNode->attrGroups;
$lastAttrGroupsEndLine = end($attrGroups)->getAttribute('endLine');

return $lastAttrGroupsEndLine + 1;
}

return $this->functionLikeNode->getAttribute('startLine');
}

Expand Down
9 changes: 9 additions & 0 deletions tests/Locator/ComposerLocatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use PHPUnit\Framework\TestCase;
use Go\ParserReflection\ReflectionClass;
use Go\ParserReflection\ReflectionEngine;

class ComposerLocatorTest extends TestCase
{
Expand All @@ -21,4 +22,12 @@ public function testLocateClass()
$locator->locateClass('\\' . ReflectionClass::class)
);
}

public function testLocateClassWithAttributes()
{
ReflectionEngine::init(new ComposerLocator());

$parsedClass = new \Go\ParserReflection\ReflectionClass(\Go\ParserReflection\Stub\RandomClassWithAttribute::class);
$this->assertIsArray($parsedClass->getAttributes());
}
}
Loading
Loading