To do this, sendmail must be available on the system, yet it is not required, since it is possible to create a PHP CLI script to combine this sort of redirecting with code from the custo
Trang 1TM
Trang 2SITEWORX control panel
NODEWORX Reseller Access
All of our servers run our in-house developed PHP/MySQL
server control panel: INTERWORX-CP
INTERWORX-CP features include:
- Rigorous spam / virus filtering
- Detailed website usage stats (including realtime metrics)
- Superb file management; WYSIWYG HTML editor
INTERWORX-CP is also available for your dedicated server Just visit
http://interworx.info for more information and to place your order
WHY NEXCESS.NET? WE ARE PHP/MYSQL DEVELOPERS
LIKE YOU AND UNDERSTAND YOUR SUPPORT NEEDS!
ORDER TODAY AND GET 10% OFF ANY WEB HOSTING PACKAGE
VISIT HTTP://NEXCESS.NET/PHPARCH FOR DETAILS
D e d i c a t e d & M a n a g e d D e d i c a t e d s e r v e r s o l u t i o n s a l s o a v a i l a b l e
/mo N EX R ESELL 2 $ 59 95
7500 MB Storage
100 GB TransferUnlimited MySQL DatabasesHost Unlimited DomainsPHP5 / MySQL 4.1.XNODEWORX Reseller Access
/mo
C O N T R O L P A N E L :
NEW! PHP 5 & MYSQL 4.1.X
PHP4 & MySQL 3.x/4.0.x options also available
We'll install any PHP extension you need! Just ask :)
MONEY BACK GUARANTEE
WITH ANY ANNUAL SIGNUP
4.1.x
3.x/4.0.x
Trang 3process more, then change
16 // or abolish the limit
34 // this is where your
work goes, whatever it may
be
35 // in this case were
going to say that data
limitation, we’re saying
that fields must
process more, then change
16 // or abolish the limit
34 // this is where your
work goes, whatever it may
be
35 // in this case were
going to say that data
limitation, we’re saying
that fields must
Discussion on building an admin page
to create and monitor a job queue with near-real-time status updates
by MIKE DeWOLFE
31 Flying with Seagull
A step-by-step guide for setting up
an example module
by WILLIAM ZELLER and WERNER M KRAUSS
45 User Management with Active Directory
Accessing, inserting or altering objects within the AD structure of Microsoft Windows Server 2003
If you want to bring a php-related topic to the attention of the professional php community, whether it
is personal research, company software, or anything else, why not write an article for php|architect?
If you would like to contribute, contact us and one of our editors will be happy to help you hone your idea and turn it into a beautiful article for our magazine Visit www.phparch.com/writeforus.php
or contact our editorial team at write@phparch.com and get started!
Download this month’s code at: http://www.phparch.com/code/
CONTENTS
WRITE FOR US!
Trang 6Those of us in the know have been aware of PHP’s readiness to take on the “enterprise” for
quite a while, now We’ve built serious applications, we’ve processed millions of dollars
worth of transactions with our favorite language, and we’ve even been heard extolling
PHP’s virtues before management-types
An all-too-common scene in the office, though, is PHP sneaking in through the
back door Upper- (or perhaps mid-) management has traditionally favored heavily-marketed, and
“proven” products over non-orthodox, community-built technologies I once worked for a CTO who
(as the joke went, anyway) would build the next project on whatever platform was advertised on
the last page of his business magazines Unfortunately, many of the developers (myself included)
were not convinced that the joke was only that—a joke
Despite—or perhaps thanks to—this lack of technological vision, our “little secret” language
has been making huge inroads into the enterprise, lately We have a few key players to thank for
this; especially Zend
Zend’s involvement with PHP is obvious—they are “The PHP Company” after all They’ve
recently made a few strategic moves that are helping to propel PHP into the minds of corporations,
IT managers, CTOs, and other business-types As far as I’m concerned, they’ve made four key moves
to promote PHP in this way
The first two are related (and the first of which is partially covered in this issue): Zend Core
for Oracle, and Zend Core for IBM Much like large-business’ dislike for non-mainstream software,
they’re often seen shunning open-source database platforms With Zend actively working with both
Oracle and IBM’s DB2, the PHP database taboo has been lifted Many corporations have already
deployed systems on Oracle or DB2, and (correctly) see no need to add yet another database
platform to their clusters (IBM has returned the favor by participating in PHP development—see
PDO_odbc (http://pecl.php.net/pdo_odbc), the PDO documentation (http://php.net/pdo) and
SDO (http://pecl.php.net/sdo).)
The next move on Zend’s part, of enterprise significance, is Marc Andreessen’s recent joining
of Zend’s board of directors Marc seems to have wholeheartedly accepted PHP as a legitimate
platform for serious web applications, facing the competition of coffee-based languages head on:
“PHP is to 2005 what Java was to 1995.” Those are strong words from a guy with both serious
technology and business credibility
The last of Zend’s recent moves that I find significant is the recent announcement of the PHP
Collaboration Project, including the Zend Framework This one has three major enterprise-friendly
parts (again, in my opinion): Engagement with the Eclipse Foundation, a standard development
framework for PHP, and corporation-friendly licensing and license-auditing of the code in the
framework
There is little information available on Zend’s work with Eclipse, but I’m eager to find out, as
I’m personally an active PHP Eclipse user, and I long for certain features of Zend Studio (especially
debugging) Just as with the database problem above, many companies already have Eclipse
deployed for their (especially Java-) developers
The framework itself (to which I’ve been invited to participate, but as yet have only lurked
on the mailing list) will hopefully breed a new generation of PHP applications that can avoid the
menial task of form handling (as one example)—see also this month’s continuation of the article
on building applications with the Seagull framework (which exists and is ready to use, today).
The licensing feature of the framework is particularly beneficial to enterprises who wish to
re-sell applications developed in PHP The PHP project has been burned by the GPL before (which
is why you won’t find any GPL-licensed extensions in PECL), and even for code licensed under a
BSDish (e.g PHP) license, there’s always the risk that code being stolen from an unknown source,
without some sort of auditing Zend provides this with their framework
All of this to say: not only is PHP ready for the enterprise, but the enterprise is starting to
awaken to this fact Kudos to the key players for making this happen!
Graphics & Layout
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, listings and figures, the publisher assumes
no responsibilities with regards of use of the information contained herein or in all associated material.
php|architect, php|a, the php|architect logo, Marco Tabini & Associates, Inc and the Mta Logo are trademarks of Marco Tabini & Associates, Inc.
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
Printed in Canada Copyright © 2003-2005 Marco Tabini & Associates, Inc All Rights Reserved
Trang 8PHP 4.4.1
php.net announces the release of PHP 4.4.1.
“PHP 4.4.1 is now available for download This
version is a maintenance release, that contains
numerous bug fixes, including a number of
security fixes related to the overwriting of the
GLOBALS array All users of PHP 4.3 and 4.4
are encouraged to upgrade to this version.
Some of the changes in PHP 4.4.1 include:
• Added missing safe_mode checks for
image* functions and cURL.
• Added missing safe_mode/open_
basedir checks for file uploads.
• Fixed a memory corruption bug
regarding included files.
• Fixed possible INI setting leak via
virtual() in Apache 2 sapi.
• Fixed possible crash and/or memory
corruption in import_request_
variables().
• Fixed potential GLOBALS overwrite
via import_request_variables().
• Fixed possible GLOBALS variable
override when register_globals are
The phpBB Group is pleased to announce
the release of phpBB 2.0.18, “The Halloween
Special” release
This is a major update to the 2.0.x
codebase and includes fixes for numerous
bugs reported by users to our Bug Tracker,
as well as updates to those issues identified
by the recent security audit of the code and
a couple of security issues reported to us In addition we have backported a further feature from our “Olympus” codebase to change the way automatic logins are handled.
We would like to thank all of those who took part in the security audit of the code for their work.
As with all new releases we urge you
to update as soon as possible You can, of course, find this download available on our downloads page As per usual, four packages are available to simplify your update.
For more information visit:
As far as the changes go, this version
is virtually identical to the prior release candidate The one major addition was the integration of the Indonesian translation that now makes the forum available in a whooping
26 languages, 2 more than in the prior stable release.
Get all the latest info from ilia.ws.
symfony 0.4.1
symfony-project.com announces the release
of version 0.4.1 What is symfony? The site describes it as professional web tools for lazy folks:
Based on the best practices of web development, thoroughly tried on several active websites, symfony aims to speed
up the creation and maintenance of web applications, and to replace the repetitive
coding tasks by power, control and pleasure
If you have been looking for a Rails/ Django-like framework for PHP projects with features such as:
• simple templating and helpers
• multilingual and I18N support
• object model and MVC separation
• Ajax support where all elements work seamlessly together, then symfony is made for you Check out the latest version of symfony at
symfony-project.com.
PHP Québec 2006:
call for speakers
PHP Québec is pleased to announce the
2006 PHP Québec conference, which will be held between March 29th and 31st, 2006
We are looking for the best speakers, willing
to share their experience and skills with professional PHP developers from eastern Canada and USA PHP Québec 2006 features
php|architect Releases New Design Patterns Book
We’re proud to announce the release of php|architect’s Guide to PHP Design Patterns, the latest release in our Nanobook series.
You have probably heard a lot about Design Patterns —a technique that helps you design rock-solid solutions to practical problems that programmers everywhere encounter
in their day-to-day work Even though there has been a lot of buzz, however, no-one has yet come up with a comprehensive resource on design patterns for PHP developers—until today.
Author Jason E Sweat’s book php|architect’s Guide to PHP Design Patterns is the first, comprehensive guide to design patterns designed specifically for the PHP developer This book includes coverage of 16 design patterns with a specific eye to their applications in PHP when building complex web applications, both in PHP 4 and PHP 5 (where appropriate, sample code for both versions of the language is provided).
For more information, http://www.phparch.com/shop_product.php?itemid=96.
Trang 9Looking for a new PHP Extension?
Check out some of the latest
offerings from PECL.
- RFC compliant HTTP date handling
- Parsing of HTTP headers and
messages
- Caching by “Last-Modified” and/
or ETag (with ‘on the fly’ option
for ETag generation from buffered
output)
- Sending data/files/streams with
(multiple) ranges support
- Negotiating user preferred language/
PDO_OCI 1.0RC2
This extension provides an Oracle driver for PDO.
Check out some of the hottest new
releases from PEAR.
ScriptReorganizer 0.3.0
Library/Tool focusing exclusively on the file
size aspect of PHP script optimization.
HTTP_Request 1.3.0
Provides an easy way to perform HTTP
requests
Calendar 0.5.3
Calendar provides an API for building Calendar
data structures Using the simple iterator and
it’s “query” API, a user interface can easily be
built on top of the calendar data structure, at
the same time easily connecting it to some
kind of underlying data store, where “event”
information is being held.
It provides different calculation
“engines” the default being based on Unix
timestamps (offering fastest performance)
with an alternative using PEAR::Date which extends the calendar past the limitations
of Unix timestamps Other engines can be implemented for other types of calendars (e.g
a Chinese Calendar based on lunar cycles).
HTML_Template_Sigma 1.1.4
HTML_Template_Sigma implements Integrated Templates API designed by Ulf Wendel.
PEAR_
PackageFileManager 1.6.0a4
PEAR_PackageFileManager takes an existing package.xml file and updates it with a new filelist and changelog
PEAR 1.4.4
PEAR Base System
PEAR_RemoteInstaller 0.2.0
PEAR Remote installation plugin through FTP
Trang 10mail() Hacks
TIPS & TRICKS
by BEN RAMSEY
PHP provides an awesome built-in feature with
the mail() function I refer to it as “awesome”
because I originally came to this language
from the background of ASP and VBScript, and
to successfully send an e-mail message from
an ASP script, one had to purchase a third-party COM
object and successfully install and register the object on
a Windows server PHP has mail capabilities built right
into the language, providing developers with a powerful
and easy way to send e-mail
Sometimes, however, whether for purposes of security
(in which the server doesn’t have access to a local mail
server) or debugging (mail should be trapped and not
sent to users), it becomes necessary to redefine the
mail() function, or redirect it In this edition of Tips &
Tricks, we’ll explore how to do both
Redefining mail()
There might be times in which server administrators
do not wish to provide access to mail functionality
For example, they are unwilling to install sendmail,
CODE DIRECTORY: hacks
TO DISCUSS THIS ARTICLE VISIT:
http://forum.phparch.com/262
postfix, or any other mail servers There are valid security reasons for disallowing mail servers, such as the fear of a Web server being used as a spam relay, but this lack of functionality can put a damper on Web application features Furthermore, while applications can
be written in such a way as to get around this limitation (e.g using sockets and SMTP), there are many third-party applications and tools that rely on PHP’s mail()
function, and it is far too time consuming to rework these applications to use your own mail function Thus, for full compatibility, it becomes necessary to hack away
at PHP’s mail() command and create your own, but, as difficult as this sounds, it’s actually quite simple to do
To completely redefine the mail() function, it is necessary to recompile PHP without support for the function Afterwards, we’ll create a new mail() function
How do you send e-mail on a server in which there is no mail server installed? How do you redirect e-mail messages in a testing environment so they don’t go to your users? This edition of Tips & Tricks addresses these two questions, highlighting some useful tricks
to redefine or redirect mail().
Trang 11mail() Hacks
using PHP, and your applications will be none the wiser
First, to compile PHP without mail(), run the c
command as normal, including all desired parameters
Then, before running make, edit main/php_config.h Find
the line that reads:
#define HAVE_SENDMAIL 1
Comment out this line, so that it now reads:
/* #define HAVE_SENDMAIL 1 */
Now, run make and make install as usual
This will essentially disable the mail() function, and
it will no longer be available to your scripts So, our next
step is to create a mail() function at the application
level
Listing 1 shows one such example mail() function
using the PEAR::Mail package This function implements
the same exact parameters as the native PHP mail()
function to ensure
compatibility with any
applications that require
the use of mail();
it does not use the
$additional_parameters
parameter since that is
primarily used to pass
additional arguments to
the sendmail (or other
mailer) binary This
new function should
also behave in exactly
the same way as the
native function and all
parameters passed to it
should follow the rules for mail() as defined in the PHP
manual
NOTE: Using this method, you cannot simply create a
new mail() function using the PEAR::Mail “mail” driver,
as this driver also utilizes PHP’s built-in mail() function
to send mail Thus, redefining the mail() function to use
the PEAR::Mail “smtp” driver should also work for any
applications that use PEAR::Mail with the mail driver
Using PEAR::Mail with the sendmail driver will not work
if sendmail is not available on the system
Now that we have defined a new mail() function,
we need to make it accessible to the applications that
require it The quickest and easiest way to do this is to
use the auto_prepend_file setting in php.ini:
auto_prepend_file = /path/to/new_mail.php
You may also set this in your Apache httpd.conf or
.htaccess file:
php_value auto_prepend_file /path/to/new_mail.php
Now, we have a mail() function that will perform similarly
to the built-in function, and all PHP applications on the system have access to use it Keep in mind that other PHP mailing libraries could be used; you are not limited
to PEAR::Mail
Redirecting mail()
At times it is preferable to turn off mail functionality altogether without recompiling PHP This includes applications that are running on testing servers and need
to use mail() for debugging purposes, but should not send any actual mail messages—or should send messages but only to the developers In cases such as these, it is possible to redirect mail messages sent through mail()
by modifying the php.ini sendmail_path value
Modifying sendmail_path is a simple task The
complexities lie in the script to which all mail
is redirected This script may be as simple as directing all mail to a log file or redirecting it to the project developers,
or it may be as complex
as implementing a scale mail solution using
full-a PHP commfull-and-line interface (CLI) script
to both send mail, as illustrated in Listing 1, and log everything We’ll examine all of these options
If the goal is to temporarily turn off mail and redirect
it to a log file, simply create a script named logmail, set the permissions level to 755 (chmod 755 logmail), and put the following line in the script:
cat >> /tmp/logmail.log
Then, set sendmail_path in php.ini to /path/to/logmail Don’t forget to restart your web server Now, all e-mail sent by applications will be stored in /tmp/logmail.log
rather than reaching the recipient in the To header.NOTE: The sendmail_path directive may be set only in
php.ini or Apache’s httpd.conf It cannot be set from an
.htaccess file
There may be times, however, when properly testing
an application means that all e-mail messages generated
by the application must be sent somewhere, but they
When testing,
mail() shouldn’t
go to real users.
Trang 12shouldn’t go to any real users Thus, we need to trap the
mail, which is another fairly simple task
Create a script named trapmail, set the permissions
level, again, to 755, and place the following in the script
(replacing developer@example.org with your choice of
e-mail address, of course):
Then, as with earlier, set the sendmail_path directive
to /path/to/trapmail This will successfully redirect
all e-mail messages sent by the application to
developer@example.org, and the original To, Cc, and Bcc
headers will be rewritten to X-original-to, X-original-cc,
and X-original-bcc respectively
To do this, sendmail must be available on the system,
yet it is not required, since it is possible to create a PHP
CLI script to combine this sort of redirecting with code
from the custom mail() script of Listing 1 to redirect and
log any messages sent by a PHP application Listing 2
gives a glimpse into how this is possible
I would save the code in Listing 2 to a file such as
/usr/local/bin/php_mailer and set its permissions to
755 Then, I would implement some form of logging,
perhaps using PHP 5’s file_put_contents(), along with
a mailer package to send mail to either the intended
recipient (on a production server) or the developers (on
a testing server)
Also, notice that the mail is received on standard
input in Listing 2 The message is being received in
exactly the same format that sendmail would receive it
Thus, this script must parse the received message, extract
the headers and body (we have already done this), and
send them to PEAR::Mail in the format it expects
Finally, the sendmail_path directive must be set to
/usr/local/bin/php_mailer to make use of it
These suggestions are simple, yet effective, ways to
either send e-mail when your server can’t support PHP’s
native mail() function or you wish to redirect messages
during development or testing I hope you can see how
these methods are versatile and can be extended to
implement some rather complex mailing functionality
I’d like to thank Sean Coates and Davey Shafik,
who allowed me the use of content from their blogs
to make this column possible If you have tip and/or
trick that you’d like to see published here, send it to
tnt@benramsey.com, and, if I use it, you’ll receive a free
digital subscription to php|architect
Until next time, happy coding!
27 $tmp_headers = explode ( “\r\n” , $additional_headers );
28 foreach ( $tmp_headers as $header )
35 /* Set default headers, if not present */
36 $headers [ ‘Subject’ ] = $subject ;
37 if (!isset( $headers [ ‘To’ ]))
38 $headers [ ‘To’ ] = $to ;
39 if (!isset( $headers [ ‘Date’ ]))
40 $headers [ ‘Date’ ] = date ( ‘r’ );
41 if (!isset( $headers [ ‘From’ ]))
42 $headers [ ‘From’ ] = $from ;
43
44 /* Send the mail message */
45 $mail_object =& Mail :: factory ( ‘smtp’ , $smtp_params );
46 $e = $mail_object -> send ( $to , $headers , $message );
11 // Now, use a mailer package and implement some logging
12 // using $headers and $body
13 ?>
LISTING 2
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
ramsey@php.net or read his blog at http://benramsey.com/.
Trang 13Tips & Tricks
Trang 14processing file database
and got into
37 // a destination
file
38 // to make a
limitation, we’re saying
that fields must
processing file database
and got into
37 // a destination
file
38 // to make a
limitation, we’re saying
that fields must
It does sound strange doesn’t it? The marriage of
PHP and Oracle is a little like the odd couple
One side is bred of the free spirited open source community while the other springs from the requirements of a strict corporate culture In fact, when you put the two together they complement each other very nicely, unlike the two fellows of 70’s TV fame
PHP enjoys the benefit of having a huge community of developers who create numerous quality open source components while Oracle is a proven enterprise database management system that offers a suite of applications that handle performance, scalability, and security
Over the past couple of years, the relationship between Oracle and PHP has grown closer, and more intimate Both are the major players in their respective milieus and in spite of their differing backgrounds, the two have found a themselves sharing the spotlight recently as the partnership between Zend Technologies and Oracle has produced some tangible results On October 11, 2005, Oracle and Zend announced that the new Zend core for Oracle was released for general distribution In the past, in order to get PHP working with Oracle you had to download PHP, Apache, and Oracle Instant Client separately You had to then create/
modify a make file, compile, link components and then configure each component, once installed The Zend Core was built to deliver a tested, high performance OCI8 driver that integrates with Oracle 10g client libraries It bundles together all of the install code for both PHP 5 and Oracle, which makes it a one stop shop for PHP and Oracle installations The new Zend core is configurable from a single web-based interface and is available as a
FEATURE
LINKS:
http://www.oracle.com/technology/tech/php/index.html http://www.zend.com/core/oracle
CODE DIRECTORY: oracle
TO DISCUSS THIS ARTICLE VISIT:
http://forum.phparch.com/263
Recently, there has been a lot of attention paid to the partnership between Zend Corporation and Oracle This article examines the
benefits that this partnership offers web developers.
free download from Zend.com
Originally, the PHP/Oracle APIs were quite limited The original set of APIs for connecting PHP and Oracle were called ORA functions These ORA functions are now deprecated and are no longer bundled with PHP They were useful for simple insert and select statements but accessing stored procedures was problematic because this API lacked support for BLOBS, CLOBS and variable binding
The OCI8 functions, introduced in later versions
of PHP 4, require Oracle 8 or greater to run on either Windows or Linux These functions give more options for connecting to an Oracle database by allowing for the programmer to specify new or persistent connections for each database call from the web server Large object support was introduced to make use of Oracles LOB data types, and variable binding was introduced to allow for execution of more complex stored procedures
PHP: 4 1.2 and 5.0.5 OTHER SOFTWARE: Oracle 9i
by ROBERT MARK
PARTNERSHIP
Trang 151995, web development with Oracle has generally been associated with Java Times have changed and it is now recognized that PHP and Oracle complement each other quite well This article will explore some ways in which Oracle can benefit PHP development.
Use database caching or data caching
Data caching is one of the best strategies for improving web application performance It basically refers to a technique for improving performance by identifying frequently used information and storing it in an easily accessible place Usually this means that the query or data set will be loaded into memory The best way to handle data from a database is to design your application
so that there are as few database calls as possible, in order to reduce the load on the server—which will slow your applications down
From the illustration in Figure 1, you can see how database caching works If a dataset is specified as being cached, it is loaded into memory If a query needs to access the dataset, it can pull it directly from memory If the dataset has different requirements, such as customized parameters, the next step is to look into the indexing on
a table If the dataset still requires customization then the table must be accessed
Depending on your business rules, and if the data that you are requesting needs to be up to the minute and
could change at any second then you will need to call the database for each and every page request Often multiple calls will be necessary There are many circumstances where
a database call is unnecessary and the extra overhead can
be avoided by simply caching a data set Of course, the performance of a database management system depends
a great deal on the experience and ability of the database designer, programmer and administrator
Oracle is very good at handling large numbers of requests, but there is no substitute for good database design As an example, some of the applications that
I have had a hand in writing were required to access database tables with hundreds of thousands of rows Without proper indexing and data caching, the queries that would access data and then display it on a page would timeout before and query would finish processing
A single web page could have several such large database calls for a single page view
One page that I created with my development team for a web portal required that each person would view a customized page after they logged in This meant that
we would first have to conduct a search for a password combination within a table that contained nearly half a million rows This would return a login confirmation and a unique identifier We would then query another table to find a list of that person’s interests; another query was for news stories related to a particular school and yet another query to display academic events based on the interest of that individual
username-All of the queries were contained in stored procedures that used complex SQL statements to present the user with a personalized home page based on a profile that was created from the available data In order to reduce the amount of time it took to return data, several queries were cached, that is, they were stored in memory for ready access As a result, displaying each customized home page now takes a fraction of a second Data caching reduces the number and types of database queries that may be required to display a page
These features are not unreasonable in a world of Amazon.com type customization where each user is presented with a customized web interface but they place
a huge burden on any database Of course, all of this depends upon the business rules and the requirements
of the project
PDO vs OCI and ADODB
(PHP 5 Data Objects
vs Oracle Call Interface and ADOdb)
The method for accessing data from Oracle through PHP has undergone several changes over the years One thing that is notable about PHP is that because the development FIGURE 1
PARTNERSHIP
Trang 16of PHP tends to respond to the needs of the present,
there seems to be less regard for future planning This
is one reason why there are so many separate database
driver functions available in PHP Each set of driver
functions corresponds to a particular database platform
although much of the functionality requirement remains
the same from platform to platform Why do we need a
separate API for MySQL, Postgres and Oracle? That’s a
good question As PHP was being developed, the syntax
and functionality for database access was created by
separate groups of PHP developers, which resulted in
different APIs for each database This means that certain
features that are available for developing in a MySQL
environment will not be available to PHP developers
using an Oracle database and vise-versa This is why the
PHP community is developing a new, reengineered API
for all database calls
PHP Data Objects (PDO) is the new, more consistent
database API that is currently still in development
but is due to be included in
the PHP 5.1 release Some of
the features, as they relate to
Oracle, include a re-factored
Oracle driver, statements with
bound parameters, transactions,
large objects and better error
handling Most importantly,
PDO will provide a common
interface for all database
access By abstracting the
database layer you can use the
same API for any database
The concept is similar to that
of any database abstraction
layer such as the ADOdb
Database Abstraction Library
(http://adodb.sourceforge.net)
ADOdb provides a unified API
for accessing SQL databases It
is fairly easy to learn, however
it does not completely succeed
in abstracting Oracle database
access because some Oracle
features are not available in
other databases Specifically,
Oracle uses a datatype called
ref cursor which is basically a
pointer to a recordset Because
Oracle is the only database
that uses this data type there
is a function in ADOdb that
is unique to Oracle called
ExecuteCursor The ADOdb driver has also been proven
to be faster in benchmarks performed by phpLense (http://phplense.com/lens/adodb/)
A database abstraction layer makes moving from one database to another much easier, although this is not something to be taken lightly Because much of the business logic is tied to the database you would be well advised to commit to one
As you can see in the example in Listing 1, the syntax
is different for both the stored procedures in Oracle and in MySQL Calls to stored procedures in Oracle are invoked with “BEGIN” while MySQL’s stored procedures are executed with “CALL”
This is one of the weaknesses of using a database abstraction layer If the purpose of an abstraction layer
is to facilitate code portability, then it would make sense
to standardize the procedure calls Using strictly entry level ANSI SQL in the PDO prepare statement would allow for this because all databases understand this standard, FIGURE 2
Trang 17PHP & Oracle
but calls to stored procedures are database specific
A database abstraction layer’s main strength is
simplifying and reducing the amount of code it takes to
complete the same task If you can’t wait for PHP 5.1 to
be released then ADOdb is your best bet You should be
1 //MySql Stored Procedure Database Call:
2
3 $objResult = mysqli_query($link,”CALL SP_GET_LIST()”)
4 while ($arrRow = mysqli_fetch_array($objResult)) {
5 echo “First: “.$arrRow[‘FIRST_NAME’].” “
1 set up database table for images
2 CREATE TABLE DB_IMAGE_LIBRARY(
3 IMG_ID NUMBER,
4 CAT_ID VARCHAR2(2 BYTE),
5 FILE_NAME VARCHAR2(50 BYTE),
11 SHORT_DESC VARCHAR2(255 BYTE),
12 DATE_ADDED DATE DEFAULT SYSDATE,
13 DATE_MODIFIED DATE DEFAULT SYSDATE,
14 C_OPERATOR_ID VARCHAR2(10 BYTE),
15 STORAGE (initial 50K next 50K pctincrease 0)
16 TABLESPACE DB_TABLE_DATA
17 LOB (IMG_BIN) STORE AS (TABLESPACE IMAGE_DATA
18 STORAGE(initial 100K next 100K pctincrease 0));
19
20
21 Create a package to retrieve image data
22
23 CREATE OR REPLACE PACKAGE PKG_IMAGE_MGMT AS
24 TYPE my_ref_cursor IS REF CURSOR;
25
26 PROCEDURE SP_GET_IMAGE (img_id IN NUMBER,
27 my_cursor OUT my_ref_cursor);
28 END;
29 /
30
31 CREATE OR REPLACE PACKAGE BODY PKG_IMAGE_MGMT AS
32 TYPE my_ref_cursor IS REF CURSOR;
Stored Procedures, Functions, Indexes and Views
In order to take advantage of Oracle’s security features and speed, it is best to use stored procedures and functions because these modules are internal to the Oracle database They can be written in either in Oracle’s proprietary PL/SQL language or in Java PL/SQL is
a procedural language that dates back to the days of COBOL It is relatively easy to learn the syntax As a native Oracle language, performance is improved because commands don’t have to be compiled before processing
As much business logic as possible should be left to the database in the form of stored procedures because the database is generally more suited to handle cross referencing large amounts of data than PHP (or any other web scripting language)
Procedures are best used to promote reusability and to encapsulate complex business logic In the university alumni portal example mentioned earlier, a single procedure could be used to provide all of the user information required to display the page Also, the same database logic that applies to procedures may be required
in several places, which encourage code reuse
For example, if your database contains a procedure that returns a schedule for next week for an employee, you can avoid constructing the query, adjusting the date, validating the individual and formatting the data
by simply calling the procedure with certain parameters When calling a stored procedure from PHP, the statement
is first parsed, data is bound and then the statement is executed The same parsed statement can be used over again with different variables bound to it
Oracle functions have the same basic qualities as Oracle stored procedures except that there are no OUT
variables available in the function call A function must return only one value
Another advantage of using stored procedures and functions is that variables are “bound” which means that because an input variable is not being used to form the SQL statement, the contents of a variable can be inserted directly into a table This helps prevent SQL injections and frees the programmer from worrying about escaping quotes because this work is offloaded to the driver
Trang 18Oracle-Specific Features
Virtual Private Database (VPD), also called fine-grained
access control or row-level security, is a feature unique
to Oracle that gives database administrators an extra set
of tools that not only set user rights for a table but also
define permissions for specific rows in a table
This feature was introduced in Oracle 8i and is
available in all later versions This feature, when used
with Oracle’s “application context” will ensure that each
user will be able to see a different data set while using
the same SQL code This provides a more manageable
approach to security when developing for the web (see
Figure 2)
A major advantage of the VPD is that code does not
have to be rewritten for each type of database user,
as each is given a set of access rights, based in their
context The VPD essentially appends a WHERE clause to
the end of each query
For example, a company may have regional offices,
and each regional office will only be able to view data
that relates to their specific region, even though they
are querying the same data set A database can then be
constructed so that all offices will be able to access data
in the same table, but queries to that table will only
return data as it relates to the individual office
With fine-grained access control, an application can
be written using standard SQL queries and output the
data according to the privileges of the user requesting
the data Maintenance is also easier because it reduces
the amount of code and simplifies the design
The “column masking” feature of the VPD also allows
the database administrator to show or hide data fro,
specific columns In the previous example, a query like
SELECT * FROM TBL_EMPLOYEES may return names, email
addresses, fax numbers, etc., for all employees for queries
performed by users in regional offices while the head
office users may also see a column for salaries
Oracle 9i introduced a new tool called policy manager
that provided a GUI for managing and setting up
row-level security The steps involved in setting up a VDP are
as follows:
1 Define business rules This implies creating a set of guidelines that determine which users are permitted to view the data and dictate how that data will be manipulated
2 Set up roles, create users and grant roles to the users’ accounts
3 Create the VPD policy function This creates
a function to be called whenever the table is accessed The function returns a string that
is essentially appended to the query’s WHERE clause Oracle calls this function “the dynamic access predicate.”
4 Apply the policy to the table being accessed by registering the policy function or procedure using
DBMS_RLS
5 Apply the application context when the user logs in This application context will then be passed to the policy when a table is accessed and the resulting recordset will be based on the permissions applied in the policy function
Managing Multiple Database Names
You should manage multiple database names in Oracle’s
tnsnames.ora file, not in PHP
The OCI8 login function ocilogin() requires a username and password The third parameter is the host database
A development environment in a larger enterprise my
have a development web server, a testing web server and a production web server Each server may connect to
a different database The test and development servers may connect to a test database while the live web server will always connect to a live database Problems may arise when moving PHP code from one server to another because there may be a line of code placed on a web server that connects it to the wrong database
For example, when we test our code, we put an application in beta and we move it from the development web server to the test web server, where users may try
One of the strengths of Oracle is
its ability to handle large amounts of data.
Trang 19PHP & Oracle
to find bugs in the system The test environment will
use the same database as the development environment
Once testing has been completed, the code is transferred
to the production server We wanted to avoid having
to make changes to the code before we moved it to
production, so we decided to leave all of the settings
that were unique to the server on the server That is,
the PHP code should always remain the same but the
tmsnames.ora files can vary from server to server
Put Oracle and PHP
on Separate Servers
If you spend the money for an Oracle license, then
it would be a good idea to put the database on a
separate piece of hardware A dedicated database server
essentially implies that the server is used solely by one
database application This gives the advantage of higher
speed and better database performance because the
processing power will not have to be shared by other
server applications
PHP should be on a separate server from the database,
primarily for maintainability This will allow for easier
maintenance because upgrades and patches will need
to be applied to both PHP and Oracle as they become
available If the web server needs to be rebooted, there
is no reason to also take down the database server If
you do decide to use a remote database, you must install
the Oracle client on your PHP server and then configure
it to access the database, which involves installing
the Oracle instant client As the database grows larger,
Oracle database servers can be clustered to handle the
requests Clustering is accomplished by installing an
add-on called Real Application Cluster (RAC), which was
originally developed for Oracle 9i With this feature, an
Oracle database can run any application across a set of
clustered servers If one server fails, the application will
continue running on the remaining servers until another
server is added, and the functioning servers don’t need
to be shut down
Handling Large Objects
One of the strengths of Oracle is its ability to handle large amounts of data
There are 4 types of LOB data types The Character Large Object (CLOB, NCLOB) can handle up to 4 GB of character data; Binary Large Objects (BLOB) store up to 4
GB of binary data; and BFILEs which are stored separately
on the server’s filesystem, and are limited in size only by the operating system
These data types become useful when the database
is used to store large binary or character objects The
CLOB field is especially useful because it can be searched Larger enterprise databases can make heavy use of these features because the database can then act as a file server with built in security Using the database to store binary data eliminates the need to duplicate security policies across server types
Let’s use an image server as an example Normally, a website would keep images on the web server in a folder off of the root folder Often, as the number of images increases so do the number of folders under the images But what happens when you have several thousand images that must be stored, searched and categorized?
An image library may contain several thousand images consisting of everything from buttons, banners and icons that people use for web pages to mug shots of employees to stock photos Some of the photos may need to have security rights attached to them so that users do not infringe on any copyright laws by using images that are not supposed to be publicly available.One method of keeping track of the images that I have seen is to assign a unique identifier in a database table and associate it with a path to the location of the image on the file server or web server In this situation there is an obvious problem with data integrity because there was no way to associate the location of the file with the database record If a folder or a file were moved then the database record would become invalid
Enterprises that use PHP and Oracle
can benefit from
a large pool of PHP expertise.
Trang 20Oracle stores a pointer in the table to the place in
the data store where the actual data is located The LOB
data storage is defined by specifying a separate storage
clause in the create table command In Listing 2, the LOB
clause tells Oracle where to store that data, while the
parameters for the STORAGE clause control the physical
areas that the data object will use on the disk
Storing images in a database table ensures that data
integrity is maintained and all of the attributes of the
image relate directly to the image With Oracle Virtual
Private Database the PHP programmer is freed from
worrying about which images should be permitted in
each context If the VPD context is set to allow access by
the general public then a simple select query will return
all of the images that have this attribute attached
Oracle in PHP:
Strengths and Weaknesses
One of the major strengths of using PHP for development
is that support is offered through a large network of
PHP developers who share problems and solutions in
discussion forums, which are then made available to the
general public Support for PHP/Oracle combination is
harder to find than support for MySQL This is why the
support that Oracle is offering to PHP developers through
the Oracle Technology Network is essential to help the
drive that Oracle is making towards supporting PHP
The Oracle Technology Network PHP Developer Center
presents featured articles, forums and news related to
1 //Presently, the steps required to make a database call are as follows:
2
3 //OCILogon() – Login to the Database, OCIParse() – Parse the SQL string,
4 //OCIExecute() – Execute the statement, OCIFetch() – Fetch Results
21 //construct query string
22 $strSQL = “SELECT FIRST_NAME, LAST_NAME FROM PEOPLE.TBL_NAMES”;
23 //parse query string
59 catch (PDOException $objException){
60 echo “ERROR: “ $objException->getMessage();
61 }
62 //construct query string
63 $strSQL = “SELECT FIRST_NAME, LAST_NAME FROM PEOPLE.TBL_NAMES”;
is an existing repository of database code that will need
to be leveraged for the web If an organization that is presently using an Oracle database for their internal operations is planning to provide data access through the web, they will need a simple means to develop applications that can access and present Oracle data.PHP fits the bill nicely in this context because it can be understood by a beginner programmer while at the same time offering more complex object oriented features that one would expect to see in more complex languages such as Java
Enterprises that wish to embark on web application development using PHP and Oracle benefit from a large pool of PHP expertise As PHP database access becomes more standardized through the introduction of a database abstraction layer of through the use of the new PDO functions From the web developer side, less emphasis will need to be placed on specific database knowledge, which will result in more rapid application development.These features, combined with Oracle’s data security, blend nicely to form a very robust and practical web application development environment
OCI vs PDO
ROBERT MARK is a senior web and database programmer working at McGill University in Montreal, Quebec He uses Oracle/PHP regularly.
Trang 21Job Management with PHP & Cron
FEATURE
I have been working on a system to import 10,000
user profiles and 20,000 forum discussion
messages—all coming from different sources
Some of the imports may have missing information
that the system has to bring in through an
external election system Some of the imports may not
work at all The biggest consideration: each import job
may take tens of minutes and create a lot of server load
The duration of each job is such that if it is launched
from a web client, the response will timeout before the
job is complete
If a script times out before completion, some users
may re-run the job and thereby cause problems It is also
difficult to assess which parts of the job have run and
which parts remain to be processed I could have built
a one-off script for my import jobs but after the initial
imports there will likely be more imports in the future
While I could be tasked to do the imports later, a clear
tool that a non-programmer could use would be ideal
We need a way to see the job progress without
interrupting the script to output HTML Once the server
has received a request or generated a response to the
client, the clock is ticking for an impending script timeout By keeping the job script operating solely on the server without direct client interaction, the script can run for as long as necessary
In essence, you need a worker script and a supervisory script On top of that, some sophisticated interaction between the client and the server will both save on bandwidth and also make for a slick presentation It’s not hard to pull off; it puts job tasks in the hands of non-programmers without giving them the keys to server; and
it has an immediacy and polish that will create a good
up with nearly real time status updates.
PHP: 4+
O/S: Linux
Job Management
with PHP & Cron
CODE DIRECTORY: phpcron
Trang 22“wow” factor from users.
To build this tool, you’ll need five components: one
is the data—the database; two worker scripts, and two
more scripts for show The job script is launched by the
cron job (and in turn by the cron daemon); the status
editing page is built infrequently; the JavaScript does a
lot of work, looking at the status of the jobs in progress
These rely on a source of continuity: a database to direct
jobs, record job status changes You may wish to add a
log viewer (see “Job Log” and “Log Viewer” below)
The Data Structure
There are two primary tables in use: jobs and job_status
If you wish to track individual tasks of a job, you’ll need
a job_log table, as well In this example, jobs is a table
of its own, but in your application, it is possible that its
role is much more subdued or rolled into another part of
your application
jobs (Listing 1) holds the information about the jobs
you wish to carry out It’s packed with the data you
need to run your automated jobs It does not contain
any information about status It’s read-only as far as its
interaction with the jobs as they are carried out Your
admin/display page can add records to jobs and alter
them after the fact
job_status (Listing 2) is the orchestration of this
system It has a 1 to 1 relationship with jobs It holds a
list of statuses, information about completed jobs, and
about jobs that are currently underway The job script
(see below) will write to this table, frequently
job_log (Listing 3) holds information about the
individual elements of a given job If this information
isn’t desirable, leave out this table and its relevant code
in the job_script.php script (in Listing 4)
The Job Script
At the core of this system is the main worker—the job
script (Listing 4) The “job” can be whatever you decide
to plug into a system like this It could be something
too large to allow for a proper request-response from the
server, whether it’s a matter of the number of items to
process, their complexity, or a combination of both The
job could be something from an external source that does
not directly interact with the admin system Examples:
posts coming into a discussion system from users; a long
duration script that is going out to search for many news
feeds; remote resources challenge/response, etc
In my case, I wrote a routine to process imported
records bound for phpBB—large CSV files The import
job was very complex and specifically built for phpBB
In the example that I cite in this article, we are taking
a list of database tables and inserting the values from those tables into another database table It is a simple illustration of the concepts that you need to consider, and fits nicely into our cron-based system
Really, any of your server’s cron jobs could report their status to a database and you can tap that database through the admin/display script to see when a cron job last ran and any notations as to that job’s status.You could build a particular script to handle each job and write a handler for each individual job, but that means you’re opening up the crontab (the file that
controls when and how often a particular job runs) for
editing by a PHP script Cron jobs are very important to the security and well being of a web server and because
of that, I preferred not to give PHP the opportunity to
open the crontab and edit jobs I prefer to have the server call a single script, frequently If this script is run without any pending jobs, only a small load is put on the server Keeping the PHP script’s hands off of the crontab leaves a security door shut
The Cron Job
Cron jobs are routine tasks that the server initiates at specific intervals From a shell session, you can edit your cron job list by typing crontab –e This puts the list of cron jobs into a text editor The timing for every job
is declared with five slots for time (minute, hour, day
of the month, month and weekday) and the command
or application to execute along with any supporting arguments The time slots are either wildcard (*) to execute on every possible opportunity of a day, hour, etc., dash-joined or comma separated specific interval times This means you can set cron jobs to execute every minute of every day or take some times and some days off
1 CREATE TABLE jobs
11 started datetime DEFAULT ‘0000-00-00 00:00:00’,
12 finished datetime DEFAULT ‘0000-00-00 00:00:00’
13 }
LISTING 2
Trang 231 CREATE TABLE job_status
11 started datetime DEFAULT ‘0000-00-00 00:00:00’,
12 finished datetime DEFAULT ‘0000-00-00 00:00:00’
10 $query = “SELECT *, jobs.job_id as id
11 FROM jobs job, job_status status
12 WHERE job.job_id = status.job_id
13 AND status.job_status = 0
14 ORDER BY job.job_id ASC LIMIT 1” ;
15 // if you want to process more, then change
16 // or abolish the limit
17
18 if ( $recordset = mysql_query ( $query ))
19 {
20 // if you have a limit of 1 this can be an if statement
21 while ( $row = mysql_fetch_array ( $recordset ))
34 // this is where your work goes, whatever it may be
35 // in this case were going to say that data comes
36 // from a processing file database and got into
37 // a destination file
38 // to make a limitation, we’re saying that fields must
39 // be less than 100 characters long
40
41 DB_Connect ( “your_db” );
42
43 // set it to “1” to say the job is underway
44 $sql = “UPDATE job_status SET job_status = 1
45 WHERE job_id = “ $fields [ ‘id’ ];
46 if( !( $result = mysql_query ( $sql )) )
47 {
48 die( “problems with the import job” );
49 }
50
51 $query = “SELECT * FROM “ $fields [ ‘processing_file’ ];
52 if ( $recordset = mysql_query ( $query ))
67 $names = $comma $key ;
68 $values = $comma ”’” addslashes ( $value ) ”’” ;
78 $sql = “INSERT INTO “ $fields [ ‘destination_file’ ]
79 “ (“ $names ”) VALUES (“ $values ”)” ;
87 write_log_post ( $job_id , $status_notice , $status );
88 $ $status ++; // $$status = $green or $red or $yellow
95 $sql = “UPDATE job_status SET
96 Status_Green = Status_Green + “ $Status_Green ”,
97 Status_Yellow = Status_Yellow + “ $Status_Yellow ”,
98 Status_Red = Status_Red + “ $Status_Red ”,
99 processed = processed + 50
100 WHERE job_id = “ $fields [ ‘id’ ];
101 if( !( $result = mysql_query ( $sql )) )
115 Status_Green = Status_Green + “ $Status_Green ”,
116 Status_Yellow = Status_Yellow + “ $Status_Yellow ”,
117 Status_Red = Status_Red + “ $Status_Red ”,
118 processed = “ ( intval ( $pass ) - 1 ) ”
119 WHERE job_id = “ $fields [ ‘id’ ];
120 if( !( $result = mysql_query ( $sql )) )
152 $dbc = mysql_connect ( $dbhost , $dbuser , $dbpass )
153 || die( “Cannot connect to database” );
Trang 241 <?php
2
3 // Connect to the DB
4 // For simplicity, all of the DB call
5 // happen in the main part of the script
6 DB_Connect ( “your_db” );
7
8 // if we have input, we can add it here:
9
10 $status [- 1 ] = “Not Ready” ;
11 $status [ ] = “Not Done” ;
69 $sql = “INSERT INTO job_status (`job_id`,`job_status`)
70 VALUES (‘” $input -> job_id ”’,’”
94 // this will only work on jobs that are not complete
95 $sql = “UPDATE job_status SET `job_status` = ‘”
96 $input -> job_status ”’ WHERE `job_status` < 2
97 AND job_id = “ $input -> job_id ;
110 $query = “SELECT DISTINCT *, jobs.job_id as id
111 FROM jobs job, job_status status
112 WHERE job.job_id = status.job_id
113 ORDER BY status.started DESC” ;
134 echo “\t<td> “ $row [ ‘processing_file’ ] ”</td>” ;
135 echo “\t<td><b>” $row [ ‘destination_file’ ] ”</b></td>” ;
136 echo “\t<td> “ ( $row [ ‘job_status’ ] == 2 ) ? “yes” : “no” ;
137 // status form fields
143 <input type=”text” size=”14” readonly name=”status”
144 style=”font-weight: bold; text-align: center”
145 value=” <? echo $status [ $row [ ‘job_status’ ]] ?> ”></td>
146 <td valign=”top”>
147 <input type=”text” size=”5” name=”Status_Green”
148 style=”background-color: #008800; color: #ffffff;
149 border: 0px; text-align: right”
150 value=” <? echo $row [ ‘Status_Green’ ]; ?> ”></td>
151 <td valign=”top”>
152 <input type=”text” size=”5” name=”Status_Yellow”
153 style=”background-color: #FFFF33; color: #000000;
154 border: 0px; text-align: right”
155 value=” <? echo $row [ ‘Status_Yellow’ ]; ?> ”></td>
156 <td valign=”top”>
157 <input type=”text” size=”5” name=”Status_red”
158 style=”background-color: #AA0000; color: #ffffff;
159 border: 0px; text-align: right”
160 value=” <? echo $row [ ‘Status_Red’ ]; ?> ”></td>
161 <td valign=”top”>
162 <input type=”text” size=”5” name=”Status_Total”
163 style=”background-color: #fafafa; color: #800000;
164 border: 0px; text-align: right”
165 value=” <? echo ( intval ( $row [ ‘Status_Green’ ]) +
166 intval ( $row [ ‘Status_Yellow’ ]) +
167 intval ( $row [ ‘Status_Red’ ])) ?> ”>
173 echo “<a href=\”#Row” $row [ ‘id’ ] ”\”” ;
174 echo “onClick=\”jsShow(‘Form_” $row [ ‘id’ ] ”’);” ;
LISTING 3
Trang 25262 echo “</form>\n” ;
263 if ( $fields [ ‘id’ ] > 0 )
264 {
265 echo “<br/>” ;
266 echo “<a href=\”#\” onClick=\”jsShow(‘” ;
267 echo $fields [ ‘id’ ] ”’);\”>Hide</a>” ;
285 document.forms[“status_” + id].elements[“Status_Yellow”].value = yellow;
286 document.forms[“status_” + id].elements[“Status_Red”].value = red;
287 document.forms[“status_” + id].elements[“Status_Total”].value = total;
319 <! Feel free to dress this page up as
320 much as you choose >
176 echo “ - <a href=\”viewlog.php?id=” $row [ ‘id’ ];
177 echo “\” target=\”ViewLog\”>View Log</a>” ;
178 echo “</td>” ;
179 echo “</tr>” ;
180 // put in a form field to allow editing and then
181 // HIDE it A JavaScript will make it visible
182 echo “<tr class=\”Form\” style=\”display: none;\”” ;
183 echo “id=\”Form_” $row [ ‘id’ ] ”\”>” ;
184 echo “<a name=\”Row” $row [ ‘id’ ] ”\”></a>” ;
209 $dbc = mysql_connect ( $dbhost , $dbuser , $dbpass )
210 || die( “Cannot connect to database” );
222 echo “<td>Id: </td><td><input type=\”hidden\” “
223 echo “name=\”id\” value=\”” ;
224 echo htmlspecialchars ( $fields [ ‘id’ ]);
225 echo “\”>” $fields [ ‘id’ ] ”</td>\n” ;
226 echo “<td>Status</td>” ;
227 echo “<td>” ;
228 echo “<select name=\”job_status\”>\n” ;
229
230 echo “\t<option value=\”-1\”” ;
231 echo.( $fields [ ‘job_status’ ] == - 1 ) ?
232 “ SELECTED” : ”\”>Not Ready</option>\n” ;
233 echo “\t<option value=\”0\”” ;
234 echo ( $fields [ ‘job_status’ ] == 0 ) ?
235 “ SELECTED” : ”\”>Not Done</option>\n” ;
236 echo “\t<option value=\”1\”” ;
237 echo ( $fields [ ‘job_status’ ] == 1 ) ?
238 “ SELECTED” : ”\”>Processing </option>\n” ;
239 echo “\t<option value=\”2\”” ;
240 echo ( $fields [ ‘job_status’ ] == 2 ) ?
260 echo “<input type=\”submit\” name=\”save\” value=\”” ;
261 echo ( $fields [ ‘id’ ] > 0 ) ? “Update” : “Insert” ”\”>\n” ;
LISTING 3
Job Management with PHP & Cron
Trang 26The following cron job executes every minute of every
day
* * * * * php /path/to /job_script.php
This job takes Sundays (day 0) off:
* * * * 1,2,3,4,5,6 php /path/to/job_script.php
After the timing comes the command call If you want
to execute a PHP script, call its interpreter (php) and use
the absolute location of your job script as its argument
In our case, we want to call a single job script repeatedly
and let the script look to a database for relevant jobs to
run If there are no eligible jobs to be executed, the brief
execution of the script will come to a close Then, next
time the cron tab matches the current time the script will
be run again
If you need to know more about how to build a cron
tab, take a look at: http://www.mkssoftware.com/docs/
man1/crontab.1.asp It explains the intricacies of
crontabs: how to edit them, review them and set
them up in the first place Another resource at
http://www.csgnetwork.com/crongen.html can help you
build a sensible crontab via a JavaScript tool
Server load can affect your decision for when to run a
cron job If your server has a processing crunch at a peak
time, it’s good idea to give a load intensive cron job a
break at that time
You may ask yourself: are we really running a script
every minute of every day? Well, yes While that does
mean 1440 calls per day, if there are no jobs waiting in
the queue, it means the script runs, saying “nothing to
do” and exits 59 seconds later it does it again
You could run the script less often than every minute;
or less often than every day (e.g take Sundays off); or
run it only during certain hours (e.g run this on hours
0-18 (midnight to 6PM)) The upside of less frequency
is less server load The downside of less frequency is
reduced convenience If your job makes a sweep every minute, then when an administrator sets a job in motion, they need only wait a minute or so to see it executed If you went to a cron job that executed every five minutes (or 288 times per day) you would have to wait until the next cron job triggers Again, it not a big deal: it’s a question of immediacy through frequency vs infrequency
to make for a lower overall server load
When the jobs are underway an admin will want to monitor their status There are two ways accomplish this: 1) reload the entire admin screen to get a refreshed view of the progress; or 2) make some surreptitious calls
to the server for status updates “Quiet calls” to the server will load the same dataset as a full page refresh, but they can serve out much less HTML to update the client Rather than refresh the whole page, the refresh occurs in a nearly invisible IFRAME (1 pixel x 1 pixel)
that sits on the admin page Instead of building HTML, the IFRAME contains JavaScript that calls functions in the parent page When this frame page is loaded, the
BODY onload() event calls a function that calls all of the update functions (see Listing 6) A meta-rage refresh will reload the page after so many seconds, to trigger the event, repeatedly, which will change the contents of the frame’s parent page
How Much Work to Perform?
How many jobs should you process for each execution? That’s a good question, and there are two schools of thought If you have the job script load all of the jobs that it is eligible to process, there are two side effects.First, you will have a dataset of jobs to process before script execution completes If the administrator changes his mind at some point after the script begins to run, and before a particular job runs, it’s too late to delay or rethink the execution without going in and killing the main process
Second, if you don’t want a process to run for a long time, you may wish to give the worker job fewer tasks, as
For long-running tasks, the script will timeout before the job is complete.
Trang 27Job Management with PHP & Cron
more tasks equates to longer runtime
My preference is to have each instance of the main
script load and process a single job This allows the
worker script to tackle a smaller task, and complete more
quickly On the downside, it means that if there are many
jobs in the queue you will have to wait many minutes
before a newly-inserted task begins execution
In the end, it’s a matter of finesse If jobs are likely
to take more than a minute each, and you have your cron
job set to execute the job script every minute, there is
nothing to lose by executing only one job per iteration
If your jobs are expected to last less than one minute
per execution, then you could either process all of the
available jobs on each execution, or split the difference
and limit the set of jobs to a smaller number (the number
of jobs expected to take one minute)
Calling long duration scripts via the Linux/Unix cron
mechanism means they launch quietly and they close
quietly As each is processed, its current status will be
exhibited by updating certain fields in the job database
table Scripts can also log their output by writing to an
activity log database table
Go, Caution, Stop
In the example job script, we’ll use three general result
codes for each leg of the job script: green indicates that
everything went well, yellow means that the job passed,
but there were problems, and red when an action has
failed You may choose to use only two statuses—success
or failure—or you may have more finely defined levels
Our three statuses are named “Status_Green”,
“Status_Yellow” and “Status_Red” These names are
used as both variable names and database field names
As each part of the job is processed, you could update
the database, but I think that’s too intensive Instead,
what I my script keep track of the $Status_Green,
$Status_Yellow and $Status_Red results It increments
the appropriate variable by naming $Current_Status
“Status_Green”, “Status_Yellow” or “Status_Red”, and
uses variable-variables to handle the switch
When you call $$Current_Status++ you increment
either $Status_Green, $Status_Yellow or $Status_Red
dependant on the value of $Current_Status
Then, every 50 iterations—when the modulus of
processed items (the $pass variable) is equal to 0, it
stops, does a database update to the “Status_” and
“Total_Processed” fields for the currently-running job,
using its job_id field as a key, resets the PHP variables,
and continues processing At the end of the processing,
it does this update one more time, thus passing the
remaining status updates into the database This
information isn’t used by the job script; it’s used by the
JavaScript generation page to update the admin control panel page
Job Log
For job, you may wish to see a quick notation about what happened during execution This data is nice when your script succeeds, but it’s critical when something fails Writing a note to a log will slow down processing, slightly, but if you consider that a fair tradeoff, then each part of a job should be annotated with a log entry.With the log available, the status/editing page can link to the current log for a particular job In the example job script (Listing 4), you can see the job log insert statement
The Display/Management Page
The admin user will see the status page For all intents and purposes, it’s the only visible part of this tool It loads data from the import job page, along with the current set
of statuses by joining two tables: jobs and job_status Hidden on this page is an iframe: 1 pixel by 1 pixel in size This connects another document to the admin page, but the secondary document is effectively invisible The
parent and iframe documents can communicate with each other through JavaScript The iframe document can refresh, frequently, and the parent page appears static even though its data is changing in real time through function calls spawned from the nested frame
The status display page is very simple in its functionality; it lists all of the jobs available in the database, and the script contains a means to update existing or insert new jobs As you see fit, you can elaborate
on the functionality to suit your own purposes
If we have form input available, the form data will
be passed to the UpdateData($input) function If there is
an $input[‘job_id’] available, it will perform an update
If there is no positive id number, it will insert a new job into the jobs table
In this example, a joined SELECT from the jobs and
job_status tables yields all of the information we need to build the initial page with proper values and statuses The code shown in Listing 5 takes a database row and passes that array to the BuildFormCells($object) function This function uses the input to create HTML for output (and the edit form)
Objects in Use
For each line item, there are two form objects For each job, one form contains the status information The second form is for inline editing of a job’s settings, which we’ll get to a little later
The status information form uses a form object as
Trang 28a container, because that has the most widespread
adoption True, you could use DIV tagged elements and
alter the innerHTML of objects, but as you degrade through
browsers flavors and versions, this functionality will start
to get spotty Forms can be addressed by everything from
Netscape 2.0 and IE 3.0+ Through CSS, you can dress up
form elements like text fields to look like inline text—
right down to allowing only the embedded JavaScript to
alter the input fields Best of all, you can implant text in
text boxes, which makes them much more versatile than
graphical status bars
Getting Graphic
In addition to altering form field contents, we’ll look at
two ways to achieve a more graphic look for your status
updates You can either use a graphic (an image tag),
and then change it as the status changes, or you can
change the CSS dimensions of the form field to reflect
the progress of your job script The CSS method is nice
because once you open up the crayon-box of directives,
you can change background color, visibility, size and font
facing from the source Nevertheless, let’s discuss both
options
To make an image into a representative status bar, you
need to make its size relative to the page In addition to
positioning the image on the page you need to name it
appropriately In the example below, the graphical bars
are named “status_[id#]_[color]img” We immediately
make the image invisible While the form elements are
inside of their form’s container object, the images are
free-floating references within the document, itself, so
you want the names to reflect specific pieces of data
Let’s create the images:
<nobr>
<img src=”greenbar.jpg” width=1 height=12
style=”display: none” name=”status_1_greenimg”>
<img src=”yellowbar.jpg” width=1 height=12
style=”display: none” name=”status_1_yellowimg”>
<img src=”redbar.jpg” width=1 height=12
style=”display: none” name=”status_1_redimg”>
</nobr>
The <NOBR> will snug the three next to each other allowing
no part of the bar to wrap down to the next line The
images won’t actually show up, because the style has
been set to not be visible
After the first invocation of “UpdateStatus()”, eligible
bars will appear on the status page
You’ll also need to add the JavaScript code from
Listing 5
Perhaps you want to trick out the functionality of the
graphic display even further? Here’s an example of how
to do it What if you wanted to indicate that having more
than 20 red statuses is bad? One way to show off a dire situation is to change the image source for one of the status bars based on the status
Again, there’s JavaScript in Listing 5 to allow this (look for the part that changes the image’s src to
skulls.jpg.When the status changes for the worse, the image source changes; when it improves, it can change back While in this particular example, red > 20 is the qualifier, you may want the trigger to be red > green or
red > (green + yellow) meaning that a majority of the job has gone bad
To change the CSS, it’s simple to pull off:
8 $dbc = mysql_connect ( $dbhost , $dbuser , $dbpass )
9 || die( “Cannot connect to database” );
24 $status [- 1 ] = “Not Ready” ;
25 $status [ ] = “Not Done” ;
Trang 29Job Management with PHP & Cron
1 <?php
2
3 // Connect to the DB
4 // For simplicity, all of the DB calls happen in the
5 // main part of the script
6 DB_Connect ( “your_db” );
7
8 // if we have input, we can add it here:
9
10 $status [- 1 ] = “Not Ready” ;
11 $status [ ] = “Not Done” ;
28 $query = “SELECT * FROM job_log WHERE job_id = “ $id
29 “ ORDER BY log_id.started ASC” ;
48 echo “\t<td><b>” $status [ $row [ ‘status’ ]] ”</b></td>” ;
49 echo “\t<td>” $row [ ‘statsus_notice’ ] ”</td>” ;
50 echo “\t<td>” $row [ ‘import_date’ ] ”</td>” ;
61 $dbc = mysql_connect ( $dbhost , $dbuser , $dbpass )
62 || die( “Cannot connect to database” );
73 <! Feel free to dress this page up
74 as much as you choose >
document.forms[“status_”+id].element(“red”).style.width = (red+10) + “px”;
Another graphic option is to change the background image, based on the overall status of your jobs:
if (red > 20) {
document.getElementById(“status_” + id + “_redimg”).element(“green”).style = “skulls.jpg”; }
else { document.getElementById(“status_” + id + “_redimg”) src =
to “display: inline” This displays the cell, and allows editing
While more than doubles the size of the HTML served out to the admin, everything is available for editing and
it all comes from the same dataset that is used to display static information Also, by editing and/or hiding import job information, it allows the admin user to have second thoughts without departing from the general status page In other words, he can get status updates while he’s preparing an import job This sort of functionality
is becoming more common everyday—for example, Blogger’s post editing table of contents contains a similar mechanism
JavaScript Generation Page
The part of this system that gives the page the most flash is the JavaScript The code, itself, is very easy to pull off You run a SELECT query on the job_status table
to produce rows of data The data from those rows are output as arguments for number of parent.UpdateStatus()
statements; one for each row The resulting calls are bundled into a SendStatusUpdates() function that is executed by the HTML body’s onLoad event (see Listing 6) When this page is loaded into the iframe, it calls
UpdateStatus() in the parent page (the main status page) One thing to note: because of the XSS (cross-site scripting) risks, parent pages and their child frames can only communicate with one another if they are of the same domain
Trang 30MIKE DeWOLFE is a developer for international aid organization, the Communications Initiative (http://www.comminit.com/) He can be contacted via http://mike.dewolfe.bc.ca/ (you’ll have to forgive him for hosting his site on Microsoft platform).
This page is regenerated via a meta-tag refresh How
often this happens is at your discretion Each refresh
will incur a small amount of server load In my example,
the meta-tag refreshes roughly every 30 seconds My
code considers the number of rows it will update Its
count becomes the number of seconds ($timing++) After
creating the parent.UpdateStatus( ) statements, it
uses $timing = max(2,(10 - ($timing * 2))); to derive
a variable that is between 2 and 10, and it uses that as
the refresh interval (in seconds)
The Log Viewer
If you have selected to build this tool with the logging
functionality, include the code for the log (Listing 7)
From the admin/display page, include a link to “View
Log” This will spawn a new page that contains the
relevant log entry The log viewer generates a list of log
entries that match the job_id that was passed to the log
viewer as a variable in the query string
This log is intentionally brief and simple so that the
script can be tweaked out in any direction Rather than
display and hide a table of editing material, this space
could hold an iframe of content or a flash movie—or
both
Conclusion
Jobs can be set up to run at a particular time of day When the cron job and the time value for a job coincide, that can trigger the job Once the job has been processed, time and cron job may coincide again, but the status will have changed and so the job would not run again Aborting an operation that is currently underway is not a good idea, but such functionality could be added
If the application works with a database capable of committing transactions, blocks of operations could be rolled back when a process is aborted
This tool can be used to encapsulate large admin tools and lengthy jobs; or put a layer of protection between the client and applications in use
Trang 31Flying with Seagull
Oriented programming is recommended.
A web framework is a necessity when developing
a serious website Programmers should not
recreate basic web elements when great tools
to help them get the job done already exist
One of these tools, Ruby on Rails, garnered
much attention when it was released in July 2004 It
simplified Ruby development, separated data from
display, and made web development fun
In the last issue, and Part 1 of this article, we
introduced the Seagull Framework, and covered its basics
This month, we’ll walk through a practical example of
how to deploy these new skills
Creating an Example Module
To demonstrate how to extend Seagull, we’re going to
give a detailed example of creating a custom module
The module we’ve chosen is a “wish list” module A user
will be able to sign up and add/edit/delete items from
his wish lists Guests will be able to view the contents of
a user’s wishlist and notify the system if they’ve bought
an item on the list This example covers the basics of
module creation and we hope will be an enjoyable site to
PHP: 4.1, better 4.3, also works with 5 OTHER SOFTWARE: Database: MySQL, PostgreSQL and
Oracle are supported, but theoretically all databases supported by PEAR::DB (e.g MSSQL, SQLite or ODBC) can be used without problems
LINKS:
http://seagull.phpkitchen.com http://seagull.phpkitchen.com/apidocs/
http://pear.php.net/package/HTML_Template_Flexy
FLYING WITH
SEAGULL
CODE DIRECTORY: seagull2
create But first one more piece of theory:
Seagull provides a validate/process/display workflow, which simply means that all data that passes through the system must be filtered by the following methods:
Trang 32• validate: The raw $_REQUEST is passed in to this
method, validations are performed, and acceptable
data is mapped to an $input object
• process: If all data is valid, the $input object is
passed to the process method, which redirects to
the relevant action method Once data has been
manipulated, it is mapped to an $output object
If one or more validations have failed, all data
is deemed to be invalid, and is passed directly
to the display() method with appropriate error
messages This will most likely be presented back
to the user for correction
• display: This takes the data, whether valid
or invalid, adds a few system properties like
execution time, etc., and sends it to the template
engine for rendering into HTML
The first step to create the wish list modules is to
create the table in our database to store users’ wish lists
The SQL for this can be found in Listing 0
Go to Modules->Maintenance, click
“Rebuild Dataobjects Now.” This will create a file
in [path-to-seagull]/var/cache/entities/ called
Wishlist.php
Now we need to create our module You can either
choose a module that is similar to your needs and modify
it, or begin with a new one In the former case, just copy
the old module files and directories and rename them as
needed In the latter, you can either start from scratch
or use the “Module Skeleton Generator.”
This functionality is not meant for creating the
complete module (that is not yet possible) But it
creates the basic structure using a simple form, including
directories for classes, translations and templates, and
some basic files for your new module
Go to the “Create a module” section of the
Modules->Maintenance tab and select all the checkboxes except
Create Templates (check add, edit, insert, ini file, etc)
Enter Wishlist for the module name and WishlistMgr
for the manager name You will need to make sure the
[path-to-seagull]/modules/ directory is writable
If these steps were performed successfully, you will
now have the following files:
7 SGL :: logMessage ( null , PEAR_LOG_DEBUG );
8 $this -> module = ‘wishlist’ ;
9 $this -> pageTitle = ‘Wishlist Manager’ ;
10 $this -> template = ‘WishlistList.html’ ;
11
12 $this -> _aActionsMapping = array(
13 ‘add’ => array( ‘add’ ),
14 ‘insert’ => array( ‘insert’ , ‘redirectToDefault’ ),
15 ‘edit’ => array( ‘edit’ ),
16 ‘update’ => array( ‘update’ , ‘redirectToDefault’ ),
17 ‘delete’ => array( ‘delete’ , ‘redirectToDefault’ ),
18 ‘list’ => array( ‘list’ ),
24 SGL :: logMessage ( null , PEAR_LOG_DEBUG );
25 $this -> validated = true ;
26 $input -> error = array();
27 $input -> pageTitle = $this -> pageTitle ;
28 $input -> masterTemplate = $this -> masterTemplate ;
29 $input -> template = $this -> template ;
30 $input -> action =
31 ( $req -> get ( ‘action’ )) ? $req -> get ( ‘action’ ) : ‘list’ ;
32 $input -> aDelete = $req -> get ( ‘frmDelete’ );
33 $input -> submit = $req -> get ( ‘submitted’ );
34
35 // if errors have occured
36 if (isset( $aErrors ) && count ( $aErrors )) {
37 SGL :: raiseMsg ( ‘Please fill in the indicated fields’ );
38 $input -> error = $aErrors ;
39 $this -> validated = false ;
1 CREATE TABLE `wishlist` (
2 `wishlist_id` int(11) unsigned NOT NULL auto_increment,
3 `uid` int(11) unsigned NOT NULL default ‘0’,
4 `name` varchar(50) NOT NULL default ‘’,
5 `url` varchar(255) NOT NULL default ‘’,
6 `description` mediumtext NOT NULL,
7 `cost` float NOT NULL default ‘0’,
8 `priority` tinyint(1) NOT NULL default ‘0’,
9 `bought` tinyint(1) NOT NULL default ‘0’,
10 PRIMARY KEY (`wishlist_id`)
11 ) TYPE=MyISAM;
LISTING 0