title besides title

 
Showing posts with label PHP - Client-Side PHP. Show all posts
Showing posts with label PHP - Client-Side PHP. Show all posts

Sunday, November 25, 2012

PHP : Client-Side PHP - [20.1] Introduction

PHP was created for web programming and is still used mostly for that purpose. However, newer versions of PHP are increasingly more capable as a general-purpose scripting language. Using PHP for scripts you run from the command line is especially helpful when they share code with your web applications. If you have a discussion board on your web site, you might want to run a program every few minutes or hours to scan new postings and alert you to any messages that contain certain keywords. Writing this scanning program in PHP lets you share relevant discussion-board code with the main discussion-board application. Not only does this save you time, but also helps avoid maintenance overhead down the road.
With the PHP-GTK extension, your command-line PHP programs can be full-featured GUI applications. These can also share code with PHP web applications and text-based command-line programs. Like PHP, PHP-GTK is cross-platform, so the same code runs on Unix and Windows.
The same PHP binary built to be executed as a CGI program can be run from the command line. To run a script, pass the script filename as an argument:
% php scan-discussions.php
On Unix, you can also use the "hash-bang" syntax at the top of your scripts to run the PHP interpreter automatically. If the PHP binary is in /usr/local/bin, make the first line of your script:
#!/usr/local/bin/php
You can then run the script just by typing its name on the command line, as long as the file has execute permission.
Command-line PHP scripts almost always use the -q flag, which prevents PHP from printing HTTP response headers at the beginning of its output:
% php -q scan-discussions.php
You can also use this:
#!/usr/local/bin/php -q
Another helpful option on the command line is the -c flag, which lets you specify an alternate php.ini file to load settings from. If your default php.ini file is /usr/local/lib/php.ini, it can be helpful to have a separate configuration file at /usr/local/lib/php-commandline.ini with settings such as max_execution_time = 0; this ensures that your scripts don't quit after 30 seconds. Here's how to use this alternate file:
% php -q -c /usr/local/lib/php-commandline.ini scan-discussions.php
You can also use this :
#!/usr/local/bin/php -q -c /usr/local/lib/php-commandline.ini
If it's likely that you'll use some of your classes and functions both for the web and for the command line, abstract the code that needs to react differently in those different circumstances, such as HTML versus plain-text output or access to environment variables that a web server sets up. A useful tactic is to make your code aware of a global variable called $COMMAND_LINE. Set this to true at the top of your command-line scripts. You can then branch your scripts' behavior as follows:
if ($GLOBALS['COMMAND_LINE']) {
  print "Database error: ".mysql_error()."\n";
} else {
  print "Database error.<br>";
  error_log(mysql_error());
}
This code not only adjusts the output formatting based on the context it's executing in (\n versus <br>), but also where the information goes. On the command line, it's helpful to the person running the program to see the error message from MySQL, but on the Web, you don't want your users to see potentially sensitive data. Instead, the code outputs a generic error message and stores the details in the server's error log for private review.
Beginning with Version 4.3, PHP builds include a command-line interface (CLI) binary.[1] The CLI binary is similar to the CGI binary but has some important differences that make it more shell-friendly. Some configuration directives have hardcoded values with CLI; for example, the html_errors directive is set to false, and implicit_flush is set to true. The max_execution_time directive is set to 0, allowing unlimited program runtime. Finally, register_argc_argv is set to true. This means you can look for argument information in $argv and $argc instead of in $_SERVER['argv'] and $_SERVER['argc']. Argument processing is discussed in Section 20.2 and Section 20.3.
[1] The CLI binary can be built under 4.2.x versions by explicitly configuring PHP with --enable-cli.

PHP : Client-Side PHP - [20.2] Parsing Program Arguments

20.2.1 Problem

You want to process arguments passed on the command line.

20.2.2 Solution

Look in $_SERVER['argc'] for the number of arguments and $_SERVER['argv'] for their values. The first argument, $_SERVER['argv'][0], is the name of script that is being run:
if ($_SERVER['argc'] != 2) {
    die("Wrong number of arguments: I expect only 1.");
}

$size = filesize($_SERVER['argv'][1]);

print "I am $_SERVER[argv][0] and report that the size of ";
print "$_SERVER[argv][1] is $size bytes.";

20.2.3 Discussion

In order to set options based on flags passed from the command line, loop through $_SERVER['argv'] from 1 to $_SERVER['argc']:
for ($i = 1; $i < $_SERVER['argc']; $i++) {
    switch ($_SERVER['argv'][$i]) {
    case '-v':
        // set a flag
        $verbose = 1;
        break;
    case '-c':
        // advance to the next argument
        $i++;
        // if it's set, save the value
        if (isset($_SERVER['argv'][$i])) {
            $config_file = $_SERVER['argv'][$i];
        } else {
            // quit if no filename specified
            die("Must specify a filename after -c");
        }
        break;
    case '-q':
        $quiet = 1;
        break;
    default:
        die('Unknown argument: '.$_SERVER['argv'][$i]);
        break;
    }
}
In this example, the -v and -q arguments are flags that set $verbose and $quiet, but the -c argument is expected to be followed by a string. This string is assigned to $config_file.

PHP : Client-Side PHP - [20.3] Parsing Program Arguments with getopt

20.3.1 Problem

You want to parse program options that may be specified as short or long options, or they may be grouped.

20.3.2 Solution

Use PEAR's Console_Getopt class. Its getopt( ) method can parse both short-style options such as -a or -b and long-style options such as --alice or --bob:
$o = new Console_Getopt;

// accepts -a, -b, and -c
$opts = $o->getopt($_SERVER['argv'],'abc');

// accepts --alice and --bob
$opts = $o->getopt($_SERVER['argv'],'',array('alice','bob'));

20.3.3 Discussion

To parse short-style options, pass Console_Getopt::getopt( ) the array of command-line arguments and a string specifying valid options. This example allows -a, -b, or -c as arguments, alone or in groups:
$o = new Console_Getopt;
$opts = $o->getopt($_SERVER['argv'],'abc');
For the previous option string abc, these are valid sets of options to pass:
% program.php -a -b -c
% program.php -abc
% program.php -ab -c
The getopt( ) method returns an array. The first element in the array is a list of all of the parsed options that were specified on the command line, along with their values. The second element is any specified command-line option that wasn't in the argument specification passed to getopt( ). For example, if the previous program is run as:
% program.php -a -b sneeze
then $opts is:
Array
(
    [0] => Array
        (
            [0] => Array
                (
                    [0] => a
                    [1] => 
                )
            [1] => Array
                (
                    [0] => b
                    [1] => 
                )
        )
    [1] => Array
        (
            [0] => program.php
            [1] => sneeze
        )
)
Put a colon after an option in the specification string to indicate that it requires a value. Two colons means the value is optional. So, ab:c:: means that a can't have a value, b must, and c can take a value if specified. With this specification string, running the program as:
% program.php -a -b sneeze
makes $opts:

PHP : Client-Side PHP - [20.4] Reading from the Keyboard

20.4.1 Problem

You need to read in some typed user input.

20.4.2 Solution

Use fopen( ) with the special filename php://stdin:
print "Type your message. Type '.' on a line by itself when you're done.\n";

$fh = fopen('php://stdin','r') or die($php_errormsg);
$last_line = false;  $message = '';
while (! $last_line) {
    $next_line = fgets($fp,1024);
    if (".\n" == $next_line) {
      $last_line = true;
    } else {
      $message .= $next_line;
    }
}

print "\nYour message is:\n$message\n";
If the Readline extension is installed, use readline( ):
$last_line = false; $message = '';
while (! $last_line) {
    $next_line = readline();
    if ('.' == $next_line) {
        $last_line = true;
    } else {
        $message .= $next_line."\n";
    }
}

print "\nYour message is:\n$message\n";

20.4.3 Discussion

Once you get a file handle pointing to stdin with fopen( ), you can use all the standard file-reading functions to process input (fread( ), fgets( ), etc.) The solution uses fgets( ), which returns input a line at a time. If you use fread( ), the input still needs to be newline-terminated to make fread( ) return. For example, if you run:
$fh = fopen('php://stdin','r') or die($php_errormsg);
$msg = fread($fh,4);
print "[$msg]";
And type in tomato and then a newline, the output is [toma]. The fread( ) grabs only four characters from stdin, as directed, but still needs the newline as a signal to return from waiting for keyboard input.
The Readline extension provides an interface to the GNU Readline library. The readline( ) function returns a line at a time, without the ending newline. Readline allows Emacs and vi-style line editing by users. You can also use it to keep a history of previously entered commands:
$command_count = 1;
while (true) {
    $line = readline("[$command_count]--> ");
    readline_add_history($line);
    if (is_readable($line)) {
        print "$line is a readable file.\n";
    }
    $command_count++;
}
This example displays a prompt with an incrementing count before each line. Since each line is added to the readline history with readline_add_history( ), pressing the up and down arrows at a prompt scrolls through the previously entered lines.

PHP : Client-Side PHP - [20.5] Reading Passwords

20.5.1 Problem

You need to read a string from the command line without it being echoed as it's typed; for example, when entering passwords.

20.5.2 Solution

On Unix systems, use /bin/stty to toggle echoing of typed characters:
// turn off echo
`/bin/stty -echo`;

// read password
$password = readline();

// turn echo back on
`/bin/stty echo`;
On Windows, use w32api_register_function( ) to import _getch( ) from msvcrt.dll:
// load the w32api extension and register _getch()
dl('php_w32api.dll');
w32api_register_function('msvcrt.dll','_getch','int');

while(true) {
    // get a character from the keyboard
    $c = chr(_getch());
    if ( "\r" == $c ||  "\n" == $c ) {
        // if it's a newline, break out of the loop, we've got our password
        break;
    } elseif ("\x08" == $c) {
        /* if it's a backspace, delete the previous char from $password */
        $password = substr_replace($password,'',-1,1);
    } elseif ("\x03" == $c) {
        // if it's Control-C, clear $password and break out of the loop
        $password = NULL;
        break;
    } else {
        // otherwise, add the character to the password
        $password .= $c;
    }
}

20.5.3 Discussion

On Unix, you use /bin/stty to control the terminal characteristics so that typed characters aren't echoed to the screen while you read a password. Windows doesn't have /bin/stty, so you use the W32api extension to get access _getch( ) in the Microsoft C runtime library, msvcrt.dll. The _getch( ) function reads a character without echoing it to the screen. It returns the ASCII code of the character read, so you convert it to a character using chr( ) . You then take action based on the character typed. If it's a newline or carriage return, you break out of the loop because the password has been entered. If it's a backspace, you delete a character from the end of the password. If it's a Control-C interrupt, you set the password to NULL and break out of the loop. If none of these things are true, the character is concatenated to $password. When you exit the loop, $password holds the entered password.

PHP : Client-Side PHP - [20.6] Displaying a GUI Widget in a Window

20.6.1 Problem

You want to display a window with a GUI widget, such as a button, in it.

20.6.2 Solution

Create the window, create the widget, and then add the widget to the window:
// create the window
$window = &new GtkWindow();

// create the button and add it to the window
$button = &new GTKButton('Click Me, Alice');
$window->add($button);

// display the window
$window->show_all();

// necessary so that the program exits properly
function shutdown() { gtk::main_quit(); }
$window->connect('destroy','shutdown');

// start GTK's signal handling loop
gtk::main();

20.6.3 Discussion

First, you create a window by instantiating a new GtkWindow object. GTK objects must be created as references: &new GtkWindow( ), not new GtkWindow( ). You then create a new GtkButton object with a label "Click Me, Alice". Passing $button to the window's add( ) method adds the button to the window. The show_all( ) method displays the window and any widgets inside of it. The only widget inside the window in this example is the button. The next two lines ensure that the program quits when the window is closed. The shutdown( ) function is a callback, as is explained later in Recipe 20.8.
The last line is necessary in all PHP-GTK programs. Calling gtk::main( ) starts the signal-handling loop. This means that the program waits for signals emitted by its GUI widgets and then responds to the signals as they occur. These signals are activities like clicking on buttons, resizing windows, and typing in text boxes. The only signal this program pays attention to is the destroy signal. When the user closes the program's main window, the destroy signal is emitted, and gtk::main_quit( ) is called. This function exits the program.

PHP : Client-Side PHP - [20.7] Displaying Multiple GUI Widgets in a Window

20.7.1 Problem

You want to display more than one widget in a window.

20.7.2 Solution

Add all of the widgets in a container, and then add the container in the window:
// create the window
$window = &new GtkWindow();

// create the container - GtkVBox aligns widgets vertically
$container = &new GtkVBox();

// create a text entry widget and add it to the container
$text_entry = &new GtkEntry();
$container->pack_start($text_entry);

// create a button and add it to the container
$a_button = &new GtkButton('Abort');
$container->pack_start($a_button);

// create another button and add it to the container
$r_button = &new GtkButton('Retry');
$container->pack_start($r_button);

// create yet another button and add it to the container
$f_button = &new GtkButton('Fail');
$container->pack_start($f_button);

// add the container to the window
$window->add($container);

// display the window
$window->show_all();

// necessary so that the program exits properly
function shutdown() { gtk::main_quit(); }
$window->connect('destroy','shutdown');

// start GTK's signal handling loop
gtk::main();

20.7.3 Discussion

A window is a container that can hold only one widget. To put multiple widgets in a window, you must place all widgets into a container that can hold more than one widget and then put that container in the window. This process can be nested: the widgets inside a container can themselves be containers.
In the Solution, widgets are added to a GtkVBox container, which aligns the child widgets vertically, as shown in Figure 20-1. The add( ) method adds widgets to the GtkVBox, but pack_start( ) is used instead so that the size of the container is automatically updated with each new widget.
Figure 20-1. Widgets in a GtkVBox
GtkHBox is similar to GtkVBox. It aligns its child widgets horizontally instead of vertically. Figure 20-2 shows the four widgets from the Solution in a CtkHBox.
Figure 20-2. Widgets in a GtkHBox

PHP : Client-Side PHP - [20.8] Responding to User Actions

20.8.1 Problem

You want to do something when a user clicks a button, chooses an item from a dropdown list, or otherwise interacts with a GUI widget.

20.8.2 Solution

Write a callback function and then associate the callback function with a signal using the connect( ) method:
// create the window
$window = &new GtkWindow();

// create a button with the current time as its label
$button = &new GtkButton(strftime('%c'));

// set the update_time() function as the callback for the "clicked" signal
$button->connect('clicked','update_time');

function update_time($b) {
    // the button's text is in a child of the button - a label widget
    $b_label = $b->child;
    // set the label text to the current time
    $b_label->set_text(strftime('%c'));
}

// add the button to the window
$window->add($button);

// display the window
$window->show_all();

// necessary so that the program exits properly
function shutdown() { gtk::main_quit(); }
$window->connect('destroy','shutdown');

// start GTK's signal handling loop
gtk::main();

20.8.3 Discussion

The code in the Solution displays a window with a button in it. On the button is the time, rendered by strftime('%c'). When the button is clicked, its label is updated with the current time.
The update_time( ) function is called each time the button is clicked because $button->connect('clicked','update_time') makes update_time( ) the callback function associated with the button's clicked signal. The first argument to the callback function is the widget whose signal triggered the call as its first argument. In this case, that means that $button is passed to update_time( ). You tell connect( ) to pass additional arguments to the callback by passing them to connect( ) after the callback function name. This example displays a window with a button and a separate label. The time is printed in the label and updated when the button is clicked:
// create the window
$window = &new GtkWindow();

// create a container for the label and the button
$container = &new GtkVBox();

// create a label showing the time
$label = &new GtkLabel(strftime('%c'));

// add the label to the container
$container->pack_start($label);

// create a button
$button = &new GtkButton('Update Time');

/* set the update_time() function as the callback for the "clicked" signal
   and pass $label to the callback */
$button->connect('clicked','update_time',$label);

function update_time($b,$lb) {
    $lb->set_text(strftime('%c'));
}

// add the button to the container
$container->pack_start($button);

// add the container to the window
$window->add($container);

// display the window
$window->show_all();

// necessary so that the program exits properly
function shutdown() { gtk::main_quit(); }
$window->connect('destroy','shutdown');

// start GTK's signal handling loop
gtk::main();
Because $label is on the list of arguments passed to $button->connect( ), $label is passed to update_time( ). Calling set_text( ) on $label updates the text displayed in the label.

PHP : Client-Side PHP - [20.9] Displaying Menus

20.9.1 Problem

You want to display a menu bar at the top of a GTK window.

20.9.2 Solution

Create a GtkMenu. Create individual GtkMenuItem objects for each menu item you want to display and add each menu item to the GtkMenu with append( ). Then, create a root menu GtkMenuItem with the label that should appear in the menu bar (e.g., "File" or "Options"). Add the menu to the root menu with set_submenu( ). Create a GtkMenuBar and add the root menu to the menu bar with append( ). Finally, add the menu bar to the window:
// create the window
$window = &new GtkWindow();

// create a menu
$menu = &new GtkMenu();

// create a menu item and add it to the menu
$menu_item_1 = &new GtkMenuItem('Open');
$menu->append($menu_item_1);

// create another menu item and add it to the menu
$menu_item_2 = &new GtkMenuItem('Close');
$menu->append($menu_item_2);

// create yet another menu item and add it to the menu
$menu_item_2 = &new GtkMenuItem('Save');
$menu->append($menu_item_2);

// create a root menu and add the existing menu to it
$root_menu = &new GtkMenuItem('File');
$root_menu->set_submenu($menu);

// create a menu bar and add the root menu to it
$menu_bar = &new GtkMenuBar();
$menu_bar->append($root_menu);

// add the menu bar to the window
$window->add($menu_bar);

// display the window
$window->show_all();

// necessary so that the program exits properly
function shutdown() { gtk::main_quit(); }
$window->connect('destroy','shutdown');

// start GTK's signal handling loop
gtk::main();

20.9.3 Discussion

A menu involves a hierarchy of quite a few objects. The GtkWindow (or another container) holds the GtkMenuBar. The GtkMenuBar holds a GtkMenuItem for each top-level menu in the menu bar (e.g., "File," "Options," or "Help"). Each top-level GtkMenuItem has a GtkMenu as a submenu. That submenu contains each GtkMenuItem that should appear under the top-level menu.
As with any GTK widget, a GtkMenuItem object can have callbacks that handle signals. When a menu item is selected, it triggers the activate signal. To take action when a menu item is selected, connect its activate signal to a callback. Here's a version of the button-and-label time display from Section 20.8 with two menu items: "Update," which updates the time in the label, and "Quit," which quits the program:
// create the window
$window = &new GtkWindow();

PHP : Client-Side PHP - [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;

PHP : Client-Side PHP - [20.11] Program: Displaying Weather Conditions

The gtk-weather.php program shown in Example 20-2 uses SOAP and a weather web service to display weather conditions around the world. It incorporates a number of GTK widgets in its interface: menus, keyboard accelerators, buttons, a text entry box, labels, scrolled windows, and columned lists.
To use gtk-weather.php, first search for weather stations by typing a search term in the text-entry box and clicking the Search button. Searching for weather stations is shown in Figure 20-4.
Figure 20-4. Searching for weather stations
Once you've retrieved a list of weather stations, you can get the conditions at a specific station by selecting the station and clicking the Add button. The station code and its current conditions are added to the list at the bottom of the window. You can search again and add more stations to the list. The gtk-weather.php window with a few added stations is shown in Figure 20-5.
Figure 20-5. Added weather stations
The web service this program uses is called GlobalWeather; look for more information about it at http://www.capescience.com/webservices/globalweather/index.shtml.
Example 20-2. gtk-weather.php
// Load the GTK extension
dl('php_gtk.'. (((strtoupper(substr(PHP_OS,0,3))) == 'WIN')?'dll':'so'));

// Load the SOAP client class
require 'SOAP/Client.php';