Build an Enterprise-Grade PHP Stack with Zend Server 5.0 and Oracle 11g

April 21, 2010

Uncategorized

Over the last few years, PHP has become increasingly popular in the enterprise space, both on account of its friendly licensing terms and its ease of use in building robust, secure applications. Its support for a wide variety of databases means that it’s often being used in conjunction with Oracle, the database system of choice for large enterprises.

There’s only one problem with this happy picture: setting up an Oracle/PHP/Apache/Linux (OPAL) development environment isn’t the easiest of tasks, especially in corporate IT environments which are used to point-and-click simplicity. Online reference material such as the Underground PHP Oracle Manual, Oracle’s PHP FAQ Wiki and various “getting started” guides help in the process, but it still isn’t as easy as clicking a bunch of buttons and letting an automated installer do the heavy lifting.

One of the easiest ways to quickly configure a PHP environment in this environment is Zend Server, a PHP stack that runs on both Windows and Linux and that can be used to build Oracle-based applications out of the box. Zend Server provides a high-performance, enterprise-ready OPAL stack that allows corporate IT departments to add PHP middleware to their existing installations without breaking a sweat.

Introducing Zend Server

Zend Server is a ready-to-use PHP stack that makes it easy to develop and run PHP application in both Windows and *NIX environments. It includes an updated and fully-tested version of PHP, support for a wide variety of database systems (including Oracle via the Oracle Instant Client libraries, MySQL, DB2 and Microsoft SQL Server), and a number of Zend-specific add-ons for improving PHP performance and diagnostics.

Zend Server is currently available in two versions. If you’re reasonably experienced with PHP and are happy to maintain the server and manage updates manually, you can opt for the “community” version of Zend Server, known as Zend Server CE. This version is considered production ready for “non-critical” applications and is available free of charge for both Windows, Linux, and Mac OS X; however, it doesn’t include the page caching, code tracing, offline job queuing, automatic updates and diagnostic features that are included in the “commercial” version. If these features are important to you – and they should be if you’re using the server for business-critical PHP applications – then you should consider purchasing the commercial version of Zend Server, which entitles you to all of the above features plus technical support from Zend. You can try before you buy as well with a 30-day trial license key for Zend Server; registration is required.

Here’s a quick overview of the key features of Zend Server (unless specifically noted otherwise, these features are available in both versions):

  • Web-based server management console: Zend Server significantly simplifies the task of managing your PHP development environment, by providing a Web-based interface to PHP configuration and maintenance. Instead of altering PHP parameters by editing a configuration file in a text editor, Zend Server allows you to accomplish the same task via any standard Web browser, using a point-and-click interface. Common tasks such as enabling or disabling extensions, altering the PHP include path or restarting the server can all be accomplished through this Web-based console.
  • Built-in performance optimization suite: Zend Server isn’t just a pre-packaged version of PHP – it also includes a suite of supplementary tools created by Zend to make troubleshooting and performance optimization easier. This suite includes tools such as the Zend Optimizer+, an extension for run-time performance enhancement, and the Zend Data Cache, a set of caching functions for use in PHP applications. The commercial version of Zend Server also includes the Zend Page Cache, which supports rule-based caching of HTML output.
  • Comprehensive application monitoring and troubleshooting: PHP doesn’t include a built-in debugger, and this can make finding and squashing application errors a time-consuming and expensive process. With Zend Server, this problem is almost non-existent, due to the variety of monitoring and debugging tools available. Both versions of Zend Server include Zend Debugger, an extension to help in debugging PHP scripts. The commercial version of Zend Server also includes two new features: Zend Code Tracing simplifies tracking, analyzing and debugging PHP script execution, and Zend Job Queue makes it possible to automatically run PHP scripts asynchronously, or at pre-specified intervals. And finally, the Zend Monitor provides an interface to recording, tracking and reporting application and server alerts.
  • PHP certification: Zend Technologies is a well-known vendor of tools for PHP application development and deployment, and every version of Zend Server includes a certified and up-to-date version of PHP that has been tested on different platforms. For large enterprises that are just stepping into the world of open-source technologies, this certification is extremely important to promote a sense of confidence in the PHP platform. Both versions of Zend Server include an up-to-date version of the Zend Framework, and commercial users of Zend Server also have access to a constant stream of security updates and bug fixes, to ensure the integrity of their PHP applications.

Installing Zend Server

For purposes of this article, I’ll assume that you already have a version of Oracle installed and working. In case you don’t, you can download a version of Oracle suitable for your development system from the Oracle Web site. The examples in this article were developed using Oracle 11g (V11.1.0.6.0); however, the PHP extensions bundled with Zend Server also work with older versions of Oracle.

If you’re running Oracle on Linux, chances are you’re using either Oracle Enterprise Linux or Red Hat Enterprise Linux. RPM-based installation with yum is the recommended way to install Zend Server on this platform. To accomplish this, the Zend Server manual suggests that you log in as the root user and tell yum about the Zend repository, by creating a new file at /etc/yum.repos.d/zend.repo and adding the following contents to it:

[Zend]
name=Zend Server
baseurl=http://repos.zend.com/zend-server/rpm/$basearch
enabled=1
gpgcheck=0

[Zend_noarch]
name=Zend Server - noarch
baseurl=http://repos.zend.com/zend-server/rpm/noarch
enabled=1
gpgcheck=0

Once this file has been created, Zend Server (and its dependencies) can be installed with a single command, as below:

shell> yum install zend-server-php-5.3

Here’s a screenshot of yum at work:

If you’re using another, Debian-based Linux distribution, such as Ubuntu, Zend Server can also be installed using a package manager such as aptitude. The Zend Server manual provides detailed instructions to accomplish this. Briefly, the process requires you to first add the following line to your system’s master repository list, at /etc/apt/sources.list:

deb http://repos.zend.com/zend-server/deb server non-free

While you’re at it, also uncomment the lines for the ‘universe’ repository so that aptitude can locate and download necessary dependencies – for example:

deb http://archive.ubuntu.com/ubuntu intrepid universe

Then, add Zend’s public key to your system’s repository database:

shell> wget http://repos.zend.com/deb/zend.key 
shell> apt-key add zend.key

Finally, download and install Zend Server, together with all necessary dependencies, using aptitude:

shell> aptitude update
shell> aptitude install zend-server-php-5.3

Here’s an example of what you might see during the installation process:

Regardless of which method you use, Zend Server will be installed to /usr/local/zend/ and the installer will automatically start it up and present a confirmation message, as below:

Using the Server Administration Console

Once Zend Server has been installed, you can access it using your favorite Web browser at the URL http://localhost:10081/ZendServer. The first time you try accessing it, it will prompt for an administrator password and your license key, as below:

Enter the requested information and you’ll be transferred to the server management console, which offers a tabbed interface to the main server administration areas, as below:

Here’s a quick list of things you can do through this Web-based console:

Obtain server information: Detailed information on the current PHP build can be obtained from the Monitor -> PHP Info page, which displays the uncensored output of the phpinfo() command. Similarly, you will get a birds-eye view of the different installed components of Zend Server via the Monitor -> Server Info page, which includes version information and key file paths. You’ll also find a continually-refreshed view of various important log files at Monitor -> Logs.

Activate or deactivate server extensions: Zend Server makes it easy to add support for new technologies to your PHP build from the Server Setup -> Extensions page, which offers a checkbox-like interface to turn PHP extensions on or off. A similar interface to activate or deactivate Zend’s own performance and diagnosis tools, such as Zend Optimizer+ or Zend Monitor, can be found at the Server Setup -> Components page. External debuggers can be configured using the Server Setup -> Debugger page.

Modify the PHP configuration file: Normally, altering PHP configuration is a messy affair, requiring you to figure out which PHP configuration variables need to be changed and then manually edit the php.ini configuration file to make the changes. Zend Server significantly simplifies this, by allowing you to edit PHP configuration variables through a single Web form (Server Setup -> Directives). This form interface also includes brief snippets of information on each configuration variable, making it easy to quickly figure out what goes where. The Zend Server Web interface also includes a control to restart PHP, making it very simple to immediately see the result of your configuration changes.

Set up page caching: URL-based page caching is one of the most important performance tools available in Zend Server (this feature is not available in Zend Server CE). This feature, accessible from the Rule Management -> Caching page, allows the server administrator to set up URL matching rules (based on regular expressions) and have the output of those URLs automatically cached by the server, so that subsequent requests for the same URL are served from the cache. As you’ll see a little further along in this article, this feature can improve performance by several orders of magnitude.

Monitor PHP alerts and exceptions: Another key feature of Zend Server is its ability to collect PHP alerts and exceptions and display them in a single, central location: the Monitor -> Events page. This feature is of particular benefit in typical corporate environments, as it provides immediate notification of worrying server events (for example, excessive memory usage) and makes it possible to take corrective action. Administrators can also set up custom “watch rules” for the event monitor using the Rule Management -> Monitoring page. However, this event monitoring feature is not available in Zend Server CE.

Debug and backtrace code errors: Backtracing application errors is a frustrating and time-consuming process, especially when working with long or complex object hierarchies. Previously, the only way to efficiently debug these errors was with a stack trace, or by manually setting debug statements at different points in the code. Zend Server 5.0 significantly simplifies this process with code tracing, a new feature that provides developers with a complete backtrace of a URL request (this feature is not available in Zend Server CE). Code tracing can be used for both manual and automated (event monitoring) workflows, and typically provides a comprehensive map of request execution, including the functions invoked, their input arguments and return values, memory consumption and execution time. Errors, if any, are flagged for easy reference, complete with line and file number, error message and error code. Code tracing reports can be accessed through the Monitor -> Code Tracing page, and monitoring rules can be set up through the Rule Management -> Monitoring page. You’ll find an example a little further along in this article.

Set up and manage recurring jobs: One of the key new features in Zend Server 5.0 is the ability to set up a job queue, similar to a cron table. This feature allows administrators to define recurring tasks for the server to execute automatically, and provides an interface to check job status and view failed jobs. Jobs can be defined through the Rule Management -> Recurring Jobs page, and job status may be checked through the Monitor -> Jobs and Monitor -> Queue Statistics views.

Change the server administration password: The Administration page offers a simple interface to set a new administrator password for Zend Server, update license details and update Zend Server itself.

Accessing Oracle Databases with Zend Server

As noted earlier, Zend Server comes with built-in support for Oracle. This means that once the server is installed, you can immediately start writing Oracle-backed applications with PHP. Let’s see this in action, by filling an Oracle table with some example data, and then writing a PHP script that uses the Oracle Call Interface and PHP’s oci8 extension to retrieve this data and present it as a Web page.

To begin, log in to Oracle as the system administrator, and create a new user account:

shell> sqlplus / as sysdba
SQL*Plus: Release 11.1.0.6.0 - Production on Tue Apr 6 07:29:18 2010
Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Release 11.1.0.6.0 - Production

SQL> CREATE USER john IDENTIFIED BY doe;
User created.

SQL> GRANT ALL PRIVILEGES TO john;
Grant succeeded.

Then, connect as that user and create a new table:

shell> sqlplus 
SQL*Plus: Release 11.1.0.6.0 - Production on Tue Apr 6 07:29:18 2010
Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Enter user-name: john
Enter password: ***

Connected to:
Oracle Database 11g Release 11.1.0.6.0 - Production

SQL> CREATE TABLE CITIES (
  2  CITY_ID NUMBER NOT NULL,
  3  CITY_NAME VARCHAR2(50)
  4  );
Table created.

SQL> INSERT  ALL
 2  INTO CITIES (CITY_ID, CITY_NAME) VALUES (1, 'Mumbai')
 3  INTO CITIES (CITY_ID, CITY_NAME) VALUES (2, 'London')
 4  INTO CITIES (CITY_ID, CITY_NAME) VALUES (3, 'Paris')
 5  INTO CITIES (CITY_ID, CITY_NAME) VALUES (4, 'Rome')
 6  SELECT * FROM dual;
4 rows created.

Check that the records were successfully inserted with a quick SELECT:

SQL> SELECT * FROM CITIES;
CITY_ID    CITY_NAME
---------- --------------------------------------------------
	   1 Mumbai
	   2 London
	   3 Paris
	   4 Rome

Now, do the same thing using PHP, by creating a new PHP script and filling it with the code below (oci.php). This script should be saved to the Web server document root, which is usually /var/www on Ubuntu and Debian distributions, and /var/www/html on other distributions:

<html>
  <head>
    <style type="text/css">
    table { border-collapse: collapse; }
    td { border: solid 1px black; padding: 3px; }
    </style>
  </head>
  <body>
  <h2>Cities</h2>
  <?php
  // open database connection
  $db = oci_connect('john', 'doe', '//achilles/orcl');
  if (!$db) {
    die('Unable to connect to database');
  }
  
  // formulate and parse query
  $sql = 'SELECT * FROM CITIES';  
  $stmt = oci_parse($db, $sql);

  // execute query  
  $rslt = oci_execute($stmt);

  // iterate over result set
  $count = 0;
  echo '<table>';
  while ($row = oci_fetch_object($stmt)) {
    echo '<tr>';
    echo '<td>' . $row->CITY_ID . '</td>';
    echo '<td>' . $row->CITY_NAME . '</td>';
    echo '</tr>';
    $count++;
  }
  echo '</table><br/>';
  echo $count . ' record(s) found.';  

  // close connection  
  oci_close($conn);
  ?>
  </body>
</html>  

This script begins by opening a connection to the database server using the oci_connect() function with the server DSN and user credentials. It then prepares a query with oci_parse() function, which returns a statement object; this statement object is then used to executes the query on the database server via the oci_execute() function.

A loop is used to iterate over the result set, returning individual records as objects via oci_fetch_object(). Individual fields of each record can now be accessed as object properties; it’s then fairly simple to present these field values as an HTML table. Once all the records in the result set have been processed, the connection is closed with oci_close().

When you access this script through a Web browser, you should see a Web page that looks something like this:

Using Connection Pooling

Oracle 11g includes a new feature aimed specifically at applications requiring high scalability: database-resident connection pooling (DRCP). DRCP makes it possible to share database connections between different application processes, resulting in more efficient use of server resources and an overall improvement in performance. The PHP OCI extension (currently V1.3.5) that ships with Zend Server includes out-of-the-box support for DRCP, allowing developers to make immediate use of this feature in their PHP applications. More details on PHP and DRCP can be found here and here.

To enable DRCP in Oracle, log in to the database server and start up the connection pool:

shell> sqlplus / as sysdba
SQL*Plus: Release 11.1.0.6.0 - Production on Tue Apr 6 14:24:13 2010
Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Release 11.1.0.6.0 - Production

SQL> execute dbms_connection_pool.start_pool();
PL/SQL procedure successfully completed.

Check that the pool has been started by querying the special DBA_CPOOL_INFO view:

SQL> SELECT CONNECTION_POOL, STATUS, MAXSIZE
  2  FROM DBA_CPOOL_INFO;

CONNECTION_POOL             STATUS		    MAXSIZE
----------------------------------------------------------
SYS_DEFAULT_CONNECTION_POOL ACTIVE		    40

Then, on the Server Setup -> Directives page of the Zend Server administration console, find the oci8 section, and set a name for the DRCP connection class used by the PHP application in the oci8.connection_class variable. This connection class allows for logical divisions between pooled servers for different applications:

Finally, enable your PHP application to use DRCP by adding the keyword POOLED to your oci_connect() connection string. While not mandatory, it’s also recommended that you replace the oci_connect() function with the oci_pconnect() function. As an example, here’s the previous example, revised to use DRCP (drcp.php):

<html>
  <head>
    <style type="text/css">
    table { border-collapse: collapse; }
    td { border: solid 1px black; padding: 3px; }
    </style>
  </head>
  <body>
  <h2>Cities</h2>
  <?php
  // open database connection
  $db = oci_pconnect('john', 'doe', '//achilles/orcl:POOLED');
  if (!$db) {
    die('Unable to connect to database');
  }
  
  // formulate and parse query
  $sql = 'SELECT * FROM CITIES';  
  $stmt = oci_parse($db, $sql);

  // execute query  
  $rslt = oci_execute($stmt);

  // iterate over result set
  $count = 0;
  echo '<table>';
  while ($row = oci_fetch_object($stmt)) {
    echo '<tr>';
    echo '<td>' . $row->CITY_ID . '</td>';
    echo '<td>' . $row->CITY_NAME . '</td>';
    echo '</tr>';
    $count++;
  }
  echo '</table><br/>';
  echo $count . ' record(s) found.';  

  // close connection  
  oci_close($conn);
  ?>
  </body>
</html>  

Note that you can also specify that server connections should be pooled via the $ORACLE_HOME/network/admin/tnsnames.ora file, as in the example below:

ORCL =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = achilles)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = POOLED)
      (SERVICE_NAME = orcl)
    )
  )

Using the Zend Page Cache

One of the most important performance improvement features built into Zend Server is the Zend Page Cache, which provides URL-based caching of HTML output. Applications that dynamically generate HTML pages using PHP code benefit the most from this feature, as retrieving cached output from the Zend Page Cache is significantly faster than having PHP dynamically regenerate the same page on each new request.

The Zend Page Cache can be enabled through the Zend Server administration console, and makes use of regular expressions to match and identify URLs to be cached. This feature is entirely server-based; no changes are required to the application code to enable it.

To illustrate how it works, let’s try benchmarking a popular open-source application with Zend Server: phpBB3, a sophisticated PHP-based bulletin board that works with various database systems, including Oracle. phpBB3 can be freely downloaded from its official Web site, and comes with an automated installer that lets you get it up and running with minimal effort. The following discussion assumes that you have already configured and installed phpBB3; detailed instructions for how to accomplish this can be found in the phpBB3 Quick Start Guide.

Assuming you’ve got it all set up, log in to the phpBB3 Administration Control Panel.

Now, create a new forum (Forums -> Manage Forums). Remember to copy forum permissions from the default (example) forum so that the new forum appears in the main board index page.

Repeat this process until you have 3-4 new forums.

Now, when you visit the main board index page, you should see your newly-added forums.

This index page is dynamically generated by PHP, which queries the Oracle database server, retrieves a list of forums, and formats the result into HTML on each request. Note down the URL of this page, you’ll need it a little further along.

The next step is to analyze the quantitative benefit of the Zend Page Cache with the ApacheBench monitoring tool. First, visit the Server Setup -> Components page of the Zend Server administration console and turn off the Zend Page Cache and Zend Optimizer+. Remember to restart Zend Server to have the changes take effect.

Then, use ApacheBench to generate a benchmark of server response times, by sending multiple simultaneous requests for the forum index page using the URL from the previous step. Here’s the output for 500 requests, sent 10 at a time:

shell> ab -n 500 -c 10 http://192.168.1.3/phpBB3/index.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.1.3 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests

Server Software:        Apache/2.2.11
Server Hostname:        192.168.1.3
Server Port:            80

Document Path:          /phpBB3/index.php
Document Length:        11599 bytes

Concurrency Level:      10
Time taken for tests:   49.903 seconds
Complete requests:      500
Failed requests:        0
Write errors:           0
Total transferred:      6152000 bytes
HTML transferred:       5799500 bytes
Requests per second:    10.02 [#/sec] (mean)
Time per request:       998.058 [ms] (mean)
Time per request:       99.806 [ms] (mean, across all concurrent requests)
Transfer rate:          120.39 [Kbytes/sec] received


Now, go back to the Server Setup -> Components page of the Zend Server administration console and turn on the Zend Page Cache. Set up a new caching rule for the forum index page URL, as below:

Restart PHP to have your changes take effect, and then try running ApacheBench again with the same parameters as earlier:

shell> ab -n 500 -c 10 http://192.168.1.3/phpBB3/index.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.1.3 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests

Server Software:        Apache/2.2.11
Server Hostname:        192.168.1.3
Server Port:            80

Document Path:          /phpBB3/index.php
Document Length:        11606 bytes

Concurrency Level:      10
Time taken for tests:   0.253 seconds
Complete requests:      500
Failed requests:        0
Write errors:           0
Total transferred:      6002916 bytes
HTML transferred:       5826212 bytes
Requests per second:    1974.79 [#/sec] (mean)
Time per request:       5.064 [ms] (mean)
Time per request:       0.506 [ms] (mean, across all concurrent requests)
Transfer rate:          23153.27 [Kbytes/sec] received

Compare the output of this run with the previous one, and you’ll see that using the Zend Page Cache has reduced the time per request from 998 ms to 5 ms! The server is able to handle many more requests/second, up from 10 to 1974.

Now, go back to the Server Setup -> Components page of the Zend Server administration console and turn on the Zend Optimizer+. Then, restart PHP and give ApacheBench another whirl:

shell> ab -n 500 -c 10 http://192.168.1.3/phpBB3/index.php
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 192.168.1.3 (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests

Server Software:        Apache/2.2.11
Server Hostname:        192.168.1.3
Server Port:            80

Document Path:          /phpBB3/index.php
Document Length:        11606 bytes

Concurrency Level:      10
Time taken for tests:   0.158 seconds
Complete requests:      500
Failed requests:        0
Write errors:           0
Total transferred:      6002916 bytes
HTML transferred:       5826212 bytes
Requests per second:    3171.82 [#/sec] (mean)
Time per request:       3.153 [ms] (mean)
Time per request:       0.315 [ms] (mean, across all concurrent requests)
Transfer rate:          37187.88 [Kbytes/sec] received

It should be clear that Zend Optimizer+ improves performance even further, with the average time per request now dropping to 3 ms, and the server now able to handle 3171 transactions/second. Compare that with the initial run, and you’ll see that just turning on these two components (with no additional fine-tuning or code optimization needed) produces a significant improvement in performance.

Analyzing Performance and Errors with Code Tracing

Zend Server 5.0 includes a powerful new code tracing system, which allows developers to examine, in minute detail, the complete history of a particular request. This includes a list of all the functions and methods invoked by the script, together with their input arguments, return values and memory consumption. The data is presented hierarchically, making it easy to perform granular code profiling, or to drill down to the exact line of code causing an error.

Zend Server’s code tracing is enabled by default. To see it in action, visit the Monitor -> Code Tracing page and enter a request URL. Zend Server will then request the specified URL, generating a trace file for the entire request. Here’s an example of what the trace output looks like:

Notice that errors, if any, are automatically flagged by Zend Server in the trace output, making it easy to debug errors.

The trace output also includes statistics for the requested script, showing how many times a particular function or method was called and also providing information on execution time and memory usage.

As the above examples show, Zend Server is a stable and secure PHP stack that works with Oracle out of the box, making it easy to develop and deploy Oracle+PHP applications in an enterprise environment. A powerful and flexible caching engine, a browser-based administration console and built-in support for recent Oracle innovations such as database-resident connection pooling round out the package, making this a good alternative for anyone who’s ever needed to deploy a PHP application server with an Oracle-based infrastructure.

Copyright Zend Technologies, 2010.

One Response to “Build an Enterprise-Grade PHP Stack with Zend Server 5.0 and Oracle 11g”

  1. donaldp Says:

    I’m an Oracle PL/SQL programmer by day and a website owner with PHP by night. I didn’t know that you could use them together. Thank you for the great detailed information.
    http://www.creating-electricity.com