<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>PHP Cookbook/Client-Side PHP - Revision history</title>
		<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Client-Side_PHP&amp;action=history</link>
		<description>Revision history for this page on the wiki</description>
		<language>en</language>
		<generator>MediaWiki 1.11.0</generator>
		<lastBuildDate>Wed, 22 May 2013 10:31:18 GMT</lastBuildDate>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Client-Side_PHP&amp;diff=7332&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 13:36, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Fri, 07 Mar 2008 13:36:04 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:PHP_Cookbook/Client-Side_PHP</comments>		</item>
		<item>
			<title>Evanlenz: 1 revision(s)</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Client-Side_PHP&amp;diff=3143&amp;oldid=prev</link>
			<description>&lt;p&gt;1 revision(s)&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:30, 6 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Thu, 06 Mar 2008 22:30:17 GMT</pubDate>			<dc:creator>Evanlenz</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:PHP_Cookbook/Client-Side_PHP</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=PHP_Cookbook/Client-Side_PHP&amp;diff=3142&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{PHP Cookbook/TOC}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
 % '''php scan-discussions.php'''&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
On Unix, you can also use the &amp;quot;hash-bang&amp;quot; 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:&lt;br /&gt;
&lt;br /&gt;
 #!/usr/local/bin/php&lt;br /&gt;
&lt;br /&gt;
You can then run the script just by typing its name on the command line, as long as the file has execute permission.&lt;br /&gt;
&lt;br /&gt;
Command-line PHP scripts almost always use the &amp;lt;tt&amp;gt;-q&amp;lt;/tt&amp;gt; flag, which prevents PHP from printing HTTP response headers at the beginning of its output:&lt;br /&gt;
&lt;br /&gt;
 % '''php -q scan-discussions.php'''&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
You can also use this:&lt;br /&gt;
&lt;br /&gt;
 #!/usr/local/bin/php -q&lt;br /&gt;
&lt;br /&gt;
Another helpful option on the command line is the &amp;lt;tt&amp;gt;-c&amp;lt;/tt&amp;gt; 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 &amp;lt;tt&amp;gt;max_execution_time = 0&amp;lt;/tt&amp;gt;; this ensures that your scripts don't quit after 30 seconds. Here's how to use this alternate file:&lt;br /&gt;
&lt;br /&gt;
 % '''php -q -c /usr/local/lib/php-commandline.ini scan-discussions.php'''&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
You can also use this :&lt;br /&gt;
&lt;br /&gt;
 #!/usr/local/bin/php -q -c /usr/local/lib/php-commandline.ini&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;tt&amp;gt;$COMMAND_LINE&amp;lt;/tt&amp;gt;. Set this to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; at the top of your command-line scripts. You can then branch your scripts' behavior as follows:&lt;br /&gt;
&lt;br /&gt;
 if ($GLOBALS['COMMAND_LINE']) {&lt;br /&gt;
   print &amp;quot;Database error: &amp;quot;.mysql_error().&amp;quot;\n&amp;quot;;&lt;br /&gt;
 } else {&lt;br /&gt;
   print &amp;quot;Database error.&amp;amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
   error_log(mysql_error());&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This code not only adjusts the output formatting based on the context it's executing in (&amp;lt;tt&amp;gt;\n&amp;lt;/tt&amp;gt; versus &amp;lt;tt&amp;gt;&amp;amp;lt;br&amp;gt;&amp;lt;/tt&amp;gt;), 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.&lt;br /&gt;
&lt;br /&gt;
Beginning with Version 4.3, PHP builds include a command-line interface (CLI) binary.&amp;lt;ref&amp;gt;The CLI binary can be built under 4.2.x versions by explicitly configuring PHP with &amp;lt;tt&amp;gt;--enable-cli&amp;lt;/tt&amp;gt;.&amp;lt;/ref&amp;gt; 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 &amp;lt;tt&amp;gt;html_errors&amp;lt;/tt&amp;gt; directive is set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;implicit_flush&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;max_execution_time&amp;lt;/tt&amp;gt; directive is set to 0, allowing unlimited program runtime. Finally, &amp;lt;tt&amp;gt;register_argc_argv&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;. This means you can look for argument information in &amp;lt;tt&amp;gt;$argv&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$argc&amp;lt;/tt&amp;gt; instead of in &amp;lt;tt&amp;gt;$_SERVER['argv']&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$_SERVER['argc']&amp;lt;/tt&amp;gt;. Argument processing is discussed in [[PHP Cookbook/Client-Side PHP#Parsing Program Arguments|Recipe 20.2]] and [[PHP Cookbook/Client-Side PHP#Parsing Program Arguments with getopt|Recipe 20.3]].&lt;br /&gt;
&lt;br /&gt;
The CLI binary accepts a slightly different set of arguments than the CGI binary. It doesn't accept the &amp;lt;tt&amp;gt;-q&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;-C&amp;lt;/tt&amp;gt; flags because it does what these flags indicate by default. While &amp;lt;tt&amp;gt;-q&amp;lt;/tt&amp;gt; tells the CGI binary not to print headers, the CLI binary never prints headers. Even the &amp;lt;tt&amp;gt;header( )&amp;lt;/tt&amp;gt; function produces no output under CLI. Similarly, the &amp;lt;tt&amp;gt;-C&amp;lt;/tt&amp;gt; flag tells the CGI binary not to change to the directory of the script being run. The CLI binary never changes to the script directory.&lt;br /&gt;
&lt;br /&gt;
The CLI binary also takes one new argument: &amp;lt;tt&amp;gt;-r&amp;lt;/tt&amp;gt;. When followed by some PHP code without &amp;lt;tt&amp;gt;&amp;lt;?php&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;?&amp;gt;&amp;lt;/tt&amp;gt; script tags, the CLI binary runs the code. For example, here's how to print the current time:&lt;br /&gt;
&lt;br /&gt;
 % '''php -r 'print strftime(&amp;quot;%c&amp;quot;);''''&lt;br /&gt;
          &lt;br /&gt;
&lt;br /&gt;
Finally, the CLI binary defines handles to the standard I/O streams as the constants &amp;lt;tt&amp;gt;STDIN&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;STDOUT&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;STDERR&amp;lt;/tt&amp;gt;. You can use these instead of creating your own file handles with &amp;lt;tt&amp;gt;fopen( )&amp;lt;/tt&amp;gt; :&lt;br /&gt;
&lt;br /&gt;
 // read from standard in&lt;br /&gt;
 $input = fgets(STDIN,1024);&lt;br /&gt;
 &lt;br /&gt;
 // write to standard out&lt;br /&gt;
 fwrite(STDOUT,$jokebook);&lt;br /&gt;
 &lt;br /&gt;
 // write to standard error&lt;br /&gt;
 fwrite(STDERR,$error_code);&lt;br /&gt;
&lt;br /&gt;
If you're using the CLI binary, you can use &amp;lt;tt&amp;gt;php_sapi_name( )&amp;lt;/tt&amp;gt; instead of &amp;lt;tt&amp;gt;$GLOBALS['COMMAND_LINE']&amp;lt;/tt&amp;gt; to test whether a script is running in a web or command-line context:&lt;br /&gt;
&lt;br /&gt;
 if ('cli' == php_sapi_name()) {&lt;br /&gt;
   print &amp;quot;Database error: &amp;quot;.mysql_error().&amp;quot;\n&amp;quot;;&lt;br /&gt;
 } else {&lt;br /&gt;
   print &amp;quot;Database error.&amp;amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
   error_log(mysql_error());&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
You can use the CLI binary or the CGI binary to run programs that use the PHP-GTK extension. This extension is an interface to the GTK+ toolkit, which is a library of widgets, screen drawing code, and other necessary functions for building a GUI application.&lt;br /&gt;
&lt;br /&gt;
Widgets are GUI interface elements such as buttons, scrollbars, windows, menus, and select boxes. To build a PHP-GTK application, your code must create widgets and arrange them on the screen. [[PHP Cookbook/Client-Side PHP#Displaying a GUI Widget in a Window|Recipe 20.6]] shows how to create and display a widget, [[PHP Cookbook/Client-Side PHP#Displaying Multiple GUI Widgets in a Window|Recipe 20.7]] shows how to arrange multiple widgets for display together, and [[PHP Cookbook/Client-Side PHP#Displaying Menus|Recipe 20.9]] explains how to display a menu bar.&lt;br /&gt;
&lt;br /&gt;
Widgets communicate with each other and with the rest of your program using signals. When something happens to a widget, it emits a signal; for example, when a button is clicked, it emits a &amp;lt;tt&amp;gt;clicked&amp;lt;/tt&amp;gt; signal. [[PHP Cookbook/Client-Side PHP#Responding to User Actions|Recipe 20.8]] discusses how to capture these signals and take action when one is emitted. The sample application in [[PHP Cookbook/Client-Side PHP#Program: Displaying Weather Conditions|Recipe 20.11]] combines PHP-GTK with some SOAP function calls to display weather conditions around the world.&lt;br /&gt;
&lt;br /&gt;
To install PHP-GTK on Unix, download the latest version of PHP-GTK from ''http://gtk.php.net/download.php'' and the GTK+ libraries from ''http://www.gtk.org/download''. You also need ''libtool'' 1.4.2, ''automake'' 1.4, and ''autoconf'' 2.13 (available at ''http://www.gnu.org/directory/'' if they're not already installed on your system).&lt;br /&gt;
&lt;br /&gt;
Once you've downloaded all the necessary files and installed the support libraries and tools, unpack the PHP-GTK source distribution. In the PHP-GTK directory, run ''./buildconf'' to create configuration files, ''./configure'' to create makefiles, then ''make'' to build the PHP-GTK extension. Last, run ''make install'' to install the PHP-GTK extension in your PHP extensions directory. You can find detailed Unix installation instructions, including common build problems, at ''http://gtk.php.net/manual/en/install.unix.php''.&lt;br /&gt;
&lt;br /&gt;
To install PHP-GTK on Windows, no compiling is necessary. From ''http://gtk.php.net/download.php'', you can download a compiled PHP-GTK extension and supporting libraries. Once you've downloaded and unzipped the Windows distribution, copy the files in the ''php4'' subdirectory to your PHP binary directory (or create one if it doesn't already exist). Copy the files in the ''winnt\system32'' subdirectory to your ''system32'' directory (''C:\WINNT\SYSTEM32'' for Windows NT and Windows 2000; ''C:\WINDOWS\SYSTEM32'' for Windows 95 and Windows 98). If you don't already have a ''php.ini'' file in place, copy the ''winnt\php.ini'' file to your Windows directory (''C:\WINNT'' or ''C:\WINDOWS''). If you already have a ''php.ini'' file in place, add these lines to the end of it:&lt;br /&gt;
&lt;br /&gt;
 [PHP-GTK]&lt;br /&gt;
 php-gtk.extensions = php_gtk_libglade.dll, php_gtk_sqpane.dll&lt;br /&gt;
&lt;br /&gt;
Detailed Windows installation instructions are at ''http://gtk.php.net/manual/en/install.win32.php''.&lt;br /&gt;
&lt;br /&gt;
On either platform, once you've installed the PHP-GTK extension, you need to use the &amp;lt;tt&amp;gt;dl( )&amp;lt;/tt&amp;gt; function to load it in any script in which you want to use GTK functionality. On Windows:&lt;br /&gt;
&lt;br /&gt;
 if (! class_exists('gtk')) {&lt;br /&gt;
     dl('php_gtk.dll');&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
On Unix:&lt;br /&gt;
&lt;br /&gt;
 if (! class_exists('gtk')) {&lt;br /&gt;
     dl('php_gtk.so');&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
If you want the same script to run unaltered on Unix or Windows, you can load the PHP-GTK extension like this:&lt;br /&gt;
&lt;br /&gt;
 if (! class_exists('gtk')) {&lt;br /&gt;
     dl('php_gtk.'. (((strtoupper(substr(PHP_OS,0,3))) == 'WIN')?'dll':'so'));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The GTK+ toolkit is large and powerful. PHP-GTK makes it easy to create and manipulate GTK+ objects, but designing and planning a GUI application is still a significant task. In addition to the comprehensive PHP-GTK documentation at ''http://gtk.php.net/manual/'', also take advantage of the GTK+ documentation itself at ''http://developer.gnome.org/doc/API/gtk/index.html''. The C class and function names in the GTK+ documentation map almost directly to their PHP equivalents. Also, the tutorial at ''http://www.gtk.org/tutorial/'' is for GTK+ 2.0 (not 1.2), but it is still a good introduction to the concepts and practices of GTK+ application building.&lt;br /&gt;
&lt;br /&gt;
== Parsing Program Arguments ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to process arguments passed on the command line.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Look in &amp;lt;tt&amp;gt;$_SERVER['argc']&amp;lt;/tt&amp;gt; for the number of arguments and &amp;lt;tt&amp;gt;$_SERVER['argv']&amp;lt;/tt&amp;gt; for their values. The first argument, &amp;lt;tt&amp;gt;$_SERVER['argv'][0]&amp;lt;/tt&amp;gt;, is the name of script that is being run:&lt;br /&gt;
&lt;br /&gt;
 if ($_SERVER['argc'] != 2) {&lt;br /&gt;
     die(&amp;quot;Wrong number of arguments: I expect only 1.&amp;quot;);&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 $size = filesize($_SERVER['argv'][1]);&lt;br /&gt;
 &lt;br /&gt;
 print &amp;quot;I am $_SERVER[argv][0] and report that the size of &amp;quot;;&lt;br /&gt;
 print &amp;quot;$_SERVER[argv][1] is $size bytes.&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
In order to set options based on flags passed from the command line, loop through &amp;lt;tt&amp;gt;$_SERVER['argv']&amp;lt;/tt&amp;gt; from &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;$_SERVER['argc']&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 for ($i = 1; $i &amp;lt; $_SERVER['argc']; $i++) {&lt;br /&gt;
     switch ($_SERVER['argv'][$i]) {&lt;br /&gt;
     case '-v':&lt;br /&gt;
         // set a flag&lt;br /&gt;
         $verbose = 1;&lt;br /&gt;
         break;&lt;br /&gt;
     case '-c':&lt;br /&gt;
         // advance to the next argument&lt;br /&gt;
         $i++;&lt;br /&gt;
         // if it's set, save the value&lt;br /&gt;
         if (isset($_SERVER['argv'][$i])) {&lt;br /&gt;
             $config_file = $_SERVER['argv'][$i];&lt;br /&gt;
         } else {&lt;br /&gt;
             // quit if no filename specified&lt;br /&gt;
             die(&amp;quot;Must specify a filename after -c&amp;quot;);&lt;br /&gt;
         }&lt;br /&gt;
         break;&lt;br /&gt;
     case '-q':&lt;br /&gt;
         $quiet = 1;&lt;br /&gt;
         break;&lt;br /&gt;
     default:&lt;br /&gt;
         die('Unknown argument: '.$_SERVER['argv'][$i]);&lt;br /&gt;
         break;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
In this example, the &amp;lt;tt&amp;gt;-v&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;-q&amp;lt;/tt&amp;gt; arguments are flags that set &amp;lt;tt&amp;gt;$verbose&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$quiet&amp;lt;/tt&amp;gt;, but the &amp;lt;tt&amp;gt;-c&amp;lt;/tt&amp;gt; argument is expected to be followed by a string. This string is assigned to &amp;lt;tt&amp;gt;$config_file&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Client-Side PHP#Parsing Program Arguments with getopt|Recipe 20.3]] for more parsing arguments with ''getopt''; documentation on &amp;lt;tt&amp;gt;$_SERVER['argc']&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$_SERVER['argv']&amp;lt;/tt&amp;gt; at &amp;lt;tt&amp;gt;http://www.php.net/reserved.variables&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Parsing Program Arguments with getopt ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to parse program options that may be specified as short or long options, or they may be grouped.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Use PEAR's &amp;lt;tt&amp;gt;Console_Getopt&amp;lt;/tt&amp;gt; class. Its &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt; method can parse both short-style options such as &amp;lt;tt&amp;gt;-a&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;-b&amp;lt;/tt&amp;gt; and long-style options such as &amp;lt;tt&amp;gt;--alice&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;--bob&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;$o = new Console_Getopt;&lt;br /&gt;
&lt;br /&gt;
// accepts -a, -b, and -c&lt;br /&gt;
$opts = $o-&amp;gt;getopt($_SERVER['argv'],'abc');&lt;br /&gt;
&lt;br /&gt;
// accepts --alice and --bob&lt;br /&gt;
$opts = $o-&amp;gt;getopt($_SERVER['argv'],'',array('alice','bob'));&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
To parse short-style options, pass &amp;lt;tt&amp;gt;Console_Getopt::getopt( )&amp;lt;/tt&amp;gt; the array of command-line arguments and a string specifying valid options. This example allows &amp;lt;tt&amp;gt;-a&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;-b&amp;lt;/tt&amp;gt;, or &amp;lt;tt&amp;gt;-c&amp;lt;/tt&amp;gt; as arguments, alone or in groups:&lt;br /&gt;
&lt;br /&gt;
 $o = new Console_Getopt;&lt;br /&gt;
 $opts = $o-&amp;gt;getopt($_SERVER['argv'],'abc');&lt;br /&gt;
&lt;br /&gt;
For the previous option string &amp;lt;tt&amp;gt;abc&amp;lt;/tt&amp;gt;, these are valid sets of options to pass:&lt;br /&gt;
&lt;br /&gt;
 % '''program.php -a -b -c'''&lt;br /&gt;
 % '''program.php -abc'''&lt;br /&gt;
 % '''program.php -ab -c'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt; 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 &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt;. For example, if the previous program is run as:&lt;br /&gt;
&lt;br /&gt;
 % '''program.php -a -b sneeze'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
then &amp;lt;tt&amp;gt;$opts&amp;lt;/tt&amp;gt; is:&lt;br /&gt;
&lt;br /&gt;
 Array&lt;br /&gt;
 (&lt;br /&gt;
     [0] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; Array&lt;br /&gt;
                 (&lt;br /&gt;
                     [0] =&amp;gt; a&lt;br /&gt;
                     [1] =&amp;gt; &lt;br /&gt;
                 )&lt;br /&gt;
             [1] =&amp;gt; Array&lt;br /&gt;
                 (&lt;br /&gt;
                     [0] =&amp;gt; b&lt;br /&gt;
                     [1] =&amp;gt; &lt;br /&gt;
                 )&lt;br /&gt;
         )&lt;br /&gt;
     [1] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; program.php&lt;br /&gt;
             [1] =&amp;gt; sneeze&lt;br /&gt;
         )&lt;br /&gt;
 )&lt;br /&gt;
&lt;br /&gt;
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, &amp;lt;tt&amp;gt;ab:c:&amp;lt;/tt&amp;gt;: means that &amp;lt;tt&amp;gt;a&amp;lt;/tt&amp;gt; can't have a value, &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt; must, and &amp;lt;tt&amp;gt;c&amp;lt;/tt&amp;gt; can take a value if specified. With this specification string, running the program as:&lt;br /&gt;
&lt;br /&gt;
 % '''program.php -a -b sneeze'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
makes &amp;lt;tt&amp;gt;$opts&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 Array&lt;br /&gt;
 (&lt;br /&gt;
     [0] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; Array&lt;br /&gt;
                 (&lt;br /&gt;
                     [0] =&amp;gt; a&lt;br /&gt;
                     [1] =&amp;gt; &lt;br /&gt;
                 )&lt;br /&gt;
             [1] =&amp;gt; Array&lt;br /&gt;
                 (&lt;br /&gt;
                     [0] =&amp;gt; b&lt;br /&gt;
                     [1] =&amp;gt; sneeze&lt;br /&gt;
                 )&lt;br /&gt;
         )&lt;br /&gt;
     [1] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; program.php&lt;br /&gt;
         )&lt;br /&gt;
 )&lt;br /&gt;
&lt;br /&gt;
Because &amp;lt;tt&amp;gt;sneeze&amp;lt;/tt&amp;gt; is now set as the value of &amp;lt;tt&amp;gt;b&amp;lt;/tt&amp;gt;, it is no longer in the array of unparsed options. Note that the array of unparsed options always contains the name of the program.&lt;br /&gt;
&lt;br /&gt;
To parse long-style arguments, supply &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt; with an array that describes your desired arguments. Put each argument in an array element (leave off the leading &amp;lt;tt&amp;gt;--&amp;lt;/tt&amp;gt;) and follow it with &amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt; to indicate a mandatory argument or &amp;lt;tt&amp;gt;= =&amp;lt;/tt&amp;gt; to indicate an optional argument. This array is the third argument to &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt;. The second argument (the string for short-style arguments) can be left blank or not, depending on whether you also want to parse short-style arguments. This example allows &amp;lt;tt&amp;gt;debug&amp;lt;/tt&amp;gt; as an argument with no value, &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; with a mandatory value, and &amp;lt;tt&amp;gt;size&amp;lt;/tt&amp;gt; with an optional value:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;require 'Console/Getopt.php';&lt;br /&gt;
$o = new Console_Getopt;&lt;br /&gt;
$opts = $o-&amp;gt;getopt($_SERVER['argv'],'',array('debug','name=','size=='));&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These are valid ways to run this program:&lt;br /&gt;
&lt;br /&gt;
 % '''program.php --debug'''&lt;br /&gt;
 % '''program.php --name=Susannah'''&lt;br /&gt;
 % '''program.php --name Susannah'''&lt;br /&gt;
 % '''program.php --debug --size'''&lt;br /&gt;
 % '''program.php --size=56 --name=Susannah'''&lt;br /&gt;
 % '''program.php --name --debug'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
The last example is valid (if counterproductive) because it treats &amp;lt;tt&amp;gt;--debug&amp;lt;/tt&amp;gt; as the value of the &amp;lt;tt&amp;gt;name&amp;lt;/tt&amp;gt; argument and doesn't consider the &amp;lt;tt&amp;gt;debug&amp;lt;/tt&amp;gt; argument to be set. Values can be separated from their arguments on the command line by either a &amp;lt;tt&amp;gt;=&amp;lt;/tt&amp;gt; or a space.&lt;br /&gt;
&lt;br /&gt;
For long-style arguments, &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt; includes the leading &amp;lt;tt&amp;gt;--&amp;lt;/tt&amp;gt; in the array of parsed arguments; for example, when run as:&lt;br /&gt;
&lt;br /&gt;
 % '''program.php --debug --name=Susannah'''&lt;br /&gt;
             &lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;$opts&amp;lt;/tt&amp;gt; is set to:&lt;br /&gt;
&lt;br /&gt;
 Array&lt;br /&gt;
 (&lt;br /&gt;
     [0] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; Array&lt;br /&gt;
                 (&lt;br /&gt;
                     [0] =&amp;gt; --debug&lt;br /&gt;
                     [1] =&amp;gt; &lt;br /&gt;
                 )&lt;br /&gt;
             [1] =&amp;gt; Array&lt;br /&gt;
                 (&lt;br /&gt;
                     [0] =&amp;gt; --name&lt;br /&gt;
                     [1] =&amp;gt; Susannah&lt;br /&gt;
                 )&lt;br /&gt;
         )&lt;br /&gt;
     [1] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; program.php&lt;br /&gt;
         )&lt;br /&gt;
 )&lt;br /&gt;
&lt;br /&gt;
We've been using &amp;lt;tt&amp;gt;$_SERVER['argv']&amp;lt;/tt&amp;gt; as the array of command-line arguments, which is fine by default. &amp;lt;tt&amp;gt;Console_Getopt&amp;lt;/tt&amp;gt; provides a method, &amp;lt;tt&amp;gt;readPHPArgv( )&amp;lt;/tt&amp;gt;, to look also in &amp;lt;tt&amp;gt;$argv&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$HTTP_SERVER_VARS['argv']&amp;lt;/tt&amp;gt; for command-line arguments. Use it by passing its results to &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;require 'Console/Getopt.php';&lt;br /&gt;
$o = new Console_Getopt;&lt;br /&gt;
$opts = $o-&amp;gt;getopt($o-&amp;gt;readPHPArgv(),'',array('debug','name=','size=='));&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Both &amp;lt;tt&amp;gt;getopt( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;readPHPArgv( )&amp;lt;/tt&amp;gt; return a &amp;lt;tt&amp;gt;Getopt_Error&amp;lt;/tt&amp;gt; object when these encounter an error; for example, having no option specified for an option that requires one. &amp;lt;tt&amp;gt;Getopt_Error&amp;lt;/tt&amp;gt; extends the &amp;lt;tt&amp;gt;PEAR_Error&amp;lt;/tt&amp;gt; base class, so you can use familiar methods to handle errors:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;require 'Console/Getopt.php';&lt;br /&gt;
$o = new Console_Getopt;&lt;br /&gt;
$opts = $o-&amp;gt;getopt($o-&amp;gt;readPHPArgv(),'',array('debug','name=','size=='));&lt;br /&gt;
&lt;br /&gt;
if (PEAR::isError($opts)) {&lt;br /&gt;
    print $opts-&amp;gt;getMessage();&lt;br /&gt;
} else {&lt;br /&gt;
    // process options&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
[[PHP Cookbook/Client-Side PHP#Parsing Program Arguments|Recipe 20.2]] for parsing of program options without ''getopt''; documentation on &amp;lt;tt&amp;gt;Console_Getopt&amp;lt;/tt&amp;gt; at ''http://pear.php.net/manual/en/core.console.getopt.php'' .&lt;br /&gt;
&lt;br /&gt;
== Reading from the Keyboard ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You need to read in some typed user input.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Use &amp;lt;tt&amp;gt;fopen( )&amp;lt;/tt&amp;gt; with the special filename ''php://stdin'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;print &amp;quot;Type your message. Type '.' on a line by itself when you're done.\n&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
$fh = fopen('php://stdin','r') or die($php_errormsg);&lt;br /&gt;
$last_line = false;  $message = '';&lt;br /&gt;
while (! $last_line) {&lt;br /&gt;
    $next_line = fgets($fp,1024);&lt;br /&gt;
    if (&amp;quot;.\n&amp;quot; == $next_line) {&lt;br /&gt;
      $last_line = true;&lt;br /&gt;
    } else {&lt;br /&gt;
      $message .= $next_line;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
print &amp;quot;\nYour message is:\n$message\n&amp;quot;;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the Readline extension is installed, use &amp;lt;tt&amp;gt;readline( )&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;$last_line = false; $message = '';&lt;br /&gt;
while (! $last_line) {&lt;br /&gt;
    $next_line = readline();&lt;br /&gt;
    if ('.' == $next_line) {&lt;br /&gt;
        $last_line = true;&lt;br /&gt;
    } else {&lt;br /&gt;
        $message .= $next_line.&amp;quot;\n&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
print &amp;quot;\nYour message is:\n$message\n&amp;quot;;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
Once you get a file handle pointing to ''stdin'' with &amp;lt;tt&amp;gt;fopen( )&amp;lt;/tt&amp;gt;, you can use all the standard file-reading functions to process input (&amp;lt;tt&amp;gt;fread( )&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;fgets( )&amp;lt;/tt&amp;gt;, etc.) The solution uses &amp;lt;tt&amp;gt;fgets( )&amp;lt;/tt&amp;gt;, which returns input a line at a time. If you use &amp;lt;tt&amp;gt;fread( )&amp;lt;/tt&amp;gt;, the input still needs to be newline-terminated to make &amp;lt;tt&amp;gt;fread( )&amp;lt;/tt&amp;gt; return. For example, if you run:&lt;br /&gt;
&lt;br /&gt;
 $fh = fopen('php://stdin','r') or die($php_errormsg);&lt;br /&gt;
 $msg = fread($fh,4);&lt;br /&gt;
 print &amp;quot;[$msg]&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
And type in &amp;lt;tt&amp;gt;tomato&amp;lt;/tt&amp;gt; and then a newline, the output is &amp;lt;tt&amp;gt;[toma]&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;fread( )&amp;lt;/tt&amp;gt; grabs only four characters from ''stdin'', as directed, but still needs the newline as a signal to return from waiting for keyboard input.&lt;br /&gt;
&lt;br /&gt;
The Readline extension provides an interface to the GNU Readline library. The &amp;lt;tt&amp;gt;readline( )&amp;lt;/tt&amp;gt; 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:&lt;br /&gt;
&lt;br /&gt;
 $command_count = 1;&lt;br /&gt;
 while (true) {&lt;br /&gt;
     $line = readline(&amp;quot;[$command_count]--&amp;gt; &amp;quot;);&lt;br /&gt;
     readline_add_history($line);&lt;br /&gt;
     if (is_readable($line)) {&lt;br /&gt;
         print &amp;quot;$line is a readable file.\n&amp;quot;;&lt;br /&gt;
     }&lt;br /&gt;
     $command_count++;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
This example displays a prompt with an incrementing count before each line. Since each line is added to the readline history with &amp;lt;tt&amp;gt;readline_add_history( )&amp;lt;/tt&amp;gt; , pressing the up and down arrows at a prompt scrolls through the previously entered lines.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on &amp;lt;tt&amp;gt;fopen( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/fopen'', &amp;lt;tt&amp;gt;fgets( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/fgets'', &amp;lt;tt&amp;gt;fread( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/fread'', and the Readline extension at ''http://www.php.net/readline''; the Readline library at ''http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html''.&lt;br /&gt;
&lt;br /&gt;
== Reading Passwords ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You need to read a string from the command line without it being echoed as it's typed; for example, when entering passwords.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
On Unix systems, use ''/bin/stty'' to toggle echoing of typed characters:&lt;br /&gt;
&lt;br /&gt;
 // turn off echo&lt;br /&gt;
 `/bin/stty -echo`;&lt;br /&gt;
 &lt;br /&gt;
 // read password&lt;br /&gt;
 $password = readline();&lt;br /&gt;
 &lt;br /&gt;
 // turn echo back on&lt;br /&gt;
 `/bin/stty echo`;&lt;br /&gt;
&lt;br /&gt;
On Windows, use &amp;lt;tt&amp;gt;w32api_register_function( )&amp;lt;/tt&amp;gt; to import &amp;lt;tt&amp;gt;_getch( )&amp;lt;/tt&amp;gt; from ''msvcrt.dll'':&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;// load the w32api extension and register _getch()&lt;br /&gt;
dl('php_w32api.dll');&lt;br /&gt;
w32api_register_function('msvcrt.dll','_getch','int');&lt;br /&gt;
&lt;br /&gt;
while(true) {&lt;br /&gt;
    // get a character from the keyboard&lt;br /&gt;
    $c = chr(_getch());&lt;br /&gt;
    if ( &amp;quot;\r&amp;quot; == $c ||  &amp;quot;\n&amp;quot; == $c ) {&lt;br /&gt;
        // if it's a newline, break out of the loop, we've got our password&lt;br /&gt;
        break;&lt;br /&gt;
    } elseif (&amp;quot;\x08&amp;quot; == $c) {&lt;br /&gt;
        /* if it's a backspace, delete the previous char from $password */&lt;br /&gt;
        $password = substr_replace($password,'',-1,1);&lt;br /&gt;
    } elseif (&amp;quot;\x03&amp;quot; == $c) {&lt;br /&gt;
        // if it's Control-C, clear $password and break out of the loop&lt;br /&gt;
        $password = NULL;&lt;br /&gt;
        break;&lt;br /&gt;
    } else {&lt;br /&gt;
        // otherwise, add the character to the password&lt;br /&gt;
        $password .= $c;&lt;br /&gt;
    }&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
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 &amp;lt;tt&amp;gt;_getch( )&amp;lt;/tt&amp;gt; in the Microsoft C runtime library, ''msvcrt.dll''. The &amp;lt;tt&amp;gt;_getch( )&amp;lt;/tt&amp;gt; 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 &amp;lt;tt&amp;gt;chr( )&amp;lt;/tt&amp;gt; . 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 &amp;lt;tt&amp;gt;NULL&amp;lt;/tt&amp;gt; and break out of the loop. If none of these things are true, the character is concatenated to &amp;lt;tt&amp;gt;$password&amp;lt;/tt&amp;gt;. When you exit the loop, &amp;lt;tt&amp;gt;$password&amp;lt;/tt&amp;gt; holds the entered password.&lt;br /&gt;
&lt;br /&gt;
The following code displays &amp;lt;tt&amp;gt;Login&amp;lt;/tt&amp;gt;: and &amp;lt;tt&amp;gt;Password&amp;lt;/tt&amp;gt;: prompts, and compares the entered password to the corresponding encrypted password stored in ''/etc/passwd''. This requires that the system not use shadow passwords.&lt;br /&gt;
&lt;br /&gt;
 print &amp;quot;Login: &amp;quot;;&lt;br /&gt;
 $fh = fopen('php://stdin','r')   or die($php_errormsg);&lt;br /&gt;
 $username = rtrim(fgets($fh,64)) or die($php_errormsg);&lt;br /&gt;
 &lt;br /&gt;
 preg_match('/^[a-zA-Z0-9]+$/',$username) &lt;br /&gt;
     or die(&amp;quot;Invalid username: only letters and numbers allowed&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
 &lt;br /&gt;
 print 'Password: ';&lt;br /&gt;
 `/bin/stty -echo`;&lt;br /&gt;
 $password = rtrim(fgets($fh,64)) or die($php_errormsg);&lt;br /&gt;
 `/bin/stty echo`;&lt;br /&gt;
 print &amp;quot;\n&amp;quot;;&lt;br /&gt;
 &lt;br /&gt;
 // nothing more to read from the keyboard&lt;br /&gt;
 fclose($fh);&lt;br /&gt;
 &lt;br /&gt;
 // find corresponding line in /etc/passwd &lt;br /&gt;
 $fh = fopen('/etc/passwd','r')   or die($php_errormsg);&lt;br /&gt;
 $found_user = 0;&lt;br /&gt;
 while (! ($found_user || feof($fh))) {&lt;br /&gt;
     $passwd_line = fgets($fh,256);&lt;br /&gt;
     if (preg_match(&amp;quot;/^$username:/&amp;quot;,$passwd_line)) {&lt;br /&gt;
         $found_user = 1;&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
 fclose($fh);&lt;br /&gt;
 &lt;br /&gt;
 $found_user or die (&amp;quot;Can't find user \&amp;quot;$username\&amp;quot;&amp;quot;);&lt;br /&gt;
 &lt;br /&gt;
 // parse the correct line from /etc/passwd &lt;br /&gt;
 $passwd_parts = split(':',$passwd_line);&lt;br /&gt;
 &lt;br /&gt;
 /* encrypt the entered password and compare it to the password in&lt;br /&gt;
    /etc/passwd */&lt;br /&gt;
 $encrypted_password = crypt($password,&lt;br /&gt;
                             substr($passwd_parts[1],0,CRYPT_SALT_LENGTH));&lt;br /&gt;
 &lt;br /&gt;
 if ($encrypted_password == $passwd_parts[1]) {&lt;br /&gt;
     print &amp;quot;login successful&amp;quot;;&lt;br /&gt;
 } else {&lt;br /&gt;
     print &amp;quot;login unsuccessful&amp;quot;;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on &amp;lt;tt&amp;gt;readline( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/readline'', &amp;lt;tt&amp;gt;chr( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/chr'', on &amp;lt;tt&amp;gt;w32api_register_function( )&amp;lt;/tt&amp;gt; at ''http://www.php.net/w32api-register-function'', and on &amp;lt;tt&amp;gt;_getch( )&amp;lt;/tt&amp;gt; at ''http://msdn.microsoft.com/library/en-us/vccore98/HTML/_crt_ _getch.2c_._getche.asp''; on Unix, see your system's ''stty(1)'' manpage.&lt;br /&gt;
&lt;br /&gt;
== Displaying a GUI Widget in a Window ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to display a window with a GUI widget, such as a button, in it.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Create the window, create the widget, and then add the widget to the window:&lt;br /&gt;
&lt;br /&gt;
 // create the window&lt;br /&gt;
 $window = &amp;amp;new GtkWindow();&lt;br /&gt;
 &lt;br /&gt;
 // create the button and add it to the window&lt;br /&gt;
 $button = &amp;amp;new GTKButton('Click Me, Alice');&lt;br /&gt;
 $window-&amp;gt;add($button);&lt;br /&gt;
 &lt;br /&gt;
 // display the window&lt;br /&gt;
 $window-&amp;gt;show_all();&lt;br /&gt;
 &lt;br /&gt;
 // necessary so that the program exits properly&lt;br /&gt;
 function shutdown() { gtk::main_quit(); }&lt;br /&gt;
 $window-&amp;gt;connect('destroy','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // start GTK's signal handling loop&lt;br /&gt;
 gtk::main();&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
First, you create a window by instantiating a new &amp;lt;tt&amp;gt;GtkWindow&amp;lt;/tt&amp;gt; object. GTK objects must be created as references: &amp;lt;tt&amp;gt;&amp;amp;new GtkWindow( )&amp;lt;/tt&amp;gt;, not &amp;lt;tt&amp;gt;new GtkWindow( )&amp;lt;/tt&amp;gt;. You then create a new &amp;lt;tt&amp;gt;GtkButton&amp;lt;/tt&amp;gt; object with a label &amp;quot;Click Me, Alice&amp;quot;. Passing &amp;lt;tt&amp;gt;$button&amp;lt;/tt&amp;gt; to the window's &amp;lt;tt&amp;gt;add( )&amp;lt;/tt&amp;gt; method adds the button to the window. The &amp;lt;tt&amp;gt;show_all( )&amp;lt;/tt&amp;gt; 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 &amp;lt;tt&amp;gt;shutdown( )&amp;lt;/tt&amp;gt; function is a callback, as is explained later in [[PHP Cookbook/Client-Side PHP#Responding to User Actions|Recipe 20.8]].&lt;br /&gt;
&lt;br /&gt;
The last line is necessary in all PHP-GTK programs. Calling &amp;lt;tt&amp;gt;gtk::main( )&amp;lt;/tt&amp;gt; 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 &amp;lt;tt&amp;gt;destroy&amp;lt;/tt&amp;gt; signal. When the user closes the program's main window, the &amp;lt;tt&amp;gt;destroy&amp;lt;/tt&amp;gt; signal is emitted, and &amp;lt;tt&amp;gt;gtk::main_quit( )&amp;lt;/tt&amp;gt; is called. This function exits the program.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on the &amp;lt;tt&amp;gt;GtkWindow&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtkwindow.php'', on &amp;lt;tt&amp;gt;GTKContainer::add( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.gtkcontainer.method.add.php'', on &amp;lt;tt&amp;gt;GtkWidget::show_all( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.gtkwidget.method.show_all.php'', on the &amp;lt;tt&amp;gt;GtkButton&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtkbutton.php'', on &amp;lt;tt&amp;gt;gtk::main_quit( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.method.main_quit.php'', on and &amp;lt;tt&amp;gt;gtk::main( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.method.main.php''; the tutorial at ''http://gtk.php.net/manual/en/tutorials.hellow.php'' is a helpful introduction to basic GTK programming.&lt;br /&gt;
&lt;br /&gt;
== Displaying Multiple GUI Widgets in a Window ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to display more than one widget in a window.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Add all of the widgets in a container, and then add the container in the window:&lt;br /&gt;
&lt;br /&gt;
 // create the window&lt;br /&gt;
 $window = &amp;amp;new GtkWindow();&lt;br /&gt;
 &lt;br /&gt;
 // create the container - GtkVBox aligns widgets vertically&lt;br /&gt;
 $container = &amp;amp;new GtkVBox();&lt;br /&gt;
 &lt;br /&gt;
 // create a text entry widget and add it to the container&lt;br /&gt;
 $text_entry = &amp;amp;new GtkEntry();&lt;br /&gt;
 $container-&amp;gt;pack_start($text_entry);&lt;br /&gt;
 &lt;br /&gt;
 // create a button and add it to the container&lt;br /&gt;
 $a_button = &amp;amp;new GtkButton('Abort');&lt;br /&gt;
 $container-&amp;gt;pack_start($a_button);&lt;br /&gt;
 &lt;br /&gt;
 // create another button and add it to the container&lt;br /&gt;
 $r_button = &amp;amp;new GtkButton('Retry');&lt;br /&gt;
 $container-&amp;gt;pack_start($r_button);&lt;br /&gt;
 &lt;br /&gt;
 // create yet another button and add it to the container&lt;br /&gt;
 $f_button = &amp;amp;new GtkButton('Fail');&lt;br /&gt;
 $container-&amp;gt;pack_start($f_button);&lt;br /&gt;
 &lt;br /&gt;
 // add the container to the window&lt;br /&gt;
 $window-&amp;gt;add($container);&lt;br /&gt;
 &lt;br /&gt;
 // display the window&lt;br /&gt;
 $window-&amp;gt;show_all();&lt;br /&gt;
 &lt;br /&gt;
 // necessary so that the program exits properly&lt;br /&gt;
 function shutdown() { gtk::main_quit(); }&lt;br /&gt;
 $window-&amp;gt;connect('destroy','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // start GTK's signal handling loop&lt;br /&gt;
 gtk::main();&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
In the Solution, widgets are added to a &amp;lt;tt&amp;gt;GtkVBox&amp;lt;/tt&amp;gt; container, which aligns the child widgets vertically, as shown in [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-FIG-1|Figure 20-1]]. The &amp;lt;tt&amp;gt;add( )&amp;lt;/tt&amp;gt; method adds widgets to the &amp;lt;tt&amp;gt;GtkVBox&amp;lt;/tt&amp;gt;, but &amp;lt;tt&amp;gt;pack_start( )&amp;lt;/tt&amp;gt; is used instead so that the size of the container is automatically updated with each new widget.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-20-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 20-1. Widgets in a GtkVBox'''&lt;br /&gt;
&lt;br /&gt;
[[Image:PHP Cookbook_I_20_tt1159.png|Widgets in a GtkVBox]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;GtkHBox&amp;lt;/tt&amp;gt; is similar to &amp;lt;tt&amp;gt;GtkVBox&amp;lt;/tt&amp;gt;. It aligns its child widgets horizontally instead of vertically. [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-FIG-2|Figure 20-2]] shows the four widgets from the Solution in a &amp;lt;tt&amp;gt;CtkHBox&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-20-FIG-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 20-2. Widgets in a GtkHBox'''&lt;br /&gt;
&lt;br /&gt;
[[Image:PHP Cookbook_I_20_tt1160.png|Widgets in a GtkHBox]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;GtkTable&amp;lt;/tt&amp;gt; is a more flexible layout container; it aligns its child elements on a grid:&lt;br /&gt;
&lt;br /&gt;
 // create the window&lt;br /&gt;
 $window = &amp;amp;new GtkWindow();&lt;br /&gt;
 &lt;br /&gt;
 // create the container with 3 rows and 2 columns&lt;br /&gt;
 $container = &amp;amp;new GtkTable(3,2);&lt;br /&gt;
 &lt;br /&gt;
 // create a text entry widget and add it to the container&lt;br /&gt;
 $text_entry = &amp;amp;new GtkEntry();&lt;br /&gt;
 $container-&amp;gt;attach($text_entry,0,2,0,1);&lt;br /&gt;
 &lt;br /&gt;
 // create a button and add it to the container&lt;br /&gt;
 $a_button = &amp;amp;new GtkButton('Abort');&lt;br /&gt;
 $container-&amp;gt;attach($a_button,0,1,1,2);&lt;br /&gt;
 &lt;br /&gt;
 // create another button and add it to the container&lt;br /&gt;
 $r_button = &amp;amp;new GtkButton('Retry');&lt;br /&gt;
 $container-&amp;gt;attach($r_button,1,2,1,2);&lt;br /&gt;
 &lt;br /&gt;
 // create yet another button and add it to the container&lt;br /&gt;
 $f_button = &amp;amp;new GtkButton('Fail');&lt;br /&gt;
 $container-&amp;gt;attach($f_button,0,2,2,3);&lt;br /&gt;
 &lt;br /&gt;
 // add the container to the window&lt;br /&gt;
 $window-&amp;gt;add($container);&lt;br /&gt;
 &lt;br /&gt;
 // display the window&lt;br /&gt;
 $window-&amp;gt;show_all();&lt;br /&gt;
 &lt;br /&gt;
 // necessary so that the program exits properly&lt;br /&gt;
 function shutdown() { gtk::main_quit(); }&lt;br /&gt;
 $window-&amp;gt;connect('destroy','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // start GTK's signal handling loop&lt;br /&gt;
 gtk::main();&lt;br /&gt;
&lt;br /&gt;
Widgets are added to a &amp;lt;tt&amp;gt;GtkTable&amp;lt;/tt&amp;gt; container with the &amp;lt;tt&amp;gt;attach( )&amp;lt;/tt&amp;gt; method. The first argument to &amp;lt;tt&amp;gt;attach( )&amp;lt;/tt&amp;gt; is the widget to add, and the next four arguments describe where in the grid to put the widget. The second and third arguments are the starting and ending columns for the widget. The fourth and fifth arguments are the starting and ending rows for the widget. For example:&lt;br /&gt;
&lt;br /&gt;
 $container-&amp;gt;attach($text_entry,0,2,0,1) &lt;br /&gt;
&lt;br /&gt;
means that the text-entry widget starts in column zero and ends in column two, spanning two columns. It starts at row zero and ends at row one, so it spans only one row. Rows and columns are numbered beginning with zero. The text entry and button widgets aligned in a &amp;lt;tt&amp;gt;GtkTable&amp;lt;/tt&amp;gt; container are shown in [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-FIG-3|Figure 20-3]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-20-FIG-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 20-3. Widgets in a GtkTable'''&lt;br /&gt;
&lt;br /&gt;
[[Image:PHP Cookbook_I_20_tt1163.png|Widgets in a GtkTable]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on containers at ''http://gtk.php.net/manual/en/gtk.containers.whatare.php'', the &amp;lt;tt&amp;gt;GtkVBox&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtkvbox.php'', the &amp;lt;tt&amp;gt;GtkHBox&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtkhbox.php'', &amp;lt;tt&amp;gt;GtkBox::pack_start( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.gtkbox.method.pack_start.php'', the &amp;lt;tt&amp;gt;GtkTable&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtktable.php'', and &amp;lt;tt&amp;gt;GtkTable::attach( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.gtktable.method.attach.php'' .&lt;br /&gt;
&lt;br /&gt;
== Responding to User Actions ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Write a callback function and then associate the callback function with a signal using the &amp;lt;tt&amp;gt;connect( )&amp;lt;/tt&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
 // create the window&lt;br /&gt;
 $window = &amp;amp;new GtkWindow();&lt;br /&gt;
 &lt;br /&gt;
 // create a button with the current time as its label&lt;br /&gt;
 $button = &amp;amp;new GtkButton(strftime('%c'));&lt;br /&gt;
 &lt;br /&gt;
 // set the update_time() function as the callback for the &amp;quot;clicked&amp;quot; signal&lt;br /&gt;
 $button-&amp;gt;connect('clicked','update_time');&lt;br /&gt;
 &lt;br /&gt;
 function update_time($b) {&lt;br /&gt;
     // the button's text is in a child of the button - a label widget&lt;br /&gt;
     $b_label = $b-&amp;gt;child;&lt;br /&gt;
     // set the label text to the current time&lt;br /&gt;
     $b_label-&amp;gt;set_text(strftime('%c'));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // add the button to the window&lt;br /&gt;
 $window-&amp;gt;add($button);&lt;br /&gt;
 &lt;br /&gt;
 // display the window&lt;br /&gt;
 $window-&amp;gt;show_all();&lt;br /&gt;
 &lt;br /&gt;
 // necessary so that the program exits properly&lt;br /&gt;
 function shutdown() { gtk::main_quit(); }&lt;br /&gt;
 $window-&amp;gt;connect('destroy','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // start GTK's signal handling loop&lt;br /&gt;
 gtk::main();&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
The code in the Solution displays a window with a button in it. On the button is the time, rendered by &amp;lt;tt&amp;gt;strftime('%c')&amp;lt;/tt&amp;gt; . When the button is clicked, its label is updated with the current time.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;update_time( )&amp;lt;/tt&amp;gt; function is called each time the button is clicked because &amp;lt;tt&amp;gt;$button-&amp;gt;connect('clicked','update_time')&amp;lt;/tt&amp;gt; makes &amp;lt;tt&amp;gt;update_time( )&amp;lt;/tt&amp;gt; the callback function associated with the button's &amp;lt;tt&amp;gt;clicked&amp;lt;/tt&amp;gt; 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 &amp;lt;tt&amp;gt;$button&amp;lt;/tt&amp;gt; is passed to &amp;lt;tt&amp;gt;update_time( )&amp;lt;/tt&amp;gt;. You tell &amp;lt;tt&amp;gt;connect( )&amp;lt;/tt&amp;gt; to pass additional arguments to the callback by passing them to &amp;lt;tt&amp;gt;connect( )&amp;lt;/tt&amp;gt; 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:&lt;br /&gt;
&lt;br /&gt;
 // create the window&lt;br /&gt;
 $window = &amp;amp;new GtkWindow();&lt;br /&gt;
 &lt;br /&gt;
 // create a container for the label and the button&lt;br /&gt;
 $container = &amp;amp;new GtkVBox();&lt;br /&gt;
 &lt;br /&gt;
 // create a label showing the time&lt;br /&gt;
 $label = &amp;amp;new GtkLabel(strftime('%c'));&lt;br /&gt;
 &lt;br /&gt;
 // add the label to the container&lt;br /&gt;
 $container-&amp;gt;pack_start($label);&lt;br /&gt;
 &lt;br /&gt;
 // create a button&lt;br /&gt;
 $button = &amp;amp;new GtkButton('Update Time');&lt;br /&gt;
 &lt;br /&gt;
 /* set the update_time() function as the callback for the &amp;quot;clicked&amp;quot; signal&lt;br /&gt;
    and pass $label to the callback */&lt;br /&gt;
 $button-&amp;gt;connect('clicked','update_time',$label);&lt;br /&gt;
 &lt;br /&gt;
 function update_time($b,$lb) {&lt;br /&gt;
     $lb-&amp;gt;set_text(strftime('%c'));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // add the button to the container&lt;br /&gt;
 $container-&amp;gt;pack_start($button);&lt;br /&gt;
 &lt;br /&gt;
 // add the container to the window&lt;br /&gt;
 $window-&amp;gt;add($container);&lt;br /&gt;
 &lt;br /&gt;
 // display the window&lt;br /&gt;
 $window-&amp;gt;show_all();&lt;br /&gt;
 &lt;br /&gt;
 // necessary so that the program exits properly&lt;br /&gt;
 function shutdown() { gtk::main_quit(); }&lt;br /&gt;
 $window-&amp;gt;connect('destroy','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // start GTK's signal handling loop&lt;br /&gt;
 gtk::main();&lt;br /&gt;
&lt;br /&gt;
Because &amp;lt;tt&amp;gt;$label&amp;lt;/tt&amp;gt; is on the list of arguments passed to &amp;lt;tt&amp;gt;$button-&amp;gt;connect( )&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;$label&amp;lt;/tt&amp;gt; is passed to &amp;lt;tt&amp;gt;update_time( )&amp;lt;/tt&amp;gt;. Calling &amp;lt;tt&amp;gt;set_text( )&amp;lt;/tt&amp;gt; on &amp;lt;tt&amp;gt;$label&amp;lt;/tt&amp;gt; updates the text displayed in the label.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on signals and callbacks at ''http://gtk.php.net/manual/en/gtk.signals.php'', on &amp;lt;tt&amp;gt;GtkObject::connect( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.gtkobject.method.connect.php'', and on &amp;lt;tt&amp;gt;GtkButton&amp;lt;/tt&amp;gt;'s &amp;lt;tt&amp;gt;clicked&amp;lt;/tt&amp;gt; signal at ''http://gtk.php.net/manual/en/gtk.gtkbutton.signal.clicked.php''.&lt;br /&gt;
&lt;br /&gt;
== Displaying Menus ==&lt;br /&gt;
&lt;br /&gt;
=== Problem ===&lt;br /&gt;
&lt;br /&gt;
You want to display a menu bar at the top of a GTK window.&lt;br /&gt;
&lt;br /&gt;
=== Solution ===&lt;br /&gt;
&lt;br /&gt;
Create a &amp;lt;tt&amp;gt;GtkMenu&amp;lt;/tt&amp;gt; . Create individual &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt; objects for each menu item you want to display and add each menu item to the &amp;lt;tt&amp;gt;GtkMenu&amp;lt;/tt&amp;gt; with &amp;lt;tt&amp;gt;append( )&amp;lt;/tt&amp;gt;. Then, create a root menu &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt; with the label that should appear in the menu bar (e.g., &amp;quot;File&amp;quot; or &amp;quot;Options&amp;quot;). Add the menu to the root menu with &amp;lt;tt&amp;gt;set_submenu( )&amp;lt;/tt&amp;gt; . Create a &amp;lt;tt&amp;gt;GtkMenuBar&amp;lt;/tt&amp;gt; and add the root menu to the menu bar with &amp;lt;tt&amp;gt;append( )&amp;lt;/tt&amp;gt;. Finally, add the menu bar to the window:&lt;br /&gt;
&lt;br /&gt;
 // create the window&lt;br /&gt;
 $window = &amp;amp;new GtkWindow();&lt;br /&gt;
 &lt;br /&gt;
 // create a menu&lt;br /&gt;
 $menu = &amp;amp;new GtkMenu();&lt;br /&gt;
 &lt;br /&gt;
 // create a menu item and add it to the menu&lt;br /&gt;
 $menu_item_1 = &amp;amp;new GtkMenuItem('Open');&lt;br /&gt;
 $menu-&amp;gt;append($menu_item_1);&lt;br /&gt;
 &lt;br /&gt;
 // create another menu item and add it to the menu&lt;br /&gt;
 $menu_item_2 = &amp;amp;new GtkMenuItem('Close');&lt;br /&gt;
 $menu-&amp;gt;append($menu_item_2);&lt;br /&gt;
 &lt;br /&gt;
 // create yet another menu item and add it to the menu&lt;br /&gt;
 $menu_item_2 = &amp;amp;new GtkMenuItem('Save');&lt;br /&gt;
 $menu-&amp;gt;append($menu_item_2);&lt;br /&gt;
 &lt;br /&gt;
 // create a root menu and add the existing menu to it&lt;br /&gt;
 $root_menu = &amp;amp;new GtkMenuItem('File');&lt;br /&gt;
 $root_menu-&amp;gt;set_submenu($menu);&lt;br /&gt;
 &lt;br /&gt;
 // create a menu bar and add the root menu to it&lt;br /&gt;
 $menu_bar = &amp;amp;new GtkMenuBar();&lt;br /&gt;
 $menu_bar-&amp;gt;append($root_menu);&lt;br /&gt;
 &lt;br /&gt;
 // add the menu bar to the window&lt;br /&gt;
 $window-&amp;gt;add($menu_bar);&lt;br /&gt;
 &lt;br /&gt;
 // display the window&lt;br /&gt;
 $window-&amp;gt;show_all();&lt;br /&gt;
 &lt;br /&gt;
 // necessary so that the program exits properly&lt;br /&gt;
 function shutdown() { gtk::main_quit(); }&lt;br /&gt;
 $window-&amp;gt;connect('destroy','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // start GTK's signal handling loop&lt;br /&gt;
 gtk::main();&lt;br /&gt;
&lt;br /&gt;
=== Discussion ===&lt;br /&gt;
&lt;br /&gt;
A menu involves a hierarchy of quite a few objects. The &amp;lt;tt&amp;gt;GtkWindow&amp;lt;/tt&amp;gt; (or another container) holds the &amp;lt;tt&amp;gt;GtkMenuBar&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;GtkMenuBar&amp;lt;/tt&amp;gt; holds a &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt; for each top-level menu in the menu bar (e.g., &amp;quot;File,&amp;quot; &amp;quot;Options,&amp;quot; or &amp;quot;Help&amp;quot;). Each top-level &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt; has a &amp;lt;tt&amp;gt;GtkMenu&amp;lt;/tt&amp;gt; as a submenu. That submenu contains each &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt; that should appear under the top-level menu.&lt;br /&gt;
&lt;br /&gt;
As with any GTK widget, a &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt; object can have callbacks that handle signals. When a menu item is selected, it triggers the &amp;lt;tt&amp;gt;activate&amp;lt;/tt&amp;gt; signal. To take action when a menu item is selected, connect its &amp;lt;tt&amp;gt;activate&amp;lt;/tt&amp;gt; signal to a callback. Here's a version of the button-and-label time display from [[PHP Cookbook/Client-Side PHP#Responding to User Actions|Recipe 20.8]] with two menu items: &amp;quot;Update,&amp;quot; which updates the time in the label, and &amp;quot;Quit,&amp;quot; which quits the program:&lt;br /&gt;
&lt;br /&gt;
 // create the window&lt;br /&gt;
 $window = &amp;amp;new GtkWindow();&lt;br /&gt;
 &lt;br /&gt;
 // create a container for the label and the button&lt;br /&gt;
 $container = &amp;amp;new GtkVBox();&lt;br /&gt;
 &lt;br /&gt;
 // create a menu&lt;br /&gt;
 $menu = &amp;amp;new GtkMenu();&lt;br /&gt;
 &lt;br /&gt;
 // create a menu item and add it to the menu&lt;br /&gt;
 $menu_item_1 = &amp;amp;new GtkMenuItem('Update');&lt;br /&gt;
 $menu-&amp;gt;append($menu_item_1);&lt;br /&gt;
 &lt;br /&gt;
 // create another menu item and add it to the menu&lt;br /&gt;
 $menu_item_2 = &amp;amp;new GtkMenuItem('Quit');&lt;br /&gt;
 $menu-&amp;gt;append($menu_item_2);&lt;br /&gt;
 &lt;br /&gt;
 // create a root menu and add the existing menu to it&lt;br /&gt;
 $root_menu = &amp;amp;new GtkMenuItem('File');&lt;br /&gt;
 $root_menu-&amp;gt;set_submenu($menu);&lt;br /&gt;
 &lt;br /&gt;
 // create a menu bar and add the root menu to it&lt;br /&gt;
 $menu_bar = &amp;amp;new GtkMenuBar();&lt;br /&gt;
 $menu_bar-&amp;gt;append($root_menu);&lt;br /&gt;
 &lt;br /&gt;
 // add the menu to the container&lt;br /&gt;
 $container-&amp;gt;add($menu_bar);&lt;br /&gt;
 &lt;br /&gt;
 // create a label showing the time&lt;br /&gt;
 $label = &amp;amp;new GtkLabel(strftime('%c'));&lt;br /&gt;
 &lt;br /&gt;
 // add the label to the container&lt;br /&gt;
 $container-&amp;gt;pack_start($label);&lt;br /&gt;
 &lt;br /&gt;
 // create a button&lt;br /&gt;
 $button = &amp;amp;new GtkButton('Update Time');&lt;br /&gt;
 &lt;br /&gt;
 /* set the update_time() function as the callback for the &amp;quot;clicked&amp;quot; signal&lt;br /&gt;
    and pass $label to the callback */&lt;br /&gt;
 $button-&amp;gt;connect('clicked','update_time',$label);&lt;br /&gt;
 &lt;br /&gt;
 function update_time($b,$lb) {&lt;br /&gt;
     $lb-&amp;gt;set_text(strftime('%c'));&lt;br /&gt;
 }&lt;br /&gt;
 &lt;br /&gt;
 // add the button to the container&lt;br /&gt;
 $container-&amp;gt;pack_start($button);&lt;br /&gt;
 &lt;br /&gt;
 // when the Update menu item is selected, call update_time()&lt;br /&gt;
 $menu_item_1-&amp;gt;connect('activate','update_time',$label);&lt;br /&gt;
 &lt;br /&gt;
 // when the Quit menu item is selected, quit&lt;br /&gt;
 $menu_item_2-&amp;gt;connect('activate','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // add the container to the window&lt;br /&gt;
 $window-&amp;gt;add($container);&lt;br /&gt;
 &lt;br /&gt;
 // display the window&lt;br /&gt;
 $window-&amp;gt;show_all();&lt;br /&gt;
 &lt;br /&gt;
 // necessary so that the program exits properly&lt;br /&gt;
 function shutdown() { gtk::main_quit(); }&lt;br /&gt;
 $window-&amp;gt;connect('destroy','shutdown');&lt;br /&gt;
 &lt;br /&gt;
 // start GTK's signal handling loop&lt;br /&gt;
 gtk::main();&lt;br /&gt;
&lt;br /&gt;
Callbacks are connected to the menu items with their &amp;lt;tt&amp;gt;connect( )&amp;lt;/tt&amp;gt; methods. The callbacks are connected to the &amp;lt;tt&amp;gt;activate&amp;lt;/tt&amp;gt; signals towards the end of the code because the call to &amp;lt;tt&amp;gt;$menu_item_1-&amp;gt;connect( )&amp;lt;/tt&amp;gt; passes &amp;lt;tt&amp;gt;$label&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;update_time( )&amp;lt;/tt&amp;gt; . For &amp;lt;tt&amp;gt;$label&amp;lt;/tt&amp;gt; to be successfully passed to &amp;lt;tt&amp;gt;update_time( )&amp;lt;/tt&amp;gt; while the program is running, &amp;lt;tt&amp;gt;connect( )&amp;lt;/tt&amp;gt; has to be called after &amp;lt;tt&amp;gt;$label&amp;lt;/tt&amp;gt; is instantiated.&lt;br /&gt;
&lt;br /&gt;
=== See Also ===&lt;br /&gt;
&lt;br /&gt;
Documentation on the &amp;lt;tt&amp;gt;GtkMenu&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtkmenu.php'', &amp;lt;tt&amp;gt;GtkMenuShell::append( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.gtkmenushell.method. append.php'', the &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtkmenuitem. php'', &amp;lt;tt&amp;gt;GtkMenuItem::set_submenu( )&amp;lt;/tt&amp;gt; at ''http://gtk.php.net/manual/en/gtk.gtkmenuitem. method.set_submenu.php'', &amp;lt;tt&amp;gt;GtkMenuItem&amp;lt;/tt&amp;gt;'s &amp;lt;tt&amp;gt;activate&amp;lt;/tt&amp;gt; signal at ''http://gtk.php.net/manual/en/gtk.gtkmenuitem.signal.activate.php'', and the &amp;lt;tt&amp;gt;GtkMenuBar&amp;lt;/tt&amp;gt; class at ''http://gtk.php.net/manual/en/gtk.gtkmenubar.php''.&lt;br /&gt;
&lt;br /&gt;
== Program: Command Shell ==&lt;br /&gt;
&lt;br /&gt;
The ''command-shell.php'' program shown in [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-EX-1|Example 20-1]] provides a shell-like prompt to let you execute PHP code interactively. It reads in lines using &amp;lt;tt&amp;gt;readline( )&amp;lt;/tt&amp;gt; and then runs them with &amp;lt;tt&amp;gt;eval( )&amp;lt;/tt&amp;gt;. By default, it runs each line after it's typed in. In multiline mode (specified with &amp;lt;tt&amp;gt;-m&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;--multiline&amp;lt;/tt&amp;gt;), however, it keeps reading lines until you enter . on a line by itself; it then runs the accumulated code.&lt;br /&gt;
&lt;br /&gt;
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.&lt;br /&gt;
&lt;br /&gt;
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:&lt;br /&gt;
&lt;br /&gt;
 % '''php -q command-shell.php'''&lt;br /&gt;
 [1]&amp;gt; '''require 'DB.php';'''&lt;br /&gt;
 &lt;br /&gt;
 [2]&amp;gt; '''$dbh = DB::connect('mysql://user:pwd@localhost/phpc');'''&lt;br /&gt;
 &lt;br /&gt;
 [3]&amp;gt; '''print_r($dbh-&amp;gt;getAssoc('SELECT sign,planet,start_day FROM zodiac WHERE element '''&lt;br /&gt;
             '''LIKE &amp;quot;water&amp;quot;'));'''&lt;br /&gt;
 Array&lt;br /&gt;
 (&lt;br /&gt;
     [Cancer] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; Moon&lt;br /&gt;
             [1] =&amp;gt; 22&lt;br /&gt;
         )&lt;br /&gt;
     [Scorpio] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; Mars&lt;br /&gt;
             [1] =&amp;gt; 24&lt;br /&gt;
         )&lt;br /&gt;
     [Pisces] =&amp;gt; Array&lt;br /&gt;
         (&lt;br /&gt;
             [0] =&amp;gt; Neptune&lt;br /&gt;
             [1] =&amp;gt; 19&lt;br /&gt;
         )&lt;br /&gt;
 )&lt;br /&gt;
&lt;br /&gt;
The code for ''command-shell.php'' is in [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-EX-1|Example 20-1]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-20-EX-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 20-1. command-shell.php'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
// Load the readline library&lt;br /&gt;
if (! function_exists('readline')) {&lt;br /&gt;
    dl('readline.'. (((strtoupper(substr(PHP_OS,0,3))) == 'WIN')?'dll':'so'))&lt;br /&gt;
        or die(&amp;quot;Readline library required\n&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Load the Console_Getopt class&lt;br /&gt;
require 'Console/Getopt.php';&lt;br /&gt;
&lt;br /&gt;
$o = new Console_Getopt;&lt;br /&gt;
$opts = $o-&amp;gt;getopt($o-&amp;gt;readPHPArgv(),'hm',array('help','multiline'));&lt;br /&gt;
&lt;br /&gt;
// Quit with a usage message if the arguments are bad&lt;br /&gt;
if (PEAR::isError($opts)) {&lt;br /&gt;
    print $opts-&amp;gt;getMessage();&lt;br /&gt;
    print &amp;quot;\n&amp;quot;;&lt;br /&gt;
    usage();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// default is to evaluate each command as it's entered&lt;br /&gt;
$multiline = false;&lt;br /&gt;
&lt;br /&gt;
foreach ($opts[0] as $opt) {&lt;br /&gt;
    // remove any leading -s&lt;br /&gt;
    $opt[0] = preg_replace('/^-+/','',$opt[0]);&lt;br /&gt;
&lt;br /&gt;
    // check the first character of the argument&lt;br /&gt;
    switch($opt[0][0]) {&lt;br /&gt;
    case 'h':&lt;br /&gt;
        // display help&lt;br /&gt;
        usage();&lt;br /&gt;
        break;&lt;br /&gt;
    case 'm':&lt;br /&gt;
        $multiline = true;&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// set up error display&lt;br /&gt;
ini_set('display_errors',false);&lt;br /&gt;
ini_set('log_errors',true);&lt;br /&gt;
&lt;br /&gt;
// build readline completion table&lt;br /&gt;
$functions = get_defined_functions();&lt;br /&gt;
foreach ($functions['internal'] as $k =&amp;gt; $v) {&lt;br /&gt;
    $functions['internal'][$k] = &amp;quot;$v(&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
function function_list($line) {&lt;br /&gt;
    return $GLOBALS['functions']['internal'];&lt;br /&gt;
}&lt;br /&gt;
readline_completion_function('function_list');&lt;br /&gt;
&lt;br /&gt;
$cmd = '';&lt;br /&gt;
$cmd_count = 1;&lt;br /&gt;
&lt;br /&gt;
while (true) {&lt;br /&gt;
    // get a line of input from the user&lt;br /&gt;
    $s = readline(&amp;quot;[$cmd_count]&amp;gt; &amp;quot;);&lt;br /&gt;
    // add it to the command history&lt;br /&gt;
    readline_add_history($s);&lt;br /&gt;
    // if we're in multiline mode:&lt;br /&gt;
    if ($multiline) {&lt;br /&gt;
        // if just a &amp;quot;.&amp;quot; has been entered&lt;br /&gt;
        if ('.' == rtrim($s)) {&lt;br /&gt;
            // eval() the code&lt;br /&gt;
            eval($cmd);&lt;br /&gt;
            // clear out the accumulated code&lt;br /&gt;
            $cmd = '';&lt;br /&gt;
            // increment the command count&lt;br /&gt;
            $cmd_count++;&lt;br /&gt;
            // start the next prompt on a new line&lt;br /&gt;
            print &amp;quot;\n&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
            /* otherwise, add the new line to the accumulated code&lt;br /&gt;
               tacking on a newline prevents //-style comments from&lt;br /&gt;
               commenting out the rest of the lines entered&lt;br /&gt;
            */&lt;br /&gt;
            $cmd .= $s.&amp;quot;\n&amp;quot;;;&lt;br /&gt;
        }&lt;br /&gt;
    } else {&lt;br /&gt;
        // if we're not in multiline mode, eval() the line&lt;br /&gt;
        eval($s);&lt;br /&gt;
        // increment the command count&lt;br /&gt;
        $cmd_count++;&lt;br /&gt;
        // start the next prompt in a new line&lt;br /&gt;
        print &amp;quot;\n&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// display helpful usage information&lt;br /&gt;
function usage() {&lt;br /&gt;
    $my_name = $_SERVER['argv'][0];&lt;br /&gt;
&lt;br /&gt;
    print&amp;lt;&amp;lt;&amp;lt;_USAGE_&lt;br /&gt;
Usage: $my_name [-h|--help] [-m|--multiline]&lt;br /&gt;
&lt;br /&gt;
  -h, --help: display this help&lt;br /&gt;
  -m, --multiline: execute accumulated code when &amp;quot;.&amp;quot; is entered&lt;br /&gt;
                   by itself on a line. The default is to execute&lt;br /&gt;
                   each line after it is entered.    &lt;br /&gt;
&lt;br /&gt;
_USAGE_;&lt;br /&gt;
    exit(-1);&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Program: Displaying Weather Conditions ==&lt;br /&gt;
&lt;br /&gt;
The ''gtk-weather.php'' program shown in [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-EX-2|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.&lt;br /&gt;
&lt;br /&gt;
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 [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-FIG-4|Figure 20-4]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-20-FIG-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 20-4. Searching for weather stations'''&lt;br /&gt;
&lt;br /&gt;
[[Image:PHP Cookbook_I_20_tt1169.png|Searching for weather stations]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
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 [[PHP Cookbook/Client-Side PHP#phpckbk-CHP-20-FIG-5|Figure 20-5]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-20-FIG-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 20-5. Added weather stations'''&lt;br /&gt;
&lt;br /&gt;
[[Image:PHP Cookbook_I_20_tt1170.png|Added weather stations]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The web service this program uses is called ''GlobalWeather'' ; look for more information about it at ''http://www.capescience.com/webservices/globalweather/index.shtml''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;phpckbk-CHP-20-EX-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 20-2. gtk-weather.php'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;// Load the GTK extension&lt;br /&gt;
dl('php_gtk.'. (((strtoupper(substr(PHP_OS,0,3))) == 'WIN')?'dll':'so'));&lt;br /&gt;
&lt;br /&gt;
// Load the SOAP client class&lt;br /&gt;
require 'SOAP/Client.php';&lt;br /&gt;
&lt;br /&gt;
// Create the main window and set its title and size&lt;br /&gt;
$window = &amp;amp;new GtkWindow();&lt;br /&gt;
$window-&amp;gt;set_title('PHP Cookbook GTK Demo');&lt;br /&gt;
$window-&amp;gt;set_default_size(500,200);&lt;br /&gt;
&lt;br /&gt;
// The main layout container for the window is a VBox&lt;br /&gt;
$vbox = &amp;amp;new GtkVBox();&lt;br /&gt;
$window-&amp;gt;add($vbox);&lt;br /&gt;
&lt;br /&gt;
// Create a GtkAccelGroup to hold keyboard accelerators&lt;br /&gt;
$accelgroup = &amp;amp;new GtkAccelGroup();&lt;br /&gt;
$window-&amp;gt;add_accel_group($accelgroup);&lt;br /&gt;
&lt;br /&gt;
// Build the menu, starting with the GtkMenuBar. The arguments to &lt;br /&gt;
// pack_start() prevent the menu bar from expanding if the window does.&lt;br /&gt;
$menubar = &amp;amp;new GtkMenuBar();&lt;br /&gt;
$vbox-&amp;gt;pack_start($menubar, false, false);&lt;br /&gt;
&lt;br /&gt;
// Create the &amp;quot;File&amp;quot; menu and its keyboard accelerator&lt;br /&gt;
$menu_file_item = &amp;amp;new GtkMenuItem('_File');&lt;br /&gt;
$menu_file_item_label = $menu_file_item-&amp;gt;child;&lt;br /&gt;
$menu_file_item-&amp;gt;add_accelerator('activate',$accelgroup,&lt;br /&gt;
                                 $menu_file_item_label-&amp;gt;parse_uline('_File'),&lt;br /&gt;
                                 GDK_MOD1_MASK,0);&lt;br /&gt;
// Add the &amp;quot;File&amp;quot; menu to the menu bar&lt;br /&gt;
$menubar-&amp;gt;add($menu_file_item);&lt;br /&gt;
&lt;br /&gt;
// Create the submenu for the options under &amp;quot;File&amp;quot;&lt;br /&gt;
$menu_file_submenu = &amp;amp;new GtkMenu();&lt;br /&gt;
$menu_file_item-&amp;gt;set_submenu($menu_file_submenu);&lt;br /&gt;
&lt;br /&gt;
// Create the &amp;quot;Quit&amp;quot; option under &amp;quot;File&amp;quot; and its accelerator&lt;br /&gt;
// GDK_MOD1_MASK means that the accelerator is Alt-Q, not Q&lt;br /&gt;
// GTK_ACCEL_VISIBLE means that the accelerator is displayed in the menu&lt;br /&gt;
$menu_file_choices_quit = &amp;amp;new GtkMenuItem('_Quit');&lt;br /&gt;
$menu_file_choices_quit_label = $menu_file_choices_quit-&amp;gt;child;&lt;br /&gt;
$menu_file_choices_quit-&amp;gt;add_accelerator('activate',$accelgroup,&lt;br /&gt;
    $menu_file_choices_quit_label-&amp;gt;parse_uline('_Quit'),GDK_MOD1_MASK,&lt;br /&gt;
    GTK_ACCEL_VISIBLE);&lt;br /&gt;
&lt;br /&gt;
// Add the &amp;quot;File | Quit&amp;quot; option to the &amp;quot;File&amp;quot; submenu&lt;br /&gt;
$menu_file_submenu-&amp;gt;append($menu_file_choices_quit);&lt;br /&gt;
&lt;br /&gt;
// Create the &amp;quot;Help&amp;quot; menu and its keyboard accelerator&lt;br /&gt;
$menu_help_item = &amp;amp;new GtkMenuItem('_Help');&lt;br /&gt;
$menu_help_item_label = $menu_help_item-&amp;gt;child;&lt;br /&gt;
$menu_help_item-&amp;gt;add_accelerator('activate',$accelgroup,&lt;br /&gt;
                                 $menu_help_item_label-&amp;gt;parse_uline('_Help'),&lt;br /&gt;
                                 GDK_MOD1_MASK,0);&lt;br /&gt;
// Add the &amp;quot;Help&amp;quot; menu to the menu bar&lt;br /&gt;
$menubar-&amp;gt;add($menu_help_item);&lt;br /&gt;
&lt;br /&gt;
// Create the submenu for the options under &amp;quot;Help&amp;quot;&lt;br /&gt;
$menu_help_submenu = &amp;amp;new GtkMenu();&lt;br /&gt;
$menu_help_item-&amp;gt;set_submenu($menu_help_submenu);&lt;br /&gt;
&lt;br /&gt;
// Create the &amp;quot;About&amp;quot; option under &amp;quot;Help&amp;quot; and its accelerator&lt;br /&gt;
$menu_help_choices_about = &amp;amp;new GtkMenuItem('_About');&lt;br /&gt;
$menu_help_choices_about_label = $menu_help_choices_about-&amp;gt;child;&lt;br /&gt;
$menu_help_choices_about-&amp;gt;add_accelerator('activate',$accelgroup,&lt;br /&gt;
    $menu_help_choices_about_label-&amp;gt;parse_uline('_About'),GDK_MOD1_MASK,&lt;br /&gt;
    GTK_ACCEL_VISIBLE);&lt;br /&gt;
&lt;br /&gt;
// Add the &amp;quot;Help | About&amp;quot; option to the &amp;quot;Help&amp;quot; submenu&lt;br /&gt;
$menu_help_submenu-&amp;gt;append($menu_help_choices_about);&lt;br /&gt;
&lt;br /&gt;
// Layout the weather station searching widgets in a GtkTable&lt;br /&gt;
$table_1 = &amp;amp;new GtkTable(2,4);&lt;br /&gt;
$vbox-&amp;gt;pack_start($table_1);&lt;br /&gt;
&lt;br /&gt;
// Put a label on the left in the first row&lt;br /&gt;
$label_sn = &amp;amp;new GtkLabel('Station Name: ');&lt;br /&gt;
$label_sn-&amp;gt;set_alignment(1,0.5);&lt;br /&gt;
$table_1-&amp;gt;attach($label_sn,0,1,0,1, GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Put a text entry field in the middle of the first row&lt;br /&gt;
// The accelerator allows you to hit &amp;quot;Return&amp;quot; in the field to submit&lt;br /&gt;
$entry_sn = &amp;amp;new GtkEntry();&lt;br /&gt;
$entry_sn-&amp;gt;add_accelerator('activate',$accelgroup,GDK_KEY_Return,0,0);&lt;br /&gt;
$table_1-&amp;gt;attach($entry_sn,1,2,0,1, GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Put a scrolled window in the second row of the table&lt;br /&gt;
$scrolledwindow_1 = &amp;amp;new GtkScrolledWindow();&lt;br /&gt;
$scrolledwindow_1-&amp;gt;set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);&lt;br /&gt;
$table_1-&amp;gt;attach($scrolledwindow_1,0,4,1,2, GTK_EXPAND | GTK_SHRINK | GTK_FILL,&lt;br /&gt;
                 GTK_EXPAND | GTK_SHRINK | GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Put a columned list in the scrolled window. By putting the list inside&lt;br /&gt;
// the scrolled window instead of directly in the GtkTable, the window doesn't&lt;br /&gt;
// have to grow to a huge size to let you see everything in the list&lt;br /&gt;
$clist_sn = &amp;amp;new GtkCList(4,array('Code','Name','Region','Country'));&lt;br /&gt;
$scrolledwindow_1-&amp;gt;add($clist_sn);&lt;br /&gt;
&lt;br /&gt;
// Set the columns in the list to resize automatically&lt;br /&gt;
for ($i = 0; $i &amp;lt; 4; $i++) { $clist_sn-&amp;gt;set_column_auto_resize($i,true); }&lt;br /&gt;
&lt;br /&gt;
// Add a &amp;quot;Search&amp;quot; button to the first row&lt;br /&gt;
$button_search =&amp;amp;new GtkButton('Search');&lt;br /&gt;
$table_1-&amp;gt;attach($button_search,2,3,0,1, GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Add an &amp;quot;Add&amp;quot; button to the first row&lt;br /&gt;
$button_add = &amp;amp;new GtkButton('Add');&lt;br /&gt;
$table_1-&amp;gt;attach($button_add,3,4,0,1, GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Layout the weather conditions display widgets in another GtkTable&lt;br /&gt;
$table_2 = &amp;amp;new GtkTable(2,3);&lt;br /&gt;
$vbox-&amp;gt;pack_start($table_2);&lt;br /&gt;
&lt;br /&gt;
// Add a label displaying how many stations are shown&lt;br /&gt;
$label_st = &amp;amp;new GtkLabel('Stations: 0');&lt;br /&gt;
$label_st-&amp;gt;set_alignment(0,0.5);&lt;br /&gt;
$table_2-&amp;gt;attach($label_st,0,1,0,1, GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Add a button to update a single station&lt;br /&gt;
$button_update_sel = &amp;amp;new GtkButton('Update Selected');&lt;br /&gt;
$table_2-&amp;gt;attach($button_update_sel,1,2,0,1, GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Add a button to update all stations&lt;br /&gt;
$button_update_all = &amp;amp;new GtkButton('Update All');&lt;br /&gt;
$table_2-&amp;gt;attach($button_update_all,2,3,0,1, GTK_FILL);&lt;br /&gt;
&lt;br /&gt;
// Add a columned list to hold the weather conditions at the stations&lt;br /&gt;
// This columned list also goes inside a scrolled window&lt;br /&gt;
$scrolledwindow_2 = &amp;amp;new GtkScrolledWindow();&lt;br /&gt;
$scrolledwindow_2-&amp;gt;set_policy(GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);&lt;br /&gt;
$table_2-&amp;gt;attach($scrolledwindow_2,0,3,1,2, GTK_EXPAND | GTK_SHRINK | GTK_FILL,&lt;br /&gt;
                 GTK_EXPAND | GTK_SHRINK | GTK_FILL);&lt;br /&gt;
$clist_st = &amp;amp;new GtkCList(5,array('Code','Temp','Precipitation','Wind','Updated'));&lt;br /&gt;
$scrolledwindow_2-&amp;gt;add($clist_st);&lt;br /&gt;
&lt;br /&gt;
// Set the columns in the list to resize automatically&lt;br /&gt;
for ($i = 0; $i &amp;lt; 5; $i++) { $clist_st-&amp;gt;set_column_auto_resize($i,true); }&lt;br /&gt;
&lt;br /&gt;
// Connect signals to callbacks&lt;br /&gt;
&lt;br /&gt;
// Clicking on the &amp;quot;Search&amp;quot; button or hitting Return in the text entry field&lt;br /&gt;
// searches for weather stations whose name match the entered text&lt;br /&gt;
$button_search-&amp;gt;connect('clicked','wx_searchByName',$entry_sn,$clist_sn,$window);&lt;br /&gt;
$entry_sn-&amp;gt;connect('activate','wx_searchByName',$entry_sn,$clist_sn,$window);&lt;br /&gt;
&lt;br /&gt;
// Clicking on the &amp;quot;Add&amp;quot; button adds the weather station to the bottom &lt;br /&gt;
// columned list&lt;br /&gt;
$button_add-&amp;gt;connect('clicked','cb_add_station',$clist_sn,$clist_st,$label_st);&lt;br /&gt;
&lt;br /&gt;
// Clicking on the &amp;quot;Update Selected&amp;quot; button updates the bottom columned list&lt;br /&gt;
// for a single station&lt;br /&gt;
$button_update_sel-&amp;gt;connect('clicked','wx_update_report',$clist_st,$label_st,&lt;br /&gt;
                            'selected');&lt;br /&gt;
// Clicking on the &amp;quot;Update All&amp;quot; button updates all stations in the bottom&lt;br /&gt;
// columned list&lt;br /&gt;
$button_update_all-&amp;gt;connect('clicked','wx_update_report',$clist_st,$label_st,&lt;br /&gt;
                            'all');&lt;br /&gt;
&lt;br /&gt;
// Closing the window or selecting the &amp;quot;File | Quit&amp;quot; menu item exits the program&lt;br /&gt;
$window-&amp;gt;connect('destroy','cb_shutdown');&lt;br /&gt;
$menu_file_choices_quit-&amp;gt;connect('activate','cb_shutdown');&lt;br /&gt;
&lt;br /&gt;
// Selecting the &amp;quot;Help | About&amp;quot; menu item shows an about box&lt;br /&gt;
$menu_help_choices_about-&amp;gt;connect('activate','cb_about_box',$window);&lt;br /&gt;
&lt;br /&gt;
// These callbacks keep track of the currently selected row (if any)&lt;br /&gt;
// in each columned list&lt;br /&gt;
$clist_sn-&amp;gt;connect('select-row','cb_clist_select_row');&lt;br /&gt;
$clist_sn-&amp;gt;connect('unselect-row','cb_clist_unselect_row');&lt;br /&gt;
$clist_st-&amp;gt;connect('select-row','cb_clist_select_row');&lt;br /&gt;
$clist_st-&amp;gt;connect('unselect-row','cb_clist_unselect_row');&lt;br /&gt;
&lt;br /&gt;
// The interface has been set up and the signals we want to pay attention&lt;br /&gt;
// to have been connected to callbacks. Time to display the window and start&lt;br /&gt;
// the GTK signal handling loop.&lt;br /&gt;
$window-&amp;gt;show_all();&lt;br /&gt;
gtk::main();&lt;br /&gt;
&lt;br /&gt;
/*&lt;br /&gt;
 * CALLBACKS AND OTHER SUPPORT FUNCTIONS&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
// use the searchByName() function over SOAP to get a list of stations&lt;br /&gt;
// whose names match the given search term&lt;br /&gt;
function wx_searchByName($button,$entry,$clist,$window) {&lt;br /&gt;
    // instantiate a new SOAP client&lt;br /&gt;
    $sc = new SOAP_Client('http://live.capescience.com/ccx/GlobalWeather');&lt;br /&gt;
&lt;br /&gt;
    $search_term = trim($entry-&amp;gt;get_text());&lt;br /&gt;
    if ($search_term) {&lt;br /&gt;
        // call the remote function if a search term is provided&lt;br /&gt;
        $res = $sc-&amp;gt;call('searchByName',&lt;br /&gt;
                         array(new SOAP_Value('name','string',$search_term)),&lt;br /&gt;
                         'capeconnect:GlobalWeather:StationInfo',&lt;br /&gt;
                         'capeconnect:GlobalWeather:StationInfo#searchByName');&lt;br /&gt;
&lt;br /&gt;
        // pop up an error dialog if the SOAP function fails&lt;br /&gt;
        if (PEAR::isError($res)) {&lt;br /&gt;
            error_dialog($res-&amp;gt;getMessage(),$window);&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
        // pop up an error dialog if there are no matches&lt;br /&gt;
        if (! is_array($res)) {&lt;br /&gt;
            error_dialog('No weather stations found.',$window);&lt;br /&gt;
            return false;&lt;br /&gt;
        }&lt;br /&gt;
        // add each station and its info to the columned list&lt;br /&gt;
        // wrapping the calls to append() with freeze() and thaw()&lt;br /&gt;
        // make all of the data appear at once&lt;br /&gt;
        $clist-&amp;gt;freeze();&lt;br /&gt;
        $clist-&amp;gt;clear();&lt;br /&gt;
        foreach ($res as $station) {&lt;br /&gt;
            $clist-&amp;gt;append(array($station-&amp;gt;icao,$station-&amp;gt;name,&lt;br /&gt;
                                 $station-&amp;gt;region,$station-&amp;gt;country));&lt;br /&gt;
        }&lt;br /&gt;
        $clist-&amp;gt;thaw();&lt;br /&gt;
    } &lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// use the getWeatherReport function over SOAP to get the weather conditions&lt;br /&gt;
// at a particular station&lt;br /&gt;
function wx_getWeatherReport($code) {&lt;br /&gt;
    $sc = new SOAP_Client('http://live.capescience.com/ccx/GlobalWeather');&lt;br /&gt;
    $res = $sc-&amp;gt;call('getWeatherReport',&lt;br /&gt;
                     array(new SOAP_Value('code','string',$code)),&lt;br /&gt;
                     'capeconnect:GlobalWeather:GlobalWeather',&lt;br /&gt;
                     'capeconnect:GlobalWeather:GlobalWeather#getWeatherReport');&lt;br /&gt;
&lt;br /&gt;
    if (PEAR::isError($res)) {&lt;br /&gt;
        error_dialog($res-&amp;gt;getMessage());&lt;br /&gt;
        return false;&lt;br /&gt;
    } else {&lt;br /&gt;
        return $res;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// add the weather report in $res to the columned list $clist&lt;br /&gt;
// if $row is null, the report is appended to the list&lt;br /&gt;
// if $row is not null, the report replaces row $row in the list&lt;br /&gt;
function wx_add_report($clist,$label,$res,$row = null) {&lt;br /&gt;
&lt;br /&gt;
    // format the timestamp&lt;br /&gt;
    $timestamp = str_replace('T',' ',$res-&amp;gt;timestamp);&lt;br /&gt;
    $timestamp = str_replace('Z',' GMT',$timestamp);&lt;br /&gt;
    $timestamp = strftime('%H:%M:%S %m/%d/%Y',strtotime($timestamp));&lt;br /&gt;
&lt;br /&gt;
    // format the wind information&lt;br /&gt;
    $wind = sprintf(&amp;quot;%.2f m/s from %s&amp;quot;,&lt;br /&gt;
                    $res-&amp;gt;wind-&amp;gt;prevailing_speed,&lt;br /&gt;
                    $res-&amp;gt;wind-&amp;gt;prevailing_direction-&amp;gt;compass);&lt;br /&gt;
        &lt;br /&gt;
    $clist-&amp;gt;freeze();&lt;br /&gt;
    if (! is_null($row)) {&lt;br /&gt;
        // replace the information in row number $row&lt;br /&gt;
        $clist-&amp;gt;set_text($row,1,$res-&amp;gt;temperature-&amp;gt;string);&lt;br /&gt;
        $clist-&amp;gt;set_text($row,2,$res-&amp;gt;precipitation-&amp;gt;string);&lt;br /&gt;
        $clist-&amp;gt;set_text($row,3,$wind);&lt;br /&gt;
        $clist-&amp;gt;set_text($row,4,$timestamp);&lt;br /&gt;
    } else {&lt;br /&gt;
        // add the information to the end of the columned list&lt;br /&gt;
        $clist-&amp;gt;append(array($res-&amp;gt;station-&amp;gt;icao,&lt;br /&gt;
                             $res-&amp;gt;temperature-&amp;gt;string,&lt;br /&gt;
                             $res-&amp;gt;precipitation-&amp;gt;string,&lt;br /&gt;
                             $wind,&lt;br /&gt;
                             $timestamp));&lt;br /&gt;
&lt;br /&gt;
        // update the columned list's internal row count&lt;br /&gt;
        $rows = 1 + $clist-&amp;gt;get_data('rows');&lt;br /&gt;
        $clist-&amp;gt;set_data('rows',$rows);&lt;br /&gt;
        // update the label that displays a station count&lt;br /&gt;
        $label-&amp;gt;set_text(&amp;quot;Stations: $rows&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    $clist-&amp;gt;thaw();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// update conditions for one station or all stations, depending on $mode&lt;br /&gt;
function wx_update_report($button,$clist,$label,$mode) {&lt;br /&gt;
    switch ($mode) {&lt;br /&gt;
    case 'selected':&lt;br /&gt;
&lt;br /&gt;
        // if there is a row selected&lt;br /&gt;
        $selected_row = $clist-&amp;gt;get_data('selected_row');&lt;br /&gt;
        if (($selected_row &amp;gt;= 0) &amp;amp;&amp;amp; (! is_null($selected_row))) {&lt;br /&gt;
            $code = $clist-&amp;gt;get_text($selected_row,0);&lt;br /&gt;
            &lt;br /&gt;
            // get the report and update the columned list&lt;br /&gt;
            if ($res = wx_getWeatherReport($code)) {&lt;br /&gt;
                wx_add_report($clist,$label,$res,$selected_row);&lt;br /&gt;
            } &lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
    case 'all':&lt;br /&gt;
        // for each row in the columned list&lt;br /&gt;
        for ($i = 0, $j = $clist-&amp;gt;get_data('rows'); $i &amp;lt; $j; $i++) {&lt;br /&gt;
            // get the report and update the list&lt;br /&gt;
            if ($res = wx_getWeatherReport($clist-&amp;gt;get_text($i,0))) {&lt;br /&gt;
                wx_add_report($clist,$label,$res,$i);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
        break;&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// add a station to the bottom list of weather reports&lt;br /&gt;
function cb_add_station($button,$clist,$clist_2,$label) {&lt;br /&gt;
    $selected_row = $clist-&amp;gt;get_data('selected_row');&lt;br /&gt;
    // if there's a selected row in the top list of stations&lt;br /&gt;
    if ($selected_row &amp;gt;= 0) {&lt;br /&gt;
        $code = $clist-&amp;gt;get_text($selected_row,0);&lt;br /&gt;
        // get the weather report for that station&lt;br /&gt;
        if ($res = wx_getWeatherReport($code)) {&lt;br /&gt;
            // find the row if this code is already in the list&lt;br /&gt;
            $row = null;&lt;br /&gt;
            for ($i = 0, $j = $clist_2-&amp;gt;get_data('rows'); $i &amp;lt; $j; $i++) {&lt;br /&gt;
                if ($clist_2-&amp;gt;get_text($i,0) == $code) {&lt;br /&gt;
                    $row = $i;&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
            // add the station and its report to the bottom list of&lt;br /&gt;
            // reports (or update the existing row)&lt;br /&gt;
            wx_add_report($clist_2,$label,$res,$row);&lt;br /&gt;
        } &lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// update a columned list's internal selected row value when a row is selected&lt;br /&gt;
function cb_clist_select_row($clist,$row,$col,$e) {&lt;br /&gt;
    $clist-&amp;gt;set_data('selected_row',$row);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// clear a columned list's internal selected row value when a row is unselected&lt;br /&gt;
function cb_clist_unselect_row($clist) {&lt;br /&gt;
    $clist-&amp;gt;set_data('selected_row',-1);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// display the &amp;quot;About Box&amp;quot;&lt;br /&gt;
function cb_about_box($menu_item,$window) {&lt;br /&gt;
    $about_box = &amp;amp;new GtkDialog();&lt;br /&gt;
    $vbox = $about_box-&amp;gt;vbox;&lt;br /&gt;
    $action_area = $about_box-&amp;gt;action_area;&lt;br /&gt;
    $about_box-&amp;gt;set_title('About');&lt;br /&gt;
    $label = &amp;amp;new GtkLabel(&amp;quot;This is the PHP Cookbook PHP-GTK Demo.&amp;quot;);&lt;br /&gt;
    $button = &amp;amp;new GtkButton('OK');&lt;br /&gt;
    $button-&amp;gt;connect('clicked','cb_dialog_destroy',$about_box);&lt;br /&gt;
    $vbox-&amp;gt;pack_start($label);&lt;br /&gt;
    $action_area-&amp;gt;pack_start($button);&lt;br /&gt;
    $about_box-&amp;gt;set_modal(true);&lt;br /&gt;
    $about_box-&amp;gt;set_transient_for($window);&lt;br /&gt;
    $about_box-&amp;gt;show_all();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// display an error dialog box&lt;br /&gt;
function error_dialog($msg,$window) {&lt;br /&gt;
    $dialog = &amp;amp;new GtkDialog();&lt;br /&gt;
    $vbox = $dialog-&amp;gt;vbox;&lt;br /&gt;
    $action_area = $dialog-&amp;gt;action_area;&lt;br /&gt;
    $dialog-&amp;gt;set_title('Error');&lt;br /&gt;
    $label = &amp;amp;new GtkLabel(&amp;quot;Error: $msg&amp;quot;);&lt;br /&gt;
    $button = &amp;amp;new GtkButton('OK');&lt;br /&gt;
    $button-&amp;gt;connect('clicked','cb_dialog_destroy',$dialog);&lt;br /&gt;
    $vbox-&amp;gt;pack_start($label);&lt;br /&gt;
    $action_area-&amp;gt;pack_start($button);&lt;br /&gt;
    $dialog-&amp;gt;set_modal(true);&lt;br /&gt;
    $dialog-&amp;gt;set_transient_for($window);&lt;br /&gt;
    $dialog-&amp;gt;show_all();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// close a dialog box&lt;br /&gt;
function cb_dialog_destroy($button,$dialog) {&lt;br /&gt;
    $dialog-&amp;gt;destroy();&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// quit the main program&lt;br /&gt;
function cb_shutdown() { gtk::main_quit(); }&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
== Notes ==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;references/&amp;gt;&lt;/div&gt;</description>
			<pubDate>Thu, 06 Mar 2008 22:28:45 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:PHP_Cookbook/Client-Side_PHP</comments>		</item>
	</channel>
</rss>