|
The Hitchhiker's Guide to PHP
The PHP 5 Data Object (PDO) Abstraction Layer and Oracle
by Wez Furlong, wez@php.net
An overview of the new PHP Data Objects (PDO) data abstraction layer, with a focus on Oracle, from one of its original developers.
Require-PHP: 5.0
Require-Other: Oracle 8 or higher client libraries
Download PDO for Oracle (Windows): php_pdo.dll, php_pdo_oci.dll
Download PDO for Oracle (Unix): pdo, pdo_oci
Download sample code
Introducing PDO
PHP is largely a volunteer driven project; although there are a handful of regular "core" contributors, none of us are paid full time to sit down and develop PHP. Couple this with the fact that we're spread out across the globe and you'll see how it can be difficult to coordinate long-term development. As a result, PHP has evolved largely based on the short-term needs of the occasional motivated individual, with reasons varying from experimentation to "I have a deadline tomorrow". While this has generally improved PHP with each step, the long term results have been fragmentarywith the database extensions being a prime example.
There is no real consistency between the various database extensions (oci, mysql, postgresql, mssql etc.) and in some cases even within the extensions. Almost all of them are doing the same kind things using different code that is tightly coupled to the underlying database API. This results in code that is much harder to maintain, and poses a big problem for PHP, since our (the PHP core and extension developers) human resources are extremely limited.
Due to the ever increasing popularity and success of PHP, the maintainers of each of the major PHP database extensions were present in Germany at the LinuxTag 2003 conference, where we talked about what we wanted to see in the future of PHP. During the discussion of PHP's random-evolution problem, we identified a number of goals for database access in PHP:
- Provide a lightweight, clear, convenient API
- Unify the common features of the various RDBMS libraries, but don't exclude the more advanced features.
- Provide optional heavier abstraction/compatibility through PHP scripts.
We called this concept PHP Data Objects (PDO), since we hoped to apply the advanced OO features of the Zend Engine 2 (the heart of PHP 5) to achieve some of the nicer aspects of the API.
The concept of a data abstraction layer in PHP is nothing new; Google tells me there are "about 83,200" matches for "PHP database abstraction". It seems to be the holy grail of a large number of PHP developers, and part of the reason for this is due to our fragmented API. If you've ever tried to use a third party abstraction layer for anything really serious, you'll usually find that they are massively over-engineered for the task at hand something that manifests as either a massive learning curve before you can make use of it, or as a very slow interface, passing parameters through many layers of script function calls before it hits the native database API. Often, they suffer from a combination of both of these things.
Why do they have this problem? They're trying to do too much, perhaps even the impossible. We decided to be pragmatic and implement only the most common database API features as our base, and allow PDO drivers to expose their product specific features as regular extension functions.
Why use PDO?
Most of the people that have heard the rumours of a database abstraction extension immediately got hung up on the abstraction aspects of itwill we be parsing SQL and translating it to the appropriate back-end dialect? How will we handle feature X or feature Y and so on. It might come as a shock then, to hear that we haven't lost sleep over it in PDO; we don't want to make things totally uniform as this is impossible without limiting yourself to the lowest common denominator.
If PDO isn't a total abstraction layer, why else should you consider using it?
- Performance. PDO capitalized from the start on the successes and failures of the existing database extensions. Since PDO is all-new code, we've had the opportunity to start afresh and design-in performance, leveraging the latest features of PHP 5.
- Power. PDO is designed to provide common database features as a base, while still providing convenient access to the unique features of your RDBMS.
- Easy. PDO is designed to make it easy for you to work with your database. The API does not intrude on your code, while at the same time clearly indicating what each function call is doing.
- Runtime Extensible. The PDO extension is modular, allowing you to load a driver for your database backend at runtime, without having to recompile or reinstall your entire PHP distribution. For example, the PDO_OCI extension implements the Oracle database API on behalf of the PDO extension. There are also drivers for MySQL, PostgreSQL, ODBC and Firebird, with more in development.
You might be wondering how PDO compares to other popular abstraction layers out there, for example, PEAR DB or ADODB. PDO is lighter than either of those, both in terms of API and in terms of performance, but doesn't go so far when it comes to providing uniformity across database back-ends; that is the job of, for example, the PEAR MDB 2 abstraction layer which will handle a large number of portability issues.
Where can I get PDO?
PDO is available through PECL (pronounced "pee-kle", European style), the PHP extension repository. If you're running a Linux style machine, following the instructions below should set you up; Windows installation details can be found a little further on.
Please note that PDO and its drivers are currently in an "alpha" state; this means that we are reasonably sure that there are no major bugs, but that the package is not feature completethere's plenty more we want to add. While we are encouraging you to test it, we really don't recommend putting it into production at this time.
Unix/Linux installation
If you haven't tried PHP 5 before, take a few moments to read through the NEWS and various announcements. On UNIX machines you may have to install or upgrade libxml2; without it, the "pear" package manager tool won't be able to function, and you will have a much harder time installing PDO.
Grab PHP 5, compile and install it. Be sure to specify a prefix other than /usr/local/ so that it doesn't conflict with a PHP 4 installation:
% ./configure --prefix=/usr/local/php5 --with-zlib [other options here]
% make install
Now you can use the "pear" tool to fetch and install PDO and the Oracle driver for PDO. Since PDO is currently marked as alpha, the pear tool will not, by default, download the package. Adding the suffix "-alpha" to the package name informs the pear tool that it is okay to install an alpha version:
% PATH="/technology/usr/local/php5/bin:$PATH"
% pear install PDO-alpha
You need to tell PHP to load the PDO driver from your PHP 5 specific php.ini file. If you used the same prefix as I did, PHP will look for the php.ini file in /usr/local/php5/lib/php.ini. Add the following line to that file:
extension=pdo.so
And now you need to fetch the database specific driver; for Oracle this is called PDO_OCI. From your shell type:
% pear install PDO_OCI-alpha
This also needs to be loaded from your php.ini file; add this line after the line you added above:
extension=pdo_oci.so
And now check to make sure it worked:
% php -m
In the list of modules, you should see PDO and PDO_OCI.
Firewall getting in the way?
If you're behind a firewall, you might have problems obtaining the packages using the pear installer. If that is the case, you may download the packages manually and install them by following these instructions:
% wget http://pecl.php.net/get/PDO
% pear install PDO-0.1.1.tgz
[ add extension=pdo.so to php.ini ]
% wget http://pecl.php.net/get/PDO_OCI
% pear install PDO_OCI-0.1.tgz
[ add extension=pdo_oci.so to php.ini ]
In both cases, you'll need to invoke "pear install" followed by the actual package that was downloaded; the version numbers in the sample above are current at the time of writing but will change as development proceeds.
Windows Installation
If you're running Windows, follow these instructions instead:
- Grab PHP 5 from http://www.php.net/downloads.php#v5 and extract it to C:\php5.
- Get PDO and PDO_OCI from http://snaps.php.net/win32/PECL_5_0/php_pdo.dll and
http://snaps.php.net/win32/PECL_5_0/php_pdo_oci.dll respectively, and put them into C:\php5\ext. Alternatively, you'll find all the PDO drivers in the "Collection of PECL modules for PHP 5.0.0" zip file listed on the PHP 5 download page, along with all the windows versions of all the PECL packages.
- Edit your C:\php5\php.ini file and add:
extension=php_pdo.dll
extension=php_pdo_oci.dll
When you edit your php.ini file, it is important that the PDO extension is loaded before any other PDO driver, otherwise it won't be initialized correctly (it will error out in that case).
If you have a global php.ini file from PHP 4 in your Windows directory, you might run into problems. The best solution is to isolate your PHP 4 installation by moving the php.ini file so that it resides in the same folder as your PHP 4 SAPI; for example, move it to the same folder as php4apache.dll. Please note that not all of the documentation in the PHP 5 distribution is up to date yet; the recommended install procedure as I've described abovedo not copy any DLL's into your windows or system folders as the install.txt file claimseverything is self contained. If you are running apache and get errors about failing to load DLL's, make sure you add C:\php5 to your PATH. Also note that the CGI version of PHP 5 is now named php-cgi.exe.
Connecting to PDO
The first thing to do is create an instance of the PDO class to use as our database handle. It doesn't matter which underlying driver you want to use; you always use the PDO class name. The first parameter to the constructor is the Data Source Name (DSN), the second parameter is the username and the third is the password for that username. The PDO convention for a DSN is to have the name of the PDO driver, followed by a colon, followed by optional driver specific information. In our sample we are loading the OCI driver and not specifying any other information; this causes the default database to be used. For other drivers, such as the ODBC driver, everything after the first colon is used as the ODBC DSN. The MySQL driver interprets its DSN differently again.
If the driver could not be loaded, or a connection failure occurred, a PDOException is thrown so that you can decide how best to handle the failure.
<?php
try {
$dbh = new PDO("OCI:", "scott", "tiger");
} catch (PDOException $e) {
echo "Failed to obtain database handle " . $e->getMessage();
}
?>
There are two optional parameters you may specify in the connection string; the first is the database name and the second is the character set; these correspond to the optional 3rd and 4th parameters that you may have already used in the past with the oci8 extension functions ociconnect() or ociplogon(). To connect to a specific database using a particular character set, you might do the following:
<?php
try {
$dbh = new PDO("OCI:dbname=accounts;charset=UTF-8", "scott", "tiger");
} catch (PDOException $e) {
echo "Failed to obtain database handle " . $e->getMessage();
}
?>
It's worth noting that if you omit the try..catch control structure, and don't have an exception handler defined at a higher level in your application, the script will terminate if your database connection could not be established.
Connection Management
Currently, PDO performs absolutely no connection management of its own, so each call to "new PDO" will establish a new connection to the database. The connection is released when your $dbh variable goes out of scope, or when you assign it the NULL value.
<?php
try {
$dbh = new PDO("OCI:dbname=accounts;charset=UTF-8", "scott", "tiger");
} catch (PDOException $e) {
echo "Failed to obtain database handle " . $e->getMessage();
exit;
}
// do something with the database here
// ...
// now we are done, release the connection
$dbh = null;
?>
Connection caching is planned for the not too distant future of PDO; as with the current oci8 extension, connections to existing servers will be re-used, and within those connections, idle logins will also be re-used. When operating in cached connection mode, releasing the $dbh as shown in the above snippet will mark the login as available for re-use by other connections.
If you're using the ODBC driver to access Oracle, you might be pleased to note that the PDO_ODBC driver supports ODBC connection pooling by default.
PDO in action
The best way to get an overview of a programming API is to see it in action, so let's talk through the attached demo of how to make a batch update (see sample code)
Now that we have successfully connected to Oracle, let's create a table to hold some data. For this example, we're taking a list of PHP extensions and their authors and putting them into a database. The exec() method of the database handle object can be used to issue fast one-shot queries that don't return result-sets, so we use that here to issue the CREATE TABLE query.
For a less contrived example, I've extracted the information about the extensions and their authors from the PHP source code and stored it into a CSV file (see "credits.csv"). This represents a typical scenario: a batch import of data from a CSV file. In our example we will take full advantage of Oracle's prepared statements and bound parameters to achieve an efficient data import script. Before we get into the example, it's important to understand how PDO handles transactions.
Transactions in PDO
Oracle has a sensible default mode of operation: when you connect you're in an implicit transaction where your changes don't take full effect until you commit that transaction. In addition to the standard benefits of transactions (Atomicity, Consistency, Isolation, DurabilityACID), the database server doesn't need to rebuild the indices and other internal structures after each update that you make; it can defer until you commit. This leads to faster code. Oracle is nice.
Unfortunately, not every database vendor supports transactions, and since PDO aims to support those in a fairly portable way it defaults to running in auto-commit mode. When auto-commit mode is on, the database driver will implicitly commit each successful update. When you call $dbh->beginTransaction(), you're requesting that auto-commit be turned off until you call either $dbh->commit() or $dbh->rollBack(), depending on how things worked out for your code. If the underlying driver doesn't support transactions, a PDOException will be thrown.
If something bad happens and PHP errors out, if your script exits with a pending transaction, or when you close a database handle, PDO will call $dbh->rollBack() automatically on any pending transaction. This behaviour reduces the risk of committing possibly undefined or broken data to the database and is the standard semantic for dealing with abandoned transactions.
Prepared Statements, Stored Procedures
PDO supports prepared statements using the Oracle style named placeholder syntax to bind variables into the SQL (similar to ocibindbyname() in the oci8 extension). PDO also provides emulation of named placeholders for other databases (such as ODBC), and can even emulate prepared statements and bound parameters for databases that don't support the concept natively (such as MySQL). This is a positive step forward for PHP, since it allows developers to write "Enterprise Level" database applications in PHP without having to special-case less capable database platforms.
Preparing a statement using PDO is simply a matter of calling the prepare() method of the database handle. It returns a statement handle object which you can then use to bind parameters and execute the statement. In this example, we're defining two named placeholders; ":extension" and ":name", which correspond to a PHP extension name and the name of one of the authors from the .CSV file.
$stmt = $dbh->prepare("INSERT INTO CREDITS (extension, name) VALUES (:extension, :name)");
Having prepared the statement, we use the bindParam() method to associate those named parameters with PHP variables name "$extension" and "$name" respectively (this is analagous to ocibindbyname()). We also inform Oracle that the data will be formatted as strings and will have a maximum length of 64 characters.
$stmt->bindParam(':extension', $extension, PDO_PARAM_STR, 64);
$stmt->bindParam(':name', $name, PDO_PARAM_STR, 64);
We're just about ready to start inserting the datawe just need to open up the CSV file and get the data out of it. This is accomplished quite simply using the fopen() and fgetcsv() functions. We then assign the CSV columns directly to the variables "$extension" and "$name" using the PHP list() construct. Since those variables are already bound into our statement all we need to do now is call the execute() method of the statement object to have it make the insertion. This is both convenient and fasta two line per iteration loop running inside a transaction. Once we've reached the end of the file, we can commit the changes using the commit() method of the database handle.
If you're only passing input parameters, and you have a number of them, then you will appreciate the shortcut syntax show below; it allows you to omit the calls to $stmt->bindParam().
$stmt = $dbh->prepare("INSERT INTO CREDITS (extension, name) VALUES (:extension, :name)");
$stmt->execute(array(':extension' => $extension, ':name' => $name));
You can also use bindParam to set up input/output parameters for stored procedures; the syntax is identical, it is only the query that is different. Code below demonstrates how to call a stored procedure named "sp_add_item"; the intention is to set $item_name on input, and the stored produce will update $error_code when it returns.
$stmt = $dbh->prepare("begin sp_add_item(:item_name, :error_code); end");
$stmt->bindParam(':item_name', $item_name, PDO_PARAM_STR, 12);
$stmt->bindParam(':error_code', $error_code, PDO_PARAM_STR, 12);
$stmt->execute();
Fetching Data
Fetching data with PDO is similar to making insertions or updates, except that once you have executed the query, you will repeatedly call the fetch() method to obtain the next row of your result set. The simplest case for fetching is shown below, and it is worth noting that you may also bind parameters to the query to control such things as the WHERE clause; the syntax for that is identical to bindParam() code that we have already seen.
$stmt = $dbh->prepare("SELECT extension, name from CREDITS");
if ($stmt->execute()) {
while ($row = stmt->fetch()) {
print_r($row);
}
}
PDO supports a few different fetching strategies, which vary in convenience and performance; by specifying one of the following options as the parameter to the fetch() method, you can alter its return value to suit your script:
- PDO_FETCH_NUM each row fetch returns an array indexed by column position, 0-based (the first column is the zeroth element).
while ($row = $stmt->fetch(PDO_FETCH_NUM)) {
printf("Extension %s, by %s<br>", $row[0], $row[1]);
}
- PDO_FETCH_ASSOC each row fetch returns an array indexed by column name, according to the column names in your rowset.
while ($row = $stmt->fetch(PDO_FETCH_ASSOC)) {
echo "Extension $row[EXTENSION] by $row[NAME]<br>";
}
- PDO_FETCH_BOTH each row fetch returns an array indexed both by column position and column name. A literal combination of the above two cases. If no fetch mode is specified, this is the default.
- PDO_FETCH_OBJ each row fetch returns an anonymous object with property names corresponding to column names.
while ($row = $stmt->fetch(PDO_FETCH_ASSOC)) {
echo "Extension {$row->EXTENSION} by {$row->NAME}<br>";
}
- PDO_FETCH_LAZY each row fetch returns an overloaded object that references the statement object. This "looks" like a combination of PDO_FETCH_OBJ and PDO_FETCH_BOTH, except that the PHP variables are only created as you access them in your script.
- PDO_FETCH_BOUND each row is fetched, and TRUE is returned. This is useful if you are using bound output columns, as it avoids creating any arrays or objects that you won't be using. (see below for an example).
Regardless of which fetch strategy you use, the fetch() method will return FALSE when there are no more rows to fetch.
I'm about to reveal some tips that might be of use if you need to tune the last drop of performance out of your scripts. But first a warning: premature optimization is something to avoid like the plague. You should always prefer the clearest, most maintainable solution first. Keep in mind that in a typical web application, you wouldn't be able to measure the difference between the different fetch modes unless your script is processing a very large number of rows. I'll repeat that again: the performance difference between the fetch modes is marginaluse whichever mode best suits your code.
With that in mind, PDO_FETCH_NUM is the cheapest to use, since accessing the column data is a simple numeric lookup. PDO_FETCH_OBJ allows you to use OO syntax to access the columns of your data set as properties of the object, but each property access involves an additional hash-lookup, making it roughly the same cost to use as PDO_FETCH_ASSOC. Each of these modes will make a copy of the entire row, resulting in slightly higher memory usage.
Many database drivers will pre-fetch and cache a certain number of rows on your behalf. Whenever PHP accesses a column from one of these rows, it needs to make a copy of it into its own private memory area. If your query has a very large row size, and you only need to access particular columns of a given row based on some complicated logic, then you might find that PDO_FETCH_LAZY is a useful way to avoid using quite so much memory, as it defers copying a given column until you access it. The caveat with this approach is that the "lazy object" you get for each fetch() from a given statement is the same object on each iteration (to reduce overhead for creating/destroying it each time). This has the implication that you can't simply store the object for later comparison, since it will still be referencing the current row of the statement you will need to manually copy the parts you need.
The final mode is PDO_FETCH_BOUND, which tells PDO that you have bound all the columns to PHP variables and that you don't need it to do anything other than tell you when you've reached the end of the row-set. Bound output columns are similar in concept to bound input parameters, except that they are supported for all database drivers. You can bind a PHP variable to a named column and PDO will update it for you each time you call execute(). This technique can be used to shave off a couple of virtual machine op-codes (which are slower than native code) per column, per row of your result set. The disadvantage of this technique is that it can make your code harder to follow (also known as a higher WTF factor), and you need to be especially vigilant with your use of variable names. The code below illustrates the use of bound output columns. Note that you don't have to specify PDO_FETCH_BOUND to be able to use $stmt->bindColumn(); PDO_FETCH_BOUND is merely an optimization for the case where you know you'll only be using the bound values.
$stmt = $dbh->prepare("SELECT extension, name from CREDITS");
if ($stmt->execute()) {
$stmt->bindColumn('EXTENSION', $extension);
$stmt->bindColumn('NAME', $name);
while ($stmt->fetch(PDO_FETCH_BOUND)) {
echo "Extension: $extension, Author: $name\n";
}
}
Portability
Case sensitive columns
PDO aims to make scripts that use portable SQL, well, portable. All of the queries shown in this article (with the exception of calling a stored procedure) should work equally well using any PDO driverand that includes all the bound input variables and bound output columns.
There is a gotcha thoughwhen you're fetching data using PDO_FETCH_ASSOC, different drivers will return the column names in different wayssome force the column names to upper case, some lower case, and some leave it as it was specified in the query. This is a potential problem for PHP scripts, since array keys are case sensitive. PDO provides a compatibility attribute to help normalize the results for your script. The snippet below is a portable version of the PDO_FETCH_BOUND example above, because the setAttribute() method call instructs PDO to upper-case the column names returned from the fetch:
$dbh = new PDO('OCI:', 'scott', 'tiger');
$dbh->setAttribute(PDO_ATTR_CASE, PDO_CASE_UPPER);
stmt = $dbh->prepare("SELECT extension, name from CREDITS");
if ($stmt->execute()) {
$stmt->bindColumn('EXTENSION', $extension);
$stmt->bindColumn('NAME', $name);
while ($stmt->fetch(PDO_FETCH_BOUND)) {
echo "Extension: $extension, Author: $name\n";
}
}
In addition to PDO_CASE_UPPER, there are also PDO_CASE_LOWER (which lowercases the column names) and PDO_CASE_NATURAL (which is the default: leave the columns in the form returned by the database driver).
Errors and error handling
Another nightmare for portable scripts is handling the diverse range of error messages returned from the various database handlers; some databases have very poor support for programmatically handling errors, whereas others have a very rich range of error codes. Where feasible, PDO will present your script with a unified error code so that you are not so heavily burdened with dealing with this aspect of portability. Of course, PDO also provides the drivers native error code and error message, just in case you need it for diagnostic purposes, or in case the error code mapping isn't 100%.
Another consistency problem that has plagued the PHP database extensions is that of the error handling strategy: some extensions return error codes for which you have to manually fetch an error string while others simply emit a PHP warning. PDO allows you to choose from one of three different error handling strategies:
- PDO_ERRMODE_SILENT
This is the default mode; it will simply set the error code for you to inspect using the errorCode() and errorInfo() methods of both the statement and database handle objects.
if (!$dbh->exec($sql)) {
echo $dbh->errorCode() . "<br>";
$info = $dbh->errorInfo();
// $info[0] == $dbh->errorCode() unified error code
// $info[1] is the driver specific error code
// $info[2] is the driver specific error string
}
- PDO_ERRMODE_WARNING
In addition to setting the error code, PDO will emit a PHP warning, which you may capture using your regular PHP error handler and centrally apply whatever error handling/logging policy you have in place for your application, or simply allow the error to be displayed in the browser (very useful during internal testing).
- PDO_ERRMODE_EXCEPTION
In addition to setting the error code, PDO will throw a PDOException and set its properties to contain the error code and information. You may then catch the exception at a higher level in your code, catch it using a global exception handler, or leave it unhandled to terminate your script (any outstanding transactions will be rolled back if this happens).
try {
$dbh->exec($sql);
} catch (PDOException $e) {
// display warning message
print $e->getMessage();
$info = $e->errorInfo;
// $info[0] == $e->code; unified error code
// $info[1] is the driver specific error code
// $info[2] is the driver specific error string
}
Note that the silent mode uses least resources in the face of runtime errors, when compared to warnings or exceptions, but in return for that speed you are trading off simplicity for a little more complexity.
The unified error code list currently includes the following constants: PDO_ERR_NONE, PDO_ERR_CANT_MAP, PDO_ERR_SYNTAX, PDO_ERR_CONSTRAINT, PDO_ERR_NOT_FOUND, PDO_ERR_ALREADY_EXISTS, PDO_ERR_NOT_IMPLEMENTED, PDO_ERR_MISMATCH, PDO_ERR_TRUNCATED, PDO_ERR_DISCONNECTED.
These should be self explanatory, except for the PDO_ERR_CANT_MAP code; this is a PDO specific code that means that it was unable to map the driver specific code into a unified error code, and that you should consult the driver specific codes returned from the errorInfo() method for further information.
Data types
PDO is deliberately somewhat type agnostic in that it prefers to represent data as strings instead of converting to integer or double types. You might wonder at this, but the reasoning is quite simple: the string type is the type that is most precise and has the largest range available in PHP; prematurely converting data into integers or doubles can lead to either truncation or rounding errors. By pulling the data out as strings, PDO gives your script control over how and when the conversion happens using the usual PHP type juggling facilities (such as casting and implicitly during mathematical operations).
NULL
If a column in a result set contains a NULL value, PDO will map that as the PHP null value. Oracle converts empty strings to NULL when returning data to PDO, but none of the other databases supported by PHP do that, leading to a portability problem. PDO offers a driver level attribute PDO_ATTR_ORACLE_NULLS which emulates this behaviour for the other database drivers:
$dbh = new PDO('OCI:', 'scott', 'tiger');
$dbh->setAttribute(PDO_ATTR_ORACLE_NULLS, true);
// now empty strings will all be converted to NULL in any
// statements opened from this $dbh
Current Status, and The Future of PDO
PDO is still quite young, but maturing rapidly. At the time of writing, everything I've mentioned in this article is working for Oracle 8 and higher via the PDO_OCI driver (tested on Oracle 8.0 and 9.2).
The following major features are planned and will also be implemented in a short time frame:
- LOB support using PHP streams. Using bound parameters you will be able to pass any stream resource (such as files, sockets, http resources, compressed/filtered streams) as the input or output parameters in queries that operate on LOBs. Similarly, output parameters that are LOBs will be visible as PHP streams so that you may use fread(), fwrite(), fseek() and other streams functions to access them. At this time, there is no LOB support at all in PDO.
- Persistent connections and cached prepared statements. Persistent connections allow you to avoid the expense of opening and closing database server connection on each page hit. Cached prepared statements take this a step further and allow you to persist the prepared version of your queries alongside your database handles.
- Cursors. Currently PDO only provides forward-read-only cursors, but will provide (when supported by the underlying driver) scrollable cursors, REF-CURSOR and using cursors for making positioned updates and updatable scrolling cursors.
We are aiming to have the PDO extension enabled by default in PHP 5.1 (which is a little way off yet), but before then, we were hoping to have PDO stable in time for the PHP 5.0 release, but pressures from our day jobs have delayed things a little. In the mean time, publishing PDO via PECL allows us to respond to problem reports as they arise and release fixed versions on a release schedule separate from that of PHP 5.0, so you don't have to wait until PHP 5.1 is released to take advantage of PDO.
We need your feedback
If you've tried PDO and found a problem, please do report it to us using our bug tracking software. If you are using the Oracle driver, you should use this page:
http://pecl.php.net/bugs/report.php?package=PDO_OCI
If you are using one of the other drivers, insert its name in place of PDO_OCI in the URL instead.
If you are having problems using PDO, or questions about certain features, or feature requests, please contact pecl-dev@lists.php.net. You can, of course, contact me directly if you wish (wez@php.net), with the caveat that I get a lot of PHP related email each day; you might find that you get a more speedy response if you contact the mailing list first.
Wez Furlong is the Technical Director of The Brain Room Ltd., where he uses PHP not only for the web, but also as an embedded script engine for Linux and Windows applications and systems. Wez is a Core Developer of PHP, having contributed SQLite, COM/.Net, ActivePHP, mailparse and the Streams API (and more) and is the "King" of PECLPHP's Extension Community Library. His consulting firm can be reached at http://www.thebrainroom.net.
|