OBJECT PERSISTENCE IN PHPForgetting storage and focusing on functionality by Theo Spears 23 An OO Layered Approach to Web Apps You can more confidently develop code by knowing its place
Trang 1AN OO LAYERED APPROACH
TO WEB APPS
REFERENCES IN PHP
HOMO XAPIAN The Search for
Trang 4OBJECT PERSISTENCE IN PHPForgetting storage and focusing on functionality
by Theo Spears
23 An OO Layered Approach to Web Apps
You can more confidently develop code
by knowing its place and responsibilities
The Search for a Better Search Engine
Open-source search technology that you can integrate directly into your PHP scripts
by Marco Tabini
06.2005
Download this month’s code at:
h http://www.phparch.com/code/ /
10 TIPS & TRICKS
CAPTCHA That Form
Dealing with misuse of online forms
by Ben Ramsey
55 TEST PATTERN
The Construction Industry
Dependencies and Object Construction
by Marcus Baker
61 PRODUCT REVIEW
Agata 7 Report Generator
A cross-platform database reporting tool
by Peter B MacIntyre
67 Exit(0);
Tales from the Script
by Marco Tabini
Trang 6EDDIITTOORRIIAALL
There’s an interesting discussion taking place, on the PHP-Internals
mailing list, as I type this A couple of days ago, Andi bravely
resur-rected the PHP 5.1 thread that began many months ago, and it’s
started a flood of discussion
The Internals list is a strange beast It can lay nearly dormant for weeks
at a time, and then, overnight it seems, the sleeping giant is awakened
with an onslaught of comments and opinions It’s a really strange feeling
to wake up and find a list that usually gets 5 or fewer posts overnight,
suddenly dominating my inbox with several dozen loud messages
The topic du jour, this time around is, once again, GOTO support in
PHP What seems like a little more than half of the “voters” (not that
many of them actually carry much weight amongst the PHP core
devel-opers) are for GOTO support, while the other bunch, a slightly more
con-servative (some might even say “wise”) bunch are against it.
I, for one, am completely undecided on this issue—I see benefits to
both sides Yes, GOTO would be nice for certain types of deeply nested
parsing algorithms (I love playing with the PHP tokenizer, for example),
and for other things like code generation (many current code-generating
packages—for other languages—employ liberal use of GOTO-like
con-structs) But the other side of the story is that I know that within months
of a released GOTO implementation, I’ll get stuck debugging a huge
plate of PHP-spaghetti Some of the code I’ve had to maintain without
GOTO has been knotty enough, thank you
So, once again, we’re at the crossroads of power and simplicity
The really interesting part of this thread, for me, though, is the
politi-cal stance that people have taken with this discussion I’ve got a certain
amount of respect for an equal amount of zealotry But don’t cross the
line Fortunately, we haven’t seen much mudslinging, yet I’ll give it
another few hours—it’s only a matter of time before someone starts
whining about how “[favorite language] has this feature, we must have
it!” and this someone is met with a swift verbal kick from a member of
the PHP Group, reminding him that “PHP is not [favorite language]!”
It should be interesting to see how this all pans out Fortunately, I’ve
got a fresh beer, and an air-conditioned pub to keep me company while
I write this, and the drama plays out
As far as this issue goes, I believe it’s the best one since I started
edit-ing, three issues ago We’ve got a lot of really great content, this month,
especially interesting, for me at least, is the in-depth look at PHP variable
internals, by Derick Rethans Security Corner is on a mini-hiatus to make
room for the return of Tips & Tricks, which has a new author (whom
you’ll meet when you flip the pages)
Summer is here! Well, at least in the northern hemisphere Enjoy
read-ing this issue on your patio, or while floatread-ing on your pool (unless, of
course, you’re a PDF subscriber; in which case, the pool might not be
such a good idea, unless you’ve printed a copy)
php|architect
Volume IV - Issue 6 June, 2005
Publisher
Marco Tabini
Editorial Team
Arbi Arzoumani Peter MacIntyre Eddie Peloke
Graphics & Layout
Marco Tabini
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-2005 Marco Tabini & Associates, Inc — All Rights Reserved
TM
Political Internals
Trang 7PHP-MultiShop 0.7
Php-MultiShop.com releases the latest version of their CMS and eCommerce system,
version 0.7 The website describes php-multishop as:" Php-MultiShop is a CMS &
e-Commerce System, an OpenSource platform to realize a virtual mall that includes
vari-ous shops and contents The user will have a global vision of the portal, to read the most
interesting content (news, forums, curiosities, suggestions, reviews, cultural or
commer-cial events, fairs, recipes, tourist itineraries, ) and will have the possibility to visit the
shop desired.
Every shop will have all the functions and the personalization of a traditional
e-commerce web-site, as if it were independent from the virtual mall It will have its own
internet domain and could be administrated in full autonomy by its own administrator.
At the same time, it can be distinct from the mall and other shops thanks to the
per-sonalized graphics, individual style, organization, contents and products, like every shop
in a real market place Besides, being part of a large place able to attract different
typologies of visitors and consumers, it will be visible and more easily findable,
increas-ing its audience and potential market.
Php-MultiShop is written in PHP, run on Apache webserver and MySQL database
serv-er, and is able to run on any PHP and MySQL environment, including Linux, Solaris, BSD,
Mac OS X, and Microsoft Windows environments
To realize the portal, the popular CMS PhpNuke is used, and for each shop the
efficient osCommerce e-commerce suite."
Check out Php-MultiShop at php-multishop.com
eZ publish 3.6
ez.no announces:" eZ systems is proud
to announce the release of eZ publish 3.6 This release presents yet another big step forward for eZ publish, with many improvements throughout the system.
eZ publish 3.6 is loaded with new features The most significant new fea- tures are:
• Support for database transactions
• Real preview of new content in the administration interface
• HTML caching of static pages
• Improved support for internal links in XML fields
• Vastly improved template syntax
• A developer toolbar to clear cache and enable debug features on the fly"
Visit ez.no for all the latest tion or to download.
informa-ZEND Core for IBM Beta
IBM announces the release of the ZEND Core for IBM Beta IBM describes the core as: "a seamless out-of-the- box, easy to install and supported PHP development and production environ- ment The product includes tight inte- gration with DB2, the IBM Cloudscape database server, and native support for XML and Web Services, while also sup- porting increased adoption of Service Oriented Architectures (SOA) It deliv- ers a rapid development and deploy- ment foundation for database driven applications and offers an upgrade path from the easy-to-use, lightweight Cloudscape database to the mission critical DB2, by providing a consistent API between the two."
Get all of the latest information from
h t t p : / / w w w - 3 0 6 i b m c o m / software/data/info/zendcore/
MySQL 5.0.6
MySQL 5.0.6 has been released and is ready for download Some changes in this release
include:
• The GRANT and REVOKE statements now support an object_type clause to be used for
disambiguating whether the grant object is a table, a stored function, or a stored
pro-cedure Use of this clause requires that you upgrade your grant tables.
• Added a show-warnings option to mysql to cause warnings to be shown after each
statement if there are any This option applies to interactive and batch mode In
inter-active mode, \w and \W may be used to enable and disable warning display.
• SHOW VARIABLES now shows the slave_compresed_protocol, slave_load_tmpdir and
slave_skip_errors system variables.
• If strict SQL mode is enabled, VARCHAR and VARBINARY columns with a length greater
than 65,535 no longer are silently converted to TEXT or BLOB columns Instead, an
error occurs.
Check out http://dev.mysql.com/doc/mysql/en/news-5-0-6.html for more
changes.
Trang 8Whhaatt’’ss NNeeww??>>
Check out some of the hottest new releases from PEAR.
File_Fstab 2.0.2
File_Fstab is an easy-to-use package which can read & write UNIX fstab files It presents a pleasant object-oriented face to the fstab.
inter-Features:
• Supports blockdev, label, and UUID specification of mount device.
• Extendable to parse non-standard fstab formats by defining a new Entry class for that format.
• Easily examine and set mount options for an entry.
• Stable, functional interface.
• Fully documented with PHPDoc.
SOAP 0.9.1
Implementation of SOAP protocol and services
File_Archive 1.3.0
This library is strongly object oriented It makes it very easy to use, writing simple code, yet the library is very powerfull.
It lets you easily read or generate tar, gz, tgz, bz2, tbz, zip, ar (or deb) archives to files, memory, mail or standard put.
out-See http://poocl.la-grotte.org for a tutorial
Functions from this package are useful for number theory applications For example, in two-keys cryptography.
See /tests/RSA.php in the package for example of simple implementation of RSA-like cryptoalgorithm.
See http://pear.php.net/packages/Crypt_RSA/ project for more complex implementation of RSA-like crypto, which supports key generating, encrypting/decrypting, generating and validating of digital sign.
The package has many bitset functions, which allow to work with arbitrary length bitsets.
This package is much faster than bundled into PHP BCMath and consists almost all functions, which are implemented in PHP GMP extension, but it needn't any external libraries.
Trang 9Oracle and Zend Partnership
Oracle and Zend Technologies, Inc., the PHP company, and creator of products and services supporting the development, deployment and management of PHP-based applications, announced that the companies have partnered to produce Zend Core for Oracle™ - a fully test-
ed and supported, free download that will deliver tight integration between Oracle Database and Zend's supported PHP environment, enabling developers to get up and running in minutes with PHP and Oracle infrastructure
Scheduled for availability in CQ3, Zend Core for Oracle will deliver reliability, productivity and flexibility to run PHP applications
tight-ly integrated with Oracle Database Zend will offer support and updates for Zend Core for Oracle, which will be compatible with Zend's existing products such as Zend Platform and Zend Studio.
For more information visit: http://www.zend.com/
+
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.
Trang 10How can we combat comment
spam or verify that those
using our forms are actually
doing so from our pages and not
some remote script out there? I
don’t pretend to have the definitive
answer, and, in fact, this month’s
Tips & Tricks column doesn’t
attempt to provide a concrete
solu-tion, but I will point out a few
erro-neous practices, show how they
leave forms vulnerable by providing
examples of scripts that can misuse
your forms, and provide a few “best
practices” for securing your forms
There are several popular
meth-ods out there for protecting Web
forms Almost all of them, however,
aim to accomplish the same result,
which is to determine the difference
between a human and a computer(or automated script) Some scriptsembed a token of some sort in theform and set a cookie or sessionvariable Others provide the userwith a CAPTCHA (CompletelyAutomated Turing test to tellComputers and Humans Apart)image of a word or phrase that theuser must enter Some check theRReeffeerreerr header Still others imple-ment some variant of each of thesemethods
The problem is that any script cansimulate a valid user (read
“human”) interaction with a form,and some feel that, as long as thescript is properly simulating a usersession, it’s okay Yet, if your formsare set up improperly, these user-
simulating scripts can continuallyaccess your script using the samesession, potentially flooding youwith spam This month’s Tips &Tricks examines three popularmethods of “securing” forms andshows how to keep external scriptsfrom posting to them
The Embedded Token Method
The simplest and perhaps mostuser-friendly method to “securing”
a Web form is to use what I’m ring to as the “embedded token”method
refer-The embedded token method issimple because it only requires afew lines of code to implement, andit’s user-friendly because it does not
TTIIPPSS && TTRRIICCKKSS
CAPTCHA That Form
Before It Gets Away !
by Ben Ramsey
Abuzz with discussions, arguments, and numerous opinions on solutions to
the problem, the PHP community has been focused, lately, on how to
pre-vent weblog comment spam and how to protect one’s forms in general—
be they comment forms, e-mail forms, etc The topic has graced the pages
of blogs, and threads on the subject have adorned more than one mailing
list Some say it’s a PHP security problem; others blame the developers But
one thing is certain: it’s just plain annoying.
Trang 11require any additional action from
the user to validate their human
identity (there is no word or phrase
to type) It simply relies on the
pres-ence of a user agent (Web browser)
visiting the form The server either
sets a session variable or asks the
browser to set a cookie that is then
checked against a hidden form field
when the user submits the form
Listing 1 illustrates a very basic
implementation of this method
The problem with the embedded
token method in its most basic form
is the assumption that only a Web
browser can set a cookie or make
use of sessions This could not be
further from the truth, since theWeb server will send a Set-Cookieheader to the user agent, whichdoesn’t necessarily have to be abrowser As long as something canparse and read HTTP responseheaders and send valid HTTPrequests, it is a user agent—even ifit’s a script (PHP or otherwise)
Listing 2 illustrates a script thatuses the PEAR packageHTTP_Request to send and receivevalid HTTP headers, including theability to capture the Set-Cookieheader and send it back to the serv-
er in a valid request For all intentsand purposes, this script is a valid
user, and the form treats it as such.This script, of course, assumes thepresence of the “token” form fieldand assumes that this form field willnever change in any way—thename will always be “token,” and itwill always exist in its present form
It uses a regular expression to thengrab the actual token from the formfield to send it back in a subsequentPOST action Now, this regularexpression could be much morecomplex to accommodate forchanging parameters within theform field so that it is not so limited
to the field that it must find
However, as I see it, there must be
a constant in order for this type ofsimulated form post to work: thefield name If the field name of thetoken is always constant, then thisexternal post to the form will alwayswork If you work out a way to ran-domize the field name, then youcan block external scripts frommaking use of yours Randomizingthe field name may seem like asuperfluous extra step to block oth-ers from using your scripts, but itcould save you from unnecessaryspam, flooding, or even being used
as a spam e-mail relay
The Referrer Check Method
Another common approach toblocking scripts from using yourforms is to check the RReeffeerreerr head-
er using $$ SSEERRVVEERR[[‘‘HHTTTTPP RREEFFEERREERR’’]].This is often a suggested methodthat many believe will completelyblock external scripts from usingyour forms However, just aboutevery server-side scripting languagehas the ability to modify the HTTPRReeffeerreerr header—and I’m told evensome proxies will change it, as well.Let’s take our example in Listing
1 It’s simple to modify the code tocheck the RReeffeerreerr Just modify theiiff statement checking for the post-
ed “message” field to include a ond check against the RReeffeerreerrheader, as shown here:
sec-if (isset($_POST[‘message’]) && preg_match(“/^http:\/\/ben- ramsey.com/”,
1 <?php
2 // Embedded Token Method
3 session_start ();
4
5 if (isset( $_POST [ ‘message’ ])) {
6 if ( $_POST [ ‘token’ ] == $_SESSION [ ‘token’ ]) {
7 $message = htmlentities ( $_POST [ ‘message’ ]);
8 echo ‘<h1>’ $message ‘</h1>’ ;
9 }
10 }
11
12 $token = md5 ( uniqid ( rand ()));
13 $_SESSION [ ‘token’ ] = $token ;
14 ?>
15 <form method=”POST”>
16 Message: <input type=”text” name=”message” /><br />
17 <input type=”hidden” name=”token”
18 value=” <?php echo $token ; ?> ” />
6 /* Simulate a valid user and get a session */
7 $req -> setMethod ( HTTP_REQUEST_METHOD_GET );
8 $response = $req -> sendRequest ();
17 $cookies = $req -> getResponseCookies ();
18 foreach ( $cookies as $cookie ) {
19 if ( strcmp ( $cookie [ ‘name’ ], ‘PHPSESSID’ ) == 0 ) {
20 $session_id = $cookie [ ‘value’ ];
21 }
22 }
23
24 /* POST to the form with the session */
25 $req -> setMethod ( HTTP_REQUEST_METHOD_POST );
26 $req -> addCookie ( ‘PHPSESSID’ , $session_id );
27 $req -> addPostData ( ‘message’ , ‘I simulated a user!’ );
28 $req -> addPostData ( ‘token’ , $token );
Trang 12$_SERVER[‘HTTP_REFERER’])) {
Now, the script will only process the
form if the RReeffeerreerr matches any
page from h http://benramsey.com m
Of course, if the RReeffeerreerr were from
h
would fail, but the regular
expres-sion used here is simple; it can be
made more complex to allow for
other variations of domain names
Just as the RReeffeerreerr check method
is easy to implement, it’s similarly
easy to fake a RReeffeerreerr header with
PPEEAARR::::HHTTTTPP RReeqquueesstt Adding the
following line of code to the POST
request in Listing 2 will trick the
form into thinking that the POST
it’s receiving is being sent from
h
reality, it could be sent from
any-where on the Web
$req->addHeader(‘REFERER’,
‘http://benramsey.com’);
The RReeffeerreerr header is not a good
safeguard for your scripts It’s too
easy to manipulate, and this is not a
fault of PHP—almost every scripting
language can do this
The CAPTCHA
CAPTCHA’s are quickly becoming a
preferred method of determining
whether a form post is from a valid
user or a script Their popularity has
also led to great annoyances caused
by unfriendly user experiences due
to the terrible readability of most
CAPTCHA images Nevertheless,
the CAPTCHA seems here to stay
For the most part, the CAPTCHA
image is an effective means of
blocking external scripts from using
your forms However, I have seen
several implementations that leave
much to be desired from the
pro-grammer
For example, I have seen scripts
that simply embed the actual
CAPTCHA phrase in a hidden field
In this case, a script such as the one
shown in Listing 2 can easily grab
the phrase and return it in a post to
the form This form of security does
nothing to hinder external scripts
from using your forms It merely
gives the appearance of tightercontrol while aggravating your realusers who must squint to guess atthe CAPTCHA phrases Never storeyour CAPTCHA phrase in a hiddenfield If you must do so, use mmdd55(())and salt to disguise the word orphrase
Listing 3 uses PPEEAARR::::TTeexxtt CCAAPPTTCCHHAA
to create a simple CAPTCHA test
Much like the example from Listing
1, it sets the phrase to a sessionvariable for checking against theposted user input Instead of plac-ing the phrase in a hidden formfield like the token, however, theuser is required to enter the word orphrase here Already, the security isincreased because external scriptscannot request this page and grabthe phrase from the code as shown
in Listing 2 However, not thing is perfect here
every-If a malicious user is feeling
rather, well, malicious, he can ually access this form on your sitethrough a Web browser and grabthe session ID, which is automati-cally saved to a cookie on hismachine He can also make note ofthe CAPTCHA phrase and thenleave your site without otherwisetouching the form Now, armedwith a session ID and phrase, hecan use the code in Listing 4 to sim-ulate a normal user posting to yourform and entering a properCAPTCHA phrase As long as thesession ID remains active on theserver, the CAPTCHA phrase willwork
man-This may not seem like a big dealsince it’s a lot of work for someone
to go through simply to flood yoursite with posts, but it is an opportu-nity that you will want to close tooutside scripts, and this is easy to
do All you must do is unset the
TTIIPPSS && TTRRIICCKKSS
6 $req -> setMethod ( HTTP_REQUEST_METHOD_POST );
7 $req -> addCookie ( ‘PHPSESSID’ , ’1nkh91unrmh8d6fr4brri4tli1’ );
8 $req -> addPostData ( ‘phrase’ , ‘traiwrou’ );
4 if (isset( $_POST [ ‘phrase’ ]) && isset( $_SESSION [ ‘phrase’ ])
5 && strcmp ( $_POST [ ‘phrase’ ],
Trang 13sion variable after processing the
form
unset($_SESSION[‘phrase’]);
An external script will now be able
to fool your CAPTCHA exactly once,
but the phrase will no longer be
valid in the session after its use, so
the script cannot continue to post
to your form
This seems like a no-brainer, but
I’m amazed at how often I see this
simple step left out of code
exam-ples and actual production code
It’s not a hard thing to do, and it
doesn’t take rocket science, but it’s
an often-overlooked practice
The Security Question
Throughout this column, I’ve been
referring to these examples as
being “insecure” and giving you
tips on how to “secure” the code
In reality, these are not true security
concerns Left unchecked, your
server or database will not be open
to attacks However, your web site
forms may be open to spammingand flooding—and you couldpotentially be used as an e-mailrelay, depending on how yourforms are set up
In general—and as a relatedaside—you should never use a
“form mail” script that requires ahidden form field for a TToo address
Even if the script checks the RReeffeerreerrheader, you are vulnerable as aspam relay—it’s happened to me
Instead, always set the TToo addressfrom the server-side and within theactual PHP code
be able to tackle real security lems head on
prob-Until next time, be sure to tice safe coding!
To Discuss this article:
http://forums.phparch.com/224
Ben Ramsey is a Technology Manager for Hands On Network in Atlanta, Georgia He is an author, Principal member of the PHP Security Consortium, and Zend Certified Engineer Ben lives just north of Atlanta with his wife Liz and dog Ashley You may contact him at r ramsey@php.net tor read his blog at h http://benramsey.com/.
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
Trang 14PHP has come a long way from being a little set of
C-based helper code, used to maintain Rasmus
Lerdorf’s résumé Versions 4 and especially 5 have
added many of the features needed to build enterprise
web applications and make it reasonable to compare
PHP with technologies such as Java Servlets and
ASP.NET Foremost amongst these additions has been
the improvement of object support While objects were
possible in version 4, they were little more than
syntac-tic sugar PHP 5 now has comprehensive support for
objects and increasingly, people are using objects to
build more complicated PHP scripts However, with
comparable support for objects, PHP begins to face a
problem which has confronted other languages: how
are these objects to be stored? Relational SQL-driven
databases remain the only widespread form of storage
for web applications, but this does not fit smoothly
with object-orientated code
h
h t t p : / / p e a r p h p n e t / p a c k a g e / / D
CROSSING
DIVIDE
THE
by Theo Spears
Trang 15Persistence frameworks provide a bridge between
object code and the database To do this they perform
a number of tasks The most simple of these is
transfer-ring data from the member variables of objects to
tables in the database, and back again This often
requires marshalling the data to the correct format for
the database (see the discussion on dates, below, for an
example)
A second role performed by persistence frameworks
is managing the relationships between different
objects When objects contain references to other
objects these should be translated into foreign key
entries, or for many-to-many joins, entries in a join
table Likewise, when loading objects, references
should be automatically mapped back into objects
This, in turn, can cause problems with circular
refer-ences or a single object load bringing many other
objects from the database To overcome this, most
per-sistence frameworks use some form of lazy loading
where related objects are only loaded when they are
accessed
Lastly, loading an object from the database every
time it is needed is expensive, thus many persistence
frameworks include some form of cache This means
multiple requests for the same object result in only a
single database query This can heavily reduce database
load in complicated applications In Java and ASP.NET
this data is often cached between requests In PHP,
however—because PHP has no reliable framework for
sharing memory between requests—this caching has to
be performed on each page load This makes loading
performance more important with PHP than with other
technologies
There are several well known frameworks for object
persistence with Java or Microsoft NET, the most ular probably being Hibernate and NHibernate, respec-tively There are also an increasing number of similarframeworks for PHP that are in various states of comple-tion and offer varying functionality Here we will look atthree: DB_DataObject, Propel, and EZPDO In order todemonstrate the strengths and weaknesses of each, wewill use each to implement a very simple example that
pop-I have put together, to determine how straightforwardeach one makes coding the various parts
School Manager
In the first example, we will be making a website for aschool to keep track of its students and the classes thatthey are taking While maintaining a relatively simpleobject model, this gives scope for testing some of themore advanced features of the persistence frameworks.Our website must allow people to do the following:Add new students and delete or modify existing onesDisplay a list of all students and find students with agiven name
View teacher detailsView class detailsAlter the list of students associated with a class
Of course, any real website would need many moreoptions than these, but in terms of code, most would
be similar to the ones above, just working with ent objects
differ-From this, I have created an object model which youcan see in Figure 1 Notice that Teacher and Studentare derived from a common class—Person Inheritance
is an important part of object-orientated programmingand the part which is most difficult to translate intodatabases Considering the small size of this project Ichose to do no further high level preparation, the rest
of the design will be decided upon as the code is ten For larger projects you should, of course, put morework into these early stages
writ-Installation and Configuration
I am choosing to use MySql to provide database tionality, with all three persistence frameworks It isinstalled in the usual way, and a separate database wascreated for each framework As both Propel and EZPDOprovide their own SQL to create the database tables,and because the tables they generate are incompatible,using a shared database would be impossible Whatyou gain in coding speed with general tools like these,you sometimes lose in flexibility
func-I then installed the three frameworks Each took sometrial and error to install, initially, but once I had identi-fied and installed all the requirements, installation wasreasonably straightforward The simplest wasDB_DataObject, which, as a native member of PEAR,simply required the following PEAR packages (including
Figure 1
Trang 16dependencies): PPEEAARR, AArrcchhiivvee TTaarr, CCoonnssoollee GGeettoopptt,
XXMMLL RRPPCC, DDBB, DDaattee, and DDBB DDaattaaOObbjjeecctt
Propel can also be installed via the PEAR installer—
but it must be fetched from a separate repository–by
issuing the following commands:
Propel requires DOM support in your version of PHP
Most PHP users will already have this installed, but with
some binary distributions (such as Debian) it may be
necessary to install it as an additional module
EZPDO, by contrast, is installed by downloading a
package and extracting it somewhere in your project
directory You may wish to rename the folder to remove
the version number so your code does not break when
you upgrade to a newer version EZPDO also requires
the PHP XML and SPL modules which again can be
compiled in or installed from packages It also needs
items from PEAR including a few packages that are in
Beta state To install these, you need to run:
pear install XML_Util Log FSM
pear uninstall XML_Parser
pear -d preferred_state=beta \
install XML_Parser XML_Serializer
Although not strictly related to installation, there is
another hurdle to cross before you are able to use
EZPDO: it has automatic test suites, but these do not
test all database options so, with Pear DB and MySQL,
it is necessary to fix a small bug You should find a copy
of a patch to fix this in the code archive for this article
We can now configure each framework for our
specif-ic project Each of the three packages has its own
con-figuration file There is no need to memorise the details
for any of the frameworks as all provide examples
which you can copy-paste and then modify to suit your
application DB_DataObject is, again, the simplest of
the three to configure It uses the PEAR options so you
must call PPEEAARR::::ggeettSSttaattiiccPPrrooppeerrttyy for each property
and set an appropriate value to this static property
However, the documentation includes a small script to
load all the information from an iinnii file This method of
implementing your configuration is much simpler, but
you might wish to avoid it if you are trying to squeeze
ultimate performance There are five settings: the
data-base connection, a string your class names must start
with in order to be stored in the database, and three
paths to where the classes and schematic details are
June 2005 PHP Architect www.phparch.com
8 * The tutor for this student
9 * @orm has one Teacher
10 */
11 public $tutor;
12 /**
13 * The classes the pupil is part of
14 * @orm has many class
Trang 17stored A sample configuration file for my computer is
shown in Listing 1
Propel is configured through an XML file that
speci-fies where to log information and how to connect to
the database Unlike DB_DataObject and EZPDO,
Propel doesn’t require all classes to be stored in a
spe-cific location; you include the classes directly, so they
can be stored wherever you choose This is especially
useful for large projects, as classes from different
mod-ules can be stored in different directories
EZPDO’s configuration is the least flexible of the thee,
and it has the most complicated configuration file
Configuration must be stored in the project directory,
in a file called ccoonnffiigg xxmmll The most important
ele-ments are ssoouurrccee ddiirrss, ddeeffaauulltt ddssnn, ddbb lliibb and
aauuttoo fflluusshh ssoouurrccee ddiirrss controls the directory
contain-ing classes to be serialised Assumcontain-ing you leave
rreeccuurr ssiivvee set to ttrruuee, you can store classes anywhere under
this path, although I did not bother with any
subdirec-tories ddeeffaauulltt ddssnn and ddbb lliibb control the database
connection On a unix-like platform, you will probably
want to use ppeeaarrddbb as your ddbb lliibb and ddeeffaauulltt ddssnn is
then a standard PEAR database URI aauuttoo fflluusshh controls
whether all items are automatically stored to the
data-base when your script ends Although useful, it will
have serious effects on performance, so I recommend
turning it off You may also wish to modify the logging
options to either turn off logging altogether, or to log
to a database instead of the default file
The Object Model
Each of the three frameworks has a different way of
specifying the layout of classes and tables With
DB_DataObject, you create your tables and then either
manually define classes or run a script to automatically
generate them, while with EZPDO you define your
classes and tables are automatically created Propel
generates both SQL and classes from a separate XML
file that you’ve created
DB_DataObject fully supports string and numeric
types It also supports date types, although in a more
awkward way—it allows you to treat them as if they
were strings Unlike the other frameworks, dates are
not converted into unix timestamps, so dates before
the start of the epoch (January 1st, 1970) are
support-ed If your application has to deal with dates outside of
this range, this may be a critical consideration Otherdata types may or may not work properly Bearing this
in mind, you are free to create your tables however youchoose Once this is done, it is simplest to run theccrreeaatteeTTaabblleess pphhpp script that is included withDB_DataObject, passing it an iinnii file in the same form
as the one used for configuring the runtime This willgenerate classes for of all the tables in your schema Itwill not, however, pull out any details about joins or for-eign keys Alternatively you can manually create classesderived from DB_DataObject and create your ownschema file Personally, I do not recommend this: it isexactly the type of boring work persistence frameworksare meant to save you from
If you wish to use the join functionality ofDB_DataObject you will need to create a configurationfile specifying the links between tables This is anotheriinnii file in the same folder as the schema generated byggeenneerraatteeTTaabblleess pphhpp Its filename takes on a form like:
<<ddaattaabbaasseennaammee>> lliinnkkss iinnii In this file, there is a sectionfor each table and then entries in the form <<llooccaall ccooll uummnn>> == <<rreemmoottee ttaabbllee>>::<<rreemmoottee ccoolluummnn>> for each rela-tionship Look at ddbb ddaattaaoobbjjeecctt lliinnkkss iinnii in theDataObjects directory of the DB_DataObject project for
an example of such a configuration file DB_DataObjecthas no native support for inheritance, although it ispossible to share methods between classes by modify-ing the generated inheritance hierarchy to give them acommon superclass It is impossible to search for asuperclass and get all subclasses that match
As mentioned above, with Propel you instead create
an XML file This has various nodes for each table andcolumn, and since it is XML, it is fairly self-document-ing The only item of note is that to specify a relation-ship, you must both define a column in the table for theforeign key and also separately define the relationship.Personally, I think XML is a poor format for definingclasses and their relationships, especially as no DTD isprovided to allow editors to auto-complete I wouldsuggest that you either generate this XML from anoth-
er format, or use a GUI XML editor, if you wish to usePropel for anything more than the most simple ofobject models
Propel has a very strange form of inheritance.Essentially, it uses subtractive rather than additive inher-itance; the superclass table must specify all fields andeach subclass may choose which of those fields to use
Trang 18June 2005 PHP Architect www.phparch.com
F
FEEAATTUURREE
18
There is no built in way of simply getting objects of a
specific class, you must add code to filter the superclass
entries yourself Because of this, I chose not to use the
inheritance feature and instead just use separate
class-es Once this XML file is defined you must run the
pprroo ppeell ggeenn script, passing it the directory containing your
sscchheemmaa xxmmll file This will, in turn, generate all classes
and an SQL file that will generate the table This file will
be place under the bbuuiilldd directory of your project
Coming from a C# background, I found EZPDO the
most intuitive With EZPDO, you write your classes as if
they were not going to be persisted at all You then add
the custom phpdoc @@oorrmm tag to each field that should
be stored in the database, specifying its type You can
give a name for each table and column, but if you
don’t, EZPDO will automatically choose one This has
the added side effect of encouraging you to document
your variables
To specify relationships between classes, you use a
class name as the type, together with information as to
the type and multiplicity of the relationship EZPDO
supports composition (ccoommppoosseedd ooff) relationships,
where destroying the parent object destroys the
chil-dren and aggregation (hhaass) relationships where the
child can exist without the parent It also allows
one-to-one (oonnee) and one-to-one-to-many (mmaannyy) relationships For an
example of this, see the SSttuuddeenntt class shown in Listing
2 As you simply define classes, inheritance works as
expected, although again there is no way to fetch a
superclass and get results for all matching subclass
instances
Let’s Write Some Software
It seems to have taken a long time to get to a position
where we are ready to start writing some code, but
hopefully, all of our preparation will prove worthwhile,
by allowing us to write the code more quickly, and with
fewer bugs Working from the list of requirements, let’s
start by adding a page to list all of the students For all
three frameworks, the logic is exactly the same: ask the
framework for a list of all students and then iterate
through that list, displaying each entry in a table With
DB_DataObject, this is a matter of creating an empty
student object, either directly or through the
DDBB DDaattaaOObbjjeecctt::::ffaaccttoorryy(())function, and then calling its
ffiinndd(()) method Using DDBB DDaattaaOObbjjeecctt::::ffaaccttoorryy(()) has
the advantage that the required PHP files will
automat-ically be loaded for you As we have not set any
prop-erties on the object, calling ffiinndd(())gets a list of all
stu-dents in the database, regardless of their properties
See Listing 3 for some simple pseudo-code Note: as we
want to show details from the student’s tutor, which is
stored in a different table, we have to call ggeettLLiinnkkss(())
on each student This loads the tutor into the ttuuttoorr
member We could also have called aaddddJJooiinn(()) before
finding the entries, to reduce the number of queries,but this results in a less logical resulting object
With Propel, we create an empty CCrriitteerriiaa object tomatch all students, and then call
SSttuuddeennttPPeeeerr::::ddooSSeelleeccttJJooiinnTTeeaacchheerr(()) to directly get allstudents and their tutor details from the database Note the name is ddooSSeelleeccttJJooiinnTTeeaacchheerr(()) not
ddooSSeelleeccttJJooiinnTTuuttoorr(()); it is based on the name of the eign table not the name of the foreign key column inthe local table We can then call ggeettTTeeaacchheerr(()) to get
for-1 // Populate a class object to use for the search
2 $class = DB_DataObject::factory(‘class’);
3 $class->selectAs();
4
5 // We also want the size of each class
6 // so link a student record
Trang 19details of the student’s tutor See Listing 4 for an
exam-ple of this
Although doing this with DB_DataObject and Propel
was hardly complicated, EZPDO makes this task the
easiest Here, it is a matter of getting an instance of the
persistence manager with the eeppMMaannaaggeerr::::iinnssttaannccee(())
function, and then calling ggeettAAllll((‘‘SSttuuddeenntt’’)) on the
returned instance Again, the required files are
auto-matically included See Listing 5
There are a few other differences I came across when
working with the list of objects that each framework
returned While Propel and EZPDO return an array of
objects, DB_DataObject returns an iterators, on which
you call ffeettcchh(()),repeatedly This is slightly less
object-like, but hardly a problem More annoyingly, in terms
of increased typing, Propel objects use get and set
methods where DB_DataObject and EZPDO use direct
assignment This also means Propel code requires
slightly more effort to read, for people coming from a
C# background, although Java programmers should be
used to it
We also want to be able to look up specific students
based on their names For this, I modified the code that
shows all students, so it could optionally be limited to
showing only students matching a certain criteria All
three frameworks have a simple method for specifying
criteria when you simply wish to check for equality
DB_DataObject and EZPDO do this by setting values in
an object and then looking for all objects like it, while
with Propel, you add constraints to the CCrriitteerriiaa object
However, because I want to allow searching on
sub-strings, things are more complicated This is simplest
with Propel where it is simply a matter of adding a
CCrriitteerriiaa and specifying that a LLIIKKEE match is required
With DB_DataObject, it is necessary to use the
aaddddWWhheerree(())method to directly add an SQL WWHHEERREE
con-straint to the request EZPDO requires the most
compli-cated implementation; it uses its own query language
to allow you to constrain the results, thus you must call
the following to get a list of students matching a name:
To do this, we must select a single teacher entry fromthe database This is again done in similar ways with all
3 frameworks With DB_DataObject, you use the
DD BB DD aa tt aa OO bb jj ee cc tt :: :: ss tt aa tt ii cc GG ee tt (( $$ cc ll aa ss ss nn aa mm ee ,,
$$pprriimmaarryy kkeeyy)) function, while in Propel you must callTTeeaacchheerrPPeeeerr::::rreettrriieevveeBByyPPKK(($$pprriimmaarryy kkeeyy)) EZPDOuses the ggeett(($$ccllaassss nnaammee,, $$pprriimmaarryy kkeeyy)) function on
an instance of the persistence manager In all threecases, this returns an instance of the relevant object
As well as showing the teacher’s own properties, I
wanted to show a list of their tutees To do this withDB_DataObject, the simplest way was simply to fetchall of the students from the database, whose tutor fieldmatched the primary key of the teacher being viewed.This is effectively querying as if there was no persistenceframework, but simply an object wrapper It is effective,but breaks the object-orientated encapsulation I washoping for Propel is far more promising in this regard
It provides a TTeeaacchheerr::::ggeettSSttuuddeennttss(()) function thatreturns an array of all the teacher’s tutees By iteratingthrough this array, we get a list of student objects.Similarly, EZPDO allows us to access the ttuutteeeess proper-
ty as an array of student objects
Next, let’s get a list of courses This is slightly morecomplicated, as I also want to display how many stu-dents are enrolled in each course Here, the abstractionprovided by DB_DataObject was rather inadequate.The solution I came up with involved manually adding
a join between the course table and the course-studentjoin table, and manually adding a column to select the
CCOOUUNNTT(()) of rows in the database Although, arguablyslightly less work that using raw SQL, I still largely had
to think in terms of relational databases For the code Iused, refer to Listing 6
Propel again provided a better level of abstraction.Using the ggeettSSttuuddeennttCCoouurrsseeRReeffJJooiinnCCoouurrssee(())function, Iwas able to get an array of students which I couldcount As this suggests, many of the function names formany-to-many joins in Propel are rather cumbersome,
Trang 20but you can see the list by looking at the generated
code If you are going to be using a function often, you
can always wrap it to have a friendlier name With
EZPDO, it is again just a matter of calling ccoouunntt(()) on
the ssttuuddeennttss member
Moving on, lets now look at how we can create and
store new objects, in this case for our students In each
case, this is done by creating a new class instance and
setting its properties, then telling the framework to
store it in the database With DB_DataObject this is
done with the DDBB DDaattaaOObbjjeecctt::::ffaaccttoorryy(()) function, as
mentioned above Once the object is ready to be
added to the database, you call its iinnsseerrtt(())method to
store it Propel is the simplest of the three when it
comes to implementing this You create the object as
you would any other with the nneeww operator and then
call ssaavvee(())to store it EZPDO works slightly differently
You create the object with the ccrreeaattee(())function on the
persistence manager, but it is stored by calling the
ccoomm mmiitt(())method on the manager, rather than the object
itself This makes for slightly longer code If you
enabled aauuttoo ccoommmmiitt in the configuration file, this step
is optional
EZPDO also works slightly differently in terms of
adding relationships With DB_DataObject and Propel,
all relationships are implicitly bidirectional, so setting
the tutor for a student automatically adds the student
to that teacher’s list of tutees However, with EZPDO,
links in both directions are discrete, so you must also
modify the teacher to add a reference to the student
This is the expected behaviour from a purely
object-ori-entated point of view but is far less convenient, and
vio-lates the Don’t Repeat Yourself principle of avoiding data
duplication For an example, look at Listing 7
Editing entries is done in almost exactly the same way
as adding new ones Instead of creating a new record,
you fetch an existing one, as detailed above You
mod-ify its properties appropriately, and then save it back to
the database With Propel and EZPDO this is exactly the
same as saving a new object, with DB_DataObject you
must call uuppddaattee(())instead of iinnsseerrtt(())
Deleting is done in a similar manner With
DB_DataObject, you call ddeelleettee(()) on the relevant
object You can also create an empty object and use
wwhheerreeAAdddd(())to delete all objects matching a specific
cri-teria With Propel, you can delete a specific object or
delete any object matching a provided CCrriitteerriiaa class
With EZPDO, you call ddeelleettee(())on the persistence
man-ager to delete an object, however there is no way to
delete all objects matching a certain criteria
Other considerations
As important as the ease of use of a framework, is the
quality of documentation that is provided for it In this
respect, all three frameworks do rather well
DB_DataObject provides the standard PEAR API ence, along with a user guide that takes you througheach function, with lots of examples It is worthwhile toread all of the documentation, if you plan to useDB_DataObject, as it provides some more efficientmethods of performing certain tasks
refer-Propel also provides an extensive API reference anduser guide The user guide walks you through setting
up a simple example and then goes on to cover moreadvanced topics It is probably the best documentationprovided by any of the three frameworks
EZPDO provides a walk-through tutorial on its site that takes you through setting up a simple project,which is included with the EZPDO framework down-load Its coverage of more advanced areas is adequate
web-It may not be as comprehensive as that of Propel,though this may in part be because it is simpler to use.All of its functions are fully documented in the phpdocscheme, although unfortunately, I could not find a copy
of the generated documentation online, so if you want
it you may have to generate a copy for yourself
For people building large sites, another importantconsideration will be performance I always questionthe value of “microbenchmarks,” but Figure 2 showsthe number of queries each framework needs for vari-ous operations The results show similar performancebetween DB_DataObject and Propel but a much high-
er query count for EZPDO, especially when modifyingexisting data Clearly, there is room for improvement inthe EZPDO engine, especially where existing data ismodified, which seemed to require an absurd number
of queries This may, in part, be due to my use of lar references but this is not that uncommon a pattern.Hopefully, this is something that will be fixed in later
circu-June 2005 PHP Architect www.phparch.com
ADD A NEW STUDENT 2 5 46
MODIFY AN EXISTING STUDENT 2 6 2518
Trang 21versions as EZPDO did feel noticeably slower to me
than the other two frameworks
Lastly, a brief note on version support:
DB_DataObject fully supports PHP 4 and 5 Propel
pri-marily supports PHP 5, although work is being done on
a PHP 4 version EZPDO is firmly PHP 5 only and would
be extremely difficult to port to PHP 4 While I
recom-mend people use PHP 5 and take advantage of the new
features it provides, in some cases this is not feasible
and EZPDO may simply not be an option
Conclusion
I wish to stress that the three frameworks I looked at
here are by no means all of the options available for
PHP If there in another framework you like, and think it
is better than the ones presented here, we would love
to have you come and tell us its merits in the discussion
forum Likewise, if you think there is something crucial
I have failed to mention, let everyone else know about
it
Restricted to these three, your choice will inevitably
depend on your situation DB_DataObject is not really
do a full translation between the world of objects and
the world of databases, and is more just a wrapper
around SQL I chose to include it because of its
ggeettLLiinnkkss(()) and jjooiinnAAdddd(()) functionality but these do
not really compare with the power of Propel andEZPDO It is a good choice if you want a wrapper thatavoids the need to write SQL, but are happy to think in
a relational manner EZPDO, by contrast, completelyabstracts the database, almost to too great of anextent, though I found it the most pleasant of the threealternatives to use Unfortunately, it has severe perform-ance problems at the moment Hopefully these will beovercome in later versions but until then I cannot real-
ly recommend it for anything beyond the smallest sonal site This leaves Propel which although not ascomplete as EZPDO does a good job of providing anobject-orientated interface to databases It is morecomplete that DB_DataObject and much lighter-weight than EZPDO so if you can tolerate its XMLmethod of defining your classes, it is the one I wouldrecommend
To Discuss this article:
http://forums.phparch.com/226
Theo is a student at a university in the UK, studying Social Sciences While he claims to fer C# and ASP.NET, in his spare time he can still, often, be found writing PHP scripts or giving tips on IRC He can be contacted at p phpguy@theos.me.uk k
pre-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
Trang 23Over the past few years, web applications have
become increasingly complex This has led to
the increasing need to develop these
applica-tions with more maintainable, reusable, and extendible
designs Object oriented (OO) design patterns provide
elegant and refined solutions to these problems, but
they only apply to OO languages Until recently, for
OO-based web applications, the natural choice was
Java and C# Many developers adopted these
lan-guages to implement their code designs, and wherever
necessary, used design patterns to solve their coding
problems Along the way collaborating, sharing, and
publishing additional design patterns that were more
We’ve all heard about the benefits of OOP and that it
pro-vides the ability to have more reusable, maintainable, and
extendible code But with a great deal of PHP develepors
with a background in procedural programming, switching
to OOP may seem like an overwhelming task With the
help of a few OO design patterns, and by organizing your
code into layers, not only will you have the beginnings of
what OOP has to offer, you can more confidently develop
a piece of code by knowing its place and responsibilities in
the overall app.
Other Software PEAR::DB
Code Directory patterns
Trang 26specific to web applications Soon, server-side web
lan-guages that started off procedurally, incorporated
object oriented features Macromedia’s ColdFusion MX
now provides object-based support through Cold
Fusion Components (CFCs) PHP 5 refines its support
by providing additional class modifiers and keywords to
more fully support the properties of an object oriented
language We’re at a point now where web apps in PHP
can fully benefit from the catalogs of design patterns
that were once only available for J2EE web developers
Many web applications are faced with the same or
similar design issues, no matter which programming
language they are implemented in Design patterns put
a common name to a design solution Each design
pat-tern’s name (e.g Singleton, AbstractFactory, Adapter,
etc.) provides a common vocabulary that can be shared
and communicated when describing a class or a piece
of code This shared vocabulary would make it easier
for a developer to understand how the code works:
“Why does this class have a private constructor? Oh, it
says here it’s a Singleton I can probably assume then,
that only one instance of this class is used in the whole
application.”
Design Patterns
“Design Patterns describe the communication and
rela-tionship between objects that are customized to solve a
general design problem.” (From the famous book
Design Patterns: Elements of Reusable-Object Oriented
Software.) In most cases, design patterns are
docu-mented in a catalog A cataloged design pattern has
four essential elements:
• The pattern name
• A description of the problem that the tern solves
pat-• The solution that describes the elements thatmake up the design, their relationships,responsibilities, and collaborations The solu-tion is abstract enough that it can beapplied in many different situations
• The consequences describing the results andtrade-offs of applying the pattern Althoughthe pattern may look like a fit, you need toconsider the overall impact of introducingthis pattern into your system Patterns areonly beneficial if used appropriately
Consider the effects of flexibility,
extendibili-ty, and portability
If you are just moving from procedural to OO, thenlearning an existing Design Pattern catalog is quite a bit
to comprehend Start off by getting familiar with OOP.One of the best ways to learn OOP is to use alreadyavailable PHP classes within your app The PEAR library
is an extensive library of useful classes PHP 5 nowincludes a potentially powerful set of libraries namedthe Standard PHP Library (SPL) This library includes theIterator which is an implemented design pattern thatabstracts looping through different sets of data Thepoint is many of these classes have implemented adesign pattern in some way or another
As you become familiar with using a class from alibrary, try to determine if the class is using a designpattern—this may be mentioned in the comments.Look for the pattern in a pattern catalog, and read it
June 2005 PHP Architect www.phparch.com
Trang 27over You may discover the reason that the class
pro-vides certain methods, and perhaps understand the
reason why the class is implementing the pattern As
you become familiar with the pattern, the next time
you run into a class that implements it, it will be much
easier to understand how to use the class, how the class
behaves, and what to expect of it You will be able to
identify situations in your own application that may
benefit from that same design pattern Or better yet,
discover a new variant of it!
To aid you in this discovery process, we will go over
the BusinessObject (BO), the DataAccessObject (DAO),
the DataAccesGatewayObject (DAGO), and the
TransferObject These objects are used for the business
layer The overall design uses the
Model-View-FrontController This is a very common pattern used in
web application development In our example, we use
Model-View-FrontController for communication
between the presentation layer and the business layer
The general idea of this design comes from the
Model-View-Controller (MVC) Since its original inception in
Smalltalk, the MVC design pattern has been used in so
many different ways, under different contexts, that
there have become differing opinions and meanings of
what MVC really is The FrontController is a J2EE design
pattern for enterprise web applications As with a few of
the patterns in the J2EE design pattern catalog, it solves
a general problem for web applications independent of
the server-side scripting language being used There
are already a few presentation frameworks that
imple-ment this FrontController design in PHP We’ll discuss
further in the Layers section
The Layers
Figure 1 illustrates each object and where they lie in
each layer
The presentation layer is responsible for presenting
the model data and breaking the HTTP request
vari-ables into calls to the model layer This is part of the job
of a FrontController It resides in the presentation layer
The FrontController also drives page flow logic and
manages data and communication coming from and
going into the model It is also responsible for
compos-ing the pages that are used to display the data.Presentation (or Templating) frameworks such as Mach-
II, Fusebox, Smarty, or PHPTAL would make up thepresentation layer A couple of these templating frame-works such as Mach-II, and Fusebox already provide amechanism for the FrontController Fusebox 4, forinstance, provides controller logic with a combination
of XML files called cciirrccuuiittss xxmmll and ffuusseebbooxx xxmmll.Mach-II provides a similar approach with a mmaacchh iiii xxmmllfile
The model layer consists of the BusinessObject (BO),the DataAccessGatewayObject (DAGO), andDataAccessObject (DAO) The TransferObject is a sim-
ple object containing only getter and setter methodsand is used to encapsulate data being passed betweenthe presentation layer and the model layer An instance
of a TransferObject may be passed to a DAO, for sistence The BO, as it seems fit, may compose and pro-vide different types of TransferObjects to the presenta-tion layer for course grained views into the model The DAGO, and DAO are always in the back locatedclosest to the data source These objects contain func-tions used to access the data source e.g SQL state-ments, data from a web service, an RSS feed, etc Theseobjects encapsulate all the logic used to access the datasource This design improves maintainability by havingyour SQL statements in a central location as opposed tohaving them spread out throughout your code In addi-tion, it is located way back in the layers where it logi-cally belongs In some designs, a DAO is all that is usedfor accessing the data source For this design, we finetune the DAO’s responsibilities by pulling out its aggre-gate functions and putting them into a DAGO Thispattern is described as a gateway in the Mach-II devel-opment guide, even though it is described in the con-text of a Mach-II application, I’ve found that it solvesthe same general problem in our design with PHP Asfor most, if not all, situations when building web appli-cations, there are two types of operations being per-formed on persistent data (As stated in the Data Accesssection of the Mach-II Development Guide
per-h http://livedocs.macromedia.com/wtg/public/machiid e
• per-object access: creating, editing, working
catalogs of design patterns that were
Trang 28June 2005 PHP Architect www.phparch.com
5 * A News object used as a TransferObject A single
6 * instance of this object represents a single row in
7 * the News table.
22 public function setID ( $id ) { $this -> newsID = $id ; }
23 public function getID () { return $this -> newsID ; }
24
25 public function setPublishDate ( $pdate ) {
26 $this -> publishDate = $pdate ;
27 }
28 public function getPublishDate () {
29 return $this -> publishDate ;
35 public function getBody () {
36 return $this -> body ;
42 public function getHeading () {
43 return $this -> heading ;
49 public function getSubHeading () {
50 return $this -> subHeading ;
51 }
52
53 public function setAuthorFname ( $fname ) {
54 $this -> authorFname = $fname ;
55 }
56 public function getAuthorFname () {
57 return $this -> authorFname ;
58 }
59
60 public function setAuthorLname ( $lname ) {
61 $this -> authorLname = $lname ;
62 }
63 public function getAuthorLname () {
64 return $this -> authorLname ;
65 }
66
67 public function setAuthorEmail ( $email ) {
68 $this -> authorEmail = $email ;
69 }
70 public function getAuthorEmail () {
71 return $this -> authorEmail ;
72 }
73
74 public function getCategoryID () {
75 return $this -> categoryID ;
76 }
77 public function setCategoryID ( $catid ) {
78 $this -> categoryID = $catid ;
79 }
80
81 public static function fromArray ( $arr ) {
82 $news = new News ();
83 $news -> setId ( $arr [ “pk_NewsID” ]);
84 $news -> setHeading ( $arr [ “heading” ]);
85 $news -> setPublishDate ( $arr [ “publishDate” ]);
86 $news -> setSubHeading ( $arr [ “subHeading” ]);
87 $news -> setAuthorEmail ( $arr [ “authorEmail” ]);
88 $news -> setAuthorLname ( $arr [ “authorLname” ]);
89 $news -> setAuthorFname ( $arr [ “authorFname” ]);
90 $news -> setPublishDate ( $arr [ “publishDate” ]);
91 $news -> setBody ( $arr [ “body” ]);
92 $news -> setCategoryID ( $arr [ “categoryID” ]);
16 public function construct () {
17 $this -> newsDAO = NewsDAO :: getInstance ();
18 $this -> newsDAGO = NewsDAGO :: getInstance ();
19 }
20
21 /**
22 * Create a News item record.
23 * @param News news item to be inserted
24 */
25 public function create ( News $news ) {
26 $this -> newsDAO -> create ( $news );
27 }
28
29 /**
30 * Update the given News item record.
31 * @param News news item to be updated
32 */
33 public function update ( News $news ) {
34 $this -> newsDAO -> update ( $news );
35 }
36
37 /**
38 * Retrieve a News object with the given id.
39 * @param mixed newsid (string or integer)
40 */
41 public function read ( $newsid ) {
42 return $this -> newsDAO -> read ( $newsid );
48 public function delete ( $newsID ) {
49 $this -> newsDAO -> delete ( $newsID );
50 }
51
52 /**
53 * Retrieve all News items.
54 * @return Iterator of associative arrays where
55 * each array is a News item record.
56 */
57 public function findAllNews () {
58 return $this -> newsDAGO -> findAllNews ();
59 }
60
61 /**
62 * Retrieve all Categories
63 * @return Iterator of associative arrays where
64 * each array is a Category record.
65 */
66 public function findAllCategories () {
67 return $this -> newsDAGO -> findAllCategories ();
Trang 29in depth with a single row (object)
• aggregated access: reporting, searching,
list-ing multiple rows
Following the bullet points, the DAO is responsible
for performing per-object access It provides methods
for performing the usual CRUD operations (Create,
Read, Update, Delete) on a single record in the
data-base We use a TransferObject to encapsulate the data
being persisted This object is returned from or passed
to the CRUD methods
The DAGO is responsible for providing aggregate
68 $news -> setID ( $id );
69 $args = array( $news -> getID (),
70 $news -> getPublishDate (), $news -> getHeading (),
71 $news -> getSubHeading (), $news -> getBody (),
72 $news -> getAuthorFname (), $news -> getAuthorLname (),
73 $news -> getAuthorEmail (), $news -> getCategoryID ());
74 $this -> dbConn -> execute ( $this -> insertPST , $args );
75 }
76
77 /**
78 * Update the given News item record.
79 * @param News news item to be updated
80 */
81 public function update ( News $news ) {
82 //Lazy load the update prepared statement
83 if ( $this -> updatePST == null ) {
84 $sql = “UPDATE News SET publishDate=?,heading=?,
91 $args = array( $news -> getPublishDate (),
92 $news -> getHeading (), $news -> getSubHeading (),
93 $news -> getBody (), $news -> getAuthorFname (),
94 $news -> getAuthorLname (), $news -> getAuthorEmail (),
95 $news -> getCategoryID (), $news -> getID ());
96 $this -> dbConn -> execute ( $this -> updatePST , $args );
97 }
98
99 /**
100 * Retrieve a News object with the given id.
101 * @param mixed newsid (string or integer)
102 */
103 public function read ( $newsID ) {
104 //Lazy load the read prepared statement
105 if ( $this -> readPST == null ) {
106 $sql = “SELECT * FROM News WHERE pk_NewsID=?” ;
107 $this -> readPST = $this -> dbConn -> prepare ( $sql );
113 $result -> fetchRow ( DB_FETCHMODE_ASSOC )) {
114 $news = News :: fromArray ( $row );
123 public function delete ( $newsID ) {
124 if ( $this -> deletePST == null ) {
125 $sql = “DELETE FROM News WHERE pk_NewsID=?” ;
126 $this -> deletePST = $this -> dbConn -> prepare ( $sql );
128 $args = array( $newsID );
129 $this -> dbConn -> execute ( $this -> deletePST , $args );
10 // The single instance of the NewsDAO.
11 private static $thisInstance ;
12
13 // The connection to the database.
14 private $dbConn ;
15
16 // The reusable prepared statement
17 // for inserting a News record
18 private $insertPST = null ;
19
20 // The reusable prepared statement
21 // for updating a News record
22 private $updatePST = null ;
23
24 // The reusable prepared statement
25 // for reading a News record
26 private $readPST = null ;
27
28 // The reusable prepared statement
29 // for deleting a News record
30 private $deletePST = null ;
31
32 /**
33 * Private constructor to support the Singleton pattern.
34 */
35 private function construct () {
36 // Retrieve a database connection that is
37 // suited for the given DSN The AbstractFactory
38 // pattern is implemented behind the scenes in
46 public static function getInstance () {
47 if ( self :: $thisInstance == null ) {
48 self :: $thisInstance = new NewsDAO ();
54 * Create a News item record.
55 * @param News news item to be inserted
56 */
57 public function create ( News $news ) {
58 //Lazy load the insert prepared statement
59 if ( $this -> insertPST == null ) {
60 $sql = “INSERT INTO News
5 * Helper class that provides a single Connection
6 * for the whole request.
12 public static function getConnection () {
13 if ( self :: $dbConn == null ) {
14 self :: $dbConn = & DB :: connect ( DSN );
Trang 30access It provides methods such as
ffiinnddAAllllCCaatteeggoorriieess(()), ffiinnddAAllllNNeewwss(()), or
ffiinnddNNeewwssBByyCCaatteeggoorryyIIDD(()) Since the result set varies and
can possibly be a set of data from different sources, it is
not convenient to encapsulate each record’s data into a
TransferObject If we did, that would mean we would
have to code a different TransferObject for each varying
set of data So what is usually returned from a DAGO
method is an array of associative arrays, or an Iterator
The BO is the communication front between the
con-troller and the model layer It provides coarse grained
operations to the controller The BO uses the DAO, and
DAGO, but hides these objects and other
implementa-tion details from the controller The controller only
knows to communicate with the BO to access and
manipulate the model
The News Example
So, let’s take what we’ve discussed so far and put it into
a working example We’re going to model a news item.Here are the requirements: A news item will have aheading, a subheading, a body, a publish date, and anemail of the author Each news item must belong to acategory
The News class is a TransferObject that is passedbetween the controller and the NewsBO for CRUDoperations The News class defines the getters and set-ters (accessor methods) that map to its column values
in the News table A single instance represents a singlerow in the database See Listing 1 This type of object isanalogous to a bean in Java Because of its simplicity,some may argue that it’s not even needed Why notjust have it be an associative array? Since this object isinvolved in the data persistence logic, I’ve found thatdealing with an object keeps the system honest in thesense that the object has a type (see type hinting inListing 2 for the uuppddaattee(()), and ccrreeaattee(()) methods) ATransferObject represents a concrete piece of themodel, and provides a strict interface for manipulatingits properties With the help of type hinting, this forces
a contract between the model and the presentationlayer on what is used to communicate to each other.Also, by using a TransferObject, it sets you up for futurechanges, as data becomes more complex, and poten-tially finer- grained, you can provide a TransferObjectthat provides a more encapsulated view into themodel It may become an aggregate for other objectscontaining associated data So let’s say you run into asituation where your controller is making a lot of finegrained calls into the BO in order to retrieve little pieces
of the model that it will later use for display This maybecome a maintenance headache if somebody had to
go into the controller and see all of these calls into the
BO This would be a good point to refactor those finegrained calls, and put them into a single course grainedmethod in the BO The BO would then be responsiblefor retrieving the necessary data, and composing thatdata into a single TransferObject That TransferObjectwould encapsulate all of the little model pieces that can
be more easily managed by the controller and the play
dis-The NewsBO (BusinessObject) manages access to theNewsDAO (DataAccessObject) and the NewsDAGO(DataAccessGatewayObject) See Listing 2 For simpleapplications, such as this one, you will see that thereare often one-to-one calls between an exposed BOmethod and to the appropriate DAGO or DAO Youmay ask “why the extra layer?” There may be situationswhere additional fine-grained operations will need tohappen along with the calls to the DAO or DAGO Itcan be anything from processing an image upload, tothe management of session data, to the composition of
an aggregate TransferObject, to optimization/caching
June 2005 PHP Architect www.phparch.com
17 private function construct () {
18 // Retrieve a database connection that is suited
19 // for the given DSN The Factory pattern
20 // is implemented behind the scenes in the
28 public static function getInstance () {
29 if ( self :: $thisInstance == null ) {
30 self :: $thisInstance = new NewsDAGO ();
36 * Retrieve all Categories
37 * @return Iterator of associative arrays where
38 * each array is a Category record.
39 */
40 public function findAllCategories () {
41 $sql = “SELECT * FROM NewsCategory” ;
42 $result = $this -> dbConn -> query ( $sql );
43 return new ResultSetIterator ( $result );
44 }
45
46 /**
47 * Retrieve all News items.
48 * @return Iterator of associative arrays where
49 * each array is a News item record.
50 */
51 public function findAllNews () {
52 $sql = “SELECT * FROM News” ;
53 $result = $this -> dbConn -> query ( $sql );
54 return new ResultSetIterator ( $result );
Trang 31logic The BO is responsible for managing this, itself (ordelegating to other objects), and, since all these imple-mentation details are hidden from the controller itkeeps the model cohesive and loosely coupled from itspresentation layer.
The NewsDAO provides the CRUD operations for theNews object We are using type hinting (a PHP 5 addi-tion) with the method ccrreeaattee(()), and uuppddaattee(()) Typehinting is a runtime check to see if the argument beingpassed is an instance of the class specified in the func-tion’s parameter list If it isn’t, an error is generated.Type hinting may not work with native or primitivedata types (string, int, etc.), it is, however, a very usefulaid in narrowing the risk of possible bugs This isaccomplished by giving a visual cue to the developerusing the function that a particular instance of a class iswhat it expects to receive, and triggering an error if thefunction receives anything different This ensures thatthe correct object type is being passed, and locks downthe contract between the client and the NewsDAO.Hopefully some time in the future we will see type hint-ing for returned function values See Listing 3 TheNewsDAO and NewsDAGO classes are using thePEAR::DB database abstraction layer
Now, take a look at Listing 4 Since we are discussingdesign patterns, I should point out that the PEAR::DBpackage is able to support different RDMS through theFactory design pattern It may be even using anAbstractFactory pattern, but that can be an exercise foryou to find out The call to retrieve a database connec-tion through a static method called DDBB::::ffaaccttoorryy(())is ahint that the Factory pattern is being used
The Factory method is a creational design patternthat uses a specialized object for creating other objectswith different implementations that conform to a par-ticular interface, or abstraction Think of the specializedobject as a real-world factory that produces differentremote controls that all look the same They all havethe same volume buttons, and the same channel but-tons The difference is what they control (what is underthe cover) One may control a stereo, another may con-trol a television set The factory determines what to cre-ate based on a situation, or argument passed to it Ithides of all the creational logic from the client The fac-tory method is useful when the client does not knowhow to create what it needs during run time All it has
is knowledge of its interface The client just knows how
to operate what it’s given This type of data hidingallows the client to use an object through its interface,regardless of what is under the cover
The factory method is also very useful as a frameworkthat allows for pluggable implementations of a particu-lar interface If done correctly, this allows for developers
to provide an implementation of an interface and plug
it into the framework without modifying a piece of theframework code With the help of PHP 5’s Reflection
1 <?php
2 // ResultSetIterator.class.php
3 /**
4 * This is an example of the Adapter pattern We are
5 * using a DB_result object and adapting it to work as an
6 * Iterator The Iterator interface provides a generic
7 * look to the result set, thus hiding the implementation
8 * details of a DB_result from the client If for some
9 * reason the data source was to change from a relational
10 * database to an XML source via SOAP, we would just need
11 * to provide an Iterator implementation that loops
12 * through XML results The client looping through the
13 * data wouldn’t know the difference
29 public function destruct () {
30 // Free the resultset
31 $this -> resultSet -> free ();
45 // Retrieve the current row
46 return $this -> currRow ;
72 // Return true if we have more rows to go False
73 // if we’ve reached the end.
74 return ( $this -> rowIdx <= $this -> numRows );
Trang 32API, this type of pluggable framework can happen
fair-ly easifair-ly The Reflecion API allows a developer to
reverse-engineer classes, interfaces, functions and
methods as well as extensions Among the neat ways to
introspect a class, the Reflection API provides the
abili-ty to create an instance of a class with its class name as
a string So, back to the pluggable framework idea, that
string can be in a config file that the framework reads
to instantiate your pluggable class All that a third party
developer would need to do is to drop their pluggable
class in a predetermined location, and register it to the
framework by adding its class name to the config file
A Couple of Optimization Techniques
The NewsDAO has been coded with a couple resource
optimization techniques For one, it is implemented as
a Singleton The Singleton design pattern provides a
way to ensure that only one instance of a particular
class exists in the whole application The Singleton
design pattern is one of the easiest patterns to
imple-ment, however, in a multi-threaded system, it can
potentially be the most dangerous If a Singleton objectdoes not properly protect itself against concurrent statechanges, you will end up with an object in an invalidstate This can cause a potentially devastating effect onyour system The reasons we are implementing theNewsDAO as a singleton is that the NewsDAO is basi-cally a stateless object There is no state in it that thebusiness logic uses, and there doesn’t need to be And
so there is no real need for multiple instances of aNewsDAO to be allocated in a request, thus preventingfrom any unnecessary memory allocation
Another technique is lazy loading of prepared ments Assuming that prepared statements need to becompiled somewhere in the database abstraction layer,lazy loading defers the compilation and allocation ofprepared statements until the time they are being used
state-as opposed to creating all of the prepared statements
at once, in the constructor Lazy loading allows a oper to delay the allocation of resources until absolute-
devel-ly necessary So, for the instance variables ccrreeaatteePPSSTT,rreeaaddPPSSTT, uuppddaatteePPSSTT, and ddeelleetteePPSSTT prepared state-ments, they will not be allocated until their respectiveCRUD function is called, saving us a little memory andprocessing time Since these the prepared statementsare being set as instance variables of a Singleton object,they will be reused over and over without reallocationand recompilation
The NewsDAGO is responsible for providing gate methods for listing news items, searching, etc TheNewsDAGO—which can be seen in Listing 5—has beenimplemented as a Singleton and with lazy loading ofprepared statements for the same reasons as theNewsDAO
aggre-Aggregate methods of the NewsDAGO class return
an Iterator instance, another feature of PHP 5 TheIterator interface follows the design pattern conve-niently named “Iterator”
From the GoF book Design Patterns: Elements of
Reusable Object-Oriented Software: “The Iterator design
pattern provides a way to access the elements of anaggregate object sequentially without exposing itsunderlying representation It provides a generic inter-face for looping.” In PHP, Iterators are generic enough
to be used within the foreach loop
I’ve put together a simple implementation of theIterator interface The class is called ResultSetIterator,and can be seen in Listing 6 The Iterator interface ismeant mostly for use with the Adapter design pattern.This pattern uses inheritance to adapt one object orinterface to another In our case, the ResultSetIteratoradapts an instance of DB_result into an Iterator By hav-ing the NewsDAGO return an implementation ofIterator as a return value from its aggregate functions,the client on the other end calling these functionswould only be required to know how to use an Iterator
as opposed to a DB_result By introducing this small
7 // Create a news transfer object and
8 // set its properties
9 $news = new News ();
10 $news -> setHeading ( “A Heading” );
11 $news -> setSubHeading ( “A Sub Heading” );
12 $news -> setBody ( “A news body” );
13 $news -> setAuthorLname ( “Jenkins” );
14 $news -> setAuthorFname ( “Spock” );
15 $news -> setAuthorEmail ( “sjenkins@foo.com” );
16 $news -> setCategoryID ( “1” );
17 $news -> setPublishDate ( date ( “Y-m-d” ));
18
19 // Create a News business object
20 // to perform some operations on news
21 $bo = new NewsBO ();
22
23 // Create a new News record
24 $bo -> create ( $news );
25
26 // Retrieve a News bean and
27 // change some properties
28 $anotherNewsInstance = $bo -> read ( $news -> getID ());
29 $anotherNewsInstance -> setHeading ( “Changed heading” );
30 $anotherNewsInstance -> setBody ( “Changed body” );
31 $bo -> update ( $anotherNewsInstance );
32
33 // Retrieve all news items
34 $results = $bo -> findAllNews ();
35 foreach( $results as $row ) {
36 echo “row: “ $row [ “pk_NewsID” ] “<br />” ;
37 }
38
39 // Retrieve all categories
40 $results = $bo -> findAllCategories ();
41 foreach( $results as $row ) {
42 echo “category: “ $row [ “pk_NewsCategoryID” ] ”<br />” ;
43 }
44
45 // Retrieve all news items again, and delete them
46 $results = $bo -> findAllNews ();
47 foreach( $results as $row ) {
48 $bo -> delete ( $row [ “pk_NewsID” ]);
Trang 33piece into the design, we now have the ability to swap
out whole implementations of the NewsDAGO with
another without affecting the client code that uses the
NewsDAGO
Suppose we wanted to switch the datasource from a
relational database to a SOAP based datasource, or to
an RSS feed Say we use the SOAP alternative resulting
in the replacement of the NewsDAGO implementation
with a SOAPNewsDAGO implementation As long as
the SOAPNewsDAGO provides the same methods as
the original NewsDAGO, the client code would be left
unchanged The aggregate methods would still return
an Iterator, but in this case, we would have an Iterator
implementation that knows how to loop through an
XML result You may ask, “why use an Iterator? Why
not just return an array of all the records? It’s generic
enough, and everybody knows how to loop through
those.” We are using an Iterator for memory
optimiza-tion Dumping all the results into an array means that
there needs to be enough memory to hold that array If
the result set is fairly large, we may run into a memory
limit The ResultSetIterator fetches the rows as they are
being retrieved and does not store or cache them It
leaves it up to the programmer who is looping through
the data to decide on the necessity of saving all the
results into memory for further use
Now that we’re talking about swapping out whole
implementations of NewsDAGO, we just found a use
for the Factory pattern in our design Just like how
DDBB::::ccoonnnneecctt(())provides an abstracted DB connection of
the underlying RDBMS layer, depending on the DSN
being passed through, we can do the same for
NewsDAGO By redesigning the NewsDAGO
around the Factory pattern, the call to
NNeewwssDDAAGGOO::::ggeettIInnssttaannccee(()) would return a
SOAPNewsDAGO implementation or the usual
relation-al database implementation This can be decided by a
config setting telling which one to use We would have
to do a little refactoring on the back end, though We
would rename the relational database implemenation
of the NewsDAGO class to more specifically
RDBMSNewsDAGO, and would define an abstract class
more generically called NewsDAGO, with a set of determined aggregate functions e.g ffiinnddAAllllNNeewwss(()),
pre-ffiinnddAAllllCCaatteeggoorriieess(()), etc The RDMSNewsDAGO, andSOAPNewsDAGO would extend from the NewsDAGOabstract class and be forced to provide implementa-tions of the aggregate functions When the client callsthe static function, NNeewwssDDAAGGOO::::ggeettIInnssttaannccee(()), it would
decide on and return an instance of RDMSNewsDAGO
or SOAPNewsDAGO depending on a config setting.The client code using the NewsDAGO instances would-n’t have to change or care about which is being sup-plied We would, of course, have to apply the samedesign to the NewsDAO as well, we wouldn’t want to
be persisting data and retrieving aggregate data fromtwo different data sources in the same app
A Test Run
Since the model layer is decoupled from the tion, it can be tested independently of a presentationlayer, as you can see in Listing 7 We are just walkingthrough the NewsBO operations First, we instantiate aNewsTransferObject to prepare it for inserting, by set-ting its properties Then, we instantiate a NewsBO Wecreate the news record by calling $$bboo >>ccrreeaattee(($$nneewwss))
presenta-of the NewsBO instance
We then illustrate updating a News record in thedatasource First, we retrieve a News object from the
BO by passing in the unique id of the News record
$$aannootthheerrNNeewwssIInnssttaannccee == $$bboo >>rreeaadd(($$nneewwss >>ggeettIIDD(())))
We changed some of its properties
$$ aa nn oo tt hh ee rr NN ee ww ss II nn ss tt aa nn cc ee >> ss ee tt HH ee aa dd ii nn gg (( ““ CC hh aa nn gg ee dd hheeaaddiinngg””)) and $$aannootthheerrNNeewwssIInnssttaannccee
>>sseettBBooddyy((““CChhaannggeedd bbooddyy””)) then we call
relationship between objects that are customized
Trang 34$$bboo >>ffiinnddAAllllNNeewwss(()) We then loop through the data
echoing out the primary key
// Retrieve all news items
$results = $bo->findAllNews();
foreach($results as $row) {
echo “row: “.$row[“pk_NewsID”] “<br />”;
}
Remember, we are looping through an Iterator, by
sim-ply using a ffoorreeaacchh loop
Next, we loop through Category records in the same
The final block of code illustrates looping through and
deleting all News records
// Retrieve all news items again, and delete them.
$results = $bo->findAllNews();
foreach($results as $row) {
$bo->delete($row[“pk_NewsID”]);
}
You may have noticed that I’ve left a few things out,
such as the CRUD and aggregate functions for a
NewsCategory That task can be left to you The
NewsCategory is simple enough that it’s not necessary
to create a BO, DAO, and DAGO object for it
Since a NewsCategory isn’t much without News
items, you can add the appropriate functions to the
existing NewsBO and NewsDAO This is also
true if you wish to add more aggregate
functions to the NewsDAGO, such as
NNeewwssDDAAGGOO::::ffiinnddNNeewwssBByyCCaatteeggoorryyIIDD(())
We are also missing a boat load of error handling
PHP 5 now provides an OO approach to error handling
using Exceptions The Exception model allows
develop-ers to catch and throw Exceptions as they see fit When
an Exception is thrown, the code following the line
where it was thrown will not be executed If the
Exception is not caught, it will trickle all the way up
causing a PHP Fatal Error This mechanism allows thedeveloper to catch and handle particular types ofExceptions, thus giving them the ability to choose theappropriate course of action for that particular type.The meat of using Exceptions for error handling is that
it provides developers with the ability to define theirown errors by extending the Exception class Exceptiontypes can be specific to a related set of classes, thushelping out with the time it takes to hunt down a par-ticular error or bug An instance of Exception encapsu-lates enough information that allows the developer tofind the point of code where the error occurred.Exception handling can also be overused, making codemore difficult to read If used correctly, they can be apowerful aid in keeping your OO application rock solid
Conclusion
That’s an awful lot of code for persisting and listingnews items With OOP, that is mostly the case OOcode is more complex than procedural, but in the end,it’s easier to maintain because it’s better organized.There is no way around it You may have to think thingsthrough a little bit more when developing your webapp, but doing it right will save you maintenance time
in the future What we did, with the help of a fewdesign patterns, was re-organize the code in a way thatmakes it more manageable, maintainable, extendable,and re-usable We also acquainted ourselves to a fewfeatures in PHP 5, like Iterator and type hinting What
we have is a design with a model layer independent ofany presentation layer We can take any templatingframework and integrate the model into it
June 2005 PHP Architect www.phparch.com
F
FEEAATTUURREE
34
To Discuss this article:
http://forums.phparch.com/227
Ronel Sumibcay is a Senior Web Developer at Red Door Interactive His experience in er-side web development started off in 1998 with Java where he co-developed the first JSP parsing engine for the JRun Servlet Container He now enjoys working with PHP and “all
serv-of its object oriented goodness”.
An OO Layered Approach to Web Apps
dynamic web pages - german php.node
news scripts tutorials downloads books installation hints
www.dynamicwebpages.de
sex could not be better |