This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
The Object Oriented Evolution of PHP5 A Look At The New OOP Model Using The Zend Engine 2.0 By Zeev Suraski
Creating A Customized Template-Engine Hacking the Smarty Template Engine
www.phparch.com
Enterprise Applications: PHP in a Multitier Environment Data Fingerprinting with Hash Functions in PHP Time Travel: Breadcrumbs and Session History with PHP
Plus:
Tips&Tricks, Book Reviews, Product Reviews and much more..
The designers of PHP offer you the full spectrum of PHP solutions
Serve More. With Less. Zend Performance Suite Reliable Performance Management for PHP
Visit www.zend.com for evaluation version and ROI calculator
Technologies Ltd.
TABLE OF CONTENTS
php|architect Departments
Features 10
INDEX
4
by Peter James
EDITORIAL RANTS
18 5
Time Travel - Breadcrumbs and History with PHP
NEW STUFF
The Object Oriented Evolution of PHP by Zeev Suraski
6
REVIEWS
27
by Dave Palmer
Zend Encoder V.3.1.0
22
REVIEWS
32
LogiCreate
61
TIPS & TRICKS
BOOK REVIEWS
Creating a Customized Template-Engine by Andreas Demmer
38
by John Holmes
65
PHP in an N-Tiered World
Building a Turing Test with PHP and the GD Library by Marco Tabini
47
Using, Reusing and Extending Smarty by Tomica Jovanovic
67
exit(0); Hello, Mr. Gates?
55
Data Fingerprinting with Hash Functions in PHP by Leon Vismer
February 2003 · PHP Architect · www.phparch.com
3
EDITORIAL RANTS
EDITORIAL
L
ately, I’ve been measuring the progress of php|a in terms of “brilliant ideas”. After a careful analysis of our history up to this point, I have noticed that instead of following a smooth linear progression based on constant innovation, our ascension from the ashes of the publishing world seems to have resulted from short-lived but intense flashes of genius, which are then followed by long periods of cerebral inactivity. (A related study also showed that I have way too much time on my hands, but I was far too busy to notice or even care about that). For example, Arbi came up with the idea of calling our magazine “php|architect” after I had been brooding over names like “The PHP Gazette” and “The PHP Informer” for days. I can only be thankful for the completely random sequence of synaptic signals that made that brilliant idea come to life. Given the spontaneity of our collective genius, it would most definitely prove futile to even try to explain how we arrived at our most brilliant idea: to ask Brian Jones, who joined our editorial staff last month, to become our new Editor-in-Chief. I can only say that it was a moment of unparalleled brilliance. I’m sure that we burned a good number of those remaining brain cells which had not yet been fried by years of exposure to the radiation that our monitors happily produce to keep us warm, but it was worth it. This is, therefore, my last editorial as the Editor of php|a. I will still be the Publisher of this magazine and I will do my best to contribute my thoughts to our exit(0) column (whose name
was another flash of inspiration, if I may say so myself), but I will move on to some of the new initiatives that we have in store for you, and Brian will take over the entire editorial process of php|a. As my parting words from my editorial post, let me tell you about the Editor-in-Chief. The post of Editor-in-Chief is instrumental in defining the course and stature of a magazine, and I can only be happy that Brian has decided to accept our request to take this position with us (not to mention how happy I am that someone else will be doing it! But alas, poor foolish soul that he is, Brian will only read this once the magazine has already hit the virtual stands, and by then his fate will be sealed). Naturally, the person who can sport the title of Editor-in-Chief enjoys a great deal of power—a power that Brian promptly abused by asking me to rewrite this editorial, claiming that “my thoughts were too fragmented”. As if. That could ever. Happen. Let me now tell you about the Publisher. The post of Publisher affords great powers as well. That includes the power to edit and reject (did I say reject? I meant “evaluate”) the Editor-inChief’s monthly editorial. Regardless of whether his thoughts are fragmented or not. Ah, the bittersweet taste of revenge...
February 2003 · PHP Architect · www.phparch.com
php|architect Volume II - Issue 2 February, 2003 Publisher Marco Tabini
Editors Arbi Arzoumani Brian K. Jones Marco Tabini Graphics & Layout Arbi Arzoumani
Administration Emanuela Corso
Authors Andreas Demmer, Peter James, Tomica Jovanovic, Dave Palmer, Zeev Suraski, Marco Tabini, Leon Vismer
php|architect (ISSN 1705-1142) is published twelve times a year by Marco Tabini & Associates, Inc., P.O. Box. 3342, Markham, ON L3R 6G6, 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.
British PHP software company ionCube Ltd. released version 2.0 of their standalone Encoder product last month. The new encoder providers additional features compared to its predecessors, such as the possibility of creating text headers that are appended to the encoded files. This could be useful, for example, for creating copyright and ownership notices, as well as instructions and how-to guides at the beginning of each file. The Encoder sells for $349.00 US. For more information, you can visit the ionCube website at : http://www.ioncube.com/encoder TechMeridian Releases XAVIOUR CMS
TechMeridian, a new PHP development company based in the United States, has released a product called XAVIOUR Web Application Platform. According to TechMeridian, XAVIOUR represents a combination of the features normally found in content management and templating systems. It promotes reusability of the code and provides a flexible framework that can easily be extended by writing code directly into the templates, if necessary. XAVIOUR is entirely based on PHP and PostgreSQL, thus providing a platform that can easily be taken to an enterprise-level of stability and performance. The software product costs $199.00 US for a single-domain license. More information can be found on the TechMeridian website at : http://www.techmeridian.com/xaviour
users of the OpenWeb product free of charge. A new full subscription can be purchased for $60 US from the OpenOSX website at : http://openosx.com/openweb/ 2003 PHP Conference in Montréal
This just hot off the press—The Québec PHP Association announced that they will be holding a PHP conference in Montréal, Canada, on March 20th and 21st. The conference will attract a who’s who of the PHP community, from PHP creator Rasmus Lerdorf to Zend-engine co-creator (and php|a author) Zeev Suraski. The conference will include sessions in both French and English, and it is sure to attract visitors from the Americas as well as from Europe. The php|a team will be with a booth and to cover the event. http://phpconf.phpquebec.com/ php|a
We Dare You To Be A Professional. Subscribe to php|a Today and Win a book from Wrox Press
OpenOSX Updates OpenWeb With PHP 4.3.0
MacIntosh open-source software provider OpenOSX have updated their OpenWeb CD product to include the latest version of PHP. OpenWeb is a CD compilation of open-source software for the MacIntosh market designed to provide publishing and development tools to Web developers and webmasters. Besides technologies like Apache, PHP and MySQL, it also includes a shopping card, a content management system and a graphical management interface. The new 2.5.1 update is available to registered
February 2003 · PHP Architect · www.phparch.com
php|architect The Magazine For PHP Professionals
5
REVIEWS
REVIEWS
Reviewed For You
Zend Encoder
V.3.1.0
By Marco Tabini
T
he simplicity and immediateness of a scripting language like PHP is a double-edged sword. On the one hand, everyone has direct access to the source code; no need to compile for separate platforms and distribute separate versions of your application. On the other... everyone has direct access to the source code, which makes the protection of intellectual property really difficult. Let’s face facts, it would be difficult for a software market to exist without some form of code protection. After all, PHP consultants and software developers are selling their knowledge, which, for better or for worse, is somewhat embedded into the source code they produce. The demand for code protection has resulted in several products which have been developed to make it possible to limit the functionality of a PHP script (or a series of scripts) so as to facilitate a commercial licensing scheme. The Zend Encoder, produced by Zend Technologies (yep, the same folks who wrote the Zend Engine on which PHP is based) is a rather complete system that makes it possible to transform a PHP source file into an “intermediate” representation of itself—that is, a preinterpreted set of bytecode instructions that the PHP interpreter would be able to execute but that are quite meaningless to a human being. The resulting file is further mangled to make reverse-engineering almost impossible. In addition, it is possible to require that a
February 2003 · PHP Architect · www.phparch.com
The Cost: $960.00 - $2,880.00 (US) (or less through special small business pricing) Requirements: -Supported PHP versions: 4.0.5 up through 4.3.0. -Supported operating systems: -Linux glibc 2.1 / 2.2 (e.g. RedHat 6.x/7.x/8.x, Debian 4.2, SuSE 6.4, Mandrake 8.1, and others) -Windows® 98 / NT 4.0 / 2000 / XP. -Solaris Sparc 2.6 /2.7 /2.8 (Non-GUI version). -Supported Web Servers for deployment: -Apache 1.3.x, IIS 4 / 5 -Zeus (using FastCGI) or any Web server that supports CGI -The Graphical User Interface is supported under Windows and Linux only. Product Page: Zend Encoder Home Page: Zend Technologies (http://www.zend.com) Company Background: Zend Technologies provides web developers and enterprises using PHP, integrated software solutions for developing, protecting and scaling their PHP applications providing a foundation that allows companies to efficiently and effectively develop PHP based web applications. Zend's founders are the designers of PHP 3, PHP 4 and Zend Engine 1.0; on which all PHP sites and applications are run. They are also currently leading the design and development of PHP 5 and the Zend Engine 2.0.
6
REVIEWS
Zend Encoder V.3.1.0
script only run if a special “license file” is present and provided by the end-user, thus making it possible to limit the execution of a script to a specific timeframe (limited trial), or to specific IPs, and so on. Installation Zend installation systems are, in my experience, among the best ones available to the PHP community. As in most cases, the Encoder is a Java-based application that will run on pretty much any platform—the Zend website allows you to download a version for Windows, Linux or Solaris. On Linux, the platform I tested it on, the application is set up through a very straightforward process that even connects to the Zend website and downloads the appropriate license code automatically. A free 14-day trial of the Encoder is available, its only limitation being that your encoded files will only work for three days, regardless of what settings you choose when converting them.
The User Interface Once installed, the Encoder launched flawlessly—no ifs, ands or buts about it. It features a neat graphical user interface (shown in Figure 1) that makes using the software for the encoding of large numbers of files easy and convenient. As you can see, the interface is based on the concept of a “project”—this way, you can encode entire directories of PHP scripts at the click of a mouse. In fact, I tried to encode the entire php|a website, and the entire operation took only a few seconds. The original files were not overwritten (if they were, I’d lose our source code!), but copied over to a destination folder of our choosing. The program even skipped over our CVS folders automatically! The Encoder supports several options designed to enrich the value of encoded files. For example, it’s possible to prepend a clear-text header to the file that allows you to insert installation or usage comments, or even plain PHP code that is executed if the server is not set up properly to accept encoded files. In addition,
Figure 1
February 2003 · PHP Architect · www.phparch.com
7
REVIEWS the Encoder also applies several optimizations to the code while encoding it (although I did not find much in the way of improvement to our scripts’ performance... I guess we just write good code!), and it is possible to require that each encoded script only work with other encoded scripts. This last feature can be very important if, for example, your include files contain your custom license authentication mechanism and you don’t want them to be replaced with dummy versions that circumvent your scheme.
Zend Encoder V.3.1.0 Figure 2
Licensing Capabilities Perhaps one of the most interesting features of the Encoder is its ability to encode a project and control the functionality that an end-user has access to by issuing a special license file. This can be very useful if you plan to offer specialized “trial” versions of your application that the end-user can play with, build upon as needed and then upgrade to a “full” version by just installing a single file. Licensing is only available if support for it is activated when a script is encoded. The licensing screen (Figure 2) offers a wide variety of options, including the ability to limit the execution of the scripts using constraints such as time or IP or even a hardware ID that is generated by a small Zend application distributed together with the Encoder. Distributing the Code Once encrypted, a project can be redistributed by simply transferring the files to another system. Depending on the settings, you may also need to distribute a license file. In order to execute your script on a target system, your customers will need to install a copy of the Zend Optimizer together with their installation of PHP. This is not normally a big problem, as the Optimizer is a freely available product whose installation only require a small change in the php.ini file. However, if the application is being hosted by a third party, it might be difficult to convince them to install this software. Advanced Capabilities And Documentation In addition to the GUI, the Encoder also features the necessary tools for encoding scripts and generating license files on-the-fly through a command-line executable. This can be helpful if your goal is to hand out February 2003 · PHP Architect · www.phparch.com
licenses in an automated fashion directly from a website. There is no online help for the Encoder, but the installation includes a well-thought-out manual in PDF format that provides plenty of information on using the Encoder, both programmatically and from the GUI. Unfortunately, neither the installation program nor the GUI application itself offers any hints that the documentation itself exists, and this may discourage the less-than-zealous user. Bottom Line The Zend Encoder is not a cheap product, although you can actually get it very inexpensively through the Zend Small Business Program that we illustrated in last month’s issue of php|a ($295.00 US will grant you a license to the Encoder, the Zend Studio IDE and the Zend Performance Suite). However, it is also a very well-thought-out product that offers ease-of-use and consistency—something you don’t always find in the world of PHP. If you’re looking for a way to protect your products and want the maximum flexibility possible, then I recommend you check this product out. If your business qualifies for the Small Business Program (visit http://www.zend.com/store/products/zendsmallbiz.php for more information). I think you will find that the entire package offers tremendous value at a very reasonable price. php|a
8
FEATURES
FEATURES
Time Travel Breadcrumbs and History with PHP By Peter James
With all of the things to consider when designing and laying out your site, you can quickly become lost and overwhelmed. Usability, accessibility and localization usually require attention before a single line of code is written. While I can’t possibly hope to cover all of these topics meaningfully in a single article, what I can do is give you some code to implement one very important (and often overlooked) tool to increase your site’s usability: Breadcrumbs.
Introduction The great philosopher and novelist George Santayana once told us: “Those who cannot remember the past are condemned to repeat it.” The past has always been a vital part of the present. Like a map, it tells us where we’ve been and gives us important information with which to make decisions about the future. If you’re asking yourself what all of this has to do with PHP, let me show you. Information is critical. If you are reading this magazine, your business probably revolves around information, and you probably recognize the value of that information to the people who want it. The fact is, regardless of what your business is, every time someone visits your site you have the opportunity to capture useful information about them. That information can be used to significantly enhance their experience on your site, and there are many levels of detail to be had. For instance, you could request that users register and log in to your site, which usually has required the provision of at least some personal information on behalf of the end user. You could use this to contact your customers with promotions or newsletters, or target their demographic (age, gender, location) with advertising. While the gleaning of this personal data requires action from the user (ie, voluntary registra-
February 2003 · PHP Architect · www.phparch.com
tion), other types of information can be gathered passively and silently. A prime example of the type of data which can be collected behind the scenes is ‘patterns of use’ information. This can provide invaluable insight into how your site is being used, allowing you to make decisions about the layout of your site. This could influence link
Information is critical. If you are reading this magazine, your business probably revolves around information, and you probably recognize the value of that information to the people who want it. REQUIREMENTS PHP Version: 4.0 and Above O/S: Any Additional Software: N/A
10
FEATURES placement, advertisement placement and numerous other factors. As an aside, one popular tool that can aid in the discovery and inspection of this data is phpOpenTracker. Another silently collected class of data, which will be our primary focus in this article, is the history of the current session. This is the most easily handled type of information. We can get it as they navigate, use it while we have them on the site, and then discard it when their session ends. Unlike many other types of desirable user data, it doesn’t require a database or the associated design; it is very transient. Interestingly enough, if stored across sessions, the history provides patterns of use data, which we mention briefly above. Among many other things, this information can be used to provide enhanced navigation options, called ‘breadcrumbs’, which is the subject of this article. First, I’ll first explain what breadcrumbs are, and then I’ll examine in detail the ways in which breadcrumbs can be used, giving you plenty of usable code along the way. A note about the coding style used in my examples In all of my examples involving sessions, I will use the $_SESSION[] array, rather than the associated globals. Aside from being what I consider to be a cleaner approach, this will reduce the chance for the introduction of errors by, for example, missing a session_register() call. I also do not assume in these examples that you will have register_globals enabled in your php.ini file. This will ensure maximum compatibility with different systems and PHP implementations. Of Bread and Crumbs ‘...and Hansel comforted his little sister and said: “Just wait, Gretel, until the moon rises, and then we shall see the crumbs of bread which I have strewn about, they will show us our way home again.’ - Hansel and Gretel - Brothers Grimm, et al Earning their name from the above-quoted famous tale, breadcrumbs are a navigation tool that helps the user find their context, and quickly navigate back to anywhere in that context. Breadcrumbs can be used to expose the structure of the site to the user, allowing them to learn the location of items. Usually found near the top of a web page under the title banner, breadcrumbs can be created using any server-side or client-side language. An example breadcrumb is shown in Figure A, and a nice, live example of breadcrumbs can be found at Google’s web directory at http://directory.google.com/ February 2003 · PHP Architect · www.phparch.com
Time Travel - Breadcrumbs and History with PHP
Figure A Home > Life > Plants > Flowers > Daisy
Usability expert Keith Instone tells us that there are generally two different types of breadcrumbs: location and path. Location breadcrumbs are useful on hierarchical sites, and represent a straight line from home (or the root) to your current location. This is similar to a file path in the Windows Explorer location bar, or the output of the pwd command in Unix, and is what we saw in Figure A. Path breadcrumbs are much less structured, and show the actual path you took to your current location, as in Figure B. Hence, if you jump in from a search engine, you’ll see a breadcrumb with only one crumb. Clicking a link on that page will take you to another page on that site, where you will then see two crumbs, and so forth. This can be used on non-hierarchical sites to supplement with a visual what’s under the browser’s back button. Figure B Store > Tables > Wines > Tables > Liquers > Edit
Is that clear? Location breadcrumbs are like a ‘You are here’ marker on a map, and path breadcrumbs are like a highlighted route on a map. Location breadcrumbs are more focused on where you are in the scheme of things, whereas a path breadcrumb is more concerned about the way you came. As an aside, it may be that if you used a search engine and looked for “daisy”, you might come across the page in Figure A directly. Strangely, you may still see the full location breadcrumb, even though you didn’t navigate through those points. This is because pages on sites that implement location breadcrumbs typically know their way home. This can work in a couple of different ways, which I’ll discuss a little later. Because they are the most common form, this article will focus on the location breadcrumb concept, although I will relate back to path breadcrumbs when appropriate. In fact, the second example that we will cover will actually be a hybrid that looks like a location breadcrumb, but the processing underneath is closely related to that of the path breadcrumb method. From this point on, unless otherwise noted, when I talk about breadcrumbs I’ll be referring to location breadcrumbs. More About Breadcrumbs Breadcrumbs usually will appear like a directory struc-
11
FEATURES
Time Travel - Breadcrumbs and History with PHP
ture, which is appropriate since they work best when you have a highly hierarchical site layout. A user can quickly navigate to any previous level by clicking one of the links in the breadcrumb. For instance, if a user clicked on the Plants link in Figure A, she would likely see a page listing the different types of plants, such as flowers, trees, and grasses. The breadcrumb found on that page might look like Figure C. Figure C Home > Life > Plants
Breadcrumb separators can be anything, including images, but are commonly one of a number of semiintuitive characters, as shown in Figure D. Figure D Home Home Home Home
> | : /
Movies > Animated Plays | Comedies Cooking : Fish Monsters / Sully
The links, such as Home and Monsters above, are links that contain just enough informtion to get you back to that place in your session history. Depending on your intention, and the layout of the site, this information can be very different, ranging from a plain script name through to a full blown URL. (or even a Javascript function call). HarveysHTMLHaven.com Let’s set the stage. Harvey is an aspiring business leader, and his final project while studying for his MBA required a business idea that related to IT. Harvey thought about it (no pun intended), and decided that as a part of his business he could resell web code and graphics. All he had to do was browse the web for an hour or two, and he’d have enough code and images to start up his business, HarveysHTMLHaven.com. Harvey ended up using a highly hierarchical physical structure for his site. Directories named images and code resided in his site’s home directory. Inside of the images directory were four more directories named buttons, clipart, icons and photos. Inside each of these were, potentially, gif, jpg and png. The code directory had a similarly nested structure. For brevity, we’ll focus only on the images tree. A user could follow links down any of these paths, and Harvey was concerned that the user would lose their bearings and become frustrated. He wanted to maintain some sort of map back to home at all times. Breadcrumbs to the rescue! This is the easiest form of breadcrumb to implement, because it is a more or less static structure. Sure, Harvey may add images to his February 2003 · PHP Architect · www.phparch.com
individual library pages, but the category layout is mostly static. Let’s write some code. In Figure F, you’ll see a simple class that I created to show how easy it is to deploy this form of breadcrumb. Let’s examine this code. I needn’t have used a class here, but it provides nice encapsulation. The class’s job is to build breadcrumbs from the script’s URL, and return the finished product. There are two member variables: $_separator, which contains the character used to delimit the links in the breadcrumb, and $_label_map, which is a list of labels to use for directory names. This code works off of the directories in the script’s URL, and the label map allows us to give these directory levels descriptive labels, rather than output the raw directory names. The class also contains a basic constructor, as well as a couple of gratuitous setter functions. The main functionality, though, comes from get_output(), which actually produces the breadcrumb. Let’s look at how. It first gets the script’s path from the $PHP_SELF server variable. If this class was instantiated from the script running at http://www.example.com/foo/bar/index.php an example of what we’d get from this variable is /foo/bar/index.php. The method then gets the highest directory in the path, also the current directory, which in this case would be bar. Then it explodes the path and puts all of the pieces into the $dirs variable. It now loops over all of the entries in $dirs. $base keeps a running concatenation of the directories already processed and is the basis for all links in the breadcrumb. As each directory is processed, the loop searches for an entry in the $label_map and, if one is found, outputs a breadcrumb using the current $base value, and the label from the map. Once all of the directories are processed, the $crumbs are joined together using $separator, and Figure G ’Images’, ‘buttons’ => ’Buttons’, ‘clipart’ => ’Clip Art’, ‘icons’ => ’Icons’, ‘photos’ => ’Photographs’, ‘jpg’ => ’JPEG’, ‘gif’ => ’GIF’, ‘png’ => ’PNG’, ‘code’ => ’Code’, // etc... ); $sbc = &new SimpleBreadcrumb($crumb_label_map); print $sbc->get_output(); ?>
12
FEATURES
Time Travel - Breadcrumbs and History with PHP
returned to the caller. Figure G contains the code to use this class for Harvey’s site. Harvey simply includes this file at the point in his page where he wants the breadcrumb to appear, and he’s in business. The code is very simple. A label map is defined, and the SimpleBreadcrumb class is instantiated using it.
Harvey is happy with the default separator, so he doesn’t pass any in. Finally, he gets the output, and prints it. This is a pretty simple class, but it provides very powerful functionality for a simple directory-oriented site such as Harvey’s. Although we know that this is a very simple example,
Figure F 1 ‘; 8 var $_label_map = array(); 9 10 function SimpleBreadcrumb($label_map=null, $separator=null) 11 { 12 if (isset($separator)) 13 { 14 $this->set_separator($separator); 15 } 16 if (isset($label_map)) 17 { 18 $this->set_label_map($label_map); 19 } 20 } 21 22 function set_separator($separator) 23 { 24 $this->_separator = $separator; 25 } 26 27 function set_label_map($label_map) 28 { 29 $this->_label_map = $label_map; 30 } 31 32 function get_output() 33 { 34 // get script path, strip any trailing slashes, and 35 // split into constituents 36 // ex. /images/icons/gif 37 $dir = $_SERVER[‘PHP_SELF’]; 38 $current = basename(dirname($dir)); 39 $dirs = split(‘/’, $dir); 40 41 // base is built as we go through the dirs 42 $base = ‘/’; 43 foreach($dirs as $dir) 44 { 45 $base .= “{$dir}/”; 46 if (isset($this->_crumb_label[$dir])) 47 { 48 if ($dir == $current) 49 { 50 $crumbs[] = $this->_crumb_label[$dir]; 51 } 52 else 53 { 54 $crumbs[] = “{$this->_crumb_label[$dir]}”; 55 } 56 } 57 } 58 59 return join($this->_separator, $crumbs); 60 } 61 } 62 63 ?>
February 2003 · PHP Architect · www.phparch.com
13
FEATURES let’s mention a couple of its limitations. First, it is dependent on a directory structure, which makes it very fragile. This class simply doesn’t work if your site is not laid out by directory, since that’s where the breadcrumbs come from. Second, it doesn’t actually maintain any history. Although the URL in a site structured by directory can generally be interpreted as the history (if your site is navigated linearly) the breadcrumbs are still built directly from it without any regard for querystring variables, or the path actually taken. This severely limits how flexible this class is. Let’s leave Harvey to his questionable business ideas, and move on to a more complex situation involving (almost) everyone’s favorite relative.
Time Travel - Breadcrumbs and History with PHP
State and History
Granny Smith had a dream of being an online kingpin. After some thought, she decided that the best way to begin her dynasty was to open an e-store, and wait for the riches to flood in. She even started buying pants with extra-big pockets, so she’d have room for all the money she was going to make. Her store, GrannysGiftGaggle.com is an emporium of goodies, from health food to hinges. If you can buy it, you can buy it at Granny’s. She has hundreds of categories and thousands of sub-categories. Let’s examine the structure a little further. All activity on GrannysGiftGaggle.com comes through specialized points of access, or controller scripts, and as I mentioned earlier, there is no really structured physical underlay for Granny’s site. She mostly relies on the data passed in on the querystring to get the current state, which determines what to show. Since the querystring will determine the current page, it might be educational for us to follow a simple set of transactions on Granny’s site, and see how people navigate around in Figure H.
State can be defined as a “unique snapshot that is quantified by values for a set of variables, characterizing the web site for a period of time, and is different from other states.” This is rather like your checkbook. Right now, if it is balanced, you have a certain set of possessions, and a certain amount of money left. Your checkbook, and life, is in a particular state. Writing a check will transition you to another state (hopefully not a broke one). In order to get the information we need for the state, we must first ask the question: What identifies it? The answer to that question depends on the way you’ve put your site together, and may end up being a very complicated question to answer. Harvey used his directory names to identify state, which was very simple. Granny doesn’t really have that luxury, so she will need to perform a little more magic. While Granny doesn’t have a physically hierarchical system, we can see from the above transaction that she does have a home directory, which appears to contain a products directory and an estore directory. This allows us to use Harvey’s system for part of the breadcrumb generation. The rest of the breadcrumb will need to be created from the incoming querystrings. Granny’s breadcrumbs will be a little different, not only in method, but also in construction. Granny’s breadcrumbs will aggregate themselves based on the user’s history, rather than building fresh on each page, like Harvey’s. The reason for this is that Harvey had all of the states built into each request (because of the URL), whereas Granny doesn’t. The subcategory page request contains no direct information about what page it came from. The user’s history is just a stored collection of past states. As an example, the user’s session history, after selecting the football category on Granny’s site, would contain the following states.
Disclaimer: This is obviously not intended to be a tutorial on how to write a shopping cart application, and is only a very rough example, for demonstration purposes, of how an application like this might work.
1. Home – no variables, no directories 2. Product Categories – no variables, products directory 3. Football Sub-Categories – category=football variable , products directory
Now that we’ve got the view from 50,000 feet on how Granny’s site works, we can move on to what we need to make breadcrumbs work. You may have seen some information in the links above that might be useful for generating breadcrumbs. This information will provide what breadcrumbs require: state.
This is starting to look suspiciously like a breadcrumb! Let’s see the code to “make it so, Number One”. It’s listed in Figure I. It’s ironic that this is the ComplexBreadcrumb class, when the code is almost simpler than the SimpleBreadcrumb class that we discussed earlier. Regardless, I’ll walk through this code. Again, I really didn’t need to use a class here, but there it is. The purpose of the class is to store state history in the
GrannysGiftGaggle.com
February 2003 · PHP Architect · www.phparch.com
14
FEATURES
Time Travel - Breadcrumbs and History with PHP
user’s session and allow retrieval of state in breadcrumb form. It has two member variables, $_separator, and $_current_state. $_separator again contains the character used to delimit the breadcrumb’s links, while $_current_state contains the label of the current state. This label is used as a crude lookup later. The class also contains a very basic constructor, which can optionally initialize the breadcrumb separator at instantiation. A setter function is also provided for this purpose. The meat of the class is contained in the get_output() method. This method first checks to see if a state has been registered for the current page. If not, it returns an empty string. If get_output() did detect a current state, it makes a non-linked breadcrumb entry for it, and then proceeds to process the parents of that state. For each parent, the state variables are URL-encoded and links are produced.
Once all of the parents have been processed, the array of $crumbs is reversed because we built it rightto-left, but want to display it left-to-right. Finally, the completed breadcrumb string is returned. Figure J shows a usage example of the ComplexBreadcrumb class. This file could be either included or embedded in the code that handles the case where category is set, as in http://grannysgiftgaggle.com/products/?category=football. This usage code instantiates our new class, and just to be different, changes the output separator to a forward slash. Since this is specific to the category state, it gets the category from the $_GET array, and sets the state accordingly. Finally, it gets the output and displays it. I have no doubt that using a little ingenuity specific to the situation, it would be possible to generalize this usage code and use it as a common include file, similar to Harvey’s.
Figure H 1
http://grannysgiftgaggle.com/
First the user navigates to the main page, where they are presented with, among other things, a link to the products section.
2
http://grannysgiftgaggle.com/products/
When the user navigates to the products section, they are presented with a number of product categories
Once a product is chosen, the user may browse through any amount of relevant information about it, including the description, testimonials, specifications and supporting material.
6
https://grannysgiftgaggle.com/estor Once the user is happy with the product, they may e/?cart=add&prod_id=A1234 choose to add it to the cart.
7
https://grannysgiftgaggle.com/estor If no other products are wanted, the next logical e/?cart=checkout step is to check out.
8
https://grannysgiftgaggle.com/estor The checkout is multi-stage. First they must enter e/?cart=checkout&subpage=shipping their shipping address, and pick the delivery method.
Time Travel - Breadcrumbs and History with PHP Figure I
1 '; 6 var $_current_state; 7 8 // constructor 9 function ComplexBreadcrumb($separator=null) 10 { 11 if (isset($separator)) 12 $this->set_separator($separator); 13 } 14 15 function set_separator($separator) 16 { 17 $this->separator = $separator; 18 } 19 20 // set a state/crumb 21 // - 'parent' is the parent label (allows chaining) 22 // - 'label' is the state/crumb label you are adding 23 // - 'vars' is an array of name-value pairs (usually from $_GET) that 24 // identify the state/crumb 25 function set_state($label, $parent='home', $vars=array()) 26 { 27 $_SESSION['_crumbs'][$label] = array( 'label' => $label, 28 'path' => $_SERVER['PHP_SELF'], 29 'vars' => $vars, 30 'parent' => $parent, 31 ); 32 $this->current_state = $label; 33 } 34 35 function get_output() 36 { 37 // no current crumb means no crumb display on the page 38 if (! isset($this->current_state)) 39 return ''; 40 41 $crumbs = array(); 42 43 // don't make a link from the current state 44 $crumbs[] = $this->current_state; 45 46 // get the current state's parent and set the new current 47 $parent = $_SESSION['_crumbs'][$this->current_state]['parent']; 48 $current = $_SESSION['_crumbs'][$parent]; 49 50 // do all parents 51 do 52 { 53 foreach ($current['vars'] as $name=>$value) 54 $values[] = urlencode($name) . '=' . urlencode($value); 55 56 $crumbs[] = "" 57 . "{$current['label']}"; 58 // get the current state's parent and set the new current 59 $parent = $_SESSION['_crumbs'][$this->current_state]['parent']; 60 $current = $_SESSION['_crumbs'][$parent]; 61 } 62 while ($current['label'] != 'home')) 63 64 // reverse the array (we built it in reverse) 65 krsort($crumbs); 66 67 return 'back to ' . join(" {$this->separator} ", $crumbs); 68 } 69 } 70 71 ?>
February 2003 · PHP Architect · www.phparch.com
16
FEATURES
Time Travel - Breadcrumbs and History with PHP
You might notice that, with some effort, this more complex version of breadcrumbing would work for Harvey as well. For his situation, the first method is more than adequate, and will keep things simple, but this would certainly do the trick, too. Limitations Although these examples will cover help with the majority of applications, they still don’t address a few things: Using images as separators is not supported. This would be a very simple extension, and would really only involve the addition of a parameter to the contructor, an extra setter method, and the extra logic needed to add an tag, rather than a symbol. Saving $_POST variable state is also not supported. This is a very simple modification, requiring only that you change $_GET to $_REQUEST. Note that storing and later resubmitting $_POST values is generally considered a bad idea, though, since they usually come from submitted forms, and can have nasty side-effects (like attempting to re-insert entries to your database tables) if you aren’t careful. If you are browsing Granny’s site, and you navigate to two different products in two different categories, you may find problems with old history. If you use your browser’s back button to back out of the second product all of the way back to the first product and refresh, the categories link in the breadcrumb may be incorrect. This is because each state overwrites any state with the same name. This could be handled by further identifying state using sequences or other methods, but you will need to determine whether this is really an important feature, or just fluff. In the complex example, jumping in from a search engine, or otherwise browsing in a non-linear fashion is not supported. Because the breadcrumbs are built incrementally, you would have some difficulty building a dynamic system like this that responded well to that situation. Two solutions come to mind: 1. default crumbs 2. redirection ‘Default crumbs’ means that if history cannot be found, default crumbs will be inserted in their place. ‘Redirection’ means that if a page is navigated to that shouldn’t be, the user would be redirected back into the linear path. Both of these are reasonable solutions in some situations. What you do in your situation is up to you. Applications that use very complex messaging and event systems may have difficulty fitting into the mold that breadcrumbs often require. Remember, if you face this task, the fundamental question is ‘what defines your state at any given moment?’. If you can define February 2003 · PHP Architect · www.phparch.com
your state minimally, then you are a big step closer to having the information you need. We introduced the concept of state history here, which has a number of other uses as well. Some of these uses include preventing form resubmission, path breadcrumbs (remember those?), and in-stream authentication. In-stream authentication refers to a situation in which you click a link, are asked to authenticate, and then proceed to that link’s location directly, which is a very nice usability feature. Wrapping Up Boy, we’ve come a long way! I hope these examples have helped to clear the mud. As with just about anything in this world, there are a million ways to implement breadcrumbs. I can’t possibly attempt to cover every base, but I hope that I’ve been able to provide some insight into this topic, and provide some useful code to work with.
References “Those who cannot remember the past are condemned to repeat it.” Life of Reason, Reason in Common Sense, Scribner’s, 1905, page 284 Definition of state http://gd.tuwien.ac.at/systeng/bah ill/Definitions.html Breadcrumb concepts http://keith.instone.org/breadcrumbs/
php|a Peter James is a developer and team lead working in Edmonton, Alberta, Canada. In his spare time he tries to magically juggle his family, freelance work, tool development and learning. You can reach Peter at [email protected].
17
FEATURES
FEATURES
The Object Oriented Evolution of PHP By Zeev Suraski One of the key ingredients in the upcoming version 5 of PHP will be the Zend Engine 2.0, with support for a brand new object-oriented programming model. This article describes the evolution of the object-oriented programming support in PHP, covering the new features and changes that are scheduled for PHP 5.
Where did it all start? Few people know this, but when PHP as we know it today was being molded, back in the summer of 1997, there were no plans for it to have any object-oriented capabilities. Andi Gutmans and I were working to create a powerful, robust and efficient Web language loosely based on the PHP/FI 2.0 and C syntax. As a matter of fact, we got pretty far without having any notion of classes or objects – it was to be a purely structured language. One of these summer nights however, on August 27th that year, this changed. At the time classes were introduced to the code base of what was to become PHP 3.0, they were added as syntactic sugar for accessing collections. PHP already had the notion of associative arrays collections, and the new critters were nothing but a neat new way of accessing them. However, as time has proven, this new syntax proved to have a much more far-reaching effect on PHP than originally intended. Another thing that most people don’t know is that by the time PHP 3.0 came out officially in mid-1998 and was gaining momentum at a staggering rate, Andi Gutmans and I were already determined to rewrite the language implementation. Users may have liked PHP
February 2003 · PHP Architect · www.phparch.com
as it existed at the time - in fact we know they liked it. But as the authors of the engine we knew what was going on under the hood and we couldn’t live peacefully with that. The rewrite, which was later dubbed the ‘Zend Engine’ (Zend being a combination of Zeev and Andi), initiated and became one of the core components of the 2nd revolution that PHP experienced in just over a year. This revolution, however, left PHP’s object model mostly unchanged from version 3 – it was still very simple. Objects were still very much syntactic sugar for associative arrays, and didn’t offer users too many additional features. Objects in the old days So, what could one do with objects back in the days of PHP 3.0 or even with the current version of PHP 4.0? Not that much, really. Objects were essentially containers of properties, like associative arrays. The biggest difference was that objects had to belong to a class. Classes, as in other languages, contained a collection of properties and methods (functions), and objects could be instantiated from them using the new operator. Single inheritance was supported, allowing users to
18
FEATURES extend (or specialize) the scope of an existing class without having to write it from scratch or copy it. Finally, PHP 4.0 also added the ability to call methods of a specific class, both from within and outside object contexts. One of the biggest twists in PHP’s history was the fact that despite the very limited functionality, and despite a host of problems and limitations, object oriented programming in PHP thrived and became the most popular paradigm for the growing numbers of off-the-shelf PHP applications. This trend, which was mostly unexpected, caught PHP in a sub-optimal situation. The fact that objects were not behaving like objects in other OO languages, and were instead behaving like associating arrays was beginning to show. The limitations of the old Object Model The most problematic aspects of the PHP 3 / PHP 4 object model was the fact that objects were passed around by value, and not by reference. What does that mean? Let’s say you have a simple, somewhat useless function, called myFunction():
function myFunction($arg) { $arg = 5; } And you call this function: $myArgument = 7; myFunction($myArgument); print $myArgument;
As you probably know, the call to myFunction() will leave $myArgument unchanged; Sent to myFunction() is a copy of $myargument’s value, and not $myargument itself. This type of argument passing is called passing arguments by value. Passing arguments by reference is done by most structured languages and is extremely useful, as it allows you to write your functions or call other people’s functions without worrying about side effects they may have on variables outside their scope. However, consider the following example: function wed($bride, $groom) { if ($bride->setHusband($groom) && $groom->setWife($bride)) { return true; } else { return false; } } wed($joanne, $joe); print areMarried($joanne, $joe);
February 2003 · PHP Architect · www.phparch.com
The Object Oriented Evolution of PHP (The implementation of Woman::setHusband(), Man::setWife() and areMarried() is left as an exercise for the reader). What will areMarried() return? We would hope that the two newlyweds would manage to stay married at least until the following line of code, but as you may have guessed – they wouldn’t. areMarried() will confirm that they got divorced just as soon as they got married. Why? The reason is simple. Because objects in PHP 3.0 and 4.0 are not ‘special’, and behave like any other kind of variable, when you pass $joanne and $joe to wed(), you don’t really pass them. Instead, you pass clones or replicas of them. So, while their clones end up being married inside wed(), the real $joe and $joanne remain within a safe distance from the sacrament of holy matrimony, in their protected outer-scope. Of course, PHP 3 and 4 did give you an option to force your variables to be passed by reference, consequently allowing functions to change the arguments that were passed to them in the outer scope. If we defined wed()’s prototype like this: function wed(&$bride, &$groom)
then Joanne and Joe would have had better luck (or not, depending on your point of view). However, it gets more complicated than that. For instance, what if you want to return an object from a function, by reference? What if you want to make modifications to $this inside the constructor, without worrying about what may happen when it gets copied back from new’s result into the container variable? Don't know what I'm talking about? Say hallelujah. While PHP 3 and 4 did address these problems to a certain extent by providing syntactic hacks to pass around objects by reference, they never addressed the core of the problem:
Objects and other types of values are not created equal, therefore, Objects should be passed around by reference unless stated otherwise. The Answer – Zend Engine 2 When we were finally convinced that objects are indeed special creatures and deserve their own distinct behavior, it was only the first step. We had to come up with a way of doing this without interfering with the rest of the semantics of PHP, and preferably, without having to rewrite the whole of PHP itself. Luckily, the solution came in the form of a big light bulb that emerged above Andi Gutmans’ head just over a year
19
FEATURES ago. His idea was to replace objects with object handles. The object handles would essentially be numbers, indices in a global object table. Much like any other kind of variables, they will be passed and returned by value. Thanks to this new level of indirection we will now be moving around handles to the objects and not the objects themselves. In effect, this feature means that PHP will behave as if the objects themselves are passed by reference. Let’s go back to Joe and Joanne. How would wed() behave differently now? First, $joanne and $joe will no longer be objects, but rather, object handles, let’s say 4 and 7 respectively. These integer handles point to slots in some global objects table where the actual objects sit. When we send them to wed(), the local variables $bride and $groom will receive the values 4 and 7; setHusband() will change the object referenced by 4; setWife() will change the object referenced by 7; and when wed() returns, $joanne and $joe will already be living the first day of the rest of their lives together. What does that mean to end-users? Alright, so the ending to the story is now more idyllic, but what does it mean to PHP developers? It means quite a number of things. First, it means that your applications will run faster, as there will be much less data-copying going around. For instance, when you send $joe to a function, instead of having to create a replica, and copy over his name, birth date, parents’ name, list of former addresses, social security number and whatnot – PHP will only have to pass on one object handle, one integer. Of course, a direct result of this is also a significant amount of memory savings – storing an integer requires much less space than storing a fullfledged replica of the object. But perhaps more important, the new object model makes object oriented programming in PHP much more powerful and intuitive. No longer will you have to mess up with cryptic & signs in order to get the job done. No longer will you have to worry about whether changes you make to the object inside the constructor will survive the dreaded new-operator behavior. No longer will you ever have to stay up until 2:00AM tracking elusive bugs! Ok, maybe I’m lying with that last one, but seriously, the new object model reduces the object-related stay-up-until-2:00AM type of bugs very significantly. In turn, it means that the feasibility of using PHP for large-scale projects becomes much easier to explain. What else is new? As one could expect, the Zend Engine 2 packs quite a few other features to go along with its brand new February 2003 · PHP Architect · www.phparch.com
The Object Oriented Evolution of PHP object model. Some of the features further enhance object-oriented capabilities, such as private member variables and methods, static variables and languagelevel aggregation. Most notable is the revolutionized interaction with external component models, such as Java, COM/DCOM and .NET through overloading. In comparison to the Zend Engine 1 in PHP 4.0, which first introduced this sort of integration, the new implementation is much quicker, more complete, more reliable and even easier to maintain and extend. This means that PHP 5.0 will play very nicely in your existing Java or .NET based setup, as you will be able to use your existing components inside PHP transparently, as if they were regular PHP objects. Unlike PHP 4.0, that had a special implementation for such overloaded objects, PHP 5.0 uses the same interface for all objects, including native PHP objects. This feature ensures that PHP objects and overloaded objects behave in exactly the same way. Finally, the Zend Engine 2 also brings exception handling to PHP. To date, the sad reality is that most developers write code that does not handle error situations gracefully. It’s not uncommon to see sites that spit out cryptic database errors to your browser, instead of displaying a well-phrased ‘An error has occurred’ kind of message. With PHP, the key reason for this is that handling error situations is a daunting task – you actually have to check for the return value of each and every function. Since set_error_handler() was added, this issue became slightly easier to manage, as it was possible to centralize error handling – but it still left a lot to be desired. Adding exception handling to PHP will allow developers both fine-grained error recovery, but more important it will facilitate graceful application-wide error recovery. Conclusion The release of PHP 5.0, powered by the Zend Engine 2.0, will mark a significant step forward in PHP’s evolution as one of the key Web platforms in the world today. While keeping its firm commitment to users who prefer using the functional structured syntax of PHP, the new version will provide a giant leap ahead for those who are interested in its object oriented capabilities – especially for companies developing large scale applications.
php|a Zeev has been working for over five years on the PHP project. Along with Andi Gutmans, he started the PHP 3 and 4 projects and wrote most of their infrastructure and core components, thereby helping to forge PHP as we know it today and attracting many more developers to join the movement. Zeev is a co-founder and CTO of Zend Technologies Ltd, the leading provider of development and performance management tools for PHP-enabled enterprises. Zend’s website is www.zend.com.
20
REVIEWS
LogiCreate
REVIEWS
Tap Internet
LogiCreate is a web application framework, providing a common development base for web systems. By standardizing development practices, developers can quickly create new web-based systems such as intranets, extranets, and other database-driven applications.
A
s a developer, I have always been used to building most of my own websites from scratch, rather than using something that other people have developed. When we had to build the php|a site, we didn’t even look at any other options—we simply designed the website the way we wanted it and put it together. The reason is simple: after so many years of being in this business, I have a pretty good understanding of how things work and can take care of most things more quickly than I can learn how to use a new product that would do the same in my place (plus, let’s face it, one always likes to think that his own stuff is better than something an automated script can generate). Unfortunately, this approach does not always work. Building things from scratch—even when it comes to reusing some of your own code from previous projects—takes time, and time is money. When budgetary considerations must take precedence over personal pride and the general laziness that all programmers sport, it’s necessary to look beyond one’s personal war chest and start from solid building blocks that take care of most basic (and some advanced) aspects of a good website. It’s therefore with quite a bit of interest that I look at the LogiCreate product that we received from Tap
February 2003 · PHP Architect · www.phparch.com
The Cost: $1,495.00 - $4,950.00 (US) Requirements: Any L.A.M.P. based architecture Download Page: LogiCreate LogiCreate Home Page: http://www.logicreate.com
Internet for review this month. Now, it’s important to understand that the version Tap sent us is a development snapshot; as such, some of the kinks still need to be worked out, but this really seems to affect only the installation part of the application, and for the rest I can’t really say that the product exhibits any major flaws. I wish our “development snapshots” were quite as good as this... What Is LogiCreate? At the most basic level, LogiCreate is a web applica-
22
REVIEWS tion framework. It provides a set of modules that can be integrated into a single website, providing functionality like content management, an ad placement system, an FAQ, a search function, and more. If this sounds like something that you’ve already heard, I urge you to read on—I had exactly the same impression at the beginning, but changed my mind as I explored things in a bit more detail. As with other similar systems, an entire LogiCreate application can be managed through an easy-to-use web-based interface. Unlike other similar systems, LogiCreate takes a security-centric approach to the entire application. Most functions can only be managed by users who have the proper permissions, and it’s possible to push almost all data that is published through the website through an approval process to provide complete control over it. What’s more, unlike most content management systems and web application frameworks, LogiCreate seems to have been built with the understanding that a developer might want to work with something more than the templates offered by the framework. As such, the creation of custom templates and custom scripts is not only supported, but also encouraged as a way to provide a more flexible environment for the programmer. The Installation Process As I mentioned above, this is the only area where the version of LogiCreate that we received still needs a significant amount of work. The creation of a new LogiCreate website takes place through a simple twostep process in which the user is asked to enter only a few parameters, like the site’s name and database information (Figure 1). Here, my use of “Marco’s Site” as the name of the application caused a bit of a hiccup—the single quote was not escaped and ended up in a constant definition, thus confusing the PHP interpreter, which threw an error when I subsequently tried to open my website. Note that the installation script also asks for the database driver that should be used to
February 2003 · PHP Architect · www.phparch.com
Figure 1
access the database. Although our version only came with a driver for MySQL, the entire data access layer is abstracted, and it’s actually possible to extend it to support other database systems. The installation process also includes a post-setup phase in which the user is required to adjust the permissions of the files that have been created. Although this requires only four simple shell commands, it could certainly have been automated. The simplicity of the installation process, even with its manual portion, is somewhat staggering. As you can see from Figure 2, LogiCreate is a complex piece of software that includes a large number of modules. For anyone who has ever tried to create a new online store using Microsoft Commerce Server, the two-step approach adopted by this application framework is just plain great. LogiCreate Modules A total of twelve different modules were packaged with the version that we received, with functionality ranging from a “welcome page” to a complete con-
23
REVIEWS tent management system. The modules can be administered through the Hercules Control Center (affectionately referred to as “Herc”), a backend web-based application that allows you to control the individual settings of each aspect of your website. As I mentioned earlier, Herc provides a very wellthought-out user management system, complete with grouping and permissions at both the user and group level. Thus, for example, if I want to publish a new news item and type it into the system (Figure 3), it will be “parked” into a special repository until it can be verified and approved by someone who has the correct permissions to do so. Herc also provides a complete statistical system that can be used to create reports on the kind of traffic that your site receives (Figure 4). Although this tool does not provide the level of sophistication that a more specialized web analysis package offers, it’s still very valuable, because it produces reports in realtime, rather than on a specific schedule. Expandability How good is an application framework if it can’t be expanded to meet your actual requirements? Naturally, it depends: a non-technical person who is just looking for a quick solution to his problems probably won’t care that he can’t mess around with the source code. On the other hand, a developer who is looking for a framework on which to build his website will. LogiCreate is clearly slanted towards the latter approach. The system’s templates are really just PHP scripts that make use of the various classes and methods made available by each of the application’s modules. This, in my opinion, is the way it should be— after all, PHP provides an excellent development environment, and there simply is no way that any other artificial “templating system” can match its functionality. The core of LogiCreate, therefore, relies heavily on code reusability and, as such, it is primarily based on OOP.
February 2003 · PHP Architect · www.phparch.com
Figure 2
A non-technical person who is just looking for a quick solution to his problems probably won’t care that he can't mess around with the source code. This, however, does not prevent you from implementing your own PHP functionality inside a particular template. In fact, that’s the whole point of LogiCreate’s philosophy: the modules, classes and procedures that it provides are just there to help you by providing a simple framework on which you can build your own specialized functionality. Documentation This is probably another area in which LogiCreate needs a bit of help. The user documentation, com-
24
REVIEWS posed primarily of Herc’s user manual, is of excellent quality and very clear. For some reason, however, it is distributed separately from the Herc interface itself. This makes it difficult for a user to get help from the system, and risks negating the advantage of having an interface like Herc in the first place. If I were to develop a production system based on LogiCreate, integrating the help in the Herc web pages would certainly be on my to-do list. The developer’s manual is a bit sparse, but otherwise functional enough to be quite complete. It includes information on how to create new templates and how to modify (or integrate with) the application framework itself. Mercifully, this is not a difficult task to accomplish by an intermediate-level developer. My only real gripe about the docs is that they contain very few examples; my experience is that two lines of code can easily explain what ten lines of text can’t, so I really hope that the Tap team will add more examples and sample code to their documentation.
The Big Picture From a purely technical perspective, I am pleasantly surprised by how much LogiCreate manages to accomplish without being over-complicated or requiring a week of reading in order to get the proverbial “Hello, World!” message to appear on the screen. From a business point of view, LogiCreate is not cheap, but given its characteristics can provide tremendous value if you consider how long developing the functionality that it provides would take. The product is priced at a base of $1,495.00 (US) per server, plus $500.00 (US) for each additional module that you decide to use. Surely, other inexpensive systems provide similar frameworks, but it’s really difficult to find something that is quite as simple to understand and master as LogiCreate. Overall, if you’re building a new application and are looking for a framework to take care of the gritty work for you, I’d recommend you take a look at this system. php|a
Figure 3
February 2003 · PHP Architect · www.phparch.com
25
REVIEWS Figure 4
February 2003 · PHP Architect · www.phparch.com
26
FEATURES
FEATURES
PHP in an N-T Tiered World By Dave Palmer
In today's enterprise, many applications are coded by roomfuls of Java programmers, and deployed to a middle tier Java container (read: application server). This article introduces you to some advanced techniques in allowing PHP to interact with EJBs and other layers of your n-tiered model, in order to provide a front end interface to the application.
I
f I had to take a guess, I would say someone on our development team asks "so Dave, why can't our [application server that rhymes with "confusion"] templates just directly query the database?" at least 5 or 10 times a day (maybe that's just a slight exaggeration). I then feel compelled to explain the concept once again, spouting trade-rag-babble about how great n-tiered architectures are, and the benefits in doing things the n-tiered way. The developer usually just nods his head humoring me, then walks away reminiscing of the good old days when they could just throw everything (including the kitchen sink) into their code and be done with it. But alas, the logical conclusion is reached all the same that n-tiered architectures lend themselves perfectly to applications that must scale and be flexible. If you look at your PHP applications you'll notice a common pattern. This pattern includes things like connecting to and querying a database, performing logic on data, and presenting that data back to the user so that the user can act (or not act) on what was presented. For those of you who have built complex PHP applications I'm sure that you've noticed that your code becomes, well... complex. Maintaining it becomes rather difficult, if not impossible, especially after taking a break from it for a few days or even hours. You'll also notice that performance starts to degrade and ideals
February 2003 · PHP Architect · www.phparch.com
like scalability and flexibility start to take a back seat to the infectious mantra of "just get it working", which invades many a development project. If the above scenario strikes a little too close to home for you, have no fear! The good news is that because PHP is so accommodating in its openness and its ability to work happily with other technologies, PHP is quite comfortable fitting in with an n-tiered architecture. I hope this article will equip you with at least enough background knowledge to get you started with this invaluable development/design technique. Before I go on any further, perhaps some explanation of what I mean by 'n-tiered' would be helpful. "Tiers" are like layers. Pardon the mixture of metaphors, and I know just how tired metaphors are when trying to describe anything in the application development world, but to think of a program as being comprised of "layers" is a useful device in clarifying what I'm talking about. Each "layer" or tier represents a common set of functionality. These layers/tiers in your typical PHP application are "coupled", meaning that your presentaREQUIREMENTS PHP Version: 4.1.0 or Above O/S: Any Additional Software: Java SDK 1.4.x AND Java J2EE 1.4.x, JBoss 3.0.4, MySQL
27
FEATURES tion layer is tied to your business logic layer which is in turn tied to your data layer. It's not unlike the days when the client/server model was king. Your client is a "fat client", containing all of the code needed to not only display a nice UI, but also to access and manipulate the data! Everything (including that kitchen sink) is stuffed into the client, and the server is nothing more than a data store. This client/server model is still alive and well today in many Web applications. Even though the web clients are a bit thinner (because the browser takes care of some UI details), you still notice that everything is still crammed into that client tier. Now, along comes the n-tiered architecture, which spreads things out and creates more room for everything by encapsulating these tiers into separate and identifiable entities. The "n" in n-tiered is simply the mathematical notation for "unknown", meaning that using this type of architecture assumes nothing about what is required in order to implement the application you must develop. Essentially, you are no longer confined to a stringent implementation model and are free to scale and grow as the application and requirements demand. With the n-tiered framework, the application can scale without the pain of having to re-engineer everything each time growth is necessary. N-tiered applications are typically broken up into distinct units of work (tiers) and generally fall into these categories: Presentation Business Logic Data access Database The Presentation Layer is often referred to as the "web tier." This tier contains all of the code to display the user interface. In our example, PHP running on Apache represents our presentation or web tier. The Business Logic Layer is often thought of as the "middle tier". This tier is where the vast majority of business logic is implemented. I often think of this tier as being pretty fuzzy in that it may be comprised of many smaller tiers, as is the case (to a lesser extent) in this example. The primary business logic component, in our example, is an EJB. But there is a clear distinction between the EJB and the Data Access Object - the class which is responsible for doing the "heavy lifting" which leads us into the next tier. Data access - This tier is often part of the middle tier, but is clearly separate from the business logic tier in that its primary responsibility is in establishing connections to data stores and querying those data stores. In our example, this is done with the Data Access Object, which is a Java class running in the J2EE space which sends queries to the MySQL database and returns data back to the EJB (the main component of our business logic layer, as noted above). February 2003 · PHP Architect · www.phparch.com
PHP in an N-Tiered World Finally, there's the Database Layer. This tier is the end of the line, or beginning, depending on how you look at it. This tier represents our data storage tier. Each tier has a very specific purpose and depending on the complexity of your application each of these general tiers may be broken up into more specific tiers. For example, the Business Logic tier may be comprised of business rules that utilize messaging for systems integration, or Data Access tiers may be broken up into separate sub-tiers to handle legacy data sources and LDAP or other directory services which also supply data in some form. In this example I am using PHP as the presentation layer. The presentation layer is responsible for facilitating the actions or events that occur in an application. You'll notice in the example that the PHP code does not contain any database calls, nor does it contain any business logic. The reason is that PHP, in this example, is responsible for presenting data and enabling interaction between the user and the data. Nothing more, nothing less. The "middle tier," the tier that contains the business logic and data access tiers, will be implemented using an EJB (Enterprise Java Bean) in JBoss using the "session facade" design pattern. Using this particular design pattern in the J2EE space means we can further separate our middle tier code into clear distinctions between controller (the EJB) and data access (Data Access Object). J2EE (Java 2 Enterprise Edition) has become (or is becoming) the defacto standard technology for the middle tier because of Java's wide acceptance on most platforms and because Java's object oriented design makes modeling business processes much simpler. However, I must digress, as this is not an article about the fundamentals of EJB, object oriented design principles or JBoss. The code I have included should provide you with a good launching off point for getting your feet wet in the middle tier. I have chosen to use MySQL to represent the data tier. MySQL is a powerful and robust relational database which is free to use. This ease of accessibility, along with its compact, simple design makes MySQL perfect for our informal example code. There are lots of other reasons to use MySQL in your applications but I don't want to get political on you, so on with the show! Lets get on with the example, which I think clearly describes this n-tiered architecture and also clearly illustrates the advantages of this type of architecture. The small application I am using to illustrate this concept is a very simple user management program that enables you to add and edit a user account. Keep in mind that this is merely an example and is used only to illustrate how PHP lends itself well to n-tiered applications. I eagerly invite you to take this code and run with it, and turn it into something useful. First things first. You will need PHP (obviously) and
28
FEATURES you will need to configure PHP with Java support for this to work. Next you'll need a copy of JBoss (http://www.jboss.org). JBoss is an open source J2EE application server. Configuring JBoss can be a bit confusing for those new to the whole Java application server environment, but there are some excellent resources such as the JBoss discussion forums and the Getting Started PDF that can get you up and running rather quickly. Next, you'll need MySQL (http://www.mysql.org). MySQL is rather simple to setup and configure - just make sure everything is running on your local host for this first example. Once you get more comfortable and have the machines to do so, I would recommend spreading things out a bit so you can really see how n-tiered architectures enable logical scaling. Once you have JBoss and MySQL set up, configured and running, you'll need to run the SQL scripts provided in the source code for this article (shown in listing 1). Basically you just need a database with a single table. You can use phpMyAdmin (http://www.phpmyadmin.net) to run the SQL scripts. I used phpMyAdmin to generate them, so you should have no trouble getting them to work. Next, you'll need to create a data source for JBoss that maps to your MySQL database server and database. I provided my XML configuration files, which I use in my own JBoss distribution to help give you a leg-up in getting this running. Just open up the phpa_mysqlservice.xml file and make the modifications to the user name and password settings. You'll also need to change the login-config.xml file. Please read the README file I included. It should make getting configured and running much simpler. Yes, I am the first to admit that doing this type of development does have a bit of a steep learning curve, but once you have gotten comfortable with the whole notion of application servers and the configurations surrounding them, all of this will make perfect sense and become second nature. As part of the source code package you'll notice a Java JAR file. This JAR file contains the EJB classes you need to deploy (place into the 'deploy' directory in the JBoss server). You'll also want this JAR file in PHP's class path. This JAR file contains all of the required class files of the EJB and the client application your PHP program will use in order to "talk" to the EJB (running in JBoss). Before we get too much into the source code, take a look at figure 1. This is a simplification of the design of the User Management application we'll be looking at. There are some important concepts illustrated in this figure, one being that the presentation layer does not directly call the middle tier (EJB) but uses a client object to perform the work required to instantiate the EJB and to pass user input into the middle tier. In this example, I use the Java Collections API (Vectors and Hash tables) to pass data back and forth because February 2003 · PHP Architect · www.phparch.com
PHP in an N-Tiered World we use the WDDX API to serialize and unserialize those objects into arrays in PHP. Because we are dealing with disparate application platforms (PHP and Java) we need to pass complex objects from one platform to the next and WDDX satisfies the requirements in this area rather nicely. Let's start looking at our PHP code in the presentation tier. The interesting stuff starts on line 24 (index.php is included in this month’s package). Because we are using an EJB to serve as our business logic - or "middle" tier, we need to establish three variables so that our EJB client class can locate our EJB remote reference. We communicate with our EJB through a remote method invocation protocol, and through this protocol our business methods are said to be "exposed" through what is referred to as a "remote interface." $factory represents the naming factory interface JBoss uses to instantiate the naming service. $url points to where JBoss is running, and also indicates which remote method invocation protocol we'll be using. $jndiName is the name of the reference in JBoss's naming service which points to the actual bean's "home" interface. It is through the "home" interface that we create an instance of our remote object. I would recommend checking out http://java.sun.com/products/ejb/ to learn more about EJB. Once we have our EJB "environment" established, we want to create a Java object in our PHP script. The object we are creating is an EJB client object. This client is a simple Java class that establishes the connection between the PHP application and the EJB. We first create the object: $java_obj = new Java ("org.ew.phpa.User.clients.UserClient");
Now that we have an object reference for our EJB client, we can issue method calls against our $java_obj. The first method call we need to issue is an init() call. The UserClient class contains an init() method that performs the initial context lookup of the EJB home reference and creates the remote object reference. $java_obj->init($factory,$url,$jndiName);
Okay, so we've gotten all of the boring environmental and initialization stuff over with and out of the way. Now we can have some fun! To keep things in a single PHP script, I stuck all of the functions of this user management application into a single index.php file. The main functionality is represented by the following PHP functions:
29
FEATURES
PHP in an N-Tiered World Listing 1
saveChanges printNewUserForm printEditUserForm printEntry Let's start at the very beginning. In the printEntry() function (which is displayed if you've just accessed index.php for the first time). We first start with this: $wddx = $java_obj->getUsers(); $user_array = wddx_deserialize($wddx);
You'll notice we call a method in our $java_obj (which if you remember is a reference to our EJB client
FEATURES class). In our EJB client, we have a method called getUsers() which takes no parameters and returns a WDDX string. This getUsers() method in our EJB client maps directly to our remote EJB method called getUsers(). The WDDX string is then deserialized (using wddx_deserialize($wddx)) into a PHP array. Now that we have our data back from our database (via our middle tier) we are now ready to do something with it! for ($i = 0; $i < count($user_array); $i++) { $uid = $user_array[$i]["user_id"]; $username = $user_array[$i]["username"]; echo ''; }
Here we are looping through the array, printing individual