Skip to content

Commit 014269c

Browse files
ondrejmirtesclaude
andcommitted
Put backed enum from()/tryFrom() parameter type behind bleeding edge
The backing-type parameter of BackedEnum::from() and tryFrom() is now enabled by the backedEnumFromParameterType feature toggle. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QK2T35CUpbLV52qZNgoGai
1 parent 606b86e commit 014269c

10 files changed

Lines changed: 281 additions & 168 deletions

‎conf/bleedingEdge.neon‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,6 @@ parameters:
3030
unresolvedTemplateArguments: true
3131
closureSignaturesFromUsages: true
3232
staticVariablesFromUsages: true
33+
backedEnumFromParameterType: true
3334
unusedVariable: true
3435
unusedParameters: true

‎conf/config.neon‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ parameters:
6262
unresolvedTemplateArguments: false
6363
closureSignaturesFromUsages: false
6464
staticVariablesFromUsages: false
65+
backedEnumFromParameterType: false
6566
unusedVariable: false
6667
unusedParameters: false
6768
fileExtensions:

‎conf/parametersSchema.neon‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ parametersSchema:
5959
unresolvedTemplateArguments: bool()
6060
closureSignaturesFromUsages: bool()
6161
staticVariablesFromUsages: bool()
62+
backedEnumFromParameterType: bool()
6263
unusedVariable: bool()
6364
unusedParameters: bool()
6465
])

‎src/Reflection/Php/PhpClassReflectionExtension.php‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ public function __construct(
126126
#[AutowiredParameter]
127127
private bool $inferPrivatePropertyTypeFromConstructor,
128128
private PhpVersion $phpVersion,
129+
#[AutowiredParameter(ref: '%featureToggles.backedEnumFromParameterType%')]
130+
private bool $backedEnumFromParameterType,
129131
#[AutowiredParameter(ref: '%cache.memberCacheKeysMax%')]
130132
int $memberCacheKeysMax,
131133
)
@@ -803,7 +805,7 @@ private function createMethod(
803805
$this->findMethodTrait($methodReflection),
804806
);
805807

806-
if (in_array(strtolower($methodReflection->getName()), ['from', 'tryfrom'], true)) {
808+
if ($this->backedEnumFromParameterType && in_array(strtolower($methodReflection->getName()), ['from', 'tryfrom'], true)) {
807809
$backedEnumType = $declaringClass->getBackedEnumType();
808810
if ($backedEnumType !== null) {
809811
return new BackedEnumFromMethodReflection($method, $backedEnumType);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Reflection\Php;
4+
5+
use PHPStan\Testing\TypeInferenceTestCase;
6+
use PHPUnit\Framework\Attributes\DataProvider;
7+
use PHPUnit\Framework\Attributes\RequiresPhp;
8+
use function array_merge;
9+
10+
/**
11+
* With backedEnumFromParameterType off, from() and tryFrom() of a backed
12+
* enum keep the int|string parameter reflection declares.
13+
*/
14+
#[RequiresPhp('>= 8.1.0')]
15+
class BackedEnumFromParameterTypeToggleOffTest extends TypeInferenceTestCase
16+
{
17+
18+
/** @return iterable<mixed> */
19+
public static function dataFileAsserts(): iterable
20+
{
21+
yield from self::gatherAssertTypes(__DIR__ . '/data/backed-enum-from-parameter-type-off.php');
22+
}
23+
24+
/**
25+
* @param mixed ...$args
26+
*/
27+
#[DataProvider('dataFileAsserts')]
28+
public function testFileAsserts(
29+
string $assertType,
30+
string $file,
31+
...$args,
32+
): void
33+
{
34+
$this->assertFileAsserts($assertType, $file, ...$args);
35+
}
36+
37+
public static function getAdditionalConfigFiles(): array
38+
{
39+
return array_merge(parent::getAdditionalConfigFiles(), [
40+
__DIR__ . '/../../../../conf/bleedingEdge.neon',
41+
__DIR__ . '/backed-enum-from-parameter-type-off.neon',
42+
]);
43+
}
44+
45+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
parameters:
2+
featureToggles:
3+
backedEnumFromParameterType: false
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php // lint >= 8.1
2+
3+
declare(strict_types = 1);
4+
5+
namespace BackedEnumFromParameterTypeOff;
6+
7+
use function PHPStan\Testing\assertType;
8+
9+
enum NumberEnum: int
10+
{
11+
case One = 1;
12+
}
13+
14+
enum WordEnum: string
15+
{
16+
case One = 'one';
17+
}
18+
19+
function (): void {
20+
assertType('Closure(int|string): BackedEnumFromParameterTypeOff\NumberEnum', NumberEnum::from(...));
21+
assertType('Closure(int|string): (BackedEnumFromParameterTypeOff\NumberEnum|null)', NumberEnum::tryFrom(...));
22+
assertType('Closure(int|string): BackedEnumFromParameterTypeOff\WordEnum', WordEnum::from(...));
23+
assertType('Closure(int|string): (BackedEnumFromParameterTypeOff\WordEnum|null)', WordEnum::tryFrom(...));
24+
};

‎turbo-ext/src/PhpClassReflectionExtension.cpp‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ struct ConstructorArgs
312312
zval *allowedConstantsMapProvider;
313313
bool inferPrivatePropertyTypeFromConstructor;
314314
zval *phpVersion;
315+
bool backedEnumFromParameterType;
315316
zend_long memberCacheKeysMax;
316317
};
317318

@@ -365,6 +366,7 @@ class PhpClassReflectionExtension
365366
ZVAL_COPY(OBJ_PROP_NUM(object, entry.slot), value);
366367
}
367368
ZVAL_BOOL(OBJ_PROP_NUM(object, slots::inferPrivatePropertyTypeFromConstructor), a.inferPrivatePropertyTypeFromConstructor);
369+
ZVAL_BOOL(OBJ_PROP_NUM(object, slots::backedEnumFromParameterType), a.backedEnumFromParameterType);
368370

369371
zval lru;
370372
if (UNEXPECTED(!pt_lru_cache_new(&lru, a.memberCacheKeysMax))) return false;
@@ -1576,6 +1578,8 @@ class PhpClassReflectionExtension
15761578
zv::Val method = createUserlandMethodReflection(declaringClass, declaringClass, methodReflection, methodTrait.raw());
15771579
if (UNEXPECTED(method.isUndef())) return zv::Val();
15781580

1581+
if (Z_TYPE_P(slot(slots::backedEnumFromParameterType)) != IS_TRUE) return method;
1582+
15791583
zv::Str loweredName = zv::Str::adopt(zend_string_tolower(methodNameStr.get()));
15801584
if (zend_string_equals_literal(loweredName.get(), "from") || zend_string_equals_literal(loweredName.get(), "tryfrom")) {
15811585
zv::Val backedEnumType = call(declaringClass, PT_LC("getbackedenumtype"));
@@ -2953,7 +2957,8 @@ PT_MINIT_REGISTRATION(pt_register_php_class_reflection_extension)
29532957
cls.method(sigs::__construct, [](INTERNAL_FUNCTION_PARAMETERS) {
29542958
ConstructorArgs a;
29552959
bool inferPrivatePropertyTypeFromConstructor;
2956-
ZEND_PARSE_PARAMETERS_START(18, 18)
2960+
bool backedEnumFromParameterType;
2961+
ZEND_PARSE_PARAMETERS_START(19, 19)
29572962
Z_PARAM_OBJECT(a.scopeFactory)
29582963
Z_PARAM_OBJECT(a.phpDocsResolver)
29592964
Z_PARAM_OBJECT(a.nodeScopeResolver)
@@ -2971,9 +2976,11 @@ PT_MINIT_REGISTRATION(pt_register_php_class_reflection_extension)
29712976
Z_PARAM_OBJECT(a.allowedConstantsMapProvider)
29722977
Z_PARAM_BOOL(inferPrivatePropertyTypeFromConstructor)
29732978
Z_PARAM_OBJECT(a.phpVersion)
2979+
Z_PARAM_BOOL(backedEnumFromParameterType)
29742980
Z_PARAM_LONG(a.memberCacheKeysMax)
29752981
ZEND_PARSE_PARAMETERS_END();
29762982
a.inferPrivatePropertyTypeFromConstructor = inferPrivatePropertyTypeFromConstructor;
2983+
a.backedEnumFromParameterType = backedEnumFromParameterType;
29772984
if (UNEXPECTED(!phpstanturbo::PhpClassReflectionExtension::construct(Z_OBJ_P(ZEND_THIS), a))) RETURN_THROWS();
29782985
});
29792986

0 commit comments

Comments
 (0)