Zero Config Analysis with Static Reflection

June 8, 2020 · 5 min read

Some PHPStan releases bring new checks, some of them improve performance, and others consist mostly of bugfixes. But the latest release is about making lives of PHPStan’s users much easier. It now requires less configuration and overall fiddling when getting started with it. And it makes analysis of certain codebases possible, whereas before it wasn’t possible at all.

A little bit of history #

For the past 3.5 years of PHPStan’s existence it relied on runtime PHP reflection to find out information about the analysed code. It served us well, but came with a few tradeoffs.

Runtime reflection relies on autoloading - the analysed classes and functions have to be loaded in memory for the analysis to be successful. PHPStan users had to set up autoloading for all analysed code which had been a bit awkward - directories like tests or migrations had to be added to Composer’s autoload-dev section even if the application or test runner didn’t require it.

Also, it wasn’t possible to analyse files that mixed classes/functions and side-effects:

function doSomething(): void
// ...


Loading this file (so that the function is known and code can be analysed) would mean that the function would actually be executed!

For 3.5 years I had to tell people they need to separate their files into two piles: declarations-only and side-effects-only, otherwise PHPStan isn’t usable on their code. But today, I’m releasing a new PHPStan version (0.12.26) which I finally don’t have to be embarrassed about. [1]

Static reflection #

These tradeoffs can go away thanks to Roave’s BetterReflection library. It reimplements PHP’s reflection API using AST (same technology PHPStan itself is based on). It parses all the files related to the analysis and provides all the same information but without the aforementioned downsides.

Setting up autoloading for all the parsed sources is no longer necessary - BetterReflection simply looks at all the files in analysed paths to discover the desired symbols. PHPStan will be able to analyse most codebases without any additional configuration. I wrote a handful guide for the rest.

Such a deep integration of a technology inevitably leads to finding bugs in that technology. In the true spirit of open source, we improved BetterReflection a ton in collaboration with Marco @Ocramius and Jarda @kukulich. [2]

Unlocking new use cases #

Files that mix declarations and side-effects are no longer a problem. Previous versions of PHPStan would work only with pure-OOP codebases. But the latest one will also work with code that looks like it was written a decade or two ago.

To prove this, I tested PHPStan on Adminer which is an awesome database manager from users’ perspective, but not so much when you look at its code.

PHPStan 0.12.25 (released a month ago) reported 1,945 errors on level 0 - a totally broken analysis full of unknown symbols and misunderstood code. After the static reflection has been deployed and it was able to analyse such files, it brought the number of errors down to 18! And all of them were actionable and relevant.

Hybrids are better for the environment #

With all that said, PHPStan can still use runtime reflection where appropriate. So it’s hybrid reflection, not entirely static. Static reflection is hungrier for CPU time and RAM allocation, so if PHPStan concludes that a class can be autoloaded, it will use runtime reflection for that one, and static reflection for the rest.

If you have tried PHPStan before and it didn’t work on your project, now is the time to give it a chance again. If you’re already using PHPStan, you can definitely simplify your configuration. I’m looking forward to your feedback!

Do you like PHPStan and use it every day? Consider supporting further development of PHPStan on GitHub Sponsors. I’d really appreciate it!

  1. It’s fully compatible with the current setups of 0.12.x series. ↩︎

  2. I love my job! ↩︎

© 2016–2020 Ondřej Mirtes