技术开发 频道

PHP开发经典教程:垃圾处理

IT168 专稿等待梦醒时分 

    也许你之前已经听说过GIGO这个术语了。 

    如果你没有听说过,那么它是“Garbage In、Garbage Out”的代表,而且它是计算机编程的一个基本事实:如果你向你的程序提供无效的输入,那么你几乎可以肯定将会得到无效的输出。而且无论你以何种方式去除该无效输出,对一个想要引起注意的程序员来讲,无效输出不是一件好事情。 

    倘若你认为我在夸张,那么让我给你展示一个简单的例子。考虑一个允许用户输入需要的贷款数额、财政状况和利息率的在线贷款计数器。让我们假定该应用程序没有包含任何错误检查而且用户决定在Term字段中输入幻数0。
 
    你可以想像得到结果会是什么样子。在一系列内部计算之后,应用程序将会由于试图用0除可付的总数而结束。接下来的大量的丑陋的错误消息实在不值得讨论,如果开发人员在设计应用程序时具有足够的远见以包含一个输入确认程序的话,那么这种情况本应该可以避免的,这一点值得注意。 

    本故事的寓意是什么呢?如果你非常认真的将PHP用于Web开发,那么你必须学会的最重要的事情之一就是如何验证用户输入和处理潜在的不安全数据。这样的输入验证是开发人员所能构建到应用程序中的最重要的安全措施之一,而且不这样做在程序遇到无效或者被破坏的数据时会引发严重的问题或者甚至导致应用程序停止执行。
 
    那就是PHP 101的这个版起作用的地方。在接下来的几段中,我将会向你展示一些基本的技巧以验证用户输入、在“不良”数据破坏你的计算和数据库之前捕获它而且以优雅、可理解和没有危险的方式通知用户。为了准备本次练习,我建议你播放John Lennon的“Imagine”的CD,在你的内心中对所有的人都充满了平和和善意,然后做一些平静的深呼吸。你呼吸完之后,我们就可以开始了。空容器 

    本教程假定将要被验证的用户输入通过一个Web表单来获得。这不是PHP脚本可以获得用户数据的唯一方法;然而,它是最常见的方法。如果你的PHP应用程序需要验证命令行输入,那么我建议你阅读我的关于PEAR Console_Getopt类的文章,你可在http://www.zend.com/pear/tutorials/Console-Getopt.php中仔细阅读它。
 
    将诸如JavaScript或者VBScript的客户端脚本语言用于客户端表单验证是一种惯例。然而,这种客户端验证的类型不是十分可靠的。因为你无法控制客户端,因此如果用户关闭其浏览器中的JavaScript,那么你为了保证用户没有输入不相关的数据所做的全部努力变得不相关了。那就是为什么最有经验的开发人员使用客户端和服务器端两种验证方法的原因。服务器端验证涉及检查通过PHP脚本被递交给服务器端的值然后当输入不正确时采取适当的动作。
 
    让我们以最常见的输入错误开始:一个漏掉了其所要求的必须的值的表单字段。请看这个例子:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF'] ?>' method = 'post'> Which sandwich filling would you like? <br /> <input type = 'text' name = 'filling'> <br /> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // set database variables $host = 'localhost'; $user = 'user'; $pass = 'secret'; $db = 'sandwiches'; // get user input $filling = mysql_escape_string($_POST['filling']); // open connection $connection = mysql_connect($host, $user, $pass) or die('Unable to connect!'); // select database mysql_select_db($db) or die('Unable to select database!'); // create query $query = 'INSERT INTO orders (filling) VALUES ("$filling")'; // execute query $result = mysql_query($query) or die("Error in query: $query. ".mysql_error()); // close connection mysql_close($connection); // display message echo "Your {$_POST['filling']} sandwich is coming right up!"; } ?> </body> </html>
    上面例子中,可以很明显的看到,递交没有输入任意数据的表单将会导致一条空记录被增加到数据库中(假定目标表格中没有NOT NULL约束)。为了避免这种情况,确认实际上表单包含了有效数据然后执行INSERT查询是重要的。下面是其如何实现的:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF'] ?>' method = 'post'> Which sandwich filling would you like? <br /> <input type = 'text' name = 'filling'> <br /> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check for required data // die if absent if (!isset($_POST['filling']) || trim($_POST['filling']) == '') { die("ERROR: You can't have a sandwich without a filling!"); } else { $filling = mysql_escape_string(trim($_POST['filling'])); } // set database variables $host = 'localhost'; $user = 'user'; $pass = 'secret'; $db = 'sandwiches'; // open connection $connection = mysql_connect($host, $user, $pass) or die('Unable to connect!'); // select database mysql_select_db($db) or die('Unable to select database!'); // create query $query = 'INSERT INTO orders (filling) VALUES ("$filling")'; // execute query $result = mysql_query($query) or die("Error in query: $query. ".mysql_error()); // close connection mysql_close($connection); // display message echo "Your {$_POST['filling']} sandwich is coming right up!"; } ?> </body> </html>
    这里的错误检查既简单又符合逻辑:trim()函数用于裁剪字段值首部和尾部的空格,然后该字段和一个空字符串进行比较。如果该匹配为真,那么该字段空值递交,然后脚本在MySQL被影响之前以一个错误消息结束。
 
    尤其是在新手中一个常见的错误就是以一个告诉你一个变量是否为空的对PHP的empty()函数的调用来代替isset()和trim()的结合。这通常情况下不是一个好主意,因为empty()有一个致命的缺陷:即使一个变量包含数值0,它也会返回真。下列这个简单的例子对此做了解释说明:
<?php // no data, returns empty $data = ''; echo empty($data) ? "$data is empty" : "$data is not empty"; echo "<br />\n"; // some data, returns not empty $data = '1'; echo empty($data) ? "$data is empty" : "$data is not empty"; echo "<br />\n"; // some data, returns empty $data = '0'; echo empty($data) ? "$data is empty" : "$data is not empty"; ?>
    所以,如果你的表单字段只允许持有非空、非零数据,那么empty()是一个验证数据的好选择。但如果你的字段的有效值的范围包括数值0,那么请用isset()和trim()的结合。类型不一致 

    因此现在你知道了如何捕获最基本的错误(丢失数据)然后在发生任何损害之前停止脚本的执行。但是如果数据存在,不过是错误类型或者大小,那么该怎么办呢?你的“丢失数据”测试不会被触发,但你的计算和数据库仍然会受到影响。很明显,你需要增加进一步的安全保护层,在这个安全保护层里,用户输入的数据类型也得到了验证。 

    下面是一个解释说明这一点的例子:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF']?>' method = 'post'> How many sandwiches would you like? (min 1, max 9) <br /> <input type = 'text' name = 'quantity'> <br /> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check for required data // die if absent if (!isset($_POST['quantity']) || trim($_POST['quantity']) == '') { die ("ERROR: Can't make 'em if you don't say how many!"); } // check if input is a number if (!is_numeric($_POST['quantity'])) { die ("ERROR: Whatever you just said isn't a number!"); } // check if input is an integer if (intval($_POST['quantity']) != $_POST['quantity']) { die ("ERROR: Can't do halves, quarters or thirds... I'd lose my job!"); } // check if input is in the range 1-9 if (($_POST['quantity'] < 1) || ($_POST['quantity'] > 9)) { die ('ERROR: I can only make between 1 and 9 sandwiches per order!'); } // process the data echo "I'm making you {$_POST['quantity']} sandwiches. Hope you can eat them all!"; } ?> </body> </html>
    请注意,一旦我确认了字段包含一些数据,那么我增加一系列的测试以确认该数据满足数据类型和范围约束。首先,我用is_numeric()函数检查值是否是数值。该函数测试一个字符串以了解它是否是一个数值字符串(也就是只包含数值的字符串)。
假定你得到的是一个数字,那么下一个步骤就是确认它是一个1到9之间的整型值。为了测试该数字是否是一个整型,我使用intval()函数从该值中取出其整数部分,然后用其和值本身做比较测试。浮点型值(比如2.5)将会测试失败;而整数值将会通过测试。在放行该值之前的最后一个步骤是检查其是否落在1到9之间。这个步骤很容易通过一系列不等测试来完成。 

    当涉及该主题时,strlen()函数也是值得提及的,该函数返回字符串的长度。它使得确认表单输入不会超出一定的长度可以非常容易地做到。下列例子演示了其是如何实现的:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF']?>' method = 'post'> Enter a nickname 6-10 characters long: <br /> <input type = 'text' name = 'nick'> <br /> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check for required data // die if absent if (!isset($_POST['nick']) || trim($_POST['nick']) == '') { die ('ERROR: Come on, surely you can think of a nickname! How about Pooky?'); } // check if input is of the right length if (!(strlen($_POST['nick']) >= 6 && strlen($_POST['nick']) <= 10)) { die ("ERROR: That's either too long or too short!"); } // process the data echo "I'll accept the nickname {$_POST['nick']}, seeing as it's you!"; } ?> </body> </html>
    这里,strlen()函数用于验证字符串输入既不会太长也不会太短。它也是确认输入数据满足你的数据库的字段长度约束的一种简便方法。举例而言,如果你有一个MySQL的VARCHAR(10)字段,长度上超过10个字符的字符串将会被缩减。strlen()函数可以在本例子中作为一个早期告警系统,该系统通知用户长度不匹配从而避免数据破坏。约会博弈 

    验证日期是输入验证的另外一个重要方面。在给出一系列下列列表框或者自由的文本字段情况下,用户选择诸如2005年2月29日或者2005年4月31日这样的均为无效的日期非常容易。因此在使用用户提供的日期值计算之前检查其有效性非常重要。
 
    在PHP中,该任务比在其他语言中要简单得多,这是因为checkdate()函数。该函数接受三个参数:月、天和年,然后返回一个指示日期是否有效的布尔值。下列例子以实际操作演示了该函数的使用:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF']?>' method = 'post'> Enter your date of birth: <br /><br /> <select name = 'day'> <?php // generate day numbers for ($x = 1; $x <= 31; $x++) { echo "<option value = $x>$x</option>"; } ?> </select> <select name = 'month'> <?php // generate month names for ($x = 1; $x <= 12; $x++) { echo "<option value=$x>".date('F', mktime(0, 0, 0, $x, 1, 1)).'</option>'; } ?> </select> <select name = 'year'> <?php // generate year values for ($x = 1950; $x <= 2005; $x++) { echo "<option value=$x>$x</option>"; } ?> </select> <br /><br /> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check if date is valid if (!checkdate($_POST['month'], $_POST['day'], $_POST['year'])) { die("ERROR: The date {$_POST['day']}-{$_POST['month']}-{$_POST['year']} doesn't exist!"); } // process the data echo "You entered {$_POST['day']}-{$_POST['month']}-{$_POST['year']} - which is a valid date."; } ?> </body> </html>
    试着输入一个无效日期,然后看看PHP是如何让你操作该无效日期的,不是很酷吗? 

    如果你在MySQL表格中储存日期输入,那么你会有趣的注意到MySQL在接受一个DATE、DATETIME或者TIMESTAMP类型的值之前不执行其自身的任何严格的日期检查。相反,它期望开发人员在其自身的应用程序中集成日期验证。MySQL愿意做的最多是当它遇到一个明显不合法的值时将其转化为一个零值(根本就没有帮助)。阅读更多的关于这点信息请到http://dev.mysql.com/doc/mysql/en/datetime.html。 

    当我们关注这个主题时,让我们更多的讨论一些关于多选表单基本元素(比如,下拉列表框和单选按扭)。在强制做出选择的情况时,开发人员必须确保至少可用选择中的一个已经被用户选择。这主要涉及isset()(用于多选列表框)、is_array()和sizeof()函数的灵活使用。下一个例子解释说明了这点:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF'] ?>' method = 'post'> Pizza base: <br /> <input type = 'radio' name = 'base' value = 'thin and crispy'>Thin and crispy <input type = 'radio' name = 'base' value = 'deep-dish'>Deep-dish <br /> Cheese: <br /> <select name = 'cheese'> <option value = 'mozzarella'>Mozzarella</option> <option value = 'parmesan'>Parmesan</option> <option value = 'gruyere'>Gruyere</option> </select> <br /> Toppings: <br /> <select multiple name = 'toppings[]'> <option value = 'tomatoes'>Tomatoes</option> <option value = 'olives'>Olives</option> <option value = 'pepperoni'>Pepperoni</option> <option value = 'onions'>Onions</option> <option value = 'peppers'>Peppers</option> <option value = 'sausage'>Sausage</option> <option value = 'anchovies'>Anchovies</option> </select> <br /> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check radio button if (!isset($_POST['base'])) { die('You must select a base for the pizza'); } // check list box if (!isset($_POST['cheese'])) { die('You must select a cheese for the pizza'); } // check multi-select box if (!is_array($_POST['toppings']) || sizeof($_POST['toppings']) < 1) { die('You must select at least one topping for the pizza'); } // process the data echo "One {$_POST['base']} {$_POST['cheese']} pizza with "; foreach ($_POST['toppings'] as $topping) echo $topping.", "; echo "coming up!"; } ?> </body> </html>
    这里没有什么令你十分伤脑筋的(isset()函数只是检查确认选项集合中是否至少一个已经被选择,如果没有,则打印一条错误消息)。请注意多选列表框是如何被验证的:当表单被递交后,这里所做的选择被放置于一个数组中,然后PHP的is_array()和sizeof()函数被用于测试该数组然后确定该数组包含至少一个元素。受欢迎的人 

    到目前为止,验证程序一直都是十分简单的(检查日期、检查所需要的值及检查数据类型或大小)。然而,你常常需要更复杂的验证(例如)以测试一个电子邮件地址或者电话号码是否以正确格式来书写。为了完成这些更复杂的验证任务,聪明的PHP程序员求助于正则表达式。 

    正则表达式是一种用于模式匹配和替换的强大工具。它们一般和几乎所有的基于UNIX的工具(包括如vi的编辑器、如Perl和PHP的脚本语言以及如awk和sed这样的shell程序)相关联。你甚至会在客户端脚本语言(比如JavaScript)中找到它们。正如圣母玛利亚一样,它们的普及已经超越了语言和领土的边界。 

    正则表达式允许你使用一组特殊字符来构造模式。这些模式然后可以和文件中的文本、输入到应用程序中的数据或者Web站点的用户填写到表单的输入做比较。适当的程序代码可以取决于这些比较是否存在匹配而被执行。因而正则表达式在Web应用程序中的决策程序中起着重要的作用。 

    一个正则表达式可以像如下所示一样简单: 

    这个所做的就是匹配它所应用到的文本中的模式love。和生活中许多其他事情一样,关注模式要比关注概念更简单,但其既不在这儿也不在那儿。 

    一些更加复杂的正则表达式是什么样子呢?模式/fo+/将会匹配单词fool、footsie和four-seater。试试下面的:
<?php $array = array('fool', 'footsie', 'four-seater'); foreach ($array as $element) { if (preg_match('/fo+/', $element)) echo "$element gives a match<br />\n"; } ?>
    虽然它是一个十分愚蠢的例子,但是你必须承认它的现实性(毕竟,除了热恋中的傻瓜谁会在四座的车辆中调情)。
 
    上述正则表达式中所用的符号+被称为元字符(当用于模式中时具有特殊含义的字符)。+元字符用于匹配其前述字符的一次或者多次出现(在上面的例子中,这种匹配就是字母f后面紧跟着一个或者多个字母o)。 

    和+元字符类似的是*和?字符,它们分别用于匹配之前字符的零次或者更多次的出现以及之前字符的零次或者一次出现。因此/ab*/将会匹配aggressive、absolutely和abbey,而/Ron?/将会匹配Ronald、Roger和Roland,但是和Rimbaud或Mona不匹配。 

    如果所有这些看上去有一些不太严密,那么你也可以为匹配数指定范围。例如,正则表达式/ron{2,6}/将会匹配ronny和ronnnnnny!,但不匹配ron。大括号内的数字代表了匹配范围的下限和上限值;你可以忽略上限值以用于开端式范围的匹配。
正如你可以为待匹配的字符数指定一范围一样,你也可以指定字符的范围。例如。范围/[A-Z]/将匹配包含大写文字字符的任意字符串,而/[a-z]/将匹配任意小写文字字符,以及/[0-9]/将匹配0和9之间的所有数字。 

    使用这三种字符范围,很容易创建一个正则表达式以匹配一个有序的文字数字混合区段:/([a-z][A-Z][0-9])+/将会匹配一个以相同的字符类型序列排列的文字数字混合字符串(比如,aB2),但无法匹配abc。请注意围绕模式的圆括号(和你可能想像的相反,这些括号在那里不是纯粹来迷惑你的;当将正则表达式片段组合聚合在一起时它会派的上用场的)。 

    当然,这只是正则表达式冰山的一角。这里有更多的元字符以及数不清的组合方法以创建强大的模式匹配规则。为了更深入的介绍,请访问http://www.melonfire.com/community/columns/trog/article.php?id=2网站、http://it.metr.ou.edu/regex/处的参考页面以及http://www.php.net/manual/en/ref.regex.php和http://www.php.net/manual/en/ref.pcre.php处的PHP手册。你可以在http://www.regexlib.com/处找到一系列用于各种应用的正则表达式例子。一个模式的出现 

    在PHP中,正则表达式匹配与ereg()(ereg()函数也有一个被称为eregi()的不分大小写的版本)或者preg_match()函数一起发生。这些语义之间互相稍微不同的函数可以用于与之前定义的模式做比较来测试用户输入且因此在无效数据进入你的应用程序之前捕获它。当然,PHP正则表达式的最常见的例子就是电子邮件地址验证器…而且因为我受传统惯例限制,所以那也是我的第一个例子。请看:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF'] ?>' method = 'post'> Email address: <br /> <input type = 'text' name = 'email'> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check email address if (!ereg('^([a-zA-Z0-9])+([\.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-]+)*\.([a-zA-Z]{2,6})$', $_POST['email'])) { die("Dunno what that is, but it sure isn't an email address!"); } // process the data echo "The email address {$_POST['email']} has a valid structure. Doesn't mean it works!"; } ?> </body> </html>
    这里,模式/^([a-zA-Z0-9])+([\.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-]+)*\.([a-zA-Z]{2,6})$/(试着快速说出它!)是一个与用于user@host电子邮件地址的基本格式相匹配的正则表达式。与这个模式相匹配的输入将会被接受;与这个模式不匹配的输入将会触发一个刺耳的报警。请注意,ereg()函数不需要和更快的preg_match()函数一样的分隔符,该preg_match()函数如果在表达式的每一末端没有得到/,其将会抱怨。 

    下面是另外一个例子,该例子较佳用于测试国际电话号码:
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF'] ?>' method = 'post'> Phone number (with country/area codes): <br /> <input type = 'text' name = 'tel'> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check phone number if (!preg_match('/^(\+|00)[1-9]{1,3}(\.|\s|-)?([0-9]{1,5}(\.|\s|-)?){1,3}$/', $_POST['tel'])) { die ("Dunno what that is, but it sure isn't an international phone number!"); } // process the data echo "{$_POST['tel']} has a valid structure. Doesn't mean it works!"; } ?> </body> </html>
    如果你稍微玩过这个,你将会看到,例子会接受任意数字+1.212.1234.4567、+44 1865 123456和0091 11 1234 5678...,即使它们每一个格式都不同。这主要是因为我在正则表达式中使用了|分隔符,该分隔符如逻辑“或”一样发挥作用然后使得创建一个内部支持选择的模式变得可能。明显地,你可以在需要时加强模式。举例而言,如果你在印度而且你的应用只支持印度电话号码,那么你可以修改该模式以使得它要求以91(印度的国家代码)作为号码的前两位数字。
 
    试着使用正则表达式来重写我们早前的验证程序是有趣的。下面是本教程之前例子之一的替代版本,重新编写以使用ereg()函数代替intval()、is_numeric()和isset()函数。
<html> <head></head> <body> <?php if (!isset($_POST['submit'])) { ?> <form action = '<?php $_SERVER['PHP_SELF'] ?>' method = 'post'> How many sandwiches would you like? (min 1, max 9) <br /> <input type = 'text' name = 'quantity'> <br /> <input type = 'submit' name = 'submit' value = 'Save'> </form> <?php } else { // check for required data if (!ereg('^[1-9]$', $_POST['quantity'])) { die('ERROR: That is an invalid quantity!'); } // process the data echo "I'm making you {$_POST['quantity']} sandwiches. Hope you can eat them all!"; } ?> </body> </html>
    请注意,这里单独一个正则表达式如何代替了早期版本的四次独立测试,以及结果是如何更加简洁的。正是这种强大的能力和灵活性使得正则表达式成为输入验证工具中如此重要的组成部分。回到课堂 

    既然你掌握了输入验证的基本部分,那么你应该清楚这是一项你将会经常执行的任务。因此创建一个可重用的用于输入验证的函数库就变得有意义了,你可以在一个应用程序每次需要其输入被检查以发现错误时使用该函数库。那就是我下一步将要做的事情(创建一个PHP类,该类展示出用于数据验证和错误处理的基本的对象方法,然后使用该类验证一个表单)。 

    下面是类的定义class.formValidator.php,它是为PHP 5编写的。你只需去除类方法的public和private标记然后使得私有的errorList属性作为一个变量就可以使其适合PHP 4。下列脚本的其余部分可运行于任意PHP版本下。
<?php // PHP 5 // class definition // class encapsulating data validation functions class formValidator { // define properties private $_errorList; // define methods // constructor public function __construct() { $this->resetErrorList(); } // initialize error list private function resetErrorList() { $this->_errorList = array(); } // check whether input is empty public function isEmpty($value) { return (!isset($value) || trim($value) == '') ? true : false; } // check whether input is a string public function isString($value) { return is_string($value); } // check whether input is a number public function isNumber($value) { return is_numeric($value); } // check whether input is an integer public function isInteger($value) { return (intval($value) == $value) ? true : false; } // check whether input is alphabetic public function isAlpha($value) { return preg_match('/^[a-zA-Z]+$/', $value); } // check whether input is within a numeric range public function isWithinRange($value, $min, $max) { return (is_numeric($value) && $value >= $min && $value <= $max) ? true : false; } // check whether input is a valid email address public function isEmailAddress($value) { return eregi('^([a-z0-9])+([\.a-z0-9_-])*@([a-z0-9_-])+(\.[a-z0-9_-]+)*\.([a-z]{2,6})$', $value); } // check if a value exists in an array public function isInArray($array, $value) { return in_array($value, $array); } // add an error to the error list public function addError($field, $message) { $this->_errorList[] = array('field' => $field, 'message' => $message); } // check if errors exist in the error list public function isError() { return (sizeof($this->_errorList) > 0) ? true : false; } // return the error list to the caller public function getErrorList() { return $this->_errorList; } // destructor // de-initialize error list public function __destruct() { unset($this->_errorList); } // end class definition } ?>
    剥离该脚本的其他部分只剩其本质部分,该formValidator类包含两个主要的组成部分。
 
    第一个是接受待验证的数据的一系列的方法,测试该数据以确定其是否有效(然而,“有效性”可以定义于方法内部)然后返回一个布尔结果代码。下面是被支持的方法列表:

• isEmpty() –测试一个值是否为空字符串
• isString() –测试一个值是否是字符串
• isNumber()-测试一个值是否是数值型字符串
• isInteger()-测试一个值是否为整数
• isAlpha() –测试一个值是否只由字母字符组成
• isEmailAddress()-测试一个值是否是一个电子邮件地址
• isWithinRange()- 测试一个值是否落在一个数值范围内
• isInArray()-测试一个值是否存在于一个数组中。 

    很明显地,上述列表是不完全的(你应该按照你自己的要求自由对其进行添加)。 

    在本教程更早之前的例子中,我做了一些事情以使得当遇到输入错误时,数据验证程序将会以die()函数立即结束脚本的处理。在真实世界里,在第一次错误发生时如此突然的程序终止通常情况下不是一个好主意;相反,处理所有的用户输入,识别所有的错误然后为了用户立即修正这些错误将其列示出来会更加有效。 

    那就是该类的第二个组成部分所出现的地方。它是一个持有在验证过程中遇到的所有错误的列表的PHP数组和操作该结构的一些方法。下面是方法的列表:

• isError()-检查是否任何错误存在于错误列表中
• addError()-向错误列表中增加一个错误
• getErrorList()-取得当前的错误列表
• resetErrorList()-重置错误列表 

    此刻,这个对你来说看上去有些深奥。让我们跳入一个实际的例子然后所有的上述代码将会开始变得更加有意义。首先,我们需要一个简单的HTML表单:
<html> <head></head> <body> <b>Fields marked with * are mandatory</b> <form action = 'processor.php' method = 'post'> <b>Name*:</b> <br /> <input type = 'text' name = 'name' size = '15'> <p /> <b>Age*:</b> <br /> <input type = 'text' name = 'age' size = '2' maxlength = '2'> <p /> <b>Email address*:</b> <br /> <input type = 'text' name = 'email' size = '30'> <p /> <b>Sex*:</b> <br /> <input type = 'radio' name = 'sex' value = 'm'>Male <input type = 'radio' name = 'sex' value = 'f'>Female <p /> <b>Color*:</b> <br /> <select name = 'color'> <option value = ''>-select one-</option> <option value = 'r'>Red</option> <option value = 'g'>Green</option> <option value = 'b'>Blue</option> <option value = 's'>Silver</option> </select> <p /> <b>Insurance*:</b> <br /> <select name = 'insurance'> <option value = ''>-select one-</option> <option value = '1'>Basic</option> <option value = '2'>Enhanced</option> <option value = '3'>Premium</option> </select> <p /> <b>Optional features:</b> <br /> <input type = 'checkbox' name = 'options[]' value = 'PSTR'>Power steering <input type = 'checkbox' name = 'options[]' value = 'AC'>Air-conditioning <input type = 'checkbox' name = 'options[]' value = '4WD'>Four-wheel drive <input type = 'checkbox' name = 'options[]' value = 'SR'>Sun roof <input type = 'checkbox' name = 'options[]' value = 'LUP'>Leather upholstery <p /> <input type = 'submit' name = 'submit' value = 'Save'> </form> </body> </html>
现在,我们需要一个使用我新的formValidator对象的PHP脚本来处理通过该表单发送的输入。将下列代码保存为processor.ph:
<?php // include file containing class include('class.formValidator.php'); // instantiate object $fv = new formValidator(); // start checking the data // check name if ($fv->isEmpty($_POST['name'])) { $fv->addError('Name', 'Please enter your name'); } // check age and age range if (!$fv->isNumber($_POST['age'])) { $fv->addError('Age', 'Please enter your age'); } else if (!$fv->isWithinRange($_POST['age'], 1, 99)) { $fv->addError('Age', 'Please enter an age value in the numeric range 1-99'); } // check sex if (!isset($_POST['sex'])) { $fv->addError('Sex', 'Please select your gender'); } // check email address if (!$fv->isEmailAddress($_POST['email'])) { $fv->addError('Email address', 'Please enter a valid email address'); } // check color if ($fv->isEmpty($_POST['color'])) { $fv->addError('Color', 'Please select one of the listed colors'); } // check insurance type if ($fv->isEmpty($_POST['insurance'])) { $fv->addError('Insurance', 'Please select one of the listed insurance types'); } // check optional features if (isset($_POST['options'])) { if ($fv->isInArray($_POST['options'], '4WD') && !$fv->isInArray($_POST['options'], 'PSTR')) { $fv->addError('Optional features', 'Please also select Power Steering if you would like Four-Wheel Drive'); } } // check to see if any errors were generated if ($fv->isError()) { // print errors echo '<b>The operation could not be performed because one or more error(s) occurred.</b> <p /> Please resubmit the form after making the following changes:'; echo '<ul>'; foreach ($fv->getErrorList() as $e) { echo '<li>'.$e['field'].': '.$e['message']; } echo '</ul>'; } else { // do something useful with the data echo 'Data OK'; } ?>
    正如上述列表所说明的,我的formValidator()对象所展示的各种方法很容易验证用户的输入。在所有情况下,isEmpty()方法用于测试必需的字段是否已经被填写了,而isEmailAddress()和isWithinRange()方法用于更精确的验证。isInArray()方法(对检查框和多选列表非常有用)也是一种强制关联规则和连接特定选择的好方法。
 
    注意到上述所创建的formValidator类与表单或者表单的结果页面的可视化展现无关是重要的。它的方法仅仅测试发送给他们的输入然后返回结果码;结果码是如何被解释的完全取决于开发人员。在上面脚本中,foreach()循环对错误列表进行循环操作然后以无序列表的形式对其进行打印;然而,你可以以定制格式的方式在表格中显示错误信息或者将其写入到日志文件中。我将任你去实验这种可能性。 

    那就是PHP 101中本部分的内容。但是,别沮丧,我将很快回来而且下次,我将拾起我所交给你的一切然后使用他们来构建一个真实世界中的PHP/MySQLWeb应用。确保你不会错过那个!
0
相关文章