Testing your Drupal code base for deprecated code usage with PHPStan

Published on Sunday 3, February 2019

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.

Update!

Since writing this post, using PHPStan to find deprecations in Drupal has taken off. You can go ahead and follow the rest of the blog post, but I would like to reference a tool called Drupal Check. This is a standalone binary (PHP Phar) that will run deprecation checks without needing to install anything with Composer. You can find it, and instructions, over here: https://github.com/mglaman/drupal-check

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