Skip to content

Add better handling for willImplement and willExtend call to fix issues with PHPStan 2.1.3 and 2.1.4#366

Closed
alexander-schranz wants to merge 17 commits intomasterfrom
bugfix/will-implement
Closed

Add better handling for willImplement and willExtend call to fix issues with PHPStan 2.1.3 and 2.1.4#366
alexander-schranz wants to merge 17 commits intomasterfrom
bugfix/will-implement

Conversation

@alexander-schranz
Copy link
Collaborator

@alexander-schranz alexander-schranz commented Feb 10, 2025

This pull request is based on discussion: phpstan/phpstan#12556 about new coming issue #365

Follows #.
Related to phpstan/phpstan#12556
Fixes #365

I still not get it to work 100%:

Why the following works:

$secureStructure = $this->prophesize(StructureBehavior::class)
    ->willImplement(SecurityBehavior::class);

$this->secureStructure = $secureStructure;

it fails on direct assigning it:

$this->secureStructure = $this->prophesize(StructureBehavior::class)
    ->willImplement(SecurityBehavior::class);

/cc @ondrejmirtes

/cc @stof maybe you also have some ideas?

@stof
Copy link
Contributor

stof commented Feb 10, 2025

Do we even need this dynamic return type extension at all ? Prophecy defines the return type of those methods: https://github.com/phpspec/prophecy/blob/53ab8cee442a4e9f9827aaec92b023af841783c5/src/Prophecy/Prophecy/ObjectProphecy.php#L70-L72

I think the improvement released in phpstan 2.1.3 and 1.12.17 might make it work out of the box.

@alexander-schranz
Copy link
Collaborator Author

alexander-schranz commented Feb 10, 2025

@stof Thx for the fast response. Looking at the code I would have expected the same but the result looks unexpected, it returns some strange return type without the extension.

Property                                                                                                                           
         JanGregor\Prophecy\Test\StaticAnalysis\Test\ObjectProphecy\WillImplementTest::$prophecy                                            
         (Prophecy\Prophecy\ObjectProphecy<JanGregor\Prophecy\Test\StaticAnalysis\Src\Bar&JanGregor\Prophecy\Test\StaticAnalysis\Src\Foo>)  
         does not accept                                                                                                                    
         $this(JanGregor\Prophecy\Test\StaticAnalysis\Test\ObjectProphecy\WillImplementTest).                                               
         🪪  assign.propertyType 

See #367 ( same as this branch but without the extension )

@stof Maybe still related to your issue here: phpstan/phpstan#8439

@ondrejmirtes
Copy link
Contributor

@stof The @phpstan-this-out is not going to work in a fluent call. It'd have to be @return static<T&U> as well.

@stof
Copy link
Contributor

stof commented Feb 11, 2025

this looks like a bug in phpstan. It seems to resolve static<T&U> using the class of where the method is called (i.e. JanGregor\Prophecy\Test\StaticAnalysis\Test\ObjectProphecy\WillImplementTest) instead of on what the method is called (ObjectProphecy)

@alexander-schranz
Copy link
Collaborator Author

alexander-schranz commented Feb 11, 2025

@stof @ondrejmirtes I tried to put all in a small case on phpstan playground. Think this should be the current state without the extension:

https://phpstan.org/r/ccb8b2d4-fc46-4138-a4fd-a93548b52d18

Replace @return $this with @return static<T&U> I get one error away but not the others:

https://phpstan.org/r/9111aa05-f79a-4b51-8ac5-6ab554551fb2

@alexander-schranz
Copy link
Collaborator Author

alexander-schranz commented Feb 11, 2025

@ondrejmirtes @stof Best Result seems to be use self or ObjectProphecy instead of static: https://phpstan.org/r/a2dc1d8a-1a83-4b1a-949a-b23ae7c4cf28

Update using @template-covariant we would be here then: https://phpstan.org/r/057747fe-f1d4-4051-8407-d81b7127a668.

With some hacks (@template-covariant to @template) I would be here: https://phpstan.org/r/c2ea3768-3c1a-462f-9ae9-f3c64e4cf462 . With no errors but not sure about that solution?

@ondrejmirtes
Copy link
Contributor

ondrejmirtes commented Feb 11, 2025

Alright, what I figured out is that the extension WillExtendOrImplementDynamicReturnTypeExtension can be greatly simplified to:

    public function getTypeFromMethodCall(
        Reflection\MethodReflection $methodReflection,
        Node\Expr\MethodCall $methodCall,
        Analyser\Scope $scope
    ): ?Type\Type {
        $args = $methodCall->getArgs();
        if (0 === \count($args)) {
            return null;
        }

        $calledOnType = $scope->getType($methodCall->var);
        $templateObjectType = $calledOnType->getTemplateType(Prophecy\ObjectProphecy::class, 'T');

        $objects = [$templateObjectType];
        $argumentType = $scope->getType($args[0]->value);
        $objects[] = $argumentType->getClassStringObjectType();

        return new Type\Generic\GenericObjectType(
            Prophecy\ObjectProphecy::class,
            [
                Type\TypeCombinator::intersect(...$objects),
            ],
        );
    }

No need to make it more complicated than that. But the build still fails with that. I figured out that when I remove @phpstan-this-out static<T&U> from above public function willImplement($interface), the build starts passing correctly.

So the bug is in PHPStan and it's about not properly supporting static<T&U> in @phpstan-this-out. I'm going to take a look at that.

@alexander-schranz
Copy link
Collaborator Author

@ondrejmirtes thx, let me know If I can test something out.

@stof
Copy link
Contributor

stof commented Feb 11, 2025

@ondrejmirtes see phpstan/phpstan#12575 where I reported an issue when reproducing this without a type extension

@stof
Copy link
Contributor

stof commented Feb 11, 2025

Do we even need this extension at all or are the stubs (replacing the prophecy annotations to avoid using static<...> to work around the bug) enough to pass the test ?

Comment on lines +27 to +30
* This stubs should be able to be removed when the following bug fixed:
* - https://github.com/Jan0707/phpstan-prophecy/pull/366#issuecomment-2650390076
* - https://github.com/phpstan/phpstan/issues/12575
*
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stubs replace static with ObjectProphecy which seems resolve correctly but we could remove it if this issue is fixed on phpstan side.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look whether I can fix that in Prophecy itself. I used static<...> in prophecy because ObjectProphecy is not final, but I'm not sure we actually need this (I don't think there is a meaningful way to extend the class and use a child class anyway)

@alexander-schranz
Copy link
Collaborator Author

alexander-schranz commented Feb 11, 2025

@stof you are right my current stubs would be enough. Only with the extension the call $templateObjectType = $calledOnType->getTemplateType(Prophecy\ObjectProphecy::class, 'T'); fails because of the static<T&U>.

@@ -13,11 +13,6 @@
"php": "^7.4 || ^8.0",
"phpstan/phpstan": "^2.0.0"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be bumped to 2.1.3 as min version as it relies on the phpstan changes

Comment on lines +27 to +30
* This stubs should be able to be removed when the following bug fixed:
* - https://github.com/Jan0707/phpstan-prophecy/pull/366#issuecomment-2650390076
* - https://github.com/phpstan/phpstan/issues/12575
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will look whether I can fix that in Prophecy itself. I used static<...> in prophecy because ObjectProphecy is not final, but I'm not sure we actually need this (I don't think there is a meaningful way to extend the class and use a child class anyway)

@ondrejmirtes
Copy link
Contributor

See #369

@alexander-schranz
Copy link
Collaborator Author

replaced by: #369

@alexander-schranz alexander-schranz deleted the bugfix/will-implement branch February 9, 2026 17:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prophecy with interfaces leads to an error on PHPStan 2.1.3

3 participants