Warning: Cannot use a scalar value as an array in /home/admin/public_html/forum/include/fm.class.php on line 757

Warning: Invalid argument supplied for foreach() in /home/admin/public_html/forum/include/fm.class.php on line 770
Форумы портала PHP.SU :: Версия для печати :: Консольные интерактивные приложения на PHP под Linux.
Форумы портала PHP.SU » » Операционная система и системные вызовы » Консольные интерактивные приложения на PHP под Linux.

Страниц (1): [1]
 

1. Balancer - 15 Апреля, 2013 - 16:58:40 - перейти к сообщению
Периодически возникает задача сделать интерактивное приложение под PHP. Обычно делается всё тупо и некрасиво — echo/fgets. Есть, понятно, и готовые библиотеки на этот счёт, но там, в общем-то, всё также некрасиво.

Следующая мысль — сделать что-то на ncurses. Но придётся делать целую прослойку для стандартных элементов интерфейса и взаимодействия их с приложением. Тоже долгая история. Но на её пути родилась такая цепочка. Для языка разметки форм хорошо бы задействовать тот же HTML → Надо писать хотя бы примитивный парсер форм → Хорошо бы для этого задействовать уже имеющиеся консольные браузеры → Консольный браузер итак может показывать HTML-страницы, выданные скриптом и отсылать запросы в него же, при этом в PHP5.4 появился неплохой встроенный Web-сервер.

И, вот, основная идея готова. Скрипт запускает web-сервер, потом — консольный браузер с запросом к этому серверу. Работаем привычно, как с любым Web-приложением, когда завершаем работу и выходим из браузера, сервер убивается. Получается удобно, красиво (для консоли) и без велосипедостроения.

А вот на практической реализации уткнулся в проблемы.

Хочется иметь автономный запускающий скрипт. С состоящим из двух частей — никаких проблем. Например, bash-запускалка и php-роутер, который проинициирует систему. Но удобно, когда скрипт один. Соответственно, в голову приходит два варианта:

— Комбинированный скрип, запускающийся как bash, продолжающийся как php. Ну, грубо говоря (концепт):
PHP:
скопировать код в буфер обмена
  1.  
  2. #!/bin/bash
  3. #<?PHP /*
  4. php -S localhost:12345 $0 &> /dev/null &
  5. sleep 0.2 # Пауза, чтобы сервер успел стартовать
  6. lynx http://localhost:12345 # запуск браузера
  7. killall php # закрытие сервера
  8. exit
  9. */
  10.  
  11. // Тут пошёл PHP-код роутера
  12.  
  13. echo "Run!";
  14.  


Этот вариант прекрасно работает, но, увы, мусорит на экран двумя хэшами. Я так и не поборол пока эту проблему.

Следующий вариант более «полноценный». Всё засунуть целиком в нормальный PHP-скрипт, из него запустить фоновым процессом php с web-сервером, запустить браузер, после выхода из последнего — всё за собой подчистить. Тут вылезла другая проблема. Единственный способ запустить браузер интерактивно, который я нашёл — это pcntl_exec(). Во всех остальных случаях он тупо ждёт ввода, ничего не выводя на экран, пока его не прибить (пробовал lynx, links, w3m).

Кроме того, возникали постоянные проблемы с работой фонового web-сервера, пока не засунул его запуск в pcntl_fork(). Текущий вариант такой:
PHP:
скопировать код в буфер обмена
  1.  
  2. #!/usr/bin/env php
  3. <?PHP
  4.  
  5. $self = $_SERVER['SCRIPT_NAME'];
  6. if(empty($_SERVER['HTTP_HOST']))
  7. {
  8.     // Это часть-запускалка
  9.     if($child = pcntl_fork())
  10.     {
  11.         // Мы запустили дочерний процесс. Тут — работает основной процесс
  12.         usleep(300000); // Чтобы сервер запустился
  13.         // Запуск браузера
  14.         pcntl_exec("/usr/bin/lynx", array("http://localhost:58671/"));
  15.         // Убиваем процесс сервера
  16.         posix_kill($child, 15);
  17.     }
  18.     else
  19.     {
  20.         // Это — дочерний процесс
  21.         // Запуск сервера в фоновом режиме
  22.         pcntl_exec("/usr/bin/php", array("-S", "localhost:58671", $self));
  23.     }
  24.  
  25.     exit();
  26. }
  27.  
  28. // Часть web--сервера
  29. // Тут — роутер
  30.  


Тут вылезла другая проблема. Всё отлично работает, пока не сделаешь выход. После выхода из браузера php-скрипт вываливается, не доходя до выполнения убийства web-сервера. Если после pcntl_exec("/usr/bin/lynx") поставить логгирование в файл, то оно никогда не вызывается. Соответственно, после выхода из браузера мы оказываемся в консоли, но у нас в фоне остаётся запущенный web-сервер.

Есть у кого-нибудь мысли, как решить первую или вторую проблемы?
2. DeepVarvar - 15 Апреля, 2013 - 18:52:36 - перейти к сообщению
Ну, я делал в итоге так: http://forum.php.su/topic.php?fo...66&topic=908
3. Balancer - 15 Апреля, 2013 - 19:17:43 - перейти к сообщению
Ну, обмен данными — это не вопрос, я тоже на сигналах делал обмен, когда gearman-демона делал.

Тут вопрос именно в проблеме запуска, обмениваться данными процессам после запуска не требуется.

Я сейчас решил задачу костылём. Использую первый вариант (интеграция PHP-скрипта в bash-скрипт), а мусор вычищаю вызовом ob_end_clean(); в начале PHP-части:
PHP:
скопировать код в буфер обмена
  1.  
  2. #!/bin/bash
  3. #<?PHP /*
  4. php -S localhost:12345 $0 &> /dev/null &
  5. sleep 0.2 # Пауза, чтобы сервер успел стартовать
  6. elinks http://localhost:12345 # запуск браузера
  7. killall php # закрытие сервера
  8. exit
  9. */
  10.  
  11. // Тут пошёл PHP-код роутера
  12.  
  13. define('BORS_CORE', '/var/www/bors-test/bors-core');
  14.  
  15. require BORS_CORE.'/init.php';
  16.  
  17. class app_test extends bors_meta_sendform
  18. {
  19.     function form_fields()
  20.     {
  21.         return array(
  22.             'film_id' => 'Идентификатор фильма',
  23.             'description' => array(
  24.                 'title' => 'Описание',
  25.                 'type' => 'text',
  26.             ),
  27.         );
  28.     }
  29. }
  30.  
  31. echo bors_load('app_test', NULL)->body();
  32.  



Нажмите для увеличения


С одной стороны, работает, при чём быстро, удобно и как требуется, с другой — всё равно костыль. Так что если кто предложит более изящное решение, то буду рад Улыбка
4. DeepVarvar - 15 Апреля, 2013 - 19:29:57 - перейти к сообщению
PHP:
скопировать код в буфер обмена
  1. <?PHP
  2. $address = '11.22.33.44';
  3. $port = 80;
  4. $somaxcount = 3600;
  5. $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
  6. $ret = socket_bind($sock,$address,$port);
  7. $ret = socket_listen($sock,$somaxcount);
  8.  
  9. do {
  10.   $msgsock = socket_accept($sock);
  11.  
  12.   $script = shell_exec('/usr/bin/php /var/www/index.php');
  13.  
  14.   $msg = "HTTP/1.1 200 OK\r\n";
  15.   $msg .= "Cache-Control: no-cache,no-store,must-revalidate\r\n";
  16.   $msg .= "Connection: close\r\n";
  17.   $msg .= "Date: Fri, 13 Aug 2010 03:01:13 GMT\r\n";
  18.   $msg .= "Pragma: no-cache\r\n";
  19.   $msg .= "Server: phpinx version 0.0.1 beta\r\n";
  20.   $msg .= "Content-Length: ".strlen($script)."\r\n";
  21.   $msg .= "Content-Type: text/html; charset=utf-8\r\n\r\n";
  22.   $msg .= $script;
  23.  
  24.         socket_write($msgsock, $msg, strlen($msg));
  25.         echo "Send to client: Ok...\n";
  26.         socket_close($msgsock);
  27.  
  28.   } while (true);
  29.  
  30. socket_close($sock);   
  31. ?>
Ха-ха Ха-ха Ха-ха

 

Powered by ExBB FM 1.0 RC1