phpcpd (php copy paste detector) & Phing & Hudson

Решил довнедрять в повседневную жизнь связку hudson+phing, которую мы начали внедрять ещё после Symfony Camp UA 2010 и как-то этот процесс затянулся. Попутно напишу несколько заметок для закрепления материала.

Начну с phpcpd

phpcpd — утилита написанная Себастьяном Бергманом (автором PHPUnit), основное предназначение которой поиск copy-past кода.
Её исходники доступны на github. Там же есть руководство по установке и использованию.

Установка

Команды для установки phpcpd с PEAR канала в Debian based ОС:

$ sudo pear channel-discover pear.phpunit.de; sudo pear channel-discover components.ez.no; sudo pear install phpunit/phpcpd

Работает. Приятно :)

Для проверки работы я натравил её на исходники ZFEngine:

$ phpcpd /var/www/work/skidkaest.ru/library/ZFEngine/
phpcpd 1.3.2 by Sebastian Bergmann.
 
Found 1 exact clones with 8 duplicated lines in 2 files:
 
  - Module/UserBase/models/Base/Mailer.php:20-28
    Module/UserExtended/models/Base/Mailer.php:20-28
 
0.07% duplicated lines out of 12233 total lines of code.
 
Time: 2 seconds, Memory: 24.50Mb

Сразу нашелся небольшой копипаст :)

Дополнительные возможности

Если запустить phpcpd без параметров или с ключем –help, то можно увидеть кратку справку:

$ phpcpd 
phpcpd 1.3.2 by Sebastian Bergmann.
 
Usage: phpcpd [switches] <directory|file> ...
 
  --log-pmd <file>         Write report in PMD-CPD XML format to file.
 
  --min-lines <N>          Minimum number of identical lines (default: 5).
  --min-tokens <N>         Minimum number of identical tokens (default: 70).
 
  --exclude <directory>    Exclude <directory> from code analysis.
  --suffixes <suffix,...>  A comma-separated list of file suffixes to check.
 
  --help                   Prints this usage information.
  --version                Prints the version and exits.
 
  --verbose                Print progress bar.

Как видим ключей немного.

Например, для того чтобы сохранить результат в формате PMD-CPD, нужно запустить утилиту с параметром –log-pmd :

$ phpcpd --log-pmd ./build/logs/pmd.xml

Получился вот такой результат:

<?xml version="1.0" encoding="UTF-8"?>
<pmd-cpd version="phpcpd 1.3.2">
  <duplication lines="8" tokens="26">
    <file path="/var/www/work/skidkaest.ru/library/ZFEngine/Module/UserBase/models/Base/Mailer.php" line="20"/>
    <file path="/var/www/work/skidkaest.ru/library/ZFEngine/Module/UserExtended/models/Base/Mailer.php" line="20"/>
    <codefragment>    public function setTableDefinition()
    {
        $this-&gt;setTableName('mailer');
        $this-&gt;hasColumn('id', 'integer', 4, array(
             'type' =&gt; 'integer',
             'unsigned' =&gt; true,
             'primary' =&gt; true,
             'autoincrement' =&gt; true,
</codefragment>
  </duplication>
</pmd-cpd>

Ещё можно регулировать параметры –min-lines и –min-tokens для того чтобы ужесточить или наоборот ослабить условия поиска.
Также при сканировании больших библиотек полезным будет ключ –verbose, чтобы отображать прогресс бар.

phpcpd + Phing

Phing это инструмент предназначенный для автоматической сборки билдов (Apache Ant для PHP). За последние несколько лет я несколько раз пробовал его внедрить, но реальное удобство заключается именно в использовании Phing вместе с Hudson.

Устанавливается phing достаточно просто. Читайте офф. документацию.

Итак, создаю в /var/www/work/skidkaest.ru/library/ZFEngine/ файл build.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project name="zfengine" basedir="." default="build">
    <property name="builddir" value="./build" />
 
    <target name="init">
        <mkdir dir="${builddir}" />
        <mkdir dir="${builddir}/logs" />
    </target>
 
    <target name="clean">
        <echo msg="Clean..." />
        <delete dir="${builddir}" />
    </target>
 
    <target name="build" depends="clean,init,phpcpd" />
 
    <target name="phpcpd">
        <exec command="phpcpd --log-pmd ${builddir}/logs/pmd.xml ." escape="false" />
    </target>
</project>

И запускаю:

$ phing 
Buildfile: /var/www/work/skidkaest.ru/library/ZFEngine/build.xml
 
zfengine > clean:
 
     [echo] Clean...
   [delete] Deleting directory /var/www/work/skidkaest.ru/library/ZFEngine/build
 
zfengine > init:
 
    [mkdir] Created dir: /var/www/work/skidkaest.ru/library/ZFEngine/build
    [mkdir] Created dir: /var/www/work/skidkaest.ru/library/ZFEngine/build/logs
 
zfengine > phpcpd:
 
     [exec] Executing command: phpcpd --log-pmd ./build/logs/pmd.xml . 2>&1
 
zfengine > build:
 
 
BUILD FINISHED
 
Total time: 0.5840 seconds

Проверяю или создался лог:

$ ls -l build/logs/
total 4
-rw-r--r-- 1 stfalcon stfalcon 693 2010-12-01 02:47 pmd.xml

Работает. Приятно :)

phpcpd + phing + Hudson

Осталось совсем немного. Для чувства выполненного долга нужно выполнять эти проверки в Hudson.

Hudson это бесплатный и открытый сервер для CI. Недавно на Хабре была опубликована толковая статья “Непрерывная интеграция на примере Hudson“, которая и сподвигла меня разобраться в этом хозяйстве самостоятельно.

Установка Hudson в Debian based ОС тоже достаточно тривиальна. Читайте заметку в офф. вики проекта.

После установки нужно активировать плагины:
Duplicate Code Scanner Plug-in
Hudson Phing plugin

Первым делом я добавил конфиг build.xml в репозиторий ZFEngine. После чего создал новый проект (Build a free-style software project).

Настройки репозитория:

Настройки билда и дествий которые выполняются после билда:

Здесь главное добавить в build steps “Invoke Phing targets” и положить в репозиторий или workspace конфиг для Phing build.xml.

Пробуем сделать билд “Build Now” и если все ок, то шарик будет голубым или зеленым (плагин Green Balls).

Видите сообщение о 2х ошибках “Duplicate Code: 2 warnings from one analysis file.”? Если щелкнуть на ссылку в сообщении, то будет видна следующая картина:

В моем случае копипастом (а точнее cгенерированным Doctrine кодом) оказалась часть кода базовой модели пользователя:

Вот и все. Первые шаги к CI сделаны и завтра я буду двигаться дальше.

PS. Ну как вам Кейт Хадсон :)?

UPD.
В ходе чтения мануала по Phing увидел, что есть готовый task для phpcdp.
Т.е. правильней этот эту часть конфига в build.xml будет оформить так:

<target name="phpcpd">
    <phpcpd>
        <fileset dir="." id="filestocpd">
            <include name="*.php" />
        </fileset>
        <formatter type="pmd" outfile="${builddir}/logs/pmd.xml"/>
    </phpcpd>
</target>

UPD2. Читайте продолжение “Непрерывная интеграция ZF проекта при помощи Hudson & Phing

5 thoughts on “phpcpd (php copy paste detector) & Phing & Hudson

  1. Вижу фраза “Работает. Приятно” зацепила не только меня =)))
    За материал спасибо!

  2. Слова “Работает. Приятно.” напомнили “Я поставил стеклянные окна, он тоже поставил. Я сделал камин, он тоже сделал. Я купил радио, он не накопил. Радостно.” :D

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>