Как стать автором
Обновить

Автоматическая проверка кода для PHP

Время на прочтение4 мин
Количество просмотров2.2K
Разрешите представить Вам перевод статьи Johannes Schmitt Automated Code Reviews for PHP. Лично мне она помогла несколько иначе взглянуть на процесс разработки и тестирования своих приложений. А оригинальный подход автора к тестированию, как минимум, заслуживает внимания.
Если вам тоже интересно, добро пожаловать под кат.

С тех пор как появился Trevis, вы можете в мгновение ока внедрить непрерывную интеграцию во все свои PHP-проекты. Это помогает не только улучшить качество кода, но и существенно упрощает поддержку библиотек, предоставляя информацию о сборке прямо в запрос на обновление(pull request) и, тем самым, уменьшает время получения обратной связи. Travis очень хорош, но, как и другие инструменты тестирования, страдает от наследственной болезни — что бы что-то сделать ему нужны тесты. Готовь биться об заклад, что у вас нет ни одного проекта честно покрытого тестами на 100% или, даже, близко к этому. Это я еще надеюсь, что тесты вы пишите.

Как вам возможно известно, я поддерживаю значительное число плагинов(bundles) для Symfony2 и самостоятельных PHP-библиотек. И благодаря сообществу(спасибо ребята, так держать) я постоянно получаю запросы на обновление в свои репозитории. Некоторые из запросов совершенно бесполезные, некоторые заслуживают внимания, некоторые можно добавлять в основную ветку. Но как бы тщательно не проверялся запрос, время от времени случается так, что добавляется то что не работает или работает, но не всегда.

Пару месяцев назад я попытался изменить эту ситуацию, идея была довольно простой: создать систему которая проверяет код запроса на обновление и дает обратную связь. Я довольно быстро сделал прототип и добавил в него пару простых проверок. Затем, захотел добавить более сложны, например, проверку может ли метод быть вызван. Что бы понять пользу такой проверки, посмотрите на следующий пример:
<?php

class UserProvider
{
    /** @return User|null */
    public function loadUser($username) { /** ... */ }

    public function refreshUser(User $user)
    {
        if (null === $user = $this->loadUser($user->getUsername())) {
            throw new RuntimeException(
                sprintf('User "%s" was not found.', $user->getUsername()));
        }

        return $user;
    }
}

И так, метод refreshUser получает объект класса User при помощи метода loadUser и возвращает этот объект. А если объект не найден, то бросает исключение. Вроде бы все просто, но так ли это на самом деле? И если уж я об этом спрашиваю, то видимо нет и многие из вас уже заметили ошибку. Внутри блока if $user равен null и мы не можем вызвать у него метод getUserName. Что бы находить такого рода ошибки я испробовал несколько простых решений, но довольно быстро становилось очевидно что они работают только в очень специфичных случаях. Мне было нужно что-нибудь получше.

Type Inference of PHP Code

Я потратил довольно много времени вникая в концепции потока данных, потока управления и абстрактной интерпретации. Что само по себе выглядит довольно сложно и выходит за рамки этой статьи. Но позвольте мне привести всего несколько примеров и дать вам общее представление об этих концепциях.

Анализ потока управления(Control Flow Analysis)

Анализ этого потока позволяет определить в каком порядке будут выполняться различные блоки вашего кода.
<?php

function fooBar($i) {
    if ($i > 0) {
        echo 'foo';
    } else {
        echo 'bar';
    }
}

Для этого кода поток управления будет выглядеть так:

Мы начинаем в if, затем двигаемся к «foor» или «bar» и, наконец, выходим. Само по себе нам это вряд ли чем-то поможет, но это послужит основой для следующего шага.

Анализ потока данных(Data Flow Analysis)

Анализ потока данных позволяет определить как изменяется контекст выполнения пока мы движемся по схеме которую определили в анализе потока управления.
<?php

$x = null; // $x здесь null.

if ($y) {
    $x = new DateTime();
    $x->format(); // тут все хорошо, мы ведь знаем что $x это экземпляр DateTime
} else {
    $x = 0;
}

$x->format(); // $x экземпляр DateTime или число "integer", в зависимости от этого
                    // метод может быть вызван а может и нет

Не зная порядок выполнения кода, мы может заключить только то, что $x может быть null, число или DateTime. Но нам это не поможет выяснить может ли быть вызван метод format.

Абстрактная интерпретация(Abstract Interpretation)

Для нашего случая эта концепция сводится к вопросу «Какие предположения мы можем сделать, если знаем результат условного выражения?». Давайте взглянем на другой пример:
<?php

class Foo
{
    private $logger;

    public function __construct(Logger $logger = null)
    {
        $this->logger = $logger;
    }

    public function doSth()
    {
        if (null !== $this->logger) {
            $this->logger->log('doing sth');
        }
    }
}

В данном случае «условным выражением» будет null !== $this->logger. Если это условие истинно, то наш вопрос можно перефразировать так: «Если выражение null !== $this->logger истинно, то какое предположение можно сделать на счет $this->logger?» Как мы уже выяснили, $this->logger может быть null или Logger. Но благодаря абстрактной интерпретации мы можем быть уверены что внутри блока «if» $this->logger всегда будет экземпляром Logger, следовательно, метод может быть вызван.

Автоматическая система проверки


Какой от всего этого толк, спросите вы. В начале статьи я сказал, что моей целью было создание автоматической системы проверки кода. И я думаю что сейчас она готова для широкого использования и обсуждения. Я протестировал своей системой ведущие PHP библиотеки, такие как, Zend Framework 2, Symfony2, Doctrine, Propel и многие другие. Она содержит более 100 правил проверки, которые вы можете использовать и конфигурировать. Если у вас есть PHP-проект на Github вы можете легко попробовать. Просто залогинтесь http://jmsyst.com/automated-code-reviews и выберете нужный репозиторий. А если не понравиться, можете выключить в любое время.

Если теперь кто-то скажет, что PHP-программисты не слишком серьезно относятся к качеству кода, отправляйте их их ко мне.
Теги:
Хабы:
Всего голосов 13: ↑9 и ↓4+5
Комментарии1

Публикации

Истории

Работа

Ближайшие события