<?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>2024-08-27T00:00:00Z</updated>
	<id>https://phpstan.org/</id>
	<author>
		<name/>
		<email/>
	</author>
	
		
		<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>After three years since the initial &lt;a href="/blog/phpstan-1-0-released">PHPStan 1.0 release&lt;/a>, 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="/blog/what-is-bleeding-edge">Bleeding Edge config toggle&lt;/a>, so they can be enjoyed by PHPStan users sooner.&lt;/p>&lt;p>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>&lt;p>Here’s an equation:&lt;/p>&lt;p class="font-bold text-center text-lg">PHPStan 2.0 = PHPStan 1.12 + Bleeding Edge + BC breaks&lt;/p>&lt;p>When you upgrade to PHPStan 1.12 and enable Bleeding Edge, you can get mostly ready for PHPStan 2.0 today.&lt;/p>&lt;p>But enough about the future. Here’s what’s new in &lt;a href="https://github.com/phpstan/phpstan/releases/tag/1.12.0">today’s 1.12 release&lt;/a>.&lt;/p>&lt;h2 id="general-availability-of-precise-type-inference-for-regular-expressions" tabindex="-1">General availability of precise type inference for regular expressions &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#general-availability-of-precise-type-inference-for-regular-expressions">#&lt;/a>&lt;/h2>&lt;p>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>&lt;p>First &lt;a href="https://github.com/phpstan/phpstan/releases/tag/1.11.6">introduced in 1.11.6&lt;/a>, and improved by dozens of pull requests since then, this feature is about figuring out the precise type for &lt;code>$matches&lt;/code> by-ref argument coming from &lt;code>preg_match()&lt;/code> and other related functions:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="function token">preg_match&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="single-quoted-string string token">'/Price: (?&amp;lt;currency>£|€)\d+/'&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$s&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$matches&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="constant token">PREG_UNMATCHED_AS_NULL&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// array{0: string, currency: non-empty-string, 1: non-empty-string}&lt;/span>
	&lt;span class="function token">&lt;span class="punctuation token">\&lt;/span>PHPStan&lt;span class="punctuation token">\&lt;/span>dumpType&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$matches&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>Markus Staab and Jordi Boggiano of Composer fame &lt;a href="https://staabm.github.io/2024/07/05/array-shapes-for-preg-match-matches.html">worked really hard on this&lt;/a>. It brings a whole new level of precision to PHPStan’s type inference.&lt;/p>&lt;p>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>&lt;h2 id="fix-blind-spots-in-phpdoc-tags-type-checks" tabindex="-1">Fix blind spots in PHPDoc tags type checks &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#fix-blind-spots-in-phpdoc-tags-type-checks">#&lt;/a>&lt;/h2>&lt;p>PHPStan performs four categories of sanity checks on &lt;a href="/writing-php-code/phpdoc-types">types in PHPDocs&lt;/a>:&lt;/p>&lt;ul> &lt;li>Missing types: performed on level 6 and up, it enforces specifying array value types, generic types, and &lt;a href="/config-reference#vague-typehints">optionally also callable signatures&lt;/a>.&lt;/li> &lt;li>Existing classes: looks for nonexistent classes and also trait references&lt;/li> &lt;li>Unresolvable types: looks for &lt;code>never&lt;/code> bottom type that resulted from impossible intersections like &lt;code>string&amp;amp;int&lt;/code>, referencing undefined constants like &lt;code>Foo::BAR&lt;/code>, etc.&lt;/li> &lt;li>Generics type checks: checks sanity of generic types. Compares the number of type variables in a type against the number of &lt;code>@template&lt;/code> above the class declaration, checks the subtyping of bounds, etc.&lt;/li> &lt;/ul>&lt;p>These type checks weren’t performed consistently for all &lt;a href="/writing-php-code/phpdocs-basics">supported PHPDocs tags&lt;/a>. We were missing most or all of these checks for these tags:&lt;/p>&lt;ul> &lt;li>&lt;code>@param-out&lt;/code>&lt;/li> &lt;li>&lt;code>@param-closure-this&lt;/code>&lt;/li> &lt;li>&lt;code>@param-immediately-invoked-callable&lt;/code> and &lt;code>@param-later-invoked-callable&lt;/code>&lt;/li> &lt;li>&lt;code>@phpstan-self-out&lt;/code>&lt;/li> &lt;li>&lt;code>@mixin&lt;/code>&lt;/li> &lt;li>&lt;code>@property&lt;/code>&lt;/li> &lt;li>&lt;code>@method&lt;/code>&lt;/li> &lt;li>&lt;code>@extends&lt;/code>, &lt;code>@implements&lt;/code>, &lt;code>@use&lt;/code>&lt;/li> &lt;/ul>&lt;p>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>&lt;h2 id="too-wide-private-property-type" tabindex="-1">Too wide private property type &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#too-wide-private-property-type">#&lt;/a>&lt;/h2>&lt;p>Another addition to Bleeding Edge checks if a type could be removed from a private property union type, without affecting anything.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">// if string is never assigned to `$this->a`, we can remove it from the type&lt;/span>
&lt;span class="keyword token">private&lt;/span> &lt;span class="keyword token type-declaration">int&lt;/span>&lt;span class="operator token">|&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="token variable">$a&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>PHPStan already had this rule for function and method return types, and now we can enjoy it for properties too.&lt;/p>&lt;h2 id="php-8.4-runtime-support" tabindex="-1">PHP 8.4 runtime support &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#php-8.4-runtime-support">#&lt;/a>&lt;/h2>&lt;p>Implementing support for new PHP version in PHPStan comes in four phases:&lt;/p>&lt;ul> &lt;li>Make sure PHPStan runs on the new PHP version and all tests pass.&lt;/li> &lt;li>Understand added functions and changed signatures of existing functions in the new PHP version.&lt;/li> &lt;li>Understand new syntax and features. This removes false errors when analysing code written against the new PHP version.&lt;/li> &lt;li>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> &lt;/ul>&lt;p>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="/r/15c8954d-d090-4c01-8c24-46ef5fd93541">&lt;code>array_find()&lt;/code>&lt;/a> are already known.&lt;/p>&lt;p>Because of new syntax, the rest is dependent on upgrading to &lt;a href="https://github.com/nikic/PHP-Parser">nikic/PHP-Parser&lt;/a> v5 which has to be done in a major version.&lt;/p>&lt;p>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>&lt;hr>&lt;p>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="https://github.com/phpstan/phpstan/discussions">waiting for everyone’s feedback on GitHub&lt;/a>.&lt;/p>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="/sponsor">&lt;strong>Consider sponsoring&lt;/strong> further development of PHPStan on GitHub Sponsors and also &lt;strong>subscribe to PHPStan Pro&lt;/strong>&lt;/a>! I’d really appreciate it!&lt;/p></content>
		</entry>
	
		
		<entry>
			<title>PHPStan Reports Different Errors Locally &amp; in CI. What Should I Do?</title>
			<link href="https://phpstan.org/blog/phpstan-reports-different-errors-locally-ci-what-should-i-do"/>
			<updated>2024-08-20T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-reports-different-errors-locally-ci-what-should-i-do</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>If you find that PHPStan reports different code on your machine and in continuous integration build system, here are the steps to follow to get rid of this problem.&lt;/p>&lt;h2 id="is-it-analysing-the-same-code%3F" tabindex="-1">Is it analysing the same code? &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#is-it-analysing-the-same-code%3F">#&lt;/a>&lt;/h2>&lt;p>Make sure you’re looking at the results of the same Git commit in CI as you have checked out locally, and that you have committed all the files in your working directory.&lt;/p>&lt;p>Run &lt;code>git rev-parse HEAD&lt;/code> or look at &lt;code>git log&lt;/code> to get the Git commit hash. CI systems usually print the current Git commit hash somewhere early in the build log.&lt;/p>&lt;p>Run &lt;code>git status&lt;/code> to make sure there are no uncommitted changes you’re running the analysis against.&lt;/p>&lt;h2 id="is-it-running-the-same-php-version%3F" tabindex="-1">Is it running the same PHP version? &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#is-it-running-the-same-php-version%3F">#&lt;/a>&lt;/h2>&lt;p>Make sure to check that you’re running the same PHP version locally and in CI. You can check that with &lt;code>php -v&lt;/code>.&lt;/p>&lt;h2 id="require-phpstan-in-your-composer-project" tabindex="-1">Require PHPStan in your Composer project &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#require-phpstan-in-your-composer-project">#&lt;/a>&lt;/h2>&lt;p>Do not run PHPStan installed somewhere globally in your operating system, nor locally, neither in CI. Install PHPStan in &lt;code>composer.json&lt;/code> with your project dependencies with this command:&lt;/p>&lt;pre class="language-bash">&lt;code class="diff-highlight language-diff-bash">&lt;span class="function token">composer&lt;/span> require &lt;span class="parameter token variable">--dev&lt;/span> phpstan/phpstan&lt;/code>&lt;/pre>&lt;p>This way you will make sure everyone and everything is on the same PHPStan version - your machine, your teammates and most importantly, CI.&lt;/p>&lt;h2 id="make-sure-your-composer.lock-is-committed-in-the-git-repository" tabindex="-1">Make sure your &lt;code>composer.lock&lt;/code> is committed in the Git repository &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#make-sure-your-composer.lock-is-committed-in-the-git-repository">#&lt;/a>&lt;/h2>&lt;p>Commit and version your &lt;code>composer.lock&lt;/code> file in Git to make sure your dependencies are locked and always the same until you update them.&lt;/p>&lt;h2 id="run-composer-install%2C-not-composer-update" tabindex="-1">Run &lt;code>composer install&lt;/code>, not &lt;code>composer update&lt;/code> &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#run-composer-install%2C-not-composer-update">#&lt;/a>&lt;/h2>&lt;p>Even with &lt;code>composer.lock&lt;/code> committed, make sure you run &lt;code>composer install&lt;/code> to install dependencies in CI. Running &lt;code>composer update&lt;/code> discards the contents of your &lt;code>composer.lock&lt;/code> file and updates all dependencies. Which is something we don’t want.&lt;/p>&lt;h2 id="run-phpstan%E2%80%99s-diagnose-command-to-compare-the-environments" tabindex="-1">Run PHPStan’s &lt;code>diagnose&lt;/code> command to compare the environments &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#run-phpstan%E2%80%99s-diagnose-command-to-compare-the-environments">#&lt;/a>&lt;/h2>&lt;p>Since &lt;a href="https://github.com/phpstan/phpstan/releases/tag/1.11.8">release 1.11.8&lt;/a> PHPStan ships with a &lt;code>diagnose&lt;/code> command. It prints useful information about its configuration and more. Do not forget to pass the correct &lt;a href="/config-reference">configuration file&lt;/a> and &lt;a href="/user-guide/rule-levels">rule level&lt;/a> when running the command.&lt;/p>&lt;p>The output looks like this:&lt;/p>&lt;pre class="font-mono text-sm">
&lt;span class="text-green-500">PHP runtime version:&lt;/span> 8.3.1
&lt;span class="text-green-500">PHP version for analysis:&lt;/span> 8.3.99 (from config.platform.php in composer.json)

&lt;span class="text-green-500">PHPStan version:&lt;/span> 1.11.11
&lt;span class="text-green-500">PHPStan running from:&lt;/span>
/var/www/project/vendor/phpstan/phpstan

&lt;span class="text-green-500">Extension installer:&lt;/span>
composer/pcre: 3.2.0
phpstan/phpstan-deprecation-rules: 1.2.0
phpstan/phpstan-doctrine: 1.4.8
phpstan/phpstan-nette: 1.3.5
phpstan/phpstan-phpunit: 1.4.0
phpstan/phpstan-strict-rules: 1.6.0
phpstan/phpstan-symfony: 1.4.6
phpstan/phpstan-webmozart-assert: 1.2.7
shipmonk/phpstan-rules: 3.1.0

&lt;span class="text-green-500">Discovered Composer project root:&lt;/span>
/var/www/project

&lt;span class="text-green-500">Doctrine's objectManagerLoader:&lt;/span> In use
Installed Doctrine packages:
doctrine/dbal: 3.8.6
doctrine/orm: 2.19.6
doctrine/common: 3.4.4
doctrine/collections: 2.2.2
doctrine/persistence: 3.3.3

&lt;span class="text-green-500">Symfony's consoleApplicationLoader:&lt;/span> No
&lt;/pre>&lt;p>Compare the output between your local machine and CI to find possible differences.&lt;/p>&lt;h2 id="check-the-config-file-path" tabindex="-1">Check the config file path &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#check-the-config-file-path">#&lt;/a>&lt;/h2>&lt;p>Make sure you’re running PHPStan &lt;code>analyse&lt;/code> command with the same &lt;a href="/config-reference">configuration file&lt;/a>. If you’re passing a custom path with &lt;code>-c&lt;/code>, make sure it’s the same one as locally.&lt;/p>&lt;p>If you’re letting PHPStan autodiscover &lt;code>phpstan.neon&lt;/code> or &lt;code>phpstan.neon.dist&lt;/code> file in the current working directory, it outputs this note at the beginning of the analysis:&lt;/p>&lt;pre class="font-mono text-sm">
Note: Using configuration file /var/www/project/phpstan.neon.
&lt;/pre>&lt;p>Make sure your local machine uses the same file as the CI environment.&lt;/p>&lt;h2 id="get-rid-of-duplicate-symbols" tabindex="-1">Get rid of duplicate symbols &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#get-rid-of-duplicate-symbols">#&lt;/a>&lt;/h2>&lt;p>If you have two or more different class or function definitions with the exact same name, PHPStan does not guarantee which one it’s going to give priority to. Your local machine &lt;a href="/user-guide/discovering-symbols">might discover&lt;/a> one definition first and your CI might discover a different definition first, leading to different analysis results.&lt;/p>&lt;p>Rename your classes and functions so that they are always unique to get rid of this problem.&lt;/p>&lt;h2 id="fix-your-custom-non-deterministic-autoloader" tabindex="-1">Fix your custom non-deterministic autoloader &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#fix-your-custom-non-deterministic-autoloader">#&lt;/a>&lt;/h2>&lt;p>Sometimes the difference in results comes down to the order of files during the analysis. Because the CI environment might have a different number of CPU cores, the files are going to be analysed in different groups and in different order than on your local machine.&lt;/p>&lt;p>If the difference is about what classes are known and unknown to PHPStan, it’s possible you’re using a &lt;a href="/user-guide/discovering-symbols#custom-autoloader">custom autoloader&lt;/a> to discover them.&lt;/p>&lt;p>A following scenario can occur:&lt;/p>&lt;ul> &lt;li>You’re referencing a class name in your code with wrong case. For example. Your class name is &lt;code>AdminUser&lt;/code> but you have &lt;code>adminUser&lt;/code> somewhere in your code when referencing the same class.&lt;/li> &lt;li>On your local machine the first file that’s analysed refers to the class with the correct name - &lt;code>AdminUser&lt;/code>. That makes other occurrences even with the wrong case let the class to be successfully found by PHPStan.&lt;/li> &lt;li>In CI the first file that’s analysed refers to the class with an incorrect name - &lt;code>adminUser&lt;/code>. Your custom autoloader should still find the class successfully but maybe has a bug. This makes PHPStan report “unknown class” error and it also makes it memoize that this class does not exist.&lt;/li> &lt;li>When the second file that’s analysed in CI refers correctly to &lt;code>AdminUser&lt;/code>, it still makes PHPStan report an error about unknown class which is a different behaviour than you’re experiencing locally.&lt;/li> &lt;/ul>&lt;p>To fix this problem, make your autoloader discover classes even with wrong case.&lt;/p>&lt;h2 id="open-a-discussion-on-phpstan%E2%80%99s-github" tabindex="-1">Open a discussion on PHPStan’s GitHub &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#open-a-discussion-on-phpstan%E2%80%99s-github">#&lt;/a>&lt;/h2>&lt;p>If none of the steps above helped you to get consistent results between running PHPStan locally and in CI, please &lt;a href="https://github.com/phpstan/phpstan/discussions/new?category=support">open a discussion on GitHub,&lt;/a> so we can help you figure out this problem.&lt;/p>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="/sponsor">&lt;strong>Consider sponsoring&lt;/strong> further development of PHPStan on GitHub Sponsors and also &lt;strong>subscribe to PHPStan Pro&lt;/strong>&lt;/a>! I’d really appreciate it!&lt;/p></content>
		</entry>
	
		
		<entry>
			<title>Debugging PHPStan Performance: Identify Slow Files</title>
			<link href="https://phpstan.org/blog/debugging-performance-identify-slow-files"/>
			<updated>2024-07-05T00:00:00Z</updated>
			<id>https://phpstan.org/blog/debugging-performance-identify-slow-files</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>If you feel like PHPStan could be faster when analysing your project, you can debug what’s actually making it slow. Usually the cause of slow analysis can be pinpointed to a single file or a handful of files in a project that make PHPStan’s analysis crawl to a halt.&lt;/p>&lt;p>Before you start debugging, try enabling &lt;a href="/blog/what-is-bleeding-edge">Bleeding Edge&lt;/a>. It makes analysis in huge codebases run much faster. It comes with a &lt;a href="/blog/phpstan-1-6-0-with-conditional-return-types#lower-memory-consumption">slight backward compatibility break&lt;/a> with the advantage of breaking bidirectional memory references, making the job of garbage collector easier.&lt;/p>&lt;p>If that didn’t help, run PHPStan again but add &lt;code>-vvv --debug&lt;/code> &lt;a href="/user-guide/command-line-usage">command line options&lt;/a>. PHPStan will run in a single thread instead of in multiple processes at once, and it will print the elapsed time and consumed memory during the analysis of each file:&lt;/p>&lt;pre>&lt;code>/var/www/src/Database/Eloquent/Relations/MorphToMany.php
--- consumed 6 MB, total 200 MB, took 0.11 s
/var/www/src/Database/Eloquent/Relations/HasOneThrough.php
--- consumed 0 B, total 200 MB, took 0.06 s
/var/www/src/Database/Eloquent/Relations/MorphPivot.php
--- consumed 0 B, total 200 MB, took 0.06 s
/var/www/src/Database/Eloquent/Relations/Pivot.php
--- consumed 2 MB, total 202 MB, took 0.09 s
/var/www/src/Database/Eloquent/Relations/HasOneOrMany.php
--- consumed 0 B, total 202 MB, took 0.15 s
/var/www/src/Database/Eloquent/Relations/BelongsTo.php
--- consumed 0 B, total 202 MB, took 0.15 s
&lt;/code>&lt;/pre>&lt;p>You can either watch this log go by in realtime with your own eyes and spot files that took too much time or memory. Or you can redirect the output to a file:&lt;/p>&lt;pre>&lt;code>vendor/bin/phpstan -vvv --debug &amp;gt; phpstan.log
&lt;/code>&lt;/pre>&lt;p>And parse this file with a &lt;a href="https://gist.github.com/ruudk/41897eb59ff497b271fc9fa3c7d5fb27">simple script&lt;/a> by &lt;a href="https://github.com/ruudk">Ruud Kamphuis&lt;/a> that sorts the file list and puts the files that took the most time on top:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="language-php php token">&lt;span class="delimiter important token">&amp;lt;?php&lt;/span> &lt;span class="keyword token">declare&lt;/span>&lt;span class="punctuation token">(&lt;/span>strict_types&lt;span class="operator token">=&lt;/span>&lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>

&lt;span class="token variable">$log&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">SplFileObject&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="double-quoted-string string token">"phpstan.log"&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>

&lt;span class="token variable">$logs&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="punctuation token">[&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="token variable">$file&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="constant token">null&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="keyword token">while&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="operator token">!&lt;/span>&lt;span class="token variable">$log&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">eof&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="token variable">$line&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="function token">trim&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$log&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">fgets&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$line&lt;/span> &lt;span class="operator token">===&lt;/span> &lt;span class="single-quoted-string string token">''&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
        &lt;span class="keyword token">continue&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="punctuation token">}&lt;/span>
    &lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$file&lt;/span> &lt;span class="operator token">===&lt;/span> &lt;span class="constant token">null&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
        &lt;span class="token variable">$file&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="token variable">$line&lt;/span>&lt;span class="punctuation token">;&lt;/span>
        &lt;span class="keyword token">continue&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="punctuation token">}&lt;/span>
    &lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="function token">preg_match&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="single-quoted-string string token">'/took (?&amp;lt;seconds>[\d.]+) s/'&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$line&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$matches&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="operator token">!==&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
        &lt;span class="keyword token">continue&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="punctuation token">}&lt;/span>

    &lt;span class="token variable">$logs&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="punctuation token">]&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="punctuation token">[&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-casting">float&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="token variable">$matches&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="single-quoted-string string token">'seconds'&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$file&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="token variable">$file&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="constant token">null&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="function token">usort&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$logs&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="keyword token">fn&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">array&lt;/span> &lt;span class="token variable">$left&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="keyword token type-hint">array&lt;/span> &lt;span class="token variable">$right&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="token variable">$right&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="number token">0&lt;/span>&lt;span class="punctuation token">]&lt;/span> &lt;span class="operator token">&amp;lt;=>&lt;/span> &lt;span class="token variable">$left&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="number token">0&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="token variable">$logs&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="function token">array_slice&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$logs&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="number token">0&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="number token">20&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>

&lt;span class="keyword token">echo&lt;/span> &lt;span class="double-quoted-string string token">"Slowest files"&lt;/span> &lt;span class="operator token">.&lt;/span> &lt;span class="constant token">PHP_EOL&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="keyword token">foreach&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$logs&lt;/span> &lt;span class="keyword token">as&lt;/span> &lt;span class="token variable">$log&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="keyword token">echo&lt;/span> &lt;span class="function token">sprintf&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="double-quoted-string string token">"%.2f seconds: %s"&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$log&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="number token">0&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$log&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="operator token">.&lt;/span> &lt;span class="constant token">PHP_EOL&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>
&lt;/span>&lt;/code>&lt;/pre>&lt;p>Save this file into &lt;code>parse.php&lt;/code> in the same directory where &lt;code>phpstan.log&lt;/code> is and run it:&lt;/p>&lt;pre class="language-bash">&lt;code class="diff-highlight language-diff-bash">php parse.php&lt;/code>&lt;/pre>&lt;p>It will output 20 files that took the most time:&lt;/p>&lt;pre>&lt;code>Slowest files
4.46 seconds: /var/www/src/Collections/LazyCollection.php
2.71 seconds: /var/www/src/Support/Str.php
2.56 seconds: /var/www/src/Console/Command.php
2.45 seconds: /var/www/src/Validation/Validator.php
2.44 seconds: /var/www/src/Database/Query/Builder.php
2.41 seconds: /var/www/src/Collections/Collection.php
2.12 seconds: /var/www/src/Database/Eloquent/Builder.php
2.10 seconds: /var/www/src/Foundation/Testing/TestCase.php
2.01 seconds: /var/www/src/Database/Eloquent/Model.php
...
&lt;/code>&lt;/pre>&lt;p>What to do with this list? It’s up to you to decide. Maybe the slowest file is really large and maybe even auto-generated. Analysing it with PHPStan doesn’t probably bring too much value, so you can &lt;a href="/user-guide/ignoring-errors#excluding-whole-files">exclude it from the analysis&lt;/a> and make PHPStan immediately faster.&lt;/p>&lt;p>But if the slowest file isn’t that large, you’ve just found a real bottleneck which is an opportunity to make PHPStan faster. Take this file and &lt;a href="https://github.com/phpstan/phpstan/issues/new?template=Bug_report.yaml">open a new bug report&lt;/a> in PHPStan’s GitHub repository so we can take a look and use it to make PHPStan faster.&lt;/p>&lt;p>Happy debugging!&lt;/p>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="/sponsor">&lt;strong>Consider sponsoring&lt;/strong> further development of PHPStan on GitHub Sponsors and also &lt;strong>subscribe to PHPStan Pro&lt;/strong>&lt;/a>! I’d really appreciate it!&lt;/p></content>
		</entry>
	
		
		<entry>
			<title>PHPStan 1.11 With Error Identifiers, PHPStan Pro Reboot and Much More</title>
			<link href="https://phpstan.org/blog/phpstan-1-11-errors-identifiers-phpstan-pro-reboot"/>
			<updated>2024-05-13T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-1-11-errors-identifiers-phpstan-pro-reboot</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>This release has been in the works for a year. But it doesn’t mean there hasn’t been anything else released for a year. After starting the work on PHPStan 1.11 I continued working on 1.10.x series in parallel, bringing 54 releases into the world between April 2023 and 2024. But at some point earlier this year I said “enough!” and couldn’t resist finally bringing the &lt;a href="https://github.com/phpstan/phpstan/releases/tag/1.11.0">awesome improvements in 1.11&lt;/a> over the finish line and into the world.&lt;/p>&lt;h2 id="error-identifiers" tabindex="-1">Error Identifiers &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#error-identifiers">#&lt;/a>&lt;/h2>&lt;p>When I decided to add error identifiers I knew it was going to be a lot of work. They’re a way of labeling and categorizing errors reported by PHPStan useful for ignoring errors of a certain category among other things.&lt;/p>&lt;p>I had to go through all PHPStan rules and decide how the identifier should look like for all of them. I’ve settled on two-part format &lt;code>category.subtype&lt;/code> pretty early on as being easy to read and memorize. Other tools like TypeScript have settled on simple numbers like &lt;code>TS2322&lt;/code>. In my opinion that’s as human-friendly as IP addresses. There are reasons why we need DNS to translate that to actual names.&lt;/p>&lt;p>Another easy way out would be to mark the rule’s implementation class name as its identifier. But I’ve never considered these to be user-facing and stable. They’re implementation details. To give them public meaning would really tie our hands when evolving PHPStan. Because some rule classes report very different issues resulting in multiple identifiers, and at the same time, different rules report very similar issues in PHP code resulting in a single identifier.&lt;/p>&lt;p>So a more flexible approach was needed. You can see for yourself how it turned out. There’s a catalogue on PHPStan’s website with all the identifiers. You can &lt;a href="/error-identifiers#gbi-">group them by identifier&lt;/a> (and see all the rules where the same identifier is reported), or &lt;a href="/error-identifiers#gbr-">group them by the rule class&lt;/a> (and see all the identifiers reported by the same class). This page is generated by analysing PHPStan sources with PHPStan to be used on PHPStan’s website. There’s probably an Inception or &lt;a href="https://knowyourmeme.com/memes/xzibit-yo-dawg">Yo Dawg&lt;/a> joke somewhere in there.&lt;/p>&lt;p>So let’s say we have an error and we want to find out its identifier in order to ignore it. If we’re using the default &lt;code>table&lt;/code> output formatter, we can run PHPStan with &lt;code>-v&lt;/code> and see the identifiers right in the output:&lt;/p>&lt;p class="border border-gray-200 p-2 rounded-lg">&lt;img src="https://phpstan.org/identifiers-table-v.557d601f.png" alt="Table error formatter running with -v showing error identifiers">&lt;/p>&lt;p>Or we can run &lt;a href="/blog/introducing-phpstan-pro" class="phpstan-pro-label">PHPStan Pro&lt;/a> by launching PHPStan with &lt;code>--pro&lt;/code> CLI option and see the identifier in the UI next to the reported error:&lt;/p>&lt;p class="border-2 border-purple-600 overflow-hidden rounded-lg">&lt;img src="https://phpstan.org/identifiers-pro-hover.1365208e.png" alt="PHPStan Pro showing error identifiers">&lt;/p>&lt;p>Once we know the identifier we can use it in the new &lt;code>@phpstan-ignore&lt;/code> comment annotation that replaces the current pair of &lt;code>@phpstan-ignore-line&lt;/code> and &lt;code>@phpstan-ignore-next-line&lt;/code> annotations which ignore all errors on a certain line. With &lt;code>@phpstan-ignore&lt;/code> PHPStan figures out if there’s some code besides the comment on the current line (and ignores an error in that code), or if we want to ignore an error in the code on the next line:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">// Both variants work:&lt;/span>
&lt;span class="comment token">// @phpstan-ignore echo.nonString&lt;/span>
&lt;span class="keyword token">echo&lt;/span> &lt;span class="punctuation token">[&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>

&lt;span class="keyword token">echo&lt;/span> &lt;span class="punctuation token">[&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// @phpstan-ignore echo.nonString&lt;/span>&lt;/code>&lt;/pre>&lt;p>In &lt;code>@phpstan-ignore&lt;/code> specifying an identifier is always required.&lt;/p>&lt;p>If we want to ignore multiple errors on the same line, we can place multiple identifiers separated by a comma in the &lt;code>@phpstan-ignore&lt;/code> annotation.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">echo&lt;/span> &lt;span class="token variable">$foo&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$bar&lt;/span>&lt;span class="punctuation token">;&lt;/span>  &lt;span class="comment token">// @phpstan-ignore variable.undefined, variable.undefined&lt;/span>&lt;/code>&lt;/pre>&lt;p>Yes, we need to repeat the same identifier twice if we want to ignore two errors of the same type on the same line. Better safe than sorry!&lt;/p>&lt;p>We might also want to explain why an error is ignored. The description has to be put in parentheses after the identifier:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">// @phpstan-ignore offsetAccess.notFound (exists, set by a reference)&lt;/span>
&lt;span class="function token">data_set&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$target&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="token variable">$segment&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$segments&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$value&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="token variable">$overwrite&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>We can also use identifiers in our configuration’s &lt;code>ignoreErrors&lt;/code> section. This ignores all errors that match both the message and the identifier:&lt;/p>&lt;pre class="language-yaml">&lt;code class="diff-highlight language-diff-yaml">&lt;span class="atrule key token">parameters&lt;/span>&lt;span class="punctuation token">:&lt;/span>
	&lt;span class="atrule key token">ignoreErrors&lt;/span>&lt;span class="punctuation token">:&lt;/span>
		&lt;span class="punctuation token">-&lt;/span>
			&lt;span class="atrule key token">message&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="string token">'#Access to an undefined property Foo::\$[a-zA-Z0-9\\_]#'&lt;/span>
			&lt;span class="atrule key token">identifier&lt;/span>&lt;span class="punctuation token">:&lt;/span> property.notFound&lt;/code>&lt;/pre>&lt;p>Or we can ignore all errors of a specific identifier:&lt;/p>&lt;pre class="language-yaml">&lt;code class="diff-highlight language-diff-yaml">&lt;span class="atrule key token">parameters&lt;/span>&lt;span class="punctuation token">:&lt;/span>
	&lt;span class="atrule key token">ignoreErrors&lt;/span>&lt;span class="punctuation token">:&lt;/span>
		&lt;span class="punctuation token">-&lt;/span>
			&lt;span class="atrule key token">identifier&lt;/span>&lt;span class="punctuation token">:&lt;/span> property.notFound&lt;/code>&lt;/pre>&lt;p>Across PHPStan core and 1&lt;sup>st&lt;/sup> extensions there’s a total of 728 identifiers in 364 rule classes. &lt;sup class="footnote-ref">&lt;a href="#fn1" id="fnref1">[1]&lt;/a>&lt;/sup> I think I managed to strike a good middle ground with the granularity between being too coarse and being too fine.&lt;/p>&lt;h2 id="phpstan-pro-wizard" tabindex="-1">PHPStan Pro Wizard &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#phpstan-pro-wizard">#&lt;/a>&lt;/h2>&lt;p>I &lt;a href="/blog/introducing-phpstan-pro">introduced&lt;/a> PHPStan Pro in September 2020 as a paid addon for PHPStan that doesn’t remove anything from the beloved open-source version, but adds extra features that make PHPStan easier to use. Since that PHPStan Pro grew steadily and contributes about one third to my income from PHPStan. That made my open-source work sustainable and I’m really grateful for that.&lt;/p>&lt;p>I’ve had many ideas how to make PHPStan Pro more useful and interesting but I mostly sat on them for the first three years since the initial release, choosing to work on open-source PHPStan instead.&lt;/p>&lt;p>Error identifiers brought the perfect opportunity to turn to PHPStan Pro again. Right now your codebase is probably full of &lt;code>@phpstan-ignore-line&lt;/code> and &lt;code>@phpstan-ignore-next-line&lt;/code> comments. They ignore all errors on specific lines. All future errors introduced from the point you type out these comments are silently ignored without you ever seeing them. Which is dangerous.&lt;/p>&lt;p>What if you could snap your fingers and all of these comments magically turned into the new &lt;code>@phpstan-ignore&lt;/code> with the right identifiers filled out?&lt;/p>&lt;pre class="language-diff-php">&lt;code class="diff-highlight language-diff-diff-php">&lt;span class="deleted deleted-sign language-php token">&lt;span class="deleted prefix token">-&lt;/span> &lt;span class="comment token">// @phpstan-ignore-next-line&lt;/span>
&lt;/span>&lt;span class="inserted inserted-sign language-php token">&lt;span class="inserted prefix token">+&lt;/span> &lt;span class="comment token">// @phpstan-ignore argument.type&lt;/span>
&lt;/span>&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span> &lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">requireInt&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$string&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;/span>&lt;/code>&lt;/pre>&lt;p>PHPStan Pro introduces a &lt;strong>migration wizard&lt;/strong> that’s going to do this for you! Let’s run PHPStan with &lt;code>--pro&lt;/code> CLI option and see it in action:&lt;/p>&lt;video class="aspect-[1680/1080] border border-gray-200 mb-8 overflow-hidden rounded-lg w-full" autoplay muted loop playsinline poster="https://phpstan.org/ignore-line-wizard-poster.a9d06902.jpg"> &lt;source src="https://phpstan.org/ignore-line-wizard.fa7fce0b.mp4" type="video/mp4"> &lt;/video>&lt;p>With just a few clicks your codebase can be modernized and made safer thanks to this wizard.&lt;/p>&lt;p>PHPStan Pro now ships with this single wizard, but I have a lot more ideas how wizards could be used to update various aspects of your codebases related to static analysis like typehints and PHPDocs and leave them in a better state. My plan is to do regular “wizard drops” (like feature drops). As the foundation has been laid out, it shouldn’t take long for more awesome wizards to appear!&lt;/p>&lt;h2 id="phpstan-pro-ui-revamp" tabindex="-1">PHPStan Pro UI Revamp &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#phpstan-pro-ui-revamp">#&lt;/a>&lt;/h2>&lt;p>But wizards are not the only thing that has changed about PHPStan Pro. Since the beginning PHPStan Pro has been about browsing errors in a web UI instead of a terminal.&lt;/p>&lt;p>You’re probably familiar with this saying:&lt;/p>&lt;blockquote> &lt;p>If you’re not embarrassed by the first version of your product, you’ve launched too late.&lt;/p> &lt;/blockquote>&lt;p>Well I’m definitely not guilty of that 🤣 PHPStan Pro up until today did not have a good UI. Each error was rendered in a separate box so if you had multiple errors around neighbouring lines, you could quickly lose context. They weren’t presented in a clear and understandable way. Additionally, you could lose the focus on currently showing file if you reloaded the webpage. The sidebar panel wasn’t sticky so it shifted away if you scrolled a long file. It was not possible to remap Docker path to the host’s filesystem.&lt;/p>&lt;p>I could continue down this unending embarrassing list of shortcomings of the previous version, but let me put an end to that.&lt;/p>&lt;p>The new version you’ll get once you update to PHPStan 1.11 and launch Pro with &lt;code>--pro&lt;/code> is much better. Let’s have a look:&lt;/p>&lt;video class="aspect-[1652/1080] border border-gray-200 mb-8 overflow-hidden rounded-lg w-full" autoplay muted loop playsinline poster="https://phpstan.org/phpstan-pro-browsing-poster.5a0b32b8.jpg"> &lt;source src="https://phpstan.org/phpstan-pro-browsing.05ce8bbc.mp4" type="video/mp4"> &lt;/video>&lt;p>The layout is more natural and resembles an IDE. Each file is rendered just once and errors are attached to lines where they are reported. You can expand hidden lines to see more context around an error. Docker paths can be remapped so that editor links lead to the correct file.&lt;/p>&lt;p>PHPStan lets you see ignored errors too. If you’ve inherited a project and want to see what errors the previous team ignored, or if you want to check the errors lurking in your huge baseline, PHPStan Pro lets you do that with ease. By default it will show a small pill button you can use to see ignored errors near normally reported errors, but you can also change the setting to show and browse all ignored errors:&lt;/p>&lt;video class="aspect-[1656/1080] border border-gray-200 mb-8 overflow-hidden rounded-lg w-full" autoplay muted loop playsinline poster="https://phpstan.org/phpstan-pro-ignored-errors-poster.91678915.jpg"> &lt;source src="https://phpstan.org/phpstan-pro-ignored-errors.ebb52b6b.mp4" type="video/mp4"> &lt;/video>&lt;p>PHPStan Pro analyzes your codebase in the background and refreshes the UI with the latest errors automatically. If you don’t want to keep your hands warm while your laptop struggles to keep up with too-frequent analysis, you can choose to run it only when the PHPStan Pro window is in focus, or pause it altogether:&lt;/p>&lt;video class="aspect-[3624/1080] border border-gray-200 mb-8 mx-auto overflow-hidden rounded-lg w-1/2" autoplay muted loop playsinline> &lt;source src="https://phpstan.org/play-window-pause.34ffd815.mp4" type="video/mp4"> &lt;/video>&lt;p>PHPStan Pro costs &lt;strong>€7/month&lt;/strong> for individuals and &lt;strong>€70/month&lt;/strong> for teams. If you decide to pay annually, you’ll get PHPStan Pro for 12 months for the price of 10 months – that’s &lt;strong>€70/year&lt;/strong> for individual developers, &lt;strong>€700/year&lt;/strong> for teams.&lt;/p>&lt;p>There’s a 30-day free trial period for all the plans. And there’s no limit on the number of projects - you can run PHPStan Pro on any code you have on your computer.&lt;/p>&lt;p>Start by running PHPStan with &lt;code>--pro&lt;/code> or by going to &lt;a href="https://account.phpstan.com/">account.phpstan.com&lt;/a> and creating an account.&lt;/p>&lt;h2 id="is-this-function-really-pure%3F" tabindex="-1">Is this function really pure? &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#is-this-function-really-pure%3F">#&lt;/a>&lt;/h2>&lt;p>We’re not even close to the end of the list of what’s new in PHPStan 1.11.&lt;/p>&lt;p>The &lt;code>@phpstan-pure&lt;/code> annotation has been supported for a long time to mark functions that don’t have any side effects. That’s useful for &lt;a href="/blog/remembering-and-forgetting-returned-values">remembering and forgetting returned values&lt;/a> among other things.&lt;/p>&lt;p>But PHPStan didn’t enforce the truthiness of this annotation. It always obeyed it. This release changes that. I went through the trouble of deciding what language construct is pure or not for all statement and expression types. PHPStan now understands for example that &lt;code>$a + $b&lt;/code> is always pure but calling &lt;code>sleep(5)&lt;/code> or assigning a property outside of constructor is impure.&lt;/p>&lt;p>Once we have that information we can use it for multiple opportunities as marking some pieces code as wrong. Pure expressions always have to be used so it doesn’t make sense to have these on a separate line without using the results:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="token variable">$a&lt;/span> &lt;span class="operator token">+&lt;/span> &lt;span class="token variable">$b&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">ClassWithNoConstructor&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="token variable">$cb&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="keyword token">static&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="keyword token">return&lt;/span> &lt;span class="number token">1&lt;/span> &lt;span class="operator token">+&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="token variable">$cb&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// does not do anything&lt;/span>&lt;/code>&lt;/pre>&lt;p>Besides reporting functions annotated with &lt;code>@phpstan-pure&lt;/code> but having side effects as wrong, we can now also afford marking functions with &lt;code>@phpstan-impure&lt;/code> without any side effects also as wrong. And finally, &lt;code>void&lt;/code>-returning functions (which are understood as impure implicitly) that do not have any side effects are also reported as wrong. What would be a point of calling a function that doesn’t have a side effect and doesn’t return any value?&lt;/p>&lt;p>Clamping this problem space from all sides allowed us to weed out some bugs by testing development versions on real-world projects.&lt;/p>&lt;p>For all of this to be applicable to real-world situations, we also added new &lt;code>pure-callable&lt;/code> and &lt;code>pure-Closure&lt;/code> PHPDoc types that can be called even in pure functions.&lt;/p>&lt;p>Make sure to enable &lt;a href="/blog/what-is-bleeding-edge">bleeding edge&lt;/a> to take advantage of these new rules.&lt;/p>&lt;h2 id="when-is-a-passed-callable-called%3F" tabindex="-1">When is a passed callable called? &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#when-is-a-passed-callable-called%3F">#&lt;/a>&lt;/h2>&lt;p>Callables are severely underdocumented in PHP projects. You could already type and enforce the signature of a callable to make it more useful for static analysis:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @param callable(int, int): string $cb
 */&lt;/span>&lt;/code>&lt;/pre>&lt;p>But other aspects of callables remained mysterious for PHPStan. What happens to a callback when it’s passed to another function? Is it called immediately or later? This information can become handy for purity checks described in previous section, and also for &lt;a href="/blog/bring-your-exceptions-under-control">bringing exceptions under control&lt;/a>.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">function&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="function token">rand&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="number token">0&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
        &lt;span class="keyword token">throw&lt;/span> &lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">MyException&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>It would be useful for PHPStan to know if this call to method &lt;code>doFoo&lt;/code> should be surrounded with a try-catch or if the thrown &lt;code>MyException&lt;/code> should be documented in &lt;code>@throws&lt;/code> PHPDoc tag. But it needs to know if this callback is called immediately, or saved for later invocation.&lt;/p>&lt;p>PHPStan 1.11 introduces a new pair of PHPDoc tags for this purpose:&lt;/p>&lt;ul> &lt;li>&lt;code>@param-immediately-invoked-callable&lt;/code>&lt;/li> &lt;li>&lt;code>@param-later-invoked-callable&lt;/code>&lt;/li> &lt;/ul>&lt;p>The default reasonable convention if these tags are not present is that callables passed to functions are invoked immediately, and callables passed to class methods are invoked later.&lt;/p>&lt;p>You need these PHPDoc tags only to override the defaults, so you’ll use &lt;code>@param-immediately-invoked-callable&lt;/code> above methods and &lt;code>@param-later-invoked-callable&lt;/code> above functions.&lt;/p>&lt;h2 id="what-is-a-passed-closure-bound-to%3F" tabindex="-1">What is a passed closure bound to? &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#what-is-a-passed-closure-bound-to%3F">#&lt;/a>&lt;/h2>&lt;p>PHP’s methods &lt;a href="https://www.php.net/manual/en/closure.bind.php">&lt;code>Closure::bind&lt;/code>&lt;/a> and &lt;a href="https://www.php.net/manual/en/closure.bindto.php">&lt;code>Closure::bindTo&lt;/code>&lt;/a> are used to change what &lt;code>$this&lt;/code> refers to in a non-static closure. Doing that can lead to confusing PHPStan:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">// we're in class Foo&lt;/span>
&lt;span class="comment token">// $this is Foo here&lt;/span>
&lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">doSomething&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">function&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="comment token">// PHPStan still thinks $this is Foo here&lt;/span>
    &lt;span class="comment token">// but it could be something different&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>If your function binds passed closure to some other object, you can now use new PHPDoc tag &lt;code>@param-closure-this&lt;/code> to inform PHPStan about it:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @param-closure-this \stdClass $cb
 */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">callable&lt;/span> &lt;span class="token variable">$cb&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
    &lt;span class="token variable">$cb&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">bindTo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">new&lt;/span> &lt;span class="class-name class-name-fully-qualified token">&lt;span class="punctuation token">\&lt;/span>stdClass&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="comment token">// ...&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>It fully works with &lt;a href="/blog/generics-in-php-using-phpdocs">generics&lt;/a> and &lt;a href="/writing-php-code/phpdoc-types#conditional-return-types">conditional types&lt;/a> so you can really go crazy in there!&lt;/p>&lt;h2 id="new-options-for-possibly-nonexistent-offsets-in-arrays" tabindex="-1">New options for possibly nonexistent offsets in arrays &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#new-options-for-possibly-nonexistent-offsets-in-arrays">#&lt;/a>&lt;/h2>&lt;p>PHPStan by default tries not to complain too much about code that might be &lt;em>totally fine&lt;/em>. One example is accessing offsets of arrays without checking that these offsets actually exist first.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @param array&amp;lt;string, Item> $items
 */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">array&lt;/span> &lt;span class="token variable">$items&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="keyword token type-hint">string&lt;/span> &lt;span class="token variable">$key&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
    &lt;span class="comment token">// this might exist but might not&lt;/span>
    &lt;span class="token variable">$selectedItem&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="token variable">$items&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="token variable">$key&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>&lt;a href="https://github.com/phpstan/phpstan-src/pull/3028">Tom De Wit kindly contributed&lt;/a> a pair of new configuration options to have potential these issues reported.&lt;/p>&lt;p>To have the error reported in the example above, turn on &lt;a href="/config-reference#reportpossiblynonexistentgeneralarrayoffset">&lt;code>reportPossiblyNonexistentGeneralArrayOffset&lt;/code>&lt;/a>.&lt;/p>&lt;p>To have the same error reported for constant arrays, also known as &lt;a href="/writing-php-code/phpdoc-types#array-shapes">array shapes&lt;/a>, turn on &lt;a href="/config-reference#reportpossiblynonexistentconstantarrayoffset">&lt;code>reportPossiblyNonexistentConstantArrayOffset&lt;/code>&lt;/a>.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">string&lt;/span> &lt;span class="token variable">$s&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
    &lt;span class="token variable">$a&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="punctuation token">[&lt;/span>&lt;span class="single-quoted-string string token">'foo'&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>
    &lt;span class="keyword token">echo&lt;/span> &lt;span class="token variable">$a&lt;/span>&lt;span class="punctuation token">[&lt;/span>&lt;span class="token variable">$s&lt;/span>&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>My personal preference is to only turn on the latter option, but your experience may vary.&lt;/p>&lt;hr>&lt;p>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="https://github.com/phpstan/phpstan/discussions">waiting for everyone’s feedback on GitHub&lt;/a>.&lt;/p>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="/sponsor">&lt;strong>Consider sponsoring&lt;/strong> further development of PHPStan on GitHub Sponsors and also &lt;strong>subscribe to PHPStan Pro&lt;/strong>&lt;/a>! I’d really appreciate it!&lt;/p>&lt;hr class="footnotes-sep">&lt;section class="footnotes"> &lt;ol class="footnotes-list"> &lt;li id="fn1" class="footnote-item">&lt;p>The fact there are exactly two identifiers per rule class on average is a total coincidence. It made me re-check my algorithm that counts them. &lt;a href="#fnref1" class="footnote-backref">↩︎&lt;/a>&lt;/p> &lt;/li> &lt;/ol> &lt;/section></content>
		</entry>
	
		
		<entry>
			<title>Enhancements in Handling Parameters Passed by Reference in PHPStan 1.10.60</title>
			<link href="https://phpstan.org/blog/enhancements-in-handling-parameters-passed-by-reference"/>
			<updated>2024-03-07T00:00:00Z</updated>
			<id>https://phpstan.org/blog/enhancements-in-handling-parameters-passed-by-reference</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>&lt;a href="https://github.com/phpstan/phpstan/releases/tag/1.10.60">PHPStan 1.10.60&lt;/a> introduces enhancements in handling parameters passed by reference. These parameters, besides typical returned values, also serve as a form of output that a called function can produce:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="boolean constant token">true&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="token variable">$v&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="boolean constant token">false&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">var_dump&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// bool(true)&lt;/span>&lt;/code>&lt;/pre>&lt;p>You might be surprised to learn that you can set a type for a parameter passed by reference, but it doesn’t limit the output type:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="token variable">$v&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="single-quoted-string string token">'foo'&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">var_dump&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// int(1)&lt;/span>&lt;/code>&lt;/pre>&lt;p>Previously, PHPStan’s type inference discarded the type of a variable passed into such a parameter:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="token variable">$v&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="single-quoted-string string token">'foo'&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">&lt;span class="punctuation token">\&lt;/span>PHPStan&lt;span class="punctuation token">\&lt;/span>dumpType&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// mixed&lt;/span>&lt;/code>&lt;/pre>&lt;p>When analysing code that calls a function, PHPStan relies on function’s signature and PHPDoc to understand what’s going to happen. It does not dive into the function implementation because it’d hurt the performance of the analyser. So changing the type of the variable to &lt;code>mixed&lt;/code> was a sensible solution - we didn’t know what’s going on inside the function so we could not assume anything.&lt;/p>&lt;p>In the latest release, this behavior has changed.&lt;/p>&lt;p>All of the following changes are part of &lt;a href="/blog/what-is-bleeding-edge">Bleeding Edge&lt;/a> because we intend to keep our generous &lt;a href="/user-guide/backward-compatibility-promise">backward compatibility promise&lt;/a>. You can get them in PHPStan 1.10.60 today if you include the Bleeding Edge config. Everybody else will get them as part of the next major release, PHPStan 2.0.&lt;/p>&lt;h2 id="checking-the-type-of-argument-passed-into-a-parameter-by-reference" tabindex="-1">Checking the Type of Argument Passed into a Parameter by Reference &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#checking-the-type-of-argument-passed-into-a-parameter-by-reference">#&lt;/a>&lt;/h2>&lt;p>The recent &lt;a href="https://github.com/phpstan/phpstan-src/pull/2941">pull request&lt;/a> by Lincoln Maskey initiated the development of these enhancements.&lt;/p>&lt;p>Previously, PHPStan skipped type checking for all parameters passed by reference. Consequently, code like the following did not trigger any warnings:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// ...&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="token variable">$v&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>This was because some internal built-in PHP functions have parameters passed by reference in their signature, but the type enforcement didn’t match that of userland functions.&lt;/p>&lt;p>However, PHPStan is now equipped to check the type in such cases, ensuring more comprehensive type validation moving forward.&lt;/p>&lt;h2 id="assumption-of-output-type" tabindex="-1">Assumption of Output Type &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#assumption-of-output-type">#&lt;/a>&lt;/h2>&lt;p>Instead of setting the type of the passed variable to &lt;code>mixed&lt;/code>, we now assume that the user does not intend to change the type entirely:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// ...&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="token variable">$v&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="single-quoted-string string token">'foo'&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">&lt;span class="punctuation token">\&lt;/span>PHPStan&lt;span class="punctuation token">\&lt;/span>dumpType&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// string&lt;/span>&lt;/code>&lt;/pre>&lt;p>However, this creates a problem: what if we do want to change the type? You can still achieve this using &lt;a href="/writing-php-code/phpdocs-basics#setting-parameter-type-passed-by-reference">&lt;code>@param-out&lt;/code>&lt;/a> PHPDoc tag:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @param-out int $p
 */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="token variable">$v&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="single-quoted-string string token">'foo'&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="function token">&lt;span class="punctuation token">\&lt;/span>PHPStan&lt;span class="punctuation token">\&lt;/span>dumpType&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$v&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// int&lt;/span>&lt;/code>&lt;/pre>&lt;h2 id="enforcing-assigned-type" tabindex="-1">Enforcing Assigned Type &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#enforcing-assigned-type">#&lt;/a>&lt;/h2>&lt;p>Psst, I’ll let you in on a little secret. Not all extra PHPDoc features static analysers offer are actually enforced by their rules. &lt;code>@param-out&lt;/code> was one example. You could have assigned anything to the variable and PHPStan would not complain.&lt;/p>&lt;p>That changes today. These assignments are now checked by new set of rules:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// Parameter &amp;amp;$p by-ref type of function foo() expects string, int given.&lt;/span>
	&lt;span class="comment token">// Tip: You can change the parameter out type with @param-out PHPDoc tag.&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>Yes, PHPStan will even contextually hint to you that you can write a &lt;code>@param-out&lt;/code> PHPDoc tag if the assignment to an integer is intentional. These tips can be seen next to a 💡 in the CLI output, and also as &lt;a href="/r/846c85d9-2159-4a18-9118-04eca47e266d">a small grey text on the on-line playground&lt;/a>.&lt;/p>&lt;p>If &lt;code>@param-out&lt;/code> is already involved, the message is a little bit different:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @param-out string $p
 */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// Parameter &amp;amp;$p @param-out type of function foo() expects string, int given.&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>And if the type of &lt;code>@param-out&lt;/code> is different to the input type, but we don’t reassign the variable, PHPStan is also able to notice it:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @param-out int $p
 */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span> &lt;span class="comment token">// Parameter &amp;amp;$p @param-out type of function foo() expects int, string given.&lt;/span>
&lt;span class="punctuation token">{&lt;/span>

&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;h2 id="detecting-overly-broad-%40param-out-type" tabindex="-1">Detecting Overly Broad &lt;code>@param-out&lt;/code> Type &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#detecting-overly-broad-%40param-out-type">#&lt;/a>&lt;/h2>&lt;p>Similar to how PHPStan handles return types, it now detects when a union type in the output parameter type includes unused parts:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="operator token">?&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// Function foo() never assigns null to &amp;amp;$p so it can be removed from the by-ref type.&lt;/span>
	&lt;span class="comment token">// Tip: You can narrow the parameter out type with @param-out PHPDoc tag.&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="single-quoted-string string token">'foo'&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>If your function accepts &lt;code>null&lt;/code> but the variable never leaves the function as &lt;code>null&lt;/code> anymore, you should inform the caller with the &lt;code>@param-out&lt;/code> tag as well:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @param-out string $p
 */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="operator token">?&lt;/span>&lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="operator token">&amp;amp;&lt;/span>&lt;span class="token variable">$p&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// No errors&lt;/span>
	&lt;span class="token variable">$p&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="single-quoted-string string token">'foo'&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="/sponsor">&lt;strong>Consider supporting further development of PHPStan&lt;/strong>&lt;/a>. I’d really appreciate it!&lt;/p></content>
		</entry>
	
		
		<entry>
			<title>A guide to call-site generic variance</title>
			<link href="https://phpstan.org/blog/guide-to-call-site-generic-variance"/>
			<updated>2023-09-19T00:00:00Z</updated>
			<id>https://phpstan.org/blog/guide-to-call-site-generic-variance</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>PHPStan has supported what is called declaration-site variance for a long time. An example you might be familiar with is the infamous &lt;a href="/blog/whats-up-with-template-covariant">&lt;code>@template-covariant&lt;/code>&lt;/a>. And although not as often useful, it also has &lt;a href="https://jiripudil.cz/blog/contravariant-template-types">a contravariant counterpart&lt;/a>.&lt;/p>&lt;p>To freshen your memory: you can mark a template type as covariant:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @template-covariant ItemType */&lt;/span>
&lt;span class="keyword token">interface&lt;/span> &lt;span class="class-name class-name-definition token">Collection&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">/** @param ItemType $item */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">mixed&lt;/span> &lt;span class="token variable">$item&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>&lt;span class="punctuation token">;&lt;/span>

	&lt;span class="comment token">/** @return ItemType|null */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">int&lt;/span> &lt;span class="token variable">$index&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">mixed&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>This allows you to pass &lt;code>Collection&amp;lt;Cat&amp;gt;&lt;/code> into functions where &lt;code>Collection&amp;lt;Animal&amp;gt;&lt;/code> is expected. But it comes at a cost:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @param Collection&amp;lt;Animal> $animals */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Collection&lt;/span> &lt;span class="token variable">$animals&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="token variable">$animals&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">Dog&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>By itself, this is a perfectly valid code. But with &lt;code>Collection&lt;/code> being covariant over its template type, we can now mix cats with dogs. That’s why PHPStan prevents us from using the &lt;code>ItemType&lt;/code> in &lt;code>Collection::add()&lt;/code> method’s parameter:&lt;/p>&lt;blockquote> &lt;p>Template type ItemType is declared as covariant, but occurs in contravariant position in parameter item of method &lt;code>Collection::add()&lt;/code>.&lt;/p> &lt;/blockquote>&lt;p>That’s the trade-off: if you want the &lt;code>Collection&lt;/code> to be covariant, it can only have the &lt;code>get&lt;/code> method. If you want it to have the &lt;code>add&lt;/code> method too, it has to be invariant in its &lt;code>ItemType&lt;/code>.&lt;/p>&lt;p>Until now, there has not been an easy way around this. You could split the interface into a read-only one that can safely be covariant, and an invariant one:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @template-covariant ItemType */&lt;/span>
&lt;span class="keyword token">interface&lt;/span> &lt;span class="class-name class-name-definition token">ReadonlyCollection&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">/** @return ItemType|null */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">int&lt;/span> &lt;span class="token variable">$index&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">mixed&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="comment token">/**
 * @template ItemType
 * @extends ReadonlyCollection&amp;lt;ItemType>
 */&lt;/span>
&lt;span class="keyword token">interface&lt;/span> &lt;span class="class-name class-name-definition token">Collection&lt;/span> &lt;span class="keyword token">extends&lt;/span> &lt;span class="class-name token">ReadonlyCollection&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">/** @param ItemType $item */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">mixed&lt;/span> &lt;span class="token variable">$item&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>But that is a lot of work and can quickly get tedious. Call-site variance is a way of having PHPStan do this work for you.&lt;/p>&lt;h2 id="call-site-variance" tabindex="-1">Call-site variance &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#call-site-variance">#&lt;/a>&lt;/h2>&lt;p>As the name suggests, call-site variance (or type projections, if you prefer fancier words) moves the variance annotation from the declaration to the call site. This means that the declaration of the interface can remain invariant, and therefore contain both &lt;code>get&lt;/code> and &lt;code>add&lt;/code> methods:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @template ItemType */&lt;/span>
&lt;span class="keyword token">interface&lt;/span> &lt;span class="class-name class-name-definition token">Collection&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">/** @param ItemType $item */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">mixed&lt;/span> &lt;span class="token variable">$item&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>&lt;span class="punctuation token">;&lt;/span>

	&lt;span class="comment token">/** @return ItemType|null */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">int&lt;/span> &lt;span class="token variable">$index&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">mixed&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>If you need a specific function to accept &lt;code>Collection&amp;lt;Cat&amp;gt;&lt;/code> in place of &lt;code>Collection&amp;lt;Animal&amp;gt;&lt;/code>, you can instruct it so by attaching the &lt;code>covariant&lt;/code> keyword to the generic type argument:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @param Collection&amp;lt;covariant Animal> $animals */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">foo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Collection&lt;/span> &lt;span class="token variable">$animals&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="token variable">$animals&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">Dog&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>Correspondingly, the error has moved from the declaration to the call-site: if the implementation does something that would break type safety, like adding a &lt;code>Dog&lt;/code> into the collection above, PHPStan will tell us:&lt;/p>&lt;blockquote> &lt;p>Parameter #1 $item of method &lt;code>Collection&amp;lt;covariant Animal&amp;gt;::add()&lt;/code> expects never, Dog given.&lt;/p> &lt;/blockquote>&lt;h2 id="call-site-contravariance" tabindex="-1">Call-site contravariance &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#call-site-contravariance">#&lt;/a>&lt;/h2>&lt;p>Although not as useful, it’s worth mentioning that you can also use contravariant type projections. For example, if we wanted a happy little function that fills a collection with dogs, we could make it so that it accepts not only &lt;code>Collection&amp;lt;Dog&amp;gt;&lt;/code>, but also &lt;code>Collection&amp;lt;Animal&amp;gt;&lt;/code>, or even &lt;code>Collection&amp;lt;mixed&amp;gt;&lt;/code>:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @param Collection&amp;lt;contravariant Dog> $collection */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">fill&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Collection&lt;/span> &lt;span class="token variable">$collection&lt;/span>&lt;span class="punctuation token">)&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">while&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="boolean constant token">true&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
		&lt;span class="token variable">$collection&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">Dog&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>This too has a limitation, but at least this time it’s not so drastic: if we wanted to &lt;code>get&lt;/code> a value from the collection, we can:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @param Collection&amp;lt;contravariant Dog> $collection */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">fill&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Collection&lt;/span> &lt;span class="token variable">$collection&lt;/span>&lt;span class="punctuation token">)&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">while&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$collection&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="number token">42&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="operator token">===&lt;/span> &lt;span class="constant token">null&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
		&lt;span class="token variable">$collection&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">Dog&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>But because a contravariant type is not bounded from the top, we cannot make any assumption about the type of the retrieved item, and neither can PHPStan, therefore the type of &lt;code>$collection-&amp;gt;get(42)&lt;/code> would be &lt;code>mixed&lt;/code>.&lt;/p>&lt;h2 id="star-projections" tabindex="-1">Star projections &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#star-projections">#&lt;/a>&lt;/h2>&lt;p>Sometimes, you just don’t care about the type of the values you’re working with. Let’s add a &lt;code>count()&lt;/code> method to the &lt;code>Collection&lt;/code>:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @template ItemType */&lt;/span>
&lt;span class="keyword token">interface&lt;/span> &lt;span class="class-name class-name-definition token">Collection&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">/** @param ItemType $item */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">add&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">mixed&lt;/span> &lt;span class="token variable">$item&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>&lt;span class="punctuation token">;&lt;/span>

	&lt;span class="comment token">/** @return ItemType|null */&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">int&lt;/span> &lt;span class="token variable">$index&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">mixed&lt;/span>&lt;span class="punctuation token">;&lt;/span>

	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">count&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">int&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>This method doesn’t reference the &lt;code>ItemType&lt;/code> template type at all. We are using it in a function &lt;code>printSize&lt;/code> that prints the size of a collection. In this case, the function can accept a collection of &lt;em>anything&lt;/em>. Inspired by other languages such as Kotlin, PHPStan provides an idiomatic way of writing this, using an asterisk:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @param Collection&amp;lt;*> $collection */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">printSize&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Collection&lt;/span> &lt;span class="token variable">$collection&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">int&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">echo&lt;/span> &lt;span class="token variable">$collection&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">count&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>Obviously, we cannot make any assumptions about the collection’s item type &lt;em>whatsoever&lt;/em>. In other words, star projections combine the limitations of covariant and contravariant projections. If we were to &lt;code>add()&lt;/code> anything into the collection inside this &lt;code>printSize&lt;/code> function, we would get a similar error as above:&lt;/p>&lt;blockquote> &lt;p>Parameter #1 $item of method &lt;code>Collection&amp;lt;*&amp;gt;::add()&lt;/code> expects never, Dog given.&lt;/p> &lt;/blockquote>&lt;p>And if we wanted to &lt;code>get&lt;/code> a value from the collection, it would be &lt;code>mixed&lt;/code>:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @param Collection&amp;lt;*> $collection */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">printSize&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Collection&lt;/span> &lt;span class="token variable">$collection&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">int&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="token variable">$item&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="token variable">$collection&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="number token">0&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="comment token">// $item is mixed&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre></content>
		</entry>
	
		
		<entry>
			<title>Using RuleErrorBuilder to enrich reported errors in custom rules</title>
			<link href="https://phpstan.org/blog/using-rule-error-builder"/>
			<updated>2023-07-12T00:00:00Z</updated>
			<id>https://phpstan.org/blog/using-rule-error-builder</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>When writing &lt;a href="/developing-extensions/rules">custom rules&lt;/a> for PHPStan, developers can either return plain strings or RuleError instances.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @phpstan-param TNodeType $node
 * @return (string|RuleError)[] errors
 */&lt;/span>
&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">processNode&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Node&lt;/span> &lt;span class="token variable">$node&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="class-name token type-declaration">Scope&lt;/span> &lt;span class="token variable">$scope&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">array&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>The latter is a way to attach additional information to the reported errors.&lt;/p>&lt;p>RuleError &lt;a href="https://github.com/phpstan/phpstan-src/blob/1.12.x/src/Rules/RuleError.php">is just an interface&lt;/a>. The way you create an instance is through &lt;a href="https://apiref.phpstan.org/1.12.x/PHPStan.Rules.RuleErrorBuilder.html">RuleErrorBuilder&lt;/a>:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">return&lt;/span> &lt;span class="punctuation token">[&lt;/span>
	&lt;span class="class-name static-context token">RuleErrorBuilder&lt;/span>&lt;span class="operator token">::&lt;/span>&lt;span class="function token">message&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="single-quoted-string string token">'This is an error message.'&lt;/span>&lt;span class="punctuation token">)&lt;/span>
		&lt;span class="operator token">->&lt;/span>&lt;span class="function token">build&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">,&lt;/span>
&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>Besides setting an error message RuleErrorBuilder offers the following capabilities:&lt;/p>&lt;ul> &lt;li>&lt;code>-&amp;gt;line(int $line)&lt;/code>: Set a different file line. Useful when you want to report a different line than the node which the rule was called for&lt;/li> &lt;li>&lt;code>-&amp;gt;file(string $file)&lt;/code>: Set a different file path. Useful for &lt;a href="/developing-extensions/collectors">collector rules&lt;/a>.&lt;/li> &lt;li>&lt;code>-&amp;gt;tip(string $tip)&lt;/code>: Shows an additional text next to a 💡 emoji on the command line. You can tell the user why the error is reported or how to solve it.&lt;/li> &lt;li>&lt;code>-&amp;gt;nonIgnorable()&lt;/code>: Makes the error non-ignorable by any means.&lt;/li> &lt;li>&lt;code>-&amp;gt;metadata(array&amp;lt;mixed&amp;gt; $metadata)&lt;/code>: Attach additional metadata to the error that can later be read in the &lt;a href="/developing-extensions/error-formatters">error formatter&lt;/a>.&lt;/li> &lt;li>&lt;code>-&amp;gt;identifier(string $identifier)&lt;/code>: Sets an error identifier. More about this below.&lt;/li> &lt;/ul>&lt;h2 id="error-identifiers" tabindex="-1">Error identifiers &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#error-identifiers">#&lt;/a>&lt;/h2>&lt;p>The flagship feature of the upcoming PHPStan 1.11 release are error identifiers. You will be able to use them to ignore specific errors:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// @phpstan-ignore argument.type&lt;/span>
	&lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">foo&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">doSomethingWithString&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="number token">1&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>

	&lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">foo&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="function token">doSomethingWithString&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="number token">2&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// @phpstan-ignore argument.type&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>There’s a &lt;a href="/error-identifiers">generated catalogue&lt;/a> of all the identifiers in PHPStan itself and 1&lt;sup>st&lt;/sup> party extensions. Each identifier links to source code where it’s reported so this serves as a great educational resource about PHPStan internals.&lt;/p>&lt;p>To ensure great experience for all users, custom rules will also be required to provide their own identifiers. At first it will only be soft-enforced in &lt;a href="/blog/what-is-bleeding-edge">Bleeding Edge&lt;/a>, and in PHPStan 2.0 it will be hard-enforced with a native return type.&lt;/p>&lt;p>This means that the ability to return plain strings from custom rules &lt;strong>will eventually go away&lt;/strong>. I recommend you to switch to RuleErrorBuilder sooner rather than later.&lt;/p>&lt;p>PHPStan 1.11 with Bleeding Edge enabled will require custom rules to return errors with identifiers. As a first step, remove any PHPDoc above your &lt;code>processNode&lt;/code> method. If your rule does not yet have &lt;code>@implements Rule&amp;lt;...&amp;gt;&lt;/code> generic PHPDoc tag, add it:&lt;/p>&lt;pre class="language-diff-php">&lt;code class="diff-highlight language-diff-diff-php">&lt;span class="inserted inserted-sign language-php token">&lt;span class="inserted prefix token">+&lt;/span>&lt;span class="comment token">/**
&lt;span class="inserted prefix token">+&lt;/span> * @implements Rule&amp;lt;StaticCall>
&lt;span class="inserted prefix token">+&lt;/span> */&lt;/span>
&lt;/span>&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>&lt;span class="keyword token">class&lt;/span> &lt;span class="class-name class-name-definition token">MyRule&lt;/span> &lt;span class="keyword token">implements&lt;/span> &lt;span class="class-name token">Rule&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>&lt;span class="punctuation token">{&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">getNodeType&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">string&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">{&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>		&lt;span class="keyword token">return&lt;/span> &lt;span class="class-name static-context token">StaticCall&lt;/span>&lt;span class="operator token">::&lt;/span>&lt;span class="keyword token">class&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">}&lt;/span>
&lt;/span>
&lt;span class="deleted deleted-sign language-php token">&lt;span class="deleted prefix token">-&lt;/span>	&lt;span class="comment token">/**
&lt;span class="deleted prefix token">-&lt;/span>	 * @param StaticCall $node
&lt;span class="deleted prefix token">-&lt;/span>	 * @param Scope $scope
&lt;span class="deleted prefix token">-&lt;/span>	 * @return string[]
&lt;span class="deleted prefix token">-&lt;/span>	 */&lt;/span>
&lt;/span>&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">processNode&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Node&lt;/span> &lt;span class="token variable">$node&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="class-name token type-declaration">Scope&lt;/span> &lt;span class="token variable">$scope&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">array&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">{&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>		&lt;span class="comment token">// ...&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">}&lt;/span>
&lt;/span>&lt;/code>&lt;/pre>&lt;p>Even without the PHPDoc both PHPStan and PhpStorm will understand that the parameter &lt;code>$node&lt;/code> coming into the &lt;code>processNode&lt;/code> method is a &lt;code>StaticCall&lt;/code>.&lt;/p>&lt;p>After that migrate the plain strings to RuleErrorBuilder, and add error identifiers for each of them:&lt;/p>&lt;pre class="language-diff-php">&lt;code class="diff-highlight language-diff-diff-php">&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">processNode&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Node&lt;/span> &lt;span class="token variable">$node&lt;/span>&lt;span class="punctuation token">,&lt;/span> &lt;span class="class-name token type-declaration">Scope&lt;/span> &lt;span class="token variable">$scope&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">array&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">{&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>		&lt;span class="keyword token">return&lt;/span> &lt;span class="punctuation token">[&lt;/span>
&lt;/span>&lt;span class="deleted deleted-sign language-php token">&lt;span class="deleted prefix token">-&lt;/span>			&lt;span class="single-quoted-string string token">'This is an error message.'&lt;/span>&lt;span class="punctuation token">,&lt;/span>
&lt;/span>&lt;span class="inserted inserted-sign language-php token">&lt;span class="inserted prefix token">+&lt;/span>			&lt;span class="class-name static-context token">RuleErrorBuilder&lt;/span>&lt;span class="operator token">::&lt;/span>&lt;span class="function token">message&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="single-quoted-string string token">'This is an error message.'&lt;/span>&lt;span class="punctuation token">)&lt;/span>
&lt;span class="inserted prefix token">+&lt;/span>				&lt;span class="operator token">->&lt;/span>&lt;span class="function token">identifier&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="single-quoted-string string token">'some.problem'&lt;/span>&lt;span class="punctuation token">)&lt;/span>
&lt;span class="inserted prefix token">+&lt;/span>				&lt;span class="operator token">->&lt;/span>&lt;span class="function token">build&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">,&lt;/span>
&lt;/span>&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>		&lt;span class="punctuation token">]&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">}&lt;/span>
&lt;/span>&lt;/code>&lt;/pre>&lt;p>For an inspiration how the identifiers should look like check out &lt;a href="/error-identifiers">the catalogue&lt;/a>.&lt;/p>&lt;p>The identifier must consist of lowercase and uppercase ASCII letters, and optionally can have one or more dots in the middle. &lt;a href="https://github.com/phpstan/phpstan-src/blob/1.12.x/tests/PHPStan/Analyser/ErrorTest.php">See the tests&lt;/a> for examples of valid and invalid identifiers.&lt;/p>&lt;hr>&lt;p>PHPStan 1.11 will be released at some time in the coming months. Don’t worry, your old rules will not break with that release, but it would be great to modernize them to take advantage (and let your users take advantage) of the latest PHPStan features. Thanks!&lt;/p></content>
		</entry>
	
		
		<entry>
			<title>Solving PHPStan error "Access to an undefined property"</title>
			<link href="https://phpstan.org/blog/solving-phpstan-access-to-undefined-property"/>
			<updated>2023-03-31T00:00:00Z</updated>
			<id>https://phpstan.org/blog/solving-phpstan-access-to-undefined-property</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>“Access to an undefined property &lt;code>Foo::$x&lt;/code>” is one of the most common errors you will encounter when running PHPStan. Here are all the ways you can solve it.&lt;/p>&lt;h2 id="fix-the-name" tabindex="-1">Fix the name &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#fix-the-name">#&lt;/a>&lt;/h2>&lt;p>The easiest situation you can find yourself in is that there’s a typo in the source code, and the property access needs to be corrected to the right name:&lt;/p>&lt;pre class="language-diff-php">&lt;code class="diff-highlight language-diff-diff-php">&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">getName&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">string&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">{&lt;/span>
&lt;/span>&lt;span class="deleted deleted-sign language-php token">&lt;span class="deleted prefix token">-&lt;/span>		&lt;span class="keyword token">return&lt;/span> &lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">nama&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;/span>&lt;span class="inserted inserted-sign language-php token">&lt;span class="inserted prefix token">+&lt;/span>		&lt;span class="keyword token">return&lt;/span> &lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">name&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;/span>&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">}&lt;/span>
&lt;/span>&lt;/code>&lt;/pre>&lt;h2 id="narrow-the-type-the-property-is-accessed-on" tabindex="-1">Narrow the type the property is accessed on &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#narrow-the-type-the-property-is-accessed-on">#&lt;/a>&lt;/h2>&lt;p>If you encounter errors like:&lt;/p>&lt;blockquote> &lt;p>Access to an undefined property object::$a. Cannot access property $a on mixed.&lt;/p> &lt;/blockquote>&lt;p>The problem isn’t the name of the property but the type we’re accessing the property on:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">// $object is object&lt;/span>
&lt;span class="keyword token">echo&lt;/span> &lt;span class="token variable">$object&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">a&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>We can’t be sure the property exists on this type because it can be an object of any class. The solution is to &lt;a href="/writing-php-code/narrowing-types">narrow the type first&lt;/a> and only then access the property.&lt;/p>&lt;h2 id="declare-the-property" tabindex="-1">Declare the property &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#declare-the-property">#&lt;/a>&lt;/h2>&lt;p>In older versions of PHP you weren’t required to declare the property, and the code might have actually run fine. But for obvious type safety reasons it’s better to declare the property if it’s missing:&lt;/p>&lt;pre class="language-diff-php">&lt;code class="diff-highlight language-diff-diff-php">&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>&lt;span class="keyword token">class&lt;/span> &lt;span class="class-name class-name-definition token">HelloWorld&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>&lt;span class="punctuation token">{&lt;/span>
&lt;/span>
&lt;span class="inserted inserted-sign language-php token">&lt;span class="inserted prefix token">+&lt;/span>	&lt;span class="keyword token">private&lt;/span> &lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="token variable">$name&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="inserted prefix token">+&lt;/span>
&lt;/span>&lt;span class="language-php token unchanged">&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">setName&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">string&lt;/span> &lt;span class="token variable">$name&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>	&lt;span class="punctuation token">{&lt;/span>
&lt;span class="prefix token unchanged"> &lt;/span>		&lt;span class="token variable">$this&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">name&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="token variable">$name&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;/span>&lt;/code>&lt;/pre>&lt;h2 id="php-8.2%3A-add-%23%5Ballowdynamicproperties%5D" tabindex="-1">PHP 8.2: Add &lt;code>#[AllowDynamicProperties]&lt;/code> &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#php-8.2%3A-add-%23%5Ballowdynamicproperties%5D">#&lt;/a>&lt;/h2>&lt;p>If the class is meant to have dynamic properties (when they vary from object to object), it needs to meet one of the following conditions:&lt;/p>&lt;ul> &lt;li>Have &lt;code>__get&lt;/code> and/or &lt;code>__set&lt;/code> magic methods for handling dynamic properties&lt;/li> &lt;li>Be &lt;code>stdClass&lt;/code> or extend &lt;code>stdClass&lt;/code>&lt;/li> &lt;li>Have &lt;code>#[AllowDynamicProperties]&lt;/code> class attribute&lt;/li> &lt;/ul>&lt;p>This is because &lt;a href="https://php.watch/versions/8.2/dynamic-properties-deprecated">dynamic properties have been deprecated&lt;/a> in PHP 8.2+.&lt;/p>&lt;p>Addressing these requirements will &lt;strong>not make the PHPStan error go away&lt;/strong>, but it will &lt;strong>allow&lt;/strong> the following solutions to work if you use PHP 8.2 or later.&lt;/p>&lt;h2 id="universal-object-crates" tabindex="-1">Universal object crates &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#universal-object-crates">#&lt;/a>&lt;/h2>&lt;p>Classes without predefined structure are common in PHP applications. They are used as universal holders of data - any property can be set and read on them. Notable examples include &lt;code>stdClass&lt;/code>, &lt;code>SimpleXMLElement&lt;/code> (these are enabled by default), objects with results of database queries etc. Use &lt;a href="/config-reference#universal-object-crates">&lt;code>universalObjectCratesClasses&lt;/code>&lt;/a> key to let PHPStan know which classes with these characteristics are used in your codebase by setting them in your &lt;a href="/config-reference">configuration file&lt;/a>:&lt;/p>&lt;pre class="language-yaml">&lt;code class="diff-highlight language-diff-yaml">&lt;span class="atrule key token">parameters&lt;/span>&lt;span class="punctuation token">:&lt;/span>
	&lt;span class="atrule key token">universalObjectCratesClasses&lt;/span>&lt;span class="punctuation token">:&lt;/span>
		&lt;span class="punctuation token">-&lt;/span> Dibi\Row
		&lt;span class="punctuation token">-&lt;/span> Ratchet\ConnectionInterface&lt;/code>&lt;/pre>&lt;p>See also &lt;a href="/writing-php-code/phpdoc-types#object-shapes">object shape&lt;/a> PHPDoc type for a better alternative that lets you describe types of properties of such objects.&lt;/p>&lt;h2 id="add-%40property-phpdoc" tabindex="-1">Add &lt;code>@property&lt;/code> PHPDoc &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#add-%40property-phpdoc">#&lt;/a>&lt;/h2>&lt;p>If the class features &lt;a href="/writing-php-code/phpdocs-basics#magic-properties">magic properties&lt;/a> but these properties are always the same, you can declare them with &lt;code>@property&lt;/code> PHPDocs:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @property int $foo
 * @property-read string $bar
 * @property-write \stdClass $baz
 */&lt;/span>
&lt;span class="keyword token">class&lt;/span> &lt;span class="class-name class-name-definition token">Foo&lt;/span> &lt;span class="punctuation token">{&lt;/span> &lt;span class="operator token">...&lt;/span> &lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;h2 id="making-%40property-phpdoc-above-interfaces-work-on-php-8.2%2B" tabindex="-1">Making &lt;code>@property&lt;/code> PHPDoc above interfaces work on PHP 8.2+ &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#making-%40property-phpdoc-above-interfaces-work-on-php-8.2%2B">#&lt;/a>&lt;/h2>&lt;div class="bg-green-100 border border-green-600 inline-block mb-4 px-1 rounded text-green-600 text-xs">Available in PHPStan 1.10.56&lt;/div>&lt;p>You might find that the following code is not sufficient to declare a property on an interface when you analyse code in PHP 8.2 and newer:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @property string $bar
 */&lt;/span>
&lt;span class="keyword token">interface&lt;/span> &lt;span class="class-name class-name-definition token">Foo&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="keyword token">function&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Foo&lt;/span> &lt;span class="token variable">$foo&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="comment token">// Error: Access to an undefined property Foo::$bar.&lt;/span>
    &lt;span class="keyword token">echo&lt;/span> &lt;span class="token variable">$foo&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">bar&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>That’s because PHP 8.2 requires &lt;a href="#php-8.2%3A-add-%23%5Ballowdynamicproperties%5D">extra steps to allow dynamic properties&lt;/a>. And there’s actually no way in the language to make these extra steps work on interfaces.&lt;/p>&lt;p>Fortunately there’s a PHPDoc feature that helps you get rid of this error. It’s &lt;code>@phpstan-require-extends&lt;/code>. Class implementing an interface that uses this PHPDoc tag is required to extend a parent referenced in this tag. If this parent class allows dynamic properties, it makes this error go away. Additionally, it makes all properties and methods from this parent class also available when working with the interface.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/**
 * @property string $bar
 * @phpstan-require-extends Model
 */&lt;/span>
&lt;span class="keyword token">interface&lt;/span> &lt;span class="class-name class-name-definition token">Foo&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="keyword token">class&lt;/span> &lt;span class="class-name class-name-definition token">Model&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
    &lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">__get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">string&lt;/span> &lt;span class="token variable">$name&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">mixed&lt;/span>
    &lt;span class="punctuation token">{&lt;/span>
        &lt;span class="comment token">// some magic logic for dynamic properties&lt;/span>
    &lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="keyword token">function&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name token type-declaration">Foo&lt;/span> &lt;span class="token variable">$foo&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="comment token">// OK - No error&lt;/span>
    &lt;span class="keyword token">echo&lt;/span> &lt;span class="token variable">$foo&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">bar&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>&lt;a href="/writing-php-code/phpdocs-basics#enforcing-class-inheritance-for-interfaces-and-traits">Learn more about &lt;code>@phpstan-require-extends&lt;/code> and &lt;code>@phpstan-require-implements&lt;/code> »&lt;/a>&lt;/p>&lt;h2 id="mixin" tabindex="-1">Mixin &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#mixin">#&lt;/a>&lt;/h2>&lt;p>When a class delegates unknown method calls and property accesses to a different class using &lt;code>__call&lt;/code> and &lt;code>__get&lt;/code>/&lt;code>__set&lt;/code>, we can describe the relationship using &lt;code>@mixin&lt;/code> PHPDoc tag:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">class&lt;/span> &lt;span class="class-name class-name-definition token">A&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token type-declaration">string&lt;/span> &lt;span class="token variable">$name&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="single-quoted-string string token">'Class A'&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="comment token">/**
 * @mixin A
 */&lt;/span>
&lt;span class="keyword token">class&lt;/span> &lt;span class="class-name class-name-definition token">B&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">__get&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token type-hint">string&lt;/span> &lt;span class="token variable">$name&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">mixed&lt;/span>
	&lt;span class="punctuation token">{&lt;/span>
		&lt;span class="keyword token">return&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">A&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="token variable">$name&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="token variable">$b&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="keyword token">new&lt;/span> &lt;span class="class-name token">B&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>
&lt;span class="keyword token">echo&lt;/span> &lt;span class="token variable">$b&lt;/span>&lt;span class="operator token">->&lt;/span>&lt;span class="property token">name&lt;/span>&lt;span class="punctuation token">;&lt;/span> &lt;span class="comment token">// No error&lt;/span>&lt;/code>&lt;/pre>&lt;h2 id="extensions-for-popular-frameworks" tabindex="-1">Extensions for popular frameworks &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#extensions-for-popular-frameworks">#&lt;/a>&lt;/h2>&lt;p>If you use a popular framework like Laravel, where model properties are often not declared on class, you can use framework-specific extensions to make PHPStan understand your code properly.&lt;/p>&lt;p>Check out the &lt;a href="/user-guide/extension-library">extension library&lt;/a> and search for &lt;a href="https://packagist.org/?type=phpstan-extension">PHPStan extensions on Packagist&lt;/a>.&lt;/p>&lt;h2 id="write-your-own-extension" tabindex="-1">Write your own extension &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#write-your-own-extension">#&lt;/a>&lt;/h2>&lt;p>You can write your own &lt;a href="/developing-extensions/class-reflection-extensions">class reflection extension&lt;/a> to let PHPStan know which properties exist on your classes.&lt;/p>&lt;p>This solution works best if there’s custom logic in your &lt;code>__get()&lt;/code> and &lt;code>__set()&lt;/code> magic methods. For example if you can say “Property &lt;code>$name&lt;/code> on class &lt;code>Foo&lt;/code> exists if there’s a getter called &lt;code>getName()&lt;/code>”, writing a custom class reflection extension to describe this lets you solve this PHPStan error in a nice and easy way.&lt;/p>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="https://github.com/sponsors/ondrejmirtes/">&lt;strong>Consider supporting further development of PHPStan on GitHub Sponsors&lt;/strong>&lt;/a>. I’d really appreciate it!&lt;/p></content>
		</entry>
	
		
		<entry>
			<title>Website Improvement: Social Post Preview Image</title>
			<link href="https://phpstan.org/blog/website-improvement-social-post-preview-image"/>
			<updated>2023-03-13T00:00:00Z</updated>
			<id>https://phpstan.org/blog/website-improvement-social-post-preview-image</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>PHPStan’s website is statically generated thanks to this stack:&lt;/p>&lt;ul> &lt;li>&lt;a href="https://www.11ty.dev/">Eleventy&lt;/a> - think Jekyll, but for the modern JavaScript age&lt;/li> &lt;li>&lt;a href="https://parceljs.org/">Parcel&lt;/a> - my favourite bundler &lt;sup class="footnote-ref">&lt;a href="#fn1" id="fnref1">[1]&lt;/a>&lt;/sup>&lt;/li> &lt;/ul>&lt;p>There’s also plenty of other cool technologies like &lt;a href="https://tailwindcss.com/">TailwindCSS&lt;/a>, &lt;a href="https://daringfireball.net/projects/markdown/syntax">Markdown&lt;/a>, &lt;a href="https://mermaid.js.org/">Mermaid&lt;/a>, and &lt;a href="https://prismjs.com/">Prism&lt;/a>.&lt;/p>&lt;p>Being statically generated means I can host it on S3 &amp;amp; Cloudfront CDN and serve 120k+ views a month for literally pennies. But it makes implementing some features… not straightforward.&lt;/p>&lt;p>One day I noticed that &lt;a href="https://twitter.com/VotrubaT">Tomáš Votruba&lt;/a> has nice custom previews for his articles when he shares them on Twitter.&lt;/p>&lt;blockquote class="twitter-tweet" data-lang="en" data-dnt="true">&lt;p lang="en" dir="ltr">Want to ensure your codebase is rock-solid? 💪 &lt;br>&lt;br>Measure your type coverage and improve your code&amp;#39;s reliability! 🚀 &lt;a href="https://t.co/d3ZU1bXyek">https://t.co/d3ZU1bXyek&lt;/a>&lt;/p>&amp;mdash; Tomas Votruba (@VotrubaT) &lt;a href="https://twitter.com/VotrubaT/status/1634863819896901638?ref_src=twsrc%5Etfw">March 12, 2023&lt;/a>&lt;/blockquote>&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>&lt;p>And I wanted the same thing! I checked out some Eleventy plugins, but didn’t fall in love with any of them. I wanted to define how the image will look in HTML &amp;amp; TailwindCSS, screenshot it in a headless browser, and save the image to be uploaded alongside other regular website images.&lt;/p>&lt;p>So I’ve spent a few hours figuring it out, and designing the image. I love the result, both &lt;a href="https://github.com/phpstan/phpstan/commit/2304ef6326c4e29407e653f6be0cde04cc57b53e">the code that achieved that&lt;/a>, and the look. See for yourself:&lt;/p>&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">A short tale about how &lt;a href="https://twitter.com/VotrubaT?ref_src=twsrc%5Etfw">@VotrubaT&lt;/a>&amp;#39;s tweet inspired me to improve PHPStan&amp;#39;s statically generated website.&lt;a href="https://t.co/ivmHJeGCze">https://t.co/ivmHJeGCze&lt;/a>&lt;/p>&amp;mdash; Ondřej Mirtes (@OndrejMirtes) &lt;a href="https://twitter.com/OndrejMirtes/status/1635298779510472705?ref_src=twsrc%5Etfw">March 13, 2023&lt;/a>&lt;/blockquote>&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="https://github.com/sponsors/ondrejmirtes/">&lt;strong>Consider supporting further development of PHPStan on GitHub Sponsors&lt;/strong>&lt;/a>. I’d really appreciate it!&lt;/p>&lt;hr class="footnotes-sep">&lt;section class="footnotes"> &lt;ol class="footnotes-list"> &lt;li id="fn1" class="footnote-item">&lt;p>It might as well mean “I can’t configure Webpack and I’m not ashamed!” &lt;a href="#fnref1" class="footnote-backref">↩︎&lt;/a>&lt;/p> &lt;/li> &lt;/ol> &lt;/section></content>
		</entry>
	
		
		<entry>
			<title>PHPStan 1.10 Comes With a Lie Detector</title>
			<link href="https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector"/>
			<updated>2023-02-21T00:00:00Z</updated>
			<id>https://phpstan.org/blog/phpstan-1-10-comes-with-lie-detector</id>
			<content type="html" xml:base="https://phpstan.org">&lt;p>I’ve been looking forward to implementing and releasing the ideas present in &lt;a href="https://github.com/phpstan/phpstan/releases/tag/1.10.0">PHPStan 1.10&lt;/a> for a long time.&lt;/p>&lt;h2 id="validate-inline-phpdoc-%40var-tag-type" tabindex="-1">Validate inline PHPDoc &lt;code>@var&lt;/code> tag type &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#validate-inline-phpdoc-%40var-tag-type">#&lt;/a>&lt;/h2>&lt;blockquote class="twitter-tweet" data-dnt="true">&lt;p lang="en" dir="ltr">My personal mission after PHPStan 1.0 is to eradicate inline @​var tags from existence. Developers reach for it as an immediate remedy for their problems but it&amp;#39;s the worst solution ever.&lt;br>&lt;br>With @​var tags you&amp;#39;re giving up all the type safety static analysis offers. &lt;a href="https://t.co/WvRvFooce4">pic.twitter.com/WvRvFooce4&lt;/a>&lt;/p>&amp;mdash; Ondřej Mirtes (@OndrejMirtes) &lt;a href="https://twitter.com/OndrejMirtes/status/1458020442258739206?ref_src=twsrc%5Etfw">November 9, 2021&lt;/a>&lt;/blockquote>&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8">&lt;/script>&lt;p>There are multiple problems with inline &lt;code>@var&lt;/code> PHPDoc tag. PHP developers use it for two main reasons:&lt;/p>&lt;ul> &lt;li>To fix wrong 3rd party PHPDocs. A dependency &lt;sup class="footnote-ref">&lt;a href="#fn1" id="fnref1">[1]&lt;/a>&lt;/sup> might have &lt;code>@return string&lt;/code> in a PHPDoc but in reality can return &lt;code>null&lt;/code> as well.&lt;/li> &lt;li>To narrow down the returned type. When a function returns &lt;code>string&lt;/code> but we know that in this case it can only return &lt;code>non-empty-string&lt;/code>.&lt;/li> &lt;/ul>&lt;p>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>@var&lt;/code> and didn’t report any possible mistakes. Obviously that’s dangerous because the type in &lt;code>@var&lt;/code> 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>&lt;p>With the latest release and &lt;a href="https://phpstan.org/blog/what-is-bleeding-edge">bleeding edge&lt;/a> enabled, PHPStan validates the inline &lt;code>@var&lt;/code> tag type against the native type of the assigned expression. It finds the lies spread around in &lt;code>@var&lt;/code> tags:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">string&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
    &lt;span class="comment token">// ...&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

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

&lt;span class="comment token">// PHPDoc tag @var with type string|null is not subtype of native type string.&lt;/span>&lt;/code>&lt;/pre>&lt;p>It doesn’t make sense to allow &lt;code>string|null&lt;/code>, because the type can never be &lt;code>null&lt;/code>. 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>string&lt;/code> or &lt;code>non-empty-string&lt;/code> would be okay.&lt;/p>&lt;p>By default PHPStan isn’t going to report anything about the following code:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">/** @return string */&lt;/span>
&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
    &lt;span class="comment token">// ...&lt;/span>
&lt;span class="punctuation token">}&lt;/span>

&lt;span class="comment token">/** @var string|null $a */&lt;/span>
&lt;span class="token variable">$a&lt;/span> &lt;span class="operator token">=&lt;/span> &lt;span class="function token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">;&lt;/span>&lt;/code>&lt;/pre>&lt;p>Because the &lt;code>@return&lt;/code> PHPDoc might be wrong and that’s what the &lt;code>@var&lt;/code> tag might be trying to fix. If you want this scenario to be reported too, enable &lt;a href="/config-reference#reportwrongphpdoctypeinvartag">&lt;code>reportWrongPhpDocTypeInVarTag&lt;/code>&lt;/a>, or install &lt;a href="https://github.com/phpstan/phpstan-strict-rules">phpstan-strict-rules&lt;/a>.&lt;/p>&lt;p>I’d like the PHP community to use inline &lt;code>@var&lt;/code> tags less and less over time. There are many great alternatives that promote good practices and code deduplication: &lt;a href="/writing-php-code/phpdoc-types#conditional-return-types">Conditional return types&lt;/a>, &lt;a href="/writing-php-code/narrowing-types#custom-type-checking-functions-and-methods">&lt;code>@phpstan-assert&lt;/code>&lt;/a>, &lt;a href="/blog/generics-in-php-using-phpdocs">generics&lt;/a>, &lt;a href="/user-guide/stub-files">stub files&lt;/a> for overriding 3rd party PHPDocs, or &lt;a href="/developing-extensions/dynamic-return-type-extensions">dynamic return type extensions&lt;/a>.&lt;/p>&lt;h2 id="encourage-handling-of-all-enum-cases" tabindex="-1">Encourage handling of all enum cases &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#encourage-handling-of-all-enum-cases">#&lt;/a>&lt;/h2>&lt;p>What’s wrong with the following example?&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">enum&lt;/span> &lt;span class="class-name class-name-definition token">Foo&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">case&lt;/span> &lt;span class="constant token">ONE&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="keyword token">case&lt;/span> &lt;span class="constant token">TWO&lt;/span>&lt;span class="punctuation token">;&lt;/span>

	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">getLabel&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">string&lt;/span>
	&lt;span class="punctuation token">{&lt;/span>
		&lt;span class="keyword token">return&lt;/span> &lt;span class="keyword token">match&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$this&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
			&lt;span class="keyword static-context token">self&lt;/span>&lt;span class="operator token">::&lt;/span>&lt;span class="constant token">ONE&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="single-quoted-string string token">'One'&lt;/span>&lt;span class="punctuation token">,&lt;/span>
			&lt;span class="keyword static-context token">self&lt;/span>&lt;span class="operator token">::&lt;/span>&lt;span class="constant token">TWO&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="single-quoted-string string token">'Two'&lt;/span>&lt;span class="punctuation token">,&lt;/span>
			&lt;span class="keyword token">default&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="keyword token">throw&lt;/span> &lt;span class="keyword token">new&lt;/span> &lt;span class="class-name class-name-fully-qualified token">&lt;span class="punctuation token">\&lt;/span>Exception&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="single-quoted-string string token">'Unexpected case'&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">,&lt;/span>
		&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>PHPStan 1.9 didn’t complain about anything in that code. But it has problems:&lt;/p>&lt;ul> &lt;li>The &lt;code>default&lt;/code> arm isn’t really needed.&lt;/li> &lt;li>When we add &lt;code>case THREE&lt;/code> in the enum, PHPStan will not tell us about an unhandled case, but in runtime the application will throw exceptions in our face.&lt;/li> &lt;/ul>&lt;p>PHPStan 1.10 reports “Match arm is unreachable because previous comparison is always true.” for the line with the &lt;code>default&lt;/code> case. It encourages removing the &lt;code>default&lt;/code> case, which solves both problems at once.&lt;/p>&lt;p>When we add &lt;code>case THREE&lt;/code>, PHPStan will now report: “Match expression does not handle remaining value: Foo::THREE”. Which would not happen if the &lt;code>default&lt;/code> case was still there.&lt;/p>&lt;h2 id="changes-to-%E2%80%9Calways-true%E2%80%9D-expressions-and-unreachable-code" tabindex="-1">Changes to “always true” expressions and unreachable code &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#changes-to-%E2%80%9Calways-true%E2%80%9D-expressions-and-unreachable-code">#&lt;/a>&lt;/h2>&lt;p>There’s a few more related changes spawned from the previous section. For a long time PHPStan reported inconsistently “always true” and “always false” conditions. But there was some logic to it. I didn’t want you to have dead code.&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name class-name-fully-qualified token type-declaration">&lt;span class="punctuation token">\&lt;/span>Exception&lt;/span> &lt;span class="token variable">$o&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="comment token">// not reported&lt;/span>
	&lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$o&lt;/span> &lt;span class="keyword token">instanceof&lt;/span> &lt;span class="class-name class-name-fully-qualified token">&lt;span class="punctuation token">\&lt;/span>Exception&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
		&lt;span class="comment token">// code inside always executed&lt;/span>
	&lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>If there was an &lt;code>else&lt;/code> branch involved, PHPStan would report:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">doFoo&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="class-name class-name-fully-qualified token type-declaration">&lt;span class="punctuation token">\&lt;/span>Exception&lt;/span> &lt;span class="token variable">$o&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">void&lt;/span> &lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$o&lt;/span> &lt;span class="keyword token">instanceof&lt;/span> &lt;span class="class-name class-name-fully-qualified token">&lt;span class="punctuation token">\&lt;/span>Exception&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
	&lt;span class="punctuation token">}&lt;/span> &lt;span class="keyword token">else&lt;/span> &lt;span class="punctuation token">{&lt;/span>
		&lt;span class="comment token">// dead code here&lt;/span>
		&lt;span class="comment token">// reports:&lt;/span>
		&lt;span class="comment token">// Else branch is unreachable because previous condition is always true.&lt;/span>
	&lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>You could turn on a few &lt;a href="https://github.com/phpstan/phpstan-strict-rules/blob/66b378f5b242130908b8a2222bf8110f14f4375a/rules.neon#L4-L7">specific options&lt;/a>, or install &lt;a href="https://github.com/phpstan/phpstan-strict-rules">phpstan-strict-rules&lt;/a>, to also have the &lt;code>instanceof&lt;/code> reported as “always true”.&lt;/p>&lt;p>I chose this way because I didn’t want to discourage writing “safe” code like this, because some developers prefer it:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="comment token">// $foo is One|Two&lt;/span>
&lt;span class="keyword token">if&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$foo&lt;/span> &lt;span class="keyword token">instanceof&lt;/span> &lt;span class="class-name token">One&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>

&lt;span class="punctuation token">}&lt;/span> &lt;span class="keyword token">elseif&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$foo&lt;/span> &lt;span class="keyword token">instanceof&lt;/span> &lt;span class="class-name token">Two&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
    &lt;span class="comment token">// PHPStan reports "instanceof always true", wants you to write "else {" instead&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;p>Very similar to the &lt;code>match&lt;/code> example above, right? I also noticed and realized that people expect PHPStan to report “always true” out of the box more often than not, except for these examples.&lt;/p>&lt;p>With &lt;a href="https://phpstan.org/blog/what-is-bleeding-edge">bleeding edge&lt;/a> enabled and for everyone in the next major version, PHPStan will report “always true” by default, no extra options needed. To support the “last elseif” use-case, “always true” will not be reported for the last elseif condition and for the last arm in a match expression. You can override that with &lt;a href="/config-reference#reportalwaystrueinlastcondition">&lt;code>reportAlwaysTrueInLastCondition&lt;/code>&lt;/a>.&lt;/p>&lt;p>We no longer need unreachable branches reported at all - we’ll find out about them thanks to “always true” errors from previous branches. These rules are now completely disabled in &lt;a href="https://phpstan.org/blog/what-is-bleeding-edge">bleeding edge&lt;/a>.&lt;/p>&lt;p>Thanks to these changes, the error reported for the &lt;code>enum&lt;/code> with the &lt;code>default&lt;/code> case are different with and without bleeding edge on PHPStan 1.10:&lt;/p>&lt;pre class="language-php">&lt;code class="diff-highlight language-diff-php">&lt;span class="keyword token">enum&lt;/span> &lt;span class="class-name class-name-definition token">Foo&lt;/span>
&lt;span class="punctuation token">{&lt;/span>
	&lt;span class="keyword token">case&lt;/span> &lt;span class="constant token">ONE&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="keyword token">case&lt;/span> &lt;span class="constant token">TWO&lt;/span>&lt;span class="punctuation token">;&lt;/span>

	&lt;span class="keyword token">public&lt;/span> &lt;span class="keyword token">function&lt;/span> &lt;span class="function function-definition token">getLabel&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">:&lt;/span> &lt;span class="keyword return-type token">string&lt;/span>
	&lt;span class="punctuation token">{&lt;/span>
		&lt;span class="keyword token">return&lt;/span> &lt;span class="keyword token">match&lt;/span> &lt;span class="punctuation token">(&lt;/span>&lt;span class="token variable">$this&lt;/span>&lt;span class="punctuation token">)&lt;/span> &lt;span class="punctuation token">{&lt;/span>
			&lt;span class="keyword static-context token">self&lt;/span>&lt;span class="operator token">::&lt;/span>&lt;span class="constant token">ONE&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="single-quoted-string string token">'One'&lt;/span>&lt;span class="punctuation token">,&lt;/span>
			&lt;span class="keyword static-context token">self&lt;/span>&lt;span class="operator token">::&lt;/span>&lt;span class="constant token">TWO&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="single-quoted-string string token">'Two'&lt;/span>&lt;span class="punctuation token">,&lt;/span>
			&lt;span class="keyword token">default&lt;/span> &lt;span class="operator token">=>&lt;/span> &lt;span class="keyword token">throw&lt;/span> &lt;span class="keyword token">new&lt;/span> &lt;span class="class-name class-name-fully-qualified token">&lt;span class="punctuation token">\&lt;/span>Exception&lt;/span>&lt;span class="punctuation token">(&lt;/span>&lt;span class="single-quoted-string string token">'Unexpected case'&lt;/span>&lt;span class="punctuation token">)&lt;/span>&lt;span class="punctuation token">,&lt;/span>
		&lt;span class="punctuation token">}&lt;/span>&lt;span class="punctuation token">;&lt;/span>
	&lt;span class="punctuation token">}&lt;/span>
&lt;span class="punctuation token">}&lt;/span>&lt;/code>&lt;/pre>&lt;pre class="language-diff">&lt;code class="diff-highlight language-diff-diff">&lt;span class="deleted deleted-sign token">&lt;span class="deleted prefix token">-&lt;/span>&lt;span class="line token">13     Match arm is unreachable because previous comparison is always true.
&lt;/span>&lt;/span>&lt;span class="inserted inserted-sign token">&lt;span class="inserted prefix token">+&lt;/span>&lt;span class="line token">12     Match arm comparison between $this(aaa\Foo)&amp;amp;aaa\Foo::TWO and aaa\Foo::TWO is always true.
&lt;/span>&lt;span class="inserted prefix token">+&lt;/span>&lt;span class="line token">       💡 Remove remaining cases below this one and this error will disappear too.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;p>PHPStan tries to be helpful and shows a tip next to the 💡 emoji on the command line. These tips are now &lt;a href="https://phpstan.org/r/1b9cf4e1-5c2a-4e2f-b56d-b0846a303bd5">incorporated into the playground&lt;/a> as well. If you remove the &lt;code>default&lt;/code> case, PHPStan will no longer complain about this piece of code. Until you add a new enum case.&lt;/p>&lt;h2 id="why-my-type-isn%E2%80%99t-accepted-here%3F" tabindex="-1">Why my type isn’t accepted here? &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#why-my-type-isn%E2%80%99t-accepted-here%3F">#&lt;/a>&lt;/h2>&lt;p>It’s not always obvious what is PHPStan complaining about. Type safety is of the most importance, it really doesn’t want to leave any gap for possible runtime errors. The developer might not know what situation it’s trying to avoid with certain checks, and might not know why they should fix it and how.&lt;/p>&lt;p>PHPStan 1.10 includes helpful contextual tips in these less intuitive scenarios. My favourites are:&lt;/p>&lt;ul> &lt;li>&lt;a href="https://phpstan.org/r/61cfbb65-1a04-471a-a5c5-d61f0540ae1d">About &lt;code>@template-covariant&lt;/code>&lt;/a>&lt;/li> &lt;li>&lt;a href="https://phpstan.org/r/24a23b74-af27-4443-986c-04af61427d50">About callable parameter contravariance&lt;/a>&lt;/li> &lt;li>&lt;a href="https://phpstan.org/r/fed1c275-46d0-434f-b9c4-3212f4df6d1c">About complex array shapes&lt;/a>&lt;/li> &lt;/ul>&lt;h2 id="deprecation-of-instanceof-*type" tabindex="-1">Deprecation of &lt;code>instanceof *Type&lt;/code> &lt;a class="header-anchor hover:text-black ml-1 text-gray-300" href="#deprecation-of-instanceof-*type">#&lt;/a>&lt;/h2>&lt;p>PHPStan 1.10 also comes with deprecations of less-than-ideal code patterns in custom rules and other extensions. I’ve written a separate article about those two weeks ago: &lt;a href="/blog/why-is-instanceof-type-wrong-and-getting-deprecated">Why Is instanceof *Type Wrong and Getting Deprecated?&lt;/a>&lt;/p>&lt;hr>&lt;p>Do you like PHPStan and use it every day? &lt;a href="https://github.com/sponsors/ondrejmirtes/">&lt;strong>Consider supporting further development of PHPStan on GitHub Sponsors&lt;/strong>&lt;/a>. I’d really appreciate it!&lt;/p>&lt;hr class="footnotes-sep">&lt;section class="footnotes"> &lt;ol class="footnotes-list"> &lt;li id="fn1" class="footnote-item">&lt;p>That probably doesn’t use static analysis. &lt;a href="#fnref1" class="footnote-backref">↩︎&lt;/a>&lt;/p> &lt;/li> &lt;/ol> &lt;/section></content>
		</entry>
	
</feed>