Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
33 changes: 32 additions & 1 deletion src/Resolver/NodeExpressionResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -534,6 +534,37 @@ protected function resolveExprClassConstFetch(Expr\ClassConstFetch $node): mixed
return $refClass->getConstant($constantName);
}

/**
* Resolves property fetch on an object, e.g. SomeEnum::CASE->value
*/
protected function resolveExprPropertyFetch(Expr\PropertyFetch $node): mixed
{
$object = $this->resolve($node->var);
if (!is_object($object)) {
throw new ReflectionException("Property fetch requires an object, got " . gettype($object));
}

if ($node->name instanceof Node\Identifier) {
$propertyName = $node->name->toString();
} else {
$resolvedName = $this->resolve($node->name);
if (!is_string($resolvedName)) {
throw new ReflectionException("Could not resolve property name for property fetch.");
}
$propertyName = $resolvedName;
}

if (!property_exists($object, $propertyName)) {
throw new ReflectionException(sprintf("Property '%s' does not exist on object of type %s", $propertyName, get_class($object)));
}

$this->isConstant = false;
$this->constantName = null;
$this->isConstExpr = true;

return $object->$propertyName;
}

/**
* @return array<int|string, mixed>
*/
Expand Down Expand Up @@ -803,7 +834,7 @@ private function fetchReflectionClass(Node\Name $node)
// PHP's ReflectionClass to determine if the class is user defined
if (class_exists($className, false)) {
$refClass = new \ReflectionClass($className);
if (!$refClass->isUserDefined()) {
if (!$refClass->isUserDefined() || $refClass->isEnum()) {
return $refClass;
}
}
Expand Down
20 changes: 20 additions & 0 deletions tests/ReflectionParameterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,26 @@ public function testParametersWithNewExpressionDefaults(): void
$this->assertInstanceOf(\stdClass::class, $defaultValue3);
}

/**
* Test that parameters with backed enum property fetch default values work correctly
*/
public function testParametersWithBackedEnumPropertyDefault(): void
{
$fileName = __DIR__ . '/Stub/FileWithClasses81.php';
$reflectionFile = new ReflectionFile($fileName);
$parsedFileNamespace = $reflectionFile->getFileNamespace('Go\ParserReflection\Stub');

$parsedClass = $parsedFileNamespace->getClass('Go\ParserReflection\Stub\ClassWithBackedEnumDefaultValue');
$parsedMethod = $parsedClass->getMethod('getRefusalDescription');
$parsedParameter = $parsedMethod->getParameters()[0];

$this->assertSame('channel', $parsedParameter->getName());
$this->assertTrue($parsedParameter->isDefaultValueAvailable());

$defaultValue = $parsedParameter->getDefaultValue();
$this->assertSame('get', $defaultValue);
}

/**
* @inheritDoc
*/
Expand Down
13 changes: 13 additions & 0 deletions tests/Resolver/NodeExpressionResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,17 @@ public function testResolveNewExpressionDateTimeImmutable(): void
$value = $expressionSolver->getValue();
$this->assertInstanceOf(\DateTimeImmutable::class, $value);
}

/**
* Testing resolving property fetch on a backed enum case (e.g. Enum::CASE->value)
*/
public function testResolvePropertyFetchOnEnumCase(): void
{
require_once __DIR__ . '/../Stub/FileWithClasses81.php';

$expressionNodeTree = $this->parser->parse("<?php \\Go\\ParserReflection\\Stub\\BackedPhp81EnumHTTPMethods::GET->value;");
$expressionSolver = new NodeExpressionResolver(NULL);
$expressionSolver->process($expressionNodeTree[0]);
$this->assertSame('get', $expressionSolver->getValue());
}
}
8 changes: 8 additions & 0 deletions tests/Stub/FileWithClasses81.php
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,11 @@ function functionWithPhp81NeverReturnType(): never
class ClassWithPhp81FinalClassConst {
final public const TEST = '1';
}

class ClassWithBackedEnumDefaultValue
{
public function getRefusalDescription(string $channel = BackedPhp81EnumHTTPMethods::GET->value): string
{
return $channel;
}
}
Loading