Неожиданно не нашёл информации на русском языке о такой замечательной возможности HipHop, как статический анализ кода для PHP, а потому встречайте обзор, на идею которого меня натолкнула презентация Расмуса на DevConf.
А как это вообще?
Статический анализ кода — вещь весьма полезная, ведь иначе ошибку мы не увидим, пока функция, её содержащая, не будет вызвана. Как же это делает HipHop? Он транслирует PHP в C++!
Таким образом мы получаем возможность статически проанализировать C++ код, что, в общем, давно никого не удивляет, а потом применить полученную информацию к PHP (естественно автоматически).
Итак, начнём.
Окружение
Для тестов я поднял новый сервер Centos 5.8 x64
# uname -a
Linux TEST 2.6.18-308.8.1.el5xen #1 SMP Tue May 29 15:38:25 EDT 2012 x86_64 x86_64 x86_64 GNU/Linux
Установил на нём HipHop
rpm -ivh http://epel.osuosl.org/5/x86_64/epel-release-5-4.noarch.rpm
rpm -ivh http://dl.iuscommunity.org/pub/ius/stable/Redhat/5/x86_64/ius-release-1.0-10.ius.el5.noarch.rpm
rpm -ivh http://pkg.tag1consulting.com/hphp/x86_64/hphp-release-1.0-2.el5.noarch.rpm
yum install hiphop-php -y
Добавил переменную окружения (почему-то сам RPM этого не сделал)
export HPHP_HOME=/usr/lib64/hphp
И создал папку для отчётов
mkdir /var/reports
export SRC_DIR=/var/www/
export OUTPUT_DIR=/var/reports
Теперь окружение готово и можно приступать к экспериментам.
Подготовка к анализу
Сначала нужно составить список файлов, которые мы будем анализировать.
Я анализировал один тестовый файлик, но если вы хотите проверить сразу весь свой проект, то можете сделать так:
find $SRC_DIR -name '*.php' > $OUTPUT_DIR/files.txt
После того, как список файлов составлен, надо скормить его HipHop'у со следующими параметрами:
--target=analyze — зачем мы вызвали HipHop?
--input-list=$OUTPUT_DIR/files.txt — список файлов для проверки
--input-dir=$SRC_DIR — где лежат файлы, которые нужно проверить?
--output-dir=$OUTPUT_DIR — куда сохранить результаты проверки?
--gen-stats=1 — создать файл со статистической информацией об ошибках
--log=3 — уровень лога, который выводится в консоль (3 — оптимально)
--force=1 — игнорировать найденные ошибки и продолжать анализ
--parse-on-demand=0 — не трогать файлы, которых нет в списке
Анализ
Поехали!
$HPHP_HOME/src/hphp/hphp --input-list=$OUTPUT_DIR/files.txt --input-dir=$SRC_DIR --output-dir=$OUTPUT_DIR --gen-stats=1 --log=3 --force=1 --parse-on-demand=0 --target=analyze
Кстати, если в процессе парсинга у вас вылетают ошибки вроде "Unable to parse file: /var/www/user.phpn (Line: 14, Char: 23):
Это значит, что в файле синтаксическая ошибка и он вообще не работает, так что обратите на него внимание!
Результат анализа
Когда hphp завершил свою работу (а это происходит удивительно быстро!), в папке /var/reports/ появляются два файла:
CodeError.js и Stats.js
Содержимое последнего расшифровывается примерно так:
Array
(
[FileCount] => 125
[LineCount] => 11784
[CharCount] => 433255
[FunctionCount] => 350
[ClassCount] => 17
[TotalTime] => 0
[AvgCharPerLine] => 36
[AvgLinePerFunc] => 33
[SymbolTypes] => Array
(
[Array] => 64
[Boolean] => 43
[Double] => 30
[Int32] => 68
[Int64] => 2208
[Numeric] => 23
[Object] => 11
[Object - Specific] => 58
[Primitive] => 31
[Sequence] => 2
[String] => 221
[Variant] => 1458
[_all] => 4217
[_strong] => 2703
[_weak] => 1514
)
[VariableTableFunctions] => Array
(
)
)
На мой взгляд всё понятно и пояснять тут нечего.
А вот CodeError.js надо рассмотреть поподробнее.
Он тоже в формате JSON и содержит информацию обо всех ошибках, что нашёл HipHop.
А ищет он следующие виды неисправностей:
- Отсутствующие файлы для require include
- Вызовы неопределённых функций / методов
- Обращение к неопределённым переменным, классам, константам
- Переопределение констант
- Одинаковые имена аргументов в определении функции
- Обязательные аргументы после необязательных в определении функции
- Недостаточность избыточность аргументов при вызове функции
- Код, который не выполнится ни при каких условиях
- Методы и функции, возвращающие void
- Использование $this в статических методах
-
И много другоеBadPHPIncludeFile, PHPIncludeFileNotFound, UnknownClass, UnknownBaseClass, UnknownFunction, UseEvaluation, UseUndeclaredVariable, UseUndeclaredGlobalVariable, UseUndeclaredConstant, UnknownObjectMethod, InvalidMagicMethod, BadConstructorCall, DeclaredVariableTwice, DeclaredConstantTwice, BadDefine, RequiredAfterOptionalParam, RedundantParameter, TooFewArgument, TooManyArgument, BadArgumentType, StatementHasNoEffect, UseVoidReturn, MissingObjectContext, MoreThanOneDefault, InvalidArrayElement, InvalidDerivation, InvalidOverride, ReassignThis, MissingAbstractMethodImpl, BadPassByReference, ConditionalClassLoading, GotoUndefLabel, GotoInvalidBlock, AbstractProperty, UnknownTrait, MethodInMultipleTraits, UnknownTraitMethod, InvalidAccessModifier, CyclicDependentTraits, InvalidTraitStatement, RedeclaredTrait, InvalidInstantiation
JSON в файле представляет собой массив, в первом элементе которого указано количество найденных ошибок, а во втором собственно их список, сортированный по группам.
Для того, чтобы посмотреть все ошибки в удобочитаемом формате можно использовать скрипт от Patrick Allaert либо написать что-нибудь своё красивое и с плюшками =)
Пример
Есть вот такой ужасный файл
<?php
ini_set('display_errors', 0);
require("$a.php");
define('CONSTANT', 1);
function myfunc($arg1 = 'optional', $arg2) {
echo $missing_var;
}
define('CONSTANT', 1);
Запустим на него тест, а потом попробуем посмотреть ошибки
./CodeErrorFormatter.php /var/report/CodeError.js
Oops, у меня PHP выдал ошибку getopt, но я просто вписал путь к файлу ручками и получил вот такой вывод:
CodeErrorFormatter 1.1.0 by Patrick Allaert.
UseUndeclaredVariable
================================================================================
/var/www/index.php
9:9 -> 9:21 details: $missing_var
4:9 -> 4:11 details: $a
DeclaredConstantTwice
================================================================================
/var/www/index.php
12:0 -> 12:21 details: define('CONSTANT', 1)
RequiredAfterOptionalParam
================================================================================
/var/www/index.php
8:16 -> 8:41 details: $arg2
Как видите, здесь указаны для каждой ошибки тип, адрес файла, название непонравившейся фукции переменной, её строка и позиция в файле. Что с ними делать дальше — уже сами думайте (либо автоматический багрепорт, либо сами исправляйте, либо ещё что).
Заключение
В общем тема статического анализа для PHP не нова, однако русскоязычных статей на эту тему немного и ни одна из них не затрагивает HipHop.
Призываю всех просто попробовать ею воспользоваться. Разумеется не стоит думать, что это «серебряная пуля», но анализ показывает те вещи, которые очень трудно выловить именно человеку.
Надеюсь этот обзор принесёт кому-то пользу, а может кто-то ещё и напишет визуалку, чтобы красиво тестировать код из браузера?
===================
Проект Facebook HipHop
CodeError.js CodeFormatter
Презентация Расмуса Лердорфа на DevConf 2012 talks.php.net/show/devconf/ — обязательно к просмотру!
Автор: nswbit