Skip to content

feat: add dynamic parameter type extensions#4290

Open
calebdw wants to merge 4 commits into
phpstan:2.2.xfrom
calebdw:dynamic_parameter_extension
Open

feat: add dynamic parameter type extensions#4290
calebdw wants to merge 4 commits into
phpstan:2.2.xfrom
calebdw:dynamic_parameter_extension

Conversation

@calebdw

@calebdw calebdw commented Sep 5, 2025

Copy link
Copy Markdown
Contributor

Closes phpstan/phpstan#11707, closes phpstan/phpstan#12585

Supersedes #3828, supersedes #3131, supersedes #3823

Hello!

This adds generalized dynamic parameter type extensions and deprecates the parameter closure type extensions per phpstan/phpstan#11707 (comment).

This also fixes the return.type and the argument.type errors described in phpstan/phpstan#12585 when the parameter type is overridden via the extension.

Note

This currently only works for array and closure arguments, if support is desired for other argument types in the future then that will have to be added.

CC: @canvural, @Neol3108

Thanks!

@calebdw calebdw force-pushed the dynamic_parameter_extension branch 4 times, most recently from 49c7da8 to 58e51fb Compare September 5, 2025 14:15
@calebdw calebdw marked this pull request as draft September 5, 2025 16:35
@calebdw calebdw force-pushed the dynamic_parameter_extension branch from 58e51fb to 79de0a6 Compare September 5, 2025 17:05
@calebdw calebdw marked this pull request as ready for review September 5, 2025 17:36
@phpstan-bot

Copy link
Copy Markdown
Collaborator

This pull request has been marked as ready for review.

@canvural

canvural commented Sep 6, 2025

Copy link
Copy Markdown
Contributor

I tested it by porting my previous implementation for Larastan to this one, and looks like it works fine! Will try to implement more use cases and see if I can find any bugs. Also will try to test it on real projects.But so far so good. Thanks for this!

I'll also try to review this PR (though Ondrej would do a better job 😄 )

@ondrejmirtes ondrejmirtes left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks solid, just a few questions as a start, will do deep review later 👍

Comment thread e2e/parameter-type-extension/src/ParameterTypeExtension.php
Comment thread src/Analyser/ExpressionContext.php Outdated
Comment thread src/Analyser/ExpressionContext.php Outdated
@canvural

canvural commented Oct 2, 2025

Copy link
Copy Markdown
Contributor

@calebdw @ondrejmirtes Sorry to bother you, but I wanted to ask how we can move this forward? I'm really interested in this feature.

@calebdw

calebdw commented Oct 2, 2025

Copy link
Copy Markdown
Contributor Author

No worries, just been busy with personal life, I'll try to get to the comments soon

@calebdw calebdw force-pushed the dynamic_parameter_extension branch 2 times, most recently from b9ce6ac to b293d32 Compare November 10, 2025 15:46
@calebdw

calebdw commented Nov 10, 2025

Copy link
Copy Markdown
Contributor Author

@ondrejmirtes, @canvural, sorry it took so long to address these comments---just been busy with personal life but hoping to move this forward now 🙏

@CamKemBell

Copy link
Copy Markdown

@calebdw - is this still planned on going ahead? Does this fix the issue with using whereHas with custom builders?

I've seen multiple projects now look to using custom builders since the implementation of the attribute & official support from Laravel last year, only to find issues when trying to type the arguments in eloquent method for type hints / auto complete, etc.

https://laravel-news.com/defining-a-dedicated-query-builder-in-laravel-12-with-php-attributes

In the meantime, we have been using whereRelation, which compiles to whereHas under the hood, but it would be nice to use whereHas directly.

image

@dnyg

dnyg commented Jan 15, 2026

Copy link
Copy Markdown

My team are also eagerly awaiting the outcome of this PR! Hope it can be pushed forward 🙏

@calebdw calebdw force-pushed the dynamic_parameter_extension branch 2 times, most recently from 08bc956 to 7823716 Compare January 19, 2026 15:17
@calebdw

calebdw commented Jan 19, 2026

Copy link
Copy Markdown
Contributor Author

@CamKemBell, yes I just rebased and resolved all the conflicts

@ondrejmirtes, this is ready to review 👍

@dnyg

dnyg commented Feb 12, 2026

Copy link
Copy Markdown

What is the status on this PR?

@VincentLanglet

Copy link
Copy Markdown
Contributor

Hi @calebdw sorry for the delay there is now more review for the PHPStan codebase.

Are you still interested by the PR ?
There is a lot of conflict, can you solve them please ?

Thanks !

@calebdw calebdw force-pushed the dynamic_parameter_extension branch from 9e68ce4 to c67e140 Compare February 16, 2026 14:43
@calebdw calebdw force-pushed the dynamic_parameter_extension branch from 781f1dc to f59eab2 Compare February 18, 2026 14:37
@calebdw

calebdw commented Feb 18, 2026

Copy link
Copy Markdown
Contributor Author

@VincentLanglet,

  • I've fixed the PHP 7.4 tests
  • most of the integration tests are failing due to the deprecation warning from the old interfaces---not sure there's any way to fix these besides the respective projects updating their baselines after this has been released
  • the Level test failures appear unrelated
  • I'm not too sure how to prevent the escape mutants, some of these would already be skipped (e.g., if an array is not a constant array) so having a test case for every mutant is tricky

@calebdw calebdw force-pushed the dynamic_parameter_extension branch from f59eab2 to 46c462d Compare February 23, 2026 15:43

@VincentLanglet VincentLanglet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Comment thread phpstan-baseline.neon Outdated
@calebdw calebdw force-pushed the dynamic_parameter_extension branch 4 times, most recently from 64bf789 to 0a17a77 Compare May 20, 2026 03:46
@ondrejmirtes

Copy link
Copy Markdown
Member

What is this one waiting for? 😊

@VincentLanglet

Copy link
Copy Markdown
Contributor

I think it's mainly conflict and targeting 2.2.x

@ondrejmirtes

Copy link
Copy Markdown
Member

@VincentLanglet @staabm Once you give a green light and you're happy with this PR, I will take over, take a look and bring it over the finish line 😊

use PHPStan\Reflection\ParameterReflection;

/**
* This is the interface for dynamic parameter type extensions for functions.

@staabm staabm Jun 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I am looking at this for the first time, and my initial question is:

where to draw the line between FunctionTypeSpecifyingExtension, FunctionParameterOutTypeExtension, DynamicFunctionParameterTypeExtension? when to use which?
(same for Method and StaticMethod ofc)

all of the above can specify/narrow/change types of funtion calls (or function call parameters).

I feel extension implementators will have this question and after we properly discussed the differences, it might make sense to document these

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is more of a documentation comment right? Not blocking this PR.

@staabm staabm Jun 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

its not blocking, but I am interessted in the anwser. it would help me to get a better understanding for the review

@canvural canvural Jun 12, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

FunctionTypeSpecifyingExtension is for narrowing the types after a function call. We do not influence the type itself inside that function. And here we have access to the context so we know if we are in a if statement.

FunctionParameterOutTypeExtension I always thought for parameters that are passed by reference, but documentation gives an example without reference 😄 So I guess, it works for normal parameters too. But again we specify how the passed variables type changes after the function call.

DynamicFunctionParameterTypeExtension is when you want the variable type changed for that function body. So it'll be analyzed by PHPStan with the changed type. So compared to others this affects the types for the function body itself. Not after the call. And currently it only works for arrays and closures.

These are my understandings. Hope it's clear and I'm not wrong 😄

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is everything clear here? 👀

Sorry, if I come up as very pushy but I really want this to move forward. Especially when Ondrej said he can take it over the finish line after approval from other maintainers.

@staabm staabm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it looks mostly good, a few questions

  • if I did not miss it, it seems none of the tests is covering a error case?
  • $overriddenType is passed thru a lot of handlers. I think we should have a few more tests which show that the overridden type gets correctly passed thru complex expressions
  • I wonder if we really need end-to-end test-cases only or whether we could at least have some in-phpunit suite tests (these are easier to run, debug and maintain)
  • you should look into issue-bot results and judge whether these are expected/related (some of them might go-away after rebase)
  • we need this PR to target 2.2.x

Comment on lines +99 to +100
} elseif ($keyType instanceof ConstantIntegerType) {
$nextAutoIndex = $keyType->getValue() + 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

could keyType be a integer range here?
or e.g. null|4

Comment thread e2e/bug-12585/src/test.php
@canvural

Copy link
Copy Markdown
Contributor

@calebdw Would you have time to address the comments? Or if you allow, I can open another PR with your code and fix there.

@calebdw

calebdw commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Yes, sorry been busy---I'll get to this shortly

@calebdw calebdw force-pushed the dynamic_parameter_extension branch from 0a17a77 to 79dd1ad Compare June 23, 2026 02:59
path: phpstan-dist
token: ${{ secrets.PHPSTAN_BOT_TOKEN }}
ref: 2.1.x
ref: 2.2.x
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: 2.1.x
ref: 2.2.x

@github-advanced-security github-advanced-security AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

zizmor found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.


- name: "Install bashunit"
run: "curl -s https://bashunit.typeddevs.com/install.sh | bash -s e2e/ 0.37.0"
uses: "TypedDevs/bashunit@ffa9c79e71ecbb9990e777348bc9ba12314b62d0" # 0.39.1

- name: "Install bashunit"
run: "curl -s https://bashunit.typeddevs.com/install.sh | bash -s e2e/ 0.37.0"
uses: "TypedDevs/bashunit@ffa9c79e71ecbb9990e777348bc9ba12314b62d0" # 0.39.1
@calebdw calebdw force-pushed the dynamic_parameter_extension branch from 79dd1ad to edb86a0 Compare June 23, 2026 03:03
@calebdw calebdw changed the base branch from 2.1.x to 2.2.x June 23, 2026 03:05
@calebdw calebdw force-pushed the dynamic_parameter_extension branch from edb86a0 to 3e2e16d Compare June 23, 2026 03:16
@calebdw calebdw force-pushed the dynamic_parameter_extension branch from 3e2e16d to 9b99f72 Compare June 23, 2026 03:23
@canvural

Copy link
Copy Markdown
Contributor

Tested the latest PHAR, still works alright 👍🏽 Static analysis fails on CI looks legit though. But didn't happen at runtime 🤔 Maybe didn't hit that code path.

@@ -1,3 +1,4 @@
---

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

typo

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.

Invalid argument types when using MethodParameterClosureTypeExtension Allow specifying that parameters should be covariant

9 participants