php data object 2

January 31st, 2010

使用PHP数据库对象
第一步
上一章节里,我们简单了解了PDO是什么,怎么通过PDO连接到你喜爱的数据库,怎么执行简单查询和处理错误,现在你可以认为PDO是个很好的工具,在想怎么才能灵活地运用它。我们将以运用所有的它提供的特点进行开发。
在这一章,我们将看到更多关于使用PDO连接数据库,连接字符串(数据源),PDOstatement类,遍历结果集。我们将要那立一个小型图书库管理应用程序,它充许我们管理你的家庭图书库所收藏的图书。这个应用程序也能列出图片和作者,并能添加和修改它。
我们先从连接字符串开始,因为没有它我们装不能连接到任数据库。我们将创建一个示例数据库,以它为基础,创建所有的书的例子。
我们将舍弃十分简单虚构的汽车数据库,创建一个真正运行几个表的数据库。我们之所以选择这个例子,是因为它存在普遍性。关系模型也相对简单,如果你在别的地方见过这样的数据库,你可以很轻松地理解例子。

连接字符串
在PDO文档里被引用连接字符串或数据源名(简称DSN)是一个包含数据库管理系统名称,数据库本身的PHP字符串,和其它连接参数.

与使用传统方式创建数据连接相比,其优点是如果改变数据库管理系统不必去修改码。连接字符串可以被定义在配置文件里,这个文件被程序处理,如果你的数据库要改变,你只要修改配置文件,其它的代码保持不动。

连接字符串因不同的数据库管理系统而不同,但他们常常有一个通用前缀,它代表基础数据库驱动程序。记住第一章MySQL, SQLite, 和PostgreSQL的例子,三个连接字符串像下面这样:
mysql:host=localhost;dbname=cars
sqlite:/path/to/cars.db
pgsql:host=localhost dbname=cars
正如我们看到的,前缀(每一个分号前的了句)通常保存PDO驱动程序的名称。PDO使用相同的程序去创建连接,前缀告诉我们那个内部驱动程序要被使用。字符串的其余部分由该驱动程序进行分析,进一步启动连接。这种情况我们为MySQL and PostgreSQL提供了数据库名称,我们也提供服务器的主机名。(因为SQLite是一个本地数据库引擎,所以这样的参数没有意义)
如果你想指定额外的参数,你要去查阅你的数据库手册((www.php.net/pdo能常是一个错的开始之处),例如,MySQL PDO驱动程序能接受下面的参数

host-数据库的主机名(在我们例子中的localhost)
port-数据库服务器监听的端口(默认:3306)
dbname-数据库名称(例子中的cars)
unix_socket-mysql uxix 套接字(替代主机名,端口)

noted:SQLite:前缀表示到SQLite3的连接,要连接到SQLite2,你必须使用SQLite2:前缀。可到http://www.php.net/manual/en/ref.pdo-sqlite.connection.php查看更多细节。

你可能已经明白,不同的驱动程序使用不同的字符定界参数,比如mysql用分号,PostgreSQL用空格。
创建一个简单的数据库
可能你有一个不错的图书库,你想用你的电脑帮你管理它,你决定用PHP建立一个基于网络的数据库,当然,是通过PDO。从现在起,例子将是关于mysql和SQLite数据库。

数据库模型
我们的数据库是非常简单,我们只有两个实体,author和books,我们也将用这两个名字建立两个表,现在让我们考虑一下这两个实体要有什么属性。

作者要有他们的姓和名,简介。这个表也要有一个id作为主键。我们利用它关键别一个名为books的表。
书被作者所写。(有时它们是多人一起写的,在这里我们只考虑为一个所写)。所以我们需要一个作者ID的字段,还有书的标题,ISBN号码,出版社名称,出版日期,也将有一个关书此书的简介。
我们需要为作者创建一个单独的表,国为一个作者可能会写了不止一本书。我们的例子是非常的简单,因此我们选择了两个表的结构,如果我们认为书可以被多人所写,那我们将需要三个表,那我们会例子将非常复杂。

创建Mysql数据库
当你启动你的Mysql命令行客户端,你将看到mysql>提示,你可以执行命令创建数据库和表。
mysql> create database pdo;
Query OK, 1 row affected (0.05 sec)
mysql> use pdo;
Database changed
mysql> create table books(
-> id int primary key not null auto_increment,
-> author int not null,
-> title varchar(70) not null,
-> isbn varchar(20),
-> publisher varchar(30) not null,
-> year int(4) not null,
-> summary text(2048));
Query OK, 0 rows affected (0.17 sec)
mysql> create
mysql> create table authors(
-> id int primary key not null auto_increment,
-> firstName varchar(30) not null,
-> lastName varchar(40) not null,
-> bio text(2048));
Query OK, 0 rows affected (0.00 sec)
正如你看到的,正如我们计划的,我们已经创那了名为pdo的数据库,也创那建了books,authors两个表.让我们看看如何在SQLite里怎么做。因为我们不能在SQLite命令行客户端里创建数据库,我们像这样开始:

> sqlite3 pdo.db
sqlite> create table books(
…> id integer primary key,
…> author integer(11) not null,
…> title varchar(70) not null,
…> isbn varchar(20),
…> publisher varchar(30) not null,
…> year integer(4) not null,
…> summary text(2048));
sqlite> create table authors(
…> id integer(11) primary key,
…> firstName varchar(30) not null,
…> lastName varchar(40) not null,
…> bio text(2048));

正如你所看到的,SQL与SQLite还是有所不同:主键没有声明NOT NULL 和aut0_increment选项。在SQLite,列声明为INTEGER PRIMARY KEY是自动增加的,现在让我们插话一些数据到我们的数据库里,两者的语法都是一样的,所以这里我们注只列举mysql的命令行客户端。我们从作者表开始,因为我们需要的主键的值,它要被插入到books表中。
mysql> insert into authors(firstName, lastName, bio) values(
-> ‘Marc’, ‘Delisle’, ‘Marc Delisle is a member of the MySQL
Developers Guide’);
Query OK, 1 row affected (0.14 sec)
mysql> insert into authors(firstName, lastName, bio) values(
-> ‘Sohail’, ‘Salehi’, ‘In recent years, Sohail has contributed
to over 20 books, mainly in programming and computer graphics’);
Query OK, 1 row affected (0.00 sec)
mysql> insert into authors(firstName, lastName, bio) values(
-> ‘Cameron’, ‘Cooper’, ‘J. Cameron Cooper has been playing
around on the web since there was not much of a web with which to
play around’);
Query OK, 1 row affected (0.00 sec)
现在我们已经添加了三位作者,让我们添加一些书。在做之前,我们要知道作者对应的id.一个简单的select查询就可以办到:
mysql> select id, firstName, lastName from authors;
+—-+———–+———-+
| id | firstName | lastName |
+—-+———–+———-+
| 1 | Marc | Delisle |
| 2 | Sohail | Salehi |
| 3 | Cameron | Cooper |
+—-+———–+———-+
3 rows in set (0.03 sec)
现在我们终于可以使用这些信息添加三本书,每一本被一个作者所写。
mysql> insert into books(author, title, isbn, publisher, year, summary) values(
-> 1, ‘Creating your MySQL Database: Practical Design Tips and
Techniques’, ‘1904811302′, ‘Packt Publishing Ltd’, ‘2006′,
-> ‘A short guide for everyone on how to structure your data and
set-up your MySQL database tables efficiently and easily.’);

Query OK, 1 row affected (0.00 sec)
mysql> insert into books(author, title, isbn, publisher, year, summary) values(
-> 2, ‘ImageMagick Tricks’, ‘1904811868′, ‘Packt Publishing
Ltd’, ‘2006′,
-> ‘Unleash the power of ImageMagick with this fast, friendly
tutorial, and tips guide’);
Query OK, 1 row affected (0.02 sec)
mysql> insert into books(author, title, isbn, publisher, year,
summary) values(
-> 3, ‘Building Websites with Plone’, ‘1904811027′, ‘Packt
Publishing Ltd’, ‘2004′,
-> ‘An in-depth and comprehensive guide to the Plone content
management system’);
Query OK, 1 row affected (0.00 sec)
现在我们已经填充了authors和books表,我们也将开始创建第一个小型图书库管理WEB应用程序页面。
notes:
这些数据是Packt Publishing Ltd已经出版的图书真实数据。(你所读的书的出版商正是他们。)你可以到他们的网站上找到更多信息:http://www.packpub.com

设计你的代码
除了好的数据库模型,好的程序体系结构是另外一个应用程序的关键因素。因为我们这章要开发的应用程序是很简单,相对比较小,所以任务不是很复杂。首先,我们创建两个页面去罗列books和aauthors.我们先考虑一下这些页面的外观,使我们的页面简单,紧凑。我们将在所有的页面显示一个头部,它包含
链接到books列表和作者列表的锚。稍后我们添加别外两个添加author,book的页面。

当然,我们将创建一个公用的包含文件,它定义了显示头部与底部的公用程序和连接到数据库。我们的例子真的很小,所以我们将不用任何模板系统或面向对象的语法。(这些话题也超出了本书的范围),总结一下:
所有的公用程序(包括创建PDO连接对象的代码)都包含在一个引用文件(名为common.inc.php).
每个页面将保留一个独立的文件,它引用 common.inc.php文件
每个页面将访问数据,并显示它,(所以我们没有把处理数据和显示数据分开,正如应用程序设计所期望的model-view-controller模式思想)
现在我们计划先从commn.icn.php文件开始,正如我们讨论的,它包含了显示的头部和底部的程序,它也能创建连接对象。让我们把PDO对象赋于给全局变量 $conn,和头部程序showHeader()底部程序showFooter().我们也把数据库连接字符串,用户名,密码放在这个引用文件里:

<?php
/**
* This is a common include file
* PDO Library Management example application
* @author Dennis Popel
*/
// DB connection string and username/password
$connStr = ‘mysql:host=localhost;dbname=pdo’;
$user = ‘root’;
$pass = ‘root’;
/**
* This function will render the header on every page,
* including the opening html tag,
* the head section and the opening body tag.
* It should be called before any output of the
* page itself.
* @param string $title the page title
*/
function showHeader($title)
{
?>
<html>
<head><title><?=htmlspecialchars($title)?></title></head>
<body>
<h1><?=htmlspecialchars($title)?></h1>
<a href=”books.php”>Books</a>
<a href=”authors.php”>Authors</a>
<hr>
<?php
}
/**
* This function will ‘close’ the body and html
* tags opened by the showHeader() function
*/
function showFooter()
{
?>
</body>
</html>
<?php
}
// Create the connection object
$conn = new PDO($connStr, $user, $pass);
正如你看到的,这个文件是很简单,你需要改动变量$user$pass值与你的设定匹配。关于SQLite,你要改动第8行适当的连接字符串,例如:
$connStr = ’sqlite:/www/hosts/localhost/pdo.db’;
当然,你要把它成指向创建SQLite数据库的路径。函数showHeader()返回hmtl代码,并把传递过来的$title通过函数htmlspecialchars()把不合法的字符合理地转义。
把文件保存到根目录,这个依赖你的服务器的设置。例如它可能是C:\Apache\htdocs or /var/www/html.
让我们创建books列表页面,我们要查询并迭代查询后的结果集,以每行一本书的形式显式,之后我们也要创建显示所有作者的页面,完成这些后,遍历结果集.
让我们创建并访问books.php
<?php
/**
* This page lists all the books we have
* PDO Library Management example application
* @author Dennis Popel
*/
// Don’t forget the include
include(’common.inc.php’);
// Issue the query
$q = $conn->query(”SELECT * FROM books ORDER BY title”);
// Display the header
showHeader(’Books’);
// now create the table
?>
<table width=”100%” border=”1″ cellpadding=”3″>
<tr style=”font-weight: bold”>
<td>Title</td>
<td>ISBN</td>
<td>Publisher</td>
<td>Year</td>
<td>Summary</td>
</tr>
<?php
// Now iterate over every row and display it
while($r = $q->fetch(PDO::FETCH_ASSOC))
{
?>
<tr>
<td><?=htmlspecialchars($r['title'])?></td>
<td><?=htmlspecialchars($r['isbn'])?></td>
<td><?=htmlspecialchars($r['publisher'])?></td>
<td><?=htmlspecialchars($r['year'])?></td>
<td><?=htmlspecialchars($r['summary'])?></td>
</tr>
<?php
}
?>
</table>

<?php
// Display footer
showFooter();
这个文件将被保存到与common.icn.php相册目录。正如你看到的,里面有很多注释和html,但一点也不复杂,我们之前所做的决定,文件引用common.inc.php文件,然后返回页面的header,在10行处执行查询,返回表格的header,从结果集迭代所有的行,显示出每本书的详细信息。
与第一章一样,我们使用函数PD)Statement的fetch()在一行内遍历结果集。我们指示该方法返回的行是数组索引,其值为表中列的名称。(指定PDO::FETCH_ASSOC为参数)

在热循环中,我们在每一行都返回了html.插入表的列。退出循环,我们关闭了表格并显示了footer.
现在是测试我们第一个PDO驱动的程序的时间了,启动浏览器并浏览http://localhost/books.php,如果一切正常(你的web服务器和数据库服务器被正确配置),你将看到与下面的截屏类似(可能你的页面看起来更宽点,我们在截图前调整了窗口尺寸,以适应打印页面)
一旦我们的应用程序与mysql能正确运行 ,让我们看看它与sqlite运行的。我们要the common.inc.php文件的第8行处添加sqlite dsn:
$connStr = ’sqlite:/www/hosts/localhost/pdo.db’;
如果一切正常,刷新你的浏览器,你将看到同样的结果。正如我们早前讨论的-如果你要使用其它的数据库,只有一处的配置选项要改动。

现在我们在上次保存两个文件的文件里创建名为author.php的author列表的页面。代码内容基本与books列表页面相同:
<?php
/**
* This page lists all the authors we have
* PDO Library Management example application
* @author Dennis Popel
*/
// Don’t forget the include
include(’common.inc.php’);
// Issue the query
$q = $conn->query(”SELECT * FROM authors ORDER BY lastName,
firstName”);
// Display the header
showHeader(’Authors’);
// now create the table
?>
<table width=”100%” border=”1″ cellpadding=”3″>
<tr style=”font-weight: bold”>
<td>First Name</td>
<td>Last Name</td>
<td>Bio</td>
</tr>
<?php
// Now iterate over every row and display it
while($r = $q->fetch(PDO::FETCH_ASSOC))
{
?>
<tr>
<td><?=htmlspecialchars($r['firstName'])?></td>
<td><?=htmlspecialchars($r['lastName'])?></td>
<td><?=htmlspecialchars($r['bio'])?></td>
</tr>
<?php
}
?>
</table>
<?php
// Display footer
showFooter();
这个文件执行一样的逻辑:引用common.inc.php文件,并执行查询,遍历结果集。如果一切运行正常,在books页面点击authors链接,浏览器将会显示下面的内容:
正如你看到的,页面正确显示我们在第一章添加的三个作者,如果你想测试一下sqlite的,修改10行Sqlite连接字符串,刷新浏览器,你将看到一样的结果,但它是连接到SQlite数据库上的。
现在我们已经创建了两个页面,了解到使用PDO不是那么复杂,让我们在扩展这个程序之前了解一些理论知识。

pdo语句和结果设置
我们的例子使用了PHP数据库对象两个类:pdo类和PDOstatement类。pdo类被用来创建连接,执行查询。PDOStatement类被用来遍历结果集。在下一章节我们将研究pdo类。这里我们将验证PDOStatement类遍历结果的其它方式。
我们已经知道,PDOStatement类是通过调用PDO::query方法后返回的。这个类的最大用途是提供结果集的接口。事实上,我们已经使用最重要的方法迭代结果集,我们只看到一种获取方法(返回行的模式),但PDO提供几种方式。这个类支持给结果集符加信息,比如行数和列,获取以二维数组方式的整个结果集。

让我们考虑一下不同的获取方式。我们已经知道PDO::FETCH_ASSOC模式,它返回以列名为索引的数组。PDOStatement类默认操作是返回索引是整数和列名的数组。它是PDO::FETCH_BOTH模式,我们也可以使用PDO::FETCH_NUM模式得到整数索引数组。PDO也支持通过使用PDO::FETCH_OBJ模式将提取行作为对象来处理。在这种情况,通过调用PDO::fetch方法将返回stdClass内部类的实例,它的属性是行的值。代码如下:
$q = $conn->query(’SELECT * FROM authors ORDER BY lastName,
firstName’);
$r = $q->fetch(PDO::FETCH_OBJ);
var_dump($r);
//would print:
object(stdClass)#4 (4)
{
["id"]=>
string(1) “3″
["firstName"]=>
string(7) “Cameron”
["lastName"]=>
string(6) “Cooper”
["bio"]=>
string(112) “J. Cameron Cooper has been playing around on the web
since there was not much of a web with which to play around”
}
PDOStatement类也允许你一次设置后来调用的fetch()方法,它通过调用PDOStatement::setFetchMode()方法。此方法接受PDO::FETCH_ASSOC,PDO_FETCH_BOTH,PDO_FETCH_NUM,PDO::FETCH_OBJ这些常量。考虑这一点,我们修改authors.php23-24行为
// Now iterate over every row and display it
$q->setFetchMode(PDO::FETCH_ASSOC);
while($r = $q->fetch())
{
你可以试着打开authors.php文件并刷新浏览器,看看显示结果。

你可能已经注册到SQLite,MySQL,pgSQL PHP扩展名提供相似的功能。的确,我们可以使用一些mysql_fetch_row(),mysql_fetch_assoc(),mysql_fetch_array();mysql_fetch_object()方法完成一样的结果。这也是为什么PDO进一步允许我们使用那三个符加获取模式。这三个模式可以被通过PDOStatement::setFetchMode()设置,它们的介绍:
PDO::FETCH_COLUMN 允许你指示PDOStatement对象每行返回指定的列,基于这种情况,PDO::fetch()将返回标量值。列数从0开始。代码如下:
$q = $conn->query(’SELECT * FROM authors ORDER BY lastName,
firstName’);
$q->setFetchMode(PDO::FETCH_COLUMN, 1);
while($r = $q->fetch())
{
var_dump($r);
}
//would print:
string(7) “Cameron”
string(4) “Marc”
string(6) “Sohail”
这表明调用$q->fetch()确实返回标量值(不是数组),如果你简单看一下作者页面,会注意到索引1的列是作者姓,不是名。我们的像select * from authors查询,它也会返回作者id,它被保存在首列,你可能会意识到,因为这你可能会发几个小时来查找逻辑错误的资源。

PDO::FETCH_INTO能被用来修改对象的引用。让我们重写下面的例子:
$q = $conn->query(’SELECT * FROM authors ORDER BY lastName,
firstName’);
$r = new stdClass();
$q->setFetchMode(PDO::FETCH_INTO, $r);
while($q->fetch())
{
var_dump($r);
}

object(stdClass)#3 (4)
{
["id"]=>
string(1) “3″
["firstName"]=>
string(7) “Cameron”
["lastName"]=>
string(6) “Cooper”
["bio"]=>
string(112) “J. Cameron Cooper has been playing around on the
web since there was not much of a web with which to play around”
}
object(stdClass)#3 (4)
{
["id"]=>
string(1) “1″
["firstName"]=>
string(4) “Marc”
["lastName"]=>
string(7) “Delisle”
["bio"]=>
string(54) “Marc Delisle is a member of the MySQL Developer
Guide”
}
object(stdClass)#3 (4)
{
["id"]=>
string(1) “2″
["firstName"]=>
string(6) “Sohail”
["lastName"]=>
string(6) “Salehi”
["bio"]=>
string(101) “In recent years, Sohail has contributed to over 20
books, mainly in programming and computer graphics”
}
notes:
在整个热循环里我们没有定义变量$r,这个变量是方法$q->fetch()返回的值,在循环之前通过调用$q->setFetchmode($r已创经被绑定在这个方法.
PDO::FETCH_CLASS能返回指定类的对象。每一行,这个类的实例被创建。命名属性并把结果集赋值于它,注意从PHP允许运行时创建对象属性,那个类并不是必须要有声明的属性,例如:
$q = $conn->query(’SELECT * FROM authors ORDER BY lastName,firstName’);
$q->setFetchMode(PDO::FETCH_CLASS, stdClass);
while($r = $q->fetch())
{
var_dump($r);
}
这将与上一个例显示相似的内容。这个获取模式允许你去创建实例,通过传递一个数组作为原素到其构造:
$q->setFetchMode(PDO::FETCH_CLASS, SomeClass, array(1, 2, 3));
(只有当SomeClass类被定义了,才会正常运行)
我们推建使用PDOStatement::setFetchMode(),因为它更容易修护(当然,有更多的特点)
描述所有的获取方法似乎过多,但它们在某些情况下很有用,的确,你可能注意到books列表少些什么,它没有包含作者的名字,我们将添加这个缺少的列,使我们例子复杂一些,让作者姓名可点击,并链接到作者单独页面(我们将创建),这个单独的页面需要作者的ID,我们可以放id放在URL里面,页面将显示关于这个作者的所有信息,并列出他所有的书,让我们开始写独立的作者页面:
<?php
/**
* This page shows an author’s profile
* PDO Library Management example application
* @author Dennis Popel
*/
// Don’t forget the include
include(’common.inc.php’);
// Get the author
$id = (int)$_REQUEST['id'];
$q = $conn->query(”SELECT * FROM authors WHERE id=$id”);
$author = $q->fetch(PDO::FETCH_ASSOC);
$q->closeCursor();
// Now see if the author is valid – if it’s not,
// we have an invalid ID
if(!$author) {
showHeader(’Error’);
echo “Invalid Author ID supplied”;
showFooter();
exit;
}
// Display the header – we have no error
showHeader(”Author: $author[firstName] $author[lastName]“);
// Now fetch all his books
$q = $conn->query(”SELECT * FROM books WHERE author=$id ORDER BY title”);
$q->setFetchMode(PDO::FETCH_ASSOC);
// now display everything
?>

<h2>Author</h2>
<table width=”60%” border=”1″ cellpadding=”3″>
<tr>
<td><b>First Name</b></td>
<td><?=htmlspecialchars($author['firstName'])?></td>
</tr>
<tr>
<td><b>Last Name</b></td>
<td><?=htmlspecialchars($author['lastName'])?></td>
</tr>
<tr>
<td><b>Bio</b></td>
<td><?=htmlspecialchars($author['bio'])?></td>
</tr>
</table>
<h2>Books</h2>
<table width=”100%” border=”1″ cellpadding=”3″>
<tr style=”font-weight: bold”>
<td>Title</td>
<td>ISBN</td>
<td>Publisher</td>
<td>Year</td>
<td>Summary</td>
</tr>
<?php
// Now iterate over every book and display it
while($r = $q->fetch())
{
?>
<tr>
<td><?=htmlspecialchars($r['title'])?></td>
<td><?=htmlspecialchars($r['isbn'])?></td>
<td><?=htmlspecialchars($r['publisher'])?></td>
<td><?=htmlspecialchars($r['year'])?></td>
<td><?=htmlspecialchars($r['summary'])?></td>
</tr>
<?php
}
?>
</table>
<?php
// Display footer
showFooter();
文件名为author.php,保存在其它被引用文件的位置。
因为防止存在安全漏洞的,我们把作者ID强制转换为数字。稍后会把变量$id下传递到查询语句中.因为它是一个数字,所以我们就不用加引号。
我们将在下章讨论13行处引用$q->closeCursor();$q=null。现在我们只需要注意到,在连接对象相同的情况下执行查询与之后设置它为null之间,引用这些方法是一个好主意。少了它我们的例子也不会正常执行。在最后查询之后我们会意识到这些也不是必须的。
我们也做了简单的错误处理,检查作者ID是否有效,如果无效,我们将显示一个错误信息,并退出程序运行.(查看22到27行)
在25-27行我们使用作者ID创建了查询语句,并设置获取模式为:PDO:FETCH_ASSOC.我们开始显示数据:首先是作者的详细信息,然后是他的所有书.
现在用浏览器打开地址:http://localhost/author.php?id=1.将显示:

如你所见,这个页面一切正常:作者的详细信息,我们首先归档的(id=1),只有这个作者的书.现在我们看一下程序是怎么应对无效的ID,我们只有三个作者,除123之外的数字都是无效的,如果没有值将被认为是0,也是无效的,如果我们把地址改成http://localhost/author.php?id=zzz.我们将看到下面的结果:

你也可以在common.inc.php里把数据库改成SQLite,也会看到这个页会正常运行.
现在让我们修改一下books.php,添加作者列链接到单独的页面.我们要一起查询两个表,books的author字段与author`s id字段相同,authors ID ,first name,last name,查询语句会像这样:
SELECT authors.id, authors.firstName, authors.lastName, books.* FROM authors, books WHERE author=authors.id ORDER BY title;

开如之前我们先在命令行里执行查询语句,我们也将修改一下查询语句,因为命令行窗口不适合整行显示.
mysql> SELECT authors.id, firstName, lastName, books.id, title FROM
authors, books WHERE books.author=authors.id;
+—-+———–+———-+—-+——————————+
| id | firstName | lastName | id | title |
+—-+———–+———-+—-+——————————+
| 1 | Marc | Delisle | 1 | Creating your MySQL… |
| 2 | Sohail | Salehi | 2 | ImageMagick Tricks | | 3 | Cameron | Cooper | 3 | Building Websites with Plone |
+—-+———–+———-+—-+——————————+
3 rows in set (0.00 sec)
正如你看到的,执行结果是有两列叫id,这意味着我们将不能使用PDO::FETCH_ASSOC模式.因为数据只能有一个id索引,这里我们有两个选择:使用PDO::FETCH_NUM模式,使用id字段的别名
让我们看看在代码中如何使用PDO::FETCH_NUM
<?php
/**
* This page lists all the books we have
* PDO Library Management example application
* @author Dennis Popel
*/
// Don’t forget the include
include(’common.inc.php’);
// Issue the query
$q = $conn->query(”SELECT authors.id, firstName, lastName, books.*
FROM authors, books WHERE author=authors.id ORDER
BY title”);
$q->setFetchMode(PDO::FETCH_NUM);
// Display the header
showHeader(’Books’);
// now create the table
?>
<table width=”100%” border=”1″ cellpadding=”3″>
<tr style=”font-weight: bold”>
<td>Author</td>
<td>Title</td>
<td>ISBN</td>
<td>Publisher</td>
<td>Year</td>
<td>Summary</td>
</tr>
<?php
// Now iterate over every row and display it
while($r = $q->fetch())
{
?>
<tr>
<td><a href=”author.php?id=<?=$r[0]?>”>
<?=htmlspecialchars(”$r[1] $r[2]“)?></a></td>
<td><?=htmlspecialchars($r[5])?></td>
<td><?=htmlspecialchars($r[6])?></td>
<td><?=htmlspecialchars($r[7])?></td>
<td><?=htmlspecialchars($r[8])?></td>
<td><?=htmlspecialchars($r[9])?></td>
</tr>
<?php
}
?>
</table>
<?php
// Display footer
showFooter();
注意高亮行-它们的不同之外,文件其它的地方是相同的.正如你所见,我们引用了方法$q->setFetchMode(),改变热循环使用了数据索引.
打开http://localhost/books.php页面,我们将看到与下面截屏相似:

我们点击作者后会转到他们独立的页面,当然换到SQLite数据库也会一样的效果.

别外一个方法是我们在sql里使用字段的别名(也是更好的方法).这样我们就不会关心每次在表中添加或删除一列后数字索引的改变.我们只需要把SQL改成:
SELECT authors.id AS authorId, firstName, lastName, books.* FROM
authors, books WHERE author=authors.id ORDER BY title;

books.php的代码最终会这样:
<?php
/**
* This page lists all the books we have
* PDO Library Management example application
* @author Dennis Popel
*/
// Don’t forget the include
include(’common.inc.php’);
// Issue the query
$q = $conn->query(”SELECT authors.id AS authorId, firstName,
lastName, books.* FROM authors, books WHERE
author=authors.id
ORDER BY title”);
$q->setFetchMode(PDO::FETCH_ASSOC);
// Display the header
showHeader(’Books’);
// now create the table
?>
<table width=”100%” border=”1″ cellpadding=”3″>
<tr style=”font-weight: bold”>
<td>Author</td>
<td>Title</td>
<td>ISBN</td>
<td>Publisher</td>
<td>Year</td>
<td>Summary</td>
</tr>
<?php
// Now iterate over every row and display it
while($r = $q->fetch()
{
?>
<tr>
<td><a href=”author.php?id=<?=$r['authorId']?>”>
<?=htmlspecialchars(”$r[firstName] $r[lastName]“)?></a></td>
<td><?=htmlspecialchars($r['title'])?></td>
<td><?=htmlspecialchars($r['isbn'])?></td>
<td><?=htmlspecialchars($r['publisher'])?></td>
<td><?=htmlspecialchars($r['year'])?></td>
<td><?=htmlspecialchars($r['summary'])?></td>
</tr>
<?php
}
?>
</table>
<?php
// Display footer
showFooter();
注意到我们把获取模式改回到PDO::FETCH_ASSOC,在34行我们通过$['authorId']访问author’s ID.因为我们在查询语句里把它重命名为authorId.

PDO也充许我们以数组的形式得到返回的结果.我们需要进一步处理,传递给一些程序.这要做一些很小结果处理.强烈建议不要像我们这样的做,因为简单显示作者和书.整个结果集变成数组会占用很多内存空间,我们是一行一行显示结果,所以只需要一行占用的空间.
这个方法是引用PDOStatement::fetchAll(),返回的数组是一个二维数组or对象列表.这依赖获取模式.与PDOStatement::fetch()相似,接受PDO::FETCH_xxxx 内容.例如 我们要以下面的方式改写books.php,以达到相同的效果.boods.php相关在部分在9-46行:
// Issue the query
$q = $conn->query(”SELECT authors.id AS authorId, firstName,
lastName, books.* FROM authors, books WHERE
author=authors.id ORDER BY title”);
$books = $q->fetchAll(PDO::FETCH_ASSOC);
// Display the header
showHeader(’Books’);
// now create the table
?>
<table width=”100%” border=”1″ cellpadding=”3″>
<tr style=”font-weight: bold”>
<td>Author</td>
<td>Title</td>
<td>ISBN</td>
<td>Publisher</td>
<td>Year</td>
<td>Summary</td>
</tr>
<?php
// Now iterate over every row and display it
foreach($books as $r)
{
?>
<tr>
<td><a href=”author.php?id=<?=$r['authorId']?>”>
<?=htmlspecialchars(”$r[firstName] $r[lastName]“)?></a></td>
<td><?=htmlspecialchars($r['title'])?></td>
<td><?=htmlspecialchars($r['isbn'])?></td>
<td><?=htmlspecialchars($r['publisher'])?></td>
<td><?=htmlspecialchars($r['year'])?></td>
<td><?=htmlspecialchars($r['summary'])?></td>
</tr>
<?php
}
?>
</table>

注意到高亮的-我们在第5行获取整个结果集到$books数组,在21行遍在结果集,打开修改的页面,你将会看到一样的结果.把数据库换成SQLite后也是一样的.
PDOStatement::fetchAll()方法也充许我们使用PDO::FETCH_COLUMN模式返回单独列.如果我们想获取所有书的标题,我们可以这样做(注意到number and ordering列)
$q = $conn->query(”SELECT authors.id AS authorId, firstName,
lastName, books.* FROM authors, books WHERE
author=authors.id ORDER BY title”);
$books = $q->fetchAll(PDO::FETCH_COLUMN, 5);
var_dump($books);
将显示
array(3)
{
[0]=>
string(28) “Building Websites with Plone”
[1]=>
string(66) “Creating your MySQL Database: Practical Design Tips and
Techniques”
[2]=>
string(18) “ImageMagick Tricks”
}
我们看到只有一行被表求,只返回一维数组.

检索结果集元数据
上一章了解到,PDOStatement类允许我们检索一些关于结果集数据内容的信息。这种信息叫元数据,你可能已经通过这种或那种方法使用过它。
最重要结果集的元数据是内容包含的行数。我们使用行数提交用户体验,例如分页,我们的应用程序是很小的,只有三本书,但是数据会增加的,我会需要为每个表统计行数的工具,为了方便浏览标出页数。
一般我们会使用mysql_num_rows(),sqlite_num_rows(),pg_um_rows()得到查询结果的行数。在PDO里,通过引用PDOStatement方法检索行数。你可以用下面的代码进行测试:
$q = $conn->query(”SELECT * FROM books ORDER BY title”);
$q->setFetchMode(PDO::FETCH_ASSOC);
var_dump($q->rowCount());
你将看到在mysql和sqlite里pdo返回0,这是因为pdo与普通的数据库扩展操作有所不同,文档里说:如果最后的sql语句是select语句,该语句被关联的PDOStatement类执行,一些数据库会返回该查询行数,但是这不能保证所有数据库有效并不能保证其可移植性,mysql和 sqlite都不支持这个功能。这也是该方法返回0的原因。我们将在第5章看到怎么通过PDO(它是一个可移植方法)得到返回的行数.
notes:
关系数据库不知道一个查询有多少行被返回,直到最后一行被检索。这样做是性能原因。大多数情况下,查询会有where子句,返回表中一部分行,数据库服务器尽最大努力确保以最快的速度查询。意味着找到与where子句相匹配结果时就返回-这要比检索到最后一行早很多。这是为什么它们预先不知道多少行被返回。mysql_num_rows(), sqlite_num_rows() function or the pg_num_rows()方法操控的结果集已经放入内存中,(缓冲查询),PDO默认操作是不使用缓冲查询,我们将在第6章讨论mysql缓冲查询。

另一个方法PDOSatement::columnCount()可能感兴趣,它返回结果集的列数。当我们执行任意查询,都很方便。(例如像phpmyadmin的数据库管理应用程序就很有用,因为它充许使用任意的SQL查询),我们能以下面的方式使用它:
$q = $conn->query(”SELECT authors.id AS authorId, firstName,
lastName, books.* FROM authors, books WHERE
author=authors.id ORDER BY title”);
var_dump($q->columnCount());
将显示结果集包含10列。
不幸的是,PDO现在还不允许检索表名或结果集的某列。这个功能是非常有用,当你的程序利用多表查询。在这种情况下,它可能获取指定的列,它的数字索引从0开始的表。列别名正确使用,无需使用这些功能,例如,当我们修改books列表页显示作者名字,我们对author的ID列使用别名来避免名字冲突。那个别名将被认为是从符于au
thors表的列。
小结
在这一章节里,我们通过PDO寻了一个动态的应用程序,它能运行在两个不同的数据库上。现在通过构建连接字符串的规则,能连接到任何被支持的数据库。能执行并遍历显示结果集。
下一章我们将处理非常重要的方法,所有动态程序的错误处理。我也将继续使用我们的例子,让它能添加修改books,authors,使它更有用。

PHP DATA Object 1-7 Appropriate Understanding of PDO

January 27th, 2010

Appropriate Understanding of PDO

Our introduction would not be complete if we didn’t mention that. PDO is a database connection abstraction library, and as such, cannot ensure that your code will work for each and every database that it supports. This will only happen if your SQL code is portable. For example, MySQL extends the SQL syntax with this form of insert:
INSERT INTO mytable SET x=1, y=’two’;
This kind of SQL code is not portable, as other databases do not understand this way of doing inserts. To ensure that your inserts work across databases, you should replace the above code with :
INSERT INTO mytable(x, y) VALUES(1, ‘two’);
This is just one example of incompatibilities that may arise when you use PDO. It is only by making your database schema and SQL portable that can ensure you that your code will be compatible with other databases. However, ensuring this portability is beyond this text.

Summary

This introductory chapter showed you the basics of using PDO when developing dynamic, database-driven applications with the PHP5 language. Also we looked at how PDO can be effectively used to eliminate the differences between different traditional database access APIs and to produce a clearer and more portable code.
In the subsequent chapters, we will be looking at each of the features discussed in this chapter in a greater detail so that you fully master the PHP Data Objects extension.

正确理解PDO

我们的介绍可能是不全面的,PDO是数据库连接抽象库,所以不能确定你的代码可以在每个数据上被支持,唯一可行的就是你的SQL代码是可以移植的,比如,mysql扩展了插入的SQL语法:
INSERT INTO mytable SET x=1, y=’two’;
这种SQL代码是不能被移植的,别的数据库是不能理解这个种插入方法,为了保证你的代码在不同的数据库之前能会正常运行 ,你要以下面的代码替换它:
INSERT INTO mytable(x, y) VALUES(1, ‘two’);
例子中不兼容的情况可能在你使用PDO时就会出现,它只有能过保证数据库模式和SQL可移植,你的代码才能与别的数据库兼容。当然,确保这种可移植性超了出书写范围。

总结

这章介绍了以PHP5用PDO开发动态的数据库驱动的应用的基础知识。我们也看到PDO是怎么有效去除不同传统数据库访问接口的差异和提供简洁、可移植的代码。
在下面章节里我们将详细了解到这章里讨论过的特性,充分利用PHP数据对象扩展。

PHP DATA Object 1-6 Prepared Statements

January 27th, 2010

Prepared Statements

This is a rather advanced topic, but you should become familiar with it. If you are a user of PHP with MySQL or SQLite, then you probably didn’t even hear of prepared statements, since PHP’s MySQL and SQLite extensions don’t offer this functionality. PostgreSQL users might have already used pg_prepare() and pg_execute() in tandem. MySQLi (the improved MySQL extension) also offers the prepared statements functionality, but in a somewhat awkward way (despite the possible object-oriented style).

For those who are not familiar with prepared statements, we will now give a
short explanation.
When developing database-driven, interactive dynamic applications, you will sooner or later need to take user input (which may originate from a form) and pass it as a part of a query to a database. For example, given our cars’ database, you might design a feature that will output a list of cars made between any two years. If you allow the user to enter these years in a form, the code will look something like this:

// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = “SELECT * FROM cars WHERE year >= $sy AND year <= $ey”;
// send the query in $sql…
In this simple example the query depends on two variables, which are part of the resulting SQL. A corresponding prepared statement in PDO would look something like this:
$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
As you can see, we replaced the $sy and $ey variables with placeholders in the query body. We can now manipulate this query to create the prepared statement and execute it:
// Assuming we have already connected and prepared
// the $sy and $ey variables
$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));
These three lines of code tells us that the prepared statements are objects (with class PDOStatement). They are created using calls to PDO::prepare() method that accepts an SQL statement with placeholders as its parameters.
The prepared statements then have to be executed in order to obtain the query results by calling the PDOStatement::execute() method. As the example shows, we call this method with an array that holds the values for the placeholders. Note how the order of the variables in that array matches the order of the placeholders in the $sql variable. Obviously, the number of elements in the array must be the same as the number of placeholders in the query.
You have probably noticed that we are not saving the result of the call to the PDOStatement::execute() method in any variable. This is because the statement object itself is used to access the query results, so that we can complete our example to look like this:
// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));
// now iterate over the result as if we obtained
// the $stmt in a call to PDO::query()
while($r = $stmt->fetch(PDO::FETCH_ASSOC))
{
echo “$r[make] $r[model] $r[year]\n”;
}
As this complete example shows, we call the PDOStatement::fetch() method until it returns a false value, at which point the loop quits—just like we did in previous examples when discussing result sets traversal.
Of course, the replacement of question mark placeholders with actual values is not the only thing that prepared statements can do. Their power lies in the possibility of being executed as many times as needed. This means that we can call the PDOStatement::execute() method as many times as we want, and every time we can supply different values for the placeholders. For example, we can do this:
$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
$stmt = $conn->prepare($sql);
// Fetch the ‘new’ cars:
$stmt->execute(array(2005, 2007));
$newCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// now, ‘older’ cars:
$stmt->execute(array(2000, 2004));
$olderCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Show them
echo ‘We have ‘, count($newCars), ‘ cars dated 2005-2007′;
print_r($newCars);
echo ‘Also we have ‘, count($olderCars), ‘ cars dated 2000-2004′;
print_r($olderCars);
Prepared statements tend to execute faster than calls to PDO::query() methods, since the database drivers optimize them only once, in a call to PDO::prepare() methods. Another advantage of using prepared statements is that you don’t have to quote the parameters passed in a call to PDOStatement::execute().
In our example we used an explicit cast of the request parameters into integer variables, but we could also have done the following:
// Assume we also want to filter by make
$sql = ‘SELECT * FROM cars WHERE make=?’;
$stmt = $conn->prepare($sql);
$stmt->execute(array($_REQUEST['make']));
The prepared statement here will take care of the proper quoting made before executing the query.
And just to finish the introduction of the prepared statements here, probably the best feature about them is that PDO emulates them for every supported database. This means you can use prepared statements with any databases; even if they don’t know what they are.

预处理语句

这 是一个相当高深的话题,但一会你就会熟悉它。如果你在使用PHP和Mysql或SQLite,可能还没有听说过预处理语句,因为它的扩展没有这样的特性。 PostgreSQL用户可能已经一并使用过使用过pg_prepare和pg_execute();MySQLi(优化过的mysql扩展)也提供预处 理语句功能的扩展,但不是一个很好的方法。

如果对预处理语句不太熟悉,为你准备了一个小例子。

当开发数据库驱动,动态交互程序,你将或早或晚要从用户那里取得输入数据(它可能来自一个表单),并把它传递给数据库查询语句。例如,对于我们的汽车数据库,你可能会设计一个能列出任意两个年份中间生产的汽车,如果你充许用户从表单里提交年份,代码就像这样:

// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = “SELECT * FROM cars WHERE year >= $sy AND year <= $ey”;
// send the query in $sql…

例子中的查询依赖两个变量,也是构成SQL的一部分,在PDO与之对应的预处理语句将是这样:
$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
如你所见,我们查询语句中使用占位符替代了变量$sy和$ey。现在我们可以利用查询创建一个预处理语句.
// Assuming we have already connected and prepared
// the $sy and $ey variables
$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));
这三行代码告诉我们预处理语句是对像(基于PDOStatement).它们被创后引用PDO::prepare()。PDO::prepare()以一个有占位符的SQL语句作为它的参数。
预 处理语句通过引用the PDOStatement::execute()方法取得查询结果。正如例子显示的,我们引用的方法的参数是一个数组,此数组的拥有替代占位符的值。那数 组的值是怎么对应上查询语句上的占位符呢,很明显,数组中元素的位置必须与查询语句中占位符的位置要一至。

你可能注意到我们没有为结果以变量的形式保存,这是因为语句本身来保存查询结果,所以你可以把我们可以把例子以这样来完成:
// Suppose the years come in the startYear and endYear
// request variables:
$sy = (int)$_REQUEST['startYear'];
$ey = (int)$_REQUEST['endYear'];
if($ey < $sy)
{
// ensure $sy is less than $ey
$tmp = $ey;
$ey = $sy;
$sy = $tmp;
}
$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
$stmt = $conn->prepare($sql);
$stmt->execute(array($sy, $ey));
// now iterate over the result as if we obtained
// the $stmt in a call to PDO::query()
while($r = $stmt->fetch(PDO::FETCH_ASSOC))
{
echo “$r[make] $r[model] $r[year]\n”;
}
通过完整的例子了解到,我们调用PDOStatement::fetch()方法直到它返回值为假时,退出循环,就像前面例子中讨论遍历结果集。
当然替换问号占位符为真实值不是预处理语句唯一能做的事。它的强大之处是可被无限次的执行。就是说我们可以想引用PDOStatement::execute()都可以,每次我们可以为占位符设置不同的值,就像这样:

$sql = ‘SELECT * FROM cars WHERE year >= ? AND year <= ?’;
$stmt = $conn->prepare($sql);
// Fetch the ‘new’ cars:
$stmt->execute(array(2005, 2007));
$newCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// now, ‘older’ cars:
$stmt->execute(array(2000, 2004));
$olderCars = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Show them
echo ‘We have ‘, count($newCars), ‘ cars dated 2005-2007′;
print_r($newCars);
echo ‘Also we have ‘, count($olderCars), ‘ cars dated 2000-2004′;
print_r($olderCars);

预处理语句执行速度比引用PDO::query()快一些,因为在调用PDO::prepare时,数据库驱动只优化他们一次。另外使用预处理语句的好处是调用PDO::execute时不必引用传递过来的参数。
在例子里我们把请求的变量显示转换成整数,我们也可以按下面的做:
// Assume we also want to filter by make
$sql = ‘SELECT * FROM cars WHERE make=?’;
$stmt = $conn->prepare($sql);
$stmt->execute(array($_REQUEST['make']));
[这段文字不知何意,唉~~~]
要注意到预处理语句在执行查询所造成的引号,在这里要结束对预处理语句的介绍,最大特点是PDO为所支持的数据库仿效它们。它的意思是在任何数据库里可以使用预处理。甚至不知道它们是什么。

PHP DATA Object 1-5 Error Handling

January 27th, 2010

Error Handling

Of course, the above examples didn’t provide for any error checking, so they are not very useful for real-life applications.

When working with a database, we should check for errors when opening the connection to the database, when selecting the database and after issuing every query. Most web applications, however, just need to display an error message when something goes wrong (without going into error detail, which could reveal some sensitive information). However, when debugging an error, you (as the developer) would need the most detailed error information possible so that you can debug the error in the shortest possible time.

One simplistic scenario would be to abort the script and present the error message (although this is something you probably would not want to do). Depending on the database, our code might look like this:
// For SQLite:
$dbh = sqlite_open(’/path/to/cars.ldb’, 0666) or die
(’Error opening SQLite database: ‘ .
sqlite_error_string(sqlite_last_error($dbh)));
$q = sqlite_query(”SELECT DISTINCT make FROM cars ORDER BY make”,
$dbh) or die(’Could not execute query because: ‘ .
sqlite_error_string(sqlite_last_error($dbh)));

// and, finally, for PostgreSQL:
pg_connect(”host=localhost dbname=cars user=boss
password=password”) or die(’Could not connect to
PostgreSQL: . pg_last_error());
$q = pg_query(”SELECT DISTINCT make FROM cars ORDER BY make”)
or die(’Could not execute query because: ‘ . pg_last_error());

As you can see, error handling is starting to get a bit different for SQLite compared to MySQL and PostgreSQL. (Note the call to sqlite_error_string(sqlite_last_error($dbh)).)
Before we take a look at how to implement the same error handling strategy with PDO, we should note that this will be only one of the three possible error handling strategies in PDO. We will cover them in detail later in this book. Here we will just use the simplest one:

// PDO error handling
// Assume the connection string is one of the following:
// $connStr = ‘mysql:host=localhost;dbname=cars’
// $connStr = ’sqlite:/path/to/cars.ldb’;
// $connStr = ‘pgsql:host=localhost dbname=cars’;
try
{
$conn = new PDO($connStr, ‘boss’, ‘password’);
}
catch(PDOException $pe)
{
die(’Could not connect to the database because: ‘ .
$pe->getMessage();
}
$q = $conn->query(”SELECT DISTINCT make FROM cars ORDER BY make”);

if(!$q)
{
$ei = $conn->errorInfo();
die(’Could not execute query because: ‘ . $ei[2]);
}

This example shows that PDO will force us to use a slightly different error handling scheme from the traditional one. We wrapped the call to the PDO constructor in a try … catch block. (Those who are new to PHP5’s object-oriented features should refer to Appendix A.) This is because while PDO can be instructed not to use exceptions, (in fact, it is PDO’s default behavior not to use exceptions), however, you cannot avoid exceptions here. If the call to the constructor fails, an exception will always be thrown.

It is a very good idea to catch that exception because, by default, PHP will abort the script execution and will display an error message like this:
Fatal error: Uncaught exception ‘PDOException’ with message ‘SQLSTATE[28000] [1045] Access denied for user ‘bosss’@'localhost’ (using password: YES)’ in /var/www/html/pdo.php5:3 Stack trace: #0 c:\www\hosts\localhost\pdo.php5(3): PDO->__construct(’mysql:host=loca…’, ‘bosss’, ‘password’, Array) #1 {main} thrown in /var/www/html/pdo.php5 on line 3

We made this exception by supplying the wrong username, bosss, in the call to the PDO constructor. As you can see from this output, it contains some details that we would not like others to see: Things like file names and script paths, the type of database being used, and most importantly, usernames and passwords. Suppose that this exception had happened when we had supplied the right username and something had gone wrong with the database server. Then the screen output would have contained the real username and password.

If we catch the exception properly, the error output might look like this:
SQLSTATE[28000] [1045] Access denied for user ‘bosss’@'localhost’ (using password: YES)
This error message contains much less sensitive information. (In fact, this output is very similar to the error output that would be produced by one of our non-PDO examples.) But we will again warn you that the best policy is just show some neutral error message like: “Sorry, the service is temporarily unavailable. Please try again later.” Of course, you should also log all errors so that you can find out later whether anything bad has happened.

错误处理

上面的例子没有验证错误,所以称不上真证实用的程序.
当 处理与数据库有关的工作,我们必须在连接一个数据库和选择数据库和执行查询时,验证错误是否发生,大部分网络程序在出现问题时仅显示一个错误信息,(可能 会有敏感信息,所以不需要显示所有错误细节),但检测一个错误时,你(作为开发者)可能需要尽可能多的错误详细信息,这样你可以在最少的时间内修正这个错 误.
有关数据库的脚本显示错误,最简单的方案可以像这样:
// For SQLite:
$dbh = sqlite_open(’/path/to/cars.ldb’, 0666) or die
(’Error opening SQLite database: ‘ .
sqlite_error_string(sqlite_last_error($dbh)));
$q = sqlite_query(”SELECT DISTINCT make FROM cars ORDER BY make”,
$dbh) or die(’Could not execute query because: ‘ .
sqlite_error_string(sqlite_last_error($dbh)));

// and, finally, for PostgreSQL:
pg_connect(”host=localhost dbname=cars user=boss
password=password”) or die(’Could not connect to
PostgreSQL: . pg_last_error());
$q = pg_query(”SELECT DISTINCT make FROM cars ORDER BY make”)
or die(’Could not execute query because: ‘ . pg_last_error());

如你看到的,SQLite与Mysql和PostgreSQL处理错误有些不同.(注意引用sqlite_error_string(sqlite_last_error($dbh)).)

在我们了解如何使用PDO实现同样的错误处理方式之前,要注意到这将是唯一一个PDO三个假设的处理错误方式.在本书的以后章节中详细说明.接下来我们将使用这个最简方案:
// PDO error handling
// Assume the connection string is one of the following:
// $connStr = ‘mysql:host=localhost;dbname=cars’
// $connStr = ’sqlite:/path/to/cars.ldb’;
// $connStr = ‘pgsql:host=localhost dbname=cars’;
try
{
$conn = new PDO($connStr, ‘boss’, ‘password’);
}
catch(PDOException $pe)
{
die(’Could not connect to the database because: ‘ .
$pe->getMessage();
}
$q = $conn->query(”SELECT DISTINCT make FROM cars ORDER BY make”);

if(!$q)
{
$ei = $conn->errorInfo();
die(’Could not execute query because: ‘ . $ei[2]);
}

这个例子展示PDO强制我们使用一个轻量级的与传统不同的方法。
在try …catch块中我们调用PDO的构造函数(对PHP的面向对象感到陌生,可到P附录A中查询)这是因为PDO可以被调用就没有例外,(事实上,PDO的默认行为不去处理例外的情形)但是在这里不可避免有例外情形。如果调用构造函数失败,事外情形将被执行。

捕捉例外是一个很好的主意,因为PHP中止脚本运行 ,并显示像这样的错误信息:
Fatal error: Uncaught exception ‘PDOException’ with message ‘SQLSTATE[28000] [1045] Access denied for user ‘bosss’@'localhost’ (using password: YES)’ in /var/www/html/pdo.php5:3 Stack trace: #0 c:\www\hosts\localhost\pdo.php5(3): PDO->__construct(’mysql:host=loca…’, ‘bosss’, ‘password’, Array) #1 {main} thrown in /var/www/html/pdo.php5 on line 3

在 引用PDO的构造函数时,我们让例外情形提供错误的名字,bosss。因为你看到输出的内容,它包括一些不想让别人看到的细节,所有像文件名,脚本路径, 使用数据库的类形,用户名和密码,更多重要的信息。假设我们使用正确的用户名和别的一些让数据库服务器出错信息,屏幕将会显示真实的用户名和密码。
如果我们适当捕捉到例外情形,输出的错误将是这样:
SQLSTATE[28000] [1045] Access denied for user ‘bosss’@'localhost’ (using password: YES)
这 个错误信息包括很少的敏感信息,(事实上,这个输出很像我们一个没有使用PDO例子发出的输出)全是要再一次的提醒你,最好的方法是只显示自然的错误信 息,比如:“对不起,服务器暂时不能被连接,请稍后重试”。当然你也可以记录所有的错误,这样当一些错误出现后,你就能找到它们。

PHP DATA Object 1-4 Issuing SQL Queries, Quoting Parameters, and

January 27th, 2010

Issuing SQL Queries, Quoting Parameters, and Handling Result Sets

PDO would not be worth a whole book, if it didn’t go beyond the single interface for creating database connections. The PDO object introduced in the previous example has all the methods needed to uniformly execute queries regardless of the database used. Let’s consider a simple query that would select all the car make attributes from an imaginary database employed at a used car lot. The query is as simple as the following SQL command:
SELECT DISTINCT make FROM cars ORDER BY make;

Previously, we would have had to call different functions, depending on the database:

// Let’s keep our SQL in a single variable
$sql = ‘SELECT DISTINCT make FROM cars ORDER BY make’;

// Now, assuming MySQL:
mysql_connect(’localhost’, ‘boss’, ‘password’);
mysql_select_db(’cars’);
$q = mysql_query($sql);

// For SQLite we would do:
$dbh = sqlite_open(’/path/to/cars.ldb’, 0666);
$q = sqlite_query($sql, $dbh);

// And for PostgreSQL:
pg_connect(”host=localhost dbname=cars user=boss
password=password”);
$q = pg_query($sql);

Now that we are using PDO, we can do the following:
// assume the $connStr variable holds a valid connection string
// as discussed in previous point

$sql = ‘SELECT DISTINCT make FROM cars ORDER BY make’;
$conn = new PDO($connStr, ‘boss’, ‘password’);
$q = $conn->query($sql);

As you can see, doing things the PDO way is not too different from traditional methods of issuing queries. Also, here it should be underlined, that a call to $conn->query() is returning another object of class PDOStatement, unlike the calls to mysql_query(), sqlite_query(), and pg_query(), which return PHP variables of the resource type. Now, let’s make our simplistic SQL query a bit more complicated so that it selects the total value of all Fords on sale in our imaginary car lot. The query would then look something like this:
SELECT sum(price) FROM cars WHERE make=’Ford’

To make our example even more interesting, let’s assume that the name of the car manufacturer is held in a variable ($make) so that we must quote it, before passing it to the database. Our non-PDO queries would now look like this:
$make = ‘Ford’;
// MySQL:
$m = mysql_real_escape_string($make);
$q = mysql_query(”SELECT sum(price) FROM cars WHERE make=’$m’”);

// SQLite:
$m = sqlite_escape_string($make);
$q = sqlite_query(”SELECT sum(price) FROM cars WHERE make=’$m’”,$dbh);

// and PostgreSQL:
$m = pg_escape_string($make);
$q = pg_query(”SELECT sum(price) FROM cars WHERE make=’$m’”);

The PDO class defines a single method for quoting strings so that they can be used safely in queries. We will discuss security issues such as SQL injection, in Chapter 3. This method does a neat thing; it will automatically add quotes around the value if necessary:
$m = $conn->quote($make);
$q = $conn->query(”SELECT sum(price) FROM cars WHERE make=$m”);

Again, you can see that PDO allows you to use the same pattern as you would have used before, but the names of all the methods are unified.
Now that we have issued our query, we will want to see its results. As the query in the last example will always return just one row, we will want more rows. Again, the three databases will require us to call different functions on the $q variable that was returned from one of the three calls to mysql_query(), sqlite_query(), or pg_query(). So our code for getting all the cars will look similar to this:

// assume the query is in the $sql variable
$sql = “SELECT DISTINCT make FROM cars ORDER BY make”;

// For MySQL:
$q = mysql_query($sql);
while($r = mysql_fetch_assoc($q))
{
echo $r['make'], “\n”;
}

// For SQLite:
$q = sqlite_query($dbh, $sql);
while($r = sqlite_fetch_array($q, SQLITE_ASSOC))
{
echo $r['make'], “\n”;
}

// and, finally, PostgreSQL:
$q = pg_query($sql);
while($r = pg_fetch_assoc($q))
{
echo $r['make'], “\n”;
}

As you can see, the idea is the same, but we have to use different function names. Also, note that SQLite requires an extra parameter if we want to get the rows in the same way as with MySQL and PostgreSQL (of course, this could be omitted, but then the returned rows would contain both column name indexed and numerically indexed elements.)
As you may already have guessed, things are pretty straightforward when it comes to PDO: We don’t care what the underlying database is, and the methods for fetching rows are the same across all databases. So, the above code could be rewritten for PDO in the following way:

$q = $conn->query(”SELECT DISTINCT make FROM cars ORDER BY make”);
while($r = $q->fetch(PDO::FETCH_ASSOC))
{
echo $r['make'], “\n”;
}

Nothing is different from what happens before. One thing to note here is that we explicitly specified the PDO::FETCH_ASSOC fetch style constant here, since PDO’s default behavior is to fetch the result rows as arrays indexed both by column name and number. (This behavior is similar to mysql_fetch_array(),sqlite_fetch_array() without the second parameter, or pg_fetch_array().) We will discuss the fetch styles that PDO has to offer in Chapter 2.

NOTES:
The last example was not intended to be used to render HTML pages as it used the newline character to separate lines of output. To use it in a real webpage, you will have to change echo $r['make'], “\n”; to echo $r['make'], “<br>\n”;

执行查询,引用变量,处理结果集

如果PDO不超越单一接口去创建数据库连接,就不值用整本书来讲述。上一个例子介绍的PDO对象拥有所有方法须要一致地执行询问不管使用的数据库。让我们考虑一个简单的查询,它将从一个虚拟的二手车店数据库中 选出所有属性为make的车,就像下面的查询语句:

SELECT DISTINCT make FROM cars ORDER BY make;

之前,我们要根据不同的数据库调用不对的方法:
// Let’s keep our SQL in a single variable
$sql = ‘SELECT DISTINCT make FROM cars ORDER BY make’;

// Now, assuming MySQL:
mysql_connect(’localhost’, ‘boss’, ‘password’);
mysql_select_db(’cars’);
$q = mysql_query($sql);

// For SQLite we would do:
$dbh = sqlite_open(’/path/to/cars.ldb’, 0666);
$q = sqlite_query($sql, $dbh);

// And for PostgreSQL:
pg_connect(”host=localhost dbname=cars user=boss
password=password”);
$q = pg_query($sql);

现在看下使用PDO,我们可以像下面那样做:
// assume the $connStr variable holds a valid connection string
// as discussed in previous point

$sql = ‘SELECT DISTINCT make FROM cars ORDER BY make’;
$conn = new PDO($connStr, ‘boss’, ‘password’);
$q = $conn->query($sql);

正 如你看到的,执行假设查询,PDO与传统方式很大的不同。此外,这里应该强调一下,通过调用 $conn->query(),返回PDOStatement类的另外一个对象,不像去调用mysql_query(), sqlite_query(), and pg_query(),返回PHP资源变量。现在使简单查询复杂一点,从虚构的汽车店选出所有在销售的福特汽车,查询语句就会像下面这样:
SELECT sum(price) FROM cars WHERE make=’Ford’

为了使例子更有趣,在查询之前,我们声明$make为停产的汽车的名字并引用它。
普通查询方式是这样的
$make = ‘Ford’;
// MySQL:
$m = mysql_real_escape_string($make);
$q = mysql_query(”SELECT sum(price) FROM cars WHERE make=’$m’”);

// SQLite:
$m = sqlite_escape_string($make);
$q = sqlite_query(”SELECT sum(price) FROM cars WHERE make=’$m’”,$dbh);

// and PostgreSQL:
$m = pg_escape_string($make);
$q = pg_query(”SELECT sum(price) FROM cars WHERE make=’$m’”);

PDO类定义了一个专门引用字符串的方法,它可使查询更安全。在第三章我们将讨论潜在的安全问题,如SQL注入。这个方法起到一个修正的作用,如果有可能它会自动在值的旁边加上引号。
$m = $conn->quote($make);
$q = $conn->query(”SELECT sum(price) FROM cars WHERE make=$m”);

你看到PDO充许使用和你以前使用的相同模式,但方法的名字是统一的。

现 在,我们已经搞定查询语句,我们也将看到查询结果。在最后的例子中,我们不仅想得到一行结果,而且想要更多行,所以,这三个数据需要在 mysql_query(), sqlite_query(),pg_query()中引用不同的方法,得到返回的变量$q;所以我们得到所有汽车的代码将和下面的相似:
// assume the query is in the $sql variable
$sql = “SELECT DISTINCT make FROM cars ORDER BY make”;

// For MySQL:
$q = mysql_query($sql);
while($r = mysql_fetch_assoc($q))
{
echo $r['make'], “\n”;
}

// For SQLite:
$q = sqlite_query($dbh, $sql);
while($r = sqlite_fetch_array($q, SQLITE_ASSOC))
{
echo $r['make'], “\n”;
}

// and, finally, PostgreSQL:
$q = pg_query($sql);
while($r = pg_fetch_assoc($q))
{
echo $r['make'], “\n”;
}

正如你看到的,方法的意思是一样的,但必须调用不同名字的方法。注意到如果我们想得到MYSQL和PostgreSQL一样的行,SQLite需要加一个参数。(当然,它也可以省略,但返回的行会包括列名为主健和数字为主键的元素)
你可能已经想过,通过使用PDO,事情会变的直接明了:我们并没有在意数据库,取得行的方法适用所有数据库。可以下面的方式重新上面的代码:
$q = $conn->query(”SELECT DISTINCT make FROM cars ORDER BY make”);
while($r = $q->fetch(PDO::FETCH_ASSOC))
{
echo $r['make'], “\n”;
}

所 发生的与之前没有什么不同。一点要注意到,我们在这里明确地指定了获取方式 PDO::FETCH_ASSOC,因为PDO的默认行为是取得一个列名和数据为主键的数组。(与没有第二个参数的 mysql_fetch_array(),sqlite_fetch_array() 相似,还有pg_fetch_array())我们将在第二章节讨论PDO提供的获取方式。

提示:
最后的例子中,我们没有使用html缩进是因为我们使用了新行字符来分隔输出的行,在实际的网页中,你可以把echo $r['make'], “\n”; 改写为 echo $r['make'], “<br>\n”;

PHP DATA Object 1-3 Connection Strings

January 27th, 2010

Connection Strings

As you have seen in previous example, PDO uses the so-called connection strings (or Data Source Names, abbreviated to DSN) that allow the PDO constructor to select proper driver and pass subsequent method calls to it. These connection strings or DSNs are different for every database management system and are the only things that you will have to change.

If you are designing a big application that will be able to work with different databases, then this connection string (together with a connection username and a password) can be defined in a configuration file and later used in the following manner (assuming your configuration file is similar to php.ini)

$config = parse_ini_file($pathToConfigFile);
$conn = new PDO($config['db.conn'], $config['db.user'],$config['db.pass']);

Your configuration file might then look like this:
db.conn=”mysql:host=localhost;dbname=test”
db.user=”johns”
db.pass=”mypassphrase”

We will cover connection strings in more detail in Chapter 2; here we gave a quick example so that you can see how easy it is to connect to different database systems with PDO.

连接字符串

从上一个例子中我们了解到,PDO使用所谓的连接字符串(或数据源名称,简称DSN),它允许PDO构造选择合适的驱动器,通过后绪的方法引用它.这些连接字符串或DSNs对不同数据库管理系统不同的,唯一要做的去改变它.
如果你设计了一个大的应用程序,它可能在不同的数据库中可以使用,所以连接字符串(用户名和密码的组合)可以定义在一个配置文件中,以下面的方式使用.(假设你的配置文件类似php.ini)

$config = parse_ini_file($pathToConfigFile);
$conn = new PDO($config['db.conn'], $config['db.user'],$config['db.pass']);

你的配置文件可能像这样
db.conn=”mysql:host=localhost;dbname=test”
db.user=”johns”
db.pass=”mypassphrase”

在第二章关于连接字符串有更多的说明,现在我们快速通过一个例子演示通过PDO连接不同的数据库是多么的简单.

PHP Data Objects 1-2 Using PDO

January 27th, 2010

Using PDO

As it has been noted in the previous section, PDO is a connection, or data access abstraction library. This means that PDO defines a unified interface for creating and maintaining database connections, issuing queries, quoting parameters, traversing result sets, dealing with prepared statements, and error handling.We will give a quick overview of these topics here and look at them in greater detail in the following chapters.

Connecting to the Database
Let’s consider the well-known MySQL connection scenario:

mysql_connect($host, $user, $password);
mysql_select_db($db);

Here, we establish a connection and then select the default database for the connection. (We ignore the issue of possible errors.)
In SQLite, for example, we would write something like the following:
$dbh = sqlite_open($db, 0666);

Here again we ignore errors (we will cover more on this later). For completeness, let’s see how we would connect to a PostgreSQL:
pg_connect(”host=$host dbname=$db user=$user password=$password”);

As you can see, all three databases require quite different ways of opening a connection. While this is not a problem now, but if you always use the same database management system in case you need to migrate, you will have to rewriteyour scripts.

Now, let’s see what PDO has to offer. As PDO is fully object-oriented, we will be dealing with connection objects, and further interaction with the database will involve calling various methods of these objects. The examples above implied the need for something analogous to these connection objects—calls to mysql_connect or pg_connect return link identifiers and PHP variables of a special type: resource. However, we didn’t use connection objects then since these two database APIs don’t require us to explicitly use them if we only have one connection in our scripts. However, SQLite always requires a link identifier.

With PDO, we will always have to explicitly use the connection object, since there is no other way of calling its methods. (Those unfamiliar with object-oriented programming should refer to Appendix A).
Each of the three above connections could be established in the following manner:

// For MySQL:
$conn = new PDO(”mysql:host=$host;dbname=$db”, $user, $pass);
// For SQLite:
$conn = new PDO(”sqlite:$db”);
// And for PostgreSQL:
$conn = new PDO(”pgsql:host=$host dbname=$db”, $user, $pass);

As you can see, the only part that is changing here is the first argument passed to the PDO constructor. For SQLite, which does not utilize username and password, the second and third arguments can be skipped.

NOTES:SQLite is not a database server, but it is an embedded SQL database library that operates on local files. More information about SQLite can be found at www.sqlite.org and more information about using SQLite with PHP can be found at www.php.net/sqlite. Information about using SQLite with PDO can be obtained from www.php.net/manual/en/ref.pdo-sqlite.php

使用PDO

上一节我们解释了PDO是连接或数据访问抽像库.言之意PDO为创建和维护数据内容,执行查询,引用变量,遍历结果集,处理准备语句,错误处理,定义了统一的接口.
我们快速预览这些主题,在接下来的章节详细讨论.

连接数据库
先看一下经典的MYSQL连接方法:

mysql_connect($host,$user,$password);
mysql_select_db($db);

我们创建了一个连接并为这个连接选择了默认数据库(我们忽略了可能出现的错误问题).

在SQLite,我们将像下面的例句那样写:
$dbh = sqlite_open($db,0666);
这里我也勿略了错误问题(我们以后在这方面介绍更多),为了完整,再让我们看看怎么去连接PostgreSQL:
pg_connect(”host=$host dbname=$db user=$user password=$password”);

正如你看到的,三个数据库需要完全不同的方式建立连接.它现在不是一个问题,但如果你总使用同一个数据库管理系统,万一要改变,你将改写你的脚本.
现 在让我们看看PDO必须有什么.因为PDO是面向对象的,我们将使用连接对象来处理,并且与数据库的交互将涉及调用对象的各种方法.上面的例子的含意:一 些类似连接对象,需要调用mysql_connect or pg_connetc 并且返回链接标识和PHP特殊变量类型:资源.我们没有使用连接对象,因为我们只使用一个连接的脚本中不要求我们明确使用两个数据库接口.但SQLite要有一个链接符.

关于PDO,我们必须去明确使用连接对象,因为没有别的途径引用它的方法.(不熟悉面向对象编程可查阅附录A)
// For MySQL:
$conn = new PDO(”mysql:host=$host;dbname=$db”, $user, $pass);
// For SQLite:
$conn = new PDO(”sqlite:$db”);
// And for PostgreSQL:
$conn = new PDO(”pgsql:host=$host dbname=$db”, $user, $pass);

正如你看到的,唯一改动之处是第一个参数传递给PDO构造函数,至于SQLite,它不用设置密码和用户名,第二和第三参数可以勿略.

提 示:SQLite不是数据库服务器,但它是嵌入可以打开本地文件的SQL数据库仓库.更多关于SQLite的介绍和使用可以在 www.sqlite.org和www.php.net/sqlite/找到.更多通过PDO使用SQLite的信息在www.php.net /manual/en/ref.pdo-sqlite.php

learning php data objects -1 Introduction

January 27th, 2010
learning php data objects

learning php data objects

最近在看关于PDO的资料,得到一份learning php data objects 的pdf copy,我的英文底子不是太好,故做此笔记,以方便日后查阅。

PHP Data Objects, (PDO) is a PHP5 extension that defines a lightweight DBMS connection abstraction library (sometimes called data access abstraction library). The need for a tool like PDO was dictated by the great number of database systems supported by PHP. Each of these database systems required a separate extension that defined its own API for performing the same tasks, starting from establishing a connection to advanced features such as preparing statements and error handling.

The fact that these APIs were not unified made transition between underlying databases painful, often resulting in the rewriting of many lines of code, which in turn, led to new programming errors that required time to track, debug and correct. On the other hand, the absence of a unified library, like JDBC for Java, was putting PHP behind the big players in the programming languages world. Now that such library exists, PHP is regaining its position and is a platform of choice for millions
of programmers.

It should be noted, however, that there exist several libraries written in PHP, that serve the same purpose as PDO. The most popular are the ADOdb library and the PEAR DB package. The key difference between them and PDO is speed. PDO is a PHP extension written in a compiled language (C/C++), while the PHP libraries are written in an interpreted language. Also, once PDO is enabled, it does not require you to include source files in your scripts and redistribute them with your application. This makes installing your applications easier, as the end user does not need to take care of third-party software.

Notes:
Here, we are neither comparing these libraries with PDO nor advocating the use of PDO over such libraries. We are just showing the advantages and disadvantages of this extension. For example, the PEAR package, MDB2, has richer functionality of an advanced database abstraction library, which PDO does not.

PDO being a PECL extension, itself relies on database-specific drivers and on other PECL extensions. These drivers must also be installed in order to use PDO (you only need the drivers for the databases you are using). Since the description of installation of PDO and database-specific drivers is beyond the scope of this book, you can refer to PHP manual at www.php.net/pdo for technical information regarding installation and upgrade issues.

Notes:
PECL is PHP Extension Community Library, a repository of PHP extensions written in C. These extensions offer functionality that would be impossible to implement in PHP, as well as some extensions that exist for performance reasons as the C code is much faster than PHP. The home page of PECL is at http://pecl.php.net

PHP数据对象是PHP5的轻量级的DBMS连接抽象库扩展,(也称数据访问抽象库)。PHP支持的大多数据库系统需要像PDO标准数据接口的工具。每一个数据库系统需要独立的扩展,它定义执行相同任务的API,从建立连接到更高级的特性,如准备语句、错误处理。

事实上这些接口没有被统一,使得基础数据转换倍加棘手,常常导致许多代码要重写,花销更多的时间去查找,调试,修改出现的错误。另一方面,缺少像java的JDBC一样的统一库,PHP落后于编程世界最大者。现在类似的库出现,PHP重获它的地位,成为千万编程者的平台。

说 明一下,已经存在与PDO功用一样的PHP库,最流行的是ADOdb库和PEAR DB.它们最主要的区别是速度.PDO是编译语言写的PHP扩展,而PHP库是解释语言写出的.PDO一旦被激活,不需要把它引用到你的脚本中,也不用在 你的应用程序里从新配置它,因为终端用户不关心其它软件,这使得安装应用很方便.

提示:在这里我们不比较PHP库和PDO或推荐PDO代替这些PHP库,我们只是展示一下这个扩散的优点和缺点.比如说,PEAR包,MDB2,有更多功能的高级数据抽像库,这些PDO没有.

PDO 是PECL的一个扩展,它依赖于数据库特性驱动程序和其它PECL扩展.为了使用PDO这些驱动程序必须安装(你只需在使用的数据库的驱动程序).PDO 和数据库特性驱动程序的安装已经超出本书讨论的范围,你可到www.php.net/pdo的手册中查找安装和升级的信息.

asus eeepc 1000he install archlinux

January 8th, 2010

家里的epc的系统是自带的ms xp系统,一直就有时间把系统给改过来,现在闲着没有事,就折腾一把。

安装前的准备:

epc没有光驱,我也没有外置光驱,只能用U盘来安装系统。

先到archlinux.org下载系统的不img文件,我用的是mirrors.163.com的源.速度很快.

  1. 首先卸载你的U盘:umount /dev/sdx1
  2. 之后把它格式化:mkfs.vfat /dev/sdx1
  3. 把系统文件复制到U盘上:dd bs=8M if=image.img of=/dev/sdx. x为你的U盘设备,但不要写sdx1这样!

    where image.img is the path to the img file and /dev/sd[x] is your USB device. *Make sure to use /dev/sdx and not /dev/sdx1. This is a very common error!

更多信息可以到http://wiki.archlinux.org/index.php/Install_from_a_USB_flash_drive

开始安装系统

开机,按键盘左上角的ESC键,选择USB为启动设备,如图:

具体的步骤可以查看一下官方的教程
http://wiki.archlinux.org/index.php/Beginners_Guide

安装步骤

  1. 键入 :/arch/setup
  2. Select an installation source,选择要安装包的来源
  3. Set Clock,设置时区,因为机器上没有别的系统,选UTC
  4. Prepare Hard Drive,开始分区,
    • /swap 2G
    • /boot 300M
    • / 20G
    • /opt 20G
    • /home 余下的空间
  5. Select Packages,选择要安装的软件包,默认为base
  6. Install Packages,安装软件包
  7. Configure the System,设置系统:默认也可以,但管理员密码最好设置一下
  8. Install Bootloader,安装起动引导文件:GRUB
  9. reboot:重起,系统到此已经安装完成,接下来安装窗口系统

安装x

更新系统

重起后,系统会以dhcp的方式连接到网络:ping www.google.com

修改:vi /etc/pacman.d/mirrorlist,加上Server = http://mirrors.163.com/archlinux/$repo/os/i686,然后执行
pacman -Syu

添加用户

useradd -m -G users,audio,lp,optical,storage,video,wheel,power -s /bin/bash username

设置密码

passwd username

安装Sudo

pacman -S sudo

设置sudo

visudo

添加一行

username ALL=(ALL) ALL

设置ALSA

pacman -S alsa-utils alsa-oss

把用户添加到audio组

gpasswd -a username audio

退出root,以普通用户登陆,然后执行:

alsamixer

测试一下是否有声音

aplay /usr/share/sounds/alsa/Front_Center.wav

切换到root,保存配置方案

alsactl store

add the alsa daemon to your DAEMONS section

vi /etc/rc.conf
DAEMONS=(syslog-ng network crond alsa)

install x

安装x前先安装libgl

pacman -S libgl

pacman -S xorg mesa xf86-video-intel

设置 xorg

Xorg -configure
cp /root/xorg.conf.new /etc/X11/xorg.conf

pacman -S xf86-input-synaptics

add the hal daemon to your DAEMONS section

vi /etc/rc.conf
DAEMONS=(syslog-ng hal network crond alsa)

退出root,以普通用户登陆

cp /etc/skel/.xinitrc ~/
vi ~/.xinitrc
添加: exec startxfce4

之后我发现,xfce4不能正常识别U盘,把它改为:exec ck-launch-session startxfce4就可以了

安装xfce4,wicd,slim.fonts

pacman -S xfce4 slim wicd xfce4-goodies fortune-mod ttf-ms-fonts ttf-dejavu ttf-bitstream-vera

设置cpu频率

pacman -S cpufrequtils
vi /etc/rc.conf
MODULES=(acpi-cpufreq cpufreq_ondemand cpufreq_powersave)
DAEMONS=(syslog-ng hal cpufreq @network crond alsa slim wicd )

vi /etc/conf.d/cpufreq
governor="ondemand"
min_freq="1GHz"
max_freq="1.67GHz"

Bluetooth

pacman -S bluez blueman
DAEMONS=(... bluetooth)

可怜的孩子--怡帆

January 4th, 2010

希望看到这个贴子的每个人都伸出缓助之手,帮助这个可爱的小姑娘。

我已通过支付宝捐了一点,尽微薄之力。

在北京工作的潘俊廷是一名优秀的 Linux 程序员,他的女儿潘怡帆因患有肺动脉高压,只能靠着便携式呼吸机生活。
要治愈小怡帆病,只能进行儿童肺移植手术。而这类手术在国内尚未有成功先例,而美国德克萨斯儿童医院则经验丰富。潘俊廷和妻子周萍已经卖掉了房子,然而依然不够手术所需的500000美元。
在下相信热爱开源技术的我们同样拥有充满关怀和仁爱的心,在此希望大家能做些力所能及的事情帮助这名 Linux 程序员,让他的女儿小怡帆拥有同龄人应有的幸福。

怡帆

怡帆

勇敢的怡帆在北京和睦家医院

勇敢的怡帆在北京和睦家医院

联系他们的网站:此情可待
捐赠方式
1. 银行转账

怡帆妈妈农业银行账户:
户名:周萍
账号:6228480010211053011
开户行:中国农业银行北京市分行白石桥支行

2. 支付宝
怡帆基金支付宝账户:yifanfoundation@gmail.com

引用自: linuxtoy.org