Archive | PHP

Using Oracle with PHP and CodeIgniter

Configuring PHP

My first mistake when configuring PHP to support Oracle was to assume that it would be as easy as MySQL; that is to say, on Windows, simply enabling the Oracle DLL that ships with PHP. No such luck, and the error message you see — when you finally remember to check your PHP error log — isn’t very helpful:

PHP Warning:  PHP Startup: Unable to load dynamic library 'C:\Program Files\Zend\Core\lib\phpext\php_oci8.dll' - The specified module could not be found.
in Unknown on line 0

Thanfully, I found a document on the Oracle website that explains how to configure PHP on Linux and Windows for Oracle support. I’d say this was the first straightforwardly helpful piece of Oracle documentation I’d ever used. Frustrations nevertheless ensued. When I downloaded Oracle 10g Express to load on my desktop, the website mentioned that I wouldn’t need any additional Oracle client downloads. (It was a gigantic enough download!) So, naturally, I figured that the client libraries needed to for PHP/Oracle support would all be somewhere in my install folder. Incorrect! As the configuration instructions suggested, I also had to download the (39-megabyte) Instant Client Basic for Windows, which contained all three libraries. I plunked them into a new directory, put that directory on my path, and revved up PHP. This time, no errors.

Easy Alternative: Zend Core offers a “for Oracle” version with Oracle support built in, out of the box.

Configuring CodeIgniter

I’ve started using CodeIgniter heavily in the last few months, and it’s a real time-saver once you get the hang of it. While I have a couple hangups about the CI User Guide — mainly that it’s slanted too severely toward the “quick example” side without comprehensive reference information to fill in the gaps — the user community tends to be quite helpful. Accordingly, the User Guide only says that CI supports an Oracle database, with no instructions on how to complete the built-in database configuration to use Oracle. So, I browsed the forums and found a few folks who had posted their Oracle configurations, and noted that, in general, one’s database config should look like this:

$db['default']['hostname'] = “//localhost/XE”;
$db['default']['username'] = “USERNAME”;
$db['default']['password'] = “PASSWORD”;
$db['default']['database'] = “DATABASE_NAME”;
$db['default']['dbdriver'] = “oci8″;

That hostname field can optionally contain a port number, if it differs from the standard 1521:

$db['default']['hostname'] = “//localhost:9999/XE”;

The “XE” in both examples is the default SID that Oracle 10g Express uses. (Unfortunately, I’m still oblivious as to the utility of an SID.) Also note that username, password, and database fields probably ought to be in uppercase — more case sensitivity implications on their way, stay tuned.

That done, we’re ready to query for some data. But wait! I tested my existing application code (previously running on a MySQL database) and got these warnings:

ERROR - 2007-12-02 15:02:05 --> Severity: Warning  --> ocifetchinto() expects parameter 1 to be resource, null given C:\codeigniter\system\database\drivers\oci8\oci8_result.php 159
ERROR - 2007-12-02 15:02:05 --> Severity: Warning  --> ociexecute() expects parameter 1 to be resource, null given C:\codeigniter\system\database\drivers\oci8\oci8_result.php 46

Yet again, the CodeIgniter forum came to the rescue: there’s an Oracle-specific bug in CI’s platform-independent database abstraction class. It’s fixed with a simple cut-and-paste. Around line 324, the $RES->num_rows line needs to be moved down below the if block, a la:

$driver 		= $this->load_rdriver();
$RES 			= new $driver();
$RES->conn_id	= $this->conn_id;
$RES->result_id	= $this->result_id;

if ($this->dbdriver == 'oci8')
{
  $RES->stmt_id		= $this->stmt_id;
  $RES->curs_id		= NULL;
  $RES->limit_used	= $this->limit_used;
}

$RES->num_rows	= $RES->num_rows();

No more warnings.

Using Active Record

CodeIgniter’s Active Record class makes most database queries dead simple and, combined with centralized configuration and automatic connections, cuts down significantly on lines of code. Using Active Record in my model class, I have a login method which checks a users table for the proper username and password:

$query = $this->db->getwhere('users', array('username' => $username, 'password' => md5($password)));

Then, I check the result for an active user by inspecting the flags field for a non-negative value:

if (isset($query->row()->flags) && $query->row()->flags >= 0) { ... }

For some reason, I just couldn’t log in, but I wasn’t getting any error messages from PHP or CodeIgniter. Then, I recalled my case sensitivity wrangling from earlier, and my cogs started turning. My database had been constructed with all uppercase table names and column names, so perhaps my result’s fields would be uppercase, too. A simple var_dump of the result object proved my theory:

object(CI_DB_oci8_result)#17 (9) {
  ["stmt_id"]=>
  resource(33) of type (oci8 statement)
  ["curs_id"]=>
  NULL
  ["limit_used"]=>
  NULL
  ["conn_id"]=>
  resource(24) of type (oci8 persistent connection)
  ["result_id"]=>
  bool(true)
  ["result_array"]=>
  array(1) {
    [0]=>
    array(3) {
      ["USERNAME"]=>
      string(8) “username”
      ["PASSWORD"]=>
      string(32) “…”
      ["FLAGS"]=>
      string(1) “0″
    }
  }
  ["result_object"]=>
  array(0) {
  }
  ["current_row"]=>
  int(0)
  ["num_rows"]=>
  int(1)
}

Now, I reference the uppercase member variables in my code and it all works fine:

if (isset($query->row()->FLAGS) && $query->row()->FLAGS >= 0) { ... }

I’m not sure whom to implicate for the misunderstanding. When I write SQL queries — against MySQL or Oracle — my schema, table, and columns names may be written in any case, and I get a proper result. Shouldn’t that case insensitivity carry through into Active Record and/or PHP?

Migrating from the PHP/Java Bridge to the Zend Java Bridge

At work, I’ve been evaluating the Zend Java Bridge (part of the commercial Zend Platform) as an alternative to the open-source PHP/Java Bridge. Our theory is that the Zend version may be more production-class (i.e. stable, secure, supported, and such), but we still want to make our code operable with either bridge so our user community isn’t locked into a commercial product. So far, it’s promising. Zend Platform sits on top of Zend Core — which bundles PHP, Apache and MySQL — making installation a snap, and the Platform management interface is a breath of fresh air, no more sifting through four separate directories of log files to figure out what’s going wrong. When things do go wrong, the Platform will wrap up all necessary support artifacts for submission to Zend in a trouble ticket — and their technical support response time has been admirable so far (at least for the easy problems).

Our code is still interoperable, but there have been some sizable snags with Zend. Mostly it’s things I’ve gotten used to after two years of wrestling with the PHP/Java Bridge that aren’t available with Zend. I’ll document them here (and their workarounds, if possible) as I come upon them.

No way to dynamically load libraries

Sometimes, especially during development, you don’t have a fixed set of JARs to enshrine in a system variable. The PHP/Java Bridge lets you programatically load libraries with the java_require("jar1;jar2;jar3") command, or will automatically load all the JARs in a configurable “extra libraries” directory. Zend, on the other hand, makes you write out your classpath in full. On Linux, you can modify the script which starts your Java Bridge by adding the name of each extra JAR it should load. On Windows, you need to change the CLASSPATH environment variable. Neither way is particularly user-friendly.

On my Windows development box, I’ve cheated a bit, and copied all my JARs to the JRE’s \lib\ext\ directory. I figure that’s okay for the time being. On our production systems, the code will be stable, so we can use a formal classpathing method. In any case, the Zend folks have said that this month’s new release of the Java Bridge will allow dynamic library loading.

No language construct to access class objects

The PHP/Java Bridge has a simple new JavaClass("java.util.Arrays") command that returns a class object, rather than an object of the class (like new Java("...") does). Zend simply does not. The workaround I’ve developed seems to work fine:

function getJavaClass($classname) {
  $c = new Java("java.lang.Class");
  return $c->forName($classname);
}

Can’t instantiate a javax.xml.datatype.DatatypeFactory

I get a bizarre exception when I try to instantiate this class.

java.lang.IllegalAccessException: Class com.zend.javamw.q can not access a member of class javax.xml.datatype.DatatypeFactory with modifiers "protected"

The boys at Zend are still looking into this one. If they can’t resolve it, we’re back to square one.

Update - October 10: Zend has reproduced the problem and logged it as a bug.

Problems with iterators

I knew off the bat that the Zend Bridge wouldn’t handle iterators as nicely at the PHP/Java Bridge (which lets you use any java.util.List from within a PHP foreach), but for no apparent reason, Zend won’t even give me an iterator or let me use any other List method in certain circumstances. I was able to create a simple reproduction of the problem using a com.sun.xacml.PolicySet:

$uri = new Java("java.net.URI", "foobar");
$alg = new Java("com.sun.xacml.combine.DenyOverridesPolicyAlg");
$tar = new Java("com.sun.xacml.Target", null, null, null);
$policySet = new Java("com.sun.xacml.PolicySet", $uri, $alg, $tar);
$children = $policySet->getChildren();
$iterator = $children->iterator();

Here’s the error I get:

Fatal error:  Uncaught exception ‘JavaException’ with message ‘Java Exception java.lang.NoSuchMethodException: iterator
java.lang.NoSuchMethodException: iterator’ in java-test2.php:9
Stack trace:
#0 java-test2.php(9): java.util.Collections$EmptyList->iterator()
#1 {main}
  thrown in java-test2.php on line 9

It’s clear that I’m trying to access an EmptyList, which, despite being empty, is still a list, and should still have an iterator() method. (All other methods, like size(), are also inaccessible.)

Update - October 9: Zend support tried to blow me off, saying that my problem “goes out of the limits of Zend Products and PHP,” but I was able to narrow down the problem even more, without any of the XACML libraries:

$class = new Java('java.lang.Class');
$collections = $class->forName('java.util.Collections');
$empty = $collections->emptyList();
$iterator = $empty->iterator();

Same NoSuchMethodException as before.

Update - October 11: This has finally been acknowledged as a legitimate bug, and it turns out it’s probably caused by the same underlying issue with their Java Bridge Connector as with the DatatypeFactory bug above.

Sparse documentation

From the start, I found the PHP/Java Bridge’s documentation frustrating. It wasn’t always clear what information applied to which version of the bridge, and there was never enough detail to make me comfortable. Fortunately, the basics were there, and the bridge’s creator was always helpful over the support mailing list.

The entirety of Zend’s bridge documentation is fourteen very lightweight pages in the Zend Platform User Guide. Of course, the code examples they give make it clear why they don’t feel more detail is necessary: they expect their users to be accessing EJBs within their existing Java infrastructure… simple getting and setting.

It may be that what we’re doing is pushing the envelope of PHP/Java symbiosis, but if the open-source rival can satisfy our needs (programatically, at least), why can’t the heavy-duty commercial system? Stay tuned for updates.