Международная конференция разработчиков
и пользователей свободного программного обеспечения

Написание консольных утилит и демонов на PHP

Андрей Синицын, Тула, Russia

LVEE Winter 2012

Usage of the PHP framework at developing console applications is reviewed, mainly targeted at creating web application background service. Specific features of PHP console programming and input/output redirection are considered as far as ones of multiprocess applications.

PHP занял и прочно удерживает пальму первенства в web-разработке. По статистике более 60% сайтов написаны именно на PHP. К сожалению, эта популярность и низкий порог вхождения породили массу некачественного кода, некачественных программистов, а также затмили собой массу достоинств этого, неплохого в общем-то, языка.

Почему PHP?

Хочется странного :) В наше время javascript используется как серверный язык, perl в стадии затянувшегося угасания, python — в сущности, аналог basic, а ruby известен своей медлительностью. Но вне зависимости от стереотипов, на всех этих языках создаются прекрасные приложения. И в рамках отказа от стереотипов я попробую раскрыть PHP с неожиданной стороны.

На PHP есть не только Wordpress, Joomla и др.: такие гиганты, как facebook или vkontakte, тоже написаны на нем. Помимо web-приложений, на PHP пишутся web-сервера, обвязка для embedded-приложений, и даже была попытка переписать на PHP весь Linux init.

Помимо этого безумия есть также биндинги к GTK и Qt, что, теоретически, позволяет создавать на PHP GUI-приложения. Но на практике все это сыро и работает довольно неустойчиво.

Зачем?

Как правило, в web-приложениях довольно много действий приходится выполнять в фоновом режиме. Это задачи по обслуживанию БД (например, чистка и синхронизация), генерация статистики и много всего другого. Если проект использует как основу какой-то фреймворк, разумно решать с его помощью также и эти задачи. И тут можно вспомнить, что PHP умеет работать в режиме командной строки (CLI), что избавляет от необходимости искать новых разработчиков или просить сисадмина написать нужные скрипты. Вы получаете интеграцию с основным приложением и решаете задачи без лишних усилий. Однако следует помнить об отличиях.

Как?

Нужно помнить о том, что в CLI-режиме у вас больше нет суперглобальных массивов, зато есть аргументы командной строки. Для облегчения работы с ними PHP предлагает свой стандартный набор: argv для работы с параметрами командной строки, readline для облегчения интерактивного взаимодействия с пользователем.

Можно использовать нужный she-bang, убрать расширение .php, сделать файл исполняемым и поместить его в директорию, присутствующую в $PATH: в этом случае пользователи вообще не смогут отличить PHP-сценарий от «родных» утилит Linux.

При написании консольных утилит следует помнить о потоках ввода/вывода. В распоряжении программиста есть STDIN для ввода, STDOUT для вывода результатов работы и STDERR для сообщений об ошибках. Также необходимо помнить о кодах возврата (число, которое ставится после команды exit и становится доступно шеллу после окончания работы сценария). Код возврата может пригодиться, например, когда скрипт используется в конвейерах.

Нюансы.

Следует помнить о быстродействии. Байт-код в CLI-режиме не кэшируется, и все I/O вызовы (инструкции include) будут постоянно повторяться. Также нужно помнить о ширине терминалов. Хорошим тоном считается форматировать строки с использованием управляющих символов (sprintf(), например) и не делать вывод длиной более 80 символов. Важен и объем информационных сообщений. Если скрипт работает пару секунд и тщательно докладывает о каждом своем шаге, это раздражает. Используйте ключ -v для такого режима работы, а по умолчанию ведите себя «тихо».

Для разбора аргументов есть функция getopt(), представляющая из себя весьма мощный инструмент.

Не стоит забывать про обработку сигналов ОС. Это весьма пригодится при написании сервера, который работает в фоне, т.к. общаться с ним придется при помощи этих самых сигналов.

standalone-сервер.

По сути — это частный случай CLI-утилиты, которая работает в фоне, ждет запросов от пользователя и в ответ выполняет некие действия. Технология «клиент-сервер» хорошо известна. В PHP есть необходимый набор функций для работы с сокетами, что позволяет занять определенный порт и ждать подключений на нем.

Сервер может быть однопоточным и многопоточным. В случае с многопоточностью основной процесс (master) принимает подключение, делает вызов fork(), передает управление порожденному процессу (worker) и продолжает ожидать подключений. Например, в нашем проекте Flirteka.Ru мы с помощью подобного демона, написанного на PHP, выполняем асинхронную загрузку и обработку изображений.

Здесь следует обратить внимание на набор функций pcntl_* (сокращение от Process Control). Эти функции доступны только в среде UNIX, так что пользователи Windows кусают локти :) В них реализовано все необходимое для комфортного управления процессами. Работа с POSIX-функциями позволяет управлять сигналами, дочерними и родительским процессом.

Особенности работы с fork().

Действия с ресурсами, такие как, например, коннект к БД и открытие файловых дескрипторов, следует выполнять в дочерних процессах. Открытие коннекта в родительском процессе и передача его в fork приводит к непредсказуемым последствиям.
Нужно также помните о кодах возврата и корректном завершении дочерних процессов. И наконец, имеет смысл зарегистрировать свои обработчики сигналов при помощи pcntl_signal.

Лицензия Creative Commons
Текст тезисов доступен под лицензией Creative Commons Attribution-ShareAlike 3.0.

Presentation