Accessing Databases with Class (
Page 1 of 3 )
PHP offers a large number of native interfaces to database
servers from different vendors. All of them are accessed in a
similar, but subtly different way, which has its reason in the
differences of the underlying C API and in the historic
development of PHP's database support. In this article, you will
learn how to create an easier interface to SQL databases by
employing the class and object features of PHP.If you are starting to create a library of reuseable PHP functions, you will
soon encounter some typical problems. For example, you will experience name
clashes if you start mixing your own components with that of other developers:
sooner or later some foreign function will have the same name as one of yours or
will use a global variable that you are using, too.
But you may even experience problems if you are using only selfmade
components. Imagine for example a set of functions that is manipulating a
database link. Example 1 shows such a set of functions which shares a common
variable named $Link_ID.
Example 1: A hypothetical set of functions for accessing a database
<?php
$Link_ID = 0; // ID of current database link
$Query_ID = 0; // ID of currently active query
$error = 0; // current database error state
function connect() { ... }
function query() { ... }
function next_record() { ... }
function num_rows() { ... }
?>
If you define an include with these functions, you could
easily write the query() function in a way that it checks for a valid $Link_ID.
If there is no valid link, query() should call connect() to establish that link.
query() would update the $Query_ID and next_record() would automatically
reference that variable to work with it. To make this work, all of these
functions would have to share common variables. Because PHP does not know
pointer or reference variable types, it is necessary for these variables to be
global.
This is going to be a problem as soon as you have a page that needs two
concurrently active queries, because these queries would fight for the global
variables. If you had PHP pointer or reference types, you could call connect(),
query() and next_record() with the with references to the appropriate variables.
But in this case you would have gained nothing, because you would be back to
dealing with $Link_IDs and $Query_IDs yourself.
PHP offers a different approach to solve this problem: You may group a number
of variables and functions together into a package and name that package. The
package itself uses no names in your global namespace. You may then create
copies of the packages and insert them under any variable name into your global
namespace, much like you can mount disks anywhere in a directory hierarchy.
Creating a package of variables and functions is called "declaring a class" in
PHP and mounting a package copy in your namespace is called "creating an object
as an instance of a class". Example 2 shows how a class is defined using the
"class" keyword and how objects are created using the "new" operator. Compare
this to the definition shown in Example 1 and see how they match one to one.
Example 2: Definition of a class DB_MiniSQL with call properties of
Example 1. Creation of two object instances $db1 and $db2 of that class.
<?php
class DB_MiniSQL {
var $Link_ID = 0; // ID of current database link
var $Query_ID = 0; // ID of currently active query
var $error = 0; // current database error state
function connect() { ... }
function query() { ... }
function next_record() { ... }
function num_rows() { ... }
}
$db1 = new DB_MiniSQL;
$db2 = new DB_MiniSQL;
?>
A declaration of a class does not use names in the global variable namespace
- the class declaration only establishes a plan how to build DB_MiniSQL
variables, but does not actually build such variables. PHP now knows what makes
up a DB_MiniSQL object, if it were asked to make one.
We ask PHP to make such objects using the "new" operator and name them by
assigning them to a variable. We can have multiple objects of the same type
under different names - $db1 and $db2 in our example. Unlike the situation in
Example 1, this does not lead to name clashes, because both variables differ in
their "pathnames" (Remember the disk mountpoint analogy!): $db1->Link_ID and
$db2->Link_ID are obviously different variables. With function calls it is
the same: $db1->query() sends a query via one link, $db2->query() via the
other link.
For library developers this is an important feature, since it allows us to
encapsulate the definition of our functionality in a non-intrusive way. We leave
it to the user of our functions to decide how many copies of them are needed and
under what name. For users of such a library it is easy to handle this: They
just have to get used to choose an appropriate name for the imported functions
(for example by writing "$db1 = new DB_MiniSQL") and then always use the
functions under that name prefix (for example by writing "$db1->query()").
But that's the view from the outside, from the users side of the code. From
the inside it is a little bit different. Why? Imagine the query() function
wanted to check the value of $Link_ID. It would have to know its own name,
because it would have to decide whether to access $db1->Link_ID or
$db2->Link_ID or another, completely different object. That would be quite
inconvenient to code. All local object variables and functions are available
under the prefix $this instead, independent of the actual name of the object. So
in our case, query() could simply access its own Link_ID as $this->Link_ID
and call its own connect() function as $this->connect(). Note that the
variable name is "this->Link_ID" and thus it is written as
"$this->Link_ID", not as "$this->$Link_ID". This is a very common
beginners error.