This is an old revision of this page, as edited by Lfdder (talk | contribs) at 02:22, 6 April 2014 (→References). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
Revision as of 02:22, 6 April 2014 by Lfdder (talk | contribs) (→References)(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)PHP Data Objects (PDO) is an interface for accessing databases. The PHP Data Objects (PDO) extension defines a lightweight, consistent interface for accessing databases in PHP. Each database driver that implements the PDO interface can expose database-specific features as regular extension functions. Note that you cannot perform any database functions using the PDO extension by itself; you must use a database-specific PDO driver to access a database server.
PDO provides a data-access abstraction layer, which means that, regardless of which database you're using, you use the same functions to issue queries and fetch data. PDO does not provide a database abstraction; it doesn't rewrite SQL or emulate missing features. You should use a full-blown abstraction layer if you need that facility.
PDO ships with PHP 5.1, and is available as a PECL extension for PHP 5.0; PDO requires the new OO features in the core of PHP 5, and so will not run with earlier versions of PHP. O'Reilly Networks - ONLamp, IBM DeveloperWorks (twice), IBM Redbooks, php|architect, InfoWorld, Linux Journal
Installation
Installing PDO on Unix systems
PDO and the PDO_SQLITE driver is enabled by default as of PHP 5.1.0. You may need to enable the PDO driver for your database of choice; consult the documentation for database-specific PDO drivers to find out more about that.
Note: When building PDO as a shared extension (not recommended) then all PDO drivers must be loaded after PDO itself.
When installing PDO as a shared module, the php.ini file needs to be updated so that the PDO extension will be loaded automatically when PHP runs. You will also need to enable any database specific drivers there too; make sure that they are listed after the pdo.so line, as PDO must be initialized before the database-specific extensions can be loaded. If you built PDO and the database-specific extensions statically, you can skip this step.
extension=pdo.so
Windows users
PDO and all the major drivers ship with PHP as shared extensions, and simply need to be activated by editing the php.ini file:
extension=php_pdo.dll
Note: This step is not necessary for PHP 5.3 and above, as a DLL is no longer required for PDO. Next, choose the other database-specific DLL files and either use dl() to load them at runtime, or enable them in php.ini below php_pdo.dll. For example:
extension=php_pdo.dll extension=php_pdo_firebird.dll extension=php_pdo_informix.dll extension=php_pdo_mssql.dll extension=php_pdo_mysql.dll extension=php_pdo_oci.dll extension=php_pdo_oci8.dll extension=php_pdo_odbc.dll extension=php_pdo_pgsql.dll extension=php_pdo_sqlite.dll
These DLLs should exist in the system's extension_dir.
Note: Remember that after making changes to your php.ini file you will need to restart PHP for your new configuration directives to take effect.
Connections and Connection Management
Connections are established by creating instances of the PDO base class. It doesn't matter which driver you want to use; you always use the PDO class name. The constructor accepts parameters for specifying the database source (known as the DSN) and optionally for the username and password (if any).
Connecting to MySQL
<?php $dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass); ?>
If there are any connection errors, a PDOException object will be thrown. You may catch the exception if you want to handle the error condition, or you may opt to leave it for an application global exception handler that you set up via set_exception_handler().
Handling connection errors
<?php try { $dbh = new PDO('mysql:host=localhost; dbname=test', $user, $pass); foreach($dbh->query('SELECT * from FOO') as $row) { print_r($row); } $dbh = null; } catch (PDOException $e) { print "Error!: " . $e->getMessage() . "<br/>"; die(); } ?>
Warning: If your application does not catch the exception thrown from the PDO constructor, the default action taken by the zend engine is to terminate the script and display a back trace. This back trace will likely reveal the full database connection details, including the username and password. It is your responsibility to catch this exception, either explicitly (via a catch statement) or implicitly via set_exception_handler().
Upon successful connection to the database, an instance of the PDO class is returned to your script. The connection remains active for the lifetime of that PDO object. To close the connection, you need to destroy the object by ensuring that all remaining references to it are deleted--you do this by assigning NULL to the variable that holds the object. If you don't do this explicitly, PHP will automatically close the connection when your script ends.
Closing a connection
<?php $dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass); // use the connection here // and now we're done; close it $dbh = null; ?>
Many web applications will benefit from making persistent connections to database servers. Persistent connections are not closed at the end of the script, but are cached and re-used when another script requests a connection using the same credentials. The persistent connection cache allows you to avoid the overhead of establishing a new connection every time a script needs to talk to a database, resulting in a faster web application.
Persistent connections
<?php $dbh = new PDO('mysql:host=localhost; dbname=test', $user, $pass, array( PDO::ATTR_PERSISTENT => true )); ?>
Note: If you wish to use persistent connections, you must set PDO::ATTR_PERSISTENT in the array of driver options passed to the PDO constructor. If setting this attribute with PDO::setAttribute() after instantiation of the object, the driver will not use persistent connections.
Note: If you're using the PDO ODBC driver and your ODBC libraries support ODBC Connection Pooling (unixODBC and Windows are two that do; there may be more), then it's recommended that you don't use persistent PDO connections, and instead leave the connection caching to the ODBC Connection Pooling layer. The ODBC Connection Pool is shared with other modules in the process; if PDO is told to cache the connection, then that connection would never be returned to the ODBC connection pool, resulting in additional connections being created to service those other modules.
Transactions and auto-commit
Now that you're connected via PDO, you must understand how PDO manages transactions before you start issuing queries. If you've never encountered transactions before, they offer 4 major features: Atomicity, Consistency, Isolation and Durability (ACID). In layman's terms, any work carried out in a transaction, even if it is carried out in stages, is guaranteed to be applied to the database safely, and without interference from other connections, when it is committed. Transactional work can also be automatically undone at your request (provided you haven't already committed it), which makes error handling in your scripts easier.
Transactions are typically implemented by "saving-up" your batch of changes to be applied all at once; this has the nice side effect of drastically improving the efficiency of those updates. In other words, transactions can make your scripts faster and potentially more robust (you still need to use them correctly to reap that benefit).
Unfortunately, not every database supports transactions, so PDO needs to run in what is known as "auto-commit" mode when you first open the connection. Auto-commit mode means that every query that you run has its own implicit transaction, if the database supports it, or no transaction if the database doesn't support transactions. If you need a transaction, you must use the PDO::beginTransaction() method to initiate one. If the underlying driver does not support transactions, a PDOException will be thrown (regardless of your error handling settings: this is always a serious error condition). Once you are in a transaction, you may use PDO::commit() or PDO::rollBack() to finish it, depending on the success of the code you run during the transaction.
Warning PDO only checks for transaction capabilities on driver level. If certain runtime conditions mean that transactions are unavailable, PDO::beginTransaction() will still return TRUE without error if the database server accepts the request to start a transaction.
An example of this would be trying to use transactions on MyISAM tables on a MySQL database.
When the script ends or when a connection is about to be closed, if you have an outstanding transaction, PDO will automatically roll it back. This is a safety measure to help avoid inconsistency in the cases where the script terminates unexpectedly--if you didn't explicitly commit the transaction, then it is assumed that something went awry, so the rollback is performed for the safety of your data.
Warning The automatic rollback only happens if you initiate the transaction via PDO::beginTransaction(). If you manually issue a query that begins a transaction PDO has no way of knowing about it and thus cannot roll it back if something bad happens.
Executing a batch in a transaction
In the following sample, let's assume that we are creating a set of entries for a new employee, who has been assigned an ID number of 23. In addition to entering the basic data for that person, we also need to record their salary. It's pretty simple to make two separate updates, but by enclosing them within the PDO::beginTransaction() and PDO::commit() calls, we are guaranteeing that no one else will be able to see those changes until they are complete. If something goes wrong, the catch block rolls back all changes made since the transaction was started, and then prints out an error message.
<?php try { $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2', array(PDO::ATTR_PERSISTENT => true)); echo "Connected\n"; } catch (Exception $e) { die("Unable to connect: " . $e->getMessage()); } try { $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $dbh->beginTransaction(); $dbh->exec("insert into staff (id, first, last) values (23, 'Joe', 'Bloggs')"); $dbh->exec("insert into salarychange (id, amount, changedate) values (23, 50000, NOW())"); $dbh->commit(); } catch (Exception $e) { $dbh->rollBack(); echo "Failed: " . $e->getMessage(); } ?>
You're not limited to making updates in a transaction; you can also issue complex queries to extract data, and possibly use that information to build up more updates and queries; while the transaction is active, you are guaranteed that no one else can make changes while you are in the middle of your work. For further reading on transactions, refer to the documentation provided by your database server.
Prepared statements and stored procedures
Many of the more mature databases support the concept of prepared statements. What are they? They can be thought of as a kind of compiled template for the SQL that an application wants to run, that can be customized using variable parameters. Prepared statements offer two major benefits:
- The query only needs to be parsed (or prepared) once, but can be executed multiple times with the same or different parameters. When the query is prepared, the database will analyze, compile and optimize its plan for executing the query. For complex queries this process can take up enough time that it will noticeably slow down an application if there is a need to repeat the same query many times with different parameters. By using a prepared statement the application avoids repeating the analyze/compile/optimize cycle. This means that prepared statements use fewer resources and thus run faster.
- The parameters to prepared statements don't need to be quoted; the driver automatically handles this. If an application exclusively uses prepared statements, the developer can be sure that no SQL injection will occur (however, if other portions of the query are being built up with unescaped input, SQL injection is still possible).
Prepared statements are so useful that they are the only feature that PDO will emulate for drivers that don't support them. This ensures that an application will be able to use the same data access paradigm regardless of the capabilities of the database.
Repeated inserts using prepared statements
This example performs an INSERT query by substituting a name and a value for the named placeholders.
<?php $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (:name, :value)"); $stmt->bindParam(':name', $name); $stmt->bindParam(':value', $value); // insert one row $name = 'one'; $value = 1; $stmt->execute(); // insert another row with different values $name = 'two'; $value = 2; $stmt->execute(); ?>
Repeated inserts using prepared statements
This example performs an INSERT query by substituting a name and a value for the positional ? placeholders.
<?php $stmt = $dbh->prepare("INSERT INTO REGISTRY (name, value) VALUES (?, ?)"); $stmt->bindParam(1, $name); $stmt->bindParam(2, $value); // insert one row $name = 'one'; $value = 1; $stmt->execute(); // insert another row with different values $name = 'two'; $value = 2; $stmt->execute(); ?>
Fetching data using prepared statements
This example fetches data based on a key value supplied by a form. The user input is automatically quoted, so there is no risk of a SQL injection attack.
<?php $stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?"); if ($stmt->execute(array($_GET))) { while ($row = $stmt->fetch()) { print_r($row); } } ?>
If the database driver supports it, an application may also bind parameters for output as well as input. Output parameters are typically used to retrieve values from stored procedures. Output parameters are slightly more complex to use than input parameters, in that a developer must know how large a given parameter might be when they bind it. If the value turns out to be larger than the size they suggested, an error is raised.
Calling a stored procedure with an output parameter
<?php $stmt = $dbh->prepare("CALL sp_returns_string(?)"); $stmt->bindParam(1, $return_value, PDO::PARAM_STR, 4000); // call the stored procedure $stmt->execute(); print "procedure returned $return_value\n"; ?>
Developers may also specify parameters that hold values both input and output; the syntax is similar to output parameters. In this next example, the string 'hello' is passed into the stored procedure, and when it returns, hello is replaced with the return value of the procedure.
Calling a stored procedure with an input/output parameter
<?php $stmt = $dbh->prepare("CALL sp_takes_string_returns_string(?)"); $value = 'hello'; $stmt->bindParam(1, $value, PDO::PARAM_STR|PDO::PARAM_INPUT_OUTPUT, 4000); // call the stored procedure $stmt->execute(); print "procedure returned $value\n"; ?>
Invalid use of placeholder
<?php $stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE '%?%'"); $stmt->execute(array($_GET)); // placeholder must be used in the place of the whole value $stmt = $dbh->prepare("SELECT * FROM REGISTRY where name LIKE ?"); $stmt->execute(array("%$_GET%")); ?>
Errors and error handling
PDO offers you a choice of 3 different error handling strategies, to fit your style of application development.* *PDO::ERRMODE_SILENT
This is the default mode. PDO will simply set the error code for you to inspect using the PDO::errorCode() and PDO::errorInfo() methods on both the statement and database objects; if the error resulted from a call on a statement object, you would invoke the PDOStatement::errorCode() or PDOStatement::errorInfo() method on that object. If the error resulted from a call on the database object, you would invoke those methods on the database object instead.
- PDO::ERRMODE_WARNING
In addition to setting the error code, PDO will emit a traditional E_WARNING message. This setting is useful during debugging/testing, if you just want to see what problems occurred without interrupting the flow of the application. - PDO::ERRMODE_EXCEPTION
In addition to setting the error code, PDO will throw a PDOException and set its properties to reflect the error code and error information. This setting is also useful during debugging, as it will effectively "blow up" the script at the point of the error, very quickly pointing a finger at potential problem areas in your code (remember: transactions are automatically rolled back if the exception causes the script to terminate).
Exception mode is also useful because you can structure your error handling more clearly than with traditional PHP-style warnings, and with less code/nesting than by running in silent mode and explicitly checking the return value of each database call.
See Exceptions for more information about Exceptions in PHP.
PDO standardizes on using SQL-92 SQLSTATE error code strings; individual PDO drivers are responsible for mapping their native codes to the appropriate SQLSTATE codes. The PDO::errorCode() method returns a single SQLSTATE code. If you need more specific information about an error, PDO also offers an PDO::errorInfo() method which returns an array containing the SQLSTATE code, the driver specific error code and driver specific error string.
Create a PDO instance and set the error mode
<?php $dsn = 'mysql:dbname=testdb;host=127.0.0.1'; $user = 'dbuser'; $password = 'dbpass'; try { $dbh = new PDO($dsn, $user, $password); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } catch (PDOException $e) { echo 'Connection failed: ' . $e->getMessage(); } ?>
Note: PDO::__construct() will always throw a PDOException if the connection fails regardless of which PDO::ATTR_ERRMODE is currently set. Uncaught Exceptions are fatal.
Create a PDO instance and set the error mode from the constructor
<?php $dsn = 'mysql:dbname=test;host=127.0.0.1'; $user = 'googleguy'; $password = 'googleguy'; /* Using try/catch around the constructor is still valid even though we set the ERRMODE to WARNING since PDO::__construct will always throw a PDOException if the connection fails. */ try { $dbh = new PDO($dsn, $user, $password, array(PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING)); } catch (PDOException $e) { echo 'Connection failed: ' . $e->getMessage(); exit; } // This will cause PDO to throw an error of level E_WARNING instead of an exception (when the table doesn't exist) $dbh->query("SELECT wrongcolumn FROM wrongtable"); ?>
Large Objects (LOBs)
At some point in your application, you might find that you need to store "large" data in your database. Large typically means "around 4kb or more", although some databases can happily handle up to 32kb before data becomes "large". Large objects can be either textual or binary in nature. PDO allows you to work with this large data type by using the PDO::PARAM_LOB type code in your PDOStatement::bindParam() or PDOStatement::bindColumn() calls. PDO::PARAM_LOB tells PDO to map the data as a stream, so that you can manipulate it using the PHP Streams API.
Displaying an image from a database
This example binds the LOB into the variable named $lob and then sends it to the browser using fpassthru(). Since the LOB is represented as a stream, functions such as fgets(), fread() and stream_get_contents() can be used on it.
See example in
Inserting an image into a database
This example opens up a file and passes the file handle to PDO to insert it as a LOB. PDO will do its best to get the contents of the file up to the database in the most efficient manner possible.
See example in
Inserting an image into a database: Oracle
Oracle requires a slightly different syntax for inserting a lob from a file. It's also essential that you perform the insert under a transaction, otherwise your newly inserted LOB will be committed with a zero-length as part of the implicit commit that happens when the query is executed:
See example in
References
- http://www.php.net/manual/en/intro.pdo.php
- ^ http://www.php.net/manual/en/pdo.lobs.php
- ^ http://www.php.net/manual/en/pdo.lobs.php
- ^ http://www.php.net/manual/en/pdo.lobs.php
See also
This database software-related article is a stub. You can help Misplaced Pages by expanding it. |