Mysql入门系列:PHP基础
2006-12-31 10:49:13 来源:WEB开发网PHP 的基本功能就是解释一个脚本,来生成发送到客户机的Web 页面。具有代表性的是,脚本包括逐字发送到客户机的HTML 和作为程序执行的PHP 代码的混合编码。无论代码生成什么样的输出,都会发送到客户机,因此客户机永远不会看到代码,它只能看结果的输出。
当PHP 开始读取文件时,假设文件内容表示文字的H T M L,则它仅仅拷贝在那里找到的输出内容。当PHP 解释程序遇到一个特殊的打开标记时,就从HTML 模式切换到PHP 代码模式,而作为要执行的PHP 代码也开始解释文件。代码的结尾由另一个特殊的标记指出,解
释程序在这个位置从代码模式切换回HTML 模式。这就允许将静态的文本( HTML 部分)与动态产生的结果( PHP 代码部分的输出)相混合,产生依赖于调用环境变化的页面。例如,可以使用PHP 脚本来处理表格的结果,在这个格式中,用户已经输入了数据库搜索的参数。
由于格式填入内容的不同,所以每次搜索的参数可能也不同,因此当脚本执行搜索的时候,每个作为结果的页面将反映不同的搜索。
让我们通过一个非常简单的PHP 脚本看一看它是如何工作的:
<HTML>
<BODY>
hello,world
</body>
</HTML>
这个脚本并不很有趣,因为它不包括PHP 代码!因此您会问:那它有什么好处?这个问题的回答是:它有时有助于建立包括想要生成页面的HTML 框架的脚本,然后再加入PHP代码。这是非常有效的,PHP 解释程序用于它是没有问题的。
为了在脚本中包括PHP 代码,您可从用两个特殊标记(脚本开始处的‘ < ? p h p’和脚本结束处的‘? >’)把它与周围的文本区分开来。当PHP 解释程序遇到开始的‘< ? p h p’标记时,就从HTML 模式切换到PHP 模式,并解释它找到的任何PHP 代码,直到看见结束的‘ ? >’标记为止。它产生的所有输出解释并替换了两个标记之间的脚本。将前面的实例再重新编写一下,它包括了少量的PHP 代码,如下所示:
此时,代码部分是很小的,由单行组成。当解释代码时,产生了输出“ hello, world”,它作为输出部分发送到客户机浏览器。这样,这个脚本产生的Web 页面与前面实例产生的Web页面一样,前面实例的脚本完全由HTML 组成。
可以使用PHP 代码产生Web 页面的任何部分。我们已经看到了一个特别的实例,在那里整个脚本都由文字的HTML 组成,而不包括PHP 代码。另一个特别的实例是整个脚本都是PHP 代码而不包括文字的H T M L:
这说明PHP 在如何产生输出方面有很大的灵活性。但PHP 也留下一个问题,那就是确定如何组合HTML 和PHP 代码才是合适的。不必把所有代码都放在一个地方, PHP 在这方面也很灵活。只要您高兴,就可以通过脚本在HTML 和PHP 代码模式之间进行转换。
PHP 脚本标记
除了本章实例中使用的标记之外, PHP还支持其他的脚本标记。您可以在其他人编写的PHP 代码中看到它们,或者可以自己使用这些标记。PHP 识别四种标记风格:
缺省标记风格。这是PHP 配置为缺省时使用的风格:
<?php print ("hello,world ");?>
简洁开标记风格。这个风格除了开标记较简洁外,其他与缺省风格相类似:
<? print ("hello,world ");?>
兼容ASP 的风格。这个风格在Active Server Page 环境内部是通用的:
<% print ("hello,world ");%>
<SCRIPT> 标记风格。如果使用与其他标记风格不同的HTML 编辑器,这个风格是有用的。当然,它比较冗长,但是当您使用其他标记风格时,编辑器可能不处理PHP 代码,这时您将发现它是必需的:
<SCRIPT LANGUAGE="php">print ("hello,world ");</SCRIPT>
缺省时,简洁开标记风格和兼容ASP 风格无效。请参阅附录H 有关开启它们的说明。
独立的PHP 脚本
当处理命令解释程序脚本或Perl 脚本时,可以编写从命令行调用的独立的PHP 脚本。这里有一个实例:
#! /usr/local/bin/php -q
<?php print ("hello,world ");?>
前面的脚本可命名为h e l l o . p h p,利用chmod +x 使之成为可执行的,并从命令解释程序中调用:
% hello.php
hello,world
本章中我们不编写任何独立的脚本。这里编写的所有实例都期望Web 服务器调用它们来生成Web 页面。
下一个脚本有一些实质性的内容,但仍然相当简短。它说明了如何较容易地从PHP 访问MySQL数据库,并在Web 页面中使用查询结果。此脚本在第5 章表达得很简短,它形成了历史同盟Web 站点主页的基础。在我们继续往下做的时候,应该使脚本更精巧一些,但直到
现在为止,它所做的只是显示简短的欢迎消息和当前同盟会员资格的计数:
欢迎消息就是一个静态文本,因此它作为文字的HTML 是最容易编写的。另一方面,会员资格的计算是动态的,而且随时会更改,因此必须在它不工作时通过查询samp_db 数据库中的member 表来确定。
在开和闭脚本标记之间的代码文本执行一个简单的任务。首先,它打开了与MySQL服务器的连接,并使samp_db 数据库成为缺省数据库。然后,它发送一个到服务器的查询来确定此刻历史同盟有多少成员(我们将它作为member 表中的行数)。查询结果将作为包括会员
资格计数消息的一部分显示出来,然后再做处理。
在处理过程中,如果任何一点发生错误,该脚本都会简单地退出。它由于太简单而不产生错误输出,因此把访问该Web 站点的人们搞糊涂了(如果依靠PHP 代码生成整个Web 页面,当因错误而退出,且不产生任何输出时,可能会使访问页面的人非常恼火,因为有些浏
览器将显示“这个页面没有数据”的对话框)。
让我们把该脚本分成几段,看看它是如何工作的。第一步是用mysql_pconnect() 连接到服务器:
上一篇 目录 下一篇=@mysql_pconnect ("pit.viper.snake.net","paul","secret")
or exit ();
mysql_pconnect() 把主机名、用户名和口令作为参数。连接建立成功,则返回连接标识符,如果发生错误,则返回FA L S E。如果连接失败,脚本立刻调用exit() 来结束脚本,并且不再产生输出。
在mysql_pconnect() 调用前的‘@’字符是什么意思呢?就是“请关闭”的意思。有些PHP 函数在失败时除了返回状态代码之外还写出错误消息。在mysql_pconnect() 情况下,失败的连接导致了下面的消息出现在发送到客户机浏览器的Web 页面上:
Warning:MySQLConnection Failed:Access denied for user:
'paul0pit-viper.snake.net' (Using password:YES)
这样很难看,参观我们站点的人们可能不知道它是如何造成的和该怎样处理它。把‘ @’字符放到mysql_pconnect() 调用的前面,就可以取消这个错误消息,以使我们能在返回值的基础上自己选择如何处理错误。对于该脚本,如果发生错误,最好根本不产生属于会员资格计数的输出。这样,页面将只包含欢迎消息。
任何PHP 函数都可以使用‘@’,但以笔者的经验来说,初始的mysql_pconnect() 调用是最可能失败的一个。因此,本章的例子禁止来自该函数中的消息。
将名称和口令嵌入到所有人都能看到的脚本中,可能会使您紧张不安。是这样的,名称和口令出现在发送到客户机的Web 页面上是对的,因为该脚本的内容由其输出的结果替换掉了。然而,如果Web 服务器不知何故配置不当,没有识别出脚本需要由PHP 代码来处理,
它就会将脚本以纯文本发送出去,且会暴露连接参数。在8 . 2 . 1节“使用函数和include 文件”中,将简要地处理这类情况。
mysql_pconnect() 返回的连接标识符可以传递到PHP API 中的几个与MySQL相关的调用中。然而,对于这样的调用,标识符总是可选择的。例如,可以使用下列格式之一来调用mysql_ s e l e c t _ db ( ):
mysql_select_db ($db_name,上一篇 目录 下一篇);
mysql_select_db ($db_name);
mysql_pconnect() 与mysql_connect() 的对比函数mysql_pconnect() 与函数mysql_connect() 相似,都具有主机名、用户名和口令
参数,并返回连接标识符或FALSE 来说明连接是否成功。两个调用之间的不同在于:mysql_pconnect() 建立了一个持久的连接,而mysql_connect() 建立了一个非持久的连接。与非持久连接不同,持久连接在脚本终止时不关闭。如果另外一个PHP 脚本随后由同一
个Apache 子处理执行,并用同样的参数调用mysql_ p c o n n e c t ( ),将重新使用这个连接。这比关闭后再建立连接的效率要高。
如果忽略了从一些相关MySQL的PHP 调用中得到的连接参数,调用就会使用最近打开的连接。这样,如果脚本只打开单个连接,那么在任何MySQL调用中永远不必指定连接参数——即连接是缺省的。这就是C 或DBI API 与MySQL程序设计的极大不同之处,因为它
们没有这样的缺省。
笔者使用上一篇 目录 下一篇 变量在简单的主页上编写了下面的连接代码,使mysql_pconnect() 返回哪种类型的值更加清晰:
上一篇 目录 下一篇=@mysql_pconnect("pit-viper.snake.net","paul","secret")
or exit();
然而,实际上,我们在脚本的其他地方都没有使用$ l i n k,因此代码可更简单地写成:
@mysql_pconnect("pit.viper.snake.net","paul","secret")
or exit();
假设连接建立成功,则下一步是选择一个数据库:
mysql_select_db("samp_db")
or exit();
如果mysql_select_db 失败,我们将会默默地退出。如果我们能够连接到服务器,并且数据库存在,那么似乎不可能发生错误,但是仍然要严格地检查问题并采取相应的行动。选择数据库之后,可将查询发送到服务器,提取结果,加以显示,然后释放结果集:
$result=mysql_query("SELECT COUNT(*) FROM member
or exit();
if ($row = mysql_fetch_array ($result))
echo "<p>The League currently has ". $row[0] . "members";
mysql_free_result($result);
mysql_query() 函数将查询发送到服务器中去执行。查询不用分号或者‘ g’终止。如果查询非法或因为某些原因不能执行,则mysql_query() 返回FA L S E,否则返回一个结果集标识符。该标识符是我们能用来获得有关结果集信息的值。对于查询,该结果集由表示会员资格计数的单列值的单行组成。为得到这个值,我们可以把结果集标识符传给mysql_ f e t c h _ r o w ( )
来获取行,将此行赋给变量$ r o w,并以$row[0] 形式访问第一个元素(只有一个元素时也是这样)。
当处理完结果集时,将结果集传递给mysql_free_result() 进行释放。实际上这种调用在我们的脚本中是不必要的,因为当脚本结束时, PHP 会自动地释放所有活动的结果集。
mysql_free_result() 有助于执行大型查询或大量查询的脚本。它防止大量内存的使用。
为了使用脚本,需要在某处安装它。本章将采用这样的约定:美国历史同盟在Apache 文档树的最高一级中有自己的目录,称为us h l,因此主页面脚本作为该树的ushl/index.php 进行安装。我们也将为学分保存方案开发脚本,因此给出目录g p。如果Web 站点主机是pit-viper.snake.net,那么这两个目录中的页面将有如下开头的URL:
http://pit-viper.snake.net/ushl/
http://pit-viper.snake.net/gp/
例如,每个目录的主页面都可称为i n d e x . p h p,并以如下方式进行访问:
http://pit-viper.snake.net/ushl/index.php
http://pit-viper.snake.net/gp/index.php
使用函数和include 文件
PHP 脚本与DBI 脚本的不同之处在于, PHP 脚本位于Web 服务器文档树的内部,而DBI 脚本位于cgi-bin 目录中,这个目录在文档树的外部。这就提出了一个安全性问题:服务器配置不当的错误可能导致位于文档树内部的页面会以纯文本方式泄露给客户机。这意味着
建立与MySQL服务器连接的用户名和口令如果在PHP 脚本而非DBI 脚本中使用,则将处于暴露给外界的很高的危险之中。
PHP 中的变量
在PHP中,可以通过简单地使用变量而使它们存在。主页脚本使用了三个变量:$ l i n k、$result 和$ r o w,没有一个变量会在所有地方都声明(声明变量的地方有上下文,如在函数内部引用全局变量时,我们随后会谈到这个问题)。
变量由美元符号(‘$’)为开头的标识符表示。无论它表示什么类型的值都是正确的,尽管对于数组和对象要添加一些额外内容来访问值的单个元素。如果变量$x 表示单个值,例如,数字或字符串,则可以写成$x 来访问它。如果$x 表示有数字索引的数组,则可
以写成$ x [ 0 ]、$x[1] 等等来访问它的元素。如果$x 表示有关联索引的数组,如“ y e l l o w”或者“l a rg e”,则可以写成$ x [“y e l l o w”]、$ x [“l a rg e”] 来访问它的元素。
PHP 数组可以同时拥有数字索引的元素和相关索引的元素。例如, $x[1] 和 $ x [“ l a rg e”] 都能作为同一数组的元素。如果$x 代表一个对象,则可写成$ x ->property_name 来访问它所具有的属性。例如, $x->yellow 和$ x - > l a rge 都是$x 的属性。
作为属性名,数字是不合法的,因此$x->1在PHP 中是不合法的。
初始的历史同盟主页脚本也存在这个问题,因为它包括MySQL用户名和口令的直接值。让我们用两个PHP 性能:函数和include 文件,把这些连接参数移到脚本外面。我们将编写函数samp_db _ c o n n e c t ( )来建立这个连接,并把函数放到一个include 文件中—不是主脚本部分的,但可以从脚本中引用的文件。这种方法的优点如下:
编写连接建立代码比较容易。不需要写出所有参数,就可以在连接后用samp_db _connect() 选择数据库,使一个函数可以进行两个PHP 函数的工作。由于你可以将精力集中于脚本的独特标记,而不必为连接建立代码分心,因此也使得脚本更加易于理解。
可从脚本中访问include 文件,但可移到Apache 文档树的外面。这使它的内容对于客户机来说是不可访问的,即使Web 服务器配置不当,连接参数也不会暴露给它们。使用include 文件,对于隐藏不想由Web 服务器发送到站点的任何类型的敏感信息都是个良策。
虽然如此,但这并不意味着用户名和口令在任何意义上说都是安全的。如果没有采取预防措施,在Web 服务器主机上注册的用户,能够直接读取include 文件。请参阅7 . 4 . 3节“从Web 脚本连接到MySQL服务器”关于安装DBI 配置文件所描述的预防措施,它们用于保护口令和用户名不受其他用户侵害。对PHP 的include 文件也要应用同样的防范措施。
PHP 语言上的影响
如果有C 程序设计经验,则可能注意到:脚本中许多语法的结构与C 程序设计中的非常类似。实际上, PHP 语法很大程度上来自于C,因此这种相似处并不是巧合。如果有些C 的背景知识,就可以将它的许多内容转换到PHP。事实上,如果不能确信如何用PHP 编写表达式或控制结构,则可以试用C 中编写它们的方法,这很可能也是正确的。虽然PHP 的基本部分主要在C 中,但也包含了使用Java 和Perl 的成分。可以在注释语法中查看它,在那里允许以下形式:
# Perl-style comment from '#' to end of line
//c++ or Java-style comment from '//' to end of line
//* C-style comment between slash-star to star-slash */
Perl 的其他相似性包括‘ .’字符串连接操作符(包括‘ . =’作为额外的连接),变量引用和转义序列的方法是在双引号内而非单引号内解释的。
include 文件可以由多个脚本使用。这提高了代码的可重用性,使代码更加可维护。同时也允许对访问这个文件的每个脚本不费力地做出全局性的更改。例如,如果我们将数据库samp_db 从pit-viper 移动到b o a,则不必更改一簇单个脚本,而只要更改包含
samp_db_connect() 函数的include 文件中mysql_pconnect() 调用的主机名参数即可。
为了使用include 文件,必须有存放它们的地方,而且必须使PHP 找到它们。如果系统已经有了这样一个位置,则可以使用。如果没有,则使用下面的过程建立一个include 文件的位置:
1) 创建一个目录来存放PHP 的include 文件。该目录不能位于Web 服务器文档树内部!笔者使用/usr/local/apache/php 的PHP include 目录,它与我的文档树在同一层次上(/ us r / l o c a l / a p a c h e / h t d o c s),而不是在其内部。
2) 通过完整的路径名或者告诉PHP 在搜索时寻找哪个目录来引用include 文件。后者的方法更方便些,因为如果我们使用了文件的基名, PHP 就会找到它( PHP include 文件与C头文件有些类似,其中包括的PHP 将在多个目录中搜寻include 文件,就像C 预处理程序在多个目录中搜寻C 头文件一样)。为了告诉PHP 去哪里查看,修改PHP 初始化文件(系统上的/ us r / l o c a l / l i b / p h p 3 . i n i)来改变include_path 的值。如果它没有值,可以将它设置为新的包含路径的完整路径名:
include_path="current_value:/usr/local/apache/php"
如果include_path 已经有值了,则把新的目录加到那个值中:
include_path="current_value:/usr/local/apache/php"
3) 创建想使用的include 文件并将它放到i n c l ud e目录中。文件应该有一些有特点的名称,为了这个目的,这里我们使用samp_db . i n c。它的内容将在下面列出。对于我们这里开发的脚本,当连接到MySQL服务器上时,会一直使用samp_db 数据库,因此连接函数samp_db_connect() 也可以为我们选择samp_db 数据库。如果连接成功并选择了这个数据库,这个函数就返回一个连接标识符;如果发生错误,则返回FA L S E。发生错误时将不打印消息,并且允许调用者静静地退出,或者在环境允许时再打印消息。
观察一下,samp_db.inc 文件的内容由‘< ? p h p’和‘? >’括在一起。这是因为PHP 是在HTML 模式中开始读取文件的。如果没有这些标记PHP 会把文件以纯文本发送出去,而不是作为PHP 代码解释。如果想在文件中包含文字的HTML 是很好的选择。但是,如果文件包含PHP 代码,就必须在脚本标记内部封闭代码。
4) 使用下面的行从脚本中引用文件:
include("samp_db.inc");
当PHP 看到这一行时,就搜寻文件并读取内容。对于脚本的下列部分,文件中的任何事物都变成可访问的。
在建立了我们的include 文件samp_db.inc 之后,就可以修改历史同盟主页来引用i n c l ud e文件,并通过调用samp_db_connect() 函数连接到MySQL服务器上:
include( ) 与require( ) 的对比
PHP 的require() 性能与include() 相类似。不同之处在于,对include() 来说,在include() 执行时文件每次都要进行读取和评估;而对于require() 来说,文件只处理一次(实际上,文件内容替换了require() 语句)。这意味着如果有包含这些指令之一的代码和
可能执行多次的代码,则使用require() 的效率比较高。另一方面,如果每次执行代码时想读取不同的文件,或者有通过一组文件迭代的循环,就使用i n c l ud e ( ),因为可以给想要包括的文件名设置一个变量,当参数为include() 时使用这个变量。
samp_db.inc 文件对其他函数也是有用的,我们可以将它作为各种其他事物的储藏库。实际上,还可以再创建两个函数放入到文件中。我们编写的每个脚本在页面的开头都会产生一组相当醒目的HTML 标记,而另一组在结尾。不必在每个脚本中将它们逐字地写出,我们可以通过编写函数html_begin() 和html_end() 来做这些事。函数html_begin() 能够提取几个指定了页面标题和头的参数。两个函数的代码如下:
然后我们可以修改历史同盟主页来使用这两个新函数,如下所示:
请注意代码被分成了两块,两块代码之间出现了欢迎消息的文字HTML 文本。
产生页面开始和最后部分的函数用法给了我们一个重要的能力。如果想改变使用这些函数的页面头和尾的外观,可以在函数中包含一些代码。使用它们的每个脚本也都将自动地受到影响。例如,您可以把消息“ Copyright USHL”放在每个历史同盟页面的底部。页面尾部函数html_end() 会很容易地做到这一点。
一个简单的查询页面
已经嵌入到历史同盟主页中的脚本运行了一个只返回单个行的查询。下一个脚本介绍了如何处理多行的结果集。它获取并显示了member 表中的内容。这就是第7 章开发的dump_members DBI 脚本的PHP 等价物,因此称它为d um p _ member s . p h p。它与DBI 版本的不同之处在于,它希望在Web 环境中使用而不是在命令行中使用。由于这个原因,它需要产生HTML 输出而不是简单地写出制表符分隔的文本。为了使行和列漂亮地排列,我们将以HTML 表形式编写会员资格记录。脚本如下:
这个脚本使用了die() 函数来打印消息,如果发生错误则退出( die() 函数与exit() 函数相类似,但是它在退出之前打印消息)。与我们在历史同盟主页中使用的静静地退出方法相比,这是一种不同的错误处理方法。在dump_membes.php 中,我们希望看到一个特殊的结果,因此打印错误消息来说明发生的问题是有道理的。
脚本可以安装在ushl 目录并用http://pit-viper.snake.net/ushl/dump_members.php 访问。可以在历史同盟主页的新脚本中增加一个连接,以便人们了解它:
处理查询结果
在这一节中,我们将更细致地检查如何执行MySQL查询并处理结果集。在PHP 中,所有的查询都通过调用mysql_query() 函数来发布,这个函数提取查询字符串和连接标识符作为参数。连接标识符是可选的,因此可以用下面任意一种形式调用mysql_query():
$result=mysql_query($query,上一篇 目录 下一篇); # use explicit connection
$result=mysql_query($query); # use default connection
对于不返回行的查询(非SELECT 的查询,如D E L E T E、INSERT、REPLACE 和UPDATE),mysql_query() 返回TRUE 或者FALSE 说明查询成功或者失败。为了获得成功的查询,可以调用mysql_ a ffected_rows() 找出有多少行被改变(可能是删除、插入、替换或者更新)。
对于SELECT 语句,mysql_query() 返回结果集标识符或者FALSE 说明查询是成功或是失败。为了获得成功的查询,使用结果集标识符可以获得更多的有关结果集的信息。例如,可以找出结果集有多少行或列,或者提取这个结果集内部包括的行。
当mysql_query() 返回FA L S E(也就是零)时,意味着查询失败—换句话说就是发生一些错误而不能执行查询。查询失败可能有下面几个原因:
查询可能是畸形的并包含语法错误。
查询依照语法可能是正确的,但是在语义上却是无意义的,例如要从不包含列的表中选择列时。
没有充分的权利执行查询。
由于网络问题,可能已经连接不到MySQL服务器主机。
在以上每种情况中(还有其他情况),mysql_query() 都返回FA L S E。如果要想知道错误的详细原因,就调用mysql_error() 或者mysql_errno() 获得错误消息字符串或者数字错误代码(请参阅8 . 2 . 4节“处理错误”一节)。
考虑mysql_query() 造成的两种最常见的错误,返回值是行计数,或者它包含查询返回的数据,两者都是错误的。
1. 处理不返回结果集的查询
下面的代码使用DELETE 说明了如何处理不返回任何行的查询:
$result=mysql_query("DELETE FROM member WHERE member_id=149");
if (!$result)
print ("query failed ");
else
printf("number of rows deleted:%d ",mysql_affected_rows());
如果有一个ID 号为149 的成员,MySQL就删除记录,且mysql_query() 返回T R U E。如果没有这样的成员呢?这种情况下mysql_query() 仍然返回T R U E!这使将精力耗在误解mysql_query() 的返回值是行计数的人们非常惊讶。两种情况都返回T R U E,是因为不管实际上是否删除了一些行,查询都是合法的。由查询作用的行的数目则是完全不同的事。若要在查询成功之后提取值,可调用mysql_ a ff e c t e d _ r o w s ( )。
2. 处理返回结果集的查询
下面的实例提供了SELECT 查询处理的大致概况:
不要假设mysql_query() 会成功
在PHP 邮件发送清单中,新的PHP 用户会共同询问:执行脚本时为什么会发生下面的错误消息:
Warning:0 is not a MySQLresult index in file on line n
这个消息将为零的结果集标识符的值传递给了期望有效结果集的一些函数(如提取行的函数)。这意味着早时的mysql_query() 调用返回了零值—就是FA L S E。换句话说就是mysql_query() 失败,并且在由其他函数使用它之前,脚本编写程序对检查返回值并不烦恼。在使用mysql_query() 时,要一直检测返回值。
如果查询失败,结果将为FA L S E,对这个结果我们只是打印一条消息(取决于环境,其他对错误的反应可能更合适)。如果查询成功,则mysql_query() 返回结果集标识符。这个返回值在许多方法中是有用的(但不是作为行计数!)。结果集标识符可用于下列目标:
将它传递给mysql_ n um _ r o w s ( ),来确定结果集中的行数。
将它传递给mysql_ n um _ f i e l d s ( ),来确定结果集中的列数。
将它传递给提取行的例程,来提取结果集的连续行。这个实例使用了mysql_ f e t c h _r o w ( ),但是还有其他的选择,我们马上就会看到它们。
将它传递给mysql_ f r e e _ r e s ul t ( ),来释放结果集并允许PHP 处理一些与之相关的源文件。
在mysql_query() 成功地执行S E L E C T查询之后(请参见表8 - 1),PHP 为检索结果集提供了几个提取行的函数。当不再有行时,每个函数都得到一个结果集标识符作为参数并返回FA L S E。
表8-1PHP 行提取函数
函数名 | 返回值 |
mysql_ f e t c h _ r o w ( ) | 一个数组,由数字索引访问其元素 |
mysql_ f e t c h _ a r r a y ( ) | 一个数组,由数字索引或相关索引访问其元素 |
mysql_ f e t c h _ o b j e c t ( ) | 一个对象,作为属性访问其元素 |
最基本的调用是mysql_ f e t c h _ r o w ( ),它返回结果集的下一行作为一个数组。数组的元素通过从0 到mysql_ n um _ f i e l d s ( )-1范围内的数字索引访问。下面的实例说明了如何在每一行都提取和打印值的简单循环中使用mysql_ f e t c h _ r o w ( ):
变量$row 是一个数组。可用$row[$i] 访问它, $ i在这里是数字列索引。如果熟悉PHP count() 函数,可以试着用它而不要用mysql_num_fields() 来确定每一行的列数。count() 只计算这个数组中已设置值的元素的数量, PHP 不是与NULL 列值相对应的元素设置值。count ( ) 对返回列数的度量是不可靠的,因为那不是它想要的。它还用于另外两种提取行的函数。
第二个提取行的函数mysql_fetch_array() 在表8 - 1中列出,它与mysql_fetch_row() 相类似,但是由数字索引和相关索引返回的数组元素都是可靠的。换句话说,可以通过数字或名称访问元素:
由mysql_fetch_array() 返回的信息是mysql_fetch_row() 返回的信息的扩展集。除此之外,两个函数之间的不同性能是可以忽略不记的,调用mysql_ f e t c h _ a r r a y ( )可以无特殊性能损耗。
第三个提取行的函数mysql_ f e t c h _ o b j e c t ( ),返回结果集的下一行作为对象,这意味着用$row->col_name 语法访问行的元素。例如,如果从President 表中检索last_name 和first_name 值,可以用$row->last_name 和$row->first_name 访问那些列:
在查询结果中测试NULL 值
可以使用isset() 函数测试SELECT 查询返回的列值是否为NULL。例如,如果行包含在$row 数组中,那么如果$row[$i] 对应于NULL 值,则isset($row[$i]) 就为FA L S E,如果$row[$i] 为非NULL 值,则isset($row[$i]) 就为T R U E。相关的函数是e m p t y ( ),但是对于NULL 和空字符串,empty() 返回的结果都是一样的,因此作为NULL 值测试而言,这个函数是无用的。
如果查询包括计算的列怎么办?例如,发布一个作为表达式结果计算的返回值的查询:
SELECT CONCAT(first_name," ",lsat_last_name)FROM president
这样编写的查询不适于使用mysql_ f e t c h _ o b j e c t ( )。选择的列名本身就是表达式,它不是合法的属性名。然而,可以通过给列赋予一个别名来提供合法的名称。下面的查询将列的别名命名为f ul l _ name,如果用mysql_fetch_object() 提取结果,就允许它以$row->full_name 形式来访问:
SELECT CONCAT(first_name," ",lsat_last_name) AS full_name FROM president
处理错误
PHP提供了三种处理错误的方法:
■ 用‘@’取消错误消息。可以对一些显示消息的函数使用‘ @’。当我们调用mysql_pconnect() 阻止来自于函数的错误消息不在发送到客户机的页面上出现时,就已经在做这一点了。
使用error_reporting() 函数。这个函数按下列级别将错误报告打开或者关闭:
错误级别 | 错误报告类型 |
1 | 正常函数错误 |
2 | 正常警告 |
4 | 分析程序错误 |
8 | 请注意 |
为了控制错误报告,可调用error_reporting() 函数,且参数为想要激活的级别的总和。关闭级别1和级别2警告应该完全能够取消来源于MySQL的消息:
error_reporting(4+8);
你可能不想关闭有关分析错误的级别4警告,如果关掉了,可能要有一段艰难的时间用来调试它对脚本造成的改动!级别8警告常常被忽略,但有时它指出脚本中应该注意的问题,因此您可能也想把它激活。还有16和3 2错误级别,它们都来自于PHP 核心发动机,而非函数,因此通常情况下不必考虑它们。
使用mysql_error() 和mysql_er r n o ( )。这些函数报告了MySQL服务器返回的错误信息。它们与相应的C API 调用相类似。mysql_error() 以字符串形式返回错误信息(如果不发生错误就返回空字符串)。mysql_errno() 返回一个错误数字(如果不发生错误就返回0)。两个函数都有指定与MySQL服务器连接的连接标识符参数,在返回状态的连接上都返回最近调用的MySQL函数的错误信息。连接标识符是可选的,如果缺失,就使用最近打开的连接。例如,可以这样报告mysql_query() 的错误:
mysql_error() 和mysql_errno() 的PHP 版本在一个重要方面与C API 中对应的部分不同。在C 中,即使试图连接服务器失败,也会得到错误信息。相反, PHP 调用直到连接建立成功,才返回有用的连接信息。换句话说,如果连接失败,就不能用mysql_error() 和mysql_er r n o ( ) 报告失败原因。如果要报告连接失败的特殊原因而不是普通原因,则必须做特殊的考虑。请参阅附录H“PHP API 参考”,其中详细介绍了如何去做。
当检测到错误时,本章的脚本打印了相当普通的错误信息,如“查询失败”。然而在开发脚本时,您会发现:加入一个mysql_error() 调用对帮您发现错误的特殊原因是很有用的。
引用问题
在PHP 中构造查询字符串时,知道引用问题是必要的,就像在C 和Perl 中一样。虽然函数名在各种语言中是不同的,但处理引用问题的方法却是类似的。假设正在构造一个,将新的记录插入到表中的查询,可以在值的前后加上引号插入到字符串列中:
这里的问题是引用值的本身还包含着引号(“O’M a l l e y”),如果将查询发送到MySQL服务器会导致语法错误。在C 中我们调用mysql_escape_string() 解决这个问题。在Perl DBI脚本中则使用quote( ) 。PHP 有一个addslashes( ) 函数可以完成同样的事情。例如,调用a d d s l a s h e s(“O’ Malley”)返回“O’ Malley” 值。将前面的实例做如下编写来解决引用问题:
DBI quote( ) 方法是把前后的引号加到字符串中。addslashes( ) 则不是,因此我们仍需在查询字符串中要插入值的周围将那些引号显式地指定出来。
当编写信息出现在Web 页面上时也将发生引用问题。如果正在编写一个将作为HTML 或U R L的部分出现的字符串,而且这个字符串包含HTML 或URL 内部的特殊字符,最好将它编码。PHP 函数htmlspecialchars( ) 和urlencode( ) 可以做到这点,它们与C G I . p m
escapeHTML( ) 和escape( ) 方法相类似。
更多精彩
赞助商链接