Heres an exam ple t hat print s each row of a result set as a com m a-separat ed list of values, w it h each
NULL
value print ed as t he st ring
NULL
: Statement s = conn.createStatement ;
s.executeQuery SELECT name, birth, foods FROM profile; ResultSet rs = s.getResultSet ;
ResultSetMetaData md = rs.getMetaData ; int ncols = md.getColumnCount ;
while rs.next loop through rows of result set {
for int i = 0; i ncols; i++ loop through columns {
String val = rs.getString i+1; if i 0
System.out.print , ; if rs.wasNull
System.out.print NULL; else
System.out.print val; }
System.out.println ; }
rs.close ; close result set s.close ; close statement
2.10 Writing an Object-Oriented MySQL Interface for PHP
2.10.1 Problem
You want an approach for writ ing PHP script s t hat is less t ied t o PHPs nat ive MySQL- specific funct ions.
2.10.2 Solution
Use one of t he abst ract int erfaces t hat are available, or writ e your own.
2.10.3 Discussion
You m ay have not iced t hat t he Perl, Pyt hon, and Java operat ions t hat connect t o t he MySQL server each ret urn a value t hat allows you t o process queries in an obj ect - orient ed m anner.
Perl has dat abase and st at em ent handles, Pyt hon has connect ion and cursor obj ect s, and Java uses obj ect s for everyt hing in sight : connect ions, st at em ent s, result set s, and m et adat a.
These obj ect - orient ed int erfaces all are based on a t wo- level archit ect ure. The t op level of t his archit ect ure provides dat abase- independent m et hods t hat im plem ent
dat abase access in a port able way t hat s t he sam e no m at t er which dat abase m anagem ent syst em youre using, be it MySQL, Post greSQL, Oracle, or what ever. The lower level consist s
of a set of drivers, each of which im plem ent s t he det ails for a part icular dat abase syst em . The t w o- level archit ect ure allows applicat ion program s t o use an abst ract int erface t hat is not t ied
t o t he det ails involved wit h accessing any part icular dat abase server. This enhances port abilit y of your program s, because you j ust select a different lower- level driver t o use a different t ype
of dat abase. That s t he t heory, at least . I n pract ice, perfect port abilit y can be som ewhat elusive:
•
The int erface m et hods provided by t he t op level of t he archit ect ure are consist ent regardless of t he driver you use, but it s st ill possible t o issue SQL st at em ent s t hat
cont ain const ruct s support ed only by a part icular server. For MySQL, a good exam ple is t he
SHOW
st at em ent t hat provides inform at ion about dat abase and t able st ruct ure. I f you use
SHOW
wit h a non- MySQL server, an error is t he likely result .
•
Low er - level drivers oft en ext end t he abst ract int erface t o m ake it m ore convenient t o get at dat abase- specific feat ures. For exam ple, t he MySQL driver for DBI m akes t he
m ost recent
AUTO_INCREMENT
value available as an at t ribut e of t he dat abase handle so t hat you can access it as
dbh-{mysql_insertid}
. These feat ures oft en m ake it easier t o writ e a program init ially, but at t he sam e t im e m ake it less port able and
require som e rewrit ing should you port t he program for use wit h anot her dat abase syst em .
Despit e t hese fact ors t hat com prom ise port abilit y, t he t wo- level archit ect ure provides significant benefit s for Perl, Pyt hon, and Java program m ers. I t would be nice t o use t his
approach when writ ing PHP script s, t oo, but PHP it self provides no such support . I t s int erface t o MySQL consist s of a set of funct ions, and t hese are inherent ly non-port able because t heir
nam es all are of t he form
mysql_ xxx
. To work around t his, you can writ e your own dat abase abst ract ion m echanism .
That is t he purpose of t his sect ion. I t shows how t o writ e an obj ect -orient ed PHP int erface t hat hides m any MySQL- specific det ails and is relat ively dat abase independent —cert ainly m ore so
t han PHPs funct ion-based MySQL int erface. As discussed here, t he int erface is writ t en specifically for MySQL, but if you want t o adapt it for use wit h a different dat abase, you should
be able t o do so by supplying a different set of underlying class m et hods. I f you want t o writ e PHP script s in a dat abase- independent fashion, but prefer not t o writ e
your own int erface, you can use a t hird- part y abst ract ion int erface. One such is t he dat abase- access class t hat is a part of t he PHP Ext ension and Add- on Reposit ory PEAR . PEAR is
included wit h current releases of PHP 4. The following discussion show s how t o w rit e a
MySQL_Access
class t hat im plem ent s an obj ect -orient ed int erface t o MySQL, and a
Cookbook_DB_Access
class t hat is built on t op of
MySQL_Access
but aut om at ically supplies default values for connect ing t o t he
cookbook
dat abase. I f youre not fam iliar wit h PHP classes, you m ay want t o consult t he Classes and Obj ect s chapt er of t he PHP m anual for background inform at ion. The prim ary goal of t his
class int erface is t o m ake it easier t o use MySQL by reducing t he num ber of operat ions your script s m ust perform explicit ly:
•
The int erface aut om at ically est ablishes a connect ion t o t he MySQL server if you issue a query wit hout connect ing first ; you need never issue a connect call explicit ly. The
connect ion param et ers m ust be specified som ehow, of course, but as well see, t hat can be done aut om at ically as well.
•
The int erface provides aut om at ic error checking for MySQL calls. This is m ore convenient t han checking for t hem yourself, and helps elim inat e one of t he m ost
com m on problem s wit h PHP script s: failure t o check for dat abase errors on a consist ent basis. The default behavior is t o exit wit h an error m essage when a problem
occurs, but you can override t hat if you want t o handle errors yourself.
•
When you reach t he end of a result set while fet ching rows, t he class aut om at ically releases t he set .
The class-based int erface also provides a m et hod for quot ing dat a values t o m ake t hem safe for use in queries, and a placeholder m echanism so you dont need t o do any quot ing at all if
you dont want t o. These capabilit ies are not present in PHPs nat ive funct ion-based int erface. The following exam ple illust rat es how using an obj ect - orient ed int erface changes t he way you
writ e PHP script s t o access MySQL, com pared t o writ ing funct ion-based script s. A script based on PHPs nat ive funct ion calls t ypically accesses MySQL som et hing like t his:
if conn_id = mysql_connect localhost, cbuser, cbpass die Cannot connect to database\n;
if mysql_select_db cookbook, conn_id die Cannot select database\n;
query = UPDATE profile SET cats=cats+1 WHERE name = Fred; result_id = mysql_query query, conn_id;
if result_id die mysql_error conn_id;
print mysql_affected_rows conn_id . rows were updated\n; query = SELECT id, name, cats FROM profile;
result_id = mysql_query query, conn_id; if result_id
die mysql_error conn_id; while row = mysql_fetch_row result_id
print id: row[0], name: row[1], cats: row[2]\n; mysql_free_result result_id;
A first st ep t oward elim inat ing som e of t hat code is t o replace t he first few lines by calling t he
cookbook_connect
funct ion from t he PHP library file, Cookbook.php, developed in Recipe 2.4
. That funct ion encapsulat es t he connect ion and dat abase select ion operat ions: include Cookbook.php;
conn_id = cookbook_connect ; query = UPDATE profile SET cats=cats+1 WHERE name = Fred;
result_id = mysql_query query, conn_id; if result_id
die mysql_error conn_id; print mysql_affected_rows conn_id . rows were updated\n;
query = SELECT id, name, cats FROM profile; result_id = mysql_query query, conn_id;
if result_id die mysql_error conn_id;
while row = mysql_fetch_row result_id print id: row[0], name: row[1], cats: row[2]\n;
mysql_free_result result_id; A class- based int erface can carry encapsulat ion furt her and short en t he script even m ore by
elim inat ing t he need t o connect explicit ly, t o check for errors, or t o close t he result set . All of t hat can be handled aut om at ically:
include Cookbook_DB_Access.php; conn = new Cookbook_DB_Access;
query = UPDATE profile SET cats=cats+1 WHERE name = Fred; conn-issue_query query;
print conn-num_rows . rows were updated\n; query = SELECT id, name, cats FROM profile;
conn-issue_query query; while row = conn-fetch_row
print id: row[0], name: row[1], cats: row[2]\n;
A class int erface can m ake MySQL easier t o use by reducing t he am ount of code you need t o writ e when creat ing new script s, but it has ot her benefit s as well. For exam ple, it can also
serve as a recipe t ranslat ion aid. Suppose a program in a lat er chapt er is shown in Perl, but youd rat her use in it PHP and t here is no PHP version on t he Cookbook web sit e. Perl DBI is
obj ect orient ed, so youll likely find it easier t o t ranslat e a Perl script int o a PHP script t hat is obj ect orient ed, rat her t han int o one t hat is funct ion based.
2.10.4 Class Overview