Tải bản đầy đủ (.pdf) (70 trang)

Tài liệu PROFILING PHP Understand and optimize your code docx

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (3.13 MB, 70 trang )

FEBRUARY 2004
VOLUME III - ISSUE 2
FEBRUARY 2004
VOLUME III - ISSUE 2
www.phparch.com
The Magazine For PHP Professionals
Plus:
Tips & Tricks, Security Corner, Product Reviews and much more
php|
Cruise
March 1
st
- March 5
th
2004
See inside for details
Get Ready For
Caching Techniques for the PHP Developer
Offline News Management with PHP-GTK
EXtending PHPEXtending PHP
Handling PHP Arrays from CHandling PHP Arrays from C
The Need for Speed
Writing More efficient PHP scripts
PROFILING PHPPROFILING PHP
Understand and optimize your code
Writing an riting an SMSSMS
Gateway ateway with ith
PHPPHP and and Gnokiinokii
]
]


Visit us at www
.php
arch.com/cruise for more details.
php|
Cruise
March 1
st
- March 5
th
2004
Andrei Zmievski - Andrei's Regex Clinic, James Cox - XML for the Masses,
Wez Furlong -
Extending PHP, Stuart Herbert
- Safe and Advanced Error Handling
in PHP5, Peter James - mod_rewrite: From Zero to Hero, George Schlossnagle -
Profiling PHP, Ilia Alshanetsky - Programming Web Services, John Coggeshall -
Mastering PDFLib, Jason Sweat - Data Caching Techniques
<?
?>
We’ve got you covered,
from port to sockets.
Port Canaveral • Coco Cay • Nassau
Plus: Stream socket programming, debugging techniques, writing high-performance code,
data mining, PHP 101, safe and advanced error handling in PHP5, programming smarty,
and much, much more!
In partnership with Zend
Technologies
Zend Studio 3.0 is the
official PHP IDE of
php|cruise

Features
php|
Crui se
Traditional PHP
Conference*
Conference Pass
$ 899.99
**
$ 1,150.00
Hotel
Included
($ 400.00)
Meals
Included***
($ 200.00)
Totals:
$ 899.99
$1,750.00
You Save $ 850
* Based on average of two major PHP conferences
** Based on interior stateroom, double occupancy
*** Alcohol and carbonated beverages not included
ENJOY LEARNING PHP IN A FUN AND EXCITING
ENVIRONMENT—AND SAVE A BUNDLE!
Signup deadline: Feb 15, 2004
5 Editorial
6 What’s New!
37 Product Review
SQLyog
58 Product Review

2003 Quebec PHP Conference DVD
by Marco Tabini
61 Security Corner
by Chris Shiflett
65 Tips & Tricks
By John W. Holmes
68 e x i t ( 0 ) ;
Why Can’t We All Just Get Along?
By Marco Tabini
9
Write SMS Applications With PHP and
Gnokii
by Eric Persson
16
Offline Content Management with
PHP-GTK
by Morgan Tocker
23
Writing PHP Extensions: Managing
Arrays
by Wez Furlong
28
The Need For Speed
Optimizing your PHP Applications
by Ilia Alshanetsky
41
Profiling PHP Applications
by George Schlossnagle
51
Caching Techniques for the PHP

Developer
by Bruno Pedro
3
February 2004

PHP Architect

www.phparch.com
TABLE OF CONTENTS
II NN DD EE XX
II NN DD EE XX
php|architect
Features
Departments
Existing
subscribers
can upgrade to
the Print edition
and save!
Login to your account
for more details.
NEW!
NEW!
*By signing this order form, you agree that we will charge your account in Canadian
dollars for the “CAD” amounts indicated above. Because of fluctuations in the
exchange rates, the actual amount charged in your currency on your credit card
statement may vary slightly.
**Offer available only in conjunction with the purchase of a print subscription.
Choose a Subscription type:
CCaannaaddaa//UUSSAA $$ 8833 9999 CCAADD (($$5599 9999 UUSS**))

IInntteerrnnaattiioonnaall SSuurrffaaccee $$111111 9999 CCAADD (($$7799 9999 UUSS**))
IInntteerrnnaattiioonnaall AAiirr $$112255 9999 CCAADD (($$8899 9999 UUSS**))
CCoommbboo eeddiittiioonn aadddd oonn $$ 1144 0000 CCAADD (($$1100 0000 UUSS))
((pprriinntt ++ PPDDFF eeddiittiioonn))
Your charge will appear under the name "Marco Tabini & Associates, Inc." Please
allow up to 4 to 6 weeks for your subscription to be established and your first issue
to be mailed to you.
*US Pricing is approximate and for illustration purposes only.
php|architect Subscription Dept.
P.O. Box 54526
1771 Avenue Road
Toronto, ON M5M 4N5
Canada
Name: ____________________________________________
Address: _________________________________________
City: _____________________________________________
State/Province: ____________________________________
ZIP/Postal Code: ___________________________________
Country: ___________________________________________
Payment type:
VISA Mastercard American Express
Credit Card Number:________________________________
Expiration Date: _____________________________________
E-mail address: ______________________________________
Phone Number: ____________________________________
Visit: for
more information or to subscribe online.
Signature: Date:
To subscribe via snail mail - please detach/copy this form, fill it
out and mail to the address above or fax to +1-416-630-5057

php|architect
The Magazine For PHP Professionals
YYoouu’’llll nneevveerr kknnooww wwhhaatt wwee’’llll ccoommee uupp wwiitthh nneexxtt
February 2004

PHP Architect

www.phparch.com
EE DD II TT OO RR II AA LL RR AA NN TT SS
php|architect
Volume III - Issue 2
February, 2004
Publisher
Marco Tabini
Editorial Team
Arbi Arzoumani
Peter MacIntyre
Eddie Peloke
Graphics & Layout
Arbi Arzoumani
Managing Editor
Emanuela Corso
Director of Marketing
J. Scott Johnson

Account Executive
Shelley Johnston

Authors
Ilia Alshanetsky, Wez Furlong, John Holmes,

Bruno Pedro, Eric Persson, George Schlossnagle,
Chris Shiflett, Morgan Tocker
php|architect (ISSN 1709-7169) is published twelve times a year by Marco Tabini &
Associates, Inc., P.O. Box 54526, 1771 Avenue Road, Toronto, ON M5M 4N5, Canada.
Although all possible care has been placed in assuring the accuracy of the contents of this
magazine, including all associated source code, listings and figures, the publisher assumes
no responsibilities with regards of use of the information contained herein or in all asso-
ciated material.
Contact Information:
General mailbox:
Editorial:
Subscriptions:
Sales & advertising:
Technical support:
Copyright © 2003-2004 Marco Tabini & Associates, Inc.
— All Rights Reserved
W
elcome to the February 2004 issue of
php|architect. As I write this, I'm sitting in my
office—about forty degrees Celsius warmer
than outside and, therefore, a much better place to
work in that that the local park—suffering from an
awful cold and sitting by a collection of (clean) tissues
discreetly stashed on my desk, ready for use. As you
can expect, I'm not particularly happy about either fact
(make that three facts—the cold outside, the cold in
my body, and the fact that I'm sitting in an office when
I could really be somewhere else far away from any-
thing that even remotely resembles a computer).
Incidentally, with php|cruise coming at the beginning

of March, I should hopefully be able to get rid of at
least two problems—and I'm still working on finding a
way to avoid computers during that trip.
But I ramble—a clear sign that the cold medicine is
wearing off. Let me instead tell you something about
this month's issue. With the popularity that PHP enjoys
nowadays comes the fact that it is used as the back-
bone of more and more high-traffic sites. A simple con-
sequence of this is that an increasing number of devel-
opers are "hitting the wall" and finally feeling the limits
of what the "let's just do it in PHP" approach can do.
Building a website is always a high-wire balance of
budgeting, respecting deadlines and writing the best
code possible, but there's nothing quite as bad as find-
ing out that the way you've done things is incapable of
meeting the demands of your website—and, by the
time you realize that you have a problem, it's usually
too late to think about a solution short of calling your
travel agent and inquiring about that non-extradition
country you heard of.
Therefore, this month we dedicate a fair amount of
room to the performance management of PHP applica-
tions. George Schlossnagle's article—based on an
excerpt from his latest book, published by SAMS—talks
about profiling, a concept that I have very rarely seen
associated with PHP applications. Profiling takes the
guesswork out of understanding where the bottlenecks
in your application are, allowing you to focus on find-
ing the best possible resolution.
The problem with profiling is that it only allows you

to identify the problems and not solve them. Luckily,
Ilia Alshanetsky and Bruno Pedro offer two other excel-
lent articles on improving the performance of PHP
without affecting the code itself (if you can, why not
avoid the risk of introducing even more bugs?). While
Ilia focuses on ways to make the PHP interpreter itself
run faster, Bruno examines the topic of caching—both
at the network and script level.
This month we also start a new column—Security
Corner—written by Chris Shiflett. The daily number of
security advisories, patches, break-ins and source-code
thefts that we see reported in the media every day has
EDITORIAL
Continued on page 8
February 2004

PHP Architect

www.phparch.com
6
NNEEWW SSTTUUFFFF
PHP 4.3.5 RC1
PHP.net has announced the
release of PHP 4.3.5 RC1
PHP 4.3.5RC1 has been released
for testing. This is the first release
candidate and should have a very low number of prob-
lems and/or bugs. Nevertheless, please download and
test it as much as possible on real-life applications to
uncover any remaining issues. List of changes can be

found in the NEWS file.
For more information visit:
/>PHP Community Logo Contest
Following Chris Shiflett’s recent announcement of the
PHP Community Site, he is holding a contest to
find a logo that embodies the spirit of the PHP
community. Everyone is welcome to participate,
and you can submit as Many entries as you like.
Please send all entries to logos@phpcommuni-
ty.org And include the name with which you want
to be credited.
The contest ends 29 Feb 2004, and php|architect is
offering a free PDF subscription to the winner. For
updated news about the contest, as well as a
chance to view the current entries, visit:
/>Good luck to all who enter!
ZEND Studio 3.0.2
Zend has announced the release of Zend Studio 3.0.2
client. What’s new? Zend.com lists some of the bug
fixes as:
• ZDE didn’t load when using a new keymap
config from an older version.
• Save As Project didn’t always work.
• Server Center activator tried to open the
wrong URL.
• .js files were not opened with JavaScript
highlighting.
• Shift-Delete and Shift-Backspace didn’t work
properly.
• Find&Replace was very slow under Linux.

• Add Comment sometimes erroneously com-
mented out a line that wasn’t selected.
• Added configurable limit for the number of
displayed syntax errors
There have also been improvements to the debugger,
code completion, code analyzer, IE toolbar, and some
Mac OSX changes.
Get more information from
Zend.com.
What’s New!
NN EE WW SS TT UU FF FF
February 2004

PHP Architect

www.phparch.com
7
MySQL Administrator
MySQL.org announces: MySQL Administrator is a pow-
erful new visual administration console that makes it
significantly easier to administer your MySQL servers
and gives you better visibility into how your databases
are operating. MySQL Administrator integrates data-
base management and maintenance into a single,
seamless environment, with a clear and intuitive graph-
ical user interface. Now you can easily perform all the
command line operations visually, including configur-
ing servers, administering users, dynamically monitor-
ing database health, and more
Get more information from:

/>NNEEWW SSTTUUFFFF
Check out some of the hottest new releases
from PEAR.
DB 1.6.0 RC4
DB is a database abstraction layer providing:
• an OO-style query API
• a DSN (data source name) format for specifying
database servers
• prepare/execute (bind) emulation for databases
that don’t support it natively
• a result object for each query response
• Compatible with PHP4 and PHP 5
• much more….
DB layers itself on top of PHP’s existing database
extensions. The currently supported extensions
are: dbase, fbsql, interbase, informix, msql, mssql,
mysql, mysqli, oci8, odbc, pgsql, sqlite and sybase
(DB style interfaces to LDAP servers and MS ADO
(using COM) are also avaible from a separate pack-
age).
System_ProcWatch 0.4
With this package, you can monitor running
processes based upon an XML configuration file,
XML string, INI file or an array where you define
patterns, conditions and actions.
Net_IMAP 0.7
Provides an implementation of the IMAP4Rev1
protocol using PEAR’s Net_Socket and the option-
al Auth_SASL class.
XML_Beautifier 1.1

XML_Beautifier will add indentation and line
breaks to you XML files, replace all entities, format
your comments and makes your document easier
to read. You can influence the way your document
is beautified with several options.
Looking for a new PHP Extension? Check out
some of the latest offerings from PECL.
opendirectory 0.2.2
Open Directory is a directory service architecture
whose programming interface provides a central-
ized way for applications and services to retrieve
information stored in directories. The Open
Directory architecture consists of the
DirectoryServices daemon, which receives Open
Directory client API calls and sends them to the
appropriate Open Directory plug-in.
statgrab 0.1
libstatgrab is a library that provides a common
interface for retrieving a variety of system statistics
on a number of *NIX like systems.
This extension allows you to call the functions
made available by libstatgrab library.
Sasl 0.1.0
SASL is the Simple Authentication and Security
Layer (as defined by RFC 2222). It provides a sys-
tem for adding plugable authenticating support to
connection-based protocols. The SASL Extension
for PHP makes the Cyrus SASL library functions
available to PHP. It aims to provide a 1-to-1 wrap-
per around the SASL library to provide the greatest

amount of implementation flexibility. To that end,
it is possible to build both a client-side and server-
side SASL implementation entirely in PHP.
SQLLite 1.0.2
SQLite is a C library that implements an embedda-
ble SQL database engine. Programs that link with
the SQLite library can have SQL database access
without running a separate RDBMS process. This
extension allows you to access SQLite databases
from within PHP. Windows binary available from:
/>hp_sqlite.dll
PHPWeather 2.2.1
PHP Weather announces the release of version 2.2.1.
PHP Weather makes it easy to show the current weath-
er on your webpage. All you need is a local airport, that
makes some special weather reports called METARs.
The reports are updated once or twice an hour.
Get more information from :
/>PHPEclipse Debugger
PHP Eclipse adds PHP support to the Eclipse IDE
Framework. This snapshot introduces the first version of
the PHPEclipse debugger plugin.
For more information visit:

MySQL and Zend Working Together
From Zend and MySQL – These two have Joined Forces
to Strengthen Open Source Web Development
MySQL AB, developer of the world’s most popular open
source database, and Zend Technologies, designers of the
PHP Web scripting engine, today announced a partnership

to simplify and improve productivity in developing and
deploying Web applications with open source technolo-
gies. Through the alliance, the companies are improving
compatibility and integration between the MySQL data-
base and Zend’s PHP products to make it easier for busi-
nesses to use complete open source solutions, such as the
popular LAMP (Linux, Apache, MySQL and PHP) software
stack.
As part of the partnership, MySQL AB and Zend are
offering partner products to their respective customers,
enabling easier product procurement and deployment
for Web application infrastructures. The companies will
also commit development resources to design product
integration and compatibility modules for both ven-
dors’ platforms.
For more information visit:
www.zend.com
SAXY 0.3
SAXY is a Simple API for XML (SAX) XML parser for PHP
4. It is lightweight, fast, and modeled on the methods
of the Expat parser for compatibility. The primary goal
of SAXY is to provide PHP developers with an alterna-
tive to Expat that is written purely in PHP. Since SAXY is
not an extension, it should run on any Web hosting
platform with PHP 4 and above installed.
This release allows CDATASection tags to be pre-
served, rather than converted to Text Nodes.
For more information visit:
/>February 2004


PHP Architect

www.phparch.com
8
NNEEWW SSTTUUFFFF
php|a
convinced us that, at the very least, one should be able to protect his sites from malicious usage, in the hope
that all the other companies we rely on to maintain their software will do so in a serious way.
Finally, we bring you three more articles that, we hope, will tickle your fancy. The first one, written by Eric
Persson, shows you how you can build an SMS gateway using PHP and a few other inexpensive components.
SMS is not yet very popular here in North America, but, judging from the amount of people I see glued to
their cell phones whenever I visit my native Italy, it is very widely used in Europe.
In his article on offline news management, Morgan Tocker writes about how PHP-GTK, that most hidden of
PHP gems, can be used to improve content management by providing a proper GUI application that doesn't
require you to completely rewrite all your code.
Finally—last but not least-Wez Furlong picks up where his article from last month left off and delves into the
deep bowels of the Zend Engine to show you how a PHP extension written in C can manipulate PHP arrays—
it's not quite as easy as from a script but close enough once you know what you're doing.
Well, that's it for this month. By the time I will be writing my next editorial, I plan to be either boasting about
my suntan or complaining about sunburn. Either way, you can expect me to report on our adventure on the
high seas—until then, happy reading!
Editorial: Contiuned from page 5
D
espite the fact that it sounds like some mysteri-
ous Italian pasta, Gnokii is really just a project
aimed to develop tools and drivers for Nokia
mobile phones-that is, software that makes it possible
to control a Nokia phone physically connected to your
server via a serial port. Gnokii works like the Nokia Data
Suite, which is shipped with more advanced models

from Nokia: you can use it to send SMS messages, edit
contacts and so on—pretty much everything you nor-
mally do with your thumb on the phone's keypad.
Gnokii itself is composed of many tools, including a
set of GUI applications that facilitate the remote opera-
tion of the telephone; we are really only interested in a
small subset of these tools called
ssmmssdd
, or SMS daemon,
which provides an interface for rapid access to the
phone's SMS capabilities. With the SMS daemon up
and running, we can use PHP to interact with the
phone, send and receive SMS messages and, of course,
build whatever logic we need based on the content of
the messages that we receive and send. In short, my
goal with this article is to show you how to configure
software and hardware so that you can get the same
kind of service as you would normally obtain from a big
company selling mobile services like SMS gateways—
but at a fraction of the price.
Major Components of the Final
Application
The final application that we will create throughout this
article is a simple SMS server that awaits a message
from a user and acts on its contents. It is made up of
three major components:
• A Nokia cell phone, which must be
connected properly to the server.
• The
ssmmssdd

application from the Gnokii
package, which must, of course, be
compiled and configured correctly.
• The PHP scripts that provide the actual
server functionality.
The flow of the application will be as follows:
• The user send an SMS message to the
server.
• The
ssmmssdd
daemon picks it up and auto-
matically puts it into its database.
• Our server scans
ssmmssdd
's database peri-
odically for new messages.
• When a new message arrives, its con-
tents are examined and the server acts
on them, for example by replying to
the user with another message.
February 2004

PHP Architect

www.phparch.com
9
FF EE AA TT UU RR EE
Write SMS Applications With PHP and Gnokii
by Eric Persson
PHP: 4.1 or higher

OS: Unix/Linux
Applications: Gnokii ()
Code: />Code Directory: sms-gnokii
REQUIREMENTS
SMS-shorthand for Short Message Service-is the standard
used by cellular phone networks worldwide to allow their
customers to exchange small text messages using their
handsets. Despite its limitations, SMS is very popular
with cell phone users-and it has rapidly become a wide-
ly-used bridge between the Internet and mobile users.
Hardware needed
When it comes to cellular communications, the bad
thing about hardware is that it often costs a lot of
money, but the goal of this project is precisely to pro-
vide a low-cost alternative, so the expenses associated
with it should be quite reasonable. What you'll need in
terms of hardware is a Nokia phone and a serial cable
to hook it up to your server. I will, of course, expect that
you already have a server and that it is capable of run-
ning the Gnokii tools and PHP. In my environment, I
have used a Nokia 3310, which is quite new but not
very expensive, and works perfectly for my needs.
There are no "official" connection cables available for
the 3310, but a company from the UK called Cellsavers
(
) have come up with a very
ingenious serial cable with a connector that you can fit
behind the battery on the phone. For those who don't
know, there are 4 metal pins that are probably used by
Nokia to install software and perform other program-

ming on to the phone, and those nice folks at
Cellsavers managed to figure out how to use them to
control the phone through a serial port. There might be
other companies supplying the same type of product,
but I have not seen any around.
Another important note about the hardware is that
you will need to get a battery charger for the phone.
One often comes with the package, and you can plug
it in and leave the phone on forever without having to
worry about the batteries.
Installing Gnokii and smsd
Before starting to install Gnokii and
ssmmssdd
, make
sure you have MySQL installed and working
properly on your server. Installing Gnokii is quite
straightforward—it involves little more than the
usual
ccoonnffiigguurree mmaakkee mmaakkee iinnssttaallll
steps. However,
there are some configuration options that I find
important.
The first might be a matter of taste, but I like to place
everything belonging to Gnokii in
//uussrr//llooccaall//GGnnookkiiii
.
Therefore, I will use
pprreeffiixx==//uussrr//llooccaall//GGnnookkiiii
when
invoking it. Next, the

wwiitthhoouutt xx
configuration switch
indicates that we will not need to use the
xxggnnookkiiii
GUI
application to send SMS messages and manage the
phone. If you want to take a look at the graphical tools,
you can of course skip this parameter, but on a Unix serv-
er where you normally do not have Xwindows installed
you'll get a whole lot of errors if you do so. The last
parameter is
eennaabbllee sseeccuurriittyy
, which turns on a lot of
security-related features in the package, like the ability to
change the PIN number. I find them useful, so I usually
turn them on.
The resulting configure line will be as follows:
./configure prefix=/usr/local/Gnokii without-x
enable-security
February 2004

PHP Architect

www.phparch.com
10
FFEEAATTUURREE
Write SMS Applications With PHP and Gnokii
1 <?
2 /*
3

4 Sms parsing utility for gnokiis smsd database.
5 LISTING 1
6 */
7 error_reporting(E_ALL);
8
9 $CONFIG = array();
10 $CONFIG[‘keywords_directory’] = ‘./keywords/’;
11 $CONFIG[‘default_email’] = ‘’;
12 $CONFIG[‘database_username’] = ‘root’;
13 $CONFIG[‘database_password’] = ‘’;
14 $CONFIG[‘database_hostname’] = ‘localhost’;
15 $CONFIG[‘database_database’] = ‘sms’;
16
17
18 /*
19 Error function, will handle an error in desired way.
20 Maybe add some notification functionality to notify an
admin?
21 */
22 function return_error($error=’’){
23
24 echo date(‘Y-m-d H:i:s’).’: ‘.$error;
25 exit();
26 }
27
28
29 /*
30 Read through the keywords directory and gather filenames
and keywords,
31 that should be match by a message.

32 */
33 function read_keywords(){
34 global $CONFIG;
35
36
37 $keywords = array();
38
39 $dh = opendir($CONFIG[‘keywords_directory’]);
40 if( $dh ){
41 while( $filename = readdir($dh) ){
42 if( ereg(‘^([a-z0-9_]*).php$’, $filename, $match)
){
43 $keywords[$match[1]] =
$CONFIG[‘keywords_directory’].$filename;
44 echo date(‘Y-m-d H:i:s’).’:
‘.$match[1].chr(10);
45 }
46 }
47
48 if( sizeof($keywords)==0 )
49 return_error(‘Keyword directory was empty.’);
50 else
51 return $keywords;
52
53 }else{
54 return_error(‘Keyword directory could not be
opened.’);
55 }
56
57 }

58
59 /*
60 This function will be executed if a message arrives that
61 doesnt match any keyword.
62 */
63 function default_action($message, $sender){
64 global $CONFIG;
65
66 #mail($CONFIG[‘default_email’], ‘Unhandled sms’,
‘Message:’.$message.”\n”.’Sender:’.$sender);
67 echo ‘DEFAULT ACTION’.chr(10);
68 }
69
70 /*
71 This function takes the message and the sender as argu-
ments
72 and then performs an ereg match for each message.
73 */
74 function match_message($message, $sender){
75 global $keywords;
76
77 $match_message = ereg_replace(‘([^a-z0-9]*)’, ‘’, str-
tolower($message));
78 reset($keywords);
79 while( list($keyword, $phpfile)=each($keywords) ){
Listing 1
Continued on page 11
Once you've downloaded the Gnokii tarball from
—the latest version at the time of
this writing is Gnokii-0.5.5—you can decompress it and

start the compilation process:
# gzip -dc Gnokii-0.5.5.tar.gz | tar -xof -
# cd Gnokii-0.5.5
# ./configure prefix=/usr/local/Gnokii without-x
enable-security
# make
# make install
If you don't encounter any major problems during the
building process, you will end up having your copy of
Gnokii installed in
//uussrr//llooccaall//GGnnookkiiii
. Before we test the
phone interface, however, we still need to create the
//eettcc//ggnnookkiiiirrcc
file, which holds some important config-
uration options, like information on where the phone is
connected to and which model it is. My
//eettcc//ggnnookkiiiirrcc
file looks like this:
[global]
port = /dev/ttyS1
model = 3310
initlength = default
connection = serial
bindir = /usr/local/Gnokii/sbin/
Make sure that you have connected your phone to the
correct serial port as you specified in the configuration.
Also, check the model of your phone and enter it accord-
ingly. The
iinniittlleennggtthh

variable controls the number of
characters sent to the phone during initialization; you
don't normally want to change this setting—unless you
have problems with the connection, I suggest that you
use the default value (at least initially).
The
ccoonnnneeccttiioonn
variable should be set to
sseerriiaall
, since
we'll be connecting to the phone using the serial port.
In case you're wondering, it's possible to configure it to
use an infrared connection instead.
Now, it's time to test it all and see if everything works
fine. A good starting point here is to try and send out an
SMS message using Gnokii:
February 2004

PHP Architect

www.phparch.com
11
FFEEAATTUURREE
Write SMS Applications With PHP and Gnokii
81
82 include_once($phpfile);
83 if( function_exists($keyword) )
84 $keyword($message, $sender);
85
86 return true;

87 }
88 }
89
90 default_action($message, $sender);
91 return false;
92 }
93
94 /*
95 A faster message match process, its not as nice to user
mistakes as the one
96 above, but its an option, if speed/processor time is
important.
97 */
98 function match_message_fast($message, $sender){
99 global $keywords;
100
101 if( strpos($message, ‘ ‘)>0 )
102 $match_part = substr($message, 0, strpos($message, ‘
‘));
103 else
104 $match_part = $message;
105
106 $match_part = trim(strtolower($match_part));
107
108 if( isset($keywords[$match_part]) ){
109 include_once($keywords[$match_part]);
110 if( function_exists($match_part) )
111 $match_part($message, $sender);
112 return true;
113 }

114
115 default_action($message, $sender);
116 return false;
117
118 }
119
120 /*
121 Just for speed measurements.
122 */
123 function diff_microtime($microtime, $end_microtime){
124
125 $start_seconds = substr($microtime, strpos($microtime, ‘
‘), strlen($microtime));
126 $start_fraction = substr($microtime, 0, strpos($microtime,
‘ ‘));
127 $end_seconds = substr($end_microtime, strpos($microtime, ‘
‘), strlen($microtime));
128 $end_fraction = substr($end_microtime, 0, strpos($micro-
time, ‘ ‘));
129
130 return sprintf(‘%1.3fs’, (($end_seconds-
$start_seconds)+$end_fraction-$start_fraction));
131
132 }
133
134
135 /* Connect to the mysql database */
136 $connection = mysql_connect($CONFIG[‘database_hostname’],
$CONFIG[‘database_username’], $CONFIG[‘database_password’]);
137

138 /* Check if the connection was successful, otherwise return
an error. */
139 if( !$connection )
140 return_error(‘Could not connect to mysql at
‘.$CONFIG[‘database_hostname’]);
141
142 /* Select the database that contains the smsd tables */
143 mysql_select_db($CONFIG[‘database_database’], $connection) or
return_error(‘Could not select database’);
144
145
146 /* Call the read_keywords() function to get all keywords cur-
rently available. */
147 $keywords = read_keywords();
148
149 /* Reversesort the keywords by the arrays key element. */
150 krsort($keywords);
151
152 /*
153 Application main loop.
154 We will loop 100 times and then finish this script, to
prevent memoryclogging.
155 */
156 for( $i=0; $i<10000000; $i++ ){
157 $microtime = microtime(); // speed measurements
Listing 1: Continued
158
159 $res = mysql_query(‘SELECT * FROM inbox WHERE processed=0
ORDER BY insertdate ASC’);
160 while( $sms = mysql_fetch_array($res) ){

161
162 /* Decide which matching function you wish to use. */
163 match_message($sms[‘text’], $sms[‘number’]);
164 //match_message_fast($sms[‘text’], $sms[‘number’]);
165
166 mysql_query(‘UPDATE inbox SET processed=1 WHERE
id=’.$sms[‘id’]);
167 }
168
169 /* Enable this for some performance statistics.
170 $end_microtime = microtime();
171 echo “time:”.diff_microtime($microtime, $end_micro-
time).”\n”;
172 */
173
174 sleep(1);
175 }
176
177 ?>
Listing 1: Continued
# cd /usr/local/Gnokii/
# bin/Gnokii sendsms "+xxxxxxxxxx"
Gnokii Version 0.5.5
Please enter SMS text. End your input with <cr><con-
trol-D>:
Success Regards, Eric
Send succeeded!
Clearly, you will need to replace the
xxxxxxxxxxxxxxxxxx
above

with a real, working phone number that you can test for
the message to arrive (you could, in fact, use the same
number as the cell phone you're using to send the mes-
sage). If you don't receive the message, or if you get an
error, you may want to step back and look at the config-
uration and build procedure once again, just to make
sure that you haven't missed anything.
The next step consists of configuring
ssmmssdd
so that we
can send messages out onto the network programmati-
cally. It's obviously important to have Gnokii working
first, since
ssmmssdd
relies on the same runtime configuration
libraries. The
ssmmssdd
source code is located in the
//ssmmssdd//
folder under the directory where you unpacked the
Gnokii tarball.
SSmmssdd
can work either with a database or with a filesys-
tem but, for the purposes of this article, we will only
focus on configuring it to use MySQL. The daemon is not
compiled by default when you compile Gnokii, so that
will have to be our next step. You will need to manually
edit the Makefile and change every instance of the path
to the MySQL installation in the DB Modules section.
Next, you can build the executables:

# make
# make libmysql.so
# make install
Setting Up the smsd Database
Since we want to use
ssmmssdd
with MySQL, we need to cre-
ate a database for it to use. For simplicity's sake, we'll call
it
ssmmss
and grant a new MySQL user with login
ssmmss
and
password
ssmmss
access to it. Naturally, if you move into a
production environment where security is a concern,
you may want to use a more secure username/password
combination. Keep in mind that anyone who can access
your
ssmmss
database can insert rows into the outbox and
therefore send messages from the connected phone.
On a larger system, the possibility for abuse is certainly
there—and therefore security is worth at least some
consideration.
In the
ssmmssdd
directory of your tarball, you will also find
a SQL file called

ssmmss ttaabblleess mmyyssqqll ssqqll
that contains the
table definitions needed to run the daemon. All you need
to do is import these into your database and you are all
set to go. There is also a file for those that prefer
PostgreSQL, but we will focus on MySQL here.
Installing daemontools
The
ddaaeemmoonnttoooollss
package is a collection of tools that
can be used to monitor and manage UNIX-based serv-
ices. Its installation procedure is quite straightforward,
since there aren't too many options or configuration
directives. The only thing to keep in mind is that some
differences in newer versions of glibc (2.3.1 and
above) may require you to patch the
ddaaeemmoonnttoooollss
source before you try to compile it. The patch you
need is called the "errno-patch" and fixes an incom-
patible declaration of the
eerrrrnnoo
variable made in the
source. I've seen some people claim that this problem
is caused by bad programming practices, but the
error really only started popping up when changes
were made to glibc, so I'm not too sure as to how true
that is. Whatever the real reason, if you encounter this
problem, simply patch the source and you'll be just
fine. If you need to download the patch, you can get
it from

/>Then, follow the daemontools installation instruc-
tions, which you can find at
/>If you're not familiar with patching software, this is
done by downloading the software, extracting it, and
then using the
ppaattcchh
program to affect the actual
changes in the source code. More information about
the
eerrrrnnoo
patching process and
ddaaeemmoonnttoooollss
can be
found at
/>2.3.1/INSTRUCTIONS
but, generally speaking, you can
get away with something like this:
# tar zxvf daemontools-0.76.tar.gz
# cd admin/daemontools-0.76
# patch -p1 /path/to/daemontools-0.76.errno.patch
On to Some PHP
Our main PHP script will be as small and efficient as
possible, since it will be running as a daemon on our
server all the time. Its main task will be to check if there
are new messages in the SMS inbox table and, if so,
February 2004

PHP Architect

www.phparch.com

12
FFEEAATTUURREE
Write SMS Applications With PHP and Gnokii
1 <?
2 /*
3 Example keyword file
4 LISTING 2
5 */
6
7 function hello($message, $sender){
8
9 $return_message = ‘Hello to you my friend!’;
10 /* Output a log message */
11 echo date(‘Y-m-d H:i:s’).’: Answeared message
“‘.$message.’” from “‘.$sender.’” with “‘.$return_mes-
sage.’”’.chr(10);
12
13 /* Send the reply to the sender */
14 mysql_query(‘INSERT INTO outbox SET
number=”’.$sender.’”, text=”’.$return_message.’”,
processed_date=”0”, insertdate=now(), error=0,
dreport=0’);
15
16 }
17
18
Listing 2
match them against the possible keywords that we
have created, so that the appropriate action can be
taken. We'll call this script

ssmmssppaarrssee pphhpp
.
First of all, let's decide how we're going to structure
our application. Since our main goal is to respond to
certain keywords, we'll start by creating a few "keyword
scripts", which are really nothing more than standalone
PHP files stored in a subdirectory called
kkeeyywwoorrddss
.
For example, if we wanted to define a keyword called
hheelllloo
, our directory structure would like this:
./keywords/
./keywords/hello.php
./smsparse.php
As you can see, each keyword has its own PHP file.
We simply use the keyword as the filename for the
script that contains the actions associated with it in
order to simplify the entire process.
Let's now have a look at
ssmmssppaarrssee pphhpp
, which you can
see in Listing 1. At the beginning of the script (in the
rreeaadd__kkeeyywwoorrddss
function), we read through the contents
of the
kkeeyywwoorrdd
directory. Each file
found in the directory is matched
against the

eerreegg(())
pattern on line 41
and, if that operation is successful, a
new item with the keyword as a key
and the file's path as the value is added
to the array that the function returns
at the end of its execution.
As you can see, on line 148 we sort
the array in a descending fashion
based on the length of each key. We
do this so that longer keywords are
checked for first and we don't end up
in a situation where a word like "eat" is
matched instead of "Seattle" because it
is shorter.
The main portion of the application works by execut-
ing a loop indefinitely. At every iteration, we check if a
new message has arrived in the inbox and, if that is the
case, match the message against the active keywords
that we have identified at the beginning of the script,
and finally sleep for 1 second before the next cycle.
For the actual matching process, I have written two
alternatives that use different approaches. The first one,
mmaattcchh__mmeessssaaggee(())
, is the most fault tolerant but also the
slowest one. The second one,
mmaattcchh__mmeessssaaggee__ffaasstt(())
, is
not as tolerant but will save some CPU resources by
using a faster algorithm. The difference won't probably

be dramatic, but on a heavily loaded server or with a
large list of keywords it may well have an impact on the
overall performance of the system.
In
mmaattcchh__mmeessssaaggee(())
(lines 74-92), the message is first
cleaned of unwanted characters such as non-alphanu-
meric values and spaces, and converted to lowercase.
Next, the function cycles through all the keywords and
performs an
eerreegg(())
match against the "clean" version of
the message. If a match occurs, the PHP file correspon-
ding to the keyword is included and executed.
The
mmaattcchh__mmeessssaaggee__ffaasstt
function, on the other hand,
works by taking the first word in the message and con-
verting it to lowercase. The word is then used to per-
form a search in the keyword array and, if a match is
found, the appropriate PHP file is included and execut-
ed.
Writing Keyword Scripts
Since keyword scripts are an idea I came up specifically
for this article, it's probably a good idea
to discuss them a little. Essentially, a
keyword script simply contains code
that determines what happens when a
keyword is matched. To make it possi-
ble for multiple scripts to coexist, the

actual functionality is stored in a func-
tion that has the same name as the key-
word that corresponds to a particular
script.
Let's assume, for example, that we
want to match the word "hello" at the
beginning of a message and reply with
an SMS of our own. In this, case, we'd
have to write a PHP script, called
hheelllloo pphhpp
, similar to the one shown in
Listing 2. As you can see, the file contains a function, called
hheelllloo(())
, that accepts the incoming message and the
sender's phone number as arguments.
Sending a reply to the sender through SMS is a simple
process—all we need to do is add a row to the outbox
table of the
ssmmss
database. The SMS daemon will period-
ically poll the database for new outgoing messages and
send them automatically.
February 2004

PHP Architect

www.phparch.com
13
FFEEAATTUURREE
Write SMS Applications With PHP and Gnokii

776 ? S 0:00 /bin/sh /command/svscanboot
805 ? S 0:01 \_ svscan /service
819 ? S 0:00 \_ supervise smsd
3009 ? S 0:00 | \_ /usr/local/gnokii/bin/smsd -u xxx -p xxx -d xxx -m mysql
820 ? S 6:26 \_ supervise smsparse
23813 ? S 0:00 | \_ /usr/local/bin/php -q /opt/www/smsparse/smsparse.php
818 ? S 0:00 \_ supervise log
837 ? S 0:01 \_ multilog t ./main
Figure 1
“The
ddaaeemmoonnttoooollss
package is a
collection of tools
that can be used to
monitor and man-
age UNIX-based
services.”
Your Own PHP Daemon: Using
daemontools
The last step in our quest consists of setting up our PHP
script to run as a daemon. You could, in theory, simply
run the script and detach it from the console, but if
you're running a proper server, a more robust configu-
ration is required—and this is where the
ddaaeemmoonnttoooollss
package comes into place.
The configuration of
ddaaeemmoonnttoooollss
is a bit complicated
compared to the other packages we have seen in this

article because it involves a relatively large number of
files and directories. However, once one realizes that
there is method to the madness, it's not quite so bad.
Given the amount of space alloted for this article, I will
leave it up to you to get
ddaaeemmoonnttoooollss
up and running—
the documentation is very clear and there are plenty of
resources for this purpose on the Net.
When
ddaaeemmoonnttoooollss
is installed it creates a directory
called
//sseerrvviiccee
. This will contain information on all the
various services that
ddaaeemmoonnttoooollss
is running; a program
called
ssuuppeerrvviissee
monitors the
//sseerrvviiccee
directory and
takes care of starting and keeping the services running
as needed. Compared to "normal daemons", which are
started at boot time,
ddaaeemmoonnttoooollss
services are started by
ssuuppeerrvviissee
and, if any of them is killed or dies unexpect-

edly,
ssuuppeerrvviissee
itself takes care of restarting them again
automatically.
Therefore,
ddaaeemmoonnttoooollss
is an excellent solution if you
want your services to be running all the time and be
monitored for failures of any kind. However, not all
services are suitable to run with this package—they
have to behave in a certain manner that makes it pos-
sible for
ssuuppeerrvviissee
to interact with them in an automat-
ed fashion.
Luckily, most applications can be modified so that
they can be compatible with
ssuuppeerrvviissee
, and our
ssmmssppaarrsseerr
script is no exception. First of all, we must
ensure that the script can be run without having to
explicitly invoke the PHP interpreter. Under a UNIX
shell, this is done by introducing a "shebang", that is, a
special command at the beginning of the file that tells
the shell interpreter which application the script should
be piped through in order for it to be executed.
Let's start by figuring out where PHP is installed:
# whereis php
On my machine, a RedHat 8 server, the commands

outputs the following:
php: /usr/local/bin/php /usr/local/lib/php
/usr/local/lib/php.ini
This means that I have the PHP interpreter's binary
installed in
//uussrr//llooccaall//bbiinn//pphhpp
.
It's now time to create a service directory for our serv-
ice. We'll start by creating a "service" directory for
ssmmssppaarrssee
in the
//uussrr//llooccaall//ssmmssppaarrssee//
directory, where
I will assume that you have stored the
ssmmssppaarrssee pphhpp
script and its underlying directory structure with all the
keyword scripts. We will call the directory
ssuuppeerrvviissee
ssmmssppaarrssee
:
# mkdir -p /usr/local/smsparse/supervise-smsparse/
We'll use the service directory to store all the informa-
tion that
ssuuppeerrvviissee
needs to run
ssmmssppaarrssee
correctly.
Next, we will focus on getting smsparse running. In
the
//uussrr//llooccaall//ssmmssppaarrssee//ssuuppeerrvviissee ssmmssppaarrssee//

directo-
ry, create an executable text file called
rruunn
that contains
the following two lines of shell commands:
#!/bin/sh
exec /usr/local/bin/php -q
/usr/local/smsparse/smsparse.php
That's it! If we now create a symlink from the
//sseerrvv
iiccee
directory to our newly created folder,
ssuuppeerrvviissee
will
automatically take care of starting and monitoring our
server:
# ln -s /usr/local/smsparse/supervise-smsparse/
/service/supervise-smsparse/
We will do the same for
ssmmssdd
, so that we can have
ssuuppeerrvviissee
monitor the Gnokii daemon process as well.
As described earlier, we installed Gnokii in
//uussrr//llooccaall//GGnnookkiiii//
and, therefore, the
ssmmssdd
binary will
reside in
//uussrr//llooccaall//GGnnookkiiii//bbiinn//ssmmssdd

. As with out serv-
er, we will create a subdirectory to house the execution
files for
ssmmssdd
:
# mkdir -p /usr/local/Gnokii/supervise-smsd/
Next, we'll write a new
rruunn
file:
#!/bin/sh
exec /usr/local/Gnokii/bin/smsd -u sms
-p sms -d sms -m mysql
Finally, to start the
ssmmssdd
run file, we link the
ssuuppeerr
vviissee ssmmssdd
directory into
//sseerrvviiccee
with:
# ln -s /usr/local/Gnokii/supervise-smsd/
/service/supervise-smsd/
If you now check your process list, you should see
your
ssmmssppaarrssee
and
ssmmssdd
processes listed-that is, if you
have done everything right:
# ps axf

776 ? S 0:00 /bin/sh /command/svscanboot
805 ? S 0:01 \_svscan /service
819 ? S 0:00 \_supervise smsd
3009 ? S 0:00 | \_/usr/local/Gnokii/bin/smsd -u xxx -p xxx -d xxx -m mysql
820 ? S 6:26 \_supervise smsparse
23813 ? S 0:00 \_/usr/local/bin/php -q
/smsparse/smsparse.php
Keeping Tabs on the Situation
Now that we have turned our
ssmmssppaarrssee
script into a true
daemon, we could use some logging capabilities so that
we can diagnose any problems properly should anything
go wrong. As part of the
ddaaeemmoonnttoooollss
package, you will
February 2004

PHP Architect

www.phparch.com
14
FFEEAATTUURREE
Write SMS Applications With PHP and Gnokii
find a small program, called
mmuullttiilloogg
, that is capable of
logging the output of a service directly to a set of auto-
matically-rotated logfiles. This means that, if we set up
our service settings properly, we won't even need to

write any special code for the purpose of creating activi-
ty logs!
To enable the logging functionality, start out by cre-
ating a log directory in
ssuuppeerrvviissee ssmmssppaarrssee
:
# mkdir -p /usr/local/smsparse/supervise-smsparse/log
The logging process acts much like a normal process
running under
ssuuppeerrvviissee
. It needs its own directory
and run file; therefore, we need to create a special run
file at
//uussrr//llooccaall//ssmmssppaarrssee//ssuuppeerrvviissee
ssmmssppaarrssee//lloogg//rruunn
that contains the following com-
mands:
#!/bin/sh
exec multilog t ./main
MMuullttiilloogg
supports a wide range of arguments, which,
in turn, make it possible to create very complex logging
rules. Our command line above, however, is quite simple
and really just means "add a timestamp on each line, and
store the logfiles in
//mmaaiinn
". The
tt
argument represents
the number

of Temps Atomique International (TAI) seconds since
1970-01-01 00:00:10 TAI. As you might remember from
Listing 1, we prepend a
ddaattee((''YY mm dd HH::ii::ss''))
string
before each line is outputted and, therefore, we will actu-
ally have double timestamps in the log file (naturally, you
can modify the script to omit its timestamp, or change
the
mmuullttiilloogg
instantiation to do the same).
We don't need to link the
lloogg
directory directly from
//sseerrvviiccee
. The
ssuuppeerrvviissee
program will execute the run-
file it contains automatically for us. However, you must
restart
ssuuppeerrvviissee
to make it aware of the new log direc-
tory. You can, once again, use the
ssvvcc
program to send
a TERM signal to the service:
# svc -t /service/supervise-smsparse/
A new look at the process list (see Figure 1) will show
you that
ssmmssppaarrssee

has been started again, together with
the logging process. This means that our services are
now managed by
ssuuppeerrvviissee
and will run indefinitely-all
the while providing us with a nice logfile, which we can
monitor by using the
ttaaiill
utility:
# tail /service/supervise-smsparse/log/main/current
@400000003ffc1dfc22cb14e4 2004-01-07 15:54:05:
Starting sms parser
@400000003ffc1dfc22cb2484 2004-01-07 15:54:05: hello
@400000003ffc1dfc22cb2c54 2004-01-07 15:54:05: suc-
cess
This example shows that
ssmmssppaarrsseerr
was started cor-
rectly, and 2 keywords where found,
hheelllloo
and
ssuucccceessss
.
As you can see, the TAI timestamp at the beginning of
each line is a bit cryptic, but it can be translated into a
human readable form by piping the
ttaaiill
output
through
ttaaii6644nnllooccaall

like this:
# tail /service/supervise-smsparse/log/main/current |
\
tai64nlocal
2004-01-07 15:57:27.380601500 2004-01-07 15:55:46:
Starting sms parser
2004-01-07 15:57:27.380605500 2004-01-07 15:55:46:
hello
2004-01-07 15:57:27.380607500 2004-01-07 15:55:46:
success
Conclusion
The easiest way to test your new Gnokii setup is to grab
another cell phone and send an SMS message contain-
ing the word "Hello" to your Gnokii phone. If all goes
well,
ssmmssppaarrssee
will pick it up and reply back with the
message we entered in the
hheelllloo
keyword script.
As you have probably by now realized, it's not that
hard to set up a mobile service through which you can
exchange information with your users by utilizing SMS.
Even if you're not in the business of running SMS gate-
ways, you could use it for a variety of other activities.
For example, you can use it to provide "fun" services,
like interactive voting, or a useful server monitoring
interface for your internal network. The list of possibili-
ties is very long—and my clients have shown great
interest in using SMS as a complement to other servic-

es.
If you're worried about scalability, this solution may
not be for you, as it will have trouble handling a very
large number of messages on a daily basis. However, it
is so inexpensive that it could well be a good starting
point for a more serious implementation. The good
news is that you'll be able to stay with Gnokii even if
your needs grow, as newer versions of the package are
slated to support multiple phones.
February 2004

PHP Architect

www.phparch.com
15
FFEEAATTUURREE
Write SMS Applications With PHP and Gnokii
About the Author ?>
To Discuss this article:
/>When Eric's not out skiing or hiking, he's working as a freelance develop-
er on various projects. His current focus is finishing his education in
open-air alpine environments.
W
elcome to the world of PHP-GTK. Why intro-
duce GTK to a largely web-based language?
Well, convenience and portability come to
mind, for example. Sometimes it's not feasible to write
a Java Swing interface when you've invested so much
time in your PHP classes, as you need to rewrite large
portions of code. While it could be done, you'd have to

fork your code in two projects, and use two different
languages. That's not something you can easily con-
vince many clients to do.
Content management—a very common task for most
websites these days—represents a typical example of
an activity that is often performed directly through the
web but that could really be best served by a "true"
GUI-based client application. In most circumstances,
creating a separate application is an expensive proposi-
tion, due to the duplication of code involved, the addi-
tional expertise needed and the difficulty of using a lan-
guage that will run properly on a wide variety of plat-
forms. In this article, we'll tackle porting an existing
HTML-based news manager to PHP-GTK-and you'll see
how easy it is to make the jump from Web to GUI with
this powerful, if often neglected, platform.
In creating our project, we'll start with a data abstrac-
tion layer and a traditional HTML interface that we'll
ditch later on. This article gets a little complex-so as a
prerequisite please install PHP-GTK, and create a table
in mysql with the schema shown in Listing 1. An SQL
dump with a few sample rows of data can be found in
the files for this article—it's always great to have some
sample data to work with.
The Data Abstraction Layer
As a general rule, I create a data abstraction layer for
every complex project I work on. Some people swear
by this approach, others swear at it. My personal praise
goes to abstraction layers because I can do things like
automatically change the modified date of a record

without remembering to do it in each instance of SQL
code. An abstraction layer can also validate data and
check the credentials of the person trying to perform
changes in a multi-user situation.
Consider the code in Listing 2, which represents a
simple data abstraction for a news item. Once you have
this example up and running, you can test creating a
row in the database with the code from listing 3. As you
can see, once the abstraction layer is established, we
don't even have to worry about embedding SQL state-
ments in our code.
February 2004

PHP Architect

www.phparch.com
16
FF EE AA TT UU RR EE
Offline Content Management with PHP-GTK
by Morgan Tocker
PHP: 4.1+ (4.3 or greater recommended)
OS: Windows, Linux
Applications: PHP-GTK, MySQL
Code: />Code Directory: gtk-cms
REQUIREMENTS
Over the years, I have had the opportunity to work on a
few content management systems for websites of varying
complexity. While each CMS is a little different from the
others, I can’t help but think that sometimes I find myself
performing the same hacks and workarounds over and

over just to get around the limitations of HTML. The
desired output of the majority of our PHP work must be
web based—but management of the content doesn’t
have to be.
+ + + + + + +
| Field | Type | Null | Key | Default | Extra |
+ + + + + + +
| id | int(11) | | PRI | NULL | auto_ increment |
| author | varchar(64) | | MUL | | |
| story | text | | | | |
| created | int(10) | YES | | NULL | |
| modified| int(10) | YES | | NUL L | |
| subject | varchar(255) | YES | | NULL | |
+ + + + + + +
Listing 1
An HTML-based News Manager
Listings 4 through 6 provide the basis for a very simple
news management system based entirely on the web.
Listing 4 (
iinnddeexx pphhpp
) is the home page of the system,
which creates a list of all the news available in the data-
base. Listing 5 (
eeddiitt pphhpp
) provides the necessary inter-
face for editing the news items and Listing 6 (
ssaavvee pphhpp
)
takes care of saving our changes to the database.
Although this example works well, there are a few

problems with it. First of all, we have no data integrity.
For example, the author "Morgan Tocker" is probably
the same as the author "Morgan J Tocker" and "M.
Tocker". But if I wanted to compile a list of authors
(
SSEELLEECCTT ddiissttiinncctt((aauutthhoorr)) FFRROOMM nneewwss WWHHEERREE vviissiibbllee ==
''11'';;
), it might well contain each of the three individual
authors that were just mentioned, since we are allow-
ing each user to enter his or her name every time a
news item is created or edited.
Another problem is the handling of whitespace in the
author's name.
''TThhiiss ''
does not equal
''tthhiiss''
and
''
tthhiiss ''
does not equal
'' tthhiiss''
. Got it? Don't laugh—it
happens. In an eternal struggle to keep data clean, we
can use
ttrriimm(())
to zap off the unwanted whitespace, or
use a HTML
<<sseelleecctt>>
to solve the typos in our first
example. This would work, but it comes with another

limitation: we couldn't easily add more authors to the
list. You could add a field called "other author", or write
a bit of JavaScript with an item called "Other " on the
list, whereby an
oonncchhaannggee(())
event would prompt the
user for the name of the new author, and then recreate
the list dynamically.
What I'd actually like to see here, however, is a
combo field. A combo box is neither a textfield or a
select box—it's actually both of them at the same
February 2004

PHP Architect

www.phparch.com
17
FFEEAATTUURREE
Offline Content Management with PHP-GTK
1 <?php
2
3 mysql_connect(‘localhost’,’morgo’,false);
4 mysql_select_db(‘articles’);
5
6 class news {
7 var $id; // primary key auto_increment
8 var $author; // author
9 var $subject; // subject of the news article
10 var $created; // date the article was published
11 var $modified; // modified date

12 var $story; // body of the news
13 var $visible; // bool ? is the record visible
14
15 function news($id=false) {
16
17 (int) $id; // cast the ID
18 if ($id) {
19 $result = mysql_query(“SELECT * FROM article WHERE id = ‘$id’”);
20
21 foreach(mysql_fetch_array($result) as $field => $value)
22 $this->$field = $value;
23
24 } else {
25 // Insert a dummy entry into the database
26 mysql_query(“INSERT into article (created, modified) VALUES (UNIX_TIMESTAMP(), UNIX_TIMESTAMP())”);
27 $this->id = mysql_insert_id();
28 }
29 }
30
31 function set_property($property, $value) {
32
33 // set a $property to value in the database
34 // Note - this mechanism causes n(fields) database queries
35 // to save data. There are alternatives
36
37 mysql_query(“UPDATE article SET $property = ‘$value’, modified = UNIX_TIMESTAMP() WHERE id = ‘“.$this->id.”’”);
38 $this->$property = $value;
39 }
40 }
41

42 ?>
Listing 2
1 <?php
2 $news = new news(); // Create a new instance of news
3
4 echo “the id of our news article is “.$news->id.” <BR>\n”;
5
6 /* Set some properties for the news item */
7
8 $news->set_property(‘author’, ‘Morgan Tocker’);
9 $news->set_property(‘subject’, ‘An article by Morgan’);
10 $news->set_property(‘visible’, ‘1’);
11 $news->set_property(‘story’, ‘This is the body of my message’);
12 ?>
Listing 3
1 <?php include(?data.php?);?>
2 <?php
3
4 foreach(news::enum_news() as $id => $subject) {
5
6 echo ?<A href=?edit.php?id=$id?>?.htmlentities($sub-
ject).?</A><BR>\n?;
7
8
9 }
10
11 ?>
Listing 4
time—and it's a blessing (or a curse if you prefer) to all
modern operating systems that someone left it out of

the HTML 4.0 specification.
Getting Your Feet Wet With GTK
Since the kind of functionality that we want cannot be
provided by a web browser (at least not without a mas-
sive amount of custom work), we'll have to turn else-
where—and that's where PHP-GTK comes into play.
Our PHP-GTK application actually provides a "true" GUI
to our news management system, and works on a dif-
ferent machine from that of the webserver.
The core of the application is shown in Listing 7. As
you can see, the PHP-GTK version of the news manag-
er is a bit more complex than the plain-HTML one,
although the length of the script is quite deceptive,
since the functionality of the three scripts that made up
the previous application has now been incorporated
into a single one.
At the core, however, the application is extremely
simple. Essentially, we create a set of GTK objects, and
connect them to various handlers, which, in turn, are
automatically called by the system when a specific
event takes place—such as, for example, the user click-
ing on a button. Figure 1 shows you the application
running on a Linux system.
The PHP-GTK application requires a copy of
ddaattaa pphhpp
,
which was our Listing 2, so, if you update your class
library, be sure to copy it over to your PHP-GTK appli-
cation. Naturally, this is a great aspect of writing all
your applications with the same language, since you're

able to happily recycle your code as many times as you
want, and you can run it on a variety of platforms.
There is a configuration option in our
ddaattaa pphhpp
which
chooses the MySQL server to connect to. In the web
server's case, it's probably
llooccaallhhoosstt
. In the case of the
PHP-GTK application, however, you will probably be
connecting to the database remotely and, therefore,
you should enter the IP or hostname of your server.
Now that the application is running, notice how the
combo box used for the author's name makes the
application easier to use. Rather than having to build
additional pages or cumbersome Javascript-based solu-
tions, we can rely on the combo box to allow the user
to either choose an existing author or create a new one
through a single control.
Remembering Data
I'm an Apple Cocoa programmer, and Cocoa applica-
tions feature a concept called "defaults". A default is
February 2004

PHP Architect

www.phparch.com
18
FFEEAATTUURREE
Offline Content Management with PHP-GTK

Figure 1
basically the PHP equivalent to a session that never
expires. It's a variable that you can set, and will remain
available to you indefinitely, even if you shut down the
application and launch it agagin.
Defaults can be really handy for settings and prefer-
ences, although they are not quite as easy to imple-
ment in a PHP-GTK application as they are in Cocoa.
Luckily, I've written a PHP script to store this data, so
you won't have to. It creates a file called
$$SSCCRRIIPPTT__NNAAMMEE sseessssiioonn
, where it stores default informa-
tion. When you first install (or execute) the application,
be sure to create this file in advance with the proper
permissions, so that no error will be output even if the
user under which the script is running does not have
write access to the folder where the defaults file resides.
To tap into the features of defaults, you'll need to add
the following line to the beginning of your file:
<?php
include_once session.php;
?>
Creating a default is the same as creating a session.
The GTK application can store data in the
$$__SSEESSSSIIOONN
super global, and the same data will be available on
relaunch. The following is an example:
<?php
include defaults.php;
$_SESSION['times_launched']++; // add 1

echo "You have launched this app
".$_SESSION['times_launched'];
?>
If you look at the source for
ddeeffaauullttss pphhpp
(Listing 8),
you will notice that it really works by setting itself up as
a custom session handler that simply saves the informa-
tion to a file. As you can see, the code is very simple,
and explaining how custom session handlers work is
beyond the scope of this article. You can, however,
refer to Sean Coates' excellent article on this topic in
the January 2004 issue of php|a.
Making the GTK-APP work offline.
Now that we have a GUI-based application that doesn't
require a browser and a web server to run, the next
step would be to make it independent of the database
as well, so that you can use it as a completely "offline"
application that can be run even when no connectivity
is available.
February 2004

PHP Architect

www.phparch.com
19
FFEEAATTUURREE
Offline Content Management with PHP-GTK
1 <?php include(‘data.php’);?>
2 <?php

3
4 (int) $_GET[‘id’];
5
6 $news = new news($_GET[‘id’]);
7
8 ?>
9 <html>
10 <head>
11 <title>Edit News</title>
12 </head>
13 <body>
14
15 <h2>Edit record <?php echo $news->id?></h2>
16 <form method=”POST” action=”save.php”>
17 <INPUT type=”hidden” name=’id’ value=’<?php echo $news->id?>’>
18 <table>
19 <tr>
20 <td>Subject</td>
21 <td><input type=’text’ name=’subject’ value=’<?php echo htmlentities($news->subject)?>’></td>
22 </tr>
23 <tr>
24 <td>Author</td>
25 <td><input type=’text’ name=’author’ value=’<?php echo htmlentities($news->author)?>’></td>
26 </tr>
27 <tr>
28 <td>Story</td>
29 <td><TEXTAREA name=”story” cols=72 rows=20><?php echo htmlentities($news->story)?></TEXTAREA></td>
30 </tr>
31 </table>
32

33 <input type=’submit’ value=’Save’>
34 </form>
35
36
37 </body>
38 </html>
Listing 5
1 <?php include(‘data.php’);?>
2 <?php
3
4 (int) $_POST[‘id’];
5
6 $news = new news($_POST[‘id’]);
7
8 foreach($_POST as $field => $value) {
9 if (get_magic_quotes_gpc())
10 $news->set_property($field, $value);
11 else
12 $news->set_property($field, addslashes($value));
13
14 }
15
16
17 header(“Location: index.php”);
18
19
20 ?>
Listing 6
We're 90% there already. All we really have to do is
build a proper system of caching and check to make

sure no changes have occurred since our last update.
There are two generally accepted ways of performing
this last operation:
• Checking if the data has changed
from the data we grabbed.
• Checking to see if the timestamp or
the last-modified date is more recent
than the timestamp from when we
grabbed the record.
For our application, I am going to select the second
of these choices, given that it's easier to compare time-
stamps than it is to compare content, particularly if
there's a lot of it. However, keep in mind that time-
stamps are always going to be based on the local
machine's clock and, without the database acting as a
broker to determine absolute time, it's possible that
your content will de-synchronize, thus causing unwant-
ed inconsistencies. Here's how we'll be performing our
up-to-date checks:
<?php
$database_copy = new news($id, true);
if ($news->modified <= $database_copy->modified) {
// Provide a warning - our copy is out of date
} else {
// you may update safely
}
?>
Caching Content
Since we cannot store the information in the database,
we need a means to cache our information until we can

synchronize it. Given that they provide a persistent
offline storage mechanism, defaults seem to be the per-
fect choice here.
We are going to cache each of the objects for later
retrieval by adding an
uuppddaattee__ccaacchhee(())
method to our
ddaattaa pphhpp
class, which you can see in Listing 9. For
example, to check if we have a cache for record ID 6,
we can see if it's an object:
<?php
If (is_object($_SESSION['record']['6'])) {
// we have cache for 6.
}
?>
To make the synchronization process faster, we could
also only accept cached data that is less than 72 hours
old as good without making the roundtrip to the data-
base to check whether it has changed.
<?php
if (is_object($_DEFAULT['record']['6']) && (time() <
$_DEFAULT['record']['6']->modified + (3600*72)) {
// we have recent cache for 6
}
?>
In this case, however, you really want to make sure
that your time is properly synchronized with the
MySQL server—you may choose to get your current
time by executing a

SSEELLEECCTT UUNNIIXX__TTIIMMEESSTTAAMMPP(())
on the
database server.
Before we write the data back to the database, we
will have to check to see that no changes have occurred
February 2004

PHP Architect

www.phparch.com
20
FFEEAATTUURREE
Offline Content Management with PHP-GTK
1 <?php
2
3 function __session_open($save_path, $session_name)
4 {
5 return true;
6 }
7
8 function __session_close()
9 {
10 return true;
11 }
12
13 function __session_read($sessid)
14 {
15
16 $fp = fopen($_SERVER[‘SCRIPT_FILENAME’].”.defaults”, ‘rb’);
17 return fread($fp,

filesize($_SERVER[‘SCRIPT_FILENAME’].”.defaults”));
18
19 }
20
21 function __session_write($sessid, $val)
22 {
23
24 $fp = fopen($_SERVER[‘SCRIPT_FILENAME’].”.defaults”,’wb’);
25
26 fwrite($fp,$val);
27 fclose($fp);
28 }
29
30 function __session_destroy($sessid)
31 {
32 return true;
33 }
34
35 function __session_gc($maxlifetime = 900)
36 {
37 true;
38 }
39
40 session_set_save_handler(‘__session_open’, ‘__session_close’,
‘__session_read’, ‘__session_write’, ‘__session_destroy’, ‘__ses-
sion_gc’);
41
42 if (!session_is_registered(“default”)) {
43 $default = array();
44 session_register(“default”);

45 }
46
47 session_start();
48
49 ?>
Listing 8
1 <?php
2
3 class news {
4
5
6
7 function update_cache() {
8
9 foreach(news::enum_news() as $id => $subject) {
10
11 $tmp = new news($id);
12 // You may even store an object in a default
13 $_SESSION[?record?][$id] = $tmp;
14 }
15 }
16
17 ?
18 }
19 ?>
Listing 9
while the application was working offline. If there were
changes, we will need to display a proper warning—for
example by showing a dialogue box.
Where to go from here

In order for the application to be more versatile, you
may want to integrate it with the equivalent of an
"Outbox", where changes to content are written to, but
no updates take place straight away. The outbox will
just be another array of records saved in your defaults—
very similar to a cache but organized in a different way
that makes it easier to catch and revise updates before
they take place.
A good news management system could work simi-
larly to the way most mail clients work, with the poten-
tial to work both online and offline depending on
whether a connection to the database is available.
Once this mechanism is in place, you can take advan-
tage of the application's layout to add more functional-
ity, such as workflow management. For example, if
your environment calls for the approval of news items
before they are published, you could manage the entire
flow of operations through a series of "drop boxes"
where each item is deposited by users with the proper
credentials.
Another possible improvement would be to include
the possibility of marking certain changes or new news
items as "drafts", so that you can save them (without
publishing them on to the database) and work on them
later.
Finally, the editing method is very basic and would be
much more effective, particularly for non-technical
users, if it were based on a more advanced interface.
Interestingly enough, PHP-GTK also supports Scintilla, a
very advanced open-source component that plugs into

GTK to provide extended editing capabilities (once you
download it from
you can com-
pile it into your version of PHP-GTK with
//ccoonnffiigguurree
eennaabbllee sscciinnttiillllaa eennaabbllee ggttkkhhttmm
). By working a
Scintilla component into your system, you could make
the editing process much easier for your users.
February 2004

PHP Architect

www.phparch.com
21
FFEEAATTUURREE
Offline Content Management with PHP-GTK
About the Author ?>
Tips for Writing Applications with PHP-GTK
To Discuss this article:
/>Morgan Tocker is a freelance developer living and working in Brisbane,
Australia. His consultancy business,
wwwwww iicceeddoottbblluuee ccoomm
, is responsi-
ble for all sorts of php hacks.
Error Checking
The lifespan of your typical PHP-GTK application is usually longer than that of its web-based coun-
terparts. It will have to keep running for several hours, with functions being called over and over
again. For a GTK application, you may find that you will want to manage your error handling, and
check the integrity of your variables frequently. While you should be doing this with web-based

applications, too, there is less of an opportunity for laziness in GTK.
For example, I had a problem with an earlier version of PHP-GTK where the incorrect data seemed
to be returned intermittently – and my application crashed and burned. In going through it with a
fine-tooth comb I checked the integrity of data at a few points and, if it didn’t return the expect-
ed results, I either tried again or produced a ‘nicer’ error.
In Summary, it’s a good idea to check that an item is still an array/object/integer (or whatever it
was supposed to be) and that it is not empty/null. Personally, I look forward to the release of PHP
5 and exception handling, when GTK & PHP can be taken to the next level and it will become eas-
ier to tackle these issues.
Portability, Recycling, and Reusing
Another good idea is to try and store the important parts of your code nested in function calls, as
opposed to using the traditional linear approach. Keeping in mind the way callbacks work, you will
find it easier to work with both a web-based and a GTK version of the same application if they both
use OOP techniques. Finally, try to separate your code from your desired output, so that you can
create a file like
ddaattaa pphhpp
and share it between the two without the need to branch your code.
Can’t stop thinking about PHP?
Write for us!
Visit us at
/>I
n the last issue, we talked a little about the Zend
Engine internals and how they relate to writing an
extension, about how to create an extension skeleton
using the
eexxtt__sskkeell
tool, how to write extension func-
tions and access their parameters (using a
ssccaannff(())
style

function), how to return simple types (like strings and
integers) and how to build up a PHP array. We covered
a fair amount of ground, but there are still plenty more
things to learn about PHP extension writing.
In this issue, we're going to look at arrays again and
see how it is possible to build multi-dimensional arrays
and how to traverse the elements of, or look-up a par-
ticular value from an array.
Multi-Dimensional Arrays
As we saw last time, PHP arrays are implemented using
hash-tables. This approach allows indexing the array
using a string or integer key to fetch its values. Since a
hash-table is not a native C type, fetching its values is
not quite as simple as with native C arrays. On top of
that, the Zend Engine has no built-in support for multi-
dimensional arrays—they are simply implemented by
storing another array in the appropriate slot of the
hash-table. This can be a difficult or daunting prospect
for the budding extension author, especially consider-
ing the state of the internals documentation, even
though it is actually quite simple to implement.
For our first example, let's create a two dimensional
array where the first dimension contains a list of first
names and the second dimension a list of surnames. If
you're not sure what I mean, Listing 1 contains the PHP
script equivalent for the C code in Listing 2. The con-
tents of Listing 1 should be self-explanatory, so let's
take a look through Listing 2 now, line by line.
Lines 1 through 5 declare a C-style 2D array. The two
sets of square brackets tell the compiler that it has two

dimensions; the first dimension has 3 slots, while the
second dimension has 2 slots. These correspond to the
3 sets of first and last names that we are going to use
to initialize our PHP array. Lines 7 and 8 are comments
describing the prototype for the function. Hopefully
you will recall that these comments, although they
have no effect on the code itself, are an important cod-
ing convention that helps to remind you how the func-
tion is intended to be used. Line 9 uses the
PPHHPP__FFUUNNCCTTIIOONN
macro to declare the actual PHP function.
Lines 11 and 12 declare some temporary variables—
ii
will represent the person whose name we are adding,
and
jj
will indicate if we are looking at their first or last
name. The
ttmmppaarrrraayy
variable, as its name implies, will
act as temporary storage for the array we create for
each person. Line 14 initializes the PHP function's
rreettuurrnn__vvaalluuee
as an array, and then we begin a loop on
line 16 which will step through each person in our
names array, using the variable
ii
as the counter. For
February 2004


PHP Architect

www.phparch.com
23
FF EE AA TT UU RR EE
Writing PHP Extensions: Managing Arrays
by Wez Furlong
PHP: 4.3+
OS: N/A
Other Software: Working PHP source and
compiler environment
Code:
/>Code Directory: extensions
REQUIREMENTS
As we saw last time, writing PHP extensions in C isn't quite
as difficult as you might think. In this issue, we're going to
dive into the hash API and use it to traverse arrays and
fetch values from them.
each person, we allocate a PHP variable using the
MMAAKKEE__SSTTDD__ZZVVAALL(())
macro, we set it up as an array (lines
17 and 18), and then we step through each of their
names and add them as string elements to our tempo-
rary array (lines 19 to 21). Having prepared our "per-
son" array, we need to add it to our "people" array—the
return value for the function (line 24).
The code should be fairly simple to follow, although
you might be wondering about two things in particu-
lar. The first thing you might ask is whether you should
(or should not) worry about freeing the temporary

array value. In this case you should not free it—we
"gave" it to the Zend Engine when we used
aadddd__nneexxtt__iinnddeexx__zzvvaall(())
, and the engine will take care of
freeing it at the appropriate time. If we were to free it
ourselves, we would cause a crash some time later in
the script that would be difficult to track down.
The other question you might be asking is whether
we need to return something from the function. The
answer is no—the C function prototype is declared as a
vvooiidd
function, so it has nothing to return in the usual
sense. Instead, PHP passes us a
rreettuurrnn__vvaalluuee
variable
that we populate—it is this variable that will be passed
back into your PHP script when the function returns.
Since the first thing we are doing is setting up the
rreettuurrnn__vvaalluuee
, we don't need to do anything special
after the loops that populate it and, therefore, we sim-
ply "fall out" of the bottom of the function.
As you can see, building a multi-dimensional array is
not that hard. Although my example is quite succinct,
the same principle can be used to build PHP arrays with
any number of dimensions—you simply create a new
intermediate array to hold the contents of the dimen-
sion you want to add, and then add it. You're not lim-
ited to strings for the values either—you can use any
valid

zzvvaall
value (integers, real numbers, strings,
resources and boolean values, or even resources if you
want to).
Now that you are have mastered returning multi-
dimension arrays, how about looking at working with
multidimensional arrays that have been passed into
your function?
Getting Stuff Out of Arrays
There are two things that you will typically want to do
with an array that has been passed to your function—
either you want to look up a specific keyed value and
do something with it, or you want to step through all
values and do something with each of them. We'll deal
with the first of these now.
So far, we've used some really convenient macros to
add items to arrays—these macros insulate us from the
not-so-pretty guts of the hash table implementation.
However, we've now reached a point where we must
step beyond these macros—because there are no
macros for fetching an item from an array.
Before we delve in, it's worth thinking for a minute
about how you use arrays in your PHP scripts. Imagine
that you have a PHP script that accepts a couple of
$$__GGEETT
parameters—name and age—and displays them
on some kind of e-card. Let's also pretend that the age
parameter is optional-the e-card will happily display
something good regardless of whether the age param-
eter is passed or not. PHP (being the nice flexible thing

that it is), will allow you to access the age parameter
using
$$__GGEETT[[''aaggee'']]
syntax, even if it is not there (the
value returned to your script will be
NNUULLLL
in that case
and, at worst, the interpreter will print out a warning
message to indicate that the element does not exist). If
you are slightly more strict with your code, you might
first want to check that the age value is present by
using
iisssseett(())
and then take a different course of
action.
This is a simple validation of input parameters and,
while PHP allows you to be a lazy script coder, it does-
n't allow you to be a lazy extension author—you must
check if an element is present before you access it, since
the NULL you get back from the hash API is the kind
that causes a crash if you don't handle it properly. With
that in mind, take a look at Listing 3, which represents
our hypothetical e-card generating function. The idea is
that you pass an array of values to the function, and it
will pull out the name and age.
Lines 1 to 3 are the usual prototype comments and
the
PPHHPP__FFUUNNCCTTIIOONN
declaration. Next, we declare a vari-
February 2004


PHP Architect

www.phparch.com
24
FFEEAATTUURREE
Writing PHP Extensions: Managing Arrays
1 <?php
2 function phpa_2d_array()
3 {
4 return array(
5 array(“Rasmus”, “Lerdorf”),
6 array(“Zeev”, “Suraski”),
7 array(“Andi”, “Gutmans”)
8 );
9 }
10 ?>
Listing 1
1 static const char *names[3][2] = {
2 { “Rasmus”, “Lerdorf” },
3 { “Zeev”, “Suraski” },
4 { “Andi”, “Gutmans” }
5 };
6
7 /* {{{ proto array phpa_2d_array()
8 Returns a 2d array of names */
9 PHP_FUNCTION(phpa_2d_array)
10 {
11 int i, j;
12 zval *tmparray;

13
14 array_init(return_value); /* $r = Array() */
15
16 for (i = 0; i < 3; i++) {
17 MAKE_STD_ZVAL(tmparray); /* $t = */
18 array_init(tmparray); /* Array() */
19 for (j = 0; j < 2; j++) {
20 /* $t[$j] = $names[$i][$j] */
21 add_index_string(tmparray, j, names[i][j], 1);
22 }
23 /* $r[] = $t; */
24 add_next_index_zval(return_value, tmparray);
25 }
26 }
27 /* }}} */
Listing 2

×