Preparing Legacy Applications for PHP 7 with Phan

PHP 7 presents enormous benefits over all previous versions. But is your code ready? In this tutorial, find out what needs to be done using the Phan static code analyser.


Preparing Legacy Applications for PHP 7 with Phan

Unless you’ve been living under a rock these last 12 - 18 months, you will have heard about PHP 7; the latest version of PHP. Not only is it fast, by some reports it’s up to twice as fast as PHP 5.6, and far less memory hungry.

It also comes with a host of new features and improvements. Here’s a short list:

  • Scalar Type Hints
  • Return Type Hints
  • Uniform Variable Syntax
  • Engine Exceptions

You may not be as interested in the type-hinting yet, but I’d suggest that you’re at least eager to take advantage of the speed improvements. So one way or another you have to upgrade that Zend Framework 1 or 2 application to be PHP 7 compliant. Am I right?

But given it’s such a major release, migrating to it isn’t as simple as upgrading your installed version of PHP and reloading your site. I wish it was. I truly do.

Unfortunately, your application’s source code may not be 100% compatible with version 7. Upgrading may well leave you with a broken application and a set of unhappy customers.

So before you go breaking your site in the interests of speed and being one of the cool kids, find out if your code’s compatible. How? By using a static code analyser, one which gives you all the changes you need to make on your code, so that you can upgrade with confidence.

A number of them have become available in recent months. Over the coming weeks, I’m going to go through some of the more significant ones. The one I’m going to step you through today is Phan, which was developed at Etsy.

#?

Phan has a number of checks which it performs, to ensure that your code is ready for PHP 7. These include:

  • PHP7/PHP5 backward compatibility
  • type safety on binary operations
  • namespaces, traits and variadics
  • No-Ops on arrays, closures, constants, properties, variables

It has a simple interface, able to be run from the command-line, and provides a detailed, easy to read output file. Given that, it can be included as part of your continuous integration and deployment processes, helping to ensure that your codebase becomes ever more compliant, as well as staying that way.

Like most modern PHP projects, Phan’s able to be included as a dependency of your application’s codebase via Composer. To do so, run the following command from the root of your project’s source:

composer require --dev etsy/phan

To run phan is quite similar to other Composer-based dependencies, which provide binaries, such as PhpUnit and Codeception. Phan provides a binary in vendor/bin/phan. To get a list of available options, run vendor/bin/phan -h. Here’s a short listing of what’s on offer.

  • A file containing a list of PHP files to be analyzed
  • A directory that should be parsed for class and method information
  • A comma-separated list of directories to excluded
  • Whether to read options from a configuration file
  • Output mode, which includes text, json, codeclimate, and checkstyle
  • The output filename
  • Whether to show progress bar
  • Whether to emit an AST for each file rather than analyze
  • Whether to enable quick mode, which tells Phan whether to recurse into all function calls
  • Whether to check for potential PHP 5 -> PHP 7 BC issues
  • Whether to ignore undeclared functions and classes
  • The minimum severity level (low=0, normal=5, critical=10) to report
  • The number of parallel processes to run

But first I need a list of files to analyse. To do that, run the following command, from the root of your project directory:

find module/ -type f -name '*.php' > phan.in

This will create a new file called phan.in with a list of all the PHP files located under the module/ directory.

For the initial test, I’m going to ignore the vendor directory, output the results to phan.out, export the results in text format, show a progress bar, use two parallel process and set a minimum severity level of 5 (or normal).

Here’s both the command to do that, and the output from running it.

./vendor/bin/phan -f phan.in -3 vendor -o phan.out \
  --output-mode text --progress-bar \
  --processes 2 --minimum-severity 5 \
  --signature-compatibility -i

    method ██████████████████████████████████████████████ 100% 27MB/27MB
   analyze ██████████████████████████████████████████████ 100% 28MB/29MB

With the analysis now run, it’s time to have a look at the output of it. Below is a sample of the contents of phan.out.

bootstrap.php:16 PhanNonClassMethodCall Call to method add on non-class type null
BasicFormTest.php:40 PhanTypeMismatchReturn Returning type \zend\form\form but _getForm() is declared to return \videohostertest\form\form
ZfcUserProfileUpdateProfileWidget.php:44 PhanTypeMismatchReturn Returning type \zend\view\model\viewmodel but __invoke() is declared to return string
UserProfileController.php:7 PhanRedefineClass Class \zfcuserprofile\controller\userprofilecontroller defined at module/ZfcUserProfile.old/src/ZfcUserProfile/Controller/UserProfileController.php:7 was previously defined as Class \zfcuserprofile\controller\userprofilecontroller at module/ZfcUserProfile/src/ZfcUserProfile/Controller/UserProfileController.php: 7

Here three issues have been identified. These are:

  • PhanNonClassMethodCall: An attempt was made to call a method on an object which wasn’t a class
  • PhanTypeMismatchReturn: The value of the return type differs from the stated return type (via annotations)
  • PhanRedefineClass: A class was redefined

As you can see, none of these are too serious, and would not take a lot of time to address. You can see the full list in the Phan documentation.

Now running Phan from the command directly isn’t always the best option. It’s much easier if a configuration file can be used, and helps integrate with build processes as well. Gladly, Phan supports this already.

The config file doesn’t support all the command line options. But it does support a number of them. Here’s what’s available:

  • allow_missing_properties
  • null_casts_as_any_type
  • scalar_implicit_cast
  • backward_compatibility_checks
  • analyze_signature_compatibility
  • dead_code_detection
  • quick_mode
  • minimum_severity
  • file_list
  • exclude_file_list
  • processes
  • directory_list
  • exclude_analysis_directory_list

Create a new file, called config.php in a directory called .phan. The file will return an array with the configuration options we need. The configuration below provides an example configuration, based mostly on the command line setup we used earlier.

<?php
use \Phan\Config;
use \Phan\Issue;

return [
    'quick_mode' => false,
    'exclude_analysis_directory_list' => [
        'vendor/'
    ],
    'processes' => 2,
    'analyze_signature_compatibility' => true,
    'dead_code_detection' => true,
    'minimum_severity' => Issue::SEVERITY_CRITICAL,
    'directory_list' => [
        'module',
        'public',
        'config'
    ]
];

With this file created, the revised command-line script will now be:

./vendor/bin/phan --project-root-directory --progress-bar -o phan.out

In the help documentation, there’s a short switch -d which is also meant to do what --project-root-directory does. However any time I tried it, no analysis was performed. That’s why i’ve specifically chosen the longer switch name.

And that’s how to use Phan to perform static analysis of your code and help prepare it for the migration to PHP 7.

Depending on a number of factors including the age of the codebase, the number of developers, their relative levels of experience, and compliance with coding standards the number of issues that need to be addressed is going to vary, perhaps wildly.

But don’t be put off if a lot of these factors pertain to your codebase. Reflect on this old Chinese proverb and then decided if it’s too late to start:

The best time to plant a tree was 20 years ago. The second best time is now.


You might also be interested in these tutorials too...

The Composer Command-Line Essentials
Tue, Nov 29, 2016

The Composer Command-Line Essentials

How well do you really know Composer? Do you just know composer install, update, and require, and some of the composer.json configuration? Or do you really know it? In this series, you’re going to take your skills to the next level; starting with the command-line.

Mon, Aug 22, 2016

How To Use Laravel’s Eloquent ORM with Zend Expressive

Laravel’s Eloquent ORM isn’t likely the first one you think of when using Zend Expressive. You likely think of Zend\Db or Doctrine. But, with a little bit of work, it’s possible to use Eloquent with Expressive. Today’s tutorial shows you how - step-by-step.

Tue, Aug 9, 2016

The 3-Step Guide to Downloading Files in Zend Expressive

A common requirement of web-based applications is to upload and download files. But, out of the box, there’s no simple way to download them in Zend Expressive. This tutorial shows you how - step-by-step.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus