Introduction to PHPGTK
PHPGTK is an extension to PHP that allows you to create graphical user interface (GUI) applications. Instead of running in a browser, your PHP application runs in its own application window. These applications are client-side only. They do not reside on the Web server. The files instead reside on the user's hard drive. For users to use the PHPGTK application, they must have the proper version of PHP (with the GTK+ extension) installed on their system.
GTK+ was originally designed for the open-source image editing program called the GIMP (GNU Image Manipulation Program). GTK stands for the GIMP Tool Kit. Later, the Gnome team decided to use GTK+ to create their desktop environment for Linux. GTK+ has been ported to Windows, Linux, and BeOS, and thus makes for a good cross-platform GUI library.
PHPGTK uses GTK+ to draw the "widgets" required in any GUI application. Widgets are things like scroll bars, text input fields, and buttons, among other things. When you start almost any Windows application, such as a Web browser, you are looking at a collection of widgets.
Widgets need to be contained in some type of framework to be useful and logical. You can't just have a bunch of buttons and text fields scattered randomly about the screen. To solve this problem, we use a special kind of widget called a container. A container is another structure that organizes the widgets however you think it best for your application. For example, the menu bar at the top of most applications is a container.
Widgets have many different properties that control how they look and act. Each type of widget has its own unique set of properties, in addition to properties that are common to other widgets.
Widgets send signals to your program when they are activated. An example of widget activation is when a user clicks on a button. Once a widget sends a signal, you generally respond to that signal with a callback. A callback is basically just a function. Some signals end up in callbacks that are automatically handled by GTK (default handlers). Other signals end up executing one of your functions (user-defined handlers). The user-defined functions are written in PHP.
As of the writing of this document, the current version of PHPGTK was 0.5.1, released on April 26, 2002. While still in its early stages, PHPGTK 0.5.1 is a vast improvement over the original 0.1 release.
PHPGTK is very complex. It takes basically two complete languages and puts them together. This lone chapter cannot hope to explain in detail the many methods and classes that are available, but I wish to provide a general overview of PHPGTK insofar as this space allows. Check the PHPGTK Web site at http://gtk.php.net for a wealth of information!
Installing PHPGTK
Before you can start using PHPGTK, you need to download the appropriate files. The PHPGTK team has set up a nice Web site at http://gtk.php.net. The PHPGTK Web site has downloads and documentation for the GTK extension.
Before You Install
PHPGTK still has not reached the 1.0 version, and as such I wouldn't recommend that you use this version of PHP on your production Web server. In fact, there really isn't a reason that you'd want to install PHPGTK on your production Web server. It is a client-side application!
PHPGTK is a developers' toy at the moment, and anything is subject to change, at least before PHPGTK reaches version 1.0. Have some fun with it, try it out, but don't base your company's next big product on PHPGTK 0.5.0.
Installing on Windows
Installing PHPGTK on a Windows machine is fairly straightforward and similar to installing the normal version of PHP. Download the Windows binary file from the PHPGTK Web site at http://gtk.php.net.
Unzip the file using a zip utility such as WinZip. Extract the files to your C: drive.
The following folders are created when you unzip the file:
php4— Contains the PHP executable, as well as some GTK library files.
test— Contains some sample *.php files that use the GTK widget set.
If you are using Windows 98 or Windows ME, then you will notice that folders called "winnt" and "winnt/system32" have been created. You should copy the contents of those folders into your C:\windows directory. Note that you may have to set your system files to be viewable so that you can see the necessary DLL files to copy them over to C:\windows.
Additionally, you should see a new php.ini file. Copy this to your C:\Windows or C:\WINNT directory. Be sure to first back up your existing php.ini file.
To test out the installation, type the following from a command prompt:
c:\php4\php.exe -q c:\test\gtk.php
See Figure 12-1 for a screen shot of the gtk.php file in action.
Figure 12-1. gtk.php in Linux (Gnome) and Windows 2000
Installing on Linux
Installing PHPGTK on Linux is easier than installing the normal PHP; you don't have to worry about compiling with Apache. You can compile GTK functionality into an existing standalone version of PHP, but for our purposes we'll start from scratch and make a brand new PHP executable that has GTK functionality built in. Before you begin:
Download the source file for PHP from the download page at www.php.net.
Download the source file PHPGTK from the download page of http://gtk.php.net.
Once you have the necessary file, unzip and untar the regular PHP source file:
tar -zxvf php-4.x.x.tar.gz
This creates a new directory named php-4.x.x, where the "x" denotes the exact version number of PHP that you downloaded.
Compile PHP using the minimum options. We just want to create a standalone executable. If you want to add additional functionality, you can recompile later. For now, you just want to make sure you can create a working version of PHPGTK. Change directory into your newly created PHP source directory. Compile by typing:
./configure
That's all there is to it. This automatically creates an executable that has built-in MySQL support as well.
Once you have the php binary file, you must copy it to /usr/local/bin. The PHPGTK installation will be looking for it in that location. You need to be root to do this.
cp php /usr/local/bin
Now, it's time to build the GTK extension onto your PHP executable. Go back to where you downloaded the PHPGTK source file and extract it:
tar -zxvf php-gtk-0.5.0.tar.gz
This creates a new directory named php-gtk-0.5.0. Change directory into that directory and compile the source file. You will need to be root to perform the final step, make install. To compile PHPGTK, type the following (a lot of text will print to the screen after you type each command):
./compile
make
make install
You can test your installation by going into the test directory and running a few of the scripts. X-Windows will need to be running!
cd test
php -q gtk.php
A window should pop up showing various GTK widget buttons. Click the different widgets to get a brief idea of what they do.
Figure 12-1 shows the gtk.php file as it appears in Linux (using the Gnome desktop) on the left and in Windows 2000 on the right.
You can also try running the other *.php files in the test directory. Use the -q flag to suppress printing HTTP header information to the screen (the php "quiet" option).
Creating Basic GTK Objects
The GTK+ extension is an object oriented programming (OOP) extension to PHP. You need to use OOP syntax and structures to use the GTK+ library. If you haven't already done so, you may want to go all the way back to Chapter 1 and review the material about OOP in PHP.
Creating Your First PHPGTK Window
Before you can create any PHPGTK applications, you must tell PHP which GTK library you want to use. Since GTK runs on Windows and many flavors of UNIX, that leaves us with two types of libraries. Windows uses dynamic link libraries, also called DLLs. Most *nix based operating systems use .so libraries. Since you want your application to be able to be used on both types of operating systems without modification, you must provide a dynamic method of loading the libraries. The standard method of doing this is:
if (!class_exists('gtk')) {
if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
dl('php_gtk.dll');
else
dl('php_gtk.so');
}
Side Note
If you intend for your script to run on platforms other than Linux or Windows (if support for these other platforms is added in later versions of PHPGTK), then you must ensure that the proper PHPGTK library is loaded using the same manner as above.
The core part of any PHPGTK application is the main window. Each application you create should have a main window that acts as a container into which you place your other widgets.
The basic window class in PHPGTK is GtkWindow(). You create a new window object like this:
$window = &new GtkWindow();
Now that you have a window, you must provide for a way to destroy that window. In most operating systems, a user can destroy a window by clicking the "X" in the upper right corner of the window (some Linux desktops provide a different control).
When you click the "X" in a PHPGTK application, two signals are sent to the application. These signals are delete_event and destroy. For now we'll just concentrate on the destroy signal.
Signals are generally user input, and callbacks are functions that you create to do something in response to the user input. However, you need to connect the signal to the callback. To do this, you use the connect() method. To connect the destroy signal to our destroy callback:
$window->connect('destroy','destroy');
The first argument is the name of the signal that is sent by PHPGTK. The second argument is the name of the function (callback) that you want to call in response to the signal. To keep things simple, we'll name the function (callback) 'destroy'.
function destroy() {
Gtk::main_quit();
}
The Gtk::main_quit(); is the PHPGTK function that kills the application.
Now we have created a window and provided a means for the user to get rid of the window. But we still can't run the application. PHPGTK requires two more things to be done. First, we need to show the window. You show a window by calling the show_all() method:
$window->show_all();
Finally, you need to start the event sequence (polling events?) by calling the special PHPGTK function main():
Gtk::main();
Side Note
You must begin your scripts with the full "<?php" tag when using PHPGTK. Using the shorthand "<?" may cause PHPGTK to just echo the entire script to the screen instead of creating a GTK window.
When we put it all together, you get Script 12-1, basic_window.php.
Script 12-1 basic_window.php
1. <?php
2. if (!class_exists('gtk')) {
3. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
4. dl('php_gtk.dll');
5. else
6. dl('php_gtk.so');
7. }
8. function destroy(){
9. Gtk::main_quit();
10. }
11. $window = &new GtkWindow();
12. $window->connect('destroy', 'destroy');
13. $window->show_all();
14. Gtk::main();
15. ?>
Run the script from the command line (remember, you need to add the full path to the PHP executable if it is not in the current directory):
php -q basic_window.php
The result should look similar to Figure 12-2. You can close the window by clicking the "X" in the upper right corner.
Figure 12-2. basic_window.php
Script 12-1. basic_window.php Line-by-Line Explanation LINE
DESCRIPTION
2
If the GTK class has not been defined, then execute lines 3–7. If the GTK class has been defined, then the script needs to do nothing.
3
Check the first three characters of the PHP_OS environment variable.
4
If it is WIN, then the system is running Windows. Load the PHPGTK library for Windows.
5–7
If the PHP_OS environment variable is not WIN, then assume that PHP is running on a Linux system and load the Linux PHPGTK library.
8–10
Define a function, destroy(), that stops the script when called.
9
Execute the Gtk::main_quit() method to stop the script and close the GTK window.
10
End the function declaration.
11
Create a new window object called $window.
12
Connect the destroy method of the window to the destroy() function that you defined earlier. The destroy method is called when the user clicks the "X" on the top right of the window.
13
Issue the show_all() function call to the $window object so that everything within the object is displayed.
14
Call the Gtk::main() method to run the script.
Adding a GTK Button
The simplest and most basic example in all of programming is the Hello World example. The goal of the example, if you didn't know, is to get the program to print out the words "Hello, World!" We are going to enhance our Hello World program slightly. Instead of merely printing out "Hello World," let's add that text to a button. When the button is clicked, we'll close our application. Try out this example, then look at the brief line-by-line explanation for details. Figure 12-3 shows the output of the script in Linux and in Windows.
Script 12-2 hello.php
1. <?php
2. if (!class_exists('gtk')) {
3. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
4. dl('php_gtk.dll');
5. else
6. dl('php_gtk.so');
7. }
8. function destroy() {
9. Gtk::main_quit();
10. }
11. function hello(){
12. global $window;
13. print "Hello World!\n";
14. $window->destroy();
15. }
16. $window = &new GtkWindow();
17. $window->connect('destroy', 'destroy');
18. $button = &new GtkButton('Hello World!');
19. $button->connect('clicked', 'hello');
20. $window->add($button);
21. $window->show_all();
22. Gtk::main();
23. ? >
Figure 12-3. hello.php in Linux (Gnome) and Windows
Script 12-2. hello.php Line-by-Line Explanation LINE
DESCRIPTION
2
If the GTK class has not been defined, then execute lines 3–7. If the GTK class has been defined, then the script needs to do nothing.
3
Check the first three characters of the PHP_OS environment variable.
4
If it is WIN, then the system is running Windows. Load the PHPGTK library for Windows.
5–7
If the PHP_OS environment variable is not WIN, then assume that PHP is running on a Linux system and load the Linux PHPGTK library.
8–10
Define a function, destroy(), that stops the script when called.
9
Execute the Gtk::main_quit() method to stop the script and close the GTK window.
10
End the function declaration.
11–14
Define a function, hello(), that prints the text "Hello World!"
12
Allow the $window object to be accessed by this function.
13
Print the text "Hello World!" to STDOUT (the same command line from which you executed the script).
14
Use the destroy() method on the $window object to close the window.
15
End the function declaration.
16
Create a new window object called $window.
17
Connect the 'destroy' method of the window to the destroy() function that you defined earlier. The 'destroy' method is called when the user clicks the "X" on the top right of the window.
18
Create a new button object called $button.
19
Connect the 'clicked' method of the button to the hello() function.
20
Add the button to the window using the add() method.
21
Issue the show_all() function call to the $window object so that everything within the object is displayed.
22
Call the Gtk::main() method to run the script.
Modifying Basic Widget Appearance
The syntax for creating buttons and windows looks very similar. That is because they are both classes of a GTK widget. Each object (buttons, windows, etc.) has certain methods available to them—for example, the connect() method.
If you run the hello.php script, you will notice that the window size is pretty small—in fact, it is only as big as the button inside it. That's because PHP shrinks the window to the smallest size possible when creating it. We can modify the default appearance of the GtkWindow widget by using some methods. For example, if you wanted to set a default size for the window, you could use the set_default_size($width, $height)method:
$window->set_default_size(200,200);
This would create a window that is 200 pixels by 200 pixels. You can also set a window title:
$window->set_title("My PHPGTK APP");
Figure 12-4 shows a window modified by the set_default _size() and set_title() methods.
Figure 12-4. Window modified with property methods (Linux and Windows)
There are many methods available that can affect the appearance and behavior of GTK widgets. See the documentation on http://gtk.php.net for details on these methods.
Text Entry
A desktop application wouldn't be too useful if you couldn't insert data into it. GTK provides a text entry widget that allows you to input a line of text to your application. To create a text entry field:
$text1 = &new GtkEntry();
A text entry field isn't really useful unless you have some way to label it. That's where the GtkLabel comes in handy:
$label1 = &new GtkLabel("A Text Entry Field:");
Now that we have our new text entry field and a label, we need to put it into our window. There's a slight problem, though. You just can't add() multiple widgets into the top-level window. Instead, you place a box into the window, and add your subobjects into that box.
hbox and vbox
There are two types of boxes that you can use to hold your widgets in place within the main window. They are the hbox (horizontal) and the vbox (vertical). As their names imply, the hbox allows you to place objects into it so that the objects are aligned horizontally, and the vbox allows you to place objects into it so that the objects are aligned vertically. Since we want to align our text entry field and label horizontally (like normal applications do), we'll create an hbox and add it to our window, then pack the label and text entry field into the box:
$box = &new GtkHBox();
$window->add($box);
The hbox and vbox widgets are special containers into which you can add multiple widgets. To add them, you need to pack the widgets into the box:
$box->pack_start($label1, false, false, 0);
$box->pack_start($text1, false, false, 0);
pack_start
The pack_start method allows you to add the widgets into the box. If you are packing widgets into vbox, then pack_start adds the widgets from the top of the box. Each time you pack another widget into the box, the preceding widget is moved down and the newly added widget is at the top of the vbox. If you are packing widgets into an hbox, then pack_start adds the widgets into the box from the left. Each time you pack a new widget into the box, it is placed to the right of the preceding widget.
The pack_start() method takes four arguments:
The object you are packing.
Whether or not to expand the object if the box is resized (true or false).
Whether or not the object should fill the space which is available to it (true or false).
The amount of padding to use around the object (an integer).
The default arguments used, if none are specified, are true, true, 0. These mean to expand and fill and use no padding.
Let's look at a script the uses the label and text entry field. See Script 12-3, text_entry.php, below. See Figure 12-5 for the example output.
Script 12-3 text_entry.php
1. <?php
2. if (!class_exists('gtk')) {
3. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
4. dl('php_gtk.dll');
5. else
6. dl('php_gtk.so');
7. }
8. function destroy(){
9. Gtk::main_quit();
10. }
11. $window = &new GtkWindow();
12. $window->connect('destroy', 'destroy');
13. $text1 = &new GtkEntry();
14. $label1 = &new GtkLabel("A Text Entry Field");
15. $box = &new GtkHBox();
16. $window->add($box);
17. $box->pack_start($label1, false, false, 0);
18. $box->pack_start($text1, false, false, 0);
19. $window->show_all();
20. Gtk::main();
21. ?>
Figure 12-5. text_entry.php
Script 12-3. text_entry.php Line-by-Line Explanation LINE
DESCRIPTION
2
If the GTK class has not been defined, then execute lines 3–7. If the GTK class has been defined, then the script needs to do nothing.
3
Check the first three characters of the PHP_OS environment variable.
4
If it is WIN, then the system is running Windows. Load the PHPGTK library for Windows.
5–7
If the PHP_OS environment variable is not WIN, then assume that PHP is running on a Linux system and load the Linux PHPGTK library.
8–10
Define a function, destroy(), that stops the script when called.
9
Execute the Gtk::main_quit() method to stop the script and close the GTK window.
10
End the function declaration.
11
Create a new window object called $window.
12
Connect the 'destroy' method of the window to the destroy() function that you defined earlier. The 'destroy' method is called when the user clicks the "X" on the top right of the window.
13
Create a new text-entry object called $text1.
14
Create a new label object called $label1 containing the text "A Text Entry Field".
15
Create a new horizontal box object called $box.
16
Add the box to the window using the add() method.
17
Pack the label object into the box.
18
Pack the text-entry object into the box.
19
Issue the show_all() function call to the $window object so that everything within the object is displayed.
20
Call the Gtk::main() method to run the script.
Putting It All Together
So now you know how to make a label, a button (with a label in it), and a text entry field. With these three tools, you can begin to build an application!
First, we need to know how to get the text the user enters in the text entry widget. The GtkTextEntry widget has a method that meets our needs:
$input = $text1->get_text();
You can assign the user-entered data using the get_text() method on the $text1 object to a variable (for example $input), then do whatever you want with that text. The following script puts it all together and provides a short example of a function to handle the user-entered text. See Figure 12-6 for example output.
Script 12-4 text_submit.php
1. <?php
2. if (!class_exists('gtk')) {
3. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
4. dl('php_gtk.dll');
5. else
6. dl('php_gtk.so');
7. }
8. function destroy(){
9. Gtk::main_quit();
10. }
11. function submit(){
12. global $text1;
13. $input = $text1->get_text();
14. echo $input . "\n";
15. $text1->set_text('');
16. }
17. $window = &new GtkWindow();
18. $window->connect('destroy', 'destroy');
19. $window->set_border_width(5);
20. $text1 = &new GtkEntry();
21. $label1 = &new GtkLabel("A Text Entry Field");
22. $button1 = &new GtkButton('Submit Text!');
23. $button1->connect('clicked', 'submit');
24. $box = &new GtkHBox();
25. $window->add($box);
26. $box->pack_start($label1, false, false, 0);
27. $box->pack_start($text1, false, false, 0);
28. $box->pack_start($button1, false, false, 0);
29. $window->show_all();
30. Gtk::main();
31. ?>
Figure 12-6. text_submit.php
Script 12-4. text_submit.php Line-by-Line Explanation LINE
DESCRIPTION
2
If the GTK class has not been defined, then execute lines 3–7. If the GTK class has been defined, then the script needs to do nothing.
3
Check the first three characters of the PHP_OS environment variable.
4
If it is WIN, then the system is running Windows. Load the PHPGTK library for Windows.
5–7
If the PHP_OS environment variable is not WIN, then assume that PHP is running on a Linux system and load the Linux PHPGTK library.
8–10
Define a function, destroy(), that stops the script when called.
9
Execute the Gtk::main_quit() method to stop the script and close the GTK window.
10
End the function declaration.
11–16
Define a function, submit(), that echoes the user-entered text to STDOUT.
12
Allow the variable $text1 to be accessed by this function.
13
Assign the user-entered text to the $input variable by calling the get_text() method of the $text1 text object.
14
Echo the user-entered text to STDOUT along with a newline character
15
Clear the user-entered text from the text input by calling the set_text() method of the $text1 text object, assigning the new text to be blank ('').
16
End the function declaration.
17
Create a new window object called $window.
18
Connect the 'destroy' method of the window to the destroy() function that you defined earlier. The 'destroy' method is called when the user clicks the "X" on the top right of the window.
19
Set a five-pixel border around the window using the set_border_width method of the window object.
20
Create a new text-entry object called $text1.
21
Create a new label object called $label1 containing the text "A Text Entry Field".
22
Create a new button object called $button.
23
Connect the 'clicked' method of the button to the hello() function.
24
Create a new horizontal box object called $box.
25
Add the box to the window using the add() method.
26
Pack the label object into the box.
27
Pack the text-entry object into the box.
28
Pack the button object into the box.
29
Issue the show_all() function call to the $window object so that everything within the object is displayed.
30
Call the Gtk::main() method to run the script.
Dialog Boxes
Most GUI applications have the ability to display small windows notifying the user that something is wrong or to confirm that the user wants to take the desired action. For example, a dialog box appears asking "Are you sure…" in most word processing applications if you try to close the application without first saving the document on which you are working. PHPGTK also provides an easy way to create simple dialog boxes using the GtkDialog class.
The GtkDialog class is simply a window that contains a vbox, a separator, and an "action area." You place your text in the vbox and place your buttons in the "action area."
This next script shows an example of how to use a GtkDialog box to confirm that a user wishes to quit out of the application. When a user clicks the "Quit" button, or the "X" in the upper right corner of the main window, a dialog box is displayed, asking the users if they are sure they want to quit. If the user clicks "OK" in the dialog box, then the application exits. If the user clicks "Cancel" or the "X" in the upper right corner of the dialog box, then the dialog box closes, but the application remains open. See Figure 12-7 for example output.
Script 12-5 dialog.php
1. <?php
2. if (!class_exists('gtk')) {
3. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
4. dl('php_gtk.dll');
5. else
6. dl('php_gtk.so');
7. }
8. function destroy() {
9. Gtk::main_quit();
10. }
11. function close_dialog($button, $dialog){
12. $dialog->destroy();
13. }
14. function delete_event(){
15. confirm();
16. return true;
17. }
18. function confirm(){
19. global $window;
20. $dialog = &new GtkDialog;
21. $dialog->set_title("Are You Sure?");
22. $dialog->set_default_size('100','100');
23. $dialog->set_modal(TRUE);
24. $dialog->set_transient_for($window);
25. $dialog->set_policy(0,0,0);
26. $label = &new GtkLabel("Are You Sure You Want To Quit?");
27. $button_yes = &new GtkButton('OK');
28. $button_yes->connect('clicked', 'destroy');
29. $button_no = &new GtkButton('Cancel');
30. $button_no->connect('clicked', 'close_dialog', $dialog);
31. $vbox1 = $dialog->vbox;
32. $vbox2 = $dialog->action_area;
33. $vbox1->pack_start($label);
34. $vbox2->pack_start($button_yes);
35. $vbox2->pack_start($button_no);
36. $dialog->show_all();
37. }
38. $window = &new GtkWindow();
39. $window->connect('delete_event', 'delete_event');
40. $button = &new GtkButton('Quit');
41. $button->connect('clicked', 'confirm');
42. $window->add($button);
43. $window->show_all();
44. Gtk::main();
45. ?>
Figure 12-7. dialog.php—initial window and confirmation dialog box
Script 12-5. dialog.php Line-by-Line Explanation LINE
DESCRIPTION
2
If the GTK class has not been defined, then execute lines 3–7. If the GTK class has been defined, then the script needs to do nothing.
3
Check the first three characters of the PHP_OS environment variable.
4
If it is WIN, then the system is running Windows. Load the PHPGTK library for Windows.
5–7
If the PHP_OS environment variable is not WIN, then assume that PHP is running on a Linux system and load the Linux PHPGTK library.
8–10
Define a function, destroy(), that stops the script when called.
9
Execute the Gtk::main_quit() method to stop the script and close the GTK window.
10
End the function declaration.
11–13
Define a function, close_dialog(), that closes a dialog box. This function takes two arguments: $button and $dialog. See the description for line 30 for a description of the arguments.
12
Call the destroy() method of the $dialog object to close the dialog window.
13
End the function declaration.
14–17
Define a function, delete_event(), that catches when a user clicks on the "X" in the upper-right corner of the main application window and runs the confirm function(). The delete_event() is a special method that is run whenever a user clicks the "X" in the upper-right corner of the application window. "delete_event" is a valid connection point for a window, similar to the destroy connection point. If you do not use a delete_event connection point, then the window is automatically destroyed.
15
Execute the confirm() function to make certain the user wishes to exit the application.
16
Return true. If you return false (which is the default behavior of the delete_event when you don't assign a connection to it), then the window is destroyed.
17
End the function declaration.
18–37
Define a function, confirm(), to confirm that the user wishes to quit the application.
19
Allow the $window object to be used by this function.
20
Create a new dialog object called $dialog.
21
Set the title for the dialog object.
22
Set the default size of the dialog window to be 100 x 100 pixels.
23
Set the dialog window as a modal window. This means that the main application window cannot be accessed while this window exists.
24
Set the dialog window as a transient of the main window. A transient window does not cause an extra taskbar item to be created in the OS for the window. This is currently broken on Windows but works under Linux.
25
Set the resize policy for the window. The resize() method takes three boolean (true or false/0 or 1) arguments:
allow_shrink— Allow the window to be smaller than its children (this should always be set to false).
allow_grow— Allow the window to be made greater than its original size.
auto_shrink— Allows the window to override its default size settings to shrink to the size of any child windows.
26
Create a new label object called $label.
27
Create a new button object called $button_yes. This is used as the "OK" button in the dialog box.
28
Use the connect() method to cause the destroy() function to be called when this button is clicked.
29
Create a new button called $button_no. This is used as the "Cancel" button in the dialog box.
30
Use the connect() method to cause the close_dialog() function to be called. Note that the connect method has an extra argument tagged on at the end. Normally, the connect() method has two arguments: the action (such as clicked) and the function that is called (such as close_dialog). You can add a third argument, which is an additional variable that is sent to the function. In this case, we are sending the dialog window object $dialog. Since we are sending two arguments, and the connected function usually takes only one by default (which is the object that is making the connection), you must define the default object ($button) and the additional object ($dialog) in the function for which you are creating the connection, in this case, close_dialog().
31
Since the GtkDialog object has built-in "boxes" for content, you need to assign a variable name for that object. Here you assign a variable name to the GtkDialog's vbox.
32
Assign a variable name to the GtkDialog's action_area.
33
Pack the label object into the vbox.
34–35
Pack the OK and Cancel buttons in the GtkDialog's action_area.
36
Show the contents of the dialog box.
37
End the function declaration.
38
Create a new window object called $window.
39
Connect the 'delete_event' method of the window to the delete_event() function that you defined earlier. The 'delete_event' method is called when the user clicks the "X" button in the upper right corner of the application. In the previous examples, we left this method out since we wanted to directly destroy the window.
40
Create a new button object called $button.
41
Connect the 'clicked' method of the button to the confirm function.
42
Add the button to the window using the add() method.
43
Issue the show_all() function call to the $window object so that everything within the object is displayed.
44
Call the Gtk::main() method to run the script.
A Simple PHPGTK Application
This next application uses GTK widgets to create a small application that encodes passwords. You enter a username and password into their respective fields, then click the "Generate…" button to create an encrypted username and password combination that can be used in some of the previous scripts in this book. The username and password are displayed in a text box in the middle of the application. When you have created as many passwords as needed, you can click the "Write To File" button to write the data to a text file. See Figure 12-8 for example output.
Script 12-6 password_gen.php
1. <?php
2. if (!class_exists('gtk')) {
3. if (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN')
4. dl('php_gtk.dll');
5. else
6. dl('php_gtk.so');
7. }
8. function destroy(){
9. Gtk::main_quit();
10. }
11. function generate(){
12. global $user_in, $pass_in, $output;
13. $text = $user_in->get_text() . ":" . encoded($pass_in->get_text()) . "\n";
14. $output->insert(null, null, null, $text);
15. $user_in->set_text('');
16. $pass_in->set_text('');
17. }
18. function encoded($text) {
19. return md5($text);
20. }
21. function write(){
22. global $output;
23. $text = $output->get_chars(0,-1);
24. $file = fopen("pass.txt", "w") or die("CANNOT OPEN FILE");
25. fwrite($file, $text);
26. fclose($file);
27. $output->delete_text(0,-1);
28. $complete = "Passwords written to file!\n";
29. $output->insert(null, null, null, $complete);
30. }
31. $window = &new GtkWindow();
32. $window->set_default_size(300, 300);
33. $window->connect('destroy', 'destroy');
34. $user_label = &new GtkLabel("Username: ");
35. $user_in = &new GtkEntry();
36. $user_box = &new GtkHBox();
37. $user_box->pack_start($user_label, false, false, 0);
38. $user_box->pack_start($user_in, false, false, 0);
39. $pass_label = &new GtkLabel("Password: ");
40. $pass_in = &new GtkEntry();
41. $pass_box = &new GtkHBox();
42. $pass_box->pack_start($pass_label, false, false, 0);
43. $pass_box->pack_start($pass_in, false, false, 0);
44. $button1 = &new GtkButton('Generate Username:Password!');
45. $button1->connect('clicked', 'generate');
46. $output = &new GtkText();
47. $button2 = &new GtkButton('Write To File');
48. $button2->connect('clicked', 'write');
49. $vbox = &new GtkVBox();
50. $vbox->pack_start($user_box);
51. $vbox->pack_start($pass_box);
52. $vbox->pack_start($button1);
53. $vbox->pack_start($output);
54. $vbox->pack_start($button2);
55. $window->add($vbox);
56. $window->show_all();
57. Gtk::main();
58. ?>
Figure 12-8. password_gen.php
Script 12-6. password_gen.php Line-by-Line Explanation LINE
DESCRIPTION
2
If the GTK class has not been defined, then execute lines 3–7. If the GTK class has been defined, then the script needs to do nothing.
3
Check the first three characters of the PHP_OS environment variable.
4
If it is WIN, then the system is running Windows. Load the PHPGTK library for Windows.
5–7
If the PHP_OS environment variable is not WIN, then assume that PHP is running on a Linux system and load the Linux PHPGTK library.
8–10
Define a function, destroy(), that stops the script when called.
9
Execute the Gtk::main_quit() method to stop the script and close the GTK window.
10
End the function declaration.
11–17
Define a function, generate(), that encrypts a username/password combination entered by the user and prints it to a text box inside the application.
12
Assign the $user_in, $pass_in, and $output variables as global so that they can be used inside this function.
13
Assign the user-entered username and password to the $text variable.
14
Insert the username and encoded password into the text box using the insert() method for the text object. insert() takes four arguments:
Font— A GDK Font object.
Color— A GDK color object.
Size— The number of characters you wish to insert.
String— The text to be inserted.
For simplicity, I set the first three arguments as null so that the default values are used.
15
Clear the user-entered text in the Username field by inserting a blank string into the entry box.
16
Clear the user-entered text in the Password field by inserting a blank string into the entry box.
17
End the function declaration.
18–20
Define a function, $encode, that encodes the entered password.
19
Encode the password using the md5() function and return it to the calling function. You can change the encryption method to suit your needs (for example by using the crypt() function).
20
End the function declaration.
21–30
Define a function, write(), that writes the encoded username/passwords to a file.
22
Assign the $output variable as global so that it can be used inside this function.
23
Get the entire text from the text object using the get_chars() method and place it into the $text variable. The get_chars() method takes two arguments:
The start position of the text (0 is the first position).
The end position of the text.
Using 0 and -1 as arguments causes the entire text in the text object to be collected.
24
Open a file for writing. Note that if you have an existing file with the same name, it will be written over with the new username/password combos. You must also set permissions on the file so that it can be written to by PHP.
25
Write the text to the file.
26
Close the file.
27
Delete the text in the text area.
28
Create a string of text notifying the user that the text has been written.
29
Write the string to the text area. Note that you will need to close and restart the application if you wish to create passwords again.
30
End the function declaration.
31
Create a new window object called $window.
32
Set the size of the window to be 300 x 300 pixels.
33
Connect the 'destroy' method of the window to the destroy() function that you defined earlier. The 'destroy' method is called when the user clicks the "X" on the top right of the window.
34
Create a new label object for the username label.
35
Create a new text-entry object for the username.
36
Create a new hbox in which to place the username label and text-entry objects.
37
Pack the label object into the hbox.
38
Pack the text-entry object into the hbox.
39
Create a new label object for the password label.
40
Create a new text-entry object for the password.
41
Create a new hbox in which to place the password label and text-entry objects.
42
Pack the label object into the hbox.
43
Pack the text-entry object into the hbox.
44
Create a new button object for the "Generate…" button.
45
Connect the "Generate…" button to the generate() function.
46
Create a new text object to display the output.
47
Create a new button object for the "Write To File" button.
48
Connect the "Write To File" button to the write() function.
49
Create a new vbox to hold the other boxes and buttons.
50
Pack the hbox containing the Username label and entry into the vbox.
51
Pack the hbox containing the Password label and entry into the vbox.
52
Pack the "Generate…" button into the vbox.
53
Pack the text object into the vbox.
54
Pack the "Write To File" button into the vbox.
55
Add the vbox to the window.
56
Show all of the contents of the window.
57
Execute the script.
Tuesday, July 14, 2009
Advanced PHP for Web Professionals By Christopher Cosentino Chapter 12
Posted by Mr Procces at 8:53 AM 0 comments
Labels: PHP, Web Applications, Web Development, Web Programming
Advanced PHP for Web Professionals By Christopher Cosentino Chapter 11
Overview
PHP is able to dynamically generate and modify images. To use this advanced functionality in PHP, you need to include some additional libraries that are not included by default when you install PHP.
The GD Library
PHP provides the ability to create and manipulate images through support of the GD library. The GD home page is located at www.boutell.com/gd/ and lists the definition of GD as:
Enabling GD Support
GD support is not enabled by default. You must enable the extension if you are using Windows, or compile-in/link the extension if you are using Linux.
Enabling GD for Windows
Enabling GD support for Windows is very easy, since the DLL file for GD has been pre-compiled and included in the basic PHP Windows installation. Open your php.ini file in a text editor and search for the line that says:
extension_dir = ./
This line should point to the place where your PHP extensions reside. If you copied the extensions to the same directory as the php.ini file, then you do not need to modify the line. If you did not move the PHP extensions to the same directory as the php.ini file, then you need to edit the line to point to the correct directory, for example:
extension_dir = C:\Apache\php\extensions
Next, find the section in the php.ini file that says:
;Windows Extensions
This line will be followed by many lines of Windows .dll extensions for PHP. To enable GD support, uncomment the line (delete the semicolon at the beginning of the line) that contains the GD library DLL:
extension=php_gd.dll
After you have uncommented the line, save the file and restart the Apache Web server.
You can verify the GD has been correctly installed by using the phpinfo(); as seen in Figure 11-1.
Figure 11-1. phpinfo() results with GD enabled for Windows
Enabling GD for Linux
If you compiled PHP using Apache's APXS functionality (compile --with-apxs=/path/to/apache/bin/apxs), then adding functionality to the PHP module is a breeze.
Before recompiling PHP, I first suggest that you delete the config.cache file and clean up files left over from the previous compile. This can be done as follows:
cd /path/to/php/source
rm config.cache
make clean
After you issue the make clean command, you will notice quite a few files being deleted. Don't worry about it. The make program is just cleaning up files it won't need when you recompile. If you don't run the make clean command, then you may start running into some problems. If you have been compiling PHP with no problems and suddenly it won't compile right even though you haven't changed anything, it's a good bet that the make clean command will solve your problem.
Once you've cleaned up the mess from the previous compile, you can get started with the new compile.
To compile PHP with GD support enabled:
./compile --with-apxs=/usr/local/apache/bin/apxs \
--with-gd \
--with-gd-native-ttf \
--with-ttf
After the configure runs, issue the command:
make
Assuming no errors occur, you can then issue the command:
make install
The final command copies the libphp4.so library file to /path/to/apache/libexec/.
Restart Apache to load the new library:
/path/to/apache/bin/apachectl restart
You can verify the GD has been correctly installed by using the phpinfo() function.
"An ANSI C library for the dynamic creation of images. GD creates PNG and JPEG images, among other formats. GD does not create GIF images."
Previously, GD was widely used for creating GIF images, but Unisys Corp., the company which owns the patent for the LZW compression algorithm used in GIF images, recently started enforcing their patent rights, and GIF creation was subsequently dropped from the list of GD features.
However, GD can still be used to create JPEG and PNG graphics, both of which are supported by most of the popular Web browsers. In addition, GD supports creation of WBMP images, which are used for small mobile wireless devices.
Basically, GD should cover your bases nicely for your scripted graphic creation needs. Note that GD is not a paint program. It can be used to manipulate existing images by adding text to them or to create new images out of two or more existing images.
GD is most useful in creating button images, as well as embedding captions in existing images.
Generating Graphics with GD
Once you have configured PHP to use the GD library, you can start creating some graphics.
The Lines and Text
The base function to use when creating images using the GD library is the ImageCreate() function.
$length = 300;
$width = 100;
$image = ImageCreate($length, $width);
Once you have created an image, you need to add some color to it using the ImageColorAllocate() function. ImageColorAllaocate() takes as its arguments the image you are creating and an RGB color value:
$blue = ImageColorAllocate($image, 0, 0, 255);
So now you have created a box and given it a color. Now all that is left is to display the image in a browser. To do that, you need two more lines of code:
Header("Content-type: image/png");
ImagePNG($image);
The first line tells the browser to expect a PNG image, and the second line is the function that outputs the image that you have created to the browser. Before you go typing that all in, though, let's add some text.
Now that you have your created your base image, you can add some shapes onto it. The most basic "shape" you can add to your image is a line. A line is created by connecting two endpoints on your image. Lines are created with the ImageLine() function:
ImageLine($image, $x1, $y1, $x2, $y2, $color);
The points which you specify in the ImageLine() function are x,y vertices, almost like those used in plotting points in mathematics, except that the grid used here is a little different. The grid used to place shapes in GD starts at the x,y point 0,0 in the top left corner. The x coordinate moves to the right, as in a normal math graph. The y coordinate however, moves down, instead of upward as it does in a math graph, so the point (10,20) is 10 pixels to the right of the upper left corner of the image and 20 pixels down.
GD also supports some basic string functions that allow you to print text onto the image. You can only use one basic font and there are only five sizes, listed in Table 11-1, but it works well for graphs and charts where flashy fonts are not required. To add text to your image:
IMageString($image, $fontSize, $x1, $y1, $string,
$color);
Table 11-1. Font Sizes That Can Be Used for GD String Functions FONT SIZE
FONT
SIZE
1
gdFontTiny
8 x 5 pixels
2
gdFontSmall
13 x 6 pixels
3
gdFontMediumBold
13 x 7 pixels
4
gdFontLarge
15 x 8 pixels
5
gdFontGiant
15 x 9 pixels
The next script creates a simple graph, shown in Figure 11-2, using the ImageLine() and ImageString() functions to illustrate how the graph works and the appearance of the text sizes:
Script 11–1 gd_grid.php
1. <?
2. $height = 301;
3. $width = 301;
4. $image = ImageCreate($width, $height);
5. $white = ImageColorAllocate($image, 255, 255, 255);
6. $blue = ImageColorAllocate($image, 0, 0, 255);
7. $red = ImageColorAllocate($image, 255, 0, 0);
8. for($i = 0; $i < $height; $i = $i + 20) {
9. ImageLine($image, $i, 0, $i, $height, $blue);
10. ImageLine($image, 0, $i, $width, $i, $blue);
11. ImageString ($image, 1, $i+2, 0, $i, $red);
12. ImageString ($image, 1, 2, $i, $i, $red);
13. }
14. ImageString ($image, 1, 82, 22, "Size 1 Font", $red);
15. ImageString ($image, 2, 82, 62, "Size 2 Font", $red);
16. ImageString ($image, 3, 82, 102, "Size 3 Font", $red);
17. ImageString ($image, 4, 82, 142,"Size 4 Font", $red);
18. ImageString ($image, 5, 82, 182, "Size 5 Font", $red);
19. header ("Content-type: image/png");
20. ImagePng($image);
21. ImageDestroy($image);
22. ?>
Figure 11-2. gd_grid.php
Script 11-1. gd_grid.php Line-by-Line Explanation LINE
DESCRIPTION
2
Define a height of 301 pixels for the image (the extra pixel is so that the final line on the outside of the grid is visible).
3
Define a width of 301 pixels for the image.
4
Create the base image using the ImageCreate() function, specifying the height and width defined earlier.
5
Define the $white variable using the ImageColorAllocate() function. The arguments allow the color white (RGB value 255,255,255) to be used by $image. It is also used as the background color for $image, since it is the first color allocated to $image.
6
Define the color blue and allow it to be used by $image.
7
Define the color red and allow it to be used by $image.
8–13
Create a for loop to loop through and draw a line every 20 pixels starting at the coordinate 0,0 (upper left corner of image).
9
While in the for loop, create a line inside $image from point $i, 0 (0,0 20,0 40,0, etc.) to point $i, $height (0, 301 20,301 40,301, etc.). Each iteration of the loop draws a vertical line from the top of the image to the bottom of the image spaced at 20 pixels from left to right.
10
While in the for loop, create a line inside $image from point 0, $i (0,0 0,20 0,40, etc.) to point $width, $i (301,0 301,20 301,40, etc.). Each iteration of the loop draws a horizontal line from the left of the image to the right of the image spaced at 20 pixels from top to bottom.
11
While in the for loop, create a small label to display the current pixel of the current vertical line using the ImageString() function. Remember, ImageString($image, $fontsize, $x, $y, $text, $color);
12
While in the for loop, create a small label to display the current pixel of the current horizontal line ImageString() function.
13
End the for loop.
14–18
Create some text at various points on the screen to display the various font types.
19
Send the image headers to the user's browser.
20
Display the image, $image, that you have created.
21
Destroy the image from the server memory, since it has already been sent to the browser.
Adding TrueType Font to Your Images
You can add better-looking text to your images using TrueType fonts. The function that accomplishes this is ImageTTFText().
ImageTTFText($image, $size, $angle, $xcoord, $ycoord,
$color, $font, $text);
The arguments for ImageTTFText() are:
$image— The image handler for the image in which you are placing this font.
$size— The size of the text, in points.
$angle— The angle in number of degrees that the text slants. Note that this isn't the slant of the characters themselves, such as when using italic font, but the slant of the "line" on which the text is written.
$xcoord— The x coordinate in the image of the bottom left of the first character in $text.
$ycoord— The y coordinate in the image of the bottom left of the first character in $text.
$color— The color of the text. This must be a color that has been previously allocated to the $image.
$font— The location of the TrueType Font file (.ttf).
$text— The text that you want to print out in the image.
Script 11–2, gd_text.php, provides an example of a simple GD image with some text, shown in Figure 11-3.
Script 11–2 gd_text.php
1. <?
2. $height = 150;
3. $width= 300;
4. $image = ImageCreate($width, $height);
5. $blue = ImageColorAllocate($image,0,0,255);
6. $white = ImageColorAllocate($image,255,255,255);
7. $font = "arial.ttf";
8. $text = "Advanced\n\rPHP";
9. ImageTTFText($image, 50, 0, 10, 55, $white, $font, $text);
10. Header("Content-type: image/png");
11. ImagePNG($image);
12. ImageDestroy($image);
13. ?>
Figure 11-3. gd_text.php
Script 11-2. gd_text.php Line-by-Line Explanation LINE
DESCRIPTION
2
Define a height of 300 pixels for the image.
3
Define a width of 150 pixels for the image.
4
Create the base image using the ImageCreate() function, specifying the height and width defined earlier.
5
Define the $blue variable using the ImageColorAllocate() function. The arguments allow the color blue (RGB value 0,0,255) to be used by $image. It is also used as the background color for $image, since it is the first color allocated to $image.
6
Define the color white and allow it to be used by $image.
7
Assign a font file to the variable $font. You need to place this font file in the same directory from which this script is executed. In Windows, you can typically find TrueType font files in C:\Windows\Fonts or C:\WINNT\fonts. You can use any TrueType font file; just make sure the file name of the TrueType font that you copy into the same directory as this script is defined here.
8
Assign a simple text string to the $text variable. You can use the syntax "\n\r" for newlines and returns. Some *nix sytems do not require the "\r".
9
Add text to $image using the ImageTTFText() function.
10
Send the image headers to the user's browser.
11
Display the image, $image, that you have created.
12
Destroy the image from the server memory, since it has already been sent to the browser.
Adding Text to Base Images
GD is also useful in that you can import an existing image to work with as the base of a new image. For example, you can take a blank image of a button, and then superimpose text onto it. This way, you can create multiple buttons from only one base image file.
To create a button, you need to use two new GD functions. These functions are ImageCreateFromPNG() and ImageTTFBbox().
ImageCreateFromPNG() is a very simple function that just creates a new image from the existing PNG file:
$image = ImageCreateFromPNG("blank_blue_bttn.png");
You would then manipulate $image just as you would if you had created it from ImageCreate.
You can get the length and width of the $image you create using the ImageCreateFromPNG() function by using the ImageSX($image) and ImageSY($image) functions, respectively; SX and SY mean Size of the X axis (the width) and Size of the Y axis (the height) of the image.
The ImageTTFBbox() function is a fairly complicated function that returns the corner coordinates of a bounding box around your text. You provide the size, angle, font, and text string of the text and the function returns the corner coordinates in an array. This array allows you to calculate how much space your text uses. Usage of the script is:
$textbox = ImageTTFBbox($size, $angle, $font, $text);
Table 11-2 lists the coordinates for each of the elements in the array returned from the ImageTTFBbox() function. Additionally, Figure 11-4 provides a graphical representation of the array elements in the bounding box.
Figure 11-4. Array Elements for the coordinates in a bounding box
Table 11-2. Coordinates Returned from ImageTTFbox() ARRAY POSITION
BOUNDING BOX COORDINATE
0
X coordinate of the lower left hand corner
1
Y coordinate of the lower left hand corner
2
X coordinate of the lower right hand corner
3
Y coordinate of the lower right hand corner
4
X coordinate of the upper right hand corner
5
Y coordinate of the upper right hand corner
6
X coordinate of the upper left hand corner
7
Y coordinate of the upper left hand corner
This next script demonstrates how to take a blank button and add text to make many different buttons. See Figure 11-5 for an example of the output.
Script 11–3 gd_button.php
1. <?
2. $image = ImageCreateFromPNG("blank_blue_bttn.png");
3. $white = ImageColorAllocate($image,255,255,255);
4. $font = "arial.ttf";
5. if(!isset($text)) { $text = "BUTTON"; }
6. $size = "20";
7. $angle="0";
8. $textbox = ImageTTFBbox($size, $angle, $font, $text);
9. $textbox_width = abs($textbox[2] - $textbox[0]);
10. $textbox_height = abs($textbox[7] - $textbox[1]);
11. $image_width = ImageSX($image);
12. $image_height = ImageSY($image);
13. $x = ($image_width - $textbox_width) / 2;
14. $y = ($image_height - $textbox_height) / 2 + $textbox_height;
15. ImageTTFText($image, $size, $angle, $x, $y, $white, $font, $text);
16. Header("Content-type: image/png");
17. ImagePNG($image);
18. ImageDestroy($image);
19. ?>
Figure 11-5. gd_button.php
Script 11-3. gd_button.php Line-by-Line Explanation LINE
DESCRIPTION
2
Create a new image using the ImageCreateFromPNG() function, specifying "blank_blue_bbtn.png" as the base image.
3
Define the $white variable using the ImageColorAllocate() function. The arguments allow the color white (RGB value 255,255,255) to be used by $image.
4
Assign a font file to the variable $font. You need to place this font file in the same directory from which this script is executed. In Windows, you can typically find TrueType font files in C:\Windows\Fonts or C:\WINNT\fonts. You can use any TrueType font file; just make sure the file name of the TrueType font that you copy into the same directory as this script is defined here.
5
Check to see if the $text variable is set. If it is not, then assign the string "BUTTON" to $text. The reason for this will be apparent in the next script.
6
Assign a font size to the $size variable.
7
Assign an angle to the font using the $angle variable. This argument is required for the ImageTTFbox variable. You assign it to be "0", since you want the text to be horizontal across the page, with no angle.
8
Create the text box by assigning the value of the function ImageTTFbox() to the variable $textbox.
9
Determine the width of the text box by getting the absolute value of the text box's lower right-hand x coordinate subtracted from the text box's lower left-hand x coordinate. You use the abs() function, since you want to have a positive number, even though the resulting number may be negative. The abs() value of a number removes the negative sign.
10
Determine the height of the text box by getting the absolute value of the text box's upper left-hand y coordinate subtracted from the text box's lower left-hand y coordinate.
11
Determine the width of the entire image by using the ImageSX() function.
12
Determine the height of the entire image by using the ImageSY() function.
13
Determine where to place the x value of the text in relation to the base image by subtracting the width of the text box from the width of the base image and dividing by 2. If your base image was 10 pixels wide, and your text box was 8 pixels wide, then your resulting x position for the text would be at 1. This leaves one pixel on either side of the text box for the base image to overlap. This, of course, does not work very well if your text box is larger than your base image.
14
Determine where to place the y value of the text in relation to the base image by subtracting the text box height from the base image height and dividing by 2. Then add the height of the text box. As above, if your base image was 10 pixels high and your text box was 8 pixels high, this would give you a value of 1 for the y coordinate. However, since you specify the lower left-hand corner (and not the upper left-hand corner as you might assume), you need to also add the height of the text box, giving you a value of 9.
15
Add the text to the base image using the ImageTTFText() function. You use the x,y values that you have just determined to properly space the text in relation to the base image.
16
Send the image headers to the user's browser.
17
Display the image, $image, that you have created.
18
Destroy the image from the server memory, since it has already been sent to the browser.
You can easily reuse this script, as shown in Figure 11-6, by adding a parameter to the URL—for example, http://localhost/gd_text.php?text=HOME.
Figure 11-6. gd_text.php?text=HOME
You can also call several buttons on one page using the HTML <img> tags and referencing the same script using different values for "text", as in the following short script. See Figure 11-7 for example output.
Script 11–4 buttons.html
1. <html>
2. <head>
3. <title>Buttons!</title>
4. </head>
5. <body>
6. <img src="gd_button.php?text=HOME"><br>
7. <img src="gd_button.php?text=GAMES"><br>
8. <img src="gd_button.php?text=NEWS"><br>
9. <img src="gd_button.php?text=LINKS">
10. </body>
11. </html>
Figure 11-7. buttons.html
Creating Rectangles with GD
In addition to text and lines, you can also easily use GD to create rectangles, either filled with a color or unfilled (use ImageRectangle() with the same arguments to create an unfilled rectangle):
ImageFilledRectangle($image, $x1coord, $y1coord, $x2coord, $y2coord, $color);
The arguments to ImageRectangle() and ImageFilledRectangle() are:
$image— The image handler for the image in which you are placing this rectangle.
$x1coord— The x coordinate of the upper left corner of the rectangle.
$y1coord— The y coordinate of the upper left corner of the rectangle.
$x2coord— The x coordinate of the lower right corner of the rectangle.
$y2coord— The y coordinate of the lower right corner of the rectangle.
$color— The color of the rectangle. This must be a color that has been previously allocated to the $image.
Rectangles are plotted from upper left to lower right. Remember that the coordinate 0,0 starts at the upper right corner of the image, not in the lower right corner as is normally done when plotting in mathematics.
This next script uses lines, text, and rectangles to create a dynamic bar graph. You can plug various numeric values into the graph and specify a size and labels for the image. The graph automatically changes scale depending on the numeric values you enter. Figure 11-8 illustrates the graph-entry screen.
Script 11–5 gd_graph.php
[View full width]
1. <?
2. if(!isset($render)) {
3. ?>
4. <form action=gd_chart.php method=post>
5. <br>Values (enter numbers seperated by a comma):
6. <br><textarea cols="40" rows="4" name="values_in">50,100,150,20,75,23,55,200,
135,63,103,163</textarea>
7. <br>Labels (enter labels seperated by a comma):
8. <br><textarea cols="40" rows="4" name="labels_in">Jan,Feb,Mar,Apr,May,Jun,Jul,
Aug,Sep,Oct,Nov,Dec</textarea>
9. <br>Image Height: <input type="text" name="height"
value="500">
10. <br>Image Width: <input type="text" name="width" value="500">
11. <br>Vertical Scale Label: <input type="text" name="vlabel" value="Widget
Production in Thousands">
12. <br>Horizontal Scale Label: <input type="text" name="hlabel" value="Month">
13. <br><input type="submit" name="render" value="Render Chart">
14. </form>
15. <?
16. } else {
17. $values = explode(",", $values_in);
18. $labels = explode(",", $labels_in);
19. $xoffset = $width / 10;
20. $yoffset = $height / 10;
21. /* Determine Font Sizes */
22. if (($width > 400) and ($height > 400)) {
23. $sm_font = 2;
24. $md_font = 3;
25. $lg_font = 4;
26. } else {
27. $sm_font = 1;
28. $md_font = 2;
29. $lg_font = 2;
30. }
31. /* Create Base Image */
32. $image = ImageCreate($width, $height);
33. /* Allocate Colors */
34. $white = ImageColorAllocate($image, 235, 235, 235);
35. $blue = ImageColorAllocate($image, 0, 0, 255);
36. $red = ImageColorAllocate($image, 255, 0, 0);
37. /* Find Largest Item In Values Array */
38. $greatest = 0;
39. for($i = 0; $i < sizeof($values); $i++) {
40. $values[$i] = trim($values[$i]);
41. if($values[$i] > $greatest)
42. $greatest = $values[$i];
43. }
44. /* Determine Scale And Spacing */
45. $scale = ($height / $greatest) * .8;
46. $h_spacing = floor(($width - $xoffset) / sizeof($values)) / 2;
47. $barwidth = $h_spacing;
48. /* Draw Bars, Labels, and Values*/
49. $x = $xoffset + 5;
50. for($i = 0; $i < sizeof($values); $i++) {
51. $y = $height - ($values[$i] * $scale);
52. ImageFilledRectangle($image, $x, $y - $yoffset, $x
+ $barwidth, $height - $yoffset, $red);
53. ImageString ($image, $sm_font, $x, $y-$yoffset-12, $values[$i], $blue);
54. $labels[$i] = trim($labels[$i]);
55. ImageString ($image, $md_font, $x, $height - $yoffset + 2, $labels[$i], $blue);
56. $x+=($h_spacing * 2);
57. }
58. /* Determine Ticks */
59. $ticks_every = "1";
60. for($i = 1; $i < (round(log10($greatest))); $i++) {
61. $ticks_every .= "0";
62. }
63. /* Draw Ticks And Numbers */
64. for($i = 0; $i < $height - $yoffset; $i+= $ticks_every*$scale) {
65. $y = ($height - $yoffset) - $i;
66. ImageLine($image, $xoffset / 2, $y, $xoffset, $y, $blue);
67. ImageLine($image, $xoffset + 5, $y, $width + $xoffset, $y, $white);
68. ImageString ($image, $sm_font, $xoffset / 2, $y, $i / $scale , $blue);
69. }
70. /* Draw Left and Bottom Edge of Graph */
71. ImageLine($image, 0 + $xoffset, 0, $xoffset, $height - $yoffset, $blue);
72. ImageLine($image, 0 + $xoffset, $height - $yoffset, $width + $xoffset, $height -
$yoffset, $blue);
73. /* Draw Axis Labels */
74. ImageString ($image, $lg_font, $width / 2, $height - ($yoffset / 2), $hlabel, $blue);
75. ImageStringUp ($image, $lg_font, 1, $height - $height / 3, $vlabel, $blue);
76. /* Display Image */
77. header ("Content-type: image/png");
78. ImagePNG($image);
79. ImageDestroy($image);
80. }
81. ?>
Figure 11-8. gd_graph.php before submitting values
Script 11-5. gd_graph.php Line-by-Line Explanation LINE
DESCRIPTION
2
Check to see if the $render variable is set. If it is not, then execute lines 3–15, else execute from line 16 to the end of the script.
3–15
Print out a standard HTML form asking for some values for the script. You can specify:
The numerical values for the bar graph, separated by a comma.
The corresponding labels for those bars, separated by a comma. You should have one label for each value you input in the form above.
The width and height of the chart.
The labels for the scales.
The script enters some values as a default for testing.
17
Take the values entered from the form and turn them into an array using the explode() function and assigning the array to the $values variable.
18
Take the labels entered from the form and turn them into an array using the explode() function and assigning the array to the $labels variable.
19
Determine the offset for the x-axis, which is 1/10th the total width of the chart. This offset allows for room for some text on the left side of the chart. 1/10th is an arbitrary number that works well for charts that are 300 pixels wide or larger.
20
Determine the offset for the y-axis, which is 1/10th the total height of the chart. This offset allows for room for some text on the bottom of the chart.
22–30
Determine the appropriate font sizes for the chart. The chart uses three sizes: small, medium, and large. If the chart is smaller than 400 x 400, then use smaller fonts. Otherwise, use larger fonts.
32
Create the base image using the width and height obtained from the form.
34–36
Allocate three colors to the image: white, blue, and red. The white here (RBG: 235,235,235) is actually a little grey so that the chart stands out better from the page.
38
Initialize the variable $greatest with 0.
39–43
Use a for loop to loop through the $values array and find the highest value. Once we know the largest number that has to be plotted, we can determine the scale of the chart. Note that, on line 40, the script trims any whitespace from the numerical value. This is to avoid errors if users separate their values when they enter them into the form with spaces in addition to commas.
45
Determine the scale of the chart by taking 80 percent of the largest value divided by the height. This creates a scale that allows the largest bar on the graph to occupy 80 percent of the height.
46
Determine the horizontal spacing for the individual bars in the chart by rounding off the width minus the width offset (which gives us the total amount of space available to the bars), then dividing it by the number of values that need to be charted, and finally dividing that number by two, since we want to create an equal space-bar ratio.
47
Determine the width of the bar. Here we set it to the same as the spacing, but you may find you need to alter it a bit for your specific chart. If you do alter it, take into account that the above line allocates half of the space to spacing. You may need to alter that line as well.
49
Initialize the variable $x to be five pixels more than the offset. This allows you to start the first bar five pixels away from the edge that you allocated as space for text.
50–57
Create a for loop that loops through $values array and draws the bars on the bar graph.
51
Determine the base y coordinate of the bar. This is a bit tricky, because the script automatically scales the presentation of the bars depending on the size of the greatest value. To determine the base y coordinate, you need to subtract the height from the size of the value multiplied by the scale. If the value was 10, then it would automatically be scaled down to 8. You then subtract the y offset (for the extra space at the bottom for text) and the overall height of the image (because the GD library uses the top left corner of the image as the 0,0 point instead of the lower left corner). The script is calculating how far the bar should start from the TOP of the image, rather than how long the bar should actually be (which would, of course, be easier, but the GD library doesn't seem to want to let you do it that way).
52
Create the bar using the ImageFilledRectangle() function. The first two coordinate arguments ($x, and $y) plot the top left corner of the rectangle. The second two coordinate arguments ($x + $barwidth, and $height – $yoffset) plot the bottom right of the rectangle.
53
Write the numerical value of the bar above the bar using the ImageString() function. The y coordinate is listed as $y – 12 so that the value is written 12 pixels above the bar itself. Remember that the script calculates how far from the top of the image the text should be written, not how far from the top of the bar.
54
Use the trim() function to trim any whitespace from around the label.
55
Write the label of the current bar at the bottom of the bar. Notice that the y coordinate is $height – $yoffset + 2. That puts the label two pixels BELOW the top of the whitespace along the bottom of the image.
56
Increment the value of the next x coordinate so that it allows room for ample space between the bars. You'll have to modify this line in addition to lines 46 and 47 if you want to muck about with the spacing. Note that in most cases the way it is coded here should work fine.
57
Close the for loop.
59
Initialize the variable $ticks_every.
60–62
Create a for loop to determine the scale of the tick marks that occur on the bars. The tick marks are actually lines that are the same color as the background of the image. They are drawn over the bars to make the bars look like equally segmented blocks. Since it's conceivable that you could have values that are in the tens or in the thousands, you would not automatically want to have a tick mark every 10 units. This for loop is a cheap way to determine how many "ticks" should be spaced in between tick lines. It takes the greatest value and computes the log10 of that value. Each time it does so and the value is less than $i, it adds a "0" to the $ticks_every function. Basically, if the greatest value is 1 to 316, then there is a line every 10 ticks (not necessarily pixels, because you need to take into account scaling). From 316 to 3162, it uses tick marks every 100 ticks, etc. It's not an exact science, but it gives pretty good results.
64–69
Create a for loop that draws "tick" marks on the bars, as well as numbers along the y axis of the graph, spaced according to the interval defined in $ticks_every.
65
Calculate the y coordinate for the tick mark.
66
Draw a short line to the left of the offset. This short blue line is used as a divider between the numerical scale that is displayed along the y axis of the chart.
67
Draw a long line across the image to divide the bars into equal easily-readable segments.
68
Display the "tick" number to the left of the y axis for the chart.
69
Close the for loop.
71
Draw the blue x-axis line across the bottom of the image (between the bars and their labels).
72
Draw the blue y-axis line along the right side of the image (between the unit numbers and the bars).
74
Display the label for x-axis. In the default case, this is "Month."
75
Display the label for the y-axis. In the default case, this is "Widget Production in Thousands."
77
Send the image headers to the user's browser.
78
Display the image, $image, that you have created.
79
Destroy the image from the server memory, since it has already been sent to the browser.
80
Close the if statement started on line 2.
Figure 11-9 shows example output for the script.
Figure 11-9. gd_graph.php after submitting values
Posted by Mr Procces at 8:51 AM 0 comments
Labels: PHP, Web Applications, Web Development, Web Programming
Advanced PHP for Web Professionals By Christopher Cosentino Chapter 10
Overview
XML (eXtensible Markup Language) is finally in the mainstream and showing the benefits that the faithful have been spouting since XML's inception. A quick look at XML support in Apache and the Java language should be enough to affirm that XML is here to stay.
PHP has built-in support for XML in the form of an extension. Unlike other extensions, XML is compiled in by default so you should not require any additional configuration to get XML to work. The PHP XML extension uses James Clark's expat library, complete details of which can be found at www.jclark.com/xml/.
A quick look, as in Figure 10-1, at a PHP page containing the phpinfo() function should confirm that XML is enabled in your build of PHP.
Figure 10-1. Confirming XML Is Enabled Using phpinfo()
PHP's XML extension allows you to parse an XML file. You can read through an XML file following its nodes and branches and perform operations on the data contained within.
PHP's XML extension does not validate XML files. That is, it cannot compare an XML file to a DTD (Document Type Definition) and verify that the file is valid according to the DTD. Having said that, you must make sure that the XML you send to PHP is either valid or well-formed.
Creating an XML Parser
Before you can use PHP's XML extension to read XML files, you must create a parser. Since XML documents are extensible, there is no way for PHP to know what elements you are searching for in the XML. Therefore, you must tell PHP how it should parse the document. You tell PHP how to parse the document by defining a new XML parser instance and then defining element handlers and character handlers. An element handler is simply a function that runs when an element is encountered in the XML. You need to define two element handlers, one handler for when an element is encountered by PHP and another handler for when the PHP parser leaves the current element. Additionally, you must specify a handler for the character data that exists between elements. If you are a little rusty on your XML-speak, here is a short example of an XML document:
<?xml version="1.0"?>
<document>
<title>XML Is Easy</title>
<body>Demystifying XML. Read about it here.</
body>
</document>
Looks simple enough, right? That's because it is. Since you are already familiar with HTML, the basics of XML should be readily apparent. Elements are enclosed by "<" and ">". Start and end elements are differentiated by the presence or absence of the "/" symbol. Think of a first-level heading tag: <h1>A Heading</h1>. The first <h1> tag starts the element, and the second </h1> tag closes the element. The characters in between are the character data. Now this is a hugely simplified example, but if you are unfamiliar with XML, then it should shed some light on what we are about to do as we create a parser.
Defining the XML Parser
You define an XML Parser by using the xml_parser_create() function:
$parser = xml_parser_create(ENCODING);
You assign xml_parser_create() to a variable, which is passed to the other functions required for parsing XML pages. Additionally, you can optionally assign the type of encoding that the parser should use. Encoding is the character encoding in the XML document. You can choose one of three encoding types:
ISO-8859-1 (default)
US-ASCII
UTF-8
Once you have defined a new parser instance, you can then create your handlers to do the actual work of reading through an XML file.
Defining the Element Handlers
Element handlers are defined using the xml_set_element_handler() function:
xml_set_element_handler(XML_PARSER, START_FUNCTION,
END_FUNCTION);
xml_set_handler takes three arguments:
XML_PARSER— The variable that you created when you called the xml_create_parser() function.
START_FUNCTION— The name of the function to call when the parser encounters a start element.
END_FUNCTION— The name of the function to call when the parser encounters an end element.
Defining Character Handlers
Character handlers are defined using the set_character_handler() function:
xml_set_character_handler(XML_PARSER,
CHARACTER_FUNCTION);
xml_set_character_handler() takes two arguments:
XML_PARSER— The variable that you created when you called the xml_create_parser() function.
CHARACTER_FUNCTION— The name of the function to call when character data is encountered.
Starting the Parser
The final piece to the puzzle is the function that starts the whole process, xml_parse():
xml_parse(XML_PARSER, XML);
xml_parse() takes two arguments:
XML_PARSER— The variable that you created when you called the xml_create_parser() function.
XML— The XML that is to be parsed.
Cleaning Up
After you have finished parsing the document, you should free the memory holding the parser by calling the xml_parser_free() function:
xml_parser_free(XML_PARSER);
xml_parser_free() takes one argument, XML_PARSER, which is the variable that you created when you called the xml_create_parser() function.
Let's look at an example to solidify the principles we've just discussed. The following example uses all of the functions just discussed. It opens a simple XML file, aptly named simple.xml, and parses the XML within. Figure 10-2 displays the output. Here is simple.xml, which you'll need to create and place in the same directory as the xml1.php example:
Script 10-1 simple.xml
1. <?xml version="1.0"?>
2. <document>
3. <title>XML Exposed</title>
4. <body>Demystifying XML. Read about it here.</body>
5. </document>
Script 10-2 xml1.php
1. <?
2. function startElement($xml_parser, $name, $attributes) {
3. print("<p><i>Encountered Start Element For:</i>$name\n");
4. }
5.
6. function endElement($xml_parser, $name) {
7. print("<p><i>Encountered End Element For:</i>$name\n");
8. }
9.
10. function characterData($xml_parser, $data) {
11. if($data != "\n") {
12. print("<p><i>Encountered Character Data:</i>$data\n");
13. }
14. }
15.
16. function load_data($file) {
17. $fh = fopen($file, "r") or die ("<P>COULD NOT OPEN FILE!");
18. $data = fread($fh, filesize($file));
19. return $data;
20. }
21. /***** MAIN *****/
22. $file = "simple.xml";
23. $xml_parser = xml_parser_create();
24. xml_set_element_handler($xml_parser, "startElement", "endElement");
25. xml_set_character_data_handler($xml_parser, "characterData");
26. xml_parse($xml_parser, load_data($file)) or die ("<P>ERROR PARSING XML!");
27. xml_parser_free($xml_parser);
28. ?>
Figure 10-2. xml1.php
Script 10-2. xml1.php Line-by-Line Explanation LINE
DESCRIPTION
2
Create a function called startElement() to handle any start elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):
$xml_parser
$name
$attributes
3
Print a message to the screen when a start element is encountered.
4
End the function declaration.
6
Create a function called endElement() to handle any end elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):
$xml_parser
$name
7
Print a message to the screen when an end element is encountered.
8
End the function declaration.
10
Create a function called characterData() to handle the data found between elements as the script parses the XML. The function takes the following as arguments (required by PHP):
$xml_parser
$data
11–13
If there is actual character data between the elements (not just a newline character), print a message to the screen when character data is encountered.
14
End the function declaration.
16
Create a function called load_data() to read the data from an XML file into the script so that it may be parsed. The function takes one argument, $file, which is the name (with an optional path) of the XML file that you want to parse.
17
Attempt to assign a file handle to the file. If unsuccessful, kill the script and print out an error message.
18
If the file opening was successful, read in the entire file into the $data variable. Note that this is wildly inefficient for large files.
19
Return the $data variable to the calling program.
20
End the function declaration.
21
Begin the main program.
22
Assign a file name to the $file variable. Note that you can use a full path to the file, such as:
Windows: $file = "C:\winnt\xml\myxmlfile.xml";
Linux: $file = "/home/me/xml/myxmlfile.xml";
23
Create a variable called $xml_parser and assign it as a PHP xml parser using the xml_parser_create() function.
24
Define the custom start and end element handler functions that you created above using the xml_set_element_handler() function. Whenever PHP encounters a start or end element, it will use the respective function.
25
Define the custom data handler function that you created above using the xml_set_character_data_handler() function. Whenever PHP encounters character data, it will use the characterData() function.
26
Begin parsing the XML with the xml_parse function. The xml_parse() function requires the name of the XML parser ($xml_parser) and the XML data that you want to parse. In this case, we provide the XML data by using the custom load_data($file) function. If the xml_parse() function fails, then kill the script and display an error message.
27
After the xml_parse() function has completed parsing the XML, free the memory associated with the parser by using the xml_parser_free() function.
Parsing and Transforming XML Documents
The example above works well for parsing ultrasimple documents, but it doesn't take into account nested elements or attributes.
In the real world, XML documents are highly structured and precisely defined. A great deal of effort goes into designing XML information products. One reason for the amount of effort that is required is the extensible nature of XML. XML gives you a set of rules that allows you to create these complex structured documents. Those rules, however, provide for an almost infinite amount of possibilities for defining a document's structure. Keep that in mind when creating a PHP XML parsing application. Each application that you create will only work with one particular document structure. The extra effort spent designing the document structure pays off when you code applications that will parse that data.
Having said that, this next example tackles a couple of problems you will find in coding parsers for your own XML documents. These problems include multiple instances of the same element (usually differentiated by attributes or content) and attributes: two things you are sure to find in "real" XML documents. The complexity of the example document, bebop.xml, is fairly basic, but as you will see, the complexity of coding a PHP parser to read more complex documents doesn't necessarily increase; it is really just doing a lot more of the same thing over and over. The basic function of the element and character handlers does not change. You just need to add additional cases to encompass the additional variations that your document requires.
This next example shows how a more complex document can be parsed and how to present the resulting data. The XML file is the beginning of series synopsis for the popular anime title, Cowboy Bebop. Although it is far from complete as far as the series information goes, the XML structure allows for adding data to make the document more comprehensive. The output appears in Figure 10-3.
Figure 10-3. xml_series.php
The first part of the script, bebop.xml, is the actual XML file that is read into the parser.
Script 10-3 bebop.xml
1. <?xml version="1.0"?>
2. <series title="Cowboy Bebop" genre="Anime" subgenre="Science Fiction">
3. <dvd number="1">
4. <episode number="1">
5. <title>Asteroid Blues</title>
6. <synopsis>Jet and Spike track down a drug dealer.</synopsis>
7. <characters>
8. <character>Jet</character>
9. <character>Spike</character>
10. </characters>
11. </episode>
12. <episode number="2">
13. <title>Stray Dog Strut</title>
14. <synopsis>Ein Joins the crew.</synopsis>
15. <characters>
16. <character>Jet</character>
17. <character>Spike</character>
18. <character>Ein</character>
19. </characters>
20. </episode>
21. <episode number="3">
22. <title>Honkey Tonk Woman</title>
23. <synopsis>Introduction of Faye Valentine.</synopsis>
24. <characters>
25. <character>Jet</character>
26. <character>Spike</character>
27. <character>Ein</character>
28. <character>Faye</character>
29. </characters>
30. </episode>
31. <episode number="4">
32. <title>Gateway Shuffle</title>
33. <synopsis>Having fun at the casino.</synopsis>
34. </episode>
35. <episode number="5">
36. <title>Ballad Of Fallen Angels</title>
37. <synopsis>Spike's past comes back to haunt him.</synopsis>
38. </episode>
39. </dvd>
40. <dvd number="2">
41. <episode number="6">
42. <title>Sympathy For The Devil</title>
43. <synopsis>The mystery of the boy Wen.</synopsis>
44. </episode>
45. <episode number="7">
46. <title>Heavy Metal Queen</title>
47. <synopsis>Truckers in space.</synopsis>
48. </episode>
49. <episode number="8">
50. <title>Waltz for Venus</title>
51. <synopsis>Welcome to Venus.</synopsis>
52. </episode>
53. <episode number="9">
54. <title>Jamming With Edward</title>
55. <synopsis>Edward joins the crew.</synopsis>
56. <characters>
57. <character>Jet</character>
58. <character>Spike</character>
59. <character>Ein</character>
60. <character>Faye</character>
61. <character>Edward</character>
62. </characters>
63. </episode>
64. <episode number="10">
65. <title>Ganymede Elegy</title>
66. <synopsis>Homecoming for Jet.</synopsis>
67. </episode>
68. </dvd>
69.</series>
Script 10-4 xml_series.php
1. <html>
2. <head>
3. <title>XML - DVD SERIES PARSER</title>
4. <style type=text/css>
5. h1, h2, h3 {font-family: verdana, helvetica, sans-serif;}
6. p, blockquote {font-family: verdana, helvetica, sans-serif; font-size: 10pt}
7. .navy {color: navy; }
8. .characters {font-size: 8pt; color: red}
9. </style>
10. </head>
11. <body>
12. <?
13. function startElement($xml_parser, $name, $attributes) {
14. global $TagsOpen, $counter;
15. switch($name) {
16. case($name = "SERIES"):
17. $TagsOpen["SERIES"] = 1;
18. ?>
19. <h1>DVD Series</h1>
20. <h2>Title: <span><?=$attributes["TITLE"]?></span>
21. <br>Genre: <span><?=$attributes["GENRE"]?></span>
22. <br>Subgenre: <span><?=$attributes["SUBGENRE"]?></span></h2>
23. <?
24. break;
25. case($name = "DVD"):
26. $TagsOpen["DVD"] = 1;
27. ?>
28. <h3>DVD <span><?=$attributes["NUMBER"]?></span>
29. <?
30. break;
31. case($name = "EPISODE"):
32. $TagsOpen["EPISODE"] = 1;
33. ?>
34. <p><span>Episode: <?=$attributes["NUMBER"]?>
35. <?
36. break;
37. case($name = "TITLE");
38. $TagsOpen["TITLE"] = 1;
39. break;
40. case($name = "SYNOPSIS"):
41. $TagsOpen["SYNOPSIS"] = 1;
42. break;
43. case($name = "CHARACTERS"):
44. $TagsOpen["CHARACTERS"] = 1;
45. ?>
46. <blockquote>Characters:<span>
47. <?
48. break;
49. case($name = "CHARACTER"):
50. $TagsOpen["CHARACTER"] = 1;
51. $counter++;
52. break;
53. }
54. }
55.
56. function endElement($parser, $name) {
57. global $TagsOpen, $counter;
58. switch($name) {
59. case($name = "SERIES"):
60. $TagsOpen["SERIES"] = 0;
61. break;
62. case($name = "DVD"):
63. $TagsOpen["DVD"] = 0;
64. break;
65. case($name = "EPISODE"):
66. $TagsOpen["EPISODE"] = 0;
67. break;
68. case($name = "TITLE");
69. $TagsOpen["TITLE"] = 0;
70. ?>
71. </span>
72. <?
73. break;
74. case($name = "SYNOPSIS"):
75. $TagsOpen["SYNOPSIS"] = 0;
76. break;
77. case($name = "CHARACTERS"):
78. $TagsOpen["CHARACTERS"] = 0;
79. ? >
80. </span></blockquote>
81. <?
82. $counter = 0;
83. break;
84. case($name = "CHARACTER"):
85. $TagsOpen["CHARACTER"] = 0;
86. break;
87. }
88. }
89.
90. function characterData($parser, $data) {
91. global $TagsOpen, $counter;
92. switch($TagsOpen) {
93. case($TagsOpen["CHARACTER"] == 1):
94. if($counter == 1) {
95. echo " $data";
96. } else {
97. echo ", $data";
98. }
99. break;
100. case($TagsOpen["SYNOPSIS"] == 1):
101. echo "<br>$data\n";
102. break;
103. case($TagsOpen["TITLE"] == 1):
104. echo " - \"$data\"";
105. }
106. }
107.
108. function load_data($file) {
109. $fh = fopen($file, "r") or die ("<P>COULD NOT OPEN FILE!");
110. $data = fread($fh, filesize($file));
111. return $data;
112. }
113.
114. $TagsOpen = array(
115. "SERIES" => 0,
116. "DVD" => 0,
117. "EPISODE" => 0,
118. "TITLE" => 0,
119. "SYNOPSIS" => 0,
120. "CHARACTERS" => 0,
121. "CHARACTER" => 0
122. );
123.
124. $counter = 0;
125.
126. $file = "bebop.xml";
127. $xml_parser = xml_parser_create();
128. xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
129. xml_set_element_handler($xml_parser, "startElement", "endElement");
130. xml_set_character_data_handler($xml_parser, "characterData");
131. xml_parse($xml_parser, load_data($file)) or die ("<P>ERROR PARSING XML!");
132. xml_parser_free($xml_parser);
133. ?>
134. </body>
135. </html>
Script 10-4. xml_series.php Line-by-Line Explanation LINE
DESCRIPTION
1–11
Display the beginning part of the HTML page for the script, including some basic styles to format the output.
12
Begin parsing the page as PHP.
13–54
Create a function called startElement() to handle any start elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):
$xml_parser
$name
$attributes
14
Allow the variables $TagsOpen and $counter to be accessed and modified by this function. $TagsOpen is an array that tracks which tags are open or closed. $counter is an integer that is used for formatting purposes when displaying character data.
15–53
Create a switch statement to evaluate the value of the start element name.
16–24
Add a case to the switch statement to check if the $name variable equals "SERIES".
17
Set the $TagsOpen["SERIES"] flag to true (1), since the SERIES tag is open.
18–23
Print out some information to the screen, including the attributes of the SERIES tag, which include the title, genre, and subgenre of the series.
24
Break out of the switch statement, since we are done evaluating the $name variable.
25–30
Add a case to the switch statement to check if the $name variable equals "DVD".
26
Set the $TagsOpen["DVD"] flag to true (1), since the DVD tag is open.
27–29
Print out some information to the screen regarding the DVD tag, including its attributes.
30
Break out of the switch statement, since we are done evaluating the $name variable.
31
Add a case to the switch statement to check if the $name variable equals "EPISODE".
32
Set the $TagsOpen["EPISODE"] flag to true (1), since the EPISODE tag is open.
33–35
Print out some information to the screen regarding the EPISODE tag, including its attributes.
36
Break out of the switch statement, since we are done evaluating the $name variable.
37
Add a case to the switch statement to check if the $name variable equals "TITLE".
38
Set the $TagsOpen["TITLE"] flag to true (1), since the TITLE tag is open. We don't need to print anything for TITLE, because there are no attributes associated with this tag.
39
Break out of the switch statement, since we are done evaluating the $name variable.
40
Add a case to the switch statement to check if the $name variable equals "SYNOPSIS".
41
Set the $TagsOpen["SYNOPSIS"] flag to true (1), since the SYNOPSIS tag is open. We don't need to print anything for SYNOPSIS because there are no attributes associated with this tag.
42
Break out of the switch statement, since we are done evaluating the $name variable.
43
Add a case to the switch statement to check if the $name variable equals "CHARACTERS".
44
Set the $TagsOpen["CHARACTERS"] flag to true (1), since the CHARACTERS tag is open.
45–47
Print out some formatting that will be used when we print out the individual character names.
48
Break out of the switch statement, since we are done evaluating the $name variable.
49
Add a case to the switch statement to check if the $name variable equals "CHARACTER".
50
Set the $TagsOpen["CHARACTER"] flag to true (1), since the CHARACTER tag is open. We don't need to print anything for CHARACTER, because there are no attributes associated with this tag.
51
Increment the $counter variable, as it is used to help format the output when the individual character names are displayed.
52
Break out of the switch statement, since we are done evaluating the $name variable.
53
Close the switch statement.
54
End the function declaration.
56
Create a function called endElement() to handle any end elements that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):
$xml_parser
$name
57
Allow the variables $TagsOpen and $counter to be accessed and modified by this function. $TagsOpen is an array that tracks which tags are open or closed. $counter is an integer that is used for formatting purposes when displaying character data.
58
Create a switch statement to evaluate the value of the start element name.
59
Add a case to the switch statement to check if the $name variable equals "SERIES".
60
Set the $TagsOpen["SERIES"] flag to false (0), since the SERIES tag has been closed.
61
Break out of the switch statement, since we are done evaluating the $name variable.
62
Add a case to the switch statement to check if the $name variable equals "DVD".
63
Set the $TagsOpen["DVD"] flag to false (0), since the DVD tag has been closed.
64
Break out of the switch statement, since we are done evaluating the $name variable.
65
Add a case to the switch statement to check if the $name variable equals "EPISODE".
66
Set the $TagsOpen["EPISODE"] flag to false (0), since the EPISODE tag has been closed.
67
Break out of the switch statement, since we are done evaluating the $name variable.
68
Add a case to the switch statement to check if the $name variable equals "TITLE".
69
Set the $TagsOpen["TITLE"] flag to false (0), since the TITLE tag has been closed.
70–72
Print out the closing span, which was started when the parser found an open TITLE tag.
73
Break out of the switch statement, since we are done evaluating the $name variable.
74
Add a case to the switch statement to check if the $name variable equals "SYNOPSIS".
75
Set the $TagsOpen["SYNOPSIS"] flag to false (0), since the SYNOPSIS tag has been closed.
76
Break out of the switch statement, since we are done evaluating the $name variable.
77
Add a case to the switch statement to check if the $name variable equals "CHARACTERS".
78
Set the $TagsOpen["CHARACTERS"] flag to false (0), since the CHARACTERS tag has been closed.
79–81
Close out the blockquote and span tags that were started when the parser encountered an open CHARACTERS tag.
82
Set the $counter variable to "0". The characterData() function will use this to help display the names of the characters.
83
Break out of the switch statement, since we are done evaluating the $name variable.
84
Add a case to the switch statement to check if the $name variable equals "CHARACTER".
85
Set the $TagsOpen["CHARACTER"] flag to false (0), since the CHARACTER tag has been closed.
86
Break out of the switch statement, since we are done evaluating the $name variable.
87
Close the switch statement.
88
End the function declaration.
90
Create a function called characterData() to handle any character data that the script encounters as it parses the XML. The function takes the following as its arguments (required by PHP):
$xml_parser
$data
91
Allow the variables $TagsOpen and $counter to be accessed and modified by this function. $TagsOpen is an array that tracks which tags are open or closed. $counter is an integer that is used for formatting purposes when displaying character data.
92
Create a switch statement to evaluate the value of the $TagsOpen array. By examing this array, the function can determine at which point in the XML file PHP is parsing and display the data in the proper format.
93
Add a case to the switch statement to check if the CHARACTER element tags are open. We check them to see if the CHARACTER element is open before we check if any other tags are open, since the CHARACTER element is the most deeply nested element in our XML file.
94–98
Check the value of the $counter variable. If the counter variable is set to 1, then it means that this is the first character encountered in the current node of the XML. If it is the first character, then just print the character name. If it is not the first character, then print a comma and the character name. This allows you to display the entries separated by a comma.
99
Break out of the switch statement, since we are done evaluating the $TagsOpen variable.
100
Add a case to the switch statement to check if the SYNOPSIS element tags are open. We check to see if the SYNOPSIS element is open next, because it is one node up from the deepest node.
101
If the SYNOPSIS tags are open, then print the data that exists inside SYNOPSIS tags.
102
Break out of the switch statement, since we are done evaluating the $TagsOpen variable.
103
Add a case to the switch statement to check if the TITLE element tags are open. We check them to see if the TITLE element is open next, because it is one node up from SYNOPSIS.
104
If the TITLE tags are open, then print the data that exists inside TITLE tags.
105
Break out of the switch statement, since we are done evaluating the $TagsOpen variable.
106
Close the switch statement.
Note: We do not have to check any of the other elements because none of those elements contain any actual character data in between their open and close tags. They contain only other elements, so there is no need to use this function to check for data in those elements.
107
End the function declaration.
108
Create a function called load_data() to read the data from an XML file into the script so that it may be parsed. The function takes one argument, $file, which is the name (with an optional path) of the XML file that you want to parse.
109
Attempt to assign a file handle to the file. If unsuccessful, kill the script and print out an error message.
110
If the file opening was successful, read the entire file into the $data variable. Note that this is wildly inefficient for large files.
111
Return the $data variable to the calling program.
112
End the function declaration.
114–122
Define and initialize the $TagsOpen array. You need to define an array element for each element that needs to be tracked. The array item's key is the name of the element, and the value is either 1 or 0 (true or false). Set all the values to false (0) in the beginning, since no tags are supposed to be open before you even start parsing the file.
124
Initialize the $counter variable to "0".
126
Assign a file name to the $file variable. Note that you can use a full path to the file such as:
Windows: $file = "C:\winnt\xml\myxmlfile.xml";
Linux: $file = "/home/me/xml/myxmlfile.xml";
127
Define a parser option using the xml_set_parser_option() function. This function changes options as to how the parser operates. It requires three arguments:
The XML parser that you created using the xml_parser_create function.
The option you want to set.
The value of the option you are setting.
In this case, we are setting the option "XML_OPTION_CASE_FOLDING" to true, which changes the case of element names to uppercase when the parser reads the XML file. This way, we do not have to worry if there is mixed case in the element names in the XML file. When checking to see if a particular element is encountered, you only need to check for the uppercase version of the element name.
128
Create a variable called $xml_parser and assign it as a PHP xml parser using the xml_parser_create() function.
129
Define the custom start and end element handler functions that you created above using the xml_set_element_handler() function. Whenever PHP encounters a start or end element, it will use the respective function.
130
Define the custom data handler function that you created above using the respective function. Whenever PHP encounters character data, it will use the characterData() function.
131
Begin parsing the XML with the xml_parse function. The xml_parse() function requires the name of the XML parser ($xml_parser) and the XML data that you want to parse. In this case, we provide the XML data by using the custom load_data($file) function. If the xml_parse() function fails, then kill the script and display an error message.
132
After the xml_parse() function has completed parsing the XML, free the memory associated with the parser by using the xml_parser_free() function.
133–135
Stop parsing the page as PHP and close out the HTML for the page.
Dumping Database Contents into an XML File
Although it doesn't require the PHP XML functions, dumping data from a database into an XML file using PHP is a great way to get some structured XML that you can use in other XML-enabled applications.
The nature of database data lends itself nicely to use in XML applications. You have already created a database structure for the data by specifying the table structures. Using PHP, you can easily extract those tables into an XML file, which can be stored in a text file or displayed in a browser window.
This next script extracts the contents of a database table containing address book entries and displays that information in the browser. If you have a browser such as Internet Explorer, then you can view the XML natively, as in Figure 10-4. Otherwise, you can view the browser source to see the XML.
Script 10-5 xml_dump.php
[View full width]
1. <?
2. /* SQL Table Required For This Script
3. create table xmladdressbook (
4. id INT NOT NULL,
5. first VARCHAR(32),
6. last VARCHAR(32),
7. phone VARCHAR(12),
8. email VARCHAR(64),
9. address VARCHAR(64),
10. city VARCHAR(32),
11. state VARCHAR(2),
12. zip VARCHAR(5),
13. type VARCHAR(16)
14. primary key(id));
15.
16. insert into xmladdressbook values (1, 'Neil','Armstrong','555-555-1234','neil@example.
com','1 Moon Road','Lunar City','LN', '99999', 'Friend');
17. insert into xmladdressbook values (2, 'Buzz','Aldrin','555-555-1235','buzz@example.com',
'2 Moon Road','Lunar City','LN', '99999', 'Business');
18. */
19.
20. function connect() {
21. if(!$db = @mssql_pconnect("localhost","mssqluser","password")) {
22. print("<h1>Cannot Connect to the DB!</h1>\n");
23. return 0;
24. } else {
25. mssql_select_db("php", $db);
26. return 1;
27. }
28. }
29.
30. if(connect()) {
31. print('<?xml version="1.0"?>');
32. ?><addressbook><?
33. $sql = "select * from xmladdressbook order by last, first";
34. $result = mssql_query($sql);
35. while($row = mssql_fetch_array($result)) {
36. ?>
37. <contact type="<?=$row["type"]?>">
38. <name>
39. <last_name><?=$row["last"]?></last_name>
40. <first_name><?=$row["first"]?></first_name>
41. </name>
42. <details>
43. <phone><?=$row["phone"]?></phone>
44. <email><?=$row["email"]?></email>
45. <address><?=$row["address"]?></address>
46. <city><?=$row["city"]?></city>
47. <state><?=$row["state"]?></state>
48. <zip><?=$row["zip"]?></zip>
49. </details>
50. </contact>
51. <?
52. }
53. ?></addressbook><?
54. }
55. ?>
Figure 10-4. xml_dump.php shown in Internet Explorer
Script 10-5. xml_dump.php Line-by-Line Explanation LINE
DESCRIPTION
2–14
Use these lines to create the table in your SQL database.
16
Create an entry in the database.
17
Create an additional entry in the database.
20–28
Create a function called connect(). This function is used to create a connection to the database.
21
Attempt to connect to the database, in this case, an MS SQL database. (You can replace all the MS SQL functions with MySQL functions simply by changing the first "s" in mssql to a "y"—"mysql").
22
If the script cannot connect to the database, then print an error.
23
Return false (0) to the calling program, because the database connection failed.
24–25
If the connection attempt was successful, then select the database on the database server that the script will use.
26
Return true (1) to the calling program, because the connection was successful.
27
Close the if statement started on line 12.
28
End the function declaration.
30
If the connect() function returns true, then continue on to the next line.
31
Print out the XML required to begin an XML page. You need to use the print statement to enclose the native <? and ?> XML processing instructions so that they are not interpreted to be PHP tags.
32
Print the top level XML tag to the screen. Since this is an address book, the top level tag is <addressbook>.
33
Generate an SQL statement to select all of the contents of the table and order the results by last name, then first name.
34
Execute the SQL query and place the result in the $result variable.
35–52
Loop through the result set with a while loop.
36–51
Stop processing the page as PHP. Since we are printing out a lot of data, it's easier to just have the server send out plain XML instead of processing each line as a print statement. Print out each of the results returned in the current row as part of a "contact" element. The subelements found within "contact" contain the details of the address book entry.
52
Close the while loop.
53
Close out the XML by printing out a close tag for the root element <addressbook>.
54
Close the if statement started on line 30.
55
End the script.
Posted by Mr Procces at 8:49 AM 0 comments
Labels: PHP, Web Applications, Web Development, Web Programming