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

Tài liệu It’s All in a Day’s Work ppt

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.8 MB, 66 trang )

Crunching
Data
PHP
with
CrunchingCr unching
DataData
PHPPHP
withwith
From TAR to RAR in a ZIP From TAR to RAR in a ZIP
INTERVIEW
PRIMING PHP FOR THE ENTERPRISE
PRIMING PHP FOR THE ENTERPRISE
Idealabs Preps LAMP Up for the Big Time
TURNING A CLASS INTO TURNING A CLASS INTO
AN APPLICATION WITHAN APPLICATION WITH
PHP-GTKPHP-GTK
Automate your tasks with a GUI app
STRENGTHENING THE STRENGTHENING THE
AUTHENTICATION PROCESSAUTHENTICATION PROCESS
Make that login more secure without
HTTPS
An XML APPROACH TO TEMPLATING An XML APPROACH TO TEMPLATING
USING PHPTALUSING PHPTAL
Making the peace between designers and developers
MARCH 2005
VOLUME IV - ISSUE 3
MARCH 2005
VOLUME IV - ISSUE 3
www.phparch.com
Get Ready For:
Plus: Security Corner, Product Review, and much more



LEARNING PHP WAS NEVER THIS MUCH FUN
Come learn PHP in Paradise with us
(and spend less than many other conferences)
Ilia Alshanetsky - Accelerating PHP Applications, Marcus Boerger - Implementing PHP 5 OOP Extensions,
John Coggeshall - Programming Smarty, Wez Furlong - PDO: PHP Data Objects, Daniel Kushner - Introduction to
OOP in PHP 5
, Derick Rethans - Playing Safe: PHP and Encryption, George Schlossnagle - Web Services in PHP 5,
Dan Scott - DB2 Universal Database, Chris Shiflett - PHP Security: Coding for Safety, Lukas Smith - How About
Some PEAR For You?
, Jason Sweat - Test-driven Development with PHP, Andrei Zmievski PHP-GTK2
The Magazine For PHP Professionals
php|
Tropics
Moon Palace Resort, Cancun, Mexico.
May 11-15 2005
At php|tropics, take the exam and
Get Zend Certified
and we'll pay your fees!
For more information and to sign up: />Early-bird discount in effect for a limited time!
II NN DD EE XX
March 2005

PHP Architect

www.phparch.com
4
6 Editorial
It’s All in a Day’s Work
7 What’s New!

42 Test Pattern
The Three Inch High Design Tool
by Marcus Baker
48 Product Review
Vertrigo: The Utopia of All-in-One’s ?
by Peter B. MacIntyre
62 Security Corner
Magic Quotes
by Chris Shiflett
65 exit(0);
HELP! I’m a PHP beauty stuck in the
body of this Java programmer!
by Marco Tabini
10 Crunching Data with PHP
by Christian Wenz
19 Turning a Class Into an
Application With PHP-GTK
by Scott Mattocks
28 Interview
Priming PHP for the Enterprise
by Marco Tabini
33 Strengthening the
Authentication Process
by Graeme Foster
52 An XML approach to Templating
using PHPTAL
by José Pablo Ezequiel Fernández Silva
TABLE OF CONTENTS
php|architect
TM

Departments
Features

EEDDIITTOORRIIAALL
I
t’s no wonder that we are getting paranoid about the security of
air travel—airports and airplanes seem to be a breeding ground
for odd and bizarre behaviour. For some unknown reason, the
normal laws of civilized society don’t seem to apply over interna-
tional waters, or as soon as you’ve passed the first (of many) secu-
rity checkpoints.
On a flight, you’re forced to be in closer contact than you would
ever allow under any circumstances with people you have never
met in your life—and, most likely, would never want to have any-
thing to do with if you knew them in the first place. Some of your
fellow passengers are just plain inconsiderate—like the guy sitting
next to you who takes off his shoes and the other one who drinks
enough Martinis to kill a small horse.
Airport security—not to be outdone by the very same people it
is meant to server—is reaching new heights of stupidity. At one
end of the line, an officer asks you to take off your shoes. “It’s
optional, but if you don’t take them off they’ll search you at the
other end of the line.” Well, duh… let’s see, should I take off my
shoes now or everything else in the presence of that seven-feet-tall
guard named “Bob” in thirty seconds? Um, let me think about it.
On my way back from a recent trip to California, I sat right
behind the security checkpoint and listened in on a screener who
was performing a search on a fellow passenger-in-waiting who had
actually refused to take off his shoes. The best part was the intro-
duction, which went something like “Sir, could you step to the

side please. Now, I will have to perform a search of your person
because you ‘fit the profile.’ Of course, we can’t tell you what the
profile is, but this will only take a moment.”
So, on one side of the line someone tells you exactly “what the
profile is,” and, on the other, someone else tells you that the pro-
file is a secret. I hate to be stating the obvious, but that strikes me
as slightly odd—then again, there is no limit to government silli-
ness.
Meanwhile, back in Canada our government is training more
search dogs and pigs (yes, I said pigs) to sniff out smugglers.
“Drugs,” you may be thinking? No. Illegally-imported food. It’s
not the guy with the three-pound package of cocaine in his back-
pack that we should be worried about—the real criminal is the
eighty-year-old Italian lady with the salami in her purse.
Until next month, happy readings!
March 2005

PHP Architect

www.phparch.com
6
php|architect
Volume IV - Issue 3
March, 2005
Publisher
Marco Tabini
Editorial Team
Arbi Arzoumani
Peter MacIntyre
Eddie Peloke

Graphics & Layout
Arbi Arzoumani
Managing Editor
Emanuela Corso
News Editor
Leslie Hill

Authors
Marcus Baker, Graeme Foster, Peter B.
MacIntyre, Scott Mattocks, Chris Shiflett,
José Pablo Ezequiel Fernández Silva,
Christian Wenz
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, list-
ings and figures, the publisher assumes no responsibilities with regards
of use of the information contained herein or in all associated material.
Contact Information:
General mailbox:

Editorial:
Subscriptions:
Sales & advertising:
Technical support:
Copyright © 2003-2004 Marco Tabini &
Associates, Inc. — All Rights Reserved
IItt’’ss AAllll iinn aa
DDaayy’’ss WWoorrkk

EE DD II TT OO RR II AA LL RR AA NN TT SS
TM
NNEEWW SSTTUUFFFF
March 2005

PHP Architect

www.phparch.com
7
What’s New!
php|architect launches php|
tropics
2005
Ever wonder what it's like to learn PHP in paradise? Well, this year we've decided to give
you a chance to find out!
We're proud to announce php|tropics 2005, a new conference that will take place between
May 11-15 at the Moon Palace Resort in Cancun, Mexico. The Moon Palace is an all-
inclusive (yes, we said all inclusive!) resort with over 100 acres of ground and 3,000 ft. of
private beach, as well as excellent state-of-the-art meeting facilities.
As always, we've planned an in-depth set of tracks for you, combined with a generous
amount of downtime for your enjoyment (and your family's, if you can take them along
with you).
We even have a very special early-bird fee in effect for a limited time only.
For more information, go to
/>.
NN EE WW SS TT UU FF FF
ZEND Core for IBM
Zend Core for IBM is a complete, certified and fully supported distribution of PHP 5 that
tightly integrates with IBM's DB2 and CloudScape products, in addition to bundling all
required third-party libraries for interaction with the outside world.

The product includes such features as security updates, GUI-based management, granular control over configuration
parameters and compatibility with Zend's other products, including Zend Platform.
Zend Core will be available as a free download from both IBM's and Zend's websites in the second quarter of 2005.
Support programs and Sevice Level Agreements will also be available for commercial clients in a variety of different
configurations.
For more information, visit the Zend Core for IBM site (
hhttttpp::////wwwwww 330066 iibbmm ccoomm//ssooffttwwaarree//ddaattaa//iinnffoo//zzeennddccoorree//
).
phpBlog 2.0.1
Want to get into the world of
blogging? Are you currently run-
ning phpBB? If so, check out the
latest release of phpBlog 2.0.1.
The project’s hompage lists some
of its features as:
• Trackbacks
• Montly archives
• Miniblog
• Rss
• More…
For more information or to down-
load, visit
hhttttpp::////wwwwww oouuttsshhiinnee ccoomm//pphhppbb
bbbblloogg//
Zend Studio 4.0
Zend has announced the release of Zend Studio 4.0
(
hhttttpp::////wwwwww zzeenndd ccoomm//ssttoorree//pprroodduuccttss//zzeenndd ssttuu
ddiioo pphhpp
)

Zend Technologies Inc. introduced Zend Studio 4.0, a
new version of their PHP integrated development envi-
ronment (IDE). Zend Studio runs on multiple operating
systems including Mac OS X.
The new release includes integrated support for all major database servers,
according to the developer, including IBM DB2, Cloudscape, MySQL, Oracle,
MS SQL Server, PostgreSQL, Derby and SQLite. New syntax highlighting
works for XML and CSS previously PHP, HTML, XHTML and JavaScript
were supported. PHPDocs support has been added and PHPDocumentor now
lets users create documentation directly from the PHP project source code.
Zend Studio 4 comes in a Standard edition for US$99 and a Professional
edition for $299. Both prices include tech support and one year of updates
and upgrades.
For more information visit:
hhttttpp::////wwwwww zzeenndd ccoomm//
NNEEWW SSTTUUFFFF
March 2005

PHP Architect

www.phparch.com
8
Check out some of the hottest new releases from PEAR.
DB_DataObject_FormBuilder 0.11.4
DDBB__DDaattaaOObbjjeecctt__FFoorrmmBBuuiillddeerr
will aid you in rapid application development using the
DDBB__DDaattaaOObbjjeecctt
and
HHTTMMLL__QQuuiicckkFFoorrmm
packages. In

order to have a quick but working prototype of your application, simply model the database, run DataObject's createTable script over it
and write a script that passes one of the resulting objects to the
FFoorrmmBBuuiillddeerr
class. The
FFoorrmmBBuuiillddeerr
will automatically generate a sim-
ple but working
HHTTMMLL__QQuuiicckkFFoorrmm
object that you can use to test your application. It also provides a processing method that will auto-
matically detect if an
iinnsseerrtt(())
or
uuppddaattee(())
command has to be executed after the form has been submitted.
If you have set up DataObject's links.ini file correctly, it will also automatically detect if a table field is a foreign key and will populate a
selectbox with the linked table's entries. There are many optional parameters that you can place in your DataObjects.ini or in the
properties of your derived classes, that you can use to fine-tune the form-generation, gradually turning the prototypes into fully-fea-
tured forms, and you can take control at any stage of the process.
DB 1.7.1
DB is a database abstraction layer providing:
• An OO-style query API
• Portability features that make programs written for one DBMS work with other DBMS's
• 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
• Portable error codes
• Sequence emulation
• Sequential and non-sequential row fetching as well as bulk fetching
• Formats fetched rows as associative arrays, ordered arrays or objects
• Row limit support

• Transactions support
• Table information interface
• DocBook and phpDocumentor API documentation
Cache_Lite 1.4.1
This package is a little cache system optimized for file containers. It is fast and safe (because it uses file locking and/or anti-corruption
tests).
XML_RPC 1.2.1
A PEAR-ified version of Useful Inc's XML-RPC for PHP. It has support for HTTP/HTTPS transport, proxies and authentication.
I18Nv2 0.11.3
This package provides basic support to localize your application, like locale based formatting of dates, numbers and currencies.
Beside that it attempts to provide an OS independent way to
sseettllooccaallee(())
and aims to provide language, country and currency names
translated into many languages.
Maguma OpenStudio
Maguma GmbH (Bolzano, Italy) will make the source code of Maguma Studio, Maguma's Windows-
exclusive IDE, open!
Beginning in March 2005 the full source code of Studio will be available for download and community
participation. Maguma OpenStudio, as Maguma has named the product, is a milestone in the pursuit to
the realization of Maguma's Open Source strategy. Maguma OpenStudio is a fast, easy and effective
PHP IDE for beginners and professional developers alike. The newest product, the modular cross-plat-
form IDE, Maguma Workbench, is Maguma’s second generation IDE and is also community focused
through its flexibility to allows users to create custom modules for it. Maguma’s goal is to allow pro-
grammers to "Have Fun Programming!" In March Maguma OpenStudio will be available for download
on the Community site
wwwwww pphhppwwiizzaarrdd nneett
and on the Maguma Community site community
hhttttpp::////ccoomm
mmuunniittyy mmaagguummaa oorrgg//
.

For more information visit:
hhttttpp::////mmaagguummaa oorrgg
NNEEWW SSTTUUFFFF
March 2005

PHP Architect

www.phparch.com
9
Looking for a new PHP Extension? Check out some of the lastest offerings from PECL.
big_int 1.0.0
Functions from this package are useful for number theory applications, or example in two-key cryptography. See
tteessttss//RRSSAA pphhpp
in the
package for example of implementation of RSA-like cryptoalgorithm.
The package has many bitset functions, which make it possible to work with arbitrary-length bitsets. This package is much faster than
the one bundled into PHP BCMath and covers almost entirely the functions implemented in the PHP GMP extension without requiring
any external libraries.
Net_Gopher 1.0.0
An
ffooppeenn(())
wrapper for retrieving documents via the gopher protocol. It includes additional function for parsing gopher directory
entries.
bz2_filter 1.1.0
A bzip2 compress/decompress stream filter implementation. It performs inline compression/decompression using the bzip2 algorithm
on any PHP I/O stream. The data produced by this filter, while compatible with the payload portion of a bz2 file, does not include
headers or trailers for full bz2 file compatibility. To achieve this format, use the
ccoommpprreessss bbzziipp22:://// ffooppeenn
wrapper built directly into
PHP.

intercept 0.2.0
Allows the user to request that a user-space function be called when a PHP function is executed. Support for class/object methods will
be added later.
mailparse 2.1.1
Mailparse is an extension for parsing and working with email messages.
It can deal with rfc822 and rfc2045 (MIME) compliant messages.
eZ publish 3.5.1
Ez.no announces the latest release of their content management system.
From the announcement:
”eZ publish is an open source content management system and development framework. As a
content management system (CMS) its most notable feature is its revolutionary, fully customiz-
able, and extendable content model. This is also what makes it suitable as a platform for gener-
al Web development. Its stand-alone libraries can be used for cross-platform, database independent PHP projects. eZ publish is also
well suited for news publishing, e-commerce (B2B and B2C), portals, and corporate Web sites, intranets, and extranets. eZ publish
is dual licenced between GPL and the eZ publish professional licence.”
Get all the details from
hhttttpp::////eezz nnoo//
The Zend PHP Certification Practice Test Book is now available!
We're happy to announce that, after many months of hard work, the Zend PHP
Certification Practice Test Book, written by John Coggeshall and Marco Tabini, is now
available for sale from our website and most book sellers worldwide!
The book provides 200 questions designed as a learning and practice tool for the
Zend PHP Certification exam. Each question has been written and edited by four
members of the Zend Education Board the very same group who prepared the
exam. The questions, which cover every topic in the exam, come with a detailed
answer that explains not only the correct choice, but also the question's intention,
pitfalls and the best strategy for tackling similar topics during the exam.
For more information, visit
hhttttpp::////wwwwww pphhppaarrcchh ccoomm//cceerrtt//mmoocckk__tteessttiinngg pphhpp
W

hen it comes to transferring data using the
Internet, trying to make your files as small as
possible is often a key element. It is rather lit-
tle known, however, that PHP supports a variety of
archive formats, in various ways: PHP extensions that
are compiled in (or loaded using
pphhpp iinnii
settings or
ddll(())
), PEAR packages and other external scripts. This
article surveys the most important and relevant possi-
bilities in this area, always with short examples that are
ready-to-use for your applications.
PHP Extensions
From a performance point of view, using a PHP exten-
sion is very often the best way to solve a problem. Since
you’re dealing with compiled code, performance is
usually much better than interpreted PHP code.
However, not all of these extensions are updated on a
frequent basis and some of them lack important fea-
tures. But before judging, let’s first have a closer look.
The file format that is probably most widely used over
the Internet is the ZIP format, because it has been
around for a long time and applications to manipulate
it are widely available on all platforms. Recent versions
of Windows come with an internal ZIP module, but do
not support other formats out of the box; Linux distri-
butions and Mac OS X offer much more in this respect.
Therefore, in order to avoid the hassle of additional
software installation, using the ZIP format is a good

idea. There is even a PHP module that supports ZIP—
you can find it in the online manual at
hhttttpp::////pphhpp nneett//mmaannuuaall//eenn//rreeff zziipp pphhpp
. The module is
a wrapper for the ZZIPlib library, a SourceForge project
available at
hhttttpp::////zzzziipplliibb ssff nneett//
. This library sup-
ports only extracting data from an archive, not creating
new ZIP files. Therefore, it can only be used with exist-
ing ZIP files. Doing so, however, is relatively easy: first,
you have to ensure that the PHP module is present. If
you are building PHP by yourself, you have to run
ccoonn
ffiigguurree
with the
——wwiitthh zziipp==//ppaatthh//ttoo//zzzziipplliibb
switch;
Windows users just need to add the following line to
their
pphhpp iinnii
file:
extension=php_zip.dll
REQUIREMENTS
PHP 4.x , 5.x
OS Any
Other Software
The modules and packages refer-
enced in the article.
Code Directory crunch

March 2005

PHP Architect

www.phparch.com
FFEEAATTUURREE
10
Crunching
Data with PHP
by Christian Wenz
There are various file formats to archive, pack, zip or
crunch data. PHP supports many of them, in different
ways: using external PHP scripts, PEAR packages or
PHP extensions.
FF EE AA TT UU RR EE
FFEEAATTUURREE
Crunching Data with PHP
March 2005

PHP Architect

www.phparch.com
11
Under some PHP configurations, the
ggddss3322 ddllll
file
(which resides in the
ddllll
subdirectory of your PHP 4
installation) has to be copied into a directory that is in

the system path, e.g.
cc::\\wwiinnddoowwss\\ssyysstteemm3322
; under PHP
5, this DLL is located in the main installation directory.
Afterwards,
pphhppiinnffoo(())
shows that the library is there.
Then, accessing the data within the ZIP archive consists
of a number of standard steps:
• Open the ZIP archive with
zziipp__ooppeenn(())
.
• Use
zziipp__rreeaadd(())
to iterate through the con-
tents of the ZIP file.
• Open one single file within the archive with
zziipp__eennttrryy__ooppeenn(())
.
• Read its contents with
zziipp__eennttrryy__rreeaadd(())
.
• To clean up, use
zziipp__eennttrryy__cclloossee(())
and
zziipp__cclloossee(())
.
And here is how it’s done: an archive is opened and its
contents are written to the current directory. Here is the
relevant excerpt from the code:

if ($zip = zip_open($archive)) {
while ($entry = zip_read($zip)) {
if (zip_entry_open($zip, $entry, ‘rb’)) {
// Output file
zip_entry_close($entry);
}
}
zip_close($zip);
echo ‘done.’;
}
Listing 1 contains the complete code, including a
PHP4-compatible version of
ffiillee__ppuutt__ccoonntteennttss(())
,
which is a function available in its native form only in
PHP 5. The library also contains some additional func-
tions for gathering information about the files in the
archive, including their size.
In practice, being only able to extract data is a seri-
ous limitation; that’s why there are other classes that
offer additional functionality. You will find them in the
user comments on the ZZIPlib manual page and later
on in this article.
Another file format that, in many cases, achieves bet-
ter compression ratios than ZIP is BZIP2, which even
phpinfo()
1 <?php
2 if (!function_exists(‘file_put_contents’)) {
3 function file_put_contents($filename, $content) {
4 if (!($file = fopen($filename, ‘w’))) {

5 return false;
6 }
7 $n = fwrite($file, $content);
8 fclose($file);
9 return $n ? $n : false;
10 }
11 }
12
13 $archive = dirname(__FILE__) . ‘/test.zip’;
14 if ($zip = zip_open($archive)) {
15 echo ‘Extracting files <br />’;
16 while ($entry = zip_read($zip)) {
17 if (zip_entry_open($zip, $entry, ‘rb’)) {
18 echo ‘ ‘ .
zip_entry_name($entry) . ‘<br />’;
19 file_put_contents(
20 zip_entry_name($entry),
21 zip_entry_read($entry,
22 zip_entry_filesize($entry)
23 )
24 );
25 zip_entry_close($entry);
26 }
27 }
28 zip_close($zip);
29 echo ‘done.’;
30 }
31 ?>
Listing 1
offers a compression mode that takes longer, uses more

memory, but ultimately generates even smaller files.
Unfortunately, this functionality is not built-in in many
operating systems and, therefore, only very few soft-
ware packages are available exclusively in BZIP2.
Of course, PHP supports BZIP2 by
wrapping the bzip2 library available from
hhttttpp::////ssoouurrcceess rreeddhhaatt ccoomm//bbzziipp22//
. Unfortunately, this
module is limited to compressing or decompressing
individual files only. Therefore, it is viable for multiple
files only if you first merge them into one tarball (more
information for TAR support can be found later in this
article; it does not come automatically with PHP). Also,
installation of the library is required—then, a run of
ccoonnffiigguurree
with the
——wwiitthh bbzz22==//ppaatthh//ttoo//bbzziipp22
switch,
followed by a build will introduce BZIP2 functionality in
your scripts. Windows users get the binary module
pphhpp__bbzz22 ddllll
as part of the official distribution, but have
to explicitly load it using either
ddll(())
or by adding this
line to their
pphhpp iinnii
file:
extension=php_bz2.dll
Using the library is easy, and only requires a small num-

ber of steps. For compressing data, the following pro-
cedure has to be followed:
• Create a BZIP2 archive using
bbzzooppeenn(())
.
• Use
bbzzwwrriittee(())
to successively write data to
the archive; the crunching is done automag-
ically.
• Finally, close the file with
bbzzcclloossee(())
.
Here is a short version of this algorithm that reads in
one file and writes it into a BZIP2 archive; Listing 2 con-
tains the complete code. Note that
ffiillee__ggeett__ccoonntteennttss(())
is binary-safe and, therefore, it
can be used to retrieve the original file’s contents.
Otherwise, you can use
ffooppeenn(())
in
‘‘wwbb’’
mode, iterate
through the file and provide the data to
bbzzwwrriittee(())
.
$infile = dirname(__FILE__) . ‘/php.ini-recommended’;
$outfile = dirname(__FILE__) . ‘/test.bz2’;
$out = bzopen($outfile, ‘wb’);

bzwrite( $out, file_get_contents($infile) );
bzclose($out);
The way back, decompressing the files, is performed in
a similar way. Here,
bbzzrreeaadd(())
is used to progressively
read all the data out of the archive and decompressed
into its original format. It can be stored in a buffer (and
then written to the hard disk using
ffiillee__ppuutt__ccoonntteennttss(())
), or directly saved, piece-by-
piece, using
ffppuuttss(())
, as seen in the following snippet.
Listing 3 contains the complete code.
$in = bzopen($infile, “rb”);
$out = fopen($outfile, “wb”);
while ($data = bzread($in, 1024)) {
fputs($out, $data, 1024);
March 2005

PHP Architect

www.phparch.com
FFEEAATTUURREE
12
Crunching Data with PHP
1 <?php
2 $infile = dirname(__FILE__) . ‘/test.bz2’;
3 $outfile = dirname(__FILE__) . ‘/php.ini-recommended’;

4
5 echo ‘Uncompressing file <br />’;
6 $in = bzopen($infile, “rb”);
7 $out = fopen($outfile, “wb”);
8 while ($data = bzread($in, 1024)) {
9 fputs($out, $data, 1024);
10 }
11 bzclose($in);
12 fclose($out);
13 echo ‘done.’;
14 ?>
Listing 3
ZIP Header
1 <?php
2 $infile = dirname(__FILE__) . ‘/php.ini-recommended’;
3 $outfile = dirname(__FILE__) . ‘/test.bz2’;
4
5 echo ‘Compressing file <br />’;
6 $out = bzopen($outfile, ‘wb’);
7 bzwrite(
8 $out,
9 file_get_contents($infile)
10 );
11 bzclose($out);
12 echo ‘done.<br />’;
13 printf(‘Old size: %d bytes; new size: %d bytes.’,
14 filesize($infile),
15 filesize($outfile));
16 ?>
Listing 2

FFEEAATTUURREE
Crunching Data with PHP
March 2005

PHP Architect

www.phparch.com
13
}
bzclose($in);
fclose($out);
The module offers a bit more flexibility if you use the
bbzzccoommpprreessss(())
function. This compresses a string provid-
ed in the first parameter, using the block size specified
in the second parameter. The block size is a value
between one and nine (included) and has a default
value of 4. However, a value of nine gives the best com-
pression, albeit at the cost of increased system
resources during the data-crunching activity.
Finally, PHP supports Zlib, GNU’s ZIP library, which is
available from
hhttttpp::////wwwwww ggzziipp oorrgg//zzlliibb//
. Again,
installation is required: DIY-compilers have to configure
PHP with the
——wwiitthh zzlliibb==//ppaatthh//ttoo//zzlliibb
, whereas
Windows users have this functionality already built-in
(starting with PHP 4.3.0), with no installation, configu-

ration or
pphhpp iinnii
tweaking required. This library is most
often used to GZIP data sent to the browser on the fly,
to make the transfer of web pages smaller and, there-
fore, quicker. Nowadays, most web browsers support
this functionality and advertise it by sending the
AAcccceepptt EEnnccooddiinngg:: ggzziipp
or
AAcccceepptt EEnnccooddiinngg:: ddeeffllaattee
HTTP headers (or both). If this is the case, PHP can send
compressed data across the wire if the following
pphhpp iinnii
setting is enabled:
zlib.output_compression = On
Note that (theoretically) nothing can go wrong if the
browser does not support GZIP compression because,
in that case, no corresponding
AAcccceepptt EEnnccooddiinngg
HTTP
header is sent and, therefore, PHP does not GZIP the
data. Older versions of Netscape have a bug with
embedded, compressed media, but do not have a rea-
sonable market share any longer.
However, the Zlib extension can also be used to com-
press files on the fly—as well as to uncompress them, of
course. In contrast to the ZZIPlib library, this extension
also allows to create archives. The standard steps apply,
again with a couple of new function names:
• Create an archive using

ggzzooppeenn(())
.
• Write data to the archive using
ggzzwwrriittee(())
.
• Close the file using
ggzzcclloossee(())
.
Here are the relevant lines (complete code in Listing 4):
$out = gzopen($outfile, ‘wb4’);
gzwrite(
$out,
file_get_contents($infile)
);
gzclose($out);
The second parameter to
ggzzooppeenn(())
is the file mode
(
‘‘wwbb’’
) plus the (optional) compression level, in this case
an average four. There are other options, including
level-nine compression, which creates smaller files (and
requires larger memory footprints).
Uncompressing files works in a very similar way:
• Open an archive using
ggzzooppeenn(())
.
• Read data from the archive using
ggzzrreeaadd(())

,
until
ggzzeeooff(())
returns
TTrruuee
.
• Close the file using
ggzzcclloossee(())
.
Again, here’s a simple snippet of code, taken straight
out of the larger example that you can find in Listing 5:
$in = gzopen($infile, “rb”);
$out = fopen($outfile, “wb”);
while (!gzeof($in)) {
fputs($out, gzread($in, 1024), 1024);
}
gzclose($in);
1 <?php
2 $infile = dirname(__FILE__) . ‘/php.ini-recommended’;
3 $outfile = dirname(__FILE__) . ‘/test.gz’;
4
5 echo ‘Compressing file <br />’;
6 $out = gzopen($outfile, ‘wb4’);
7 gzwrite(
8 $out,
9 file_get_contents($infile)
10 );
11 gzclose($out);
12 echo ‘done.<br />’;
13 printf(‘Old size: %d bytes; new size: %d bytes.’,

14 filesize($infile),
15 filesize($outfile));
16 ?>
Listing 4
1 <?php
2 $infile = dirname(__FILE__) . ‘/test.gz’;
3 $outfile = dirname(__FILE__) . ‘/php.ini-recommended’;
4
5 echo ‘Uncompressing file <br />’;
6 $in = gzopen($infile, “rb”);
7 $out = fopen($outfile, “wb”);
8 while (!gzeof($in)) {
9 fputs($out, gzread($in, 1024), 1024);
10 }
11 gzclose($in);
12 fclose($out);
13 echo ‘done.’;
14 ?>
Listing 5
“Older versions of Netscape
have a bug with embedded,
compressed media, but do
not have a reasonable market
share any longer.

March 2005

PHP Architect

www.phparch.com

FFEEAATTUURREE
14
Crunching Data with PHP
fclose($out);
The Zlib extension offers some other functions, includ-
ing
ggzzffiillee(())
and
ggzzppaasssstthhrruu(())
, which work similarly to
ffiillee(())
and
ffppaasssstthhrruu(())
, but also uncompress data
from the (GZIP) file pointer provided. Similarly,
ggzzccoommpprreessss(())
allows to directly compress a string (that
was retrieved, for instance, by
ffiillee__ggeett__ccoonntteennttss(())
),
whereas
ggzzddeeffllaattee(())
uncompresses a GZIP string into
its original form.
PHP Streams
Starting with PHP 4.3.0, the concept of streams was
introduced. They already existed in previous versions,
but only in a very limited form: as HTTP wrappers for
ffooppeenn(())
), or for Zlib (

zzlliibb::
). However, the latter was
removed from PHP 4.3.0 onwards and replaced by
something less ambiguous. PHP now supports a lot of
protocols and wrappers for streams, including two that
can be used to compress data:

ccoommpprreessss bbzziipp22::////
for BZIP2

ccoommpprreessss zzlliibb::////
for GZIP, the “successor
wrapper” to the old
zzlliibb::
.
The installation for stream wrappers works analogously
to the procedure we illustrated earlier for PHP exten-
sions. For
ccoommpprreessss bbzziipp22::////
, you need the bzip2
extension (to recall:
——wwiitthh bbzziipp22
if you compile PHP
manually,
eexxtteennssiioonn==pphhpp__bbzziipp22 ddllll
under Windows). If
you want to use GZIP, you have to provide the compi-
lation switch
——wwiitthh ggzziipp
, whereas Windows users have

this functionality built-in in their binary distributions.
From there on, usage is as simple as working with any
stream—it’s like working with a file. You do not have to
worry or care about compressing or uncompressing,
but just work with it like you would with any other PHP
stream: just read from or write to it, and PHP takes care
of the rest in a completely transparent fashion. Here is
how it’s done for GZIP—a file is read in, compressed
and then deflated:
//Compressing
$data = file_get_contents($infile);
file_put_contents(“compress.zlib://$outfile”, $data);

//Uncompressing
$data = file_get_contents(“compress.zlib://$infile”);
file_put_contents($outfile, $data);
The download code for this issue contains the complete
listing, including a tweak to make it backwards-com-
CVS
11 <<??pphhpp
22 iiff ((!!ffuunnccttiioonn__eexxiissttss((‘‘ffiillee__ppuutt__ccoonntteennttss’’)))) {{
33 ffuunnccttiioonn ffiillee__ppuutt__ccoonntteennttss(($$ffiilleennaammee,, $$ccoonntteenntt)) {{
44 iiff ((!!(($$ffiillee == ffooppeenn(($$ffiilleennaammee,, ‘‘ww’’)))))) {{
55 rreettuurrnn ffaallssee;;
66 }}
77 $$nn == ffwwrriittee(($$ffiillee,, $$ccoonntteenntt));;

88 ffcclloossee(($$ffiillee));;
99 rreettuurrnn $$nn ?? $$nn :: ffaallssee;;
1100 }}

1111 }}
1122
1133 $$iinnffiillee == ddiirrnnaammee((____FFIILLEE____)) ‘‘//pphhpp iinnii r
reeccoommmmeennddeedd’’;;
1144 $$oouuttffiillee == ddiirrnnaammee((____FFIILLEE____)) ‘‘//tteesstt bbzz22’’;;
1155 ////CCoommpprreessssiinngg
1166 eecchhoo ‘‘<<pp>>CCoommpprreessssiinngg ffiillee <<bbrr //>>’’;;
1177 $$ddaattaa == ffiillee__ggeett__ccoonntteennttss(($$iinnffiillee));;
1188 ffiillee__ppuutt__ccoonntteennttss((““ccoommpprreessss bbzziipp22::////$$oouuttffiillee””,, $$ddaattaa));;
1199 eecchhoo ‘‘ddoonnee <<bbrr //>>’’;;
2200 pprriinnttff((‘‘OOlldd ssiizzee:: %%dd bbyytteess;; nneeww ssiizzee:: %%dd bbyytteess <<//pp>>’’,,
2211 f
fiilleessiizzee(($$iinnffiillee)),,
2222 ffiilleessiizzee(($$oouuttffiillee))
2233 ));;
2244
2255 $$iinnffiillee == ddiirrnnaammee((____FFIILLEE____)) ‘‘//tteesstt bbzz22’’;;
2266 $$o
ouuttffiillee == ddiirrnnaammee((____FFIILLEE____)) ‘‘//pphhpp iinnii
rreeccoommmmeennddeedd ddeeffllaatteedd’’;;
2277 ////DDeeffllaattiinngg
2288 eecchhoo ‘‘<<pp>>UUnnccoommpprreessssiinngg ffiillee <<bbrr //>>’’;;
2299 $$ddaattaa == ffiillee__ggeett__ccoonntteennttss((““ccoommpprreessss bbzziipp22::////$$iinnffiillee””));;
3300 ffiillee__ppuutt__ccoonntteenntts
s(($$oouuttffiillee,, $$ddaattaa));;
3311 eecchhoo ‘‘ddoonnee <<//pp>>’’;;
3322 ??>>
Listing 6
patible to PHP 4 (where
ffiillee__ppuutt__ccoonntteennttss(())

does not
exist); Listing 6 shows the complete source code for the
same task being performed using BZIP2 compression.
PEAR Packages
PEAR does not have a category specifically
dedicated to archive files, but for file types
(
pear.php.net/packages.php?catpid=33). There, you will
find (as of March 2005) two packages that are relevant
to our quest:

AArrcchhiivvee__TTaarr
(
ppeeaarr pphhpp nneett//ppaacckkaaggee//AArrcchhiivvee__TTaarr
)
for tarballs

AArrcchhiivvee__ZZiipp
(
ppeeaarr pphhpp nneett//ppaacckkaaggee//AArrcchhiivvee__ZZiipp
)
for ZIP files
The first of these two packages is automatically distrib-
uted with PEAR, since the installer uses it to deflate and
install PEAR modules. Nevertheless, it might be a good
idea to run
ppeeaarr lliisstt uuppggrraaddeess
to check whether new
versions exist (or, specifically,
ppeeaarr uuppggrraaddee AArrcchhiivvee__TTaarr

to install them). Currently, there is no web-based end-
user documentation available in the PHP Manual, only
one generated automatically from the PHPDoc com-
ments in the source code. There is also a text file in
PEAR’s
ddoocc
directory that contains rather detailed infor-
mation about the package. Thankfully, the package can
be used in a straightforward manner. Again, it’s just a
matter of taking the right steps in the right order:
• First, load the PEAR module:
rreeqquuiirree__oonnccee
‘‘AArrcchhiivvee//TTaarr pphhpp’’;;
• Then, initialize the class:
$$ttaarr == nneeww
AArrcchhiivvee__TTaarr((‘‘ffiilleennaammee ttaarr’’));;
• Finally, provide a list of file names:
$$ttaarr >>ccrreeaattee
((aarrrraayy((‘‘//ppaatthh//ttoo//ffiillee11’’,, ‘‘//ppaatthh//ttoo//ffiillee22’’))));;
And that’s all: the files are automatically stored into a
tarball and compressed. If you do not want to pass a
monster array to the
ccrreeaattee(())
method, you have two
other choices:
• Once you have created the TAR file, you can
add more files to it using the
aadddd(())
method,
again providing an array of files.

• Instead of using an array, you can also pro-
vide a space separated list of file names—if
your file names do not contain spaces.
Here is a small example that creates a mini PHP distri-
bution: We take three files from a PHP binary distribu-
tion package and compress them into a single tarball:
<?php
require_once ‘Archive/Tar.php’;
$tar = new Archive_Tar(‘test.tar’);
$tar->create(
‘php4embed.lib php.ini-recommended php.ini-
dist’);
?>
It is also possible to create subdirectories. In order to do
so, you can use
ccrreeaatteeMMooddiiffyy(())
instead of
ccrreeaattee(())
, or
aaddddMMooddiiffyy(())
instead of
aadddd(())
. As a second parameter,
you need to provide the name of the diretory where
the files shall be placed:
$tar->create(‘php4embed.lib’);
$tar->addModify(
array(
‘php.ini-recommended’,
‘php.ini-dist’

),
‘ini’
);
Listing 7 contains a souped-up version: here, the con-
structor for
AArrcchhiivvee__TTaarr
gets a second parameter, the
compression mode to be used. Valid values for this
FFEEAATTUURREE
March 2005

PHP Architect

www.phparch.com
Crunching Data with PHP
15
1 <?php
2 require_once ‘Archive/Tar.php’;
3 $tar = new Archive_Tar(‘test.tar.bz2’, ‘bz2’);
4
5 echo ‘Compressing files <br />’;
6 $tar->create(‘php4embed.lib’);
7 $tar->addModify(
8 array(
9 ‘php.ini-recommended’,
10 ‘php.ini-dist’
11 ),
12 ‘ini’
13 );
14 echo ‘done.’;

15 ?>
Listing 7
bzip vs. gzip
parameter are
‘‘bbzz22’’
for BZIP2 and
‘‘ggzz’’
for GZIP. Then,
the PEAR module automatically compresses the files
after merging them into a tarball. Thus, you get both
effects: compacting several files into one distribution
tarball and then making the latter’s file size significant-
ly smaller.
Extracting files from the TAR/TGZ/TAR’ed BZ2 archive
is even easier to implement: you just open the archive
and then extract its contents to the specified path:
<?php
require_once ‘Archive/Tar.php’;
$tar = new Archive_Tar(‘test.tar.gz’, ‘gz’);
$tar->extract(‘targetdir’);
?>
Now to the second relevant PEAR package,
AArrcchhiivvee__ZZiipp
, which, internally, requires PHP’s Zlib
extension. Unfortunately, as of the time of this writing,
the PEAR module has not seen any release yet (an
issue which is, interestingly, also filed as a bug).
However, the module maintainer, Vincent Blavet, does
react to bug reports and currently maintains the
package exclusively in PEAR’s CVS system. You find the

current state of the code at
ccvvss pphhpp nneett
, which
also offers web-based access to the repository at
hhttttpp::////ccvvss pphhpp nneett//ppeeaarr//AArrcchhiivvee__ZZiipp//
. There, you
will find one file,
ZZiipp pphhpp
, that you can download and
manually place into the
AArrcchhiivvee
directory of your PEAR
installation. Then,
rreeqquuiirree__oonnccee ‘‘AArrcchhiivvee//ZZiipp pphhpp’’;;
loads the module. Once you have done this, you can
create ZIP archives using this procedure:
• Instantiate the class, providing the target file
name as the parameter.
• Create the ZIP archive with the
ccrreeaattee(())
method, providing a list of files (as an array
or a comma-separated list).
• Optionally, add further files using the
aadddd(())
method.
As you can see, the syntax is quite similar to the one
used by
AArrcchhiivvee__TTaarr
. The main difference is probably in
the way you can extract files into a specific file location:

you set the
aadddd__ppaatthh
option when using
ccrreeaattee(())
or
aadddd(())
. The following code snippet shows this; Listing 8
contains the complete code for this example:
require_once ‘Archive/Zip.php’;
$zip = new Archive_Zip(‘test.zip’);
$zip->create(‘php4embed.lib’);
$zip->add(
‘php.ini-recommended,php.ini-dist’,
array(‘add_path’ => ‘ini’)
);
The file created by this script indeed uses the directory
name provided. Getting the files back can be accom-
plished with very little code as well: you just need to
open the file and call
eexxttrraacctt(())
. Again, the
aadddd__ppaatthh
parameter can set a path to be used, this time for
deflating the data in the archive into.
<?php
require_once ‘Archive/Zip.php’;
$zip = new Archive_Zip(‘test.zip’);
$zip->extract(
array(‘add_path’ => ‘targetdir’)
);

?>
Both PEAR packages offer some more functionality, but
the elements I have just shown should fulfill most
requirements. Feel free to explore the source code fur-
ther for some interesting insights—and maybe ask the
maintainer of
AArrcchhiivvee__ZZiipp
to officially release his (really
nice and functional) package!
Notable External Scripts
Although the methods for manipulating archives using
PHP I have shown so far are excellent, there are always
alternatives—one of the good things of
Open Source. For instance, the SourceForge
project called PKZip library for PHP, available at
hhttttpp::////ssff nneett//pprroojjeeccttss//pphhppzziipplliibb//
, provides a nice
alternative way to create ZIP archives, but one that has
very limited support for reading in ZIP files. You need
the Zlib extension and, starting from version 0.3, PHP
5. The reason: the author declares his methods as pub-
lic or private, which is basically a good thing, but unfor-
tunately not supported by PHP 4. However, the rest of
the module is fully PHP-4-compliant, so all you have to
do to maintain backwards-compatibility is to remove all
occurrences of
ppuubblliicc
and
pprriivvaattee
in the code. You can

then include the
zziipplliibb pphhpp
file in your code and follow
these steps (great, more lists!):
• Instantiate the class:
$$zziipp == nneeww ZZiipplliibb;;
• Add some files with the method
zzll__aadddd__ffiillee(())
, providing the file’s contents,
its name and the compression method.
A few words about compression methods: the library
supports three possibilities.
nn
stands for none,
bb
for BZIP
compression and
gg
for GZIP compression. If the param-
eter is not provided, the class automatically uses GZIP.
After the character for the compression method, the
March 2005

PHP Architect

www.phparch.com
FFEEAATTUURREE
16
Crunching Data with PHP
1 <?php

2 require_once ‘Archive/Zip.php’;
3 $zip = new Archive_Zip(‘test.zip’);
4
5 echo ‘Compressing files <br />’;
6 $zip->create(‘php4embed.lib’);
7 $zip->add(
8 ‘php.ini-recommended,php.ini-dist’,
9 array(‘add_path’ => ‘ini’)
10 );
11 echo ‘done.’;
12 ?>
Listing 8
compression level follows, ranging from zero (no com-
pression) to nine (maximal compression, both in space
saved and time and memory consumed). Here is a
small example:
require_once ‘ziplib.php’;
$zip = new Ziplib;
$zip->zl_add_file(
file_get_contents(‘php.ini-recommended’),
‘ini/php.ini-recommended’
);
$data = $zip->zl_pack(‘Archive created with PHP!’);
file_put_contents(‘test.zip’, $data);
Listing 9 contains a complete, runnable example.
Another package that provides archive
manipulation facilities is available at
hhttttpp::////wwwwww pphhpp
ccllaasssseess oorrgg//bbrroowwssee//ppaacckkaaggee//994455 hhttmmll
—and a quick

search Google will turn up more alternatives.
Summary
As this article has shown, working with archives from
within PHP is both possible and quite easy—it is just not
as documented and talked-about as other aspects of
the language. There are several possible ways to use
archives, and all relevant file formats are supported.
This functionality can be really useful in your projects:
you can create your own download area where users
can retrieve files that are compressed on the fly before
being sent out to the user (In this case, however, you
should implement “funky caching”, meaning that once
you have crunched a file, you save the result so that
next time someone requests the same data, you save
the effort to crunch it again). Also, users may have the
opportunity to submit archives to your website and you
can have your script take a look what is inside.
FFEEAATTUURREE
March 2005

PHP Architect

www.phparch.com
Crunching Data with PHP
17
1 <?php
2 if (!function_exists(‘file_put_contents’)) {
3 function file_put_contents($filename, $content) {
4 if (!($file = fopen($filename, ‘w’))) {
5 return false;

6 }
7 $n = fwrite($file, $content);
8 fclose($file);
9 return $n ? $n : false;
10 }
11 }
12
13 require_once ‘ziplib.php’;
14 echo ‘Compressing files <br />’;
15 $zip = new Ziplib;
16 $zip->zl_add_file(
17 file_get_contents(‘php.ini-recommended’),
18 ‘ini/php.ini-recommended’
19 );
20 $zip->zl_add_file(
21 file_get_contents(‘php.ini-dist’),
22 ‘ini/php.ini-dist’
23 );
24 $zip
->zl_add_file(
25 file_get_contents(‘php4embed.lib’),
26 ‘php4embed.lib’
27 );
28 $data = $zip->zl_pack(‘Archive created with PHP!’);
29 file_put_contents(‘test.zip’, $data);
30 echo ‘done.’;
31 ?>
Listing 9
About the Author ?>
To Discuss this article:

/>Christian Wenz is author or co-author of over four dozen books, fre-
quently writes for renowned IT magazines and speaks at conferences
around the globe. He is Germany’s very first Zend Certified Professional,
principal at the PHP Security Consortium and maintainer or co-maintain-
er of several PEAR projects.
Have you had your PHP today?
Have you had your PHP today?
The Magazine For PHP Professionals

NEW COMBO NOW AVAILABLE: PDF + PRINT
NNEEWW
LLoowweerr PPrriiccee!!

FFEEAATTUURREE
March 2005

PHP Architect

www.phparch.com
19
O
ften times, a wonderful class is written that
makes a developer’s life much easier. But
almost just as often, the class requires a the
developer to write a script that loads a specific set of
data, which, in turn, can’t be used again. I first came
across this problem when writing dozens of scripts that
each loaded a different unit test and simply output the
results. I wondered how easy it would be to create an
application that loaded the test cases and showed the

results without having to write the same code over
and over again. I came across the same problem
when trying to create PEAR package files—the
PPEEAARR__PPaacckkaaggeeFFiilleeMMaannaaggeerr
class makes creating PEAR
ppaacckkaaggee xxmmll
files easy, but writing the scripts that load
the data drives me crazy. That’s when I decided that it
would be easier to create a PHP-GTK application to col-
lect the data than it would be to write those scripts. It
turns out that it isn’t that hard at all. Hopefully, you will
find the process that I went through helpful when you
find yourself in the same situation that I did.
Getting Started
The first thing to do when turning a class into an appli-
cation is to take a good look at what you are starting
with.
It is important to understand how you would use the
class in a script if you want to create a useful GUI. The
public methods of the class give a good indication as to
what your application should be doing. The
PPEEAARR__PPaacckkaaggeeFFiilleeMMaannaaggeerr
class, for example, has an
aaddddMMaaiinnttaaiinneerr(())
method. Therefore, our application
should have an “add maintainer” feature. This may
sound pretty obvious, but consciously thinking about it
will give you a good start on setting up your GUI. If you
have an incomplete or confusing layout, you might as
well stick to writing scripts.

Lupus in fabula—before getting down to work, we
should probably decide on a general layout for our
application. The
PPaacckkaaggeeFFiilleeMMaannaaggeerr
class performs
several tasks that are mostly independent of one anoth-
er. This isn’t to say that you can use just one method
Tired of having to write a new script for every PEAR pack-
age he released, Scott Mattocks decided to wrap the
PEAR_PackageFileManager class in a GUI to make gener-
ating package files a snap. The following article details the
process that he went through to create his application and
highlights how you can use PHP-GTK to do the same with
your classes.
FF EE AA TT UU RR EE
Turning a Class Into an Application
With PHP-GTK
by Scott Mattocks
REQUIREMENTS
PHP 4.3.x
OS N/A
Other Software PHP-GTK 1.01
Code Directory gtk
RESOURCES
URL
hhttttpp::////qqttkk pphhpp nneett
ii
and you have a valid package file, but adding a main-
tainer doesn’t rely on the user adding a dependency
first. Because of the design of

PPEEAARR__PPaacckkaaggeeFFiilleeMMaannaaggeerr
,
I have decided to use a
GGttkkNNootteebbooookk
as the main display
widget. A
GGttkkNNootteebbooookk
is a widget with pages that are
marked by tabs; the user can click a tab to bring a given
page to the top. I am sure you have seen this type of
layout several times on various websites, or applications
like Mozilla. A notebook layout makes it easy to hide
and show only the tools that we need at any given
time. The
GGttkkNNootteebbooookk
also helps keep our GUI small by
letting us stack things in 3-D, instead of having a huge
window with all of the tools displayed at once.
Setting up the notebook is easy. After you have creat-
ed your notebook object, you just insert or append a
new page whenever you need one. A page consists of
a container holding all of the widgets for the page and
a
GGttkkLLaabbeell
for the tab. You may be asking, “Why do I
have to put everything into another container? Why
can’t I just put everything into the notebook page?” A
GGttkkNNootteebbooookk
page is a descendant of
GGttkkBBiinn

, and
descendants of
GGttkkBBiinn
can only have one child widget.
This may sound like a strange limitation, but it isn’t,
really. If you only have one child, you don’t have to
worry about ordering, positioning or anything else that
comes along with having multiple child widgets. It
keeps the notebook pages simple and leaves the more
complex container stuff to specialized widgets.
To allow us to have a page with more than one widg-
et in it, we just need to make sure that the page’s child
is some sort of container that can hold more than one
child of its own, like a
GGttkkHHBBooxx
. Then, we can fill the
child container up withever we want, and the notebook
page will still only have one child.
If you look at Listing 1, you’ll notice that I have sever-
al helper methods that take are of creating each page’s
widgets. Each helper method returns an array holding
the container for the page, and a label for the tab. You
should also notice that I have connected a method to
the
sswwiittcchh ppaaggee
signal. This is raised any time the top
page of the notebook changes. It doesn’t matter if the
page changes because the user clicks on a tab or if our
code tells the notebook to bring a different page to the
front—by connecting the signal to the

sshhoowwWWaarrnniinnggss(())
method, our code will check for any warnings every
time a different page is brought to the front of the
notebook. If there are any warnings, our application
will bring the user to the warnings page. There are four
methods for connecting signals to callbacks. While they
do pretty much the same thing, understanding the dif-
ferences between them can save you a lot of headaches
down the road. In this instance, I have used
ccoonnnneecctt__oobbjjeecctt(())
. The difference between
ccoonnnneecctt(())
and
ccoonnnneecctt__oobbjjeecctt(())
is that
ccoonnnneecctt(())
passes the
widget that emitted the signal to the callback function.
This is useful when you have one callback that is called
by multiple widgets and you need to know which
widget emitted the signal.
ccoonnnneecctt__oobbjjeecctt(())
, on the
other hand, does not pass the widget that emitted the
signal. Using
ccoonnnneecctt__oobbjjeecctt(())
will make the callback
methods a little more straightforward. You’ll see an
example of when you might need to use
ccoonnnneecctt(())

a
little later, but in this case it would just complicate
things needlessly.
Putting the Pieces Together
Ok. So we have our application all set up and ready to
start working. The feature we should probably add first
March 2005

PHP Architect

www.phparch.com
FFEEAATTUURREE
20
Turning a Class Into an Application With PHP-GTK
1 <?
2 function &_buildNotebook()
3 {
4 // Create the notebook.
5 $this->notebook =& new GtkNotebook();
6
7 // Add the addMaintainer page.
8 $this->_addNotebookPage($this->_createSetOptionsPage());
9
10 // Add the addMaintainer page.
11 $this->_addNotebookPage($this->_createAddMaintainerPage());
12
13 // Add the addDependencies page.
14 $this->_addNotebookPage($this->_createAddDependenciesPage());
15
16 // Add the warnings page.

17 $this->_addNotebookPage($this->_createWarningsPage());
18
19 // When the page is switched, get any new warnings.
20 $this->notebook->connect_object(‘switch-page’, array(&$this, ‘showWarnings’));
21
22 // Return the notebook.
23 return $this->notebook;
24 }
25
26 function _addNotebookPage($page)
27 {
28 // Add the container and the tab label.
29 $this->notebook
->append_page($page[0], $page[1]);
30 }
31 ?>
Listing 1
is the warnings page. This page will be a visual repre-
sentation of the
ggeettWWaarrnniinnggss(())
method, and will need
to display all of the warnings that are generated when
the user tries to add or change any information. The
widget that we want to use to show the warnings
needs to be easy to update and scroll in case there is a
lot of information to display. Even though the underly-
ing GTK+ implementation of
GGttkkTTeexxtt
is technically
“broken,” it is still our best choice in this situation. The

GGttkkTTeexxtt
widget is very similar to an HTML
tteexxttaarreeaa
: it
allows for text to be easily added and will scroll when
there is too much to display at one time. We can also
set the text area so that the user cannot directly edit the
text—this is a good idea to ensure that nobody acci-
dentally deletes a few lines and then gets confused as
to why their package file didn’t get built properly.
The
sshhoowwWWaarrnniinnggss(())
method that is connected to the
notebook’s
sswwiittcchh ppaaggee
signal simply grabs the array of
errors from the package file manager and adds each
one on its own line in the warnings area. To add the
text, we just call the
iinnsseerrtt__tteexxtt(())
method of the
GGttkkTTeexxtt
widget.
iinnsseerrtt__tteexxtt(())
takes two parameters,
the text to add and the position. As with most string
functions in PHP,
11
indicates the end of the string.
Listing 2 shows the code for this page.

It would be pretty annoying if the warnings just piled
up and there was no way to get rid of them. That’s why
I have also added a “clear” button. A
GGttkkBBuuttttoonn
is a
container widget that listens for events from the user,
like pressing a key or clicking with the mouse. When a
user clicks on a button, the appropriately named
cclliicckkeedd
signal is emitted. We want the clear button to
get rid of all of the warnings, so we connect the
cclliicckkeedd
signal of the clear button to the
ddeelleettee__tteexxtt(())
method of the
GGttkkTTeexxtt
widget. As you can see, you
don’t always have to connect signals to your own func-
tions—you can connect them straight to another wid-
get’s methods instead.
FFEEAATTUURREE
March 2005

PHP Architect

www.phparch.com
21
Turning a Class Into an Application With PHP-GTK
1 <?
2 function _createWarningsPage()

3 {
4 // Pack everything in a vBox.
5 $vBox =& new GtkVBox();
6
7 // Set up the warnings area.
8 $this->warningsArea =& new GtkText();
9 $this->warningsArea->set_editable(false);
10 $this->warningsArea->set_word_wrap(false);
11
12 // Add a button to clear the warnings area.
13 $hBox =& new GtkHBox();
14 $button =& new GtkButton(‘Clear’);
15 $button->connect_object(‘clicked’, array(&$this->warningsArea, ‘delete_text’), 0, -1);
16 $hBox->pack_end($button, false, false, 5);
17
18 // Pack everything in.
19 $vBox->pack_start($this->warningsArea, true, true, 10);
20 $vBox->pack_start($hBox, false, true);
21
22 return array($vBox, new GtkLabel(‘Warnings’));
23 }
24
25 function showWarnings($message = NULL)
26 {
27 if (!empty($message)) {
28 $this->warningsArea->insert_text($message . “\n”, $this->warningsArea->get_length());
29 }
30
31 foreach ($this->_packageFileManager->getWarnings() as $warning) {
32 $this->warningsArea->insert_text($warning[‘message’] . “\n”, $this->warningsArea->get_length());

33 }
34 }
35 ?>
Listing 2
Figure 1
Hopefully, you picked up on the two extra arguments
at the end of the
ccoonnnneecctt__oobbjjeecctt(())
call. These two are
user data that will be passed to the callback function.
They are passed, in order, after any arguments that are
automatically added by the callback. Here, we passed
zero as the start character to be deleted and -1 as the
last character to be deleted. When the user clicks on the
clear button, everything in the text area will be discard-
ed. If you look at Figure 1, you’ll see that the clear but-
ton appears on the right of the page. This is because I
used
ppaacckk__eenndd(())
instead of
ppaacckk__ssttaarrtt(())
. This function
works just the same way as
ppaacckk__ssttaarrtt(())
, with the
exception that it adds widgets to the end of the con-
tainer. For
GGttkkVVBBooxx
es, this means that widgets are
packed from the bottom of the container up. For

GGttkkHHBBooxx
es, children are packed from right to left. There
will be more on packing widgets in just a few para-
graphs.
Next, let’s look at adding a maintainer. This is where
we get into really wrapping the
PPaacckkaaggeeFFiilleeMMaannaaggeerr
class into our GTK application.
A maintainer is someone who contributes to a PEAR
package. In the
ppaacckkaaggee xxmmll
file, they are identified by
four pieces of information: their handle, their name,
their email address, and their role in the package. As a
result, the
aaddddMMaaiinnttaaiinneerr(())
method expects these four
pieces of information as arguments. The number and
type of parameters a function takes gives us a clue as to
what kinds of widgets we will need. The developer’s
handle, name and email address can be just about any-
thing, so
GGttkkEEnnttrryy
fields will probably be the best fit. A
GGttkkEEnnttrryy
is very similar to an HTML text input box. It
allows the user to enter one line of text. The role, on
the other hand, has some predefined legal values;
therefore, a
GGttkkCCoommbboo

will work best for this type of
data. The layout of this page is going to be a little more
complicated than the one for the warnings page. A lot
of developers might use Glade to take care of the inter-
face work, but it really isn’t that difficult and I think you
learn more if you do it yourself.
Anyway, let’s get down to business. We already have
a method to set up the notebook page that we are
going to use, so all we have to do is add our new main-
tainer widgets. We will do this by adding a method
called
__ccrreeaatteeAAddddMMaaiinnttaaiinneerrPPaaggee(())
. This method will
create our information-gathering widgets, plus add a
way for us to get the information to the package file
manager.
Take a look at Listing 3—the layout of the widgets is
controlled using a combination of
GGttkkVVBBooxx
es and
GGttkkHHBBooxx
es. Take note of the three parameters at the end
of
ppaacckk__ssttaarrtt(())
: these are often forgotten, but can save
you lots of headaches down the road. The first,
ffiillll
,
tells the container whether or not the child widget
should be resized to take up all of the available space

when it is added. The second,
eexxppaanndd
, lets the contain-
March 2005

PHP Architect

www.phparch.com
FFEEAATTUURREE
22
Turning a Class Into an Application With PHP-GTK
1 <?
2 function _createAddMaintainerPage()
3 {
4 // To lay things out, use a combination of h and v
boxes.
5 $mainVBox =& new GtkVBox();
6 $mainHBox =& new GtkHBox();
7 $leftVBox =& new GtkVBox();
8 $rightVBox =& new GtkVBox();
9 $subHBox =& new GtkHBox();
10
11 // We need a status label to let the user know what
happened.
12 $statusLabel =& new GtkLabel(‘’);
13
14 // We need three entries and three labels.
15 $handleEntry =& new GtkEntry();
16 $handleLabel =& new GtkLabel(‘Handle’);
17 $nameEntry =& new GtkEntry();

18 $nameLabel =& new GtkLabel(‘Name’);
19 $emailEntry =& new GtkEntry();
20 $emailLabel =& new GtkLabel(‘Email’);
21
22 // We also need a combo for the developer role.
23 $roleLabel =& new GtkLabel(‘Role’);
24 $roleCombo =& new GtkCombo();
25
26 // Set up the combo.
27 $roleList
= $roleCombo->list;
28 $roleEntry = $roleCombo->entry;
29 $roleList->set_selection_mode(GTK_SELECTION_SINGLE);
30 $roleEntry->set_text(‘Select One’);
31 $roleEntry->set_editable(false);
32
33 // Add the roles to the select box.
34 $roles = array(‘Contributor’, ‘Developer’, ‘Helper’,
‘Lead’);
35 for ($i = 0; $i < count($roles); $i++) {
36 $item =& new GtkListItem();
37 $box =& new GtkHBox();
38 $label =& new GtkLabel($roles[$i]);
39 $box->pack_start($label, false, false, 10);
40 $item->add($box);
41 $roleCombo
->set_item_string($item, $roles[$i]);
42 $item->set_data(‘role’, $roles[$i]);
43 $roleList->add($item);
44 $item->show_all();

45 }
46
47 // We need a button to do the work.
48 $button =& new GtkButton(‘Add Maintainer’);
49 $button->connect_object(‘clicked’, array(&$this,
‘_addMaintainer’), $handleEntry, $roleCombo, $nameEntry,
$emailEntry, $statusLabel);
50
51 // Put it all together.
52 // The left VBox is for the labels.
53 $leftVBox->pack_start($handleLabel, false, true, 3);
54 $leftVBox->pack_start($nameLabel, false, true, 3);
55 $leftVBox->pack_start($emailLabel,
false, true, 3);
56 $leftVBox->pack_start($roleLabel, false, true, 3);
57
58 // The right VBox is for the entries and the combo.
59 $rightVBox->pack_start($handleEntry, false, false, 0);
60 $rightVBox->pack_start($nameEntry, false, false, 0);
61 $rightVBox->pack_start($emailEntry, false, false, 0);
62 $rightVBox->pack_start($roleCombo, false, false, 0);
63
64 // The two VBoxes go in the main HBox.
65 $mainHBox->pack_start($leftVBox, false, false, 2);
66 $mainHBox->pack_start($rightVBox, false, false, 0);
67
68 // The subHBox holds the button.
69 $subHBox->pack_end($button, false, false, 0);
70
71 // The label and the two HBoxes go in the main VBox.

72 $mainVBox->pack_start($statusLabel, false, false, 4);
73 $mainVBox->pack_start($mainHBox, false, false, 10);
74 $mainVBox->pack_start($subHBox, false, false, 2);
75
76 // Return the page information.
77 return array(&$mainVBox, new GtkLabel(‘Maintainers’));
78 }
79 ?>
Listing 3
er know whether or not it may resize the child widget
when the container is resized. The last parameter is the
amount of padding added around the child widget
when it is added to the container. The same three
parameters also apply if you are using
ppaacckk__eenndd(())
. If
you have every wondered how to stop widgets from
being bigger than you told them to be, now you know.
The
GGttkkEEnnttrryy
widgets used for the developer’s han-
dle, name and email address are pretty basic, so I am
going to focus on the
GGttkkCCoommbboo
for the role instead. A
GGttkkCCoommbboo
is, as its name implies, a combination of a
GGttkkEEnnttrryy
and a
GGttkkLLiisstt

. When we set up our page, we
can deal with each piece of the
GGttkkCCoommbboo
separately, but
still treat them as one widget when it comes time to
position them. First, let’s look at the
GGttkkEEnnttrryy
portion.
If we didn’t modify it, the entry part would function just
like any other
GGttkkEEnnttrryy
in our application, but the
whole idea of using the
GGttkkCCoommbboo
was so that users had
to select the role from a list, as opposed to typing it
themselves. To make sure that only the list values are
used, therefore, we need to prevent the user from edit-
ing the text in the entry area. This is done the same way
as we did for the warnings area, with
sseett__eeddiittaabbllee(())
.
Setting up the list is a little more complicated; a
GGttkkLLiisstt
is a container that will only accept
GGttkkLLiissttIItteemm
s as chil-
dren. To add the developer roles, we have to add one
GGttkkLLiissttIItteemm
for each. When the list items are created,

each one gets tagged with its role. This is done using
sseett__ddaattaa(())
, which is a
GGttkkOObbjjeecctt
method—this means
that all widgets have it. It comes in handy if you just
want to mark an object with a certain value. It is easier
for us to call
ggeett__ddaattaa(())
to retrieve the role than it is to
loop through the children of the selected list item.
Before we exit from this function, we need to create
a button that we can use to add the new maintainer
information to the package file manager. We do this by
first creating a
GGttkkBBuuttttoonn
, which we will label with ‘Add
Maintainer’. Then, we connect the
cclliicckkeedd
signal to the
__aaddddMMaaiinnttaaiinneerr(())
method. In the middle of Listing 3,
you should see that I have decided to use
ccoonnnneecctt__oobbjjeecctt(())
for this purpose. Again, this is
because the function we are connecting to the
cclliicckkeedd
signal does not need to know which button was
pressed and, therefore, doesn’t expect the widget as its
first parameter. We add all of the information widgets

to the
ccoonnnneecctt__oobbjjeecctt(())
call because our method for
adding a maintainer needs to extract the developer’s
information. If you try to pass
$$eemmaaiillEEnnttrryy
>>ggeett__tteexxtt(())
, you are passing the return value from that
method at the time you connect the signal. I have seen
many developers do that and then wonder why their
application isn’t working the way they want. We need
to pass the widgets themselves so that we can get the
return value from
ggeett__tteexxtt(())
at the time the user clicks
the button. The method can then pass the correct val-
ues along to
PPaacckkaaggeeFFiillee__MMaannaaggeerr::::aaddddMMaaiinnttaaiinneerr(())
.
After we’ve added the maintainer, it is nice to let the
user know what happened and then clear out the old
data so they can add another developer. To do this, we
pass one more widget to our
__aaddddMMaaiinnttaaiinneerr(())
method—a
GGttkkLLaabbeell
. When the user hits the “Add
Maintainer” button, we just set the label text to an
appropriate message.
Ok, that was easy. Let’s take a look at something a lit-

tle tougher. The whole idea of creating our desktop
application is to make things easier to use. Consider
what it might take to implement the “add dependen-
cy” feature. We need two pieces for this page: one for
adding the packages that can be used as dependencies,
the other to show the dependencies that we have
already added. For adding the dependencies, we could
do the same thing we did for the add maintainer fea-
ture, but that would let the user add anything they
want as a dependency and would open up a lot of
room for errors. It would be a big help to the user if we
instead grabbed the available packages and let them
chose from what’s available. We can get the list of
installed packages by using the
PPEEAARR__RReeggiissttrryy
class,
which lets us grab information about installed pack-
ages, such as their name, current version number,
description, and changelog. This will be perfect for get-
ting our dependency information, while a
GGttkkCCTTrreeee
widget is the perfect choice for displaying it in a hierar-
chical fashion. We need a hierarchy here because we
want to let the user select not only a particular package
FFEEAATTUURREE
March 2005

PHP Architect

www.phparch.com

23
Turning a Class Into an Application With PHP-GTK
1 <?php
2
3 require_once ‘HTML/QuickForm.php’;
4 $form = new HTML_QuickForm(‘frmTest’, ‘POST’,
5 $_SERVER[‘REQUEST_URI’]);
6 $form->addElement(‘text’, ‘email’, ‘Email’);
7 $form->addElement(‘text’, ‘name’, ‘Name’);
8
9 $form->addRule(‘email’, ‘That must be a valid email’,
10 ‘email’);
11 $form->addRule(‘name’, ‘Please enter your name’,
12 ‘required’);
13
14 if ($form->validate()) {
15 $form->freeze();
16 echo ‘validated!’;
17 }
18 $form->display();
19
20 ?>
Listing 4
“ PHP-GTK isn’t quite as diffi-
cult or scary as its reputation
may have led you to believe. “
but also the minimum version needed. Listing 4 shows
how to build the tree.
After a little set up, we instantiate a
PPEEAARR__RReeggiissttrryy

object and loop through the results of
ppaacckkaaggeeIInnffoo(())
.
For each package found, we build a node with the
package name as the label. We also build a child node
for the version and one node for each version in the
changelog. The most important part is the call to
nnooddee__sseett__rrooww__ddaattaa(())
. By passing an array containing
the package name and version number, we are tagging
each row with the corresponding information. Then,
when the row is selected, we can grab that information
and pass it on to the package file manager. If you look
at the manual page for the
ttrreeee rrooww sseelleecctteedd
signal,
you will see that the callback method gets not only the
tree but also the node that was selected. By setting up
our call to
ccoonnnneecctt(())
and our
__aaddddDDeeppeennddeennccyy(())
meth-
ods, we can grab the right package and version and
pass it on to the package file manager and the widget
that displays the current list of dependencies.
To show the current dependencies, I have decided to
use a
GGttkkCCLLiisstt
widget. To set up the list, we need to

pass the number of columns and an array with the col-
umn labels. A
GGkkttCCLLiisstt
is a pretty complex widget if
you get into all the bells and whistles, but all we need
to do is add entries, sort them and resize the columns
as appropriate. Let’s start with sorting the columns,
because that is the easiest of our operations. After
building the widget, we just call the
sseett__aauuttoo__ssoorrtt(())
method—we don’t need to give it any parameters or
call any other methods. The
GGttkkCCLLiisstt
will now auto-
matically sort our packages by package name and ver-
sion number. Simple enough, right?
Now how about resizing the columns? This gets a lit-
tle tougher. You actually have to pass some parameters.
To have the columns automatically resize, call
ccoolluummnn__sseett__aauuttoo__rreessiizzee(())
passing the column number
(zero in our case) and
ttrruuee
. This will make the first col-
umn stretch and shrink to fit its widest entry. The other
columns will adjust as needed without affecting the size
of the widget.
Listing 5 shows how we grab the data from the tree
and pass it to the list and package file manager. When
a node from the tree is selected, our callback method

grabs the data using
nnooddee__ggeett__rrooww__ddaattaa(())
. After verify-
ing that we have retrieved the package information
successfully, we just add the package to the list by call-
ing
iinnsseerrtt(())
. We don’t have to worry about what posi-
tion at which to insert the information, because the list
will automatically sort its entries anyway.
For both parts of this page, it is pretty easy to picture
the widgets running out of room quickly. Instead of
making our GUI huge to accommodate the lists, we can
put each widget inside a
GGttkkSSccrroolllleeddWWiinnddooww
container.
When the child inside the scrolled window gets too big,
scroll bars will appear. You can control when the scroll
bars are visible by setting the scroll bar policy. I set the
tree’s scrolling window to show the vertical scroll bar
only when it is needed and to never show the horizon-
tal scroll bar. When the user expands the tree, the scroll
bar will appear.
The next piece to our puzzle is the “set options” fea-
ture. A lot of the work for
PPEEAARR__PPaacckkaaggeeFFiilleeMMaannaaggeerr
is
performed by
sseettOOppttiioonnss(())
, which takes an associative

array of options where the key of each element pro-
vides the option’s name and the corresponding value
March 2005

PHP Architect

www.phparch.com
FFEEAATTUURREE
24
Turning a Class Into an Application With PHP-GTK
1 <?
2 function _addDependency($tree, $node, $unknown, $list)
3 {
4 $data = $tree->node_get_row_data($node);
5 if (count($data) == 2) {
6 $result = $this->_packageFileManager->addDependency($data[0],$data[1]);
7
8 // Check for errors.
9 if (PEAR::isError($result)) {
10 $this->_pushWarning($result->getCode(), array());
11 //return;
12 }
13
14 // Add the dependcy to the clist.
15 $list->insert(0, $data);
16 }
17 }
18 ?>
Listing 5
“The public methods of the class give a good indication as to

what your application should be doing.”
the option’s value. To make things easier for the end
user, each option should be its own widget. There are
lots of options that can be set, but I am just going to
focus on one of the more interesting ones—the pack-
age directory.
The package directory is the directory that contains
all of the package files. The best widget for selecting
files or directories is the appropriately named
GGttkkFFiilleeSSeelleeccttiioonn
, which is similar to the save or open
file dialogs you are used to seeing in most applications.
A file selection is a big widget that appears on its own.
We don’t always want it to be shown, so we will use an
entry to show the file path and a button to show the
file selection when it is needed. Setting up the entry
and the button should be pretty much a matter of rou-
tine by now, so let’s jump straight to the file selection
widget.
When the “Select” button is clicked, it calls the file
selection’s
sshhooww(())
method. Because
GGttkkFFiilleeSSeelleeccttiioonn
extends
GGttkkWWiinnddooww
, we can control its position when it
pops up. Instead of letting it appear wherever the oper-
ating system feels like, let’s make it show up in the mid-
dle of the screen. This is done by calling the

sseett__ppoossii
ttiioonn(())
method and passing
GGTTKK__WWIINN__PPOOSS__CCEENNTTEERR
to it.
The
GGttkkFFiilleeSSeelleeccttiioonn
widget is much like
GGttkkCCoommbboo
, in
that it is a collection of other widgets. The two pieces
that we are most interested in are the “OK” and
“Cancel” buttons. These buttons are not connected to
any methods by default, so it is up to us to set them up
correctly. If you try to call a method of a widget prop-
erty, PHP-GTK will stop you. First, you need to assign
the widget property to another variable. After that, you
can call
ccoonnnneecctt__oobbjjeecctt(())
on the buttons. The “OK”
button gets connected to a custom method that pass-
es the file path to the
GGttkkEEnnttrryy
on our main application
and then hides the file selection window, while the
“Cancel” button gets connected straight to the file
selection’s
hhiiddee(())
method. In both cases, we are using
the

hhiiddee(())
method so that we don’t have to rebuild the
GGttkkFFiilleeSSeelleeccttiioonn
every time the user clicks the “Select”
button.
The final part of our application is a menu bar that
lets the user save their package file and exit the appli-
cation. The menu bar needs a menu for file operations
and a menu for help and about information. The menu
bar is the only widget that is going to be outside of the
notebook. We’ll put it in the usual place, at the top of
the window, so that everyone knows where to find
these operations. The
GGttkkMMeennuuBBaarr
widget is a container
specially designed to hold
GGttkkMMeennuuIItteemm
widgets. These,
in turn, are widgets that can be selected by the user
and also hold
GGttkkMMeennuu
s. A
GGttkkMMeennuu
can hold
GGttkkMMeennuuIItteemmss
. Confused yet? Hopefully, Listing 6 will
make it a little clearer. Menu items, placed inside menus
placed inside menu items let you have menus that go
as deep as you want.
In Listing 6, we start of by creating a

GGttkkMMeennuuBBaarr
and
two
GGttkkMMeennuuIItteemm
s, one for the file operations and one
for the help operations. When you create a menu item,
you need to pass it a string that will be the label for the
menu item. Each
GGttkkMMeennuuIItteemm
is appended to the menu
bar and then has a
GGttkkMMeennuu
added to it by calling
sseett__ssuubbmmeennuu(())
and passing a
GGttkkMMeennuu
to it.
GGttkkMMeennuuIItteemm
s
emit the
aaccttiivvaattee
signal when they are selected; when
we add a sub menu, the
aaccttiivvaattee
signal is automatical-
ly connected to the
sshhooww(())
method for the sub menu.
For each operation, we need to create a new
GGttkkMMeennuuIItteemm

. We then connect the
aaccttiivvaattee
signal to
the appropriate method. For the save operation, we
need to call our own method that calls
ddeebbuuggPPaacckkaaggeeFFiillee(())
and then
wwrriitteePPaacckkaaggeeFFiillee(())
if
there are no problems. The exit menu item gets con-
nected to the
ggttkk::::mmaaiinn__qquuiitt(())
function.
While we are setting up the menus, it is a good time
to add accelerator keys. Accelerator keys are the short-
cuts that allow a user to save or exit without going
through the entire menu. In most applications, CTRL-S
is the shortcut for the save function. We can implement
FFEEAATTUURREE
March 2005

PHP Architect

www.phparch.com
Turning a Class Into an Application With PHP-GTK
25
1 <?
2 function &_buildMenu()
3 {
4 // Create the menu bar.

5 $menuBar =& new GtkMenuBar();
6 $accel =& new GtkAccelGroup();
7 $this->window->add_accel_group($accel);
8
9 // Create the main (only) menu item.
10 $fileHeader =& new GtkMenuItem(‘File’);
11 $helpHeader =& new GtkMenuItem(‘Help’);
12
13 // Add the menu item to the menu bar.
14 $menuBar->append($fileHeader);
15 $menuBar->append($helpHeader);
16
17 // Create the file menu
18 $fileMenu =& new GtkMenu();
19 $helpMenu =& new GtkMenu();
20
21 // Add the menu items
22 $about =& new GtkMenuItem(‘Open’);
23 $about->connect(‘activate’, array(&$this,
‘_openFile’));
24 $fileMenu->append($about);
25
26 $save =& new GtkMenuItem(‘’);
27 $saveLabel = $save->child;
28 $saveKey = $saveLabel->parse_uline(‘_Save’);
29 $save->add_accelerator(‘activate’, $accel, $saveKey,
GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
30 $save->lock_accelerators();
31 $save->connect_object(‘activate’, array(&$this,
‘_saveFile’));

32 $fileMenu->append($save);
33
34 $exit =& new GtkMenuItem(‘Exit’);
35 $exit->connect_object(‘activate’, array(‘gtk’,
‘main_quit’));
36 $fileMenu->append($exit);
37
38 $about =& new GtkMenuItem(‘About ’);
39 $about->connect(‘activate’, array(&$this
, ‘about’));
40 $helpMenu->append($about);
41
42 // Complete the menu.
43 $fileHeader->set_submenu($fileMenu);
44 $helpHeader->set_submenu($helpMenu);
45
46 return $menuBar;
47 }
48 ?
Listing 6

×