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.
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
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
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:
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...
- The Composer Command-Line Essentials
- How To Use Laravel’s Eloquent ORM with Zend Expressive
- The 3-Step Guide to Downloading Files in Zend Expressive
- Unit Testing Zend Expressive Database Classes with Codeception
- How to Test Abstract PHP Classes With PHPUnit
comments powered by Disqus