Last month I wrote about writing better Drupal code with static analysis using PHPStan. One of the more practical uses I saw for PHPStan and Drupal was the discovery of deprecated code usages through the phpstan/phpstan-deprecation-rules package. I had not fully tested it, until this week.
I'm really excited for Drupal 9 and to trim down old code by updating to Symfony 4 (or 5) and cutting all of our deprecated APIs. When I started writing the Drupal 8 Development Cookbook there was the entity_manager. The entity_manager was deprecated in 8.0.0 (! so before Drupal 8 came out) and still lingers in a lot of core's code. Of course, this is how software "just goes." But Drupal 8 had a lot of churn as things from Drupal 7 were rewritten and hard lessons in OOP PHP learned.
The need to track deprecations is more than cleaning our own code up. It's ensuring our compatibility with our dependencies, namely Symfony and PHPUnit. One of the main tasks for Drupal 9 readiness is resolving usage of the deprecated code. In fact, Lee Rowlands (larowlan) and Gábor Hojtsy run office hours for this topic.
I have wanted to contribute, but 9 am UTC is 3 am CST. I am also pretty busy at Commerce Guys with our own codebase for Drupal Commerce and numerous contrib. But, our platform is built on Drupal, so having Drupal chug forward is a big deal.
You can disable PHPStan's analysis tools and use it for just discovering deprecated code. And so I found a way I could help contribute to the effort by building some tooling!
Automating deprecation testing for Drupal core
The Drupal 9 group has needed a way to automate deprecated code tracking and improve their tooling. So I set up a sample Drupal 8 repository that runs PHPStan with depreciation rules! The repository can be found at: https://github.com/mglaman/drupal-deprecation-testing
Currently, it is set up with TravisCI and CircleCI (needs updating.) Without a composer.lock committed and Drupal set to a constraint of drupal/core:8.7.x-dev the latest HEAD for Drupal core will always be tested.
Due to the sheer size of Drupal core's codebase and the memory requirements, I had to chunk up the jobs and batch run directories at a time.
Testing your Drupal codebase for deprecations
You can test your code, too! Whether you want to test your client's codebase or your own contribs, it is easy to set up deprecation code testing.
To get started, you need to add mglaman/phpstan-drupal and phpstan/phpstan-deprecation-rules as a developer dependency.
composer require mglaman/phpstan-drupal phpstan/phpstan-deprecation-rules --dev
Then create a phpstan.neon file. To only test deprecations, use this setup:
parameters: customRulesetUsed: true reportUnmatchedIgnoredErrors: false # Ignore phpstan-drupal extension's rules. ignoreErrors: - '#\Drupal calls should be avoided in classes, use dependency injection instead#' - '#Plugin definitions cannot be altered.#' - '#Missing cache backend declaration for performance.#' - '#Plugin manager has cache backend specified but does not declare cache tags.#' includes: - vendor/mglaman/phpstan-drupal/extension.neon - vendor/phpstan/phpstan-deprecation-rules/rules.neon
This disables the level argument for PHPStan's analysis rules and also ignores errors from the Drupal extension for PHPStan. I opened an issue to try and streamline this: https://github.com/mglaman/phpstan-drupal/issues/30
Running deprecation tests on TravisCI for contrib
I set up a sample repository that runs Drupal Commerce and our dependencies against PHPStan for deprecation checks. You can find it here: https://github.com/mglaman/commerce-deprecation-testing
It runs a job for each module: https://travis-ci.com/mglaman/commerce-deprecation-testing
Here is a sample of the .travis.yml. It will cache Composer and PHPStan's generated output (makes later analysis faster.) There's also a weird warning about APCu constants if APCU is not enabled, so it is added.
language: php dist: trusty sudo: false cache: directories: - $HOME/.composer/cache/files - $HOME/.composer/cache/repo - $TMPDIR/phpstan/cache php: - 7.2 env: matrix: - ANALYZE=address - ANALYZE=commerce - ANALYZE=entity - ANALYZE=entity_reference_revisions - ANALYZE=inline_entity_form - ANALYZE=profile - ANALYZE=state_machine before_install: - echo 'sendmail_path = /bin/true' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - echo 'memory_limit = -1' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - echo "extension = apcu.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - phpenv config-rm xdebug.ini install: - composer global require "hirak/prestissimo:^0.3" - composer install -n --prefer-dist --no-suggest script: - ./vendor/bin/phpstan analyse web/modules/contrib/$ANALYZE