Phan:PHP 静态分析器

  1. 可以捕获基本的错误
  2. 检查phpdoc注释
  3. 重构旧代码
  4. 类型安全
  5. 中间步骤
  6. 参考
  7. 代码库

Phan倾向于避免假阳性,试图证明不正确性,而不是正确性。

php7推出了语法树(php-ast extension),于是静态语法分析更为方便。

看过Rasmus Lerdorf在上海的一次演讲PPT,感觉注释作为代码的一部分,从之前的code review ,到现在的静态分析,越来越重要,自动化依赖注释,代码写作也需要在注释的前提下完成。比如AI智能公司www.ai.codes根据注释写出对应的代码。

Phan能够执行如下类型的特性分析:

可以捕获基本的错误

$a = [1,2,3];
if(count($a > 1)) {
echo "Test";
}
% phan test.php
test.php:2 PhanTypeComparisonFromArray array to int comparison

检查phpdoc注释

class C {
/** @var int $prop */
public $prop;
/**
* @param string $arg
* @return int
*/
function test($arg) {
$this->prop = $arg;
return $arg;
}
}
% phan test.php
test.php:10 PhanTypeMismatchProperty Assigning string to property but \C::prop is int
test.php:11 PhanTypeMismatchReturn Returning type string but test() is declared to return int

重构旧代码

class C {
/**
* @deprecated
*/
static function legacy_function() { }
}
C::legacy_function();
% phan test.php
test.php:8 PhanDeprecatedFunction Call to deprecated function \C::legacy_function() defined at test.php:5

类型安全

声明为严格类型时,错误将以异常形式抛出

<?php declare(strict_types=1);
class Data {
function __construct(array $data) {
$this->haystack = $data;
}
function find(string $needle):bool {
return in_array($needle, $this->haystack, true);
}
}
$storage = new Data(['apple','orange','banana']);
$fruit = false;
$storage->find($fruit);
Fatal error: Uncaught TypeError: Argument 1 passed to Data::find() must be of the type string, boolean given,
called in test.php on line 13 and defined in test.php:6
Stack trace:
#0 test.php(13): Data->find(false)
#1 {main}
thrown in test.php on line 6

中间步骤

为了避免类似问题,可以提前使用Phan跑一边,就可以发现此类问题。

class Data {
/** @var array $haystack */
public $haystack;
/**
* @param array $data
*/
function __construct($data) {
$this->haystack = $data;
}
/**
* @param string $needle
* @return bool
*/
function find($needle) {
return in_array($needle, $this->haystack, true);
}
}
$storage = new Data(['apple','orange','banana']);
$fruit = false;
$storage->find($fruit);

$ phan test.php
test.php:22 PhanTypeMismatchArgument Argument 1 (needle) is bool but \Data::find() takes string defined at test.php:15

感觉有了静态分析,在写过一遍代码之后,直接用phan跑一边,就能先刨除掉一部分异常或者bug。

参考

代码库

script>