<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
	<title>PHPStan Blog</title>
	<subtitle>Find Bugs In Your Code Without Writing Tests!</subtitle>
	<link href="https://phpstan.org/rss.xml" rel="self"/>
	<link href="https://phpstan.org/"/>
	<updated>2026-10-06T00:00:00Z</updated>
	<id>https://phpstan.org/</id>
	<author>
		<name>Ondřej Mirtes</name>
		<email>ondrej@mirtes.cz</email>
	</author>
	
		
		<entry>
			<title>PHPStan 2.3: Leap in Performance, Generics Improvements, Detecting Unused Variables, and More!</title>
			<link href="https://phpstan.org/blog/phpstan-2-3-leap-in-performance"/>
			<updated>2026-10-06T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-2-3-leap-in-performance</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;It’s been over four months and &lt;a href=&quot;https://github.com/phpstan/phpstan/releases&quot;&gt;16 patch releases&lt;/a&gt; since &lt;a href=&quot;/blog/phpstan-2-2-unsealed-array-shapes-safer-array-keys&quot;&gt;the last minor PHPStan release&lt;/a&gt;. Which means it’s about time for the next one! &lt;a href=&quot;https://github.com/phpstan/phpstan/releases/tag/2.3.0&quot;&gt;PHPStan 2.3.0&lt;/a&gt; is here.&lt;/p&gt;
&lt;h2 id=&quot;performance-improvements&quot; tabindex=&quot;-1&quot;&gt;Performance improvements &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#performance-improvements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since the beginning of this year we’ve been hard at work improving PHPStan’s performance and we’ve been very successful at it. Nothing shows it better than the following chart. On my computer (a 5-year-old MacBook Pro with an M1 Pro CPU), in December 2025 it used to take 60 seconds to analyse WordPress source code. And today it only takes 8 seconds:&lt;/p&gt;
&lt;div class=&quot;line-chart&quot;&gt;
&lt;script type=&quot;application/json&quot;&gt;
{
	&quot;x&quot;: &quot;Version&quot;,
	&quot;y&quot;: {&quot;suffix&quot;: &quot; s&quot;, &quot;decimals&quot;: 1},
	&quot;tooltip&quot;: {&quot;title&quot;: &quot;PHPStan {Version}&quot;, &quot;subtitle&quot;: &quot;{Released}&quot;, &quot;description&quot;: &quot;{Note}&quot;},
	&quot;series&quot;: [
		{&quot;id&quot;: &quot;off&quot;, &quot;column&quot;: &quot;Turbo off (s)&quot;, &quot;label&quot;: &quot;Turbo off&quot;, &quot;color&quot;: &quot;text-blue-600&quot;, &quot;labels&quot;: [&quot;first&quot;, &quot;last&quot;]},
		{&quot;id&quot;: &quot;on&quot;, &quot;column&quot;: &quot;Turbo on (s)&quot;, &quot;label&quot;: &quot;Turbo on&quot;, &quot;color&quot;: &quot;text-amber-500&quot;}
	],
	&quot;comparisons&quot;: [{&quot;from&quot;: &quot;off&quot;, &quot;to&quot;: &quot;on&quot;, &quot;text&quot;: &quot;Turbo is {percent}% faster&quot;}],
	&quot;description&quot;: &quot;Time to analyse WordPress core across PHPStan releases, with and without Turbo.&quot;
}
&lt;/script&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Released&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Turbo off (s)&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Turbo on (s)&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;2.1.33&lt;/td&gt;
&lt;td&gt;December 5th 2025&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;60.51&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;&lt;/td&gt;
&lt;td&gt;Last release before this year’s performance improvements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.1.34&lt;/td&gt;
&lt;td&gt;January 19th 2026&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;46.51&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;&lt;/td&gt;
&lt;td&gt;Caching reflection objects and PHPDocs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.1.38&lt;/td&gt;
&lt;td&gt;January 30th 2026&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;44.55&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;&lt;/td&gt;
&lt;td&gt;Minor performance improvements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.1.49&lt;/td&gt;
&lt;td&gt;April 16th 2026&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;34.49&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;&lt;/td&gt;
&lt;td&gt;More performance improvements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.2.6&lt;/td&gt;
&lt;td&gt;July 26th 2026&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;32.02&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;24.68&lt;/td&gt;
&lt;td&gt;First release with Turbo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.2.10&lt;/td&gt;
&lt;td&gt;August 30th 2026&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;29.94&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;22.20&lt;/td&gt;
&lt;td&gt;Forking workers instead of spawning&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.2.13&lt;/td&gt;
&lt;td&gt;September 3rd 2026&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;23.06&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;14.12&lt;/td&gt;
&lt;td&gt;Always running with OPcache, stripping runtime checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2.3.0&lt;/td&gt;
&lt;td&gt;October 1st 2026&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;19.47&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;7.93&lt;/td&gt;
&lt;td&gt;A lot more classes shadowed by Turbo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;The first data point is PHPStan 2.1.33 which is the last release before all the contemporary performance work took place. Since then we have been chopping away at work that PHPStan doesn’t have to do at all, doesn’t have to do repeatedly, or that can be done faster. You can see it in the blue line getting steadily lower.&lt;/p&gt;
&lt;p&gt;But PHPStan 2.2.6 at the end of July brought something new into the mix: PHPStan Turbo, an optional extension that makes parts of PHPStan faster by reimplementing them in C++. It also enables other optimization techniques: forking child processes instead of spawning them, which normally isn’t possible when running from a PHAR file, and registering an OPcache optimizer pass that strips runtime type checks from PHPStan’s calls to itself. It deserves a separate article which I will publish later this week.&lt;/p&gt;
&lt;p&gt;Optional means PHPStan still runs fine without the Turbo extension. The twist is that the majority of PHPStan users have it enabled automatically and thus are already running PHPStan at the highest speed, without having to manually manage the extension on their system. The extension’s .so/.dll files are bundled inside the &lt;code&gt;phpstan/phpstan&lt;/code&gt; Composer package, PHPStan selects the correct one for the current PHP runtime, and restarts itself with the extension enabled through &lt;code&gt;php -d&lt;/code&gt; command line options.&lt;/p&gt;
&lt;p&gt;You can see the effect in the orange line: the initial release made PHPStan 23% faster against the blue baseline. But today Turbo makes PHPStan almost 60% faster.&lt;/p&gt;
&lt;p&gt;To summarize: PHPStan today is up to 7.5× faster than PHPStan as of ten months ago!&lt;/p&gt;
&lt;h2 id=&quot;result-cache-improvements&quot; tabindex=&quot;-1&quot;&gt;Result cache improvements &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#result-cache-improvements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;But that’s not even the whole story. That’s only about the “cold” runs when the &lt;a href=&quot;/user-guide/result-cache&quot;&gt;result cache&lt;/a&gt; does not exist or is too old. PHPStan doesn’t always run analysis on all files in a project. Since &lt;a href=&quot;/blog/from-minutes-to-seconds-massive-performance-gains-in-phpstan&quot;&gt;March 2020&lt;/a&gt; &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; it remembers what errors it reported in which files during previous runs. It checks which files have changed since then, and only reanalyses those, plus files dependent on the changed files.&lt;/p&gt;
&lt;p&gt;The day-to-day experience of people running PHPStan isn’t waiting for the full run to finish every time. It depends on how many files were changed since the last run. The following chart shows runs with a warm result cache on Drupal, a much larger project (a full run takes 22 seconds, compared to 8 seconds for WordPress):&lt;/p&gt;
&lt;div class=&quot;line-chart&quot;&gt;
&lt;script type=&quot;application/json&quot;&gt;
{
	&quot;x&quot;: &quot;Files&quot;,
	&quot;xDetail&quot;: &quot;Detail&quot;,
	&quot;y&quot;: {&quot;suffix&quot;: &quot; s&quot;, &quot;decimals&quot;: 1},
	&quot;tooltip&quot;: {&quot;title&quot;: &quot;{Files} files reanalysed&quot;, &quot;subtitle&quot;: &quot;{Share} of 11,194 files&quot;, &quot;description&quot;: &quot;{Note}&quot;},
	&quot;series&quot;: [
		{&quot;column&quot;: &quot;Time (s)&quot;, &quot;label&quot;: &quot;Time&quot;, &quot;color&quot;: &quot;text-amber-500&quot;, &quot;labels&quot;: [&quot;first&quot;, &quot;last&quot;]}
	],
	&quot;description&quot;: &quot;Time to analyse Drupal core with a warm result cache, depending on how many files have to be reanalysed.&quot;,
	&quot;height&quot;: {&quot;ratio&quot;: 0.42, &quot;min&quot;: 280, &quot;max&quot;: 300}
}
&lt;/script&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Files&lt;/th&gt;
&lt;th&gt;Detail&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Share&lt;/th&gt;
&lt;th style=&quot;text-align:right&quot;&gt;Time (s)&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;0&lt;/td&gt;
&lt;td&gt;no change&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;0%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;1.58&lt;/td&gt;
&lt;td&gt;Nothing changed since the last run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;10&lt;/td&gt;
&lt;td&gt;0.1%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;0.1%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;2.84&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;1,124&lt;/td&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;10.0%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;6.21&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;2,771&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;24.8%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;9.89&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;5,574&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;49.8%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;16.10&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;7,204&lt;/td&gt;
&lt;td&gt;64%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;64.4%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;19.03&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;8,956&lt;/td&gt;
&lt;td&gt;80%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;80.0%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;21.14&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:right&quot;&gt;11,194&lt;/td&gt;
&lt;td&gt;cache cleared&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;100%&lt;/td&gt;
&lt;td style=&quot;text-align:right&quot;&gt;22.08&lt;/td&gt;
&lt;td&gt;Result cache cleared&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Re-running PHPStan without changing anything takes just 1.6 seconds to print the results. When PHPStan decides 10 files need to be reanalysed, it takes 2.8 seconds. When 10% of the project is reanalysed, it takes 6.2 seconds.&lt;/p&gt;
&lt;p&gt;The recent result cache improvements are about making sure the whole result cache is invalidated and thrown away only when PHPStan is updated, or when the project’s PHPStan configuration changes. Previously, the project was fully reanalysed when any Composer dependency was updated, or when the Symfony DI container changed in any way. We’ve started tracking the dependencies between analysed files and installed packages so only the precise minimum is reanalysed.&lt;/p&gt;
&lt;p&gt;We’re also introducing new &lt;a href=&quot;/developing-extensions/result-cache-meta-extensions#tracking-dependencies-on-files&quot;&gt;extension interfaces and APIs&lt;/a&gt; that custom extensions can take advantage of to describe which files need to be invalidated and when, instead of throwing the entire result cache away when any minor detail changes.&lt;/p&gt;
&lt;p&gt;The result cache now also behaves properly in monorepo setups that take advantage of &lt;code&gt;&amp;quot;type&amp;quot;: &amp;quot;path&amp;quot;&lt;/code&gt; &lt;a href=&quot;https://getcomposer.org/doc/04-schema.md#repositories&quot;&gt;repositories&lt;/a&gt; in &lt;code&gt;composer.json&lt;/code&gt;, or &lt;a href=&quot;/user-guide/discovering-symbols#third-party-code-outside-of-composer-dependencies&quot;&gt;scanDirectories &amp;amp; scanFiles&lt;/a&gt; of often-changed files.&lt;/p&gt;
&lt;p&gt;Result cache is made to be saved and restored in CI for vastly faster pipelines. Make sure to &lt;a href=&quot;/user-guide/result-cache#setup-in-continuous-integration&quot;&gt;configure that&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&quot;generics%3A-no-more-scalar-generalization%2Cmulti-pass-inference-for-new-foo()-from-usages&quot; tabindex=&quot;-1&quot;&gt;Generics: no more scalar generalization,&lt;br&gt;multi-pass inference for &lt;code&gt;new Foo()&lt;/code&gt; from usages &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#generics%3A-no-more-scalar-generalization%2Cmulti-pass-inference-for-new-foo()-from-usages&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHPStan has had generics support &lt;a href=&quot;/blog/phpstan-0-12-released&quot;&gt;for almost seven years&lt;/a&gt;. We had to make a bunch of tough choices during development, and a few times we had to guess “how this should work” purely on gut feeling. Spoiler alert: some of those choices were wrong!&lt;/p&gt;
&lt;p&gt;For instance, take the following function that returns the same type it accepts in its parameter:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * @template T
 * @param T $a
 * @return T
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What return type should be inferred when calling &lt;code&gt;doFoo(1)&lt;/code&gt;? Is it &lt;code&gt;int&lt;/code&gt;? Is it &lt;code&gt;1&lt;/code&gt;? There’s no correct answer; each comes with different trade-offs.&lt;/p&gt;
&lt;p&gt;We opted for generalizing passed scalars so &lt;code&gt;1&lt;/code&gt; becomes &lt;code&gt;int&lt;/code&gt;. In hindsight, it was the wrong choice, and users reported many, many issues over the years because they wanted to preserve the precise scalar type.&lt;/p&gt;
&lt;p&gt;But we couldn’t fix it without breaking one important use case.&lt;/p&gt;
&lt;p&gt;Here’s another question for you: What’s the type of &lt;code&gt;$c = new Collection([1, 2, 3])&lt;/code&gt;? If it’s &lt;code&gt;Collection&amp;lt;1|2|3&amp;gt;&lt;/code&gt;, you can’t call &lt;code&gt;$c-&amp;gt;add(4)&lt;/code&gt; because it wouldn’t be accepted. If it’s &lt;code&gt;Collection&amp;lt;int&amp;gt;&lt;/code&gt;, you can’t pass it to a parameter expecting &lt;code&gt;Collection&amp;lt;positive-int&amp;gt;&lt;/code&gt;, even though that should be valid.&lt;/p&gt;
&lt;p&gt;If we take an empty collection &lt;code&gt;new Collection([])&lt;/code&gt;, the resolved type of &lt;code&gt;@template T&lt;/code&gt; is the &lt;code&gt;never&lt;/code&gt; type (the empty &lt;a href=&quot;https://en.wikipedia.org/wiki/Bottom_type&quot;&gt;bottom type&lt;/a&gt;). Strictly speaking, you shouldn’t be able to add anything to it, because it’s supposed to be empty! Correct, but not really useful in the real world.&lt;/p&gt;
&lt;p&gt;Yeah, tough choices.&lt;/p&gt;
&lt;p&gt;Fortunately, Arnaud Le Blanc, who helped me tremendously with over 50 pull requests for the initial generics implementation back in 2019, &lt;a href=&quot;https://github.com/phpstan/phpstan/issues/6732#issuecomment-1062029088&quot;&gt;came to the rescue again on March 8th 2022&lt;/a&gt;. He described a solution to this problem in an issue titled “Bidirectional type narrowing” so that’s what we’ve been calling the idea ever since.&lt;/p&gt;
&lt;p&gt;It took me over four years to gather up courage and funding to implement it correctly and efficiently.&lt;/p&gt;
&lt;p&gt;The solution is to analyse the code around &lt;code&gt;new Collection(...)&lt;/code&gt; multiple times, observe how it’s used, where it’s passed to, and clamp the final inferred &lt;code&gt;@template T&lt;/code&gt; type based on that information.&lt;/p&gt;
&lt;p&gt;You can play with this interactive widget to understand what PHPStan does with different code snippets:&lt;/p&gt;
&lt;div class=&quot;type-inference-demo&quot; data-hint=&quot;Switch the lines on and off: PHPStan infers the type of the new object from how it is used further down.&quot;&gt;
&lt;script type=&quot;application/json&quot;&gt;
[{&quot;id&quot;: &quot;first-usage&quot;, &quot;label&quot;: &quot;Passed to a function&quot;, &quot;toggles&quot;: [10, 11], &quot;defaults&quot;: [true, false], &quot;probes&quot;: [{&quot;line&quot;: 9, &quot;variable&quot;: &quot;ints&quot;, &quot;showName&quot;: false}], &quot;results&quot;: [{&quot;types&quot;: [&quot;Collection&lt;*NEVER*&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;string&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&gt;&quot;], &quot;errors&quot;: [{&quot;line&quot;: 11, &quot;message&quot;: &quot;Parameter #1 $strings of function takeStrings expects Collection&lt;string&gt;, Collection&lt;int&gt; given.&quot;, &quot;identifier&quot;: &quot;argument.type&quot;}]}]}, {&quot;id&quot;: &quot;precise-scalars&quot;, &quot;label&quot;: &quot;Precise scalars&quot;, &quot;toggles&quot;: [7, 8, 9], &quot;defaults&quot;: [true, false, false], &quot;probes&quot;: [{&quot;line&quot;: 6, &quot;variable&quot;: &quot;ids&quot;, &quot;showName&quot;: false}], &quot;results&quot;: [{&quot;types&quot;: [&quot;Collection&lt;1|2|3&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;1|2|3|4&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;-1|1|2|3&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;-1|1|2|3|4&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&lt;1, max&gt;&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&lt;1, max&gt;&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&lt;1, max&gt;&gt;&quot;], &quot;errors&quot;: [{&quot;line&quot;: 8, &quot;message&quot;: &quot;Parameter #1 $item of method Collection&lt;int&lt;1, max&gt;&gt;::add() expects int&lt;1, max&gt;, -1 given.&quot;, &quot;identifier&quot;: &quot;argument.type&quot;}]}, {&quot;types&quot;: [&quot;Collection&lt;int&lt;1, max&gt;&gt;&quot;], &quot;errors&quot;: [{&quot;line&quot;: 8, &quot;message&quot;: &quot;Parameter #1 $item of method Collection&lt;int&lt;1, max&gt;&gt;::add() expects int&lt;1, max&gt;, -1 given.&quot;, &quot;identifier&quot;: &quot;argument.type&quot;}]}]}, {&quot;id&quot;: &quot;method-calls&quot;, &quot;label&quot;: &quot;Method calls&quot;, &quot;toggles&quot;: [7, 8, 9], &quot;defaults&quot;: [true, true, false], &quot;probes&quot;: [{&quot;line&quot;: 6, &quot;variable&quot;: &quot;items&quot;, &quot;showName&quot;: false}], &quot;results&quot;: [{&quot;types&quot;: [&quot;Collection&lt;*NEVER*&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;1&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;&#39;one&#39;&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;1|&#39;one&#39;&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Collection&lt;int&gt;&quot;], &quot;errors&quot;: [{&quot;line&quot;: 8, &quot;message&quot;: &quot;Parameter #1 $item of method Collection&lt;int&gt;::add() expects int, string given.&quot;, &quot;identifier&quot;: &quot;argument.type&quot;}]}, {&quot;types&quot;: [&quot;Collection&lt;int&gt;&quot;], &quot;errors&quot;: [{&quot;line&quot;: 8, &quot;message&quot;: &quot;Parameter #1 $item of method Collection&lt;int&gt;::add() expects int, string given.&quot;, &quot;identifier&quot;: &quot;argument.type&quot;}]}]}, {&quot;id&quot;: &quot;joint-inference&quot;, &quot;label&quot;: &quot;Inferred together&quot;, &quot;toggles&quot;: [12], &quot;defaults&quot;: [false], &quot;probes&quot;: [{&quot;line&quot;: 10, &quot;variable&quot;: &quot;a&quot;, &quot;showName&quot;: false}, {&quot;line&quot;: 11, &quot;variable&quot;: &quot;b&quot;, &quot;showName&quot;: false}], &quot;results&quot;: [{&quot;types&quot;: [&quot;Box&lt;1&gt;&quot;, &quot;Box&lt;&#39;one&#39;&gt;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Box&lt;1|&#39;one&#39;&gt;&quot;, &quot;Box&lt;1|&#39;one&#39;&gt;&quot;], &quot;errors&quot;: []}]}]
&lt;/script&gt;
&lt;div data-scenario=&quot;first-usage&quot;&gt;
&lt;p&gt;&lt;strong&gt;Passed to a function&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @param Collection&amp;lt;int&gt; $ints */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;takeInts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Collection&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** @param Collection&amp;lt;string&gt; $strings */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;takeStrings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Collection&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$ints&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;takeInts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;takeStrings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$ints&lt;/code&gt; is &lt;code&gt;Collection&amp;lt;int&amp;gt;&lt;/code&gt; on line 9.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Line 11: Parameter #1 $strings of function takeStrings expects Collection&amp;lt;string&amp;gt;, Collection&amp;lt;int&amp;gt; given.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-scenario=&quot;precise-scalars&quot;&gt;
&lt;p&gt;&lt;strong&gt;Precise scalars&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @param Collection&amp;lt;positive-int&gt; $ids */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;saveIds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Collection&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ids&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$ids&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$ids&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$ids&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;saveIds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$ids&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$ids&lt;/code&gt; is &lt;code&gt;Collection&amp;lt;int&amp;lt;1, max&amp;gt;&amp;gt;&lt;/code&gt; on line 6.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Line 8: Parameter #1 $item of method Collection&amp;lt;int&amp;lt;1, max&amp;gt;&amp;gt;::add() expects int&amp;lt;1, max&amp;gt;, -1 given.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-scenario=&quot;method-calls&quot;&gt;
&lt;p&gt;&lt;strong&gt;Method calls&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @param Collection&amp;lt;int&gt; $ints */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;takeInts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Collection&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;one&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;takeInts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$items&lt;/code&gt; is &lt;code&gt;Collection&amp;lt;int&amp;gt;&lt;/code&gt; on line 6.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Line 8: Parameter #1 $item of method Collection&amp;lt;int&amp;gt;::add() expects int, string given.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-scenario=&quot;joint-inference&quot;&gt;
&lt;p&gt;&lt;strong&gt;Inferred together&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * @template T
 * @param Box&amp;lt;T&gt; $a
 * @param Box&amp;lt;T&gt; $b
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;swap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Box&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Box&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;one&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;swap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$a&lt;/code&gt; is &lt;code&gt;Box&amp;lt;1|&#39;one&#39;&amp;gt;&lt;/code&gt; on line 10, &lt;code&gt;$b&lt;/code&gt; is &lt;code&gt;Box&amp;lt;1|&#39;one&#39;&amp;gt;&lt;/code&gt; on line 11.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The first naive prototype made PHPStan around 30% slower. Going back to the drawing board made me realize PHPStan doesn’t have to reanalyse everything, only the lines directly impacted by the generic objects with unresolved template types. The performance impact of this feature is between 1 and 3%, depending on how heavily the analysed code uses generics, which is easily offset by the performance improvements made in the same release.&lt;/p&gt;
&lt;p&gt;Thanks to this work, PHPStan no longer needs to generalize scalar types in the context of generics. The implementation closed 17 related issues, plus countless duplicates reported over the years.&lt;/p&gt;
&lt;p&gt;Make sure to enable &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;bleeding edge&lt;/a&gt; to take advantage of these improvements.&lt;/p&gt;
&lt;p&gt;Bidirectional type narrowing and related work &lt;strong&gt;have been commissioned and made possible by the &lt;a href=&quot;https://www.sovereign.tech/&quot;&gt;Sovereign Tech Fund&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;detecting-unused-variables&quot; tabindex=&quot;-1&quot;&gt;Detecting unused variables &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#detecting-unused-variables&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Unused variables are a class of errors that has escaped PHPStan’s attention. Until now. They can be pretty severe: a value that has been assigned but not used later means someone made a typo or forgot to send the value somewhere. Like computing a TTL but never passing it to the &lt;code&gt;Cache::save()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;PHPStan distinguishes five scenarios of unused variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Variable is assigned but never read: &lt;a href=&quot;/error-identifiers/variable.unused&quot;&gt;&lt;code&gt;variable.unused&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The assigned value is never read before the end of the function body (the variable may still be read earlier, above the assignment): &lt;a href=&quot;/error-identifiers/assign.unused&quot;&gt;&lt;code&gt;assign.unused&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The assigned value is overwritten by another assignment before being read: &lt;a href=&quot;/error-identifiers/assign.overwritten&quot;&gt;&lt;code&gt;assign.overwritten&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Variable is assigned the value it already has: &lt;a href=&quot;/error-identifiers/assign.redundant&quot;&gt;&lt;code&gt;assign.redundant&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Variable is assigned and seemingly used, but the value doesn’t end up anywhere: &lt;a href=&quot;/error-identifiers/assign.unusedFlow&quot;&gt;&lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/a&gt;. Inspired by &lt;a href=&quot;https://psalm.dev/articles/better-unused-variable-detection&quot;&gt;Psalm’s data-flow graph&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This list isn’t exhaustive. Besides plain assignments like &lt;code&gt;$foo = ...&lt;/code&gt;, PHPStan also checks array keys that are never read or immediately overwritten, results of &lt;code&gt;++&lt;/code&gt; and &lt;code&gt;--&lt;/code&gt;, foreach keys and values, catch variables, closure &lt;code&gt;use&lt;/code&gt; variables, and parameters of functions and private methods.&lt;/p&gt;
&lt;p&gt;Explore all the different errors PHPStan can now report in this interactive widget:&lt;/p&gt;
&lt;div class=&quot;identifier-explorer&quot;&gt;
&lt;div data-identifier=&quot;variable.unused&quot;&gt;
&lt;p&gt;&lt;code&gt;variable.unused&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sendWelcomeEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Mailer&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$subject&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome to Acme!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Glad to have you.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Variable $subject is never read. &lt;code&gt;variable.unused&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;assign.unused&quot;&gt;
&lt;p&gt;&lt;code&gt;assign.unused&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cacheReport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Cache&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Report&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$report&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$ttl&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$report&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$report&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ttl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token variable&quot;&gt;$ttl&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;600&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$report&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;-summary&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$report&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSummary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 6: Value assigned to variable $ttl is never read. &lt;code&gt;assign.unused&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;assign.overwritten&quot;&gt;
&lt;p&gt;&lt;code&gt;assign.overwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;formatPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$label&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;number_format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$label&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;%.2f %s&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value assigned to variable $label is never read before being overwritten. &lt;code&gt;assign.overwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;assign.unusedFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;importRows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Importer&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$importer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$imported&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$importer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;import&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$imported&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$imported&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value assigned to variable $imported only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 6: Value assigned to variable $imported only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;assign.redundant&quot;&gt;
&lt;p&gt;&lt;code&gt;assign.redundant&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;activate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$status&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;active&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVerified&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$status&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;active&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 5: Variable $status is assigned value &#39;active&#39; but it already has that value. &lt;code&gt;assign.redundant&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;array.unusedOffset&quot;&gt;
&lt;p&gt;&lt;code&gt;array.unusedOffset&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;orderSummary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$summary&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
		&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;id&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;total&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTotal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Order #%d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$summary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;id&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 5: Offset &#39;total&#39; of array assigned to variable $summary is never read. &lt;code&gt;array.unusedOffset&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;array.offsetOverwritten&quot;&gt;
&lt;p&gt;&lt;code&gt;array.offsetOverwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;httpOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$slowNetwork&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;array&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$options&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
		&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;timeout&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;retries&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;timeout&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$slowNetwork&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 4: Offset &#39;timeout&#39; of array assigned to variable $options is never read before being overwritten. &lt;code&gt;array.offsetOverwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;array.unusedOffsetFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;array.unusedOffsetFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;countWords&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$words&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$stats&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;words&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;letters&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$words&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$word&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$stats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;words&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;words&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$stats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;letters&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;letters&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$word&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stats&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;words&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Offset &#39;letters&#39; of array assigned to variable $stats only flows into values that are never used. &lt;code&gt;array.unusedOffsetFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 6: Value assigned to $stats[&#39;letters&#39;] only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;foreach.unusedValue&quot; data-where=&quot;foreach value&quot;&gt;
&lt;p&gt;&lt;code&gt;foreach.unusedValue&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;notifyAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Mailer&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$admin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$admin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;New signup&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Someone joined.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Foreach value variable $user is never read. &lt;code&gt;foreach.unusedValue&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;foreach.valueOverwritten&quot; data-where=&quot;foreach value&quot;&gt;
&lt;p&gt;&lt;code&gt;foreach.valueOverwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;printLines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$lines&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$lines&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Line&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PHP_EOL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Foreach value variable $line is never read before being overwritten. &lt;code&gt;foreach.valueOverwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;foreach.unusedValueFlow&quot; data-where=&quot;foreach value&quot;&gt;
&lt;p&gt;&lt;code&gt;foreach.unusedValueFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;retryFailed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Queue&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$failedJobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$failedJobs&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$job&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$attempts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$attempts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$attempts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Foreach value variable $attempts only flows into values that are never used. &lt;code&gt;foreach.unusedValueFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 7: Value assigned to variable $attempts only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;foreach.unusedKey&quot; data-where=&quot;foreach key&quot;&gt;
&lt;p&gt;&lt;code&gt;foreach.unusedKey&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;totalPrice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$prices&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;float&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$prices&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$sku&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$price&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$price&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$total&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 4: Foreach key variable $sku is never read. &lt;code&gt;foreach.unusedKey&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;foreach.keyOverwritten&quot; data-where=&quot;foreach key&quot;&gt;
&lt;p&gt;&lt;code&gt;foreach.keyOverwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$options&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;array&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$labels&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$options&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 4: Foreach key variable $key is never read before being overwritten. &lt;code&gt;foreach.keyOverwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;foreach.unusedKeyFlow&quot; data-where=&quot;foreach key&quot;&gt;
&lt;p&gt;&lt;code&gt;foreach.unusedKeyFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;enqueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Queue&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$jobs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$jobs&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$position&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$job&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isFull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;flush&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$position&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$position&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Foreach key variable $position only flows into values that are never used. &lt;code&gt;foreach.unusedKeyFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 7: Value assigned to variable $position only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;preInc.unused&quot;&gt;
&lt;p&gt;&lt;code&gt;preInc.unused&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;nextInvoiceNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$next&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$last&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Issuing invoice %d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$last&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 6: Value of variable $next after ++ is never read. &lt;code&gt;preInc.unused&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;preInc.overwritten&quot;&gt;
&lt;p&gt;&lt;code&gt;preInc.overwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;pageNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$reset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$reset&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$page&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$page&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 5: Value of variable $page after ++ is never read before being overwritten. &lt;code&gt;preInc.overwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;preInc.unusedFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;preInc.unusedFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;waitForJob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;callable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$isDone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$attempts&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$isDone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$attempts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value assigned to variable $attempts only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 5: Value of variable $attempts after ++ only flows into values that are never used. &lt;code&gt;preInc.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;postInc.unused&quot;&gt;
&lt;p&gt;&lt;code&gt;postInc.unused&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;placeholders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$sql&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;(%s)&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;implode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;, &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_fill&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;?&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$sql&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 5: Value of variable $i after ++ is never read. &lt;code&gt;postInc.unused&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;postInc.overwritten&quot;&gt;
&lt;p&gt;&lt;code&gt;postInc.overwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;rowNumber&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$line&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 4: Value of variable $line after ++ is never read before being overwritten. &lt;code&gt;postInc.overwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;postInc.unusedFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;postInc.unusedFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sendAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Mailer&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$sent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$users&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;News&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hello!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$sent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value assigned to variable $sent only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 6: Value of variable $sent after ++ only flows into values that are never used. &lt;code&gt;postInc.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;preDec.unused&quot;&gt;
&lt;p&gt;&lt;code&gt;preDec.unused&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;remainingSlots&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$capacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$left&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$capacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;%d slots left&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$capacity&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 6: Value of variable $left after – is never read. &lt;code&gt;preDec.unused&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;preDec.overwritten&quot;&gt;
&lt;p&gt;&lt;code&gt;preDec.overwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;countdown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$from&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value of variable $from after – is never read before being overwritten. &lt;code&gt;preDec.overwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;preDec.unusedFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;preDec.unusedFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;drainQueue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;SplQueue&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$budget&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$queue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dequeue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$budget&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value assigned to variable $budget only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 6: Value of variable $budget after – only flows into values that are never used. &lt;code&gt;preDec.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;postDec.unused&quot;&gt;
&lt;p&gt;&lt;code&gt;postDec.unused&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;lastIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$index&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$index&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$index&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 5: Value of variable $index after – is never read. &lt;code&gt;postDec.unused&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;postDec.overwritten&quot;&gt;
&lt;p&gt;&lt;code&gt;postDec.overwritten&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;retriesLeft&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$retries&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$retries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value of variable $retries after – is never read before being overwritten. &lt;code&gt;postDec.overwritten&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;postDec.unusedFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;postDec.unusedFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;consume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;SplStack&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$remaining&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stack&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$stack&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$stack&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$remaining&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Value assigned to variable $remaining only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 6: Value of variable $remaining after – only flows into values that are never used. &lt;code&gt;postDec.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;catch.unusedVariableFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;catch.unusedVariableFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fetchStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Http&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$http&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;https://example.com/status&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TransientException&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$retries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;warning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Status check failed&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$error&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$retries&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;unknown&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 5: Catch variable $error only flows into values that are never used. &lt;code&gt;catch.unusedVariableFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 8: Value assigned to variable $error only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;closure.unusedUse&quot;&gt;
&lt;p&gt;&lt;code&gt;closure.unusedUse&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;greeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$greeting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name return-type&quot;&gt;Closure&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$greeting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$greeting&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;, &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Anonymous function has an unused use $logger. &lt;code&gt;closure.unusedUse&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;closure.unusedUseFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;closure.unusedUseFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;eachWithIndex&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;callable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name return-type&quot;&gt;Closure&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$index&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$items&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$item&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$index&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$index&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 3: Anonymous function has a use $index that only flows into values that are never used. &lt;code&gt;closure.unusedUseFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 6: Value assigned to variable $index only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;function.unusedParameter&quot;&gt;
&lt;p&gt;&lt;code&gt;function.unusedParameter&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;welcomeMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$locale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome, %s!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 1: Function welcomeMessage() has an unused parameter $locale. &lt;code&gt;function.unusedParameter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;function.unusedParameterFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;function.unusedParameterFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;retryDelay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$attempt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$delay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$attempt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$delay&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$delay&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$attempt&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 1: Function retryDelay() has a parameter $delay that only flows into values that are never used. &lt;code&gt;function.unusedParameterFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 4: Value assigned to variable $delay only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;method.unusedParameter&quot;&gt;
&lt;p&gt;&lt;code&gt;method.unusedParameter&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;InvoiceMailer&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Mailer&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;mailer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Invoice&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Order&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Invoice for order #%d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 10: Method InvoiceMailer::body() has an unused parameter $email. &lt;code&gt;method.unusedParameter&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-identifier=&quot;method.unusedParameterFlow&quot;&gt;
&lt;p&gt;&lt;code&gt;method.unusedParameterFlow&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;CsvExport&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$bytes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$bytes&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Line 8: Method CsvExport::write() has a parameter $bytes that only flows into values that are never used. &lt;code&gt;method.unusedParameterFlow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Line 11: Value assigned to variable $bytes only flows into values that are never used. &lt;code&gt;assign.unusedFlow&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Make sure to enable &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;bleeding edge&lt;/a&gt; to take advantage of these new rules.&lt;/p&gt;
&lt;h2 id=&quot;inferring-closure-types-from-usages&quot; tabindex=&quot;-1&quot;&gt;Inferring closure types from usages &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#inferring-closure-types-from-usages&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This builds on the same engine that powers bidirectional type narrowing for generics. It lets PHPStan observe how a closure stored in a variable is used after it’s assigned, and feed that information back into the final analysis pass.&lt;/p&gt;
&lt;p&gt;Closures never need to be annotated with &lt;code&gt;@param&lt;/code&gt; and &lt;code&gt;@return&lt;/code&gt; PHPDocs. Even without any typehints, PHPStan is able to precisely infer the involved types, including variables the closure takes by reference:&lt;/p&gt;
&lt;div class=&quot;type-inference-demo&quot; data-hint=&quot;Switch the lines on and off.&quot;&gt;
&lt;script type=&quot;application/json&quot;&gt;
[{&quot;id&quot;: &quot;closure-usages&quot;, &quot;label&quot;: &quot;From usages&quot;, &quot;hint&quot;: &quot;Switch the lines on and off: PHPStan infers the parameter types of the closure from where it is called and passed.&quot;, &quot;toggles&quot;: [10, 11], &quot;defaults&quot;: [false, false], &quot;probes&quot;: [{&quot;line&quot;: 7, &quot;variable&quot;: &quot;id&quot;, &quot;showName&quot;: true}, {&quot;line&quot;: 9, &quot;variable&quot;: &quot;title&quot;, &quot;showName&quot;: false}, {&quot;line&quot;: 10, &quot;variable&quot;: &quot;labels&quot;, &quot;showName&quot;: false}, {&quot;line&quot;: 11, &quot;variable&quot;: &quot;fallback&quot;, &quot;showName&quot;: false}], &quot;results&quot;: [{&quot;types&quot;: [&quot;42&quot;, &quot;&#39;Order #42&#39;&quot;, null, null], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;int&quot;, &quot;&#39;Order #42&#39;&quot;, &quot;list&lt;non-falsy-string&gt;&quot;, null], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;42|&#39;n/a&#39;&quot;, &quot;&#39;Order #42&#39;&quot;, null, &quot;&#39;Order #n/a&#39;&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;&#39;n/a&#39;|int&quot;, &quot;&#39;Order #42&#39;&quot;, &quot;list&lt;non-falsy-string&gt;&quot;, &quot;&#39;Order #n/a&#39;&quot;], &quot;errors&quot;: []}]}, {&quot;id&quot;: &quot;closure-callable&quot;, &quot;label&quot;: &quot;Passed as a callback&quot;, &quot;hint&quot;: &quot;Switch the lines on and off: PHPStan infers the parameter types of the closure from the callable parameters it is passed to, and checks the body with them.&quot;, &quot;toggles&quot;: [17], &quot;defaults&quot;: [false], &quot;probes&quot;: [{&quot;line&quot;: 10, &quot;variable&quot;: &quot;event&quot;, &quot;showName&quot;: true}], &quot;results&quot;: [{&quot;types&quot;: [&quot;Order&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;Order|Refund&quot;], &quot;errors&quot;: [{&quot;line&quot;: 11, &quot;message&quot;: &quot;Call to an undefined method Order|Refund::getEmail().&quot;, &quot;identifier&quot;: &quot;method.notFound&quot;}]}]}, {&quot;id&quot;: &quot;closure-by-ref&quot;, &quot;label&quot;: &quot;By-reference variables&quot;, &quot;hint&quot;: &quot;Switch the lines on and off: PHPStan analyses the closure where it is called, with what the variables it takes by reference hold at that moment.&quot;, &quot;toggles&quot;: [8, 9, 10], &quot;defaults&quot;: [false, false, true], &quot;probes&quot;: [{&quot;line&quot;: 5, &quot;variable&quot;: &quot;messages&quot;, &quot;showName&quot;: true}, {&quot;line&quot;: 11, &quot;variable&quot;: &quot;messages&quot;, &quot;showName&quot;: true}], &quot;results&quot;: [{&quot;types&quot;: [&quot;array{}&quot;, &quot;array{&#39;Started&#39;}&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;array{}|array{&#39;Started&#39;}&quot;, &quot;array{&#39;Started&#39;, &#39;Processing&#39;}&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;array{}&quot;, &quot;array{}&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;array{}|array{&#39;Started&#39;}&quot;, &quot;array{}&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;array{}|array{&#39;Started&#39;}&quot;, &quot;array{&#39;Started&#39;, &#39;Finished&#39;}&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;array{}|array{0: &#39;Started&#39;, 1?: &#39;Processing&#39;}&quot;, &quot;array{&#39;Started&#39;, &#39;Processing&#39;, &#39;Finished&#39;}&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;array{}&quot;, &quot;array{&#39;Finished&#39;}&quot;], &quot;errors&quot;: []}, {&quot;types&quot;: [&quot;array{}|array{&#39;Started&#39;}&quot;, &quot;array{&#39;Finished&#39;}&quot;], &quot;errors&quot;: []}]}]
&lt;/script&gt;
&lt;div data-scenario=&quot;closure-usages&quot;&gt;
&lt;p&gt;&lt;strong&gt;From usages&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @return list&amp;lt;int&gt; */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;findOrderIds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;printOrders&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$format&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Order #&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$title&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$labels&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findOrderIds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$fallback&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;n/a&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$id&lt;/code&gt; is &lt;code&gt;&#39;n/a&#39;|int&lt;/code&gt; on line 7, &lt;code&gt;$title&lt;/code&gt; is &lt;code&gt;&#39;Order #42&#39;&lt;/code&gt; on line 9, &lt;code&gt;$labels&lt;/code&gt; is &lt;code&gt;list&amp;lt;non-falsy-string&amp;gt;&lt;/code&gt; on line 10, &lt;code&gt;$fallback&lt;/code&gt; is &lt;code&gt;&#39;Order #n/a&#39;&lt;/code&gt; on line 11.&lt;/p&gt;
&lt;/div&gt;
&lt;div data-scenario=&quot;closure-callable&quot;&gt;
&lt;p&gt;&lt;strong&gt;Passed as a callback&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @param callable(Order): void $listener */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;onOrderPlaced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;callable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$listener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** @param callable(Refund): void $listener */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;onRefundIssued&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;callable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$listener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;registerListeners&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Mailer&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$notify&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$mailer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$event&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Thank you&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;We have received it.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;onOrderPlaced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;onRefundIssued&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$notify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$event&lt;/code&gt; is &lt;code&gt;Order|Refund&lt;/code&gt; on line 10.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Line 11: Call to an undefined method Order|Refund::getEmail().&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div data-scenario=&quot;closure-by-ref&quot;&gt;
&lt;p&gt;&lt;strong&gt;By-reference variables&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$messages&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$log&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$messages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$messages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Started&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Processing&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$messages&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Finished&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;implode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$messages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$messages&lt;/code&gt; is &lt;code&gt;array{}|array{&#39;Started&#39;}&lt;/code&gt; on line 5, &lt;code&gt;$messages&lt;/code&gt; is &lt;code&gt;array{&#39;Finished&#39;}&lt;/code&gt; on line 11.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Make sure to enable &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;bleeding edge&lt;/a&gt; to take advantage of these improvements.&lt;/p&gt;
&lt;h2 id=&quot;inferring-static-variable-types-from-assignments&quot; tabindex=&quot;-1&quot;&gt;Inferring static variable types from assignments &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#inferring-static-variable-types-from-assignments&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By now you’ve probably noticed the pattern 😅 Besides generic objects and closures, PHPStan can inspect assignments from the whole function body to infer types of &lt;code&gt;static&lt;/code&gt; variables. Previously, they had to be annotated with &lt;code&gt;@var&lt;/code&gt;, otherwise you’d end up with an implicit &lt;code&gt;mixed&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;Switch the assignments off to see what happens:&lt;/p&gt;
&lt;div class=&quot;type-inference-demo&quot; data-hint=&quot;Switch the assignments on and off: PHPStan infers the type of the static variable from all of them.&quot;&gt;
&lt;script type=&quot;application/json&quot;&gt;
[{&quot;id&quot;: &quot;static-cache&quot;, &quot;label&quot;: &quot;Static variable&quot;, &quot;toggles&quot;: [6, 8], &quot;defaults&quot;: [true, true], &quot;probes&quot;: [{&quot;line&quot;: 3, &quot;variable&quot;: &quot;cache&quot;, &quot;showName&quot;: true}], &quot;results&quot;: [{&quot;types&quot;: [&quot;null&quot;], &quot;errors&quot;: [{&quot;line&quot;: 4, &quot;message&quot;: &quot;Strict comparison using === between null and null will always evaluate to true.&quot;, &quot;identifier&quot;: &quot;identical.alwaysTrue&quot;}, {&quot;line&quot;: 12, &quot;message&quot;: &quot;Function getCache() should return Cache but returns null.&quot;, &quot;identifier&quot;: &quot;return.type&quot;}]}, {&quot;types&quot;: [&quot;ApcuCache|null&quot;], &quot;errors&quot;: [{&quot;line&quot;: 12, &quot;message&quot;: &quot;Function getCache() should return Cache but returns ApcuCache|null.&quot;, &quot;identifier&quot;: &quot;return.type&quot;}]}, {&quot;types&quot;: [&quot;FileCache|null&quot;], &quot;errors&quot;: [{&quot;line&quot;: 12, &quot;message&quot;: &quot;Function getCache() should return Cache but returns FileCache|null.&quot;, &quot;identifier&quot;: &quot;return.type&quot;}]}, {&quot;types&quot;: [&quot;ApcuCache|FileCache|null&quot;], &quot;errors&quot;: []}]}]
&lt;/script&gt;
&lt;div data-scenario=&quot;static-cache&quot;&gt;
&lt;p&gt;&lt;strong&gt;Static variable&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name return-type&quot;&gt;Cache&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extension_loaded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;apcu&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ApcuCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FileCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;/tmp/cache&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan infers that &lt;code&gt;$cache&lt;/code&gt; is &lt;code&gt;ApcuCache|FileCache|null&lt;/code&gt; on line 3.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Again, you can enjoy this if you enable &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;bleeding edge&lt;/a&gt; in your PHPStan config file.&lt;/p&gt;
&lt;h2 id=&quot;under-the-hood%3A-single-pass-inside-out-engine-rewrite&quot; tabindex=&quot;-1&quot;&gt;Under the hood: single-pass inside-out engine rewrite &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#under-the-hood%3A-single-pass-inside-out-engine-rewrite&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is the &lt;a href=&quot;https://github.com/phpstan/phpstan-src/pull/5857&quot;&gt;preliminary work&lt;/a&gt; that enabled all of these improvements. From the very beginning until 2.2.x, PHPStan had three “god” classes that each traversed the &lt;a href=&quot;/developing-extensions/abstract-syntax-tree&quot;&gt;AST&lt;/a&gt; on their own:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NodeScopeResolver::processNodes()&lt;/code&gt;: updates &lt;a href=&quot;/developing-extensions/scope&quot;&gt;Scope&lt;/a&gt; based on assignments, entering classes, functions etc.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MutatingScope::resolveType(Expr $expr): Type&lt;/code&gt;: resolves &lt;a href=&quot;/developing-extensions/type-system&quot;&gt;type&lt;/a&gt; of an expression by checking types of child expressions it consists of&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TypeSpecifier&lt;/code&gt;: narrows types of expressions, for example decides what happens to &lt;code&gt;$foo&lt;/code&gt; after &lt;code&gt;$foo instanceof Bar&lt;/code&gt; for both truthy and falsey contexts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem with this approach was that it could either be slow or imprecise. If an expression also contains an assignment or modifies Scope some other way, the type resolution in MutatingScope will not know about it, unless it processes the node again. PHPStan used to process some nodes repeatedly to get rid of a certain class of bugs. So you could say it was both slow and imprecise 😅&lt;/p&gt;
&lt;p&gt;Take this code as an example:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$c&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$c&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$c&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;dumpType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In PHPStan 2.2.x and before, the type of the array was inferred as &lt;code&gt;array{1, (float|int), *ERROR*, (float|int), *ERROR*, *ERROR*}&lt;/code&gt;. PHPStan had no clue about the correct type of the array, because later array items referenced variables assigned or changed in the earlier ones.&lt;/p&gt;
&lt;p&gt;In PHPStan 2.3.0 and onward, the type of the array from the code snippet above is correctly inferred as &lt;code&gt;array{1, 2, 1, 3, 1, 2}&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The rewrite changes where and when expression types are inferred. Everything now happens when a node is processed in &lt;code&gt;NodeScopeResolver&lt;/code&gt;. Once it’s finished, the returned result object contains everything: the updated Scope, the expression type, and how the expression narrows other nested expressions. We took extra care not to break any extension APIs, so &lt;code&gt;$scope-&amp;gt;getType($expr)&lt;/code&gt; continues to work as before.&lt;/p&gt;
&lt;p&gt;Before the rewrite, some code, like deeply nested &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; / &lt;code&gt;||&lt;/code&gt; expressions, could lead to real slowdowns because they’d be processed many times. It wouldn’t have been possible to introduce the awesome new features described in this article with the old internals because of the multiplicative nature of multiple passes needed for these features. You won’t notice going from 1 to 2 if 1 takes very little time, but you’d certainly notice if something was analysed 20 times instead of 10 (which was already pretty slow).&lt;/p&gt;
&lt;h2 id=&quot;update-extensions-today-too!&quot; tabindex=&quot;-1&quot;&gt;Update extensions today too! &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#update-extensions-today-too!&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A bunch of official PHPStan extensions have new releases today related to changes and enhancements in PHPStan 2.3.0. So don’t forget to update them alongside PHPStan.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/phpstan/phpstan-strict-rules&quot;&gt;phpstan-strict-rules&lt;/a&gt; 2.1.0 makes &lt;a href=&quot;/error-identifiers/foreach.valueOverwrite&quot;&gt;&lt;code&gt;foreach.valueOverwrite&lt;/code&gt;&lt;/a&gt; less annoying and no longer reports it in harmless scenarios. &lt;a href=&quot;https://github.com/phpstan/phpstan-strict-rules/issues/332&quot;&gt;More info »&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/phpstan/phpstan-symfony&quot;&gt;phpstan-symfony&lt;/a&gt; 2.1.0 no longer invalidates the entire result cache when the DI container changes. It uses &lt;a href=&quot;/developing-extensions/result-cache-meta-extensions#tracking-dependencies-on-files&quot;&gt;new extensions and APIs&lt;/a&gt; to reanalyse only files referencing the changed parameters and services while keeping the rest of the result cache intact.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/phpstan/phpstan-phpunit&quot;&gt;phpstan-phpunit&lt;/a&gt; 2.1.0 makes &lt;code&gt;DataProviderDataRule&lt;/code&gt; hook onto multiple node types at once using the new &lt;a href=&quot;https://apiref.phpstan.org/2.3.x/PHPStan.Rules.MultipleNodeTypesRule.html&quot;&gt;MultipleNodeTypesRule&lt;/a&gt; interface, mostly for performance reasons.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider sponsoring&lt;/strong&gt; further development of PHPStan on GitHub Sponsors and also &lt;strong&gt;subscribe to PHPStan Pro&lt;/strong&gt;&lt;/a&gt;! I’d really appreciate it!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;So for about 18 years. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
		</entry>
	
		
		<entry>
			<title>PHPStan 2.2: Unsealed Array Shapes, Safer Array Keys, and More!</title>
			<link href="https://phpstan.org/blog/phpstan-2-2-unsealed-array-shapes-safer-array-keys"/>
			<updated>2026-05-28T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-2-2-unsealed-array-shapes-safer-array-keys</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;It’s been almost a year and a half since we last bumped the minor version. Although we released many improvements in the 2.1.x series, grouped together in over 55 releases, we also cooked up some bigger changes people have been asking for for a long time.&lt;/p&gt;
&lt;p&gt;Two of them have stood out ever since array shapes were &lt;a href=&quot;https://github.com/phpstan/phpstan-src/commit/c4ed3c7c8ded963aa8bec0c6c789a5090998f5c9&quot;&gt;first introduced seven years ago&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ability to intersect an array shape with a general array: &lt;code&gt;array&amp;lt;string, X&amp;gt;&amp;amp;array{a: Y}&lt;/code&gt; - meaning &lt;code&gt;array{a: Y}&lt;/code&gt; plus the possibility of more &lt;code&gt;string&lt;/code&gt; keys whose values are &lt;code&gt;X&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The ability to intersect two distinct array shapes like &lt;code&gt;array{a: X}&amp;amp;array{b: Y}&lt;/code&gt; to produce one combined shape &lt;code&gt;array{a: X, b: Y}&lt;/code&gt;. The motivating use cases are composable type aliases, and modelling a function that returns its input array with an extra key added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem with both of those is that they don’t make sense from the type system’s point of view. The type system is a mathematical system where hacks and workarounds lead to leaks and collapse quickly. Elements of an &lt;a href=&quot;/blog/union-types-vs-intersection-types&quot;&gt;intersection type&lt;/a&gt; get &lt;a href=&quot;/developing-extensions/type-system#type-normalization&quot;&gt;normalized&lt;/a&gt; to the type that’s the smaller one out of a pair (the subtype). So &lt;code&gt;mixed&amp;amp;int&lt;/code&gt; results in &lt;code&gt;int&lt;/code&gt; and &lt;code&gt;int&amp;amp;int&amp;lt;0, max&amp;gt;&lt;/code&gt; results in &lt;code&gt;int&amp;lt;0, max&amp;gt;&lt;/code&gt;. With this definition &lt;code&gt;array&amp;lt;string, X&amp;gt;&amp;amp;array{a: Y}&lt;/code&gt; gets normalized to &lt;code&gt;array{a: Y}&lt;/code&gt;. And &lt;code&gt;array&amp;lt;int, X&amp;gt;&amp;amp;array{a: Y}&lt;/code&gt; would be normalized to &lt;code&gt;never&lt;/code&gt; type (the bottom type) because there’s no overlap (no intersection) between these two types.&lt;/p&gt;
&lt;p&gt;The intersection of &lt;code&gt;array{a: X}&lt;/code&gt; and &lt;code&gt;array{b: Y}&lt;/code&gt; is equivalent to having this code:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// $x is X, $y is Y&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;a&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;b&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$y&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// here we&#39;d have the intersection&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// but in reality these types are distinct and do not overlap&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// the condition is always false!&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These reasons are why these operations didn’t work as expected. But I acknowledge the real need for them. In order to make them work, we’d have to introduce a new concept and new syntax.&lt;/p&gt;
&lt;h2 id=&quot;introducing-unsealed-array-shapes&quot; tabindex=&quot;-1&quot;&gt;Introducing unsealed array shapes &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#introducing-unsealed-array-shapes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The intersection &lt;code&gt;array{a: X}&amp;amp;array{b: Y}&lt;/code&gt; would make sense if the array shapes allowed for extra keys. So this is the syntax:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;array{foo: int, ...&amp;lt;string, int&amp;gt;}&lt;/code&gt; - constrain the extra keys’ key and value types&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array{foo: int, ...&amp;lt;Bar&amp;gt;}&lt;/code&gt; - a single type inside &lt;code&gt;...&amp;lt;&amp;gt;&lt;/code&gt; is the value type (key is implied to be &lt;code&gt;array-key&lt;/code&gt; which is a &lt;a href=&quot;/config-reference#checkbenevolentuniontypes&quot;&gt;benevolent union&lt;/a&gt; of &lt;code&gt;int|string&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;list{X, Y, ...&amp;lt;Z&amp;gt;}&lt;/code&gt; - lists only allow a single value type after first two keys (the key is always &lt;code&gt;int&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array{foo: int, ...}&lt;/code&gt; - bare &lt;code&gt;...&lt;/code&gt; means any extra keys (shorthand for &lt;code&gt;...&amp;lt;array-key, mixed&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I consider this very intuitive because the part after &lt;code&gt;...&lt;/code&gt; works the same way in general arrays like &lt;code&gt;array&amp;lt;string, int&amp;gt;&lt;/code&gt;. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The great news is that this solves both problems. We can now describe array shapes with extra keys. We can also produce a combined shape by intersecting unsealed array shapes. When one or both sides allow for extra keys, the intersection suddenly makes sense!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;array{a: int, ...}&amp;amp;array{b: string, ...}&lt;/code&gt; leads to &lt;code&gt;array{a: int, b: string, ...}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array{a: int, b: string, ...}&amp;amp;array{a: int&amp;lt;0, max&amp;gt;, ...}&lt;/code&gt; leads to &lt;code&gt;array{a: int&amp;lt;0, max&amp;gt;, b: string, ...}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array{a: int, ...}&amp;amp;array{a: string, ...}&lt;/code&gt; still leads to &lt;code&gt;never&lt;/code&gt; - same known key with incompatible value types.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;sealed-array-shapes-become-truly-sealed&quot; tabindex=&quot;-1&quot;&gt;Sealed array shapes become truly sealed &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#sealed-array-shapes-become-truly-sealed&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHPStan’s modelling of array shapes has been flawed for the past seven years. They allow extra keys but they are inconsistent about it.&lt;/p&gt;
&lt;p&gt;Let’s take a function:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * @param array{a: int} $a
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan doesn’t check whether the argument has extra keys, so calls with &lt;code&gt;array{a: int, b: string}&lt;/code&gt; are accepted as well.&lt;/p&gt;
&lt;p&gt;PHPStan even accepts a general array as long as we check that key &lt;code&gt;a&lt;/code&gt; is present:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * @param array&amp;lt;int&gt; $b
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;isset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;a&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// No errors!&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s not okay, because the array shape contradicts itself the moment we start asking it questions:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * @param array{a: int} $a
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;dumpType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// reports 1, but that&#39;s a lie&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;b&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;foo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// PHPStan reports the offset doesn&#39;t exist - also a lie, b may well exist at runtime&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$k&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;dumpType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$k&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Reports &#39;a&#39;, but the user might have passed more keys&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;dumpType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$v&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Reports int, but should be mixed&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Array shapes look great on the surface - they let legacy applications describe the arrays flowing through their code. But under the hood they behave inconsistently and unsafely, leading to undetected runtime errors.&lt;/p&gt;
&lt;p&gt;These mistakes are remedied in PHPStan 2.2 when you turn on &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;bleeding edge&lt;/a&gt;. Array shapes like &lt;code&gt;array{a: int}&lt;/code&gt; become truly sealed and report everything consistently. You can no longer pass general arrays to them and you can’t pass extra keys either.&lt;/p&gt;
&lt;p&gt;For now it’s opt-in, and it will become the only behaviour in the next major PHPStan version. It’s a big disruption - real-world projects will typically see hundreds of new errors when they turn this on. These errors are valuable and important to fix, but we decided to gate it behind bleeding edge because of PHPStan’s &lt;a href=&quot;/user-guide/backward-compatibility-promise&quot;&gt;backward compatibility promise&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When a sealed array shape rejects another array, the rejection comes with a reason, shown next to the error message (with a 💡 emoji prefix in the default &lt;code&gt;table&lt;/code&gt; formatter).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/images/sealed-tip.png&quot; alt=&quot;Sealed array tip&quot;&gt;&lt;/p&gt;
&lt;p&gt;These tips also appear on the &lt;a href=&quot;/r/716542aa-66ae-453f-969e-4cb71a2ecb3c&quot;&gt;online playground&lt;/a&gt; and in &lt;a href=&quot;/blog/introducing-phpstan-pro&quot; class=&quot;phpstan-pro-label&quot;&gt;PHPStan Pro&lt;/a&gt;. Some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sealed array shape can only accept a constant array. Extra keys are not allowed.&lt;/li&gt;
&lt;li&gt;Sealed array shape does not accept array with extra key ‘year’.&lt;/li&gt;
&lt;li&gt;Sealed array shape does not accept unsealed array shape.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These messages will help users understand why their types are rejected.&lt;/p&gt;
&lt;p&gt;Unsealed array shapes and related work &lt;strong&gt;have been commissioned and made possible by the &lt;a href=&quot;https://www.sovereign.tech/&quot;&gt;Sovereign Tech Fund&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;safer-array-keys&quot; tabindex=&quot;-1&quot;&gt;Safer array keys &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#safer-array-keys&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Changes to array shapes are not the only thing added to PHPStan 2.2.0, and not even the only thing related to arrays. We’re also trying to tackle a long-standing PHP quirk around string array keys. This has been extensively described in the recent article &lt;a href=&quot;/blog/why-array-string-keys-are-not-type-safe&quot;&gt;Why Array String Keys Are Not Type-Safe in PHP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The TL;DR: you can’t rely on the key type in &lt;code&gt;array&amp;lt;string, X&amp;gt;&lt;/code&gt;. Even if you only use strings as keys there, you might end up with an array that’s &lt;code&gt;array&amp;lt;int|string, X&amp;gt;&lt;/code&gt;. Specifically, strings that contain decimal integers (like &lt;code&gt;&#39;123&#39;&lt;/code&gt;) get cast to integers.&lt;/p&gt;
&lt;p&gt;PHPStan 2.2.0 offers several countermeasures to this problem. It introduces the &lt;code&gt;non-decimal-int-string&lt;/code&gt; type which describes strings that don’t get cast to &lt;code&gt;int&lt;/code&gt; when used as array keys. You can’t use an arbitrary &lt;code&gt;string&lt;/code&gt; as a &lt;code&gt;non-decimal-int-string&lt;/code&gt;, because it might be cast to &lt;code&gt;int&lt;/code&gt; at runtime.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @param array&amp;lt;non-decimal-int-string, mixed&gt; $data */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// might produce an int key&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Error:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Parameter #1 $data of function processData&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// expects array&amp;lt;non-decimal-int-string, mixed&gt;, non-empty-array&amp;lt;string, 1&gt; given.&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;processData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use &lt;code&gt;non-decimal-int-string&lt;/code&gt; for the array keys where type safety matters most, and PHPStan will enforce it. For completeness, we’re also introducing &lt;code&gt;decimal-int-string&lt;/code&gt; which also no doubt &lt;a href=&quot;/r/d46cc2dd-07b5-482f-8b48-3697580f4968&quot;&gt;has its uses&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, PHPStan 2.2.0 introduces a new configuration parameter &lt;a href=&quot;/config-reference#reportunsafearraystringkeycasting&quot;&gt;&lt;code&gt;reportUnsafeArrayStringKeyCasting&lt;/code&gt;&lt;/a&gt; with three possible values that change the interpretation of PHPDoc types:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;/strong&gt; - Current behaviour. No additional checks. String keys that look like decimal integers are silently cast to &lt;code&gt;int&lt;/code&gt; at runtime without any PHPStan errors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;&#39;detect&#39;&lt;/code&gt;&lt;/strong&gt; - A typehinted &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt; accepts other &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt;, but when you get a key out of it (e.g. via &lt;code&gt;foreach&lt;/code&gt; or &lt;code&gt;array_keys&lt;/code&gt;), the key type will be &lt;code&gt;int|non-decimal-int-string&lt;/code&gt; instead of &lt;code&gt;string&lt;/code&gt;. This reveals potential issues without requiring you to change all your array type annotations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;&#39;prevent&#39;&lt;/code&gt;&lt;/strong&gt; - A typehinted &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt; is essentially treated as &lt;code&gt;array&amp;lt;non-decimal-int-string, mixed&amp;gt;&lt;/code&gt;. It will only accept another array with safe string keys. Additionally, any &lt;code&gt;string&lt;/code&gt; being used as an array key is correctly narrowed to &lt;code&gt;int|non-decimal-int-string&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For now the default is &lt;code&gt;null&lt;/code&gt;. Time and prolonged exposure to these options will tell whether we should change the default to one of the stricter behaviours. From my experience, both of them are brutal in their own ways and cause hundreds of new errors in real-world projects.&lt;/p&gt;
&lt;h2 id=&quot;checking-constants-passed-as-function-arguments&quot; tabindex=&quot;-1&quot;&gt;Checking constants passed as function arguments &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#checking-constants-passed-as-function-arguments&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Try to spot an error in this code:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;json_decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Seems correct, doesn’t it? Except &lt;code&gt;JSON_THROW_ON_ERROR&lt;/code&gt; is being passed as &lt;code&gt;$depth&lt;/code&gt;, not &lt;code&gt;$flags&lt;/code&gt;. So this code decodes the JSON with depth set to &lt;code&gt;4194304&lt;/code&gt;, but it still won’t throw on a syntax error.&lt;/p&gt;
&lt;p&gt;In order to recognize these errors, we had to extract data from various places (mostly PHP documentation) about which constants are valid for which function parameters. The result is yet another &lt;a href=&quot;https://github.com/phpstan/phpstan-src/blob/2.2.x/resources/constantToFunctionParameterMap.php&quot;&gt;multi-thousand-line map&lt;/a&gt;. The rule’s logic is simple: when it sees a call to a function or method in this map and one of its arguments is a built-in PHP constant, the constant is checked against the map entry.&lt;/p&gt;
&lt;p&gt;Some examples of what is and isn’t reported:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Error:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Constant JSON_THROW_ON_ERROR is not allowed for parameter #3 $depth of function json_decode.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;json_decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Not an error: correct usage&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;json_decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token argument-name&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;JSON_THROW_ON_ERROR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Error:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Constant SORT_NATURAL is not allowed for parameter $flags of function json_decode.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;json_decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token argument-name&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;SORT_NATURAL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// No errors: our constants, not built-in&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;json_decode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token argument-name&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DEPTH&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token argument-name&quot;&gt;flags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;THROW&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The map also knows where bitmasks are allowed, and which combinations of flags are invalid within them:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Error:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Combining constants with | is not allowed for parameter #2 $component of function parse_url.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;parse_url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHP_URL_SCHEME&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHP_URL_HOST&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// No error - expected usage&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;flock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LOCK_SH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LOCK_NB&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Error:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Constants LOCK_SH, LOCK_EX cannot be combined for parameter #2 $operation of function flock.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;flock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LOCK_SH&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LOCK_EX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This has historically been a blind spot in PHPStan. Not anymore. These easy-to-get-wrong code snippets are now reported in PHPStan 2.2.0!&lt;/p&gt;
&lt;h2 id=&quot;detecting-named-arguments-with-renamed-parameters-in-subtypes&quot; tabindex=&quot;-1&quot;&gt;Detecting named arguments with renamed parameters in subtypes &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#detecting-named-arguments-with-renamed-parameters-in-subtypes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s take the following code:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;FooImpl&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing wrong with this code, right? And there isn’t, as long as you call it without named arguments:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// OK&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you use named arguments, you suddenly have a bug:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$cb&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token argument-name&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// correct parameter name for Foo::doFoo() method&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// PHP: Fatal error: Uncaught Error: Unknown named parameter $order&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$cb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FooImpl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the longest time &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; I’ve resisted reporting the renamed parameter in an overridden method as a bug, I didn’t want to bother developers with that. After all, it isn’t a bug if you never use named arguments, they’re still a pretty rare occurrence in real-world code.&lt;/p&gt;
&lt;p&gt;But if PHPStan 2.2.0 &lt;a href=&quot;/error-identifiers/argument.parameterRenamedInSubtype&quot;&gt;detects&lt;/a&gt; that you’re calling a method on an interface or non-final class with named arguments, and the parameter is renamed in an overriding method in a subclass, it’s certain you have a bug. You either need to drop the named argument (and pass it positionally), or sync the parameter names. You’ll see this error message:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Call to Foo::doFoo() uses named argument for parameter $order, but FooImpl renames it to $number.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;reducing-false-positives-about-constant-conditions-in-traits&quot; tabindex=&quot;-1&quot;&gt;Reducing false positives about constant conditions in traits &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#reducing-false-positives-about-constant-conditions-in-traits&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHPStan &lt;a href=&quot;/blog/how-phpstan-analyses-traits&quot;&gt;analyses traits&lt;/a&gt; in the context of each class that uses them. An unused trait is not analysed at all; a trait used in three classes is analysed three times. This approach benefits codebases that use traits heavily. Your code in traits can reference properties and methods declared only on the using classes, and as long as it lines up, PHPStan doesn’t report any false positives.&lt;/p&gt;
&lt;p&gt;But there’s another downside to this approach. PHPStan still complained about things that only make sense to flag in code that isn’t reused.&lt;/p&gt;
&lt;p&gt;In normal code it might make sense to ask about &lt;code&gt;$this-&amp;gt;getFoo() !== null&lt;/code&gt; if the return type is &lt;code&gt;string|null&lt;/code&gt;. Totally valid.&lt;/p&gt;
&lt;p&gt;But it’s different in a trait, when &lt;code&gt;getFoo&lt;/code&gt; returns a non-nullable type in one of the using classes:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token variable&quot;&gt;$str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hello&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39; World&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Bar&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Bar&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Zar&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;foo&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Past PHPStan versions analysed each trait usage in isolation with no regard to other contexts. It’d report:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PHPStan: Strict comparison using !== between string and null will always evaluate to true.
(in the context of class Bar)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In PHPStan 2.2.0 we updated a group of rules reporting “constant conditions” to defer these errors until the trait has been analysed in all contexts. If the reports differ across contexts, no error is reported.&lt;/p&gt;
&lt;p&gt;If the condition is always true or always false in all contexts, it’s still reported. So code like this is still reported even when it lives in a trait:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$o&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;stdClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// PHPStan: If condition is always true.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$o&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;allowing-custom-rules-to-emit-collector-data&quot; tabindex=&quot;-1&quot;&gt;Allowing custom rules to emit collector data &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#allowing-custom-rules-to-emit-collector-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The community has taken great advantage of &lt;a href=&quot;/developing-extensions/collectors&quot;&gt;collectors&lt;/a&gt;, implementing awesome extensions like &lt;a href=&quot;https://github.com/shipmonk-rnd/dead-code-detector&quot;&gt;dead-code-detector&lt;/a&gt;. Collectors work exactly like &lt;a href=&quot;/developing-extensions/rules&quot;&gt;custom rules&lt;/a&gt;, but instead of reporting errors directly, they collect data for later evaluation. At the end of the analysis, developers can look at all the collected data from the entire codebase at once, and decide what errors should be reported.&lt;/p&gt;
&lt;p&gt;In past PHPStan versions, you’d have to decide whether you’re writing a custom rule or a collector. What PHPStan 2.2.0 brings to the table is that a custom rule can easily become a collector too. It can report some errors directly, and defer others to later evaluation. All thanks to the &lt;a href=&quot;https://apiref.phpstan.org/2.2.x/source-src.Analyser.CollectedDataEmitter.html#32&quot;&gt;CollectedDataEmitter&lt;/a&gt; interface, implemented by &lt;code&gt;Scope&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Instead of the usual &lt;code&gt;processNode&lt;/code&gt; signature:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Scope&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;array&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also typehint with the new interface:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
 * @param Scope&amp;amp;CollectedDataEmitter $scope
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Scope&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;array&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And emit collected data for an existing collector class:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;emitCollectedData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MyCollector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;some&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what made the previous two features in this article possible in the first place!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider sponsoring&lt;/strong&gt; further development of PHPStan on GitHub Sponsors and also &lt;strong&gt;subscribe to PHPStan Pro&lt;/strong&gt;&lt;/a&gt;! I’d really appreciate it!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;When writing this article, I went down the memory lane and tried to find the origin of this syntax. The whole concept was initially discussed on Psalm’s GitHub but &lt;a href=&quot;https://github.com/vimeo/psalm/issues/4700#issuecomment-869987365&quot;&gt;my idea&lt;/a&gt; was more TypeScript-like: &lt;code&gt;array{foo: Foo, [k: string]: Bar}&lt;/code&gt;. Then I found this &lt;a href=&quot;https://github.com/vimeo/psalm/issues/8804&quot;&gt;issue&lt;/a&gt; from Vincent Langlet (now a major PHPStan contributor!) asking to officially support the syntax in PHPDocs (Matt Brown originally only used it in Psalm’s error messages). So I guess that’s where it started. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Ever since PHP 8.0 was released. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
		</entry>
	
		
		<entry>
			<title>Using PHPStan to Extract Data About Your Codebase</title>
			<link href="https://phpstan.org/blog/using-phpstan-to-extract-data-about-your-codebase"/>
			<updated>2026-03-26T00:00:00Z</updated>
			<id>https://phpstan.org/blog/using-phpstan-to-extract-data-about-your-codebase</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;PHPStan is known for finding bugs in your code. But that’s not all it can do. When PHPStan analyses your codebase, it builds a detailed model of every class, method, property, type, and relationship. All of that knowledge is accessible through &lt;a href=&quot;/developing-extensions/scope&quot;&gt;Scope&lt;/a&gt; and &lt;a href=&quot;/developing-extensions/reflection&quot;&gt;Reflection&lt;/a&gt;. It’d be a shame to only use it for error reporting.&lt;/p&gt;
&lt;p&gt;In this article, I’m going to show you how to use PHPStan as a data extraction tool — to query your codebase and produce machine-readable output you can use for documentation, visualization, or any other purpose.&lt;/p&gt;
&lt;h2 id=&quot;the-general-approach&quot; tabindex=&quot;-1&quot;&gt;The general approach &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-general-approach&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The idea relies on three PHPStan extension points working together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;/developing-extensions/collectors&quot;&gt;Collectors&lt;/a&gt;&lt;/strong&gt; gather data about your codebase during analysis. They visit AST nodes, query the Scope and Reflection, and return structured data.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A &lt;a href=&quot;/developing-extensions/rules&quot;&gt;rule&lt;/a&gt;&lt;/strong&gt; that processes &lt;code&gt;CollectedDataNode&lt;/code&gt; — a special node that gives you access to all collected data after the analysis is complete. Instead of reporting actual errors, it packages the collected data as metadata attached to rule errors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A custom &lt;a href=&quot;/developing-extensions/error-formatters&quot;&gt;error formatter&lt;/a&gt;&lt;/strong&gt; that reads the metadata from these errors and outputs machine-readable data (like JSON) instead of the usual error table.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is a bit of a creative hack — we’re using PHPStan’s error reporting pipeline as a data transport mechanism. The “errors” aren’t real errors at all, they’re just carriers for the data we extracted.&lt;/p&gt;
&lt;p&gt;All of this is tied together with a &lt;a href=&quot;/config-reference&quot;&gt;configuration file&lt;/a&gt; that registers the collectors, the rule, and the error formatter, and points PHPStan at the code you want to analyse.&lt;/p&gt;
&lt;p&gt;Let’s walk through a real-world example.&lt;/p&gt;
&lt;h2 id=&quot;example%3A-generating-a-call-map&quot; tabindex=&quot;-1&quot;&gt;Example: Generating a call map &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#example%3A-generating-a-call-map&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://gitlab.com/stella-maris/callmap&quot;&gt;stella-maris/callmap&lt;/a&gt; by &lt;a href=&quot;https://github.com/heiglandreas&quot;&gt;Andreas Heigl&lt;/a&gt; is a PHPStan extension that maps every method call in your codebase — which method calls which other method, and in which class. The result is a JSON file you can feed into &lt;a href=&quot;https://gitlab.com/stella-maris/callmapcli&quot;&gt;CallMap CLI&lt;/a&gt; to generate a call graph.&lt;/p&gt;
&lt;h3 id=&quot;the-collector&quot; tabindex=&quot;-1&quot;&gt;The Collector &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-collector&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The collector visits every &lt;code&gt;MethodCall&lt;/code&gt; node in the AST and records four things: the calling class, the calling method, the called class, and the called method.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PhpParser&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PhpParser&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Node&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Expr&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;MethodCall&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Analyser&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Collectors&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Collector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * @implements Collector&amp;lt;MethodCall, array{callingClass?: string, callingMethod: string, calledClass: string, calledMethod: string}&gt;
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;MethodCallCollector&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Collector&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getNodeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;MethodCall&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Scope&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$methodName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$methodName&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;instanceof&lt;/span&gt; &lt;span class=&quot;token class-name class-name-fully-qualified&quot;&gt;Node&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Identifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Find the class that declares the called method&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$type&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;var&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$methodReflection&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMethodReflection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$methodName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$methodReflection&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$calledClass&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$methodReflection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDeclaringClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// Find the calling context&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$callingClass&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isInClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$callingClass&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getClassReflection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callingClass&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$callingClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callingMethod&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;calledClass&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$calledClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;calledMethod&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$methodName&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key thing here is that we’re using &lt;code&gt;$scope-&amp;gt;getMethodReflection()&lt;/code&gt; to resolve the &lt;em&gt;declaring&lt;/em&gt; class of the called method. This means if you call &lt;code&gt;$user-&amp;gt;save()&lt;/code&gt; and &lt;code&gt;save()&lt;/code&gt; is declared on a parent &lt;code&gt;Model&lt;/code&gt; class, the collector records &lt;code&gt;Model&lt;/code&gt; as the called class, not the concrete type. This is the kind of insight that would be difficult to get from simple text-based analysis.&lt;/p&gt;
&lt;h3 id=&quot;the-rule&quot; tabindex=&quot;-1&quot;&gt;The Rule &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-rule&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The rule processes &lt;code&gt;CollectedDataNode&lt;/code&gt; and wraps each row of collected data as a rule error with metadata:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Node&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;CollectedDataNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Rules&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;RuleErrorBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/**
 * @implements &#92;PHPStan&#92;Rules&#92;Rule&amp;lt;CollectedDataNode&gt;
 */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Rule&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name class-name-fully-qualified&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Rules&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Rule&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getNodeType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;CollectedDataNode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Node&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Scope&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;array&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token variable&quot;&gt;$errors&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MethodCallCollector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$rows&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$errors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;RuleErrorBuilder&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Metadata&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callmapFormatter.data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$row&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$errors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;$node-&amp;gt;get(MethodCallCollector::class)&lt;/code&gt; call retrieves all data collected by &lt;code&gt;MethodCallCollector&lt;/code&gt; across the entire codebase. Each entry is wrapped as a &lt;code&gt;RuleError&lt;/code&gt; with a specific identifier and the original data stashed in the metadata.&lt;/p&gt;
&lt;h3 id=&quot;the-error-formatter&quot; tabindex=&quot;-1&quot;&gt;The Error Formatter &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-error-formatter&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The error formatter reads these metadata-carrying errors and outputs them as JSON:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Command&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;AnalysisResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Command&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;ErrorFormatter&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;ErrorFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Command&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;ErrorFormatter&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;TableErrorFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Command&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;CallmapFormatter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ErrorFormatter&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;TableErrorFormatter&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tableErrorFormatter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;formatErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token class-name type-declaration&quot;&gt;AnalysisResult&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$analysisResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name type-declaration&quot;&gt;Output&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$analysisResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;hasInternalErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// fall back to table output&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;tableErrorFormatter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$analysisResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token variable&quot;&gt;$output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token variable&quot;&gt;$json&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$analysisResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFileSpecificErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getIdentifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callmapFormatter.data&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// An unexpected real error — fall back to table output&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;tableErrorFormatter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;formatErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                    &lt;span class=&quot;token variable&quot;&gt;$analysisResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                    &lt;span class=&quot;token variable&quot;&gt;$output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token variable&quot;&gt;$metadata&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getMetadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token variable&quot;&gt;$json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callingClass&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callingClass&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callingMethod&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callingMethod&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;calledClass&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;calledClass&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;calledMethod&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$metadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;calledMethod&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token function&quot;&gt;file_put_contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;callmap.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;json_encode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;data&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The formatter checks for the specific error identifier. If it encounters any unexpected errors (real PHPStan errors from the analysed code), it falls back to the standard table output. This is a good safety measure — you want to know if something went wrong during the analysis instead of silently producing incomplete data.&lt;/p&gt;
&lt;h3 id=&quot;the-configuration&quot; tabindex=&quot;-1&quot;&gt;The Configuration &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-configuration&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All of this is tied together with a NEON configuration file:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-diff-yaml diff-highlight&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;errorFormat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; callmap
    &lt;span class=&quot;token key atrule&quot;&gt;paths&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; src

    &lt;span class=&quot;token comment&quot;&gt;# Makes PHPStan not require a `--level` for `analyse` command.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Our rule will be the only one that runs&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;customRulesetUsed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; StellaMaris&#92;Callmap&#92;Rule

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;errorFormatter.callmap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; StellaMaris&#92;Callmap&#92;CallmapFormatter
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; StellaMaris&#92;Callmap&#92;MethodCallCollector
        &lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; phpstan.collector&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You run it like any other PHPStan analysis:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-diff-bash diff-highlight&quot;&gt;vendor/bin/phpstan analyse &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; callmap.neon&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the output is a &lt;code&gt;callmap.json&lt;/code&gt; file with every method call in your codebase:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-diff-json diff-highlight&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;callingClass&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;App&#92;&#92;Service&#92;&#92;UserService&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;callingMethod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;getUser&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;calledClass&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;App&#92;&#92;Repository&#92;&#92;UserRepository&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token property&quot;&gt;&quot;calledMethod&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;find&quot;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;a-more-complex-example%3A-extracting-error-identifiers&quot; tabindex=&quot;-1&quot;&gt;A more complex example: Extracting error identifiers &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#a-more-complex-example%3A-extracting-error-identifiers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I use this same pattern myself to &lt;a href=&quot;/error-identifiers&quot;&gt;generate the error identifier documentation&lt;/a&gt; on this website. Every PHPStan error has a &lt;a href=&quot;/blog/phpstan-1-11-errors-identifiers-phpstan-pro-reboot&quot;&gt;unique identifier&lt;/a&gt; like &lt;code&gt;argument.type&lt;/code&gt; or &lt;code&gt;deadCode.unreachable&lt;/code&gt;. The &lt;a href=&quot;https://github.com/phpstan/phpstan/tree/2.2.x/identifier-extractor&quot;&gt;identifier extractor&lt;/a&gt; scans the PHPStan source code and all 1st-party extensions to find where these identifiers are defined.&lt;/p&gt;
&lt;p&gt;It’s more complex than the callmap example because it uses four different collectors, each targeting a different code pattern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;RuleErrorBuilderCollector&lt;/code&gt; — finds calls to &lt;code&gt;RuleErrorBuilder::message()-&amp;gt;identifier(&#39;...&#39;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ErrorWithIdentifierCollector&lt;/code&gt; — finds calls to &lt;code&gt;Error::withIdentifier(&#39;...&#39;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RestrictedUsageCollector&lt;/code&gt; — finds calls to &lt;code&gt;RestrictedUsage::create()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RuleConstructorParameterCollector&lt;/code&gt; — maps rule-to-rule injection dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last one is interesting — some rules delegate error reporting to helper classes. The extractor traces these dependency chains to figure out which top-level rule ultimately emits a given identifier.&lt;/p&gt;
&lt;p&gt;The output is a JSON file that gets &lt;a href=&quot;https://github.com/phpstan/phpstan/tree/2.2.x/identifier-extractor/merge.php&quot;&gt;merged from multiple repositories&lt;/a&gt;, transformed, and used to generate the “Rules that report this error” section you can see at the bottom of each &lt;a href=&quot;/error-identifiers/argument.type&quot;&gt;error identifier detail page&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;build-your-own&quot; tabindex=&quot;-1&quot;&gt;Build your own &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#build-your-own&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to extract data from your codebase, the pattern is always the same:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write one or more &lt;a href=&quot;/developing-extensions/collectors&quot;&gt;collectors&lt;/a&gt; that implement &lt;code&gt;PHPStan&#92;Collectors&#92;Collector&lt;/code&gt; and gather the data you’re interested in.&lt;/li&gt;
&lt;li&gt;Write a &lt;a href=&quot;/developing-extensions/rules&quot;&gt;rule&lt;/a&gt; for &lt;code&gt;CollectedDataNode&lt;/code&gt; that packages collected data as error metadata.&lt;/li&gt;
&lt;li&gt;Write a custom &lt;a href=&quot;/developing-extensions/error-formatters&quot;&gt;error formatter&lt;/a&gt; that reads the metadata and produces the output you need.&lt;/li&gt;
&lt;li&gt;Create a NEON configuration file with &lt;code&gt;customRulesetUsed: true&lt;/code&gt; and your custom &lt;code&gt;errorFormat&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The possibilities are endless. You could generate dependency graphs between namespaces, find all usages of a deprecated API, catalogue all database queries, or map out event listener registrations. Anything PHPStan can see through its type inference and reflection, you can extract.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider supporting further development of PHPStan&lt;/strong&gt;&lt;/a&gt;. I’d really appreciate it!&lt;/p&gt;
</content>
		</entry>
	
		
		<entry>
			<title>Why Array String Keys Are Not Type-Safe in PHP</title>
			<link href="https://phpstan.org/blog/why-array-string-keys-are-not-type-safe"/>
			<updated>2026-03-19T00:00:00Z</updated>
			<id>https://phpstan.org/blog/why-array-string-keys-are-not-type-safe</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;PHP silently casts array keys. This is one of the oldest and most well-known quirks in the language. But I don’t think its consequences for type safety have been fully appreciated — until now.&lt;/p&gt;
&lt;h2 id=&quot;the-casting-rules&quot; tabindex=&quot;-1&quot;&gt;The casting rules &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-casting-rules&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.php.net/manual/en/language.types.array.php&quot;&gt;PHP manual&lt;/a&gt; lists the following automatic key casts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Strings&lt;/strong&gt; containing valid decimal integers are cast to &lt;code&gt;int&lt;/code&gt;. The key &lt;code&gt;&amp;quot;8&amp;quot;&lt;/code&gt; is stored as &lt;code&gt;8&lt;/code&gt;. But &lt;code&gt;&amp;quot;08&amp;quot;&lt;/code&gt; stays as a string because it’s not a valid decimal integer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Floats&lt;/strong&gt; are truncated to &lt;code&gt;int&lt;/code&gt;. The key &lt;code&gt;8.7&lt;/code&gt; becomes &lt;code&gt;8&lt;/code&gt;. Deprecated since PHP 8.1.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bools&lt;/strong&gt; are cast to &lt;code&gt;int&lt;/code&gt;. &lt;code&gt;true&lt;/code&gt; becomes &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt; becomes &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Null&lt;/strong&gt; becomes the empty string &lt;code&gt;&amp;quot;&amp;quot;&lt;/code&gt;. Deprecated since PHP 8.5.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The classic demonstration:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$array&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;    &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;a&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;1&#39;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;b&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token number&quot;&gt;1.5&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;c&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;var_dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// int(1)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Four entries, four different literal types — but PHP treats them all as the same key &lt;code&gt;1&lt;/code&gt;. Only the last value survives.&lt;/p&gt;
&lt;p&gt;This isn’t just a quirky language feature. It breaks type safety in ways that static analysis has historically been unable to catch.&lt;/p&gt;
&lt;h2 id=&quot;the-strict_types-trap&quot; tabindex=&quot;-1&quot;&gt;The strict_types trap &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-strict_types-trap&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Consider this innocent-looking code:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strict_types &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** @param array&amp;lt;string, mixed&gt; $data */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;processKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan sees &lt;code&gt;$data&lt;/code&gt; typed as &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt;, so &lt;code&gt;$key&lt;/code&gt; must be &lt;code&gt;string&lt;/code&gt;. No errors reported. The code looks perfectly safe.&lt;/p&gt;
&lt;p&gt;But at runtime, if &lt;code&gt;$data&lt;/code&gt; was built with a key like &lt;code&gt;&#39;123&#39;&lt;/code&gt;, PHP stored it as &lt;code&gt;int(123)&lt;/code&gt;. When the &lt;code&gt;foreach&lt;/code&gt; reaches that entry, &lt;code&gt;$key&lt;/code&gt; is &lt;code&gt;int&lt;/code&gt;, not &lt;code&gt;string&lt;/code&gt;. With &lt;code&gt;strict_types&lt;/code&gt; enabled, &lt;code&gt;processKey($key)&lt;/code&gt; throws a &lt;code&gt;TypeError&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;PHPStan told you everything was fine. PHP disagrees. The type annotation &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt; promises something that PHP arrays simply cannot guarantee.&lt;/p&gt;
&lt;h2 id=&quot;array_merge-re-indexes-your-%E2%80%9Cstring%E2%80%9D-keys&quot; tabindex=&quot;-1&quot;&gt;array_merge re-indexes your “string” keys &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#array_merge-re-indexes-your-%E2%80%9Cstring%E2%80%9D-keys&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;array_merge()&lt;/code&gt; has different behaviour for integer keys and string keys: integer keys get re-indexed starting from &lt;code&gt;0&lt;/code&gt;, while string keys are preserved. When your string keys get silently cast to integers, you get re-indexing you didn’t ask for:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;100&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;foo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Stored as [100 =&gt; &#39;foo&#39;]&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;200&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;bar&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Stored as [200 =&gt; &#39;bar&#39;]&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$merged&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_merge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Result: [0 =&gt; &#39;foo&#39;, 1 =&gt; &#39;bar&#39;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Not [&#39;100&#39; =&gt; &#39;foo&#39;, &#39;200&#39; =&gt; &#39;bar&#39;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The developer wrote string keys. They got integer keys. And &lt;code&gt;array_merge&lt;/code&gt; silently re-indexed them.&lt;/p&gt;
&lt;h2 id=&quot;strict-key-lookups-fail&quot; tabindex=&quot;-1&quot;&gt;Strict key lookups fail &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#strict-key-lookups-fail&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you use &lt;code&gt;array_keys()&lt;/code&gt; and then search with strict comparison, you’ll be surprised:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;123&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;value&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$keys&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// $keys is [123] — an integer!&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;in_array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;123&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// false!&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Strict comparison: &#39;123&#39; !== 123&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You put in &lt;code&gt;&#39;123&#39;&lt;/code&gt; as a string key, you search for &lt;code&gt;&#39;123&#39;&lt;/code&gt; as a string — and it’s not found. Because PHP silently changed the key’s type behind your back.&lt;/p&gt;
&lt;h2 id=&quot;a-real-world-example&quot; tabindex=&quot;-1&quot;&gt;A real-world example &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#a-real-world-example&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagine working with postal codes:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @return array{&#39;1234aa&#39;, &#39;2345bb&#39;} */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getPostals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;1234aa&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;2345bb&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$pc4Set&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getPostals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$postal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$pc4Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$postal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$pc4&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$pc4Set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;dumpType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$pc4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 1234|2345 (int, not string!)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The developer extracted the first 4 characters of each postal code using &lt;code&gt;substr()&lt;/code&gt; — a function that returns a &lt;code&gt;string&lt;/code&gt;. But because those strings happen to be &lt;code&gt;&#39;1234&#39;&lt;/code&gt; and &lt;code&gt;&#39;2345&#39;&lt;/code&gt;, PHP casts them to integers when they’re used as array keys. The resulting &lt;code&gt;array_keys()&lt;/code&gt; returns integers, not strings.&lt;/p&gt;
&lt;h2 id=&quot;what-phpstan-already-does&quot; tabindex=&quot;-1&quot;&gt;What PHPStan already does &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#what-phpstan-already-does&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve been laying the groundwork for solving this problem.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;/try&quot;&gt;online playground&lt;/a&gt; already notifies you about key casts. When you write a literal array or access an array with a key that will be cast, PHPStan tells you:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Key ‘1234’ (string) will be cast to 1234 (int) in the array access.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;/r/188f1e7b-a708-4f8f-b483-7b812d4ab09c&quot;&gt;See this example in the playground »&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Additionally, the &lt;a href=&quot;/config-reference#reportnonintstringarraykey&quot;&gt;&lt;code&gt;reportNonIntStringArrayKey&lt;/code&gt;&lt;/a&gt; option (available since PHPStan 2.1.39) can report when non-int/non-string types like booleans, floats, or &lt;code&gt;null&lt;/code&gt; are used as array keys:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-diff-yaml diff-highlight&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;reportNonIntStringArrayKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$array&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;value&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Reported: Invalid array key type true.&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;value&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// Reported: Invalid array key type float.&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;value&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Reported: Invalid array key type null.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These features catch explicit misuses of non-int/non-string types as keys. But they don’t solve the fundamental problem: any &lt;code&gt;string&lt;/code&gt; might be a decimal integer string, and once it’s used as an array key, it silently becomes an &lt;code&gt;int&lt;/code&gt;. PHPStan couldn’t do anything about this — until now.&lt;/p&gt;
&lt;h2 id=&quot;phpstan-2.2.0%3A-non-decimal-int-string&quot; tabindex=&quot;-1&quot;&gt;PHPStan 2.2.0: non-decimal-int-string &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#phpstan-2.2.0%3A-non-decimal-int-string&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHPStan 2.2.0 introduces two new types: &lt;code&gt;non-decimal-int-string&lt;/code&gt; and &lt;code&gt;decimal-int-string&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;em&gt;decimal integer string&lt;/em&gt; is a string that PHP considers a valid decimal integer and would cast to &lt;code&gt;int&lt;/code&gt; when used as an array key — like &lt;code&gt;&#39;0&#39;&lt;/code&gt;, &lt;code&gt;&#39;123&#39;&lt;/code&gt;, or &lt;code&gt;&#39;42&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;non-decimal-int-string&lt;/code&gt; is any string that is &lt;em&gt;not&lt;/em&gt; one of these — like &lt;code&gt;&#39;hello&#39;&lt;/code&gt;, &lt;code&gt;&#39;08&#39;&lt;/code&gt;, &lt;code&gt;&#39;+1&#39;&lt;/code&gt;, or &lt;code&gt;&#39;1.5&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The key insight: if your array keys are &lt;code&gt;non-decimal-int-string&lt;/code&gt;, PHP will never cast them. They will always remain strings. This means &lt;code&gt;array&amp;lt;non-decimal-int-string, mixed&amp;gt;&lt;/code&gt; is genuinely type-safe:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strict_types &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** @param array&amp;lt;non-decimal-int-string, mixed&gt; $data */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;processData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;processKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Always safe — $key is guaranteed to be string&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No more &lt;code&gt;TypeError&lt;/code&gt; at runtime. No more surprise re-indexing by &lt;code&gt;array_merge&lt;/code&gt;. No more failed strict key lookups. The type system finally tells the truth about what your array keys actually are.&lt;/p&gt;
&lt;p&gt;You can start using &lt;code&gt;non-decimal-int-string&lt;/code&gt; in your type annotations today. It makes sense anywhere you need array keys that are guaranteed to stay as strings.&lt;/p&gt;
&lt;p&gt;And if you have a plain &lt;code&gt;string&lt;/code&gt; that you want to narrow down to one of these types, PHPStan understands an &lt;code&gt;int&lt;/code&gt; round-trip check:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// $x is decimal-int-string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// $x is non-decimal-int-string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;reportunsafearraystringkeycasting&quot; tabindex=&quot;-1&quot;&gt;reportUnsafeArrayStringKeyCasting &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#reportunsafearraystringkeycasting&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;But you shouldn’t have to rewrite all your &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt; annotations by hand. That’s where the new &lt;a href=&quot;/config-reference#reportunsafearraystringkeycasting&quot;&gt;&lt;code&gt;reportUnsafeArrayStringKeyCasting&lt;/code&gt;&lt;/a&gt; config parameter comes in. It has three values:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;null&lt;/code&gt;&lt;/strong&gt; (default) — behaviour as it is today. No additional checks. &lt;a href=&quot;/r/dcbe6433-b2bb-4c1e-b378-2d61190ff253&quot;&gt;Playground example »&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;&#39;detect&#39;&lt;/code&gt;&lt;/strong&gt; — typehinted &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt; accepts other &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt;, but when you get a key out of it, the key type will be &lt;code&gt;int|non-decimal-int-string&lt;/code&gt; instead of &lt;code&gt;string&lt;/code&gt;. This reveals the potential issues in your code without requiring you to change any type annotations. &lt;a href=&quot;/r/170d5184-42cd-4ad1-8cc7-c10d83d0f967&quot;&gt;Playground example »&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;&#39;prevent&#39;&lt;/code&gt;&lt;/strong&gt; — typehinted &lt;code&gt;array&amp;lt;string, mixed&amp;gt;&lt;/code&gt; is essentially treated as &lt;code&gt;array&amp;lt;non-decimal-int-string, mixed&amp;gt;&lt;/code&gt;. It will only accept another array with safe string keys. Additionally, any &lt;code&gt;string&lt;/code&gt; being used as an array key is correctly narrowed to &lt;code&gt;int|non-decimal-int-string&lt;/code&gt;. This is the strictest mode and gives you the most protection. &lt;a href=&quot;/r/c05a336c-727e-44ae-81d8-1e63bfaac563&quot;&gt;Playground example »&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-diff-yaml diff-highlight&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;reportUnsafeArrayStringKeyCasting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; detect &lt;span class=&quot;token comment&quot;&gt;# or: prevent&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’d recommend starting with &lt;code&gt;detect&lt;/code&gt; to see how your codebase is affected, and then moving to &lt;code&gt;prevent&lt;/code&gt; once you’ve addressed the findings.&lt;/p&gt;
&lt;p&gt;Please note this is highly experimental and things can change as I adjust and make the types more practical and useful.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider supporting further development of PHPStan&lt;/strong&gt;&lt;/a&gt;. I’d really appreciate it!&lt;/p&gt;
</content>
		</entry>
	
		
		<entry>
			<title>PHPStan fully supports PHP 8.5!</title>
			<link href="https://phpstan.org/blog/phpstan-fully-supports-php-8-5"/>
			<updated>2026-02-13T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-fully-supports-php-8-5</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;PHP 8.5 has been out for a while now, and so has PHPStan’s support for it. &lt;a href=&quot;https://github.com/phpstan/phpstan/releases/tag/2.1.32&quot;&gt;PHPStan 2.1.32&lt;/a&gt;, released back in November 2025, shipped with over 17 PHP 8.5-related changes. It was a massive community effort with many contributors. But I haven’t had the chance to sit down and write about all the interesting implementation details until now. It’s bigger than how it might seem on the first sight.&lt;/p&gt;
&lt;h2 id=&quot;%23%5Bnodiscard%5D-attribute&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;#[NoDiscard]&lt;/code&gt; attribute &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#%23%5Bnodiscard%5D-attribute&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is the feature I’m most excited about. It feels like it was made for static analysis, even though it’s really a runtime feature.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://wiki.php.net/rfc/marking_return_value_as_important&quot;&gt;&lt;code&gt;#[NoDiscard]&lt;/code&gt;&lt;/a&gt; lets you mark functions where ignoring the return value is a likely bug. Call such a function without doing anything with the result and PHP gives you a warning.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#[&lt;/span&gt;&lt;span class=&quot;token attribute-content&quot;&gt;&#92;&lt;span class=&quot;token attribute-class-name class-name&quot;&gt;NoDiscard&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;createDatabaseBackup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$backupPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// You probably wanted to do something with the path!&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;createDatabaseBackup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan reports: &lt;code&gt;Call to function createDatabaseBackup() on a separate line discards return value.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This is great for API design. Think about immutable objects where methods like &lt;code&gt;withHeader()&lt;/code&gt; or &lt;code&gt;withStatus()&lt;/code&gt; return a new instance — calling them without using the result is always a mistake. I’ve seen this bug in real codebases way more times than I’d like to admit.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;#[NoDiscard]&lt;/code&gt; errors are &lt;strong&gt;non-ignorable&lt;/strong&gt; in PHPStan. The attribute is something you put in your code on purpose, so it doesn’t really make sense to then ignore the errors it produces. If you don’t want the errors, just remove the attribute.&lt;/p&gt;
&lt;h3 id=&quot;the-(void)-cast&quot; tabindex=&quot;-1&quot;&gt;The &lt;code&gt;(void)&lt;/code&gt; cast &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-(void)-cast&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PHP 8.5 also introduces a &lt;code&gt;(void)&lt;/code&gt; cast to explicitly say “I know this returns something, and I don’t care”:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;createDatabaseBackup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// No warning&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But here’s a gotcha: you can’t use &lt;code&gt;(void)&lt;/code&gt; inside other expressions:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error!&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;var_dump&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;someFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan reports: &lt;code&gt;The (void) cast cannot be used within an expression.&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;phpstan-goes-a-step-further&quot; tabindex=&quot;-1&quot;&gt;PHPStan goes a step further &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#phpstan-goes-a-step-further&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;PHPStan doesn’t just warn about discarding &lt;code&gt;#[NoDiscard]&lt;/code&gt; return values — it also reports &lt;strong&gt;unnecessary&lt;/strong&gt; &lt;code&gt;(void)&lt;/code&gt; casts:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;canDiscard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;canDiscard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// PHPStan reports this!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan reports: &lt;code&gt;Call to function canDiscard() in (void) cast but function allows discarding return value.&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I always try to clamp new language features from both sides — if you forget to use the return value, that’s an error. But if you use &lt;code&gt;(void)&lt;/code&gt; where there’s nothing to suppress, that’s also an error. This way you can’t go wrong either way.&lt;/p&gt;
&lt;h3 id=&quot;interaction-with-the-pipe-operator&quot; tabindex=&quot;-1&quot;&gt;Interaction with the pipe operator &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#interaction-with-the-pipe-operator&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;NoDiscard&lt;/code&gt; rules also handle pipe operator expressions. When they encounter a &lt;code&gt;Node&#92;Expr&#92;BinaryOp&#92;Pipe&lt;/code&gt;, they unwrap the right-hand side to get at the actual function being called:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;withSideEffects&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Error: return value discarded&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Big thanks to &lt;a href=&quot;https://github.com/DanielEScherzer&quot;&gt;Daniel Scherzer&lt;/a&gt; for the &lt;a href=&quot;https://github.com/phpstan/phpstan-src/pull/4253&quot;&gt;initial &lt;code&gt;#[NoDiscard]&lt;/code&gt; implementation&lt;/a&gt;!&lt;/p&gt;
&lt;h2 id=&quot;pipe-operator&quot; tabindex=&quot;-1&quot;&gt;Pipe operator &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#pipe-operator&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The pipe operator &lt;code&gt;|&amp;gt;&lt;/code&gt; passes the left-hand side as the sole argument to a callable on the right-hand side:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strtolower&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;strlen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Instead of: strlen(strtolower(trim(&quot;Hello World&quot;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What’s interesting is that most of PHPStan doesn’t actually see the pipe operator at all. The AST gets rewritten on-the-fly to traditional function calls. This is pretty neat because it means all existing rules — type checking, argument validation, return type inference — just work with pipe expressions for free. No need to go and teach every single rule about a new syntax. The transformation handles &lt;code&gt;FuncCall&lt;/code&gt;, &lt;code&gt;MethodCall&lt;/code&gt;, and &lt;code&gt;StaticCall&lt;/code&gt; with first-class callables, so &lt;code&gt;$x |&amp;gt; $obj-&amp;gt;method(...)&lt;/code&gt; simply becomes &lt;code&gt;$obj-&amp;gt;method($x)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The only rule that actually operates on the &lt;code&gt;Pipe&lt;/code&gt; node directly is &lt;a href=&quot;https://github.com/phpstan/phpstan-src/blob/2.3.x/src/Rules/Operators/PipeOperatorRule.php&quot;&gt;&lt;code&gt;PipeOperatorRule&lt;/code&gt;&lt;/a&gt;, which checks that the callable on the right side doesn’t accept its parameter by reference:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// PHPStan reports:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Parameter #1 $value of callable on the right side of pipe operator&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// is passed by reference.&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One syntax gotcha: &lt;strong&gt;arrow functions need parentheses&lt;/strong&gt; when used in a pipe chain.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This doesn&#39;t work:&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// You need parentheses:&lt;/span&gt;
&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$x&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;clone-with&quot; tabindex=&quot;-1&quot;&gt;Clone with &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#clone-with&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’ve all been writing &lt;code&gt;withFoo()&lt;/code&gt; methods on immutable value objects for years. The new &lt;code&gt;clone()&lt;/code&gt; with property overrides makes this pattern way cleaner:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Response&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$statusCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$response&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;OK&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$error&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;statusCode&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;body&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Internal Server Error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan enforces the same rules for property assignments during cloning as it does for regular assignments — you can’t set &lt;code&gt;private&lt;/code&gt; properties from outside the class, you can’t assign &lt;code&gt;&#39;wrong&#39;&lt;/code&gt; to an &lt;code&gt;int&lt;/code&gt; property, and readonly properties can only be set from the appropriate scope.&lt;/p&gt;
&lt;p&gt;It also infers types from clone expressions. If you clone a generic &lt;code&gt;object&lt;/code&gt; and pass property overrides, PHPStan knows the result has those properties:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$cloned&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;clone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;foo&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;bar&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// PHPStan knows: object&amp;amp;hasProperty(bar)&amp;amp;hasProperty(foo)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;new-functions%3A-array_first()-and-array_last()&quot; tabindex=&quot;-1&quot;&gt;New functions: &lt;code&gt;array_first()&lt;/code&gt; and &lt;code&gt;array_last()&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#new-functions%3A-array_first()-and-array_last()&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Finally, PHP gets proper functions for getting the first and last element of an array. Unlike &lt;code&gt;reset()&lt;/code&gt; and &lt;code&gt;end()&lt;/code&gt;, these don’t mess with the array’s internal pointer, so they’re truly pure functions.&lt;/p&gt;
&lt;p&gt;PHPStan’s &lt;a href=&quot;https://github.com/phpstan/phpstan-src/blob/2.3.x/src/Type/Php/ArrayFirstLastDynamicReturnTypeExtension.php&quot;&gt;&lt;code&gt;ArrayFirstLastDynamicReturnTypeExtension&lt;/code&gt;&lt;/a&gt; (&lt;a href=&quot;https://github.com/phpstan/phpstan-src/pull/4499&quot;&gt;#4499&lt;/a&gt;, thanks &lt;a href=&quot;https://github.com/canvural&quot;&gt;@canvural&lt;/a&gt;!) understands them precisely:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @param non-empty-array&amp;lt;int, string&gt; $nonEmpty */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$nonEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;array_first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$strings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// string|null&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;array_first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$nonEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// string (no null - guaranteed non-empty!)&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;array_first&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;a&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;b&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;c&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &#39;a&#39;|&#39;b&#39;|&#39;c&#39;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice how PHPStan narrows the return type based on whether the array can be empty — pass a &lt;code&gt;non-empty-array&lt;/code&gt; and you’ll never get &lt;code&gt;null&lt;/code&gt; back. We liked these functions so much we started using &lt;code&gt;array_last()&lt;/code&gt; inside PHPStan itself right away (&lt;a href=&quot;https://github.com/phpstan/phpstan-src/pull/4504&quot;&gt;#4504&lt;/a&gt;, thanks &lt;a href=&quot;https://github.com/staabm&quot;&gt;@staabm&lt;/a&gt;!).&lt;/p&gt;
&lt;h2 id=&quot;deprecated-casts&quot; tabindex=&quot;-1&quot;&gt;Deprecated casts &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#deprecated-casts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHP 8.5 deprecates the non-canonical cast names:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Deprecated! Use (int)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-casting&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Deprecated! Use (bool)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;double&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Deprecated! Use (float)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;binary&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// Deprecated! Use (string)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/phpstan/phpstan-src/blob/2.3.x/src/Rules/Cast/DeprecatedCastRule.php&quot;&gt;&lt;code&gt;DeprecatedCastRule&lt;/code&gt;&lt;/a&gt; reports these so you can clean them up before upgrading.&lt;/p&gt;
&lt;h2 id=&quot;deprecated-backtick-operator&quot; tabindex=&quot;-1&quot;&gt;Deprecated backtick operator &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#deprecated-backtick-operator&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The backtick operator for &lt;code&gt;shell_exec()&lt;/code&gt; is deprecated in PHP 8.5:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$output&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string backtick-quoted-string&quot;&gt;`ls -la`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Deprecated!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pssst, I have a secret for you. If you still have backticks in your source code, and PHPStan reports them as deprecated, try re-running your analysis but add &lt;code&gt;--fix&lt;/code&gt; to the CLI options. You’ll be surprised what happens. I’ll have more to say about this later.&lt;/p&gt;
&lt;h2 id=&quot;more-php-8.5-goodies&quot; tabindex=&quot;-1&quot;&gt;More PHP 8.5 goodies &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#more-php-8.5-goodies&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here are some other notable changes PHPStan now supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;#[Override]&lt;/code&gt; on properties&lt;/strong&gt; — You can now use the &lt;code&gt;#[Override]&lt;/code&gt; attribute on properties to assert that the property overrides a parent class property. There’s a new configuration option &lt;code&gt;checkMissingOverridePropertyAttribute: true&lt;/code&gt; which can enforce them, similar to &lt;a href=&quot;/config-reference#checkmissingoverridemethodattribute&quot;&gt;the one for methods&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asymmetric visibility for static properties&lt;/strong&gt; — Previously only instance properties supported different read/write visibility. Now static properties can too.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Closure::getCurrent()&lt;/code&gt;&lt;/strong&gt; — Returns the current closure, enabling recursion without assigning to a variable first. PHPStan will know the correct signature of the closure if you fetch it with this method.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Closures and first-class callables in constant expressions&lt;/strong&gt; — Static closures can now be used in constant expressions like property defaults, constants, and attribute parameters. They must be static and can’t capture variables.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Casts in constant expressions&lt;/strong&gt; — You can now use type casts in constant initializers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Support for deprecated traits&lt;/strong&gt; — The &lt;code&gt;#[Deprecated]&lt;/code&gt; attribute now works on traits.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;FILTER_THROW_ON_FAILURE&lt;/code&gt;&lt;/strong&gt; — New flag for &lt;code&gt;filter_var()&lt;/code&gt; (&lt;a href=&quot;https://github.com/phpstan/phpstan-src/pull/4495&quot;&gt;#4495&lt;/a&gt;, thanks &lt;a href=&quot;https://github.com/canvural&quot;&gt;@canvural&lt;/a&gt;!).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Global constants support attributes&lt;/strong&gt; — Constants can now have attributes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PHP_BUILD_DATE&lt;/code&gt; type&lt;/strong&gt; (&lt;a href=&quot;https://github.com/phpstan/phpstan-src/pull/4468&quot;&gt;#4468&lt;/a&gt;, thanks &lt;a href=&quot;https://github.com/staabm&quot;&gt;@staabm&lt;/a&gt;!).&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider supporting further development of PHPStan&lt;/strong&gt;&lt;/a&gt;. I’d really appreciate it!&lt;/p&gt;
</content>
		</entry>
	
		
		<entry>
			<title>Restricted Usage Extensions - You Don&#39;t Always Need a Custom Rule</title>
			<link href="https://phpstan.org/blog/restricted-usage-extensions-you-dont-always-need-custom-rule"/>
			<updated>2025-04-27T00:00:00Z</updated>
			<id>https://phpstan.org/blog/restricted-usage-extensions-you-dont-always-need-custom-rule</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;Recently I’ve wanted to implement support for the &lt;code&gt;@internal&lt;/code&gt; PHPDoc tag. The good news was there was already a similar set of rules in &lt;a href=&quot;https://github.com/phpstan/phpstan-deprecation-rules&quot;&gt;phpstan-deprecation-rules&lt;/a&gt; for the &lt;code&gt;@deprecated&lt;/code&gt; tag. The bad news was the logic around &lt;code&gt;@deprecated&lt;/code&gt; was hardcoded there, meaning I’d have to copy and adjust all the rules.&lt;/p&gt;
&lt;p&gt;I thought there should be a better way because this is a very common use-case and not everyone should be forced to reinvent the same wheel. If people have a need for rules around attributes like &lt;a href=&quot;https://github.com/DaveLiddament/php-language-extensions#namespacevisibility&quot;&gt;&lt;code&gt;#[NamespaceVisibility]&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://github.com/DaveLiddament/php-language-extensions#friend&quot;&gt;&lt;code&gt;#[Friend]&lt;/code&gt;&lt;/a&gt;, it should not be their job to figure out all the rules needed for a comprehensive coverage of these restrictions.&lt;/p&gt;
&lt;p&gt;Take methods for example: Everyone would implement a rule for a traditional method call like &lt;code&gt;$foo-&amp;gt;doBar(1, 2, 3)&lt;/code&gt;, but only a few would remember that &lt;a href=&quot;https://wiki.php.net/rfc/first_class_callable_syntax&quot;&gt;first-class callables&lt;/a&gt; like &lt;code&gt;$foo-&amp;gt;doBar(...)&lt;/code&gt; should be covered as well.&lt;/p&gt;
&lt;p&gt;Or class names. The new extension currently &lt;a href=&quot;https://github.com/phpstan/phpstan-src/blob/ecfc5417bb2bd2f9f478f0c005b10ab1937cf149/src/Rules/ClassNameUsageLocation.php#L18-L43&quot;&gt;covers 26 locations&lt;/a&gt; where a class name can occur in PHP code or &lt;a href=&quot;https://phpstan.org/writing-php-code/phpdocs-basics&quot;&gt;PHPDocs&lt;/a&gt;. The great advantage of the extension interface is that it’s future-proof and we can call it as new places with class names appear in the PHP language or as we add new PHPDoc features.&lt;/p&gt;
&lt;p&gt;Besides taking advantage of the new extensions to implement the &lt;a href=&quot;https://github.com/phpstan/phpstan-src/tree/2.3.x/src/Rules/InternalTag&quot;&gt;&lt;code&gt;@internal&lt;/code&gt; tag rules&lt;/a&gt;, I went back to phpstan-deprecation-rules and &lt;a href=&quot;https://github.com/phpstan/phpstan-deprecation-rules/compare/96f93574dd20a293df14700e84502123103178d7...9d8e7d4e32711715ad78a1fb6ec368df9af01fdf&quot;&gt;refactored&lt;/a&gt; it with these new capabilities. It fixed small inconsistencies like missing class name check for static property fetch, wrong class names in some error messages, or reported line numbers in multi-line function signatures. With &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;bleeding edge&lt;/a&gt; enabled, it will report deprecated class names in more places.&lt;/p&gt;
&lt;p&gt;The Restricted Usage Extensions were released in &lt;a href=&quot;https://github.com/phpstan/phpstan/releases/tag/2.1.13&quot;&gt;PHPStan 2.1.13&lt;/a&gt;. Head over to the &lt;a href=&quot;/developing-extensions/restricted-usage-extensions&quot;&gt;developer guide&lt;/a&gt; to learn how to use them!&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider supporting further development of PHPStan&lt;/strong&gt;&lt;/a&gt;. I’d really appreciate it!&lt;/p&gt;
</content>
		</entry>
	
		
		<entry>
			<title>PHPStan 2.1: Support For PHP 8.4&#39;s Property Hooks, and More!</title>
			<link href="https://phpstan.org/blog/phpstan-2-1-support-for-php-8-4-property-hooks-more"/>
			<updated>2024-12-31T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-2-1-support-for-php-8-4-property-hooks-more</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;This release is a culmination of the last four months of my work. Beginning in September, I branched 2.0.x off of 1.12.x. In order to support PHP 8.4 syntax, I had to upgrade to &lt;a href=&quot;https://github.com/nikic/PHP-Parser/releases/tag/v5.0.0&quot;&gt;PHP-Parser v5&lt;/a&gt;. In order to upgrade to PHP-Parser v5, I had to release PHPStan 2.0. &lt;a href=&quot;/blog/phpstan-2-0-released-level-10-elephpants&quot;&gt;Which I did on November 11th&lt;/a&gt;, alongside the cute and also long-awaited PHPStan elephpant. PHPStan’s fans ordered 750 of those! They will be manufactured in China and sent on a ship to Europe which is going to take some time. I hope they’re going to find themselves in the hands of their happy owners in May-June 2025, as promised in the order confirmation emails.&lt;/p&gt;
&lt;p&gt;Today’s &lt;a href=&quot;https://github.com/phpstan/phpstan/releases/tag/2.1.0&quot;&gt;PHPStan 2.1&lt;/a&gt; brings full understanding of flagship PHP 8.4 features like &lt;a href=&quot;https://wiki.php.net/rfc/property-hooks&quot;&gt;property hooks&lt;/a&gt;, &lt;a href=&quot;https://wiki.php.net/rfc/asymmetric-visibility-v2&quot;&gt;asymmetric visibility&lt;/a&gt;, and the &lt;a href=&quot;https://wiki.php.net/rfc/deprecated_attribute&quot;&gt;&lt;code&gt;#[Deprecated]&lt;/code&gt; attribute&lt;/a&gt;. As usual I’m going to describe what considerations went into supporting these language features in PHPStan.&lt;/p&gt;
&lt;h2 id=&quot;property-hooks&quot; tabindex=&quot;-1&quot;&gt;Property hooks &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#property-hooks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a massive and complex new language feature which is obvious just by looking at the length of &lt;a href=&quot;https://wiki.php.net/rfc/property-hooks&quot;&gt;the RFC&lt;/a&gt;. After reading it I wrote down about 70-80 todos about what should be done in PHPStan’s codebase in order to properly support it 😅 Out of those there are still &lt;a href=&quot;https://github.com/phpstan/phpstan/issues/12336&quot;&gt;around 32 todos left&lt;/a&gt;. They are not necessary enough to be included in this initial release, but it’d be nice to have them.&lt;/p&gt;
&lt;p&gt;In short, property hooks let you intercept reading and writing properties with your own code. They also allow properties to be declared on interfaces for the first time in PHP. You can have virtual properties without a backing value stored in the object. And last but not least, hook bodies can be written with either long or short syntax.&lt;/p&gt;
&lt;p&gt;Property hooks are very similar to class methods, but they are also their own thing. Which means I had to adjust or replicate a lot of existing rules around methods:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/r/041c903d-87c4-4de1-97aa-12e5c80975e5&quot;&gt;Missing return&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/r/1d82e9b9-0e10-4b3b-a444-f5ad65523b48&quot;&gt;Returned type&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/r/e5d7d8c8-4133-4f3a-b003-7c877931d152&quot;&gt;Existence of parameter types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/r/0f0103a7-6226-4dbb-894b-3462b831ab8c&quot;&gt;Compatibility of PHPDocs with parameter types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/r/611182a8-ac7f-4155-ab1a-d7553511282e&quot;&gt;Syntax errors in PHPDocs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/r/02f64b10-6579-4cbc-b1b8-939a32d89f13&quot;&gt;Unknown &lt;code&gt;@phpstan-&lt;/code&gt; tags in PHPDocs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/bring-your-exceptions-under-control&quot;&gt;Checked exceptions thrown in the body must either be handled or documented with &lt;code&gt;@throws&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/r/b721ee1f-d2df-4d28-b84c-fb072f19b97a&quot;&gt;Rule for unused private properties must ignore property access inside hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/r/7083a3bb-fc8b-4e2b-aa68-722a87e3a6e6&quot;&gt;Attributes above property hooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, there are new special rules for property hooks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Backing value of non-virtual property &lt;a href=&quot;/r/386f6fa6-2c6e-4e22-818f-47d35fa1ee28&quot;&gt;must be read&lt;/a&gt; in get hook&lt;/li&gt;
&lt;li&gt;Backing value of non-virtual property &lt;a href=&quot;/r/ce9480ce-fdf1-4ff7-9377-105ca7fa4313&quot;&gt;must be always assigned&lt;/a&gt; in set hook&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Set hook parameter type can be wider than the actual property type so PHPStan also has to be aware of the types it &lt;a href=&quot;/r/5b12238d-8206-4194-a30c-19ad4071bbf9&quot;&gt;allows to assign to properties in and out of hooks&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		get &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
		&lt;span class=&quot;token function&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
			&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// string not allowed&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;10&#39;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;foo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// string allowed&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Property hooks also bring properties that &lt;a href=&quot;/r/2494f530-17c0-4da7-8d91-263d171766a9&quot;&gt;can only be read, or can only be written&lt;/a&gt;, to the language:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; get&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$j&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; set&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// invalid&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// invalid	&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And my final remark is that access to properties can now be &lt;a href=&quot;/r/39b8d6a8-99db-4bfc-a99b-9af4b214abee&quot;&gt;expected to throw any exception&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Foo&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$i&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; get&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Foo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$f&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyCustomException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// dead catch on PHP 8.3-, but not on 8.4+&lt;/span&gt;
		
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even non-hooked properties are subject to this, because they can be overwritten in a subclass with a hook. If you want to persuade PHPStan that access to your properties cannot throw exceptions, make them &lt;code&gt;private&lt;/code&gt; or &lt;code&gt;final&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Hooks can be documented with &lt;code&gt;@throws&lt;/code&gt; PHPDoc tag which &lt;a href=&quot;/blog/precise-try-catch-finally-analysis&quot;&gt;improves analysis of try-catch blocks&lt;/a&gt;, same as with functions and methods.&lt;/p&gt;
&lt;p&gt;I also posted a &lt;a href=&quot;https://github.com/phpstan/phpstan/discussions/12337&quot;&gt;community update&lt;/a&gt; which goes into depth what you should do if you want to support property hooks in your custom rules.&lt;/p&gt;
&lt;p&gt;A huge thanks to &lt;a href=&quot;https://github.com/kukulich&quot;&gt;Jarda Hanslík&lt;/a&gt; for &lt;a href=&quot;https://github.com/Roave/BetterReflection/pull/1462&quot;&gt;initial property hooks support&lt;/a&gt; in BetterReflection, and for &lt;a href=&quot;https://github.com/Roave/BetterReflection/pull/1465&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;https://github.com/Roave/BetterReflection/pull/1466&quot;&gt;many&lt;/a&gt; &lt;a href=&quot;https://github.com/Roave/BetterReflection/pull/1470&quot;&gt;subsequent&lt;/a&gt; &lt;a href=&quot;https://github.com/Roave/BetterReflection/pull/1471&quot;&gt;fixes&lt;/a&gt; for bugs I found 😅&lt;/p&gt;
&lt;p&gt;And also a huge thanks to &lt;a href=&quot;https://github.com/nikic&quot;&gt;Nikita Popov&lt;/a&gt;, not just for his work on PHP-Parser in general, but also for merging a &lt;a href=&quot;https://github.com/nikic/PHP-Parser/pull/1049&quot;&gt;couple&lt;/a&gt; of &lt;a href=&quot;https://github.com/nikic/PHP-Parser/pull/1051&quot;&gt;fixes&lt;/a&gt; I made, and also for fixing a &lt;a href=&quot;https://github.com/nikic/PHP-Parser/issues/1053&quot;&gt;couple&lt;/a&gt; of other &lt;a href=&quot;https://github.com/nikic/PHP-Parser/issues/1050&quot;&gt;issues&lt;/a&gt; I reported.&lt;/p&gt;
&lt;h2 id=&quot;asymmetric-visibility&quot; tabindex=&quot;-1&quot;&gt;Asymmetric visibility &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#asymmetric-visibility&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHP now allows for different properties visibility when reading then and when assigning them. So property can for example be publicly readable, but only privately writable.&lt;/p&gt;
&lt;p&gt;This feature is much &lt;a href=&quot;https://wiki.php.net/rfc/asymmetric-visibility-v2&quot;&gt;easier to grasp&lt;/a&gt; than property hooks, but also comes with a few gotchas.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Property that’s public-readable or protected-readable, but only privately writable, is &lt;a href=&quot;/r/eb32ab7b-1f3d-4abf-898a-b22523e88e8c&quot;&gt;implicitly final and cannot be overridden&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Acquiring reference to a property follows write visibility, not read visibility, so I added &lt;a href=&quot;/r/add82dc4-8cd2-4d5a-bed4-05f8d51e8cfa&quot;&gt;a new rule for that&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Similarly to property hooks, I also have a few nice-to-have todos &lt;a href=&quot;https://github.com/phpstan/phpstan/issues/12347&quot;&gt;left for later&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;%23%5Bdeprecated%5D-attribute&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;#[Deprecated]&lt;/code&gt; attribute &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#%23%5Bdeprecated%5D-attribute&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://wiki.php.net/rfc/deprecated_attribute&quot;&gt;This attribute&lt;/a&gt; allows the PHP engine &lt;a href=&quot;https://3v4l.org/MEJTq&quot;&gt;trigger deprecated warnings&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I suspect it’s not that useful in practice because it’s allowed only above functions, methods, and class constants. Most notably, it can’t be used to mark entire classes deprecated. It also does not work for properties. This is something that PHP could address in the future.&lt;/p&gt;
&lt;p&gt;Nevertheless, you can use it in PHPStan 2.1 in tandem with &lt;a href=&quot;https://github.com/phpstan/phpstan-deprecation-rules&quot;&gt;phpstan-deprecation-rules&lt;/a&gt; to mark deprecated code and have it reported when used.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider sponsoring&lt;/strong&gt; further development of PHPStan on GitHub Sponsors and also &lt;strong&gt;subscribe to PHPStan Pro&lt;/strong&gt;&lt;/a&gt;! I’d really appreciate it!&lt;/p&gt;
</content>
		</entry>
	
		
		<entry>
			<title>Upgrading from PHPStan 1.x to 2.0</title>
			<link href="https://phpstan.org/blog/upgrading-from-phpstan-1-to-2"/>
			<updated>2024-11-11T00:00:00Z</updated>
			<id>https://phpstan.org/blog/upgrading-from-phpstan-1-to-2</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;This is the upgrading guide for &lt;a href=&quot;/blog/phpstan-2-0-released-level-10-elephpants&quot;&gt;PHPStan 2.0&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;php-version-requirements&quot; tabindex=&quot;-1&quot;&gt;PHP version requirements &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#php-version-requirements&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHPStan now requires PHP 7.4 or newer to run.&lt;/p&gt;
&lt;h2 id=&quot;upgrading-guide-for-end-users&quot; tabindex=&quot;-1&quot;&gt;Upgrading guide for end users &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#upgrading-guide-for-end-users&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The best way to get ready for upgrade to PHPStan 2.0 is to update to the &lt;strong&gt;latest PHPStan 1.12 release&lt;/strong&gt;
and enable &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;&lt;strong&gt;Bleeding Edge&lt;/strong&gt;&lt;/a&gt;. This will enable the new rules and behaviours that 2.0 turns on for all users.&lt;/p&gt;
&lt;p&gt;Also make sure to install and enable &lt;a href=&quot;https://github.com/phpstan/phpstan-deprecation-rules&quot;&gt;&lt;code&gt;phpstan/phpstan-deprecation-rules&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you get to a green build with no deprecations showed on latest PHPStan 1.12.x with Bleeding Edge enabled, you can update all your related PHPStan dependencies to 2.0 in &lt;code&gt;composer.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-diff-json diff-highlight&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;require-dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan-deprecation-rules&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan-doctrine&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan-nette&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan-phpunit&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan-strict-rules&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan-symfony&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;phpstan/phpstan-webmozart-assert&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    ...
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t forget to update &lt;a href=&quot;/user-guide/extension-library&quot;&gt;3rd party PHPStan extensions&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;After changing your &lt;code&gt;composer.json&lt;/code&gt;, run &lt;code&gt;composer update &#39;phpstan/*&#39; -W&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It’s up to you whether you go through the new reported errors or if you just put them all to the &lt;a href=&quot;/user-guide/baseline&quot;&gt;baseline&lt;/a&gt; ;) Everyone who’s on PHPStan 1.12 should be able to upgrade to PHPStan 2.0.&lt;/p&gt;
&lt;h3 id=&quot;noteworthy-changes-to-code-analysis&quot; tabindex=&quot;-1&quot;&gt;Noteworthy changes to code analysis &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#noteworthy-changes-to-code-analysis&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/blog/enhancements-in-handling-parameters-passed-by-reference&quot;&gt;&lt;strong&gt;Enhancements in handling parameters passed by reference&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/phpstan-1-10-comes-with-lie-detector#validate-inline-phpdoc-%40var-tag-type&quot;&gt;&lt;strong&gt;Validate inline PHPDoc &lt;code&gt;@var&lt;/code&gt; tag type&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/blog/phpstan-1-9-0-with-phpdoc-asserts-list-type#list-type&quot;&gt;&lt;strong&gt;List type enforced&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Always &lt;code&gt;true&lt;/code&gt; conditions always reported&lt;/strong&gt;: previously reported only with phpstan-strict-rules, this is now always reported.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;removed-option-checkmissingiterablevaluetype&quot; tabindex=&quot;-1&quot;&gt;Removed option &lt;code&gt;checkMissingIterableValueType&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#removed-option-checkmissingiterablevaluetype&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It’s strongly recommended to add the missing array typehints.&lt;/p&gt;
&lt;p&gt;If you want to continue ignoring missing typehints from arrays, add &lt;code&gt;missingType.iterableValue&lt;/code&gt; error identifier to your &lt;code&gt;ignoreErrors&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-neon&quot;&gt;&lt;code class=&quot;language-diff-neon diff-highlight&quot;&gt;&lt;span class=&quot;token key property&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;token key property&quot;&gt;ignoreErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;
			&lt;span class=&quot;token key property&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token literal string&quot;&gt;missingType.iterableValue&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;removed-option-checkgenericclassinnongenericobjecttype&quot; tabindex=&quot;-1&quot;&gt;Removed option &lt;code&gt;checkGenericClassInNonGenericObjectType&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#removed-option-checkgenericclassinnongenericobjecttype&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It’s strongly recommended to add the missing generic typehints.&lt;/p&gt;
&lt;p&gt;If you want to continue ignoring missing typehints from generics, add &lt;code&gt;missingType.generics&lt;/code&gt; error identifier to your &lt;code&gt;ignoreErrors&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-neon&quot;&gt;&lt;code class=&quot;language-diff-neon diff-highlight&quot;&gt;&lt;span class=&quot;token key property&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;token key property&quot;&gt;ignoreErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;
			&lt;span class=&quot;token key property&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token literal string&quot;&gt;missingType.generics&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;removed-checkalwaystrue*-options&quot; tabindex=&quot;-1&quot;&gt;Removed &lt;code&gt;checkAlwaysTrue*&lt;/code&gt; options &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#removed-checkalwaystrue*-options&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These options have been removed because PHPStan now always behaves as if these were set to &lt;code&gt;true&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;checkAlwaysTrueCheckTypeFunctionCall&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;checkAlwaysTrueInstanceof&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;checkAlwaysTrueStrictComparison&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;checkAlwaysTrueLooseComparison&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;removed-option-excludes_analyse&quot; tabindex=&quot;-1&quot;&gt;Removed option &lt;code&gt;excludes_analyse&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#removed-option-excludes_analyse&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;It has been replaced with &lt;a href=&quot;/user-guide/ignoring-errors#excluding-whole-files&quot;&gt;&lt;code&gt;excludePaths&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;paths-in-excludepaths-and-ignoreerrors-have-to-be-a-valid-file-path-or-a-fnmatch-pattern&quot; tabindex=&quot;-1&quot;&gt;Paths in &lt;code&gt;excludePaths&lt;/code&gt; and &lt;code&gt;ignoreErrors&lt;/code&gt; have to be a valid file path or a fnmatch pattern &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#paths-in-excludepaths-and-ignoreerrors-have-to-be-a-valid-file-path-or-a-fnmatch-pattern&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you are excluding a file path that might not exist but you still want to have it in &lt;code&gt;excludePaths&lt;/code&gt;, append &lt;code&gt;(?)&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-neon&quot;&gt;&lt;code class=&quot;language-diff-neon diff-highlight&quot;&gt;&lt;span class=&quot;token key property&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;token key property&quot;&gt;excludePaths&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token literal string&quot;&gt;tests/*/data/*&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token literal string&quot;&gt;src/broken&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token literal string&quot;&gt;node_modules&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token literal string&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# optional path, might not exist&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have the same situation in &lt;code&gt;ignoreErrors&lt;/code&gt; (ignoring an error in a path that might not exist), use &lt;code&gt;reportUnmatchedIgnoredErrors: false&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-neon&quot;&gt;&lt;code class=&quot;language-diff-neon diff-highlight&quot;&gt;&lt;span class=&quot;token key property&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
	&lt;span class=&quot;token key property&quot;&gt;reportUnmatchedIgnoredErrors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Appending &lt;code&gt;(?)&lt;/code&gt; in &lt;code&gt;ignoreErrors&lt;/code&gt; is not supported.&lt;/p&gt;
&lt;h3 id=&quot;changes-in-1st-party-phpstan-extensions&quot; tabindex=&quot;-1&quot;&gt;Changes in 1st party PHPStan extensions &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#changes-in-1st-party-phpstan-extensions&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/phpstan/phpstan-doctrine&quot;&gt;phpstan-doctrine&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Removed config parameter &lt;code&gt;searchOtherMethodsForQueryBuilderBeginning&lt;/code&gt; (extension now behaves as when this was set to &lt;code&gt;true&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Removed config parameter &lt;code&gt;queryBuilderFastAlgorithm&lt;/code&gt; (extension now behaves as when this was set to &lt;code&gt;false&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/phpstan/phpstan-symfony&quot;&gt;phpstan-symfony&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Removed legacy options with &lt;code&gt;_&lt;/code&gt; in the name&lt;/li&gt;
&lt;li&gt;&lt;code&gt;container_xml_path&lt;/code&gt; -&amp;gt; use &lt;code&gt;containerXmlPath&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;constant_hassers&lt;/code&gt; -&amp;gt; use &lt;code&gt;constantHassers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;console_application_loader&lt;/code&gt; -&amp;gt; use &lt;code&gt;consoleApplicationLoader&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;minor-backward-compatibility-breaks&quot; tabindex=&quot;-1&quot;&gt;Minor backward compatibility breaks &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#minor-backward-compatibility-breaks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Removed unused config parameter &lt;code&gt;cache.nodesByFileCountMax&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Removed unused config parameter &lt;code&gt;memoryLimitFile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Removed unused feature toggle &lt;code&gt;disableRuntimeReflectionProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Removed unused config parameter &lt;code&gt;staticReflectionClassNamePatterns&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;fixerTmpDir&lt;/code&gt; config parameter, use &lt;code&gt;pro.tmpDir&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;tempResultCachePath&lt;/code&gt; config parameter, use &lt;code&gt;resultCachePath&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;&lt;code&gt;additionalConfigFiles&lt;/code&gt; config parameter must be a list&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;upgrading-guide-for-extension-developers&quot; tabindex=&quot;-1&quot;&gt;Upgrading guide for extension developers &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#upgrading-guide-for-extension-developers&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Please switch to PHPStan 2.0 in a new major version of your extension. It’s not feasible to try to support both PHPStan 1.x and PHPStan 2.x with the same extension code.&lt;/p&gt;
&lt;p&gt;You can definitely get closer to supporting PHPStan 2.0 without increasing major version by solving reported deprecations and other issues by analysing your extension code with PHPStan &amp;amp; phpstan-deprecation-rules &amp;amp; Bleeding Edge, but the final leap and solving backward incompatibilities should be done by requiring &lt;code&gt;&amp;quot;phpstan/phpstan&amp;quot;: &amp;quot;^2.0&amp;quot;&lt;/code&gt; in your &lt;code&gt;composer.json&lt;/code&gt;, and releasing a new major version.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;phpstan-now-uses-nikic%2Fphp-parser-v5&quot; tabindex=&quot;-1&quot;&gt;PHPStan now uses nikic/php-parser v5 &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#phpstan-now-uses-nikic%2Fphp-parser-v5&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://github.com/nikic/PHP-Parser/blob/master/UPGRADE-5.0.md&quot;&gt;UPGRADING&lt;/a&gt; guide for PHP-Parser.&lt;/p&gt;
&lt;p&gt;The most notable change is how &lt;code&gt;throw&lt;/code&gt; statement is represented. Previously, &lt;code&gt;throw&lt;/code&gt; statements like &lt;code&gt;throw $e;&lt;/code&gt; were represented using the &lt;code&gt;Stmt&#92;Throw_&lt;/code&gt; class, while uses inside other expressions (such as &lt;code&gt;$x ?? throw $e&lt;/code&gt;) used the &lt;code&gt;Expr&#92;Throw_&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Now, &lt;code&gt;throw $e;&lt;/code&gt; is represented as a &lt;code&gt;Stmt&#92;Expression&lt;/code&gt; that contains an &lt;code&gt;Expr&#92;Throw_&lt;/code&gt;. The
&lt;code&gt;Stmt&#92;Throw_&lt;/code&gt; class has been removed.&lt;/p&gt;
&lt;h3 id=&quot;phpstan-now-uses-phpstan%2Fphpdoc-parser-v2&quot; tabindex=&quot;-1&quot;&gt;PHPStan now uses phpstan/phpdoc-parser v2 &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#phpstan-now-uses-phpstan%2Fphpdoc-parser-v2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;See &lt;a href=&quot;https://github.com/phpstan/phpdoc-parser/blob/2.0.x/UPGRADING.md&quot;&gt;UPGRADING&lt;/a&gt; guide for phpstan/phpdoc-parser.&lt;/p&gt;
&lt;h3 id=&quot;returning-plain-strings-as-errors-no-longer-supported%2C-use-ruleerrorbuilder&quot; tabindex=&quot;-1&quot;&gt;Returning plain strings as errors no longer supported, use RuleErrorBuilder &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#returning-plain-strings-as-errors-no-longer-supported%2C-use-ruleerrorbuilder&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Identifiers are also required in custom rules.&lt;/p&gt;
&lt;p&gt;Learn more: &lt;a href=&quot;/blog/using-rule-error-builder&quot;&gt;Using RuleErrorBuilder to enrich reported errors in custom rules&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;My error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token class-name static-context&quot;&gt;RuleErrorBuilder&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;My error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;identifier&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;my.error&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;deprecate-various-instanceof-*type-in-favour-of-new-methods-on-type-interface&quot; tabindex=&quot;-1&quot;&gt;Deprecate various &lt;code&gt;instanceof *Type&lt;/code&gt; in favour of new methods on &lt;code&gt;Type&lt;/code&gt; interface &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#deprecate-various-instanceof-*type-in-favour-of-new-methods-on-type-interface&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Learn more: &lt;a href=&quot;/blog/why-is-instanceof-type-wrong-and-getting-deprecated&quot;&gt;Why Is instanceof *Type Wrong and Getting Deprecated?&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;removed-deprecated-parametersacceptorselector%3A%3Aselectsingle()&quot; tabindex=&quot;-1&quot;&gt;Removed deprecated &lt;code&gt;ParametersAcceptorSelector::selectSingle()&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#removed-deprecated-parametersacceptorselector%3A%3Aselectsingle()&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ParametersAcceptorSelector.html#_selectFromArgs&quot;&gt;&lt;code&gt;ParametersAcceptorSelector::selectFromArgs()&lt;/code&gt;&lt;/a&gt; instead. It should be used in most places where &lt;code&gt;selectSingle()&lt;/code&gt; was previously used, like dynamic return type extensions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$defaultReturnType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;ParametersAcceptorSelector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectSingle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$functionReflection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getVariants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getReturnType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$defaultReturnType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;ParametersAcceptorSelector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectFromArgs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$functionCall&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getArgs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$functionReflection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getVariants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getReturnType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you’re analysing function or method body itself and you’re using one of the following methods, ask for &lt;code&gt;getParameters()&lt;/code&gt; and &lt;code&gt;getReturnType()&lt;/code&gt; directly on the reflection object:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Node.InClassMethodNode.html&quot;&gt;InClassMethodNode::getMethodReflection()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Node.InFunctionNode.html&quot;&gt;InFunctionNode::getFunctionReflection()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Node.FunctionReturnStatementsNode.html&quot;&gt;FunctionReturnStatementsNode::getFunctionReflection()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Node.MethodReturnStatementsNode.html&quot;&gt;MethodReturnStatementsNode::getMethodReflection()&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.Scope.html#_getFunction&quot;&gt;Scope::getFunction()&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Before&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$function&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFunctionReflection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$returnType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;ParametersAcceptorSelector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;selectSingle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$function&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getVariants&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getReturnType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;After&lt;/strong&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$returnType&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$node&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFunctionReflection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getReturnType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;changed-typespecifier%3A%3Acreate()-and-specifiedtypes-constructor-parameters&quot; tabindex=&quot;-1&quot;&gt;Changed &lt;code&gt;TypeSpecifier::create()&lt;/code&gt; and &lt;code&gt;SpecifiedTypes&lt;/code&gt; constructor parameters &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#changed-typespecifier%3A%3Acreate()-and-specifiedtypes-constructor-parameters&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.TypeSpecifier.html#_create&quot;&gt;&lt;code&gt;PHPStan&#92;Analyser&#92;TypeSpecifier::create()&lt;/code&gt;&lt;/a&gt; now accepts (all parameters are required):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Expr $expr&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Type $type&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;TypeSpecifierContext $context&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Scope $scope&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to change &lt;code&gt;$overwrite&lt;/code&gt; or &lt;code&gt;$rootExpr&lt;/code&gt; (previous parameters also used to be accepted by this method), call &lt;code&gt;setAlwaysOverwriteTypes()&lt;/code&gt; and &lt;code&gt;setRootExpr()&lt;/code&gt; on &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html&quot;&gt;&lt;code&gt;SpecifiedTypes&lt;/code&gt;&lt;/a&gt; (object returned by &lt;code&gt;TypeSpecifier::create()&lt;/code&gt;). These methods return a new object (SpecifiedTypes is immutable).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html&quot;&gt;&lt;code&gt;SpecifiedTypes&lt;/code&gt;&lt;/a&gt; constructor now accepts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;array $sureTypes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;array $sureNotTypes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to change &lt;code&gt;$overwrite&lt;/code&gt; or &lt;code&gt;$rootExpr&lt;/code&gt; (previous parameters also used to be accepted by the constructor), call &lt;code&gt;setAlwaysOverwriteTypes()&lt;/code&gt; and &lt;code&gt;setRootExpr()&lt;/code&gt;. These methods return a new object (SpecifiedTypes is immutable).&lt;/p&gt;
&lt;h3 id=&quot;constantarraytype-no-longer-extends-arraytype&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;ConstantArrayType&lt;/code&gt; no longer extends &lt;code&gt;ArrayType&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#constantarraytype-no-longer-extends-arraytype&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;Type::getArrays()&lt;/code&gt; now returns &lt;code&gt;list&amp;lt;ArrayType|ConstantArrayType&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;$type instanceof ArrayType&lt;/code&gt; is &lt;a href=&quot;/blog/why-is-instanceof-type-wrong-and-getting-deprecated&quot;&gt;being deprecated anyway&lt;/a&gt; so the impact of this change should be minimal.&lt;/p&gt;
&lt;h3 id=&quot;changed-typespecifier%3A%3Aspecifytypesincondition()&quot; tabindex=&quot;-1&quot;&gt;Changed &lt;code&gt;TypeSpecifier::specifyTypesInCondition()&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#changed-typespecifier%3A%3Aspecifytypesincondition()&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This method now longer accepts &lt;code&gt;Expr $rootExpr&lt;/code&gt;. If you want to change it, call &lt;code&gt;setRootExpr()&lt;/code&gt; on &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.SpecifiedTypes.html&quot;&gt;&lt;code&gt;SpecifiedTypes&lt;/code&gt;&lt;/a&gt; (object returned by &lt;code&gt;TypeSpecifier::specifyTypesInCondition()&lt;/code&gt;). &lt;code&gt;setRootExpr()&lt;/code&gt; method returns a new object (SpecifiedTypes is immutable).&lt;/p&gt;
&lt;h3 id=&quot;node-attributes-parent%2C-previous%2C-next-are-no-longer-available&quot; tabindex=&quot;-1&quot;&gt;Node attributes &lt;code&gt;parent&lt;/code&gt;, &lt;code&gt;previous&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt; are no longer available &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#node-attributes-parent%2C-previous%2C-next-are-no-longer-available&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Learn more: &lt;a href=&quot;/blog/preprocessing-ast-for-custom-rules&quot;&gt;Preprocessing AST for custom rules&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;removed-config-parameter-scopeclass&quot; tabindex=&quot;-1&quot;&gt;Removed config parameter &lt;code&gt;scopeClass&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#removed-config-parameter-scopeclass&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As a replacement you can implement &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.ExpressionTypeResolverExtension.html&quot;&gt;&lt;code&gt;PHPStan&#92;Type&#92;ExpressionTypeResolverExtension&lt;/code&gt;&lt;/a&gt; interface instead and register it as a service.&lt;/p&gt;
&lt;h3 id=&quot;removed-phpstan%5Cbroker%5Cbroker&quot; tabindex=&quot;-1&quot;&gt;Removed &lt;code&gt;PHPStan&#92;Broker&#92;Broker&lt;/code&gt; &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#removed-phpstan%5Cbroker%5Cbroker&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ReflectionProvider.html&quot;&gt;&lt;code&gt;PHPStan&#92;Reflection&#92;ReflectionProvider&lt;/code&gt;&lt;/a&gt; instead.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;BrokerAwareExtension&lt;/code&gt; was also removed. Ask for &lt;code&gt;ReflectionProvider&lt;/code&gt; in the extension constructor instead.&lt;/p&gt;
&lt;p&gt;Instead of &lt;code&gt;PHPStanTestCase::createBroker()&lt;/code&gt;, call &lt;code&gt;PHPStanTestCase::createReflectionProvider()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;list-type-is-enabled-for-everyone&quot; tabindex=&quot;-1&quot;&gt;List type is enabled for everyone &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#list-type-is-enabled-for-everyone&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Removed static methods from &lt;code&gt;AccessoryArrayListType&lt;/code&gt; class:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isListTypeEnabled()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setListTypeEnabled()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;intersectWith()&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of &lt;code&gt;AccessoryArrayListType::intersectWith($type)&lt;/code&gt;, do &lt;code&gt;TypeCombinator::intersect($type, new AccessoryArrayListType())&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;minor-backward-compatibility-breaks-1&quot; tabindex=&quot;-1&quot;&gt;Minor backward compatibility breaks &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#minor-backward-compatibility-breaks-1&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Classes that were previously &lt;code&gt;@final&lt;/code&gt; were made &lt;code&gt;final&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Parameter &lt;code&gt;$callableParameters&lt;/code&gt; of &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterAnonymousFunction&quot;&gt;&lt;code&gt;MutatingScope::enterAnonymousFunction()&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.MutatingScope.html#_enterArrowFunction&quot;&gt;&lt;code&gt;enterArrowFunction()&lt;/code&gt;&lt;/a&gt; made required&lt;/li&gt;
&lt;li&gt;Parameter &lt;code&gt;StatementContext $context&lt;/code&gt; of &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Analyser.NodeScopeResolver.html#_processStmtNodes&quot;&gt;&lt;code&gt;NodeScopeResolver::processStmtNodes()&lt;/code&gt;&lt;/a&gt; made required&lt;/li&gt;
&lt;li&gt;ClassPropertiesNode - remove &lt;code&gt;$extensions&lt;/code&gt; parameter from &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Node.ClassPropertiesNode.html#_getUninitializedProperties&quot;&gt;&lt;code&gt;getUninitializedProperties()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Type::getSmallerType()&lt;/code&gt;, &lt;code&gt;Type::getSmallerOrEqualType()&lt;/code&gt;, &lt;code&gt;Type::getGreaterType()&lt;/code&gt;, &lt;code&gt;Type::getGreaterOrEqualType()&lt;/code&gt;, &lt;code&gt;Type::isSmallerThan()&lt;/code&gt;, &lt;code&gt;Type::isSmallerThanOrEqual()&lt;/code&gt; now require &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html&quot;&gt;&lt;code&gt;PhpVersion&lt;/code&gt;&lt;/a&gt; as argument.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CompoundType::isGreaterThan()&lt;/code&gt;, &lt;code&gt;CompoundType::isGreaterThanOrEqual()&lt;/code&gt; now require &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Php.PhpVersion.html&quot;&gt;&lt;code&gt;PhpVersion&lt;/code&gt;&lt;/a&gt; as argument.&lt;/li&gt;
&lt;li&gt;Removed &lt;code&gt;ReflectionProvider::supportsAnonymousClasses()&lt;/code&gt; (all reflection providers support anonymous classes)&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ArrayType::generalizeKeys()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ArrayType::count()&lt;/code&gt;, use &lt;code&gt;Type::getArraySize()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ArrayType::castToArrayKeyType()&lt;/code&gt;, &lt;code&gt;Type::toArrayKey()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;UnionType::pickTypes()&lt;/code&gt;, use &lt;code&gt;pickFromTypes()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;RegexArrayShapeMatcher::matchType()&lt;/code&gt;, use &lt;code&gt;matchExpr()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove unused &lt;code&gt;PHPStanTestCase::$useStaticReflectionProvider&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;PHPStanTestCase::getReflectors()&lt;/code&gt;, use &lt;code&gt;getReflector()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ClassReflection::getFileNameWithPhpDocs()&lt;/code&gt;, use &lt;code&gt;getFileName()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;AnalysisResult::getInternalErrors()&lt;/code&gt;, use &lt;code&gt;getInternalErrorObjects()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantReflection::getValue()&lt;/code&gt;, use &lt;code&gt;getValueExpr()&lt;/code&gt; instead. To get &lt;code&gt;Type&lt;/code&gt; from &lt;code&gt;Expr&lt;/code&gt;, use &lt;code&gt;Scope::getType()&lt;/code&gt; or &lt;code&gt;InitializerExprTypeResolver::getType()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;PropertyTag::getType()&lt;/code&gt;, use &lt;code&gt;getReadableType()&lt;/code&gt; / &lt;code&gt;getWritableType()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;GenericTypeVariableResolver&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getTemplateType&quot;&gt;&lt;code&gt;Type::getTemplateType()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Rename &lt;code&gt;Type::isClassStringType()&lt;/code&gt; to &lt;code&gt;Type::isClassString()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;Scope::isSpecified()&lt;/code&gt;, use &lt;code&gt;hasExpressionType()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::isEmpty()&lt;/code&gt;, use &lt;code&gt;isIterableAtLeastOnce()-&amp;gt;no()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::getNextAutoIndex()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Removed methods from &lt;code&gt;ConstantArrayType&lt;/code&gt; - &lt;code&gt;getFirst*Type&lt;/code&gt; and &lt;code&gt;getLast*Type&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;getFirstIterable*Type&lt;/code&gt; and &lt;code&gt;getLastIterable*Type&lt;/code&gt; instead&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::generalizeToArray()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::findTypeAndMethodName()&lt;/code&gt;, use &lt;code&gt;findTypeAndMethodNames()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::removeLast()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_popArray&quot;&gt;&lt;code&gt;Type::popArray()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::removeFirst()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_shiftArray&quot;&gt;&lt;code&gt;Type::shiftArray()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::reverse()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_reverseArray&quot;&gt;&lt;code&gt;Type::reverseArray()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::chunk()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_chunkArray&quot;&gt;&lt;code&gt;Type::chunkArray()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantArrayType::slice()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_sliceArray&quot;&gt;&lt;code&gt;Type::sliceArray()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Made &lt;code&gt;TypeUtils&lt;/code&gt; thinner by removing methods:
&lt;ul&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::getArrays()&lt;/code&gt; and &lt;code&gt;getAnyArrays()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getArrays&quot;&gt;&lt;code&gt;Type::getArrays()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::getConstantArrays()&lt;/code&gt; and &lt;code&gt;getOldConstantArrays()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantArrays&quot;&gt;&lt;code&gt;Type::getConstantArrays()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::getConstantStrings()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantStrings&quot;&gt;&lt;code&gt;Type::getConstantStrings()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::getConstantTypes()&lt;/code&gt; and &lt;code&gt;getAnyConstantTypes()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue&quot;&gt;&lt;code&gt;Type::isConstantValue()&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize&quot;&gt;&lt;code&gt;Type::generalize()&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::generalizeType()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_generalize&quot;&gt;&lt;code&gt;Type::generalize()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::getDirectClassNames()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getObjectClassNames&quot;&gt;&lt;code&gt;Type::getObjectClassNames()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::getConstantScalars()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantScalarValue&quot;&gt;&lt;code&gt;Type::isConstantScalarValue()&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getConstantScalarTypes&quot;&gt;&lt;code&gt;Type::getConstantScalarTypes()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::getEnumCaseObjects()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getEnumCases&quot;&gt;&lt;code&gt;Type::getEnumCases()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TypeUtils::containsCallable()&lt;/code&gt;, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isCallable&quot;&gt;&lt;code&gt;Type::isCallable()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Removed &lt;code&gt;Scope::doNotTreatPhpDocTypesAsCertain()&lt;/code&gt;, use &lt;code&gt;getNativeType()&lt;/code&gt; instead&lt;/li&gt;
&lt;li&gt;Parameter &lt;code&gt;$isList&lt;/code&gt; in &lt;code&gt;ConstantArrayType&lt;/code&gt; constructor can only be &lt;code&gt;TrinaryLogic&lt;/code&gt;, no longer &lt;code&gt;bool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Parameter &lt;code&gt;$nextAutoIndexes&lt;/code&gt; in &lt;code&gt;ConstantArrayType&lt;/code&gt; constructor can only be &lt;code&gt;non-empty-list&amp;lt;int&amp;gt;&lt;/code&gt;, no longer &lt;code&gt;int&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;ConstantType&lt;/code&gt; interface, use &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_isConstantValue&quot;&gt;&lt;code&gt;Type::isConstantValue()&lt;/code&gt;&lt;/a&gt; instead&lt;/li&gt;
&lt;li&gt;&lt;code&gt;acceptsNamedArguments()&lt;/code&gt; in &lt;code&gt;FunctionReflection&lt;/code&gt;, &lt;code&gt;ExtendedMethodReflection&lt;/code&gt; and &lt;code&gt;CallableParametersAcceptor&lt;/code&gt; interfaces returns &lt;code&gt;TrinaryLogic&lt;/code&gt; instead of &lt;code&gt;bool&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;FunctionReflection::isFinal()&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.Type.html#_getProperty&quot;&gt;&lt;code&gt;Type::getProperty()&lt;/code&gt;&lt;/a&gt; now returns &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Reflection.ExtendedPropertyReflection.html&quot;&gt;&lt;code&gt;ExtendedPropertyReflection&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;__set_state()&lt;/code&gt; on objects that should not be serialized in cache&lt;/li&gt;
&lt;li&gt;Parameter &lt;code&gt;$selfClass&lt;/code&gt; of &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.TypehintHelper.html#_decideTypeFromReflection&quot;&gt;&lt;code&gt;TypehintHelper::decideTypeFromReflection()&lt;/code&gt;&lt;/a&gt; no longer accepts &lt;code&gt;string&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LevelsTestCase::dataTopics()&lt;/code&gt; data provider made static&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PHPStan&#92;Node&#92;Printer&#92;Printer&lt;/code&gt; no longer autowired as &lt;code&gt;PhpParser&#92;PrettyPrinter&#92;Standard&lt;/code&gt;, use &lt;code&gt;PHPStan&#92;Node&#92;Printer&#92;Printer&lt;/code&gt; in the typehint&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;Type::acceptsWithReason()&lt;/code&gt;, &lt;code&gt;Type:accepts()&lt;/code&gt; return type changed from &lt;code&gt;TrinaryLogic&lt;/code&gt; to &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html&quot;&gt;&lt;code&gt;AcceptsResult&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;CompoundType::isAcceptedWithReasonBy()&lt;/code&gt;, &lt;code&gt;CompoundType::isAcceptedBy()&lt;/code&gt; return type changed from &lt;code&gt;TrinaryLogic&lt;/code&gt; to &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html&quot;&gt;&lt;code&gt;AcceptsResult&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;Type::isSuperTypeOfWithReason()&lt;/code&gt;, &lt;code&gt;Type:isSuperTypeOf()&lt;/code&gt; return type changed from &lt;code&gt;TrinaryLogic&lt;/code&gt; to &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html&quot;&gt;&lt;code&gt;IsSuperTypeOfResult&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;CompoundType::isSubTypeOfWithReasonBy()&lt;/code&gt;, &lt;code&gt;CompoundType::isSubTypeOf()&lt;/code&gt; return type changed from &lt;code&gt;TrinaryLogic&lt;/code&gt; to &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html&quot;&gt;&lt;code&gt;IsSuperTypeOfResult&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;TemplateType::isValidVarianceWithReason()&lt;/code&gt;, changed &lt;code&gt;TemplateType::isValidVariance()&lt;/code&gt; return type to &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.IsSuperTypeOfResult.html&quot;&gt;&lt;code&gt;IsSuperTypeOfResult&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RuleLevelHelper::accepts()&lt;/code&gt; return type changed from &lt;code&gt;bool&lt;/code&gt; to &lt;a href=&quot;https://apiref.phpstan.org/2.0.x/PHPStan.Type.AcceptsResult.html&quot;&gt;&lt;code&gt;RuleLevelHelperAcceptsResult&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Changes around &lt;code&gt;ClassConstantReflection&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;Class &lt;code&gt;ClassConstantReflection&lt;/code&gt; removed from BC promise, renamed to &lt;code&gt;RealClassConstantReflection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Interface &lt;code&gt;ConstantReflection&lt;/code&gt; renamed to &lt;code&gt;ClassConstantReflection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Added more methods around PHPDoc types and native types to the (new) &lt;code&gt;ClassConstantReflection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Interface &lt;code&gt;GlobalConstantReflection&lt;/code&gt; renamed to &lt;code&gt;ConstantReflection&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Renamed interfaces and classes from &lt;code&gt;*WithPhpDocs&lt;/code&gt; to &lt;code&gt;Extended*&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ParametersAcceptorWithPhpDocs&lt;/code&gt; -&amp;gt; &lt;code&gt;ExtendedParametersAcceptor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ParameterReflectionWithPhpDocs&lt;/code&gt; -&amp;gt; &lt;code&gt;ExtendedParameterReflection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FunctionVariantWithPhpDocs&lt;/code&gt; -&amp;gt; &lt;code&gt;ExtendedFunctionVariant&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ClassPropertyNode::getNativeType()&lt;/code&gt; return type changed from AST node to &lt;code&gt;Type|null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Class &lt;code&gt;PHPStan&#92;Node&#92;ClassMethod&lt;/code&gt; (accessible from &lt;code&gt;ClassMethodsNode&lt;/code&gt;) is no longer an AST node
&lt;ul&gt;
&lt;li&gt;Call &lt;code&gt;PHPStan&#92;Node&#92;ClassMethod::getNode()&lt;/code&gt; to access the original AST node&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
</content>
		</entry>
	
		
		<entry>
			<title>PHPStan 2.0 Released With Level 10 and Elephpants!</title>
			<link href="https://phpstan.org/blog/phpstan-2-0-released-level-10-elephpants"/>
			<updated>2024-11-11T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-2-0-released-level-10-elephpants</id>
			<content type="html" xml:base="https://phpstan.org">&lt;img src=&quot;/images/phpstan-2-0.jpg&quot; alt=&quot;PHPStan 2.0&quot; class=&quot;rounded-lg mb-8&quot;&gt;
&lt;p&gt;PHPStan 1.0 was released a little over &lt;a href=&quot;/blog/phpstan-1-0-released&quot;&gt;three years ago&lt;/a&gt;. I’m happy to report the project is thriving! We did about 176 new releases since then, implementing new features, fixing bugs, and laying the groundwork for 2.0. Yeah, we didn’t catch a break and we didn’t rest on our laurels.&lt;/p&gt;
&lt;p&gt;I’ve been looking forward to 2.0 for a long time. Everyone will finally be able to enjoy new features we’ve been working on. Some of them have already been enjoyed by &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;early adopters&lt;/a&gt; for more than two years.&lt;/p&gt;
&lt;p&gt;But code and analysis changes are not the only things being released today! PHPStan joins &lt;a href=&quot;https://elephpant.me/&quot;&gt;the family of elephpants&lt;/a&gt; with its own take on the legendary PHP mascot.&lt;/p&gt;
&lt;p class=&quot;mt-4 rounded-lg mb-8 border border-blue-500 p-4 hover:border-blue-400&quot;&gt;&lt;a href=&quot;/merch&quot;&gt;&lt;img src=&quot;/images/elephpant-trio.png&quot; alt=&quot;PHPStan Elephpant&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also order PHPStan T-shirts again, in both blue and white, and straight/fitted cut:&lt;/p&gt;
&lt;a href=&quot;/merch&quot; class=&quot;flex w-full justify-center mt-4 rounded-lg mb-8 border border-blue-500 py-2 hover:border-blue-400&quot;&gt;
	&lt;img class=&quot;aspect-[1610/913] max-w-xs w-1/2&quot; src=&quot;/images/tshirt-2024-blue-straight.jpg&quot;&gt;
	&lt;img class=&quot;aspect-[1610/913] max-w-xs w-1/2&quot; src=&quot;/images/tshirt-2024-white-fitted.jpg&quot;&gt;
&lt;/a&gt;
&lt;p&gt;We’re &lt;a href=&quot;/merch&quot;&gt;accepting orders&lt;/a&gt; for the next four weeks (until Sunday, December 8th), so don’t miss the opportunity and order the elephpant and T-shirt today! I can’t wait to see them in the wild.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;And now to the code part. The &lt;a href=&quot;https://github.com/phpstan/phpstan/releases/tag/2.0.0&quot;&gt;comprehensive release notes&lt;/a&gt; are massive, consisting of over 180 items. That’s why, for the first time ever, PHPStan 2.0 also comes with easy-to-follow &lt;a href=&quot;/blog/upgrading-from-phpstan-1-to-2&quot;&gt;upgrading guide&lt;/a&gt;, for both end users and extension developers.&lt;/p&gt;
&lt;p&gt;It’s really hard to pick and choose my favourite features and changes from 2.0, but I’ll try. Here we go:&lt;/p&gt;
&lt;h2 id=&quot;level-10&quot; tabindex=&quot;-1&quot;&gt;Level 10 &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#level-10&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;New challenge for the level max enthusiasts! Previously added level 9 acknowledges using &lt;code&gt;mixed&lt;/code&gt; in your code isn’t actually safe at all, and that you should really do something about it. But it has some blind spots and still lets some errors through. Internally it’s named &lt;code&gt;checkExplicitMixed&lt;/code&gt;. Meaning it will report errors for explicitly-typed &lt;code&gt;mixed&lt;/code&gt; values in your code.&lt;/p&gt;
&lt;p&gt;Level 6 forces you to add missing types. If you manage to skip that with the help of &lt;del&gt;cheating&lt;/del&gt;, ahem, &lt;a href=&quot;/user-guide/baseline&quot;&gt;the baseline&lt;/a&gt;, or if you call a third party code that doesn’t use a lot of return types, unknown types may be present in your code during analysis. These are implicitly-typed &lt;code&gt;mixed&lt;/code&gt;, and they will be picked up by level 10&lt;/p&gt;
&lt;h2 id=&quot;list-type&quot; tabindex=&quot;-1&quot;&gt;List type &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#list-type&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHP arrays are really powerful, but they represent several computer science concepts in a single data structure, and sometimes it’s difficult to work with that. That’s why it’s useful to narrow it down when we’re sure we only want a single concept like a list.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/List_(abstract_data_type)&quot;&gt;List&lt;/a&gt; in PHPStan is an array with sequential integer keys starting at 0 and with no gaps. It joins &lt;a href=&quot;/writing-php-code/phpdoc-types&quot;&gt;many other advanced types expressible in PHPDocs&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @param list&amp;lt;int&gt; $listOfIntegers */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$listOfIntegers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;lower-memory-consumption-leads-to-faster-performance&quot; tabindex=&quot;-1&quot;&gt;Lower memory consumption leads to faster performance &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#lower-memory-consumption-leads-to-faster-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHPStan used to be a really hungry beast. To the point of being killed by CI runners because it consumed not just all the memory up to the &lt;code&gt;memory_limit&lt;/code&gt; in php.ini, but also all the memory assigned to the runner hardware.&lt;/p&gt;
&lt;p&gt;Now it’s a less hungry beast. How did I make it happen? It’s useful to realize what’s going on inside a running program. It’s fine to consume memory as useful things are being achieved, but in order to consume less memory in total, it has to be freed afterwards so that it can be used again by different data needed when analysing the next file in line.&lt;/p&gt;
&lt;p&gt;To debug memory leaks, I use the &lt;a href=&quot;https://github.com/BitOne/php-meminfo&quot;&gt;&lt;code&gt;php-meminfo&lt;/code&gt;&lt;/a&gt; extension. I quickly realized that most of the memory is occupied by &lt;a href=&quot;/developing-extensions/abstract-syntax-tree&quot;&gt;AST&lt;/a&gt; nodes. PHP frees the memory occupied by an object when there are no more references to it &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. It didn’t work in case of AST nodes because they kept pointing at each other:&lt;/p&gt;
&lt;img class=&quot;mb-8&quot; src=&quot;/images/mermaid-71e40c64f0ee4fe194aaf0df1edd9ac271c8045ad55885037f8a0ca171d988bb.svg&quot; /&gt;
&lt;p&gt;Getting rid of &lt;code&gt;parent&lt;/code&gt;/&lt;code&gt;previous&lt;/code&gt;/&lt;code&gt;next&lt;/code&gt; node attributes is a backward compatibility break for custom rules that read them. I’ve written &lt;a href=&quot;/blog/preprocessing-ast-for-custom-rules&quot;&gt;an article on how to make these rules work again&lt;/a&gt; even without those memory-consuming references.&lt;/p&gt;
&lt;p&gt;The object graph is now obviously cleaner and with no reference cycles:&lt;/p&gt;
&lt;img class=&quot;mb-8&quot; src=&quot;/images/mermaid-8d998e1cd58969a3fb12c7755d01efdbfc2bac1a7c308a1d1927d6014ba537e1.svg&quot; /&gt;
&lt;p&gt;In my testing PHPStan now consumes around 50–70 % less memory on huge projects with thousands of files. Analysing PrestaShop 8.0 codebase now takes 3 minutes instead of 9 minutes in GitHub Actions with 2 CPU cores.&lt;/p&gt;
&lt;h2 id=&quot;validate-inline-phpdoc-%40var-tag-type&quot; tabindex=&quot;-1&quot;&gt;Validate inline PHPDoc &lt;code&gt;@var&lt;/code&gt; tag type &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#validate-inline-phpdoc-%40var-tag-type&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are multiple problems with inline &lt;code&gt;@var&lt;/code&gt; PHPDoc tag. PHP developers use it for two main reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To fix wrong 3rd party PHPDocs. A dependency &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; might have &lt;code&gt;@return string&lt;/code&gt; in a PHPDoc but in reality can return &lt;code&gt;null&lt;/code&gt; as well.&lt;/li&gt;
&lt;li&gt;To narrow down the returned type. When a function returns &lt;code&gt;string|null&lt;/code&gt; but we know that in this case it can only return &lt;code&gt;string&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @var Something $a */&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;makeSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By looking at the analysed code we can’t really tell which scenario it is. That’s why PHPStan always trusted the type in &lt;code&gt;@var&lt;/code&gt; and didn’t report any possible mistakes. Obviously that’s dangerous because the type in &lt;code&gt;@var&lt;/code&gt; can get out of sync and be wrong really easily. But I came up with an idea what we could report without any false positives, keeping existing use-cases in mind.&lt;/p&gt;
&lt;p&gt;PHPStan 2.0 validates the inline &lt;code&gt;@var&lt;/code&gt; tag type against the native type of the assigned expression. It finds the lies spread around in &lt;code&gt;@var&lt;/code&gt; tags:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** @var string|null $a */&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// PHPDoc tag @var with type string|null is not subtype of native type string.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It doesn’t make sense to allow &lt;code&gt;string|null&lt;/code&gt;, because the type can never be &lt;code&gt;null&lt;/code&gt;. PHPStan says “string|null is not subtype of native type string”, implying that only subtypes are allowed. Subtype is the same type or narrower, meaning that &lt;code&gt;string&lt;/code&gt; or &lt;code&gt;non-empty-string&lt;/code&gt; would be okay.&lt;/p&gt;
&lt;p&gt;By default PHPStan isn’t going to report anything about the following code:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @return string */&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/** @var string|null $a */&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;doFoo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Because the &lt;code&gt;@return&lt;/code&gt; PHPDoc might be wrong and that’s what the &lt;code&gt;@var&lt;/code&gt; tag might be trying to fix. If you want this scenario to be reported too, enable &lt;a href=&quot;/config-reference#reportwrongphpdoctypeinvartag&quot;&gt;&lt;code&gt;reportWrongPhpDocTypeInVarTag&lt;/code&gt;&lt;/a&gt;, or install &lt;a href=&quot;https://github.com/phpstan/phpstan-strict-rules&quot;&gt;phpstan-strict-rules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’d like the PHP community to use inline &lt;code&gt;@var&lt;/code&gt; tags less and less over time. There are many great alternatives that promote good practices and code deduplication: &lt;a href=&quot;/writing-php-code/phpdoc-types#conditional-return-types&quot;&gt;Conditional return types&lt;/a&gt;, &lt;a href=&quot;/writing-php-code/narrowing-types#custom-type-checking-functions-and-methods&quot;&gt;&lt;code&gt;@phpstan-assert&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;/blog/generics-in-php-using-phpdocs&quot;&gt;generics&lt;/a&gt;, &lt;a href=&quot;/user-guide/stub-files&quot;&gt;stub files&lt;/a&gt; for overriding 3rd party PHPDocs, or &lt;a href=&quot;/developing-extensions/dynamic-return-type-extensions&quot;&gt;dynamic return type extensions&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;checking-truthiness-of-%40phpstan-pure-with-impure-points&quot; tabindex=&quot;-1&quot;&gt;Checking truthiness of &lt;code&gt;@phpstan-pure&lt;/code&gt; with impure points &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#checking-truthiness-of-%40phpstan-pure-with-impure-points&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pure functions always return the same value for the same input (arguments and current object state). They also don’t have any side effects like depending on current time, random number generator, or IO like reading file contents or accessing resources over the network.&lt;/p&gt;
&lt;p&gt;It’s useful to mark functions and methods with &lt;code&gt;@phpstan-pure&lt;/code&gt; if their logic should be pure. PHPStan 2.0 now enforces this annotation, meaning it will report any impure code inside as an error.&lt;/p&gt;
&lt;p&gt;We achieved that with the help of impure points. For every statement and expression type PHP supports PHPStan decides if it’s pure or not.&lt;/p&gt;
&lt;p&gt;Knowing impure points for any code also helps PHPStan to report more dead code. There’s no point in calling a pure method on a separate line without using its result. If it does not have any side effect, and we’re not using the returned value, then we can safely delete this line:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Call to method Foo::pureMethod() on a separate line has no effect.&lt;/span&gt;
&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pureMethod&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Also: if a &lt;code&gt;void&lt;/code&gt; method does not have any impure points, it doesn’t have to exist:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Method Foo::add() returns void but does not have any side effects.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token variable&quot;&gt;$c&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;less-caching%2C-disk-space-cleanup&quot; tabindex=&quot;-1&quot;&gt;Less caching, disk space cleanup &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#less-caching%2C-disk-space-cleanup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Getting rid of a cache without slowing down the analysis is a clear win. Less stuff to invalidate, less stuff to worry about, less disk space to occupy.&lt;/p&gt;
&lt;p&gt;PHPStan used to cache information whether a function is variadic because of &lt;a href=&quot;https://www.php.net/manual/en/function.func-get-args.php&quot;&gt;&lt;code&gt;func_get_args()&lt;/code&gt;&lt;/a&gt; call or similar in its body. I realized we have this information freshly available in each run anyway, so we don’t need to cache it. This saves literally tens of thousands of files that PHPStan previously needed to save to disk and then read.&lt;/p&gt;
&lt;p&gt;PHPStan now solely relies on the &lt;a href=&quot;https://phpstan.org/user-guide/result-cache&quot;&gt;result cache&lt;/a&gt; to speed up the analysis.&lt;/p&gt;
&lt;p&gt;We took this opportunity to clean up old cache items on disk, so on the first PHPStan 2.0 run you might see some previously occupied disk space free up. On a proprietary project I test PHPStan on it freed up about 1 GB of space.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-future&quot; tabindex=&quot;-1&quot;&gt;The future &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#the-future&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I said it &lt;a href=&quot;/blog/phpstan-1-0-released#a-bright-future&quot;&gt;three years ago&lt;/a&gt;, and I’m saying it again. PHPStan’s future is bright. It’s my full-time job thanks to &lt;a href=&quot;https://phpstan.org/blog/introducing-phpstan-pro&quot;&gt;PHPStan Pro&lt;/a&gt; and &lt;a href=&quot;/sponsor&quot;&gt;GitHub Sponsors&lt;/a&gt;, splitting the revenue roughly 50-50 between them.&lt;/p&gt;
&lt;p&gt;I love my job, I love PHP and I don’t plan to stop. Contributors are &lt;a href=&quot;https://github.com/phpstan/phpstan-src/graphs/contributors?from=11.+11.+2023&quot;&gt;very active&lt;/a&gt; and indispensable. PHPStan has a thriving &lt;a href=&quot;/user-guide/extension-library&quot;&gt;ecosystem of extensions&lt;/a&gt; as well.&lt;/p&gt;
&lt;p&gt;I have a lot of ambitious and even crazy ideas I’d like to try out. But the first thing I will address once the dust settles on 2.0 is adding support for PHP 8.4. You can expect it to be added before the end of 2024.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider sponsoring&lt;/strong&gt; further development of PHPStan on GitHub Sponsors and also &lt;strong&gt;subscribe to PHPStan Pro&lt;/strong&gt;&lt;/a&gt;! I’d really appreciate it!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;That’s called reference counting. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;That probably doesn’t use static analysis. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
		</entry>
	
		
		<entry>
			<title>PHPStan 1.12: Road to PHPStan 2.0</title>
			<link href="https://phpstan.org/blog/phpstan-1-12-road-to-phpstan-2-0"/>
			<updated>2024-08-27T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-1-12-road-to-phpstan-2-0</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p&gt;After three years since the initial &lt;a href=&quot;/blog/phpstan-1-0-released&quot;&gt;PHPStan 1.0 release&lt;/a&gt;, we’re getting closer to PHPStan 2.0. After sifting through my list of ideas for the new major version, I realized I can move some of them forward and release them in 1.x series and hide them behind the &lt;a href=&quot;/blog/what-is-bleeding-edge&quot;&gt;Bleeding Edge config toggle&lt;/a&gt;, so they can be enjoyed by PHPStan users sooner.&lt;/p&gt;
&lt;p&gt;This isn’t true just for PHPStan 1.12 but ever since 1.0. If you enable Bleeding Edge, you basically live in the future. You get new rules and behavior changes that will be enabled for everyone in the next major version. That’s your reward as an early adopter.&lt;/p&gt;
&lt;p&gt;Here’s an equation:&lt;/p&gt;
&lt;p class=&quot;text-center font-bold text-lg&quot;&gt;PHPStan 2.0 = PHPStan 1.12 + Bleeding Edge + BC breaks&lt;/p&gt;
&lt;p&gt;When you upgrade to PHPStan 1.12 and enable Bleeding Edge, you can get mostly ready for PHPStan 2.0 today.&lt;/p&gt;
&lt;p&gt;But enough about the future. Here’s what’s new in &lt;a href=&quot;https://github.com/phpstan/phpstan/releases/tag/1.12.0&quot;&gt;today’s 1.12 release&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;general-availability-of-precise-type-inference-for-regular-expressions&quot; tabindex=&quot;-1&quot;&gt;General availability of precise type inference for regular expressions &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#general-availability-of-precise-type-inference-for-regular-expressions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is a rare example of a feature that began its life in Bleeding Edge but got out of it sooner than the next major version. Because of its complexity we needed a staged rollout to weed out the bugs.&lt;/p&gt;
&lt;p&gt;First &lt;a href=&quot;https://github.com/phpstan/phpstan/releases/tag/1.11.6&quot;&gt;introduced in 1.11.6&lt;/a&gt;, and improved by dozens of pull requests since then, this feature is about figuring out the precise type for &lt;code&gt;$matches&lt;/code&gt; by-ref argument coming from &lt;code&gt;preg_match()&lt;/code&gt; and other related functions:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preg_match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;/Price: (?&amp;lt;currency&gt;£|€)&#92;d+/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PREG_UNMATCHED_AS_NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// array{0: string, currency: non-empty-string, 1: non-empty-string}&lt;/span&gt;
	&lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPStan&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;dumpType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$matches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Markus Staab and Jordi Boggiano of Composer fame &lt;a href=&quot;https://staabm.github.io/2024/07/05/array-shapes-for-preg-match-matches.html&quot;&gt;worked really hard on this&lt;/a&gt;. It brings a whole new level of precision to PHPStan’s type inference.&lt;/p&gt;
&lt;p&gt;From what I gathered, this ability is pretty unique and not many programming languages offer it. And now we have it in PHP!&lt;/p&gt;
&lt;h2 id=&quot;fix-blind-spots-in-phpdoc-tags-type-checks&quot; tabindex=&quot;-1&quot;&gt;Fix blind spots in PHPDoc tags type checks &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#fix-blind-spots-in-phpdoc-tags-type-checks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;PHPStan performs four categories of sanity checks on &lt;a href=&quot;/writing-php-code/phpdoc-types&quot;&gt;types in PHPDocs&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Missing types: performed on level 6 and up, it enforces specifying array value types, generic types, and &lt;a href=&quot;/config-reference#vague-typehints&quot;&gt;optionally also callable signatures&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Existing classes: looks for nonexistent classes and also trait references&lt;/li&gt;
&lt;li&gt;Unresolvable types: looks for &lt;code&gt;never&lt;/code&gt; bottom type that resulted from impossible intersections like &lt;code&gt;string&amp;amp;int&lt;/code&gt;, referencing undefined constants like &lt;code&gt;Foo::BAR&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Generics type checks: checks sanity of generic types. Compares the number of type variables in a type against the number of &lt;code&gt;@template&lt;/code&gt; above the class declaration, checks the subtyping of bounds, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These type checks weren’t performed consistently for all &lt;a href=&quot;/writing-php-code/phpdocs-basics&quot;&gt;supported PHPDocs tags&lt;/a&gt;. We were missing most or all of these checks for these tags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@param-out&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@param-closure-this&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@param-immediately-invoked-callable&lt;/code&gt; and &lt;code&gt;@param-later-invoked-callable&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@phpstan-self-out&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@mixin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@property&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@method&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@extends&lt;/code&gt;, &lt;code&gt;@implements&lt;/code&gt;, &lt;code&gt;@use&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PHPStan 1.12 fixes that. Wrong and corrupted types are checked consistently across the board. Because these are essentially new rules which would cause new errors reported for most codebases, they were added only to Bleeding Edge, and will be enabled for everyone in PHPStan 2.0.&lt;/p&gt;
&lt;h2 id=&quot;too-wide-private-property-type&quot; tabindex=&quot;-1&quot;&gt;Too wide private property type &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#too-wide-private-property-type&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another addition to Bleeding Edge checks if a type could be removed from a private property union type, without affecting anything.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-diff-php diff-highlight&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// if string is never assigned to `$this-&gt;a`, we can remove it from the type&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PHPStan already had this rule for function and method return types, and now we can enjoy it for properties too.&lt;/p&gt;
&lt;h2 id=&quot;php-8.4-runtime-support&quot; tabindex=&quot;-1&quot;&gt;PHP 8.4 runtime support &lt;a class=&quot;header-anchor ml-1 text-gray-300 hover:text-black&quot; href=&quot;#php-8.4-runtime-support&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Implementing support for new PHP version in PHPStan comes in four phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure PHPStan runs on the new PHP version and all tests pass.&lt;/li&gt;
&lt;li&gt;Understand added functions and changed signatures of existing functions in the new PHP version.&lt;/li&gt;
&lt;li&gt;Understand new syntax and features. This removes false errors when analysing code written against the new PHP version.&lt;/li&gt;
&lt;li&gt;Implement new rules specific to new syntax and features. Discover which parts of the new PHP features are tricky and need to be checked with new static analysis rules.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PHPStan 1.12 implements the first two phases. It runs on PHP 8.4 without any deprecation notices, and new functions like &lt;a href=&quot;/r/15c8954d-d090-4c01-8c24-46ef5fd93541&quot;&gt;&lt;code&gt;array_find()&lt;/code&gt;&lt;/a&gt; are already known.&lt;/p&gt;
&lt;p&gt;Because of new syntax, the rest is dependent on upgrading to &lt;a href=&quot;https://github.com/nikic/PHP-Parser&quot;&gt;nikic/PHP-Parser&lt;/a&gt; v5 which has to be done in a major version.&lt;/p&gt;
&lt;p&gt;My plan for the upcoming months is straightforward: finish and release PHPStan 2.0, and then work on PHP 8.4-specific features.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Me and PHPStan contributors put a lot of hard work into this release. I hope that you’ll really enjoy it and take advantage of these new features! We’re going to be &lt;a href=&quot;https://github.com/phpstan/phpstan/discussions&quot;&gt;waiting for everyone’s feedback on GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Do you like PHPStan and use it every day? &lt;a href=&quot;/sponsor&quot;&gt;&lt;strong&gt;Consider sponsoring&lt;/strong&gt; further development of PHPStan on GitHub Sponsors and also &lt;strong&gt;subscribe to PHPStan Pro&lt;/strong&gt;&lt;/a&gt;! I’d really appreciate it!&lt;/p&gt;
</content>
		</entry>
	
</feed>
