Консольные интерактивные приложения на PHP под Linux.

Balancer

Новичок
Периодически возникает задача сделать интерактивное приложение под PHP. Обычно делается всё тупо и некрасиво — echo/fgets. Есть, понятно, и готовые библиотеки на этот счёт, но там, в общем-то, всё также некрасиво.

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

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

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

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

— Комбинированный скрип, запускающийся как bash, продолжающийся как php. Ну, грубо говоря (концепт):
PHP:
#!/bin/bash
#<?php /*
php -S localhost:12345 $0 &> /dev/null &
sleep 0.2 # Пауза, чтобы сервер успел стартовать
lynx http://localhost:12345 # запуск браузера
killall php # закрытие сервера
exit
*/

// Тут пошёл PHP-код роутера

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

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

Кроме того, возникали постоянные проблемы с работой фонового web-сервера, пока не засунул его запуск в pcntl_fork(). Текущий вариант такой:
PHP:
#!/usr/bin/env php
<?php

$self = $_SERVER['SCRIPT_NAME'];
if(empty($_SERVER['HTTP_HOST']))
{
    // Это часть-запускалка
    if($child = pcntl_fork())
    {
        // Мы запустили дочерний процесс. Тут — работает основной процесс
        usleep(300000); // Чтобы сервер запустился
        // Запуск браузера
        pcntl_exec("/usr/bin/lynx", array("http://localhost:58671/"));
        // Убиваем процесс сервера
        posix_kill($child, 15);
    }
    else
    {
        // Это — дочерний процесс
        // Запуск сервера в фоновом режиме
        pcntl_exec("/usr/bin/php", array("-S", "localhost:58671", $self));
    }

    exit();
}

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

Есть у кого-нибудь мысли, как решить первую или вторую проблемы?
 

WMix

герр M:)ller
Партнер клуба
есть такая библиотечка dialog (http://unstableme.blogspot.de/2009/12/linux-dialog-utility-short-tutorial.html), позволяющая рисовать простые формы обычными средствами командной строки! может ну его эту рысь? ну или выключать программу не контрол+С а по нажатию на линк выйти (который положит файлик стоп, который прочтется демоном)
 

Balancer

Новичок
может ну его эту рысь?
Удобно с ней получается. И единообразно с привычным Web'ом :)

ну или выключать программу не контрол+С а по нажатию на линк выйти (который положит файлик стоп, который прочтется демоном)
Эта задача тоже потребуется, чтобы выходить из приложения единообразно, нажатием кнопки «Выход», а не через меню или хоткей :) Но тут всё просто будет.

Я пока я решил задачу костылём. Использую первый вариант (интеграция PHP-скрипта в bash-скрипт), а мусор вычищаю вызовом ob_end_clean(); в начале PHP-части:
PHP:
#!/bin/bash
#<?php /*
php -S localhost:12345 $0 &> /dev/null &
sleep 0.2 # Пауза, чтобы сервер успел стартовать
elinks http://localhost:12345 # запуск браузера
killall php # закрытие сервера
exit
*/

// Тут пошёл PHP-код роутера
ob_end_clean();

define('BORS_CORE', '/var/www/bors-test/bors-core');

require BORS_CORE.'/init.php';

class app_test extends bors_meta_sendform
{
    function form_fields()
    {
        return array(
            'film_id' => 'Идентификатор фильма',
            'description' => array(
                'title' => 'Описание',
                'type' => 'text',
            ),
        );
    }
}

echo bors_load('app_test', NULL)->body();
[/php]
 

MiksIr

miksir@home:~$
Код:
- php -S localhost:12345 $0 &> /dev/null &
+ sed -n '/<?php/,$ p' $0 | php -S localhost:12345 &>/dev/null &
В случае чистого php нужно 2 форка - три процесса
 
  • Like
Реакции: WMix
Сверху