wez Sun Sep 11 17:10:37 2005 EDT
Modified files:
/phpdoc/en/reference/pdo reference.xml
Log:
Add some content.
http://cvs.php.net/diff.php/phpdoc/en/reference/pdo/reference.xml?r1=1.35&r2=1.36&ty=u
Index: phpdoc/en/reference/pdo/reference.xml
diff -u phpdoc/en/reference/pdo/reference.xml:1.35
phpdoc/en/reference/pdo/reference.xml:1.36
--- phpdoc/en/reference/pdo/reference.xml:1.35 Sun Sep 11 03:22:22 2005
+++ phpdoc/en/reference/pdo/reference.xml Sun Sep 11 17:10:36 2005
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='iso-8859-1'?>
-<!-- $Revision: 1.35 $ -->
+<!-- $Revision: 1.36 $ -->
<!-- Purpose: database.abstract -->
<!-- Membership: pecl, bundled -->
<!-- State:experimental -->
@@ -21,6 +21,14 @@
PDO driver</link> to access a database server.
</para>
<para>
+ PDO provides a <emphasis>data-access</emphasis> 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
+ <emphasis>not</emphasis> provide a <emphasis>database</emphasis>
+ abstraction; it doesn't rewrite SQL or emulate missing features. You
+ should use a full-blown abstraction layer for PDO.
+ </para>
+ <para>
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.
@@ -216,6 +224,522 @@
</para>
</section>
+ <section id='pdo.connections'>
+ <title>Connections and Connection management</title>
+ <para>
+ 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).
+ </para>
+ <para>
+ <example><title>Connecting to mysql</title>
+ <programlisting role='php'>
+<![CDATA[
+<?php
+$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ If there are any connection errors, a <literal>PDOException</literal>
+ 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
+ <function>set_exception_handler</function>.
+ </para>
+ <para>
+ <example><title>Handling connection errors</title>
+ <programlisting role='php'>
+<![CDATA[
+<?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();
+}
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ <example><title>Closing a connection</title>
+ <programlisting role='php'>
+<![CDATA[
+<?php
+$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
+// use the connection here
+
+
+// and now we're done; close it
+$dbh = null;
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ 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.
+ </para>
+ <para>
+ <example><title>Persistent connections</title>
+ <programlisting role='php'>
+<![CDATA[
+<?php
+$dbh = new PDO('mysql:host=localhost;dbname=test', $user, $pass, array(
+ PDO_ATTR_PERSISTENT => true
+));
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <note>
+ <para>
+ 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.
+ </para>
+ </note>
+ </section>
+
+ <section id='pdo.transactions'>
+ <title>Transactions and auto-commit</title>
+ <para>
+ Now that you're connected via PDO, you should to 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.
+ </para>
+ <para>
+ 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).
+ </para>
+ <para>
+ 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 <function>PDO::beginTransaction</function> 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 <function>PDO::commit</function> or
+ <function>PDO::rollBack</function> to finish it, depending on the success
+ of the code you run during the transaction.
+
+ </para>
+ <para>
+ 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.
+ </para>
+ <warning>
+ <para>
+ The automatic rollback only happens if you initiate the transaction via
+ <function>PDO::beginTransaction</function>. 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.
+ </para>
+ </warning>
+ <para>
+ <example><title>Executing a batch in a transaction</title>
+ <para>
+ 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
+ <function>PDO::beginTransaction</function> and
+ <function>PDO::commit</function> 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.
+ </para>
+ <programlisting role='php'>
+<![CDATA[
+<?php
+try {
+ $dbh = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2',
+ array(PDO_ATTR_PERSISTENT => true));
+ echo "Connected\n";
+ $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();
+}
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ 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. In truth, this isn't 100% correct, but it is a
+ good-enough introduction, if you've never heard of transactions before.
+ </para>
+ </section>
+
+ <section id='pdo.prepared-statements'>
+ <title>Prepared statements and stored procedures</title>
+ <para>
+ Many of the more mature databases support the concept of prepared
+ statements. What are they? You can think of them as a kind of compiled
+ template for the SQL that you want to run, that can be customized using
+ variable parameters. Prepared statements offer two major benefits:
+ </para>
+ <itemizedlist>
+ <listitem>
+ <simpara>
+ 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 it's
+ plan for executing the query. For complex queries this process can take
+ up enough time that it will noticably slow down your application if you
+ need to repeat the same query many times with different parameters. By
+ using a prepared statement you avoid repeating the
+ analyze/compile/optimize cycle. In short, prepared statements use fewer
+ resources and thus run faster.
+ </simpara>
+ </listitem>
+ <listitem>
+ <simpara>
+ The parameters to prepared statements don't need to be quoted; the
+ driver handles it for you. If your application exclusively uses
+ prepared statements, you can be sure that no SQL injection will occur.
+ (However, if you're still building up other parts of the query based on
+ untrusted input, you're still at risk).
+ </simpara>
+ </listitem>
+ </itemizedlist>
+ <para>
+ 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 you
+ will be able to use the same data access paradigm regardless of the
+ capabilities of the database.
+ </para>
+ <para>
+ <example><title>Repeated inserts using prepared statements</title>
+ <simpara>
+ This example performs an INSERT query by substituting a
<literal>name</literal>
+ and a <literal>value</literal> for the named placeholders.
+ </simpara>
+ <programlisting role='php'>
+<![CDATA[
+<?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();
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ <example><title>Repeated inserts using prepared statements</title>
+ <simpara>
+ This example performs an INSERT query by substituting a
<literal>name</literal>
+ and a <literal>value</literal> for the positional <literal>?</literal>
placeholders.
+ </simpara>
+ <programlisting role='php'>
+<![CDATA[
+<?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();
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ <example><title>Fetching data using prepared statements</title>
+ <simpara>
+ 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.
+ </simpara>
+ <programlisting role='php'>
+<![CDATA[
+<?php
+$stmt = $dbh->prepare("SELECT * FROM REGISTRY where name = ?");
+if ($stmt->execute(array($_GET['name']))) {
+ while ($row = $stmt->fetch()) {
+ print_r($row);
+ }
+}
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ If the database driver supports it, you 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 you must know how large a given
+ parameter might be when you bind it. If the value turns out to be larger
+ than the size you suggested, an error is raised.
+ </para>
+
+ <para>
+ <example><title>Calling a stored procedure with an output parameter</title>
+ <programlisting role='php'>
+<![CDATA[
+<?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";
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+
+ <para>
+ You 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.
+ </para>
+
+ <para>
+ <example><title>Calling a stored procedure with an input/output
parameter</title>
+ <programlisting role='php'>
+<![CDATA[
+<?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";
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ </section>
+
+ <section id='pdo.error-handling'>
+ <title>Errors and error handling</title>
+ <para>
+ PDO offers you a choice of 3 different error handling strategies, to fit
+ your style of application development.
+ </para>
+ <itemizedlist>
+ <listitem>
+ <para>
+ PDO_ERRMODE_SILENT
+ </para>
+ <para>
+ This is the default mode. PDO will simply set the error code for you
+ to inspect using the <function>PDO::errorCode</function> and
+ <function>PDO::errorInfo</function> methods on both the
+ statement and database objects; if the error resulted from a call on a
+ statement object, you would invoke the
+ <function>PDOStatement::errorCode</function> or
+ <function>PDOStatement::errorInfo</function>
+ 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.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ PDO_ERRMODE_WARNING
+ </para>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ PDO_ERRMODE_EXCEPTION
+ </para>
+ <para>
+ 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).
+ </para>
+ <para>
+ 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.
+ </para>
+ </listitem>
+ </itemizedlist>
+ <para>
+ 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 <function>PDO::errorCode</function>
+ method returns a single SQLSTATE code. If you need more specific
+ information about an error, PDO also offers an
+ <function>PDO::errorInfo</function> method which returns an array
+ containing the SQLSTATE code, the driver specific error code and driver
+ specific error string.
+ </para>
+ </section>
+
+ <section id='pdo.lobs'>
+ <title>Large Objects (LOBs)</title>
+ <para>
+ 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 <function>PDOStatement::bindParam</function> or
+ <function>PDOStatement::bindColumn</function> calls. PDO_PARAM_LOB tells
+ PDO to map the data as a stream, so that you can manipulate it using the
+ <link linkend='ref.stream'>PHP Streams API</link>.
+ </para>
+ <para>
+
+ </para>
+ <para>
+ <example><title>Displaying an image from a database</title>
+ <para>
+ This example binds the LOB into the variable named $lob and then sends
+ it to the browser using <function>fpassthru</function>. Since the LOB
+ is represented as a stream, functions such as
+ <function>fgets</function>, <function>fread</function> and
+ <function>stream_get_contents</function> can be used on it.
+ </para>
+ <programlisting role='php'>
+<![CDATA[
+<?php
+$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
+$stmt = $db->prepare("select contenttype, imagedata from images where id=?");
+$stmt->execute(array($_GET['id']));
+$stmt->bindColumn(1, $type, PDO_PARAM_STR, 256);
+$stmt->bindColumn(2, $lob, PDO_PARAM_LOB);
+$stmt->fetch(PDO_FETCH_BOUND);
+
+header("Content-Type: $type");
+fpassthru($lob);
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ <para>
+ <example><title>Inserting an image into a database</title>
+ <para>
+ 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.
+ </para>
+ <programlisting role='php'>
+<![CDATA[
+<?php
+$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
+$stmt = $db->prepare("insert into images (id, contenttype, imagedata) values
(?, ?, ?)");
+$id = get_new_id(); // some function to allocate a new ID
+
+// assume that we are running as part of a file upload form
+// You can find more information in the PHP documentation
+
+$fp = fopen($_FILES['file']['tmp_name'], 'rb');
+
+$stmt->bindParam(1, $id);
+$stmt->bindParam(2, $_FILES['file']['type']);
+$stmt->bindParam(3, $fp, PDO_PARAM_LOB);
+
+$stmt->execute();
+?>
+]]>
+ </programlisting>
+ </example>
+ </para>
+ </section>
+
<section id='pdo.classes'>
&reftitle.classes;
<section id='pdo.class.PDO'>