第2章 数据的存储与检索

本章,介绍如何将客户订单保存到一个文件,然后再将其读出来。

我们还介绍为什么这不是一个好的解决方案;

当具有大量订单时,应该使用一个数据库管理系统。例如mysql

2.1 保存数据以便后期使用

存储数据有两种基本方法:保存到普通文件,或者保存到数据库中。

文件的读写操作与大多数编程语言的文件读写操作时类似的。

2.2 存储和检索Bob的订单

增加一个表单域:address,送货地址。

本章,我们将所有订单都写入同一个文件。

2.3 文件处理

将数据写入一个文件,有以下3步:

1)打开这个文件,如果文件不存在,需要先创建它

2)将数据写入这个文件

3)关闭这个文件

同样,从一个文件中独处数据,也需要3步:

1)打开文件。如果文件不能打开,就应该意识到这一点并且正确地退出。

2)从文件中读出数据。

3)关闭这个文件

现在,我们从打开文件开始

2.4 打开文件

服务器上的操作系统文件必须知道要对打开的文件进行什么操作。操作系统需要了解在打开这个文件之后,这个文件是否还允许其他的程序脚本再打开,还需要了解脚本的属主用户是否具有在这种方式下使用该文件的权限。

2.4.1 选择文件模式

当在打开文件时有三种选择:

  • 打开一个文件为了只读、只写或者是读和写。

  • 如果要写一个文件,可以覆盖所有已有的文件内容,或者需要将新数据追加到文件末尾。如果文件已经存在,也可以终止程序的执行而不是覆盖该文件。

  • 如果在一个区分二进制文件和纯文本文件的系统上写一个文件,还必须指定采用的方式。

函数fopen()也支持以上三种方式的组合。

2.4.2 使用fopen()打开函数

打开一文件,

$fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt", "w");

调用fopen()需要传递2个、3个或4个参数。通常是2个参数。

第一个参数:要打开的文件,可以指定该文件的路径。

我们已经使用了PHP内置变量$_SERVER['DOCUMENT_ROOT']。由于整个表单变量名称太长了,我们可以指定一个简短的名称。

====

..表示文档根目录的父目录。

处于安全考虑,这个目录位于整个文档树的外部。

在这个例子中,除了通过我们所提供的接口之外,不希望其他web接口能访问它,这个路径称为相对路径,因为它描述了一个相对于文档根目录的文件系统位置。

====

$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT']

可使用不同的方法访问预定义的服务器变量,根据服务器设置不同:

  • $_SERVER['DOCUMENT_ROOT']

  • $DOCUMENT_ROOT

  • $HTTP_SERVER_VARS['DOCUMENT_ROOT']

还可以指定文件的绝对路径。这个路径是从根目录开始的。

在UNIX服务器中,根目录就是/home/book/orders。UNIX中,目录中的间隔符是/。

如果使用windows平台,可以使用/或者\。如果使用反斜线,就必须使用转义字符。要转义一个字符,只需简单地在其前面添加一个反斜线。

$fp = fopen("$DOCUMENT_ROOT\\..\\orders\\orders.txt", "w");

在PHP代码中,只有少数人使用反斜线,因为这意味着代码只能在windows上运行。

fopen()的第二个参数是文件模式,如下是所有的文件模式及其意义:

122405_FooG_724288.png

在我们的例子中所使用的文件模式取决于需要如何使用这个系统。我们已经用了“w”,这表示可以将一个订单写入文件中。每当一个新订单被写入文件,它将覆盖以前的订单。这样做可能没有什么意义,所以最好使用追加模式(以及推荐的二进制模式):

$fp = fopen("$DOCUMENT_ROOT/../orders/orders.txt","ab");

fopen()函数的第3个参数是可选的。如果要在include_path(在PHP的配置中设置)中搜索一个文件,就可以用它。如果希望进行此操作,可以将这个参数设置为1.如果希望PHP搜索include_path,就不需要提供目录名称或路径:

$fp = fopen('orders.txt','ab',true);

第4个参数也是可选的。fopen()函数允许文件名称以协议名称开始(例如,http://)并且在一个远程的位置打开文件。对于这个额外的参数,它还支持一些其他的协议。

如果fopen()成功地打开一个文件,该函数将返回一个指向这个文件的文件指针。在这个例子中,文件指针保存在$fp中。当读者的确希望能够读写这个文件时,将使用这个变量来访问文件。

2.4.3 使用FTP或HTTP打开文件

除了打开一个本地文件进行读写操作之外,也可以使用fopen()函数通过FTP、HTTP或其他协议来打开文件。在php.ini文件中,可以通过关闭allow_url_fopen指令来禁用这个功能。如果在使用该函数打开一个远程文件时遇到问题,请检查php.ini文件。

如果使用的文件名是以ftp://开始的,fopen()函数将建立一个连接到指定服务器的被动模式,并返回一个指向文件开始的指针。

如果使用的文件名是以http://开始的,fopen()函数将建立一个到指定服务器的HTTP连接,并返回一个指向HTTP响应的指针。当使用PHP早期版本的HTTP模式时,必须在目录名称后添加结束斜线,如下所示:

http://www.example.com/

而不是:

http://www.example.com

当使用后一种地址形式(不带斜线)时,Web服务器通常会使用HTTP重定向到第一个地址(带斜线的地址)。

你可以在浏览器中试一试。在PHP 4.0.5版本以前,fopen()函数不支持HTTP重定向,所以必须指定以斜线为结束的URL。

而在PHP 4.3.0版本中,只要在编译PHP时打开了对OpenSSL的支持,就可以通过SSL打开文件,而且文件名称可以以https://开始。

请记住,URL中的域名不区分大小写,但是路径和文件名可能会区分大小写。

2.4.4 解决打开文件时可能遇到的问题

当打开文件时,可能经常遇到的错误是试图打开一个没有权限进行读写操作的文件(这种错误通常只会在类似于UNIX的操作系统见到,但是偶尔也会在Windows平台上遇到。)

如果遇到这样的问题,必须确认运行该脚本的用户是否有权访问要使用的文件。根据服务器设置的不同,该脚本可能是作为Web服务器用户或者脚本所在目录的属主来运行的。

在大多数系统中,该脚本将作为Web服务器用户来运行。如果脚本是在UNIX系统的~/public_ html/chapter2/目录下,输入如下所示的命令,可以创建一个全局可写的目录来存储订单:

mkdir ~/orders
chmod 777 ~/orders

请记住,任何人都可以写的目录和文件是非常危险的。不应该具有可以从Web上直接可写的目录。正是由于这个原因,orders目录是两个子目录,都在public_html目录之上。在第15章,“电子商务的安全问题”中详细介绍安全问题。

设置了不正确的访问权限可能是造成打开文件时出现错误的常见原因。如果文件不能打开,你需要知道这一点,这样就不会再去读写数据。

如果fopen()函数调用失败,函数将返回false。可以以一种对于用户友好的方式来处理这个错误,可以通过抑制PHP的错误信息并且根据自己的方式给出错误信息:

    @$fp = fopen("/home/work/apache2/htdocs/study/orders.txt", "w");
    if ($fp < 0) {
        echo "no forbidden.";
        exit;
    }

fopen()函数调用前面的@符号可以告诉PHP抑制所有由该函数调用所产生的错误。通常,在出现错误的时候,这是一个不错的方法。但是,在这种情况下,要在其他地方处理它。

以上代码也可以写成: 

$fp = @fopen("/home/work/apache2/htdocs/study/orders.txt", "w");

但是,这样使用错误抑制操作符并不是非常直观,而且只会使得代码调试更困难。这里所介绍的方法只是处理错误的简单方法。在第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(),其函数原型如下所示:

int file_put_contents(string filename, 
		sring data
		[, flags
		[, resource context]])

这个函数可以在不需要调用fopen()(或fclose())函数打开要写的文件以前,将包含在data中的字符串数据写入到filename所指定的文件中。这个函数是PHP5新引入的,与之匹配的函数是file_get_contents(),稍后我们将介绍这两个函数。当使用FTP或HTTP向远程文件写入数据时,最常用的是可选参数flags和context。(我们将在第19章中详细介绍这些函数)。

2.5.1 fwrite()的参数

实际上,函数fwrite()具有3个参数,但是第3个参数是可选的。fwrite()的原型如下所示: 

int fwrite(resource handle, string string [, int length])

第3个参数length是写入的最大字符数。如果给出了这个参数,fwrite()将向handle指向的文件写入字符串,一直写到字符串的末尾,或者已经写入了length字节,满足这两个条件之一就停止写入。

可以通过PHP的内置strlen()函数获得字符串的长度,如下所示: 

fwrite($fp, $outputstring, strlen($outputstring))

当使用二进制模式执行写操作的时候,你可能会希望使用第3个参数,因为它可以帮助你避免一些跨平台的兼容性问题。

2.5.2 文件格式

当创建一个如我们例子中使用到的数据文件时,保存数据的格式将完全由你决定。(然而,如果打算在另一个应用程序中使用这个数据文件,你可能就不得不遵循那个应用程序的规则。)

下面,让我们构造一个表示数据文件中一条记录的字符串。可以使用如下所示语句:

$outputstring = $date."\t".$tireqty."tires\t".$oilqty."oil\t"
                      .$sparkqty."spark plugin\t\$".$totalamount.
                      "\t".$address."\n";

在这个简单的例子中,我们将每一个订单记录保存在文件的一行中。我们选择每行记录一个订单这种格式是因为这样可以使用换行字符作为简单的记录间隔符。

在本书的所有例子中,我们每次按照相同的顺序写入数据字段,并且使用制表符来分隔每一个字段。需要再次提到的是,由于制表符是不可见的,因此可以使用控制序列“\t”来表示。可以选择任何便于以后读取的、有意义的定界符。

分隔字符或定界字符一定不能出现在输入中,或者我们对输入进行处理,将分隔符删除或者进行转义处理。在第4章,“字符串操作与正则表达式”中详细介绍输入的处理。现在,我们假设没有人会在订单中输入制表符。对于一个用户来说,在一行HTML的输入域中输入一个制表符或者换行字符是比较困难的,但是这并不是没有可能的。

使用特殊的域分隔符便于在读取数据的时候将数据分隔成不同的变量。从现在开始,我们将每一个订单当作一个字符串进行处理。

处理了一些订单后,该文件的内容将类似于如下。

135112_oje2_724288.png

2.6 关闭文件

当使用完文件后,应该将其关闭。应该按照如下所示的方式调用fclose()函数: 

fclose($fp)

如果该文件被成功地关闭,函数将返回一个true值。反之,该函数将返回false。通常,关闭文件的操作并不像打开文件容易出错,所以在这个例子中我们并没有对该操作进行测试。

processorder.php的脚本清单如下:

    $outputstring = $date."\t".$tireqty."tires\t".$oilqty."oil\t"
                      .$sparkqty."spark plugin\t\$".$totalamount.
                      "\t".$address."\n";
    echo "outputstring: ".$outputstring."<br>";
    @$fp = fopen("/home/work/apache2/htdocs/study/orders.txt", "aw");
    flock($fp, LOCK_EX);
    if ($fp < 0) {
        echo "no forbidden.";
        exit;
    }
    fwrite($fp, $outputstring, strlen($outputstring));
    flock($fp, LOCK_UN);
    fclose($fp);

2.7 读文件

我们可以建立一个Web界面,从而方便Bob的员工读取这些文件。这个界面代码如下:

<?php
$filename = "/home/work/apache2/htdocs/study/orders.txt";
@$fp = fopen($filename, 'r');
if (!$fp) {
    echo "no forbidden";
    exit;
}
while(!feof($fp)) {
    $orders = fgets($fp, 999);
    echo $orders."<br>";
}
fclose($fp);
?>

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

仍然使用fopen()函数打开文件。在这个例子中,以只读模式打开这个文件,所以使用了“rb”文件模式:

@$fp = fopen("/home/work/apache2/htdocs/study/orders.txt", "rb");

2.7.2 知道何时读完文件:feof()

在这个while循环语句中,使用feof()函数作为文件结束的测试条件: 【File End Of File】

函数feof()的惟一参数是文件指针。如果该文件指针指向了文件末尾,它将返回true。

在这个例子(通常是在读文件的时候)中,我们持续进行读文件操作,直至遇到EOF。

2.7.3 每次读取一行数据:fgets()、fgetss()和fgetcsv()

在这个例子中,使用fgets()函数来读取文件内容: 

$order = fgets($fp, 999);

这个函数可以从文件中每次读取一行内容。这样,它将不断地读入数据,直至读到一个换行字符(\n)、或者文件结束符EOF,或者是从文件中读取了998B。可以读取的最大长度为指定的长度减去1B。

也可以使用许多不同的函数来读文件。当需要按块方式处理一些纯文本文件时,fgets()函数将会非常有用。

fgets()函数的一个非常有趣的变体是fgetss()函数,其函数原型如下所示:

string fgetss(resource handle, int length, string [allowable_tags])

fgetss()函数与fgets()非常相似,但是它可以过滤字符串中包含的PHP和HTML标记。如果要过滤任何特殊的标记,可以将它们包含在allowable_tags字符串中。当读取由别人所编写的文件或者包含用户输入的文件时,出于操作安全的考虑,可以使用fgetss()函数。允许无限制的HTML代码出现在文件中可能会破坏你精心设计好的格式。允许无限制的PHP代码出现在文件可能会让恶意用户以几乎自由的方式控制服务器。

fgetcsv()函数是fgets()的另一个变体。它具有如下所示的函数原型:

 arrry fgetcsv(resource handle, int length [, string delimiter
				[, string enclosure])

当在文件中使用了定界符时,例如我们在前面所介绍的制表符或者在电子制表软件和其他应用程序中使用的逗号,可以使用fgetcsv()函数将文件分成多行。如果希望重新构建订单中的变量,而不是将整个订单作为一行文本,使用fgetcsv()函数可以很容易实现。可以像调用fgets()一样调用它,但是必须向这个函数传递一个用于分隔表单域的定界符。例如: 

$order = fgetcsv($fp, 100, "\t")

以上代码将从文件中读取一行,并且在有制表符(\t)的地方将文件内容分行。该函数结果将返回一个数组(在以上代码就是$order)。我们将在第3章详细介绍数组。

参数length应该比要读的文件中最长数据行的字符数大。

enclosure参数用来指定每行中封闭每一个域的字符。如果没有指定任何字符,在默认情况下,这个字符就是”(双引号)。

2.7.4 读取整个文件:readfile()、fpassthru()和file()

除了可以每次读取文件一行外,还可以一次读取整个文件。PHP提供了4种不同的方式来读取整个文件。

第一种方式是readfile()。可以使用如下一行语句来代替前面所编写的所有脚本:

readfile("/home/work/apache2/htdocs/study/orders.txt");

调用readfile()函数将打开这个文件,并且将文件内容输出到标准输出(浏览器)中,然后再关闭这个文件。readfile()的函数原型如下所示: 

int readfile(string filename, [ int use_include_path[, resource context]])

第二个可选参数指定了PHP是否应该在include_path中查找文件,这一点与fopen()函数一样。可选的context参数只有在文件被远程打开(例如通过HTTP)时才使用;我们将在第19章详细介绍这种用法。这个函数的返回值是从文件中读出的字节总数。

第二种方式是fpassthru()。要使用这个函数,必须先使用fopen()打开文件。然后将文件指针作为参数传递给fpassthru()。这样就可以把文件指针所指向的文件内容发送到标准输出。然后再将这个文件关闭。

可以使用如下代码替代前面的脚本:

$fp = fopen("/home/work/apache2/htdocs/study/orders.txt", "rb");
fpassthru($fp);

如果读操作成功,fpassthru()函数将返回true,否则返回false。

第三种读取整个文件的函数是file()。除了可以将文件内容回显到标准输出外,它和readfile()是一样的,它是把结果发送到一个数组中。我们将在第3章介绍数组时详细介绍。作为参考,可以按如下方式调用它: 

$filearray = file("/home/work/apache2/htdocs/study/orders.txt");

这行代码可以将整个文件读入到一个名为$filearray的数组中。文件中的每一行都将作为一个元素保存在这个数组中。

最后,在PHP 4.3.0中,还可以使用file_get_contents()函数。这个函数与readfile()相同,但是该函数将以字符串的形式返回文件内容,而不是将文件内容回显到浏览器中。这个新函数的优点在于,它是二进制安全的,这一点不同于file()函数。

2.7.5 读取一个字符:fgetc()

文件处理的另一个方法是从一个文件中一次读取一个字符。可以使用fgetc()函数来实现。它具有一个文件指针参数,这也是该函数的惟一参数,而且它将返回文件的下一个字符。我们可以使用具有fgetc()函数的循环来代替原来脚本中的while循环,如下所示:

<?php
@$fp = fopen("/home/work/apache2/htdocs/study/orders.txt", "rb");
if (!$fp) {
    echo "no forbidden";
    exit;
}
while(!feof($fp)){
    $char = fgetc($fp);
    if (!feof($fp)) {
        echo $char=="\n"?"<br>":$char;
    }
}
?>

这段代码使用fgetc()函数从文件中一次读取一个字符,并且将该字符保存在$char中,直到文件结束。然后再用HTML的换行符(<br/>)替换文本中的行结束符(\n)。

这样做仅仅是为了整理输出格式。如果输出文件的记录之间带有\n,那么整个文件将显示在一行中(试一下就会知道)。Web浏览器并不会表示空格,例如新行。因此你必须用HTML的换行符(<br/>)替换文本中的行结束符(\n)。我们可以使用三元运算符来完成此操作。

使用fgetc()函数的一个缺点就是它返回文件结束符EOF,而fgets()则不会。读取出字符后还需要判断feof(),因为我们并不希望将文件结束符EOF回显到浏览器中。

如果不是为了某些原因需要对文件逐个字符进行处理,这种逐个字符读取的方法现实意义并不大。

2.7.6   读取任意长度:fread()

读取一个文件的最后一种方法是使用fread()函数从文件中读取任意长度的字节。这个函数的原型如下所示: 

使用该函数时,它或者是读满了length参数所指定的字节数,或者就是读到了文件末尾或网络数据包的结束。

string fread(resource fp, int length)

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

在PHP中,还有许多我们经常使用的有用的文件函数。

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

如果希望在不打开文件的前提下,检查一个文件是否存在,可以使用file_exists()函数,如下所示:

if (file_exists($filename)) {
    echo $filename." exists <br>";
} else {
    echo $filename." not exists <br>";
}

2.8.2   确定文件大小:filesize()

可以使用filesize()函数来查看一个文件的大小。如下所示: 

它以字节为单位返回一个文件的大小,结合fread()函数,可以使用它们一次读取整个文件(或者文件的某部分)。可以用如下的代码来替换以前的代码:

<?php
@$fp = fopen("/home/work/apache2/htdocs/study/orders.txt", "rb");
if (!$fp) {
    echo "no forbidden";
    exit;
}
echo nl2br(fread($fp, filesize("/home/work/apache2/htdocs/study/orders.txt")));
fclose($fp);
?>

nl2br()函数将输出的\n字符转换成HTML的换行符(<br/>)。

2.8.3   删除一个文件:unlink()

在处理完订单后,可能希望删除这个订单文件,可以使用unlink()函数。(PHP中没有名为delete的函数。)例如: 

unlink($filename);

如果无法删除这个文件,该函数将返回false。通常,如果对该文件的访问权限不够或者该文件不存在,该函数将返回false。

2.8.4   在文件中定位:rewind()、fseek()和ftell()

可以使用rewind()、fseek()和ftell()对文件指针进行操作,或者确定发现它在文件中的位置。

rewind()函数可以将文件指针复位到文件的开始。ftell()函数可以以字节为单位报告文件指针当前在文件中的位置。例如,我们可以在初始脚本的结束处添加如下几行代码(在fclose()命令之前):

echo "fianl postion:".ftell($fp)."<br>";
rewind($fp);
echo "new postion:".ftell($fp)."<br>";

可以使用fseek()函数将文件指针指向文件的某个位置。其函数原型如下所示: 

int fseek(resource fd, int offset [,whence]))

调用fseek()函数可以将文件指针fp从whence位置移动offset个字节。whence是一个可选参数,其缺省值SEEK_SET表示文件的开始处。该参数的其他可能值为SEEK_CUR(文件指针的当前位置)和SEEK_END(文件的结束)。

rewind()函数等价于调用一个具有零偏移量的fseek()函数。例如,可以使用fseek()函数找到文件中间的记录,或者完成一个二进制查找。通常,如果所涉及的数据文件具有一定的复杂程度,在必须完成这些操作时,使用数据库可以使得这些工作变得更加简单。

2.9   文件锁定

假设遇到这种情况,两个客户试图同时订购同一件商品(这种情况并不少见,尤其是当网站上遇到某种程度的网络堵塞的时候。)如果一个客户调用fopen()函数打开一个文件并且开始写这个文件,而此时其他客户也调用了fopen()函数打开这个文件并且要写这个文件,将会出现什么情况呢?文件的最终内容是什么?会是第一个订单后面就是第二个订单吗?还是恰好相反呢?订单是第一个客户的还是第二个客户的?或者将变成一些没用的东西,就像两个订单交错在一起?这些疑问取决于操作系统,但是,通常都是不可知的。

为了避免这样的问题,可以使用文件锁定的方法。在PHP中,文件锁定是通过flock()函数来实现的。当一个文件被打开并且在进行读写操作之前,应该调用这个函数。

flock()函数原型如下所示: 

还必须将一个指向被打开文件的指针和一个表示所需锁定类型的常数作为参数传递给这个函数。如果文件锁定成功,其返回值为true,否则为false。如果获得文件锁将导致当前的进程被阻塞(也就是,不得不等待),可选的第3个参数将包含值true。

operation参数的可能值如下所示。在PHP 4.0.1中,该参数的可能值已经发生了变化,因此如下给出了两个值集合。

185054_tGyb_724288.png

如果打算使用flock()函数,必须将其添加到所有使用文件的脚本中;否则,就没有任何意义。

请注意,flock()函数无法在NFS或其他网络文件系统中使用。它还无法在其他更早不支持文件锁定的文件系统中使用,例如FAT。在某些操作系统中,它是在进程级别上实现的,因此,如果你在多线程服务器API中使用,该函数也无法正确使用。

要在这个例子中使用flock()函数,我们可以对processorder.php脚本进行如下所示的修改:

@$fp = fopen("/home/work/apache2/htdocs/study/orders.txt", "aw");
    flock($fp, LOCK_EX);
    if ($fp < 0) {
        echo "no forbidden.";
        exit;
    }
    fwrite($fp, $outputstring, strlen($outputstring));
    flock($fp, LOCK_UN);
    fclose($fp);

你还应该在vieworders.php脚本中添加如下所示的文件锁:

@$fp = fopen($filename, 'r');
if (!$fp) {
    echo "no forbidden";
    exit;
}
flock($fp, LOCK_SH);
while(!feof($fp)) {
    $orders = fgets($fp, 999);
    echo $orders."<br>";
}
flock($fp, LOCK_UN);

现在,我们的代码更加健壮,但是还不完美。如果有两个脚本同时申请对一个文件加锁,情况又会如何呢?这将导致竞争条件的问题,这两个进程将竞争加锁,但是无法确定哪一个进程将会成功,这样就会导致更多的问题。使用数据库管理系统(DBMS),我们可以很好地解决这个问题。

2.10   更好的方式:数据库管理系统

到目前为止,在我们的例子中所使用的文件都是普通文件。在本书的下一篇中,我们将介绍如何使用MySQL,它是一个关系数据库管理系统(RDBMS)。你可能会问,“我为什么要使用它?”

2.10.1   使用普通文件的几个问题

使用普通文件,你可能会遇到如下这些问题:

当文件变大时,使用普通文件将会变得非常慢。

在一个普通文件中查找特定的一个或者一组记录将会非常困难。如果记录是按顺序保存,你可以使用某种二分法并结合按定长记录来搜索一个关键字域。如果你希望查找模式信息(例如,需要查找所有生活在Smalltown的客户),就不得不读入每一个记录并且进行逐个检查。

处理并发访问可能会遇到问题。你已经了解了如何锁定文件,但是锁定可能导致我们前面介绍的竞争条件。它也可以导致一个瓶颈。如果一个站点具有太多的访问量,大量的用户就可能在能够创建订单之前必须等待该文件解锁。如果该等待时间太长,人们可能会到其他地方购买。

到目前为止,我们所看到的文件处理都是顺序的文件处理—也就是我们从文件开始处一直读到文件的结束。如果我们希望在文件中间插入记录或者删除记录(随机访问),这可能会比较困难—你将必须将整个文件读入到内存中,在内存中修改它,然后再将整个文件写回去。如果这是一个很大的数据文件,这可能会带来巨大的开销。

除了使用文件访问权限作为限制外,还没有一个简单的方法可以区分不同级别的数据访问。

2.10.2   RDBMS是如何解决这些问题的

关系型数据库管理系统(RDBMS)可以解决以上所有问题:

RDBMS提供了比普通文件更快的数据访问。我们在本书中所使用的数据库系统MySQL在许多方面都拥有比任何RDBMS更快的速度。

RDBMS可以很容易地查找并检索满足特定条件的数据集合。

RDBMS具有内置的处理并发访问的机制。作为一位编程人员,你不必担心这一点。

RDBMS可以随机访问数据。

RDBMS具有内置的权限系统。MySQL在这一方面具有特别的优势。

使用RDBMS的主要原因是RDBMS实现了数据存储系统所必需的所有(或者至少是大多数)功能。当然,也可以编写你自己的PHP函数库,但是为什么不利用已有的功能呢?

在本书的第二篇,“使用MySQL”将介绍关系数据库的基本工作原理,以及如何安装并使用MySQL来创建支持后台数据库的Web站点。

如果要创建一个简单的系统而又觉得不需要一个功能全面的数据库,但是又希望避免锁定和其他与使用普通文件相关的问题,你可能会考虑使用PHP的SQLite扩展。这个扩展对普通文件提供了一个基本的SQL接口。在本书中,我们的重点是使用MySQL,但是如果希望获得更多关于SQLite的信息,可以在http://sqlite.org/和http://www.php.net/sqlite找到。

2.11   进一步学习

关于与文件系统进行交互的更多信息,可以参阅第18章,“与文件系统和服务器的交互”。在该部分中,我们将详细介绍如何修改文件权限、属主和名称;如何使用目录以及如何与文件系统环境进行交互。

如需阅读PHP在线手册中关于文件系统的介绍,可以在http://www.php.net/filesystem中找到。

2.12   下一章

在下一章中,我们将介绍什么是数组及如何在PHP脚本中使用它们来处理数据。


转载于:https://my.oschina.net/cqlcql/blog/540441

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值