打印本文 打印本文  关闭窗口 关闭窗口  
PHP、MySQL教程
作者:Kevin Yank  文章来源:网络文摘  点击数  更新时间:2006/1/18 9:38:07  文章录入:admin  责任编辑:admin

  现在我们基本上已经大功告成了。我们已经设计了一个数据库用来存储笑话,将它们组织到目录中,并标志它们的作者。我们也已经学会了如何建立一个Web页来向我们的访问者显示我们的笑话库。我们还开发了一系列的Web页以使得网站的管理者不需要任何数据库的知识就能管理笑话库。

  当我们这样做之后,我们已经解决了这么一个头疼的问题,那就是不断地插入新的内容到HTML模板中,并产生大量难管理的HTML文件。现在,HTML已经完全和显示的数据分开了。如果你想重新设置这个网站,你只需要改变显示给访问者的PHP文件中的HTML。对一个文件夹的修改(例如改变字体)会立即反映到所有笑话的显示,因为所有的笑话都使用同一个PHP文件来显示。在管理这个Web站点的内容时仅仅还有一个情况需要用到HTML:内容的格式。

  除了一些最简单的Web站点之外,我们总是需要允许内容(在这里是指笑话)有自己可应用的格式。一个简单的情况是,你仅需要能将文本分段。然而,更通常的情况是,内容的提供者还希望能够简单地实现文字的粗体、斜体或者是超连接等等。

  我们目前的数据库和网站设计支持所有的这些功能,因为一个网站的管理者可以在笑话的内容中包含HTML标志,而在将笑话的内容插入到网站的访问者请求的页面中时也能起到它当初的效果。然而,为了实现我们系统管理完全不需要HTML的知识,我们需要提供一些其它的方法以格式化文本。

  在本章中,我们会学习一些新的PHP函数,这些函数可以让我们能够提供基本的文本格式而不需要使用HTML。当我们这样做了之后,我们会真正拥有一个足够简单的可以供第一个有Web浏览器的人使用的内容管理系统。有了这个功能之后,我们可以再次允许用户提供它们自己的笑话--而这一次,我们不必再担心我们的站点会充满令人讨厌的或其它不适当的东西。

  排除原有的干扰

  在我们提供一个有关格式化内容的新的方法之前,我们首先必须排除旧东西的影响。一些没有HTML知识的人可能会不知不觉地在一个纯文本的文档中包含HTML语法(然而是无效的),这将引起意想不到的后果,甚至会损坏我们的页面的显示效果。我们先来看看下面的句子:

The gunman drew his weapon. <BANG!>

  如果有人将上面的文字录入到数据库中,在Web页显示这个内容他也许会奇怪最后的那个单词(<BANG!>)怎么不见。有一些基本的HTML知识的人会意识到Web浏览器已经将这一段作为无效的HTML标志忽略了,但是我们需要迎合根本没有任何HTML知识的用户。

  在第五章中,我们曾经看到一个PHP函数可以巧妙地处理这个问题:htmlspecialchars。这个函数在将我们的笑话插入到一个Web页时,将其转换成一种“HTML安全”的格式:

The gunman drew his weapon. <BANG!>

  当反馈到网站访问者的Web浏览器时,这会显示出我们当初所预想的结果。因此,作为第一步,我们需要修改我们的网站用来显示笑话内容的PHP文件,在文本输出到页面之前使用htmlspecialchars:

<!-- joke.php -->
...
// Get the joke text from the database
$joke = mysql_query("SELECT JokeText FROM Jokes
WHERE ID=$id");
$joke = mysql_fetch_array($joke);
$joketext = $joke["JokeText"];
// Filter out HTML code
$joketext = htmlspecialchars($joketext);
echo( $joketext );
...

  我们已经使出现在内容中的HTML代码失效了。现在我们可以通过执行我们自己的特别的标志语言来执行我们自己的内容格式。

  正则表达式

  执行我们自己的标志语言会包含在笑话的文本中找出我们自己的标志并在输出到用户的浏览器前将其替换成相应的HTML。每一个了解正则表达式的人都会意识到使用与此正则表达式很适合于处理这些工作。

  一个正则表达式是一个可能包含指定代码的字符串,它可以被一些PHP函数用来搜寻和处理文本。下面的这个例子就是一个正则表达式,它可以用来搜索“PHP”(没有引号):

PHP

  不太复杂,是吗?要使用一个正则表达式,你必须熟悉PHP中使用正则表达式的函数。ereg是其中最基本的,它可以用来判断一个正则表达式是否匹配具体的字符串。请参看下面这段代码:

$text = "PHP rules!";
if ( ereg("PHP", $text) ) {
echo('$text contains the string "PHP".');
} else {
echo('$text does not contain the string "PHP".');
}

  在这个例子中,正则表达式是匹配的,因为存储在变量$text中值包含“PHP”。这样,上面的代码的输出将是这样的(注意这里的单引号使用PHP不会用相应的$text的值取代$text):

$text contains the string "PHP".

  eregi的功能基本上和ereg相同,只是它忽略被搜索的文本的大小写:

$text = "What is Php?";
if ( eregi("PHP", $text) ) {
echo('$text contains the string "PHP".');
} else {
echo('$text does not contain the string "PHP".');
}

  这会输出同样的信息:

$text contains the string "PHP".

  正如我们上面提到的,在正则表达式中可以使用一些特殊的代码。其中的一些代码可以容易混淆,而且也很难记,如果你想要很好地使用它,你需要找到一本好的参考。有关标准正则表达式的语法的指南格式的参考你可以从http://www.delorie.com/gnu/docs/rx/rx_toc.html(编者注:中文的在这里,Exceed PHP - 超越PHP -- 站点搜索)中找到,在WroxPress编写的专业PHP程序设计一书的附录中也包含了正则表达式语法的参考。让我们通过一些例子来学习一些基本的正则表达式的语法。

  首先,一个脱字符(^)被用来标志字符串的开始,而一个美元符($)被用来标志字符串的结束:

PHP //匹配"What is PHP?"
^PHP //匹配"PHP rules!"但不匹配"What is PHP?"
PHP$ //匹配"I love PHP"但不匹配"What is PHP?"
^PHP$ //只匹配"PHP"

  明显地,你们有时会想要使用^、$或其它一些特殊字符来表示在搜索字符串中的相应的字符,而不是它们在正则表达式中的相应的特殊含义。要达到这个目的,你只需要在前面添加一个反斜杠:

$$$ //匹配"Show me the $$$!"

  方括号可以用来定义一组可以匹配的字符。例如,下面的正则表达式可以匹配1到5的任何数字。

[12345] //匹配"1"和"3",但不匹配"a"或"12"

  数字和字母的范围也可以这样表示:

[1-5] //和前面一样
[a-z] //匹配所有小写字母
[0-9a-zA-Z] //匹配所有字母或数字

  字符?、+和*也有其各自特殊的含义。具体地说,?表示“前面的字符是可选的”,+表示“前面的字符重复一次或多次”,而*表示“前面的字符重复零次或多次”。

bana?na //匹配"banana"和"banna",
//但不匹配"banaana".
bana+na //匹配"banana"和"banaana",
//但不匹配banna".
bana*na //匹配"banna"、"banana"和"banaaana",
//但不匹配"bnana".
^[a-zA-z]+$ //匹配所有由仅由一个或多个字母组成的字符串。

  圆括号用来将字符组合起来作为一个整体来应用?、+和*。

ba(na)+na //匹配"banana"和"banananana",
//但不匹配"bana"或"banaana".

  这儿有一些代码用来在正则表达式中匹配特殊的字符:
\n //匹配一个换行符
\. //匹配除了换行符以外的所有其它字符
\r //匹配一个回车符
\f //匹配一个制表符

  在正则表达式中还有许多其它的特殊代码和语法,这些你都可以从一些参考书中获得(例如我们上面提到的)。现在,要达到我们的目的,这些已经足够了。

  用正则表达式进行字符串的替换

  使用一个我们刚刚学习过的带正则表达式的ereg或eregi,我们可以很容易地判断在给出的字符串中是否包括标志。然而,我们想要做的是找出这些标志并将其替换成相应的HTML标志。要完成这一功能,我们需要PHP提供的另两个正则表达式函数:ereg_replace和eregi_replace。

  ereg_replace,和ereg一样,接受一个正则表达式和一个文本字符串,并试图将二者进行匹配。然而,此外,ereg_replace还接受第二个文本字符串,并用其替换第一个文本字符串中每一个和正则表达式匹配的部分。

ereg_replace的语法如下所示:

$newstring = ereg_replace(<regexp>, <replacewith>, <oldstring>);

  在这里,<regexp>是一个正则表达式,<replacewith>是我们用来替换<regexp>在<oldstring>中匹配的部分的字符串。这个函数返回替换后的字符串。在上面的代码中,新的字符串被存储在$newstring中。

  eregi_replace,和我们预想的一样,基本上等同于ereg_replace,只是它要匹配时不考虑字母的大小写。

现在我们已经准备好建立我们自己的标志语言了。

  黑体和斜体

  让我们首先实现建立文本的黑体和斜体的标志。我们定义[B]开始黑体文本,而[EB]结束一个黑体文本。明显地,我们需要将[B]替换为<B>,而将[EB]替换为</B>。使用eregi_replace这将变得很简单:

$joketext = eregi_replace("[b]", "<B>", $joketext);
$joketext = eregi_replace("[eb]", "</B>", $joketext);

  请注意,因为[在正则表达式中通常是指一组可接受的字符的开始,所以我们在前面加了一个反斜杠以去除其特殊的含义。而没有了[,]也不再具有特殊的含义,因此不需要反斜杠,当然,如果你想做得更彻底,你也可以在前面添加一个反斜杠。

  另外还请注意,我们在这里使用的是eregi_replace,它是对大小写不敏感的,因此[B]和[b]在我们自己的标志语言会起到同样的作用。

  斜体可以通过同样的方法实现:

$joketext = eregi_replace("[i]", "<I>", $joketext);
$joketext = eregi_replace("[ei]", "</I>", $joketext);

  分段

  要实现分段和前面我们实现黑体和斜体一样,甚至还更为简单。因为用户在将内容录入到表单域中时,会使用回车键来格式化文本,我们使用单个的换行符()来代表换行(<BR>),而两个换行符(
)则代表分段(<P>)。当然,由于PC会用一对换行-回车符()来表示新的一行,我们首先需要删除回车符,代码将是这样的:

//删除回车符
$joketext = ereg_replace("\r","",$joketext);
//处理分段
$joketext = ereg_replace("\n\n","<P>",$joketext);
//处理换行
$joketext = ereg_replace("\n","<BR>",$joketext);

这就是我们想要的!现在文本可以象用户所预期的那样分段了,而他们甚至不需要学习任何特殊的标志。

  超链接

  虽然在笑话的内容中支持超链接似乎很可笑,但在其它应用程序中经常会有这种应用。超链接要比简单地将一些代码转换成HTML标志稍微复杂一些。因为我们同时要输出URL和链接显示的内容。

  这里,我们需要用到ereg_replace和eregi_replace的另一个特征了。通过圆括号将正则表达式分成几部分,你可以“获得”匹配的文本中相应的部分并在替代的字符串中用代码\n表示,这里n中的1表示正则表达式第一个圆括号中的部分,2表示第二个,一直到9表示第9个。请先参考下面的代码:

$text = "banana";
$text = eregi_replace("(.*)(nana)", "21", $text);
echo($text); // outputs "nanaba"

  在上面的代码中,替代字符串中的\1由ba取代,这对应于正则表达式中的(.*)(零个或更多个非换行符)。\2由nana取代,这对应于正则表达式中的(nana)。

  在建立我们的超链接时可以使用同样的规则。让我们先从一个简单的连接开始,这里链接的文本同时也是URL。我们需要支持下面的语句:

Visit [L]http://www.php.net/[EL].

  我们想要输出的相应的HTML代码是这样的:

Visit <A HREF="http://www.php.net/">http://www.php.net/</A>.

  首先,我们需要一个正则表达式以匹配这种格式的链接。这个正则表达式将是这样的:

\[L][-_./a-zA-Z0-9!&%#?,'=:~]+\[EL]

  我们又在[L]和[EL]的前面用了反斜杠,这标志着它们将按字面意思解释。我们使用了方括号列出了我们认为可以作为URL的一部分的所有字符。在这个方括号后面我们使用了一个+表示URL可以由其中的一个或多个字符组成。

  要输出我们的链接,我们需要取出URL同时作为A标签的HREF属性以及链接的文字来输出。要取出URL,我们需要在我们的正则表达式的相应的部分上加上圆括号:

\[L]([-_./a-zA-Z0-9!&%#?,'=:~]+)\[EL]

  于是我们用下面的代码对链接进行转换:

$joketext = ereg_replace("\[L]([-_./a-zA-Z0-9!&%#?,'=:~]+)\[EL]",
"<AHREF=\"\\1\">\\1</A>", $joketext);

  请注意我们在链接的HTML代码中的双引号前面使用了反斜杠以防止PHP将其误以为是替代字符串的起止引号。总之,\1被这个链接的URL所取代,而输出正是我们所期望的!

  我们还希望支持这样的超链接,那就是链接的文字和他们的URL不一样。这样的链接的格式如下所示:

Check out [L=http://www.php.net/]PHP[EL].

  这是我们的正则表达式:

\[L=([-_./a-zA-Z0-9!&%#?,'=:~]+)]([-_./a-zA-Z0-9!&%#?,'=:~]+)\[EL]

  相当混乱,是吗?先不要去管它,你会明白这正是你所需要的,你会同时取出这个链接的URL(\1)和文字(\2)。执行这个转换的PHP代码将是这样的:

$joketext = ereg_replace("\[L=([-_./a-zA-Z0-9!&%#?,'=:~]+)]" .
"([-_./a-zA-Z0-9!&%#?,'=:~]+)\[EL]",
"<AHREF=\"\\1\">\\2</A>", $joketext);

  内容的分页

  虽然也许没有笑话会长得要分页显示,但是许多内容驱动的站点防止内容过长的最好的方法就是将其分页显示。使用PHP的另一个正则表达式的函数可以很容易地实现这种功能。

  split是这样一个函数:它接受一个正则表达式和一个文本字符串并通过字符串对正则表达式的匹配将其分开,并存储到数组中。参看下面的例子:

$regexp = "[ ]+"; //一个或更多的空白字符
$text = "This is a test.";
$textarray=split($regexp, $text);
echo("$textarray[0]<BR>"); //输出"This<BR>"
echo("$textarray[1]<BR>"); //输出"is<BR>"
echo("$textarray[2]<BR>"); //输出"a<BR>"
echo("$textarray[3]<BR>"); //输出"test.<BR>"

  如果用一个[PAGEBREAK]标志取代这里的空白字符,并用显示我们感兴趣的页面(在页面请求中通过$page变量传递)取代显示全部的划分结果,我们就可以成功将内容分页。

//If no pagespecified, default to the
//first page($page=0)
if ( !isset($page) ) $page=0;
//Split the text into an array of pages
$textarray = split("[PAGEBREAK]", $text);
//Select the page we want
$pagetext = $textarray[$page];

  当然,我们需要提供一些方法以在页面之间进行切换。我们可以在当前页的顶端放置一个到上一页的链接,在底端放置一个到下一页的链接。

  如果这是第一页,我们就不需要一个到上一页的链接。我们知道如果$page变量等于零就意味着当前页是第一页。同样地,在最后一页我们也不需要到下一页的链接。要确定当前页是不是最后一页,我们需要一个新的名为count的PHP的函数,它以一个数组为参数并返回数组的数目。通过页面的数组,count会告诉我们这儿有多少页。如果有10页,那么$textarray[9]就是最后一页。这样,如果$page等于count($textarray)减一就说明当前页是最后一页。

  有关我们的页面切换的链接的代码将是这样的:

if( $page != 0 ) {
$prevpage = $page-1;
echo("<P><AHREF=\"$PHP_SELF?id=$id&page=$prevpage\">" .
"Previous Page</A></P>");
}
//Output page content here...
if ( $page < count($textarray)-1 ){
$nextpage = $page+1;
echo("<P><AHREF=\"$PHP_SELF?id=$id&page=$nextpage\">" .
"Next Page</A></P>");
}

  进行组装

  完整的用来输出我们的笑话的内容的代码(包含了对所有特殊字符和自定义标志的转换)如下所示:

<!--joke.php-->
...
//Get the joketext from the database
$joke = mysql_query("SELECT JokeText FROM Jokes
WHERE ID=$id");
$joke = mysql_fetch_array($joke);
$joketext = $joke["JokeText"];
//Filter out HTML code
$joketext = htmlspecialchars($joketext);
//If no page specified, default to the
//firstpage($page=0)
if ( !isset($page) ) $page=0;
//Split the text into an array of pages
$textarray = split("[PAGEBREAK]", $joketext);
//Select the page we want
$joketext = $textarray[$page];
//Bold and italics
$joketext = eregi_replace("[b]", "<B>", $joketext);
$joketext = eregi_replace("[eb]", "</B>", $joketext);
$joketext = eregi_replace("[i]", "<I>", $joketext);
$joketext = eregi_replace("[ei]", "</I>", $joketext);
//Paragraphs and line breaks
$joketext = ereg_replace("\r","",$joketext);
$joketext = ereg_replace("\n\n","<P>",$joketext);
$joketext = ereg_replace("\n","<BR>",$joketext);
//Hyperlinks
$joketext = ereg_replace("\[L]([-_./a-zA-Z0-9!&%#?,'=:~]+)\[EL]",
"<AHREF=\"\\1\">\\1</A>", $joketext);
$joketext = ereg_replace("\[L=([-_./a-zA-Z0-9!&%#?,'=:~]+)]" .
"([-_./a-zA-Z0-9!&%#?,'=:~]+)\[EL]",
"<AHREF=\"\\1\">\\2</A>", $joketext);
if( $page != 0 ) {
$prevpage = $page-1;
echo("<P><AHREF=\"$PHP_SELF?id=$id&page=$prevpage\">" .
"Previous Page</A></P>");
}
if ( $page < count($textarray)-1 ){
$nextpage = $page+1;
echo("<P><AHREF=\"$PHP_SELF?id=$id&page=$nextpage\">" .
"Next Page</A></P>");
}
...

  不要忘记提供一份相关的文档,以使得提交笑话的用户知道可以使用那些标志以及怎么用这些标志。

  内容的自动提交

  如果我们花费了这么多时间和精力的内容管理系统,仅仅是供站点管理员一个人使用,那就有点可惜了。而且,虽然对于一个站点管理员来说,在维护站点的内容时不再需要编辑HTML是相当方便的,但是他还是需要将提交的文档录入到"Add NewJoke"表单中,并将文本格式转变为我们上面开发的自已的文本格式,这至少是一个机械的单调乏味的工作。

  如果我们把"AddNewJoke"表单交到普通的网站访问者手中,那又怎么样呢?让我们回忆一下,实际上在第四章中我们曾经提供了一个表单让用户来提交他们自己的笑话。当时,这是用来演示如何在PHP脚本中使用INSERT语句的。但是我们很快又因为安全性的考虑取消了这项功能。毕竟,谁愿意将自己的站点向所有试图毁坏它的人都开放呢?

  但是接受笑话的提交并不意味着提交的内容立即在站点中显示出来。我们可以向Jokes表中添加一个新的名为Visible的数据列,这个列可以有两个值:'Y'和'N'。刚提交的笑话可以自动设置为Visible='N',而在对Jokes表的查询中添加WHERE Visible='Y'就可以不显示这些笑话。Visible='N'的笑话仅仅是在数据库中等候内容管理者的处理,只有管理者可以将其设置为可见,或者最终将其删除。

  对于这样的一个在两个值中取一个值,而其中有一个默认值的数据列,可以使用称为ENUM的MySQL数据列类型:

mysql> ALTER TABLE Jokes ADD COLUMN
    -> Visible ENUM('N', 'Y') NOT NULL;

  圆括号中的第一个数值(这里是'N')是默认值,使用INSERT语句新插入记录时,如果没有指定这个列的数值,默认值将自动起作用。

  因为新笑话是不公开的,唯一剩下来的安全问题就是作者的识别。我们想要搞清楚一个笑话是由数据库中哪一个作者提交的,但是在"AddNewJoke"表单中使用以前的那种下拉菜单来决定作者显然是不太合适的,因为这可能在作者之间发生混淆。在这种情况下,明显地我们可以使用用户名/口令识别机制。

  在Authors数据表中存储口令和增加一个其它的列没什么不同。然后你可以要求作者在向一个数据库提交一个笑话时相应地输入他的email地址和口令。你也许还需要一个同样的登录过程以允许作者更改它自己的详细资料(姓名、email地址等等)。你也许甚至还可以给每一个作者一个“控制中心”,在那里他可以显示他提交到站点的笑话的当前状态。

  结语

  对前面所讲的内容提交系统进行一下认真的钻研,你将会掌握所有必须的技巧,这样你就可以建立一个你自己的系统。你是不是想要让用户能够对站点中的笑话进行评价呢?是不是可以考虑让作者更改它提交的笑话,并在管理者对这种更改进行确认?所有的这些只需要你有足够的想象力。

  通过这一章的学习,你已经学会了建立你自己的数据库驱动的站点的基本技巧。在剩下的章节中,我们会学习一些更复杂的问题以使你的站点能够工作得更好。当然,我们也会看到有关PHP和MySQL的更多的令人兴奋的功能。

  在第八章中,我们将对我们的笑话数据库进行一些改进,并认真研究一下MySQL服务的维护和管理。我们会学习到如何对我们的数据库进行备份(对于一个基于Web的应用,这是一个需要认真对待的问题!),如何管理MySQL的用户和它们的口令,当然我们还会看到如果你忘记了你的口令,如何登录到一个MySQL服务。(XMW)

上一页  [1] [2] [3] [4] [5] [6] [7] [8] [9] [10]  ...  下一页 >> 

打印本文 打印本文  关闭窗口 关闭窗口