Among the many innovations in PHP 7, there was also ast-an abstract syntax tree that is used inside the language for syntax analysis. After you have installed the PHP-ast extension, this tree is also available for PHP code in your application. This is what Phan uses when analyzing code.
In November 2014, Rasmus Lerdorf announced the start of work on a new static analyzer using ast, half a year later he showed a version of “proof of concept” which after the next 6 months was adopted and rewritten by Andrew Morssison (Rasmus collaborator at Etsy). Since then, Andrew has been developing the code available on GitHub and preparing to release the first stable version.
During the analysis, Phan finds many different types of errors-in addition to “normal” syntax errors, it also finds possible developer errors. Such errors that are correct PHP code, but there is a suspicion that the intentions of the programmer were different than those that were programmed. A comprehensive description of the detected errors can be found on the relevant project page, but in order for the reported errors to make sense, you need to prepare a little.
Class in PHP with a minor error:
require_once 'vendor/autoload.php';
use RamseyUuidUuid;
class User
{
/**
* @var Uuid
*/
private $id;
public function __construct()
{
$this->id = Uuid::uuid4();
}
/**
* @return int
*/
public function getId()
{
return $this->id;
}
}
Let’s try to check this class:
phan User.php
We get a list of errors:
User.php:12 PhanUndeclaredTypeProperty Property of undeclared type ramseyuuiduuid
User.php:16 PhanUndeclaredClassMethod Call to method uuid4 from undeclared class ramseyuuiduuid
User.php:24 PhanTypeMismatchReturn Returning type ramseyuuiduuid but getId() is declared to return int
The first two errors mean that Phan does not know the uuid class to which we refer in the code, just add the appropriate files for analysis:
phan -l vendor User.php
The-l parameter adds the vendor directory to the list of analyzed files:
User.php:24 PhanTypeMismatchReturn Returning type ramseyuuiduuid|ramseyuuiduuidinterface but getId() is declared to return int
vendor/ramsey/uuid/src/Converter/Number/BigNumberConverter.php:34 PhanUndeclaredClassMethod Call to method convertToBase10 from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Number/BigNumberConverter.php:36 PhanUndeclaredClassMethod Call to method __construct from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Number/BigNumberConverter.php:46 PhanUndeclaredTypeParameter Parameter of undeclared type moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Number/BigNumberConverter.php:49 PhanUndeclaredClassMethod Call to method __construct from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Number/BigNumberConverter.php:52 PhanUndeclaredClassMethod Call to method convertFromBase10 from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Number/DegradedNumberConverter.php:50 PhanSignatureMismatch Declaration of function toHex(mixed $integer) : void should be compatible with function toHex(mixed $integer) : string defined in vendor/ramsey/uuid/src/Converter/NumberConverterInterface.php:43
vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php:38 PhanUndeclaredClassMethod Call to method __construct from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php:40 PhanUndeclaredClassMethod Call to method __construct from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php:41 PhanUndeclaredClassMethod Call to method multiply from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php:43 PhanUndeclaredClassMethod Call to method __construct from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php:44 PhanUndeclaredClassMethod Call to method multiply from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php:46 PhanUndeclaredClassMethod Call to method add from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Time/BigNumberTimeConverter.php:50 PhanUndeclaredClassMethod Call to method convertToBase from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Converter/Time/DegradedTimeConverter.php:35 PhanSignatureMismatch Declaration of function calculateTime(string $seconds, string $microSeconds) : void should be compatible with function calculateTime(string $seconds, string $microSeconds) : string[] defined in vendor/ramsey/uuid/src/Converter/TimeConverterInterface.php:32
vendor/ramsey/uuid/src/DegradedUuid.php:35 PhanUndeclaredClassMethod Call to method __construct from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/DegradedUuid.php:36 PhanUndeclaredClassMethod Call to method subtract from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/DegradedUuid.php:37 PhanUndeclaredClassMethod Call to method divide from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/DegradedUuid.php:38 PhanUndeclaredClassMethod Call to method round from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/DegradedUuid.php:39 PhanUndeclaredClassMethod Call to method getValue from undeclared class moontoastmathbignumber
vendor/ramsey/uuid/src/Generator/CombGenerator.php:91 PhanTypeMismatchReturn Returning type string but timestamp() is declared to return int
vendor/ramsey/uuid/src/Generator/DefaultTimeGenerator.php:88 PhanTypeMismatchArgument Argument 1 (seconds) is int but ramseyuuidconvertertimeconverterinterface::calculatetime() takes string defined at vendor/ramsey/uuid/src/Converter/TimeConverterInterface.php:32
vendor/ramsey/uuid/src/Generator/DefaultTimeGenerator.php:88 PhanTypeMismatchArgument Argument 2 (microSeconds) is int but ramseyuuidconvertertimeconverterinterface::calculatetime() takes string defined at vendor/ramsey/uuid/src/Converter/TimeConverterInterface.php:32
vendor/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php:33 PhanUndeclaredFunction Call to undeclared function uuid_create()
vendor/ramsey/uuid/src/Generator/PeclUuidRandomGenerator.php:35 PhanUndeclaredFunction Call to undeclared function uuid_parse()
vendor/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.php:34 PhanUndeclaredFunction Call to undeclared function uuid_create()
vendor/ramsey/uuid/src/Generator/PeclUuidTimeGenerator.php:36 PhanUndeclaredFunction Call to undeclared function uuid_parse()
vendor/ramsey/uuid/src/Generator/RandomLibAdapter.php:31 PhanUndeclaredTypeProperty Property of undeclared type randomlibgenerator
vendor/ramsey/uuid/src/Generator/RandomLibAdapter.php:41 PhanUndeclaredTypeParameter Parameter of undeclared type randomlibgenerator
vendor/ramsey/uuid/src/Generator/RandomLibAdapter.php:46 PhanUndeclaredClassMethod Call to method __construct from undeclared class randomlibfactory
vendor/ramsey/uuid/src/Generator/RandomLibAdapter.php:48 PhanUndeclaredClassMethod Call to method getMediumStrengthGenerator from undeclared class randomlibfactory
vendor/ramsey/uuid/src/Generator/RandomLibAdapter.php:60 PhanUndeclaredClassMethod Call to method generate from undeclared class randomlibgenerator
vendor/ramsey/uuid/src/Generator/SodiumRandomGenerator.php:34 PhanUndeclaredFunction Call to undeclared function sodiumrandombytes_buf()
vendor/ramsey/uuid/src/Provider/Node/FallbackNodeProvider.php:55 PhanTypeMismatchReturn Returning type null but getNode() is declared to return string
vendor/ramsey/uuid/src/Provider/Node/SystemNodeProvider.php:35 PhanTypeMismatchReturn Returning type null but getNode() is declared to return string
vendor/ramsey/uuid/src/Uuid.php:211 PhanUndeclaredProperty Reference to undeclared property ramseyuuiduuidinterface->codec
vendor/ramsey/uuid/src/Uuid.php:212 PhanUndeclaredProperty Reference to undeclared property ramseyuuiduuidinterface->converter
vendor/ramsey/uuid/src/Uuid.php:213 PhanUndeclaredProperty Reference to undeclared property ramseyuuiduuidinterface->fields
vendor/ramsey/uuid/src/Uuid.php:233 PhanSignatureMismatch Declaration of function equals($other) should be compatible with function equals(object $other) : bool defined in vendor/ramsey/uuid/src/UuidInterface.php:51
vendor/ramsey/uuid/src/UuidFactory.php:163 PhanSignatureMismatch Declaration of function fromBytes($bytes) should be compatible with function fromBytes(string $bytes) : ramseyuuiduuidinterface defined in vendor/ramsey/uuid/src/UuidFactoryInterface.php:68
vendor/ramsey/uuid/src/UuidFactory.php:168 PhanSignatureMismatch Declaration of function fromString($uuid) should be compatible with function fromString(string $uuid) : ramseyuuiduuidinterface defined in vendor/ramsey/uuid/src/UuidFactoryInterface.php:76
vendor/ramsey/uuid/src/UuidFactory.php:181 PhanSignatureMismatch Declaration of function uuid1($node = null, $clockSeq = null) should be compatible with function uuid1(int|null|string $node = null, int|null $clockSeq = null) : ramseyuuiduuidinterface defined in vendor/ramsey/uuid/src/UuidFactoryInterface.php:33
vendor/ramsey/uuid/src/UuidFactory.php:189 PhanSignatureMismatch Declaration of function uuid3($ns, $name) should be compatible with function uuid3(string $ns, string $name) : ramseyuuiduuidinterface defined in vendor/ramsey/uuid/src/UuidFactoryInterface.php:43
vendor/ramsey/uuid/src/UuidFactory.php:206 PhanSignatureMismatch Declaration of function uuid5($ns, $name) should be compatible with function uuid5(string $ns, string $name) : ramseyuuiduuidinterface defined in vendor/ramsey/uuid/src/UuidFactoryInterface.php:60
Uuups, a whole bunch of errors in the attached library, you need to exclude it from the analysis:
phan -l vendor -3 vendor User.php
Another parameter: -3 Is A list of directories that will not be analyzed for correctness, this time the result is consistent with the plan:
User.php:24 PhanTypeMismatchReturn Returning type ramseyuuiduuid|ramseyuuiduuidinterface but getId() is declared to return int
Phanco optimization finally finds the expected problem-the incompatibility of the phpdoc declaration with the operation of the method.
Adding the entire vendors catalog to parsing is very convenient, but with slightly larger projects it extends the analysis time and increases the appetite for memory (in my tests I reached 30s of analysis and 2GB of memory).
I used the Phan configuration file (default location is .Phan / config.php) – all possible settings are described in the config file.php, I needed three settings:
return [
'file_list' => [
'app/AppKernel.php',
'vendor/doctrine/collections/lib/Doctrine/Common/Collections/ArrayCollection.php',
// 87 innych plików z vendor
'vendor/symfony/symfony/src/Symfony/Component/Yaml/Yaml.php',
],
'directory_list' => [
'src',
],
'exclude_analysis_directory_list' => [
'vendor/',
],
];
In this example, I added the entire src directory for analysis, disabled vendors/ for validation, and added 90 files (mostly from the vendors directory) for parsing. Thanks to this configuration, the time for studying project sources is reduced to 2 seconds. From time to time (when referring to a new interface) you need to add more files to file_list but we are ready for such a duty.
Phan is another tool that we study the code, we appreciate in it that it finds problems overlooked by other tools (for example, the mentioned phpdoc analysis).
Phan is developing dynamically and is just approaching the first stable version, which means that often the latest code (from the master branch) does not work as it should – after updating, there are false errors in the code that are the result of a mistake in Phan. Fortunately, the developers of the project quickly respond to bug reports and within one day correct the reported defects.
When the project reaches maturity, Phan will be a very good addition to the tools supporting CI and code reviews, at the moment we use it “manually” analyzing the code before adding it to the repository.
Author: Tomasz Tybulewicz, Speednet SP. z O. O.
1A Sportyvna sq, Kyiv, Ukraine 01023
2187 SW 1st St, Miami, FL 33135, USA
info@servreality.com
info@servreality.com