Tài liệu Agile software development with PHPUnit doc - Pdf 90

The WURFL projectThe WURFL project
Implementing search with Lucene
Integrating a Java search engine
API into your PHP site
Extreme cross-platform WAP development with PHP
Getting a grip on Getting a grip on LDAPLDAP
A coder's introduction to working with
directory services
Plus:
Tips & Tricks, Product Reviews and much more...
www.phparch.com
JUNE 2003
The Magazine For PHP Professionals
Agile software development with PHPUnitAgile software development with PHPUnit
Industrial strength MVC
Building a reusable development
framework with Open Source tools
PHP Unit TestingPHP Unit Testing
Use OOP to manage your formsUse OOP to manage your forms
Implementing object-oriented form
libraries that promote uniformity and
reusability
VOLUME II - ISSUE 6
2
This is the LAST month to submit your project for approval.
The deadline is June 18th! Hurry and submit your proposal.
As PHP’s importance grows on the IT
scene—something that is happening every
day—it’s clear that its true capabilities go
well beyond what it’s being used for
today. The PHP platform itself has a lot of

position in the IT world.
In order to be eligible for the Grant
Program, a project must be strictly related
to PHP, but not necessarily written in PHP.
For example, a new PHP extension written
in C, or a new program in any language
that lends itself to using PHP in new and
interesting ways would also be acceptable.
The only other important restriction is that
the project must be released under either
the LGPL, the GPL or the PHP/Zend
license. Thus, commercial products are
not eligible.
Submit Your Project Today!
Visit http://www
.phparch.com/grant
for more information
Introducing
the php|architect Grant
Program
IINNDDEEXX
3
Departments
php|architect
Features
9 Industrial Strength MVC
Building a reusable development frame-
work with open source tools
By Jason E. Sweat
24 Agile Software Development With

Technologies Ltd.
Visit
www.zend.com
for evaluation version and ROI calculator
Zend Performance Suite
Reliable Performance Management for PHP
Serve More.
With Less.
The designers of PHP offer you the full spectrum of PHP solutionsThe designers of PHP offer you the full spectrum of PHP solutions
EEDDIITTOORRIIAALL
5
EEDDIITTOORRIIAALL RRAANNTTSS
php|architect
Volume II - Issue 6
June, 2003
Publisher
Marco Tabini
Editor-in-Chief
Brian K. Jones
brian@phpar
ch.com
Editorial Team
Arbi Arzoumani
Brian Jones
Peter James
Marco Tabini
Graphics & Layout
Arbi Arzoumani
Administration
Emanuela Corso

because it would only give them public-
ity. However, there is a certain develop-
er’s website which fails to list PHP with-
out MySQL by its side. It has ‘PHP &
MySQL Tips and Tutorials’, ‘PHP &
MySQL Apps and Reviews’, and a couple
of other departments devoted to the
PHP/MySQL ‘platform’, without a single
hint that PHP can be used in other ways.
In addition, documentation for a certain
large company’s commercial IDE also
refers to this popular duo as a ‘platform’.
Probably the most surprising offenders in
the perpetuation of this stereotype are
the PHP conference organizers. MySQL
plays such a prominent role in the talks
and tutorials at PHP conferences that, if
you go to one with no experience in
PHP, you would leave thinking that PHP
is primarily an interface to the MySQL
database!
This is unfortunate, to put it lightly.
Truthfully, it disgusts me. Unfortunately,
there are a number of conditions which
currently exist in the world of PHP that
could arguably be used to justify the
actions of these groups. For example,
have you done a search for ‘PHP MySQL’
at Amazon lately? I did this recently and
EDITORIAL

might think that MySQL is the only
choice they have when it comes to using
PHP for their development needs.
PHP has native support for Sybase,
Oracle, DB2, Informix, MS SQL Server,
and other databases (yes, there are other
databases). Aside from databases, PHP
has native support for alternative sources
of data such as SNMP agents and LDAP
directories. In short, PHP is a very capa-
ble development platform without the
help of MySQL.
In this month’s issue of php|architect,
you can capture a glimpse of a couple of
these different data sources in action. As
promised, I’ve written a coder’s overview
of using PHP with LDAP. It’s a very high-
level, gentle discussion, light on code
and long on cold, hard facts you’ll need
to know if a client’s environment ever
forces you to code against an LDAP
directory. In addition, Jason Sweat
returns this month with a look at using
different ‘ready made’ open source tools
and frameworks to lighten the load on
enterprise application developers. In the
article, you’ll be able to get a feel for the
kinds of things you can do with some of
the other databases out there (Jason’s
article uses PostgreSQL, in particular).

most advanced MySQL credential -- to demonstrate
strong proficiency in working with the MySQL data-
base.
Through the MySQL certification program, MySQL
software developers can earn one or more formal cre-
dentials that validate their knowledge, experience and
skill with the MySQL database and related MySQL AB
products. This program now includes two certifications:
MySQL Core Certification and MySQL Professional
Certification.
The MySQL Core Certification provides MySQL users
with a formal credential that demonstrates proficiency
in SQL, data entry and maintenance, data extraction for
reporting and more. The MySQL Professional
Certification is for the more experienced MySQL user
who wants to certify his or her knowledge in MySQL
database management, installation, security, disaster
prevention and optimization. For more information,
visit MySQL.com
.
Pear Info Package
Pear announced the release of a new info package.
This package generates a comprehensive information
page for your current PEAR install. The format for the
page is similar to that for phpinfo() except using
PEAR colors. The output has complete
PEAR Credits (based on the packages you
have installed) and will show if there is a
newer version than the one presently
installed (and what it's state is). Each

FEATURES
June 2003 · PHP Architect · www.phparch.com
9
Introduction
This article assumes that you have either read the afore-
mentioned “An Introduction to MVC Using PHP” article,
or that you are already somewhat familiar with the
MVC pattern, OO programming in PHP, and have at
least looked at the Phrame examples. The previous
article highlighted proper use of the MVC pattern, with
business logic in the Model classes, presentation logic
in the View classes, and application flow directed by the
Controller classes (ActionController, Action,
ActionForms and ActionForwards in Phrame). Where
the previous article only stored data in the session, this
article steps it up a notch towards the “real world” by
making extensive use of a database.
The Application
To give this article a little more “real world” flavor, I
would like to start with a hypothetical set of require-
ments for the application. The application is a manage-
ment system for hyperlinks. The people who commis-
sioned the application have identified three key sets of
requirements: users, administrators and infrastructure.
User:
• The user will access this application as a web
site
• The list of links will be organized into groups
• The main link list will contain all of the links
in the application on a single page, so they

O/S: Any
Database: PostgreSQL 7.3
Additional Software: Phrame, ADOdb, Smarty, Eclipse
REQUIREMENTS
FEATURES
June 2003 · PHP Architect · www.phparch.com
10
Industrial Strength MVC
• Security should be in place to prevent unau-
thorized users from becoming the applica-
tion administrator, or from using the admin-
istrative functions without being authorized
as the administrator.
Infrastructure
• Security is a serious concern, in particular,
and credentials used by PHP scripts to access
the database should have the bare minimum
rights required to perform the tasks required
(in case the web site code is ever compro-
mised)
• The application needs to be “future proof”,
specifically this web application might not
be the only client and/or the only
source/editor for links in this application
• This application will transition to other
resources for maintenance, so it is important
that it is well structured for both flexibility
and ease of maintenance
• The application should be designed so
HTML designers can alter the appearance of

it is appropriate to take a look at the overall infrastruc-
ture first, which dovetails into Models. Next, reviewing
how the Controller (Phrame) implements application
flow in this application, and lastly how the views are
implemented in this application.
Infrastructure
The first decision is what language the web application
will be constructed in. Since this is a magazine devot-
ed to PHP, I think that is an appropriate choice ;) The
choice of PHP allows us to pull out our now familiar bag
PHP, mySQL and Curl Optimized for OSCommerce
Free Shared Certificate & Integrated SSL Server
20+ Contributions Pre-Installed on MS1 Release
Web Mail and Web Based File Manager
Full FTP and phpMyAdmin Access
Free Ongoing Hands-On Support
Web Stats by Urchin Reports
Free Installation and Configuration
USE PROMO CODE: phpa
Get an Extended Free Trial and Free Setup!
As the publishers of Ian's Loaded Snapshot we
know OSCommerce!
100's of OSCommerce powered sites rely on our
years of experience with OSCommerce, direct
866-994-7377 or [email protected]
www.chainreactionweb.com
www.chainreactionweb.com/reseller.
As the publishers of Ian's Loaded Snapshot we
know OSCommerce!
100's of OSCommerce powered sites rely on our

er the other application ends up being developed in.
This could also lead to possible differences in the
implementation logic, and would be overall a bad
design choice.
Fortunately, there is an alternative available to us:
code the business logic for the system into the data-
base itself. This means the business rules are imple-
mented in a single location, accessible by any applica-
tions needing to view or modify this data. By making
data available to client applications only through
views, and modifications to the data only performed
using stored procedures, we can also address some of
the security requirements.
For long term maintenance, performance and flexi-
bility, we will implement ADOdb for a database
abstraction layer. Continuing to build on what we
learned regarding the Phrame implementation of an
MVC in PHP, we will again use Phrame for this exam-
ple application.
We can implement several of the “look and feel”
design requirements by adoption Smarty templates is
our views, and having a common “header” and “foot-
er” template inclusion for common elements. These
details will be covered further in the section of the arti-
cle dealing with Views.
With all of these infrastructure decisions in place, we
can now visualize our application as a “stack” of tech-
nologies. Viewed from this perspective, our applica-
tion can be depicted as illustrated in figure 1.
This figure is useful to help enforce some of the con-

Model classes are also where you can implement data
validation, error handling, and other rules of your busi-
ness logic.
The application
flow is directed by
the Controller,
which coinciden-
tally is the middle
of our application
stack. This project
is implemented in
Phrame, but you
could substitute
any of the other
projects men-
tioned in the prior
article, or roll your
own Controller.
The chief role of the controller is to delegate the user’s
choice of actions to the appropriate Models or Views. In
this application, we have also implemented security at
this level, as each restricted action requires validation
that the user is in fact an administrator prior to per-
forming the action.
Views then perform the task of interacting with the
application Models to extract the data required for the
user. Views may need to transform this data in order to
make it fit with the presentation technology used in
your application. For this example, we continue to
make use of the Smarty template engine, as we did in

syntax, there are a few nuances to pay attention to
here. First of all the link_group_id field is declared as
type serial with a constraint of PRIMARY KEY. The seri-
al type is a shortcut for creating a sequence in the data-
base, and selecting the next value from the sequence as
the default to populate the field when performing and
insert operation. The PRIMARY KEY constraint enforces
that the field must be unique and not null. The next
DROP TABLE link_group CASCADE;
CREATE TABLE link_group
(
link_group_id serial PRIMARY KEY,
group_name varchar(50) UNIQUE NOT NULL,
group_desc varchar(255)NULL,
group_ord integer NULL,
date_crtd timestamp(0)
with time zone DEFAULT CURRENT_TIMESTAMP,
date_last_chngd timestamp(0)
with time zone DEFAULT CURRENT_TIMESTAMP
);
GRANT ALL ON link_group TO GROUP links_admin;
GRANT ALL ON link_group_link_group_id_seq TO GROUP
links_admin;
NOTE: To prepare your Postgres db for this
example, login as linkdbo to the links database and
run these scripts from the code bundle in the fol-
lowing order:
link_group_ddl.sql
link_ddl.sql
link_views_ddl.sql

on.
In the links table, a new type of constraint is intro-
duced: REFERENCES. This constraint is how Postgres
implements referential integrity. In this case, we have
specified that this field will match the primary key from
the link_group table. With just this portion of the con-
straint alone, you will never be able to insert rows into
the link table without an appropriate value for the
link_group_fk (fk stands for foreign key). We have also
qualified this constraint to further clarify the expected
behavior of this relationship. ON UPDATE CASCADE
means if the link_group_id changed for any reason on
a row in the link_group table that was referenced in the
link table, all of the associated links would also change
(we have no intention of doing this in the application,
but it does not hurt us either). ON DELETE NO
ACTION means that the database will prevent any SQL
statement that tries to delete a row from link_group
that is referenced by one or more links from happen-
ing. Herein lies the power of referential integrity: the
database is doing housekeeping for us. As PHP pro-
grammers, we can focus on manipulation and presen-
tation of the data without worrying about corrupting
the data model with our SQL statements.
Security on the link table, as with the link_group
table, grants no SELECT privileges to the links_user
group. How is it that we will be able to query the data-
base for this data? The answer is views, which are basi-
cally a pre-defined SELECT statement that appears as if
it were another table of data you can query. The fol-

END;
‘ LANGUAGE ‘plpgsql’;
CREATE TRIGGER link_group_upd
BEFORE UPDATE
ON link_group
FOR EACH ROW EXECUTE PROCEDURE trig_upd_dates();
DROP VIEW groups;
CREATE VIEW groups AS
SELECT
lg.link_group_id
,lg.group_name
,lg.group_desc
,lg.group_ord
,count(l.link_id) AS link_cnt
,max(l.date_crtd) AS link_add
,max(l.date_last_chngd) AS link_upd
FROM link_group lg
LEFT JOIN link l
ON (lg.link_group_id = l.link_group_fk)
GROUP BY
lg.link_group_id
,lg.group_name
,lg.group_desc
,lg.group_ord
ORDER BY
lg.group_ord;
GRANT ALL ON groups TO GROUP links_admin;
GRANT SELECT ON groups to GROUP links_user;
DROP TABLE link CASCADE;
CREATE TABLE link

This one is going to take some explanation, so here
we go! First of all, near the bottom we see LANGUAGE
‘plpgsql’, so the language this function is written in is
plpgsql. This procedural SQL language is distributed
with Postgres, and you can read the Postgres documen-
tation for instructions on how to install and use plpgsql
(http://www
.postgresql.org/docs/view.php?ver-
sion=7.3&idoc=1&file=plpgsql.html).
The CREATE FUNCTION statement defines the name
of the function and the parameters it takes (Postgres
supports function overloading-multiple functions with
the same name but different input parameters-but
don’t worry, I didn’t use any), in this case, the function
accepts two integer values as parameters.
In the DECLARE section, we can create local variables
we want to use (Postgres is statically typed, so unlike
PHP, you must declare a variable and its type prior to
use). We can also create more useful names for the
input parameters than “$1” as evidenced by the use of
ALIAS. By looking at the DECLARE section we can see
that the first integer parameter is the id of the link we
want to change, and the second is the id of the group
we want to change the link to.
The rest of the func-
tion is in the statement
delimited by BEGIN and
END. The first step in
our function is to vali-
date the link requested

linkrec link%ROWTYPE;
grouprec link_group%ROWTYPE;
BEGIN
SELECT INTO linkrec * FROM link WHERE link_id =
ch_link_id;
IF FOUND THEN
IF linkrec.link_group_fk = ch_group_id THEN
RAISE NOTICE ‘’link % is already in group
%’’,ch_link_id,ch_group_id;
RETURN 0;
END IF;
SELECT INTO grouprec * FROM link_group WHERE
link_group_id = ch_group_id;
IF FOUND THEN
SELECT INTO max_ord count(1) FROM link WHERE
link_group_fk = linkrec.link_group_fk;
IF linkrec.link_ord < max_ord THEN
PERFORM ord_link(ch_link_id, max_ord);
END IF;
SELECT INTO max_ord count(1) FROM link WHERE
link_group_fk = ch_group_id;
UPDATE link
SET link_group_fk = ch_group_id
,link_ord = max_ord + 1
WHERE link_id = ch_link_id;
RETURN 1;
ELSE
RAISE EXCEPTION ‘’no group with id %
found’’,ch_group_id;
RETURN 0;

Industrial Strength MVC
exists, the next thing we check is if we are asking to
change the group to an identical value. If so, we RAISE
NOTICE, that we were asked to essentially do nothing,
and RETURN 0. Because we raised a NOTICE, a result
set will still be returned as a result of this function call.
Assuming the link group we are changing to is not
the same as the existing link’s group, the next step is to
validate that the requested link group we want to
change to exists. This is performed in a similar fashion
to checking for the existence of the link. If we do not
find the link, it is time to RAISE EXCEPTION again.
Since that all checks out, we are almost ready to
update. But first, we need to see if this link is the last
link in it’s group. If not, we move it to the end (using
another function we have already defined - ord_link).
This is done so that the sequence of links within a
group does not get a gap in it. We will also determine
the end position in the new group, so we can position
the link there. Then we perform the actual UPDATE
statement, and RETURN 1, indicating success.
This may all look like a lot of work - is it really worth
it? Remember this is all part of the requirements for
data integrity in our project. Consider this from the
perspective of coding in a PHP script. Would you want
to code and run all of the above logic in PHP, or simply
execute SELECT chgrp_link(1,2)?
The last line is SECURITY DEFINER, which specifies the
function should run with the security of linkdbo, rather
than the user executing the function. This is what

groups
View providing details of link
groups, including summary
data regarding associated
links.
Data Manipulation
Function/Description Parameters
add_link_group
adds a new link group to
the database
Varchar - group name
Varchar - group description
upd_link_group
updates an existing link
group
Integer - link group id
Varchar - group name
Varchar - group description
del_link_group
remove an existing link
group
Integer - link group id
ord_link_group
change the sequence of
an existing link group
Integer - link group id
Integer - new order
sequence
add_link
add a new link to the

Integer - link id
Integer - new link group id
Table 1: Database API Functions
FEATURES
June 2003 · PHP Architect · www.phparch.com
16
Industrial Strength MVC
we will have a model for Errors, similar to the previous
article. An excerpt from Groups.php can be found in
Listing 1.
I like to create constants for the SQL statements I
intend to use in the class. To avoid potential name
space conflicts, I generally prefix the constants with the
name of the class (or an abbreviation of the class name
if it is long). The heredoc syntax is used for additional
clarity on the the multi-line SQL statements. One item
to consider is how to deal with values that change at
runtime (substituting values like dates or ID fields into
the statement). How can you make a constant flexible
enough to handle this? There are two easy approach-
es I have used: format the constant for processing with
[sprintf()], or user ADOdb bind variables. The latter
method is shown in the example code and described
below.
Next we define the Model class itself.
Groups::GetInfo() and Groups::Add() are rep-
resentative examples of model methods. Each uses a
global ADOdb connection object. This database con-
nection is established in the application setup file
(links_setup.php). Groups::GetInfo() next

hash value.
The User::ValidateAdmin() method makes use
of the IsAdmin() method to check the current user. If
the user is not an administrator, then we trigger an
error and redirect to a safe location in our application.
This method can now be used anywhere in our appli-
cation where this type of validation is necessary.
The User::SetAdmin() essentially implements a
password check. If the correct password is passed to
this method, it will drop a cookie with the correct name
and value to pass the IsAdmin() method checks. This
method, coupled with the AdminLogin Action, allows
us to have a “backdoor” entry into the
system as an administrator using a url like:
define('GROUPS_INFO_SQL', <<<EOS
SELECT *
FROM groups
WHERE link_cnt > 0
EOS
);

define('GROUPS_DETAIL_SQL', <<<EOS
SELECT *
FROM groups
EOS
);

define('GROUPS_ADD_SQL',
'SELECT add_link_group(?, ?)');


return true;
} else {
trigger_error(DB_OOPS."\n"
.$go_conn->ErrorMsg());
return false;
}
}

// ... additional functions defined
}
Listing 1: Excerpt from Groups.php
FEATURES
June 2003 · PHP Architect · www.phparch.com
17
Industrial Strength MVC
http://example.org/links/links.php?action=AdminLogin&pw
=letMeIn. You could also code in a login page and use
the posted password to pass to this method.
Controller
This application makes use of Phrame, and is there-
fore, in many respects, very similar to the example pre-
sented in the previous article. One main difference is
the implementation of a “default action”. In my expe-
rience, I have found it to be the case that if no explicit
action is specified, then the “default action” to show a
view is implied. The revised bootstrap file (links.php)
reflects this (Listing 3).
Our revised bootstrap file is now down to four active
lines of code. require_once
‘links_setup.php’; includes the libraries, estab-

client anyway.
The next mapping shows an example of an action
with multiple forwards. The first, “index” has no for-
ward path specified, so it will use the mapping default
of APPL_BASE.’index’. The second, “edit”, speci-
fies APPL_BASE.’groupedit’ as the forward path.
<?
define('USER_LOCAL_SUBNET', '192.168.10.');
define('USER_LOCAL_HOST', '127.0.0.1');
define('USER_ADMIN_PASS', 'letMeIn');
define('USER_ADMIN_VAL',
md5('links application administrator'));
define('USER_ADMIN_COOKIE', 'c_links_admin');

class User
{
function IsAdmin()
{
$s_user_ip = $_SERVER['REMOTE_ADDR'];
$b_admin = false;
if ( USER_LOCAL_SUBNET == substr($s_user_ip,
0,strlen(USER_LOCAL_SUBNET))
|| USER_LOCAL_HOST == substr($s_user_ip,0,
strlen(USER_LOCAL_HOST))
|| (array_key_exists(USER_ADMIN_COOKIE,
$_COOKIE)
&& USER_ADMIN_VAL ==
$_COOKIE[USER_ADMIN_COOKIE])
) {
$b_admin = true;

}
}
?>
Listing 2: User.php
//application setup
require_once 'links_setup.php';

//set default action if none specified
if (!array_key_exists(_ACTION, $_REQUEST)) {
$_REQUEST[_ACTION] = 'ShowView';
}

//create Phrame controller
$go_controller = new ActionController(
$go_map->GetOptions());

//release control to controller for further
//processing
$go_controller->Process($go_map->GetMappings(),
$_REQUEST);
Listing 3: Revised bootstrap file (Links.php).
FEATURES
June 2003 · PHP Architect · www.phparch.com
18
Industrial Strength MVC
These are used in the action based on success or failure
of the login action. On success, you would forward to
“edit” and allow the administrator to edit the applica-
tion, otherwise, you should just forward the user to the
index page with an error message indicating the failed

$this->_AddForm('updlinks', 'LinkForm');

//default action to show views
// no forwards are required becuase this action displays HTML pages
$this->_AddMapping('ShowView', 'ShowViewAction', APPL_ACTN, 'links');
//admin login action
$this->_AddMapping('AdminLogin', 'LoginAction', APPL_BASE.'index', 'links');
$this->_AddForward('AdminLogin', 'index');
$this->_AddForward('AdminLogin', 'edit', APPL_BASE.'groupedit');
//group edit actions
$this->_AddMapping('AddGroup', 'AddGroupAction', APPL_BASE.'groupedit', 'links');
$this->_AddForward('AddGroup', 'edit');
$this->_AddMapping('UpdGroup', 'UpdGroupAction', APPL_BASE.'groupedit', 'updgroup');
$this->_AddForward('UpdGroup', 'edit');
$this->_AddMapping('OrdGroup', 'OrdGroupAction', APPL_BASE.'groupedit', 'links');
$this->_AddForward('OrdGroup', 'edit');
$this->_AddMapping('DelGroup', 'DelGroupAction', APPL_BASE.'groupedit', 'links');
$this->_AddForward('DelGroup', 'edit');
//link edit actions
$this->_AddMapping('AddLink', 'AddLinkAction', APPL_BASE.'linkedit', 'links');
$this->_AddForward('AddLink', 'edit');
$this->_AddMapping('UpdLink', 'UpdLinkAction', APPL_BASE.'linkedit', 'updlinks');
$this->_AddForward('UpdLink', 'edit');
$this->_AddMapping('OrdLink', 'OrdLinkAction', APPL_BASE.'linkedit', 'links');
$this->_AddForward('OrdLink', 'edit');
$this->_AddMapping('DelLink', 'DelLinkAction', APPL_BASE.'linkedit', 'links');
$this->_AddForward('DelLink', 'edit');
}
Listing 4: Action class dealing with views.
function &Perform(&$poActionMapping,

(code/app/GroupForm.php) and UpdGroupAction
(code/app/UpdGroupAction.php) classes in
Listing 6.
Instead of a single hidden input for group_id, you will
instead make a hidden input array that is populated
with all of the group_id’s for the table as you iterate
over them in the edit view. Instead of having an input
like <input type=”text” name=”group_name”
value=”first old group name”> you will instead
code the group_id into the name for all of
the input fields: <input type=”text”
name=”group_name1” value=”first old group
name”>. All other inputs will be named similarly.
You will want to create an easy way to iterate over
these inputs in your action, and use a model class
update method for each of the different groups posted.
The Phrame controller will “load” your form class with
the $_REQUEST array. It does this using the
ActionForm::PutAll() method. This is the method
overridden in the GroupForm class above, in which a
Phrame ArrayList object is created and stored in the
GroupFrom class. This ArrayList is created in the
PutAll() method and a Phrame ListIterator is
retrieved using the GetList() method.
You can see this ListIterator being used in the ‘while’
statement in the UpdGroupAction::Perform()
method. While the ListIterator still has values, we
class GroupForm extends ActionForm
{
var $_moUpdList;

{
User::ValidateAdmin('You must be an administrator to Update Groups');

$o_group =& new Groups;
$o_list = $poActionForm->GetList();

while ($o_list->HasNext()) {
$a_vals = $o_list->Next();
$o_group->Update($a_vals);
}

if (!$o_group->IsChanged()) {
appl_error('Please change a value before updating.');
}

$o_action_forward =& $poActionMapping->Get('edit');
return $o_action_forward;
}
}
Listing 6: “table at a time” editing classes.
FEATURES
June 2003 · PHP Architect · www.phparch.com
20
Industrial Strength MVC
extract the next value as $a_vals and use this array of
values as a parameter to the Groups::Update()
method. It is important to note that we can not access
this method statically, because we are tracking in a class
variable whether any of these updates actually changed
the database. This is checked in the statement if

method is User::ValidateAdmin(‘You
must be an administrator to Update
Groups’);. This statement will trigger an error
message and redirect to a public view if the user
is not an administrator. You can be confident that
any code after this statement will only be used by
the administrator of the application. Any actions
you want similarly secured should contain a call to
User::ValidateAdmin() as the first line of
your Perform() method.
function &Perform(&$poActionMapping, &$poActionForm)
global $gb_debug;

$o_view_factory =& new LinksViewFactory;
$o_smarty =& new Smarty;
$o_smarty->autoload_filters = array(//'pre' => array('trim', 'stamp'),
'output' => array('trimwhitespace'));

$s_view = strtolower($poActionForm->Get('view'));
$o_view =& $o_view_factory->Build($s_view);
$o_view->Init($o_smarty, $poActionForm);

//security check
switch (get_class($o_view)) {
case 'indexview':
case 'listview':
$b_restricted = false;
break;
default:
$b_restricted = true;

default to your “index” or “main” view. The only other
assumption made by the View Factory is that the sub-
class is defined in a file in the views subdirectory, with
the class name and the php extension. The
_GetViewClass() method for LinksViewFactory is
shown in Listing 8.
How do the view subclasses work? The
View::Init() method take the Smarty object and
the ActionForm object, both by reference, and assigns
them to class vars. This is important, especially in the
case of Smarty, because assignments made to the
Smarty object after initialization are still present in the
$this->_moTpl var when used later in the
View::Render() method. The Render() method
calls a Prepare() method (where each subclass will
assign view specific data), then handles errors, and dis-
plays the subclasses Smarty template.
There are only two things to do for each subclass of
View to make another view for your application: assign
the template to the $_msTemplate var, and imple-
ment a Prepare() method. Listing 9 is a sample view
class for the groupedit view.
You might want to take a look at how the templates
in this application are organized. Each view-specific
template calls {include file=”header.tpl”} as
the first statement and {include
file=”footer.tpl”} as the final statement. These
give the site the common “look and feel” with the
header.tpl handling the site title and errors, and the
footer.tpl handling the timestamp, navigation and

function _GetViewClass($psView)
{
switch(strtolower($psView)) {
case 'list':
$s_ret = 'ListView';
break;
case 'groupedit':
$s_ret = 'GroupEditView';
break;
case 'linkedit':
$s_ret = 'LinkEditView';
break;
case 'index':
default:
$s_ret = 'IndexView';
}
return $s_ret;
}
}
Listing 8
define('GROUPEDIT_VIEW_TEMPLATE', 'groupedit.tpl');
require_once 'models/Groups.php';
require_once 'models/Links.php';

class GroupEditView extends View
{
var $_msTemplate = GROUPEDIT_VIEW_TEMPLATE;

function Prepare()
{

al variable $gb_debug is can be set to
(strpos($_SERVER[‘SCRIPT_FILENAME’],
‘public_html’)>0) ? true : false; to
dynamically detect if the script is running from a user’s
public web directory (a sign the script is in develop-
ment in my environment). The same variable can be
coded to false to simulate the production environment.
All debugging outputs should be conditional on this
boolean, i.e. if ($gb_debug) {
var_dump($foo); }.
Another very simple means of viewing the state of
variables in your system is to trigger the
appl_error() function by hand. If you want to see
the state of a simple variable (number or string), you
can write something like if ($gb_debug)
appl_error(‘foo=’$foo);. This technique is use-
ful because the message shows up in a conveniant loca-
tion (the application error box) and the information
can be captured in the processing of an
Action::Perform() method and displayed after the
forward to the appropriate view.
Sometimes you may want to dump a larger variable,
for example, one of the data arrays you retrieve from a
model. These can sometimes be hard to look at in the
error box, so an alternative is to assign the
var_export($array, true); value to a template
variable named test, and then in the footer.tpl, detect if
we are in debugging mode and output
<pre>{$test}</pre>. At this same point, I often
enable the Smarty debugging console. I recommend

ate a nicer looking layout (perhaps with some CSS posi-
tioning).
You might want to extend the groups data model to
include an image source for a more graphical flair to
the list. In this case, you are altering something pretty
fundamental to the application, so you would need
make sure you hit all the blocks in the application stack
where it is affected: alter the link_group table, add
img_src to the add_group and upd_group plpgsql
functions, add the column to the groups views so the
PHP database user can query the data, the Groups Add
and Update methods to handle processing of the new
field, to the Add and Update actions to process and
add to the groupedit.tpl forms so we pass the value.
Lastly, add the img tag to the links.tpl file to display for
the user.
This might sound like you are altering a significant
portion of the system, but remember that your code is
now well organized into compact function oriented
blocks: you need to store the data somewhere, you
need to be able to securely access and modify the data,
you need to be able to edit the data as an administra-
tor and you need to retrieve and display the data for
the user. Each of these tasks now has a location within
your framework, and you can make a modification like
this, which essentially amounts to a new application
requirement, without breaking any of the previously
implemented functionality and requirements.
What else could be altered? You might want to cre-
ate a “link popularity” feature, i.e. Measuring the num-

templates. Once familiar with this kind of application
architecture, you can deploy effective web applications
by writing rock-solid Model classes, Action::Process(),
View::Prepare() and Smarty templates. Deploying MVC
based PHP applications addresses many common func-
tional requirements: robust, flexible, maintainable,
secure.
These two articles and the example code provided
have been a whirlwind tour of PHP features, some cov-
ered in depth and others just mentioned or touched on
briefly (or even assumed). Here is a selection of some
of the PHP features, functions and concepts we have
applied in this article and example:
• the MVC design pattern
• practicing separation of business logic, appli-
cation flow and presentation logic
• the Phrame PHP implementation of the
Jakarta Struts MVC controller
• Object Oriented programming in PHP
• creating abstract base classes
• using static methods of classes
• using the PostgreSQL database
• coding in plpgsql, a procedural SQL lan-
guage
• using a database abstraction layer (ADOdb)
• practicing good security habits
• using templates to separate presentation
logic (Smarty)
• writing custom Smarty variable modifiers
• using PHP’s session to store data

.
Click HERE To Discuss This Article
http://www.phparch.com/discuss/viewforum.php?f=24
Publish your data fast with PHPLens
PHPLens is the fastest rapid application tool you can find for
publishing your databases and creating sophisticated web
applications. Here’s what a satisfied customer, Ajit Dixit of Shreya
Life Sciences Private Ltd has to say:
I have written more than 650 programs and have almost covered 70% of MIS,
Collaboration, Project Management, Workflow based system just in two
months. This was only possible due to PHPLens. You can develop high
quality programs at the speed of thinking with PHPLens
Visit phplens.com for more details. Free download.
Connect
with your database
FFEEAATTUURREESS
FEATURES
June 2003 · PHP Architect · www.phparch.com
24
Introduction
It is unfortunate that so many software projects are not
successful. There can be many different reasons for this
and, of course, some circumstances cannot be prevent-
ed. By relying on the experiences of other people,
however, many common problems in the software
development process can be mitigated.
Agile software processes are “best practices” that
have been identified through experience. In this article
I want to introduce the agile approach and its benefits
for PHP developers. I focus primarily on patterns and

come, ensuring customer acceptance.
There are a number of agile methodologies, includ-
ing “Scrum”, “Crystal”, and “Extreme Programming”.
Extreme Programming, or XP, was introduced by Kent
Beck and has definitely received the most attention. XP
is a process model focusing on small incremental releas-
Agile Software
Development
With PHPUnit
By Michael Hüttermann
Are you a responsible project manager who feels
depressed due to failed projects? Are you a developer
frustrated with defective applications and project stress?
Perhaps agile software processes are the cure you’ve
been waiting for.
PHP: version 4.3+
PHPUnit: version 0.5+
Code Directory: agilemethods
REQUIREMENTS
FEATURES
June 2003 · PHP Architect · www.phparch.com
25
Agile Software Development With PHPUnit
es, and iterative development. Over several iterative
cycles more and more features are added to the prod-
uct, but even the first iteration contains real functional-
ity. Customers are able to run through mini acceptance
tests, and can offer feedback very early on. This incre-
mental release cycle prevents misunderstandings, and
keeps projects on the right track.

Continuous integration
Let’s assume we are using the waterfall model. The
coding begins and proceeds in a more or less uncoor-
dinated manner while developers create their modules.
Shortly before final code freeze they are asked: “Are
you finished? Does your code work?”. “Sure,” they
answer, “I implemented the template engine here, and
there is the database abstraction. Also, the business
logic is complete.” At that time all single modules are
frozen and integrated, resulting in a big bang. The sin-
gle modules may work, but the interaction between
them doesn’t. And this may happen shortly before
release!
The solution for this is continuous integration. We
freeze our code as often as possible, and integrate.
Small releases and pieces are more manageable. The
best case is that the result of each integration cycle is a
runnable version. The worst case is that bugs prevent
the integration. At least we know about them now and
can fix them, rather than finding out about them at the
end of the cycle. Above all, we learn by integrating the
product. It will not be a single event we are afraid of;
it will be routine. We get a good feeling for our appli-
cation, and no big surprises await us at the end of the
project.
Test-driven development
Now that we’ve introduced some of the patterns used
in XP, the remainder of this article will focus on
arguably the most important pattern: test-driven devel-
opment.

Unit tests are informal functional (black box) tests
normally executed by the developers of code. They are
often quite low-level and test the behavior of special
software components such as classes, modules, func-
tions, and so on. We use unit tests while practicing
test-driven development. Test-driven development
means that we code our unit tests first. No unit code is
written before its test.
Units are as finely-grained as makes sense. We may
write a unit test for a single method, for a whole mod-
ule, or for any other kind of component. The smaller
the component is, the better. Returning to the PHP
templating system example, you might write a set of


Nhờ tải bản gốc

Tài liệu, ebook tham khảo khác

Music ♫

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