Tài liệu It’s All in a Day’s Work - Pdf 84

Crunching
Data
PHP
with
CrunchingCrunching
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:

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

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


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

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,

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).


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.

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

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(())

}
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 }

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

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);

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

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
.

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
“O
lder 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-

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

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

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-

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

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(())

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!):

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

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-

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

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,

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

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

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.

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

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


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,

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

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

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’;

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

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

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

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

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

‘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


Nhờ tải bản gốc
Music ♫

Copyright: Tài liệu đại học © DMCA.com Protection Status