accessing databases with class - dev shed

Upload: anup-mishra

Post on 08-Apr-2018

218 views

Category:

Documents


0 download

TRANSCRIPT

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    1/11

    Accessing Databases with Class

    By Kristian Kohntopp

    All materials Copyright 19972002 Developer Shed, Inc. except where otherwise noted.

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    2/11

    Table of ContentsBuilding library components with objects........................................................................................................1

    A database access class as example...................................................................................................................3

    Extending classes.................................................................................................................................................5

    Queries and query results..................................................................................................................................6

    Footnotes..............................................................................................................................................................9

    Accessing Databases with Class

    i

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    3/11

    Building library components with objects

    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

    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 youwould 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.

    Building library componen... 1

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    4/11

    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 nonintrusive 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.

    Accessing Databases with Class

    Building library componen... 2

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    5/11

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    6/11

    to change the current database to $Database employing a MySQL "use" command.

    Accessing Databases with Class

    A database access class a... 4

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    7/11

    Extending classes

    Our class is now useable, albeit not productively. We are able to establish a database link, but without query()

    and next_record() functions we are unable to make use of it. We are going to use the class anyway, to show

    how to set it up for a production environment. Example 5 shows a workable, but inconvenient method how

    you could deploy the class.

    Example 5: A nonrecommended method to configure and deploy DB_Sql.

    This is not a recommended method to configure a class for use, because after creation of the object you have

    to set up all variables within that object manually and you have to do it over and over on each page where youare using it. It would be much nicer if we were able to define a class that is just like DB_Sql, but with

    different connect parameters. In fact we can easily do this: We can extend any given class and base the

    definition of a new class on any single existing class. Example 6 shows the definition of a class DB_Sample,

    which performs exactly the same connect as Example 5. Example 6b shows how to use this class.

    Example 6: Definition of a new class DB_Sample, based on DB_Sql

    // DB_Sample is just like DB_Sql, only different.. :) class DB_Sample extends DB_Sql {

    var $Host = "localhost"; var $User = "kris"; var $Password = ""; var $Database =

    "sampleserv"; }

    Example 6b: Using DB_Sample.

    // This include file contains the definiton of DB_Sql require("db_mysql.inc"); // This include

    file contains the definition of DB_Sample require("local.inc"); // Create a database connection

    object $db = new DB_Sample; // Connection to database... $db>connect();

    DB_Sample is not empty, but contains exactly the same variables and functions as DB_Sql, although these arenot written down explicitly in the class definition. The magic is in the class definition: DB_Sample extends

    DB_Sql, that is, DB_Sample starts as a simple copy of DB_Sql. Within the class body of DB_Sample certain

    definitions of DB_Sql are overwritten, specifically we redefine the database connection parameters.

    On use of DB_Sample as shown in Example 6b, the database connection will be created using these redefined

    parameters. Unlike Example 5 we do not have to mention these parameters on each page, but DB_Sample

    "automatically" knows the appropriate parameters and does the right thing. If we had to change the connection

    parameters, we could do so by editing a single file, local.inc. This is very convenient, especially in larger

    projects.

    Extending classes 5

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    8/11

    Queries and query results

    Example 7: Adding the functions query(), next_record() and seek() to DB_Sql.

    function query($Query_String) { $this>connect(); # printf("Debug: query = %s
    n",

    $Query_String); $this>Query_ID = mysql_query($Query_String,$this>Link_ID);

    $this>Row = 0; $this>Errno = mysql_errno(); $this>Error = mysql_error(); if

    (!$this>Query_ID) { $this>halt("Invalid SQL: ".$Query_String); } return

    $this>Query_ID; } function next_record() { $this>Record =

    mysql_fetch_array($this>Query_ID); $this>Row += 1; $this>Errno = mysql_errno();

    $this>Error = mysql_error(); $stat = is_array($this>Record); if (!$stat) {

    mysql_free_result($this>Query_ID); $this>Query_ID = 0; } return $stat; } function

    seek($pos) { $status = mysql_data_seek($this>Query_ID, $pos); if ($status) $this>Row =

    $pos; return; }

    Example 7 adds three functions to our DB_Sql class which make the class actually useful: Finally we are able

    to make use of the database link for sending queries to the database and retrieving the results. For this

    purpose, query() calls connect() internally to create the database link. This saves you a manual call to connect

    if you are using the database class later in your pages.

    If you activate the disabled printf() statement within query(), you get a list of all queries on a page as they are

    made on the page. This is very useful for debugging your SQL and to get a feeling how expensive the creation

    of a certain page actually is.

    When you send a query, a new $Query_ID is being generated and the current row number is reset to zero.

    After that we check the error state to see if the query was legal. If not, we raise an error and halt the program.If the query was legal, we return the query id to the caller.

    The next_record() function can be used to retrieve the query result. The function reads the current result row,

    increments the row counter and checks for errors. If the result set has been read completely, we call

    mysql_free_result() to save application memory. next_record() returns "true" as long as there are still result

    records so that you may use the function as the condition of a while()loop.

    Using seek() you may move within the current result set and read a single result multiple times (unless it has

    been freed) or skip certain records at the beginning of the result set. Example 8 shows how to use query() and

    next_record() to get data from a table.

    Example 8: Query to the table ad_customers within the database sampleserv.

    ID Graphics Link Desc

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    9/11

    $db>Record["link"] ?>

    Example 8b: Definition of table ad_customers.

    CREATE TABLE ad_customers ( id int(11) DEFAULT '0' NOT NULL auto_increment,

    name varchar(127) DEFAULT '' NOT NULL, graphics varchar(127) DEFAULT '' NOT

    NULL, link varchar(127) DEFAULT '' NOT NULL, desc varchar(127) DEFAULT '' NOT

    NULL, PRIMARY KEY (id), KEY name (name), ); CREATE TABLE banner_rotate ( pos

    int(11) DEFAULT '0' NOT NULL, );

    Many webservers keep rotating banner ads at the top or bottom of their pages. These banners are present as

    GIF images with known path names. In our sample database we keep a table named ad_customers, which lists

    information about each banner. We keep a banner name, the pathname to the GIF image on disk, a link target

    that is to be activated when the banner is clicked and a description text for the images altattribute.

    Example 8 shows, how to read this table using the DB_Sample class. We are generating an HTML table with

    all banner names and related data. Example 8b shows the database table definitions involved. The second

    table, banner_rotate, contains just a single row with a single column with the currently active ad banner

    number. The rotation program uses this information to control the banner rotation.

    The actual banner rotation program (Example 9) is just a single function banner_rotate(), which does nothing

    more than incrementing the pos counter from the banner_rotate table and produces the appropriate image tag.

    The locking shown in that function is specific to MySQL (MySQL does not do proper transactions).

    The function is pretty linear: It locks the banner_rotate table and updates the counter using an SQL updatestatement. After that it uses a SQL select statement to read that counter value and unlocks the table. Using the

    counter value corrected modulo the number of actual ad customers the appropriate customer data from the

    ad_customers table is selected and an image tag is created which is embedded into a link. We do not directly

    jump into the customers presentation using this link, but we refer the user to another local program which

    registers the click and additional data about the users browser. It is that second program which generates a

    Location header to redirect the user to the final external destination. This is the only way to measure the

    efficiency of a banner and to get provable data for the customer.

    Example 9: Function banner_rotate() to rotate banner ads.

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    10/11

  • 8/7/2019 Accessing Databases with Class - Dev Shed

    11/11

    Footnotes

    Footnote 1

    The complete code for DB_Sql is part of PHPLIB and is available for download from the PHPLIB website.

    PHPLIB also contains versions of this class for other databases such as Postgres, ODBC and Oracle.

    Footnotes 9

    http://phplib.shonline.de/