fully-fea-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 dat
Trang 1INTERVIEW PRIMING PHP FOR THE ENTERPRISEIdealabs Preps LAMP Up for the Big Time
TURNING A CLASS INTO
AN APPLICATION WITH PHP-GTK
Automate your tasks with a GUI appSTRENGTHENING THE AUTHENTICATION PROCESSMake that login more secure without HTTPS
An XML APPROACH TO TEMPLATING USING PHPTAL
Making the peace between designers and developers
Get Ready For:
Trang 3LEARNING 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
At php|tropics, take the exam and
Get Zend Certified
and we'll pay your fees!
For more information and to sign up: http://www.phparch.com/tropics
Early-bird discount in effect for a limited time!
Trang 4HELP! I’m a PHP beauty stuck in the
body of this Java programmer!
Trang 6EDDIITTOORRIIAALL
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!
php|architect
Volume IV - Issue 3 March, 2005
Publisher
Marco Tabini
Editorial Team
Arbi Arzoumani Peter MacIntyre Eddie Peloke
Graphics & Layout
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: info@phparch.com
Editorial: editors@phparch.com
Subscriptions: subs@phparch.com
Sales & advertising: sales@phparch.com
Technical support: support@phparch.com
Copyright © 2003-2004 Marco Tabini & Associates, Inc — All Rights Reserved
IItt’’ss AAllll iinn aa
Trang 7We'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 http://www.phparch.com/tropics
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 ( h http://www-306.ibm.com/software/data/info/zendcore/ ).
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:
dio.php ) 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: h http://www.zend.com/ /
Trang 8NEEWW SSTTUUFFFF
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 tured forms, and you can take control at any stage of the process.
fully-fea-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
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 w www.phpwizard.net and on the Maguma Community site community h http://com m-
-m
munity.maguma.org/ /
For more information visit: h http://maguma.org
Trang 9Net_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-gener-alone libraries can be used for cross-platform, database independent PHP projects eZ publish is gener-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 h http://ez.no/
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 h http://www.phparch.com/cert/mock_testing.php p
Trang 10W hen it comes to transferring data using the
Internet, trying to make your files as small aspossible 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 additionalsoftware installation, using the ZIP format is a goodidea There is even a PHP module that supports ZIP—you can find it in the online manual ath
http://php.net/manual/en/ref.zip.php p The module is
a wrapper for the ZZIPlib library, a SourceForge projectavailable at h http://zziplib.sf.net/ / This library sup-ports only extracting data from an archive, not creatingnew 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 Ifyou 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 totheir pphhpp iinnii file:
Trang 11FEEAATTUURREE
Crunching Data with PHP
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:
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 inPHP 5 The library also contains some additional func-tions for gathering information about the files in thearchive, including their size
In practice, being only able to extract data is a ous limitation; that’s why there are other classes thatoffer additional functionality You will find them in theuser comments on the ZZIPlib manual page and later
3 function file_put_contents ( $filename , $content ) {
4 if (!( $file = fopen ( $filename , ‘w’ ))) {
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 />’ ;
Trang 12offers 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
h
http://sources.redhat.com/bzip2/ / 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 ically
automag-• Finally, close the file with bbzzcclloossee(()).Here is a short version of this algorithm that reads inone 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, itcan be used to retrieve the original file’s contents.Otherwise, you can use ffooppeenn(()) in ‘‘wwbb’’ mode, iteratethrough 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 progressivelyread all the data out of the archive and decompressedinto its original format It can be stored in a buffer (andthen written to the hard disk using
ffiillee ppuutt ccoonntteennttss(())), or directly saved, piece, using ffppuuttss(()), as seen in the following snippet.Listing 3 contains the complete code
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 );
2 $infile = dirname ( FILE ) ‘/php.ini-recommended’ ;
3 $outfile = dirname ( FILE ) ‘/test.bz2’ ;
4
5 echo ‘Compressing file <br />’ ;
6 $out = bzopen ( $outfile , ‘wb’ );
Trang 13The 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 specifiprovid-ed
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
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 uncomcom-press 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) );
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 straightout 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’ );
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 );
have a bug with embedded,
compressed media, but do
not have a reasonable market
Trang 14March 2005 PHP Architect www.phparch.com
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 PHPstream: just read from or write to it, and PHP takes care
of the rest in a completely transparent fashion Here ishow it’s done for GZIP—a file is read in, compressedand 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 completelisting, including a tweak to make it backwards-com-
CVS
1 < <?php
2 i if ( (! ! function_exists ‘ ‘file_put_contents’ )) { {
3 f function file_put_contents $ $filename , $content t ) { {
4 i if ( (!( $file = fopen $ $filename , ‘w’ ))) { {
13 3 $ $infile = dirname _ FILE _ ) ‘/php.ini-recommended’ ’ ;
25 5 $ $infile = dirname _ FILE _ ) ‘/test.bz2’ ;
Trang 15patible 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
find (as of March 2005) two packages that are relevant
to our quest:
• AArrcchhiivvee TTaarr (p pear.php.net/package/Archive_Tar r)
for tarballs
• AArrcchhiivvee ZZiipp (p pear.php.net/package/Archive_Zip p)
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
• Once you have created the TAR file, you canadd more files to it using the aadddd(())method,again providing an array of files
• Instead of using an array, you can also vide a space separated list of file names—ifyour file names do not contain spaces
pro-Here is a small example that creates a mini PHP bution: We take three files from a PHP binary distribu-tion package and compress them into a single tarball:
distri-<?php require_once ‘Archive/Tar.php’;
$tar = new Archive_Tar(‘test.tar’);
$tar->create(
‘php4embed.lib recommended dist’);
php.ini-?>
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 wherethe files shall be placed:
5 echo ‘Compressing files <br />’ ;
6 $tar -> create ( ‘php4embed.lib’ );
Trang 16parameter 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:
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
also offers web-based access to the repository at
h
http://cvs.php.net/pear/Archive_Zip/ / 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:
);
The file created by this script indeed uses the directoryname provided Getting the files back can be accom-plished with very little code as well: you just need toopen the file and call eexxttrraacctt(()) Again, the aadddd ppaatthhparameter can set a path to be used, this time fordeflating 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, butthe elements I have just shown should fulfill mostrequirements Feel free to explore the source code fur-ther for some interesting insights—and maybe ask themaintainer of AArrcchhiivvee ZZiipp to officially release his (reallynice and functional) package!
Notable External Scripts
Although the methods for manipulating archives usingPHP I have shown so far are excellent, there are alwaysalternatives—one of the good things of Open Source For instance, the SourceForge
project called PKZip library for PHP, available at
h http://sf.net/projects/phpziplib/, provides a nicealternative way to create ZIP archives, but one that hasvery limited support for reading in ZIP files You needthe Zlib extension and, starting from version 0.3, PHP
5 The reason: the author declares his methods as lic or private, which is basically a good thing, but unfor-tunately not supported by PHP 4 However, the rest ofthe module is fully PHP-4-compliant, so all you have to
pub-do to maintain backwards-compatibility is to remove alloccurrences of ppuubblliicc and pprriivvaattee in the code You canthen include the zziipplliibb pphhpp file in your code and followthese 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 librarysupports three possibilities nn stands for none, bb for BZIPcompression 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
5 echo ‘Compressing files <br />’ ;
6 $zip -> create ( ‘php4embed.lib’ );
Trang 17compression level follows, ranging from zero (no
com-pression) to nine (maximal compression, both in space
saved and time and memory consumed) Here is a
Listing 9 contains a complete, runnable example
Another package that provides archive manipulation facilities is available at h http://www.php- c
classes.org/browse/package/945.html—and a quicksearch Google will turn up more alternatives
3 function file_put_contents ( $filename , $content ) {
4 if (!( $file = fopen ( $filename , ‘w’ ))) {
14 echo ‘Compressing files <br />’ ;
15 $zip = new Ziplib ;
28 $data = $zip -> zl_pack ( ‘Archive created with PHP!’ );
29 file_put_contents ( ‘test.zip’ , $data );
30 echo ‘done.’ ;
31 ?>
Listing 9
To Discuss this article:
http://forums.phparch.com/204
Christian Wenz is author or co-author of over four dozen books, 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-
fre-er of sevfre-eral PEAR projects.
Have you had your PHP today?
The Magazine For PHP Professionals
Trang 19FEEAATTUURREE
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 towhat your application should be doing ThePPEEAARR PPaacckkaaggeeFFiilleeMMaannaaggeerr class, for example, has an
aaddddMMaaiinnttaaiinneerr(()) method Therefore, our applicationshould have an “add maintainer” feature This maysound pretty obvious, but consciously thinking about itwill give you a good start on setting up your GUI If youhave an incomplete or confusing layout, you might aswell stick to writing scripts
Lupus in fabula—before getting down to work, we
should probably decide on a general layout for ourapplication The PPaacckkaaggeeFFiilleeMMaannaaggeerr class performsseveral 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
RESOURCES
URL h http://qtk.php.net t
i
Trang 20and 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’swidgets Each helper method returns an array holdingthe container for the page, and a label for the tab Youshould also notice that I have connected a method tothe sswwiittcchh ppaaggee signal This is raised any time the toppage of the notebook changes It doesn’t matter if thepage changes because the user clicks on a tab or if ourcode tells the notebook to bring a different page to thefront—by connecting the signal to the sshhoowwWWaarrnniinnggss(())
method, our code will check for any warnings everytime a different page is brought to the front of thenotebook If there are any warnings, our applicationwill bring the user to the warnings page There are fourmethods for connecting signals to callbacks While they
do pretty much the same thing, understanding the ferences between them can save you a lot of headachesdown the road In this instance, I have usedccoonnnneecctt oobbjjeecctt(()) The difference between ccoonnnneecctt(())and ccoonnnneecctt oobbjjeecctt(()) is that ccoonnnneecctt(()) passes thewidget that emitted the signal to the callback function.This is useful when you have one callback that is called
dif-by multiple widgets and you need to know whichwidget emitted the signal ccoonnnneecctt oobbjjeecctt(()), on theother hand, does not pass the widget that emitted thesignal Using ccoonnnneecctt oobbjjeecctt(()) will make the callbackmethods a little more straightforward You’ll see anexample of when you might need to use ccoonnnneecctt(()) alittle later, but in this case it would just complicatethings needlessly
Putting the Pieces Together
Ok So we have our application all set up and ready tostart working The feature we should probably add first
March 2005 PHP Architect www.phparch.com
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 [ ], $page [ ]);
30 }
31 ?>
Listing 1
Trang 21is 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 thecclliicckkeedd signal of the clear button to the ddeelleettee tteexxtt(())
method of the GGttkkTTeexxtt widget As you can see, youdon’t always have to connect signals to your own func-tions—you can connect them straight to another wid-get’s methods instead
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 );
31 foreach ( $this -> _packageFileManager -> getWarnings () as $warning ) {
32 $this -> warningsArea -> insert_text ( $warning [ ‘message’ ] “\n” , $this -> warningsArea -> get_length ());
Trang 22Hopefully, 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 GGttkkVVBBooxxes, this means that widgets are
packed from the bottom of the container up For
GGttkkHHBBooxxes, 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 GGttkkVVBBooxxes and
GGttkkHHBBooxxes 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
5 $mainVBox =& new GtkVBox ();
6 $mainHBox =& new GtkHBox ();
7 $leftVBox =& new GtkVBox ();
8 $rightVBox =& new GtkVBox ();
9 $subHBox =& new GtkHBox ();
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 ();
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
Trang 23er 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 GGttkkLLiissttIItteemms 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 foradding a maintainer needs to extract the developer’sinformation If you try to pass $$eemmaaiillEEnnttrryy
>>ggeett tteexxtt(()), you are passing the return value from thatmethod at the time you connect the signal I have seenmany developers do that and then wonder why theirapplication isn’t working the way they want We need
to pass the widgets themselves so that we can get thereturn value from ggeett tteexxtt(())at the time the user clicksthe 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 theuser know what happened and then clear out the olddata so they can add another developer To do this, wepass one more widget to our aaddddMMaaiinnttaaiinneerr(())
method—a GGttkkLLaabbeell When the user hits the “AddMaintainer” button, we just set the label text to anappropriate message
Ok, that was easy Let’s take a look at something a tle tougher The whole idea of creating our desktopapplication is to make things easier to use Considerwhat it might take to implement the “add dependen-cy” feature We need two pieces for this page: one foradding the packages that can be used as dependencies,the other to show the dependencies that we havealready added For adding the dependencies, we could
lit-do the same thing we did for the add maintainer ture, but that would let the user add anything theywant as a dependency and would open up a lot ofroom for errors It would be a big help to the user if weinstead grabbed the available packages and let themchose from what’s available We can get the list ofinstalled 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 GGttkkCCTTrreeeewidget is the perfect choice for displaying it in a hierar-chical fashion We need a hierarchy here because wewant to let the user select not only a particular package
6 $form -> addElement ( ‘text’ , ‘email’ , ‘Email’ );
7 $form -> addElement ( ‘text’ , ‘name’ , ‘Name’ );
“ PHP-GTK isn’t quite as
diffi-cult or scary as its reputation
may have led you to believe “
Trang 24but 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 tle tougher You actually have to pass some parameters
lit-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 othercolumns will adjust as needed without affecting the size
For both parts of this page, it is pretty easy to picturethe widgets running out of room quickly Instead ofmaking our GUI huge to accommodate the lists, we canput 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 scrollbars are visible by setting the scroll bar policy I set thetree’s scrolling window to show the vertical scroll baronly when it is needed and to never show the horizon-tal scroll bar When the user expands the tree, the scrollbar will appear
The next piece to our puzzle is the “set options” ture A lot of the work for PPEEAARR PPaacckkaaggeeFFiilleeMMaannaaggeerr isperformed by sseettOOppttiioonnss(()), which takes an associativearray of options where the key of each element pro-vides the option’s name and the corresponding value
fea-March 2005 PHP Architect www.phparch.com
8 // Check for errors
9 if ( PEAR :: isError ( $result )) {
10 $this -> _pushWarning ( $result -> getCode (), array());
11 //return;
13
14 // Add the dependcy to the clist
15 $list -> insert ( , $data );
17 }
18 ?>
Listing 5
what your application should be doing.”
Trang 25the 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 ttiioonn(()) method and passing GGTTKK WWIINN PPOOSS CCEENNTTEERR to it.The GGttkkFFiilleeSSeelleeccttiioonn widget is much like GGttkkCCoommbboo, inthat it is a collection of other widgets The two piecesthat we are most interested in are the “OK” and
sseett ppoossii “Cancel” buttons These buttons are not connected toany methods by default, so it is up to us to set them upcorrectly If you try to call a method of a widget prop-erty, PHP-GTK will stop you First, you need to assignthe widget property to another variable After that, youcan 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 applicationand then hides the file selection window, while the
“Cancel” button gets connected straight to the fileselection’s hhiiddee(())method In both cases, we are usingthe hhiiddee(())method so that we don’t have to rebuild theGGttkkFFiilleeSSeelleeccttiioonn every time the user clicks the “Select”button
The final part of our application is a menu bar thatlets the user save their package file and exit the appli-cation The menu bar needs a menu for file operationsand a menu for help and about information The menubar is the only widget that is going to be outside of thenotebook We’ll put it in the usual place, at the top ofthe window, so that everyone knows where to findthese operations The GGttkkMMeennuuBBaarr widget is a containerspecially designed to hold GGttkkMMeennuuIItteemm widgets These,
in turn, are widgets that can be selected by the userand also hold GGttkkMMeennuus A GGttkkMMeennuu can holdGGttkkMMeennuuIItteemmss Confused yet? Hopefully, Listing 6 willmake it a little clearer Menu items, placed inside menusplaced inside menu items let you have menus that go
as deep as you want
In Listing 6, we start of by creating a GGttkkMMeennuuBBaarr andtwo GGttkkMMeennuuIItteemms, one for the file operations and onefor the help operations When you create a menu item,you need to pass it a string that will be the label for themenu item Each GGttkkMMeennuuIItteemm is appended to the menubar and then has a GGttkkMMeennuu added to it by calling
sseett ssuubbmmeennuu(())and passing a GGttkkMMeennuu to it GGttkkMMeennuuIItteemmsemit 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 newGGttkkMMeennuuIItteemm We then connect the aaccttiivvaattee signal tothe appropriate method For the save operation, weneed to call our own method that calls
ddeebbuuggPPaacckkaaggeeFFiillee(()) and then wwrriitteePPaacckkaaggeeFFiillee(()) ifthere 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 cuts that allow a user to save or exit without goingthrough the entire menu In most applications, CTRL-S
short-is the shortcut for the save function We can implement
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 ,
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 );
Trang 26this function by setting up the menu item in a slightly
different way than we have this far First, we pass an
empty string as the label Then, we grab the menu
item’s label child and use it to create a special label
using ppaarrssee uulliinnee(()) This method takes a string with
an underscore as its only parameter The character after
the underscore will become the shortcut key Next, we
call aadddd aacccceelleerraattoorr(()) The first argument is the signal
that should be emitted when the accelerator key is
pressed, while the second is the GGttkkAAcccceellGGrroouupp for the
entire application This is usually added to the main
window using aadddd aacccceell ggrroouupp(()) The third argument
is the special label that we created
Next, we pass a mask that represents any other keys
that need to be pressed at the same time as the
accel-erator’s main key, like CTRL or ALT To require the user
to press the control button, for example, we pass
GGDDKK CCOONNTTRROOLL MMAASSKK To require Control and ALT, we
would pass GGDDKK CCOONNTTRROOLL MMAASSKK || GGDDKK AALLTT MMAASSKK If we
don’t want the user to have to press any other keys, we
can simply pass zero The final parameter determines
whether or not the shortcut key, along with any
modi-fiers, should be shown to the right of the menu item’s
label After using lloocckk aacccceelleerraattoorrss(()) to make sure
that the shortcuts don’t get changed, we finally
con-nect the aaccttiivvaattee signal to our ssaavveeFFiillee(())method
Wrapping it up
There are many features left to be implemented, butyou should, by now, have a good understanding of thewide range of ways that PHP-GTK can be used to make
a class easier to understand and use
It may take a little longer to create a desktop tion than to do the same work as one script but whenyou have to write dozens of scripts, you might want torethink your strategy a little A well-designed GUI appli-cation can make the developer’s life a breeze I thinkthat I’ve shown that PHP-GTK isn’t quite as difficult orscary as its reputation may have led you to believe.Please feel free to take what we started here andexpand on it It is always interesting to see what otherdevelopers do with the freedom that a desktop GUIallows
applica-F
FEEAATTUURREE
Turning a Class Into an Application With PHP-GTK
To Discuss this article:
http://forums.phparch.com/205
Scott Mattocks is a developer for Yamaha Music Interactive When he isn’t working with PHP and PHP-GTK, he enjoys relaxing at home with his wife and dog If you have any questions or comments feel free to email him at s scottmattoks@php.net t.
Award-winning IDE for dynamic languages, providing a powerful workspace for editing, debugging and testing your programs Features advanced support for Perl, PHP, Python, Tcl and XSLT, on Linux, Solaris and Windows
Download your free evalutation at www.ActiveState.com/Komodo30
Trang 27NEXCESS.NET Internet Solutions
SITEWORX control panel
/mo SMALL BIZ $ 21 95
NODEWORX Reseller Access
All of our servers run our in-house developed PHP/MySQL
server control panel: INTERWORX-CP
INTERWORX-CP features include:
- Rigorous spam / virus filtering
- Detailed website usage stats (including realtime metrics)
- Superb file management; WYSIWYG HTML editor
INTERWORX-CP is also available for your dedicated server Just visit
http://interworx.info for more information and to place your order
WHY NEXCESS.NET? WE ARE PHP/MYSQL DEVELOPERS
LIKE YOU AND UNDERSTAND YOUR SUPPORT NEEDS!
ORDER TODAY AND GET 10% OFF ANY WEB HOSTING PACKAGE
VISIT HTTP://NEXCESS.NET/PHPARCH FOR DETAILS
D e d i c a t e d & M a n a g e d D e d i c a t e d s e r v e r s o l u t i o n s a l s o a v a i l a b l e
/mo N EX R ESELL 2 $ 59 95
7500 MB Storage
100 GB TransferUnlimited MySQL DatabasesHost Unlimited DomainsPHP5 / MySQL 4.1.XNODEWORX Reseller Access
NEW! PHP 5 & MYSQL 4.1.X
PHP4 & MySQL 3.x/4.0.x options also available
We'll install any PHP extension you need! Just ask :)
MONEY BACK GUARANTEE
FREE DOMAIN NAME
4.1.x
3.x/4.0.x
Trang 28March 2005 PHP Architect www.phparch.com 28
IINNTTEERRVVIIEEWW
way, and the topic of
Enterprise PHP comes back
into our discussions, one company
has taken matters into its own
hands in a bid to produce a set of
open-source tools that large
organ-izations can depend upon
If the largest fear that an
enter-prise-class organization has of
open-source software is the fact
that it’s not dependable and that
no-one steps up to the plate to offer
the appropriate level of support for
it (what could otherwise be called
the “I have no-one to blame”
fac-tor), Seattle, Washington-based
SourceLabs was founded precisely
with the intent of providing
“certi-fied” software stacks that have been
thoroughly tested and can be
sup-ported in a mission-critical
environ-ment
SourceLabs’ approach to the
problem is, at least on the surface,
very simple They start by building
a “stack” of software based either
on a customer’s needs or on
gener-al common-sense decisions Forexample, in our case this may con-stitute Apache, PHP and MySQL, allrunning on a Linux platform ofchoice
The newly-formed stack is thensubject to a series of tests, whichthe company has collectivelydubbed “CERT-7.” These includeseven different levels of inspections,ranging from security, to scalability,reliability and even regression test-ing Once the stack passes themuster in a particular configuration,they recommend its installation,usage and maintenance and pro-vide support contracts for thosecustomers interested in this type ofservice
While the enterprise-level IT ager will undoubtedly find this serv-ice useful for the peace of mind that
man-it affords in the face of increasing ITscrutiny (especially in large public
companies), the overall conceptcould be really useful for everyonewho uses open-source software formission-critical applications
Don’t Take My Word For It…
A great idea is always backed (ordispelled) by a set or solid, harddata In SourceLabs’ case, this was aparticularly challenging aspect oftheir business, as most of the dataavailable out there is hardly… well,hard at all Therefore, they commis-sioned a full study from Evans Data
to determine whether open-source
in general (and PHP in particular)was anywhere near reaching a level
of acceptance that would justify—
or require—the introduction of ing methodologies and supportedstacks
test-A Friendly Chat
Armed with this information, wethought it might have been a goodidea to let the SourceLabs folks
Interview
Priming PHP for the Enterprise
by Marco Tabini
“PHP in the Enterprise” is beginning
to sound like “the paperless office.”
Luckily, there’s a lot less of a
vapour-ware aura around the former than
around the latter, as this interview
with Cornelius Willis, IdeaLabs’
Vice-president of Sales and Marketing,
Trang 29speak for themselves and explain to
our readers (and to us—we were
quite curious ourselves) who they
are, what they do, and why the PHP
community should take notice We
hooked up with Cornelius Willis,
SourceLabs’ Vice-president of
Marketing, for a quick Q&A session:
php|architect: Can you
intro-duce our readers to SourceLabs?
Cornelius Willis: SourceLabs
pro-vides tested, certified
pre-integrat-ed open source infrastructure
soft-ware (“stacks”) free of charge, and
sells support and maintenance
sub-scriptions for those stacks While
there are many excellent open
source projects, there are no
certi-fied and supported combinations of
open source systems We are in the
process of assembling and testing
our first downloadable stack, based
on AMP (Apache, MySQL, PHP) and
will continue to test and integrate
stacks in the future
php|a: What is your goal?
CW: We want to transform the
enterprise software market
Software should be provided at
much lower cost and without the
vendor lock-in that has
character-ized so many technology business
models to date Open source
soft-ware provides an instrument that
will allow us to accelerate this
restructuring Our goal is to provide
the best of both worlds: softwarewithout lock-in, that is also depend-able, and backed by the same fullservice support and maintenancethat a legacy enterprise softwarevendor provides today
php|a: What is your target ket?
mar-CW: IT users in Global 2000 panies, ASPs, and ISVs
com-php|a: Can you tell us about CERT7?
CW: CERT7 is our overall testmethodology It’s how we deter-mine and improve the overalldependability of an open sourcestack The testing regime embodied
in CERT7 is similar to that used byinfrastructure software providerssuch as Oracle, IBM, SAP andMicrosoft, but is something that isessentially unprecedented in theopen-source world Open-sourcetesting is usually limited to func-tional testing (e.g.: ”does it work asper the design requirements?”)within the context of a single prod-uct module CERT7 testing takes amore holistic view of the depend-ability and performance of an entireintegrated stack, as Figure 1 callingout “system” level testing indicates
You can find out more aboutCERT7 and subscribe to the CERT7forum at:
h http://www.sourcelabs.com/c7.php p
php|a: Tell us about your LAMP stack
CW: We are currently working on
a certified AMP (Apache, MySQLand PHP) stack, which we will test
on the leading Linux tions We are also consideringdoing a version tested on Windows.We’d be interested in hearing fromthe php|architect community abouttheir interest in such an offering.This distribution will have been test-
implementa-ed and certifiimplementa-ed per the CERT7 testregime, and we will share our certi-fication test results to help buyersunderstand the reliability profile ofthe software in unprecedenteddetail We will also provide criticalupdates as needed and regular serv-ice packs
php|a: How does your LAMP stack fit with Zend’s new Platform product? Are you a competitor?
CW: No Zend Platform is focused
on providing the reliability, ity and interoperability business-critical PHP applications need Itdelivers features that enterprisedevelopers and system administra-tors require: run time diagnostics,performance management, PHPconfiguration and control, andinteroperability with Java.SourceLabs’ AMP stack will be freelydownloadable, and its main feature
scalabil-is its certification process andSourceLabs support contract for it.Customers could definitely use
IINNTTEERRVVIIEEWW
Priming PHP for the Enterprise
CERT7 Test Areas Legacy Enterprise
Software Open Source Software
SourceLabs CERT7 Testing
Unit Functional Testing
System Functional Testing
System Stress Testing
System Scalability Profile
System Failover Profile
System Security Testing
System Regression Testing
Figure 1
Trang 30Zend Platform with a SourceLabs
AMP stack
php|a: Why did you commission
the Evans Data report?
CW: We wanted to get a clear
understanding of LAMP stack
adop-tion as we entered the market
Frankly, the data we’d been hearing
directly from IT strategists and
buy-ers was at odds with the hype in the
marketplace, so we wanted to
understand how developers were
spending their precious training
and evaluation time Profiling the
habits and adoption patterns of this
population is what Evans Data
spe-cializes in
The data is fascinating: it shows
that developers are heavily
invest-ing in MySQL, PHP, and Perl, which
is a good leading indicator This is
substantiated by the success of lots
of open-source projects with the
developer community, a
phenome-non that many of us are familiar
with
But our primary research (and
direct interviews with target
Fortune 2000 IT organizations)
shows us that lack of integrated
sys-tems, lack of mission critical
sup-port, and lack of dependability
cer-tification and testing is impeding IT
adoption of open source
infrastruc-ture beyond Linux The conclusion
we’ve reached is that developers, as
one would expect, reflect the
lead-ing edge of a larger trend The
opportunity for SourceLabs is giving
the IT buyer confidence in open
source infrastructure: one throat to
choke for support on integrated,
tested and certified open source
systems, without technology
agen-das or lock-in business models
php|a: Can you give us some of
the important highlights from
the report?
CW: The data gives a fascinating
snapshot of some of the places
where open source development is
growing, and where it is lagging
Some of the highlights:
1) The open source
script-ing languages are strong,but still have a ways to
go to overtake theMicrosoft-supportedscripting languages Thistracks (no surprise)directly to the displace-ment of Windows serverinfrastructure by Linux
This is one of the reasons
we think that a WAMPoffering might be inter-esting to the market, as away to provide a migra-tion strategy for mixed-server shops (See Figure
2 for a breakdown)2) MySQL is widely used,
evaluated, and tested,with 48.5% of databasedevelopers (and it isgaining even more) This
is an example of wherethe gap between whatdevelopers downloadand use and what their
IT gatekeepers are aware
of and sanction, isnotable
3) Adoption of PHP, as a nal indicator of opensource scripting adop-tion, lags in the enter-prise space (just over40% evaluating, intend-ing to or using) with the
IINNTTEERRVVIIEEWW
Priming PHP for the Enterprise
will use or are evaluating
not evaluated or will not use
Trang 31largest evaluated/will not use response, but
is strongest (near 60% evaluating, intending
or using) among small VARs/SIs This
data-point at once shows the opportunity for
companies like Zend and SourceLabs, and
also the ongoing challenge of delivering
demonstrated dependability that IT
organi-zations demand (See Figure 3)
php|a: What picture does the report paint of
LAMP?
CW: I think the report shows that LAMP is just
enter-ing mainstream adoption now When I talk to people
who are deep in the open software movement, and
who have been using LAMP for years, they often
per-ceive this as a nạve, marketing guy sort of comment
But LAMP has a way to go among US IT buyers who
aren’t used to doing their own testing, integration and
support With some of these services coming on line
from companies like SourceLabs, LAMP is really coming
into its own, and emerging as one of the most
impor-tant platforms of this decade
php|a: How do you see the data provided by the
report changing in the near future?
CW: Clearly, Linux, MySQL and PHP adoption are on
a dramatic upswing Apache is reaching saturation, butmore and more companies are trying out open sourceinfrastructure higher in the stack every day
php|a: What about the long term?
CW: We’ll see a much higher penetration of prise IT shops with LAMP (and other OSS infrastruc-ture); over the next year or two, companies and organ-izations like Zend, Apache, MySQL will play a big part
enter-in this Open-source software gives developers andusers of software a fundamentally better way to trans-act, by cutting out the structural and pricing inefficien-cies of the current business model This is similar to theway Napster restructured our thinking about the musicbusiness At SourceLabs, our goal is to deliver servicesthat will give IT customers confidence in open sourceand in so doing dramatically accelerate that restructuring
F
FEEAATTUURREE
Priming PHP for the Enterprise
Available Right At Your Desk
All our classes take place entirely through the Internet and feature a real, live instructor that interacts with each student through voice or real-time messaging.
What You Get
Your Own Web Sandbox Our No-hassle Refund Policy Smaller Classes = Better Learning
Sign-up and Save!
For a limited time, you can get over $300 US in savings
just by signing up for our training program!
New classes start every three weeks!
http://www.phparch.com/cert
To Discuss this article:
http://forums.phparch.com/206
Trang 33FEEAATTUURREE
user to login to access different parts of the web
site This process is so common that few users are
confused by a login screen The problem is that when
the user sends this data, it is transmitted in plain text
(assuming you’re not over an HTTPS connection) This
means that anyone who has access to the network can
discover the password by examining the packets that
are sent across it In many cases, the problem doesn’t
warrant the extra expense of an HTTPS connection, but
it would be nice if the password could be sent
encrypt-ed rather than in plain text
This is where the PHP programmer needs to turn
away from PHP and look at how JavaScript can be used
to solve the problem A careful blending of the two
technologies can provide an excellent solution that will
deter all but the most determined hacker
Security is a balancing act between what is the value
of the data or access to certain privileges against the
effort required to get at it through unauthorized
means A bulletin board probably comes fairly low on
the security horizon, whereas credit card data is right
up there with heads of state This article is not a
replacement for security tools like HTTPS, but it can
help with the dozens of low-priority login situations
which barely register a blip on the security radar
Login Screen
I’ve lost count of the number of times that I have
writ-ten a login procedure I would joke that I could do it in
my sleep—and that is when it starts to get dangerous,because I’m coding without thinking, or, at least, I’mnot thinking about the reason for writing the code Infact, let me challenge you to pause for a moment andthink about what is the purpose of a login screen
We request a user to login so that we know who theyare and, once their identity has been established, wecan determine what they can do There are two distinct
parts to this process: authentication and authorization.
The authentication process is about ascertaining whothe person is; the web server says something akin to
“Stop! Who goes there?” while the browsers mayrespond “Don’t worry, it’s only me.” Obviously, the
REQUIREMENTS
PHP 5.x + SimpleXML extension
OS Linux/Unix, Windows Other Software Apache Httpd, Microsoft IIS Code Directory auth
Strengthening the Authentication