Использование ncurses в PHP
Как программист и веб-разработчик, вы наверное время от времени пишете программы на php для администрирования сайта из консоли. Средствами языка можно сделать только выводящую/читающую из консоли программу, но с помощью библиотеки ncurses можно создать консольную утилиту с довольно сложным внешним видом, которая будет одинаково выглядеть в разных видах терминалов.
PHP очень гибок и расширяем, с его помощью можно создавать скрипты, которыми можно заменить аналогичные на других языках (Perl/bash-скрипты и прочее). Функциональность ncurses поразительна и действительно выводит возможности работы с терминальным php-приложением на новый уровень.
Предназначение статьи — дать вам «толчок» в сфере написания консольных утилит на php, которые будут использовать возможность этой библиотеки. Я собираюсь показать только самое важное, относящееся к самой библиотеке, и надеюсь дать вам базу для использования ncurses в вашем приложении.
Для кого предназначена статья
Материал предназначен для опытных php-программистов, заинтересованных в создании консольных php-приложений с пользовательским интерфейсом. (прим. переводчика: материал пригодится разработчику любого уровня, здесь нет ничего сложного).
Изучаемые темы
Из данной статьи вы узнаете:
- Как создавать окна, используя ncurses, и заполнять их данными
- Как создавать несколько окон и динамически изменять их размеры до размеров терминала
- Как «ловить» нажатия клавиш
- Как написать небольшую программу, расширяющую функциональность traceroute, используя все изученные возможности библиотеки ncurses
Определения
- Ncurses (new curses) — библиотека, свободное ПО, имитирующая curses (System V Release 4.0), и даже больше. Она использует формат Terminfo, поддерживает знакоместа, цвета, множественную подсветку, использование функциональных клавиш и все другие возможности SYSV curses. Если вы использовали linux, то вы скорее всего видели его в действии: midnight commander,
ncftp, Iptraf, trafshow и многие другие используют ncurser для создания интерфейса. - Окно означает лишь секцию терминала, созданную с помощью ncurses.
От автора
Документация по функциями ncurses в php сильно ограничена. Некоторые функции использованные в данной статье отсутствуют на официальном сайте. Приложения с ncurses следуют стилю программирования на C, как если бы они были настоящими программами, а не скриптами.
Если у вас есть опыт программирования в C или C++, возможно вы уже знакомы с ncurses и можете представить, каким полезным инструментом может быть ncurses при использовании с интерпретируемым языком, таким как PHP.
Что нужно знать
Вам нужен PHP, скомпилированный с опцией -with-ncurses
на unix-подобной системе. Не все терминалы поддерживают цветной вывод, поэтому я не стал включать использование цветов в статью.
Знайте, что вам может понадобиться «сбрасывать» ваш терминал командной reset. Если возникнет какая-либо ошибка иncurses_end()
не будет вызван, то ваш терминал будет работать неправильно.
Порядок выполнения нижеприведённых функций тоже важен. PHP может показать ошибку, если вы попытаетесь обновить окно, которое не имеет соответствующего обработчика.
Начнём с простого приложения прежде чем переходить к сложному.
Простой пример с окнами
Сначала создадим основное окно, которое будет использовать всё доступное пространство. Потом добавим меньшее окно посередине экрана и поместим туда строчку текста. Чтобы получилось вот так:
Начать нужно с вызова ncurses_init();
, чтобы перейти в режим ncurses. Без этого при вызове функций библиотеки PHP будет падать с ошибкой.
Сценарий для вывода двух окон и строки
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?php // начинаем с инициализации библиотеки $ncurse = ncurses_init(); // используем весь экран $fullscreen = ncurses_newwin ( 0, 0, 0, 0); // рисуем рамку вокруг окна ncurses_border(0,0, 0,0, 0,0, 0,0); // создаём второе окно $small = ncurses_newwin(10, 30, 7, 25); // рамка для него ncurses_wborder($small,0,0, 0,0, 0,0, 0,0); ncurses_refresh(); // рисуем окна // пишем в маленьком окне ncurses_mvwaddstr($small, 5, 5, " Test String "); // обновляем маленькое окно для вывода строки ncurses_wrefresh($small); $pressed = ncurses_getch(); // ждём нажатия клавиши ncurses_end(); // выходим из режима ncurses, чистим экран |
Попробуйте изменить размеры окна и запустить скрипт ещё раз. Основное окно опять будет занимать ровно всё пространство.
Теперь вы может захотите увидеть какое-нибудь действо при нажатий на клавишу, или вы хотите сделать возможность выхода из приложения при нажатии на какую-либо кнопку. Я предпочитаю использовать для этого esc (27 ascii-код).
Чтобы добавить возможность выхода по нажатию на ESC, добавьте данный код вместо $pressed = ncurses_getch();
1 2 3 4 5 6 7 8 9 |
while (true) { $pressed = ncurses_getch(); // ждём нажатия клавиши if ($pressed == 27) { break; } else { ncurses_mvwaddstr($small, 5, 5, $pressed); ncurses_wrefresh($small); } } |
Теперь если вы нажмёте esc, программа завершится, в ином случае ascii-код нажатой клавиши будет отображён в маленьком окне.
Можно добавить заголовок, поместив код перед вызовом ncurses_refresh();
:
1 2 3 |
ncurses_attron(NCURSES_A_REVERSE); ncurses_mvaddstr(0,1,"My first ncurses application"); ncurses_attroff(NCURSES_A_REVERSE); |
Вы не ограничены режимом REVERSE (цвет и фон меняются местами), также есть DIM, UNDERLINE и другие.
Можно повысить комфорт, добавив интерактивный выбор из меню.
Создаём меню
Многие программы имеют меню и возможность выбора из него. В прошлом на «чистом» PHP мы могли лишь выводить список вариантов, и давать возможность ввести номер нужного. Такой неудобный выбор одного варианта можно заменить более интуитивно понятным.
Сценарий вывода меню с возможностью выбора
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
<?php define('ESCAPE_KEY', 27); $ncurse = ncurses_init(); $fullscreen = ncurses_newwin ( 0, 0, 0, 0); ncurses_border(0,0, 0,0, 0,0, 0,0); $small = ncurses_newwin(10, 30, 7, 25); ncurses_wborder($small,0,0, 0,0, 0,0, 0,0); ncurses_attron(NCURSES_A_REVERSE); ncurses_mvaddstr(0,1,"My first ncurses application"); ncurses_attroff(NCURSES_A_REVERSE); ncurses_refresh(); $currently_selected = 0; $menu = array('one', 'two', 'three', 'four'); while (true) { for($i=0; $i<count($menu); $i++){ $out = $menu[$i]; if($currently_selected == intval($i)){ ncurses_wattron($small,NCURSES_A_REVERSE); ncurses_mvwaddstr($small, 1+$i, 1, $out); ncurses_wattroff($small,NCURSES_A_REVERSE); } else { ncurses_mvwaddstr($small, 1+$i, 1, $out); } } ncurses_wrefresh($small); $pressed = ncurses_getch(); if ($pressed == NCURSES_KEY_UP) { $currently_selected--; if ($currently_selected < 0) $currently_selected = 0; } elseif ($pressed == NCURSES_KEY_DOWN) { $currently_selected++; if ($currently_selected >= count($menu)) $currently_selected = count($menu)-1; } elseif($pressed == ESCAPE_KEY) { break; } else { ncurses_mvwaddstr($small, 5, 5, $pressed); } } ncurses_end(); |
По получившемуся меню можно перемещаться кнопками со стрелками и выходить с помощью esc.
Сейчас вы возможно думаете о консольных приложениях, которые вы можете создать, зная только несколько выше использованных функций. Далее по тексту мы создадим полезное приложение, которое будет стартовой точкой дальнейшей разработки.
Всё вместе
На сайте php.net перечислены 119 функций библиотеки ncurses. В нижеприведённой таблице есть список использованных функций и ссылки на документацию. Замечу, что четыре из перечисленных функций отсутствуют в официальной документации. (прим. переводчика: в настоящий момент все функции содержаться в документации на php.net, правда без подробного описания).
Функция | Описание | Документация |
---|---|---|
ncurses_init | Инициализирует ncurses | www.php.net/manual/en/function.ncurses-init.php |
ncurses_newwin | Создаёт новое окно | www.php.net/manual/en/function.ncurses-newwin.php |
ncurses_getmaxyx(resource window, int return Y, int return X); |
Записывает в переменные X и Y максимальных размеров терминала | – |
ncurses_border | Рисует рамку вокруг основного окна | www.php.net/manual/en/function.ncurses-border.php |
ncurses_refresh | Обновляет основное окно. Для перерисовки второстепенных окон используйте ncurses_wrefresh | www.php.net/manual/en/function.ncurses-refresh.php |
ncurses_attron | Применяет атрибут к выводимому тексту | www.php.net/manual/en/function.ncurses-attron.php |
ncurses_attroff | Отключает применение атрибута | www.php.net/manual/en/function.ncurses-attroff.php |
ncurses_mvaddstr | Выводит строку | www.php.net/manual/en/function.ncurses-mvaddstr.php |
ncurses_wborder (resource window, int left, int right, int top, int bottom, int tl_corner, int tr_corner, int bl_corner, int br_corner); |
Рисует рамку для второстепенного окна. | – |
ncurses_wattron(resource window, int attribute) |
Идентично ncurses_attron, только применяется для окна window | – |
ncurses_mvwaddstr | Помещает строку во второстепенное окно | www.php.net/manual/en/function.ncurses-mvwaddstr.php |
ncurses_wattroff (resource window, int attribute) |
Идентично ncurses_wattroff, только применяется для окна window | – |
ncurses_wrefresh | Перерисовывает второстепенное окно. | www.php.net/manual/en/function.ncurses-wrefresh.php |
ncurses_getch | Ждёт ввода с клавиатуры или мыши. | www.php.net/manual/en/function.ncurses-getch.php |
Улучшенный traceroute
Сейчас создадим действительно полезную программу: traceroute с выводом whois-информации о каждом прыжке.
Этот скрипт запускает traceroute до zend.com (макс 10 прыжков) и показывает результаты в одном окне. По элементам можно переходить с помощью стрелок. При нажатии на enter информация о данном ip будет показана в нижнем окне. По нажатию на esc произойдёт завершение работы программы.
В заключение
Выше изложенный код довольно прост и может послужить базой для вашего приложения с использованием ncurses.
Мы не рассматривали использование мыши, цветов и многих других функций вывода.
Возможно вы создадите набор функций для использования этой библиотеки вывода. В качестве старта предлагаю вам две функции, которые упростят создание окон. Вы можете добавить в них создание панелей, рисование заголовков или что-нибудь ещё.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/** Creates an ncurses window that is write-safe on the left-hand side of the screen * @param integer $size is how wide it will be * @return window handle of inner window. */ function left_window($size=15){ global $fullscreen; ncurses_getmaxyx($fullscreen, $MAX_Y, $MAX_X); $c = ncurses_newwin ($MAX_Y-2 ,$size, 1, 1); ncurses_wborder($c,0,0, 0,0, 0,0, 0,0); // border it // now create window overtop the other just // slightly smaller so that we won't write over // the border. $d = ncurses_newwin ($MAX_Y-4 ,$size-2, 1+1, 2); ncurses_wrefresh($c); // show it ncurses_wrefresh($d); return $d; } # # creates an upper-right window # function upperr_window($size=15){ global $fullscreen; ncurses_getmaxyx($fullscreen, $MAX_Y, $MAX_X); $c = ncurses_newwin ($size ,$size, 1, $MAX_X-($size+1)); ncurses_wborder($c,0,0, 0,0, 0,0, 0,0); // border it ncurses_wrefresh($c); // show it return $c; } |
Удачного программирования!
Полезные ссылки
- www.opengroup.org/onlinepubs/007908799/xcurses/curses.h.html — это документация по заголовочному файлу ncurses, которая будет отличным справочником по функциям библиотеки.
- dickey.his.com/ncurses/ncurses-intro.html — полезный материал по использованию ncurses в языке C. Много чего из него можно использовать с PHP.
- И всегда используйте
man ncurses
. К тому же каждая функция имеет свою страницу справки (напр.man wborder
От переводчика
Установка расширения ncurses
- Установить pecl. Пакет php5-dev в ubuntu, php-pear в arch
- Установить само расширение:
pecl install ncurses
(права рут, поэтому с sudo) - Подключить расширение. Добавить строчку «extension=ncurses.so» в свой php.ini
Источник: habrahabr.ru