Linux.yaroslavl.ru
| ||||||||||||||||||||||||
Начиная с версии 4.3, РНР поддерживает новый SAPI -тип (Server Application Programming Interface) под названием CLI , что означает Command Line Interface . Как следует из названия, главной задачей этого SAPI -типа является разработка приложений оболочки/shell (или рабочего стола) с помощью РНР. Имеются весьма небольшие отличия CLI SAPI от других SAPI , которые будут далее рассмотрены в этой главе. CLI SAPI был выпущен в первый раз с PHP 4.2.0 , но тогда это был эксперимент, и нужно было явно включать его командой —enable-cli при запуске ./configure . Начиная с PHP 4.3.0 , CLI SAPI больше не является экспериментальным и всегда встроен и устанавливается как двоичный исполняемый файл php (называется php.exe в Windows). Существенные отличия CLI SAPI от других SAPI : В отличие от CGI SAPI , никакие шапки/headers не записываются в вывод. Хотя в CGI SAPI имеется способ подавления HTTP-шапок, эквивалентного переключателя для их включения в CLI SAPI нет. Имеются определённые директивы php.ini , которые переопределены в CLI SAPI , поскольку они не имеют смысла в среде окружения оболочки: Таблица 24-1. Переопределение php.ini -директив
Легче работать в среде оболочки, когда определены следующие константы: Таблица 24-2. Специфические CLI-константы
Имея всё это, вы не должны, например, самостоятельно открывать поток для stderr , а просто используете константу вместо ресурса потока: Вам не нужно явно закрывать эти потоки, это делается РНР автоматически. CLI SAPI не изменяет текущую директорию на директорию исполняемого скрипта! Пример, показывающий отличие CGI SAPI : Когда используется CGI -версия, на выходе будет: Это ясно показывает, что PHP изменяет свою текущую директорию на директорию исполняемого скрипта. Использование CLI SAPI даёт: Это даёт большую гибкость при написании утилит командной строки на PHP .
Список опций командной строки исполняемого файла PHP может быть получен в любое время путём запуска PHP с ключом -h : CLI SAPI имеет три разных способа получения PHP -кода, который нужно выполнить: Сказать PHP выполнить определённый файл. Оба способа (с/без использования переключателя -f ) выполняют данный файл my_script.php . Вы можете выбрать для выполнения любой файл; названия ваших файлов скриптов PHP не обязаны заканчиваться расширением .php , а могут иметь любое имя или расширение. Передать PHP код для выполнения непосредственно из командной строки. Особого внимания требует замена переменных оболочки и использование кавычек.
Предоставить PHP -код для выполнения через стандартный ввод ( stdin ). Это позволяет динамически создавать PHP -код и передавать его экзешнику, как показано в данном (надуманном) примере: Вы не можете комбинировать эти три способа при выполнении кода. Как и в любом приложении оболочки, не только сам PHP , но и ваши скрипты PHP также принимают аргументы. Количество передаваемых в скрипт аргументов в РНР не ограничивается (оболочка имеет ограничение на количество передаваемых символов). Если аргументы, которые вы хотите передать в скрипт, не начинаются с символа дефиса (-) , ничего специально наблюдать не надо. Передача в скрипт аргумента, начинающегося с — , создаст проблемы, поскольку PHP думает, что должен сам их обработать. Чтобы предотвратить это, используйте в качестве сепаратора аргументов списка — . После того как аргумент будет разобран PHP , каждый последующий аргумент передаётся в ваш скрипт без изменений/не разобранным. Однако, вот другой способ использования PHP для скриптинга оболочки. Вы можете написать скрипт, первая строка которого начинается с #!/usr/bin/php , а затем идёт нормальный PHP -код, содержащийся между начальным и конечным тэгами PHP , и соответствующим образом устанавливаются атрибуты выполнения файла. Таким способом он может быть исполнен как нормальный скрипт оболочки или perl: Приняв, что файл называется test и находится в текущей директории, мы можем выполнить: Как вы видите, ничего особо не нужно делать при передаче параметров в скрипт, который начинается с — . Таблица 24-3. Опции командной строкиОтображать синтаксис в цвете. Эта опция использует внутренний механизм разбора файла, производит его расцвеченную HTML -версию и записывает её в стандартный вывод. Заметьте, что генерируется лишь блок [. ] HTML -тэгов без HTML -header.
Отобразить исходный текст без комментариев и пробелов.
Разбирает и выполняет данный файл. Этот переключатель является необязательным и может быть опущен. Достаточно предоставить имя файла для выполнения. Записывает PHP, PHP SAPI и Zend-версии в стандартный вывод, например: | ||||||||||||||||||||||||
-c | ||||||||||||||||||||||||
-a | ||||||||||||||||||||||||
-e | ||||||||||||||||||||||||
-i | Эта опция командной строки вызывает phpinfo() и печатает на выводе результаты. Если PHP не работает правильно, советуем выполнить php -i и посмотреть, выводятся ли сообщения об ошибке до или вместо таблиц информации. Имейте в виду, что вывод будет на HTML и, следовательно, довольно сумбурным. | |||||||||||||||||||||||
-r | ||||||||||||||||||||||||
-h | С помощью данной опции вы можете получить информацию о действующем списке опций командной строки и небольшое описание их работы. |
Исполняемый файл PHP может быть использован для запуска PHP-скриптов абсолютно независимо от web-сервера.
Если вы работаете под Unix, вы должны добавлять специальную первую строчку в ваши PHP-скрипты и делать их исполняемыми, чтобы система знала, какая программа должна выполнять эти скрипты.
Под Windows вы можете ассоциировать php.exe с опцией двойного щелчка по файлам .php либо сделать batch-файл (.bat) для запуска скрипта через PHP. Строка, добавленная в начало скрипта для работы под Unix, не помешает под Windows, поэтому вы можете писать таким образом межплатформенные программы. Ниже дан пример простой РНР-программы для выполнения из командной строки.
Пример 24-1. Скрипт, предназначенный для запуска из командной строки (script.php)
Здесь мы используем специальную первую строку для указания на то, что этот файл должен быть запущен в PHP. Мы работаем с CLI-версией, поэтому не выполняется вывод HTTP-шапок/header. Имеются две переменные, которые вы можете использовать при написании РНР-приложений для командной строки: $argc и $argv . Первая — это количество аргументов плюс 1 (имя запущенного скрипта). Вторая — это массив аргументов, начиная с имени скрипта с индексом ноль ( $argv[0] ).
Мы проверяем, имеется ли менее или более одного аргумента. Также, если аргумент был —help , -help , -h или -? , мы печатаем help-сообщение, выводя имя скрипта динамически. Если мы получили какой-либо другой аргумент, мы выводим его (echo).
Если вы хотите выполнить вышеприведённый скрипт под Unix, вам необходимо сделать его executable и просто вызвать как script.php echothis или script.php -h . Под Windows вы можете создать batch-файл для выполнения этой задачи:
Пример 24-2. Пакетный/Batch-файл для запуска PHP-скрипта в командной строке (script.bat)
Приняв, что вы назвали программу script.php и что ваш php.exe находится в c:\php\php.exe , этот batch-файл запустит её с добавленными вами опциями: script.bat echothis или script.bat -h .
См. также в документации по расширению Readline о функциях, которые можно использовать для усовершенствования вашего РНР-приложения для командной строки.
Источник
Написание системных утилит на PHP CLI
Для большинства специалистов PHP не является языком, который бы всерьёз использовался для написания консольных утилит, и для этого есть много причин. PHP изначально разрабатывался как язык для создания веб-сайтов, но, начиная с PHP 4.3, в 2002-ом году появилась официальная поддержка режима CLI, поэтому он уже давно перестал быть таковым. Разработчики Badoo на протяжении нескольких лет вполне успешно используют множество интерактивных CLI-утилит на PHP.
В данной статье нам хотелось бы поделиться своим опытом работы с CLI-режимом в PHP и дать несколько рекомендаций тем, кто собирается писать скрипты на PHP, при условии, что они будут запускаться в *nix-системе (впрочем, почти всё верно и для Windows).
Рекомендации
Скорость работы
Вывод на экран
В CLI- и в веб-режиме вывод на экран значительно отличается. В веб-режиме вывод, как правило, буферизуется, у пользователя нельзя ничего спросить во время исполнения скрипта; отсутствует как класс понятие вывода в поток ошибок. В CLI-режиме, естественно, неприемлем вывод HTML, а также крайне нежелателен вывод длинных строк. В CLI echo по умолчанию вызывает flush() (подробнее можно посмотреть здесь) — это удобно тем, что можно не заботиться о вызове flush() вручную, если, к примеру, вывод перенаправляется в файл.
Также для CLI-скриптов имеет смысл выводить ошибки не в STDOUT (используя echo), а в STDERR: таким образом, даже если вывод программы будет перенаправлен куда-либо еще (например, в /dev/null или grep), пользователь не пропустит текст ошибки в случае ее появления. Это стандартное поведение для большинства «родных» *nix’овых консольных утилит, и STDERR существует именно по причине, описанной выше. В PHP для записи в STDERR можно пользоваться, к примеру, fwrite(STDERR, $message) или error_log($message).
Использование кодов возврата
Код возврата — это число, которое равно 0 в случае успешного выполнения команды и не равно 0 в противном случае. Код возврата, равный 1, часто применяется в случае некритичных ошибок (например, если указаны неправильные аргументы командной строки), а 2 — в случае критичных системных ошибок (например, при ошибке сети или диска). Значения наподобие 127 или 255 обычно используются для каких-либо специальных случаев, которые отражаются отдельно в документации.
По умолчанию при простом завершении PHP-скрипта предполагается, что все команды отработали успешно и возвращается 0. Чтобы выйти с определенным кодом возврата, нужно явно вызвать exit(NUM), где NUM — это и есть код возврата (помним, что он равен 0 в случае успеха и имеет другое значение в случае ошибок).
Чтобы понять, что внешняя команда, исполняемая с помощью exec() или system(), завершилась неуспешно, нужно передавать переменную $return_var в качестве параметров соответствующих функций и проверять значение на равенство нулю.
Внимание! Если вы собираетесь написать exec(‘some_cmd … 2>&1’, $output), чтобы ошибки тоже попали в $output, рекомендуем ознакомиться с причинами разделения STDOUT и STDERR и убрать явное перенаправление потока ошибок в STDOUT (2>&1). Такое перенаправление требуется намного реже, чем может показаться. Единственный случай, когда его использование хоть немного оправдано (в PHP-скрипте) — необходимость распечатать на веб-странице (не в CLI!) результат выполнения команды, включая ошибки, которые произошли (иначе они попадут в лог веб-сервера или вообще уйдут в /dev/null).
«Маскировка» под встроенные команды системы
Хорошая консольная утилита должна себя вести стандартным образом и пользователи могут даже и не знать, что она на PHP. Для этого в *nix-системах предусмотрен механизм, который многим известен по запуску скриптов на Perl/Python/Ruby, но в равной степени применимый и к PHP.
Если добавить в начало PHP-файла, к примеру, #!/usr/bin/env php и перенос строки, дать ему права на исполнение (chmod 755 myscript.php) и убрать расширение .php (последнее не обязательно), то файл можно будет исполнить, как и любой другой исполняемый файл (./myscript). Можно добавить директорию со скриптом в PATH или переместить его в одну из стандартных директорий PATH, например, /usr/local/bin, и тогда скрипт можно будет вызывать простым набором «myscript», как и любые другие системные утилиты.
Обработка аргументов командной строки
Существует соглашение о формате аргументов командной строки, которому следуют большинство встроенных системных утилит, и мы рекомендуем следовать ему и ваших скриптах.
Пишите краткую справку для своего скрипта, если он получил неверное количество аргументов.
Чтобы узнать имя вызываемого скрипта, используйте $argv[0]:
Для облегчения обработки флагов можно использовать getopt(). Getopt() — одна из встроенных функций для обработки аргументов командной строки. С другой стороны, нет ничего сложного в том, чтобы обрабатывать часть аргументов вручную, т.к. на PHP это не представляет особого труда. Такой способ может понадобиться, если нужно обработать аргументы в стиле ssh или sudo (sudo -u nobody echo Hello world выполнит echo Hello world из-под пользователя nobody, который указан после флага -u перед командой).
Рекомендации для более сложного уровня
Вызов «правильного» system() для CLI
О реализации system() уже было написано здесь. Речь идет о том, что стандартный system() в PHP является не вызовом system() в С, а оберткой над popen(), соответственно, «портит» STDIN и STDOUT у вызываемого скрипта. Чтобы этого не происходило, нужно пользоваться следующей функцией:
Работа с файловой системой
К возможному удивлению, мы рекомендуем не писать свои реализации рекурсивного удаления (копирования, перемещения) файлов, а вместо этого использовать встроенные команды mv, rm, cp (под Windows — соответствующие аналоги). Такое не переносимо между Windows/*nix, но зато позволяет избежать некоторых проблем, описанных ниже.
Давайте рассмотрим простой пример реализации рекурсивного удаления директории на PHP:
На первый взгляд всё верно, так? Более того, даже в известных файловых менеджерах на PHP (например, в eXtplorer и в комментариях к документации) удаление папки реализовано именно таким способом. Теперь создадим символическую ссылку на несуществующий файл (ln -s some_test other_test) и попробуем её удалить. Или создадим в папке символическую ссылку на себя, или на корень ФС (рекомендуем не тестировать такой вариант)… Конкретно для recursiveDelete() фикс, конечно же, тривиален, но понятно, что лучше не изобретать велосипед и использовать встроенные команды, пусть и теряя немного в производительности.
Очистка в случае ошибок
Если ваш скрипт делает какие-то операции с файлами (базой данных, сокетами и пр.), то зачастую возникает необходимость корректно завершать работу программы в случае возникновения непредвиденных ошибок: это может быть запись в лог, очистка временных файлов, снятие файловых блокировок и т.д.
В веб-режиме PHP это реализуется с помощью register_shutdown_function(), которая срабатывает даже тогда, когда скрипт завершился с фатальной ошибкой (этот способ, кстати, годится для отлова почти любых ошибок, в том числе ошибок нехватки памяти). В CLI-режиме всё немного сложнее, поскольку пользователь, к примеру, может послать вашему скрипту Ctrl+C, и register_shutdown_function() при этом не сработает.
Но объясняется это просто: PHP по умолчанию вообще не обрабатывает UNIX-сигналы, поэтому получение любого сигнала немедленно вызывает завершение скрипта. Это можно исправить путем добавления declare(ticks=1), в начало файла после
Источник