Используем php-cgi в своих приложениях

    PHP — один из самых распространенных языков программирования. Созданный для написания небольших домашних страничек, он постепенно вырос и теперь используется миллионами веб-сайтов. Однако веб-сайтами дело не ограничивается — его можно использовать практически в любых приложениях, благо есть интерактивный, CGI и FastCGI режимы.
    Режим CGI я и хочу описать. Его плюсы в относительной простоте и возможности передачи скриптам различных данных (в том числе бинарных). Минус у него лишь один — скорость работы, из-за многократных запусков приложения. Впрочем, минус этот исправляется с помощью более нового протокола FastCGI.

    Для начала определим, что же такое Common Gateway Interface (CGI). На деле это всего лишь запуск приложения с определенными переменными окружения. Так как это все-таки веб-технология, то и переменные будут тесно связаны с протоколом HTTP. Заглянем в RFC 3875 и посмотрим, какие переменные окружения нужны для CGI/1.1:
    • AUTH_TYPE — тип HTTP-авторизации (basic, digest или др.) Может быть пустым.
    • CONTENT_LENGTH — длина передаваемых в POST или PUT запросе данных. Обычно берется из заголовка Content-Length запроса.
    • CONTENT_TYPE — тип передаваемых в POST или PUT запросе данных. Обычно берется из заголовка Content-Type запроса.
    • GATEWAY_INTERFACE — используемая версия CGI. Обычно «CGI/1.1».
    • PATH_INFO — часть пути после пути к CGI-приложению, но до переменных запроса. Например, для запроса "/somewhere/cgiapp.exe/test/?qwerty=123" это будет "/test/".
    • PATH_TRANSLATED — то же самое, что и PATH_INFO, но спроецированное на файловую систему. Например, "./htdocs/test/".
    • QUERY_STRING — переменные запроса (без вопросительного знака). Например, «qwerty=123».
    • REMOTE_ADDR — IP-адрес удаленного пользователя.
    • REMOTE_HOST — доменное имя удаленного пользователя. Обычно равно REMOTE_ADDR.
    • REMOTE_IDENT — идентификационные данные пользователя согласно RFC 1314. Используется редко.
    • REMOTE_USER — имя удаленного пользователя, прошедшего HTTP-авторизацию.
    • REQUEST_METHOD — тип запроса (GET, POST, PUT и т. п.)
    • SCRIPT_NAME — часть запроса с путем к CGI-приложению. Например, "/somewhere/cgiapp.exe".
    • SERVER_NAME — название сервера (имя хоста или доменное имя).
    • SERVER_PORT — порт, на котором запущен сервер.
    • SERVER_PROTOCOL — протокол, используемый сервером. Обычно «HTTP/1.1».
    • SERVER_SOFTWARE — версия сервера. Например, «MyServer 1.0».

    Для POST и PUT запросов данные запроса передаются в стандартный ввод приложения.
    Тем не менее, для запуска php-cgi все эти переменные лишь желательны, но необязательны. Обязательная переменная окружения есть только одна, да и то не указанная в RFC — SCRIPT_FILENAME. В ней должен содержаться путь к PHP-скрипту в файловой системе, например "/home/some-user/htdocs/test.php". Для запуска PHP-скрипта через CGI в своем приложении вам достаточно лишь указать ее и запустить php-cgi (не «php», а именно «php-cgi»!), перенаправив его стандартные ввод и вывод.
    Если вы не пишите свой веб-сервер, вам нужно обрезать выводимые PHP заголовки. Перехватывая вывод, ждите, пока не появится первая пустая строка (просто два переноса строки подряд — "\n\n") и используйте только то, что после нее.
    PHP в режиме CGI желательно использовать только тогда, когда нужно не особо активно работать со скриптами, передавая им какие-либо данные или переменные. Если нужна активная работа — лучше использовать FastCGI, а если же не нужно ничего передавать скрипту — то проще будет запустить интерпретатор PHP, передав ему в качестве аргумента путь к скрипту.
    И чтобы не быть голословным, небольшой пример работы (Windows, C#):
    using System;
    using System.Diagnostics;
    
    namespace PhpApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                Process php = new Process();
    
                // Путь к php-cgi
                php.StartInfo.FileName = "c:\\php\\php-cgi.exe";
                // Через ShellExecute запускать нельзя - нужно перенаправить выходной поток
                php.StartInfo.UseShellExecute = false;
                // Указываем, что нужно перенаправить выходной поток
                php.StartInfo.RedirectStandardOutput = true;
                // Добавляем обработчик для принятия данных из выходного потока приложения
                php.OutputDataReceived += new DataReceivedEventHandler(php_OutputDataReceived);
    
                // Устанавливаем единственную обязательную для php-cgi переменную окружения
                php.StartInfo.EnvironmentVariables.Add("SCRIPT_FILENAME", "test.php");
    
                // Запускаем приложение
                php.Start();
                // Начинам читать данные из его выходного потока
                php.BeginOutputReadLine();
                // Ждем, пока php-cgi завершит работу
                php.WaitForExit();
                // Закрываем его окончательно
                php.Close();
    
                // Ожидаем нажатия клавиши
                Console.ReadKey();
            }
    
            static void php_OutputDataReceived(object sender, DataReceivedEventArgs e)
            {
                // Если нет принятых данных (такое бывает), выходим из процедуры
                if (e.Data == null)
                    return;
    
                // Выводим строку на экран
                Console.WriteLine(e.Data);
            }
        }
    }
    
    

    Метки:
    Поделиться публикацией
    Комментарии 20
    • +2
      Интересно, зачем из C# может понадобиться вызывать PHP-скрипт? Мне кажется, статье не хватает примеров реального использования PHP в совокупности с другими технологиями. Ну, или хотя бы областей, в которых этот подход может пригодиться.
      • 0
        На шарпе я лишь написал пример использования. Шарп — довольно простой язык, так что исходник получился компактным, но при этом показательным.
        php-cgi предназначен прежде всего для использования в веб-серверах. Если вы пишите небольшой веб-сервер для себя (как вариант — для курсовой или дипломной работы), то вполне сможете его использовать.
        • 0
          То есть получается, что основной областью вызова PHP из другого runtime будут прежде всего веб-серверы?
          • +1
            PHP создавался для веб-серверов. Естественно, что с ним работать будут в первую очередь веб-сервера :-) Тем не менее, это не означает, что его нельзя применять нигде больше. Вон, даже прикладные программы на нем пишут. Полный простор фантазии :-)
            • 0
              прикольно, не знал что openserver написали на пыхе…
        • +1
          хм, первое что приходит в голову — написанный не пхп менеджер базы Mysql. При условии что база меняется а перекомпилировать программу на си нет возможности.
          а вообще да, зачем сям пхп?
          • 0
            Есть же HeidiSQL — отличный и бесплатный менеджер.
            • +1
              не тот менеджер. я имел ввиду связывающее звено между программой на си и сервером mysql, я забыл как оно называется правильно.
              • +2
                Т.е. php-скрипт по какой-то логике и с какими-то параметрами запрашивает данные из базы, отдает их в виде текста через DataReceivedEventHandler программе, та их парсит и использует? Извращение, однако…
            • 0
              Как вариант, можно использовать для генерации отчетов. Использовать PHP как гибкий и настраиваемый шаблонизатор с возможностью подключения к базе данных и редактирования шаблона без изменения исходного кода программы.
          • +4
            С PHP 5.3 очень даже неплохо писать демонизированные скрипты под Linux
            procname + PNCTL + немного мозга (и в моём случае Yii, который имеет каркас для консольных приложений) — получается очень даже не дурно и стабильно. Но естественно руки должны рости из правильного места и быдлокодер не напишет нормальное приложение :)
            • 0
              • +7
                Затем, что нанимать человека для программирования такого демона на чём-то более православном, когда там работы на 3-4 дня, нету никакого смысла.
                К тому же оно отлично работает, память не жрёт, не вылетает и вообще отрабатывает шустро. Конечно оно и написано не абы как, а по лучшим практикам демонов под *nix — start, stop, restart, PID, fork, логирование и обработка ошибок через исключения.
                Хрень можно написать на любом языке, просто на некоторых это сделать сложнее :) А когда за дело берёшься по человечески, то такую вещь как демона сделать на PHP ничуть не отличается от Python или Ruby — процесс тот-же. Запустились, установили обработчики сигналов, инициализировали логи, форкнулить, поставили пид и вошли в бесконечный цикл. Дальше тока пишем логику демона и всё.
                • 0
                  простого демона написать на Си/Питоне куда проще чем на PHP, хотя бы потому что существует множество статей и howto.
                  ну а написание более-менее сложного демона сведет на нет все преимущества PHP, и написать будет перемежаться матом :)
                  • +3
                    Берем phpDaemon и в-общем то никаких проблем.
                    • +2
                      Я согласен с мнением что PHP не самый подходящий инструмент, но о сложностях вы сильно приувиличиваете. Скорее проблема в том, что 97% тех, кто работает на PHP, без понятия о системном программировании, демонах и прочих сопутствующих вещах. Следовательно их попытки буду вызывать реакцию «ВЫРВИТЕ МНЕ ГЛАЗА ОМГ!».
                      А вот какраз таки работать с текстом, базами (да и вообще набор расширений для PHP), а так же лёгкость правки и изменения кода вполне себе оправдывает такие вещи.
                      Да, числодробилку или работу с действительно большими объёмами данных на PHP делать это самоубийство, но когда задача небольшая по объёму — вполне вариант.
                      Да и товарищь ertaquo вам ссылки дал :)
                      • 0
                        Мат появляется только там где какая-то падла начинает жрать память. На моей памяти единственной такой падлой была Doctrine1, которая во внутренних объектах сохраняла все компии объектов моделей, с которыми работала.

                        А вцелом, конечно, писать демоны на РНР — извращение, но получилось достаточно эффективно. Даже если писать самому с нуля, без документаций (а их пока действительно маловато). На самом деле всё логично и понятно, но я писал ещё когда был на Винде и впервые узнал, что в php есть pcntl-функции. Короче меня удивило и поставило в ступор только одно: оно всё просто, оно работает и практически никто об этом не говорит!
                        • +1
                          Писал демона по статье на хабре — до сих пор работает (я не об up time =) ). Даже без pcntl, демоны замечательно стопаются по старнике, файловыми флагами.
                • 0
                  %комментарий_про_преимущества_png_над_jpg%
                  • 0
                    отлично спасибо… попробую использовать в своей программе…

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