LEARNING PHP WAS NEVER THIS MUCH FUN
Come learn PHP in Paradise with us
(and spend less than many other conferences)
Ilia Alshanetsky -
Accelerating PHP Applications
, Marcus Boerger -
Implementing PHP 5 OOP Extensions
,
John Coggeshall -
Programming Smarty
, Wez Furlong -
PDO: PHP Data Objects
, Daniel Kushner -
Introduction to
OOP in PHP 5
, Derick Rethans -
Playing Safe: PHP and Encryption
, George Schlossnagle -
Web Services in PHP 5
,
Dan Scott -
DB2 Universal Database
, Chris Shiflett -
PHP Security: Coding for Safety
, Lukas Smith -
How About
Some PEAR For You?
, Jason Sweat -
Test-driven Development with PHP
, Andrei Zmievski
PHP-GTK2
The Magazine For PHP Professionals
php|
Tropics
Moon Palace Resort, Cancun, Mexico.
May 11-15 2005
At php|tropics, take the exam and
Get Zend Certified
...and we'll pay your fees!
For more information and to sign up:
/>Early-bird discount in effect for a limited time!
II NN DD EE XX
6 EDITORIAL
Out with the Old
7 What’s New!
48 Test Pattern
Spring Cleaning
by Marcus Baker
54 Product Review
Visustin 3.0: The Flowcharter of the
People ?
by Peter B. MacIntyre
68 Security Corner
BBCode
71 exit(0);
Old School, New School,
NO SCHOOL
by Marco Tabini
10 Secure SOAP Transactions
in Command Line Applications
by Ron Korving
20 Database Abstraction in PHP
by Lukas Smith
29 Advanced Sessions and
Authentication in PHP 5
by Ed Lecky-Thompson
40 Building a MySQL Database
Abstraction Class
by Tom Whitbread
58 An XML approach to
Templating using PHPTAL Part II
by José Pablo Ezequiel Fernández Silva
TABLE OF CONTENTS
php|architect
TM
Departments
Features
Have you had your PHP today?Have you had your PHP today?
The Magazine For PHP Professionals
NEW COMBO NOW AVAILABLE: PDF + PRINT
NNEEWW
LLoowweerr PPrriiccee!!
EEDDIITTOORRIIAALL
E
diting php|architect is, at the same time, a blessing and a curse. On the plus
side, I get to read some really exciting material every month. On the minus
side… I have to read all that material every month before the deadline for the
next issue!
Being an editor is very challenging—something I would have never guessed
when I got into this line of work. I dare anybody to do it for six months and read
a book the way they used to before. Gone is the lust for knowledge—to be replaced
for a compulsive, incurable need to find typos and fix someone else’s grammar. Of
course, someone else is the key here—it’s never your own mistakes you catch
(regardless of whether you actively made them part of your own writing or didn’t
catch them in another author’s work).
There are, of course, many reasons why being the editor of this magazine no
longer makes sense for me. First, our activities have grown so much from a single
PDF publication to a group that encompasses PHP education on so many levels—
print, books, training and conferences—that I constantly feel guilty that I’m not
dedicating as much time as I should to making sure that the contents of php|a are
always the best of the best (even though editing the magazine keeps me up many
nights every month).
Second, and most important, we must ensure that our supply of fresh ideas is,
well, fresh. Change is everything—and it’s been time for some new thought pat-
terns to be formed in the php|a brain for a while now.
Armed with these problems, we have been working hard at finding a new Editor-
in-Chief for php|architect. It’s not been easy, but I hope that you’ll join me in wel-
coming Sean Coates to the gang. Sean is an active member of the PHP team (he
works on the documentation—and I can’t think of a better way to be exposed to
as much PHP technology as possible) and, like the rest of us, uses PHP in his every-
day life. But don’t take my word for it—he will be introducing himself shortly.
For my part, I bid you all farewell. Of course, you can’t get rid of me quite that eas-
ily—I’m still hanging on to my exit(0), and I will as always lurk on our forums try-
ing my very best to confuse as many people as possible for every single post.
Until next month… well, it’s up to Sean now!
In with the New
One random afternoon, on IRC, I noticed Marco complaining about
having to go edit an article, when he’d rather be doing something else. I naively
retorted with “I actually like editing!” and, over the next few days, we worked out
the details, evaluated my skills, and speculated on how much work was involved in
editing an issue of php|architect. Now, only a month later, here I am.
Allow me to introduce myself. As Marco indicated, I’ve been actively involved in
the PHP community for approximately two years, now (and not-so-actively
involved, before that, for another year). My attention and keystrokes are primarily
spent writing and editing the PHP manual, but I’m also involved in several other
projects, including documentation meta-projects and the maintenance of a popu-
lar PEAR package. I’ve been writing PHP, professionally, for over 5 years for various
companies, involved in many sectors, from marketing to credit card processing.
It is with great pleasure (and already some late nights) that I take the reins of
what I believe to be the best recurring resource that is currently available for pro-
fessional PHP developers. I’m also happy that Marco can offload some of his work
to me, freeing him up to do the things he mentioned above. I believe that the
owner of a business should be involved in his creation, but not necessarily intimate-
ly so. There’s a certain value in having the ability to take a step back, and view the
fruits of your labor from a distance.
With this pleasure, though, comes great responsibility. I hope to be accessible to
you, our readers, in as many ways as possible. Please don’t hesitate to contact me
with any complaints, criticism, snide remarks, ideas, or encouragement you may
have. I’m usually very responsive by email (
sseeaann@@pphhppaarrcchh..ccoomm
), or you might find
it more convenient to drop your thoughts in our online discussion forums
(
hhttttpp::////pphhppaarrcchh..ccoomm//ddiissccuussss//
). I look forward to hearing from you.
Until next month, happy reading! (Yes, I stole his line.)
April 2005
●
PHP Architect
●
www.phparch.com
6
php|architect
Volume IV - Issue 4
April, 2005
Publisher
Marco Tabini
Editor-in-Chief
Sean Coates
Editorial Team
Arbi Arzoumani
Peter MacIntyre
Eddie Peloke
Graphics & Layout
Arbi Arzoumani
Managing Editor
Emanuela Corso
News Editor
Leslie Hill
Authors
Marcus Baker, Peter B. MacIntyre,
Chris Shiflett, Ron Korving, José Pablo
Ezequiel Fernández Silva, Lukas Smith,
Ed Lecky-Thompson, Tom Whitbread
php|architect (ISSN 1709-7169) is published twelve times a year by
Marco Tabini & Associates, Inc., P.O. Box 54526, 1771 Avenue Road,
Toronto, ON M5M 4N5, Canada.
Although all possible care has been placed in assuring the accuracy of
the contents of this magazine, including all associated source code, list-
ings and figures, the publisher assumes no responsibilities with regards
of use of the information contained herein or in all associated material.
Contact Information:
General mailbox:
Editorial:
Subscriptions:
Sales & advertising:
Technical support:
Copyright © 2003-2005 Marco Tabini &
Associates, Inc. — All Rights Reserved
OOuutt wwiitthh tthhee OOlldd
EE DD II TT OO RR II AA LL RR AA NN TT SS
TM
NNEEWW SSTTUUFFFF
April 2005
●
PHP Architect
●
www.phparch.com
7
What’s New!
php|architect prepares for php|
tropics
2005
Ever wonder what it's like to learn PHP in paradise? Well, this year we've decided to give
you a chance to find out!
We're proud to announce php|tropics 2005, a new conference that will take place between
May 11-15 at the Moon Palace Resort in Cancun, Mexico. The Moon Palace is an all-
inclusive (yes, we said all inclusive!) resort with over 100 acres of ground and 3,000 ft. of
private beach, as well as excellent state-of-the-art meeting facilities.
As always, we've planned an in-depth set of tracks for you, combined with a generous
amount of downtime for your enjoyment (and your family's, if you can take them along
with you).
We even have a very special early-bird fee in effect for a limited time only.
For more information, go to
/>.
NN EE WW SS TT UU FF FF
PHP Input Filter 1.2.0
Need help filtering data and pre-
venting attacks? Check out PHP
Input Filter. According to the pro-
ject's homepage, PHP Input
Filter:
"is a free php class that allows
developers to easily filter input
coming from the user (HTML
forms, cookies etc) for a num-
ber of reasons. The focus of
this tool is on customization.
v1.2.0 features much more
comprehensive anti-XSS pro-
tection, as well as the option
of auto-stripping bad tags sep-
arate from any specified by
the developer."
To see a demo or to download,
visit
www.cyberai.com/inputfil-
ter/
.
CONFERENCES
Zend/PHP Conference
and Expo 2005
Zend.com announces: Zend
Technologies and KB Conferences proudly announce the Zend/PHP Conference & Expo 2005 taking
place at the Hyatt Regency San Francisco Airport on October 18-21, 2005. The theme of the con-
ference will be "Power Your Business With PHP" and will feature sessions in the following four
tracks: The Business Case for PHP; Developing, Deploying and Managing Large-Scale PHP
Applications; Integrating PHP with the Enterprise (including Web Services and XML); and PHP
Resources: Tools, Libraries and Techniques.
"We invite interested speakers to submit session proposals between now and July 15, 2005. Visit
the conference website for more information about the conference or if you are interested in sub-
mitting a session proposal."
Get all the latest conference information from
Zend.com.
International PHP Conference 2005 Spring Edition
Don't want to wait until October for the Zend/PHP Conference? Zend.com brings news of the
International PHP Conference coming in May: "The International PHP Conference 2005 Spring
Edition will take place from May 2, 2005 to May 4, 2005. The Conference features a
PowerWorkshop day on May 2 with PHP/MySQL Best Practices, XML/WebServices with PHP 5,
Rapid Application Development and a PHP Starter Workshop for Beginners. The main Conference
days will include sessions on PHP Internals, XML, Databases, Migration to PHP 5 and others. Early
bird discounts are available until April 1, 2005."
For more information, visit
phpconference.com.
Fast Template 1.3
Grafxsoftware.com announces the latest release of their PHP templating system, Fast Template.
What's new in this version?
• Added DELETE_CACHE function, to delete files what is older then expire time.
• Added file extension to cache for example now a cache file name will be
62327a34b389dca70c7c15e9d81e57bd.ft (notice the extension .ft) This was necessary because
of DELETE_CACHE function
• Added include block which include another template by statement (like SSI do) <!--#include
file="include2.html"--> It is useful if you have several different templates for different parts of
page and you don't need to write any php code to gather all "blocks" of the page. Also is very
helpful from designer point of view, he will see in a visual editor the result.
Get more information from
/>NNEEWW SSTTUUFFFF
April 2005
●
PHP Architect
●
www.phparch.com
8
Check out some of the hottest new releases from PEAR.
Net_Monitor 0.2.2
A unified interface for checking the availability of services on external servers and sending meaningful alerts through a variety of
media if a service becomes unavailable.
LiveUser_Admin 0.2.1
LiveUser_Admin is meant to be used with the LiveUser package. It is composed of all the classes necessary to administer data used by
LiveUser. You'll be able to
aadddd//eeddiitt//ddeelleettee//ggeett
things like:
• Rights
• Users
• Groups
• Areas
• Applications
• Subgroups
• ImpliedRights
And all other entities within LiveUser.
LiveUser 0.15.1
LiveUser is a set of classes for dealing with user authentication and permission management. Basically, there are three main elements
that make up this package:
• The LiveUser class
• The Auth containers
• The Perm containers
The LiveUser class takes care of the login process and can be configured to use a certain permission container and one or more differ-
ent auth containers. That means, you can have your users' data scattered among many data containers and have the LiveUser class
try each defined container until the user is found.
For example, you can have all website users who can apply for a new account online on the webserver's local database. Also, you want
to enable all your company's employees to login to the site without the need to create new accounts for all of them. To achieve that, a
second container can be defined to be used by the LiveUser class.
You can also define a permission container of your choice that will manage the rights for each user. Depending on the container, you
can implement any kind of permission schemes for your application while having one consistent API.
Using different permission and auth containers, it's easily possible to integrate newly written applications with older ones that have
their own ways of storing permissions and user data. Just make a new container type and you're ready to go!
Currently available are containers using:
PPEEAARR::::DDBB,, PPEEAARR::::MMDDBB,, PPEEAARR::::MMDDBB22,, PPEEAARR::::XXMMLL__TTrreeee
and
PPEEAARR::::AAuutthh
.
File 1.2.0
Provides easy access to read/write to files along with some common routines to deal with paths. Also provides interface for handling
CCSSVV
files.
XML_Wddx 1.0.1
XXMMLL__WWddddxx
does 2 things:
a) functions as a drop in replacement for the
XXMMLL__WWddddxx
extension (if it's not built in)
b) produces an editable
WWDDDDXX
file (with indenting etc.) and uses
CCDDAATTAA
, rather than char tags
This package contains 2 static methods:
XXMMLL__WWddddxx::sseerriiaalliizzee(($$vvaalluuee))
and
XXMMLL__WWddddxx::ddeesseerriiaalliizzee(($$vvaalluuee))
. It should be 90%
compatible with wddx_deserialize(), and the deserializer will use wddx_deserialize if it is built in. No support for recordsets is available
at present in the PHP version of the deserializer.
PHP 5 ionCube Encoder
The good people at ioncube have announced the release of the new ionCube Encoder
for PHP 5.
"We are happy to announce the release of the new ionCube Encoder for PHP 5! The
new Encoder fully supports all PHP 5 language constructs and can deliver a substantial
increase in performance over unencoded PHP 5. The PHP 4 Encoder is provided for free with the PHP 5 Encoder.
We have added Package Foundry support to the Windows version of the new Encoder, enabling a one-stop solution for those wishing
to create, package, and deploy PHP applications. To demonstrate this support the Encoder download bundle now includes a Package
Foundry evaluation. Existing PHP 4 Encoder customers are eligible for a discount when purchasing the new PHP 5 Encoder."
For more details please visit
www.ioncube.com.
NNEEWW SSTTUUFFFF
April 2005
●
PHP Architect
●
www.phparch.com
9
Looking for a new PHP Extension? Check out some of the lastest offerings from PECL.
pecl_http 0.7.0
pecl_http provides:
• Building absolute URIs
• RFC compliant HTTP redirects
• RFC compliant HTTP date handling
• Parsing of HTTP headers and responses
• 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/charset
• Convenient request functions to HEAD/GET/POST if libcurl is available
• HTTP auth hooks (Basic)
• HTTPi, HTTPi_Response and HTTPi_Request classes (HTTPi_Request only with libcurl)
maxdb 7.5.00.24
MaxDB PHP is an extension which provides access to the MySQL MaxDB databases. It is compatible with MySQL's
mysqli extension.
big_int 1.0.1
Functions from this package are useful for number theory applications. For example, in two-keys cryptography. See
//tteessttss//RRSSAA..pphhpp
in the package for example of implementation of RSA-like cryptoalgorithm.
The package has many bitset functions, which allow to work with arbitrary length bitsets. This package is much faster than bundled
into PHP BCMath and consists almost all functions, which implemented in PHP GMP extension, but it needn't any external libraries.
crack 0.2
This package provides an interface to the cracklib (libcrack) libraries that come standard on most unix-like distributions. This allows
you to check passwords against dictionaries of words to ensure some minimal level of password security.
From the cracklib README CrackLib makes literally hundreds of tests to determine whether you've chosen a bad password.
• It tries to generate words from your username and gecos entry to tries to match them against what you've chosen.
• It checks for simplistic patterns.
• It then tries to reverse-engineer your password into a dictionary word, and searches for it in your dictionary.
• after all that, it's PROBABLY a safe(-ish) password. 8-)
The crack extension requires cracklib (libcrack) 2.7, some kind of word dictionary, and the proper header files (crack.h and packer.h)
to build. For cracklib RPMs for Red Hat systems and a binary distribution for Windows systems, visit
/>php-Booba 0.8.1
The php-Booba team announces the release of php-Booba 0.8.1.
"php-Booba is a simple framework for developing Web applications. It contains classes for validating incoming data from forms, a
powerful ticket-based request handling system, and a very fast template system."
For more information, or to download, visit
/>The Zend PHP Certification Practice Test Book is now available!
We're happy to announce that, after many months of hard work, the Zend PHP
Certification Practice Test Book, written by John Coggeshall and Marco Tabini, is now
available for sale from our website and most book sellers worldwide!
The book provides 200 questions designed as a learning and practice tool for the
Zend PHP Certification exam. Each question has been written and edited by four
members of the Zend Education Board--the very same group who prepared the
exam. The questions, which cover every topic in the exam, come with a detailed
answer that explains not only the correct choice, but also the question's intention,
pitfalls and the best strategy for tackling similar topics during the exam.
For more information, visit
hhttttpp::////wwwwww..pphhppaarrcchh..ccoomm//cceerrtt//mmoocckk__tteessttiinngg..pphhpp
S
OAP (Simple Object Access Protocol) is a protocol
that enables you to run functions on a remote sys-
tem (Remote Procedure Calls). It is derived from
XML-RPC, which has been available in PHP since ver-
sion 4.1, and as we will see later, SOAP messages are
formatted in XML. Because it is such an open protocol,
SOAP is programming language and operating system
independent. This enables you to use PHP to commu-
nicate with any application as long as it can communi-
cate using SOAP.
The PHP SOAP extension was introduced in PHP 5
and is particularly useful when combined with PHP 5’s
object oriented possibilities, because SOAP handler
functions can all be implemented in a single class, and
because the extension itself is completely implemented
as classes.
One of the nice things about having a SOAP exten-
sion in PHP is the ability to use this protocol to commu-
nicate with custom-made daemon applications that are
running on remote servers. The wonderful thing about
having a daemon running on the command line inter-
face (CLI), instead of a web interface, is that you can
run it with root permissions, enabling it to do virtually
everything a web script is not allowed to do.
Generally, SOAP relies on the HTTP protocol, which is
a good thing, since it’s such a commonly spoken pro-
tocol. HTTP is, however, insecure by default. Of course,
you can use the secure HTTPS protocol for SOAP trans-
actions, but if you want to create a secure command-
line daemon in PHP, you’ll have to embed an HTTPS
web server in it. Luckily, the SOAP extension allows you
to modify requests before they are sent, and responses
before they are received. This allows you to apply the
cryptographic algorithms and key-distribution mecha-
nisms of your choice!
REQUIREMENTS
PHP 5.x
OS Any
Other Software N/A
Code Directory soap
April 2005
●
PHP Architect
●
www.phparch.com
FFEEAATTUURREE
10
Secure SOAP Transactions
in Command Line Applications
by Ron Korving
Remote procedure calls using PHP have become increas-
ingly popular in the past few years. Since the introduction
of PHP 5, a SOAP extension has been bundled with the
core PHP distribution. SOAP does not, in itself, provide a
security mechanism, nor is the PHP-extension very suitable
for command line applications. In this article, I will explain
how you can achieve security for your SOAP transactions,
and create your own SOAP-driven daemons on your
servers.
FF EE AA TT UU RR EE
RESOURCES
URL
hhttttpp::////wwwwww..pphhpp..nneett//mmaannuuaall//eenn//rreeff..ssooaapp..pphhpp
URL
hhttttpp::////pphhpp..nneett//mmaannuuaall//eenn//rreeff..mmyyssqqll..pphhpp
URL
hhttttpp::////eenn..wwiikkiippeeddiiaa..oorrgg//wwiikkii//BBlloocckk__cciipphheerr__
mmooddeess__ooff__ooppeerraattiioonn
ii
FFEEAATTUURREE
Secure SOAP Transactions in Command Line Applications
April 2005
●
PHP Architect
●
www.phparch.com
11
In this article, you will learn how to write both a SOAP
client and a server script that will be able to communi-
cate over an encrypted channel. Later, you will see how
to run a SOAP server run from the command line and
the cool possibilities that are created when doing so.
A Simple SOAP Client and Server
Let’s write a small script that we can use to connect to
our server process. If you have never worked with the
SOAP extension before, you will see how easy it is to
communicate with the server: you can call remote
methods as if they were local to your client. First, we
will create an instance of the predefined
SSooaappCClliieenntt
class. I will try to keep things as simple as possible, but
keep in mind that a lot of extra information—such as a
WSDL filename and proxy server information—can be
passed to the
SSooaappCClliieenntt
constructor. An example of
creating an instance of
SSooaappCClliieenntt
and calling a SOAP
server function can be seen in Listing 1.
The function we call is
ssyysstteemmNNaammee(())
—why not? If
we’re enabling ourselves to talk to other servers, let’s
find out what their names are—and the SOAP server
will be listening at the same host that the client will be
running from. In our case, the URI we use won’t even
matter—it is totally up to you.
Now that we have set up our client script, let’s have
a look at how to write a simple SOAP server script. It is
always wise to put SOAP functions in a class so that
everything is nicely bundled into a single package. You
can tell your
SSooaappSSeerrvveerr
instance to use the functions
within that class. Our server script might look some-
thing like Listing 2.
When the script is called, the
hhaannddllee(())
function will
create an instance of our
SSooaappHHaannddlleerr
class and execute
the function that was called by our
client. You may wonder how the
SSooaappSSeerrvveerr
class
knows what the client was asking. It gets
its information from the superglobal variable
$$GGLLOOBB--
AALLSS[[““HHTTTTPP__RRAAWW__PPOOSSTT__DDAATTAA””]]
which contains all the data
that was posted through HTTP. The code in Listing 2
would function the same way if we passed that variable
as a parameter to
hhaannddllee(())
. The wonderful thing about
this behaviour is that we can alter the incoming data
before sending it off to our
SSooaappSSeerrvveerr
object. The
same behaviour can be achieved with our
SSooaappCClliieenntt
object, allowing us to alter the SOAP request just before
it’s sent off to the server.
Altering a SOAP Transaction
Let’s start simple and make our SOAP transaction a lit-
tle smaller by compressing both the request and the
response. As we have seen, it is easy to alter the SOAP
request that is passed into the
SSooaappSSeerrvveerr
object. There
are three additional pieces of data must be altered to
make this work: the sent SOAP request, the sent SOAP
response and the received SOAP response.
On the client side, there is a very useful method in
SSooaappCClliieenntt
that, when implemented, allows us to alter
the data in the request and the response. This can be
done by extending
SSooaappCClliieenntt
to form our own custom
class. When a SOAP function is called, the
____ddooRReeqquueesstt(())
method in our
SSooaappCClliieenntt
object is trig-
gered. By extending the parent class, we can alter its
behaviour to compress the request before it is sent to
the server. This also allows us to decompress the
response from the server before it is used. You can see
how this is done in Listing 3.
Now that our client is compressing and decompress-
ing transactions, automatically, it is time to move on to
our server script. We have already seen how to modify
the input of the
hhaannddllee(())
function, but we haven’t seen
how to influence its output.
hhaannddllee(())
creates a SOAP
response (based on the return value of the invoked
function) and streams that response to standard out-
put. We can capture the contents of the SOAP response
by using PHP’s output buffering mechanism (a simple
Listing 1
1 <?php
2 $soap = new SoapClient(null, array(
3 “location” => “http://localhost/soapdemo/server.php”,
4 “uri” => “http://yourownuri/”));
5
6 echo $soap->systemName();
7 ?>
Listing 2
1 <?php
2 class SoapHandler
3 {
4 public function systemName()
5 {
6 return implode(“ “, posix_uname());
7 }
8 }
9
10 $soap = new SoapServer(null, array(
11 “uri” => “http://yourownuri/”));
12 $soap->setClass(“SoapHandler”);
13 $soap->handle();
14 ?>
Listing 3
1 <?php
2 class MySoapClient extends SoapClient
3 {
4 public function __doRequest($request, $location, $action,
$version)
5 {
6 $request = gzcompress($request, 5);
7
8 $response = parent::__doRequest($request, $location,
$action, $version);
9
10 return gzuncompress($response);
11 }
12 }
13
14 $soap = new MySoapClient(null, array(
15 “location” => “http://localhost/soapdemo/server.php”,
16 “uri” => “http://yourownuri/”));
17
18 echo “system: “.$soap->systemName();
19 ?>
call to
oobb__ssttaarrtt(())
and another to
oobb__ggeett__cclleeaann(())
).
Once we have captured the data, it can be altered, and
must be sent back to the client, explicitly, by
eecchhoo
ing
the modified response and its corresponding headers
The headers that we specify are:
CCoonntteenntt--LLeennggtthh
(we’ve
compressed the data, so there’s probably less to send),
and
CCoonntteenntt--TTyyppee
(the data is no longer formatted as
XML—it has become binary). Take a look at Listing 4 to
see how our SOAP server looks after these changes.
Using Encryption
Instead of compressing our data, we could encrypt it or
even do both. Encryption can easily be achieved using
the mcrypt extension which has been available in PHP
since PHP 3, but really only became powerful in PHP 4.
I really want to recommend that you read more about
cryptography before you start using it in your software.
It’s best to understand why you do something a certain
way than to just assume that it will be secure enough,
when following the instructions I give below.
There are many different kinds of encryption that you
can apply with mcrypt, but we will stick to 256 bit AES
encryption which is secure enough for our purposes.
We will specify a key that both the client and server will
use to encrypt and decrypt their data. We will also gen-
erate a non-secret initialization vector (IV), on the fly,
which will scramble all data before encrypting it. This
way, our key will be harder to crack, even if part of the
contents of our data is known—which is the case with
plaintext SOAP.
As we have seen in the previous examples, only two
functions are necessary to compress our data. We will
now implement two functions that can be used on
both the client- and server-side for encrypting and
decrypting information.
When sending an initialization vector and encrypted
data, we must choose a format in which to put this
information. In this example, I will format the informa-
tion as such:
1 byte to specify the IV-length in bytes,
the IV,
the encrypted data.
These items are concatenated to form the transaction
data. For easy handling, we will define a few constants
to contain the encryption key, the cryptography algo-
rithm, and the block cipher mode. Packaging these
items like this will make it easier to maintain our soft-
ware if this configuration ever changes.
We now have a good reason to extend the
SSooaappSSeerrvveerr
class: we can re-implement the encryption
and decryption methods, and make our own
hhaannddllee(())
implementation. The new encryption and decryption
functions will have to initialize (and close) the mcrypt
library, declare/determine the cryptography algorithm,
extract the initialization vector and the block cipher
mode, and encrypt/decrypt the passed data with the
proper key.
In the case of the encryption function, we can have
mcrypt generate the IV and encrypt our data. The func-
tion can then return a long string of data that is formed
by concatenating the length of the IV (in bytes), the IV
itself, and the encrypted request.
In the decryption function, we will parse this string to
determine the IV and the encrypted data. Once these
are separated, they can be passed to the mcrypt library,
which will give us what we want: the decrypted SOAP
message. You can see the code for the client and serv-
er scripts in Listings 5 and 6, respectively.
In the client script you will notice that
__ddooRReeqquueesstt(())
has hardly changed. The calls to the compression func-
tions have simply been replaced by calls to our crypto
functions. Note that the key used in these examples is
much too simple to be used in real-world situations. To
be safe, generate your own key and make sure it’s as
random as possible. Remember that your key doesn’t
have to consist of “readable” characters. When you
look at the server script you will see that our
hhaannddllee(())
function completely mimics the behaviour of
SSooaappSSeerrvveerr::::hhaannddllee(())
. Other than this new implemen-
tation, and the addition of our encryption and decryp-
tion functions (and the three defined constants), little
has changed.
April 2005
●
PHP Architect
●
www.phparch.com
FFEEAATTUURREE
12
Secure SOAP Transactions in Command Line Applications
Listing 4
1 <?php
2 class SoapHandler
3 {
4 public function systemName()
5 {
6 return implode(“ “, posix_uname());
7 }
8 }
9
10 $soap = new SoapServer(null, array(“uri” =>
“http://yourownuri/”));
11 $soap->setClass(“SoapHandler”);
12
13 $request = gzuncompress($GLOBALS[“HTTP_RAW_POST_DATA”]);
14
15 ob_start();
16 $soap->handle($request);
17 $response = ob_get_clean();
18
19 $response = gzcompress($response, 5);
20
21 header(“Content-Type: application/octet-stream”);
22 header(“Content-Length: “.strlen($response));
23
24 echo $response;
25 ?>
“...only two functions are nec-
essary to compress our data.”
FFEEAATTUURREE
Secure SOAP Transactions in Command Line Applications
April 2005
●
PHP Architect
●
www.phparch.com
13
SOAP In a Command Line Application
Now that we’ve seen how to apply encryption to SOAP
transactions, let’s move on to the tricky job of mimick-
ing a SOAP server within a command line PHP applica-
tion. The impact that this will have on the code that we
have seen so far, is that we have to do all of the receiv-
ing and responding ourselves. We will write a simple
and incomplete, yet functional (for what we need)
HTTP server. Don’t let this scare you; it is easier than it
may seem.
Since our script now needs to handle the HTTP pro-
tocol, I will explain how a SOAP transaction works at
this level. There are several types of HTTP requests. The
most commonly known are
GGEETT
and
PPOOSSTT
which can be
used to retrieve a webpage, or, in this case, a SOAP
Listing 5
1 <?php
2 define(“CRYPTO_KEY”, ‘this is our highly secret key..!’);
// a 256 bit (32 bytes) key
3 define(“CRYPTO_METHOD”, ‘rijndael-256’);
4 define(“CRYPTO_MODE”, ‘ctr’);
5
6
7 class MySoapClient extends SoapClient
8 {
9 public function __doRequest($request, $location, $action,
$version)
10 {
11 $request = $this->encrypt($request);
12
13 $response = parent::__doRequest($request, $location,
$action, $version);
14
15 return $this->decrypt($response);
16 }
17
18
19 private function encrypt($data)
20 {
21 $td = mcrypt_module_open(CRYPTO_METHOD, ‘’, CRYPTO_MODE,
‘’
);
22 $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td),
MCRYPT_DEV_URANDOM);
23
24 mcrypt_generic_init($td, CRYPTO_KEY, $iv);
25 $data = mcrypt_generic($td, $data);
26 mcrypt_generic_deinit($td);
27
28 mcrypt_module_close($td);
29
30 return chr(strlen($iv)).$iv.$data;
31 }
32
33 private function decrypt($data)
34 {
35 $ivlen = ord($data[0]);
36 $iv = substr($data, 1, $ivlen);
37 $data = substr($data, 1 + $ivlen);
38
39 $td = mcrypt_module_open(CRYPTO_METHOD,
‘’, CRYPTO_MODE,
‘’);
40
41 mcrypt_generic_init($td, CRYPTO_KEY, $iv);
42 $data = mdecrypt_generic($td, $data);
43 mcrypt_generic_deinit($td);
44
45 mcrypt_module_close($td);
46
47 return $data;
48 }
49 }
50
51
52 $soap = new MySoapClient(null, array(
53 “location” => “http://localhost/soapdemo/server.php”,
54 “uri” => “http://yourownuri/”));
55
56 echo “system: “.$soap->systemName();
57 ?>
Listing 6
1 <?php
2 define(“CRYPTO_KEY”, ‘this is our highly secret key..!’);
// a 256 bit (32 bytes) key
3 define(“CRYPTO_METHOD”, ‘rijndael-256’);
4 define(“CRYPTO_MODE”, ‘ctr’);
5
6
7 class SoapHandler
8 {
9 public function systemName()
10 {
11 return implode(“ “, posix_uname());
12 }
13 }
14
15
16 class MySoapServer extends SoapServer
17 {
18 public function handle($request=””)
19 {
20 if (empty($request))
21 $request = $this-
>decrypt($GLOBALS[“HTTP_RAW_POST_DATA”]);
22 else
23 $request = $this->decrypt($request);
24
25 if ($request)
26 {
27 ob_start
();
28 parent::handle($request);
29 $response = ob_get_clean();
30
31 echo $this->encrypt($response);
32 }
33 }
34
35
36 private function encrypt($data)
37 {
38 $td = mcrypt_module_open(CRYPTO_METHOD, ‘’, CRYPTO_MODE,
‘’);
39 $iv = mcrypt_create_iv(mcrypt_enc_get_iv_size($td),
MCRYPT_DEV_URANDOM);
40
41 mcrypt_generic_init($td, CRYPTO_KEY, $iv);
42 $data = mcrypt_generic($td, $data);
43 mcrypt_generic_deinit($td);
44
45 mcrypt_module_close($td);
46
47 return chr(strlen($iv)).$iv.$data;
48 }
49
50 private function decrypt($data)
51 {
52 $ivlen = ord($data[0]);
53 $iv = substr($data, 1, $ivlen);
54 $data = substr($data, 1 + $ivlen);
55
56 $td = mcrypt_module_open(CRYPTO_METHOD, ‘’, CRYPTO_MODE,
‘’);
57
58 mcrypt_generic_init($td, CRYPTO_KEY, $iv);
59 $data = mdecrypt_generic($td, $data);
60 mcrypt_generic_deinit($td);
61
62 mcrypt_module_close($td);
63
64 return $data;
65 }
66 }
67
68
69 $soap = new MySoapServer(null, array(“uri” =>
“http://yourownuri/”));
70
$soap->setClass(“SoapHandler”);
71
72 ob_start();
73 $soap->handle();
74 $response = ob_get_clean();
75
76 header(“Content-Type: application/octet-stream”);
77 header(“Content-Length: “.strlen($response));
78
79 echo $response;
80 ?>
April 2005
●
PHP Architect
●
www.phparch.com
FFEEAATTUURREE
14
Secure SOAP Transactions in Command Line Applications
response. The difference is in how the methods send
data to the web server. With the
GGEETT
method, all infor-
mation—besides cookies—is embedded in the request
path like this:
GET /some_dir/some_file.php?name=John&sirname=Doe
HTTP/1.1
An HTTP
PPOOSSTT
request is suitable for sending relatively
large chunks of data to a web server. The first line of a
post-action is simpler:
POST /some_dir/some_file.php HTTP/1.1
After the other HTTP headers are sent, the
PPOOSSTT
data is
appended to the request, in “var=value” form. In the
case of a SOAP message, the contents of the request
made up of the
PPOOSSTT
data. In Listing 7, you can see
what a transaction actually looks like (it is unaltered).
Our task is to parse this request and pass the correct
data—which is everything we receive after the
PPOOSSTT
header—to our
hhaannddllee(())
function. Listing 8 contains a
typical HTTP response. We will have to send something
like this back to the client, but we will first encrypt the
SOAP message.
To handle the network operations, we can use the
socket stream API, which was introduced in PHP 5. We
will listen on a TCP port for SOAP clients to connect to
our server, by calling
ssttrreeaamm__ssoocckkeett__sseerrvveerr(())
, and
when they do, we will retrieve the socket resource with
ssttrreeaamm__ssoocckkeett__aacccceepptt(())
. This sequence will be per-
formed in a loop, so we can accept clients until the
process is killed.
Once a client is connected, it will send the SOAP
request, starting with the HTTP
PPOOSSTT
header. By parsing
the headers, we can determine the HTTP version spo-
ken by the client, and the length of the data that is
being sent (in a line that looks like “Content-Length:
123”). These headers can be received with
ssttrreeaamm__ggeett__lliinnee(())
, but, unfortunately, this function
was buggy up to, and including, PHP 5.0.3. To make
things a little easier, I’ve created a function called
ssttrreeaamm22__ggeett__lliinnee(())
which we can use instead.
After all of the headers have been received—that is,
after we receive a blank line—we can read the SOAP
request data. We will read the proper number of bytes
(determined a header, as described above) with
ssttrreeaamm__ggeett__ccoonntteennttss(())
.
Now that we have received the encrypted data, we
have the same situation as before: we can serve the
body of the request to our
hhaannddllee(())
function which will
decrypt and execute the SOAP call, and output the
encrypted response. We will catch this response and
send it to the client, but only after we have sent the
proper headers and a blank line, to mimic an HTTP
server’s response. Once this is complete, we can close
the client socket and wait for a new connection to be
made. The modifications to our web-based server script
can be seen in Listing 9.
Because our server script is listening on a certain port,
and the path of our server script no longer matters, it is
not necessary to request a certain path on the client
side (the server script can basically ignore the request-
ed path). So, in the case of our example, something
similar to this would suffice:
$soap = new MySoapClient(null, array(
“location” => “http://localhost:12345/”,
“uri” => “http://yourownuri/”));
Error handling
Unfortunately, because the SOAP extension was
designed to be run in a PHP environment that is host-
ed on an external HTTP server, throwing exceptions
from within it is somewhat problematic—especially in
“persistent” mode, which we will use when we imple-
ment session support (explained below). Hopefully, this
will change in the future, but until then we will have to
Listing 8
1 HTTP/1.1 200 OK
2 Date: Sat, 05 Mar 2005 10:05:14 GMT
3 Server: Apache/1.3.26 (Unix) Debian GNU/Linux PHP/5.0.3-1.dot-
deb.0
4 X-Powered-By: PHP/5.0.3-1.dotdeb.0
5 Content-Length: 575
6 Keep-Alive: timeout=15, max=100
7 Connection: Keep-Alive
8 Content-Type: text/xml; charset=utf-8
9
10 <?xml version=”1.0” encoding=”UTF-8”?>
11 <SOAP-ENV:Envelope
12 xmlns:SOAP-ENV=”
13 xmlns:ns1=”http://yourownuri/”
14 xmlns:xsd=”
15 xmlns:xsi=”
16 xmlns:SOAP-ENC=”
17 SOAP-
ENV:encodingStyle=”
18 <SOAP-ENV:Body>
19 <ns1:systemNameResponse>
20 <return xsi:type=”xsd:string”>Linux deepthought 2.4.26 #2
Sat Apr 24 14:02:08 CEST 2004 i686</return>
21 </ns1:systemNameResponse>
22 </SOAP-ENV:Body>
23 </SOAP-ENV:Envelope>
Listing 7
1 POST /soapdemo/server.php HTTP/1.1
2 Host: localhost
3 Connection: Keep-Alive
4 User-Agent: PHP SOAP 0.1
5 Content-Type: text/xml; charset=utf-8
6 SOAPAction: “http://yourownuri/#systemName”
7 Content-Length: 442
8
9 <?xml version=”1.0” encoding=”UTF-8”?>
10 <SOAP-ENV:Envelope
11 xmlns:SOAP-ENV=”
12 xmlns:ns1=”http://yourownuri/”
13 xmlns:xsd=”
14 xmlns:xsi=”
15 xmlns:SOAP-ENC=”
16 SOAP-
ENV:encodingStyle=”
17 <SOAP-ENV:Body>
18 <ns1:systemName/>
19 </SOAP-ENV:Body>
20 </SOAP-ENV:Envelope>
FFEEAATTUURREE
April 2005
●
PHP Architect
●
www.phparch.com
Secure SOAP Transactions in Command Line Applications
15
Listing 9
1 <?php
2 // stream2_get_line() is used because stream_get_line() is
not usable up to PHP 5.0.4
3
4 function stream2_get_line($stream, $length, $limit)
5 {
6 $line = “”;
7
8 while (($c = fgetc($stream)) !== $limit)
9 {
10 if ($c === false) return false;
11 $line .= $c;
12 if (strlen($line) == $length) break;
13 }
14 return $line;
15 }
16
17
18 // handle SOAP requests:
19
20 $server = stream_socket_server(“tcp://0.0.0.0:12345”, $errno,
$errstr) or die(“Could not create server socket ($errno):
$errstr\n”);
21
22 while (true)
23 {
24 if ($client = @stream_socket_accept($server))
25 {
26 $contentlength = 0;
27
28 if (($line = stream2_get_line($client, 256, “\n”)) !==
false)
29 {
30 if (preg_match(“/HTTP\/([0-9]\.[0-9])$/”, rtrim($line,
“\r”), $match))
31 {
32 $httpversion = $match[1];
33
34 while (($line = stream2_get_line($client, 256, “\n”))
!== false)
35 {
36 $line = rtrim($line, “\r”);
37
38 if (stripos($line, “content-length:”) === 0)
39 $contentlength = (int)substr($line, 15);
40
41 if ($line == “”) // the http-header has ended
42 {
43 if ($contentlength > 0)
44 if (
$data = stream_get_contents($client,
$contentlength))
45 {
46 ob_start();
47
48 $soap->handle($data);
49
50 $response = ob_get_clean();
51
52 fwrite($client,
53 “HTTP/”.$httpversion.” 200 OK\r\n”.
54 “Date: “.gmdate(“D, d M Y H:i:s”).”
GMT\r\n”.
55 “Content-Length: “.strlen($response).”\r\n”.
56 “Content-Type: application/octet-stream\r\n”.
57 “Connection: Closed\r\n\r\n”.
58 $response
59 );
60 }
61 break;
62 }
63 }
64 }
65 }
66 fclose($client);
67 }
68 }
69 ?>
Listing 10
1 <?php
2 class SoapHandler
3 {
4 private $server;
5
6 public function __construct(SoapServer $server)
7 {
8 $this->server = $server;
9 }
10
11 private function error($str)
12 {
13 $this->server->error = $str;
14 return null;
15 }
16
17 public function systemName()
18 {
19 if (!function_exists(“posix_uname”))
20 return $this->error(“the function posix_uname() does
not exist”);
21
22 return implode(“ “, posix_uname());
23 }
24 }
25
26
27 class MySoapServer extends SoapServer
28 {
29 public $error;
30
31
32 public function handle(
$request=””)
33 {
34 if (empty($request))
35 $request = $this->decrypt($GLOBALS[“HTTP_RAW_POST_DATA”]);
36 else
37 $request = $this->decrypt($request);
38
39 if ($request)
40 {
41 $this->error = null;
42
43 ob_start();
44 parent::handle($request);
45
46 if ($this->error === null)
47 $response = ob_get_clean();
48 else
49 {
50 ob_end_clean();
51 $response = “<?xml version=\”1.0\” encoding=\”UTF-
8\”?>\n”
52 . “<SOAP-ENV:Envelope xmlns:SOAP-
ENV=\”
53 . “ <SOAP-ENV:Body>\n”
54 . “ <SOAP-ENV:Fault>\n”
55 . “ <faultcode>RPC</faultcode>\n”
56 . “ <faultstring>”.utf8_encode($this-
>error
).”</faultstring>\n”
57 . “ </SOAP-ENV:Fault>\n”
58 . “ </SOAP-ENV:Body>\n”
59 . “</SOAP-ENV:Envelope>\n”;
60 }
61
62 echo $this->encrypt($response);
63 }
64 }
65
66 ...
67 }
68
69
70 $soap->setClass(“SoapHandler”, $soap);
71 ?>
work around this problem.
Luckily, there is a way to throw exceptions that a
SOAP client can catch: the server can respond with a
SOAP fault. A SOAP fault is not exactly the same as a
regular exception, though. When a client receives a
fault, the error information can be extracted from pub-
lic variables called
ffaauullttccooddee
and
ffaauullttssttrriinngg
. To
account for this type of response, our client script’s
remote procedure call would change to the following:
try
{
echo “system: “.$soap->systemName();
}
catch (SoapFault $e)
{
echo “Error (“.$e->faultcode.”): “.$e->faultstring;
}
In our server script, we will have to generate a SOAP
error message that contains the appropriate
ffaauullttccooddee
and
ffaauullttssttrriinngg
. Unfortunately, we can’t throw an
exception and handle that, so we will have to consider
an alternative: we can implement a private function
called
eerrrroorr(())
in our
SSooaappHHaannddlleerr
class, and we’ll call
this function whenever an error is generated.
The
eerrrroorr(())
function will place the error description
into a public variable in our
MMyySSooaappSSeerrvveerr
object. When
its
hhaannddllee(())
function notices that there was an error, it
will discard the SOAP message generated by
SSooaappSSeerrvveerr::::hhaannddllee(())
, and generate (and output) a
SOAP fault message.
When the client receives this message, the
SSooaappCClliieenntt
object will interpret it as a remote excep-
tion, and in turn, throw a local exception that our script
can catch, allowing it ti act in a responsible way. Our
SOAP handler will need to know which object on the
SOAP server contains the error variable. This can be
determined by adding a constructor to our
April 2005
●
PHP Architect
●
www.phparch.com
FFEEAATTUURREE
16
Secure SOAP Transactions in Command Line Applications
Listing 11
1 <?php
2 class SoapSession
3 {
4 const SESSION_KEY_LENGTH = 15;
5 const KEYCHARS =
“abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789”;
6 const DEFAULT_LANGUAGE = “en”;
7
8 private $server;
9
10 public $key;
11 public $lasttouch;
12 public $language;
13
14
15 public function __construct(SoapServer $server, $language)
16 {
17 $this->server = $server;
18 $this->lasttouch = time();
19 $this->language = strtolower($language);
20
21 $this->key = “”;
22 $n = strlen(self::KEYCHARS)-1;
23
24 for ($i=0; $i < self::SESSION_KEY_LENGTH
; $i++)
25 $this->key .= substr(self::KEYCHARS, mt_rand(0, $n), 1);
26 }
27
28
29 public function error($msgs)
30 {
31 if (isset($msgs[$this->language]))
32 $this->server->error = $msgs[$this->language];
33 else
34 if (isset($msgs[self::DEFAULT_LANGUAGE]))
35 $this->server->error = $msgs[self::DEFAULT_LANGUAGE];
36 else
37 $this->server->error = reset($msgs);
38
39 return null;
40 }
41 }
42
43
44 class SoapHandler
45 {
46 const SESSION_TIMEOUT = 3;
47
48 private $server;
49 private $sessions;
50
51
52 public function __construct(SoapServer $server)
53 {
54 $this->server = $server;
Listing 11 (cont’d)
55 $this->sessions = array();
56 }
57
58
59 private function checkSession($sessionkey)
60 {
61 $found = null;
62 $time = time();
63
64 foreach ($this->sessions as $key => $session)
65 {
66 if ($time - $session->lasttouch > self::SESSION_TIMEOUT)
67 unset($this->sessions[$key]);
68 else
69 if (strcmp($session->key, $sessionkey) == 0)
70 {
71 $session->lasttouch = $time;
72 $found = $session;
73 }
74 }
75
76 if ($found === null)
77 $this->server->
error = “Invalid session key”;
78
79 return $found;
80 }
81
82
83 public function handshake($language)
84 {
85 $session = new SoapSession($this->server, $language);
86
87 $this->sessions[] = $session;
88
89 return $session->key;
90 }
91
92
93 public function systemName($sessionkey)
94 {
95 if (!$session = $this->checkSession($sessionkey)) return;
96
97 if (!function_exists(“posix_uname”))
98 {
99 return $session->error(array(
100 “en” => “The function posix_uname() does not exist”,
101 “nl” => “De functie posix_uname() bestaat niet”,
102 “de” => “Die Funktion posix_uname() ist nicht
vorhanden”));
103 }
104
105 return implode(“ “
, posix_uname());
106 }
107 }
108 ?>
handler class. We can pass the handle of our
MMyySSooaappSSeerrvveerr
object as a second parameter to
SSooaappSSeerrvveerr::::sseettCCllaassss(())
so the constructor of our han-
dler can receive it and store it in a private variable that
will be used whenever the handler needs to generate
an error.
Here’s a summary of what we will have to change in
our server script to enable error handling (as shown in
Listing 10):
• implement a constructor in
SSooaappHHaannddlleerr
to
receive a
SSooaappSSeerrvveerr
object and store its
handle in a private variable
• implement a private
eerrrroorr(())
function in
SSooaappHHaannddlleerr
that can be called from handler
functions
• add a public
$$eerrrroorr
variable to
MMyySSooaappSSeerrvveerr
that can be set from our
SSooaappHHaannddlleerr
• extend our
MMyySSooaappSSeerrvveerr::::hhaannddllee(())
function
to deal with errors
• change the call to
ffSSooaappSSeerrvveerr::::sseettCCllaassss(())::ff
pass the
SSooaappSSeerrvveerr
’s handle as a parameter
when constructing the
MMyySSooaappSSeerrvveerr
object
Sessions
It would be nice if our command-line script could com-
municate with the server at a more advanced level. We
could establish a handshake procedure, during which
the client could tell the server about itself. The server
could then return a unique session key to the client.
Using this session key, our server could verify the
client’s identity during the following remote procedure
calls. Now, it becomes possible for a client to execute
multiple commands without losing state-information.
Other possibilities include:
• applying rules and boundaries to the client,
depending on authorization levels
• returning errors and warnings in the pre-
ferred language of the user
• making sure the client and server use the
same version of your API
I’m sure you can probably think of a thing or two, your-
self. To get you started, I’ll show you a way to create a
session system, and provide multi-language exceptions.
One thing is vitally important for any session system:
persistence. You may not have realized this, but a
SSooaappHHaannddlleerr
instance was created (and destructed) for
each SOAP request we processed. It’s impossible to
store session information in our handler because it’s
destroyed after every request. We can alter this behav-
iour with the
SSooaappSSeerrvveerr::::sseettPPeerrssiisstteennccee(())
function,
and by passing the constant
SSOOAAPP__PPEERRSSIISSTTEENNCCEE__SSEESSSSIIOONN
.
This ensures that our handler is only destroyed once—
when our script terminates.
As I’ve mentioned, our session system requires that
we implement a handshake procedure. This will simply
be a single call (to the
hhaannddsshhaakkee(())
function) with
which the server can determine the client’s preferences.
In this case, we only need to tell the server that we
want to receive error messages in English. Our client
code becomes:
try
{
$session = $soap->handshake(“en”);
echo “system: “.$soap->systemName($session);
}
catch (SoapFault $e)
{
echo “Error (“.$e->faultcode.”): “.$e->faultstring;
}
As you can see, the server’s
hhaannddsshhaakkee(())
function
returns a session key which we will use for the follow-
ing function call to
ssyysstteemmNNaammee(())
. This way, the server
will be able to identify us and can, if necessary, throw a
SOAP fault with a message in the language that was
asked for.
The client was, of course, the easy part. The implica-
tions on the server side are a bit more extensive, but
our
MMyySSooaappSSeerrvveerr
class can remain the same—the error
handling mechanism that we’ve already introduced can
still be used the same way.
The big changes are in our
SSooaappHHaannddlleerr
class. First,
we’ll add a
hhaannddsshhaakkee(())
SOAP function that will initial-
ize a new session, add it to the stack of sessions and
return a generated session key. The easiest and cleanest
way to achieve this is to create an instance of the
SSooaappSSeessssiioonn
class whose constructor will generate the
session key which can then be passed, as a parameter,
to our
ssyysstteemmNNaammee(())
implementation. Of course, this
parameter must be passed to all SOAP functions we will
write, under this framework, so we should create a pri-
vate session checking function that will return the han-
dle to an instance of our
SSooaappSSeessssiioonn
object, if it could
be found, for the given session key. This session check-
ing function can set an error flag if an appropriate,
existing session can not be found.
Because the session retrieval function must crawl
through the session stack, it’s a very convenient place
to destroy sessions that have timed out. Our sessions
will remember when they were last validated—this hap-
pens once for every SOAP function call—and the
destruction function can simply compare the session’s
FFEEAATTUURREE
April 2005
●
PHP Architect
●
www.phparch.com
Secure SOAP Transactions in Command Line Applications
17
“O
ne thing is vitally impor-
tant for any session system:
persistence.”
timestamp with the current time. If the difference is
greater than a predetermined interval, the session can
be destroyed.
In order to return errors in the correct language, the
eerrrroorr(())
function must be called with error messages in
multiple languages, and because it will have to pick the
correct language, it’s wise to move this function to the
SSooaappSSeessssiioonn
class. This class, as you’ve seen, contains
session object that enables us to determine which lan-
guage to use.
Now that we know what needs to change in our
SSooaappHHaannddlleerr
class, let’s take a closer look at the
SSooaappSSeessssiioonn
class that we introduced, above. As I men-
tioned, its constructor will create a key for the session,
and there will be a multi-language error handler. The
key-generating constructor can be implemented in
many different ways. The approach that I’ve chosen
calls
mmtt__rraanndd(())
, a number of times, to fetch random
characters from a constant that contains all of the pos-
sible characters that we could use in a session key. The
results are concatenated to create a random, pseudo-
unique key.
The error handler will have to be changed so it can
accept error messages in several languages, at the same
time. This can be accomplished by passing an associa-
tive array whose keys denote a specific language, and
whose values contain the text that we will return. The
function call becomes:
$session->error(array(
“en” => “Something went very wrong”,
“nl” => “Er ging iets heel erg fout”,
“de” => “Etwas ist sehr falsch gegangen”));
The error handler should be able to fall back to a
default language, if the passed pool of messages does
not contain the language specified in the session. To
satisfy this requirement, we will define a default lan-
guage constant in our class, so our handler can use the
preferred language, if available, and if not, use the
default. In a situation where even this is not possible,
the error handler will return the first message in the
passed array. This way, at least something will be
returned, in a case where an error situation does not
have a message for the preferred language.
That’s almost all we have to do to implement a multi-
language session system. All that’s left is to make our
SOAP handling functions use the new
eerrrroorr(())
function
in the
SSooaappSSeessssiioonn
object. To see what our
SSooaappHHaannddlleerr
and
SSooaappSSeessssiioonn
classes look like, take a look at Listing
11, where you will see that if the
ppoossiixx__uunnaammee(())
func-
tion does not exist, an error would be triggered.
What’s Next?
In this article I’ve shown you how to create a SOAP
client and server, web- as well as command-line-based.
You’ve seen how to alter transaction data (in-line),
apply encryption, handle errors, and create sessions.
There are, however, a few things we didn’t cover that
you should probably look at before you put all of this
into a production environment.
First, ask yourself what would happen if corrupt data
were sent to your SOAP server. The decryption would
still work, because a crypto algorithm has no idea what
“good” or “bad” data is, but the
SSooaappSSeerrvveerr
class was
built to terminate the script on an error event, which is
exactly what would happen. So, before you call the
hhaannddllee(())
function, you might want to verify that the
incoming data is a genuine SOAP message.
Next, you should consider how easy it is to mount a
DOS (denial of service) attack against our SOAP server.
A malicious client could keep sending (partial) requests
with just enough time in between to keep the connec-
tion from timing out: other SOAP clients would have to
wait forever. To solve this issue, you could create a more
robust TCP server that can handle multiple connec-
tions, simultaneously. Check out
ssttrreeaamm__sseelleecctt(())
if
you’re planning on implementing such a system.
PHP 5 provides a very useful and easy to use exten-
sion for SOAP that, as we’ve seen, is relatively easy to
use in a CLI environment. Error handling can’t, current-
ly, be implemented the way I would like, but in the
(perhaps near) future, things will probably improve.
Until they do, I hope the method I described will help
you out. Good luck and have fun coding!
April 2005
●
PHP Architect
●
www.phparch.com
FFEEAATTUURREE
18
Secure SOAP Transactions in Command Line Applications
About the Author ?>
To Discuss this article:
/>Ron Korving is an advanced computer science graduate and senior PHP
developer located in Breda (The Netherlands). He enjoys developing
complex software architectures and writing low level software. You can
contact Ron through his website:
hhttttpp::////wwwwww..rroonnkkoorrvviinngg..nnll
.