Использование преимуществ встроенного PHP сервера

от автора

Одним из крутейший новшеств в php 5.4 является встроенный сервер, созданный специально для разработки и тестирования. Теперь вы можете писать и тестировать свой код не имея полноценного веб-сервера — просто запустите встроенный сервер, протестируйте свой код, и выключите его, когда закончите.
Сервер, так же, предоставляет возможность и для творческого использования. Например, вы можете распространять портативное web-приложение на CD или USB, или даже как десктопное приложение, созданное на PHP без использования GTK или других графических библиотек.

В мануале PHP подчеркивается, что встроенный сервер предназначен для разработки и рекомендуется не использовать его на боевом сервере. Отсутствуют какие-либо INI директивы отвечающие за сервер (за исключением раскарашивания вывода в консоли), и, кажется, что основная идея документации: «у нас теперь тоже есть Web сервер, отстаньте от нас».
Несмотря на это, я считаю, что встроенные сервер может быть ценным инструментом для разработки и тестирования. К примеру, на моей машине я использую предустановленный Apache с кастомной конфигурацией, подходящей для меня, но иногда я хочу попробовать какие-либо новые Web-приложения. Со встроенным web-сервером я могу протестировать приложение прямо из папки «downloads» или временной папки и переместить в обычную среду, только тогда, когда это будет необходимо.
Но для начала, это не так просто, ведь многие написанные приложения используют .htaccess и mod_rewrite. Но я уверен, что кто-то (может быть один из вас, почему бы и нет?) напишет адаптер для этого, и я хотел бы быть первым, кто протестируют его.
В этой статье я объясню некоторые основные примеры использования встроенного сервера и покажу вам как сделать его полезным для разработки и тестирования.

Используем встроенный сервер

Итак, для использования сервера нам необходим php 5.4 или выше. Для проверки версии PHP, выполните:

php -v 

Так же вы можете определить доступен ли сервер в вашей сборке выполнив:

php -h 

и найдите там описание параметров "-S" и "-t", которые используются только для сервера.
Для проверки сервера вы можете создать в текущей директории файл index.php, который будет содержать в себе вызов функции phpinfo() и затем запустить сервер:

[ec2-user@ip-10-229-67-156 ~]$ php -S 127.0.0.1:8080 PHP 5.4.0RC7 Development Server started at Fri Feb 26 18:49:29 2012 Listening on 127.0.0.1:8080 Document root is /home/ec2-user Press Ctrl-C to quit. 

И теперь вы можете увидеть содержимое отданной встроенным web-сервером:
image
В консоль же будет писаться каждый запрос клиента:

[Sun Feb 26 18:55:30 2012] 80.180.55.37:36318 [200]: / [Sun Feb 26 18:56:23 2012] 80.180.55.37:36584 [200]: / 

Возвращаясь назад, разберем параметр командной строки "-S", который используется для указания адреса, с которого сервер будет доступен. Возможные значения:
localhost — сервер будет доступен только с локальной машины,
0.0.0.0 — на любому интерфейсе машины,
Любой внешний или серый IP — только на указанном IP
Параметр "-t" устанавливает указанную директорию «directory root». Например:

[ec2-user@ip-10-229-67-156 ~]$ php -S <localhost or your public IP>:8090 -t /home/ec2-user/public 

Кроме того,. вы может указать имя конкретного файла-роутера. Например:

[ec2-user@ip-10-229-67-156 ~]$ php -S >localhost or your public IP>:8080 -t /home/ec2-user/public public/index.php 

Вывод этого роутера будет парситься и выполняться сервером. Простой пример:

<?php $extensions = array("php", "jpg", "jpeg", "gif", "css");  $path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); $ext = pathinfo($path, PATHINFO_EXTENSION); if (in_array($ext, $extensions)) {     // let the server handle the request as-is     return false;     }  echo "<p>Welcome to PHP</p>"; 

Если скрипт вернет FALSE, тогда запрашиваемый URI будет обрабатываться сервером, который будет выдавать запрошенный ресурс, либо вернет 404 ошибку. Если скрипт возвращает что-либо ещё, вывод скрипта передастся клиенту.
Хотя данный подход даёт нам больше контроля, есть несколько вещей, которые вы должны знать. Во-первых, PHP сервер отдаёт только минимальный набор HTTP заголовков:

Connection: closed Content-Type: text/html Host: aws-dev-01.vtardia.com X-Powered-By: PHP/5.4.0RC7 

D
Сравним это с заголовками, возвращаемыми сервером Apache:

Accept-Ranges: bytes Connection: Keep-Alive Content-Length: 631 Content-Type: text/html Date: Sat, 04 Feb 2012 18:24:42 GMT Etag: "bbb99-277-4ace8c5470a40" Keep-Alive: timeout=15, max=100 Last-Modified: Wed, 14 Sep 2011 15:54:09 GMT Server: Apache/2.2.21 (Unix) DAV/2 

Если ваше приложение использует заголовки, то оно должно учитывать разницу в development-среде и в production.
Во-вторых, встроенный сервер имеет другое SAPI (Server API). Таким образом выполняя маршрутизацию в index,php вы можете определить на тестовом или боевом сервер происходит обращение к скрипту. php_sapi_name() вернет «cli-server» на встроенном сервере:

<?php if (php_sapi_name() == "cli-server") {     // running under built-in server so     // route static assets and return false     $extensions = array("php", "jpg", "jpeg", "gif", "css");     $path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);     $ext = pathinfo($path, PATHINFO_EXTENSION);     if (in_array($ext, $extensions)) {         return false;       } } 

Существует одна специальная INI директива — «cli_server.color». Данная директива возвращает раскрашенный вывод в консоли. Создайте пустой файл с именем cli-server.ini и вставьте эту строку:

cli_server.color = on 

Вы можете создать уникальную конфигурацию окружения для вашего сервера, указав в вашем INI файле необходимые директивы. Не объявленные директивы примут значения по-умолчанию. Сейчас мы объявили только одну директиву — cli_server.color.
Запустить сервер с параметром "-c" с указанием INI файла:

[ec2-user@ip-10-229-67-156 ~]$ php -S localhost -c cli-server.ini 

Если ваш терминал поддерживает цвета, то вы сможете увидеть «цветной» вывод в консоли. 200 статус будет выделен зеленым, 404 — оранжевым, а ошибки сценария будут выделены красным цветом.

Создаём персональный сервер

Теперь, когда вы знаете всё, что необходимо знать о встроенном сервере, давайте сделаем что-нибудь крутое. Создадим собственный портативный сервер!
Я начну со следующей структуры нашего приложения:

Папка «library» содержит код приложения, «public» — корневая директория, содержит index.php и несколько статичных файлов. Особое внимание в этом руководстве будет уделено папке «server», и поэтому наше приложение будет состоять из простого «Hello Word!» и нескольких картинок и css.
Наша цель — получить возможность запускать сервер из директории приложения одной командой, а наш сервер будет заботиться о роутинге, HTTP заголовках и ошибках.

[ec2-user@ip-10-229-67-156 myapp]$  ./start.sh 

Давайте рассмотрим сценарий запуска:

#! /bin/bash  INIFILE="$(pwd)/server/server.ini" DOCROOT="$(pwd)/public" ROUTER="$(pwd)/server/router.php" HOST=0.0.0.0 PORT=8080  PHP=$(which php) if [ $? != 0 ] ; then     echo "Unable to find PHP"     exit 1 fi  $PHP -S $HOST:$PORT -c $INIFILE -t $DOCROOT $ROUTER 

Я предполагаю, что скрипт запускается из директории приложения, поэтому INIFILE, DOCROOT, ROUTER определяются используя pwd. Путь до php определяется используя команду which. Если php не был найден в пользовательском $PATH, то скрипт завершит работу ошибкой.
Данный способ работает достаточно хорошо, но давайте предоставим пользователю возможность изменить любой из заданных параметров из командной строки, например:

if [ ! -z $INIFILE ]; then     INIFILE="$(pwd)/server/server.ini" fi 

Продолжим, папка «errors» содержит файлы для сообщений об HTTP ошибках. Вот пример о 403 ошибке: хотя я и использовал только HTML, скрипт будет подключен, использую include, поэтому вы можете использовать любой php код:

<!doctype html>   <html lang="en">  <head>   <meta charset="utf-8">   <title>403</title>    </head>  <body>   <h1>403: Forbidden</h1>   <p>Sorry, the requested resource is not accessible.</p>  </body>   </html> 

Теперь посмотрим на router.php. Задача данного файла в получении и управлении всеми запросами и передавать их серверу, только если данный файл существует. Все страницы ошибку отображаются путём подключения шаблона.

<?php // Set timezone date_default_timezone_set("UTC");  // Directory that contains error pages define("ERRORS", dirname(__FILE__) . "/errors");  // Default index file define("DIRECTORY_INDEX", "index.php");  // Optional array of authorized client IPs for a bit of security $config["hostsAllowed"] = array();  function logAccess($status = 200) {     file_put_contents("php://stdout", sprintf("[%s] %s:%s [%s]: %s\n",         date("D M j H:i:s Y"), $_SERVER["REMOTE_ADDR"],         $_SERVER["REMOTE_PORT"], $status, $_SERVER["REQUEST_URI"])); }  // Parse allowed host list if (!empty($config['hostsAllowed'])) {     if (!in_array($_SERVER['REMOTE_ADDR'], $config['hostsAllowed'])) {         logAccess(403);         http_response_code(403);         include ERRORS . '/403.php';         exit;     } }  // if requesting a directory then serve the default index $path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH); $ext = pathinfo($path, PATHINFO_EXTENSION); if (empty($ext)) {     $path = rtrim($path, "/") . "/" . DIRECTORY_INDEX; }  // If the file exists then return false and let the server handle it if (file_exists($_SERVER["DOCUMENT_ROOT"] . $path)) {     return false; }  // default behavior logAccess(404); http_response_code(404); include ERRORS . "/404.php"; 

В первых строках я определяю некоторые глобальные параметры, такие как DIRECTORY_INDEX, директория с шаблонами ошибок. Параметр date_default_timezone_set() должен совпадать с настройками ОС, иначе будут несоответствия между записями в логе и на сервере. Так же я добавил список разрешенных IP адресов, для повышения безопасности.
Функция logAccess() необходима, потому что когда скрипт роутинга принимает запрос лог сервера по-умолчанию игнорируется. Функция принимает только код статуса, а формат вывода полностью соответствует формату сервера.
Наша первая задача — проверка безопасности. Если IP клиента не находится в массиве разрешенных IP, выводим сообщение об ошибке и завершаем работу скрипта. Нам необходимо отдавать код статуса отличный от 200 и функция header() не будет работать в здесь, поэтому мы используем новую функцию — http_response_code.
Если IP клиента находится в массиве разрешенных IP, то следующий наш шаг — получение запрашиваемого пути и расширения файла. Если расширение пустое, считаем, что пользователь запрашивает папку и строим получаем путь, используя определенный сначала DIRECTORY_INDEX.
В завершении, если запрашиваемый файл существует, возвращаем FALSE, и позволяем серверу обратиться к файлу. Если же нет, то отображается сообщение о 404 ошибке.

Резюме

Это всё. Как видите, php сервер просто в использовании. Наш персональный сервер очень прост. Код можно оптимизировать и включать в более сложные и функциональные классы. Happy coding!

p.s. С радостью приму критику и замечания к переводу в личку.

ссылка на оригинал статьи http://habrahabr.ru/post/155853/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *