Phan 是 PHP 的静态分析器,它更愿意最大限度地减少误报。潘试图证明错误而不是正确。
Phan 寻找常见问题,并在类型信息可用或可以推断时验证各种操作的类型兼容性。 Phan 对流量控制有很好的(但不全面)理解,并且可以跟踪一些用例中的值(例如数组、整数和字符串)。
使用 Phan 最简单的方法是通过 Composer。
composer require phan/phan
安装 Phan 后,您需要在项目中创建一个.phan/config.php
文件来告诉 Phan 如何分析源代码。配置完成后,您可以通过./vendor/bin/phan
运行它。
Phan 5 依赖于带有 php-ast 扩展的 PHP 7.2+(首选 1.1.1+),并支持分析 PHP 7.0-8.2 版本语法。 php-ast 的安装说明可以在这里找到。 (Phan 可以在没有 php-ast 的情况下使用 CLI 选项--allow-polyfill-parser
,但在解析文档注释时略有不同)
Wiki 有更多有关使用 Phan 的信息。
Phan 能够执行以下类型的分析:
object
、 void
、 iterable
、 ?T
、 [$x] = ...;
、负字符串偏移量、多个异常捕获等)--dead-code-detection
)--unused-variable-detection
)--redundant-condition-detection
)use
语句。这些问题和其他一些问题类型可以使用--automatic-fix
自动修复。@template
)。int[]
、 UserObject[]
、 array<int,UserObject>
等。array{key:string,otherKey:?stdClass}
等(内部和 PHPDoc 标签中)这也支持通过array{requiredKey:string,optionalKey?:string}
指示数组形状的字段是可选的(对于@param
有用)@deprecated
注解来弃用类、方法和函数@internal
注释作为定义它的包的内部元素。@suppress <ISSUE_TYPE>
注释来抑制问题。@property <union_type> <variable_name>
)@method <union_type> <method_name>(<union_type> <param1_name>)
)class_alias
注释(实验性的,默认关闭)@phan-closure-scope
指示闭包将绑定到的类(示例)array_map
、 array_filter
和其他内部数组函数的闭包和返回类型。pcntl
)请参阅 Phan 问题类型,了解 Phan 可以检测到的所有问题的描述和示例。查看 PhanIssue 以查看每种错误类型的定义。
查看分析大型草率代码库的教程,了解持续分析的过程对您来说可能是什么样的。
Phan 可以通过语言服务器协议在各种编辑器和 IDE 中使用,以进行错误检查、“转到定义”支持等。编辑器和工具还可以使用更简单的守护程序模式请求对项目中的各个文件进行分析。
有关各种检查的一些示例,请参阅测试目录。
Phan 并不完美,不应该用来证明您的基于 PHP 的火箭制导系统没有缺陷。
插件提供了额外的分析功能。
{ throw new Exception("Message"); return $value; }
)*printf()
格式字符串(以及检查常见错误)preg_*()
的 PCRE 正则表达式是否有效@suppress
注释。示例:Phan 的自我分析插件。
安装 Phan 后,Phan 需要配置有关在哪里查找要分析的代码以及如何分析代码的详细信息。告诉 Phan 在哪里可以找到源代码的最简单方法是创建一个.phan/config.php
文件。一个简单的.phan/config.php
文件可能如下所示。
<?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 : `'5.6'` , `'7.0'` , `'7.1'` , `'7.2'` , `'7.3'` , `'7.4'` ,
// `'8.0'` , `'8.1'` , `'8.2'` , `'8.3'` , `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 " => null ,
// 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 ' => [
' src ' ,
' vendor/symfony/console ' ,
],
// 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 3 rd
// 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 .
// Plugins which are bundled with Phan can be added here by providing their name
// ( e . g . 'AlwaysReturnPlugin' )
//
// Documentation about available bundled plugins can be found
// at https : // github . com / phan / phan / tree / v5 / . phan / plugins
//
// Alternately , you can pass in the full path to a PHP file
// with the plugin ' s implementation .
// ( e . g . 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php' )
' plugins ' => [
// checks if a function , closure or method unconditionally returns .
// can also be written as 'vendor/phan/phan/.phan/plugins/AlwaysReturnPlugin.php'
' AlwaysReturnPlugin ' ,
' DollarDollarPlugin ' ,
' DuplicateArrayKeyPlugin ' ,
' DuplicateExpressionPlugin ' ,
' PregRegexCheckerPlugin ' ,
' PrintfCheckerPlugin ' ,
' SleepCheckerPlugin ' ,
// Checks for syntactically unreachable statements in
// the global scope or function bodies .
' UnreachableCodePlugin ' ,
' UseReturnValuePlugin ' ,
' EmptyStatementListPlugin ' ,
' LoopVariableReusePlugin ' ,
],
];
有关更多详细信息,请参阅创建配置文件和增量强化分析。
运行phan --help
将显示使用信息和命令行选项。
Phan 可以阅读并理解大多数 PHPDoc 类型注释,包括联合类型(如int|MyClass|string|null
)和通用数组类型(如int[]
或string[]|MyClass[]
或array<int,MyClass>
)。
请查看注释源代码和关于联合类型,以获取在代码中定义类型的一些帮助。
Phan 支持(int|string)[]
样式注释,并在内部将它们表示为int[]|string[]
(这两个注释都被视为可能包含整数和/或字符串的数组)。当您有混合类型的数组时,只需使用array
。
以下代码显示了支持的各种注释。
/ * *
* @ return void
* /
function f () {}
/ * * @ deprecated * /
class C {
/ * * @ var int * /
const C = 42 ;
/ * * @ var string [] | null * /
public $ p = null ;
/ * *
* @ param int | null $ p
* @ return string [] | null
* /
public static function f ( $ p ) {
if ( is_null ( $ p )) {
return null ;
}
return array_map (
/ * * @ param int $ i * /
function ( $ i ) {
return " thing $ i " ;
},
range ( 0 , $ p )
);
}
}
就像在 PHP 中一样,任何类型都可以在函数声明中为 null,这也意味着允许为该参数传入 null。
Phan 检查数组每个元素的类型(包括键和值)。实际上,这意味着[$int1=>$int2,$int3=>$int4,$int5=>$str6]
被视为array<int,int|string>
, Phan 表示为array<int,int>|array<int,string>
。 [$strKey => new MyClass(), $strKey2 => $unknown]
将表示为array<string,MyClass>|array<string,mixed>
。
[12,'myString']
之类的文字将在内部表示为数组形状,如array{0:12,1:'myString'}
该静态分析器不会跟踪包含或尝试找出自动加载器的魔力。它将您扔给它的所有文件视为一个大应用程序。对于封装在类中的代码来说,这很有效。对于在全局范围内运行的代码,它会变得有点棘手,因为顺序很重要。如果你有一个index.php
其中包含一个设置一堆全局变量的文件,然后你尝试在index.php
中的include(...)
之后访问这些变量,静态分析器将不会知道这些变量。
实际上,这仅仅意味着您应该将入口点和全局范围内的任何文件设置放在文件列表的顶部。如果您有一个config.php
设置其他所有需要的全局变量,那么您应该将其放在列表中的第一个,然后是各种入口点,然后是包含类的所有库文件。
请参阅 Phan 开发人员指南,以获取开始对 Phan 进行黑客攻击的帮助。
当您发现问题时,请花时间创建一个小的重现代码片段来说明该错误。一旦你做到了这一点,就修复它。然后将您的代码片段转换为测试并将其添加到测试中,然后./test
并发送包含您的修复和测试的 PR。或者,您可以打开一个包含详细信息的问题。
要运行 Phan 的单元测试,只需运行./test
。
要运行 Phan 的所有单元测试和集成测试,请运行./tests/run_all_tests.sh
我们致力于营造一个热情的社区。任何参与者和贡献者都必须遵守我们的行为准则。
这需要最新版本的 Firefox/Chrome 和至少 4 GB 的可用 RAM。 (这是一个 15 MB 的下载)
完全在浏览器中运行 Phan。