Tải bản đầy đủ

Webmapping with PHP

JULY 2004
VOLUME III - ISSUE 7
JULY 2004
VOLUME III - ISSUE 7
www.phparch.com
The Magazine For PHP Professionals
TM
Plus:
Tips & Tricks, Product Reviews, Security Corner and much more...

Sign up before July 20th and save up to $100!
Christian Mayaud —
Getting Your OSS Business Funded
, Rasmus Lerdorf —
Best Practices for PHP Developers
,
Jim Elliott —
Open Source: The View from IBM
, Daniel Kushner —
Attacking the PHP Market
, Andrei Zmievski —

Andrei’s
Regex Clinic
, Wez Furlong —
Introducing PDO
, Regina Mullen —
OSS in Legal Technology
, Derick Rethans —
Multilingual Development with PHP
,
George Schlossnagle —
PHP Design Patterns
... and many, many more!
php|w rks
Toronto, Sept. 22-24, 2004
Three days of pure PHP
http://www.phparch.com/phpworks
Jump Right To It.
05 Editorial
06 What’s New!
43 Product Review
MicroOLAP Database Designer for MySQL
by Peter B. MacIntyre
61 Security Corner
Form Spoofing
64 Tips & Tricks
By John W. Holmes
68 exit(0);
Constant Variables in an Invariant Environment
by Andi Gutmans and Marco Tabini
10 Charted Data Mining with PHP and
JpGraph
by Jason E. Sweat
19 User Abandonment Tracking with PHP
and DHTML
by Darrell Brogdon
25 Webmapping with MapServer
by Rodrigo Becke Cabral
37 Build Your Own Debugging Library
Debug PHP—the Smart Way
by Michael Paul Bailey


48 Socket Interprocess Communication
Cool stuff with PHP, sockets and XUL
by Rick Morris
3
July 2004

PHP Architect

www.phparch.com
TABLE OF CONTENTS
II NN DD EE XX
II NN DD EE XX
php|architect
Features
Departments
TM
*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.
Choose a Subscription type:
CCaannaaddaa//UUSSAA $$ 9977..9999 CCAADD (($$6699..9999 UUSS**))
IInntteerrnnaattiioonnaall AAiirr $$113399..9999 CCAADD (($$9999..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: http://www.phparch.com/print 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
S
ubscribe to the print
edition and get a copy of
Lumen's LightBulb — a
$499 value
absolutely FREE

!
In collaboration with:
Upgrade to the
Print edition
and save!
For existing
subscribers
Login to your account
for more details.
EXCLUSIVE!
EXCLUSIVE!
† Lightbulb Lumination offer is valid until 12/31/2004 on the purchase of a 12-month print subscription.
July 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 7
July, 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
scott@phparch.com
Account Executive
Shelley Johnston
shelley@phparch.com
Authors
Michael Paul Bailey, Darrell Brogdon,
Rodrigo Becke Cabral,
Andi Gutmans, John W. Holmes,
Peter B. MacIntyre, Rick Morris,
Chris Shiflett, Jason E. Sweat
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: info@phparch.com
Editorial: editors@phparch.com
Subscriptions: subs@phparch.com
Sales & advertising: sales@phparch.com
Technical support: support@phparch.com
Copyright © 2003-2004 Marco Tabini & Associates, Inc.
— All Rights Reserved
J
uly is, usually, a month whose highlights are just
how good your tan got at the beach and how many
days you managed to stretch those summer long
weekends to—hardly front-page material.
This year, however, two important events have
taken place.
First, as you undoubtedly know, PHP 5 is now out.
The final release of the new version has been long-
awaited—I remember talking about a release date
over dinner with a few friends almost a year ago—and
now that it’s out we should finally be able to see some
real usage of it out there.
The second event is the launch of the official Zend
Certification program. PHP certification is a topic that
has popped up several times over the past couple of
years on blogs, websites and mailing lists, but most of
us were waiting for a company like Zend to take the
initiative and start a formal certification program that
the industry at large could rely on.
Having been part of the team that developed the
certification exam itself, I think I can safely say that it
is no pushover—although you should have no prob-
lem passing it if you have a good working knowledge
of PHP. We tried, however, to create a series of ques-
tions that fairly test the candidate’s knowledge of PHP,
with a very strong focus on the practical aspects of the
language. If you’re going to take the test, expect lots
of questions based on your ability to analyze and
understand portions of code, rather than simple theo-
retical questions, because that’s what we thought a
professional developer is most likely to encounter as
part of his day-to-day life. You will also find that none
of the questions rely on your ability to understand
English, rather than your knowledge of PHP—we felt
that this was an important distinction to make, given
how popular PHP is worldwide.
At php|a, we believe firmly in the importance of the
certification program, and have moved to place our
full support behind it. It all starts with our Certification
Central website (
wwwwww..pphhppaarrcchh..ccoomm//cceerrtt
), where we
have collected a number of excellent resources with
the goal of providing you with a one-stop shop for
everything that you need to get certified, from online
training, which starts in August, to the official guide,
which we developed in collaboration with Zend,
SAMS Publishing and some of the best PHP authors
out there, practice exams and even a discount on the
exam itself.
It’s the training, in particular, that I want to discuss
in a bit more detail here. IT training is, traditionally,
quite expensive—it’s not unusual for a three or four-
day course to cost upwards of $2,000, and that does-
n’t include the fact that you have to leave your office,
which causes lost work and transportation expenses.
EDITORIAL
Continued on page 9...
July 2004

PHP Architect

www.phparch.com
6
NNEEWW SSTTUUFFFF
What’s New!
NN EE WW SS TT UU FF FF
PHP 5 Final Released
The PHP team is proud to announce the final release of PHP 5!
Some of the key features of PHP 5 include:
• The Zend Engine II with a new object model and dozens of new features.
• XML support has been completely redone in PHP 5, all extensions are now
focused around the excellent libxml2 library
(
hhttttpp::////wwwwww..xxmmllssoofftt..oorrgg//
).
• A new SimpleXML extension for easily accessing and manipulating XML as PHP objects. It can also
interface with the DOM extension and vice-versa.
• A brand new built-in SOAP extension for interoperability with Web Services.
• A new MySQL extension named MySQLi for developers using MySQL 4.1 and later. This new exten-
sion includes an object-oriented interface in addition to a traditional interface; as well as support for
many of MySQL's new features, such as prepared statements.
• SQLite has been bundled with PHP. For more information on SQLite, please visit
their website.
• Streams have been greatly improved, including the ability to access low-level
socket operations on streams.
• And lots more...
For more information, visit
hhttttpp::////wwwwww..pphhpp..nneett
.
version
5
PHPx 3.5.1 Released
PHPx.org announces the release of PHPx 3.5.1.
What is it? According to PHPx.org
”PHPX is a constantly evolving and changing Content Management System (CMS). PHPX is highly customizable
and high powered all in one system. PHPX provides content management combined with the power of a portal by
including in the core package modules such as FAQ, polls, and forums. PHPX uses dynamic-template-design, what
this means is that you have the power to control what your site will look like. Themes are included, but not required.
You can create the page however you want, and PHPX will just insert code where you want it. No more 3 columns
if you don’t want it! Written in the powerful server language, PHP, and utilizing the amazingly fast and secure data-
base MySQL, PHPX is a great solution for all size website communities, at the best price possible…free! “
Get more information from
PPHHPPxx..oorrgg
.
July 2004

PHP Architect

www.phparch.com
7
NNEEWW SSTTUUFFFF
Looking for a new PHP Extension? Check out some of the lastest offerings from PECL.
Xdebug 1.3.2
The Xdebug extension helps you debugging your script by providing a lot of valuable debug information. The
debug information that Xdebug can provide includes the following:
• stack and function traces in error messages with:
• full parameter display for user defined functions
• function name, file name and line indications
• support for member functions
• memory allocation
• protection for infinite recursions
zeroconf 0.1.1
Provides an interface for browsing and publishing network services via ZeroConf using the Howl library. You can
browse the network for specific services like database servers (PostgreSQL, Sybase, InterBase), Apple File Sharing,
web services via Apache's mod_rendezvous, etc. and discover the IP address and port for each found service.
imagick 0.9.11
imagick is a native php-extension that incorporates the functionality of the popular ImageMagick graphics-
manipulation package.
See the examples in the examples/ directory for some hints on how to use it.
A compiled windows extension is available at
hhttttpp::////kkrroommaannnn..iinnffoo//ppeeaarr--ppeeccll..pphhpp
.
You need the ImageMagick libraries from
wwwwww..iimmaaggeemmaaggiicckk..oorrgg
or GraphicsMagick libraries from
hhttttpp::////wwwwww..ggrraapphhiiccssmmaaggiicckk..oorrgg//
to get it running.
ImageMagick 6 support is EXPERIMENTAL. Please report successes or failures.
APC 2.0.4
APC is the Alternative PHP Cache. It was conceived of to provide a free, open, and robust framework for
caching and optimizing PHP intermediate code.
PhpMan 2.0 a
Need help publishing your manuals or other text documents? PHPMan is here to help. According to the
PHPMan home page PHPMan is a: ”PHP/MySQL based system to publish your manuals and documentations. This
is a very easy-to-handle solution to publish manuals, documentations and other texts. The main feature is its use
of nested sets to store the sections/chapters of the text in the database. Each chapter can be commented upon by
users, similar to the php.net manual. Phpman is in i's very early stage of development, but the developers hope they
can improve the code and the features when it's public. Phpman has also a nice search engine. One page can show
all notes in a manual, another can display a printable version of the manual.“
Get More information from the PHPMan homepage (
wwwwww..mmyyllaannssiittee..oorrgg//iinnddeexx22..pphhpp??ssiittee==pphhppmmaann
).
phpMyFAQ 1.4.0
RC 3
phpMyFAQ.de announces the release of the third release candidate of version 1.4.0.
This version includes an improved IDN domain support. The password reset function now generates mnemon-
ic passwords and there are many bug fixes. Do not use this version in production systems, but test this version
and report bugs!
Download from
pphhppMMyyFFAAQQ..ddee
.
July 2004

PHP Architect

www.phparch.com
8
NNEEWW SSTTUUFFFF
Check out some of the hottest new releases from PEAR.
Net_NNTP 0.10.2
Package for communicating with NNTP/USENET servers. Includes features like post, view, list, authentication,
overview, etc.
HTML_Progress 1.2.0RC3
This package provides a way to add a loading bar fully customizable in existing XHTML documents. Your
browser should be DHTML-compatible in order to use this component.
Features:
• Create horizontal and vertical bars, as well as circle, ellipse and polygons (square, rectangle)
• Allows the usage of an existing external StyleSheet and/or JavaScript
• All elements (progress, cells, string) are customizable by their html properties
• Percent/string is floating all around the progress bar
• Compliant with all CSS/XHMTL standards
• Integration with all template engines is very easy
• Implements an Observer design pattern. It is possible to add Listeners.
• Adds a customizable UI monitor pattern to display a progress bar. The user can abort progress at
any time.
• Look and feel can be set by internal API or external config file.
• Allows many progress bars to coexist on same page without the use of iframes.
Auth 1.3.0r2
The PEAR::Auth package provides methods for creating an authentication
system using PHP.
Currently, it supports the following storage containers to read/write
the login data:
• All databases supported by the PEAR database layer
• All databases supported by the MDB database layer
• All databases supported by the MDB2 database layer
• Plaintext files
• LDAP servers
• POP3 servers
• IMAP servers
• vpopmail accounts
• RADIUS
• SAMBA password files
• SOAP
Net_Server 0.11.3
Generic server class based on ext/sockets, used to develop any kind of server.
File::Bittorrent 0.1.2 Alpha
This package consists of two classes which handles the encoding and decoding of data in Bittorrent format.
You can also extract useful informations from .torrent files.
Text::Text_wiki 0.20.1 alpha
This package abstracts the parsing and rendering of Wiki markup in structured plain text
July 2004

PHP Architect

www.phparch.com
9
In a recent survey on our website, almost 70% of the 3,000 people who answered expressed interest in some
form of online training from php|a.
We listened, and are planning a complete set of PHP courses that give you the best of a classroom and the
Internet by providing you with a live instructor, with whom you can interact in real-time (either by speaking
with him and the rest of the class or through text messaging), in a completely-online setting that doesn’t require
you to leave your office. Thus, no matter where you are you can take advantage of a great learning experience
as if you were in a real classroom, without the hassle of having to wait for a course to come to your town, and—
most importantly—at a very convenient price.
Our first training course is dedicated exclusively to the certification exam. It covers all the topics that are part
of the exam itself in a total of over 18 hours of training spread over three weeks, and will be taught by Ilia
Alshanetsky, who is a regular collaborator to php|a and a well-known PHP expert. It’s all available for a very con-
venient price (particularly if you sign up before July 31st), and we’re even throwing in a special offer that
includes a copy of the certification guide, an exam voucher and a full-copy of the Zend Studio IDE.
As we prepare our fall lineup of training courses, it’s important that you let us know what you would like to
learn about. Drop me a note at
mmaarrccoott@@ttaabbiinnii..ccaa
with all your ideas, and I promise that we’ll do our best to
design our training courses to fit your needs.
Until next month, happy readings!
Editorial:
Contiuned from page 5
php|a
EDITORIAL
D
ata Mining is the art of transforming raw data
into actionable knowledge. PHP has some
unique technical capabilities that make it well-
suited for use in Data Mining applications:
• Data connectivity (database, file, LDAP...)
• Graphing (GD, JpGraph)
• Web application (ease of use, central deploy-
ment, ubiquitous clients)
The goal of this article is to explore these capabilities of
PHP, and to show you concrete examples of how to
mine a dataset for useful information.
Data Mining
Data Mining takes raw data (i.e. Log files, data from
databases, etc.) and presents this data in a more useful
format. Data Mining can be a hard process, mainly
because you have to be very creative in integrating
business process knowledge, the visual display of data,
and the technical capabilities of your chosen delivery
method (PHP, HTML, SQL, GD, JavaScript...). This arti-
cle can help you with the second and third point, but it
is up to you to either acquire the business process
knowledge, or to find a process expert whom you can
work closely with to identify what data is meaningful,
and the best means of displaying the data.
Dr. Edward Tufte is the world-renowned expert on
the presentation of visual data. I highly recommend
reading his book, “The Visual Display of Quantitative
Data”. Tufte has definite opinions on what graphs
should look like, and provides recommendations for
how to alter your charts to best accurately and clearly
represent your data. Reading reviews on Amazon, it
looks like people’s opinion of Tufte’s work is very polar-
ized, so you may want to look over a copy at your local
bookstore to see if it is useful for you.
I also recommend “Information Graphics,” by
Robbert L. Harris. This book is less prescriptive of what
you should be doing, and instead presents a broad cat-
alog of ideas you might choose to use for presenting
data graphically. I find this book useful to review when
I am struggling to find the best way to present the data
I have access to, and need some inspiration for what
kinds of graphs are possible.
Coding
In writing this article, I have not used any particular
framework, but I have used layering within the applica-
tion by using a template engine (Smarty in this case)
where any HTML is generated. I have used ADOdb
(
hhttttpp::////aaddooddbb..ssff..nneett//
) as a database abstraction layer,
for reasons of ease of use, performance and syntax
portability (between Oracle, which I use at work, and
Postgres/MySQL at home). In addition, you should
bone up on your SQL aggregate functions, and try to
July 2004

PHP Architect

www.phparch.com
10
FF EE AA TT UU RR EE
Charted Data Mining with PHP and JpGraph
by Jason E. Sweat
PHP: 4.3.x
OS:
Any
Other software: JpGraph
(1.13p used in the article)
Code Directory: jpgraph
REQUIREMENTS
Data Mining transforms raw data into useful knowledge.
This article shows you how to use PHP, and the JpGraph
library, to generate several kinds of graphs based on the
PHP bug database as the raw data set.
offload as much of the “heavy lifting” of data manipu-
lation to the database server rather than using array
manipulation in PHP.
JpGraph
JpGraph (
hhttttpp::////wwwwww..aaddiittuuss..nnuu//jjppggrraapphh//
) is an object-ori-
ented library for producing graphs (charts, plots) from
data. JpGraph’s architecture is based on a modular
design (meaning you include only the functionality you
need for a given graph). One issue to consider when
selecting JpGraph as a graphing library is its dual licens-
ing model. If you are working on an open-source/non-
commercial application, the QT license applies; howev-
er, if you are working on a commercial application, you
should purchase the commercial license (currently €85
per server), which provides several additional Plot
types, as well as commercial-level support.
For each graph, you will instantiate a graph object,
and one or more plot objects. The graph object repre-
sents the canvas on which you are drawing and plot
objects are the line or bar graphs you add to show your
data. By exercising methods and properties of these
objects, you can create the graphs with the data and
appearance you choose. Finally, you can use the
GGrraapphh::::SSttrrookkee(())
method to output the graph.
There is a four step process for developing graphs
that I find myself going through each time I create a
page using JpGraph:
• retrieve and format the data you want to
graph
• create and modify the properties of the
Graph object
• create, modify the properties, and add each
of the Plot objects you want on the graph
• finalize the Graph object and output the
graph
Each of these steps will be reviewed in a bit more
detail when we get to the examples that follow.
A Case Study: The PHP Bug Database
PHP is the primary topic of this magazine. Even though
PHP is very flexible and powerful, it is not without prob-
lems. Many people report these problems every day
through the PHP bug web site (
hhttttpp::////bbuuggss..pphhpp..nneett//
),
and you see summary information regarding
the bugs on the statistics page at
hhttttpp::////bbuuggss..pphhpp..nneett//bbuuggssttaattss..pphhpp
. This study is focused
on what can be done to examine this same bug data
graphically—which is a great way to show you what
data mining can do.
Data
This study is based on a dump of the
bbuuggddbb
database for
PHP as of December 11th, 2002. Many thanks to the
PHP team for providing the data for these examples.
Please keep in mind that I was not involved with the
data modeling or the PHP scripts that access the data
from the
bbuuggddbb
tables. In some cases, investigation of
the PHP source for the
hhttttpp::////bbuuggss..pphhpp..nneett//
site provid-
ed me with a context for the data. In other cases, I
made some educated guesses regarding the tables con-
tent and their relations with each other.
The main table used in these examples can be creat-
ed using the SQL shown below; the
bbuuggddbb
table con-
tained 20,650 rows from the export:
CREATE TABLE bugdb (
id int(8) NOT NULL auto_increment,
bug_type varchar(32) NOT NULL default ‘’,
email varchar(40) NOT NULL default ‘’,
sdesc varchar(80) NOT NULL default ‘’,
ldesc text NOT NULL,
php_version varchar(100) default NULL,
php_os varchar(32) default NULL,
status varchar(16) default NULL,
ts1 datetime default NULL,
ts2 datetime default NULL,
dev_id varchar(16) default NULL,
assign varchar(16) default NULL,
bugpack_id int(5) default NULL,
PRIMARY KEY (id),
KEY php_version (php_version(1)),
FULLTEXT KEY email (email,sdesc,ldesc)
) TYPE=MyISAM PACK_KEYS=1;
The other table used in these examples,
bbuuggddbb__vvootteess
,
contained 7,312 rows when I imported it, and can be
created using this SQL command:
CREATE TABLE bugdb_votes (
bug int(8) NOT NULL default ‘0’,
ts timestamp(14) NOT NULL,
ip int(10) default NULL,
score int(3) NOT NULL default ‘0’,
reproduced int(1) NOT NULL default ‘0’,
sameos int(1) default NULL,
samever int(1) default NULL,
tried int(1) NOT NULL default ‘0’
) TYPE=MyISAM;
Graphs
This study presents the development of two graphs.
The first is a high level overview of PHP bugs being
opened and closed each week. The second is a tool for
graphically analyzing the votes on individual bugs to
determine relative importance.
The first question that comes to mind when consid-
ering the PHP bug database is “How are the developers
doing?” One way to gauge this would be to evaluate
the number of bugs being opened and compare it to
the number of bugs being closed. Because you can
group this data, you can consider the data from the
perspective of a weekly time series.
You could choose to graph this data as a grouped bar
graph (each week having two vertical bars, one right
next to the other), perhaps with red bars representing
opened bugs and green bars representing closed bugs
July 2004

PHP Architect

www.phparch.com
11
FFEEAATTUURREE
Charted Data Mining with PHP and JpGraph
(this choice of color comes from the stop light analo-
gy—green means good and red means bad—therefore
opening new bugs is bad but closing bugs is good).
This will accurately represent the data, but it is some-
times hard to make comparisons of which bar is higher
week to week when viewing relatively small bars.
Another option would be a line graph, but this does a
better job of highlighting the individual trends, rather
than focusing our attention on the differences—are
more bugs being opened or closed.
The field of econometrics has a graph that we can
take inspiration from called balance of trade. This graph
plots two lines, one for net imports and one for net
exports. The area between the two lines is then shaded
red when imports are greater than exports—represent-
ing a negative balance of trade—and green when
exports are greater than imports (representing a posi-
tive balance of trade). This is the effect to be created on
this graph, but using opened and closed PHP bugs
instead of trade data.
JpGraph supports area graphs, but they always fill to
the bottom of the chart. At first, it would seem you are
unable to implement the desired graph—but, of
course, you can get creative, use data manipulation to
create additional series, and come up with a solution. In
this case, you can retrieve bugs opened per week, and
bugs closed per week from the database, then create a
synthetic series that is the lower of those two values for
each week—the “minimum” series. You can then create
a filled area graph laying down a red filled graph for
open bugs, a green filled graph for closed bugs on top
of that, and finally a white filled graph with the mini-
mum series. If your background is also white, this
should generate a graph with the look you desire.
Following the graph development methodology, you
first need to retrieve the data. The bugs opened per
week can be determined using this SQL query:
SELECT YEARWEEK(`ts1`) AS dt, COUNT( 1 ) AS cnt
FROM `bugdb`
WHERE YEARWEEK(`ts1`) > ‘200201’
GROUP BY YEARWEEK(`ts1`)
ORDER BY YEARWEEK(`ts1`) ASC
The following should work for closed bugs:
SELECT YEARWEEK(`ts2`) AS dt, COUNT( 1 ) AS cnt
FROM `bugdb`
WHERE YEARWEEK(`ts2`) > ‘200201’
AND `status` IN (‘Closed’, ‘Bogus’)
GROUP BY YEARWEEK(`ts2`)
ORDER BY YEARWEEK(`ts2`) ASC
Instead of using the
GGeettAArrrraayy(())
method, we retrieve
this data into an associative array using
$$ooppeennDDbbDDaattaa ==&&
$$rrss-->>GGeettAAssssoocc(());;
and
$$cclloosseeDDbbDDaattaa ==&& $$rrss--
>>GGeettAAssssoocc(());;
. Then, we use the
ffoorreeaacchh
loop from
Listing 1 to construct the additional plot series.
This completes the data preparation step. The next
step involves the creation of the graph object. In most
July 2004

PHP Architect

www.phparch.com
12
FFEEAATTUURREE
Charted Data Mining with PHP and JpGraph
1 <?
2
3 $i = 0;
4 foreach($openDbData as $key => $value) {
5 $xData[$i] = $key;
6 $openData[$i] = $value;
7 $closeData[$i] = $closeDbData[$key];
8 $labelX[$i] = strftime(‘%m/%d’,
9 mktime(0, 0, 0, 1,
10 1+(int)substr($key, 4, 2)*7,
11 substr($key, 0, 4)));
12 $minData[$i] = min($openData[$i] , $closeData[$i]);
13 $i++;
14 }
15
16 ?>
Listing 1
Figure 1
cases, you are determining the size of the graph,
caching options, and appearance issues, like back-
ground colors, frames and shadows, and fonts. Most of
the time, this setup code is very straightforward, and
similar to the many examples available in the JpGraph
distribution. The only notable difference as far as our
graph’s set up is concerned is that we will be disabling
the y-axis grid (because it will not show through the
min data series) and only plot every other text label on
the x-axis.
$graph->SetScale(‘textlin’);
$graph->ygrid->show(false);
$graph->xaxis->SetTextLabelInterval(2);
You can now create your plots, add them to the
graph and display the result (which you can see in
Figure 1):
$l1 = new LinePlot($openData);
$l1->SetFillColor(‘red’);
$l1->SetLegend(‘more bugs opened’);
$l2 = new LinePlot($closeData);
$l2->SetFillColor(‘green’);
$l2->SetLegend(‘more bugs closed’);
$l3 = new LinePlot($minData);
$l3->SetFillColor(‘white’);
Note that the highlighted area was created using an
image editor to show you a bit more detail, not
JpGraph and PHP.
Success? Not quite—the graph gives the general
appearance desired, but there are some jagged edges
caused by the alignment of the points. Perhaps, using
the step style line graph will correct the problem. This
can be added using three lines of code:
$l1->SetStepStyle();
$l2->SetStepStyle();
$l3->SetStepStyle();
This graph, shown in Figure 2, does a good job of
achieving the desired effect. There is one more possible
look to consider, with the goal of smoothing the lines.
This can be achieved using the
SSpplliinnee
class
(
jjppggrraapphh__rreeggssttaatt..pphhpp
).
You first need to do some additional work to create
the series to plot:
define(‘NUM_POINTS’, 200);
$spline = new Spline($xData, $openData);
list($openXSmooth, $openYSmooth) = $spline-
>Get(NUM_POINTS);
$spline = new Spline($xData, $closeData);
list($closeXSmooth, $closeYSmooth) = $spline-
>Get(NUM_POINTS);
$minData = array();
for ($i=0, $j=count($openYSmooth); $i<$j; $i++) {
$minData[$i] = min(array($openYSmooth[$i],
$closeYSmooth[$i]));
}
Note: since I started working on
this article, the alpha-blending
capabilities of JpGraph/GD2 have
improved, so that now you could
create an “invisible” bar by using an
AAccccBBaarrPPlloott
, and setting the outline
and fill color to
wwhhiittee@@11
for a “Base”
data series, and allow the horizontal
grid lines to display.
July 2004

PHP Architect

www.phparch.com
13
FFEEAATTUURREE
Charted Data Mining with PHP and JpGraph
Figure 2
Next, you can modify the graph creation…
$graph->SetScale(‘linlin’, 1, 1, min($xData),
max($xData));
…and change the plots created:
$l1 = new LinePlot($openYSmooth, $openXSmooth);
$l1->SetFillColor(‘red’);
$l1->SetLegend(‘more bugs opened’);
$l2 = new LinePlot($closeYSmooth, $closeXSmooth);
$l2->SetFillColor(‘green’);
$l2->SetLegend(‘more bugs closed’);
$l3 = new LinePlot($minData, $openXSmooth);
$l3->SetFillColor(‘white’);
One additional point of interest: the PHP developers
looked very busy in early July. A quick check of past
news on the php.net site revealed a possible reason:
PHP version 4.2.2 was released on July 22nd. You can
alert your viewers to the possible cause of this outlier
data using a text box:
$t1 = new Text(“PHP 4.2.2\nReleased 7/22/02”);
$t1->Pos(0.55, 0.35);
$t1->SetOrientation(‘h’);
$t1->SetFont(FF_ARIAL, FS_BOLD);
$t1->SetBox(‘yellow’, ‘red’, ‘gray’);
$t1->SetColor(‘black’);
You can see the resulting graph in Figure 3.
On to the Second Example
The goal of the second graphing example is to create a
graphical comparison of the voting data on bugs.
Voting on several different criteria can be thought of as
July 2004

PHP Architect

www.phparch.com
14
FFEEAATTUURREE
Charted Data Mining with PHP and JpGraph
1 <?
2
3 $bug = check_passed_bug(‘bug’);
4 if (!$bug) {
5 graph_error(‘bug parameter incorrect’);
6 }
7
8 function check_passed_bug( $parm ) {
9 global $conn;
10
11 //not indented because the end of the heredoc must be the
first char
12 $sql = <<<EOS
13 SELECT COUNT( 1 ) AS cnt,
14 `id`
15 FROM `bugdb`
16 WHERE `id` = ?
17 GROUP BY `id`
18 EOS;
19
20 if (array_key_exists($parm, $_GET)) {
21 $bug_id = (int)$_GET[$parm];
22 $rs = $conn->Execute($sql, array($bug_id));
23 if ($rs && !$rs->EOF) {
24 $row = $rs->fetchRow();
25 if ($row[‘cnt’] == ‘1’) {
26 return $row[‘id’]; //if found return the id
27 }
28 }
29 }
30 return false; //otherwise return false
31 }
32
33 function graph_error($msg) {
34 $graph = new CanvasGraph(WIDTH, HEIGHT);
35
36 $t1 = new Text($msg);
37 $t1->Pos(0.05, 0.5);
38 $t1->SetOrientation(‘h’);
39 $t1->SetFont(FF_ARIAL, FS_BOLD);
40 $t1->SetColor(‘red’);
41 $graph->AddText($t1);
42
43 $graph->Stroke();
44 exit;
45 }
46
47 ?>
Listing 2
Figure 3
an evaluation or test—and the perfect type of graph for
this particular need is called the radar.
Our script will work by accepting a bug parameter to
identify the bug on which to generate the graph.
Because you must always validate any data
the users sends to you, in Listing 2 I have coded a func-
tion to validate the bug ID passed. Also, because the
result of this script is a graph, the only way to return an
error message to the user is by embedding it in the
image itself (throwing a text-based error will result in
the user seeing an image not found “X” instead of the
error message). The
ggrraapphh__eerrrroorr(())
function is an easy
means of doing this.
When this script is later incorporated into a naviga-
tion system, you will want to identify the relative order
of the bug. To do this, pass a
bbnn
(bug number) param-
eter to it. You can have much looser checking on this
parameter and just assume 0 when not passed:
$bugColors = array(‘lightyellow’, ‘lightblue’,
‘lightgreen’);
if (array_key_exists(‘bn’, $_GET)) {
$bn = abs((int)$_GET[‘bn’]);
if ($bn>count($bugColors)) {
$bn=0;
}
} else {
$bn = 0;
}
Use the SQL code from Listing 3 to populate
$$bbuuggDDaattaa
using the
ffeettcchhRRooww(())
method (since there will be only a
single row returned).
The construction of
$$ggrraapphhDDaattaa
, shown in Listing 4, is
slightly different for this graph. Each of the axes is a
data point in the series. You also want to scale all the
data to a uniform value (JpGraph does not support dif-
ferent scales on each radar plot axis). You want the
larger values on the graph to represent greater impor-
tance of the bug. In this case, if you report a bug using
the same OS, this is “less important” than reporting the
bug on a different OS, since multi-platform bugs can
reasonably indicate that more people are affected by
the same problem. Therefore, the scaling for OS and
Version is reversed.
With data in hand, you can now move to generating
the graph. The graph setup is shown in Listing 5.
Next comes adding the radar plot:
$r1 = new RadarPlot($graphData);
$r1->SetLegend(“Bug $bug”);
$r1->SetColor(‘red’, $bugColors[$bn]);
$r1->SetLineWeight(2);
July 2004

PHP Architect

www.phparch.com
15
FFEEAATTUURREE
Charted Data Mining with PHP and JpGraph
1 SELECT COUNT( 1 ) AS votes,
2 b.`id`,
3 b.`bug_type`,
4 b.`status`,
5 b.`sdesc`,
6 AVG( v.`score` ) AS avg_score,
7 AVG( v.`reproduced` ) AS avg_repro,
8 AVG( v.`sameos` ) AS avg_os,
9 AVG( v.`samever` ) AS avg_samever,
10 AVG( v.`tried` ) AS avg_tried
11 FROM `bugdb_votes` v, `bugdb` b
12 WHERE v.`bug` = b.`id`
13 AND b.`id` = ?
14 GROUP BY b.`id`,
15 b.`bug_type`,
16 b.`status`,
17 b.`sdesc`
Listing 3
1 <?
2
3 $axesTitles = array( ‘Votes’, ‘Score’, ‘Repr.’, ‘Same OS’,
‘Ver.’, ‘Tried’);
4
5 $graph =& new RadarGraph(WIDTH, HEIGHT);
6 $graph->SetScale(‘lin’, 0, GRAPH_MAX);
7 $graph->SetTitles($axesTitles);
8 $graph->yscale->ticks->Set(5, 5);
9 $graph->axis->SetColor(‘silver’);
10 $graph->axis->SetLabelFormatCallback(‘no_format’);
11 $graph->SetFrame(false);
12 $graph->ShowMinorTickMarks(2);
13 $graph->SetCenter(0.5, 0.4);
14 $graph->SetSize(.60);
15 $graph->grid->Show();
16 $graph->grid->SetColor(‘silver’);
17 $graph->grid->SetLineStyle(‘dotted’);
18
19 $r1 =& new RadarPlot($graphData);
20 $r1->SetLegend(“Bug $bug”);
21 $r1->SetColor(‘red’, $bugColors[$bn]);
22 $r1->SetLineWeight(2);
23
24 $graph->Add($r1);
25 $graph->legend->SetLayout(LEGEND_HOR);
26 $graph->legend->Pos(0.5, 0.95, “center”, “bottom”);
27 $graph->legend->SetLineWeight(0);
28 $graph->legend->SetShadow(false);
29 $graph->legend->SetFillColor(‘white’);
30 $graph->Stroke();
31
32 ?>
Listing 5
1 <?
2
3 define(‘WIDTH’, 200);
4 define(‘HEIGHT’, 200);
5 define(‘GRAPH_MAX’, 10);
6 define(‘VOTE_SCALE’, 20);
7 define(‘SCORE_SCALE’, 0.2);
8 define(‘REPRO_SCALE’, 0.1);
9 define(‘OS_SCALE’, 0.1);
10 define(‘VER_SCALE’, 0.1);
11 define(‘TRIED_SCALE’, 0.1);
12
13 $graphData = array();
14 $graphData[] = $bugData[‘votes’] / VOTE_SCALE;
15 $graphData[] = $bugData[‘avg_score’] / SCORE_SCALE;
16 $graphData[] = $bugData[‘avg_repro’] / REPRO_SCALE;
17 $graphData[] = GRAPH_MAX - $bugData[‘avg_os’] / OS_SCALE;
18 $graphData[] = GRAPH_MAX - $bugData[‘avg_samever’] / VER_SCALE;
19 $graphData[] = $bugData[‘avg_tried’] / TRIED_SCALE;
20 for ($i=0,$j=count($graphData); $i<$j; $i++) {
21 if ($graphData[$i] > GRAPH_MAX) {
22 $graphData[$i] = GRAPH_MAX;
23 } elseif ($graphData[$i] <0) {
24 $graphData[$i] = 0;
25 }
26 }
27
28 $axesTitles = array( ‘Votes’, ‘Score’, ‘Repr.’, ‘Same OS’,
‘Ver.’, ‘Tried’);
29
30 ?>
Listing 4
July 2004

PHP Architect

www.phparch.com
16
FFEEAATTUURREE
Lastly, you finalize the graph and output it:
$graph->Add($r1);
$graph->legend->SetLayout(LEGEND_HOR);
$graph->legend->Pos(0.5, 0.95, “center”, “bottom”);
$graph->legend->SetLineWeight(0);
$graph->legend->SetShadow(false);
$graph->legend->SetFillColor(‘white’);
$graph->Stroke();
Now that you have a working graph, shown in Figure
4, you can integrate it into a web page for navigation.
This code will look similar to the census data naviga-
tion. First, you need validate the bug parameter passed
Charted Data Mining with PHP and JpGraph
1 <?
2
3 define(‘TEMPLATE’, ‘bug_compare.tpl’);
4 define(‘GRAPH_FILE’, ‘bug_radar_graph.php?bug=’);
5
6 $t = new Smarty;
7 $t->autoload_filters = array(‘pre’=>array(‘showinfoheader’)
8 , ‘output’=>array(‘trimwhitespace’));
9 $t->assign(array(
10 ‘bug’ => $bug,
11 ‘b1’ => $b1,
12 ‘b2’ => $b2,
13 ‘bugData’ => $bugData,
14 ‘graphSrc’ => GRAPH_FILE,
15 ‘selfLink’ => phpself()
16 ));
17
18 $t->display(TEMPLATE);
19
20 ?>
Listing 7
Figure 5
1 SELECT COUNT( 1 ) AS votes,
2 b.`id`,
3 b.`bug_type`,
4 b.`status`,
5 b.`sdesc`,
6 AVG( v.`score` ) AS avg_score,
7 AVG( v.`reproduced` ) AS avg_repro,
8 AVG( v.`sameos` ) AS avg_os,
9 AVG( v.`samever` ) AS avg_samever,
10 AVG( v.`tried` ) AS avg_tried
11 FROM `bugdb_votes` v, `bugdb` b
12 WHERE v.`bug` = b.`id`
13 AND b.`status` <> “Closed”
14 AND b.`status` <> “Bogus”
15 GROUP BY b.`id`,
16 b.`bug_type`,
17 b.`status`,
18 b.`sdesc`
19 ORDER BY 1 DESC
20 LIMIT 0, 30
Listing 6
Figure 4
to the script. You will also need to validate the “trailing
bug” parameters of
bb11
and
bb22
. This can easily be
accomplished using the
cchheecckk__ppaasssseedd__bbuugg(())
function
from the prior example:
$bug = check_passed_bug(‘bug’);
$b1 = check_passed_bug(‘b1’);
$b2 = check_passed_bug(‘b2’);
You can load the data needed for the navigation page
into an array called
$$bbuuggDDaattaa
using the SQL in Listing 6.
The last additions to the PHP navigation script consist
of creating the Smarty object, assigning the data to it,
and use the
ddiissppllaayy(())
method on your navigation tem-
plate as shown in Listing 7.
The
bbuugg__ccoommppaarree..ttppll
file in the source code for this
article contains a smarty template to display up to three
bugs and a list of bugs to compare.
Figure 5 shows several bugs displayed in the context
of the bug comparison navigation page.
Summary
What I hope you learned from this article is some of the
capabilities PHP has for Data Mining. In covering the
four steps to generate a graph, we looked at querying
the database and generating arrays for JpGraph con-
sumption in a loop, sometimes creating synthetic data
series as well. We looked at several types of plot objects,
including area-filled line graphs and radar charts. We
worked with some of the more obscure functionality
offered by JpGraph, like the spline utilities for smooth-
ing data. Also, we reviewed adding text to the image
for clarification or error messages, and looked at how to
integrate PHP scripts that generate images into a web
page for displaying other text data and navigation. I
hope you now have a better feeling for how you can
use PHP to create an effective Data Mining tool.
July 2004

PHP Architect

www.phparch.com
17
FFEEAATTUURREE
Charted Data Mining with PHP and JpGraph
About the Author ?>
To Discuss this article:
http://forums.phparch.com/157
Jason works as an application developer and webmaster for a Fortune
100 company. He started using PHP as a replacement for IIS/ASP on a
home business project, and has since adopted PHP for intranet develop-
ment and scripting professionally. Jason’s PHP articles have been pub-
lished by Zend.com and php|architect. He recently started blogging at
hhttttpp::////bblloogg..ccaasseeyy--sswweeaatt..uuss//
and can be emailed at
jjsswweeaatt__pphhpp@@yyaahhoooo..ccoomm
.
FavorHosting.com offers reliable and cost effective web hosting...
SETUP FEES WAIVED AND FIRST 30 DAYS FREE!
So if you're worried about an unreliable hosting provider who won't be
around in another month, or available to answer your PHP specific
support questions. Contact us and we'll switch your information and
servers to one of our reliable hosting facilities and you'll enjoy no
installation fees plus your first month of service is free!*
Please visit http://www.favorhosting.com/phpa/
call 1-866-4FAVOR1 now for information.
- Strong support team
- Focused on developer needs
- Full Managed Backup Services Included
Our support team consists of knowledgable and experienced
professionals who understand the requirements of installing and
supporting PHP based applications.

R
ecently, while in discussions to build an e-com-
merce storefront for our corporate web site, the
question was asked if we might be able to tell
when a user abandons the order process. Recognizing
this as an interesting problem to solve, I sadistically said
it could be done before giving serious thought to how
I might go about actually doing it. What better way to
motivate one’s self?
In any case, I knew that to be able to effectively and
transparently accomplish this, I would need to use a
combination of DHTML and PHP. Normally, this can
easily be done using PHP by itself. One simple method
is to simply log the page name and session ID each
time the user accesses a page. In our case, however, we
wanted to be able to obtain data at a more granular
level—where on any given page has the user changed
a form field, for example.
Say we have a page listing five products, any of which
could be selected before submitting the page. We
wanted to be able to see if a user had selected the first
three items and then given up. More importantly, we
wanted to be able to see how many users get to the
very last page before completing the order and aban-
doning it. If before getting to this page they went back
and forth between two product pages changing their
selections, we can probably deduce that something has
apparently confused them. For example, there could be
something wrong with the way the product offering is
displayed, which is possibly what caused them to give
up completely. Marketing types just love this kind of
data—they get a strangely giddy look on their faces
when you give it to them. I suppose it’s like when we
geeks get new hardware or a trade show bag full of
schwag.
To make things simple for this go-around, we decid-
ed to store the data as a comma separated value (CSV)
file. It is easy to open in Microsoft Excel (perfect for the
marketing heads) and easy to manipulate with a
processor in PHP using the
ffggeettccssvv(())
function. Storing
it in a database is just as trivial and will definitely lend
itself to more flexible and powerful reporting and
analysis capabilities, but for now we decided to just
keep it simple.
Parts Is Parts
As you may have guessed by now, there are two parts
to such a system—the client, which is using DHTML,
and the server using PHP. First, let’s cover the DHTML
part, which allows us to interact with the PHP part in
real-time as the user advances through a page.
DHTML (or Dynamic HTML) is a great method of
integrating JavaScript with HTML to produce a more
dynamic and interactive browser-based user experi-
ence. When it was first introduced, DHTML was mostly
used for things like client-side form validation and,
unfortunately, pop-ups and pop-unders. Essentially, it
July 2004

PHP Architect

www.phparch.com
19
FF EE AA TT UU RR EE
User Abandonment Tracking with PHP and
DHTML
by Darrell Brogdon
PHP: 4.3
OS: Any
Other Software: Any DOM level 2 compliant
browser.
Code Directory: user
REQUIREMENTS
In this article, we will discuss the technique of using PHP
and JavaScript to capture real-time information entered
into a form for the purposes of user abandonment track-
ing. You will also gain a solid understanding of the pow-
erful way client-side DHTML can interact with server-side
PHP.
was developed as a simple alternative to Java applets
and Flash. DHTML was easier to implement than its
counterparts but not as powerful, at least at the time.
Luckily, that’s no longer the case. Browser makers
have continually beefed up their browser’s capabilities
so that DHTML is considerably more robust. One of the
early and more interesting uses of DHTML that I
remember seeing was a version of the old Atari game
Asteroids! These days, DHTML developers are really
pushing the envelope even further and building some
world-class web applications.
To return to the problem at hand, let’s take a look at
some simple DHTML by going back to our product list-
ing page that has five products. In the following exam-
ples we’ll use different colors of Duck-billed Platypuses.
Or is it Platypi? Regardless of whichever word is correct,
we’re going to have five different colors of them: red,
green, blue, yellow and orange. Let’s assume that the
product descriptions are listed on the page with a
checkbox next to each. When a user checks or un-
checks a checkbox, we want to store that an action was
executed on that particular product’s checkbox. Thus,
the checkbox’s HTML might look something like this:
<input type=“checkbox” name=“red_ball” id=“red”
value=“1”>
A Red Duck-billed Platypus.
To gain access to the properties of this checkbox, we
can use the following JavaScript code:
var platypus_checkbox =
document.getElementById(‘red’);
ggeettEElleemmeennttBByyIIdd(())
is a DOM method that will return
an object that references a particular element on the
page. In this case, the object represents the checkbox.
This object gives you access to the various methods and
properties of the checkbox, such as its name, value,
and whether or not it’s checked. For example let’s say
we want to make the newly created checkbox checked.
The command
ppllaattyyppuuss__cchheecckkbbooxx..cchheecckkeedd == ttrruuee;;
will
do just that. If we want to make it go away altogether,
we can use
ppllaattyyppuuss__cchheecckkbbooxx..ssttyyllee..ddiissppllaayy ==
‘‘nnoonnee’’;;
Pretty simple, huh? Now, let’s get a little more
advanced. Since we’re going to need to know when
the checkbox has been checked or unchecked, we’ll
need to fire an event when such an action takes place.
For now, let’s just display an alert message box as the
event. First, we need to define a function to perform
the alert when an action has been taken on a checkbox,
as seen in Listing 1.
The first thing this function does is to get an object
that references the checkbox. Then, we determine
whether it has been checked or unchecked. If the for-
mer is true then we send an alert notifying the user that
it has indeed been checked. Likewise, if it has been
unchecked, we alert the user accordingly.
In Listing 2, we attach that new function to the
checkbox’s “onclick” event, so that whenever the user
clicks the checkbox, the
aalleerrttIIffCChheecckkeedd(())
function will
be fired.
Most of this example is pretty self explanatory. The
key is how we are passing the color of the Platypus that
this particular checkbox represents. In JavaScript, as in
Java and PHP,
tthhiiss
refers to the current object—in our
case, the checkbox object (at least while we are firing
the event). By providing
tthhiiss..iidd
as the argument to
the
aalleerrttIIffCChheecckkeedd(())
function, we can easily get the
colour, since that’s what the value of the checkbox ID
July 2004

PHP Architect

www.phparch.com
20
FFEEAATTUURREE
User Abandonment Tracking with PHP and DHTML
“D
HTML was easier
to implement than its
counterparts but not
as powerful, at least
at the time”
<input type=”checkbox” name=”red_ball” id=”red” value=”1” onclick=”alertIfChecked(this.id)”>
Listing 2
<script type=”text/JavaScript”>
function alertIfChecked(color) {
var ball = document.getElementById(color);
if (true == ball.checked) {
alert(color + ‘ is checked!’);
} else {
alert(color + ‘ is not checked.’);
}
}
</script>
Listing 1
is. We could just as easily have passed this argument as
a string, but that is less portable. What if we wanted to
change this to a “Bright Red” Platypus? Simply change
the value of the checkbox’s ID and we’re done.
While this is a very simple example, it does give you
a good idea of some basic DHTML principles. If you
haven’t dealt much with JavaScript and DHTML, you
can see by this example that the syntax is very much
like that of PHP. One of a few exceptions is that you use
a dot (.) operator to access object methods and prop-
erties instead of PHP’s arrow (->) operator. Also, there is
no need for prefixing variables with a dollar symbol.
I should note that this code is cross-browser compat-
ible as long as you are using a DOM Level 2 compliant
browser. Most of the newer browsers, such as Internet
Explorer 6, Netscape Navigator 7, Mozilla 1, Mozilla
Firebird 0.9, and Apple’s Safari all seem to handle this
code just fine.
The PHP Parts
Now that we have some basic DHTML code to work
with, let’s take a look at how the PHP side of things
might work.
The data that the PHP page will use will be coming
from the client as an HTTP GET query string. Using our
Duck-billed Platypus example, we can assume that the
following URL is a good sample of what we can expect
going forward.
hhttttpp::////eexxaammppllee..ccoomm//hhaannddlleerr..pphhpp??ppaaggee==pprroodduucctt__lliisstt&&pprroo
dduucctt__iidd==rreedd__ppllaattyyppuuss&&aaccttiioonn==cchheecckkeedd
Based on that assumption, let’s build a PHP page to
handle it. The page will need to write to a log file and
store not only the query argument values, but the
date/time and session ID as well. You can see how this
is done in Listing 3.
Putting It All Together
Since we now have some PHP that can handle the log-
ging of the abandonment data, let’s return to the
DHTML side, so that we can send some data to our
script. Listing 4 shows the complete HTML code for our
Duck-billed Platypus example product listing page.
One of the key sections of this code is the use of the
HTML
iiffrraammee
tag. This is how we handle passing infor-
mation to the PHP abandonment handler page.
Because the size attributes of this tag are set to zero, we
don’t have to worry about the user seeing what is
going on during the update. At most, they will hear the
standard click sound that Internet Explorer makes when
a page is loaded.
Looking at the
ssaavveeAAbbaannddoonnmmeennttDDaattaa(())
JavaScript
function, you can see exactly how the data is built.
First, we get object references to the checkbox and
Iframe tags. Using that information, we can determine
what the action is that we want to save—in this case,
whether or not the user checked the checkbox. After
having defined all of that information, we concatenate
it together into a string and update the iframe’s
ssrrcc
attribute. Doing this works exactly as if you had called
that URL from within a browser.
If you ran the code in Listing 4 several times, you
should get log data similar to the log data shown in
Figure 1.
And Now For Something Almost
Completely Different
Using the method above to pass the information to an
July 2004

PHP Architect

www.phparch.com
21
FFEEAATTUURREE
User Abandonment Tracking with PHP and DHTML
1 <?php
2 session_start();
3
4 if (isset($_GET[‘action’])) {
5 $save = $_COOKIE[‘PHPSESSID’]
6 . ‘,’
7 . $_GET[‘page’]
8 . ‘,’
9 . $_GET[‘product_id’]
10 . ‘,’
11 . $_GET[‘action’]
12 . ‘,’
13 . ‘“‘ . date(‘r’) . “\”\n”;
14
15 if ($fp = fopen(‘/tmp/abandonment.log.txt’, ‘a’)) {
16 fputs($fp, $save);
17 fclose($fp);
18 }
19 }
20 ?>
Listing 3
“X
mlHttp is an ActiveX com-
ponent that allows you to
make an HTTP connection in
the background. “
8781cd9b7669ee3325aa216ca9a99e3c,http://example.com/product.php,red_ball,checked,”Mon, 21 Jun 2004 23:55:23 -0600”
8781cd9b7669ee3325aa216ca9a99e3c,http://example.com/product.php,orange_ball,checked,”Mon, 21 Jun 2004 23:55:25 -0600”
8781cd9b7669ee3325aa216ca9a99e3c,http://example.com/product.php,red_ball,unchecked,”Mon, 21 Jun 2004 23:55:26 -0600”
Figure 1
Iframe is all well and good, but there is a simpler and
somewhat more elegant solution. Microsoft has seen it
fit to provide web developers with a solution called
XmlHttp. XmlHttp is an ActiveX component that allows
you to make an HTTP connection in the background.
The Mozilla team being the bright hackers that they
are, they have provided an alternative to that ActiveX
solution which provides an API that is mostly compati-
ble with its Microsoft cousin.
Note that I said “mostly” compatible—the two tech-
nologies do differ somewhat both in their initialization
and the naming of certain methods and properties. You
can see a list of what few caveats there are at
hhttttpp::////wwwwww..mmoozziillllaa..oorrgg//xxmmlleexxttrraass//##DDooccuummeennttaattiioonn
.
There is also a great article at
hhttttpp::////wweebbffxx..eeaaee..nneett//ddhhttmmll//xxmmlleexxttrraass//xxmmlleexxttrraass..hhttmmll
out-
lining a JavaScript abstraction function to make life eas-
ier where the issue of initialization is concerned. I
encourage you to review that site as I will be using that
method for the rest of this article.
For a complete reference on XmlHttp you can go to
hhttttpp::////mmssddnn..mmiiccrroossoofftt..ccoomm//lliibbrraarryy//ddeeffaauulltt..aasspp??uurrll==//lliibbrraa
rryy//eenn--uuss//xxmmllssddkk//hhttmmll//ddoomm__rreeffeerreennccee..aasspp
. According to the
documentation at
hhttttpp::////wwwwww..mmoozziillllaa..oorrgg
, this reference
is complete enough to apply to their non-ActiveX ver-
sion as well.
Making Something Useful
“Less talking and more coding,” I can hear you saying!
So then, let’s take a look at how to use this new
method. Listing 5 has an updated example of the orig-
inal JavaScript function.
You can see that this function is only slightly different
from the original, but it is quite a bit simpler. Since the
XmlHttp object is handling everything in the back-
ground, there is no longer any need for an iframe.
There is one slight difference you may notice in the
above code from what is described in the WebFX arti-
cle. Their way of creating the XmlHttp object doesn’t
quite work in the versions of the browsers I’m using.
They suggest creating the object statically using var
ooXXmmllHHttttpp == XXmmllHHttttpp..ccrreeaattee(())
. I found that you have to
create XmlHttp separately from the call to the
ccrreeaattee(())
method:
var oXmlHttp = new XmlHttp();
var oHttp = oXmlHttp.create();
This newly created XmlHttp object can be used to
send and retrieve data back and forth between the
client and the server. Since this connection is essential-
ly transparent to the user, you don’t have the minor
annoyances of the cursor changing and a clicking noise
going off in Internet Explorer. Who at Microsoft decid-
July 2004

PHP Architect

www.phparch.com
22
FFEEAATTUURREE
User Abandonment Tracking with PHP and DHTML
1 <html>
2 <head>
3 <title>Duck-billed Platypus Product List</title>
4 <script type=”text/javascript”>
5 function saveAbandonmentData(item_id) {
6 var oItem = document.getElementById(item_id);
7 var oHdlr = document.getElementById(‘handler’);
8 var sActn = (oItem.checked ? ‘checked’ : ‘unchecked’);
9 var sUrl = oHdlr.src
10 + ‘page=products1.php’
11 + ‘&product_id=’ + oItem.name
12 + ‘&action=’ + sActn;
13
14 oHdlr.src = sUrl;
15 }
16 </script>
17 </head>
18 <body>
19
20 <form method=”post”>
21 <?php
22 $colors = array(‘red’, ‘green’, ‘blue’, ‘orange’, ‘yellow’);
23
24 foreach ($colors as $color) {
25 $uc_color = ucfirst($color);
26 ?>
27 <p>
28 <input type=”checkbox” name=”<?=$color?>_platypus” id=”<?=$color?>” value=”1” onclick=”saveAbandonmentData(this.id);”>
29 A <?=$uc_color?> Platypus.
30 </p>
31 <?php
32 }
33 ?>
34 <p>
35 <input type=”submit” value=”Add To Cart”>
36 </p>
37 </form>
38
39 <iframe src=”abandonment_handler.php” id=”handler” width=”0” height=”0” frameborder=”0”></iframe>
40
41 </body>
42 </html>
Listing 4
ed this was a good thing anyway?
In this example, we’re just blindly sending the data to
the server without ensuring that the transmission oper-
ation succeeded. Naturally, it is good practice to obtain
acknowledgment from the server that it received the
data and was able to process it properly. That way, if for
some reason something went wrong, you can attempt
a resend or otherwise handle the failure more graceful-
ly. For this example, however, we will forgo that in the
interest of keeping things nice and simple.
The workhorse of this new page is the
ooppeenn(())
method. Its first argument is the HTTP method to use.
As with our first attempt, we’re going to use the GET
method, but you could substitute this with POST if the
application calls for that.
The second argument is the URL of the server to
which we want to make a connection. This must be a
fully formed URL. Even though we can’t see it in the
Address bar in the browser, there is really no difference
as far as the server is concerned.
The third argument is whether this connection
should be synchronous or asynchronous. The Boolean
value of
ffaallssee
for the former and
ttrruuee
for the latter.
Why does this matter? Well, a synchronous connection
means XmlHttp will wait for the server to close the con-
nection before it will allow you to proceed. Since we
are trying to make this as transparent as possible, a syn-
chronous connection probably isn’t what we want, so
we give it a value of
ffaallssee
to make it asynchronous. I
know, these last few sentences were starting to devolve
into a programmer’s version of “How much wood
would a wood chuck chuck...” but I hope you get the
drift!
That’s All Folks!
That’s pretty much it in a nutshell. There are definitely
many paths you can take with this from here. One
thought that comes to mind is the ability to track exact-
ly where the user moves the mouse on the page. Such
an application might be very useful if you are develop-
ing a usability study for web applications in order to see
how users are interacting with the pages. Another idea
is a web-based chat system. I’ve seen several of these
before but most if not all tend to perform automatic
page refreshes. Using XmlHttp there would be no need
for such a refresh.
I hope that, after having read this article, you will
have a better understanding of how much more robust
you can make your web applications by integrating the
use of DHTML. If you want an excellent desk reference
on DHTML, I highly recommend O’Reilly’s “Dynamic
HTML: The Definitive Reference, 2nd Edition”. It’s an
invaluable resource for any developer building dynam-
ic web applications.
July 2004

PHP Architect

www.phparch.com
23
FFEEAATTUURREE
User Abandonment Tracking with PHP and DHTML
<script type=”text/javascript”>
function saveAbandonmentData(prd_id) {
var oXmlHttp = new XmlHttp();
var oHttp = oXmlHttp.create();
var oProduct = document.getElementById(prd_id);
var sAction = (oProduct.checked ? ‘checked’ : ‘unchecked’);
var sUrl = “http://example.com/processor.php?”
+ ‘page=’ + window.location
+ ‘&product_id=’ + oProduct.name
+ ‘&action=’ + sAction;
oHttp.open(‘GET’, sUrl, false);
oHttp.send(null);
}
</script>
Listing 5
About the Author ?>
To Discuss this article:
http://forums.phparch.com/158
Darrell Brogdon is a Software Engineer for MX Logic, Inc. were he helps
rid the world of Spam and Viruses. He has been writing PHP web appli-
cations for over seven years now and thus has to consciously refrain from
beginning every thing he types with “
<<??pphhpp
”.
dynamic web pages - german php.node
news . scripts . tutorials . downloads . books . installation hints
Dynamic Web Pages
www.dynamicwebpages.de
sex could not be better
|
Can’t stop thinking about PHP?
Write for us!
Visit us at
http://www.phparch.com/writeforus.php

Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay

×