Phan Static Analyzer

When trying to determine what needs to be updated in a project to make it PHP 7.* compatible, it is nice to use a commandline program to assist. I straight away tried to use phan static analyzer since I had used it on other projects to check the quality of code but it was impossibly slow, when using it for a codebase plus vendor libs, even when target_php_version was the only analyzer turned on when running phan . with the following config:


<?php

/**
 * This configuration will be read and overlaid on top of the
 * default configuration. Command line arguments will be applied
 * after this file is read.
 */
return [

    // Supported values: '7.0', '7.1', '7.2', null.
    // If this is set to null,
    // then Phan assumes the PHP version which is closest to the minor version
    // of the php executable used to execute phan.
    "target_php_version" => '7.1',

    // A list of directories that should be parsed for class and
    // method information. After excluding the directories
    // defined in exclude_analysis_directory_list, the remaining
    // files will be statically analyzed for errors.
    //
    // Thus, both first-party and third-party code being used by
    // your application should be included in this list.
    'directory_list' => [
        'public'
    ],

    // A directory list that defines files that will be excluded
    // from static analysis, but whose class and method
    // information should be included.
    //
    // Generally, you'll want to include the directories for
    // third-party code (such as "vendor/") in this list.
    //
    // n.b.: If you'd like to parse but not analyze 3rd
    //       party code, directories containing that code
    //       should be added to the `directory_list` as
    //       to `exclude_analysis_directory_list`.
    "exclude_analysis_directory_list" => [
        'vendor/'
    ],

    // A list of plugin files to execute.
    // See https://github.com/phan/phan/tree/master/.phan/plugins for even more.
    // (Pass these in as relative paths.
    // The 0.10.2 release will allow passing 'AlwaysReturnPlugin' if referring to a plugin that is bundled with Phan)
    'plugins' => [
        // checks if a function, closure or method unconditionally returns.
        //'AlwaysReturnPlugin',  // can also be written as 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'
        // Checks for syntactically unreachable statements in
        // the global scope or function bodies.
        //'UnreachableCodePlugin',
        //'DollarDollarPlugin',
        //'DuplicateArrayKeyPlugin',
        //'PregRegexCheckerPlugin',
        //'PrintfCheckerPlugin',
    ],
];

The Misfits: php7cc and phpstan

After waiting for phan after what seemed to be an inordinate amount of time, I gave up and decided to find a different solution. I, of course, found PHP 7 Compatibility Checker (php7cc) but the project is no longer supported. I immediately ditched php7cc since it may not work for what I’m intending to check.

PHPStan Static Analysis Tool is actively maintained but I couldn’t find any references to checking PHP version compatibility in a code base. While it might still check for php version incompatibility, I opted for a tool that was specifically designed to assist with the migration of older PHP codebases to PHP 7.

The Winner: PHP 7 Migration Assistant Report (php7mar)

After searching and testing a few different tools out, I finally settled on php7mar since it is specifically designed to do the job that I was looking for a tool to do along with being fast enough to be bearable. Whereas phan never did complete before I gave up, php7mar was done in a minute or so for a codebase that contained nearly a thousand files and 318,557 lines.

However, there is a downside. php7mar uses the installed PHP binary that you have on your system. While you can specify a different PHP binary than the default one by running the command with the --php="/path/to/php/binary/php" option, you still have to figure out how to install different PHP versions on your particular OS or use another workaround such as Docker or Vagrant.

Even given this downside, it is still the best option that I found given its speed and the comprehensiveness of the generated report.

To install php7mar, you can git clone it, download the zip off of Github, or just download the mar.php file from the Github repository. Move the file to wherever you’d like to run it from.

I used the following command to run php7mar: php /path/to/php7mar-master/mar.php -f="/path/to/code/to/check" -r="/path/to/output/directory"

The output is a markdown file that is timestamped. It’s contents resemble the following:

2018-04-20T17:34:17-06:00
Scanning /path/to/codebase
Including file extensions: php
Processed 318557 lines contained in 901 files.
Processing took 21.512763977051 seconds.


# critical
#### /path/to/public/wp-admin/includes/class-ftp-pure.php
* oldClassConstructors
 * Line 28: `	function ftp($verb=FALSE, $le=FALSE) {`

#### /path/to/public/wp-admin/includes/class-ftp-sockets.php
* oldClassConstructors
 * Line 28: `	function ftp($verb=FALSE, $le=FALSE) {`
 
... and so on ...

As you can see, there is a bit of Wordpress code in the codebase that I checked. Unsurprisingly, this version of Wordpress is not compatible.

In addition to using php7mar, you can also use PHPStorm to check code as you write it for version compatibility. However, I believe that a commandline tool is much easier to use, especially when checking vendor files, than an IDE is for doing a bulk verification of compatibility.