PHP的include和函数

包含多个文件
到此为止,本书中的每个脚本都由单个文件组成,它包含所有需要的HTML和PHP代码。但是,在你开发更复杂的Web站点时,将看到这种方法有很多局限性。PHP可以很容易地利用外部文件,从而能够把脚本分成各个不同的部分。你将频繁地使用外部文件,从PHP中提取HTML,或者分离出常用的过程。
PHP有4个用于外部文件的函数:include()、include_once()、require()和require_once()。为了使用它们,PHP脚本中将包括如下代码行:
include_once(‘filename.php’);
require(‘/path/to/filename.html’);


使用其中任何一个函数的最终结果都是,获取包含文件(included file)的所有内容,并在那一刻从父脚本(调用该函数的脚本)中删除该文件。包含文件的一个重要属性是,PHP将把包含代码视作HTML(即直接把它发送到浏览器),除非它包含PHP标签内的代码。
从功能上讲,包含文件使用什么扩展名无关紧要,它可以是.php或.html。不过,通过给文件提供一个象征性的名称有助于传达其目的(例如,HTML的包含文件可能使用.inc.html)。另请注意:可以使用指向包含文件的绝对路径或相对路径(参见框注“绝对路径与相对路径”以了解更多信息)。

绝对路径与相对路径
在引用任何外部项目(它可以是PHP中的包含文件、HTML中的CSS文档或者图像)时,可以选择使用绝对路径或相对路径。绝对路径从计算机的根目录开始指出文件的位置。不管引用(父)文件的位置是什么,这种路径总是正确的。例如,PHP脚本可以使用如下代码包含文件:
include (‘C:/php/includes/file.php’);
include(‘/usr/xyz/includes/file.php’);
假定file.php存在于指定的位置,这种包含方式将会工作(除非有任何许可问题)。第二个示例是一个UNIX(和Mac OS X)绝对路径,以防你不熟悉语法。绝对路径总是以像C:/或/这样的内容开头。
相对路径使用引用(父)文件作为起点。要移到上一级文件夹,可以一起使用两个句点。要移到一个文件夹中,可使用其名称后面接着一个斜杠。假定当前脚本位于www/ex1文件夹中,你想在www/ex2中包含一些内容,代码如下:
include(‘../ex2/file.php’);
相对路径将保持准确,即使移到另一个服务器上,只要文件保持它们的当前关系即可。
include()和require()函数在正确工作时完全一样,但是在它们失败时会表现得有所不同。如果include()函数不工作(由于某种原因而不能包含文件),就会向Web浏览器打印一个警告(参见图3-1),但是脚本会继续运行。如果require()失败,就会打印一个错误,并且脚本会中止运行(参见图3-2)。
图3-1 两个失败的include()调用生成这2条错误消息(假定将PHP配置成显示错误),但是余下的页面会继续执行
图3-2 require()函数调用失败将会打印一个错误,并中止脚本的执行。如果没有把PHP配
置成显示错误,那么脚本将终止,而不会首先打印问题(即它将是一个空白页面)
这两个函数还有一个*_once()版本,它们保证处理的文件只会被包含一次,而不管脚本可能(假定不经意地)试图包含它多少次。
require_once(‘filename.php’);
include_once(‘filename.php’);
在下一个示例中,将使用包含文件把HTML格式化代码与PHP代码隔开。这样,本章余下的示例将具有相同的外观(就好像它们都是相同Web站点的一部分一样),而不必每次都重写HTML代码。这种技术创建了一个模板系统,这是使大型应用程序具有一致性和可管理性的一种容易的方式。这些示例关注的焦点是PHP代码本身。你还应该阅读本章后面的“站点结构”框注,以便理解服务器上的组织模式。如果你有关于示例中使用的CSS或(X)HTML的任何问题,可以参阅关于这些主题的专用资源。

提示
在php.ini配置文件中,可以调整include_path设置,它指示是否允许PHP检索包含文件。
如你将在第9章中所看到的,包含敏感信息(如数据库访问)的任何包含文件都应该存储在Web文档目录外部,使得不能在Web浏览器内查看它。
因为在require()失败时,会对脚本产生更多的影响,所以我建议把它用于任务关键的包含文件(像那些连接到数据库的包含文件),为不怎么重要的包含文件使用include()。
如果PHP代码段只包含一行语句,一般将这条语句与PHP标签写在同一行:

由于CSS的工作方式,如果不使用CSS文件或者如果浏览器不能读取CSS,那么生成的结果仍然是实用的,只不过从审美观点上讲不那么令人愉悦。
站点结构
当你开始在Web应用程序中使用多个文件时,总体站点结构将变得更重要。在对站点进行布局时,有两个考虑事项:
易于维护;
安全性。
使用外部文件保存标准过程(即PHP代码)、CSS、JavaScript和HTML设计,将极大地改进维护站点的简易性,因为共同编辑的代码都放置在一个中央位置。我将频繁建立includes或templates目录来存储这些文件,将其与主要的脚本(直接在Web浏览器中访问它们)隔离开。
我建议为安全性不成问题的文档(如HTML模板)使用.inc或.html文件扩展名,为那些包含更多敏感数据(如数据库访问信息)的文档使用.php扩展名。你也可以同时使用.inc和.html或.php,以将一个文件明显地指定为某种类型的包含文件,如db.inc.php或header.inc.html。

再论处理HTML表单
第2章中一个很好的部分涉及用PHP处理HTML表单。其中所有的示例都使用了两个单独的文件:一个用于显示表单,另一个用于接收表单。虽然这种方法肯定没有任何错误,但是把整个过程集中到一个脚本中更有利。
为了让一个页面同时显示和处理表单,必须使用一个条件语句检查应该采取哪种动作(显示或处理):
if (/* form has been submitted */) {
// Handle the form.
} else {
// Display the form.
}
接下来的问题是判断表单是否已提交,这只需稍加解释即可。
如果表单使用POST方法并且提交回原表单,那么脚本会产生两类请求(参见图3-6)。第一个是加载表单的GET请求。这是大多数页面生成的标准请求。当表单提交后,会产生第二个请求——POST(只要表单使用POST方法)。了解以上内容后,你可以通过检查请求方法($_SERVER数组)判断表单提交状态:
if ($_SERVER[‘REQUEST_METHOD’] == ‘POST’) {
// Handle the form.
} else {
// Display the form.
}
图3-6 用户与服务器上的这个PHP脚本之间的交互涉及用户建立对这个脚本的两个请求
如果想让页面处理表单,然后再次显示它(例如,添加一条记录到数据库中,然后给出一个选项用于添加另一条记录),则可省略else子句:
if ($_SERVER[‘REQUEST_METHOD’] == ‘POST’) {
// Handle the form.
}
// Display the form.
使用上面的代码,如果提交了表单,脚本将会处理它们,并会在每次加载页面时显示该表单。
为了演示这种重要的技术(让同一个页面显示和处理表单),让我们创建一个简单的销量计算器(基于用户输入的数据,参见图3-7)。

提示
你还可以通过使用无值的action属性,让表单提交回它自身。

通过这样做,表单将总是提交回这个相同的页面,即使你往后更改了脚本的名称也会如此。建立黏性表单
黏性表单(sticky form)只是一种标准的HTML表单,它能记住你是如何填写它的。对最终用户来说,这是一种特别好的特性,尤其是当第一次非正确地填写它们之后,你要求他们重新提交表单时则更是如此,如图3-8所示。
要预先设置文本框中输入的内容,可使用它的value属性:

要让PHP预先设置该值,可打印相应的变量(这假定存在被引用的变量):

(这个示例也体现了PHP是HTML嵌入的这个性质的好处:可以把PHP代码放在任何位置,包括放在表单元素内)。
为了预先设置单选按钮或复选框的状态(即预先检查它们),可以把代码checked=”checked”添加到它们的输入标签中。使用PHP,可以编写如下代码:
/>
(你可以看到,语句可以很快地完成。创建表单元素,然后再添加PHP代码,就这么简单。)
为了预先设置textarea的值,可以把该值放在textarea标签之间:

注意:这里的textarea标签不像标准的text输入框那样具有value属性。
为了预先选择下拉菜单,可以把代码selected=”selected”添加到合适的选项中。如果你也使用PHP生成菜单,这实际上非常容易:
echo ‘



‘;
记住这些新信息,让我们重写calculator.php,以使它具有黏性。与前面的示例不同,现存的值放在$_POST变量中。由于引用的变量要求含有值,因而使用条件语句先检查变量是否设置后再打印它的值。
建立黏性表单
(1)在文本编辑器或IDE中打开calculator.php(参见脚本3-5)。
(2)更改数量输入框以读取(参见脚本3-6)
脚本3-6 计算器的表单现在会回忆起以前输入的值(创建黏性表单)
1 Total Estimated Cost
20

The total cost of driving ‘ . $_POST[‘distance’] . ‘ miles, averaging ‘ . $_POST
[‘efficiency’] . ‘ miles per gallon, and paying an average of $’ . $_POST[‘gallon_price’] .
‘ per gallon, is $’ . number_format ($dollars, 2) . ‘. If you drive at an average of 65 miles
per hour, the trip will take approximately ‘ . number_format($hours, 2) . ‘ hours.

‘;
21
22 } else { // Invalid submitted values.
23 echo ‘

Error!

24

Please enter a valid distance, price per gallon, and fuel efficiency.

‘;
25 }
26
27 } // End of main submission IF.
28
29 // Leave the PHP section and create the HTML form:
30 ?>
31
32

Trip Cost Calculator

33
34

Distance (in miles):

35

Ave. Price Per Gallon:
36 /> 3.00
37 /> 3.50
38 /> 4.00
39

40

Fuel Efficiency:

46

47

48
49

√提示
由于Price Per Gallon(每加仑价格)和Fuel Efficiency(燃油效率)都是数值,因此在&&条件语句中的比较值既可以用引号也可以不用引号括起来。我将它们用引号括了起来,因为从技术上讲,它们实际上是带数值的字符串。
由于这个示例中的一些PHP代码存在于HTML表单的value属性内,出错消息可能不明显。如果出现问题,可以检查页面的HTML源文件,查看PHP错误是否打印在value属性内。
应该总是用双引号括住HTML属性,特别是表单输入框的value属性。如果没有这样做,由多个单词组成的值(如Elliott Smith)在Web浏览器中只会显示为Elliott。
一些浏览器也可以记住用户在表单中输入的值,这是在使用PHP完成这个功能时独立存在的、可能会重叠的问题。

创建自己的函数
PHP具有许多内置函数,可以应对几乎所有的需要。不过,更重要的是,无论出于何种目的,PHP都能够让你定义和使用你自己的函数。建立自己的函数的语法如下:
function function_name () {
// Function code.
}
你的函数名称可以是字母、数字和下划线的任意组合,但是它必须以字母或下划线开头。你还不能为自己的函数使用现有的函数名(print、echo、isset,等等)。一个完全有效的函数定义如下:
function do_nothing() {
// Do nothing.
}
如第1章中所述,在PHP中,函数名称不区分大小写(与变量名称不同),因此可以使用do_Nothing()、DO_NOTHING()或Do_Nothing()等(但是不能使用donothing()或DoNothing())调用该函数。
函数内的代码几乎可以做任何事情,从生成HTML代码到预先格式化计算。在本章中,我将介绍几个示例,在本书余下部分,你还将看到其他一些示例。
创建自定义函数的最常见目的如下所述:
将重复代码关联到一个函数调用中;
将敏感的或复杂的过程独立出来;
让常用代码更易于重用。

提示
如果你曾经见过call to undefined function function_name(调用了未定义的函数function_name)错误,这意味着你正在调用一个未定义的函数。如果你拼错了函数的名称(在定义或调用它时)或者你无法包含定义函数的文件,就可能发生这种错误。
由于用户定义的函数会占用一些内存,所以在使用这样一个函数时应该小心谨慎。一般的规则是,最好把这些函数用于可能在脚本或Web站点的多个位置执行的大段代码。

 

创建带参数的函数

就像PHP的内置函数一样,你可以编写带参数(argument,也称为parameter)的函数。例如,strlen()接受一个要确定其字符长度的字符串作为参数。

函数可以带有任意数量的参数,但是它们的排列顺序很重要。将变量添加到函数定义中以允许使用参数:

function print_hello ($first, $last) {

// Function code.

}

用于参数的变量名称与脚本的余下部分无关(在本章末尾的框注“变量作用域”中介绍了关于这方面的更多信息),但是要尽量使用有效的、有意义的名称。

一旦定义了函数,以后就可以像调用PHP中的任何其他函数那样调用这个函数,并把字面值或变量发送给它:

print_hello (‘Jimmy’, ‘Stewart’);

$surname = ‘Stewart’;

print_hello (‘Jimmy’, $surname);

与PHP中的任何函数一样,如果发送的参数数目有误,就会导致一个错误(参见图3-13)。

图3-13 给函数发送错误数目(或类型)的参数将会引发错误

为了演示这个概念,我将重写计算表单,使用自定义函数创建每加仑价格的单选按钮。这样可以使代码更简洁。

定义带参数的函数

(1)在文本编辑器或IDE中打开calculator.php(参见脚本3-6)。

(2)在PHP起始标签后,定义create_gallon_radio() 函数(参见脚本3-8)。

function create_gallon_radio ($value) {

该函数会创建以下代码:

<input type=”radio” name= “gallon_price” value=”XXX” checked=”checked” /> XXX

或:

<input type=”radio” name= “gallon_price” value=”XXX” /> XXX

 

 

设置默认的参数值

定义自己的函数的另一个变体是预先设置参数的值。要这样做,可以在函数定义中给参数赋值:

function greet ($name, $msg = ‘Hello’) {

echo “$msg, $name!”;

}

设置默认参数值的最终结果是,当调用函数时,特定参数变为可选的。如果把一个值传递给它,就会使用传递的值;否则,就会使用默认值。

你可以根据需要为多个参数设置默认值,只要这些参数出现在函数定义的最后面即可。换句话说,必需的参数应该总是出现在最前面。

利用刚才定义的示例函数,下面的任何一个函数都将会工作:

greet ($surname, $message);

greet (‘Zoe’);

greet (‘Sam’, ‘Good evening’);

不过,只有greet()不会工作,并且如果不给$name传递一个值,就无法给$msg传递一个值(必须按顺序传递参数值,不能跳过必需的参数)。

为了利用默认参数值,下面改进create_gallon_radio()函数。此函数只创建一个名为gallon_price的单选按钮。最好能在表单中多次使用该函数,以创建单选按钮组。(但一般情况下不会像此脚本中这样使用。)

 

 

√提示

为了不给函数的参数传递任何值,可以使用空字符串(’ ‘)、NULL或FALSE。

在PHP手册中,方括号([])用于指示函数的可选参数(参见图3-16)。

图3-16 PHP手册中number_format()函数的说明,只有第一个参数是必须的,其他都是可选的。

 

 

 

从函数返回值

应该讨论的用户定义的函数的最后一个属性是返回值。一些(但不是全部)函数会返回值。例如,print将会返回1或0来指示它是否成功执行,而echo则不会返回值。另一个例子是,number_format()函数会返回一个与字符串中的字符个数相关的数字(参见图3-16)。

要让函数返回一个值,可以使用return语句。以下函数会返回给定月份和日期的所属星座:

function find_sign ($month, $day) {

// Function code.

return $sign;

}

这个函数可以返回一个值(比如一个字符串或一个数字),或者一个变量(这个函数已经产生了它的一个值)。当调用这个返回值的函数时,可以将函数结果赋予一个变量。

$my_sign = find_sign (‘October’, 23);

或者把它用作调用另一个函数时的参数。

echo find_sign (‘October’, 23);

 

√提示

return语句会终止代码执行,因此,在执行return语句之后,永远也不会运行函数内的任何代码。

一个函数可以具有多条return语句(例如,在switch语句或条件语句中),但是,至多只会调用其中的一条return语句。例如,函数通常会做以下事情:

function some_function () {if (/* condition */) {return TRUE;} else {return FALSE;} }

要让一个函数返回多个值,可以使用array()函数返回一个数组。通过在脚本3-10中把返回行更改为:

return array ($var1, $var2);

函数可以返回计算的总额和使用的税率(它可以是默认值或者用户提供的值)。

在调用返回一个数组的函数时,可使用list()函数将数组元素赋予各个变量:

list($v1, $v2) = some_function();

变量作用域

PHP中的每个变量都有一个针对它的作用域,它是指可以在其中访问变量(从而访问它的值)的一个领域。对于初学者来说,变量的作用域是它们所驻留的页面。因此,如果你定义了$var,页面的余下部分就可以访问$var,但是,其他页面一般不能访问它(除非使用特殊的变量)。

因为包含文件像它们是原始(包含)脚本的一部分那样工作,所以在include()那一行之前定义的变量可供包含文件使用(就像你已经通过$page_title和header.html所看到的那样)。此外,包含文件内定义的变量可供include()那一行之后的父(包含)脚本使用。

当使用你自己定义的函数时,所有这些都将变得不那么明显。这些函数具有它们自己的作用域,这意味着在一个函数内使用的变量不能在其外部使用,在一个函数外部定义的变量不能在其内部使用。由于这个原因,函数内部的变量可以具有与其外部的变量相同的名称,但是它们仍然是完全不同的变量,并且具有不同的值。对于大多数初级程序员来说,这是一个使人糊涂的概念。

要改变一个函数内的变量的作用域,可以使用global语句。

function function_name() {

global $var;

}

$var = 20;

function_name(); // Function call.

在这个示例中,函数内部的$var现在与函数外部的$var相同。这意味着变量$var已经具有一个值20,如果在函数内部改变了这个值,外部的$var值也会改变。

避开变量作用域的另一个方法是利用超全局变量:$_GET、$_POST、$_REQUEST等。这些变量在你的函数内是自动可访问的(因此,它们是超全局变量)。也可以添加元素到$GLOBALS数组中,使得可以在函数内使用它们。

也就是说,最好不要在函数内使用全局变量。在设计函数时,应该使它们根据需要接受每个值作为参数,并根据需要返回任何值。依靠函数内的全局变量将使得它们更依赖于上下文,因而不太有用。