Настройка и использование в PHP профилировщика кода xDebug

Краткая инструкция по настройке профилировщика под Windows.

В консоле вызываем команду

php -i phpinfo() > D:\phpinfo.txt

Копируем в буфер обмена полученное содержимоей файла phpinfo.txt

Заходим на сайт http://xdebug.org/wizard.php

Вставляем в текстовое поле и нажимаем кнопку «Analyse my phpinfo() output»

Будет выдана инструкция:

Instructions

1. Скачать php_xdebug-2.3.3-5.4-vc9.dll (http://xdebug.org/files/php_xdebug-2.3.3-5.4-vc9.dll)

2. Положить этот файл в C:\Server\PHP\ext

3. Отредактировать файл C:\Server\PHP\php.ini и добавить новую строку

zend_extension = C:\Server\PHP\ext\php_xdebug-2.3.3-5.4-vc9.dll

Для включения профилировщика добавляем в php.ini следующие строки

[xdebug]
zend_extension = C:\Server\PHP\ext\php_xdebug-2.3.3-5.4-vc9.dll
xdebug.remote_enable=1
xdebug.remote_host=localhost
xdebug.remote_port=9000
xdebug.profiler_enable = 1
xdebug.profiler_output_name = callgrind.out.%t
xdebug.profiler_output_dir = D:\prof
xdebug.profiler_enable_trigger = 1

Все файлы профилировщик будет создавать в папке D:\prof

Перезапускаем web-server (Apache).

Находим программу qcachegrind для вашей платформы. Для Windows можно скачать здесь http://sourceforge.net/projects/qcachegrindwin/

Распаковываем архив в любую папку. И запускаем qcachegrind.exe

В этой программе открываем полученные файлы из папки D:\prof и смотрим на результаты в виде таблицы и диграмм.

Практическое использование PHPUnit на Windows 7 + NetBeans 8 + Yii 1.1.x

Устанавливаем PHPUnit на Windows 7

  1. В php.ini раскоментировать строку extension=php_openssl.dll (нужно для установки  Composer)
  2. Скачиваем  Composer —  https://getcomposer.org/Composer-Setup.exe
  3. Запускаем установщик Composer-Setup.exe
  4. Открываем консоль и выполняем команду composer global require "phpunit/phpunit=4.1.*"
  5. Открываем консоль и выполняем команду composer global require "phpunit/phpunit-selenium=1.3.3"
  6. Открываем страницу в Firefox и устанавливаем плагин http://release.seleniumhq.org/selenium-ide/2.5.0/selenium-ide-2.5.0.xpi (ссылка на плагин находится на странице http://docs.seleniumhq.org/download/)
  7.  Открываем страницу в Firefox и устанавливаем плагин для экспорта теста виде PHP-кода — https://addons.mozilla.org/en-US/firefox/addon/selenium-ide-php-formatters/
  8. Скачиваем Selenium http://selenium-release.storage.googleapis.com/2.42/selenium-server-standalone-2.42.2.jar (эта ссылка расположена на странцие http://docs.seleniumhq.org/download/)
  9. Запускаем Selenium Server из консоли java -jar selenium-server-standalone-2.42.2.jar

Настройка NetBeans 8

Заходим в меню «Сервис» -> «Параметры». Вверху окошка выбираем «PHP», вкладку «Платформы и инструменты», слева в списке «PHPUnit». В поле «Сценарий PHPUnit» пишем:

C:\Users\Sergey\AppData\Roaming\Composer\vendor\phpunit\phpunit\phpunit

Настройка NetBeans для PHPUnit

Затем идем в «Свойства» проекта. На вкладке «Путь к include» добавляем папку » C:\Users\Sergey\AppData\Roaming\Composer\vendor». Чтобы работало автодополнение при написании тестов.

Настройка NetBeans для PHPUnit

На вкладке «Тестирование» добавляем папку с тестами из проекта на Yii и отмечаем галочку «PHPUnit»:

Настройка NetBeans для PHPUnit

Настройка проекта на Yii для запуска тестов

Для запуска модульных тестов понадобиться в коде фреймворка закомментировать несколько строчек. Нужно открыть файл «CTestCase.php»:

//require_once('PHPUnit/Runner/Version.php');
//require_once('PHPUnit/Util/Filesystem.php'); // workaround for PHPUnit <= 3.6.11
//require_once('PHPUnit/Autoload.php');

Далее можно подредактировать файл «protected/tests/phpunit.xml». В секции <selenium> можно добавить в каких браузерах запускать тесты. Я сначала написал «*chrome», чтобы запускать тесты в Google Chrome, но оказалось, что в этом случае запускается Firefox в каком-то особом режиме, поэтому надо писать  «*googlechrome».

 <selenium>
   <browser name="Internet Explorer" browser="*iexplore" />
   <browser name="Chrome" browser="*googlechrome" />
   <browser name="FireFox" browser="*firefox" />
 </selenium>

В папке «protected/config» нужно добавить файл конфига для тестов «test.php».

В файле «protected/tests/WebTestCase.php» определяем TEST_BASE_URL — хост на котором будет запускаться проект:

define('TEST_BASE_URL', 'http://pachkov.local/');

Запускать тесты можно прямо из NetBeans. Нажатием клавиш Alt+F6 — можно запустить выполнение всех тестов. Либо можно выполнить отдельный тест. Для этого щелкаем правой кнопкой мыши по коду теста и выбираем пункт меню «Метод выполнения специализированного теста». Либо в окне «Проекты» можно запустить тесты из отдельного файла или папку тестов.

После выполнения тестов появится окно «Результаты тестирования»:

Настройка NetBeans для PHPUnit

Создание тестов и экспорт кода

Записать функциональный тест можно в Firefox с помощью Selenium IDE. Выполняя действия на сайте — заполняя поля, делая клики — в Selenium IDE записываются все действия.

Firefox Selenium IDE

После записи теста можно экспортировать результат в виде PHP кода (именно для это мы устанавливали вначале плагин Selenium IDE: PHP Formatters). В меню Selenium IDE выбираем «Файл»-> «Export Test Case As…» -> «PHP (PHPUnit)».

Конструкции используемые для функциональных тестов

Проверяем открытие страницы и наличие определенного текста на странице:

public function testIndex()
{
   $this->open('');
   $this->assertTextPresent('Текст на странице');
}

Можно проверять как наличие определенного текста на странице «$this->assertTextPresent(«text»);», так и его отсутствие «assertTextNotPresent».

Проверить видимость определенного элемента можно так:

$this->assertVisible("id=NameFieldID");

Если по клику выполняется AJAX запрос, то после клина надо ждать появления на странице определенного текста. Например, нажимаем кнопку для отправки формы и ждем результата AJAX валидации полей формы:

$this->click("name=yt0");
$this->waitForTextPresent('Ваш E-mail обязательное поле');

Заполнение полей формы:

$this->type("name=login", "email@mail.ru");

Для записи значения в hidden поле используется следующая конструкция:

$this->runScript("javascript{ this.browserbot.getCurrentWindow().document.getElementById('NameField').value = '93384159f527b445948fdc60939bb6e230bc2cff.png'; }");

Перед тестирование входа пользователя на сайт, можно проверить залогинен он, если да, то делаем выход

if ($this->isTextPresent('Выход'))
   $this->clickAndWait('css=span.btn_logout');

Если по клику выполняется javascript анимация, то можно сделать паузу перед выполнение проверок:

sleep(1);

После клика дождаться загрузки страницы:

$this->clickAndWait("name=log_in");

Пример модульного теста

class ThumbnailTest extends CTestCase
{

    /**
     * Загрузка слишком маленького изображения.
     */
    public function testUploadImageValidation1()
    {
        $_FILES = array(
            'file' => array(
                'name' => 'test.jpg',
                'type' => 'image/jpeg',
                'size' => 2001,
                'tmp_name' => dirname(__FILE__) . '/../../../images/vk.png',
                'error' => 0
            )
        );
        $confParam = Yii::app()->params['avatars'];
        $result = Thumbnail::upload_image_validation($_FILES, $confParam['min_width'], $confParam['min_height']);

        $this->assertContains('Слишком маленькое изображение.', $result);
    }
    
    /**
     * Не правильное расширение файла.
     */
    public function testUploadImageValidation4()
    {
        $_FILES = array(
            'file' => array(
                'name' => 'test.pic',
                'type' => 'image/jpeg',
                'size' => 5737,
                'tmp_name' => dirname(__FILE__) . '/../../../images/img_question.jpg',
                'error' => 0
            )
        );
        $confParam = Yii::app()->params['avatars'];
        $result = Thumbnail::upload_image_validation($_FILES, $confParam['min_width'], $confParam['min_height']);

        $this->assertContains('Не допустимое расширение файла', $result);
    }
    
    /**
     * Тестирование метода createAvatar
     */
    public function testCreateAvatar()
    {
        $image = dirname(__FILE__) . '/../../../images/header_03.png';
        $userID = 186855;
        $listPrefix = Thumbnail::get_avatar_file_name('header_03.png', $userID);
        $result = Thumbnail::createAvatar($image, $userID, $listPrefix);
        $this->assertTrue($result);
        $imageNew = Yii::app()->params['pathUpload'] . '/' . Yii::app()->params['avatars']['path'] . '/' . $userID . '/' . $listPrefix['big'];
        $this->assertFileExists($imageNew, 'Файл изображения big не найден.');
        $this->assertGreaterThan(100000, filesize($imageNew), 'Слишком маленький размер изображения big.');
        $imageNew = Yii::app()->params['pathUpload'] . '/' . Yii::app()->params['avatars']['path'] . '/' . $userID . '/' . $listPrefix['small'];
        $this->assertFileExists($imageNew, 'Файл изображения small не найден.');
        $this->assertGreaterThan(5000, filesize($imageNew), 'Слишком маленький размер изображения small.');
        $imageNew = Yii::app()->params['pathUpload'] . '/' . Yii::app()->params['avatars']['path'] . '/' . $userID . '/' . $listPrefix['large'];
        $this->assertFileExists($imageNew, 'Файл изображения large не найден.');
        $this->assertGreaterThan(50000, filesize($imageNew), 'Слишком маленький размер изображения large.');
    }
}

Выводы

Использование тестирования заставляет писать более продуманный код. Во время написания тестов для одного проекта, пришлось переписать часть кода отвечающего за загрузку изображения, так чтобы его можно было протестировать. При этом в коде были найдены и исправлены ошибки. Тесты позволяют обнаружить ошибки (или даже сломанный функционал) когда сделаны изменения в одном месте, а ошибка вылезла в другом.

Множественное число (plural format) на PHP

На просторах сети после долгих поисков было обнаружено самое короткое решение проблемы множественного числа для русского языка. В отличие от громоздких функций этот вариант занимает всего одну строку кода.

Все, что нужно это указать слово в трех его формах. В переменную $plural заносится правильный вариант склонения слова. Переменная $n содержит число элементов для рассчета.

$plural = $n%10==1&&$n%100!=11?'число':($n%10>=2&&$n%10<=4&&($n%100<10||$n%100>=20)?'числа':'чисел');

Ошибка в NetBeans при создании PhpDoc (for Windows)

Пытаясь настроить NetBeans 7.0 столкнулся с проблемой запуска автоматической генерации документации — PhpDocumentor.

Я использовал сборку XAMPP. Чтобы Windows знала, где у меня лежит pear, добавил в системную переменную path «C:\xampp\php» (надо нажать комбинацию Windows+Pause, на вкладке «Дополнительно» кнопка «Переменные среды», выделяем системную переменную Path и кнопку «Изменить»). Чтобы установить PhpDocumentor надо было выполнить простую команду:

pear install —alldeps PhpDocumentor

Но я получил отказ:
Ignoring installed package pear/PhpDocumentor
Nothing to install

И даже флаг принудительной установки ( -f ) не помог:
ERROR: unable to unpack PhpDocumentor-1.4.3.tgz

Пришлось устанавливать вручную. Распаковал архив в C:\xampp\php\PEAR\PhpDocumentor\. Изменил одну строчку (line 16) в phpdoc.bat и добавил свою:
SET phpCli=C:\xampp\php\php.exe
CD C:\xampp\php\PEAR\PhpDocumentor

После этого документация стала успешно генерироваться из консоли, но при вызове из NetBeans выдавалось окошко:

Ошибка при вызове фильтра. Было создано следующее сообщение об ошибке:
java.util.regex.PatternSyntaxException: Illegal hexadecimal escape sequence near index 10
(.*)(C:\xampp\htdocs\PhpProject1\phpdoc/errors\.html)(.*)

Решение проблемы (указано на http://forums.netbeans.org/topic37340.html) сводится к замене в путях к phpdoc.bat и каталоге для генерации документации «\» на «/». К сожалению, я не сразу понял, где (в каком месте) менять эти слэши. В итоге пути должны выглядеть следующим образом:
C:/xampp/htdocs/PhpProject1/phpdoc
C:/xampp/php/PEAR/PhpDocumentor/phpdoc.bat

После этих изменений документация успешно сгенерировалась.

P.S. Windows легко понимает «\» и «/» в путях к файлам.