Preparing Legacy Applications for PHP 7 with Phan

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' >

This will create a new file called 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 -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.

use \Phan\Config;
use \Phan\Issue;

return [
    'quick_mode' => false,
    'exclude_analysis_directory_list' => [
    'processes' => 2,
    'analyze_signature_compatibility' => true,
    'dead_code_detection' => true,
    'minimum_severity' => Issue::SEVERITY_CRITICAL,
    'directory_list' => [

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.


About Matthew

Matthew Setter Matthew Setter is a PHP & Zend Framework specialist. If you're in need of a custom software application, need to migrate an existing legacy application, or want to know your current application's GPA - get in touch.

Don’t Miss The Next Tutorial!

Drop your email in the box below, and get it straight to your inbox, PLUS exclusive content only available by email. No spam, and you can unsubscribe at any time.