【IT168 PHP开发】如果你来这里是想学习基本的PHP4或者PHP5知识,那这就是为你准备的,开始享受吧!
如果你一直都很专心,那么你现在就知道如何使用PHP的MySQL API来执行查询和处理结果集了。你甚至也许已经开始考虑如何重新编写你的站点以运行MySQL数据库了。所有这些都是好事情,这意味着你对使用PHP的数据库支持来为你的应用提供动力感觉很得心应手了,但是还需要进一步的深入。
正如你在第八章中(《PHP开发经典教程(Part 8): 数据库操作》,http://tech.it168.com/o/2007-09-20/200709201021747.shtml)所见,在PHP 5.0中对MySQL的支持不像它过去那样简单了。PHP现在需要你在允许你将你的脚本连接至MySQL数据库之前做所有的关于版本和使用的库的决定而不是直接支持MySQL。如果你比较懒惰(但心里明白,我们都知道你懒惰),你可能宁愿尝试一个更简单的选择:SQLite数据库引擎。
内建的SQLite支持是PHP5.0的新特征,它为用户提供了一种快速、高效而且能完成工作的轻量级数据库系统。因为在PHP 5.0中,默认情况下它能够起作用,所以它提供了一种可行的对于MySQL的替代;你可以直接使用它而不需要在版本检查和依赖库的下载上花费时间;只需要安装PHP 5然后开始打字即可。那就是为什么我专门为其投入了一个完整的教程。
SQLite是什么?
在进入代码之前,让我们确认你对SQLite是什么(及不是什么)有了一个清楚的认识。和MySQL不同,MySQL按照客户-服务器的模式来运行,而SQLite是一种基于文件的数据库引擎,而且使用文件I/O(输入/输出)函数来存储和读取来自磁盘上文件的数据库。它也比MySQL小很多很多(SQLite命令行版本的大小小于200KB)而且支持你所熟悉的大部分SQL命令。
这个小尺寸不应该欺骗得了你,然而根据官方SQLite Web站点,SQLite支持数据库增长到2000G字节大小而且实际上在某种情况下要比MySQL速度更快。SQLite数据库文件易于携带,而且创建于Windows平台下的SQLite数据库在*NIX平台下工作的很好,反之亦然。
SQLite最吸引人的方面之一就是它是完全无类型的。SQLite数据库中的字段不需要与特定类型相关联,而且即使它们相关联,你仍然可以向这些字段中插入不同类型的值(对此有一个例外,但我会在稍后谈到它)。这点是重要的,因为它意味着如果你担心错误类型的值进入你的表格,那么你需要在你的应用程序中编写代码来实现类型检查。
在MySQL和SQLite之间另一个重要的差异在于它们的许可策略:与MySQL不同,SQLite源代码是完全公众域的,这意味着你无论在商业还是非商业产品中均可以选择使用和分发它。
请看http://sqlite.org/copyright.html以获得更多的关于这方面的信息。
为了一起使用SQLite和PHP,你的PHP必须包含SQLite。默认情况下,在PHP5的UNIX和Windows版本中,这个功能是起作用的。
阅读更多关于这方面的内容请参阅http://www.php.net/manual/en/ref.sqlite.php。
然而,如果你是PHP 4.x版本的用户,那么别灰心,你依然可以通过手动下载和安装来自http://snaps.php.net (Windows)的php_sqlite.dll文件或者来自http://pecl.php.net/package/SQLite(UNIX)的最新的tar格式的包来使用SQLite。你不需要下载其他任何东西;SQLite的‘客户端’就是它自身的引擎。
SQL命令
和MySQL一样,你可以使用一般的SQL命令来和SQLite数据库交换作用。SQLite所使用的准确的SQL语法列在http://sqlite.org/lang.html上面,但对大部分操作来说,SQL命令是标准的。
下面是一个例子,它建立了我将在本教程中使用的表格:
C:\WINDOWS\Desktop\sqlite>sqlite library.db
SQLite version 2.8.15
Enter ".help" for instructions
sqlite> create table books (
...> id integer primary key,
...> title varchar(255) not null,
...> author varchar(255) not null
...>);
sqlite> insert into books (title, author) values
('The Lord Of The Rings', 'J.R.R. Tolkien');
sqlite> insert into books (title, author) values
('The Murders In The Rue Morgue', 'Edgar Allen Poe');
sqlite> insert into books (title, author) values
('Three Men In A Boat', 'Jerome K. Jerome');
sqlite> insert into books (title, author) values
('A Study In Scarlet', 'Arthur Conan Doyle');
sqlite> insert into books (title, author) values
('Alice In Wonderland', 'Lewis Carroll');
sqlite> .exit
你可以通过SQLite命令行程序交互式或者非交互式的输入上述命令,SQLite命令行程序可以Windows和Linux平台下的编译好的二进制文件形式从http://sqlite.org/download.html中得到。SQLite 2.* 是当前用于PHP两个分支中的版本,同时SQLite 3.*预期支持PDO和之后的PHP5.*版本。
将下载的文件解压到你选择的一个目录中,在你的shell或者DOS窗口中用cd命令进入到该目录然后输入‘sqlite’。你可以看到SQLite版本信息和下面的一行:
Enter ".help" for instructions
请阅读http://sqlite.org/sqlite.html以获得更多的关于如何使用命令行程序的信息。
一旦将资料输入到数据库文件library.db中,立即运行SELECT查询以检查是否一切情况工作良好:
sqlite> select * from books;
1|The Lord Of The Rings|J.R.R. Tolkien
2|The Murders In The Rue Morgue|Edgar Allen Poe
3|Three Men In A Boat|Jerome K. Jerome
4|A Study In Scarlet|Arthur Conan Doyle
5|Alice In Wonderland|Lewis Carroll
如果你看到和上述输出一样的结果,那么你已经准备好可以出发了。
PHP和SQLite通信
现在,使用PHP和SQLite通信,生成同样的结果集然后将其格式化为HTML页面。下面是代码:
<html> <head></head> <body> <?php // set path of database file $db = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // open database file $handle = sqlite_open($db) or die("Could not open database"); // generate query string $query = "SELECT * FROM books"; // execute query $result = sqlite_query($handle, $query) or die("Error in query:
".sqlite_error_string(sqlite_last_error($handle))); // if rows exist if (sqlite_num_rows($result) > 0) { // get each row as an array // print values echo "<table cellpadding=10 border=1>"; while($row = sqlite_fetch_array($result)) { echo "<tr>"; echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>"; echo "</tr>"; } echo "</table>"; } // all done // close database file sqlite_close($handle); ?> </body> </html>
如果所有都运行良好,那么你应该看到像下面这样的结果:

如果你记得你在第八章中所学到的,那么上述的PHP脚本应该很容易理解。如果你不理解,那么下面是一个快速的纲要:
1. 脚本以sqlite_open()函数开始,该函数接受数据库文件名称作为参数然后试图打开该文件。如果不能找到该数据库文件,那么一个以所提供的名字命名的空白数据库文件将被创建(假定脚本对于目录有写的权限)。
<?php $db = $_SERVER['DOCUMENT_ROOT']."/../library.db"; $handle = sqlite_open($db) or die("Could not open database"); ?>
数据库文件library.db需要保存在不能被你的站点的访问者通过浏览器访问的地方。这意味着你需要在public_html、www或者htdocs目录之外的目录内创建该文件,该目录允许你的脚本读/写。提供Web站点主机的公司通常会在你的可见Web目录之上提供一空间,在该空间中你可以创建文件。$_SERVER['DOCUMENT_ROOT']."/.."是直接在你的可见Web目录之上的目录。
如果sqlite_open()执行成功,那么它会返回该文件一个句柄,该句柄存储在变量$handle中而且用于与数据库的所有的后续通信。
2. 下一步是创建查询然后使用sqlite_query()函数来执行该查询。
<?php $query = "SELECT * FROM books"; $result = sqlite_query($handle, $query) or die("Error in query:
".sqlite_error_string(sqlite_last_error($handle))); ?>
该函数也需要两个参数:数据库句柄和查询字符串。函数根据查询是否成功而返回真或假;如果出现失败,sqlite_error_string()和sqlite_last_error()函数可以用来显示发生的错误。
3. 如果sqlite_query()执行成功,查询所返回的结果集存储于变量$result中。你可以使用sqlite_fetch_array()函数来获取结果集中的记录,该sqlite_fetch_array()函数可以取出一单行数据而且将其作为为$row数组表示。上述被取出的记录中的字段用数组元素表示且可以使用标准的索引符号来访问。
每次当你调用sqlite_fetch_array()函数时,结果集中的下一条记录被返回。这使得sqlite_fetch_array()非常适合用于while()循环中,与之前使用mysql_fetch_row()函数的方式完全相同。
查询所返回的记录的数目可以使用sqlite_num_rows()函数来取得。或者如果你真正感兴趣的是结果集中的字段数,那么请使用sqlite_num_fields()函数。当然,这些函数只可用于返回记录的查询;将它们用于INSERT、UPDATE或者DELETE查询实际上没有多少意义。<?php if (sqlite_num_rows($result) > 0) { echo "<table cellpadding=10 border=1>"; while($row = sqlite_fetch_array($result)) { echo "<tr>"; echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>"; echo "</tr>"; } echo "</table>"; } ?>
4. 一旦你完成了所有的任务,那么调用sqlite_close()函数关闭数据库句柄然后将消耗的内存返回给系统是个好注意。
<?php sqlite_close($handle); ?>
在PHP 5中,你也可以以面向对象的方式来使用SQLite API,其中上述每一个函数都变成了SQLiteDatabase()对象的一个方法。请看下面的列表,它和上面的是等价的:
<html> <head></head> <body> <?php // set path of database file $file = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // create database object $db = new SQLiteDatabase($file) or die("Could not open database"); // generate query string $query = "SELECT * FROM books"; // execute query // return result object $result = $db->query($query) or die("Error in query"); // if rows exist if ($result->numRows() > 0) { // get each row as an array // print values echo "<table cellpadding=10 border=1>"; while($row = $result->fetch()) { echo "<tr>"; echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>"; echo "</tr>"; } echo "</table>"; } // all done // destroy database object unset($db); ?> </body> </html>
在这里,通过传递给对象构造器数据库文件名称,new关键字用于实例化类SQLiteDatabase()的一个对象。如果数据库文件不存在,那么创建一个新的数据库文件。存储于变量$db中的生成的对象然后展示用于执行查询的方法和属性。每个查询返回类SQLiteResult()的一个实例,而该实例反过来展示用于取得和处理记录的方法。
如果你仔细查看上述两个脚本,那么你会看到过程化的函数名称和对象方法名称之间的大量的相似性。虽然两者之间的对应不完美,但通常情况下足够接近以使得如果你知道一个的话猜到另外一个是可能的。
不同的函数
和MySQL API一样,PHP的SQLite API为你提供了多种方法。例如,你可以使用sqlite_fetch_object()方法来获取每行并将其做为一个对象,然后通过使用字段名作为对象属性来访问字段值。下面是一个例子:
<html> <head></head> <body> <?php // set path of database file $db = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // open database file $handle = sqlite_open($db) or die("Could not open database"); // generate query string $query = "SELECT * FROM books"; // execute query $result = sqlite_query($handle, $query) or die("Error in query:
".sqlite_error_string(sqlite_last_error($handle))); // if rows exist if (sqlite_num_rows($result) > 0) { // get each row as an object // print field values as object properties echo "<table cellpadding=10 border=1>"; while($obj = sqlite_fetch_object($result)) { echo "<tr>"; echo "<td>".$obj->id."</td>"; echo "<td>".$obj->title."</td>"; echo "<td>".$obj->author."</td>"; echo "</tr>"; } echo "</table>"; } // all done // close database file sqlite_close($handle); ?> </body> </html>
另外一种选择是使用sqlite_fetch_all()函数一下子获取整个的结果集。该函数以一个数组的数组来获得完整的记录集;外部数组的每个元素都代表一个记录,而且其自身也构造为一个数组,该数组中的元素代表记录中字段。
这里给出一个例子,它可能使得这个更加清楚:
在所有之前的例子中,当处理结果集时数据库保持打开,这是因为记录是使用sqlite_fetch_array()或者sqlite_fetch_object()函数一条一条的来获取的。上述例子是独特的,因为数据库可在处理结果集数组前被关掉。这是因为整个结果集立即被获取而且存储到$data数组中,因此确实不需要在处理它的时候保持数据库打开。<html> <head></head> <body> <?php // set path of database file $db = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // open database file $handle = sqlite_open($db) or die("Could not open database"); // generate query string $query = "SELECT * FROM books"; // execute query $result = sqlite_query($handle, $query) or die("Error in query:
".sqlite_error_string(sqlite_last_error($handle))); // get the complete result set as a series of nested arrays $data = sqlite_fetch_all($result); // all done // close database file sqlite_close($handle); // check the array to see if it contains at least one record if (sizeof($data) > 0) { echo "<table cellpadding=10 border=1>"; // iterate over outer array (rows) // print values for each element of inner array (columns) foreach ($data as $row) { echo "<tr>"; echo "<td>".$row[0]."</td>"; echo "<td>".$row[1]."</td>"; echo "<td>".$row[2]."</td>"; echo "</tr>"; } echo "</table>"; } ?> </body> </html>
如果你的结果集只包含一个字段,那么使用sqlite_fetch_single()函数,该函数取得一行记录第一个字段的值。PHP手册上说得好:“当你只对一列数据有兴趣时,这是最优化的获取数据的方法”。请看:
<html> <head></head> <body> <?php // set path of database file $db = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // open database file $handle = sqlite_open($db) or die("Could not open database"); // generate query string // this query returns only a single record with a single field $query = "SELECT author FROM books WHERE title = 'A Study In Scarlet'"; // execute query $result = sqlite_query($handle, $query) or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); // if a row exists if (sqlite_num_rows($result) > 0) { // get the value of the first field of the first row echo sqlite_fetch_single($result); } // all done // close database file sqlite_close($handle); ?> </body> </html>
你甚至可以结合while()循环来使用sqlite_fetch_single()函数而对包含很多记录但只有一个字段的结果集进行重复。也请注意检查下一行是否存在的sqlite_has_more()函数的我的用法。
当然,你可以使用PHP 5中的对象符号来做同样的事情。然而,你需要知道sqlite_has_more()是一个确实不需翻译为其对象方法名称的函数;在一个面向对象的脚本中,你会需要调用$result->valid();下面的脚本是上一个脚本的面向对象的等价物:<html> <head></head> <body> <?php // set path of database file $db = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // open database file $handle = sqlite_open($db) or die("Could not open database"); // generate query string $query = "SELECT DISTINCT author FROM books"; // execute query $result = sqlite_query($handle, $query) or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); // if rows exist if (sqlite_num_rows($result) > 0) { echo "<table cellpadding=10 border=1>"; // check for more rows while (sqlite_has_more($result)) { // get first field from each row // print values $row = sqlite_fetch_single($result); echo "<tr>"; echo "<td>".$row."</td>"; echo "</tr>"; } echo "</table>"; } // all done // close database file sqlite_close($handle); ?> </body> </html>
<html> <head></head> <body> <?php // set path of database file $file = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // create database object $db = new SQLiteDatabase($file) or die("Could not open database"); // generate query string $query = "SELECT DISTINCT author FROM books"; // execute query $result = $db->query($query) or die("Error in query"); // if rows exist if ($result->numRows() > 0) { echo "<table cellpadding=10 border=1>"; // check for more rows while ($result->valid()) { // get first field from each row // print values $row = $result->fetchSingle(); echo "<tr>"; echo "<td>".$row."</td>"; echo "</tr>"; } echo "</table>"; } // all done // destroy database object unset($db); ?> </body> </html>
类型不一致
当谈到INSERT主题时,还记得几页之前我关于SQLite是如何无类型的以及因此你可以插入任意类型的数据到任意字段中的话吗?这里有该规则的一个重要的例外:标记为整型关键字的字段。在SQLite中,标记为整型关键字的字段需要做两件重要的事情:它们为表中的每条记录提供一个唯一的数字标识符,且如果你将NULL值插入其中,那么SQLite自动插入一个比字段中已有的最大值大一的数值。
因此,SQLite中的整型关键字字段执行和MySQL中的自动增加字段一样的任务,而且是自动对记录编号的一种方便的方法。很明显地,你无法插入非数值到这样的字段中,这就是我为什么说它们是无类型规则的一个例外。
阅读更多的关于这方面的内容请参看:http://www.sqlite.org/datatypes.html。
因为之前例子中用到的books表格包含这样的字段(id字段),所以很明显地,每个插入到该字段的NULL值会生成一个新的记录号。如果你打算读取该号,那么PHP也有一种方法这样做(只使用sqlite_last_insert_rowid()即可,该函数返回最后被插入一行的ID号(这等价于PHP的MySQL API中的mysql_insert_id()函数))。
为了看到这个的实际效果,更新上面脚本中间的if()循环以包含对sqlite_last_insert_rowid()的调用,如下所示:
<?php // check to see if the form was submitted with a new record if (isset($_POST['submit'])) { // make sure both title and author are present if (!empty($_POST['title']) && !empty($_POST['author'])) { // generate INSERT query $insQuery = "INSERT INTO books (title, author) VALUES
(\"".sqlite_escape_string($_POST['title'])."\",
\"".sqlite_escape_string($_POST['author'])."\")"; // execute query $insResult = sqlite_query($handle, $insQuery) or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); // print success message echo "<i>Record successfully inserted with ID
".sqlite_last_insert_rowid($handle)."!</i><p />"; } else { // missing data // display error message echo "<i>Incomplete form input. Record not inserted!</i><p />"; } } ?>
如果你需要,你也可以使用sqlite_changes()函数发现多少行受到影响(你自己试试然后看看结果)。
sqlite_query()函数
你会记得,在本教程的开始,我就建议你用SQLite命令行程序初始化library.db数据库。但那并不是唯一的创建新的SQLite数据库的方法(你可以使用PHP自身通过sqlite_query()函数发出必要的CREATE TABLE和INSERT命令来创建)。下面是具体的做法:
<?php // set path of database file $db = $_SERVER['DOCUMENT_ROOT']."/../library2.db"; // open database file $handle = sqlite_open($db) or die("Could not open database"); // create database sqlite_query($handle, "CREATE TABLE books
(id INTEGER PRIMARY KEY, title VARCHAR(255) NOT NULL, author VARCHAR(255) NOT NULL)")
or die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); // insert records sqlite_query($handle, "INSERT INTO books (title, author) VALUES
('The Lord Of The Rings', 'J.R.R. Tolkien')") or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); sqlite_query($handle, "INSERT INTO books (title, author) VALUES
('The Murders In The Rue Morgue', 'Edgar Allan Poe')") or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); sqlite_query($handle, "INSERT INTO books (title, author) VALUES
('Three Men In A Boat', 'Jerome K. Jerome')") or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); sqlite_query($handle, "INSERT INTO books (title, author) VALUES
('A Study In Scarlet', 'Arthur Conan Doyle')") or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); sqlite_query($handle, "INSERT INTO books (title, author) VALUES
('Alice In Wonderland', 'Lewis Carroll')") or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); // print success message echo "<i>Database successfully initialized!"; // all done // close database file sqlite_close($handle); ?>
或者在PHP 5中,你可以使用面向对象方法:
<?php // set path of database file $file = $_SERVER['DOCUMENT_ROOT']."/../library3.db"; // create database object $db = new SQLiteDatabase($file) or die("Could not open database"); // create database $db->query("CREATE TABLE books
(id INTEGER PRIMARY KEY, title VARCHAR(255) NOT NULL, author VARCHAR(255) NOT NULL)") or
die("Error in query"); // insert records $db->query("INSERT INTO books (title, author) VALUES
('The Lord Of The Rings', 'J.R.R. Tolkien')") or die("Error in query"); $db->query("INSERT INTO books (title, author) VALUES
('The Murders In The Rue Morgue', 'Edgar Allan Poe')") or die("Error in query"); $db->query("INSERT INTO books (title, author) VALUES
('Three Men In A Boat', 'Jerome K. Jerome')") or die("Error in query"); $db->query("INSERT INTO books (title, author) VALUES
('A Study In Scarlet', 'Arthur Conan Doyle')") or die("Error in query"); $db->query("INSERT INTO books (title, author) VALUES
('Alice In Wonderland', 'Lewis Carroll')") or die("Error in query"); // print success message echo "<i>Database successfully initialized!"; // all done // destroy database object unset($db); ?>
几个额外的工具
最后,SQLite API也包括了一些辅助函数来向你提供关于SQLite版本、编码以及最后一次失败操作所产生的错误代码和错误消息的信息。下列例子演示了sqlite_libversion()和sqlite_libencoding()函数,该等函数分别返回版本号和连接的SQLite库的编码:
<?php // version echo "SQLite version: ".sqlite_libversion()."<br />"; // encoding echo "SQLite encoding: ".sqlite_libencoding()."<br />"; ?>
当发生错误时,请使用sqlite_last_error()函数,它返回SQLite返回的最后错误代码。当然,这个错误代码(一个数值)本身并不是非常有用;为了将其转变为一条人类可读取的消息,将其结合sqlite_error_string()函数一起使用。考虑下面的例子,该例子通过试图运行一条故意有错的查询来对其进行解释说明:
<?php // set path of database file $db = $_SERVER['DOCUMENT_ROOT']."/../library.db"; // open database file $handle = sqlite_open($db) or die("Could not open database"); // generate query string // query contains a deliberate error $query = "DELETE books WHERE id = 1"; // execute query $result = sqlite_query($handle, $query) or
die("Error in query: ".sqlite_error_string(sqlite_last_error($handle))); // all done // close database file sqlite_close($handle); ?>
下面就是输出看上去的样子:

请注意,即使它们看起来似乎相似,但是sqlite_last_error()和sqlite_error_string()函数之间与mysql_errno()和mysql_error()函数之间的工作方式不完全相同。mysql_errno()和mysql_error()函数可以相互独立使用以分别获得最后的错误代码和消息,而sqlite_error_string()函数依赖sqlite_last_error()函数所返回的错误代码。
如果你的兴趣得到激发,那么你可以在Zend的深入PHP 5部分阅读更多的关于PHP用SQLite所能做的事情。
那就是我在本教程为你准备的一切。更多的秘密在PHP 101的第十章中等着你,所有为了那个秘密,一定要回来啊!