Using Ncurses in PHP
Intended audience
Overview
Learning Objectives
Definitions
Background Information
Prerequisites
How it Works
Putting it all together
The Script
Conclusion
Links
About the author
Intended audience
This tutorial is intended for expert PHP programmers interested in extending command-line PHP applications to generate terminal-based user interfaces. You need SSH access to a UNIX server with PHP 4 compiled with ncurses.Overview
As a programmer you are often assigned to write programs for system administration tasks. By default, command line applications in PHP lack aesthetics. By taking advantage of thencurses functions you can create a
friendly user interface for the application that will be generally uniform
across the various terminal types. PHP is a highly flexible and extensible language and can be used to augment your existing shell/Perl scripting solutions or replace them; so
ncurses
functionality is a natural progression for PHP.This tutorial will give you a kick-start towards writing decent looking command-line applications that benefit the user as well as the writer. I will focus on the ‘meat’ of what is needed and hopefully give you a framework for adding ncurses functionality into your PHP application.
Learning Objectives
In this tutorial you will:- Learn how to create basic windows using ncurses and PHP, and write data to them.
- Learn how to create ncurses window sets in PHP, and dynamically size the windows to fit the terminal.
- Learn the specifics of window handles, and how to capture function keys.
- See an example of using the ncurses functions by creating a small application that extends the Linux traceroute program while using all the functions discussed herein.
Definitions
- Ncurses : The Ncurses (new curses) library is a free software emulation of curses in System V Release 4.0, and more. It uses Terminfo format, supports pads and color and multiple highlights and forms characters and function-key mapping, and has all the other SYSV-curses enhancements over BSD Curses. If you have used Linux you have probably seen ncurses in action. Programs such as Midnight commander, ncftp, Iptraf, trafshow and many others use ncurses for the visual aspect of the application.
- Window: The term ‘window’ as used in this document refers to a section of a terminal that is created via ncurses in PHP.
Background Information
This tutorial is written for the advanced programmer.It has been said that the Curses library of functions is appropriately named. The documentation for the PHP ncurses functions is very limited and several of the functions used in this tutorial do not even appear on the php.net website as of this writing. Ncurses applications follow a c-style format, as they are written to be true applications and not simply scripts with a defined start and finish.
If you have experience programming in C or C++ then you are probably already familiar with what ncurses can do, and can probably imagine how useful ncurses can be when used in conjunction with a scripting language like PHP.
Prerequisites
You will need to have access to PHP compiled with --with-ncurses on a Unix-based system. Not all terminals will support colorized ncurses output so this tutorial will not use any coloring functions.Keep in mind that you might have to reset your terminal often while working with ncurses. If you have an error
ncurses_end() is not called, so your
terminal will be fried and you will have to issue the ‘reset’
command to fix it.Order is also very important for these functions, you can segfault PHP by attempting to refresh a window that does not have an appropriate resource handle.
How it Works
Using ncurses we will create a functional window system and increase interface usability.Lets start with a very basic ncurses introduction before moving into anything more advanced:
First let’s create an ncurses main window that uses the entire screen region it has available. Then let’s add in a smaller window into the middle of this screen and write a string to it. It will look like this.

Start ncurses with
ncurses_init()
which lets PHP know that you wish to enter into ncurses mode. Without making a
call to
ncurses_init()
before calling ncurses functions, PHP will error.The code to produce the above window:
<?
// we begin by initializing ncurses
$ncurse = ncurses_init();
// let ncurses know we wish to use the whole screen
$fullscreen = ncurses_newwin ( 0, 0, 0, 0);
// draw a border around the whole thing.
ncurses_border(0,0, 0,0, 0,0, 0,0);
// now lets create a small window
$small = ncurses_newwin(10, 30, 7, 25);
// border our small window.
ncurses_wborder($small,0,0, 0,0, 0,0, 0,0);
ncurses_refresh();// paint both windows
// move into the small window and write a string
ncurses_mvwaddstr($small, 5, 5, " Test String ");
// show our handiwork and refresh our small window
ncurses_wrefresh($small);
$pressed = ncurses_getch();// wait for a user keypress
ncurses_end();// clean up our screen
?>
Now you may want to see something actually happen when you press a key, or you may not want to exit the application unless a certain key is pressed. I personally prefer using the escape key (char(27)) for this. To do this, at the top of the script add in:
define("ESCAPE_KEY", 27);
while(1){
... #code
}
ncurses_end()
to read
If($pressed == ESCAPE_KEY){
ncurses_end();
exit;
}else{
ncurses_mvwaddstr($small, 5, 5, $pressed);
}
We can make this look better by adding a title-bar.
ncurses_attron(NCURSES_A_REVERSE);
ncurses_mvaddstr(0,1,"My first ncurses application");
ncurses_attroff(NCURSES_A_REVERSE);
REVERSE
either; you can use DIM,
UNDERLINE as well as several
others.To add intuitive usability to a program you will want to add a menu system.
Most programs have user interaction and the ability to select menu items. In the past we used number or alpha systems where the user would select which menu item he wanted by typing a number or letter on the keyboard and the item would be selected. Selection menus have been replaced by interactive menu systems. You want to have a menu that visually signals the user as to which option is currently selected.
The following code will replace
“$pressed =
ncurses_getch();” in the previous
example
$menu = array("one","two","three","four");
for($a=0;$a<count($menu);$a++){
$out = $menu[$a];
if($currently_selected == intval($a)){
ncurses_wattron($small,NCURSES_A_REVERSE);
ncurses_mvwaddstr ($small, 1+$a, 1, $out);
ncurses_wattroff($small,NCURSES_A_REVERSE);
}else{
ncurses_mvwaddstr ($small, 1+$a, 1, $out);
}
}
ncurses_wrefresh($small); // otherwise we will not see
$pressed = ncurses_getch($lower_main_window); // wait for user to press key
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){
ncurses_end();
exit;
}else{
ncurses_mvwaddstr($small, 5, 5, $pressed);
}

Now that you have this knowledge of ncurses you are probably thinking of all kinds of applications that you can create that utilize these functions. So the next portion of this tutorial will focus on using ncurses to create a real world application that is both useful and an excellent starting point for further development.
Putting it all together
The online documentation for PHP currently has 119 listed functions for ncurses; the following list contains the functions used in the script below with links to the online documentation where available. The four functions listed below which do not appear in the online documentation are explained.|
ncurses_init |
Initialize ncurses. |
|
ncurses_newwin |
Create a new window. |
|
ncurses_getmaxyx( resource window, int
return Y, int return X); |
Sets Y and X to maximum size of current
terminal. |
|
ncurses_border |
Draw a border around the main
window. |
|
ncurses_refresh |
Refresh the main window; use
ncurses_wrefresh for refreshing individual windows. |
|
ncurses_attron |
Turn output attribute
on. |
|
ncurses_mvaddstr |
Move and insert a
string. |
|
ncurses_attroff |
Turn output attribute
off. |
|
ncurses_wborder (resource window, int
left, int right, int top, int bottom, int tl_corner, int tr_corner, int
bl_corner, int br_corner); |
Identical to ncurses_border except that you pass the window resource as an additional first parameter. |
|
ncurses_wattron(resource window, int
attribute) |
Identical to ncurses_attron except that you pass the window resource as an additional first parameter |
|
ncurses_mvwaddstr |
Select a window and place a string
within it a coordinates. |
|
ncurses_wattroff (resource window, int
attribute) |
Identical to ncurses_attroff except that you pass the window resource as an additional first parameter. |
|
ncurses_wrefresh |
Refresh named window
resource. |
|
ncurses_getch |
Wait for user keyboard or mouse
input. |
The Script
The following code will produce a window which looks like this:
This script runs a traceroute to zend.com, limits the hops to only ten, and displays the results within a window. The items in the upper window are scrollable by using your arrow keys and when you press Enter on each one a reverse
whois is
done and the results are placed in the lower window. If you press the escape key
the program will exit.
<?
// define some key constants.
define("ESCAPE_KEY", 27);
define("ENTER_KEY", 13);
// get our initial data
$tr_return = traceroute("www.zend.com");
$ncurses_session = ncurses_init();
$main = ncurses_newwin(0, 0, 0, 0); // main window
ncurses_getmaxyx(&$main, $lines, $columns);
ncurses_border(0,0, 0,0, 0,0, 0,0);
while(1){
// border the main window
ncurses_attron(NCURSES_A_REVERSE);
ncurses_mvaddstr(0,1,"Traceroute example");
ncurses_attroff(NCURSES_A_REVERSE);
// create a lower window which is dynamically sized...
$lower_frame_window = ncurses_newwin (10, $columns-3, $lines-11, 1);
ncurses_wborder($lower_frame_window, 0,0, 0,0, 0,0, 0,0); // border it
$lower_main_window = ncurses_newwin (8, $columns-5, $lines-10, 2);
$main_list_window = ncurses_newwin ($lines-12, $columns-3, 1, 1);
ncurses_wborder($main_list_window, 0,0, 0,0, 0,0, 0,0); // border it
if($currently_selected == ""){$currently_selected = 0;}
for($a=0;$a<count($tr_return);$a++){
$out = $tr_return[$a];
if($currently_selected == intval($a)){
ncurses_wattron($main_list_window,NCURSES_A_REVERSE);
ncurses_mvwaddstr ($main_list_window, 1+$a, 1, $out);
ncurses_wattroff($main_list_window,NCURSES_A_REVERSE);
}else{
ncurses_mvwaddstr ($main_list_window, 1+$a, 1, $out);
}
}
if($y == ENTER_KEY){
$newout = explode(" ", $check_me);
$rwhois_return = rwhois(trim($newout[3]));
$a=0;
while(list($key,$val)=each($rwhois_return)){
ncurses_mvwaddstr($lower_main_window, 1+$a, 1, $key . " - " . $val);
$a++;
}
}elseif($y == ESCAPE_KEY){
ncurses_end();
exit;
}
ncurses_move(-1,1); // toss cursor out of sight.
ncurses_wrefresh($lower_frame_window);
ncurses_wrefresh($lower_main_window); // show it
ncurses_wrefresh($main_list_window);
// wait for user to press a key
$y = ncurses_getch($lower_main_window);
// check for the presence of up-arrow and down-arrow keys
if($y == NCURSES_KEY_UP){
$currently_selected--;
if($currently_selected < 0){ $currently_selected = 0; }
}elseif( $y == NCURSES_KEY_DOWN){
$currently_selected++;
if($currently_selected >= count($tr_return)){ $currently_selected = count($tr_return)-1; }
}
}//end main while
// the following are two helper functions for
// this ncurses example.
function traceroute($address){
exec("/usr/sbin/traceroute -x -n -m 10 $address",
$trreturn);
return $trreturn;
}//end function
// reverse whois
function rwhois($query){
$fp = fsockopen ("rwhois.arin.net", 4321, $errno, $errstr, 30);
if (!$fp) {
$ret[] = "$errstr ($errno)\n";
}else{
fputs ($fp, "$query\r\n\r\n");
$count=1;
while (!feof($fp) && $count < 17) {
$back = fgets ($fp,128);
$data = explode(":",$back);
$ret[$data[1]] = $data[2];
$count++;
}//wend
fclose ($fp);
}//fi
return $ret;
}//end function
?>
Conclusion
The code above is obviously a very simple framework for an ncurses application. With so many functions at your disposal there is quite a world of interfaces that can be developed.This tutorial has not touched upon using the mouse in terminals, using colors or many of the output control functions and their defined constants.
You will want to create a library of functions for using ncurses for doing things like starting up ncurses and returning a handle to the main window or for drawing windows.
The following are two functions which show in more depth how to use the
ncurses_newwin()
function and how to create dynamically sized
windows within terminal screens. These will be a good start for much more
advanced functions. These can easily be extended to add in title bars, panes or
anything else you can think up.
#
# left_window(int size) creates an ncurses window
# that is write-safe on the left-hand side of the
# screen $size is how wide it will be
# returns 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;
}
Links
http://www.opengroup.org/onlinepubs/007908799/xcurses/curses.h.htmlThis is the curses.h documentation, which will be an excellent reference while working on any PHP application that uses the ncurses functions.
http://dickey.his.com/ncurses/ncurses-intro.html
A great write-up on programming with ncurses in C, much of which translates directly into PHP.
And as always, use
‘man ncurses’ as each function in C has a man page for it. (i.e. ´man wborder’´).About the author
Joel De Gan is a seasoned developer currently working for a high-profile domain name registrar. He is obsessed with PHP and despite working with it on a daily basis he often continues late into the night to sit up and program.Joel additionally is the CTO for Tenshimedia LLC ( http://tenshimedia.com ) a multi-faceted media company that among other things runs and maintains JOIhost ( http://www.joihost.com ).
Joel is currently, in his spare time, working on a PHP/ncurses interface for the Internet file sharing application eDonkey, and a PHP/ncurses packet logger, which logs from tcpdump in addition to a small side project (http://listbid.com/affil/) called Listbid.

Comments