Many modules, for example, depend on Flexinode, a module that allows non-programmers to create new node types from within the content types menu, without having to custom load database
Trang 2A simple way to implement
logging in your applications
by KNUT URDALEN
A database solution to simplify your code and
help you create high-performance applications
by PETER LAVIN
SPECIAL REPORT
PHP Version Tracker Report
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
CONTENTS
WRITE FOR US!
Trang 4Graphics & 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
If you follow PHP releases, closely, you’ll have noticed that PHP 5.1.0
was released on Thursday November 24, 2005 This release was billed as
paramount in PHP’s history, touted by many core developers as “the first
solid PHP 5 release.”
This supposed banner release was quickly overshadowed on the developers’
mailing list as problematic: a change that was made late in the release candidacy
cycle (a new internal Date class) will break hundreds, if not thousands (or perhaps
hundreds of thousands?) of deployed PHP applications It was generally accepted
knowledge that new functionality can’t break existing code, but this “knowledge”
proved simply incorrect
In this case, PEAR has an existing (and well-deployed) Date class of its own As
you know, PHP doesn’t currently have namespacing, so you obviously cannot have a
Date class in PHP and then declare a second one for PEAR
Much discussion ensued, including a strong push towards an accepted namespace
standard for PHP (perhaps we’ll see it in the 5.x series, and all signs point to
namespaces in PHP 6 at the latest) The really interesting discussion for me, though
was something along the lines of “why should core care about PEAR?”
Yet another political discussion It seems that the technical problems are
(relatively) easy to solve, and the real problem is on the social side So, should PHP
core care about PEAR? Tough call On one hand core should be given first-rights
to any sort of class, function, variable or constant name The other side (again) is
social: when most users download and install a package from PEAR, they do so from
pear.php.net As far as those users are concerned, they’re downloading both PHP
and PEAR from php.net, so they should work together
Unfortunately, there’s no easy and permanent solution to this problem However,
Ilia, the 5.1 release manager—and past author for php|architect—wisely reacted
quickly, and on November 28, 2005, PHP 5.1.1 was released, temporarily solving
this problem, by removing the new internal Date class One growing pain averted,
if only temporarily
PHP’s integration and many offerings from php.net gives it somewhat of an
“empire” status This metaphor is further extended when see how PHP has conquered
much of the web in its short lifetime Tips & Tricks takes a break this month to make
way for a special feature called “Where in the World is PHP?” In this piece, Damien
Seguy shares his company’s findings on how PHP has infiltrated nearly every market,
territory, and size of web application
This boom is no accident As you saw last month, PHP is making great strides
in the enterprise, and is shedding its “quick & dirty” reputation Long live the
dynamically typed variable!
THE
PHP
EMPIRE
Trang 6PHP 5.5.1
“The PHP Development Team would like to
announce the immediate release of PHP 5.1.1
This is a regression correction release aimed at
addressing several issues introduced by PHP
5.1.0, the core changes as follows:
• Native date class is withdrawn
to prevent namespace conflict
with PEAR’s date package.
• Fixed fatal parse error when the last
line of the script is a PHP comment.
• eval() hangs when the code being
evaluated ends with a comment.
• Usage of \{$var} in PHP 5.1.0
resulted in the output of {$var}
instead of the $var variable’s
checks inside the cURL extension.
Get your hands on the latest release at
php.net!
PHPBase Alpha
Phpbase.org announces their alpha release.
What is it? According to phpbase.org,
“PhpBase is a set of Open Source PHP classes and functions aimed to help developers submitting their data to Google Base.
The main purpose for a tool like this is the need to keep data submissions accurate and avoid common errors that might occur when submitting to standard schemes recommended by Google.
The code still in development, and will be added to a subversion repository once we get the project approved at berlios.de.”
Get all the latest info from phpbase.org.
PHP applications across the enterprise Zend Studio 5, the most widely used Integrated Development Environment for PHP, has been enhanced with new features to assist
in the development, debugging, testing, and deployment of PHP applications, including easy integration with other enterprise applications.
Available in three editions; Standard, Professional, and Enterprise, Zend Studio 5 includes support for PHP 5 as well as an easy switching mechanism allowing users to move between PHP 4 and PHP 5 for full application development, and also seamlessly integrates with Zend Platform 2 to provide a complete development and deployment solution for business-critical, PHP-based applications.” Visit Zend.com to grab the latest release
and check out all the latest features.
OBM-Open Business Management 1.0.2
OBM is proud to announce the latest release
of their Business Management application version 1.0.2.
Need a free management application for your business? Check out OBM According
to OBM’s site:
“BM is an Intranet application which goal is to help manage a company or an organization
news
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 7Looking for a new PHP Extension? Check out the latest from PECL.
hash 1.0
Native implementations of common message digest algorithms using a generic factory method.
ps 1.3.3
ps is an extension similar to the pdf extension but for creating PostScript files Its api is modelled after the pdf extension.
Fileinfo 1.0.2
This extension allows retrieval of information regarding vast majority of files This information may include dimensions, quality, length etc.
Additionally, it can also be used to retrieve the mime type for a particular file and, for text files, the proper language encoding.
sdo 0.7.0
Service Data Objects (SDOs) enable PHP applications to work with data from different sources (like a database query, an XML file, or
a spreadsheet) using a single interface.
Check out the hottest new releases from PEAR.
2) Find the file you need Each function
is in its own file, e.g array_walk_
Log 1.9.3
The Log framework provides an abstracted logging system It supports logging to console, file, syslog, SQL, Sqlite, mail, and mcal targets It also provides a subject - observer mechanism.
File_SearchReplace 1.1.0
Provides various functions to perform search/
replace on files Preg/Ereg regex supported along with faster but more basic str_replace routine.
Cache_Lite 1.6.0
This package is a little cache system optimized for file containers It is fast and safe (because it uses file locking and/or anti-corruption tests).
Net_DNS 1.0.0rc3
A resolver library used to communicate with
a name server to perform DNS queries, zone transfers, dynamic DNS updates, etc
Creates an object hierarchy from a DNS server response, which allows you to view all
of the information given by the DNS server
It bypasses the system resolver library and communicates directly with the server.
HTML_QuickForm_
advmultiselect 1.1.0
The HTML_QuickForm_advmultiselect package adds an element to the HTML_ QuickForm package that is two select boxes next to each other emulating a multi-select.
tracking sections) but can be used simply as a
contact database or as a shared calendar.
OBM represents a framework above which
many modules are written.”
Interested? Get more info from
• Full PHP 5 support, phpDocumentor
both runs in and parses Zend
Engine 2 language constructs
Note that you must be running
phpDocumentor in PHP 5 in
order to parse PHP 5 code
• XML:DocBook/peardoc2:
default converter now beautifies
the source using PEAR’s
XML_Beautifier if available
• inline {@example} tag - this works
just like {@source} except that it
displays the contents of another file.
• customizable README/
INSTALL/CHANGELOG files
• phpDocumentor tries to run
.ini files out of the current
directory first, to allow you to
put them anywhere you want
• multi-national characters are
now allowed in package/
subpackage names
• images in tutorials
• un-modified output
• html/xml source highlighting
This release also contains many bug fixes.
Get the latest info and start documenting at
phpdoc.org.
Trang 8an introduction
In their every day development, most web
consulting companies encounter many of the
same components when designing pages for
non-profit organizations and activist groups These
requirements include forums, a donation system,
membership management, newsletters, and articles,
just to name a few Many of these organizations also
have small to mid-size e-commerce needs, to effectively
sell and distribute promotional materials, or to handle
signups for paid events
Developers often tackle this task by rolling their own
systems in an attempt to make their sites as customizable
as possible, while still providing core components to
build on for each individual client In theory, it sounds
like a good idea In practice, the limitations of their
underlying frameworks ultimately result in nothing more
than a mash of hacks that only loosely integrate the
unique elements required for their clients
In the worst case, web development shops end up
coding the entire thing from scratch, each and every
Organizations these days are demanding
content management applications,
from company homepages to large,
community websites Let Drupal help
you build these sites quickly and
efficiently by building on a common,
modular framework.
PHP: 4.4 O/S: Debian OTHER SOFTWARE: Drupal 4.6.3
by TITUS BARIK
Trang 9time Somehow, rolling your own system
never quite works out the way it needs
to Even if you have two well-written
components, it is often the case that
it’s not the components themselves that
are the problem, but the integration
points that add burdensome complexity
Integrating disjoint components, say
Forum A with Shopping Cart B, has until
now, been a difficult problem to solve
This article introduces Drupal, a
modular, open source content management
platform that attempts to address these
difficult challenges Drupal provides
a flexible, extensible framework—an
alternative to roll-your-own content
management solutions It can be refit for
a variety of different content systems,
including community portal sites, personal
weblogs, and resource directories, simply
by adding and removing sophisticated
modules
Drupal offers you a working, tested
framework for building components, and
handles the insignificant but tedious
details of module management, user
management, and integration, so you
can focus on developing the core of your
project More importantly, Drupal has
a very large user base, and modules to
handle most common functionality are
already available When these modules
don’t exist, the hooks system allows
the developer to create custom modules
to interact with the Drupal core, while
leveraging the existing Drupal building
blocks
It’s not at all surprising, then, that
the focus of this article is not on complex
development or PHP code, at least not
directly In fact, it’s quite the opposite In
this article, we’ll develop a mock political
organization’s web site, Democratica,
without writing a single line of code
Along the way, I’ll demostrate the various
built-in and contributed modules that allow you to get a site up running quickly and effortlessly
Installation
This section discusses the installation and configuration details of Drupal, including the pre-requisite software requirements Our test installation consists of Drupal 4.6.3, PHP 4, and Apache 2, running on Linux with a MySQL 4.1 database server
Though Drupal supports both MySQL and PostgrseSQL, be advised that most available third-party modules are coded specifically for MySQL This effectively forces you to use the former, regardless of
what the Drupal core modules support
Drupal allows you to host multiple sites on a single Drupal instance These sites can be found under the sites folder For simplicity, I’ll only utilitize a single site setup in this article
After creating and assigning the appropriate permissions to your database, load the tables:
mysql -u nobody -p drupal < database/database.mysql
Next, enter the sites/default folder and modify settings.php; namely, modify the db_url, and base_url
Drupal works best on dedicated hosting systems Since Drupal
FIGURE 1
The first screen you’ll encounter after installing Drupal
Trang 10memory requirements increase proportionally with the
number of active modules, the default PHP memory limit
of 8 MB provided by most shared hosting providers is
typically not enough To be safe, set this value to 32M
or 64M in php.ini If the memory requirements are not
successfully met, Drupal will behave strangely, displaying
broken administrative menus and erratic page rendering
Next, add the following to your crontab:
0 * * * * wget -O - -q http://democratica/cron.php
This allows for scheduled events to trigger properly
within Drupal Such events include search indexing, mass
mailing, and scheduled publishing
That’s all there is to the installation Navigate to
your Drupal site using your web browser, and you’ll be
presented with a base page, as shown in Figure 1
Finding Your Way
Around
It’s time to create your first
account User #1 is provided
with full administrative access
All subsequent users are given
authorized user access Additional
groups can be generated though
the Drupal system as well, providing
fine granularity amongst different
group types In Democratica, for
example, we may want special
access for employees, and different
access priveleges for members and
guests,
After logging in, select the
administer menu You’ll be presented
with Drupal’s recent system events,
a web-based logging system for
monitoring the activity on your web
site A typical administration page
is shown in Figure 2 For now, enter
the settings menu, and change the
Administration section of Drupal
default name of the site to Democratica You should also set your time zone Feel free to modify any other settings, as well
Perhaps the two most useful menus under administer are blocks and modules The modules menu allows you
to extend the core functionality of Drupal Within this menu, you’ll activate and install module components
to tailor the functionality that your site requires For example, you could enable search, forums, events, and so forth The blocks menu enables and disables boxes that can be positioned on the left and right side bars of your web site The available blocks vary, depending on which modules are currently active Under modules, notice that the page and story modules are checked by default
Now, jump up to the create content menu You’ll notice
that there are two available content types: page and
Trang 11story, the same as the modules we previously witnessed
Enabling content modules will add additional content
types to this menu After initial setup, most interaction
with the Drupal site will occur through this menu
Finally, let’s examine the content menu, again under
administer This menu allows us to configure the properties
of content nodes, which at first, can be a bit perplexing
Unlike a dedicated content system such as WordPress for
blogging, or osCommerce for e-commerce, Drupal aims to
be flexible, to suit any type of web platform In order to
provide such flexiblility, the fundamental type of almost
all content is that of a simple node Custom behavior
is then implemented by simulating the base node type,
and subsequently adding or removing properties to that
node type The content menu allows us to set additional
properties, modify nodes, and add custom nodes to
facilitate our development process
Democratica Theme
In Drupal, themes are rendered through the use of
template engines Unfortunately, Drupal by default only
provides xtemplate, and the Democratica theme that
we’re interested in requires PHPTemplate Luckily, this
takes very little effort Simply download the PHPTemplate
engine from the Drupal web site, and extract it to the
themes/engines folder Next, download and extract the
Democratica theme, and place it in your themes folder
Login to your web site as administrator and access
the themes menu The Democratica theme is now
automatically available Set it to default, and save your
configuration Your new theme is now applied
Theming is a powerful component of the Drupal
framework that will greatly streamline development
Namely, it enables you to parallelize your backend
business logic and your presentation code Your graphic
designers, for example, can independently develop multiple, pluggable layouts, while your logic developers can write custom modules and other core functionality
Modules
The remainder of this article will focus on modules The process of installing some modules is more involved than others, though none of the modules are exceptionally difficult to integrate The typical installation procedure for a module is as follows:
First, load the database schema This is usually one or more iterations of the following command:
mysql -u user -p database < schema.sql
Next, copy the module to the modules directory Then
activate the module in the administer modules menu.
Then, set configuration parameters for the module
under the administer settings menu, and configure access
control permissions on the module through the access
control menu
Finally, if the module has a presentation layer, enable
the block in the block menu, specifying its location and
ordering
Some modules have dependencies on other modules
Many modules, for example, depend on Flexinode, a
module that allows non-programmers to create new node types from within the content types menu, without having to custom load database node schemas for each new node type
Before continuing, install and activate the Flexinode module A content type menu will appear under content, and a flexinode menu item will appear under settings.
Unlike a dedicated content system
such as WordPress for blogging,
or osCommerce for e-commerce, Drupal aims to be flexible,
to suit any type of web platform.
Trang 12Our first module will be a simple one Most political
organizations have a need for donations, and our site
is no different The Donations module allows us to
accomplish just this goal, with seamless
integration to the Donorge.org donation
service Under settings, modify the
text as desired Since this isn’t a live
site, the donation ID doesn’t actually
matter
Nothing appears to have actually
happened, but that’s because we
haven’t told Drupal to place the
content block on the web site Let’s
do that now Click on the blocks
menu, enable Donations, and place it
on the right sidebar A third column
on the Democratica theme will now
be enabled, with your new donation
module, as shown in Figure 3
Without having to modify a
single line of code, we’ve managed
to add donation functionality to our
Democratica site Reusable components
such as the donations module are what
make developing under the Drupal
framework so efficient
Newsletter
After interviewing several non-profit
organizations, the most demanded
feature next to donations is that of
newsletters Newsletters, when used
effectively, are a vital method of
directly addressing your members,
notifying them of important news and
issues Our political organization, in
particular, wants to let our subscribers
know about the latest campaign news
Drupal provides such functionality
through the simplenews module Install it now Like the donation module, it will appear as though nothing has changed Click the new newsletters menu item
under administration; this will create a default Drupal
newsletters Under the newsletters
tab, click edit type for Drupal, and change the name to “Democratica Newsletter.” The Newsletters interface allows you to create multiple newsletters, track the progress of newsletters, and track subscribers, as shown in Figure 4 Under blocks, activate the newly created newsletter, placing it on the right sidebar
Next, create a newsletter
from the create content menu
Creating a newsletter item is simple enough that it doesn’t require any further explanation here Assuming your PHP configuration and mail server are setup correctly, all subscribers will receive an e-mailed copy
of the newsletters you’ve just posted
Survey
Our political organization wants
to collect data that may help in predicting the outcome of the next election To do this, they would like to create a survey, asking various questions on the web site At first glance, such
a task seems like a great deal
of work We’d have to design
a backend database to store
a variety of survey questions,
FIGURE 5
Election survey.
FIGURE 6
Election survey, administrator perspective
Drupal provides an excellent, out of the box, event handling system.
Trang 13modules In addition, activate
the cod module and the tangible
module, for tangible products
Under create content, a product
item will now appear Here, you can set the product title, description, price, and the type
of product For Democratica, the fulfillment house handles all inventory, and we thus disable inventory management
Under the block menu, enable the Shopping Cart module Under the create content
menu, create your product Once completed, it will appear in the top-level products menu From here, you can add it to your cart and checkout The e-commerce module can and probably will need to be customized through PHP to suit your particular needs However, the provided shopping cart already provides much of the required base functionality
Events
Democratica can’t be successful
in rallying support if it can’t successfully organize events Drupal provides an excellent, out of the box, event handling system
The event module relies on the Flexinode module, which was
installed earlier in this article A
close cousin of the event module
is the location module, which allows for additional location
information
The location module will add the final touch to
our event system Currently, the location module fully supports United States zip codes The module provides routines for conducting postal code proximity searches, linking to Google maps, and other functions for collecting locations Follow the directions for installing this module, carefully This module is unique in that it requires you to load three mysql data files: a location schema, a zipcode schema, and US zip code data Then, just as we’ve done
for all other modules, activate it through the module
menu
implement forms on the front-end,
and provide facilities to allow the
organization to collect and download
the data in a user-friendly format In
addition, we would have to provide
some sort of dynamic form creation
system to allow users to create their
own surveys
Unsurprisingly, Drupal makes this
task simple and reusable through the
survey module The survey module
depends on the forms module, which
provides an API for adding
user-customizable form elements within
modules First, install the form
module and activate it Then, install
and activate the survey module.
Under the create content menu, the
survey item will appear, in addition to
the standard story and page items
Let’s now create a survey called
“Election Survey.” After entering
the general information, you’ll find
yourself in a survey menu, from where
you’ll go to the form tab
Add a radio field We’d like to
know which political party you belong
to Under selection options, enter
“Republican; Democrat; Libertarian”
The semi-colon is used to delimit
the multiple options for the given
field type Add any additional desired
fields A non-administrator user will be
presented with the survey, as shown
in Figure 5 As an administrator, your
window will appear more like that in
Figure 6 When a user completes a
survey, his or her results can be found
under the responses tab.
E-Commerce
Throughout the political campaign, our organization
wants to sell tangible items through its fulfillment house
These items include bumper stickers, signs, buttons, and
so on
The e-commerce package includes several modules
Some of the modules are located in Figure 7
E-commerce preferences can set under the settings
menu Among other options, items include things like
whether customers must create an account to order,
shipping methods, and payment methods
For our purposes, activate all of the required
Trang 14After enabling both modules, access the block menu
and enable List of upcoming events An empty upcoming
events listing will appear, depending on your sidebar
selection
Unlike most of our previous modules, an event is not
a node type in itself Specifically, event properties can be
added to any node type So, for example, you can create
your own Generic Event node type using Flexinodes, or
you can create a Birthday node type for birthdays, and so
on, to describe different categories of events
Let’s create a Generic Event node now Click on content
type in the content menu under administration, and click
the add content type tab Call our content type name
“event,” and give it a short blurb in the description
You’ll now be placed in the list tab, where you can add
additional fields to your custom node Title is already
included, so let’s add a text area for the event description
After following these steps, your screen should resemble
Figure 8
Though we’ve created a generic
event node, we have yet to bind
the event module properties to
it To do so, click on the content
menu, then the configure tab
From here, click the content type
subtype, where you’ll find the
configure option for the event
Yes, it’s deeply buried under a lot
of menus, so look at Figure 9 for
assistance
Under configure, scroll down
to “show in event calendar.” Set
this to all views to enable event
times for the generic event node
that you’ve just created Under
the blocks menu, enable “List of
upcoming events.” In addition,
select “Enable for location,” a
feature contributed by the location
module Set “Allow” for street, city,
state, postal, and country
You’re on the final stretch It’s
time to actually add an event Under
create content, you’ll now find an
event item Fill in the details for
your event In our example, we’re
celebrating the Democratica Launch
Party, shown in Figure 10 Notice
also, that the event has been
added to the upcoming events list
This is where the RSVP module comes in RSVP lets users invite people to attend an event It sends an invitation email to a list of people, and can the track which users have looked at the invitation and their responses Invitees can view and reply without having user accounts
After enabling the RSVP option, a Create RSVP link
will become active on all events From here, you can invite selected guests to your event An invite preview is shown in Figure 11
Other Modules
Our last module is as big one CiviCRM is a web-based,
open source, internationalized, constituent relationship management (CRM) application, designed specifically to meet the needs of advocacy, non-profit and non-governmental organizations
In short, it keeps track of people
CiviCRM is large enough to be
a product on its own It is also the most complicated of modules
to install, and its installation instructions would comprise an entire article by itself The CiviCRM module is also a dependency for the other modules which require contact management, and it is also an optional requirement for the event finder module, which allows the user to search for events based on event type, geographic location, and proximity to major metropolitan area
Thankfully, the OpenNGO web
site provides excellent installation
instructions for the CiviCRM module
Here, I’ll omit the installation steps and show you the brief demo of the
CiviCRM and Event Finder modules CiviCRM primarily enables
the Find Contacts feature, shown
in Figure 12 It also allows you
to manage groups, relationship types between contacts, and
Trang 15TITUS BARIK is a content application developer with an interest in open source Enterprise solutions He has deployed both open source and proprietary content management systems successfully in corporate and non-profit environments His personal weblog is available at barik net, and he welcomes your comments and suggestions.
For those who don’t
have in-house contact
management tools,
CiviCRM may be a good
option
Event Finder takes
the functionality of the
existing events module
and adds the ability to
actively search for local
events by category,
event type, and zip
code It also enables
online registration for
events, where one can
specify the maximum
number of registrants
Conclusion
The final result is shown
in Figure 13 In the
course of about an hour
or two, you’ve created
a dynamic site that
each and every one of
these components for
your specific project,
the time saved in
comparison to writing
a custom solution from scratch is simply tremendous
Drupal is a large and powerful framework, and you’ll
no doubt run into a few obstacles here and there, along
the way When you do, the Drupal Handbook is available
online for your use The Drupal web site also provides
forums to address questions and issues that aren’t
covered by the Handbook
Sites that have successfully implemented Drupal
include Spread Firefox, Kernel Trap, Linux Journal, and
The Onion I hope that you’ll be next
Trang 16When we write a PHP application, it may
rely on a remote program that performs
certain operations what we can’t, or don’t
want to do locally
For instance, we may need to fetch some up-to-date data about, the weather in Los Angeles
Since it is unlikely that our application will have access
to this data through a local database, we must somehow
communicate with the web site of the local meteorological
station in L.A
We have a similar problem if we want to make our
user perform a web search without leaving the pages
of our site—our PHP application must send a query to
(e.g.) the Google Application Program Interface, and
retrieve the response
Perhaps you want to implement an online shop, with
a payment system based on PayPal; you’d have to set up
secure communications between your site and the PayPal
servers
Finally, you may have a local database of user names,
mails and contacts and want to make some of this
information available to a certain number of authorized
users
All of the examples above require the use of some sort of communication with a remote application: what
we are looking for are Web Services
A web service is a software system designed to support interoperable machine-to-machine interaction over a
Trang 17network So, when we talk about WS, we are implicitly
referring to many parts:
• A client application, which can be written
in any language (e.g C, Java, Python, and
of course PHP) and run on any Operating
System
• A remote application, with which we want to
communicate
• A remote interface, often described by WSDL
(Web Service Description Language)
• A protocol, normally based on TCP/IP—the
most used are HTTP and HTTPS
• A document to be sent or received, which is
usually encoded using particular XML subsets
like SOAP (Simple Object Access Protocol) or
XML-RPC (Remote Procedure Call)
In Figure 1 you can see an example of an application
which connects to a remote Application Program Interface
to fetch data from its database: while the communication
between the program and the user (at least, his browser)
is encoded in HTML, the traffic between the two machines
is transported via SOAP
This article is a short introduction to web services
Our purpose is to explore what we can do with them
and how they work We will have a closer look at
SOAP-based web services over HTTP, as they are the most
common, and are pretty easy to use We will also take a
look at a sample application which consists of a client
& server system, and performs a simple operation (in
our case, retrieves data from a remote MySQL database)
Out sample takes advantage of NuSOAP, a predefined PHP
library, designed to perform Remote Procedure Calls
Before we get into the real work, however, we will
cover SOAP and WSDL basics, without getting too deep,
just to make things a bit clearer We won’t, however,
see any complex practical application of Web Services In
fact, this article’s intent is introductory If you’re looking
for a little more meat, you should take a look at the
upcoming php|a NanoBook, “Practical Web Services,”
which I will quote several times in the following text
Simple Object Access Protocol:
an overview
SOAP is an XML-based protocol, created by the World
Wide Web Consortium It was created to help server
applications exchange structured and typed information
in a distributed environment A SOAP message can be
sent on any Internet protocol (HTTP and HTTPS are the
most common)
What makes SOAP so powerful is that the application
that receives the message doesn’t need to be written in
the same programming language, or even to be run on the same operating system as the one that originated the request The platform is simply irrelevant—so you could easily make a NET application exchange data with one written in Python
A SOAP message is normally composed of three
elements: an envelope, a header and a body When dealing with an error condition, a SOAP fault element will also
be present
The envelope is the first element in a SOAP message, and it’s mandatory It may contain namespace declarations or additional attributes, which must be namespace-qualified It may also contain additional namespace-qualified sub-elements that must follow the
body element.
The header is optional and, if used, must be the first child element of the envelope It may contain a set of
namespace-qualified header entries as an immediate
child element of the header element, and it may also
contain general information about the message, such as language, date, etc
The body is mandatory, and must be an immediate child element of the envelope (and follow the header,
if it’s present) It may contain a set of body entries,
Trang 18which must be an immediate child of the body element
and each entry may be namespace-qualified This is the
most important part of the message, since it contains
the request or the response, and the part that is used to
perform Remote Procedure Calls (RPCs)
As I mentioned, a particular type of body entry,
named fault, is used to indicate error messages Its
sub-elements are used to report information about the error
(the error code and name, what caused it, etc.) Any type
of application may need to use this kind entry, so we will
examine it, too (briefly)
For a detailed description of the SOAP language,
check the World Wide Web Consortium documentation
at http://www.w3.org/TR/SOAP We won’t examine WSDL
in depth, as it is quite a complex topic, but we can’t
completely overlook it because of its importance—it’s
definitely worth a quick glance, if for nothing else, then
to understand its main purpose
SOAP Envelope element
This element must always contain a namespace URI to
define the SOAP versioning model The only valid namespace
URI is: http://schemas.xmlsoap.org/soap/envelope/
The envelope may also contain an encodingStyle
attribute to indicate the serialization rules used; here
are some examples of valid URIs:
• http://schemas.xmlsoap.org/soap/encoding/
• http://example.com/enc/spec/
• http://www.example.com/enc/
As you can see, you can use more than one URI
separated by a space (ordered from the most specific to
the least specific) A blank URI means that there are no
claims for the encoding style
Here is an example of a valid envelope element:
It has namespace and encoding style declarations We
will insert our other elements or sub-elements within
this main tag
SOAP Header Element
The SOAP header is qualified by its local name and
namespace URI It is optional, and may contain general
information about the message The encodingStyle
attribute may be used to indicate the encoding style
Two attributes are frequently used in this element:
mustUnderstand and actor
The mustUnderstand attribute indicates whether the
header entry must be processed by the recipient Its value can be 1 or 0 (if it’s absent, a default value of 0 is assumed) If it’s set to 1, the entry must be processed.The actor attribute is useful for messages that travel from the originator to their final destination by passing through one or more intermediaries (SOAP servers can have the ability to both receive and forward a SOAP message) actor specifies which of these intermediaries should receive and process the header entry (which, when processed, will be no longer forwarded) The value of actor is the URI of the recipient There are, however, two special cases: if the attribute is absent, then the recipient automatically becomes the recipient; if the attribute
is set to http://schemas.xmlsoap.org/soap/actor/next the destination is intended to be the first intermediary
SOAP Body element
The body element is used to send RPC calls or responses, and to report errors A body entry is defined by its name, composed by a valid namespace URI and a local name Immediate child elements of body element may be namespace-qualified
When sending the message over HTTP (as we will), the POST method should be used to both send the call and obtain the response In PHP, we can do this using the socket functions The content type we should specify
is text/xml, but text/plain also works The encoding depends on the application that we are dealing with
In the first part of Listing 1, there is an example of
a valid RPC call, performed using SOAP In this message, there is no header element, and that’s OK, since it is optional Look at the body element: in sub-element GetPrice (defined by the URI Some-URI), we store the product code and name, to obtain the price The second part of Listing 1 contains a possible response
Note that this is the bare XML response message, without HTTP headers (a few lines that the server sends
to indicate whether the operation was successful and additional information like date and content-length).This is a very basic example of a SOAP RPC call and response Real-life applications usually manage a larger amount of data, including arrays of sub-elements, which makes things more complex I’ve intentionally kept this example simple, to make things a bit clearer
SOAP Fault Element
If, during the execution of the remote application, an error occurs, preventing the program from producing the expected output, it is necessary to inform the client of
what happened This is exactly the purpose of SOAP fault element It is a child element of the envelope and the
Trang 191 # Dump of “transactions”
2
3 CREATE TABLE transactions(
4 tid INT UNSIGNED NOT NULL AUTO_INCREMENT ,
5 cid INT UNSIGNED NOT NULL ,
6 description VARCHAR(30) NOT NULL ,
7 amount FLOAT(7,2) UNSIGNED NOT NULL ,
8 STATUS ENUM(‘pending’,’failed’,’OK’)
9 DEFAULT ‘pending’ NOT NULL ,
10 PRIMARY KEY (tid)
5 $s = new soapclient ( “http://host/server.php” );
6 /* Creates a client object */
7
8 $params = array( ‘tid’ => 4636 , //Transaction ID
9 ‘cid’ => 1059 ); //Customer ID
10
11 $res = $s -> call ( ‘get_transaction’ , $params );
12 /* Invokes the remote method */
13
14 if(isset( $fault )) // If a fault occurred
15 die( $fault ); // Prints it and exits
16
17 if( $params [ ‘tid’ ] != $res [ ‘tid’ ])
18 die( ‘Transaction ID does not match’ );
19 if( $params [ ‘cid’ ] != $res [ ‘cid’ ])
20 die( ‘Costumer ID does not match’ );
21 /* Checks tid & cid */
4 if( $_SERVER [ “REQUEST_METHOD” ] != “POST” )
5 exit; // Check whether the req method was POST
16 if(! is_int ( $tid ) || ! is_int ( $cid ))
17 return new soap_fault ( “Sender” , “Input err” ,
18 “ID type must be INTEGER” ); //DataType fault
19
20 $q = “SELECT tid,cid,amount,status FROM transactions”
21 ” WHERE tid = $tid AND cid = $cid” ; //MySQL query
22
23 if(( $conn = @ mysql_connect ()) === FALSE )
24 return new soap_fault ( “Receiver” , “MySQL” ,
25 mysql_error ()); //MySQL connect fault
26
27 if((@ mysql_select_db ( “liceogiovio_” , $conn )) === FALSE )
28 return new soap_fault ( “Receiver” , “MySQL” ,
29 mysql_error ()); //MySQL selectDB fault
30
31 $res = @ mysql_query ( $q , $conn );
32 if( mysql_num_rows ( $res ) != 1 )
33 return new soap_fault ( “Sender” , “Bad IDs” ,
34 “No record matches the IDs” ); //No results
35
36 $resp = @ mysql_fetch_array ( $res , MYSQL_ASSOC );
37 @ mysql_close ( $conn );
38 $resp [ “time” ] = time (); //Add time() value
39 return $resp ; //Returns the result
A fault mainly consists of a mandatory standard error
code named Code, which identifies the problem (e g Sender or Receiver to identify the party responsible for the fault) and a mandatory Reason explaining what went wrong (e g Timeout) It may contain additional information, like Node which explains which node of the SOAP message path caused the error, and Role, which
is used to describe the role of that node Additionally,
a Detail element is available to explain precisely what went wrong
We will actually use these fault sub-elements in our
A WSDL document contains all the information needed about how to reach the web service—the methods to be used, the parameters they require and the type of output they will produce
If you want to learn more about WSDL, you can check the World Wide Web Consortium page about it at
http://www.w3.org/TR/wsdl
How to Write an Application
The application that we will write consists of two parts: client.php (Listing 3) is designed to send a request
to a remote application and fetch its response, while server.php (Listing 4) will receive the request, process
it, and produce output to send as a response
As we have seen in the previous paragraphs, SOAP
is simple enough that we could write the request
“manually”, simply by concatenating strings and variables The problem comes when we have to parse an XML document: there are several PHP functions designed for this purpose, but a parsing application is often quite complex to write, especially when receiving long and complex messages
Fortunately, there are open source libraries which can handle the tricky part for us This will allow us to spend more time on the other parts of our application—security, for example, is the most important aspect of a Web Service!
Of course, writing our own code is generally the best approach, as in doing so, we are fully aware of how it
Trang 20If, during the execution of the remote
application, an error occurs, it is necessary
to inform the client of what happened.
works and how to optimize it But this takes a lot of
work, and may introduce some errors in our app—it’s not
only a matter of laziness
So there isn’t a best way of coding—avoiding libraries
or using them—as it all depends on our particular situation
and time limits In this article we will use NuSOAP, an
open source PHP library, to simplify our task
PHP NuSOAP library
NuSOAP, a group of classes designed to allow developers
to manage SOAP web services, is licensed under LGPL and
is available for free at http://dietrich.ganx4.com/nusoap/
All we have to do to use it is to include nusoap.php in our
scripts, by using require()
Writing a client with this library is really easy Instead
of building a SOAP message and sending it to the remote
API we can just pass to a class the variables we need and
instruct it to send the SOAP-encoded variables over HTTP
or any of the most common TCP/IP protocols
Let’s see an example:
First of all, we include NuSOAP, then we store the
parameters we will use for the RPC in the associative array,
$params We create an object passing two arguments:
the SOAP server address $remote (a string) and $wsdl, a
boolean value which indicates if the server uses a WSDL
document
As we have seen, a WSDL (Web Services Description
Language) document contains information about the Web
Service, its methods and properties and is often used by
Web Services, especially if they reach a certain degree of
complexity
Once we have created the object, we only have to
execute the RPC: we invoke the call() method, specifying the remote method name and the parameters to be passed (contained in $params) NuSOAP automatically fetches the result and stores it in the $result associative array
If we are working with a WSDL-based server (and consequently $wsdl is set to TRUE), once created, $clientcan also invoke the remote method in this way:
<?php
$proxy = $client->getproxy();
$results = $proxy->getbyid($params);
?>
This can be useful to simplify our code: first we create
a proxy client ($proxy), then we can invoke any remote
method specified in the WSDL document using the proxy, without having to use NuSOAP call() method
Now, let’s see how to configure a basic remote interface with NuSOAP:
we pass to the server object all of the HTTP POST data we received, and it will automatically produce output
Designing the Application
Remember, our application will consist of two PHP files, both based on NuSOAP library: client.php and server.php
We won’t use a WSDL file, as our web service will be very basic Let’s have a look to his structure
In client.php we will send a SOAP message to retrieve
Trang 21information about a transaction, sending a transaction
ID (tid) and a customer ID (cid) as parameters to the
remote method, get_transaction() We expect the remote
application to send back a status code (“OK”, “pending”,
“failed”), the amount of the transaction, and the two
IDs (to verify that no errors have occurred) In addition,
server.php will append the current value of date() to
those variables
In server.php, we will first check that the remote
method has been invoked in the right way (e.g via
POST), next that all the data required is provided So, we
try to retrieve the data required from our database (we
will use MySQL), and send it back
In Listing 2, you can find the dump of the table we
will use
Writing the Client
Look at Listing 3: first, we include NuSOAP and create the
client object as we have done in the previous examples
Note that the $wsdl boolean parameter is absent: this
means that it is assumed FALSE and no WSDL file is
available for this Web Service
Next, we store the input data for the remote
application in the $params associative array Note that
if this had been a “real life” application, the input data
could have come from a form submitted by the user, from
the $_SESSION array, or from our database In order to
simplify our code, let’s assume that, no matter where the
data may have come from, it is now stored in this array
Now, the heavy lifting is done by NuSOAP: just one
line of code to build a request, send it, retrieve and parse
the response Sounds fantastic, doesn’t it?
Once the results are stored in $res, we check whether
any fault occurred (we analyze the $fault variable
created by NuSOAP), whether the IDs sent match the
ones returned, and eventually print the result
This was a really basic program, just to get started
with the simplest possible application of Web Services—
a sort of “Hello World.” Writing the server will be a bit
more difficult
Writing the server file
In Listing 4, you can find the code for the server
As the server file is not designed to be reached by a
browser, but only by a remote application via POST, we
first check whether the request method is POST; otherwise
the program exits We could also check the IP of the
remote application or require a username & password In
this case, remember that to ensure secure transmission
of data we must use HTTPS instead of HTTP
Next, we include NuSOAP, create the server object
($s) and register a method (get_transaction())
The interesting part of the code is this function It requires two parameters—the transaction ID and the customer ID—which we will use to build our MySQL query
The first step of the function is to check that the data type of the parameters is “integer”; otherwise, it will produce an error message This message is worth a closer look, as we don’t use die() but we produce a SOAP Fault document using NuSOAP soap_fault() method We specify three properties:
• the fault Code—which can be either the client
or the server—is set to “Sender”, as the problem lies in the wrong data type of the input the client sent
• the Reason, set to “Input err”,
• and the Detail, which explains the problem that occurred
Of course, using return to produce the SOAP Fault document we prevent get_transaction() from performing any other operation
Next, we build the MySQL query and store it in $q We connect to MySQL and select a database, verifying that each step was successful—producing a Fault if it wasn’t Once we have executed the query, we count the results returned An error occurs if N < 1, because this means that the query produced no result—likely because the transaction ID & customer ID sequence wasn’t correct—but also the case N > 1, which is even less probable, and represents an error, as each transaction must be unique
So, we join these two conditions as N != 1.Finally, we retrieve an associative array with the results, append a time element, in which we record the value of time(), when the operation has been performed, and return the $resp array All we have to do to execute the remote procedure call is pass the raw POST data as input to service()
So what our application will do, in the end, is fetch (over HTTP) information stored in the database of a remote machine, and make it available to our user It’s something really simple, but the underlying idea is the same at the base of Amazon or PayPal web services
A first step to Practical Web Services
This article is just a first step to the practical use of web services, as it may be regarded as a general introduction
to SOAP and to remote procedure calls There are many other things to say about SOAP, about writing a client
or server without using libraries, and also about using NuSOAP in a more sophisticated way
Of course, all of this would require a deeper theoretical introduction and—more importantly—many more
Trang 22ALESSANDRO SFONDRINI is a young programmer from Como, Italy His interests are mainly related to PHP and C, with a particular focus on Web Services Alessandro is the author of the upcoming php|a Nanobook, “Practical Web Services.” You can contact him at
alesfo@yahoo.it.
examples It is clear that an article is not the right place
to talk in an exhaustive way about a topic as elaborate
as web services
Furthermore, even if we could have dealt with the
topics above, we would still have been far away from
what mainly interests a PHP developer: how can I
use all these things in my job? What about payments
with PayPal, or online shopping with eBay? If you are
interested in any of these topics, or simply would like to
have a deeper view of PHP & Web Services, you might like
to know the topics of the upcoming NanoBook “Practical
Web Services”
Practical Web Services
The book overlooks a complete theoretical lesson about
SOAP: this article provided a brief one which is enough
to start working
As the title underlines, the attention is focused
on the practical aspects, so after a short introduction
to Web Services, we dive straight into some code In
fact, the majority of the work is composed of examples
of real-life applications, realized with many different
programming techniques and commented almost line by
line, sometimes with the help of pictures
Conclusions
Coordinating data with a remote application, or sharing local data with public or private clients is becoming more and more necessary as the Web evolves For these reasons Web Services are gaining a central position in the world
of web based application, and for a programmer is an often required skill
I hope that this article has been an useful introduction
to SOAP and Web Services, even if represents just a starting point for a developer interested in writing applications using these technologies and acquiring an important skill If you have any doubt about the content
of this article, drop me a note at alesfo@yahoo.it or leave
a message on php|a online discussion board
Trang 23In this article, I will demonstrate how you can
apply and maintain logging and monitoring in
your own web applications using log4php The
text is based on my experience with log4php in
some of our latest in-house products at Telio
The first part contains a brief introduction with some
sample code of core functionality; the second part covers
some more advanced topics and useful examples on how
to deal with errors, and how to apply context data to
logging events, as well as some optimization tips In the
last part, I will also show you how to create a centralized
logging server
Introduction
You never know what could happen with your code
Errors will always occur, either because of bugs in your
own code, or an external interface or resource going
down You could have lost your network connection to
the MySQL server, the hard disk could have become full,
there can be bugs in third party code, or there may be
bugs in your code that you did not catch before you
released your product
CODE DIRECTORY: log4php
Logging in PHP applications is traditionally done by using your own logging function to send a message to
a file or by mail, or through a small 3rd party logging class However, writing logging code with a configurable logging system lets you write this code once and allows the system administrator to decide what kind of messages
he wants to capture, and where those messages should
be sent When serving a mission critical application, you
Trang 241 <?php
2 require_once ‘log4php/LoggerManager.php’ ;
3 $logger =& LoggerManager :: getRootLogger ();
4 $logger -> debug ( “Hello World!” );
In most companies, application monitoring is the
responsibility of administrators or an operation team It
is critical for these attendants to establish procedures
and strategies on how to handle application monitoring
Communicating these strategies to developers within the
development team will help both teams work together
on improving logging and monitoring information that,
in the end, will help resolve small issues before they
become bigger problems
It is not an easy task to apply logging to a large
base of preexisting code A better approach is to make a
strategic decision early in the design phase to determine
the manner in which you will apply error logging,
monitoring and audit logging
What is log4php?
log4php is a port of the popular log4j open source logging
API for Java and has been around since early 2003
In looking at the adoption of log4php in open source
projects and activity on the project’s own mailing lists,
it does not seem that many PHP developers are actually
using it However, it is a robust and flexible logging
system and does not have any real competitors Earlier
this year, log4php was adopted by Apache’s Logging
Services Project whose intention is to provide
cross-language logging services based on the work of log4j
That means that log4php will be backed up in the same
way as log4j, log4cxx and log4net when the incubation
stage is finished
How does it work?
Log4php logically consists of three main components:
appenders, layouts and loggers.
• An appender is responsible for appending a
log message to a resource Some available
appenders are console, database, file, email,
socket and daily rotating logfiles
• A layout is the view of an appender and
how the log message should be formatted
Typically, these are HTML, XML or text based
on a pattern string
• A logger combines a set of appenders with a
unique name, which is used to reference the
logger from program code One and only one
log level is attached to a logger
Additionally, there are two other terms that are very
common: log level and root logger.
• A log level is a defined severity for the log
6 $logger =& LoggerManager :: getRootLogger ();
7 $logger -> debug ( “Hello World!” );
8 $logger -> fatal ( “System crash!” );
5 log4php.appender.email.subject = Log4php test
6 log4php.rootLogger = FATAL, email
Trang 25The root logger will catch all logging events, so you may create other loggers for different logging purposes Using the configuration in Listing 1 as a base, we can add another logger called “audit” for tracking audit messages and send them to a file with more information
in the layout (see Listing 3)
To get the new logger you can call LoggerManager::getLogger(‘audit’):
$logger =& LoggerManager::getLogger(‘audit’);
$logger->info(“User profile updated“);
Configuration
By default, log4php will look for a configuration file called log4php.properties within the current directory and use the LoggerPropertyConfigurator to parse the property file which is in a simple INI-file format The filename is set by the LOG4PHP_CONFIGURATION constant, and can be changed to whatever you’d like to name your configuration file (e.g log4php.ini)
You can also configure log4php through an XML based configuration file which tends to be a bit more readable than the simple key-value format Listing 4 shows an example of the configuration from Listing 1 converted
to an XML configuration Using XML, you get a better feeling of what you’ve put into the different parts of your configuration In order to enable the use of an XML based configuration file, you need to change that class log4php uses to parse a configuration file This is done through the LOG4PHP_CONFIGURATOR_CLASS constant Listing 5 shows a complete sample script for using the configuration from Listing 4
If you do not get log messages from log4php, you should check your setup once more If log4php does not find a resource needed for appending a logging event,
or your configuration is wrong, it will drop the logging event and continue execution This way, it does not get in the way for your application You should ensure that you get the expected messages when you configure log4php the first time to verify your setup
If you have problems with configuration you can always turn on internal debugging in log4php by adding log4php.debug = true to your configuration file This will give more output about what log4php is actually doing
If your configuration is wrong you will get warning messages like this:
Warning: LoggerDOMConfigurator::tagOpen() APPENDER cannot instantiate appender ‘default’
However, never leave this on by default; use it only for debugging
message Standard log levels are debug, info,
warn, error and fatal
• A root logger is the base logger that log events
if a named logger does not exist, or when a
logging event is caught by another logger
The root logger sits on the top of the logger
hierarchy
To get started and be able try out some of the code in
this article, you should grab the latest version of log4php
from http://logging.apache.org After unpacking the archive,
you should add the src directory within log4php to your
include_path so it is available from all scripts
I will start with an absolute minimal “Hello World!”
example, to give you an idea of how the system works
The first thing you need to do is to set up a log4php
configuration file (see Listing 1) Save this code to a
file called log4php.properties A minimal configuration
should contain at least a logger and an appender I’ve
first defined an appender named “default” attaching the
LoggerAppenderEcho class and giving it a simple layout
using the LoggerLayoutSimple class I then attach the
appender to the root logger with the log level set to
DEBUG
Listing 2 contains a small script that makes use of this
configuration file For simplicity, you should save this
code to a file in the same directory as the configuration
file, since log4php looks for the configuration file in the
same directory by default—more on this in a moment
The first thing that is done here is to include the
LoggerManager which is responsible for providing logger
instances that are defined by the configuration file The
LoggerManager operates as a singleton pattern, and the
configuration is read only once per request The last
thing in this code is to get the root logger by calling
LoggerManager::getRootLogger() and call its debug()
function with a message
Running this code will give you this result:
DEBUG - Hello World!
The most used functions for a logger are:
• debug() - used for debug messages
• info() - used in an informational context like
“username logged in” or “user typed wrong
password”
• warn() - used when entering a potentially
harmful situation
• error() - used when something goes wrong,
does not cause the application to terminate
(non-fatal)
• fatal() - used for really bad situations where
execution must be aborted
Trang 26Useful Appenders
Log4php comes with a number of appenders to choose
from Figure 1 shows a list of all available appenders
The most typical appenders for operational purpose are
LoggerAppenderDailyFile for logging events to files on
daily basis and LoggerAppenderDb for logging events to
a database, which gives you more flexibility in terms of
statistics and extracting logging data later on
Listing 6 shows how to configure a daily file appender
Notice how you apply the date pattern through the
datePattern parameter and use the conversion modifier
%s to place the date in the filename provided in the file
parameter The LoggerAppenderDailyFile makes use of
PHP’s date() function, so you can make it a weekly or
monthly appender if you like
In a mission critical application, you need instant
feedback from the application if something goes seriously
wrong Most people like to use an email appender for
that Listing 7 shows a minimal configuration for an email
appender, setting from and to addresses, and a subject
The layout provides the body of the email message You
should not use the email appender to anything above log
level FATAL or ERROR since it will most likely fill up your
mailbox with more messages than you actually want
Useful Layouts
You also have a range of layout possibilities Figure 2
shows a list of available layouts If you are logging to
file, LoggerLayoutTTCC will be a good default layout The
layout is compiled like this:
time [thread] LEVEL category - message
And here’s an actual example:
Sat Nov 26 16:56:48 2005,121 [887] FATAL root - System crash!
Listing 8 contains an example of how you can tweak
this layout to remove thread and microseconds and
modify the date format
If you want logs available through your browser you
should consider using the HTML layout and combine
it with a daily file appender so you can download the
log files in your browser Listing 9 shows a suggested
configuration for this I’ve set the loglevel to WARN here
because you probably only want to look for problems in
this type of appender I’ve also enabled locationInfo on
this layout to add the line number and file in which an
event occurred, to each message
The pattern layout is the most flexible of all the
layouts The result of this layout depends on a conversion
pattern closely related to the sprintf() function added to
the conversionPattern property of LoggerPatternLayout
socket LoggerAppenderSyslog Log event using syslog()
LoggerLayoutTTCC A text layout containing time, thread, category and
nested diagnostic context.
LoggerPatternLayout A flexible layout based on a pattern string LoggerXmlLayout An XML file with an ‘event’ element per logging
event.
FIGURE 2
CONVERSION SPECIFIER DESCRIPTION
%d Date of logging event (may be followed by a date format
specifier enclosed between braces which follows the PHP date() pattern In example: %d{d M Y H:i:s})
%n Platform-dependent newline (\n, \r\n etc.)
%p Priority of the logging event
%r Milliseconds elapsed since the start of the execution until
the creation of the logging event
%t Thread (process id)
%x NDC (nested diagnostic context) associated with the
thread
%X MDC (mapped diagnostic context) associated with the
thread The conversion character must be followed by the key for the map placed between braces (for example:
%X{referrer})
FIGURE 3
Trang 27(see Listing 10 for a complete configuration example)
A conversion pattern is a string composed of conversion specifiers A conversion specifier starts with a percentage character, an optional format modifier and a conversion character For example, the conversion pattern
“%d{Y-m-d H:i:s} %-5p %c: %m%n” will look like this:
2005-11-26 21:26:36 INFO root: User data updated ok
The first part is the date on a specified format; the second part is log level; the third part is the logger category; the fourth part is your message; and the last part is the platform dependent newline character(s) Figure 3 shows
a complete list of conversion specifiers that can be used
in a pattern layout
By default, the result of all conversion specifiers is rendered as-is However, it is possible to use a format modifier between the percent sign and the conversion character to change the minimum field width, the maximum field width and text justification within a field Use a positive integer to specify the minimum field width, a period followed by a positive integer to specify maximum field width Left-justify within a field by using the minus sign (-), or else it will be justified right Here are a few examples:
• %-5p – left-justify the priority within 5 characters
• %10c – right-justify the category if within 10 characters
• %10.15c – right-justify the category if shorter than 10 characters; if category is longer than
15 characters, truncate from the beginning
Adding Additional Filters
By using filters, you can fine tune which messages you want to receive from an appender, by specifying matching log levels or strings that you want to drop This is particularly useful when you need to attach appenders to
a logger with a different kind of log level than you really want messages from For example, if you want to attach
an email appender to only get notifications about fatal events, but the log level of the logger is set to something else Figure 4 contains a list of available filters
Listing 11 shows the configuration of an appender which drops debug events using a LoggerLevelMatchFilterwith the parameter levelToMatch set to DEBUG and acceptOnMatch to false Note that filters are only supported in XML based configuration and not in the simple property configuration You can add as many filter rules as you like to a given appender
8 <param name=”LevelToMatch” value=”DEBUG” />
9 <param name=”AcceptOnMatch” value=”false” />
3 ; CREATE TABLE log4php (
4 ; `timestamp` varchar(32) default NULL,
5 ; `logger` varchar(32) default NULL,
6 ; `username` int(11) default NULL,
7 ; `ip_address` varchar(15) default NULL,
8 ; `uri` varchar(255) default NULL,
9 ; `level` varchar(32) default NULL,
10 ; `message` varchar(64) default NULL,
11 ; `thread` varchar(32) default NULL,
12 ; `file` varchar(64) default NULL,
13 ; `line` varchar(4) default NULL,
20 log4php.appender.mysql.sql = INSERT INTO log4php
(timestamp, logger, username, ip_address, uri, level,
message, thread, file, line) VALUES (‘%d{Y-m-d H:i:s}’,’%c’,
4 LoggerMDC :: put ( ‘username’ , $_SERVER [ ‘REMOTE_USER’ ]);
5 LoggerMDC :: put ( ‘ip_address’ , $_SERVER [ ‘REMOTE_ADDR’ ]);
6 LoggerMDC :: put ( ‘uri’ , $_SERVER [ ‘REQUEST_URI’ ]);
7
8 $logger =& LoggerManager :: getRootLogger ();
9 $logger -> info ( “Testing MDC” );
10 ?>
LISTING 13
Trang 28Debugging Objects
You can pass any PHP object to any of the logging
functions in a logger, and it will be rendered through the
log4php’s object rendering mechanism This is a useful
feature when debugging instead of using print_r() or
var_dump() which will output result to screen The default
implementation of object rendering uses var_export() to
return the string representation of the instance you apply
Be careful with large objects and recursion You should
only use it for your own business objects or similar
Diagnostic Context
In a web application, you are serving multiple clients
simultaneously and you probably want to add more
information to your logging events about the request,
such as user identification: IP address and/or the
current request URI Mapped Diagnostic Context, or MDC
for short, is a technique used to distinguish between
interleaved log messages MDC properties can be set
statically anywhere before using the logger using
LoggerMDC::put(‘key’, $value)
LoggerMDC::put(‘remote_addr’, $_SERVER[‘REMOTE_ADDR’]);
To map MDC properties in your layout, you should use
the pattern conversion character “X” followed by the key,
between braces For example, %X{remote_addr}
log4php.appender.Test.layout = LoggerPatternLayout
log4php.appender.Test.layout.conversionPattern =
“%d{Y-m-d H:i:s} %-5p %c: %X{remote_addr} %m%n”
Listing 12 shows how to use LoggerAppenderDb to setup
an MDC configuration to append logging events to a
MySQL database You need to create the table that you
want to append to, yourself, in order to have fields ready
for MDC properties For that reason I’ve also disabled
the createTable property on the database appender The
sql property of the database appender can include every
conversion specifier from the pattern layout
Listing 13 contains a sample script using MDC to map
additional context data into all logging events
Dealing with Errors
When it comes to monitoring, you want to know about
all kinds of errors that are happening during runtime, in
a controlled manner It is never a good idea to display
error messages to the end user when something goes
wrong Not only does it look bad, but it can also give
potential hackers valuable information You should store
all error messages and hide them from the end user The
simplest way to fix this is to turn off display_errors
and turn on log_errors in php.ini to log them to a file
defined by error_log, but a better way is to let log4php
FILTER DESCRIPTION
LoggerLevelMatchFilter Filtering based on a level to match in the LevelToMatch
property Set AcceptOnMatch to true or false whether or not you will accept the logging event.
LoggerLevelRangeFilter Used to reject messages with levels without a certain
range based on the LevelMin and LevelMax property Set AcceptOnMatch to true or false whether or not you will accept the logging event within the range LoggerStringMatchFilter Filtering based on a string to match in the
StringToMatch property Set AcceptOnMatch to true
or false whether or not you will accept the logging event.
LoggerDenyAllFilter Drop all logging events.
41 static function exceptionHandler ( $e ) {
42 $logger =& LoggerManager :: getRootLogger ();
43 $logger -> error ( $e -> getMessage () ’ in ‘ $e -> getFile ()
50 trigger_error ( “Triggered user error” , E_USER_ERROR );
51 throw new Exception ( ‘Uncaught Exception’ );
52 }
53 }
54 $handlers = new LoggerHandlers ();
55 $test = new Test ();
56 ?>
LISTING 14
Trang 29that you get the messages and not the end user.
What about Performance?
Logging comes at a cost, and appending logger messages
to various destinations will slow down your application There are, however, some guidelines to be aware of when using log4php that can help you minimize unnecessary overhead
First of all, you should use the right log levels in the right situations For example, you should distinguish
between fatal and error as well as info and warn levels
and what they mean in your application Then you will get the most valuable output from your configuration.Second, it is a good practice to test if the log level
is enabled on a logger before logging Testing is pretty fast compared to the operation you will be doing with appending a message to a logger You can always use the functions isDebugEnabled() and isEnabledFor() for this purpose
if($logger->isDebugEnabled()) { $a = 1;
is the structure that contains all loggers that were created, based on your configuration By caching this structure, you do not have to rebuild it upon each request
Listing 15 provides a simple example of implementing this concept, first by naming a cache filename and checking if this file already exists If not, get the reference to the current LoggerHierarchy instance
take care of the messages
PHP contains both normal PHP errors and exceptions
(as of PHP5) Listing 14 shows an example of how to
implement a custom error and exception handler for
uncaught errors I’ve implemented a LoggerHandlers class
with a static function errorHandler() which appends
log messages, based on the error code, when an error
occurs This implementation is a bit strict in terms of
log levels, but I find it more useful to be strict during
development and loosen up a bit later on if necessary I’ve
also implemented a static function exceptionHandler()
to use as the exception handler Both are initialized
in the constructor with set_error_handler() and
set_exception_handler() This is a nice way of ensuring
When it comes to monitoring, you want to know about all kinds of
errors that are happening during runtime,
Trang 30through LoggerManager::getLoggerRepository() and
store a serialized version of it in the cache file You will
get the logger hierarchy back when unserializing the
cache Then you will get access to all loggers by calling
getLogger(‘name’) or in this case getRootLogger() on the
logger hierarchy instance
To create more reusable code for this concept you
could create your own cached logger configurator
extending the LoggerPropertyConfigurator or
LoggerDOMConfigurator and override the configure()
function to do the caching
The performance gain of caching the logger hierarchy
will increase the more configuration you add to your
setup In general, if performance is really critical to you
it may be a good idea to minimize the use of appenders
and only log one place, for example to a database Then
set up a cron job to do some post processing like looking
for fatal errors and sending them by email, and creating
your own script to search for messages in the database
instead of using the HTML appender with the daily file
appender described earlier
Create Your Own Logging Server
Log4php can be used extensively in a distributed
environment with many applications Whether you want
to optimize performance overhead or gather logging
messages in one central place, you can create a logging
server to send logging events to and let the logging
server do the actual logging for you Figure 5 illustrates
the centralization of logging from having all logging
configuration set up in each application to have that
configuration on a logging server With this approach,
each application then only has to define one appender
which logs directly to the logging server If you are
familiar with log4j you have probably used or heard of
Chainsaw, a utility to read and receive logging events
from log4j using an XML layout You could use Chainsaw
as your logging server, but I prefer not to use the XML
layout and write my own implementation just to show
you how easy it can be done
Listing 16 contains an application logging
configuration using a socket appender This is the only
appender needed for an application since the logging
server should do the rest of the work A socket appender
takes three parameters The first parameter remoteHost
contains the protocol combined with the host name or
IP address The second parameter port is which port
the socket appender should connect to on the remote
host And the third parameter useXml will generate
payload using the XML layout if set to true In my
example I have set this to false which will serialize the
LoggerLoggingEvent object instead and is way faster to
1 <?php
2 require_once ‘log4php/LoggerManager.php’ ;
3 $cache = ‘log4php.cache’ ;
4
5 if(! file_exists ( $cache )) {
6 $hierarchy =& LoggerManager :: getLoggerRepository ();
7 file_put_contents ( $cache , serialize ( $hierarchy ));
8 }
9 $hierarchy =& unserialize ( file_get_contents ( $cache ));
10 $logger =& $hierarchy -> getRootLogger ();
4 $logger =& LoggerManager :: getRootLogger ();
5 $logger -> info ( “Here I am :)” );
work with than parsing XML data
Listing 17 is our sample application that contains a few logging events that will be using the socket appender
in Listing 16 Listing 18 will be the logging server’s configuration which logs to screen and to a file
Listing 19 contains code for a logging server using the PEAR::Net_Server package PEAR::Net_Server is a generic server interface to create daemon scripts based on the socket extension You can choose from two different server drivers: sequential and fork I have chosen the fork driver where a new process is forked for each client that connects to the server This driver requires the pcntlextension which enables process control support on *nix based platforms If you are on Windows or can’t enable process control support on your installation, you can always use the sequential driver
Net_Server has an object oriented approach and you need to create a handler class that extends
Trang 31appender’s doAppend() function with the logging event
It will also add events to the root logger The handler is connected to the server through the setCallbackObject()function on the server driver and the server is started at the end with the start() function
Save Listing 19 as server.php and start it with the command line:
php server.php
It will start listening on port 9090 on localhost Now you can try to connect to it with the application code from Listing 17
A logging server is a good alternative to tailing log files for each application If you want to use it, you should consider using it together with Mapped Diagnostic
Context data and add properties like product and product
version, and location data like host name or request URI
to make your life a little bit easier
This example is too simple to be copied and used directly in any kind of production environment, but I hope you get some ideas on how you can make use of it
in your own environment
Where do I learn more?
Without log4php’s API manual, you have to look to log4j documentation to find some readable end user documentation about the logging API The best place
to learn about how to configure log4php is in its own tests/ directory, where you can find a sample file for most available features
If you have any questions related to log4php, you can always post them on the log4php-user@logging.apache.org
mailing list If you have any suggestions on how to improve log4php, or want to help out developing the software, you should join the log4php-dev@logging.apache.org mailing list
Final Words
log4php is a robust logging framework and a good way
to add debug, monitoring and audit logging Logging is managed by configuration files and you can keep logging statements in shipped code without worrying about heavy performance cost
16 function onReceiveData ( $clientId = 0 , $data = “” ) {
17 $events = $this -> getEvents ( $data );
18 foreach( $events as $event ) {
19 $root = $this -> hierarchy -> getRootLogger ();
20 if( $event -> getLoggerName () === ‘root’ ) {
21 $root -> callAppenders ( $event );
22 } else {
23 $loggers = $this -> hierarchy -> getCurrentLoggers ();
24 foreach( $loggers as $logger ) {
25 $root -> callAppenders ( $event );
26 $appenders = $logger -> getAllAppenders ();
27 foreach( $appenders as $appender ) {
28 $appender -> doAppend ( $event );
35 function getEvents ( $data ) {
36 preg_match ( ‘/^(O:\d+)/’ , $data , $parts );
37 $events = split ( $parts [ ], $data );
49 $server =& Net_Server :: create ( ‘fork’ , $host , $port );
50 $handler =& new Net_Server_Handler_Log ();
51 $server -> setCallbackObject ( $handler );
52 $server -> start ();
53 ?>
LISTING 19
Net_Server_Handler that can be used as a callback object
when a client connects Handlers have a set of broadcast
functions you can override and hack in your functionality
when those broadcast functions are executed I have
only defined one property in my handler, which will
hold the LoggerHierarchy object The onStart() function
is called when the server driver is initialized and
LoggerManager::getLoggerRepository() returns the whole
logger hierarchy structure with logger and appender
objects constructed from the configuration The logic
part of this server example goes into the onReceiveData()
function which is called when a client sends data to the
server The second parameter $data contains a serialized
version of all logging events that have been appended
The getEvents() function is a helper function to extract
an array of logging events from this data After all
events are in place, it will iterate through all loggers and
their appenders in the logger hierarchy, and call each
Telio Telecom AS He’s also maintainer of wsdl2php , a PHP 5 Web Service Proxy Generator His personal blog is http://www.urdalen.com/.
Trang 32application that outperforms other solutions.
This article will develop a link management
application using an SQLite database as the data
store The front end will display alphabetically
ordered web site links as shown in Figure 1
An alphabetic navigation bar of hyperlinks will
make any specific link easily accessible, and recently
added links will be highlighted
A submission form will allow visitors to suggest
additional links These links will not appear on the site
until they have been reviewed There will be a back-end
to review and maintain links
How it’s Done
In this application we take advantage of some of SQLite’s
advanced capabilities Both triggers and views will be
used A trigger will be used to mimic a datestamp field—
records will be automatically stamped whenever they are
added/changed
Views are a convenient way of storing queries and
can replace tables in a FROM clause They can also be
used with triggers so that “updating” a view updates the
associated table
No database used in conjunction with PHP can escape
comparison to MySQL Where appropriate, we will point
out differences in SQL syntax between SQLite and MySQL
Likewise, SQLite has a variety of different query methods
These will also be contrasted with MySQL functions
Throwing exceptions rather than error trapping makes
for cleaner code SQLite has a built-in OO interface and
PHP: 5.1.0 OTHER SOFTWARE: SQLite 2.8.14
LINKS:
http://sqlite.org http://zend.com/php5/articles/php5-sqlite.php http://php.net/manual/en/ref.sqlite.php http://www-128.ibm.com/developerworks/library/os-sqlite/ http://www.sqlitemanager.org/
Advanced SQLite
Development
CODE DIRECTORY: sqlite
through extending the SQLite database class we can override the query methods of SQLite so that a failed query throws an exception By extracting metadata from
a database, data verification methods will be added to the derived class This will be done by querying the sqlite_master table and through the use of pragmas
A limited number of functions are available for use with SQLite’s dialect of SQL This shortcoming can be overcome by creating User Defined Functions (UDF)
Trang 33Getting Started
SQLite comes bundled with PHP 5 so you can immediately
start writing PHP code to create a database, tables and
indices—whatever you might need
However, this approach has a couple of disadvantages
If you do things this way you’ll have to write code just
to view your data or to examine the structure of your
database Instead, download the command-line version
of SQLite PHP 5.1 RC1 comes with version 2.8.14 I
downloaded 2.8.16 and didn’t run into any problems
Don’t use version 3 though—the file format is different
and incompatible with version 2 For convenience, you
might want to install the binary of sqlite in the same
directory as your database
Creating a database is as simple as typing
sqlite dbname.ext at the command line Doing so will
run sqlite and create or open an existing database of the
specified name You can now create a table using SQL
from the command line However, let me make one more
suggestion At some point you will want to dump your
database and if you have created it from the command
line the output won’t be very readable If you use a text
editor to format your CREATE TABLE statement and then
redirect this file to the database, the results will be much
more acceptable Do this whenever you create tables,
views or triggers
The database used in this application is called
resources.sqlite and is stored in a subdirectory named
dbdir To create it and all associated database objects
redirect the dump.sql file from the command line in the
following way:
sqlite resources.sqlite < dump.sql
A database dump is formatted as a transaction, so, if everything went okay, you’ve already used one of SQLite’s advanced features
Creating a Table
The SQL used to create the tblresources table in our database is shown in Listing 1 Let’s have a look at the details
To create a table with an autonumber field named
id, the data type INTEGER is used in conjunction with PRIMARY KEY This is equivalent to identifying a field as INTEGER auto_increment PRIMARY KEY in MySQL In SQLite, this field definition is the one exception to the rule that SQLite fields are typeless—otherwise all fields are strings Creating fields as types other than string helps document the data types we are expecting but will not restrict the value entered You can put a string into a float type field and a float into a boolean Further, specifying the length of a VARCHAR type field will not truncate data that exceeds the defined length Any length of string can be entered into any field Otherwise, the syntax for creating
a table functions exactly as you might expect
The field names used in creating this table are documenting, but a few comments are in order A resource won’t be displayed until the reviewed field is set to true The field with the data type TIMESTAMP, whenaltered, will be maintained using a trigger as will the whenaddedfield
self-Views
Views are stored SELECT queries If you repeatedly use the same query, it is worthwhile creating it as a view
To make resource links easily accessible we are going
to order them alphabetically and create hyperlinks to each letter of the alphabet It is with this in mind that
FIGURE 1
FIGURE 2