PHP7-学习手册-二-

65 阅读31分钟

PHP7 学习手册(二)

原文:Learn PHP 7

协议:CC BY-NC-SA 4.0

四、安全用户界面

Electronic supplementary material The online version of this chapter (doi:10.​1007/​978-1-4842-1730-6_​4) contains supplementary material, which is available to authorized users.

对于失败者来说,所有伟大的想法都是糟糕的想法。与已知的失败者一起测试一个新想法,以确保他们不喜欢它,这总是好的。”—迪尔伯特 97 年 11 月 18 日( http://dilbert.com/strips/comic/1997-11-18/ )

章节目标/学生学习成果

完成本章后,学生将能够:

  • 解释为什么用户输入必须在界面层和业务规则层进行验证
  • 解释为什么必须在业务规则层过滤用户输入
  • 使用 HTML5 代码验证用户输入
  • 使用 JavaScript 代码验证用户输入
  • 使用 PHP if语句(条件语句)来验证和过滤输入
  • 使用foreach循环从 XML 文件中动态创建一个 HTML 选择框
  • 使用简单的数组进行过滤和验证
  • 将简单数组传递给方法(函数)
  • 了解如何使用依赖注入来控制代码版本变更

安全的用户交互

在第二章的中,Hello World的例子包括用户交互(点击提交按钮)来调用 PHP 程序。本章将使用 HTML web 表单接受来自用户的信息,然后将这些信息传递给一个 PHP 程序。

不要相信你的用户!您必须为用户将输入 web 表单的任何类型的信息做好准备。在程序中接受信息之前,您还必须确保每次都验证和保护信息。你必须记住,用户可以选择不允许 JavaScript 在他们的浏览器中运行。因此,您的程序不能依赖 JavaScript 代码来验证输入。您必须能够处理这三种情况:

If the user is using an HTML5 capable browser, you can verify all input using HTML5 before sending it to the PHP program on the web server.   If the user is using a browser that does not have HTML5 (or complete HTML5) capability, you can verify all (or some) input using JavaScript. Then you can send the validated information to the PHP program on the web server.   If the user is using a browser that does not have HTML5 capability and has JavaScript disabled, the user input can still be verified by the PHP program on the web server.  

最好使用前两种方法中的任何一种来处理初始验证。这将在将信息发送到服务器之前验证信息的正确性。方法#3 将导致更多的服务器调用,因为必须将信息发送到服务器进行验证,然后必须将任何错误消息发送回浏览器供用户更正。

即使您使用方法#1 或#2 验证信息,当 PHP 程序在 web 服务器上接收信息时,您将再次评估您已经接收到有效的信息。即使用户可能发送了有效信息,数据包嗅探程序也会将有效信息变成有害信息。

HTML5 表单验证

构建 HTML5 表单时,可以验证文本框中输入的信息。但是,没有实现 HTML5 技术(或所有 HTML5 技术)的浏览器会将对象(如文本框)视为普通的非验证对象。框中的信息将被接受,无需任何验证。因此,您还必须包含一个 JavaScript 例程来捕捉任何未经 HTML5 验证的内容。

And security—Secure programming is only one part of the whole process of protecting your information. Files, directories, servers, networks and databases must also be properly protected. In addition, any very important information (such as credit card number) must be sent through a secure channel (HTTPS) to further protect the information. User id and password should be encrypted to make it difficult for packet sniffing software to find them.

您将继续使用第三章的中的示例,更新Dog类示例以接受来自用户的属性信息。Dog类已经有了安全性,所以您不需要对该类做任何额外的安全性更新。

Example 4-1. The lab.html file with some validation

<!DOCTYPE html>

<html lan="en">

<head>

<title>Dog Object</title>

<script src="validator.js"></script>

<style type="text/css">

#JS { display:none; }

</style>

<script>

function checkJS() {

document.getElementById('JS').style.display = "inline";

}

</script>

</head>

<body onload="checkJS();">

<h1>Dog Object Creater</h1>

<div id="JS">

<form method="post" action="lab.php" onSubmit="return validate_input(this)">

<h2>Please complete ALL fields. Please note the required format of information.</h2>

Your Dog's Name (max 20 characters, alphabetic) <input type="text" pattern="[a-zA-Z ]*"  title="Up to 20 Alphabetic Characters" maxlength="20" name="dog_name" id="dog_name" /><br />

Your Dog's Breed (max 35 characters, alphabetic) <input type="text" pattern="[a-zA-Z ]*" title="Up to 35 Alphabetic Characters" maxlength="35" name="dog_breed" id="dog_breed" /><br />

Your Dog's Color (max 15 characters, alphabetic) <input type="text" pattern="[a-zA-Z]*" title="Up to 15 Alphabetic Characters" maxlength="15" name="dog_color" id="dog_color" /><br />

Your Dog's Weight (numeric only) <input type="number" min="1" max="120" name="dog_weight" id="dog_weight" /><br />

<input type="submit" value="Click to create your dog" />

</form>

</div>

<noscript>

<div id="noJS">

<form method="post" action="lab.php">

<h2>Please complete ALL fields. Please note the required format of information.</h2>

Your Dog's Name (max 20 characters, alphabetic) <input type="text" pattern="[a-zA-Z ]*"  title="Up to 20 Alphabetic Characters" maxlength="20" name="dog_name" id="dog_name" /><br />

Your Dog's Breed (max 35 characters, alphabetic) <input type="text" pattern="[a-zA-Z ]*" title="Up to 35 Alphabetic Characters" maxlength="35" name="dog_breed" id="dog_breed" /><br />

Your Dog's Color (max 15 characters, alphabetic) <input type="text" pattern="[a-zA-Z]*" title="Up to 15 Alphabetic Characters" maxlength="15" name="dog_color" id="dog_color" /><br />

Your Dog's Weight (numeric only) <input type="number" min="1" max="120" name="dog_weight" id="dog_weight" /><br />

<input type="submit" value="Click to create your dog" />

</form>

</div>

</noscript>

</body>

</html>

在示例 4-1 的head部分,在validator.js文件被插入到程序中之后,一些 CSS 被包含进来,隐藏了(display:none)一个 ID 为JS的 division ( div)标签。就在 CSS 代码的下面是少量的 JavaScript 代码。这段代码将JS分区切换为可见($("#JS").show())并隐藏noJS分区。在JSnoJS赛区的形式之间只有一个细微的区别。您应该注意到,noJS表单没有调用validate_input JavaScript 函数。

这是为什么?

答案很简单。如果浏览器没有启用 JavaScript,隐藏和显示部门的简短 JavaScript 例程将不会运行。因此,JS 分部不会显示在浏览器中(因为代码顶部的 CSS 将其显示设置为 none)。将会看到noJS分区。这允许没有 JavaScript 的浏览器将用户输入的信息直接发送到 web 服务器上的 PHP 程序。希望用户有一个 HTML5 浏览器,在信息被发送到服务器之前仍然可以验证信息(在这个场景中是第一个)。然而,即使他们不这样做,信息也将在服务器上得到验证(这种情况下的第三步,你将在本章后面看到。

如果浏览器中启用了 JavaScript,hide-show 例程将“隐藏”部门noJS并显示 JS 部门。将信息提交给validate_input函数的表单将显示在浏览器中。这将允许启用了 JavaScript 的浏览器验证使用validate_input JavaScript 函数输入的信息。如果用户有一个 HTML5 浏览器(在这个场景中是#1),这可能是多余的,因为 HTML5 可以验证信息。但是,您必须为没有升级浏览器的用户做好准备(在这种情况下是第二种)。

A978-1-4842-1730-6_4_Fig1_HTML.jpg

图 4-1。

The lab.html file

在示例 4-1 中,dog_namedog_breeddog_color文本框将字段长度设置为与第三章的Dog类中验证的最大值相同。他们还使用 HTML5 pattern属性(【a-zA-Z】)只允许字母字符。title属性用于在输入错误信息时向用户显示错误。dog_weight文本框使用输入类型number,它自动限制输入。minmax参数也被设置为将狗的体重限制在 1 到 120 磅之间。如上所述,如果浏览器没有完全实现 HTML5 标准,它可能不会解释这些限制。因此,您还必须使用 JavaScript validate_ input方法来验证数据,以确保所有的验证都已完成。

支持 HTML5 的浏览器会在信息被发送到validate_input函数之前对其进行验证。因此,如果浏览器完全是 HTML5,所有信息也将通过 JavaScript 方法的验证。您可以检查浏览器版本,以确定浏览器是否是 100% HTML5。然而,这将需要更多的 JavaScript 代码,并且是不必要的,因为应用不需要更多的代码就可以完成它的任务。

重要的是,在接受来自用户的信息并将该信息传递给应用的整个过程中,所有验证都是一致的。用户可以很容易地查看 HTML 和 JavaScript 代码(您可以在浏览器中“查看源代码”来查看代码)。任何高度安全格式的验证都应该在服务器上编译和保护的编程语言中进行。

And security-you may wonder why you bother to verify on the client side. Why not pass all the information to the program on the server and let the program tell you whether you need to fix anything? Some programmers actually do this. However, the goal is to have an efficient program. By trying to verify the information in the user's browser, you reduce the number of calls to the server. This improves the performance and efficiency of applications and web servers. In addition, as you will see, users can still adjust the contents of the text box by verifying in the browser. If the verification is done on the web server, the information in the HTML form will be lost, because every time information is sent and received from the web server, the web page will be reloaded.

在浏览器中进行验证的目的是确保用户提供的信息符合服务器上程序的要求。最初,您可能会害怕这个示例向用户显示所需信息的格式。但是,目标不是保护数据;目标是确保数据是有效的。通知用户所请求的数据格式并不违反安全。

<form method="post" action="lab.php" onSubmit="return validate_input(this)">

表单使用参数onSubmit将所有输入的信息传递给 JavaScript 方法(validate_input)。this关键字表示来自this对象(即表单本身和所有文本框)的所有信息都被传递给该方法。return关键字表示该方法将返回一个TRUEFALSE状态。如果从validate_input方法返回了TRUE状态,HTML 代码会将表单提交给服务器上的 PHP 程序(lab.php)。

做它

Adjust Example 4-1 to include gender information from the Do It in Chapter 3. Use a text box to receive the information from the user. Try to use HTML5 to restrict the type of information the user can enter in the text box. Test your code.  

JavaScript 验证

虽然这不是一本 JavaScript 书,但它将简要介绍 JavaScript 方法validate_ input中使用的表单验证。

Example 4-2. Form validation via JavaScript—validator.js

function allalphabetic(the_string)

{

var letters = /^[a-zA-Z ]+$/;

if (the_string.match(letters))

{

return true;

}

else

{

return false;

}

}

// -------------------------------------------------------------------------------------------

function validate_dog_name(the_string)

{

if ((the_string.length > 0) && (allalphabetic(the_string)) && (the_string.length < 21))

{

return true;

}

else

{

return false;

}

}

// -------------------------------------------------------------------------------------------

function validate_dog_breed_color(the_string)

{

if ((the_string.length > 0) && (allalphabetic(the_string)) && (the_string.length < 35))

{

return true;

}

else

{

return false;

}

}

function validate_dog_weight(the_string)

{

if ((the_string > 0 && this_string <= 120) && (!isNaN(the_string)))

{

return true;

}

else

{

return false;

}

}

// -----------------------------------------------------------------------------------------

function validate_input(form)

{

var error_message = "";

if (!validate_dog_name(form.dog_name.value))

{

error_message += "Invalid dog name. ";

}

if (!validate_dog_breed_color(form.dog_breed.value))

{

error_message += "Invalid dog breed. ";

}

if (!validate_dog_breed_color(form.dog_color.value))

{

error_message += "Invalid dog color. ";

}

if (!validate_dog_weight(form.dog_weight.value))

{

error_message += "Invalid dog weight. ";

}

if (error_message.length > 0)

{

alert(error_message);

return false;

}

else

{

return true;

}

}

if (!validate_dog_breed_color(form.dog_color.value))

{

error_message += "Invalid dog color. ";

}

if (!validate_dog_weight(form.dog_weight.value))

{

error_message += "Invalid dog weight. ";

}

if (error_message.length > 0)

{

alert(error_message);

return false;

}

else

{

return true;

}

}

<script src="validator.js"></script>

在示例 4-1 中,JavaScript 文件validator.js通过 HTML script标签附加到head部分的lab.html文件中。通过将 JavaScript 验证放在一个单独的文件中,简化了 HTML 文件。JavaScript 文件依赖于 HTML 文件来显示。错误信息被传递给一个警告框(alert(error_message));)在方法的底部附近。编程建议——通过只将一个状态(错误消息)传回给一个调用程序,您可以灵活处理。然后,调用程序可以确定如何显示信息。这也使您可以轻松地为其他 HTML 表单重用该方法。

让我们向后看 JavaScript 代码(示例 4-2 )。在 HTML 文件中,form标签的onSubmit参数调用了validate_input JavaScript 方法。该方法控制表单上每个文本框的所有验证。该方法使用一系列的if语句来调用验证不同类型文本框的方法。每个if报表的格式都非常相似。

if (!validate_dog_name(form.dog_name.value))

如果验证通过,调用的每个方法将返回一个真响应,如果验证失败,将返回一个假响应。(在 JavaScript 中,常量truefalse是小写的。在 PHP 中,它们是大写的。)

通过将form传递给validate_input函数,HTML 表单中的所有参数和值同时被传递。您可以使用点符号来选择要使用的参数。

在前面的例子中,form.dog_name.value将提取在dog_name文本框中输入的值。用户在该文本框中输入的值被传递给validate_dog_name方法。

if (!validate_dog_name(form.dog_name.value))

{

error_message += "Invalid dog name. ";

}

函数名前面的!符号表示not。如果来自一个方法(或比较)的响应是true,一个if语句通常会执行。但是,如果从方法(validate_dog_name)返回的响应是false(它没有被正确验证),您希望在error_message属性中保存一条错误消息。如果函数返回一个 false 值,符号!会导致if语句执行与正常情况相反的操作。

其他每种验证方法都以类似的方式工作。这些方法检查输入的信息格式是否正确。如果信息正确,该方法返回true。如果信息不正确,该方法返回false

For more information about JavaScript if conditional statements, please visit https://www.thenewboston.com/videos.php?cat=10&video=16960 for more information about JavaScript functions (methods), please visit https://www.thenewboston.com/videos.php?cat=10&video=16952

function validate_dog_name(the_string)

{

if ((the_string.length > 0) && (allalphabetic(the_string)) && (the_string.length <= 20))

{

return true;

}

else

{

return false;

}

}

每个方法通过将值传递到the_string属性(function validate_dog_name(the_string))来接受来自特定文本框的值。每个方法中的if语句完成了大部分工作。在validate_dog_name方法中,if语句试图检查狗名的三个要求。语句的第一部分使用length方法来确保字符串的长度大于零。如果字符串的长度不大于零,则用户没有在文本框中输入任何内容。接下来,if语句将the_string传递给一个allalphabetic方法,以确定文本框是否只包含字母字符(您将很快看到这个函数)。最后,if语句再次检查字符串长度,以确保它没有超过 20 个字符。&&符号是一个AND符号,要求这三个检查都为真,完整的if语句才被认为是真的。如果这三个都为真,if语句将true返回给validate_input方法。如果三者中的任何一个为假,if语句将false返回给validate_input方法。

Programming instructions-JavaScript strings contain built-in methods that will automatically exist once variables are created and string values are set. In the example in this chapter, you used both the length method and the match method. length The method returns the length of the string. match The method determines whether the string contains a set of characters passed to the method (see the next example).

dog_namedog_breeddog_color都要求只允许字母字符。所以您创建了可以被每个验证方法调用的allalphabetic方法,而不是重复相同的代码。

function allalphabetic(the_string)

{

var letters = /^[a-zA-Z ]+$/;

if (the_string.match(letters))

{

return true;

}

else

{

return false;

}

}

allaphabetic方法使用正则表达式来检查字母字符。大多数编程语言(包括 PHP)允许程序员创建一个正则表达式来检查字符串是否符合要求的格式。

var letters = /^[a-zA-Z ]+$/;

这一行设置表达式来确定字符串是否只有小写和/或大写字母字符(A 到 Z,A 到 Z)。在后面的章节中,你将更详细地学习 PHP 中的正则表达式。

For more information about JavaScript regular expressions, please visit https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions

存在于所有 JavaScript 字符串中的方法之一是match方法。该方法将字符串(the_string)的内容与正则表达式(本例中为字母字符)进行比较。如果字符串满足表达式的要求(只包含字母字符),那么方法将返回true。否则将返回false。在本例中,如果字符串是字母,则true返回到validate_dog_name(或validate_dog_colorvalidate_dog_breed)方法中的if语句。如果字符串的任何部分包含除字母字符之外的内容,则向if语句返回 false(这反过来会使if语句的结果为 false,即使字符串大小是正确的)。

回到 JavaScript 代码的底部,您决定向 HTML 程序返回什么(lab.html)。

if (!validate_dog_weight(form.dog_weight.value))

{

error_message += "Invalid dog weight. ";

}

if (error_message.length > 0)

{

alert(error_message);

return false;

}

else

{

return true;

}

如果验证失败(如果从if语句返回一个false,在validate_input方法底部的每个if语句将构建一个错误消息(如"Invalid dog weight.")并将其放入error_message属性中。+=符号将当前属性中的内容与放入属性中的内容连接起来(这样就不会覆盖已经存在的任何消息)。

最后一个if语句(if (error_message.length > 0)检查error_message属性的长度。如果它大于零(错误消息已经被传递到属性中),程序将在一个警告框中显示消息,并将false返回给 HTML 程序(lab.html)。如果长度为零,则没有错误消息;true被传递回 HTML 程序。

<form method="post" action="lab.php" onSubmit="return validate_input(this)">

在示例 4-1 中,如果从validate_input方法返回一个truefalse,表单标签的格式将自动引起响应。如果返回true(没有验证问题),HTML 程序将把表单属性和值发送给 web 服务器上的lab.php程序。如果返回false(至少有一个验证问题),HTML 程序将不会传递信息,并将在浏览器中保持可见。将向用户显示警告框,以查看发生了什么错误。当用户关闭警告框时,他们仍将在 HTML 页面上(带有他们先前输入的信息)。他们可以更正任何不正确的信息,并单击提交按钮(再次)重新验证他们输入的信息。

A978-1-4842-1730-6_4_Fig3_HTML.jpg

图 4-3。

The lab.html file after failing JavaScript verification

A978-1-4842-1730-6_4_Fig2_HTML.jpg

图 4-2。

The lab.html file with incorrect entries Note

不同的浏览器对 JavaScript 代码有不同的反应。默认情况下,Internet Explorer 会尝试阻止 JavaScript 代码。如果您看不到任何结果,并且确定您的代码是正确的,但是却收到关于缺少方法(例如 validate_input)的错误消息,这可能是因为您的浏览器执行 JavaScript 的能力被关闭了。要么在浏览器的设置中打开它,要么尝试允许 JavaScript 运行的不同浏览器。

And security-although you have verified the input on the HTML page, the data may still harm the system. Users may try to send HTML, JavaScript or even PHP code, thus causing harm. This may be because an error message was entered in the HTML page, the packet sniffer intercepted the data transmitted to the PHP page, or a program tried to completely bypass the HTML page. No matter which of these activities produces harmful information, PHP programs need to detect it and deal with problems. Therefore, the receiver (lab.php) must try to filter (remove harmful codes) the data.

做它

Adjust Example 4-2 to validate the gender text box from the first Do It. Make sure to include an error message to indicate the problem(s). Also make sure the error message is added to the $``error_message property to be displayed with all other error messages. Make sure to test your code for all possible problems with the user entering data. Don’t forget to test if they do not enter any data.  

PHP 过滤

是时候看看需要对 php 文件(lab.php)进行的更改了。您需要能够从 HTML 程序中接受属性和值(dog_namedog_breeddog_colordog_weight)。但是,您需要担心有人可能会试图发送可能会影响程序运行的信息,甚至会使运行该程序的系统崩溃。

本节采用两种方法来帮助减少有害数据的可能性。首先,您将确定是否收到了所有必需的信息。如果没有,您将要求用户返回到 HTML 页面(lab.html)来输入信息。这将至少确保您拥有所有数据,并且满足 HTML5 页面和/或 JavaScript 程序提供的验证(validator.js)。其次,您将使用一些现有的 PHP 方法从接收到的数据中过滤出 HTML、JavaScript 和 PHP 语法。这将减少可执行语句被传递到程序中的机会。

Example 4-3. Partial listing of the top of lab.php with the clean_input method

<?php

require_once("dog.php");

function clean_input($value)

{

$bad_chars = array("{", "}", "(", ")", ";", ":", "<", ">", "/", "$");

$value = str_ireplace($bad_chars,"",$value);

// This part below is really overkill because the string replace above removed special characters

$value = htmlentities($value); // Removes any html from the string and turns it into < format

$value = strip_tags($value); // Strips html and PHP tags

if (get_magic_quotes_gpc())

{

$value = stripslashes($value); // Gets rid of unwanted quotes

}

return $value;

}

if ((isset($_POST['dog_name'])) && (isset($_POST['dog_breed'])) && (isset($_POST['dog_color'])) && (isset($_POST['dog_weight'])))

{

$dog_name = clean_input($_POST['dog_name']);

$dog_breed = clean_input($_POST['dog_breed']);

$dog_color = clean_input($_POST['dog_color']);

$dog_weight = clean_input($_POST['dog_weight']);

$lab = new Dog($dog_name,$dog_breed,$dog_color,$dog_weight);

list($name_error, $breed_error, $color_error, $weight_error) = explode(',', $lab);

...

For more information about PHP function isset, please visit example: http://php.net/manual/en/function.isset.php Video: https://www.thenewboston.com/videos.php?cat=11&video=17087 For more information about $_POST, please visit example: http://php.net/manual/en/reserved.variables.post.php Video: https://www.thenewboston.com/videos.php?cat=11&video=17087

lab.php的顶部,您添加了几个项目来提供更安全的代码。

if ((isset($_POST['dog_name'])) && (isset($_POST['dog_breed'])) && (isset($_POST['dog_color'])) && (isset($_POST['dog_weight'])))

这个if语句使用isset方法和$_POST来验证所有四个属性(dog_namedog_breeddog_colordog_weight)已经通过POST方法传递到程序中。如果所有项目都已通过,那么您将过滤(清除)这些项目。如果其中任何一个没有通过,一个else语句(您将在后面看到)将要求用户返回到lab.html页面输入所有需要的信息。

$dog_name = clean_input($_POST['dog_name']);

Each property is passed to the clean_input method (after it has been retrieved using the $_POST method) to remove harmful tags. In this example, the $dog_name property (on the left side of the = sign) will receive the cleaned information.

function clean_input($value)

{

$bad_chars = array("{", "}", "(", ")", ";", ":", "<", ">", "/", "$");

$value = str_ireplace($bad_chars,"",$value);

$value = strip_tags($value); // Strips html and PHP tags

$value = htmlentities($value); // Removes any html from the string and turns it into < format

if (get_magic_quotes_gpc())

{

$value = stripslashes($value); // Gets rid of unwanted quotes

}

return $value;

}

Note

set_magic_quotes_runtimemagic_quotes_runtime从 PHP 5.4 开始已经被移除。魔术引号(从 PHP 5.4 开始)不能在运行时设置。

For more information about the str_ireplace method, please visit http://php.net/manual/en/function.str-ireplace.php

方法clean_input创建了一个数组$bad_chars,它包含了一个你想从字符串中删除的所有特殊字符的列表(你将在本章的后面和以后的章节中更详细地了解数组)。str_ireplace方法($value = str_ireplace($bad_chars,"",$value);将把所有这些字符替换为$value属性中的""(什么都没有,它把它们移除了)并将剩下的字符放入$value。有时,您可能需要这些特殊字符(例如在 SQL 语句中)。然而,在这个例子中,您不需要任何输入,所以您可以删除它们。

Note

对于这个例子来说,只删除无效的字符是有效的。在现实世界中,您不需要同时包含stripslasheshtmlentitiesstring_tags方法(如方法中所示)。这里包含它们是为了演示它们的用法,以及为什么在其他情况下,您可能会使用它们。

三个 PHP 方法(stripslasheshtmlentitiesstrip_tags)用于转换有害代码,使其无法执行。stripslashes方法删除引号中的反斜杠(实际上也可以通过str_ireplace方法完成)。htmlentities方法将 HTML 字符转换成等价的 HTML 实体(例如 a 被转换成')。方法删除任何 PHP 或 HTML 标签。如果您使用 SQL 来更新数据库,您需要添加额外的函数来禁用任何有害的 SQL 语句。

For more information about stripslashes, htmlentities and strip_tags, Please visit examples-stripes: http://php.net/manual/en/function.stripslashes.php examples-HTML entities: http://php.net/manual/en/function.htmlentities.php examples-stripe label: http://php.net/manual/en/function.strip-tags.php video-magic quotes and stripes: https://www.youtube.com/watch?v=NUqkUjbGuPY video-[ strip_tags: https://www.youtube.com/watch?v=rn_dnQFLt3U Safety and performance It is much easier to delete harmful data or reject data before using or saving data. In the face of today's major security vulnerabilities, this is absolutely necessary.

你也可以限制哪些程序调用 PHP 程序。$__SERVER全局变量可以帮助你。

if($__SERVER['HTTP_REFERER'] == 'http://www.mysite.com/lab.html

{ //  code executed if valid location }

else {exit;}

For more information about the $__SERVER array, please visit the example: http://php.net/manual/en/reserved.variables.server.php Video: https://www.thenewboston.com/videos.php?cat=11&video=17047

这个示例代码将拒绝任何调用lab.php程序的程序(除了位于站点的lab.html程序)。如果不是从正确的 HTML 页面调用的话,exit;命令会关闭程序。

演示的过滤方法不会阻止某人输入“asabsbabsa”作为狗名。然而,这些方法将防止任何进入有害。

Example 4-4. Partial list of the bottom of lab.php with the clean_input method

else

{

print "<p>Missing or invalid parameters. Please go back to the lab.html page to enter valid information.<br />";

print "<a href='lab.html'>Dog Creation Page</a>";

}

提到的if语句的true部分包括了lab.php文件中的所有活动代码。如果一个或多个参数丢失,else部分(在lab.php文件的底部)将要求用户返回到lab.html页面正确输入信息。

做它

在新的lab.php文件中调整代码(从该书的网站下载)。添加代码以过滤错误的性别代码,并确保已经从 HTML 文件接收到性别信息。确保通过clean_input方法传递您的属性,以删除任何有害的数据。

附加 HTML 输入安全性

从本章到目前为止的所有代码中可以看出,每当在 HTML 表单上使用文本框时,都必须包含几个区域的附加代码来验证用户输入的内容。当您需要允许用户灵活输入内容(例如包含姓名和地址的表单)时,文本框是必需的。但是,当您希望将用户的响应限制在一个特定的可能值列表中时,可以使用其他 form 对象(如州的两个字母缩写)。这将提供更多的有效数据,因为用户将无法输入错别字或输入无效数据。

HTML5 选择列表框和单选按钮

可以从原始 HTML 文件中更改的一项是狗的品种条目。美国养犬俱乐部目前在其网站上列出了超过 150 个品种。您可以在 HTML 文件中为每个品种键入一个选项值行。然而,这将是耗时的。此外,如果一个品种改变了,你将不得不回去调整名单。更好的选择是将品种放在一个文件中,然后使用该文件填充一个select列表。如果一个品种改变或增加,你只需在一个地方更新一个文件,所有使用它的程序将自动访问新的列表。如果这个文件托管在 web 服务器上,您也可以在dog.php代码中使用同一个文件来验证用户已经将正确的品种传递给 web 服务器。

Example 4-5. The breeds.xml file

<?xml version="1.0" encoding="UTF-8"?>

<breeds>

<breed>Affenpinscher</breed>

<breed>Afghan Hound</breed>

<breed>Airedale Terrier</breed>

<breed>Akita</breed>

<breed>Alaskan Malamute</breed>

<breed>American English Coonhound</breed>

<breed>American Eskimo Dog</breed>

<breed>American Foxhound</breed>

<breed>American Staffordshire Terrier</breed>

...

</breeds>

安全性和性能——在 HTML 表单上为用户提供一个可供选择的值列表,比使用文本框提供了更高的有效性和安全性。如果用户只能从列表中选择,他们不能选择错误,也不能输入无效或有害的信息。

breeds.xml文件包含简单的 XML 代码(两个标签— breedsbreed)列出了所有品种。如果你正在创建一个真正的狗品种网站,你可能会希望在这个文件中包含更多的信息。您可以稍后添加更多信息,而不会影响此程序。

您现在想要使用 XML 文件(来自示例 4-5 )来填充选择列表框。因为这个文件将驻留在服务器上,所以您需要在服务器上创建一个程序来调用和检索信息。假设该文件在服务器上是安全的,只能进行只读访问。您不会尝试更新或删除文件本身的任何信息。

您可以创建一个 PHP 程序,只需几行代码就可以检索您需要的信息。安全和性能—请记住,安全是一项团队工作。不仅程序需要安全,而且 web 服务器及其文件结构也必须得到适当的保护。

Example 4-6. The getbreeds.php file

<?php

$breed_file = simplexml:load_file("breeds.xml");

$xmlText = $breed_file->asXML();

print "<select name='dog_breed' id='dog_breed'>";

print "<option>Select a dog breed</option>";

foreach ($breed_file->children() as $name => $value)

{

print "<option value='$value'>$value</option>";

}

print "</select>";

?>

PHP 只需要几行代码就可以完成许多强大的任务。例 4-6 的第一行打开breeds.xml文件,将内容放入一个属性($breed_file,然后关闭breeds.xml文件。

跳到foreach语句:

foreach ($breed_file->children() as $name => $value)

XML 数据被视为父子关系。父级可以包含子级(子级可以有子级)。在 XML 文件中,初始父节点是breeds。存在于品种下的孩子都有标签breed

<breed>Affenpinscher</breed>

本例中每个品种(子品种)的值是存在于品种标签之间的文本(例如,Affenpinscher)。

$breed_file->children()指示foreach语句遍历每个子元素(本文件中的breed)。语句的as $name=> $value部分告诉系统将每个子标签名(在本例中总是breed,但是您可以有不同的子标签名)放在$name中。它还指示系统将子代(Affenpinscher)中包含的值放入$value

print "<option value='$value'>$value</option>";

foreach循环中,print语句将$value的内容放在两个地方——选项标签的值参数和选项标签之间。对第一个孩子来说,它会产生

Option value='Affenpinscher'>Affenpinscher</option>

foreach循环自动遍历文件,直到文件中不再有记录。文件中的每一个品种都将创建类似的品系。这些行与文件中的其他print行一起,根据 XML 文件的内容动态创建一个 HTML 选择框。

For more information about foreach loop, please visit the example: http://php.net/manual/en/control-structures.foreach.php Video: https://www.thenewboston.com/videos.php?cat=11&video=17027 For more information about reading XML files, please visit the video: https://www.thenewboston.com/videos.php?cat=11&video=17090

您现在需要从 HTML 文件中调用这个程序。您可以使用第二章中的示例 JavaScript 文件来做到这一点,该文件使用了 AJAX。这将允许您仅通过更新页面中将显示选择框的部分来检索选择框。与第二章的例子相比,唯一需要修改的是调用 PHP 程序的那一行。

xmlHttp.open("GET", "get_breeds.php", true);

您只需用检索选择框的程序(get_breeds.php)替换现有的文件名。然后,您可以重命名该文件(get_breeds.js)。你也可以把你想运行的程序的名字传递给 JavaScript 方法(你将在本章的后面做)。

现在您需要对 HTML 文件做一些修改,以使用get_breeds.js并为包含选择框的区域创建一个div标签。

Example 4-7. The lab.html file with a dynamic select box

<!DOCTYPE html>

<html lan="en">

<head>

<title>Dog Object</title>

<script src="get_breeds.js"></script>

<script src="validator.js"></script>

<style type="text/css">

#JS { display:none; }

</style>

<script>

function checkJS() {

document.getElementById('JS').style.display = "inline";

}

</script>

</head>

<body onload="checkJS();">

<h1>Dog Object Creater</h1>

<div id="JS">

<form method="post" action="lab.php" onSubmit="return validate_input(this)">

<h2>Please complete ALL fields. Please note the required format of information.</h2>

Enter Your Dog's Name (max 20 characters, alphabetic) <input type="text" pattern="[a-zA-Z]*"  title="Up to 20 Alphabetic Characters" maxlength="20" name="dog_name" id="dog_name" /><br /><br />

Select Your Dog's Color:<br />

<input type="radio" name="dog_color" id="dog_color" value="Brown">Brown<br />

<input type="radio" name="dog_color" id="dog_color" value="Black">Black<br />

<input type="radio" name="dog_color" id="dog_color" value="Yellow">Yellow<br />

<input type="radio" name="dog_color" id="dog_color" value="White">White<br />

<input type="radio" name="dog_color" id="dog_color" value="Mixed">Mixed<br /><br />

Enter Your Dog's Weight (numeric only) <input type="number" min="1" max="120" name="dog_weight" id="dog_weight" /><br /><br />

<script>

AjaxRequest();

</script>

<div id="AjaxResponse"></div><br />

<input type="submit" value="Click to create your dog" />

</form>

</div>

<noscript>

<div id="noJS">

<form method="post" action="lab.php">

<h2>Please complete ALL fields. Please note the required format of information.</h2>

Enter Your Dog's Name (max 20 characters, alphabetic) <input type="text" pattern="[a-zA-Z ]*"  title="Up to 20 Alphabetic Characters" maxlength="20" name="dog_name" id="dog_name" /><br /><br />

Select Your Dog's Color:<br />

<input type="radio" name="dog_color" id="dog_color" value="Brown">Brown<br />

<input type="radio" name="dog_color" id="dog_color" value="Black">Black<br />

<input type="radio" name="dog_color" id="dog_color" value="Yellow">Yellow<br />

<input type="radio" name="dog_color" id="dog_color" value="White">White<br />

<input type="radio" name="dog_color" id="dog_color" value="Mixed">Mixed<br /><br />

Enter Your Dog's Weight (numeric only) <input type="number" min="1" max="120" name="dog_weight" id="dog_weight" /><br /><br />

Enter Your Dog's Breed (max 35 characters, alphabetic) <input type="text" pattern="[a-zA-Z ]*" title="Up to 15 Alphabetic Characters" maxlength="35" name="dog_breed" id="dog_breed" /><br />

<input type="submit" value="Click to create your dog" />

</form>

</div>

</noscript>

</body>

</html>

在示例 4-7 中,与创建动态选择框相关的唯一真实变化以粗体突出显示。

<script src="get_breeds.js"></script>

在代码的顶部附近添加了script标记,以拉入包含 AJAX 的 JavaScript 文件,从而调用将显示选择框的 PHP 程序(get_breeds.php)。

<script>

AjaxRequest();

</script>

<div id="AjaxResponse"></div><br />

在代码的JS部分放置了一个脚本区域(如果浏览器中启用了 JavaScript,将执行该部分)。)它只包含一行代码(AjaxRequest();)来执行 AJAX JavaScript 代码(来自get_breeds.js文件)。<div id="AjaxResponse"></div>放在结束脚本标签下面。这一行应该很眼熟。这是包含在第二章 AJAX 示例中的同一行。AjaxRequest()方法的输出将从 web 服务器返回的响应放在 ID 为AjaxResponsediv标签之间。在本例中,动态创建的选择框被放置在该位置。

A978-1-4842-1730-6_4_Fig4_HTML.jpg

图 4-4。

The lab.html file with dynamic select box and radio buttons

您还用单选按钮的静态选择替换了颜色文本框。对于狗来说,只有几种可能的颜色组合(好吧,假装我是对的),这些颜色组合不太可能改变,所以你不需要动态列表。将选择硬编码到 HTML 文件中是有意义的。

如果用户没有启用 JavaScript,将显示原始狗品种文本框(来自代码的 NJ 部分)。但是,由于您没有使用 JavaScript 来创建单选按钮,所以您在两个部分中都包含了单选按钮,以便为未启用 JavaScript 的浏览器提供一些更好的安全编码。

程序的界面(lab.html)现在通过减少使用的文本框数量,大大降低了输入无效数据的可能性。用户(假设他们启用了 JavaScript)别无选择,只能从选择框中选择一个品种,从单选按钮中选择一种颜色。

您现在还可以使用相同的 XML 文件来验证您已经从服务器端的用户那里收到了一个有效的品种名称。

做它

调整示例 4-7 中的lab.html文件,以包含单选按钮(而不是文本框)来接受用户的性别。确保新的lab.html文件可以与之前的lab.php文件兼容。

用 XML 文件验证输入

您可以在dog.php文件中添加几行代码来验证用户不仅发送了格式正确的字符串,还发送了 AKC 列出的品种。

Example 4-8. The validator_breed function (in dog.php)

private function validator_breed($value)

{

$breed_file = simplexml:load_file("breeds.xml");

$xmlText = $breed_file->asXML();

if(stristr($xmlText, $value) === FALSE)

{

return FALSE;

}

else

{

return TRUE;

}

}

您可以创建一个private函数(只在类内部使用)来检查正确的品种。该函数将接受传入$value属性的值。该函数将使用$breed_file = simplexml:load_file("breeds.xml");将 XML 文件的内容转储到$breed_file。下一行($xmlText = $breed_file->asXML();)将$breed_file的内容转换成格式良好的字符串。

if(stristr($xmlText, $value) === FALSE)

stristr方法比较其第二个参数(在本例中是$value)的内容,看它是否存在于第一个参数($xmlText)的字符串中。如果不存在,则返回FALSE。如果确实存在,它将返回字符串的位置。为了你的需要,你只需要知道它是否存在。如果没有,你返回FALSE。如果是,则返回 TRUE。

For more information about the stristr method, please visit http://php.net/manual/en/function.stristr.php

Example 4-9. The complete dog class with validation

<?php

class Dog

{

// ----------------------------------------- Properties ------------------------------------

private $dog_weight = 0;

private $dog_breed = "no breed";

private $dog_color = "no color";

private $dog_name = "no name";

private $error_message = "??";

// ---------------------------------- Constructor -----------------------------------------

function __construct($value1, $value2, $value3, $value4)

{

if (method_exists('dog_container', 'create_dog_app')) {

$name_error = $this->set_dog_name($value1) == TRUE ? 'TRUE,' : 'FALSE,';

$breed_error = $this->set_dog_breed($value2) == TRUE ? 'TRUE,' : 'FALSE,';

$color_error = $this->set_dog_color($value3) == TRUE ? 'TRUE,' : 'FALSE,';

$weight_error= $this->set_dog_weight($value4) == TRUE ? 'TRUE' : 'FALSE';

$this->error_message = $name_error . $breed_error . $color_error . $weight_error;

}

else

{

exit;

}

}

//------------------------------------toString---------------------------------------------

public function __toString()

{

return $this->error_message;

}

// ---------------------------------- Set Methods -----------------------------------------

function set_dog_name($value)

{

$error_message = TRUE;

(ctype_alpha($value) && strlen($value) <= 20) ? $this->dog_name = $value : $this->error_message = FALSE;

return $this->error_message;

}

function set_dog_weight($value)

{

$error_message = TRUE;

(ctype_digit($value) && ($value > 0 && $value <= 120)) ? $this->dog_weight = $value : $this->error_message = FALSE;

return $this->error_message;

}

function set_dog_breed($value)

{

$error_message = TRUE;

((preg_match("/[a-zA-Z ]+$/", $value)) && ($this->validator_breed($value) === TRUE) && strlen($value) <= 35) ? $this->dog_breed = $value : $this->error_message = FALSE;

return $this->error_message;

}

function set_dog_color($value)

{

$error_message = TRUE;

(ctype_alpha($value) && strlen($value) <= 15) ? $this->dog_color = $value : $this->error_message = FALSE;

return $this->error_message;

}

// ----------------------------------------- Get Methods ----------------------------------

function get_dog_name()

{

return $this->dog_name;

}

function get_dog_weight()

{

return $this->dog_weight;

}

function get_dog_breed()

{

return $this->dog_breed;

}

function get_dog_color()

{

return $this->dog_color;

}

function get_properties()

{

return "$this->dog_weight,$this->dog_breed,$this->dog_color.";

}

// ------------------------------General Method---------------------------------------------

private function validator_breed($value)

{

$breed_file = simplexml:load_file("breeds.xml");

$xmlText = $breed_file->asXML();

if(stristr($xmlText, $value) === FALSE)

{

return FALSE;

}

else

{

return TRUE;

}

}

}

?>

//------------------------------------toString----------------------------------------------

public function __toString()

{

return $this->error_message;

}

// ---------------------------------- Set Methods ------------------------------------------

function set_dog_name($value)

{

$error_message = TRUE;

(ctype_alpha($value) && strlen($value) <= 20) ? $this->dog_name = $value : $error_message = FALSE;

return $error_message;

}

function set_dog_weight($value)

{

$error_message = TRUE;

(ctype_digit($value) && ($value > 0 && $value <= 120)) ? $this->dog_weight = $value : $error_message = FALSE;

return $error_message;

}

function set_dog_breed($value)

{

$error_message = TRUE;

((ctype_alpha($value))``&&``($this->validator_breed($value) === TRUE)``&&

return $error_message;

}

{

$error_message = TRUE;

(ctype_alpha($value) && strlen($value) <= 15) ? $this->dog_color = $value : $error_message = FALSE;

return $error_message;

}

// ----------------------------------------- Get Methods ----------------------------------

function get_dog_name()

{

return $this->dog_name;

}

function get_dog_weight()

{

return $this->dog_weight;

}

function get_dog_breed()

{

return $this->dog_breed;

}

function get_dog_color()

{

return $this->dog_color;

}

function get_properties()

{

return "$this->dog_weight,$this->dog_breed,$this->dog_color.";

}

// ------------------------------General Method---------------------------------------------

private function validator_breed($value)

{

$breed_file = simplexml:load_file("breeds.xml");

$xmlText = $breed_file->asXML();

if(stristr($xmlText, $value) === FALSE)

{

return FALSE;

}

else

{

return TRUE;

}

}

}

?>

function set_dog_color($value)

{

$error_message = TRUE;

(ctype_alpha($value) && strlen($value) <= 15) ? $this->dog_color = $value : $error_message = FALSE;

return $error_message;

}

// ----------------------------------------- Get Methods ----------------------------------

function get_dog_name()

{

return $this->dog_name;

}

function get_dog_weight()

{

return $this->dog_weight;

}

function get_dog_breed()

{

return $this->dog_breed;

}

function get_dog_color()

{

return $this->dog_color;

}

function get_properties()

{

return "$this->dog_weight,$this->dog_breed,$this->dog_color.";

}

// ------------------------------General Method---------------------------------------------

private function validator_breed($value)

{

$breed_file = simplexml:load_file("breeds.xml");

$xmlText = $breed_file->asXML();

if(stristr($xmlText, $value) === FALSE)

{

return FALSE;

}

else

{

return TRUE;

}

}

}

?>

/ -------------------------------General Method---------------------------------------------

private function validator_breed($value)

{

$breed_file = simplexml:load_file("breeds.xml");

$xmlText = $breed_file->asXML();

if(stristr($xmlText, $value) === FALSE)

{

return FALSE;

}

else

{

return TRUE;

}

}

}

?>

现在,您可以对检查dog_breed值有效性的代码行做一点小小的修改(参见示例 4-9 中粗体突出显示的行)。

((ctype_alpha($value)) && ($this->validator_breed($value) === TRUE) && strlen($value) <= 35) ? $this->dog_breed = $value : $error_message = FALSE;

该语句将$value传递到validator_breed ($this->validator_breed($value)。如果返回一个TRUE,那么if语句的那部分将为真。否则就是假的。如果完整的if语句是TRUE,那么dog_breed属性被设置为在 XML 文件中找到的品种($this->dog_breed = $value)。如果为假,则将FALSE传递给$error_message属性($error_message = FALSE)。

Note: When you call a function in the same object, you must use the $this pointer ($this->validator_breed($value) === TRUE)).

你现在已经完成了一个更安全的程序。用户不能输入任何无效信息。他们可以尝试这样做的唯一字段是在dog_name字段中。然而,即使他们试图输入程序代码或其他代码,服务器端程序也会去掉特殊字符,使代码变得无害。如果数据包嗅探程序试图在服务器端程序收到数据之前更改数据,验证和/或过滤方法将导致数据被拒绝,或者再次使数据无害。

这个程序是有效的,因为它试图在信息发送到服务器之前对其进行验证。它减少了服务器和用户之间的通信量。完成的程序有两层(接口、业务规则),可以扩展到包括第三层(数据),而无需对当前代码进行重大更改。编程建议——对于不经常使用的小型 web 应用,没有必要像本例中那样将程序拆开。然而,如前所述,如果这是一个将来可能会扩展的应用,或者将来可能会获得大量用户,那么在创建程序时就应该考虑到这一点,将程序分成几个层次。

做它

访问该书的网站并运行示例程序。看看你能否“破解”程序内置的安全性。记住,你只能尝试尽可能的安全,没有什么是 100%安全的。您是否能够向影响程序的程序发送有害信息?如果是,你做了什么?允许这种情况发生的项目中可能缺少了什么?如果不是,是什么阻止了有害数据破坏程序?您可能会对该程序进行哪些需要包含数据层的更改?程序中还存在哪些低效之处?你能做些什么来修复它们?

依赖注入

正如你所看到的,当程序被创建时,开发人员在最终的应用完成之前要经历许多迭代。在这个过程中,有经验的程序员会保留他们程序的不同版本。这使得程序员可以快速备份到以前的版本,如果他们正在工作的版本有太多的重大问题。否则,他们将不得不尝试在不损害好代码的情况下去除“坏”代码。应用有许多文件(HTML、JavaScript、CSS、PHP 类和 PHP 库)。跟踪哪个版本与哪个版本一起工作,或者轻易地改变程序的一部分以使用另一部分的新版本,会变得令人困惑。尤其是当文件名和类名被编码在程序本身的代码中时。

第二章简单讨论了依赖注入。它允许将要使用一个代码块(比如一个类)的程序(客户端)不知道它将要使用的代码块的实际实现。客户端程序不知道实际的类名。

你可以用这个想法来帮助开发过程。您将要看到的例子并不适合大规模应用。然而,它确实给了你一个机会来看看依赖注入的好处。大规模的应用应该使用 MVC(模型-视图-控制)模型或者一个已建立的层(或者组件)系统,在组件之间提供更有效的通信。

And security-in many cases, there is a trade-off between being as safe as possible and having the highest performance program as possible. The following example will make multiple system calls when checking whether a program exists, and then use require_once to load the program. File presence check allows the program to handle missing files instead of crashing the program. In later chapters, you will see the use of try/catch block to allow the program to catch any problems without crashing the program. This will reduce the number of system calls (you no longer need to check whether files exist), thus improving the performance of the program.

dog应用使用包含在不同文件中的类和方法。这需要代码也包含require_once语句来将文件拉入程序。当前的设计将实际的文件名放在了require_once语句中。这不允许您更改文件的不同版本,除非您更改代码。现在,您将删除这些依赖项,以便为应用的未来开发提供更大的灵活性。

在你陷入代码泥潭之前,图 4-5 显示了与程序的关系流程。

A978-1-4842-1730-6_4_Fig5_HTML.jpg

图 4-5。

Data flow for the dog application

dog_interface.php程序将为dog应用的所有部分提供接口。此外,它还提供了您在前面的示例中已经设计好的安全性和过滤功能。除了这些其他活动,dog_interface创建并使用dog_container对象来包含、创建和传递任何其他需要的对象(不知道对象的名称)。dog_container对象使用一个 XML 文件(未显示)来发现包含它将创建的类的文件的位置和名称(提供依赖注入)。

应用将总是使用dog_interface程序来访问其他类。dog_interface程序将决定完成特定任务需要什么类。每当需要一个类时,dog_interface将使用dog_container来确定这个类的名称和位置(通过 XML 文件),并创建这个类的一个实例(对象)。通过使用 XML 文件列出类文件名和位置,可以在不对应用中的程序进行任何代码更改的情况下进行更改。

当从lab.htm l 页面请求品种选择框时,调用dog_interface程序。它将创建一个dog_container对象。dog_container对象将发现get_breeds类文件和 breeds XML 文件的位置和文件名。一旦被发现,dog_container对象将创建一个get_breeds对象。get_breeds对象将为选择框构建代码;最终将代码返回到lab.html中的表格以显示给用户。然后所有对象(dog_containerget_breeds)被销毁(从内存中移除)。

当点击lab.ht ml 表单上的提交按钮时(假设所有验证都通过了),它将调用dog_interface程序。这个程序将创建dog_container对象。然后,dog_container对象将发现dog.php类文件的位置和文件名。一旦被发现,dog_container对象将创建dog对象。dog对象的行为将与之前的例子完全一样(验证属性和显示属性)。一旦dog对象完成,对象(dog_containerdog)被销毁(从内存中删除)。这种设计允许对象完全独立,提供两层设计(接口和业务规则)和依赖注入。

Example 4-10. The dog_applications.xml file

<?xml version="1.0" encoding="UTF-8"?>

<dog_applications>

<application>

<type ID="dog">

<location>dog3.php</location>

</type>

</application>

<application>

<type ID="selectbox">

<location>get_breeds2.php</location>

</type>

</application>

<application>

<type ID="breeds">

<location>breeds.xml</location>

</type>

</application>

</dog_applications>

在示例 4-10 中,您已经创建了一个简单的 XML 文件,该文件将用于 PHP 应用中重要文件的版本更改。每个application标签标识文件的类型(狗、选择框、品种)。application标签中的每个location标签提供了文件名和位置(尽管在这个例子中所有文件都在同一个位置)。一旦你调整程序来使用这个文件,你就可以灵活地改变文件名(比如用dog3.php代替dog.php的例子)和位置,而不必改变任何程序代码。这可以帮助您在开发过程中在应用中交换版本。

您现在将创建一个包含两个方法的Dog_ container类。get_dog_application将用于“获取”XML 文件中列出的任何文件的名称和位置。create_object方法将创建一个狗类或get_breeds类的实例。

Example 4-11. The dog_container.php file

<?php

class Dog_container

{

private $app;

private $dog_location;

function __construct($value)

{

if (function_exists('clean_input'))

{

$this->app = $value;

}

else

{

exit;

} }

public function set_app($value)

{

$this->app = $value;

}

public function get_dog_application()

{

$xmlDoc = new DOMDocument();

if ( file_exists("dog_applications.xml") )

{

$xmlDoc->load( 'dog_applications.xml' );

$searchNode = $xmlDoc->getElementsByTagName( "type" );

foreach( $searchNode as $searchNode )

{

$valueID = $searchNode->getAttribute('ID');

if($valueID == $this->app)

{

$xmlLocation = $searchNode->getElementsByTagName( "location" );

return $xmlLocation->item(0)->nodeValue;

break;

}

}

}

return FALSE;

}

function create_object($properties_array)

{

$dog_loc = $this->get_dog_application();

if(($dog_loc == FALSE) || (!file_exists($dog_loc)))

{

return FALSE;

}

else

{

require_once($dog_loc);

$class_array = get_declared_classes();

$last_position = count($class_array) - 1;

$class_name = $class_array[$last_position];

$dog_object = new $class_name($properties_array);

return $dog_object;

}

}

}

?>

首先,在类的顶部(dog_container),声明两个私有属性— $app$dog_location。这些属性被声明为 private,而不是 public,以保持它们的值只在这个类中已知。

在构造函数中,$value接受您想要在 XML 文件中查找的应用类型的名称(比如selectbox)。在代码的后面,您将把$value与 XML 文件中的类型 ID 进行比较,看看能否找到应用类型和与之相关的文件。构造函数将$value放入$app属性中。然而,该方法还包括一个if语句,该语句使用方法function_exists来确定clean_input函数是否存在。

为什么呢?在本章的开始,你简要地看了允许你限制一个程序的使用到一个调用它的特定应用的代码。在这个例子中,您将看到另一种技术来限制哪些程序可以使用这个类。if语句要求任何创建该类实例的程序也必须有一个clean_input方法。如果有人试图使用另一个不包含clean_input方法的程序来创建该程序的实例,该语句的else部分将会执行,这将导致该对象无法被创建并将关闭该程序。

And security-whenever a program receives information from the Internet or network, it is a good idea to determine the source of the information. In addition to determining the application and function names, PHP programs can also check the source IP address.

public function get_dog_application()

{

$xmlDoc = new DOMDocument();

if ( file_exists("dog_applications.xml") )

{

$xmlDoc->load( 'dog_applications.xml' );

$searchNode = $xmlDoc->getElementsByTagName( "type" );

方法的第一部分应该看起来很熟悉。这段代码打开dog_applications.xml文件(在确保它存在之后)。然后它将内容加载到$xmlDoc中。最后一行调用 PHP 方法getElementsByTagName。该方法搜索“type”在$xmlDoc中的所有出现,并将每个出现放入$searchNode

foreach( $searchNode as $searchNode )

{

$valueID = $searchNode->getAttribute('ID');

if($valueID == $this->app)

{

$xmlLocation = $searchNode->getElementsByTagName( "location" );

return $xmlLocation->item(0)->nodeValue;

break;

}

}

foreach循环查看包含在$searchNode中的每一行。它使用 PHP 方法getAttribute将带有ID XML 属性的下一行放到属性$valueID中。一旦它被放入$valueID,该属性中的值(dogselectboxbreeds)将与$this->app进行比较。(属性应用是用构造函数中的值加载的。)如果在 XML 文件中找到了ID,那么getElementsByTagName将搜索包含location XML 标签的下一行。然后,返回行获取位置标记中的值(文件名及其位置),并将其返回给调用该方法的程序。break语句用于提前退出循环,因为您已经返回了前一行代码中所需的值。如果 XML 文件不存在,方法的else部分(如示例 4-11 所示)将返回FALSE

function create_object($properties_array)

create_object方法用于创建dogget_breeds对象。dog对象构造函数在其构造函数中需要四个值(dog_name, dog_weight, dog_breed,dog_color)。get_breeds应用不接受其构造函数中的任何值。为了在dog_container中提供更高效的代码,您更改了每个类的构造函数的签名(函数的第一行),以接受一个数组,而不是单个的值。这将允许您向任一构造函数传递任意数量的项。

function __construct($properties_array)

{ // dog class constructor

if (method_exists('dog_container', 'create_object')) {

$name_error = $this->set_dog_name($properties_array[0]) == TRUE ? 'TRUE,' : 'FALSE,';

$breed_error = $this->set_dog_breed($properties_array[1]) == TRUE ? 'TRUE,' : 'FALSE,';

$color_error = $this->set_dog_color($properties_array[2]) == TRUE ? 'TRUE,' : 'FALSE,';

$weight_error= $this->set_dog_weight($properties_array[3]) == TRUE ? 'TRUE' : 'FALSE';

这需要更改dog.php文件中的这些行来接受一个数组,然后使用数组中的值来设置每个属性。

注意第一行中的$properties_array没有任何东西将它声明为数组(除了名字)。请记住,PHP 属性通过属性中传递的内容来确定它们是什么数据类型。数组也是如此。如果一个数组被传入$properties_array,它就变成了一个数组。

数组的地址被传递到方法中。接受地址($properties_array)的属性实际上指向数组在内存中的位置。这样效率很高。如果您需要向一个方法中传递 50 个项,您可以一次传递一个,并在方法签名中为这 50 个项中的每一个声明不同的属性。但是,您可以创建一个包含 50 项的数组,并传递该数组。使用这种方法只传递一个值,即数组在内存中的地址。此外,如本例所述,使用数组允许您灵活地选择要传递到method签名中的项目数量。

$name_error = $this->set_dog_name($properties_array[0]) == TRUE ? 'TRUE,' : 'FALSE,';

要从数组中复制一个项,可以使用数组名($properties_array)和该项在数组中的位置([0])来引用该项。请注意,您使用了[]括号来声明头寸。这个位置通常被称为下标。数组下标从位置 0 开始,而不是位置 1。

class GetBreeds {

function __construct($properties_array)

{ //get_breeds constructor

if (!(method_exists('dog_container', 'create_object')))

{

exit;

}

}

你必须调整get_breeds程序。GetBreeds现在是一个班。您还创建了GetBreeds构造函数的签名来接受一个数组。然而,正如您从这段代码中看到的,您实际上将忽略传递到数组中的任何内容。这允许您在dog_container中使用相同的方法来创建任一类的对象(或者实际上是接受数组的任何类的对象)。您需要找到的只是类名(dogGetBreeds),这样您就可以创建一个实例。

function create_object($properties_array)

{

$dog_loc = $this->get_dog_application();

if(($dog_loc == FALSE) || (!file_exists($dog_loc)))

{

return FALSE;

}

然后,create_object方法调用get_dog_application方法,并将返回值(文件的位置和文件名)放入$dog_loc。如果$dog_loc中的值为假或者不是一个现有的文件,该方法将把FALSE返回给调用它的程序。

else

{

require_once($dog_loc);

$class_array = get_declared_classes();

$last_position = count($class_array) - 1;

$class_name = $class_array[$last_position];

$dog_object = new $class_name($properties_array);

return $dog_object;

} } }

?>

如果路径和文件名有效,将执行代码的else部分。$dog_loc中的值在require_once语句中使用(该语句将文件内容提取到该方法中)。

您现在必须确定文件中存在的类名(依赖注入要求您不仅要发现文件名和文件路径,还要发现类名)。

PHP 方法get_declared_classes返回程序中当前存在的所有类的数组。这些课从第一节到最后一节都是有序的。因此,因为您只是用一个类(或者是dog或者是get_breeds类)包含了文件,所以您可以在创建的数组中寻找最后一个条目。你已经将get_declared_classes创建的数组放置在$class_array中。现在,您可以确定数组的大小。PHP 方法count将返回一个数组的大小。记住数组的大小是数组中的项数,而不是最后一个位置。如果数组的大小为 10,实际下标是 0 到 9,而不是 1 到 10。

$last_position = count($class_array) - 1;

For more information about get_declared _ class, please visit http://php.net/manual/en/function.get-declared-classes.php

考虑到这一点,该语句确定了$class_array的大小,然后从大小中减去 1,并将该值放入$last_position。因此,如果数组的大小为 10,数字 9 将存储在$last_position中(因为数组下标是 0 到 9,而不是 10)。

$class_name = $class_array[$last_position];

然后,您可以使用$last_position中的值从数组中取出最后一个创建的类,并将其放入$class_name中。正如你所看到的,你实际上可以在[]下标括号之间传递属性$last_position,以指示程序你想要位于$last_position中的位置的值。这允许您能够从任意大小的数组中的最后一个位置提取信息(因为您不知道$class_array的大小)。

您现在可以创建该类的一个实例,因为您在$class_na me 中有了类名。

$dog_object = new $class_name($properties_array);

这一行代码现在将创建一个刚刚被包含(require_once)到程序中的任何类的实例(接受一个数组到构造函数中)。该方法将创建一个dog类或getBreeds类的实例。

return $dog_object;

最后,对象(或者是一个dog对象或者是一个getBreeds对象)被返回给调用该方法的程序。通过返回该对象,调用程序可以完全访问该对象及其属性和方法,即使它没有实际创建该对象(dog_interface可以使用该对象,即使dog_container创建了它)。

对象在内存中的位置的返回方式与前面示例中数组的位置传递给构造函数的方式类似。你可以把它想象成新对象暂时“包含”在dog_container对象中。然而,对象被返回(到dog_interface)。

Programming notes-what? What really happens is that the address in $dog_object memory is passed to the caller (dog_interface). This allows the caller to access the object and the $dog_container object. Therefore, there is only one copy of $dog_object in memory, but two different blocks can use it. If one of the blocks (dog_container or dog_interface) is closed, another object can still access it until it is also closed. Then the garbage collector will delete it from memory $dog_object.

Example 4-12. The get_breeds class

<?php

class GetBreeds {

function __construct($properties_array)

{ //get_breeds constructor

if (!(method_exists('dog_container', 'create_object')))

{ exit;}}

private $result = "??";

public function get_select($dog_app)

{ if (($dog_app != FALSE) && ( file_exists($dog_app))) {

$breed_file = simplexml:load_file($dog_app);

$xmlText = $breed_file->asXML();

$this->result = "<select name='dog_breed' id='dog_breed'>";

$this->result = $this->result . "<option value='-1' selected>Select a dog breed</option>";

foreach ($breed_file->children() as $name => $value)

{    $this->result = $this->result . "<option value='$value'>$value</option>";  }

$this->result = $this->result . "</select>";

return $this->result;

} else {

return FALSE;

}

}

}

?>

正如您从示例 4-12 中看到的,只需要进行微小的改动。如前所述,声明了一个类并添加了一个构造函数。构造函数验证这个类是从包含dog_ containercreate_breed_app方法的程序中创建的。这种安全性试图防止其他程序知道驻留在dog_application.xml文件中的Dog应用的文件名和位置。

Example 4-13. The dog_interface.php file

<?php

function clean_input($value) {

$bad_chars = array( "{", "}", "(", ")", ";", ":", "<", ">", "/", "$" );

$value = str_ireplace($bad_chars,"",$value);

$value = htmlentities($value);

$value = strip_tags($value);

if (get_magic_quotes_gpc())

{ $value = stripslashes($value); }

$value = htmlentities($value);

return $value;

}

function error_check_dog_app($lab) {

list($name_error, $breed_error, $color_error, $weight_error) = explode(',', $lab);

print $name_error == 'TRUE' ? 'Name update successful<br/>' : 'Name update not successful<br/>';

print $breed_error == 'TRUE' ? 'Breed update successful<br/>' : 'Breed update not successful<br/>';

print $color_error == 'TRUE' ? 'Color update successful<br/>' : 'Color update not successful<br/>';

print $weight_error == 'TRUE' ? 'Weight update successful<br/>' : 'Weight update not successful<br/>';

}

function get_dog_app_properties($lab) {

print "Your dog's name is " . $lab->get_dog_name() . "<br/>";

print "Your dog weights " . $lab->get_dog_weight() . " lbs. <br />";

print "Your dog's breed is " . $lab->get_dog_breed() . "<br />";

print "Your dog's color is " . $lab->get_dog_color() . "<br />";

}

//----------------Main Section-------------------------------------

if ( file_exists("dog_container.php"))

{  require_once("dog_container.php"); }

else { print "System Error #1"; exit; }

if (isset($_POST['dog_app']))

{

if ((isset($_POST['dog_name'])) && (isset($_POST['dog_breed'])) && (isset($_POST['dog_color'])) && (isset($_POST['dog_weight'])))

{     $container = new dog_container(clean_input($_POST['dog_app']));

$dog_name = clean_input(filter_input(INPUT_POST, "dog_name"));

$dog_breed = clean_input($_POST['dog_breed']);

$dog_color = clean_input($_POST['dog_color']);

$dog_weight = clean_input($_POST['dog_weight']);

$properties_array = array($dog_name,$dog_breed,$dog_color,$dog_weight);

$lab = $container->create_object($properties_array);

if ($lab != FALSE) {

error_check_dog_app($lab);

get_dog_app_properties($lab);  }

else { print "System Error #2"; }

}

else {

print "<p>Missing or invalid parameters. Please go back to the dog.html page to enter valid information.<br />";

print "<a href='dog.html'>Dog Creation Page</a>";

}

}

else

{

$container = new dog_container("selectbox");

$lab = $container->create_breed_app();

if ($lab != FALSE) {

$container = new dog_container("selectbox");

$properties_array = array("selectbox");

$lab = $container->create_object($properties_array);

if ($lab != FALSE) {

$container->set_app("breeds");

$dog_app = $container->get_dog_application();

$method_array = get_class_methods($dog_data);

$last_position = count($method_array) - 1;

$method_name = $method_array[$last_position];

$result = $dog_data->$method_name($dog_app);

if ( $result == FALSE) {

print "System Error #3"; //select box not created

}

else

{

print $result; //pass back select box

}

}

else

{

print "System Error #4";

}

}

?>

dog_interface程序实际上是主要部分代码发生变化的lab.php程序。lab.php的方法没有任何改变。

if ( file_exists("dog_container.php"))

{  require_once("dog_container.php"); }

else { print "System Error #1"; exit; }

if (isset($_POST['dog_app']))

首先,程序使用 PHP file_exists方法确定dog_container是否存在。如果是的话,它使用require_once将代码拉入程序中。如果dog_container不存在,程序打印错误信息("System Error #1",然后关闭(exit;)。

接下来,程序使用isset来确定调用程序是否已经提供了$dog_app的值。如果这个值已经被传递,这表明调用程序想要创建一个Dog对象。

$container = new dog_container(clean_input($_POST['dog_app']));

$dog_name = clean_input(filter_input(INPUT_POST, "dog_name"));

$dog_breed = clean_input($_POST['dog_breed']);

$dog_color = clean_input($_POST['dog_color']);

$dog_weight = clean_input($_POST['dog_weight']);

$properties_array = array($dog_name,$dog_breed,$dog_color,$dog_weight);

$lab = $container->create_object($properties_array);

if ($lab != FALSE)

{

error_check_dog_app($lab);

get_dog_app_properties($lab);

} else {

print "System Error #2";

} else {

print "<p>Missing or invalid parameters. Please go back to the lab.html page to enter valid information.<br />";

print "<a href='lab.html'>Dog Creation Page</a>";

} }

然后程序创建一个dog_container ( $container)的实例,将$dog_app中的值传递给$container对象。使用clean_input方法过滤Dog对象的每个属性。然后属性被传递到$properties_array数组中。然后将数组传递给dog_container对象($containercreate_object方法。如果成功创建了Dog对象($lab),那么就调用error_check_dog_app方法来验证每个属性都有有效的信息。调用get_dog_app_properties方法来显示每个属性。

如果Dog对象所需的任何属性缺失,用户将被要求返回到lab.html页面重新输入所需的信息。

else

{ //get breeds

$container = new dog_container("selectbox");

$properties_array = array("selectbox");

$lab = $container->create_object($properties_array);

if ($lab != FALSE)

{

$container->set_app("breeds");

$dog_app = $container->get_dog_application();

$method_array = get_class_methods($lab);

$last_position = count($method_array) - 1;

$method_name = $method_array[$last_position];

$result = $lab->$method_name($dog_app);

if ( $result == FALSE) // select box not created

{

print "System Error #3";

}

else

{

print $result; // select box created!

}

}

else

{

print "System Error #4";

}

如果$dog_app值没有传入到类中,则执行else语句。假设用户想要创建一个getBreeds对象。创建了dog_container的一个实例($container,它传递值selectbox。(如果不能创建对象,将显示"System Error #4")。单词“selectbox”被传递到数组$properties_array(注意:必须使用array关键字,否则您将创建一个属性而不是数组。)容器对象($container)然后将调用create_object(传递$properties_array来创建getBreeds类的实例($lab)。如果成功创建了getBreeds对象($lab !=FALSE),那么您需要找到get_breeds.xml文件(包含品种列表)的位置。因此,您将容器中的 app 属性(通过调用set_app)重置为“breeds”。这告诉容器程序你是一个getBreeds对象,而不是一个dog对象。然后使用容器的get_dog_application方法找到 breeds XML 文件的位置。

For more information about arrays, please visit the example: http://php.net/manual/en/function.array.php Video: https://www.thenewboston.com/videos.php?cat=11&video=17024

PHP 方法get_class_methods用于创建包含在getBreeds中的方法数组。由于get_select方法是唯一的方法(除了构造函数),它也是数组中的最后一个方法。它的名字从数组中取出,然后使用属性$method_name ($result = $dog_data->$method_name($dog_app)调用它。这使得getBreed级可以完全独立于dog_interface级。开发人员可以更改get_select方法的名称,一切仍然可以工作(只要它是类中的最后一个方法)。这提供了接口层和业务规则层之间的完全分离。

XML 文件的位置被传递给getBreeds对象($lab)的get_select方法,该方法使用 XML 文件来创建选择列表框。选择列表框的代码被放到$result中。如果代码确实被放到了$result中,代码就会显示(print $result)回 HTML 表单,供用户选择一个品种。如果文件名无效,将显示错误消息(print "System Error #3"),而不是选择框。

唯一需要的其他更改是对lab.htmlget_breeds.js文件的两个微小更改。

function AjaxRequest($value)

get_breed.js文件中,AjaxRequest方法的函数头已经被修改,以传递被调用的实际文件(这不是本设计的要求,但它允许该文件用于任何通过 AJAX 调用的程序)。

xmlHttp.open("GET", $value, true);

此外,open语句已经调整为使用$value代替文件名。

lab.html程序中,有一些额外的变化。

AjaxRequest('dog_interface.php');

对 JavaScript 函数的调用现在传递文件名,该文件名也被更改为dog_interface.php文件。

<form method="post" action="dog_interface.php" onSubmit="return validate_input(this)">

最后,HTML 表单的 action 标签(在两个表单标签位置)已经被修改为调用dog_interface.php程序。

请注意,现在,每当您想要使用Dog应用时,您首先调用接口。然后界面决定你想要完成什么(获得品种选择框或者处理你正在创建的狗的属性)。

您必须通过接口与您的所有类进行通信。反过来,接口必须使用容器创建任何所需的对象(当然,容器本身除外)。这遵循了分层设计的概念。

您将不会看到与之前看到的任何不同的输出(除非您有一些系统错误)。但是,现在您可以轻松地在应用中更改文件名和位置(通过 XML 文件),而无需更改任何程序代码!

做它

Download the files for this section from the book’s web site. Change the file names for the get_breeds.xml file, the get_breeds``.php file, and the dog.php file. Try to run the program via the lab.html file. The select box will not display and the program will not run. Now go to the dog_applications.xml file and change the data in the XML file to the new file names you just created. Go back to your lab.html file (reload it). You should now see the select box. Fill in and select the information and click the Submit button. The application should now work.  

章节术语

| 生效 | validator方法 | | JavaScript 隐藏/显示 | HTML onSubmit | | 表单验证 | JavaScript 警告框 | | HTML 传递“表单” | JavaScript 点符号 | | JavaScript if语句 | JavaScript le n gth 方法 | | && AND | &#124;&#124; OR | | 正则表达式 | JavaScript match方法 | | 过滤器/过滤 | isset | | $_POST | str_ireplace | | Stripslashes | htmlentities | | strip_tags | $__SERVER | | exit | else | | HTML 选择列表 | 可扩展置标语言 | | XML 数据格式 | XML 父子 | | foreach循环 | HTML 单选按钮 | | private功能 | stristr | | 依赖注入 | function_exists | | break声明 | method签名 | | getElementsByTagName | getAttribute | | array | array subscript | | get_declared_classes | Count | | 数组的大小 | 数组的最后位置 | | array关键字 | 传递物体 | | get_class_methods |   |

第二章问题和项目

多重选择

When using form validation, which of the following is true? The server automatically successfully processes input values with no errors.   A required field is checked to make sure it has content.   A dynamic web page is updated.   All information is sent to the server.     Which of the following can be verified with a validator? E-mails   IP addresses   Integers   All of the above     Why must you validate your code? To see if your browser can complete a task.   To make sure your information is correct and secure.   To make sure that your browser can run JavaScript and HTML5.   To verify that your computer can run the latest version of PHP.     The verification code in a PHP file does which of the following? Compares the information received to an expected standard format   Verifies user program interaction   Checks and eradicates harmful data being entered by the user   Checks for incorrect PHP functions being used     stripslashes do which of the following? Remove backslashes from quotes.   Convert HTML characters to their equivalent HTML entity.   Remove any PHP or HTML tags.   All of the above.     If the size of an array is 29, what is the subscript range? 1 through 30   1 through 29   0 through 29   None of the above     in_array does which of the following? Searches for the number of empty spaces in an array.   Searches for the number of characters in an array.   Searches for a value in an array.   None of the above.     The PHP method count will return which of the following of an array? last_position   subscript   size   None of these     Which method will produce a variable that you can use to refer to the last position of the array? $last_position=count($class_array) - 1;   $class_array=$last_position(count - 1);   $count=$last_position - 1($class_array);   $last_position=$class_array -1 (count);     The exit command does which of the following? Automatically directs the user to a new page.   Closes the program if it is not called from the correct HTML page.   Turns off your computer.   None of the above.     What is not true in relation to the foreach command? Works only with arrays and objects.   Used to parse through each key/value pair in an array.   Can be iterated by reference.   The equivalent to an if/then statement.     The break statement does which of the following? Ends execution of the for, foreach, do-while, and/or switch structure(s).   Executes the for, foreach, do-while, and/or switch structure(s).   Ensures the execution of for, foreach, do-while, and/or switch structure(s).   Breaks the for, foreach, do-while, and/or switch structure(s) before they are executed.     Using the getAttribute will do which of the following? Return the value of the attribute.   Print a list of data.   Load a new HTML page.   None of these.     Which function converts HTML tags into their entities versions? strlen   htmlentities   explode   getAttribute     Which is a commonly used function to find the length of a string? strlen   getLength   String concatenation   __toString    

真/假

The count function returns the number of elements in an array.   When using an array, the index must not exceed the size of the array.   A subscript is the name given to position where the item currently exists in the array and is usually contained in [].   A private function is an event in PHP to network for a job.   One purpose of exit is to end the program.   The getElementsByTagName searches for occurrences that correspond to a specific XML tag.   get_declared_classes returns an array of all classes that currently exist in a program in order from first to last.   Dependency injection allows the program client to enter a block of code to know the implementation of the block of code it will be using.  

简答/短文

Why should you validate user input both within the interface tier and business rules tier?   Why should input received in the business rules tier be filtered? What are the different ways you can filter the information?   Explain how you can reduce errors from user input by the type of HTML objects (such as radio buttons) used to accept information.   What causes the example code shown in the dependency injection section of this chapter inefficient? How does this code help a developer with version changes to the application?  

项目

Create an application that registers a runner for the local 5K road race. The interface should accept all necessary personal information (name, address, gender, age, and T-shirt size). Whenever possible, use HTML objects that restrict input (such a select object for T-shirt size and state). Validate all information in the interface tier using both HTML5 and JavaScript. If the information is valid, pass the information to the business rules tier. The business rules tier will validate the information received and filter out any harmful information. Once all information has been accepted, the program will display the cost of entering the race (25).AnyshirtsoverXLwilladdanadditionalchargeof25). Any shirts over XL will add an additional charge of 2. Any runner 65 or older will be charged $5 less.   Develop the application described in #1 to use dependency injection to allow the developer to change file name and locations without requiring code changes to the application itself.  

学期项目

Update the Chapter 3 Term Project to validate all information as it is entered into an HTML form (via HTML and JavaScript as shown in Chapter 4) in the interface tier. After the information is validated it is passed to the business rules tier. The business rules tier will validate the information received and filter out any harmful information. Once the information is accepted (and stored in the properties) the application will display all fields of the product stored in the warehouse of the ABC Computer Parts Company. The interface tier and the business rules tier must be separated using dependency injection (via an XML file), as shown. Your completed project should use logic similar to the examples shown in this chapter.