7.2 Core Components

PEAR's core components are general-purpose, reliable packages that work with most web servers, database servers, browsers, and operating systems. If you're using PHP 4.3 or later on a Unix system, the PEAR core components and the PEAR installer for adding other packages are already installed and ready for use. For Microsoft Windows, the integration occurred in PHP 4.3.2.

The list of core components can change but at the time of writing it includes:


PEAR base and error handling classes

These are the foundations of other PEAR packages, and you don't need a detailed understanding of them unless you plan to develop your own package. We discuss error handling in our introduction to PEAR DB in the next section.


PEAR Console command-line parsing

Used for non-web scripts.


PEAR DB

Database server abstraction. Discussed in detail in the next section.


HTTP methods

Used to format HTTP-compliant dates, negotiate language, and compress data for fast transfer.


PEAR Mail

Used for mail sending, including platform independence, MIME attachments, and correct email address validation.


PEAR System

Platform-independent commands for making and removing directories and files, concatenating files, and finding the full path of a program.

7.2.1 What's Installed?

Now, let's check the core components distributed with your PHP installation.

7.2.1.1 Unix systems?PHP 4.3.0 and later

For the instructions in this section to work, you must have followed our installation instructions in Appendix A to Appendix C. You also need an active Internet connection.

To check the list of components installed, you need to login as the root user; to do this, type su at a shell prompt and provide the root user password.

If you're working with Mac OS X, type at a shell prompt:

% cd /usr/local/bin

Then, on all systems, type the following at a shell prompt:

% pear list

You'll see a list in the following format:

Installed packages:

===================

+------------------+---------+--------+

| Package          | Version | State  |

| Archive_Tar      | 0.9     | stable |

| Console_Getopt   | 1.0     | stable |

| DB               | 1.3     | stable |

| HTTP             | 1.2     | stable |

| Mail             | 1.0.1   | stable |

| Net_SMTP         | 1.0     | stable |

| Net_Socket       | 1.0.1   | stable |

| PEAR             | 1.0b3   | stable |

| XML_Parser       | 1.0     | stable |

| XML_RPC          | 1.0.4   | stable |

You may find that the versions you have are different. This isn't a problem.

7.2.1.2 Microsoft Windows?PHP 4.3.2 and later

For the instructions in this section to work, you must have followed our installation instructions in Appendix A to Appendix C. You also need an active Internet connection.

Start by launching a command window. You can do this by running the file command.com or running a DOS prompt window (if it's listed in your Accessories group under Programs in your Start Menu).

To run command.com, click on the Start Menu, then the Run option. Now, type command.com and press Enter.

In your command window, change directory to the pear install directory. If you've followed our install instructions in Appendix A to Appendix C, type:

C:\> cd c:\Progra~1\EasyPH~1\php\pear

Then, type the following:

C:\> pear.bat list

A list of installed packages is shown in the following format:

INSTALLED PACKAGES:

===================

PACKAGE           VERSION  STATE

Archive_Tar       0.9      stable

Console_Getopt    1.0      stable

DB                1.3      stable

HTTP              1.2      stable

Mail              1.0.1    stable

Net_SMTP          1.0      stable

Net_Socket        1.0.1    stable

PEAR              1.0b3    stable

XML_Parser        1.0      stable

XML_RPC           1.0.4    stable

You may find that the versions you have are different. This isn't a problem.

If you want to close the command window, type exit. However, you'll need this window later in this chapter, so keeping it open is fine.

7.2.2 Using PEAR DB

In most PHP applications, one of the server-specific database libraries is used to access the database server. In Chapter 6, we showed you how to access the MySQL sever using the MySQL library functions. In this section, we show you how to develop reasonably server-independent scripts using PEAR's DB component. We also use the PEAR DB class throughout our online winestore in Chapter 16 through Chapter 20.

7.2.2.1 Should I use PEAR DB?

If you want server-independent function calls, PEAR's DB component is ideal because the code usually doesn't change when you change the underlying database server. However, there are sometimes needs for small changes, such as catering for different function return values or rewriting code because a database server doesn't support a feature. For example, only some of the underlying servers support the tableInfo( ) method for returning metadata about table attributes.

If you don't use PEAR DB, changing database servers can be time-consuming. If you switch between similar libraries?such as the MySQL and PostgreSQL libraries?then updating the code usually doesn't require too much work: it's largely a case of changing the mysql_ prefix to a pgsql_ prefix, and perhaps tackling complex querying in a different way. However, if you change to a less-similar library?such as one of the Oracle libraries or ODBC?then more work is required even for the simple tasks.

PEAR DB will almost give you function library independence, but it won't give you complete database server independence. SQL isn't the same between any two servers: as we discussed in Chapter 1, combinations of the features of SQL-89, SQL-92, and SQL-99 are often implemented, and many servers have proprietary statements for tasks. For example, MySQL supports entry-level SQL-92, but uses proprietary clauses such as LIMIT and AUTO_INCREMENT, and attribute types such as LONGINT and TIMESTAMP. Even if you use PEAR DB, it's almost impossible (and probably not sensible) to avoid using proprietary SQL.

For many developers, it isn't clear whether database abstraction offers an advantage: many developers don't bother writing server-independent code because their SQL is tied to the database server. In fact, if you're sure you'll be using one database server for the lifetime of an application, we recommend using the proprietary library so that you can take advantage of the specialized functions designed for the database server.

In most of the applications we've developed, we've used the MySQL library functions outlined in Chapter 6. However, to illustrate how to use PEAR DB and to give you code that will work with minimal modification for other database servers, we've used it to develop our online winestore in Chapter 16 through Chapter 20.

7.2.2.2 Getting started

In this section, we assume you've read Chapter 6 and are familiar with the basic querying processes and the core MySQL library functions. Also, you'll need to be familiar with the basic object-oriented PHP features discussed in Chapter 4.

Example 7-1 shows how to connect, query, and retrieve results using PEAR DB. The example is an extended version of Example 6-1 that includes error handling.

Example 7-1. Using PEAR DB to query the winestore database
<!DOCTYPE HTML PUBLIC

                 "-//W3C//DTD HTML 4.01 Transitional//EN"

                 "http://www.w3.org/TR/html401/loose.dtd">

<html>

<head>

  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">

  <title>Wines</title>

</head>

<body><pre>

<?php

   require_once "DB.php";

   require "db.inc";



   $dsn = "mysql://{$username}:{$password}@{$hostName}/{$databaseName}";



   // Open a connection to the DBMS

   $connection = DB::connect($dsn);



   if (DB::isError($connection))

      die($connection->getMessage( ));



   // (Run the query on the winestore through the connection

   $result = $connection->query("SELECT * FROM wine");



   if (DB::isError($result))

      die ($result->getMessage( ));



   // While there are still rows in the result set, fetch the current 

   // row into the array $row

   while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC))

   {

     // Print out each element in $row, that is, print the values of 

      // the attributes

      foreach ($row as $attribute)

         print "{$attribute} ";



      print "\n";

   }

?>

</pre>

</body>

</html>

As discussed previously, there's no need to download or install any extra components to use the PEAR core components. The PEAR DB class is used within a script by requiring it:

require_once "DB.php";

If you find that your PHP engine can't find DB.php, it's likely that your include_path directive in your php.ini configuration file doesn't include the PEAR directory. Check the installation instructions for your platform in Appendix A to Appendix C.

Connecting to a database server uses a URL-style string. In the example, this string consists of the familiar $username, $password, $hostName, and $databaseName from the db.inc require file:

$dsn = "mysql://{$username}:{$password}@{$hostName}/{$databaseName}";

For the defaults in the db.inc file, this gives the string:

mysql://fred:shhh@localhost/winestore

We store the string in a variable with the acronym $dsn to signify this is the data source name . The prefix mysql:// indicates the MySQL server, and the string fred:shhh@localhost specifies the username, password, and host parameters that are used with the mysql_connect( ) and mysql_pconnect( ) functions. Rather than use the separate mysql_select_db( ) function to use the database, it's specified following a forward slash character.

The connection itself is established with the method DB::connect( ) :

$connection = DB::connect($dsn);

The notation DB:: means that the method connect( ) is a member of the class DB. Error handling is discussed in the next section.

The DB::query( ) method works similarly to mysql_query( ), taking the SQL query as a parameter and returning a result resource that can be used to retrieve data from a SELECT query:

$result = $connection->query("SELECT * FROM wine");

The result rows are retrieved using the DB::fetchRow( ) method:

while ($row = $result->fetchRow(DB_FETCHMODE_ASSOC))

The method behaves similarly to mysql_fetch_array( ). The parameter DB_FETCHMODE_ASSOC specifies that the return array has associatively-accessible elements that are named with the database attribute names or attribute aliases; however, this isn't important in this example because we use foreach to iteratively process all elements of the array.

7.2.2.3 Handling errors in PEAR DB

The error status of any database server method can be tested using DB::isError( ) . Unlike in the MySQL library, this method can be used regardless of whether a connection has been established yet or not. If an error occurs, the getMessage( ) method can be used to retrieve a descriptive string as in the following example:

// Open a connection to the DBMS

$connection = DB::connect($dsn);



if (DB::isError($connection))

  die($connection->getMessage( ));

The getMessage( ) method is part of the core PEAR error class. The method works similarly for testing errors from queries:

$result = $connection->query("SELECT * FROM wine");



if (DB::isError($result))

   die ($result->getMessage( ));

Note that the method can be used with many types of objects: for connections, we use the $connection object and for results we use $result.

If a method or parameter is unsupported by the underlying database server, you'll find that the following error is reported:

DB_error: database not capable

7.2.2.4 Essential functions for accessing MySQL with PEAR DB

Methods for interacting with database servers using PEAR DB are the subject of this section. We've included the essential methods, and omitted those that are less-frequently used, redundant, or aren't used with MySQL. More detail on all methods can be found in the PEAR manual at http://pear.php.net/manual/en/core.db.php.

For most PEAR DB methods, we've noted which native MySQL functions are used in the library to implement the functionality that's described. Chapter 6 presents the detail of the underlying MySQL functions, and you'll find that the limitations and advantages of those functions affects PEAR DB too. We recommend reading the MySQL function notes in conjunction with the PEAR DB descriptions.


mixed DB::affectedRows( )

Returns the number of rows that were affected by the previous database-modifying query. Returns a DB_ERROR object on failure. (The return type mixed indicates that the type of the value returned by the method isn't always one type.)

For MySQL, the underlying function is mysql_affected_rows( ). However, unlike mysql_affected_rows( ) a workaround ensures affectedRows( ) provides the correct value when all rows are deleted from a table. DB_Result::numRows( ) should be used for queries that do not modify the database.

This method, and database modifications in general, are discussed in Chapter 8.


mixed DB::connect(string dsn [, bool persistent])

Connect to a DBMS using the parameters specified in the data source name dsn. If persistent is true and the DBMS supports persistent connections, a persistent connection is used, otherwise the default of false returns a non-persistent connection. The function returns a database connection object on success or a DB_error object on failure.

The data source name dsn is specified in the following format or one of its simplifications:

dbms://username:password@protocol+host:port/database

where the following is applicable:


dbms

The type of DBMS to connect to. The options that are supported in release 1.17 are dbase (dBase file support), fbase (FrontBase), ibase (InterBase), ifx (Informix), mssql (Microsoft SQL server), msql (mSQL), mysql (MySQL 3.x), mysql4 (MySQL 4.x), oci8 (Oracle OCI8), odbc (ODBC), pgsql (PostgreSQL), and sybase (Sybase).


username

The username to connect with.


password

The password associated with the username.


protocol

Communication protocol such as tcp or unix. Often omitted.


host

The hostname of the DBMS server. Often localhost for the local machine.


port

The port to connect to on the host.


database

The name of the database to use on the connection.

Several simplifications of the dsn are possible and are described in the manual. By far the most common format uses the default protocol and port:

dbms://username:password@host/database

For MySQL, mysql_connect( ) is used if the second parameter is false or omitted, and mysql_pconnect( ) is used when it's true.


mixed DB::createSequence(string name)

Creates a new sequence name. Returns the result of the query that creates the sequence, or a DB_ERROR object on failure. See DB::nextId( ) for an introduction to sequences. An example of using sequences in presented in Chapter 8.


mixed DB::dropSequence(string name)

Deletes a sequence name. Returns the result of the query that deletes the sequence, or a DB_ERROR object on failure. See DB::nextId( ) for an introduction to sequences. An example of using sequences in presented in Chapter 8.


mixed DB_Result::fetchRow([int mode [, int row]])

Retrieve a row of results using an optional mode and an optional row. By default, the rows are returned into a numerically-accessed array. The function returns the row on success and NULL when there is no more data to fetch. On error, it returns a DB_ERROR object.

The mode can be one of DB_FETCHMODE_ORDERED (a numerically accessed array, which is the default when no parameter is supplied), DB_FETCHMODE_ASSOC (an associatively accessed array), or DB_FETCHMODE_OBJECT (an object with attribute names as properties). An optional row to retrieve can be specified after the mode.

For MySQL, the current release uses mysql_fetch_array( ) to provide the functionality of DB_FETCHMODE_ASSOC and DB_FETCHMODE_OBJECT, and its numeric-only sibling mysql_fetch_row( ) for numeric access. The function mysql_data_seek( ) is used to retrieve specific rows.


bool DB::isError(DB_error object)

Reports true when the parameter object is of type DB_error, and false otherwise. It is often used with the return values of DB::connect( ) and DB::query( ) as the parameter. The error is usually output using getMessage( ) as shown in the previous section.


mixed DB::nextId(string name [, bool create])

Returns the next unique identifier value associated with the string name or a DB_ERROR object on failure. The identifier that is returned is usually used as input into an INSERT statement to create a new row with a unique primary key value. If the sequence name does not exist, it is automatically created if create is set to true (which is the default). Sequences can be manually created with DB::createSequence( ) and deleted with DB::dropSequence( ).

In the PHP MySQL library, the mysql_insert_id( ) function returns the unique value associated with an INSERT operation after the operation has occurred. In contrast, the DB::nextId( ) method reports a table-independent value prior to the INSERT operation occurring. Database modifications are discussed further in Chapter 8, and an example of using DB::nextId( ) is presented there.


int DB_Result::numRows( )

Returns the number of rows associated with a query result object, or a DB_ERROR object on failure. DB::affectedRows( ) should be used for queries that modify the database.

In MySQL, the function mysql_num_rows( ) provides the underlying functionality.


mixed DB::query (string query [, array parameters])

Executes an SQL query. An optional array of parameters can be provided to prepare a query; we discuss query preparation in Appendix F.

For MySQL, the function returns a MySQL result resource for SELECT queries on success, the constant DB_OK for other successful queries, and a DB_ERROR object on failure.


string DB::quote(string query)

Escapes a query string so that it can be used a parameter to DB::query( ). It returns a copy of the input string that has any special characters escaped. For a MySQL connection, the function uses mysql_real_escape_string( ) in PHP 4.3 or later, and mysql_escape_string( ) otherwise.


mixed DB_Result::tableInfo(DB_Result result [, int mode])

Returns an array of metadata about the attributes of the result set using an optional mode. Returns a DB_ERROR object on failure. The function works for MySQL, MS-SQL, FrontBase, and PostgreSQL.

With no second parameter, the array that is returned is two-dimensional. The first dimension is the attribute number, and the second has the following associative keys:


name

The name of the attribute.


type

The attribute type.


len

The attribute maximum length.


flags

A string containing a list of attribute flags. For example, in MySQL the flags can include not_null, primary_key, auto_increment, and timestamp.


table

The name of the table associated with the attribute.

You can pass a second parameter, DB_TABLEINFO_ORDER, which makes one additional element available. This element can be retrieved through the associative key order. Its second dimension is filled with the names of the attributes and the values are set to the attribute numbers. This allows you to determine the attribute number using the attribute name, so that the metadata can be accessed in two steps by attribute name. For example, to access the attribute length metadata for the attribute wine_name:

$array = $result->tableInfo($result, DB_TABLEINFO_ORDER);



// What's the attribute number of wine_name?

$number = $array["order"]["wine_name"];



// Print out the length of the wine_name

print "Attribute length: {$array[$number]["len"]}";

Another second parameter is available, but it is unnecessary if you use attribute aliases in your queries to avoid duplicate attribute names as discussed in Chapter 6.

The function is similar in concept to mysql_fetch_field( ) but it returns an array instead of an object. It is implemented for MySQL using the non-object based siblings of mysql_fetch_field( ) (which are listed in Chapter 6 as functions we don't recommend you use).