Licensed to: Rémy Dufour [email protected] User #8203 Licensed to 8203 - Rémy Dufour ([email protected]) CONTENTS July 2009 FEATURES 13 Continuous Integration With PHP Felix De Vliegher Keep Your Code on Track.

19 Writing Custom PHP Extensions Gwynne Amaya Raskind Get in on the Extension Action

24 Future PHP, Future ? Mercel Esser Three ways to mix Java and PHP.

30 Extending OXID eShop with Custom Modules Vikram Vaswani Now You Can Get Exactly What You Want.

40 : Flex Your Flash Jeff Winesett Yii Makes It Easy.

COLUMNS Licensed to 8203 - Rémy Dufour ([email protected]) 4 Editorial Elizabeth Tucker Long 50 exit(0); Marco Tabini PHP's Future? The Seven Seas of Insanity

6 From the Cloud Ben Ramsey 51 ElePHPants! Services in the Cloud

10 Enterprise PHP Ivo Jansch Continuous Integration Background Download this month’s code at: 49 Security Roundup Arne Blankerts http://phparch.com/code Trust Me - I know What I'm Doing

If you want to bring a PHP-related topic to the attention of the professional PHP community, whether it is personal research, company software, or anything else, why not write an article for WRITE FOR US! |architect? If you would like to contribute, contact us, and one of our editors will be happy to help you hone your idea and turn it into a beautiful article for our magazine. Visit www.phparch. com/writeforus.php or contact our editorial team at [email protected] and get started! EDITORIAL PHP's Future?

July 2009 by Elizabeth Tucker Long Volume 8 - Issue 7 Publisher he theme for this issue is “The Future Arbi Arzoumani of PHP.” We could not have asked for a more abstract and wide-open theme. T Editor-in-Chief So what is the future of PHP? A staple in Elizabeth Tucker Long the enterprise scene? Wider use on the Windows platform? No one knows. The Author Liaison future of PHP could be anything because it Elizabeth Naramore is only limited by our imaginations, and as a Cathleen MacIsaac community, we are very creative (perhaps a little too creative sometimes). However, we had to pick a few topics for you, so I hope Technical Editors Simon Harris, Keith Casey, you enjoy reading about why you can benefit from Continuous Integration with Ivo, and once you know why you need it, head over to Felix’ article Clark Everetts, Luke Giuliani, on how to implement Continuous Integration. Marcel talks about merg- Jonathan Stark ing PHP and Java to give your code the ultimate portability, and Vikram introduces us to a shopping cart system that is designed to be customized Graphics & Layout and extended to fit your every whim. Jeff makes incorporating Flash in your Arbi Arzoumani site a with the Yii framework, and Ben shows you the affordable side of cloud computing. Sometimes PHP just isn’t enough. When that happens Managing Editor to Gwynne, she writes her own extensions using the Engine, and this Arbi Arzoumani month, you’ll learn how you can too. Arne gives us a lesson in unrealized security risks, and of course, Marco wraps us up with a discussion about his Authors own warped mind. Arne Blankerts, Marcel Esser, Ivo Jansch, Ben Ramsey, Gwynne Amaya Raskind, Marco Tabini, Vikram Vaswani, Felix De Vliegher, Jeff Winesett Licensed to 8203 - Rémy Dufour ([email protected])

php|architect (ISSN 1709-7169) is published twelve times a year by Marco Tabini & Associates, Inc., 28 Bombay Ave., Toronto, ON M3H1B7, Canada.

Although all possible care has been placed in assuring the accuracy of the contents of this magazine, including all associated , listings and figures, the publisher assumes no responsibilities with regards of use of the information contained herein or in all associated material.

php|architect, php|a, the php|architect logo, Marco Tabini & Associates, Inc. and the MTA Logo are trademarks of Marco Tabini & Associates, Inc.

Contact Information: General mailbox: [email protected] Editorial: [email protected] Sales & advertising: [email protected]

Printed in Canada Copyright © 2003-2009 Marco Tabini & Associates, Inc. All Rights Reserved 4 | July 2009 www.phparch.com Licensed to 8203 - Rémy Dufour ([email protected]) COLUMN From the Cloud

Services in the Cloud

by Ben Ramsey

Need to scale your but finding the cost of physical services too much for your budget? Cloud services may be just the answer for you.

hat is cloud computing? Well, loosely put, it’s Amazon Elastic Compute Cloud any form of computing that takes place over a Amazon Elastic Compute Cloud, or EC2, takes the Wnetwork. More well-defined, it’s the practice concept of virtual private servers to a new level. With of offloading expensive processes to a machine or EC2, Amazon provides a cloud in which users may run pool of machines, scaling to any number of machines any number of (VM) images with vary- depending on resource utilization. Historically, cloud ing degrees of resources allocated to each machine. Licensed to 8203 - Rémy Dufour ([email protected]) computing has been limited to use in the enterprise Each image running in the cloud looks and behaves due to the prohibitive expense of running tens of hun- just like a real server running in a server farm. This is dreds of machines, or even thousands of machines, to process data. The costs include hardware, electricity, space, and more, making it impractical for any small- to medium-sized business, but these obstacles are no RELATED URLs longer a problem with the advent of cloud services such as Amazon Elastic Compute Cloud (EC2), Windows • Amazon Elastic Cloud Compute (EC2) - Azure, and Google App Engine. http://aws.amazon.com/ec2/ Small, medium, and large businesses are now saving • Google App Engine - money by ditching their in-house, data-processing, http://code.google.com/appengine/ server farms and moving to these cloud services for • Azure Services Platform - everything from processor-intensive number crunching http://www.microsoft.com/azure/ to easy-to-scale web hosting. It’s a growing industry, • Scalr - http://code.google.com/p/scalr/ and more and more of the services we use on the Web • PHP on App Engine - http://phpwithjava.appspot.com/ are trusting their business models to external cloud computing services.

6 | July 2009 www.phparch.com From the Cloud COLUMN very attractive to businesses who rely on racks upon In addition, App Engine imposes some hard limits racks of machines in a data center. With virtualization, for the service, including a maximum of thirty seconds these business can greatly reduce costs by switching per request, 1,000 files per application, a maximum of to EC2, getting rid of hardware, cutting energy costs, 10 MB allowed in an HTTP response, and no more than and freeing up space. 1 MB per item stored in the data store. Other restric- For persistent storage, EC2 connects to Amazon tions include: read-only access to the file system, Simple Storage Service, or S3. For content distribution, execution of code only through an HTTP request, and Amazon CloudFront provides edge locations to speed data store queries are limited to only 1,000 rows re- up delivery of files to end users. Amazon Web Services turned per call. provides a full and flexible cloud-computing solu- While there certainly appear to be a lot of limita- tion, but this flexibility comes with a price: the user tions and restrictions involved in using App Engine, it is in full control and must manage his or her servers, can save you the hassle of managing a server farm. Use program them to scale properly, and set everything up App Engine when you don’t want to worry with server just as one would do with a real server farm—without set-up and maintenance, you want an environment that the hardware. That is, EC2 is not managed hosting. already comes complete with helpful APIs and even a Your system admins will still have plenty of work to built-in web application framework, and you don’t mind keep them busy. programming in Python or Java. Other languages will Still, EC2 is cheap—about $73 USD a month for the be added in the future, perhaps including PHP. smallest VM at the time of this writing—and provides As a side note, it is possible to run PHP on App plenty of options, including multiple distributions of Engine with Quercus, a Java implementation of the Linux and Windows Server images, or the ability to cre- PHP language. Follow the “PHP on App Engine” link in ate your own, and software exists, such as Scalr, to aid the Related Links section for more information. in scaling a pool of machines in EC2. Use EC2 when you want total control over the entire Windows Azure environment in which your applications run, from A relative newcomer to the cloud game, the Azure the software that runs in the cloud (such as Apache, Services Platform from Microsoft is a collection of PHP, MySQL, etc.) right down to managing the virtual various services and APIs that exist in the Microsoft servers to scale based on load. If porting applications cloud. These services include Live Services, SQL already running across multiple servers, then EC2 is Services, .NET Services, SharePoint Services, and a good fit because, for the most part, you can image Dynamics CRM Services. The hosting and management those machines and move them directly into EC2 with environment in which these services live is called minimal changes. Windows Azure, which is described by Microsoft as a “cloud services operating system.” On top of this plat- Google App Engine form and on this operating system, Microsoft runs such Google App Engine differs from EC2 in that the Google services as Windows Live, Office Live, Exchange Online, cloud is a full development platform in which you run and others, and they have opened up the platform, Licensed to 8203 - Rémy Dufour ([email protected]) applications rather than deploy virtual machines. App allowing developers to build and run applications on Engine abstracts away the low-level details of the Azure that may utilize the services of the platform. operating system, removing the need for administra- Azure is similar to App Engine in that it is a devel- tion of a virtual machine and allowing you to focus on opment platform that acts as an operating system, building applications at a higher level. Think of it as rather than a system in which you run your own vir- a massive operating system that is fully managed by tual machines, as with EC2. Scaling, reliability, load Google. balancing, and geo-replication are all handled by the Scaling in App Engine happens naturally. As your ap- Windows Azure Fabric Controller, and the Azure Storage plications need more resources, App Engine allocates Service provides an unstructured data store for storing more. Under a certain threshold, there is no cost to BLOBs and non-relational tables. REST and SOAP APIs use the service, but as demand on your applications provide access from Azure to each of its services. requires resources above that threshold, then Google Azure is naturally tightly integrated with Microsoft will start charging for resource usage. Some of the .NET technologies. However, at MIX09, support for quotas imposed on the free service include limits on FastCGI was announced, which opens the door for run- e-mails that can be sent per day, bandwidth in and out ning PHP applications in the Azure cloud. quotas per day, CPU megacycles per day, HTTP requests At the time of this writing, Azure is open to de- per day, and data store API calls per day. velopers by invitation only during their Community

www.phparch.com July 2009 | 7 COLUMN From the Cloud

Technology Preview (CTP) stage. A pricing model for a cloud was once costly and impossible to maintain for pay-as-you-go service fees is forthcoming, but during individuals and small- to medium-sized companies— the CTP, the service is free to those with an invitation and even unattractive to large companies—these ser- token. All are allowed to sign up for a token, though it vices can provide cloud technology at a fraction of the is not clear if everyone who signs up receives one, and cost and without the administrative overhead required times for receiving a token vary. to run a server farm. Use Windows Azure if your organization relies on the While I’ve mentioned three such services, these are Microsoft family of technologies and needs tighter in- not the only three. As I stated earlier, cloud services tegration with their online services. Like App Engine, is a growing industry. Other clouds include AppLogic Azure can relieve you of the overhead that comes with from 3tera, Mosso from Rackspace, and more. The managing a virtual server farm like EC2. Plus, Azure choice of which to use is dependent on your needs and has built-in FastCGI support, and, thus, can run PHP whether the service offerings meet those needs. applications, so for those wanting to run PHP in a managed cloud, Azure is a good fit.

A View from the Cloud I’ve said it before, and I’ll say it again. Software as a Service (SaaS) and software + services models are the future of computing. The software and operating systems we use are becoming so tightly integrated B e n R am s ey is a Senior Software Architect for Schematic and with the Cloud that the experience is seamless. We are the founder and organizer of the Atlanta PHP user group. He beginning to not notice the difference between appli- has a passion for HTTP and web services and longs for the cations that run on the desktop and applications that day when the Semantic Web is a reality. He likes zombie run in the Cloud. Cloud services exist to bring that movies, loves good beer, and blogs at benramsey.com. level of seamlessness to the masses. Whereas running Licensed to 8203 - Rémy Dufour ([email protected])

8 | July 2009 www.phparch.com Licensed to 8203 - Rémy Dufour ([email protected]) COLUMN

PhP Continuous Integration Background

by Ivo Jansch

Programming large systems can be challenging. Dependencies between parts of the code make it very easy to fix a feature on one side of the code base only to break something on the other. Continuous Integration is a practice that helps fight problems like this and helps improving the quality of code. Elsewhere in this issue, Felix De Vliegher has a tutorial on how to work with Continuous Integration in PHP. In this column, Ivo looks at why Continuous Integration is important. Where Felix’ article tells you how to do it, Ivo’s will show you why.

ontinuous Integration is a practice that was grown from being a methodology and toolset to im- introduced by the people that invented the prove integration, to a whole suit of automated build CExtreme Programming methodology. It’s intended tools that help improve the quality of software. In purpose was to lower the cost of integrating changes essence, a Continuous Integration tool is one that to software. If you work with multiple developers on a continuously takes the code base from the repository, project, you generally don’t all hack into the same files does some checks, compiles the code and performs in the same directory structure; you usually each fetch various tasks. PHP is not a traditional compiled lan- a working copy from a source control system. Merging guage, so for PHP that step can be skipped, but the back your changes into the repository version can be checks and other tasks are nevertheless very useful for

tricky and time consuming, especially if that version PHP applications. Licensed to 8203 - Rémy Dufour ([email protected]) has since been changed by others. What the XP team Let’s have a look at what aspects of PHP soft- discovered was that the smaller the time between ware development can be improved using Continuous these integration steps, the smaller the overhead Integration tools. incurred in merging changes. Continuous Integration was then invented to help people integrate in smaller Quality steps. The most common task in a Continuous Integration Since its inception, Continuous Integration has environment is running test suites. If your application

This column benefits greatly from reader input. If you have an interesting project or if you want to share how you employ PHP in your organization, drop me an email at [email protected]. I might contact you for a short interview, or at least I will use your input when writing this column!

Thank you! Ivo

10 | July 2009 www.phparch.com Continuous Integration Background COLUMN

contains unit tests, then every time the Continuous complex it is, and the higher the chance of Integration environment build runs, it will run all the defects. available unit tests and report the result. If any part of the application is broken, the unit test suite will • Code Coverage: this is a metric that indi- tell you. cates what percentage and which parts of the Where unit tests test the application on the lowest source code are covered by a unit test. possible level, it is also possible to use Continuous • Lines of code statistics Integration to test on a higher level. Using tools, such as , which can simulate browser users, in com- • Object Orientation analysis: PHPUnit can not bination with a Continuous Integration environment, only count how many classes, interfaces etc. makes it possible to test the entire application from there are, but can also give an indication of the browser level every time someone makes a change the maximum ’depth of the inheritance tree’. to the code. (Note that this is not easy to set up; if The larger this number, the more overhead is you commit a change in functionality, your tests might incurred when running the program. break, so it often involves also committing the test cases that test the functionality.) • Probably the most hilarious, but still use- ful, metric that can be calculated is the Regressions ’Change Risk Analysis and Prediction’ index Very much related to overall quality is regression test- that PHPUnit can measure. This analyzes the ing. A common nuisance for developers is that when amount of effort and pain that will be needed they make a change in one part of the code, something to make changes to a piece of code. (And yes, might break in a completely different part of the code the acronym is such that you can actually say that they didn’t think would be affected. Because ’your code is ...’ and prove it with numbers!) of the many dependencies between parts of a piece of software, regression problems are very common. While running unit tests on your own environment Metrics like this help analyze the quality of code, but helps fight regression problems, using a Continuous more importantly, it helps developers improve their Integration environment makes this is much easier. code. And because of the continuous nature, you’ll Every committed change gets tested against the latest always have up to date metrics. version of the code base. Consistency Metrics Can Continuous Integration help write more consis- A third element that can easily be automated using a tent code? Yes, if you hook it up to a tool such as Continuous Integration environment is the collection Licensed to 8203 - Rémy Dufour ([email protected]) of software metrics. Software metrics are calculations and reports on certain aspects of the source code. Tools such FIGURE 1 as PHPUnit or PHP_Depend and PHPMD (a ’PHP Mess Detector’) can analyze a code base and give an indication of its quality. Quality is not only defined by how well a piece of code works, it is also defined by how well it is written. Examples of metrics that can be con- tinuously calculated using a Continuous Integration environment are:

• Cyclomatic complexity: this is an indication of how many different execution paths a program has. The more different paths (e.g. many if-statements and loops), the more

www.phparch.com July 2009 | 11 COLUMN Continuous Integration Background

PHP_CodeSniffer. This tool is an analysis tool that you find a problem, the lower the cost of fixing it. (We verifies if a piece of code adheres to coding standards. covered this principle a few months ago in this column It lets you define in detail what standards you would when we talked about functional requirements). A like to enforce. If set up in a Continuous Integration Continuous Integration environment is in essence a environment, it will then always check the code for tool that helps find problems earlier. Instead of later deviations of the standard. down the line when the customer is testing the ap- plication, a problem is found right after code has been Automation committed. This makes the bug fixing less expensive, A Continuous Integration environment is essentially and later on, maintenance will be much more cost ef- a set of tools that periodically ’builds’ an application. ficient as well. This means that you can add in tasks to automate Not only will the unit tests help, but all of the ele- steps of the deployment process. This is useful if you ments we’ve seen so far, such as the software metrics want to place snapshots of an application on a down- and coding standards, will help make the software load server or automatically update a test environment more maintainable. It will also teach you to write with the latest commits. better code right from the start. The Continuous Another very useful automation is the generation Integration environment acts as a tutor that will con- of documentation. It’s possible to hook up phpDocu- stantly tell you if you’re doing the right thing. mentor into the Continuous Integration environment, Finally, you don’t have to create a full-fledged so you have API documentation built from the source Continuous Integration environment right from the code that is always accurate and up-to-date with the start. Most Continuous Integration tools such as latest changes. phpUnderControl are very flexible. You can set them up and start with maybe just PHP_CodeSniffer inte- gration, and as you get used to using that, gradually Attribution build out the other features. When ’the build breaks’ (which is Continuous Now, if I’ve managed to convince you to at least give Integration speak for when somebody committed Continuous Integration a try, head on over to Felix’ something that triggers one of the test cases or that article in this same issue, and start today! breaks one of the automated build processes), the Continuous Integration environment makes it easy to see what happened, what the latest change was that caused the problem, and who caused that problem. It FIGURE 1 might seem that this would instill a ’blame culture’ on a development team, but it is so much more efficient; instead of debugging the code to see why there is a problem, the person that did the offending commit can Licensed to 8203 - Rémy Dufour ([email protected]) immediately tackle the problem. And since the build cycle is short in a Continuous Integration environ- ment, this developer usually still has a very good grasp of the code they wrote and what might have caused the problem.

Cost Continuous Integration seems expensive. You have to invest in yet another environment that needs to be maintained, developers have to actually write test cases, the system will complain about quality so developers will need to change code. ’All this extra attention to quality is nice, but I’ rather stay within Ivo Jansch is the CTO of Ibuildings, a PHP services company the budget’ could be the response of the unknowing based in Europe. He is the author of php|architect’s Guide project manager or customer. to Enterprise PHP Development, and is an active blogger Still, I feel that it is an investment that will pay for and speaker in the PHP community. Ivo also initiated the itself. It’s well known in the industry that the sooner ATK Business Framework.

12 | July 2009 www.phparch.com FEATURE

Continuous Integration With PHP by Felix De Vliegher

Best practices for PHP such as unit Integration Hell testing or documentation writing are Some years ago, there was a project involving the con- struction of public transport rails close to where I live. getting more and more widespread. But As we didn’t have enough local engineers to do the how do you ensure that the unit tests job, someone from the United Kingdom was brought in are run each and every time, or that to speed up the project. They each started to lay down the transport rails at different places, until the point your documentation gets built? You’d where they came together and faced a serious prob- need some sort of automatic process lem: the UK engineer had designed his technical draw- ings using inches as a measuring unit, whereas the that does these tasks for you every time local engineer used the metric system to determine you integrate your piece of software his measurements. Although this case describes quite within the project. This is exactly what a serious integration problem, it’s a classical example where integration of the project has gone wrong, and Continuous Integration can help you with.

In this article, we’ll go into what exactly Licensed to 8203 - Rémy Dufour ([email protected]) is meant by Continuous Integration and REQUIREMENTS go through a step by step example on how to set up Continuous Integration PHP: 5.1+ with Xdebug installed using CruiseControl, Apache Ant and Other Software: phpUnderControl. To finish, we’ll touch • PEAR Modules: PHPDocumentor, PHPUnit, on other useful topics like feedback PHP_CodeSniffer • A recent Java version reporting and database integration. • Apache Ant After reading this article, you should have a good grasp of what Continuous Useful/Related URLs: • CruiseControl: http://cruisecontrol. Integration is, how to set it up in your sourceforge.net • phpUnderControl: http://phpundercontrol.org PHP projects and why it can be a key • Apache Ant: http://ant.apache.org facet of your software development lifecycle.

www.phparch.com July 2009 | 13 FEATURE Continuous Integration With PHP

the same principles and problems apply equally to software more rapidly.” But what exactly comprises a software projects. full build? In a traditional sense, you might think of a This might sound familiar to many of us: you’re build as a packaged or compiled version of your source working on a set of classes or modules and after fin- code, but within the scope of Continuous Integration, ishing them, you feel they work perfectly. Two other a build is actually much more than that. A Continuous colleagues are building other parts of the application, Integration build could consist of , inte- and they guarantee you that everything works great gration testing, static and dynamic analysis, documen- as well. At some point, all functionality is brought to- tation generation, packaging or deployment. gether and everything breaks. You think it’s the other If you’re still wondering why you might use developer’s fault because you’re sure of your own code, Continuous Integration, let’s look again at some of the but your colleagues are probably thinking the same. advantages: It reduces the possibility of something Very likely, you’ll be in for a long spell of debugging, going wrong by automating error-prone tasks, unit fixing errors and trying to make the various software tests get run each and every time a change occurs, components work together. That’s time that could be there’s instant reporting of project statistics and code spent doing a lot more productive tasks. metrics, you can detect bugs and problems much faster Traditionally, you could easily spend several days in due to feedback mechanisms and your developers have “integration hell”, trying to make all those indepen- a higher confidence level, knowing they will be noti- dently-working software components work together to fied in case of problems. There are more advantages end up with a running project. This could certainly be than the ones I mentioned, but these alone make done in a better and less error-prone way. You could Continuous Integration worth considering. write scripts that ease the integration by removing the repetitive tasks. Additionally, the more often you integrate, the smaller the changeset you’re dealing with and the easier the integration will be. And this is exactly what Continuous Integration is all about! Make your integration process Continuous Integration a non-event. More and more, PHP is making its way into larger busi- " nesses, traditionally an environment where Java and the Java toolset are often used. Tools like jUnit come to mind, but also Continuous Integration systems. How do you make sure that integration goes without CruiseControl problems, what guarantees do you have? This is where CruiseControl is a Continuous Integration tool writ- Continuous Integration comes to play. Martin Fowler ten in Java. It provides a framework of builders and

describes it pretty accurately: “Continuous Integration plugins to ease the integration of your project. In a Licensed to 8203 - Rémy Dufour ([email protected]) is a software development practice where members of typical environment, it will periodically check your a team integrate their work frequently, usually each version control system of choice for changes to your person integrates at least daily - leading to multiple code, update the source code, create a build, publish a integrations per day. Each integration is verified by an report and/or send out notifications with build results. automated build (including tests) to detect integra- As for the prerequisites: I’m assuming you have a tion errors as quickly as possible. Many teams find that working Linux OS with Java installed, even though this approach leads to significantly reduced integra- CruiseControl can also be installed on Windows. tion problems and allows a team to develop cohesive Installing CruiseControl is as easy as grabbing the binary from the website (see Related URLs) and un- packing it somewhere. For future reference, I’ve put FIGURE 1 the package contents in /opt/cruisecontrol/. In this directory, you should find an executable script called cruisecontrol.sh, this is what we’re going to use to start the cruisecontrol server. Let’s do that right away:

$ cd /opt/cruisecontrol && ./cruisecontrol.sh

The Jetty java server output should be flying by, and if all went well, if you go to http://yourserver:8080/

14 | July 2009 www.phparch.com Continuous Integration With PHP FEATURE cruisecontrol/, you should see the connectfour proj- But first, we need to get our hands on phpUnderCon- ect, a dummy Java project that shows off the features trol. For this example, I’m getting the sources through of CruiseControl. Figure 1 shows the default look of the SVN repository. You could get the sources pack- the CruiseControl page. Feel free to browse around aged or through PEAR as well, which is described on and have a look at the test results or metrics. At this the phpUnderControl website (see Related URLs). point, we’re almost ready to go and install phpUnder- First, let’s check out the svn repository: Control on top of it, but first, let’s have a look at what makes this build work. $ svn checkout svn://svn.PHPUnit.de/PHPUnit/phpUnderControl/tags/0.4.7 While installing CruiseControl, you may have no- /opt/phpuc ticed a configuration file in the cruisecontrol direc- tory, called config.xml. This configuration file describes As you can see, this checks out the current release and what is called the “build loop” of each project that is also grabs the eZ Graph component, which will give us active in your Continuous Integration environment. the fancy charts in the phpUnderControl dashboard. The complete config.xml file is shown in Listing 1. We’ll be working with the commandline tool phpuc.php, The XML file is divided into different projects, and located in /opt/phpuc/bin/. Installation of phpUnder- each project has a couple of different parts that make Control on top of CruiseControl is done like this: up how a project is handled. Listeners are notified $ /opt/phpuc/bin/phpuc.php install /opt/cruisecontrol/ when the status of the project changes, for example, if a build has started. By default, this status is written This takes a backup of all files that will be modified to a file, but there are listeners that write the cur- and puts the new files in place, ready to be used by rent build status to an FTP server, too. The bootstrap- CruiseControl. pers are run before a project build takes place. This is As an example, I’m going to use a dummy project where you can define what needs to be done to clean that is provided with the phpUnderControl installa- up a project before building. The modificationset ele- tion. It creates a small project inside CruiseControl by ment checks your project for changes. It supports proj- adding the project to the main CruiseControl config.xml ect source code that is stored in Subversion, CVS, file. To set up the demo project, execute the follow- or even on a regular filesystem. The schedule element ing: defines the interval that’s used to check for changes in a project and also defines the actual file we’re going $ /opt/phpuc/bin/phpuc.php example /opt/cruisecontrol/ to use to perform a full build. The log element defines where build results will be placed, and publishers de- If all goes well, you’ll see the different tasks on the fine the notifications for a certain build result. screen. A PHPDocumentor, PHP_CodeSniffer, PHPUnit and ezGraph task are installed, and even without using our CruiseControl server, we’re able to run them. After phpUnderControl all, the build script which is provided to run the dif- phpUnderControl is a modification to CruiseControl

ferent tasks is written in plain Ant. Licensed to 8203 - Rémy Dufour ([email protected]) that makes it easier to set up PHP-based projects in the Continuous Integration environ- ment. It provides support for native PHP tools such as PHPUnit, phpDocumentor, FIGURE 2 PHPMD and PHP_CodeSniffer and uses a slightly modified CruiseControl interface to display their reports. The screenshot in Figure 2 is an example of how the de- fault phpUnderControl interface should look. Now, let’s get to the actual setup! phpUnderControl works by modifying the CruiseControl files to provide a differ- ent interface to the user. To make this easier, it comes with a command line tool that allows you to install phpUnder- Control, set up your PHP projects and even do some extra maintenance tasks.

www.phparch.com July 2009 | 15 FEATURE Continuous Integration With PHP

Ant Builds we’re depending on the PHPUnit, php-codesniffer and Ant is our tool of choice to set up the different tasks php-documentor targets, which will be executed in that that are run to produce a full build. It is Java-based exact order: and aims to overcome some of the limitations that the markup language which most of us are already familiar with. An Ant configuration file is made out of dif- Next, we come to our first “real” target. Each target ferent parts, called targets. Each target executes a can consist of multiple tasks, small pieces of code that specific piece of functionality, such as running tests, can be executed. In our case, we have an exec task, generating documentation or packaging your source used to run the phpdoc binary. Using the arg element, code. In addition to that, targets can depend on other we define which commandline arguments we want to targets, which makes it easy for us to stop the build append to phpdoc: loop if a certain target has failed. Listing 2 shows the contents of the build.xml file for our demo project, Let’s run through the build file. After all, there’s a lot of information in there that makes the magic work, but if we take a closer look, it’s actually pretty easy to Right now, you should have noticed the use of proper- understand. To start off, you’ll see that the full XML is ties, which are defined using the names between the wrapped in a tag. This is used to define the “${” and “}”. Some of them are built in, such as the project name, set a default build target, which will be ones we’re using, but you can also easily define your run, and set an optional base directory: own properties:

The following 2 targets are of a similar structure. Have Then, we define the different targets to be executed. a look, and see if you can make out what they do, it The first - and default - target is some kind of ini- should be pretty straightforward. Another useful task tialization target and by its dependencies, defines would be to set up a directory structure prior to run- what will be run and in what order. As you can see, ning the build:

LISTING 1

Licensed to 8203 - Rémy Dufour ([email protected]) 1. 2. 3. 4. 5. 7. 1. 8. 2. 9. 3. 4. 11. 5. 12. 6. 14. buildfile="projects/${project.name}/build.xml"/> 7. 15. 8. 16. 9. 1 7. 10. 19. 11. 20. 21. file="logs/${project.name}/buildstatus.txt"/> 13. 22. 23. dest="artifacts/${project.name}" subdirectory="api"/> 15. 24. pmd ${basedir}/build/logs/phpunit.pmd.xml --coverage-xml ${basedir}/ 26. PhpUnderControl_Example_MathTest tests/MathTest.php"/> 2 7. 1 7. 28. 18. 29. 19.

16 | July 2009 www.phparch.com Continuous Integration With PHP FEATURE

failonerror="true"> In a similar way, there is also a task that can

1. 2. featured, well-oiled build scripts in your Continuous 9. Integration system, but without the feedback you’re

www.phparch.com July 2009 | 17 FEATURE Continuous Integration With PHP

getting from these tools, it’s useless. It makes you GNU make” (we’re loving recursive acronyms here) and act on the results of the integration, and that’s is actually written in PHP. This means that it’s easier to what makes feedback a key point of the Continuous customize for your own needs or for writing your own Integration cycle. You don’t want to realize that, two tasks. It has similar features to Ant, but adds some days after a build has failed, your application is com- PHP-specific functionality, like the opportunity to pletely broken, and you need to spend hours fixing build your own PEAR package XML with a simple task. bugs that may have been noticed at the exact moment You can find an example of such a task in Listing 5. developer x committed their changes. Fortunately, CruiseControl has quite a lot of feed- Conclusion back mechanisms in place, which are called publishers. By now, I hope you’ve seen some of what is pos- Listing 3 could be an example of publishers for the sible using a Continuous Integration system such as php-under-control project, which can be configured in CruiseControl. It might require some changes in your the config.xml file of the CruiseControl directory. software development process though. You need to As you can see, an e-mail publisher has been set think about the build process, unit testing and pro- up to notify the development team that a build has cess automation as much as possible. As a reward, failed. One person needs information on every build, you’ll get software that is more reliable, has fewer so we set up a separate rule for that. There’s quite bugs, is easier to deploy and gives you peace of mind a few more build feedback mechanisms, such as RSS at night. If you’d like to read more about Continuous feeds, SMS, X10 devices or even a Nabaztag, so choose Integration, be sure to have a look at “Continuous what fits you the best. Just remember, some build Integration - Improving software quality and reducing publishers are better suited for different groups of risk” by Paul M. Duvall. It’s one of the most compre- people, so differentiate between who you’re contact- hensive books on the topic and definitely worth a ing. These groups can be the developers, but also a read. project lead, a QA team or your manager. Each group of people may want the information in a different way, with a different level of detail for the build results.

Alternatives CruiseControl is just one possible implementation of a Continuous Integration server. Next to CruiseControl, there are many other projects around that have the Now that you've read how same goal. If you’re looking for a more commercially to implement Continuous supported product, you might want to have a look at Atlassian’s Bamboo (http://www.atlassian.com/soft- Integration, learn more ware/bamboo/). Although not open-source or free, it provides very good integration with Atlassian’s other about why you should Licensed to 8203 - Rémy Dufour ([email protected]) tools, such as Jira. There’s also Hudson (h t t p s:// implement it in Ivo's hudson.dev.java.net), a Java based Continuous Integration environment. Due to less tight integra- column on page 10. tion with PHP tools, I believe it’s a lesser fit for us. However, if you do want an alternative providing good integration with PHP, have a look at Xinc (Xinc Is Not CruiseControl - http://code.google.com/p/xinc/). Xinc is a Continuous Integration server written in PHP 5, which makes it easier for PHP developers to under- stand the Xinc code and extend or customize it if they want to. Although it is relatively new in comparison to CruiseControl, it definitely looks promising. We were using Ant as our project build tool of Felix is a long-time PHP engineer with a big interest in testing and quality assurance. He’s currently employed as choice, but there are also some alternatives to that. a software engineer by Ibuildings and loves to spend his CruiseControl supports at least 10 different build tools, free time organizing things for the Belgian PHP user group but the one that’s most interesting to us is Phing PHPBelgium (http://phpbelgium.be), where he tries to (http://phing.info). Phing stands for “Phing Is Not advocate PHP as much as possible.

18 | July 2009 www.phparch.com FEATURE

Writing Custom PHP Extensions

by Gwynne Amaya Raskind

Sometimes, what PHP provides simply isn’t enough. But fear not! The power of the has an answer. This article explores the basics of harnessing that power to build your own custom PHP extensions.

his is a beginner’s guide to writing Zend exten- If the answer to any of these questions is yes, then

sions using the C programming language and PHP stop and think. Zend extensions aren’t easy to write. Licensed to 8203 - Rémy Dufour ([email protected]) Tversion 5. For simplicity’s sake, not to mention Mistakes are inevitable, and a C compiler won’t catch brevity, it’s assumed that you understand how to write the mistakes the PHP interpreter does. One missed both PHP and C, and that you’re familiar with pointers, character can mean hours of frustrating debugging. structures, and building PHP from source. On the other hand, if writing an extension really will Before diving into this very deep pool, it’s a good solve your problem, the Zend API is a great tool to do idea to ask yourself whether a Zend extension is right it with. Extensions make PHP even more useful, and for accomplishing your task. Here are a few points to the power of C makes some amazing things possible. consider:

• Is there already a PHP function that does what you want? REQUIREMENTS • Is there already a PECL extension that does what you want? PHP: 5.1+ source (not binary) file • Can what you want be done with userland Other Software: Any ANSI C compiler (GCC recommended) code and with reasonable performance?

• Is there another way to do what you want?

www.phparch.com July 2009 | 19 FEATURE Writing Custom PHP Extensions

Starting at the Beginning (perhaps in preparation for packaging your extension You could start writing a Zend extension from scratch, for distribution), run phpize –clean to undo its ef- using a vanilla version of PHP, and issuing all your fects. build commands by hand, but that would be time- consuming, error-prone, and needlessly complicated. Hello, World! PHP has plenty of tools to ease the extension writer’s The files created by ext_skel form a nearly working burden. Hello, World! extension. To make it build, one only has A vanilla PHP, such as most systems install from to remove the M4 comment marker, “dnl”, from these binary packages, runs in release mode. It’s fast, it’s lines in config.m4: simple, and the failure modes work for end-users. This doesn’t work so well for extension writing. If some- dnl PHP_ARG_ENABLE(hello, whether to enable hello support, thing goes wrong, you need to debug, and running a dnl [ --enable-hello Enable hello support]) debugger against a vanilla PHP will produce a lot of useless numbers that tell you nothing. So before doing anything else, build yourself a debug version of PHP by And the JavaScript comment marker, “//”, from this adding –enable-debug and –enable-maintainer-zts to line in config.w32: your configure line. Backtraces of crashes will have nice \// ARG_ENABLE("hello", "enable hello support", "no"); readable function names, and Zend’s internal thread- safety and memory management code will be enabled. Once that’s done, the command Armed with a debug version of PHP, the next step is phpize && ./configure && make will compile and link to create the files from which your new extension will the module. Congratulations, you’ve just created be built. The PHP developers have thought ahead on a Zend extension which exports a function called this one by providing a tool called ext_skel, which, as confirm_hello_compiled() to userland and identifies its name suggests, creates a skeleton for an extension. itself in phpinfo() output. Here’s a rundown of what Setting aside some of the advanced options for now, each file does and why it’s important (or not!): it takes just one parameter: the name of the exten- sion. Running it as ext_skel –extname=hello produces • .cvsignore: This file tells CVS, the version a directory named hello, containing eight files and control system PHP uses, what files not to a tests subdirectory. These files will be examined in bother with. Most people don’t really need more detail shortly. this, or will use the appropriate equivalent Typically, ext_skel is run in the ext directory of your for their own version control system, such as PHP source, and creates the extension folder there. It svn:ignore or .gitignore. can also be run from somewhere else, but to do so you will have to pass it the –skel option to tell it where • CREDITS: This is a plain text file containing the skeleton directory from ext is. It’s usually easier to the credits for the extension. It has to ap- just run it in the ext folder, and move the new direc- pear in this form with this name for certain Licensed to 8203 - Rémy Dufour ([email protected]) tory where you want it. purposes, such as creating a PEAR extension An extension created by ext_skel will only work package, but can be set aside, renamed, or when compiled in the ext directory as part of a larger ignored by the majority of people. PHP build. This is fine if you’re a PHP internals devel- oper writing an extension intended to be part of the • EXPERIMENTAL: This is an empty file which core, but everyone else needs one more tool: phpize. marks the extension as being experimental This tool takes an extension directory, such as the for PHP’s build system. It can be ignored or hello directory produced by ext_skel, and fills in all deleted by most people. the pieces to make it build on its own. • config.m4: This is a file written for Autoconf It may seem logical to run phpize immediately after in the M4 macro language. Even more, it is running ext_skel, but this is a road to disaster! phpize written for PHP’s build system, and it’s how relies upon an extension’s config.m4 and config.w32 files you tell the user’s system what your exten- to do its job. If you haven’t customized those files yet, sion’s special requirements are. If you’re wrap- the work phpize does is useless. You only want to run ping around an external library, this is where it when you’re ready to actually build your extension. you tell PHP how to compile and/or link to If you do run phpize at the wrong time, or just want that library. If your extension has compile- to get rid of the myriad of extra files it generates time options, this is where they’re defined.

20 | July 2009 www.phparch.com Writing Custom PHP Extensions FEATURE

• config.w32: This is the same as config.m4, extension, which the Zend API uses to load it. except it’s written in JavaScript and talks Lines 7 through 9 are a familiar #include, grabbing to the Windows version of PHP’s build sys- the TSRM header for the case when thread-safety mode tem. If your extension will never be used on is turned on. Windows, you can forget this file, but it’s bet- Line 11 is the prototype for the extension’s userland ter to have it. function. When you want to expose more functions to userland, you give the prototypes for them here using • hello.c: Here you have the main source code the PHP_FUNCTION() macro. This will be explained in file for the extension. This contains the zend_ more detail later. module structure, support code for the exten- sion, and the functions the extension exports to userland. The Source File The relevant parts of hello.c are shown in Listing 1. • hello.php: This is a small PHP script which The lines that have a triple opening or closing brace acts as a simple test to confirm that the ({{{ and }}}) activate the “folding” feature of the extension is being loaded into PHP and that vim(1) editor. You can remove them if your text editor the functions you want available are actually is smart enough to figure out the folding for C on its being exposed. own, or you just don’t want to use it. Lines 1 through 8 include some standard PHP head- • php_hello.h: This is the header file for the ers, the configuration file with options determined by extension, containing function prototypes, configure, and the extension’s own header file. globals definitions, and a few macros for you Lines 10 through 15 declare the zend_function_entry to use. array used by the extension. Here, it has only two en- tries: The confirm_hello_compiled function, and a NULL • tests/ and 001.phpt: In this directory reside termination marker. any regression tests for your extension. One Lines 17 through 31 declare the extension’s is provided for you by ext_skel, a simple zend_module structure. This extension has the standard test which just checks that the extension is header, is named “hello”, exports a list of functions available. These tests are run whenever you defined by the hello_functions variable above, has execute make test. the standard lifecycle callbacks, is version 0.1 (this is The focus in this article will be on hello.c and a made-up number, obviously), and has the standard php_hello.h, since they’re the meat of the extension. properties. Lines 33 through 35 allow the extension to be load- ed dynamically if it’s being built as a shared module. The Header File Lines 37 through 39 are the prototype and documen- The relevant parts of php_hello.h are: tation for the confirm_hello_compiled function. Every Licensed to 8203 - Rémy Dufour ([email protected])

#ifndef PHP_HELLO_H userland function should have a header comment that #define PHP_HELLO_H looks like this, so that documentation can be gener-

extern zend_module_entry hello_module_entry; ated automatically. #define phpext_hello_ptr &hello_module_entry Finally, lines 40 through 56 are the actual confirm_hello_compiled function. The function declares #ifdef ZTS #include "TSRM.h" some variables, parses its single string parameter, and #endif returns a string based on the extension’s name to show

PHP_FUNCTION(confirm_hello_compiled); that it’s been compiled and loaded correctly. If you look at php_hello.h and hello.c yourself, #endif /* PHP_HELLO_H */ you’ll find there are quite a few lines in them that Even though most of the comments have been stripped weren’t mentioned here. That’s because those lines here for brevity, you shouldn’t take them out of your are either comments or deal with features of the Zend copy of the header until you’re sure you don’t need Engine which are beyond the scope of this article. them. They provide a lot of helpful hints. Lines 1, 2, and 13 are a familiar stanza to expe- Anatomy of a PHP Function rienced C , protecting the header from To expose a function in C to userland PHP code takes being included multiple times in the same compilation. four steps: Tell the Zend engine it’s there, parse pa- Lines 4 and 5 define the zend_module_entry for the rameters, process data, and return values. In PHP, you

www.phparch.com July 2009 | 21 FEATURE Writing Custom PHP Extensions

just do something like this: Which works fine, but how does Zend know that the C function hello($value1, $value2) function hello() can be called from PHP? What hap- { pens when you want a function that can take param- return "Hello, World! You have {$value2} of {$value1}!\n"; eters of different types and return any sort of value } at all? The PHP version can use anything for the two parameters, but the C version is limited to a NULL- But that doesn’t work in C. C is a statically compiled terminated string and an integer value. C doesn’t language. You could do this, certainly: provide constructs for dynamic typing. The Zend API defines a large set of structures, macros, and func- char *hello(const char *value1, int value2) { tions for working with the kind of data PHP supports char *str = NULL; and for exporting C functions to the PHP world. Referring back to hello.c, line 12 gives the “func- asprintf(&str, "Hello, World! You have %d of %s!\n", value2, value1); tion entry” for the confirm_hello_compiled() PHP return str; /* Caller must free this string */ function using the PHP_FE() macro. PHP_FE() expands } to a pointer to a function and a few other things you don’t have to worry about at this point. It’s meant LISTING 1 to be used as part of a NULL-terminated C array of

1. #ifdef HAVE_CONFIG_H zend_function_entry structures. Exposing a new func- 2. #include "config.h" tion to userland is as simple as adding a new PHP_FE() 3. #endif 4. line. So, assuming you want to add a function called 5. #include "php.h" 6. #include "php_ini.h" hello(), your array now looks like this: 7. #include "ext/standard/info.h" 8. #include "php_hello.h" const zend_function_entry hello_functions[] = { 9. 10. /* {{{ hello_functions[] */ PHP_FE(confirm_hello_compiled, NULL) 11. const zend_function_entry hello_functions[] = { PHP_FE(hello, NULL) 12. PHP_FE(confirm _hello_compiled, NULL) {NULL, NULL, NULL} 13. {NULL, NULL, NULL} }; 14. }; 15. /* }}} */ 16. Now you have to actually create a function named 1 7. /* {{{ hello_module_entry */ 18. zend_module_entry hello_module_entry = { hello(). This isn’t quite as simple as it seems because, 19. STANDARD_MODULE_HEADER, 20. "hello", when PHP_FE() takes a function name, it actually 21. hello_functions, expands it to a name such as zif_hello(). Not only 22. PHP_MINIT(hello), 23. PHP_MSHUTDOWN(hello), that, but a C function called from userland takes a 24. PHP_RINIT(hello), 25. PHP_RSHUTDOWN(hello), very strict set of parameters. Fortunately, the Zend 26. PHP_MINFO(hello), API has taken care of all this for you by providing the 2 7. "0.1", /* Replace with version number for your 28. extension */ PHP_FUNCTION() macro:

29. STANDARD_MODULE_PROPERTIES Licensed to 8203 - Rémy Dufour ([email protected]) 30. }; 31. /* }}} */ /* {{{ proto string hello(string value1, integer value2) 32. */ 33. #ifdef COMPILE_DL_HELLO PHP_FUNCTION(hello) 34. ZEND_GET_MODULE(hello) { 35. #endif } 36. 3 7. /* {{{ proto string confirm_hello_compiled(string arg) /* }}} */ 38. Return a string to confirm that the module is 39. compiled in */ 40. PHP_FUNCTION(confirm _hello_compiled) Of course, an empty function doesn’t do much good. 41. { Right now, you don’t have to worry about the various 42. char *arg = NULL; 43. int arg_len, len; parameters that PHP_FUNCTION() exposes, because you 44. char *strg; 45. won’t be using them directly. 46. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, This particular function takes a string parameter and 47. "s", &arg, &arg_len) == FAILURE) { 48. return; an integer parameter. The Zend API has a function for 49. } 50. getting the parameters your function expects from 51. len = spprintf(&strg, 0, "Congratulations! You have " PHP: zend_parse_parameters(). This function takes a 52. "successfully modified ext/%.78s/config.m4. " 53. "Module %.78s " few standard parameters and then a set of pointers to 54. "is now compiled into PHP.", "hello", arg); 55. RETURN_STRINGL(strg, len, 0); where you want the data stored. One of the standard 56. } parameters is a “format string”. If you’ve ever used the 5 7. /* }}} */ 58. printf() family of functions, or PHP’s date() func- tion, you know what this is: A string containing a set

22 | July 2009 www.phparch.com Writing Custom PHP Extensions FEATURE of specifiers that tells the function what to do with Zend somehow. The spprintf() function uses Zend’s its remaining parameters. allocators. You can also return other kinds of val- For this example, you’ll only be working with two of ues with macros like RETURN_BOOL(), RETURN_LONG(), the eighteen specifiers zend_parse_parameters() ac- RETURN_NULL(), and RETURN_DOUBLE(). cepts, “s” for strings and “l” for (long) integers. The Here’s the complete hello() function: “s” specifier requires two pointers in the argument list, one for the string content and another for the PHP_FUNCTION(hello) { string length. The “l” specifier takes only one parame- char *arg1 = NULL, retval = NULL; ter, the integer value. Here’s how to use them to parse int arg1_len = 0, retval_len = 0, arg2 = 0; the two parameters: if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &arg1, &arg1_len, &arg2) == FAILURE) { char *arg1; return; int arg1_len, arg2; } retval_len = spprintf(&retval, if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Hello, World! You have %d of %s!\n", arg2, "sl", arg1); &arg1, &arg1_len, &arg2) == FAILURE) { RETURN_STRINGL(retval, retval_len, 0); return; } }

Whoa, whoa, whoa! What’s ZEND_NUM_ARGS()? What’s Looking Forward this weird-looking TSRMLS_CC thing that doesn’t have a It’s been obvious throughout these examples that comma before it? Well, ZEND_NUM_ARGS() is just what it you’ve only scratched the surface of the Zend API. says, a macro that expands to the number of param- With any luck, more articles on the subject will follow eters actually passed to the PHP function. this one, but in the meantime, here are some resources TSRMLS_CC is a macro which expands to a parameter you can use to learn on your own: that can be passed or not passed to Zend functions de- pending on whether thread-safety is on or off, without • The book “Extending And Embedding PHP” having to add or remove commas. In short, it’ll work (ISBN 978-0672327049) by Sara Golemon, a in all cases. That’s what the “CC” means: “call with core PHP developer. comma”. You’ll see it being passed to a lot of Zend functions, though not all. Usually, it’s passed at the • The book “Building Custom PHP Extensions” end of a function’s parameter list, but with some func- (ISBN 978-1411601888) by Blake tions that’s not practical. Schwendiman. It’s very important that this TSRM stuff be passed around correctly, or your extension just won’t work • The official PHP documentation, found at right for a lot of people, especially on Windows. It http://www.php.net/manual/. A section on won’t even work right for you, since you turned on Zend Engine 2 is currently under development TSRM back at the beginning when you activated debug there. Licensed to 8203 - Rémy Dufour ([email protected]) mode. But on the bright side, as long as you remember to pass it where it’s needed, you can otherwise forget • The PHP mailing lists, representing a huge about it. user and developer community. Subscribe at Now that the function takes some parameters, how http://www.php.net/mailing-lists.php. do you return the string? Well, first you have to con- struct the string, which is easily done with spprintf() just as confirm_hello_compiled() does. So, now you have a NULL-terminated C string and you want to expose it as your hello() function’s return value. The easy way is the RETURN_STRINGL() macro:

RETURN_STRINGL(str, str_len, 0);

Yep, all it takes is that one line! That zero at the end of RETURN_STRINGL() tells the Zend Engine not to Gwynne Raskind is a Web contractor. She has been an duplicate your string and instead to free the pointer active member of the PHP documentation team for two you passed when its lifetime is done. But don’t do years and has contributed several patches to the PHP this unless you’ve allocated the memory through community.

www.phparch.com July 2009 | 23 FEATURE Future PHP, Future Java?

by Marcel Esser

Write PHP once, run anywhere - with anything. No PHP API? No Problem.

or better or worse, as a language matures, you will Websphere sMash implements a new PHP runtime, writ- ultimately find multiple implementations of it in ten in Java running on a JVM, which uses some really Fuse. PHP is maturing, and it is no exception. While awesome tricks to make existing PECL extensions work the vast majority of PHP installations are Zend Engine while giving you the ability to write extensions in 1 or 2, (read: PHP 4 or 5), there is other game in the Java. Last, but definitely not least, we will take a look wild - and it’s making huge leaps and bounds in prog- at PHP running on Google AppEngine using Quercus ress. PHP has been busy fighting tooth and nail with from Caucho Technologies, Inc. Quercus is another Java in boardrooms and at conferences, and no one no- ground-up PHP-in-Java implementation using no na- ticed that a few of us were off in the corner, fraterniz- tive APIs. ing with the enemy. The result should be obvious. PHP and Java made babies, and now, we have real, solid Zend Java Bridge PHP runtimes running on various JVM stacks. All the The cheapest way to run the Zend Java Bridge is using simplicity of PHP and all the software written for Java, Zend Server Community Edition. At zero dollars and all living together in harmony. zero cents (USD), it’s hard to go wrong. Since the Zend This is not a change we should fear. In actuality, it

folks have made installation a breeze, I will cover Licensed to 8203 - Rémy Dufour ([email protected]) is a good thing. It won’t fragment the language - all of it only briefly. Go to http://www.zend.com/en/prod- the serious Java implementations currently recognize ucts/server/downloads and grab the download and/ PHP compatibility as paramount. The hallmark of a mature language has long been that there are multiple implementations of it, and PHP won’t be any differ- ent. In addition, interoperability with Java can bring REQUIREMENTS untold fortunes: Groovy, Python, Squeak, Lisp, and JavaScript are just some of the languages available on PHP: 5.2.3+ JVM stacks right now. If we make PHP talk to Java, we can make it talk to all these others. Related URLs: In this article, we will review three of the ways to • Project Zero OR Websphere sMash: make your PHP work with Java. Our first method will http://www.projectzero.org be via the Zend Java Bridge using Zend Server, which • Zend Server Community Edition: is likely the most orthodox way to make Java work http://www.zend.com/products/server/ • Caucho /Quercus http://www.caucho.com in PHP. It uses a traditional PHP runtime and adds • Google AppEngine: a helper daemon that is used to make advances into http://code.google.com/appengine/ a JVM. Our second method will be IBM’s Websphere sMash, a mash-up platform for Java, Groovy, and PHP.

24 | July 2009 www.phparch.com FEATURE or installation instructions for your platform. Apt and runtime, and you have a small helper process that RPM users can install from repositories, Windows and allows you to deal with Java classes. Nothing much Mac users get downloads. When installing, be sure to changes. That doesn’t mean you are short on power. also install the Zend Java Bridge extension, which you There is nothing that prevents you from compounding can then turn on in the Zend administration panel for your Java abilities by adding third party JARs, or using your server. This is all well-documented and should go Java class files compiled from source code in lan- off without a hitch. If you do have problems, their guages other than Java. That being said, you are not forums can help. running your PHP on a JVM, so some of these things Once you restart the PHP interpreter (this may re- may get complicated for you. However, if you’re just quire you to restart Apache), you are ready to start looking to extend your PHP with a little bit of Java, running Java code. To test it out, try: say, for example, if you have a proprietary third-party Java class/JAR you must use, this is the path of least Jython, var_dump($math); // Dump the PI constant and you want to mix in some PHP? Let me show you a var_dump($math->PI); little sMash. ?>

It should produce output similar to this: IBM Websphere sMash Project Zero is the open source root of IBM Websphere object(java.lang.Math)#1 (0) { sMash, and it is the application server we’ll be work- } float(3.1415926535898) ing with in these examples. Project Zero allows you to write in PHP, Java, or Groovy and make it all run on That wasn’t hard, was it? It’s light and fast. The Java the same application server. Even more impressively, Bridge works by running a background daemon that ProjectZero’s PHP can instantiate Java classes, use handles Java requests for PHP. Hence, you’re not creat- existing PECL code, and even allows you to write new ing extra server processes to handle your Java. There PHP extensions in Java instead of C. are also facilities available to handle Java exceptions Let me explain how all this works. The PHP inter- as PHP exceptions, include classpaths, and reload jars preter running on Project Zero, called P8, is not the at runtime. For more information on the details, check Zend Engine-based PHP interpreter we all know and the Zend Java Bridge API documentation. love. P8 was written from the ground up in Java. It As mentioned before, Zend Server is the most works differently than Zend Engine in that the engine common of our three methods for mixing Java and core and extensions are strongly separated. The XAPI-J PHP. You are still using the original open-source PHP system provides a way to write extensions in Java, and Licensed to 8203 - Rémy Dufour ([email protected])

Related URLs (Continued): • IBM DeveloperWorks article: http://www.ibm.com/developerworks/websphere/library/techarticles/0809_phillips/0809_phil- lips.html

• Marcel’s pre-built AppEngine application: http://dl.getdropbox.com/u/513928/phparch/quercus-appengine.tar.gz

• Quercus installation package: http://dl.getdropbox.com/u/513928/phparch/quercus-appengine.tar.gz

• Jetty JAR download: http://mirrors.ibiblio.org/pub/mirrors/maven2/org/mortbay/jetty/ jetty/6.1.16/jetty-6.1.16.jar

• Jetty util JAR download: http://mirrors.ibiblio.org/pub/mirrors/maven2/org/mortbay/jetty/ jetty-util/6.1.16/jetty-util-6.1.16.jar

www.phparch.com July 2009 | 25 FEATURE Future PHP, Future Java?

PECL extensions are compiled against a different C API, UNIX scripts in this example. Please note that you will called XAPI-C, written by the folks at IBM, that uses need to have the Java SE SDK or better installed and JNI (Java Native Interface) to bind to the Java-based properly pathed; version 5.0 or 6.0. Once you’re done PHP runtime, P8. with that, go to your home directory and create a new A major design goal with Project Zero lies in the Project Zero project as described in Listing 1. commercial product’s name - Websphere sMash. IBM You will see a message similar to “Module mashser- wants this to be the application server for apps that vice successfully created”. The above commands will mash up the web. By allowing you to mix languages, have created a directory ’zeroprojects’ in your home creating an environment that easily lets you consume folder, and therein a directory with a new Project Zero other services, IBM is hoping to address the Long Tail project called ’mashservice’. By default, Project Zero of business. The idea behind this was well-addressed in instances will run Java, but its pluggable architecture an article in the March 2009 edition of PHP|Architect, makes PHP support a simple matter of adding a de- “Enterprise PHP” by Ivo Jansch. What isn’t immedi- pendency. Open mashservice/config/ivy.xml with your ately obvious is how many things become ridiculously favorite text editor. This is an XML configuration file easy for us. To make it even sweeter, IBM has created and is really easy to work with. Just add the following two really cool development tools for us. There is an line to the ’dependencies’ element: plug-in for generating application skeletons and a browser-based tool called AppBuilder. This ar- ticle covers the plain-vanilla CLI way, but feel free to check out the tools at http://www.projectzero.org. Once you’ve done that, we need to resolve any de- We will try to do the same thing with Project Zero pendencies we’ve introduced with this configuration that we did with the Zend Java bridge. Let’s start by change. This is also done very easily: grabbing a copy of Project Zero. At the time of this writing, the most recent version could be found here: http://www.projectzero.org/sMash/1.1.x/download/. essers-macbook-pro:mashservice marcelesser$ zero resolve CWPZT0600I: Command resolve was successful Setup is easy. Unzip the file and add the zero directory to your path. There are batch files for Windows users If you have not yet created any Zero projects with and shell scripts for UNIX/Mac users. I will use the PHP support, Project Zero will automatically download Licensed to 8203 - Rémy Dufour ([email protected])

And now for something completely different The first monthly magazine dedicated exclusively to Python

PRINT & PDF (1 year /12 issues) $69.00 CAD* PDF only (1 year /12 issues) $59.00 CAD

For more info go to: http://www.pythonmagazine.com

* Other Countries: $89.99 CAD

26 | July 2009 www.phparch.com Future PHP, Future Java? FEATURE and install a number of packages for you. In this case, A small item worthy of note here; the ’PI’ property I’ve obviously created a few, so it does my bidding. of java.lang.Math is static, hence we use the JavaClass You will see several more lines of dialog as pack- constructor. The resulting object will allow your PHP ages are downloaded into your Project Zero instal- code to directly access those static properties. If you lation. Let’s test our new Zero project with a simple want an instance of a class instead, you would use the PHP page and see how it fares. PHP documents will go ’Java’ constructor, as such: into mashservice/public/, so let’s create a file called mashservice/public/info.php and use the content: $obj = new Java("SomeObject","parameter1", 2);

"

\n"; print_r(get_loaded_extensions()); print_r(phpversion()); print_r(zend_version()); Run your PHP better with echo "\n
\n"; phpinfo(); a Java stack. This will report back details of our PHP configura- tion. Now, just one last thing: Project Zero - like many " standalone Java development servers - runs in a Jetty server container. So let’s fire up our development In the above example, all constructor parameters server: after the class name are passed as parameters to the class constructor for the Java-language class. In other essers-macbook-pro:mashservice marcelesser$ zero start Application started and servicing requests at words, the above code would instantiate the Java class http://localhost:8080/ as: CWPZT0600I: Command start was successful // A normal java class definition Our development server is now running on localhost, public class SomeObject { at port 8080, so let’s punch this address into our web // a non-static method member public SomeObject(String firstParam, int secondParam) browser: http://localhost:8080/info.php { You should note that - just like APC or Xcache - // do something here } Project Zero compiles to an opcode cache. This takes } a bit longer with Project Zero, so don’t be discour- aged if that’s the case. You should finally end up with As you can see, the Zend Java Bridge and P8 Java in- some interesting information about loaded extensions, terfaces are very similar - but not identical. If you are the current “Zend” version, and a normal phpinfo() interacting with basic classes, you can probably make dump. We have PHP running on a . your code run on either, but I recommend you pick a Cool, huh? This phpinfo() screen will look a lot like the one you are used to, but we’re obviously run- Licensed to 8203 - Rémy Dufour ([email protected]) ning on an entirely different platform, so some of the LISTING 1 details will look different. To demonstrate the level 1. essers-macbook-pro:~ marcelesser$ mkdir zeroprojects of cool at work here, let’s get right down to busi- 2. essers-macbook-pro:~ marcelesser$ cd zeroprojects/ 3. essers-macbook-pro:zeroprojects marcelesser$ zero create mashservice ness and write a small JSON web service that multi- 4. CWPZT0849I: The new module will be created from module 5. zero:zero.application.template:, located at /Users/marcelesser/ plies Pi by any value you want - using the definition 6. zero/zero-repository/stable/modules/zero/zero.application.template/ of Pi from java.lang.Math. We’ll create a file called 7. zips/zero.application.template-1.1.0.2.29269.zip 8. CWPZT0840I: Module mashservice successfully created mashservice/public/service.php with the contents: 9. essers-macbook-pro:zeroprojects marcelesser$ cd mashservice/ 10. essers-macbook-pro:mashservice marcelesser$ ls -lah 11. total 16 // Get our multiplier parameter 12. drwxr-xr-x 14 marcelesser staff 476B Apr 16 01:28 . $value = isset($_GET['times']) ? intval($_GET) : 1; 13. drwxr-xr-x 3 marcelesser staff 102B Apr 16 01:28 .. // grab a reference to the java.lang.Math class 14. drwxr-xr-x 2 marcelesser staff 68B Apr 16 01:28 .cache $math = new JavaClass("java.lang.Math"); 15. -rw-r--r-- 1 marcelesser staff 671B Apr 16 01:28 .classpath // grab the class's static property 'PI' 16. -rw-r--r-- 1 marcelesser staff 461B Apr 16 01:28 .project 1 7. drwxr-xr-x 4 marcelesser staff 136B Apr 16 01:28 .zero $pi = $math->PI; 18. drwxr-xr-x 2 marcelesser staff 68B Apr 16 01:28 META-INF // create out result array 19. drwxr-xr-x 6 marcelesser staff 204B Apr 16 01:28 app $result = 20. drwxr-xr-x 2 marcelesser staff 68B Apr 16 01:28 classes array('times'=>$value,'pi'=>$pi,'result'=>$pi * 21. drwxr-xr-x 5 marcelesser staff 170B Apr 16 01:28 config $value); 22. drwxr-xr-x 2 marcelesser staff 68B Apr 16 01:28 java 23. drwxr-xr-x 2 marcelesser staff 68B Apr 16 01:28 lib // echo it back as JSON 24. drwxr-xr-x 2 marcelesser staff 68B Apr 16 01:28 public echo json_encode($result); 25. drwxr-xr-x 5 marcelesser staff 170B Apr 16 01:28 reports 26. Pretty nifty stuff, don’t you think?

www.phparch.com July 2009 | 27 FEATURE Future PHP, Future Java?

horse and ride it, as per your business requirements, AppEngine application for you that is ready to roll, and don’t play the spread. just like the one we will build in this article. So, if Looking at the above code, you might wonder what you’d like to cheat, go ahead and grab it from the link is limiting us to just Java-language classes. The an- in Related URLs - I won’t tell anyone, I promise. swer is nothing. Another popular language to use on At the time of this writing, the current release of sMash is Groovy. However, you can really use anything Quercus is 3.2.1. I would like to have used this release, that compiles to or can run through but I couldn’t get it running properly due to some a Java-based interpreter - like Jython. This gives us permission issues. I’m sure people smarter than I can huge potential to use existing enterprise-level Java figure it out. Being the wimp that I am, I just grabbed infrastructure in our applications. If you want to know the slightly older 3.1.6 release and experienced no is- more details, visit the excellent IBM DeveloperWorks sues. article listed in Related URLs. Among other things, it explains an example integration with Apache Lucene via Websphere’s Java bridge for PHP. These tools are awesome and a lot of develop- Mash up the web; put ers will find them useful. However, not everyone has your PHP on a JVM. the luxury of deploying their own Websphere sMash system. So, let’s focus on something more practical to low-budget development; let’s get our PHP running on " Google AppEngine! It should be noted that in running various tests for writing this article, sMash has a benefit over Quercus PHP on Google AppEngine with Quercus for compatibility due simply to the fact that the Since Google recently added Java support for Caucho folks are writing everything from scratch in AppEngine, it’s actually not hard to get PHP running Java. Quercus can’t use php.net’s PECL extensions, and on Google’s cloud. Project Zero would be a pretty big it doesn’t want to. As a consequence, IBM leapfrogged bite for Google AppEngine to swallow right now, due their efforts. I would definitely recommend using to all manner of dependencies and system resource sMash over Caucho, but our goal here is to get PHP permissions. To accomplish our goal of running PHP on running on AppEngine, so let’s focus on that. AppEngine, we will introduce our third PHP implemen- The first thing we need to do is grab the Google tation: Quercus & Resin, by Caucho. Quercus is also a AppEngine SDK for Java from http://code.google.com/ ground-up implementation of PHP in Java, meaning it appengine/downloads.html. For the purposes of this ar- shares no code with the PHP Project. ticle, I will assume that you can set up the application Before you continue, please note that setting up without guidance. If you already have a JDK installed, Quercus on AppEngine is not for the faint of heart. most of the time, this just means adding the shell If you want to skip all this, I won’t blame you. As a scripts in this package to your path. The next thing Licensed to 8203 - Rémy Dufour ([email protected]) matter of fact, I’ll even help you. I’ve pre-built an we need is a Google AppEngine project. For the sake

LISTING 2 LISTING 3

1. 1. essers-macbook-pro:war marcelesser$ find . 2. 4. ./WEB-INF 5. 5. ./WEB-INF/appengine-web.xml 6. 6. ./WEB-INF/classes 7. Quercus Servlet 7. ./WEB-INF/classes/META-INF 8. com.caucho.quercus.servlet.QuercusServlet 8. ./WEB-INF/classes/META-INF/jdoconfig.xml 9. 9. ./WEB-INF/lib 10. 10. ./WEB-INF/lib/appengine-api-1.0-sdk-1.2.0.j a r 11. ini-file 11. ./WEB-INF/lib/datanucleus-appengine-1.0.0.f inal.jar 12. WEB-INF/php.ini 12. ./WEB-INF/lib/datanucleus-core-1.1.0.j a r 13. 13. ./WEB-INF/lib/datanucleus-jpa-1.1.0.j a r 14. 14. ./WEB-INF/lib/geronimo-jpa_3.0_spec-1.1.1.j a r 15. 15. ./WEB-INF/lib/geronimo-jta_1.1_spec-1.1.1.j a r 16. Quercus Servlet 16. ./WEB-INF/lib/jdo2-api-2.3-SNAPSHOT.jar 1 7. *.php 1 7. ./WEB-INF/lib/jetty-6.1.16.j a r 18. 18. ./WEB-INF/lib/jetty-util-6.1.16.j a r 19. 19. ./WEB-INF/lib/quercus.jar 20. index.php 20. ./WEB-INF/lib/resin-util.jar 21. 21. ./WEB-INF/logging.properties 22. 22. ./WEB-INF/web.xml

28 | July 2009 www.phparch.com Future PHP, Future Java? FEATURE of expedience, I used the Guestbook sample applica- tion and modified it. I will outline the basic process Our directory tree should now look something for installing Quercus in this application, but I’ve also like listing 3. I will assume you have proper- provided a ready-to-go package for the sake of conve- ly setup the AppEngine Java SDK, so you should nience (see Related URLs). be able to now start the application by running To modify the Guestbook project as a container for dev_appserver.sh . You should get out- our PHP scripts, first grab the guestbook directory put similar to this: from the demos directory in your AppEngine zip file essers-macbook-pro:war marcelesser$ dev_appserver.sh . and make a working copy of it somewhere that you 2009-04-19 21:49:15.229 java[67662:80f] like. We are interested only in the ’war’ directory of [Java CocoaComponent compatibility mode]: Enabled 2009-04-19 21:49:15.230 java[67662:80f] guestbook, since we will have no Java sources to work [Java CocoaComponent compatibility mode]: Setting with in this example. The next thing we need to do is timeout for SWT to 0.100000 grab the Quercus application from Caucho’s website. As The server is running at http://localhost:8080/ mentioned before, we are using version 3.1.6, and it is Navigating to http://localhost:8080/, you should distributed as a .war file. For those of you unfamiliar, now be greeted with a completely unfamiliar and a .war file is like a PHP phar file, and it’s just a zip of a strange phpinfo() page, that looks nothing like what Java application directory. At the time of this writing, we are used to. If you undergo one last step and edit the appropriate file can be had for download at h t t p:// WEB-INF/appengine-web.xml, changing the value of quercus.caucho.com/download/quercus-3.1.6.war. the application and version tags, you should have Google AppEngine’s development server uses Jetty an application that you can cleanly deploy to Google as an application container, and we’ll need a couple AppEngine. Congratulations; you are running PHP on of JAR files to make it not complain when we run Google AppEngine! Quercus. We will need a late version of the Jetty JAR, and a late version of the Jetty util JAR to satisfy a couple of dependencies. I used version 6.1.16 (see Summary Related URLs for download links), and slightly younger This article presents three different implementations versions should also work just fine. of PHP from three different sources. All three of them Right now, AppEngine doesn’t support war archives, can run Java, but all have different strengths. This is and we need to set up a custom app structure anyway, the essential point: having multiple implementations so let’s unzip the Quercus war file. If you are hav- of the same language is a good thing. More than that, ing trouble with this, change the file extension to it’s the future of PHP, and it’s a future where we pick a zip, and everything should be well. We need two files language based on the task at hand - not because our from the war archive: WEB-INF/lib/quercus.jar and stack dictates it. I don’t know about you, but person- WEB-INF/lib/resin-util.jar. Place these two in our ally, I can’t wait to work that way. AppEngine application’s WEB-INF/lib/ folder. Next, take the two Jetty dependencies we down- Licensed to 8203 - Rémy Dufour ([email protected]) loaded - jetty-6.1.16.jar and jetty-util-6.1.16.jar - and also place them in our AppEngine application’s WEB-INF folder. This will now take care of all our de- pendencies. Now, we just need one more configuration step. Open AppEngine application’s WEB-INF/web.xml, and replace its content with the leaner, meaner con- tents in Listing 2. Since we are not using any of the guestbook components, we will remove their servlet definitions. Finally, let’s get rid of all the remaining files from the guestbook example, and create a test php file. Remove the WEB-INF/classes/guestbook directory, as well as guestbook.jsp and the stylesheets directory. Create an index.php file in the war file directory with Marcel Esser works as a system architect and senior the following code: developer at CROSCON. He’s kind of weird, a little opinionated, and has no love for bacon. His favorite topic

www.phparch.com July 2009 | 29 FEATURE Extending

eshop with Custom Modules

by Vikram Vaswani

OXID eShop CE is a GPL shopping cart system which allows extensive customization to its interface and functionality. This customization is possible via a well-defined, extensible module API which allows developers to easily extend the system’s base objects and deliver additional functionality through a simple and intuitive framework.

This article will take an in-depth look at custom module development with OXID REQUIREMENTS eShop, making use of common tools and libraries like Smarty, PEAR and jQuery. PHP: 5.2+ with GD, MySQL, JSON, iconv and XML extensions

o be successful in today’s crowded e-marketplace, Other Software: an online store must possess not only a distinct • OXID eShop CE 4.0.0.2 brand, but also unique features and functions • Apache 1.3+ with mod_rewrite T • MySQL 5.0.33+ that reflect the needs of its users. To illustrate, consider Amazon.com, a very successful online retailer • PEAR Services_ExchangeRates 0.6.0 whose popularity stems as much from its strong brand as from its easy-to-use features, such as one-click Related URLs: shopping, personalized recommendations and user rat- • OXID eShop CE: http://www.oxid-esales.com Licensed to 8203 - Rémy Dufour ([email protected]) ings. It’s these one-of-a-kind shopping aids that make • OXID eShop tutorials: http://www.sitepoint.com/ it so hard to replicate the Amazon.com experience on article/six-steps-start-selling-online/ any other Web site and which account for the incred- • OXID eShop API documentation: http://www.oxid- ible loyalty it inspires among its users. esales.com/en/resources/help-faq/manual- eshop-pe-ce-4-0-0-0/api-documentation Now, if you’re developing an online store, there’s a • Smarty: http://www.smarty.net lesson in this for you. When you’re trying to decide the • Smarty documentation: shopping cart system on which to build your store, the http://www.smarty.net/docs.php one problem you won’t have is a shortage of choices. • jQuery: h t t p://j q u e r y.c o m A wide array of options exists, each with its own bells • AJAX/jQuery examples: and whistles, and it’s not very difficult to find one http://docs.jquery.com/Ajax that suits your platform, your budget and your taste. • PEAR: http://pear.php.net However, while these are important factors, it’s equally • PEAR Services_ExchangeRates: important to consider how easy it will be to custom- http://pear.php.net/Services_ExchangeRates • Doxygen: http://www.doxygen.org ize and extend the system to support new functions. • Cyclomatic complexity: http://en.wikipedia.org/ After all, the more extensible the system is, the easier wiki/Cyclomatic_complexity it will be for you to program new features and trans- form the e-shopping experience to keep pace with your customers’ changing trends and habits.

30 | July 2009 www.phparch.com Extending OXID eShop with Custom Modules FEATURE

This article will introduce you to one such shopping and this figure is continuously rising. Higher cart system, OXID eShop, and show you how easy it coverage means fewer bugs, because it implies is to extend the base system to support new features that fewer sections of the code tree remain with the API. It’s exciting stuff that opens up a whole untested. new world of possibilities...so hold on to your hats, and let’s get started! • Documented source code and API: OXID source code is extensively commented (h t t p://s v n. with Custom Modules oxid-esales.com), making it easy for devel- Tech Talk opers to understand how it works and to If this is the first time you’re hearing about OXID make changes for their specific requirements. eShop, a quick introduction is in order. Look at the Also, between 2007 and 2009, the code was official Web site at http://www.oxid-esales.com, and refactored and is now significantly simpler you’ll see that OXID eShop is a robust, standards- to understand, with lower overall cyclomatic compliant, PHP/MySQL-based shopping cart system complexity. Code comments are Doxygen- that supports all the traditional functions like product compliant, and an auto-generated archive of catalog management; inventory tracking; integrated API documentation is available online. payment, shipment and order processing methods; and multiple-unit pricing and discount pricing. It is suit- • SEO URLs: OXID eShop is engineered for maxi- able for use in both small and large online businesses, mum search-engine friendliness. All shop URLs has full UTF-8 support (v4.1.0+), and is extremely easy are SEO-compliant, and rendered pages also to install, configure and deploy, as all shop adminis- contain the necessary meta-information for tration functions can be managed through a standard efficient search engine spidering. Web browser. Technically, OXID eShop is also noteworthy for the OXID eShop is currently available in three differ- following reasons: ent versions: the Enterprise Edition, the Professional Edition and the Community Edition. The Enterprise and • Object-oriented architecture: OXID eShop is Professional Editions can be purchased under a com- written in accordance with object-oriented mercial license and are intended for businesses that programming (OOP) principles and takes ad- require access to OXID’s technical support and con- vantage of all the new OOP features in PHP5. sultancy services. The version used in this article is As you’ll shortly see, this modular, object- the Community Edition (v4.0.0.2), which has recently based approach is one of the reasons why it’s been released under the GNU General Public License so easy to upgrade OXID eShop with addition- (GPL) and can be downloaded free of charge. Needless al functionality. to say, in these recessionary times, a professional and customizable shopping cart solution that supports a • View-controller separation: The OXID codebase do-it-yourself approach is actually worth quite a lot! maintains a clear separation between presen- The remainder of this article will assume that you Licensed to 8203 - Rémy Dufour ([email protected]) tation and business logic and uses the Smarty have a functional OXID eShop CE installation and that template engine as the glue to bind both you’re familiar with the basics of PHP5 objects, Smarty together. This separation of ’visual interface’ templates, and AJAX/jQuery programming. For detailed from ’program logic’ has two key benefits: it instructions on how to install and configure a basic allows developers and interface designers to OXID eShop CE article, as well as some links to Smarty work on the codebase independently, without and AJAX/jQuery tutorials, look in the “Related URLs” stepping on each other’s toes, and it makes it section of this article. extremely easy to customize the appearance of your online store, either by adjusting exist- The Ratings Game ing page templates or by creating new ones. OXID eShop CE allows shop managers to define prod- • High unit test coverage: OXID development uct categories and assign individual products to these takes place in accordance with agile method- categories. Shop users can then select a particular ologies, with heavy reliance on unit testing. category to view a summary listing of all the products Recent statistics released by OXID (h t t p:// in that category (Figure 1). Selecting a product takes www.oxid-esales.com/en/news/blog/oxid-es- the user to the product details page, which contains hop-quality-report) reveal that between 88- detailed information, user reviews and product images 91% of the codebase is covered by unit tests, (Figure 2).

www.phparch.com July 2009 | 31 FEATURE Extending OXID eShop with Custom Modules

FIGURE 1 Now, although OXID eShop CE al- lows users to rate individual products on a five-point scale, these ratings are only available on the individual product page. However, it would be nice to also present this information, in summary form for each product, at the category level. With OXID’s modular system, ex- tending the product listing to include this information is not very difficult, and it also makes a good example to start with. The first step is to extend the base product details class, oxArticle, and create a new class, extArticle, that adds two new methods:

• extArticle::getNumReviews(), which returns the total number of user reviews for the product. This FIGURE 2 method simply builds on a pre- existing method, getReviews(), and counts the number of objects returned by that method.

• extArticle:: getNumRatings(), which returns the total number of ratings for the product. This method executes a SELECT COUNT(*) query on the oxratings table to retrieve the number of ratings for a product.

Listing 1 has the complete class defini- tion. Save this class definition to $OXID/ modules/extarticle/extarticle.php,

where $OXID refers to your OXID installa- Licensed to 8203 - Rémy Dufour ([email protected]) tion directory. Note that the class name and the class file name must be the same (though not necessarily the same case), or else OXID will be unable to lo- FIGURE 3 cate and load the class at run-time. You’ll notice, from Listing 1, that although the new class extArticle should extend the base class oxArticle, it instead extends a class called extAr- ticle_parent. This is a standard approach that must be followed when creating new modules for OXID eShop. Behind the scenes, the *_parent class is automati- cally created by OXID to serve as a com- mon point that dynamically connects all modules using the same class. This makes it possible for a single class to be used by more than one module and is a

32 | July 2009 www.phparch.com Extending OXID eShop with Custom Modules FEATURE key aspect of OXID’s extensibility. LISTING 1

So, if this is the case, where do you actually link 1. class extArticle extends extArticle_parent { oxArticle with extArticle? It’s fairly simple: visit the 2. //get number of reviews 3. public function getNumReviews() { OXID eShop module administration page (Master 4. $oRevs = $this->getReviews(); 5. if (!is_null($oRevs)) { Settings -> Core Settings -> System -> Modules), 6. return count($oRevs); and add the following entry telling the system about 7. } else { 8. return 0; the new class (Figure 3): 9. } 10. } 11. oxarticle => extarticle/extarticle 12. //get number of ratings 13. public function getNumRatings() { 14. $sql = "SELECT COUNT(*) FROM oxratings Roughly translated for beginners, this entry means, “at 15. WHERE oxobjectid = '" . $this->getId() ."'"; run-time, replace all instances of the oxArticle class 16. $count = (int) o x D b::getDb()->getOne($sql); 1 7. return $count; with the extArticle class.” As a result of this entry, 18. } 19. } OXID will automatically scan the $OXID/modules direc- tory, load the class definition using the path speci- fied and use the new extArticle class (with its custom LISTING 2 methods) in place of the original oxArticle class as 1.

oxarticles__oxartnum->value}]" 3. [{/if}] class="product [{if $head}] head[{/if}] [{$size|default:''}] 4. [{$class|default:''}]"> 5. ... 6.
When you're trying to 8. decide the shopping 10. [{ oxmultilang ident="INC_PRODUCTITEM_MOREINFO2" }] 11. [{if $isfiltering }] 12. [{oxid_include_dynamic file="dyn/compare_links.tpl" cart system on which to 13. testid="_`$testid`" type="compare" 14. aid=$product->oxarticles__oxid->value anid=$altproduct build your store, the one 15. in_list=$product->blIsOnComparisonList " 16. page=$pageNavigation->actPage-1 problem you won't have is 1 7. text_to_id="INC_PRODUCTITEM_COMPARE2" 18. text_from_id="INC_PRODUCTITEM_REMOVEFROMCOMPARELIST2"}] 19. [{/if}] a shortage of choices. 20.
21. ... 22.

The next step is to modify the category list- LISTING 3 ing to hold the new information. This category 1.

oxarticles__oxartnum->value}]" listing view is generated by the template file 3. [{/if}] class="product [{if $head}] head[{/if}] [{$size|default:''}] $OXID/out/basic/tpl/list.tpl, which internally 4. [{$class|default:''}]" **style="height:200px"**> 5. ... Licensed to 8203 - Rémy Dufour ([email protected]) calls $OXID/out/basic/tpl/inc/product.tpl to build 6.
the listing for each individual product. Look inside 8. [{ oxmultilang 10. ident="INC_PRODUCTITEM_MOREINFO2" }] a standard Smarty template that mixes HTML markup 11. [{if $isfiltering }] 12. [{oxid_include_dynamic file="dyn/compare_links.tpl" with template variables. 13. testid="_`$testid`" type="compare" In this file, you’ll find the markup that generates 14. aid=$product->oxarticles__oxid->value 15. anid=$altproduct in_list=$product->blIsOnComparisonList the product image, title, action links and price. It will 16. page=$pageNavigation->actPage-1 1 7. text_to_id="INC_PRODUCTITEM_COMPARE2" look something like Listing 2. Modify this to include 18. text_from_id="INC_PRODUCTITEM_REMOVEFROMCOMPARELIST2"}] some additional information - the average product 19. [{/if}] 20. rating, the total number of user reviews, and the total 21.
22. number of ratings - as in Listing 3 (highlighted lines 23. [{ if $product->getArticleRatingAverage() }] indicate additions). 24. Avg. rating: [{ $product->getArticleRatingAverage() }] 25.
Now, visit the category listing page of your eShop, 26. [{ /if }] 2 7. Reviews: [{ $product->getNumReviews() }] and you should see the new information appearing 28.
next to each product listing (Figure 4). 29. Ratings: [{ $product->getNumRatings() }] 30.
The key lesson to take away from this relatively- 31.
32.
simple example? Because the new extArticle class 33. ... extends the base oxArticle class, the newly-added 34.

www.phparch.com July 2009 | 33 FEATURE Extending OXID eShop with Custom Modules

methods are automatically available in the view and Money, Money, Money can be accessed using the $product template vari- Let’s look at another, more complex example. Let’s able. So, displaying the number of user reviews in assume that you’d like to add a new feature to the the view is as simple as using the template variable product details page: a currency converter that allows [{ $product->getNumReviews() }] in the view markup. users to immediately convert the stated product price It’s this object-based architecture, which provides au- to a different currency. Again, OXID’s architecture tomatic support for inheritance and object extensibil- makes this a snap to implement...as you’ll see! ity, that makes OXID eShop so easy to customize. As this new feature is to appear on the product de-

You can have Services_ExchangeRates cache the public data it downloads, and thereby speed up " response time even more, by using it with the built-in 'HTTP_Cached' transport.

It’s worth noting, also, that OXID eShop comes with tails page, implementing it requires you to modify the multi-language support out of the box, and it’s ex- product details template. In the previous example, you tremely easy to support languages other than English did this by modifying one of the stock templates that when adding new features. To illustrate, revisit Listing ships with OXID. An alternative approach is to create 3, and replace the English-language strings in the view your own custom template, and instruct the view class template with variable names as below (highlighted to render this custom template instead of the default lines indicate modifications): one. This approach is described below. The first step is to copy the ex- [{ if $product->getArticleRatingAverage() }] [{ oxmultilang ident="EXTARTICLE_RATING_AVG" }]: isting template file, located at [{ $product->getArticleRatingAverage() }]
$OXID/out/basic/tpl/details.tpl, to a new file, which [{ /if }] [{ oxmultilang ident="EXTARTICLE_REVIEWS" }]: we’ll call $OXID/out/basic/tpl/extdetails.tpl. Then, [{ $product->getNumReviews() }]
extend the Details view class, and override the par- [{ oxmultilang ident="EXTARTICLE_RATINGS" }]: [{ $product->getNumRatings() }] ent’s render() method to reflect your new template:

Then, pop open the translation file for each language, located in Licensed to 8203 - Rémy Dufour ([email protected]) $OXID/out/basic/{lang}/cust_lang.php, and create entries for the new variables FIGURE 4 and their localized translations. For example, here’s what the additions to $OXID/out/basic/en/cust_lang.php look like:

'Avg. rating', 'EXTARTICLE_REVIEWS' => 'Reviews', 'EXTARTICLE_RATINGS' => 'Ratings' ); ?>

And now, depending on which language your users have chosen (and assuming a translation file exists for that language), text strings will appear in that language.

34 | July 2009 www.phparch.com Extending OXID eShop with Custom Modules FEATURE

require_once("views/oxubase.php"); class extDetails extends extDetails_parent { LISTING 4 1. 'Pound Sterling', 14. 'USD' => 'US Dollar', ule (Master Settings -> Core Settings -> System -> 15. 'INR' => 'Indian Rupee', 16. 'EUR' => 'Euro', Modules) and adding the following entry: 1 7. 'AU D' => 'Australian Dollar', 18. ); details => extdetails/extdetails 19. 20. //define exchange rates with reference to base USD, ie 1 USD = xx CUR 21. protected $_rates = array( As a result of these changes, OXID will now render your 22. 'GBP' => '0.692', 23. 'USD' => '1.0', custom template, instead of the default one, when dis- 24. 'INR' => '50.15', 25. 'EUR' => '0.732', playing product details. Since class and template files 26. 'AU D' => '1.454', are both user-created, they’ll remain unchanged even 2 7. ); 28. when you upgrade your eShop. 29. //get currency list 30. public function getCurrencyList() { Now that the foundation has been laid for modifica- 31. return $this->_currencies; tions to the product details page, the next step is to 32. } 33. add the necessary methods for currency conversion to 34. //convert price to target currency, set template variable with result 35. public function convertCurrency() { the extDetails class. If you think about it, you’ll real- 36. $from = trim((string) oxConfig::getParameter('from', true)); ize there are four basic pieces you need here: 3 7. $to = trim((string) oxConfig::getParameter('to', true)); 38. $amount = trim((string) oxConfig::getParameter('a m ount', true)); 39. if (!empty($from) && !empty($to) && !empty($amount)) { 40. $result = ($amount * ($this->_rates[$to] / • A list of currencies 41. $this->_rates[$from])); 42. $this->_aViewData["convCurrResult"] = "$from $amount = 43. $to " . round($result, 2); • A list of currency exchange rates 44. } 45. } 46. } • A common or “base” currency to use for con- 47. ?> version

• A method which performs the calculations necessary to convert from source currency to LISTING 5 base to target currency 1. 2. [{ oxmultilang ident="EXTDETAILS_CURR_CONV" }] Licensed to 8203 - Rémy Dufour ([email protected]) Listing 4 updates the extDetails class defini- 3.

4. [{ oxmultilang ident="EXTDETAILS_DISPLAY_PRICE_IN"}] tion from above to incorporate these items. 5.
6. [{ $oViewConf->getHiddenSid() }] The convertCurrency() method uses the 7. [{ $oViewConf->getNavFormParams() }] oxConfig::getParameter() method to read the source 8. currency, target currency and amount from a form 10. submission, performs the necessary conversion, and 12. assigns the result to the $this->_aViewData associative 13. 14. 16. engine. The getCurrencies() method is a simple “get- 1 7.
ter method” to retrieve the list of available currencies; 18. This takes care of the back-end business logic, but 23. there’s still the front-end to consider. This can be kept 25.
26. [{ $convCurrResult }] simple for the moment: a form containing a drop-down 2 7.
list of target currency values. Listing 5 reveals the ad- dition to the extdetails.tpl view, and Figure 5 shows an example of the resulting page markup. There are a few noteworthy points about Listing 5:

www.phparch.com July 2009 | 35 FEATURE Extending OXID eShop with Custom Modules

and $_POST[’fnc’] variables, which tell OXID • The $oView and $oViewConf template variables which class and method should be used to are set by the oxView class, which is the base process the form submission - in this case, class for all views. These variables are avail- the extDetails::convertCurrency() method able in all templates. The extDetails, Details defined in Listing 4. and oxUBase classes are ultimately all children • The source currency and amount are also sub- of the oxView class (Figure 6 illustrates the in- mitted as hidden variables, with their values heritance tree), and this relationship is what retrieved through built-in class methods and makes it possible to access the getCurren- properties. The property $currency->name cies() method in the view using the notation contains the three-letter code for the cur- $oView->getCurrencies(). rency in use, while the $product->getFPrice() • The $oViewConf->getHiddenSid() and method returns the product price in that cur- $oViewConf->getNavFormParams() methods rency. These hidden variables are submitted take care of passing onward hidden session as $_POST[’from’] and $_POST[’amount’]. and navigation parameters so that the user • The target currency is selected by the user doesn’t end up on a different page after sub- from the drop-down list, and submitted mitting the form. as $_POST[’to’]. All these form variables can be retrieved in the back-end using the • A number of hidden variables are passed to oxConfig::getParameter() method. the form controller from the front-end. Of particular importance are the $_POST[’cl’]

LISTING 6 LISTING 7

1. require_once("views/oxubase.php"); 1. 2. class extDetails extends extDetails_parent { 2. [{ oxmultilang ident="EXTDETAILS_CURR_CONV"}] 3. 3. 5. function render() { 5. 21. $this->_service->fetch($this->_rateProvider, 21.

22. $this->_currencyProvider); 22. [{ oxmultilang ident="EXTDETAILS_DISPLAY_PRICE_IN"}] 23. } 23.
25. //return list of currencies 25. [{ $oViewConf->getHiddenSid() }] 26. public function getCurrencyList() { 26. [{ $oViewConf->getNavFormParams() }] 2 7. return $this->_service->validCurrencies; 2 7. 29. 29. 31. public function convertCurrency($from, $to, $amount) { 31. 32. return $this->_service->convert($from, $to, $amount); 32. 33. } 33. 35. //AJAX-only function return result and stop processing 35. 36. //to avoid sending rest of template in AJAX response 36.
3 7. public function convertCurrencyAJAX() { 3 7. 42. echo "$from $amount = $to $result"; 42. 44. } 44.
45. } 45. 46.
47.

36 | July 2009 www.phparch.com Extending OXID eShop with Custom Modules FEATURE

• The result of the currency conver- update them on a daily basis; however, leaving them sion at the back-end is stored as as is will result in inaccurate calculations. $this->_aViewData[’convCurrResult’]. 2. After submitting the currency conversion form, The elements of the $this->_aViewData the user has to wait for the entire page to be re-ren- array are automatically converted into dered in order to view the result. This “waiting time” template variables when the render() degrades the overall shopping experience. method is invoked. The value stored in It’s possible to solve both these problems by making $this->_aViewData[’convCurrResult’] is thus some modifications to the extDetails class: easily interpolated into the details page after 1. The first problem can be solved by dynamically the form is submitted, via the template vari- retrieving currencies and exchange rates from public able $convCurrResult. online sources at run-time. There’s actually a PEAR package, the Services_ExchangeRates package (h t t p:// • The labels for the section header and the form pear.php.net/package/Services_ExchangeRates), which submit button are a localized string, gener- can do this for you with minimal fuss, and it’s ex- ated from the appropriate language file. The tremely easy to integrate this with OXID’s object-ori- entries for the English language file would ented architecture. look something like this: 2. Using AJAX to asynchronously update a sec- 'Check price in your own currency.', 'EXTDETAILS_CURR_CONV' => 'Currency Converter', FIGURE 5 'EXTDETAILS_CURR_CONV_SUBMIT' => 'Convert!', ); ?>

Figures 7 and 8 show the currency converter in action on the product details page.

Going Live Now, while the currency converter developed in the previous example works very well, it does have two important problems: 1. The currency exchange rates are static; they’re defined at design-time and do not reflect daily move- ments. It is time- and resource-intensive to manually Licensed to 8203 - Rémy Dufour ([email protected])

Get your framework-based site up and running in no time. Hosting customized.

www.phparch.com July 2009 | 37 FEATURE Extending OXID eShop with Custom Modules

reloading the entire page, solves the second problem. With the package installed, the next step is to up- The OXID eShop CE distribution already includes the date the extDetails class to make use of it (Listing 6). jQuery library, so implementing an AJAX-based solu- The first thing that should leap out at you from this tion becomes even simpler. listing is the class constructor, which initializes a new The first step is to install the PEAR Services_ Services_ExchangeRates object and then uses the ob- ExchangeRates package. This package is maintained by ject’s fetch() method to retrieve a list of currencies Marshall Roch and Colin Ross and is available under a and exchange rates in XML format from public online PHP license (note: this license is incompatible with the sources. In this case, the two sources used are the GPL under which OXID eShop CE is released). The easi- European Central Bank and United Nations Web sites, est way to install this package is with the automated however, the package also supports data retrieval PEAR installer. Drop to your shell prompt and invoke from the National Bank of Israel (’Rates_NBI’) and the this installer as below: National Bank of Poland (’Rates_NBP’). Converting between currencies also becomes much shell> pear install Services_ExchangeRates-0.6.0 simpler now because the Services_ExchangeRates In case the installer doesn’t automatically do it for package includes a convert() method which does all you, you’ll need to install the package dependencies the hard work for you. All you need to do is wrap this using the same method. method in your own convertCurrency() method, and you’re good to go! The list of available currencies is shell> pear install HTTP_Request now stored in a class member; the getCurrencyList() shell> pear install Cache_Lite method has been updated to use this. shell> pear install XML_Tree-2.0.0RC2 A word here about the convertCurrencyAJAX() Before proceeding, you should also confirm that your method, which is intended specifically for use with PEAR installation directory is part of your PHP in- AJAX requests. You might be wondering why this is clude_path. If not, you can add it through the php.ini needed - after all, there’s already a convertCurrency() configuration file, or at run-time using the ini_set() method which performs the same task. The reason function. is that OXID’s default behaviour is to respond to a Licensed to 8203 - Rémy Dufour ([email protected])

38 | July 2009 www.phparch.com Extending OXID eShop with Custom Modules FEATURE request by executing one or more controller methods FIGURE 7 and then transferring control to the render() method, which renders the complete view. However, AJAX is designed for asynchronous updates of existing pages and the response to an AJAX request should therefore only contain the output for the section that is to be updated. The convertCurrencyAJAX() method makes this possible, by sending back the result of currency conversion and then terminating script processing, so that the entire view is not re-sent to the client. The template must change as well. While the curren- cy conversion form remains largely intact, there’s a bit of JavaScript/jQuery programming needed to convert the form submission into an AJAX query and write the response back to the output page. A new JavaScript function, ajaxSubmitCurrencyForm(), is invoked when the form is submitted and uses jQuery to serialize the form input and submit it to the convertCurrencyAJAX() controller method. The response is then handled by FIGURE 8 the aptly-named handleResponse() method, which again uses jQuery to write the result back to the page. Listing 7 has the complete code, with the important changes highlighted, and Figure 9 shows it in action. You can have Services_ExchangeRates cache the public data it downloads, and thereby speed up re- sponse time even more, by using it with the built-in ’HTTP_Cached’ transport. As these examples illustrate, OXID’s intelligent design and object-based architecture make it easy to extend the system and bring in new features and func- tions, while simultaneously allowing easy upgrades of the base platform. If you get stuck, extensive API documentation, tutorials and community forums are there to help. So, regardless of whether you’re rolling a home-grown module or integrating third-party pack- ages into OXID, the sky’s the limit...all you need to do Licensed to 8203 - Rémy Dufour ([email protected]) is get out there and start playing! FIGURE 9

FIGURE 6

Vikram Vaswani is the founder and CEO of Melonfire, a consulting services firm with special expertise in open- source product development, deployment and marketing. He is the author of five books on PHP/MySQL/XML topics, including “MySQL: The Complete Reference” and “PHP: A Beginners Guide”, and has extensive experience deploying PHP applications in high-availability enterprise environments.

www.phparch.com July 2009 | 39 FEATURE Yii: Flex Your Flash

by Jeff Winesett

Adding a little Flash to applications is a common approach web developers take as they strive to meet today’s buzzword compliance. Maximizing interoperability and data exchange by leveraging web Services to allow application-to-application communication has revolutionized the Web business model. It’s hard to imagine the Internet today without the Web service APIs of, among many others, Google, Yahoo and Amazon. This article shows how to easily accomplish both of these modern Web development demands by using the extremely powerful and light-weight PHP framework, Yii.

he ability to deliver and manage rich media, such Web service?, SOAP?, Flex? as audio and video, is fast becoming a ubiquitous For our purposes, a “Web service” is a Web API Trequirement when building new applications or where the interface is described by a Web Services overhauling existing ones. Many developers are look- Description Language (WSDL) document. It is a means ing to Adobe’s Flash technology, built using the Flex to allow machine-to-machine interaction over the framework, because it can easily handle the delivery of Internet. The manner in which this interaction takes rich media as well as meet certain usability demands place is prescribed by the WSDL definitions using of Web applications that require more of a responsive SOAP-messages. SOAP is a communication protocol desktop application look-and-feel.

Another requirement of today’s Web applications is Licensed to 8203 - Rémy Dufour ([email protected]) that they be “mashup” and mobile-device friendly. The REQUIREMENTS ability for data exchange between different applica- tions on different platforms by exposing and consum- PHP: 5.1.0+, with the PHP SOAP extension enabled ing standard compliant APIs has clearly revolutionized Web applications. Web services, using XML and SOAP Other Software: over HTTP, have paved the way for this revolution and • Yii Framework 1.0.+ remain the standard method by which to expose and • Flex Builder 3 or the Flex Builder plug-in for consume data and services over the Web. Building pub- Eclipse lic API gateways into your application data and func- • Apache or another PHP-enabled Web server tionality will help increase its longevity by positioning it to play in the current mashup and mobile market. Related URLs: What does one have to do with the other? Well, I • Yii Framework: http://www.yiiframework.com want to show how easy it is to create SOAP-compliant • PHP SOAP manual: http://www.php.net/manual/en/soap.setup.php Web services using the Yii PHP application framework, • Adobe Flex 3 LiveDocs: and then use these services to drive the functionality http://livedocs.adobe.com/flex/3 of Flex-based Flash components embedded within the application.

40 | July 2009 www.phparch.com Yii: Flex Your Flash FEATURE specification that relies on XML for its message format What is Yii? and HTTP for message transmission. Basically, we will Yii (an acronym for ’Yes, it is’, pronounced “E”) is a be making requests of remote methods using an XML high-performance, component-based, Web application format that follows the SOAP standard and is sent over framework written in PHP 5. Yii makes it easier to cre- HTTP and receiving responses back from these remote ate and maintain Web applications and is one of the methods in the same SOAP-compliant XML format. fastest, most efficient PHP frameworks around. One Flex is an Adobe development framework that assists of the many reasons we will be using Yii is its great in the creation of rich internet Flash applications that support for building SOAP servers. One drawback to deploy consistently across all browsers. Flex has really using SOAP is the need to create the WSDL document. opened-up the Flash door to non-Flash developers. It Writing WSDL documents can be a little tricky and removes the need to work within the esoteric Flash difficult. Yii removes all the difficulty by building our movie “timeline” and allows traditional programmers WSDL documents for us based on simple PHP doc com- a more familiar environment in which to build applica- ments we place around the methods we want to add to tions. You use the ActionScript and our WSDL definitions. This makes turning a Yii applica- an XML-based markup language called MXML to build tion into a SOAP server a snap. Flex applications. The Demo Application Why SOAP? What About AMF or JSON? To explore these ideas, we will be building a simple Yii Web application. The application will contain a simple The popularity of SOAP is evident in that most all Flex application component that allows a user to login major Web Service APIs offer SOAP as a request and and display a list of all users of the application. This response format. However, it is certainly not the only almost trivial functionality will serve to accomplish message format to choose when writing something the following two main goals: to support machine-to-machine interaction. Action Message Format (AMF) is one that comes to mind, es- • Demonstrate how to setup a Yii application pecially when thinking about Flex/Flash applications as a SOAP-server and host publicly-accessible as the consumers of the services. JSON is another Web service methods available to be con- one gaining a lot of momentum. AMF was developed sumed by any SOAP-client. by Adobe and is a great choice if you know the only consumer will be an Abode Flash application. Both AMF • Demonstrate how to add Flex-based Flash and JSON do seem to be more efficient when dealing components to your Yii application by setting with large data packets, but the real power with AMF them up as SOAP-clients to consume these and JSON is the ability to pack/unpack the data di- services. rectly from/into native language objects before/after sending. In order to gain this benefit, both require the Setup Assumptions Licensed to 8203 - Rémy Dufour ([email protected]) existence of external libraries designed to encode and This article assumes a basic understanding of the Yii decode the serialized content. As XML has enjoyed a framework and references some of Yii’s features and concepts with little or no explanation. I will not be long life, all modern languages and development plat- covering how to create a new Yii app or how to cre- forms come complete with XML parsers and XML data ate controllers (or even what a “controller” is). If you manipulation tools. Sticking with XML/SOAP/HTTP as are brand new to Yii, I recommend first reading my the data communication structure and protocol will introductory article published in the March 2009 issue guarantee our public APIs the widest adoption avail- of this magazine. Also, the Yii blog tutorial located at able. Also, XML is very easy to read and understand http://www.yiiframework.com/doc/blog/ demonstrates, when dealing with an arbitrary public Web interface. with detailed examples, how to build an entire data- Many are following a trend to use the more efficient base-driven Yii blog application from the ground up. AMF or JSON message format for private communica- As we move forward, I am assuming you have cre- tions (non public-facing APIs) and XML/SOAP for the ated a new Yii application, preferably using the helpful service interface you want to expose to the general yiic command-line tool, in a Web-accessible directory, public. Hard to say what direction Web services will can access the application in your browser by visiting take in the future, but SOAP is still a very safe bet to http://localhost (or whatever vhost name you may reach the widest audience. have setup) and can view the content as in Figure 1. To speed things up, I also assume you have created a new

www.phparch.com July 2009 | 41 FEATURE Yii: Flex Your Flash

controller, called webservicesController, that we’ll use these two purposes to fulfill. If present in the service to house our Web service methods. request, the requested method will be invoked. If ab- Also, the Web service implementation in Yii relies on sent, the WSDL content will display. Let’s call our ac- the PHP SOAP extension. Make sure you have enabled tion ’demo’, and define it by adding the following code it before trying these examples. to the WebservicesController class:

public function actions() { Define A Web Service Action return array( 'demo'=>array( Actions within Yii define the public API for the direct 'class'=>'CWebServiceAction') ); requests made of controllers. These can take the sim- } ple form of methods coded directly in the class with names that begin with the word ’action’. They can also After saving your work, you should be able to view the be defined in a more advanced way by using external very simple WSDL content in the browser by requesting classes to define the functionality and have the con- the demo action of the WebservicesController: h t t p:// troller load the class when that action is requested. localhost/index.php?r=webservices/demo. As we are This allows you to reuse actions across different con- only going to use this controller to house our Web trollers and promotes a more modular design model. services API methods, we can make ’demo’ the default This is the way we define our Web service action. action for this controller. To do so, simply add the We define this action by overriding the CController following public property definition at the top of this (i.e. the parent class of our webservicesController controller class: class) actions() method and return an action of type CWebServiceAction. CWebServiceAction is the base Yii public $defaultAction='demo'; framework class used to create actions that provide After setting ’demo’ as the default action, you should Web services. This action serves two purposes: be able to access the WSDL content without the /demo at the end of the above URL. • It is used to automatically create and display our WSDL content that defines our Web ser- vice APIs. Adding Our Web Service Methods Now, we have a controller with a Web services-enabled • It is used to invoke the requested Web service action defined, but what can we do with it? Not much. methods. We have not defined any functionality in our API. So, let’s define some. Let’s expose login functionality so CWebServiceAction uses the presence of a GET param- we can authenticate the users of our services. eter named ’ws’ in the request to determine which of By default, the ’demo’ action will look in the con- troller in which it was defined for methods to expose

LISTING 1 in the Web service API. It does this by examining Licensed to 8203 - Rémy Dufour ([email protected])

1. /** the doc comments on public methods defined in the 2. * @param string username controller. The ’@soap’ tag is used to denote a method 3. * @param string password 4. * @return boolean whether login is valid you want to be part of the public API. Along with the 5. * @soap 6. */ 7. public function login($username,$password) 8. { LISTING 3 9. $identity=new UserIdentity($username,$password); 10. if($identity->authenticate()) 1. /** 11. Yii::app()->user->login($identity); 2. * This action serves as a test SOAP client to verify our Web service 12. return $identity->isAuthenticated; 3. * login method. 13. } 4. */ 5. public function actionTestLogin() 6. { 7. //use the Yii application to build the proper request url format 8. $wsdlUrl=Y ii::app()->request->hostInfo.$this->createUrl('dem o'); LISTING 2 9. 10. //create the php soap client passing in a parameter to instruct 1. ... 11. //it not to cache the wsdl for testing purposes 2. 12. $client=new SoapClient($wsdlUrl, array('cache_wsdl' => 0)); 3. 13. 4. 14. echo "

"; 5.  15. echo "lo g in...\n"; 6. - 16. if($client->login('admin','admin')) echo "Successful Login!"; 7.  1 7. else echo "Sorry, the username and/or password supplied is invalid."; 8.  18. echo "
"; 9. 19. } 10. ...

42 | July 2009 www.phparch.com Yii: Flex Your Flash FEATURE

’@soap’ tag, the type and name of every input param- care of mapping our data types we defined in our ph- eter and the type of the return value must also be pdoc comments to the appropriate xsd specification. declared using the standard phpdoc format. Following However, input and output types can take on a variety these rules, let’s add a login method and declare it so of type declarations. The primitive type declarations it becomes part of our Web service API. Below the ’ac- recognized are: tions()’ method, add the code from Listing 1. • str/string: maps to xsd:string We’ve defined a login method with two string input • int/integer: maps to xsd:int parameters, username and password, and are returning • float/double: maps to xsd:float a Boolean type to indicate whether or not the login • bool/boolean: maps to xsd:boolean was a success. We’ve also defined it with the ’@soap’ • date: maps to xsd:date tag, which allows it to be remotely called. If you save • time: maps to xsd:time your file and view the WSDL again in your browser, you • datetime: maps to xsd:dateTime will notice a lot more content in the WSDL definition. • array: maps to xsd:string Listing 2 highlights the loginRequest and loginRe- • object: maps to xsd:struct sponse that was added to our WSDL content. By com- • mixed: maps to xsd:anyType paring the contents of Listings 1 and 2, it is easy to see the relationship between the phpdoc comments One thing you might notice is that types such as date, above the login method and the generated WSDL con- time and datetime are not primitive data types in PHP. tent. The WSDL generator does assume you know enough about the xsd specifications to keep your PHP data in Test It Out the right format to conform to these specifications. In order to test this out, we need a SOAP client to So, for example, if you declare a return type of ’date’ make the request and consume the response. The in your phpdoc comment, but in fact, return something fastest approach for this is to create one right in the that does not conform to the xsd:date specification, same controller. We’ll create a simple test action, you will end up with an invalid data-to-type map- actionTestLogin(), and explicitly create a SOAP client ping in the WSDL. Please refer to http://www.w3.org/ to make the login request and display the results in TR/xmlschema-2/ for more information on these type the browser. Below the login method, add the code in specifications. Listing 3. If the type defined is not one of these primitive Save your file, and point your browser to re- types, the Yii WSDL generator will assume it is a class quest this test action: http://localhost/index. type and will look within the class for its public prop- php?r=webservices/testLogin. erty declarations. The WSDL generator also recog- You should see the “Successful Login!” result in nizes the array type by appending [] to the end of a your browser. This was successful using the username/ primitive or class type. This would specify an array password combination of ’demo’/’demo’ because we are of the specified type. For example, specifying a type Licensed to 8203 - Rémy Dufour ([email protected]) using the same authentication logic that was auto- of string[] will be interpreted as an array of string generated for us when we asked the yiic command to build our skeleton application (You remembered to use the yiic tool to build the initial demo application, FIGURE 1 right?). Just as with the login form that came with the skeleton application, we expect to be able to success- fully log in using demo/demo or admin/admin. Other combinations should fail. You can convince yourself it is working as expected by changing the username/ password input parameters when calling the login method.

Primitive Types And Custom Composite Types We have kept things very simple in our API definition so far, defining only two input string parameters and a simple Boolean return type. Yii’s WSDL creator took

www.phparch.com July 2009 | 43 FEATURE Yii: Flex Your Flash

LISTING 4 values. Specifying a type of SomeCustomClass[] will

1. class AppUser be interpreted as an array of objects of type class 2. { SomeCustomClass. 3. /** 4. * @var integer ID of this user Let’s add a second service method to demonstrate 5. * @soap 6. */ returning an array of objects of a custom class type. 7. public $id; Let’s create a custom AppUser class that will repre- 8. 9. /** sent a user of our application. Create a new file under 10. * @var string username 11. * @soap /protected/models/ called AppUser.php and add the 12. */ code from Listing 4. Even though this does not pull 13. public $username; 14. data from a database, we’ll add it to the models di- 15. /** 16. * @var string email rectory because in a normal application, such a class 1 7. * @soap would probably play the role of a Model in the MCV Yii 18. */ 19. public $email; architecture. Also, because our Yii application came 20. } pre-configured to load classes from within this direc- tory, we don’t have to make any further config changes for this class to be loaded when requested. With our AppUser class in place, we now need to LISTING 5 set the classMap property on the CWebServiceAction 1. /** class. The classMap option is used to map WSDL types 2. * Returns all application users as an array of AppUser objects. 3. * @return AppUser[] an array of AppUser objects to/from PHP classes. This is a requirement of the 4. * @soap PHP SOAP extension Yii is leveraging in order to cor- 5. */ 6. public function getUsers() rectly format our custom type in the SOAP response. 7. { 8. return $this->createArrayOfUsers(); Basically, this allows our response to have an array of 9. } custom items typed in the SOAP envelope as: 10. 11. /** 12. * private helper method to create and return an array of AppUser objects 13. */ 14. private function createArrayOfUsers() where the ns1 namespace is our WSDL 15. { 16. $fakeUsers = array('Jim', 'Bob', 'Suzy', 'Nancy'); (WebservicesControllerwsdl) definition. If we do not 1 7. $users = array(); include this mapping, the PHP5 SOAP extension will 18. foreach ($fakeUsers as $key => $value) { 19. $users[] = $this->createAppUser($key+1, $value, $value.'@ wrap our array items in a SOAP-ENC:Struct type like someaddress.com'); 20. } this: 21. return $users; 22. 23. } 24. 25. /** which might cause an error in the SOAP client when 26. * private helper method to create an AppUser object based on input parameters trying to consume these custom types. Licensed to 8203 - Rémy Dufour ([email protected]) 2 7. */ 28. private function createAppUser($id, $name, $email) We set this property by simply adding another 29. { name=>value pair to the demo array defined in the ac- 30. $appuser = new AppUser(); 31. $appuser->id = $id; tions() method: 32. $appuser->username = $name; 33. $appuser->email = $email; 34. return $appuser; return array( 35. } 'demo'=>array( 36. 'class'=>'CWebServiceAction', 3 7. /** 'classMap'=>array( 38. * This action serves as a test SOAP client to verify our Web service getUsers method. 'AppUser', 39. */ ), 40. public function actionTestGetUsers() ), 41. { ); 42. //use the Yii application to build the proper request url format 43. $wsdlUrl=Y ii::app()->request->hostInfo.$this->createUrl('dem o'); 44. Now, we can add a method that will return an array 45. //create the php soap client passing in a parameter to instruct it not to cache the wsdl for testing purposes of AppUsers. Add the code in Listing 5. This code 46. $client=new SoapClient($wsdlUrl, array('cache_wsdl' => 0)); also includes a new test action so we can separately 47. 48. echo "

"; test our getUsers method (*Note: The test method is 49. //login as admin in order to retrieve users 50. $client->login('admin','admin'); using the ’admin’ user as the client login in order to 51. echo "retrieving all application users...\n"; retrieve the list of users because I will restrict this 52. print_r($client->getUsers()); 53. echo "
"; functionality to only admin users in the next section). 54. } After making this addition, point your browser to

44 | July 2009 www.phparch.com Yii: Flex Your Flash FEATURE request this new test action: http://localhost/index. to perform that action. And after every service call, php?r=webservices/testGetUsers. it will log a simple informational message to the ap- The result should be the array of AppUser class ob- plication.log file located in the protected/runtime/ jects of the 4 fake application users, Jim, Bob, Suzy directory. If you alter the actionTestGetUsers and Nancy. method to use demo/demo to log in, you should get the SOAPFAULT error “You are not authorized Before and After Service Invocation to perform this action.” You can also open up the Before leaving our Web service provider controller, /protected/runtime/application.log file to verify the I want to introduce one other great feature. If we logging is working after successful Web service calls. implement the IWebServiceProvider interface, the provider instance will be able to intercept the remote Get To Flex'ing method invocation both before the requested service Now that we have some simple services in place, we action is taken, and after the requested action has can turn our attention to the second part of our agen- been completed. For example, say we want to make da. We want to use Flex to create a simple widget that sure the client making the request is an authenticated will interact with these service methods to drive its user, maybe even of a certain role, before allowing functionality. Now, I realize that a simple login widget the action to take place. Or maybe we want to per- that will display a list of users, if you are the admin, is form some logging or reporting prior to, or just after, probably not the rich, interactive, flashiness your site a web service method is invoked. By implementing the has been longing for. However, it will provide a very IWebServiceProvider interface, we have the two meth- simple example that will lay a foundation upon which ods, ’beforeWebMethod’ and ’afterWebMethod’, to place your imagination can build. such logic. First, we need to set up a flex project, create the Let’s implement this interface and the mandatory needed mxml and ActionScript, and compile it to an methods required. We’ll restrict access to our ’getUsers’ swf file for inclusion into our application. We will method to only admin users. We’ll also log an informa- focus on how to stitch the Web services we created in tional message to the application.log file after each the first part of this article together with a Flex app web service request. Change the class declaration to and not about how to create a Flex application. I am include the implements keyword: glossing over a lot of details, but here are the basic steps: class webservicesController extends CController implements IWebServiceProvider • Create a new folder in the root of your yii- Implement the ’beforeWebMethod’ to restrict the ac- demo application, called ’flex’. cess: • Open up your Flex 3 builder and create a new

public function beforeWebMethod($service) Flex project: File->New->Flex Project. Give it a Licensed to 8203 - Rémy Dufour ([email protected]) { if($service->methodName == 'getUsers') { project name, yiidemo, and choose the folder if(Yii::app()->user->name == 'admin') return you created above as the project location, see true; Figure 2. Then click “Finish” as we don’t need else throw new CException('You are not authorized to perform this action.'); any further custom settings for this example. } else return true; • There should be one file under the src direc- } tory of this new project called yiidemo.mxml. And log which methods were called, by whom, and This is the main Application file of the Flex when by implementing ’afterWebMethod’ as: project. You can delete all the contents of this file and replace with the contents of the public function afterWebMethod($service) mxml application file of Listing 6. { $message = "AFTER SERVICE CALL: " . $service- >methodName . " service call made by " . Yii::app()- • Make sure to save and build the swf. The end >user->name . " on " . date("D M j G:i:s T Y"); result should be a yiidemo.swf file in the bin Yii::log($message, 'warning', 'system.web. WebservicesController'); (or bin-debug) directory of the project (also } /flex/bin/yiidemo.swf of the application).

Now, before any service method is called, it will check Let’s quickly go through the important parts of this to verify whether the user has the proper authority flex mxml file. When the application completes loading

www.phparch.com July 2009 | 45 FEATURE Yii: Flex Your Flash

in the browser, it will call the initApp() function that the login form toward the bottom of the file in Listing loads our WSDL file. 6. Then, when you click the login button, the doLogin method is invoked, and it uses validators to ensure private function initApp():void both username and password are not empty. It then { ws.loadWSDL(application.parameters.wsdl); makes the Web service call to our login method, and ... enables a progress bar to give the user some feedback } while waiting for a response. Processing transfers to We’ll be feeding this WSDL file in the Flash application the loginReturned method when a response is received. parameters when we create the Yii Flex widget in our It evaluates the response by taking one of three paths: HTML pages. ws refers to the id we gave the mxml tag that allows all of this communication to happen: • If the credentials supplied were not authenti- cated, it displays an error popup alert and the login form remains. • If the credentials were authenticated, and the username is ’admin’, it makes another service This definition allows us access to the operations of call to our ’getUsers’ method, and changes the our SOAP-compliant Web services. We’ve explicitly application state to an administrative view, defined the login operation in the mx:WebService defi- which simply lists the returned users. nition because we want to define a method to handle our results and take different actions depending on • If the credentials were authenticated, but the the result. This tells our Flex app to transfer process- user is not ’admin’, it toggles to a ’normalUser’ ing to the loginReturned method whenever a response state, which simply displays a simple greet- is received back from the login request. We handle our ing. call to getUsers slightly differently by binding the re- sults returned directly to our Flex datagrid component And that is all there is to this trivial application. to display the list. If you are not already logged in, the page will load Yii Is So Flexible with the default view for the Flex application, which is Now that we have our compiled swf in place, we need to display it in our application. Adding a Flex app to LISTING 7 your Yii site is a snap because there is a core frame- work widget that is ready-made to handle this. The 1. pageTitle=Y ii::app()->name; ?> 2. CFlexWidget comes packaged with the Yii framework 3.

4. Welcome, user->name; ?>! and is built to assist you in embedding Flex-generated 5.

applications into your Yii app. 6.

Licensed to 8203 - Rémy Dufour ([email protected]) 7.

Let’s add this simple application to the home 8. 10.
11.

FIGURE 2 12.

13.

14. 15. widget('CFlexWidget',array( 16. 'b a se Url'=>Y ii::app()->baseUrl.'/flex/bin-debug', 1 7. 'name'=>'yiidemo', 18. 'width'=>'800', 19. 'height'=>'200', 20. 'align'=>'middle', 21. 'f lashVars'=>array( 22. 'w s dl'=>$this->createUrl('webservices/demo'), 23. 'isGuest'=>Y ii::app()->user->isGuest, 24. 'username'=>Y ii::app()->user->name, 25. ))); ?> 26. 2 7. Sorry, the PHP SOAP extension is not enabled on your Web server. 28. 29.
30.

31. 32.

If you have problems in accomplishing any of the above tasks, please 33. read Yii documentation 34. or visit Yii forum for 35. help.

46 | July 2009 www.phparch.com Yii: Flex Your Flash FEATURE

LISTING 6 LISTING 6: Continued...

1. 79. /** 2. 82. */ 7. 83. private function doLogin():void 8. 85. if(Validator.validateAll([val3, val4]).length == 0) 9. 86. { 10. 87. ws.login(usernameEdit.text, passwordEdit.text); 11. 88. usernameEdit.enabled=false; 12. 89. passwordEdit.enabled=false; 13. 90. loginProgress.visible=true; 14. 91. } 15. 92. } 16. 94. /** 17. 95. * Handles general Web service call data errors. Defined as 18. 96. * the mx:WebService definition above 19. 97. */ 20. 98. private function loginReturned(event:ResultEvent):void 21. 99. { 22. 100. if(!event.result) 23. 101. { 24. 102. usernameEdit.enabled=true; 25. 103. passwordEdit.enabled=true; 26. 104. loginProgress.visible=false; 27. 105. Alert.show("Username and password did not match!", "Login 28. Error"); 29. 106. } 30. 1 0 7. else 31. 108. { 32. 109. this.userName = usernameEdit.text; 33. 111. } 34. 112. } 35. 113. 36. 114. /** 37. user role. If the role is 38. 116. * admin, it makes a call to the getUsers Web service 39. method 40. 1 1 7. */ 41. 118. private function setState():void 42. 119. { 43. 51. 128. 52. /** 129. Licensed to 8203 - Rémy Dufour ([email protected]) 53. * Loads our wsdl url into our mxml Web Service definition 130. 54. * if the user has already logged in 131. 55. */ 132. 56. private function initApp():void 133. 57. { 134. 58. ws.loadWSDL(application.parameters.wsdl); 135. 59. 136. 60. this.userName = application.parameters.username; 1 3 7. 62. //they are a returning logged in user, so change state to 138. appropriate state 139. 63. if(application.parameters.isGuest!="1") 140. 64. { 141. 65. this.setState(); 142. 69. /** 144. 70. * Handles general Web service call data errors. Defined as 145. our general fault hander in 146. 71. * the mx:WebService definition above 1 47. 72. */ 73. private function dataError(event:FaultEvent):void 74. { 75. Alert.show(event.fault.faultString,"Data Communication Error"); 76. Alert.show(event.message.toString()); 77. } 78.

www.phparch.com July 2009 | 47 FEATURE Yii: Flex Your Flash

page of our demo site by using the CFlexWidget. and can then access it in our Flex app by referencing I removed some of the stock auto-generated text application.parameters.wsdl. from /views/site/index.php (which is our homepage We are also passing in the ’isGuest’ and ’username’ view) and added the call to the widget. Change the parameters to help the Flex app know if the user is /views/site/index.php file to have the same contents already logged in and whether or not they are the as that of Listing 7. Now, as long as you are not al- admin. We would reference those in ActionScript ready logged in, the homepage should resemble Figure the same way: application.parameters.username and 3. If you happen to still be logged in from performing application.parameters.isGuest respectively. the previous tests, you can logout using the ’Logout’ We’re done! We have successfully created SOAP- link in the top navigation. based Web services that can be requested by any You should be able to test out this Flex widget SOAP-client. We then created a Flex application as a by logging in as demo/demo or admin/admin. Using SOAP-client to consume these services in order to add demo/demo should result in the Flex widget shrinking a little “Flash”-iness to our own Yii app. These basic down to a simple box displaying a basic intro greet- development tools have a lot of potential. This trivial ing, see Figure 4. If you refresh your browser, you will example application has done little more than explore see the rest of the application change in response to the tip of the possibility iceberg. However, I hope it you being logged in as demo. The main HTML mes- has left you in a position to embark further on that sage will change from “Welcome, Guest!” to “Welcome, exploration and helped to seed some big ideas for fu- demo!” and the “Login” link in the top nav will change ture projects. to “Logout”. If you logout and try admin/admin, it should result in the four fake application usernames and emails being displayed in a simple grid. If you log in with any other credentials, you should get an error popup indicating an invalid username/password com- Jeff Winesett is director of application development at bination. Control Group, Inc. in New York City. He has developed Let’s briefly cover the code we added. To use many professional, large-scale web applications using PHP and various PHP-based frameworks and is happy CFlexWidget, you set the name property to be the to spend what extremely little free time he has being a Flex application name but without the .swf suffix, Yii evangelist. Jeff can be contacted at jeff.winesett@ and baseUrlproperty to be the URL of the directory controlgroup.com. containing the swf file of the Flex application with- out the ending slash. These are the only mandatory properties that must be set. Most of the other prop- erties are layout related. However, I do want to draw FIGURE 4 attention to the flashVars property. This allows us to specify an array of name=>value pairs that will be passed into our Flex application and accessible via the Licensed to 8203 - Rémy Dufour ([email protected]) parameters property of the application component. We are passing in the URL of our Web service WSDL con- tent to our Flex application using these parameters. We set the name ’wsdl’ to the value of our WSDL URL,

FIGURE 3 FIGURE 5

48 | July 2009 www.phparch.com Security Roundup COLUMN

technically nobody did anything wrong in a security sense, there is still an exploit you may need to take Trust Me - I action against. A very “nice” exploit just recently hit twitter: An overlaid iframe with zero opacity allowed for unexpected transmitting of messages, causing a know What I'm lot of additional tweets distributing the viral message from a potentially trusted source. This type of attack is called “click jacking” (derived from click and hijack- Doing ing). And even though the twitter development staff didn’t do anything wrong from a technical perspec- tive, they had to face the consequences of this attack: clean everything up and develop counter measures. by Arne Blankerts They published a blog post on the subject (h t t p:// blog.twitter.com/2009/02/clickjacking-blocked.html). Another blog post describing how the attack worked hile not actually related to security on the first can be found online as well (http://softwareas.com/ glimpse, the modern web and its security have explaining-the-dont-click-clickjacking-tweetbomb/) Wa lot to do with trust: be it in the abilities of – though this time not from the twitter staff. Luckily, the developers, the company running the site as well there was no real harm to the end users in this case, as their way of handling personal data or in the infra- but the news coverage was still as negative as it structure on which all of this relies. The best and most can get: “twitter got hit by a worm” or “twitter got securely programmed website can easily be exploited if hacked” and the obvious “security problems at twit- the server it runs on or the ISP it is located at are not ter”. This bad press may very well cost a lot of money, up to the task of protecting their services. In general, decreasing the user trust in a service. we trust that our ISP knows how to secure their infra- So, what does thid teach us? It’s all about user per- structure – and I think we can and we should. Unless ception. It doesn’t matter if you can prove that you we run our own data center, this is exactly what an ISP did everything correctly and securely handled every is for - being the expert in a field we either don’t feel input – if the user doesn’t feel safe, all the effort is in at home in or have neither the time nor the budget vain. There will always be new trouble: new kinds of for gathering experiences in. On the other hand, web attacks, new waves of phishing attempts going on and agencies quite frequently do everything to undermine of course, simple mistakes by an end user when using the best efforts of their respective ISPs: using ftp to even the simplest of functions. Luckily, there are vari- update the customer website instead of using the pro- ous ways of securing your own network and website vided ssh/sftp alternative, for example, or by unknow- from being easily exploited, though listing them all ingly running an open relay by not effectively filtering in their respective context would be far beyond the input when sending out semi-automated mail. One scope of this column. It is of vital importance, though, Licensed to 8203 - Rémy Dufour ([email protected]) might argue that all this is not the problem of the ISP to monitor security lists for new attack vectors, and if but a clear mistake by the developer. While this is true, the need arises, investigate possible counter measures it doesn’t protect the ISP from receiving the blame for your own website. Being aware of the fact that an anyway: Their services and their IPs will eventually attack might exploit user habits rather than actual end up blacklisted for being a source of spam or being security flaws on your end is already a big step in the a known distribution site for viruses and trojans when right direction. someone manages to hack the weak ftp password. As bad as the situation might be for the ISP, that’s a part of their business. So what does that have to do with web security, you may ask? And why should you Arne Blankerts is the head of development at NonFood care? To understand this, it is important to realize that advertising agency in Hamburg, Germany. Arne is the even if you did everything right at the development inventor and lead developer of an open source site system stage, verify and validate all user input as well as hav- “fCMS” (ht tp://fcms.de) that is written in object-oriented ing a secure and tested infrastructure, you still might PHP and makes heavy use of XML. Furthermore he is a get attacked by phishing sites, iframe overlays and trainer, regular author writing articles for the German PHP others. Phishing sites cloning a website’s look and feel Magazine, co-authoring books and speaking at (PHP-) but actually do harmful things: they target the weak- conferences. In the unusual case of spare time, he helps maintaining the German translation of the PHP manual. est link in the chain of trust – the end user. So, while

www.phparch.com July 2009 | 49 COLUMN exit(0); The Seven Seas of Insanity

by Marco Tabini

y now, you are probably aware of the fact that, with a chicken-and-egg problem: we know that devel- come this fall, the php|a team will undertake opers who actually attend a conference discover the Bwhat is probably the most ambitious project we value, but the high cost of a professional conference have ever been capable of dreaming of—and, I assure keeps many of them away. you, we have a pretty vivid collective imagination. CodeWorks is our solution to this problem: instead of Thus, I thought I’d give you a little bit of insight asking people to travel to our conferences, we’re tak- into what my colleagues have already started calling ing the conferences themselves on the road, traveling “Marco’s Folly” (seriously, I’m honoured). from city to city with a team of speakers—all well- Despite my frequent jokes on the subject, the ulti- known names in the community—and offering a series mate motivation behind CodeWorks 09 is not a secret of seven two-day events aimed at local audiences. longing for world domination. It is, in fact, a very The benefits are obvious: by catering to a local au- simple consideration: the developers who don’t attend dience, we’re taking attendee travel expenses out of technology conferences are doing themselves (and the picture, and we are not taking people away from their employers) a huge disservice—and they don’t their offices for a week at a time. The longer prod- even know it. uct cycle also allows us to amortize our costs better, Organizing conferences is a tough business—every which makes for an overall lower entry price as well. time you get one under way, it starts as a huge hole in CodeWorks fulfills what I have always seen as php|a’s your corporate pocket that must be filled. It’s not until primary mission: educating developers, no matter Licensed to 8203 - Rémy Dufour ([email protected]) you’ve paid $80 for a gallon of coffee—going rates at where they are. This is something that can’t just be mid-range hotels—that you realize just how expen- done remotely—for example, through online training— sive these things can be to set up. Unfortunately, this because, as everyone who has attended a conference reflects itself in the price to the end user, and not will testify, remote training lacks the “hallway track” a conference goes by that a few angry e-mails from of networking and peer-to-peer interaction that makes folks who complain about the cost of our events reach in-person events so useful. (a more appropriate term might be “burst screaming Of course, CodeWorks is also a massive undertak- into”) my inbox. ing—it’s essentially the same as organizing seven con- The problem is, in the greater scheme of things, ferences, in seven different cities (all with their own conferences are immensely important for a develop- quirks and regulations) in a fortnight. Hence the term er—and I am happy to say that this is not limited to “Marco’s Folly,” a label that I take in stride—although, our conferences. Even though they can be expensive, personally, I would have preferred something more conferences are a useful investment that will, most dramatic, like Seven Seas of Insanity, which has a nice likely, yield plenty of return even in the short term. alliteration to it. From new techniques to the unique opportunities for networking, any developer can draw plenty of benefit from attending. Clearly, this presents us, as conference organizers,

50 | July 2009 www.phparch.com ElePHPants! The elePHPants love to travel the world! Here are some photos that they have sent us from their travels.

I took my Elephant sailing today in Lathum, The Netherlands. He enjoyed it a lot. Photo courtesy of Bart McLeod Licensed to 8203 - Rémy Dufour ([email protected]) Do you have photos of your ElePHPant? We would love to print them. Send us a high-resolution image (or if you have the high-resolution image posted online, just send us the link) of your ElePHPant. We'll choose several pho- tos a month to publish.

What's New at Python Magazine Learn more at: http://pythonmagazine.com

July 2009 Topics: • Welcome to Python: String Formatting ... • Hashing With Python ... (Jose R.C. Cruz) (Mark Mruss) • Faster Page Templates with Chameleon ... • Acceptance Testing a .NET Application Using (Malthe Borch) IronPython ... (Jonathan Hartley) • and much more...

www.phparch.com July 2009 | 51 Licensed to 8203 - Rémy Dufour ([email protected])