1、第四节:PHP 访问 MySQL 篇在这一章内我们会学习到如何在一个 Web 页面中向数据库中存储信息并显示它。之前我们已经安装了 MySQL 这个关系型数据库引擎以及 PHP 这个服务器端脚本语言,并学习了有关它们的基本知识。在学完这一章后,我们将明白如何综合利用这两个新的工具来构建一个数据库驱动的网站!对前一部分的回顾在我们往下继续之前,回顾一下我们学习的目的应该是件有价值的事。现在有我们的系统中有了两个强有力的新的工具:脚本语言 PHP 和数据库引擎 MySQL。搞清楚两者是如果协同工作是很重要的。数据库驱动的网站的实质就是允许站点的内容存在于一个数据库中,并且可以通过这个数据库来动态地
2、产生 Web 页面来让我们的访问者通过标准的 Web 浏览器来显示它。所以在你的系统的一端是一个访问你的站点的浏览者,他通过访问: . 来获得一个标准的 HTML 格式的 Web 页面并在 Web 浏览器中显示它。在你的系统的另一端是通过一个或几个数据表存储在一个只理解如何响应 SQL 查询(命令)的 MySQL 数据库中的你的站点的内容。PHP 脚本语言承担了两者之间的联络员的角色,使用 PHP,你可以编写一个标准 HTML 的“模板”,这个“ 模板”决定了你的站点的外观(包括图画和页面设计)。这时内容是属于这个“模板” 的,你可以使用一些 PHP 代码来连接 MySQL 数据库并且使用 S
3、QL 查询来获得数据并在其相应位置显示它,这里的 SQL 查询是和我们在第二章中用来建立笑话数据表时一样的。现在对于访问者在访问你的数据库驱动的网站的一个页面时,到底会发生什么事,你应该有个明确的认识了:访问者的 Web 浏览器使用一个标准的 URL 请求这个页面。Web 服务器软件( Apache、IIS 或其他)认定被请求的页面是一个 PHP 脚本,因而在响应这个页面请求之前用它的 PHP插件来解释它。一些 PHP 命令(我们还没学到)会连接 MySQL 数据库并向数据库请求属于这个 Web 页面的内容。MySQL 数据库作出响应并且向 PHP 脚本发出被请求的内容。PHP 脚本将内容存储
4、到一个或几个 PHP 变量中,并使用我们熟悉的 echo 函数将其作为 Web 页面的一部分输出。PHP 插件完成处理并将生成的 HTML 副本返回到 Web 服务器。Web 服务器将这个 HTML 副本发送到 Web 浏览器,这将是一个标准的 HTML 文件,只不过它不是直接来自于一个HTML 文件,而是来自于 PHP 插件提供的输出。用 PHP 连接 MySQL在我们从我们的 MySQL 数据库中获取我们的 Web 页面所包含的内容之前,我们首先必须知道如何建立与 MySQL的连接。在第二章中,我们使用了一个叫 mysql 的程序来做这样的连接。PHP 不需要这样的一个程序,对连接 MyS
5、QL 的支持是语言内置的。下面的这个函数用来建立这样的连接:mysql_connect(, , );在这里,是 MySQL 服务软件在其上运行的计算机的 IP 地址或主机名(如果这与运行 Web 服务软件的计算机是同一台,你可以使用“localhost“),和就是你在第二章中用来连接到 MySQL服务器的用户名及口令。你可能还记得 PHP 中的函数在被调用时往往会返回(输出)一个值。请不要担心我们没有提醒你,我们在最初接触一个函数时都会为你详细详细它。绝大多数的函数在被调用后,都会返回一个可以在存储在变量中的值以备下次使用。例如我们上面介绍的 mysql_connect 函数,会返回一个数字来
6、标识已经建立的连接。因为我们会要使用这个连接,所以我们必须保存这个值。下面是一个关于如何连接我们的 MySQL 数据库的一个实例:$dbcnx = mysql_connect(“localhost“, “root“, “mypasswd“);需要说明的是,对于你的 MySQL 服务器,上面这个函数中的三个参数的值可能是不同的。你应该注意到在这儿我们的 mysql_connect 返回了一个值(我们称之为一个连接标识),这个值被我们存储在变量$dbcnx 中。因为 MySQL 是一个完全分布式的软件,我们必须考虑到这些可能性:服务不可用、网络堵塞或者是我们的用户名及口令不匹配。在这些情况下,my
7、sql_connect 函数不能返回一个连接标识(因为连接未被建立)。这时,会返回一个逻辑假。这使得我们可以用一个 if 语句来处理连接的情况: $dbcnx = mysql_connect(“localhost“, “root“, “mypasswd“);if (!$dbcnx) echo( “Unable to connect to the “ .“database server at this time.“ );exit();在上面的代码段中出现了三个新的东西,首先,我们在 mysql_connect 函数前加了一个符号。包括mysql_connect 在内的许多函数会在失败后显示难看的
8、错误信息。在函数名前加一个符号可以告诉这个函数当执行失败时,允许我们显示我们自己友好的出错信息。其次,在我们的 if 语句的条件中,$dbcnx 变量前面加了一个惊叹号。这个惊叹号是 PHP 中的“否运算符”。也就是说将逻辑真变为逻辑假,将逻辑假变为逻辑真。这样,如果这个连接是失败的,mysql_connect 会返回一个逻辑假,!$dbcnx 将等于逻辑真,这样我们的 if 语句将被执行。相反,如果这个连接是成功的,存储在 $dbcnx 中的连接标识将等于逻辑真(在 PHP 中,任何非零的数字都被认为是逻辑真),所以!$dbcnx 会等于逻辑假,if 语句将不会被执行。最后一个是 exit
9、函数,这是我们遇到的第一个没有参数的函数。这个函数的全部作用就是导致 PHP 停止对本页的阅读。如果数据库连接失败这是一个很好的响应,因为绝大多数情况下,如果不能连接到数据库,这一页不会显示任何有用的信息。和我们在第二章做过的一样,连接被建立后下一步就是选择工作的数据库。我们将要在第二章中所建立的笑话数据库中工作。这个数据库被命名为 jokes。在 PHP 中用来选择数据库的另外一个函数:mysql_select_db(“jokes“, $dbcnx);请注意我们在这儿使用了$dbcnx 变量来指出了这个函数所使用的连接标识。这个参数实际上是可省略的。当省略这个参数时,函数会自动使用最后开启的
10、那一个连接。这个函数成功后返回逻辑真,失败后返回逻辑假。为了谨慎起见,我们也用了一个 if 语句来处理错误:if (! mysql_select_db(“jokes“) ) echo( “Unable to locate the joke “ .“database at this time.“ );exit();当建立了连接并选择了数据库之后,我们可以开始使用存储在数据库中的数据了。在 PHP 中执行 SQL 查询在第二章中,我们使用一个叫 mysql 的程序来连接到 MySQL 数据库服务器,在这个程序中,我们可以输入 SQL 查询(命令)并立即显示查询结果。在 PHP 中,有着类似的机制:
11、mysql_query 函数。mysql_query(, );在这儿,是一个包含将执行的 SQL 命令的字符串。和 mysql_select_db 一样,连接标识这个参数也是可选的。这个函数的返回决定于发出的查询的类型。对于绝大多数的 SQL 命令来说,mysql_query 返回逻辑真或逻辑假来标明执行是否成功。请参看下面这个例子,这是用来建立我们在第二章中建立的 Jokes 数据表的:$sql = “CREATE TABLE Jokes ( “ .“ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY, “ .“JokeText TEXT, “ .“Joke
12、Date DATE NOT NULL “ .“)“;if ( mysql_query($sql) ) echo(“Jokes table successfully created!“); else echo(“Error creating Jokes table: “ .mysql_error() . “);这儿使用的 mysql_error 将以字符串的形式返回 MySQL 服务器最后发出的错误信息。对于 DELETE、 INSERT 以及 UPDATE(用来修改存储的数据),MySQL 可以知道有多少数据行被这个查询影响。参看下面的 SQL 命令,这个命令我们曾在第二章中用来设置所有包含单
13、词“chicken”的笑话的日期:$sql = “UPDATE Jokes SET JokeDate=1990-04-01 “ .“WHERE JokeText LIKE %chicken%“;当我们执行这个查询时,我们可以使用 mysql_affected_rows 函数来显示这个修改所影响的数据行的数目:if ( mysql_query($sql) ) echo(“Update affected “ .mysql_affected_rows() . “ rows.“); else echo(“Error performing update: “ .mysql_error() . “);SE
14、LECT 命令会有一些不同,因为它会得到许多信息,而 PHP 必须提供方法来处理这些信息。处理 SELECT 结果集对于绝大多数的 SQL 查询来说,mysql_query 函数仅仅返回逻辑真或逻辑假。而对于 SELECT 查询来说,仅仅这样显然是不够的。你应该还记得 SELECT 查询是用来显示数据库中存储的数据的。除了指出查询成功还是失败之外, PHP 还必须得到查询的结果。作为一个结果,当我们执行一个 SELECT 查询的时候, mysql_query 会返回一个标识“结果集”的数字,这将包含了这个查询返回的所有行的列表。如果查询失败,函数仍然是返回一个逻辑假。$result = mys
15、ql_query(“SELECT JokeText FROM Jokes“);if (!$result) echo(“Error performing query: “ .mysql_error() . “);exit();假定在执行查询时没有遇到错误,上面的代码会定位一个有关存储在笑话库中所有笑话的正文的结果集,这个定位被存储在变量$result 中。因为数据库中的笑话的数目是没有限制的,这个结果集可能非常庞大。我们之前曾经提到过 while 循环对于处理大量的数据来说是一个非常有用的控制语句,这是我们逐个处理结果集中数据行的代码的基本格式:while ( $row = mysql_fetc
16、h_array($result) ) / process the row.在这个 while 循环中的条件可能看上去与我们曾经使用过的有所不同,所以我们有必要在这里解释它的工作机理。你可以先把这个条件看成一个独立的语句:$row = mysql_fetch_array($result);mysql_fetch_array 函数以一个参数(对于这个例子来说是存储在 $result 变量中)接受到一个结果集,并以一个数据的形式返回结果集中的下一行。如果你还不熟悉数组的概念,不要担心,我们会在下面详细讨论它。如果在这个结果集中不再有其它数据行时,mysql_fetch_array 返回逻辑假。现在,
17、我们上面的语句定义了一个值到$row 变量中,与此同时,整个语句也获得了同样的值。这就是我们在 while循环的条件中使用这个语句的原因,因为 while 循环会一直执行循环直到条件等于逻辑假,结果集有几行,这个循环就会执行几次,每一次$row 都会得到一个下一行的值,现在剩下的就是如何在循环中从$row 变量中获得相应的值了。结果集中的行被描述成一个数组。数组是一个特殊类型的变量,这个变量可以包含多个值,如果你把一个变量看成是值的容器,你可以把数组看成是有间隔的容器,在每一个间隔中可以存储一个单独的值。对于我们的数据行来说,这个间隔是以数据表的列名命名的。如果$row 是我们结果集中的一行,
18、那么$row“JokeText“就是这一行中 JokeText 列的值。所以如果我们想要显示我们的数据库中所在笑话的正文,while 循环应该是这样的:while ( $row = mysql_fetch_array($result) ) echo(“ . $row“JokeText“ . “);最后,作为一个总结,这是一段完整的 PHP 的 Web 页面的代码,它用来连接我们的数据库,取出数据库中所有笑话的正文,并将其在 HTML 中显示出来:Our List of Jokes Unable to connect to the “ .“database server at this time
19、.“ );exit();/ Select the jokes databaseif (! mysql_select_db(“jokes“) ) echo( “Unable to locate the joke “ .“database at this time.“ );exit();?Here are all the jokes in our database: Error performing query: “ .mysql_error() . “);exit();/ Display the text of each joke in a paragraphwhile ( $row = mys
20、ql_fetch_array($result) ) echo(“ . $row“JokeText“ . “);?向数据库中插入数据在这一节里,我们会看到我们会如何综合利用这些工具来让我们站点的访问者向数据库中添加他们自己的笑话。如果你喜欢挑战,你可以试试在向下看之前想想大致上应该怎么做。在这一节里只有很少新的东西。对于我们学过的东西来说,这只是一个简单的应用。如果我们想要让访问者能够输入新的笑话,我们首先需要一个表单,这儿是这个表单的代码:“ METHOD=POSTType your joke here:正如我们上面看到的那样,这个表单在提交时会载入同一个页面(因为我们在表单的 ACTION
21、属性中使用了$PHP_SELF 变量),但是在再次载入时请求中包含了两个变量,首先是$joketext,这是在 text 域中输入的笑话的正文,另一个是$submitjoke ,这个变量的值将始终是 “SUBMIT“,这用来标志笑话已被提交。要将已提交的笑话添加到数据库中,我们需要用 mysql_query 来运行一个 INSERT 查询,这个查询中将包含已经提交的$joketext 变量的值:if (“SUBMIT“ = $submitjoke) $sql = “INSERT INTO Jokes SET “ .“JokeText=$joketext, “ .“JokeDate=CURDAT
22、E()“;if (mysql_query($sql) echo(“Your joke has been added.“); else echo(“Error adding submitted joke: “ .mysql_error() . “);在全部的内容中只有 SQL 代码中出现了一个新的东西。在这里我们使用了一个 MySQL 函数 CURDATE()来将新插入数据库的笑话的 JokeDate 列的值置为当前日期。事实上, MySQL 有很多这样的函数,但是我们只会在使用到他们时才会介绍他们,要得到一个完整的函数的说明,你可以参看 MySQL 参考手册。现在我们已经有了允许用户输入一个笑
23、话并将其加入到我们的数据库中的程序代码。现在剩下的就是将其加入到我们已做好的笑话显示页面。因为绝大多数的用户只会想要看看笑话,所以我们不想对我们的页面做大的更改,除非用户表示想要添加一个新的笑话。因为这个原因,我们的应用程序应该是一个多功能的页面。下面是程序的代码:.“ METHOD=POSTType your joke here:Unable to connect to the “ .“database server at this time.“ );exit();/ Select the jokes databaseif (! mysql_select_db(“jokes“) ) echo
24、( “Unable to locate the joke “ .“database at this time.“ );exit();/ If a joke has been submitted,/ add it to the database.if (“SUBMIT“ = $submitjoke) $sql = “INSERT INTO Jokes SET “ .“JokeText=$joketext, “ .“JokeDate=CURDATE()“;if (mysql_query($sql) echo(“Your joke has been added.“); else echo(“Erro
25、r adding submitted joke: “ .mysql_error() . “);echo(“ Here are all the jokes “ .“in our database: “);/ Request the text of all the jokes$result = mysql_query(“SELECT JokeText FROM Jokes“);if (!$result) echo(“Error performing query: “ .mysql_error() . “);exit();/ Display the text of each joke in a pa
26、ragraphwhile ( $row = mysql_fetch_array($result) ) echo(“ . $row“JokeText“ . “);/ When clicked, this link will load this page/ with the joke submission form displayed.echo(“ .“Add a Joke!“);endif;?现在我们有了一个单独的文件,这个文件包含不太多的 PHP 代码,通过这个文件,我们可以显示我们的 MySQL 数据库中的笑话并能向我们的 MySQL 数据库中添加笑话。一个挑战作为家庭作业,你可以看看你是不
27、是能解决这么一个问题:在页面上显示的每一个笑话后面放置一个叫“Delete this Joke”的超连接,当单击这个连接时,会从数据库中删除这个笑话并显示更改过以后的笑话列表。下面是对你的一些提示:你可以还在一个多功能页面完成全部的功能。你需要使用 SQL 的 DELETE 命令,这个命令我们曾在第二章中学习过。这是一个关键的问题。要删除一个指定的数据库,你需要能够唯一地标识它。Jokes 表中的 ID 可以完成这个功能。你必须将要被删除的笑话的 ID 传递到删除笑话的请求中。将这个值放到“Delete this Joke”连接的查询字符串中是比较合适的。如果你觉得你已经有了答案,或者你只想知
28、道解决方案,那就去看看下一页。祝你好运!结语在这一章中,我们学习了一些新的用来实现与 MySQL 数据库服务接口的 PHP 函数。使用这些函数,我们建立了我们的第一个数据库驱动的网站,我们的这个网站可以在线地发布我们数据库中笑话并允许访问者向其中添加他们自己的笑话。在第五章中,我们会回到 MySQL 命令行去学习如何使用关系型数据库的原理以及其他一些更高级的 SQL 查询去描述更为复杂的信息,并给予访问者对他们自己添加的笑话以特别的权限!挑战的解决这是我们上面提出的“家庭作业”的解决方案。要在每一个笑话后面添加一个“Delete this Joke”连接,必须作下面的改动:之前,我们曾经在我们
29、的页面的底端的“Add a Joke!”连接中传递过一个$addjoke 变量,通过这个变量来通知我们的脚本不再显示通常的笑话列表,而是显示一个录入笑话的表单。与此相类似,我们在我们的“Delete this Joke”连接中传递一个$deletejoke 变量来表示我们想要删除一个笑话。在获得每一个笑话的 JokeText 列的同时,我们还获得了 ID 列的值,所以我们获得了与数据库中每一个笑话关联的ID。我们将要删除的笑话的 ID 值赋予$deletejoke 变量。这是通过将从数据库中获得的 ID 值插入到每一个笑话的“Delete this Joke”连接中来实现的。使用了一个 if
30、语句,如果在载入这一页时,我们的$deletejoke 被赋予了一个值(使用 isset 函数),我们会在一个 SQLDELETE 语句中使用这个值(将被删除的笑话的 ID)来删除指定的笑话。这儿是全部的源代码:.“ METHOD=POSTType your joke here:Unable to connect to the “ .“database server at this time.“ );exit();/ Select the jokes databaseif (! mysql_select_db(“jokes“) ) echo( “Unable to locate the jok
31、e “ .“database at this time.“ );exit();/ If a joke has been submitted,/ add it to the database.if (“SUBMIT“ = $submitjoke) $sql = “INSERT INTO Jokes SET “ .“JokeText=$joketext, “ .“JokeDate=CURDATE()“;if (mysql_query($sql) echo(“Your joke has been added.“); else echo(“Error adding submitted joke: “
32、.mysql_error() . “);/ If a joke has been deleted,/ remove it from the database.if (isset($deletejoke) $sql = “DELETE FROM Jokes “ .“WHERE ID=$deletejoke“;if (mysql_query($sql) echo(“The joke has been deleted.“); else echo(“Error deleting joke: “ .mysql_error() . “);echo(“ Here are all the jokes “ .“
33、in our database: “);/ Request the ID and text of all the jokes$result = mysql_query(“SELECT ID, JokeText FROM Jokes“);if (!$result) echo(“Error performing query: “ .mysql_error() . “);exit();/ Display the text of each joke in a paragraph/ with a “Delete this Joke“ link next to each.while ( $row = mysql_fetch_array($result) ) $jokeid = $row“ID“;$joketext = $row“JokeText“;echo(“$joketext “ .“ .“Delete this Joke“);/ When clicked, this link will load this page/ with the joke submission form displayed.echo(“ .“Add a Joke!“);endif;?