Автоматическая сборка PHP-проектов с помощью PhiNG. Как завести всё это вместе

Jenkins? Сборка? PHP? – спросите вы и сделаете удивлённые глаза О_о. Какое отношение сборка проектов, естественная для большинства компилируемых языков типа C или Java, имеет отношение к проектам на PHP? Они же не компилируются и не “собираются”.

Когда я искал материалы по непрерывной интеграции (continuous integration), мне так и не удалось найти ни одной простой статьи, где простым русским языком объяснялось бы – зачем (нафига) всё это надо применительно к PHP. Поэтому в посте я расскажу, во-первых, что к чему, а во-вторых – как это использовать в повседневной жизни.

Кому предназначен этот пост:

Разработчикам, лидерам команд и менеджерам проектов, которые доросли до мысли что поставка новых версий программного продукта (даже на PHP) должна быть стабильной и регулярной, как на конвейере японского завода. Желательно, чтобы слова “автоматизированное тестирование” и “деплой” были для вас не пустым звуком. Ну или хотя бы вы заинтересованы в том, как (чёрт побери) перестать выкладывать “бажные” обновления.

Также пост может быть полезен командам, во главе которых стоит типичный маркетоид или иной гуманитарий, и они вынуждены хотя бы внутри себя контролировать качество кода.

Поскольку мне хочется написать обзорный пост, а не пошаговую инструкцию для чайников, сначала несколько базовых понятий на примере Ubuntu Server:

sudo apt-cache search – поиск пакета name;
sudo apt-get install – установка пакета name;
pear list -a – список установленных пакетов PEAR;
pear config-set auto-discover 1 – автодискавер каналов PEAR (удобно, чтоб не добавлять эти каналы ручками);
pear install –alldeps – установка PEAR-овского пакета name со всеми зависимостями.

Теория:

Никакой сборки, в общем случае, в PHP-проектах нет. Поэтому забудьте слово “сборка”, и думайте о наборе каких-то важных процедур, которые стоит проходить регулярно по расписанию или перед очередным релизом проекта. Это может быть выкладывание новой версии продукта или обновление сайта, или какое-то иное событие.

Суть в том, что вы изо дня в день коммитите, коммитите, коммитите изменения, и периодически вам хочется проверять – а не сломалось ли чего? А работают ли все бизнес-функции? А не накосячил ли новый сотрудник? А тот лентяй в углу наконец-то научился правильно применять кэмэл-кейс в наименованиях и ставить принятые в команде отступы и скобочки? А не растёт ли у нас объём “мертвого” кода, который уже нигде не используется? А как насчёт бездумного копипаста, избыточных классов, неиспользуемых переменных?

Причём как любому нормальному лентяю – вам хочется проводить всё это автоматически. А если что-то не так – то и вам и конкретному виновнику надо получать репорт. Итак, предположим что у вас есть некий сервер с линуксом на борту, где есть веб-окружение и где вы уже умеете разворачивать проект из svn/git/итд.

Инструменты:

Jenkins – написанный на адском Java, со страшным GUI, адски тормозящий “сервер” непрерывной интеграции. Смысла в нём почти никакого, кроме того что он – готовый базис для перечисленных задач. Сам он почти ничего не умеет, а только рулит сторонними процессами и плагинами. Как будет понятно дальше – это нам и надо.

Ant – скромная программа для выполнения набора шелл-команд в линуксе. От простого sh-файла отличается несколькими полезными плюшками типа удобного синтаксиса для манипуляции с файлами и каталогами, очередностью и зависимостями выполнения задач и прочими мелочами. Конфигурируется в простом xml-файлике, ничего сложного. Применяется для последовательного запуска перечисленных ниже инструментов. Также может применяться для “подчистки” временных файлов, сборки документации (PHPDoc), компрессии CSS/JS-файлов и любых других задач.

Phpunit – приложение (по сути набор php-скриптов) для выполнения автоматизированных тестов вашего продукта. Почему я пишу “приложение” – потому что его удобно запускать из командной строки и у него есть дофига различных параметров. Помимо собственно тестов, умеет рисовать картинки покрытия тестами (coverage) вашего проекта.

PHP CodeSniffer – пакет PEAR, который следит за соблюдением стандартов кодирования (отступы, длины строк, наименования переменных и так далее). Можно использовать готовые стандарты или написать свои. Если у вас в команде каждый день бушуют холивары типа “4 пробела против табуляции” или “отделять ли пробелами точку в конкатенации строк” – самое то.

PHP Mess Detector – один из самых полезных пакетов PEAR в этой подборке. Следит за качеством кода с точки зрения кода (а не его стиля написания). Например, упрекнёт вас, если у вас слишком много паблик-методов в классе. В общем, подскажет где вас ждёт рефакторинг.

PHP Copy/Paste Detector – как понятно из названия, пакет PEAR для отслеживания копипаста. Бездумно не копипастить, помните?

PHP Dead Code Detector – очередной пакет PEAR с говорящим названием. Находит неиспользуемые участки кода. Кстати, единственный пакет, который мне не удалось прикрутить к Jenkins-у (если знаете как – welcome в комментарии).

PHP Depend – самый адски непонятный пакет PEAR, который строит сложные графики и “пирамиды” зависимостей, наследуемостей, и много чего ещё. Стоит применять, если со всем остальным вы уже разобрались.

А так же вам надо установить набор плагинов для связки Jenkins-PHP по образцу с сайта jenkins-php.org (руками через админку ставить – возни на час, поэтому воспользуйтесь готовыми командами из абзаца “using Jenkins CLI”).

Как завести всё это вместе:

Итак, вы взяли отдельный линукс-сервер и установили туда всё это хозяйство. Сначала вам нужно определиться с рабочей директорией. Будет удобно, если это будет каталог выше уровнем относительно того, куда разворачивается ваш проект из системы контроля версий.

Теперь вам нужно настроить Ant, а именно составить для него конфигурационный файл build.xml в рабочей директории, которую вы определили абзацем выше. По Ant-у есть отличные доки, гуглите и берите примеры. Лично у меня Ant используется для прогонки тестов и всех вышеперечисленных анализаторов. Результаты их работы складываются в специальную поддиректорию.

Затем нужно создать проект (задачу) в Jenkins. На это тоже есть готовые доки, отмечу несколько важных моментов:

– проверьте, что в настройках проекта используется корректная базовая директория, потому что относительно неё будут указываться пути к файлам-результатам;
– настройте апдейт из системы контроля версий;
– в разделе “Post-build Actions” подключите плагины: PMD, Checkstyle, Duplicate code, Coverage (Clover), JDepend, и другие на свой вкус.

Что должно получиться в результате:

По нажатию “Собрать сейчас” должен запускаться процесс сборки. Найдите пункт с названием типа “Смотреть консоль” (перевод хромает, да) – и там вы увидите весь лог происходящего. Например, шаги могут быть такими:

– апдейт из системы контроля версий;
– запуск Ant, который внутри себя запустит phpunit, phpmd, phpcs и прочее;
– парсинг результатов, заданный вами в post-build actions.

В результате вы получите кучу различной статистики о том, что происходит в вашем проекте на момент этой “сборки”. Не только результаты тестов, но и результаты вышеперечисленных анализаторов. Jenkins показывает её в относительно удобочитаемом виде прямо в браузере.

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

Продолжение (с картинками и советами) следует, .

У меня есть PHP script, который имеет большой массив людей, он захватывает свои данные с внешнего ресурса через SOAP, изменяет данные и отправляет их обратно. Из-за размера деталей я увеличил память PHP до 128 МБ. Примерно через 4 часа работы (это, вероятно, займет 4 дня), у него закончилась нехватка памяти. Вот основы того, что он делает:

$people = getPeople(); foreach ($people as $person) { $data = get_personal_data(); if ($data == "blah") { importToPerson("blah", $person); } else { importToPerson("else", $person); } }

После того, как у него закончилась нехватка памяти и я разбился, я решил инициализировать $data до цикла foreach , и в соответствии с top использование памяти для процесса не поднялось выше 7,8%, и оно продолжалось 12 часов.

Итак, мой вопрос: не работает ли PHP сборщик мусора на переменных, инициализированных внутри цикла, даже если они используются повторно? Является ли система исправлением памяти, и PHP еще не отметил ее как полезную, и в конечном итоге снова рухнет (я повысил ее до 256 МБ, так что я изменил 2 вещи и не уверен, что исправил ее, я, вероятно, мог бы изменить свой script назад, чтобы ответить на этот вопрос, но не хотите ждать еще 12 часов, чтобы он упал, чтобы понять)?

Я не использую рамки Zend, так что другой вопрос, подобный этому, я не считаю релевантным.

EDIT: у меня нет проблемы с script или тем, что он делает. На данный момент, насколько это касается всей системы, у меня нет никаких проблем. Этот вопрос касается сборщика мусора и того, как/когда он восстанавливает ресурсы в цикле foreach и/или как система сообщает об использовании памяти в php-процессе.

2 ответов

Я не знаю внутренности PHP VM, но по моему опыту, это не сбор мусора, пока ваша страница работает. Это происходит потому, что он удаляет всю вашу страницу, когда она заканчивается.

В большинстве случаев, когда на странице заканчивается память, а предел довольно высокий (а 128 Мб невысока), есть проблема с алгоритмом. Многие PHP-программисты собирают структуру данных, а затем передают ее на следующий шаг, который выполняет итерацию по структуре, обычно создавая другую. Намочите, промойте, повторите. К сожалению, этот подход представляет собой большой всплеск памяти, и вы создаете несколько копий ваших данных в памяти. Две из очень больших изменений в PHP 5 заключались в том, что объекты подсчитываются, а не копируются, а вся строковая подсистема выполняется намного быстрее. Но это все еще проблема.

Чтобы свести к минимуму использование памяти, вы бы посмотрели на реструктуризацию своего алгоритма, чтобы он мог работать с одним куском данных от начала до конца. Затем вы получите следующее и начнете снова. Лучший сценарий - это то, что у вас никогда не было всего набора данных в памяти. Для веб-сайта с поддержкой базы данных это будет означать обработку строки данных из запроса базы данных до презентации до получения следующего. Конечно, такой подход не всегда возможен, и script просто должен хранить огромное количество данных в памяти.

Тем не менее, вы можете сделать этот подход к экономии памяти для части данных. Фокус в том, что вы явно unset() ключевой переменной или двух в конце цикла. Это должно вернуть пространство. Другой "лучший практический" трюк состоит в том, чтобы переместить из обработки данных цикла, которая не должна находиться в цикле. Как вы, кажется, обнаружили.

Я запускаю скрипты PHP, которые нуждаются в объеме 1 ГБ памяти. Фактически, вы можете установить ограничение памяти на script с помощью ini_set("memory_limit", "1G");

В процессе сбора данных наши сборщики будут работать сутками и нам надо следить за их работоспособностью. Например упал сервер, изменились условия сбора или просто отключили свет. Чтобы постоянно не следить за работой нужен механизм извещения о процессе работе сборщиков данных.…

В предыдущих статьях я описал как создать многопоточные поисковые сборщики на PHP по следующим системам: Google Suggest — сборщик поисковых подсказок Google Yandex Suggest — сборщик поисковых подсказок Yandex Rambler Suggest — сборщик поисковых подсказок Rambler Nigma Suggest — сборщик поисковых подсказок Nigma…

Идем далее по местам где можно получить поисковые подсказки. Следующая поисковая система это Rambler. С нее тоже можно получать вполне приличные поисковые запросы. Посмотрев в интернете мы найдем, что для запросов используется следующий адрес. Все это хорошо вписывается в наш…

Еще одно место где можно получить поисковые запросы это подсказки поисковой системы Nigma.ru. Набрав в ней например «поисковые п» мы получим следующее: Поискав в интернете, определяем что запрос поисковых подсказок идет по следующему адресу. Т.е в самом конце…

При наборе слов в Google нам выдаются определенные поисковые подсказки. Т.е например при вводе автоматизация бизнеса, гугл нам предлагает следующие варианты поисковых запросов: Поискав в интернете можно найти урл, по которому идет запрос этих данных: т.е в самом…

В прошлых постах, я создал многопоточный сборщик поисковых результатов Google API на PHP. Из опыта использование получилось что скорость его работы сильно зависела от качества использованных проксей. Сегодня я видоизменил механизм использования проксей в этом сборщике. Для этого был полностью…

Библиотека поддержки DNS Lookup.

  • Number4 дополнения к математической части.
  • Win32Build готовые библиотеки, необходимые для сборки.
  • Mssql библиотеки для программинга под MS SQL 6.5.
  • CVS клиент для CVS repository нужен для того, чтобы скачать исходный код PHP.
  • Также понадобятся файлы от MSVC++ 6.0 (только для пользователей MSVC++ 5.0)

    • OLE - положить в VC\include. Необходимы для СОМ поддержки.
    • HTTP - переименовать имеющиеся в *.hold, а новые положить в VC\include. Необходимы для сборки ISAPI фильтра.

    ИНСТАЛЯЦИЯ

    Инсталируем пакет CYGWIN, например в каталог C:\Program Files\Cygnus, в NT надо завести переменную среды CYGWIN со значением %SystemDrive%\Program Files\cygnus\cygwin-b20

    Делается это так: Идем в Start->Settings->Control Pannel запускаем ярлык System, выбираем карточку Environment на ней щелкаемся на окно System variables внизу две строки Variable и Value, в строке Variable пишем CYGWIN а в строке Value %SystemDrive%\Program Files\cygnus\cygwin-b20 И жмем Set а потом Apply.

    Cоздаем от корня каталог Tmp на системном диске и точно так же добавляем переменную TMP со значением %SystemDrive%\Tmp. Добавляем в переменную path путь %SystemDrive%\Tmp

    Жмем Apply и OK. После этого необходимо перегрузиться.

    Разворачиваем архив win32build.zip в каталог, например C:\Win32build

    Запускаем среду MSVC++ 5.0 идем в Tools->Options на карточку Directory и добавляем в разделы

    • Include
    • Libraries

    следующие пути соответственно:

    • C:\Program Files\cygnus\cygwin-b20\H-i586-cygwin32\bin
    • С:\Win32Build\include
    • C:\Win32Build\lib

    Разворачиваем Bindlib_w32.zip в каталог, например C:\Bindlib, ищем в нем файл проекта bindlib.dsp и собираем его из среды MSVC. В каталоге C:\Bindlib\Debug появится resolve.lib, ее надо переписать в каталог C:\Win32Build\Lib поверх старой.

    СБОРКА

    Топаем в каталог C:\Php4\Tsrm и собираем TSRM.dsp, после сборки в каталоге C:\Php4\Tsrm\Debug появится Tsrm.lib, скопировать ее в каталог C:\Win32Build\Lib.

    Переходим в каталог C:\Php4\Zend, открываем проект ZendTS.dsp и собираем его, после сборки в каталоге C:\Php4\Zend\Debug появится библиотека ZendTS.lib, копируем ее в каталог C:\Win32Build\Lib.

    Переходим в каталог C:\Php4 и открываем проект php4ts.dsp. В нем 4 проекта сначала собираем php4ts (не забудьте установить активный проект Project->Set Active Project->php4ts).

    После сборки в каталоге C:\Php4\Debug появится файл php4ts.lib, php4ts.dll, php.exe. Php4ts.lib опять копируем в C:\Win32Build\Lib. Эта библиотека нужна для сборки внешних модулей php, например для модуля поддержки mssql.

    Делаем активным проект php4isapi и собираем его. В каталоге C:\php4\sapi\isapi\debug будет лежать php4isapi.dll – фильтр для IIS.

    Известные баги, которые шибко мешают работать и пути их лечения

    1. Через элеметы формы не постятся данные размером более 4К, не аплоадятся файлы размером больше 4К. php4.exe процесс подвисает и снимается только рестартом IISа. (Отписал в репорт баг, но когда исправят непонятно, попытаюсь поправить сам).Решение: Обходится использованием php 3.12-3.14 для обработки файлов реакции форм. Медленнее, зато работает.

    2. При выполнении динамического кода в котором происходит использование сериализованных переменных парсер выдает оштбку. Например:

    Result: parser error on line 5 …..

    Происходит это из-за того, что в четвертой версии символы {} используются для инкапсуляции переменных в строку, например $a=”Такой вот элемент {$NotSer}”;

    Но сериализованное представление массива тоже содержит символы {}.

    В примере, описанном выше парсер пытается инкапсулировать выражение, которое на самом деле выражением то и не является и естественно валится с ошибкой.

    Решение: Надо поправить в файле \php4\ext\standard\var.c код, заменив { на [ и } на ]. Ищем в файле подстроку %d:{ и заменяем ее на %d:[ (будет две замены), и меняем { на [, В строке if (**p != ":" || *((*p) + 1) != "{") замена { на [, для строк for ((*p) += 2; **p && **p != "}" && i > 0; i--) и return *((*p)++) == "}"; меняем } на ]. Потом пересобираем php4ts.dsp. После этого все нормально отрабатывает.

    Сборка модуля Php 4.03 betta для MS SQL 6.5 (mssql.dll)

    Распаковать mssql.zip. *.lib забрасываем в C:\Win32Build\Lib, а *.h в C:\Win32Build\Include.