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

Tài liệu Simple, Affordable, Reliable PHP / MySQL Web Hosting Solutions docx

64 380 0

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

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

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Simple, Affordable, Reliable PHP / MySQL Web Hosting Solutions
Trường học Nexcess.net Internet Solutions
Chuyên ngành PHP / MySQL
Thể loại Tài liệu
Năm xuất bản 2025
Thành phố Ann Arbor
Định dạng
Số trang 64
Dung lượng 4,38 MB

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

Nội dung

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 1

TM

Trang 2

SITEWORX 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 3

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

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 6

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

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

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

mail() 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 11

mail() 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 12

shouldn’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 13

Tips & Tricks

Trang 14

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

1995, 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 16

of 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 17

PHP & 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 18

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

PHP & 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 20

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

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

1 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 24

1 <?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 25

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

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

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

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

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

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

Flying 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

Ngày đăng: 21/12/2013, 12:15

TỪ KHÓA LIÊN QUAN

w