A few weeks back at MageTestFest 2019 in Florence, I gave a presentation on QA tools I like and gave some insights how they can be used in a Magento project. One of the tools I mentioned was PHPStan - a static analysis tool. PHPStan helps you to detect a lot of problems in your code due to using wrong types, calling methods that might not exist, passing the wrong number or types of arguments to methods and so on. PHPStan is also able to create your own rule sets of what behavior you want to check for.
Using PHPStan out-of-the-box with Magento does not work for a couple of reasons like PHPStan not knowing what instances the ObjectManager calls return, the usage of magic methods like __set() and __get() or just wrong type hints. Luckily one can write extensions for PHPStan to help it to gain more knowledge about the code that should be analyzed. This led me to create a prototype of an extension called phpstan-magento which already fixes a few problems. I used the extension against some of our modules, it's far away from being feature complete and to be fair some problems can only be corrected in the Magento code base :)
If you are already using Magento 2.3 you can install PHPStan as a Composer dependency, for older Magento versions there's a Docker image that you can use. To install PHPStan via Composer, run this command:
composer.phar require --dev phpstan/phpstan
Install the PHPStan Magento Extension via Composer as well:
composer.phar require --dev bitexpert/phpstan-magento
Create a PHPStan configuration file called phpstan.neon in the root folder of your project which looks like this:
There's no need to start with level 7 from the get go. Start with level 0, fix the findings and increase the level step by step.
Now you can run PHPStan like this:
What does the extension offer?
- The extension adds an class generator for factory & proxy classes similar as Magento does it. When running PHPStan in context of a Magento application this is not needed if you point PHPStan also the the generated files folder. When running Magento in a context of a module, this is required so that PHPStan get's the full picture of all classes needed.
- The extension adds an autoloader for "mocked" classes. These are classes that replace the Magento specific implementations to fix problems with type hints or missing methods in interfaces and such. The autoloader will check if a class, interface or trait exists locally in the extension. If so, it will load the local version instead of the one being shipped by Magento. Once those problems are fixed in Magento, those mocks can be removed again.
- A type extension was added so that ObjectManager calls return the correct return type.
- For some classes like the DataObject or the SessionManager logic was added to be able to support magic method calls.