Phan — статический анализатор PHP, который предпочитает минимизировать ложные срабатывания. Фан пытается доказать не правильность, а ошибочность.
Phan ищет общие проблемы и проверяет совместимость типов в различных операциях, когда информация о типе доступна или может быть выведена. Фан хорошо (но не всесторонне) понимает управление потоками и может отслеживать значения в некоторых случаях использования (например, массивы, целые числа и строки).
Самый простой способ использовать Phan — через Composer.
composer require phan/phan
После установки Phan вам нужно будет создать в своем проекте файл .phan/config.php
чтобы указать Phan, как анализировать ваш исходный код. После настройки вы можете запустить его через ./vendor/bin/phan
.
Phan 5 зависит от PHP 7.2+ с расширением php-ast (предпочтительно 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_*()
@suppress
, которые больше не нужны.Пример: Плагины Фана для самоанализа.
После установки 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, включая типы Union (например 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.
Фан проверяет тип каждого элемента массивов (включая ключи и значения). На практике это означает, что [$int1=>$int2,$int3=>$int4,$int5=>$str6]
рассматривается как array<int,int|string>
, который Фан представляет как 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
включающий файл, который устанавливает кучу глобальных переменных, и вы затем пытаетесь получить к ним доступ после include(...)
в index.php
статический анализатор ничего об этом не узнает.
На практике это просто означает, что вы должны поместить точки входа и любые файлы, устанавливающие параметры в глобальной области, вверху списка файлов. Если у вас есть config.php
, который устанавливает глобальные переменные, необходимые для всего остального, вам следует поместить его первым в список, затем различные точки входа, а затем все файлы библиотеки, содержащие ваши классы.
Ознакомьтесь с Руководством разработчика по Phan, чтобы узнать, как начать взламывать Phan.
Обнаружив проблему, найдите время и создайте небольшой фрагмент кода, иллюстрирующий ошибку. И как только вы это сделаете, исправьте это. Затем превратите фрагмент кода в тест и добавьте его в тесты, затем ./test
и отправьте PR с исправлением и тестом. Кроме того, вы можете открыть проблему с подробностями.
Чтобы запустить модульные тесты Phan, просто запустите ./test
.
Чтобы запустить все модульные и интеграционные тесты Phan, запустите ./tests/run_all_tests.sh
Мы стремимся способствовать развитию гостеприимного сообщества. Любой участник и участник обязан соблюдать наш Кодекс поведения.
Для этого требуется актуальная версия Firefox/Chrome и не менее 4 ГБ свободной оперативной памяти. (это загрузка 15 МБ)
Запустите Phan полностью в своем браузере.