Book HomePHP CookbookSearch this book

20.10. Program: Command Shell

The command-shell.php program shown in Example 20-1 provides a shell-like prompt to let you execute PHP code interactively. It reads in lines using readline( ) and then runs them with eval( ). By default, it runs each line after it's typed in. In multiline mode (specified with -m or --multiline), however, it keeps reading lines until you enter . on a line by itself; it then runs the accumulated code.

Additionally, command-shell.php uses the Readline word-completion features to more easily enter PHP functions. Enter a few characters and hit Tab to see a list of functions that match the characters you've typed.

This program is helpful for running snippets of code interactively or testing different commands. The variables, functions, and classes defined in each line of code stay defined until you quit the program, so you can test different database queries, for example:

% php -q command-shell.php
[1]> require 'DB.php';

[2]> $dbh = DB::connect('mysql://user:pwd@localhost/phpc');

[3]> print_r($dbh->getAssoc('SELECT sign,planet,start_day FROM zodiac WHERE element 
LIKE "water"'));
Array
(
    [Cancer] => Array
        (
            [0] => Moon
            [1] => 22
        )
    [Scorpio] => Array
        (
            [0] => Mars
            [1] => 24
        )
    [Pisces] => Array
        (
            [0] => Neptune
            [1] => 19
        )
)

The code for command-shell.php is in Example 20-1.

Example 20-1. command-shell.php

// Load the readline library
if (! function_exists('readline')) {
    dl('readline.'. (((strtoupper(substr(PHP_OS,0,3))) == 'WIN')?'dll':'so'))
        or die("Readline library required\n");
}

// Load the Console_Getopt class
require 'Console/Getopt.php';

$o = new Console_Getopt;
$opts = $o->getopt($o->readPHPArgv(),'hm',array('help','multiline'));

// Quit with a usage message if the arguments are bad
if (PEAR::isError($opts)) {
    print $opts->getMessage();
    print "\n";
    usage();
}

// default is to evaluate each command as it's entered
$multiline = false;

foreach ($opts[0] as $opt) {
    // remove any leading -s
    $opt[0] = preg_replace('/^-+/','',$opt[0]);

    // check the first character of the argument
    switch($opt[0][0]) {
    case 'h':
        // display help
        usage();
        break;
    case 'm':
        $multiline = true;
        break;
    }
}

// set up error display
ini_set('display_errors',false);
ini_set('log_errors',true);

// build readline completion table
$functions = get_defined_functions();
foreach ($functions['internal'] as $k => $v) {
    $functions['internal'][$k] = "$v(";
}
function function_list($line) {
    return $GLOBALS['functions']['internal'];
}
readline_completion_function('function_list');

$cmd = '';
$cmd_count = 1;

while (true) {
    // get a line of input from the user
    $s = readline("[$cmd_count]> ");
    // add it to the command history
    readline_add_history($s);
    // if we're in multiline mode:
    if ($multiline) {
        // if just a "." has been entered
        if ('.' == rtrim($s)) {
            // eval() the code
            eval($cmd);
            // clear out the accumulated code
            $cmd = '';
            // increment the command count
            $cmd_count++;
            // start the next prompt on a new line
            print "\n";
        } else {
            /* otherwise, add the new line to the accumulated code
               tacking on a newline prevents //-style comments from
               commenting out the rest of the lines entered
            */
            $cmd .= $s."\n";;
        }
    } else {
        // if we're not in multiline mode, eval() the line
        eval($s);
        // increment the command count
        $cmd_count++;
        // start the next prompt in a new line
        print "\n";
    }
}

// display helpful usage information
function usage() {
    $my_name = $_SERVER['argv'][0];

    print<<<_USAGE_
Usage: $my_name [-h|--help] [-m|--multiline]

  -h, --help: display this help
  -m, --multiline: execute accumulated code when "." is entered
                   by itself on a line. The default is to execute
                   each line after it is entered.    

_USAGE_;
    exit(-1);
}


Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.