发新话题
打印

《PHP和MySQL Web开发》学习笔记(三)

本主题被作者加入到个人文集中

《PHP和MySQL Web开发》学习笔记(三)

第2章 数据的存储与检索

2.1 保存数据以便后期使用
存储数据有两种基本方法:保存到普通文件(flat file),或者保存到数据库中。

2.2 存储和检索Bob的订单

2.3 文件处理
将数据写入一个文件,有3步操作:1)打开这个文件。如果这个文件不存在,需要先创建它。2)将数据写入这个文件。3)关闭这个文件。
从一个文件中读出数据,也有3步操作:1)打开这个文件。如果这个文件不能开始(例如,文件不存在),就应该意识到这一点并且正确地退出。2)从文件中读出数据。3)关闭这个文件。

2.4 打开文件
要在PHP中打开一个文件,可以使用fopen()函数。当打开一个文件的时候,还需要指定如何使用它,即文件模式。

2.4.1 选择文件模式
文件模式可告诉操作系统一种机制,这种机制可以决定如何处理来自其他人或脚本的访问请求,以及一种用来检查你是否有权访问这个特定文件的方法。
当打开一个文件的时候,有3种选择。
1)打开文件为了只读、只写或者读和写。
2)如果要写一个文件,你可能希望覆盖所有已有的文件内容,或者仅仅将新数据追加到文件末尾。如果该文件已经存在,也可以终止程序的执行而不是覆盖该文件。
3)如果希望在一个区分了二进制方式和纯文本方式的系统上写一个文件,还必须指定采用的方式。
函数fopen()支持以上3种方式的组合。

2.4.2 使用fopen()打开文件
假设要将一个顾客订单写入Bob的订单文件中。可以使用如下语句打开这个文件:$fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'w');

由于我们为表单变量定义了一个简短名称,我们需要在脚本的开始处加上如下的代码:$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
将冗长风格变量内容复制给简短风格的变量名称。

就像有不同的方法可以访问表单数据一样,也可以使用不同的方法访问预定义的服务器变量。根据服务器设置不同,可以通过如下3种方式得到文档根目录:$_SERVER['DOCUMENT_ROOT'],$DOCUMENT_ROOT,$HTTP_SERVER_VARS['DOCUMENT_ROOT']。
对于表单数据,第一个风格是首选的。
在UNIX环境下,目录中的间隔符是正斜线(/),如果你使用的是Windows平台,可以使用正斜线或者反斜线。如果使用反斜线,就必须使用转义(escape,标注为一个特殊字符),这样fopen()函数才能正确理解这些字符。要转义一个字符,只需简单地在其前面添加一个反斜线。如:$fp = fopen("$DOCUMENT_ROOT\\..\\orders\\orders.txt", 'w');
在PHP代码中,只有少数人会使用反斜线,因为这意味着代码只能在Windows上运行。如果使用了正斜线,代码不需要任何修改就可以在Windows和UNIX机器上运行。

fopen()函数的第2个参数是文件模式,它是一个字符串,指定了将对文件进行的操作。
r,r+,只读;w,w+,只写;x,x+,谨慎写;a,a+,追加;b,二进制;t,文本。

fopen()函数的第3个参数是可选的。如果要在include_path中搜索一个文件,就可以使用它。如果希望PHP搜索include_path,就不需要提供目录名称或路径。

fopen()函数的第4个参数也是可选的。fopen()函数允许文件名称以协议名称开始(例如,http://)并且在一个远程的位置打开文件。

如果fopen()成功地打开一个文件,该函数将返回一个指向这个文件的文件指针。

2.4.3 通过FTP或HTTP打开文件
除了打开一个本地文件进行读定操作之外,也可以使用fopen()函数通过FTP、HTTP或其他协议来打开文件。在php.ini文件中,可以通过关闭allow_url_fopen指令来禁用这个功能。
在PHP 4.0.5版本以前,fopen()函数不支持HTTP重定向,所以必须指定以斜线为结束的URL。
而在PHP4.3.0版本中,只要在编译PHP时打开了对OpenSSL的支持,就可能通过SSL打开文件,而且文件名称可以以https://开始。
请记住,URL中的域名不区分大小写,但是路径和文件名可能会区分大小写。

2.4.4 解决打开文件时可能遇到的问题
当打开文件时,可能经常遇到的错误是试图打开一个没有权限进行读写操作的文件(这种错误通常只会在类似于UNIX的操作系统见到,但是偶尔也会在Windows平台上遇到)。PHP将会给出警告。
根据服务器设置的不同,脚本可能是作为Web服务器用户或者脚本所在目录的属主来运行的。在大多数系统中,脚本将作为Web服务器用户来运行。
请记住,任何人都可以写的目录和文件是非常危险的。不应该具有可以从Web上直接可写的目录。
设置了不正确的访问权限可能是造成打开文件时出现错误的常见原因。
如果fopen()函数调用失败,函数将返回false。可以以一种对于用户友好的方式来处理这个错误,可以通过抑制错误信息并且根据自己的方式给出错误信息。
如:
@ $fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'ab');
if (!$fp)
{
echo '<p><strong> Your order could not be processed at this time.
        .'Please try again later.</strong></p></body></html>';
exit;
}
fopen()函数调用前面的@符号可以告诉PHP抑制所有由该函数调用所产生的错误。通常,在出现错误的时候,这是一个不错的方法。但是,在这种情况下,要在其他地方处理它。如:$fp = @fopen("$DOCUMENT_ROOT/../orders/orders.txt", 'a');
但是,这样使用错误抑制操作符并不是非常直观,而且只会便得代码调试更困难。这里所介绍的方法只是处理错误的简单方法。在第7章“异常处理”中,我们将详细介绍错误处理的好方法。
if语句可以用来测试变量$fp,查看fopen()函数是否返回了一个有效的文件指针。如果没有,它就会打印出一个错误信息并且终止脚本的执行。由于页面在这一步结束执行,请注意,我们在这里关闭了HTML标记,从而可以生成有效的HTML。

2.5 写文件
在PHP中写文件相对比较简单。可以使用fwrite()(file write,文件写)或者fputs()(file put string),fputs()是fwrite()的别名函数。我们可以使用如下方式调用fwrite():fwrite($fp, $outputstring); 这个函数告诉PHP将保存在$outputstring中的字符串写入到$fp指向的文件中。
fwrite()函数的一个新的替换函数是file_put_contents()。这个函数是PHP5新引入的,与之匹配的函数是file_get_contents()。

2.5.1 fwrite()的参数
fwrite()的第3个参数length是写入的最大字符数。如果给出了这个参数,fwrite()将向文件写入字符串直到字符串的末尾,或者写入了达到length的字节,满足这两个条件之一就停止写入。
可以通过PHP的内置strlen()函数获得字符串的长度,如下所示:fwrite($fp, $outputstring, strlen($outputstring));
当使用二进制模式执行写操作的时候,你可能会希望使用第3个参数,因为它可以帮助你避免一些跨平台的兼容性问题。

2.5.2 文件格式
控制序列“\n”表示换行,“\t”表示制表符。
使用特殊的域分隔符便于在读取数据的时候将数据分隔成不同的变量。


2.6 关闭文件
当使用完文件后,应该将其关闭。应该按照如下所示的方式调用fclose()函数。
如果该文件被成功地关闭,函数将返回一个true值。反之,该函数将返回false。通常,关闭文件的操作并不像打开文件容易出错。


2.7 读文件

2.7.1 以只读模式打开文件:fopen()

2.7.2 知道何时读完文件:feof()
函数feof()的唯一参数是文件指针。如果该文件指针指向了文件末尾,它将返回true虽然这个函数名称看上去有点古怪,但是如果知道feof表示File End Of File,就会很容易记住它。
在读文件的时候,我们持续进行读文件操作,直至遇到EOF。

2.7.3 每次读取一行数据:fgets()、fgetss()和fgetcsv()
$order = fgets($fp, 999);
fgets()函数可以从文件中每次读取一行内容。这样,它将不断地读入数据,直至读到一个换行字符(\n)、或者文件结束符EOF,或者是从文件中读取了998B。可以读取的最大长度为指定的长度减去1B。
当需要按块方式处理一些纯文件文件时,fgets()函数将会非常有用。

fgets()函数的一个非常有趣的变体是fgetss()函数。它与fgets()非常相似。但是它可以过滤字符串中包含的PHP和HTML标记。如果要过滤任何特殊的标记,可以将它们包含在allowalbe_tags字符串中。允许无限制的HTML代码出现在文件中可能会破坏你精心设计好的格式。允许无限制的PHP代码出现在文件可能会让恶意用户以几乎自由的方式控制服务器。

fgetcsv()函数是fgets()的另一个变体。
当在文件中使用了定界符时,例如我们在前面所介绍的制表符或者在电子制表软件和其他应用程序中使用的逗号,可以使用fgetcsv()函数将文件分成多行。如果希望重新构建订单中的变量,而不是将整个订单作为一行文本,使用fgetcsv()函数可以很容易实现。可以像调用fgets()一样调用它,但是必须向这个函数传递一个用于分隔表单域的定界符。例如:$order = fgetcsv($fp, 100, "\t");
以上代码将从文件中读取一行,并且在有制表符(\t)的地方将文件内容分行。该函数结果将返回一个数组(在以上代码就是$order)。
参数length应该比要读的文件中最长数据行的字符数大。enclosure参数用来指定每行中封闭一个域的字符。如果没有指定任何字符,在默认情况下,这个字符就是双引号“"”。

2.7.4 读取整个文件:readfile()、fpassthru()和file()
除了可以每次读取文件一行外,还可以一次读取整个文件。PHP提供了4种不同的方式来读取整个文件。
第一种方式是readfile()。调用readfile()函数将打开这个文件,并且将文件内容输出到标准输出(浏览器)中,然后再关闭这个文件。
第二种方式是fpassthru()。要使用这个函数,必须先使用fopen()打开文件。然后将文件指针作为参数传递给fpassthru()。这样就可以把文件指针所指向的文件内容发送到标准输出。然后再将这个文件关闭。
第三种方式是file()。除了可以将文件内容回显到标准输出外,它和readfile()是一样的,它把结果发送到一个数组中。
最后,在PHP 4.3.0中,还可以使用file_get_contents()函数。这个函数与readfile()相同,但是该函数将以字符串的形式返回文件内容,而不是将文件内容回显到浏览器中。这个新函数的优点在于,它是二进制安全的,这一点不同于file()函数。

2.7.5 读取一个字符:fgetc()
文件处理的另一个方法是从一个文件中一次读取一个字符。可以使用fgetc()函数来实现。它具有一个文件指针参数,这也是该函数的唯一参数,而且它将返回文件的下一个字符。
使用fgetc()函数的一个缺点就是它返回文件结束符EOF,而fgets()则不会。读取出字符后还需要判断feof(),因为我们并不希望将文件结束符EOF回显到浏览器中。

2.7.6 读取任意长度:fread()
读取一个文件的最后一种方法是使用fread()函数从文件中读取任意长度的字节。
使用该函数时,它或者是满足了length参数所指定的字节数,或者就是读到了文件末尾或网络数据包的结束。

2.8 使用其他有用的文件函数

2.8.1 查看文件是否存在:file_exists()

2.8.2 确定文件大小:filesize()
它以字节为单位返回一个文件的大小,结合fread()函数,可以使用它们一次读取整个文件(或者文件的某部分)。
nl2br()函数将输出的\n字符转换成HTML的换行符(<br />)。


2.8.3 删除一个文件:unlink()
PHP中没有名为delete的函数。
如果无法删除文件,该函数将返回false。通常,如果对该文件的访问权限不够或者该文件不存在,该函数将返回false。

2.8.4 在文件中定位:rewind()、fseek()和ftell()
rewind()函数可以将文件指针复位到文件的开始。ftell()函数可以以字节为单位报告文件指针当前在文件中的位置。
调用fseek()函数可以将文件指针从whence位置移动offset个字节。
rewind()函数等价于调用一个具有零偏移量的fseek()函数。

2.9 文件锁定
在PHP中,文件锁定是通过flock()函数来实现的。当一个文件被打开并且在进行读写操作之前,应该调用这个函数。
还必须将一个指向被打开文件的指针和一个表示所需锁定类型的常数作为参数传递给这个函数。如果文件锁定成功,其返回值为true,否则为false。
如果打算使用flock()函数,必须将其添加到所有使用文件的脚本中;否则,就没有任何意义。
请注意,flock()函数无法在NFS或其他网络文件系统中使用。它还无法在其他更早不支持文件锁定的文件系统中使用,例如FAT。在某些操作系统中,它是在进程级别上实现的,因此,如果你在多线程服务器API中使用,该函数也无法正确使用。

2.10 更好的方式:数据库管理系统
MySQL是一个关系数据库管理系统(RDBMS)。

2.10.1 使用普通文件的几个问题。
当文件变大时,使用普通文件将会变得非常慢。
在一个普通文件中查找特定的一个或者一组记录将会非常困难。
处理并发访问可能会遇到问题。
到目前为止,我们所看到的文件处理都是顺序的文件处理——也就是我们从文件开始处一直读到文件的结束。如果我们希望在文件中间插入记录或者删除记录(随机访问),这可能会比较困难——你将必须将整个文件读入到内存中,在内存中修改它,然后再将整个文件写回去。如果这是一个很大的数据文件,这可能会带来巨大的开销。
除了使用文件访问权限作为限制外,还没有一个简单的方法可以区分不同级别的数据访问。

2.10.2 RDBMS是如何解决这些问题的。
RDBMS提供了比普通文件更快的数据访问。
RDBMS可以很容易地查找并检索满足特定条件的数据集合。
RDBMS具有内置的处理并发访问的机制。
RDBMS具有内置的权限系统。
如果要创建一个简单的系统而又觉得不需要一个功能全面的数据库,但是又希望避免锁定和其他与使用普通文件相关的问题,你可能会考虑使用PHP的SQLite扩展。这个扩展对普通文件提供了一个基本的SQL接口。


2.11 进一步学习

2.12 下一章

[ 本帖最后由 shizu3 于 2007-9-4 15:27 编辑 ]
一直在路上……

TOP

发新话题