ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Using PEAR and PHPUnit - Learning PHP, MySQL, & JavaScript

by Robin Nixon

If you’re going to use PHP as a web development language, why not make use of the wealth of packages that have already been written for it? The community has turned out in force to write an enormous amount of add-ons, a whole host of which have been combined in PEAR (the PHP Extension and Application Repository). Among these submissions is MDB2, a powerful package that makes it easier to access MySQL. Table E.1, “Categories of PEAR packages (number in each category)” lists some of the PEAR packages.

JavaScript: The Good Parts book cover

This excerpt is from Learning PHP, MySQL, and JavaScript . Discover how the powerful combination of PHP and MySQL provides an easy way to build modern websites complete with dynamic data and user interaction. You'll also learn how to add JavaScript to create rich Internet applications and websites.

buy button

Table E.1. Categories of PEAR packages (number in each category)

Authentication (8)Filesystem (5)Math (19)Streams (2)

Caching (2)

Gtk Components (4)

Networking (55)

Structures (30)

Console (7)

Gtk2 Components (7)

Numbers (2)

System (8)

Database (31)

HTML (40)

Payment (4)

Text (19)

Date and Time (22)

HTTP (14)

PEAR (18)

Tools & Utilities (9)

Encryption (13)

Images (19)

PEAR Website (5)

Validate (29)

Event (2)

Internationalization (6)

PHP (20)

Web Services (40)

File Formats (33)

Mail (8)

Semantic Web (5)

XML (32)


Installation

Installation of PEAR will vary according to which operating system you are using. A Linux/Unix machine (especially if XAMPP has been installed on it as described in Chapter 2, Setting Up a Development Server) will generally be ready to go after issuing just a couple of commands. But Windows and Mac OS X require a little more work.

Windows

The EasyPHP setup that you installed in Chapter 2, Setting Up a Development Server comes packaged with a version of PEAR that you can install by selecting Start→Programs→Accessories and then right-clicking on the Command Prompt and choosing the “Run as Administrator” option. You must have administrative privileges to install PEAR.

Now navigate to C:\Program Files\EasyPHP 3.0\php\, then run the batch file go-pear.bat by typing the following (and then pressing Return):

go-pear

During installation, accept the defaults by pressing Return whenever you’re asked to do something. Figure E.1, “Installing PEAR on Windows” shows the installation process.

Figure E.1. Installing PEAR on Windows

Installing PEAR on Windows

Next you need to install PEAR’s database helper package, called MDB2, by typing in the following (see Figure E.2, “Installing the PEAR MDB2 package”):

pear install MDB2

Figure E.2. Installing the PEAR MDB2 package

Installing the PEAR MDB2 package

To finish your installation, install the MDB2 add-on driver that understands how to interact with MySQL. To do this, type in the following (see Figure E.3, “Installing the PEAR MySQL MDB2 driver”):

pear install -f MDB2_Driver_mysql

Figure E.3. Installing the PEAR MySQL MDB2 driver

Installing the PEAR MySQL MDB2 driver

If you receive an error message at any point from a pop-up window that says “Invalid configuration directive” while installing PEAR or MDB2, you should be able to safely ignore it by clicking the OK button.

Finally, as there appears to be a problem with file and path locations in EasyPHP 3.0, you need to type the following command to copy the PEAR files to a location where they can be found by PHP:

xcopy /E pear\*.* includes

Mac OS

Most Macs come supplied with a version of PEAR, but often it’s out of date. The safest bet is to ensure that you have the latest version by visiting http://pear.php.net/go-pear in your browser and then using Save As to save the file that loads into your browser as go-pear.php in your MAMP htdocs folder. Once saved, ensure that you have MAMP running and enter the following into your browser’s address bar:

http://localhost/go-pear.php

Now all you have to do is click on the Next >> button to see the main installation screen (see Figure E.4, “Installing Pear and MDB2”). Ensure that the MDB2 checkbox is checked and then edit the Installation prefix field to read:

/Applications/MAMP

Figure E.4. Installing Pear and MDB2

Installing Pear and MDB2

Finally, scroll to the bottom of the page and click the Install button. You can now sit back and watch the installation as it progresses.

Once the installation completes, you will need to add the PEAR installation path to your include path. To do this, open up the file /Applications/MAMP/conf/php5/php.ini in a text or program editor and locate the line that reads:

include_path = ".:/Applications/MAMP/bin/PHP5/lip/php"

Now change the string after include_path = to read:

".:/Applications/MAMP/bin/PHP5/lip/php:/Applications/MAMP/PEAR"

Once you have done this, pull up the MAMP control panel and stop and restart the servers. If prompted, you may also have to enter your Mac password, too.

The last part of the installation involves downloading and adding the MySQL driver to PEAR. To do this, call up the Terminal and type the following. The output on the Terminal will look like Figure E.5, “Installing the PEAR MySQL MDB2 driver”.

/Applications/MAMP/bin/pear install MDB2_Driver_mysql

Figure E.5. Installing the PEAR MySQL MDB2 driver

Installing the PEAR MySQL MDB2 driver

Linux/Unix

If you installed the XAMPP package in Chapter 2, Setting Up a Development Server, you already have PEAR installed. However, you will need to install the MDB2 database access package and the MySQL driver for it. To do this, you should need to issue only the following two commands:

pear install MDB2
pear install MDB2_Driver_mysql

Creating a Connect Instance

With all of PEAR, the MDB2 package, and the MySQL driver installed, you can start to take advantage of these new additions. But to do so, you need to understand what MDB2 is providing you with: a layer of abstraction.

In other words, MDB2 knows everything about accessing any major brand of database program you may have installed. You simply use a common set of commands and tell MDB2 which database to access. This means you can migrate to another SQL database such as PostgreSQL and will only have to install the new MDB2 driver and change a single line of code in your PHP file to be up and running again.

You connect to a MySQL database using MDB2 with code such as the following, where $db_username and the other $db_ variables have already been read in from the login.php file:

require_once 'MDB2.php';

$dsn = "$db_username:$db_password@$db_hostname/$db_database";
$mdb2 = MDB2::connect("mysql://$dsn");

The require_once line loads MDB2. In the next line, the variable $dsn stands for data source name and is an identifier for the database. It comprises username:password@hostname/database. The variable $mdb2 is an object returned by calling the connect method within the MDB2 class. Recall that as mentioned in Chapter 5, PHP Functions and Objects, the double colon (::) token indicates a class to be used on the left and a method to call from that class to the right.

The full string passed to the connect method is as follows:

mysql://username:password@hostname/database

The mysql:// at the head of the string identifies the MDB2 driver to use and hence the type of database to access. If, for example, you were using a PostgreSQL database you would replace the head with pgsql://. The possible database types supported (as long as you install the drivers) are fbsql, ibase, mssql, mysql, mysqli, oci8, pgsql, querysim, and sqlite.

To check whether the program successfully connected to the database, you can issue a call to the PEAR isError method, like this:

if (PEAR::isError($mdb2))
    die("Unable to connect to MySQL: " . $mdb2->getMessage());

Here the $mdb2 object is passed to the isError method, which returns TRUE if there is an error. In that case the die function is called, and an error message is issued before calling the getMessage method from within the $mdb2 object to output the last message, describing the error encountered.

Querying

Once you have an MDB2 object in $mdb2, you can use it to query the database. Instead of calling the mysql_query function, call the query method of the $mdb2 object as follows (assuming that the variable $query has already been assigned a query string):

$result = $mdb2->query($query);

Fetching a Row

The variable $result, returned by the query method, is another object. To fetch a row from the database, just call the object’s fetchRow method like this:

$row = $result->fetchRow();

You can also determine the number of rows in $result using the numRows method like this:

$rows = $result->numRows();

Closing a Connection

To close an MDB2 database connection, call the disconnect method of the $mdb2 object:

$mdb2->disconnect();

Hopefully you now have the hang of this new object-oriented approach to accessing MySQL. So let’s look at how the sqltest.php program in Example 10.8, “Inserting and deleting using sqltest.php” can be rewritten using PEAR’s MDB2 package (see Example E.1, “Inserting and deleting using MDB2: sqltest_mdb2.php”, sqltest_mdb2.php).

Example E.1. Inserting and deleting using MDB2: sqltest_mdb2.php

<?php // sqltest_mdb2.php
require_once 'login.php';

require_once 'MDB2.php';

$dsn = "mysql://$db_username:$db_password@$db_hostname/$db_database";
$options = array('debug' => 2);
$mdb2 = MDB2::connect($dsn,$options);

if (PEAR::isError($mdb2))
    die("Unable to connect to MySQL: " . $mdb2->getMessage());

if (isset($_POST['author']) &&
    isset($_POST['title']) &&
    isset($_POST['category']) &&
    isset($_POST['year']) &&
    isset($_POST['isbn']))
{
    $author   = get_post('author');
    $title    = get_post('title');
    $category = get_post('category');
    $year     = get_post('year');
    $isbn     = get_post('isbn');

    if (isset($_POST['delete']) && $isbn != "")
    {
        $query = "DELETE FROM classics WHERE isbn='$isbn'";

        if (!$mdb2->query($query))
            echo "DELETE failed: $query<br />" .
            $mdb2->getMessage() . "<br /><br />";
    }
    else
    {
        $query = "INSERT INTO classics VALUES" .
        "('$author', '$title', '$category', '$year', '$isbn')";

        if (!$mdb2->query($query))
            echo "INSERT failed: $query<br />" .
            $mdb2->getMessage() . "<br /><br />";
    }
}

echo <<<_END

<form action="sqltest_mdb2.php" method="post"><pre>
  Author <input type="text" name="author" />
   Title <input type="text" name="title"/ >
Category <input type="text" name="category" />
    Year <input type="text" name="year" />

    ISBN <input type="text" name="isbn" />
         <input type="submit" value="ADD RECORD" />
</pre></form>
_END;

$query = "SELECT * FROM classics";
$result = $mdb2->query($query);

if (!$result)     die ("Database access failed: " . $mdb2->getMessage());
$rows = $result->numRows();

for ($j = 0 ; $j < $rows ; ++$j)
{
    $row = $result->fetchRow();
    echo <<<_END

<pre>
  Author $row[0]
   Title $row[1]
Category $row[2]
    Year $row[3]
    ISBN $row[4]
</pre>
<form action="sqltest_mdb2.php" method="post">
<input type="hidden" name="delete" value="yes" />
<input type="hidden" name="isbn" value="$row[4]" />
<input type="submit" value="DELETE RECORD" /></form>

_END;
}

$mdb2->disconnect();

function get_post($var)
{
    return mysql_real_escape_string(@$_POST[$var]);
}
?>

Looking through this code, you should see that very little has been changed from the nonobject-oriented version of the program, other than replacing the database accessing functions with calls to methods contained within the $mdb2 object, and objects returned from them. The differences have been highlighted in bold.

I recommend that you try modifying sqltest.php for yourself to use MDB2, as in the example, and then save it as sqltest_mdb2.php to test it.

Using PEAR, you can save yourself a considerable amount of work, but if you still prefer not to use it, at least you will now recognize PEAR packages when you see them used in other programs and will know how to work with them.

Adding Other PEAR Packages

With PEAR properly installed on your system, you are able to install additional packages from the command line by using one of the commands in Table E.2, “Installing PEAR packages on different systems”, where package is the name of a PEAR package, as listed at the web page http://pear.php.net/packages.php.

Table E.2. Installing PEAR packages on different systems

System

Command

Windows

"C:\Program Files\EasyPHP 3.0\php\pear" install package

Mac OS X

/Applications/MAMP/bin/pear install package

Linux Unix

pear install package


To determine which packages are installed, replace install package in Table E.2, “Installing PEAR packages on different systems” with the word list and the output should be similar to the following:

INSTALLED PACKAGES, CHANNEL PEAR.PHP.NET:
=========================================
PACKAGE            VERSION STATE
Archive_Tar        1.3.2   stable
Console_Getopt     1.2.3   stable
MDB2               2.4.1   stable
MDB2_Driver_mysql  1.4.1   stable
PEAR               1.7.2   stable
Structures_Graph   1.0.2   stable

Unit Testing with PHPUnit

Now that you are familiar with object-oriented programming, it’s a good idea to get a taste of unit testing. This is a method of code testing that verifies whether individual units of source code are working correctly.

Unit testing provides the following benefits:

  • It allows for automation of the testing process.

  • It reduces the difficulty of discovering errors within more complex code.

  • Testing is often enhanced because attention is given to each unit.

Install PHPUnit with the PEAR installer. To do this, go to a Command or Terminal prompt, ensure that you have Administrator or Superuser privileges, and issue the two lines of code in Table E.3, “Commands for Installing PHPUnit on different systems”, according to your operating system. (On Windows systems, ignore and close any pop-up “Invalid configuration directive” alerts that may appear.)

Table E.3. Commands for Installing PHPUnit on different systems

System

Instructions to type from an Administrator Command prompt or a Terminal window

XP/Vista

(4 instructions)

cd \Program Files\EasyPHP 3.0\php

pear channel-discover pear.phpunit.de

pear install phpunit/PHPUnit

Xcopy /E pear\*.* includes

Mac OS X

(3 instructions)

cd /Applications/MAMP/bin

pear channel-discover pear.phpunit.de

pear install phpunit/PHPUnit

Linux/Unix

(2 instructions)

pear channel-discover pear.phpunit.de

pear install phpunit/PHPUnit


The two main commands register the PEAR channel on your system, download PHPUnit, and install it. On Windows, you need the additional XCOPY command to resolve a file and path bug in EasyPHP 3.0. Press the A key if prompted to “Overwrite (Yes/No/All)?”.

You are then ready to take on some powerful bug testing. But first, let’s look at how you might perform testing without PHPUnit. So, for example, consider the case of testing PHP’s in-built array and the function sizeof and its alias count. For a newly created array, sizeof should return a value of 0 and then increase by 1 for every new element added, as in Example E.2, “Testing array and sizeof”.

Example E.2. Testing array and sizeof

$names = array();
echo sizeof($names) . "<br />";
$names[] = 'Bob';
echo count($names) . "<br />";  // count is an alias of sizeof

As you would expect, the output from this code is:

0
1

In Example E.3, “Modified Example E.2, “Testing array and sizeof” to output OK/Not OK”, this code is expanded to support automatic interpretation by writing the comparison of the expected and actual values, outputting “OK” if the value is correct and “Not OK” if it isn’t.

Example E.3. Modified Example E.2, “Testing array and sizeof” to output OK/Not OK

$names = array();
echo sizeof($names) == 0 ? "OK<br />" : "Not OK<br />";
$names[] = 'Bob';
echo sizeof($names) == 1 ? "OK<br />" : "Not OK<br />";

Helpful as this code is, there’s an even better way to handle errors, which is to display a message only when a value is incorrect. Example E.4, “Modified Example E.2, “Testing array and sizeof” to throw an exception” uses function assertTrue to do this by throwing an exception.

Example E.4. Modified Example E.2, “Testing array and sizeof” to throw an exception

$names = array();
assertTrue(sizeof($names) == 0);
$names[] = 'Bob';
assertTrue(sizeof($names) == 1);

function assertTrue($condition)
{
    if (!$condition) throw new Exception('Assertion failed.');
}

Now we’ve arrived at a fully automated test, let’s look at how we would rewrite it using PHPUnit (see Example E.5, “PHPUnit testing in action”).

Example E.5. PHPUnit testing in action

require_once 'PHPUnit/Framework.php';

class ArrayTest extends PHPUnit_Framework_TestCase
{
    public function testNewArrayIsEmpty()
    {
        $names = array();
        $this->assertEquals(0, sizeof($names));
    }

    public function testArrayContainsAnElement()
    {
        $names = array();
        $names[] = 'Bob';
        $this->assertEquals(1, sizeof($names));
    }
}

$testObject = new ArrayTest;
$testObject->testNewArrayIsEmpty();
$testObject->testArrayContainsAnElement();

The first thing to notice is that PHPUnit/Framework.php has been included in order to make the PHPUnit classes available to the program. After that, the program defines a new class that extends the PHPUnit_Framework_TestCase class. This new class contains two methods: one for testing a newly created array and another for testing an array containing an element.

The rules for writing PHPUnit tests are:

  • The tests for a class called Class go into a class with the name ClassTest.

  • ClassTest usually inherits from PHPUnit_Framework_TestCase.

  • The tests are public methods that are named testSomethingDescriptive.

  • Inside the test methods, assertion methods such as assertEquals are used to assert that an actual value matches an expected value.

And there you have it. The three lines of code at the end of Example E.5, “PHPUnit testing in action” create a new test object called $testObject and then call each of the object’s methods in turn. All being well, this program will display nothing, so to see the output from PHPUnit, try changing the 0 or 1 parameters in the assertEquals calls to other values.

A comprehensive and easy-to-follow manual on PHPUnit is available at http://www.phpunit.de. Click “Read the documentation” to view it in either HTML or PDF format.

If you enjoyed this excerpt, buy a copy of Learning PHP, MySQL, & JavaScript.

Copyright © 2009 O'Reilly Media, Inc.