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 the
ncurses 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 yourterminal 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
?>
Try resizing the terminal and running
the code again. Notice that the main window will border itself using the maximum
values for the height and width.
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
}
Change
ncurses_end()
to read
If($pressed == ESCAPE_KEY){
ncurses_end();
exit;
}else{
ncurses_mvwaddstr($small, 5, 5, $pressed);
}
Now if you press any key except
Escape, its integer equivalent will be placed within the window resource named
“small”. This creates a little application that responds to key
presses and exits only when you press Escape.
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);
You are not just limited to using
REVERSE
either; you can use DIM, as well as several
UNDERLINE
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 = in the previous
ncurses_getch();”
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);
}
When run you will have an
application that looks like the following illustration. It has a menu that you
can navigate through using the arrow keys and exits when you press
escape.

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. |
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 |
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. |
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;
}
Happy programming!
Links
http://www.opengroup.org/onlinepubs/007908799/xcurses/curses.h.html
This 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.




March 14, 2009 at 9:43 pm
The article on ncurses is GREAT!!!!
Just what I was looking for.
But don’t bother looking at the links for
tenshimedia.com and joihost.com
apparently they expired long ago and have been taken over by the usual domain squatter scum.
April 12, 2010 at 3:16 pm
采用php开发终端字符界面
http://www.phpcoding.cn/2009/06/01/dev-terminal-by-php/
http://www.phpcoding.cn/2009/06/03/print-control-on-terminal-by-php/