-
Notifications
You must be signed in to change notification settings - Fork 467
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
Allow mixed type dynamic constants #3583
base: 2.0.x
Are you sure you want to change the base?
Conversation
'MixedTypeConstants\NoMixedTypeConstantClass::MIXED_TYPE_CONSTANT_IN_CLASS', | ||
], | ||
[ | ||
'false', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't seem right to me, but this test is copied and modified from testDynamicConstants
where it also has false
. That changed from bool
to false
in 285b49f#diff-52d2cce81a3daff27b7d6d8ba619b045420ab9a9acffc57c1c379af4eba0b127R8384.
I tried tracking down how I might be able to fix it. I found that the constant gets defined here:
phpstan-src/src/Type/Php/DefineConstantTypeSpecifyingExtension.php
Lines 45 to 60 in d73acef
$constantName = $scope->getType($node->getArgs()[0]->value); | |
if ( | |
!$constantName instanceof ConstantStringType | |
|| $constantName->getValue() === '' | |
) { | |
return new SpecifiedTypes([], []); | |
} | |
return $this->typeSpecifier->create( | |
new Node\Expr\ConstFetch( | |
new Node\Name\FullyQualified($constantName->getValue()), | |
), | |
$scope->getType($node->getArgs()[1]->value), | |
TypeSpecifierContext::createTruthy(), | |
$scope, | |
)->setAlwaysOverwriteTypes(); |
The issue is that $scope->getType($node->getArgs()[1]->value)
is just going based off the value, so it never reaches where I think it would determine that the constant is dynamic:
phpstan-src/src/Analyser/MutatingScope.php
Line 1943 in d73acef
$constantType = $this->constantResolver->resolveConstant($node->name, $this); |
It'd make sense to me to allow this syntax to force a specific constant type: parameters:
dynamicConstantNames:
- DATABASE_ENGINE
- Foo::BAR_CONSTANT
ES_ENGINE: string|null
Foo::BAZ_CONSTANT: mixed |
That makes sense. I can get it as far as detecting that it's in |
I think you can take inspiration from phpstan-src/src/Reflection/SignatureMap/SignatureMapParser.php Lines 49 to 56 in 830b6d5
|
Thanks, @staabm! That got me on the right track. I got this working. I even fixed #3583 (comment). I'm not 100% sure if the new interfaces I added are desired, but it gets the job done. Please let me know if you want me to make any changes. I could use some help with the 1 failing test though: |
src/Analyser/ConstantResolver.php
Outdated
if ($constantType->isConstantValue()->yes()) { | ||
if (array_key_exists($constantName, $this->dynamicConstantNames)) { | ||
$phpdocTypes = $this->dynamicConstantNames[$constantName]; | ||
return $this->reflectionProviderProvider->getReflectionProvider()->getSignatureMapProvider()->getTypeFromString($phpdocTypes, null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should use TypeStringResolver for this, not SignatureMapProvider.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would I get an instance of TypeStringResolver
from here?
src/Analyser/ConstantResolver.php
Outdated
|
||
if ($constantType->isConstantValue()->yes()) { | ||
$phpdocTypes = $this->dynamicConstantNames[$lookupConstantName]; | ||
return $this->getReflectionProvider()->getSignatureMapProvider()->getTypeFromString($phpdocTypes, $className); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dtto
@@ -36,4 +37,6 @@ public function getConstant(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAn | |||
|
|||
public function resolveConstantName(Node\Name $nameNode, ?NamespaceAnswerer $namespaceAnswerer): ?string; | |||
|
|||
public function getSignatureMapProvider(): SignatureMapProvider; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't add these methods please.
Addresses phpstan/phpstan#7520 Revert "Allow mixed type dynamic constants." This reverts commit 1854985. Allow setting types for dynamic constants Unit test fixes Unit test fix 2 Lint fixes Use TypeStringResolver directly Remove old code
a654da5
to
4c71fb5
Compare
Thanks, @ondrejmirtes. I made updates. I'm not sure if passing around the container is the right thing to do, but I got it working. |
@@ -41,6 +43,7 @@ public function __construct( | |||
private array $dynamicConstantNames, | |||
private ?PhpVersion $composerMinPhpVersion, | |||
private ?PhpVersion $composerMaxPhpVersion, | |||
private ?Container $container, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
make this new param the TypeStringResolver itself.
It will be injected by the DIC automatically
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I initially thought that might be the way to do it. However, when I tried it, I wound up getting an error about circular dependencies. So that's why I switched to the entire container. Do you have any ideas on how to get around the circular dependency error? It's also possible I just did it wrong. I assumed I needed to update the places that called the constructor, so I was using the container there to get the TypeStringResolver.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah I can see it now.
TypeStringResolver
has a TypeNodeResolver
contruction dependency which in turn has a ConstantResolver
dependency.
so when you add a TypeStringResolver
dependency on ConstantResolver
you build a cycle.
I will think about it and report back
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess one way we could break the cycle is to remove the ConstantResolver
construction-time dependency of TypeNodeResolver
and turn it into a setter injection (similar how we do it in TypeSpecifierFactory
for the TypeSpecifier
).
in my local testing I did not yet succeed in doing so :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or maybe better instead remove TypeNodeResolver
from TypeStringResolver
construction-time deps and move it to setter-injection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, @staabm. I don't have experience with DI in PHP, so I'm not entirely sure how to do this. I assume you meant to add a setTypeNodeResolver
function to TypeStringResolver
. I'm not sure where I would call setTypeNodeResolver
though. I thought maybe I needed to add a TypeStringResolverFactory
. I'm guessing that maybe I need to update conf/config.neon
, so I changed it to this:
-
class: PHPStan\PhpDoc\TypeStringResolver
typeStringResolverFactory:
class: PHPStan\PhpDoc\TypeStringResolverFactory
In a new TypeStringResolverFactory
class, I added this:
public function create(): TypeStringResolver
{
$typeStringResolver = new TypeStringResolver(
$this->typeLexer,
$this->typeParser
);
$typeStringResolver->setTypeNodeResolver($this->container->getByType(TypeNodeResolver::class));
return $typeStringResolver;
}
I still wound up with this when running the test though:
In Container.php line 327:
Circular reference detected for: reflectionProvider, betterReflectionProvider, 093,
058, 042, 040.
Am I on the right track? Do you have any suggestions? Thanks.
Addresses phpstan/phpstan#7520