1. Trang chủ
  2. » Công Nghệ Thông Tin

CROSSING THE DIVIDE

68 247 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Crossing the Divide Object Persistence in PHP
Tác giả Marco Tabini
Năm xuất bản 2005
Định dạng
Số trang 68
Dung lượng 4,76 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 1

AN OO LAYERED APPROACH

TO WEB APPS

REFERENCES IN PHP

HOMO XAPIAN The Search for

Trang 4

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

EDDIITTOORRIIAALL

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 7

PHP-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 8

Whhaatt’’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 9

Oracle 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 10

How 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 11

require 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 13

sion 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 14

PHP 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 15

Persistence 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 16

dependencies): 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 17

stored 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 18

June 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 19

details 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 20

but 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 21

versions 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 23

Over 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 26

specific 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 27

over 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 28

June 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 29

in 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 30

access 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 31

logic 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 32

API, 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 33

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

Ngày đăng: 16/10/2013, 16:15

TỪ KHÓA LIÊN QUAN

w