Problem Solution Discussion Writing an Object-Oriented MySQL Interface for PHP

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