Ondřej Mirtes shares the idea behind the creation of PHPStan – a static analysis tool for PHP:
Compiled languages need to know about the type of every variable, return type of every method etc. before the program runs. This is why the compiler needs to make sure that the program is “correct” and will happily point out to you these kinds of mistakes in the source code, like calling an undefined method or passing a wrong number of arguments to a function. The compiler acts as a first line of defense before you are able to deploy the application into production.
On the other hand, PHP is nothing like that. If you make a mistake, the program will crash when the line of code with the mistake is executed. When testing a PHP application, whether manually or automatically, developers spend a lot of their time discovering mistakes that wouldn’t even compile in other languages, leaving less time for testing actual business logic.
I’d like to change that.
This made sense to me, so I rushed to the repository. I have quite a few projects to try this on. I hurried so much that I didn’t pay attention to the important notes (aka prerequisities). These are:
PHPStan requires PHP 7.0. You have to run it in environment with PHP 7 but the actual code does not have to use PHP 7 features. (Code written for PHP 5.6 and earlier can run on 7 mostly unmodified.)
PHPStan works best with modern object-oriented code. The more strongly-typed your code is, the more information you give PHPStan to work with.
Properly annotated and typehinted code (class properties, function and method arguments, return types) helps not only static analysis tools but also other people that work with the code to understand it.
Erm … if I had properly annotated and typehinted code, which is nicely organized into objects, I think, I wouldn’t need PHPStan as much as I need it now. Anybody can analyze beautiful code. Try figuring out what’s going on in a WordPress theme with 150 PHP files, split into classes, functions and chunks of unmaintainable code. That’s where I wanted PHPStan to help me.
But OK. Let’s see what it can do. Gladly, my laptop already runs PHP 7 – here is a good first use for it.
Intstalling PHPStan with composer was easy. All I had to do was resolve the nikic/php-parser dependency conflict between PHPStan and Sami, which is our source code documentation tool of choice (the newer version uses a much more recent version of the PHP Parser, so it wasn’t rocket science).
Once installed, a simple “vendor/bin/phpstan analyse ./src” command produced a report with a few issues. Most of those were false positives, which can be fixed with a bit of PHPStan configuration. But a few real problems that were found, were indeed bits that sneaked through our automated and manual testing. For example:
------ --------------------------------- Line src/Shell/EmailShell.php ------ --------------------------------- 37 Return typehint of method App\Shell\EmailShell::getOptionParser() has invalid type App\Shell\ConsoleOptionParser. ------ ---------------------------------
I don’t think we’ll use PHPStan across all our code base just yet. It’ll be too noisy for some projects. And the PHP 7 requirement is not that easy to satisfy just yet. But maybe sometime next year, once we finalize our move to PHP 7, I will integrate it into our automatic testing process.
All in all, it’s quite a useful tool and much needed for larger code bases.
4 thoughts on “PHP Static Analysis Tool – discover bugs in your code without running it!”
I am really glad you spent some time trying out PHPStan and even writing about it :) It’s great to see my creation in hands of so many people!
I’ve read the article and I offer some explanation and even advice how it should work.
> Erm … if I had properly annotated and typehinted code, which is nicely organized into objects, I think, I wouldn’t need PHPStan as much as I need it now. Anybody can analyze beautiful code.
The point is that with more beautiful code, as you put it, it’s the same possibility to create errors (calling methods on wrong objects, passing wrong number of parameters etc.), but it’s much easier to find them. So when you write quality code, you have an advantage because you can employ tools such as PHPStan to find errors.
So you can try using PHPStan on legacy WordPress code without typehints and phpDocs, but it will be blind and not so helpful. But the more your code is typed and annotated, the better results will PHPStan give you. So there’s no reason to say that it’s useless on a modern codebase. Exactly the opposite.
> But a few real problems that were found, were indeed bits that sneaked through our automated and manual testing.
Glad to hear that it’s useful :) Finding these bits is exactly what PHPStan is good at.
> I don’t think we’ll use PHPStan across all our code base just yet. It’ll be too noisy for some projects.
Did you find it noisy even on the default rule level 0? It’s designed to find obvious errors in your code – it checks only calls on $this, static ones, simply anything it can be sure about its type. The default level 0 is meant to be suitable for everyone because it doesn’t make any guesses inferred from phpDocs and other possibly inaccurate data. So you can start using it immediately at level 0 and if you have time later, try to increase the level as you clean out your codebase.
> But maybe sometime next year, once we finalize our move to PHP 7, I will integrate it into our automatic testing process.
One tip for this scenario – maybe you could build your codebase on both PHP 5.6 (for compatibility) and PHP 7 and simply skip running PHPStan on 5.6. That’s exactly what we do in this Phing condition: https://github.com/slevomat/coding-standard/blob/master/build.xml#L89
first of all, thanks for all the hard work on PHPStan, and for taking the time to comment here.
I didn’t mean to say that the tool is not useful. Quite the opposite. I meant that it’s not very useful right now for the projects and the team that I am involved with. Just to clarify:
As for the noise levels, I do understand that PHPStan can be tweaked and customized to check only the necessary rules. That’s great. For now though, I didn’t spend enough time looking into it and sort of tried it “out of the box”. One particular thing which was surprising in the output, is the error about some PHP functions not being recognized. PHPStan was complaining about ldap_connect(), ldap_bind(), ldap_search(), etc. These come from the LDAP extension for PHP, but the extension was installed on the system just fine. A few other things were more of a configuration issue and our use case, not the issues with the PHPStan itself.
As I said, it’s a great tool and I see a lot of value in it, and everybody should give it a try. The two reasons I wrote the blog post were:
Once again, thanks for the great work on PHPStan and for taking the time to comment here. :)