Using An Object Oriented Approach for Implementing PHP Classes to Interact with Oracle

Exclusive offer: get 50% off this eBook here
PHP Oracle Web Development: Data processing, Security, Caching, XML, Web Services, and Ajax

PHP Oracle Web Development: Data processing, Security, Caching, XML, Web Services, and Ajax — Save 50%

A practical book and eBook guide to combining the power, performance, scalability, and reliability of the Oracle Database with the ease of use, short development time, and high performance of PHP

$29.99    $15.00
by Yuli Vasiliev | November 2007 | AJAX BPEL MySQL SOA Web Services Content Management Open Source Oracle PHP Web Development

This article by Yuli Vasiliev examines the object-oriented approach for developing PHP/Oracle applications, as an efficient means to reduce the development time and complexity, and increase the maintainability and flexibility of your applications.

In the following sections, you will learn how to create a simple PHP class to interact with Oracle and then how that class can be modified and reused in different scripts.

Before you start developing object-oriented solutions with PHP 5, it is important to understand that its object model provides more features than PHP 4's object model. Like most object-oriented languages, PHP 5 allows the developer to take advantage of interfaces, abstract classes, private/public/protected access modifiers, static members and methods, exception handling, and other features that were not available in PHP 4. But perhaps the most important thing to note about the object-oriented improvements of PHP 5 is that objects are now referenced byhandle, and not by value.

Building Blocks of Applications

As you no doubt know, the fundamental building block in any object-oriented language is a structure called a class.

A class is a template for an object. It describes the data and behavior of its instances (objects). During run time, an application can create as many instances of a single class as necessary.

The following diagram conceptually depicts the structure of a class.

Using An Object Oriented Approach for Implementing PHP Classes to Interact with Oracle

You might find it handy to think of an object-oriented application as a building made of blocks, where classes are those blocks. However, it is important to note that all blocks in this case are exchangeable. What this means is that if you are not satisfied with the implementation of a certain class, you can use a relevant class that has the same Application Programming Interface (API) but a different implementation instead. This allows you to increase the reusability and maintainability of your application, without increasing the complexity.

The intent of the example discussed in this section is to illustrate how you can rewrite the implementation of a class so that this doesn't require a change in the existing code that employs this class. In particular, you'll see how a custom PHP 4 class designed to interact with Oracle can be rewritten to use the new object-oriented features available in PHP 5.

Creating a Custom PHP Class from Scratch

To proceed with the example, you first need to create a PHP 4 class interacting with Oracle. Consider the following dbConn4 class:

    <?php
//File: dbConn4.php
class dbConn4 {
var $user;
var $pswd;
var $db;
var $conn;
var $query;
var $row;
var $exec_mode;
function dbConn4($user, $pswd, $db, $exec_mode= OCI_COMMIT_ON_SUCCESS)
{
$this->user = $user;
$this->pswd = $pswd;
$this->db = $db;
$this->exec_mode = $exec_mode;
$this->GetConn ();
}
function GetConn()
{
if(!$this->conn = OCILogon($this->user, $this->pswd, $this->db))
{
$err = OCIError();
trigger_error('Failed to establish a connection: ' .
$err['message']);
}
}
function query($sql)
{
if(!$this->query = OCIParse($this->conn, $sql))
{
$err = OCIError($this->conn);
trigger_error('Failed to parse SQL query: ' .
$err['message']);
return false;
}
else if(!OCIExecute($this->query, $this->exec_mode))
{
$err = OCIError($this->query);
trigger_error('Failed to execute SQL query: ' .
$err['message']);
return false;
}
return true;
}
function fetch()
{
if(!OCIFetchInto($this->query, $this->row, OCI_ASSOC))
{
return false;
}
return $this->row;
}
}
?>

In the above script, to define a class, you use the class keyword followed by the class name. Then, within curly braces, you define class properties and methods. Since this class is designed to work under both PHP 4 and PHP 5, all the class properties are defined with the var keyword. Declaring a property with var makes it publicly readable and writable. In PHP 5, you would use the public keyword instead.

In PHP 4, you define the class constructor as a function with the same name as the class itself. This still works in PHP 5 for backward compatibility. However, in PHP 5, it's recommended that you use __construct as the constructor name.

In the above example, the class constructor is used to set the member variables of a class instance to the values passed to the constructor as parameters. Note the use of the self-referencing variable $this that is used here to access the member variables of the current class instance.

Within class methods, you can use $this, the special variable that points to the current instance of a class. This variable is created automatically during the execution of any object's method and can be used to access both member variables of the current instance and its methods.

Then, you call the GetConn method from within the constructor to obtain a connection to the database. You reference the method using the $this variable. In this example, the GetConn method is supposed to be called from within the constructor only. In PHP 5, you would declare this method as private.

To obtain a connection to the database in this example, you use the OCILogon function. In PHP 5, you would use the oci_connect function instead. The query method defined here takes an SQL string as the parameter and then parses and executes the query. It returns true on success or false on failure. This method is supposed to be called from outside an object. So, in PHP 5, you would declare it as public.

Finally, you define the fetch method. You will call this method to fetch the results retrieved by a SELECT statement that has been executed with the query method.

Testing the Newly Created Class

Once written, the dbConn4 class discussed in the preceding section can be used in applications in order to establish a connection to an Oracle database and then issue queries against it as needed. To see this class in action, you might use the following PHP script. Assuming that you have saved the dbConn4 class as the dbConn4.php file, save the following script as select.php:

    <?php
//File: select.php
require_once 'dbConn4.php';
require_once 'hrCred.php';
$db = new dbConn4($user, $pswd, $conn);
$sql="SELECT FIRST_NAME, LAST_NAME FROM employees";
if($db->query($sql)){
print 'Employee Names: ' . '<br />';
while ($row = $db->fetch()) {
print $row['FIRST_NAME'] . '&nbsp;';
print $row['LAST_NAME'] . '<br />';
}
}
?>

The above select.php script employs the employees table from the hr/hr demonstration schema. So, before you can execute this script, you must create the hrCred.php file that contains all the information required to establish a connection to your Oracle database using the HR account. The hrCred.php file should look as shown below (note that the connection string may vary depending on your configuration):

    <?php
//File: hrCred.php
$user="hr";
$pswd="hr";
$conn="(DESCRIPTION=
(ADDRESS_LIST=
(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))
)
(CONNECT_DATA=(SID=orcl)(SERVER=DEDICATED))
)";
?>

Once you have created the hrCred.php script, you can execute the select.php script. As a result, it should output the names of employees from the employees table in the hr/hr demonstration schema.

Taking Advantage of PHP 5's Object-Oriented Features

Turning back to the dbConn4 class, you may have noticed that it was written for PHP 4. Of course, it still can be used in new applications written for PHP 5. However, to take advantage of the new object-oriented features available in PHP 5, you might want to rewrite this class as follows:

    <?php
//File: dbConn5.php
class dbConn5 {
private $user;
private $pswd;
private $db;
private $conn;
private $query;
private $row;
private $exec_mode;
public function __construct($user, $pswd, $db,
$exec_mode= OCI_COMMIT_ON_SUCCESS)
{
$this->user = $user;
$this->pswd = $pswd;
$this->db = $db;
$this->exec_mode = $exec_mode;
$this->GetConn();
}
private function GetConn()
{
if(!$this->conn = oci_connect($this->user, $this->pswd,
$this->db))
{
$err = oci_error();
trigger_error('Failed to establish a connection: ' .
$err['message']);
}
}
public function query($sql)
{
if(!$this->query = oci_parse($this->conn, $sql)) {
$err = oci_error($this->conn);
trigger_error('Failed to execute SQL query: ' .
$err['message']);
return false;
}
else if(!oci_execute($this->query, $this->exec_mode)) {
$err = oci_error($this->query);
trigger_error('Failed to execute SQL query: ' .
$err['message']);
return false;
}
return true;
}
public function fetch()
{
if($this->row=oci_fetch_assoc($this->query)){
return $this->row;
}
else {
return false;
}
}
}
?>

As you can see, the implementation of the class has been improved to conform to the new standards of PHP 5. For instance, the above class takes advantage of encapsulation that is accomplished in PHP 5—like most other object-oriented languages—by means of access modifiers, namely public, protected, and private. The idea behind encapsulation is to enable the developer to design the classes that reveal only the important members and methods and hide the internals. For instance, the GetConn method in the dbConn5 class is declared with the private modifier because this method is supposed to be called only from inside the constructor when a new instance of the class is initialized; therefore, there is no need to allow client code to access this method directly.

Since the implementation of the newly created dbConn5 class is different from the one used in dbConn4, you may be asking yourself: "Does that mean we need to rewrite the client code that uses the dbConn4 class as well?" The answer is obvious: you don't need to rewrite client code that uses the dbConn4 class since you have neither changed the Application Programming Interface (API) of the class nor,more importantly, its functionality. Thus, all you need to do in order to make the select.php script work with dbConn5 is simply replace all the references to dbConn4 with references to dbConn5 throughout the script.

PHP Oracle Web Development: Data processing, Security, Caching, XML, Web Services, and Ajax A practical book and eBook guide to combining the power, performance, scalability, and reliability of the Oracle Database with the ease of use, short development time, and high performance of PHP
Published: July 2007
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Functionality and Implementation

As you learned from the preceding example, you can easily replace one class with another in a calling script given that both the classes deliver the same API and the same functionality. Practically, this means that you can improve the implementation of the class that is used in existing applications until this changes the API of that class or its functionality. Of course, you can still extend the API of the class with some new public methods in order to add new functionality that might be employed in new applications using the class. However, in this case you must ensure that the new class still works with old clients.

Essentially, the above technique lets you improve object code without having to rewrite any client code. However, it is worth remembering that sometimes it is very hard to retain the functionality of the class when rewriting its implementation. Bear in mind that the implementation in essence forms the functionality. In some situations, like the one discussed in the previous section, you can rewrite the implementation of the class so that this doesn't change its functionality. However, in other situations, rewriting the implementation of the class, results in a change in the functionality provided by the class.

Imagine what would happen if you decided to use the oci_new_connect function in place of oci_connect in the GetConn method of the dbConn5 class. This would allow you to establish distinctly new connections to the database within a script using the class, and thereby separate transactions. In this case, you could apply commits and rollbacks to the specified connection only.

By contrast, using oci_connect assumes that commits and rollbacks are applied to all open transactions in the page. This is because oci_connect doesn't establish a new connection if you have already established a connection with the same parameters. Instead, it returns the identifier of the already opened connection.

The following script will generate different results depending on which connection function you use in the dbConn5 class.

    <?php
//File: testDbConn.php
require_once 'dbConn5.php';
require_once 'hrCred.php';
$exec_mode=OCI_DEFAULT;
$job_id='FI_MGR';
$db = new dbConn5($user, $pswd, $conn, $exec_mode);
$sql="UPDATE employees SET salary = salary*1.1
WHERE job_id='".$job_id."'";
if(!$db->query($sql)){
print 'Failed to update the employees table';
}
$newDb = new dbConn5($user, $pswd, $conn, $exec_mode);
$sql="SELECT last_name, salary FROM employees
WHERE job_id='".$job_id."'";
if($newDb->query($sql)){
print '<table>';
while ($row = $newDb->fetch()) {
print '<tr>';
print '<td>'.$row['LAST_NAME'].'</td>';
print '<td>'.$row['SALARY'].'</td >';
print '</tr>';
}
print '</table>';
}
?>

If you run the testDbConn.php script shown above, it should produce the following output:

Greenberg 12000

In this example, you don't need to worry about setting Greenberg's salary to its original value. When the script execution ends, the transaction is automatically rolled back. This is because you execute the UPDATE operation in OCI_DEFAULT mode and then you don't commit the opened transaction explicitly.

Now, if you replace the oci_connect function in the GetConn method of the dbConn5 class with the oci_new_connect function, the testDbConn.php script will produce the following result:

Greenberg 12000

So, if you make such a replacement in the dbConn5 class—oci_new_connect in place of oci_connect—then you will no longer be able to use the dbConn5 class in place of dbConn4 in any application; you won't be able to use the dbConn5 class in the applications that are designed to work with transactions, assuming that connections are shared at the page level.

As you can see, having two classes that have the same API doesn't automatically mean that these classes provide the same functionality. Thus, care must be taken when you are replacing one class with another in existing applications.

Reusability

Once a class has been written and debugged, you can then reuse it over and over in new projects. In the Building Blocks of Applications section earlier in this article, you learned how to use the dbConn5 class in a script that selects data from the database.

In fact, you are not limited to a SELECT operation and can use the query method of the dbConn5 class to perform any SQL statement against the database. For instance, you might use the following script to insert a row into the departments table under the hr/hr database schema:

    <?php
//File: insert.php
require_once 'dbConn5.php';
require_once 'hrCred.php';
$db = new dbConn5($user, $pswd, $conn);
$sql="INSERT INTO departments VALUES(320, 'DB design',
null, 1700)";
if($db->query($sql)){
print 'data have been submitted';
}
else {
print 'failed to submit data';
}
?>

This example shows the simplest way in which you can take advantage of code reuse. Specifically, it demonstrates how to reuse an existing class to solve a similar problem in another script. Similarly, you might use the query method to perform DDL operations against the database.

Handling Exceptions

For handling errors, PHP 5 offers a new mechanism that is completely different from that in PHP 4. In PHP 5, you can create and throw an instance of the built-in Exception class in response to an error that has occurred in your object code.

Throwing an exception terminates method execution and makes the appropriate Exception instance available to the calling code. To be able to handle an exception thrown from inside an object's method, the calling code must call that method inside a try block. It also must have an appropriate catch block to handle the exception thrown. It might look like the following figure.

Using An Object Oriented Approach for Implementing PHP Classes to Interact with Oracle

Modifying an Existing Class to use Exceptions

Now that you have a rough idea of how PHP 5's exceptions work in an object‑oriented environment, it is time to look at an example of how exceptions can be used. Turning back to the dbConn5 class discussed in the Taking Advantage of PHP 5's Object-Oriented Features section earlier, you might rewrite it to support PHP 5's new exception model as shown below:

    <?php
//File: dbConn5e.php
class dbConn5e {
private $user;
private $pswd;
private $db;
private $conn;
private $query;
private $row;
private $exec_mode;
public function __construct($user, $pswd, $db,
$exec_mode= OCI_COMMIT_ON_SUCCESS)
{
$this->user = $user;
$this->pswd = $pswd;
$this->db = $db;
$this->exec_mode = $exec_mode;
$this->GetConn();
}
private function GetConn()
{
if(!$this->conn = oci_connect($this->user, $this->pswd,
$this->db))
{
$err = oci_error();
throw new Exception('Could not establish a connection: ' .
$err['message']);
}
}
public function query($sql)
{
if(!$this->query = oci_parse($this->conn, $sql)) {
$err = oci_error($this->conn);
throw new Exception('Failed to execute SQL query: ' .
$err['message']);
}
else if(!oci_execute($this->query, $this->exec_mode)) {
$err = oci_error($this->query);
throw new Exception('Failed to execute SQL query: ' .
$err['message']);
}
return true;
}
public function fetch()
{
if($this->row=oci_fetch_assoc($this->query)){
return $this->row;
}
else {
return false;
}
}
}
?>

In the above class, you obtain the error as an associative array, with the help of the oci_error function, just as you did in the preceding examples earlier in this book. Then, you assign the error message to the exception object thrown here.

As you can see, the dbConn5e class no longer uses the trigger_error function that you saw in the dbConn5 class discussed earlier. Instead, when something goes wrong, the code in the dbConn5e class throws an exception using the throw new Exception syntax. As previously mentioned, when an exception is thrown, it becomes available to the client context. On the client side, you should place any code that might throw an exception inside the try block. When you have defined the try block, you must then define at least one catch block, which will be used to handle thrown exceptions. So, client code no longer needs to check the return value of a called method to see whether an error has occurred during the execution of the method—the code within a catch block takes care of the thrown exception.

Note that the fetch method of the dbConn5e class still returns false on failure to obtain the next row from the result data, rather than generating an exception. This makes sense, since generating an exception just because all the retrieved rows have been fetched might not seem appropriate.

Now that you have written the dbConn5e class, you might want to write a script to test that class. The following select_e.php script makes use of the newly created dbConn5e class.

    <?php
//File: select_e.php
require_once 'dbConn5e.php';
require_once 'hrCred.php';
try {
$db = new dbConn5e($user, $pswd, $conn);
$sql="SELECT last_name FROM employees";
$db->query($sql);
print 'Employee Name: ' . '<br />';
while ($row = $db->fetch()) {
print $row['LAST_NAME'] . '<br />';
}
}
catch (Exception $e) {
print $e->getMessage();
exit();
}
?>

In the select_e.php script shown in the listing, you no longer need to check whether the query method of dbConn5 has returned true. Instead, you simply wrap the code that might throw an exception in the try block. Then, you define the catch block to handle the thrown exceptions. The script will simply print the appropriate error message when an exception is thrown.

Distinguishing between Different Error Types

In a real-world application you might want your application to distinguish between the different error types in the catch block. One way to do this is to use theuser-defined exception flag that can be passed as an optional parameter to the Exception constructor.

The dbConn5e2 class shown below is a revision of the dbConn5e class discussed in the preceding section. The new dbConn5e2 class can throw exceptions that will differ by error type.

    <?php
//File: dbConn5e2.php
class dbConn5e2 {
private $user;
private $pswd;
private $db;
private $conn;
private $query;
private $row;
private $exec_mode;
const CONNECTION_ERROR = 1;
const SQLEXECUTION_ERROR = 2;
function __construct($user, $pswd, $db)
{
$this->user = $user;
$this->pswd = $pswd;
$this->db = $db;
$this->exec_mode = $exec_mode;
$this->GetConn();
}
function GetConn()
{
if(!$this->conn = oci_connect($this->user, $this->pswd,
$this->db))
{
$err = oci_error();
throw new Exception('Could not establish a connection: '
. $err['message'], self::CONNECTION_ERROR);
}
}
function query($sql)
{
if(!$this->query = oci_parse($this->conn, $sql)) {
$err = oci_error($this->conn);
throw new Exception('Failed to execute SQL query: '
. $err['message'], self::SQLEXECUTION_ERROR);
}
else if(!oci_execute($this->query, $this->exec_mode)) {
$err = oci_error($this->query);
throw new Exception('Failed to execute SQL query: '
. $err['message'], self::SQLEXECUTION_ERROR);
}
return true;
}
function fetch()
{
if($this->row=oci_fetch_assoc($this->query)){
return $this->row;
}
else {
return false;
}
}
}
?>

Now, you can improve the catch block in the client code so that it recognizes different error categories. In the select_e2.php script shown below, a failure to connect to the database results in a fatal error, which means the script will terminate. On the other hand, failure to perform a query against the database leads only to generation of the appropriate warning message and execution continues after the catch block.

    <?php
//File: select_e2.php
require_once 'dbConn5e2.php';
require_once 'hrCred.php';
try {
$db = new dbConn5e2($user, $pswd, $conn);
$sql="SELECT last_name FROM employees";
$db->query($sql);
print 'Employee Name: ' . '<br />';
while ($row = $db->fetch()) {
print $row['ENAME'] . '<br />';
}
}
catch (Exception $e) {
if ($e->getCode() == dbConn5e2::CONNECTION_ERROR) {
die($e->getMessage());
}
else if ($e->getCode() == dbConn5e2::SQLEXECUTION_ERROR) {
print $e->getMessage();
}
}
//Continue execution
?>

It is interesting to note that using user-defined exception codes is not the only way to separate different types of exceptions in your application. Alternatively, you might define subclasses of the built-in Exception class and then use multiple catch blocks to catch different classes of exceptions.

Summary

In this article,  we have seen not only PHP 5's major object-oriented features required to build an efficient object-oriented PHP application interacting with Oracle, but also some of Oracle's native object-relational features that make it possible for you to organize data into object structures at the database level.

PHP Oracle Web Development: Data processing, Security, Caching, XML, Web Services, and Ajax A practical book and eBook guide to combining the power, performance, scalability, and reliability of the Oracle Database with the ease of use, short development time, and high performance of PHP
Published: July 2007
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

About the Author :


Yuli Vasiliev

Yuli Vasiliev is a software developer, freelance author, and consultant currently specializing in open-source development, Oracle technologies, and service-oriented architecture (SOA). He has over 10 years of software development experience as well as several years of technical writing experience. He wrote a series of technical articles for Oracle Technology Network (OTN) and Oracle Magazine.

Contact Yuli Vasiliev

Books From Packt

RESTful PHP Web Services
RESTful PHP Web Services

Magento: Beginner's Guide
Magento: Beginner's Guide

MODx Web Development
MODx Web Development

Oracle SOA Suite Developer's Guide
Oracle SOA Suite Developer's Guide

Learning Joomla! 1.5 Extension Development
Learning Joomla! 1.5 Extension Development

Choosing an Open Source CMS: Beginner's Guide
Choosing an Open Source CMS: Beginner's Guide

Seam 2.x Web Development
Seam 2.x Web Development

WCF Multi-tier Services Development with LINQ
WCF Multi-tier Services Development with LINQ

 

 

 

 

Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software