韶华将逝,雄心未已;欲与众君,共习此技.

《PHP和MySQL Web开发》学习笔记(七)

上一篇 / 下一篇  2007-07-16 15:53:29 / 天气: 晴朗 / 心情: 平静 / 个人分类:PHP & MySQL

第5章 代码重用与函数编写
 
5.1 代码重用
在理想情况下,一个新的项目是这样创建的:它将已有的可重新利用的组件进行组合,并将新的开发难度降低到最小。
 
5.1.1 成本
如果要编写商业代码,应该尽量限制结构中所用到的代码行数。一个最常使用的方法就是:重新使用已有的代码,而不是为一个新任务编写一个和原来代码只有微小区别的新代码。更少的代码意味着更低的成本。
修改已有的代码可能会比编写新代码更加困难。
 
5.1.2 可靠性
 
使用现存的成熟的代码通常要比新鲜的“绿色”代码更可靠。
 
5.1.3 一致性
系统的外部接口应该是一致的,其中包括用户接口和系统的外部接口。
只要原来的代码是模块化的而且编写良好,那么重复使用代码还会节省许多工作
 
5.2 使用require()和include()函数
 
5.2.1 require()函数
当需要一个文件的时候,可以使用require()语句。require()语句将被请求的文件内容代替,然后再执行脚本。
当使用require()语句时,必须注意处理文件扩展名和PHP标记的不同方式。
 
5.2.2 文件扩展名和require()语句
PHP并不会查看所需文件的扩展名。这就意味着,只要不想直接调用这个文件,就可以任意命名该文件。当使用require()语句载入文件时,它会作为PHP文件的一部分被执行。
通常,如果PHP语句放在一个HTML文件(例如,名为page.html的文件)中时,它们是不会被处理的。PHP通常用来解析扩展名被定义成.php的文件(在Web服务器配置文件中可能不是这样)。但是,如果通过require()语句载入这个page.html,文件内的任何PHP命令都会被处理。因此,可以使用任何扩展名来命名包含文件,但是尽量遵循一个约定,例如将扩展名命名为.inc是一个很好的办法。
需要注意的一个问题是,如果扩展名为.inc中一些其他的非标准扩展名的文件保存在Web文档树中,而且用户可以在浏览器中直接载入它们,用户将可以以普通文本的形式查看源代码,包括任何密码。因此,将被包含文件保存在文档树之外,或使用标准的文件扩展名是非常重要的。
 
5.2.3 PHP标记和require()函数
 
5.3 使用require()制作Web站点的模版
如果Web页面具有一致的外观,可以在PHP中使用require()语句将模版和标准元素加入到页面中。
正如前面所提到的,在通过require()调用的时候,文件的名称并不会影响对它们的处理。一个常见并且是可选的约定就是调用那些包含在其他文件something.inc(此处inc代表include)中的部分文件代码,这些文件代码若不被调用,将会停止执行。还有一个常用的好办法就是:将包含文件放在一个脚本可读的目录中,但不允许包含文件通过Web服务器自行载入——也就是,放在Web文档树之外。这样会防止下面两种情况的发生:a) 如果文件扩展名是.php,但只包含部分页面或脚本,此时可能会引起错误。b) 如果已经使用了其他的扩展名,别人就可以读取源码。
如果希望保证一个文件将被当作普通文本或HTML,而且不会执行任何PHP,可以使用readfile()作为替代方法。这个函数将回显文件内容,不会对其进行解析。如果使用的是用户提供的文本,这可能就是一个重要的安全问题。
 
5.3.1 使用include()
require()语句和include()语句几乎是等价的。二者的差异在于,当这两个语句调用失败后,requir()将给出一个致使错误,而include()只是给出一个警告。
 
5.3.2 使用require_once()和include_once()
require_once()和include_once()的用途是确保一个被包含文件只有被包含一次。
当使用require()和inlude()包含函数库时,这个功能就非常有用。使用这些函数可 防止意外地多次包含相同的函数军,从而导致函数的重复定义并产生错误。
 
5.3.3 使用auto_prepend_file和auto_append_file
如果希望使用require()将页眉和页脚加入到每个页面中,还有另外一种办法。在配置文件php.ini中有两个选项auto_prepend_file和auto_append_file。通过这两个选项来设置页眉和页脚,可以保证它们在每个页面的前后被载入。使用这些指令包含的文件可以像使用inlude()语句包含的文件一样;也就是,如果该文件不存在,将产生一个警告。
如果使用了这些指令,就不需要再输入include()语句,但页眉和页脚在页面中不再是页面的可选内容。
如果使用的是Apache Web服务器,可以对单个目录进行不同配置选项的修改。这样做的前提是服务器允许重设其主配置文件。要给目录设定自动前加入和自动追加,需要在该目录中创建一个名为.htaccess的文件。请注意,其语法与配置文件php.ini中的相应选项有所不同。许多php.ini中的配置设定也可以按这种方法进行修改。
在.htaccess中设置选项,而不是php.ini或是在Web服务器的配置文件中进行设置,将带来极大的灵活性。可以在一台只影响你的目录的共享机器上进行。不需要重新启动服务器而且不需要管理员权限。使用.htaccess方法的一点缺点就是目录中每个被读取和被解析的文件每次都要进行处理,而不是只在启动时处理一次,所以性能会有所降低。
 
5.4 在PHP中使用函数
函数是一个给出了调用接口的自包含模块,它可以执行一些任务,还可以返回结果(可选的)。
 
5.4.1 调用函数
在测试时,你会发现函数phpinfo()是非常有用的,因为它显示了已安装的PHP的版本,关于PHP的信息,Web服务器的设置和众多的PHP和服务器变量的值。这个函数不需要任何参数,通常可以忽略它的返回值,所以,可以使用如下所示方式调用函数phpinfo():phpinfo();
大多数函数都需要一个或更多的参数,它们都是函数的输入参数。我们通过将数据或变量名放在函数名称后面的括号内,从而以参数形式传给函数。为函数提供一个参数并对其进行调用。
大多数函数都需要一个或更多的参数,它们都是函数的输入参数。我们通过将数据或变量名放在函数后面的括号内,从而以参数形式传给函数。
参数可以是任何数据类型,但特定的函数通常会要求特定的数据类型。
可以通过函数原型来了解函数所需的参数个数,每一个参数所表示的对象以及每一个参数的数据类型。
请注意,一个具有多个可选值的函数,必须按照从左到右的顺序使用默认值。
 
5.4.2 调用未定义的函数
两如果调用一个并不存在的函数,会得到错误信息。如果看到这个错误信息,必须对件事情进行检查:函数名称的拼写是否正确;这个函数是否存在于所用的PHP版本中。看到这个错误信息的另一个原因就是所调用的函数是PHP扩展的一部分,而该部分并没有被载入。例如,如果尝试使用gd库(图像操作函数库)的某些函数而没有安装gd,将看到这个错误信息。
通常,PHP给出的错误信息是非常有用的。它可以告诉我们错误出现在哪个文件中,错误在文件中的哪一行,以及我们试图调用的函数名称。这样就可以很容易地找到并纠正错误。
 
5.4.3 理解字母大小写和函数名称
请注意,函数调用将不区分大小写,所以调用function_name()、Function_Name()或FUNCTION_NAME()都是有效的,而且都将返回相同的结果。可以按照便于自己阅读的方式任意使用大小写,但应该尽量保持一致。本书和和大多数PHP文件使用的命名惯例是:所有都用小写字母。
注意到函数名称和变量名称是不同的,这一点很重要。变量名是区分大小写的,所以$Name和$name是两个不同的变量,但Name()和name()则是同一个函数。
 
5.5 理解为什么要定义自己的函数
声明一个函数可以让我们像内置函数那样使用自己的代码。只要简单地调用这个函数并提供给它必须的参数。这就意味着,在整个脚本中,都可以调用和多次重复使用相同的函数。
 
5.6 了解基本的函数结构
一个函数声明将创建或者声明一个新的函数。声明是以关键字function开始的,接下来,给出函数名称和必要的参数,然后再给出每次调用这个函数时要执行的代码。
内置函数在所有的PHP脚本中都可以使用,但是如果声明了自己的函数,它们只是在声明它们的脚本中可以使用。将经常用到的函数包含在一个文件中是一个很好的主意。然后可以在所有的脚本中可以使用。将经常用到的函数包含在一个文件中是一个很好的主意。然后可以在所有的脚本中调用require()语句,这样这些函数就可以使用了。
在一个函数中,花括号包括了完成所要求任务的代码。在花括号中,可以包含任何在PHP脚 本的其他地方都合法的代码,其中包括函数调用、新的变量或函数声明、require()或include()语句类声明以及HTML脚本。
在给函数命名的时候,最重要的就是函数名称必须精练都又要有描述性。如果函数是用来创建页眉的,那么pageheader()或page_header()是不错的名称。
函数命名具有如下几个限制:函数名称不能和已有的函数重名;函数名称只能包含字母、数字和下划线;函数名称不能以数字开始。
许多语言允许重复使用函数名称,这个特性叫做函数的重载。但是PHP不支持函数重载,所以自定义函数不能和内置函数或是用户已定义的函数重名。请注意,虽然每个PHP脚本知道所有的内置函数,但对于用户定义的函数,PHP只能识别那些存在于本脚本之中的。这就意味着,虽然可以在不同的文件中重复使用一个函数名,但这会引起混乱,所以应该避免。
请注意,虽然$name并不是一个函数的合法名称,但是一个类似于如下所示的函数调用:$name();也可以正确地执行,这是根据$name的值来确定的。其原因就是PHP可以取出保存在$name中的值,寻找具有那个名称的函数,并且调用该函数。这种函数类型被称为可变函数,而且有时候是有用的。
 
5.7 使用参数
要使函数正常工作,它们中的大多数都需要一个或多个参数。参数允许将数据传给函数。
传递参数允许我们获得在函数外面生成的数据。
和内置函数一样,用户定义函数可以有多个参数和可选参数。
参数的可选值不用全部给出;可以给出一部分而忽略一部分。参数将会按照从左到右的顺序进行赋值。
请记住,不能漏掉一个可选参数而给出参数列表中的后一个参数。这是编程过程中的常见错误,也是可选参数在每个参数列表中最后被指定的原因。
也可以声明能够接收可变参数数量的函数。通过3个帮助器函数:func_num_args()、func_get_arg()以及func_get_args(),可以确定已经传递了多少个参数以及这些参数的值。func_num_args()函数将返回传入的参数个数。而func_get_args()函数将返回参数的数组。或者,可以使用func_get_arg()函数一次获得一个参数,该函数需要以希望访问的参数个数作为参数(参数从0开始)。
 
5.8 理解作用域
变量的作用域可以控制变量在哪里是可见并且可用的。不同的编程语言有不同的变量作用域规则。PHP具有相当简单的规则:在函数内部声明的变量作用域是从声明它们的那条语句开始到函数末尾。这叫做函数作用域。这些变量称为局部变量;在函数外部声明的变量作用域是从声明它们的那条语句开始到文件末尾,而不是函数内部。这叫做全局作用域。这些变量称为全局变量;特殊的超级全局变量在函数内部和外部都是可见的;使用require()和include()并不影响作用域。如果这两个语句用于函数内部,函数作用域适用。如果它不在函数内部,全局作用域适用;关键字“global”可以用来手动指定一个在函数中定义或使用的变量具有全局作用域;通过调用unset($variable_name)可以手动删除变量。如果变量被删除,它就不在参数所指定的作用域中了。
在函数内部创建的变量具有全局域,其作用域是从定义它的那行语句开始的。函数的声明可以在调用它之前名之后(请注意,函数的作用域不同于变量的作用域!),因此在哪里声明函数并不重要,重要的是在哪里调用并执行其中的代码。
当一个变量要在整个脚本中都要用到时,也可以在脚本的开始处使用关键字“global”。这可能是使用关键字global更常见的办法。
在函数的内部和外部重复命名一个变量名是合法的,而且两者互不影响。但是一般来说,这并不是一个好办法,因为如果不认真阅读代码并考虑作用域,人们可能会认为这些变量都是同一个。
 
5.9 参数的引用传递和值传递
如果希望编写一个名为increment()的函数来增加一个变量的值,如下所示:
function increment($value, $amount=1)
{
 $value = $value + $amount;
}
这段代码是没有用的。下面测试代码的输出结果是“10”。
$value = 10;
increment($value);
ech $value;
$value的内容没有被修改。这要归因于作用域规则。这段代码将创建一个名为$value的变量,它的值是10。然后调用函数increment()。当函数被调用时,它内部的变量$value被创建。它的值加上1,所以$value在函数内部的值为11,直到函数结束,接下来我们返回到调用它的代码。在这段代码中,变量$value是一个不同的变量,具有全局域,所以它的值没有变。
解决这个问题的一个办法是将函数内的$value声明为全局变量,但这意味着为了使用这个函数,要进行变量运算的变量需要被命名为$value。一个更好的办法是引用传递。
通常,函数获取参数的方式是值传递。当传递一个参数的时候,一个新的并且包含该传入值的变量被创建。它是原来那个变量的拷贝。可以以任意的方式修改它,但函数外部原来变量的值是不会改变的。
更好的办法是使用引用传递。在这里,在参数被传递给函数的时候,函数不会再创建一个新变量,而是函数获得一个原来变量的引用。这个引用有一个变量名称,它以美元符号开始,可以像另一个变量那样使用它。其区别在于不是获得变量本身的值,而是指向原来的值 。任何对该引用的修改都会影响到原始变量值。
可以通过在函数定义的参数名前加一个地址符(&)来指定参数的引用传递。在函数调用处不用修改。
前面的increment()的例子就可以修改为引用传递参数,这样它就可以正常工作了。
function increment(&$value, $amount=1)
{
 $value=$value+$amount;
}
现在,我们有了一个可运行的函数,而且可以任意给想要进行增量运算的变量命名。正如前面所提到的,在函数的内外使用同样的名称会引起混淆,所以我们给主脚本变量一个新的名称。如下所示的测试代码在调用increment()之前将显示10,调用之后会显示11。
$a=10;
echo $a.'<br />';
increment ($a);
echo $a.'<br />';
 
5.10 从函数中返回
关键字“return”将终止函数的执行。当一个函数的执行结束时,要么是因为所有的命令都执行完了,要么就是因为使用了关键字“return”。在函数结束后,程序返回到调用函数的下一条语句。
一个错误条件是在程序执行到函数末尾之前使用“return”语句中断函数执行的最常见原因。
内置函数isset()将告诉我们一个变量是否已经被创建并被赋值了。
 
5.11 从函数返回一个值
通常,执行特定任务但又不返回任何具体值的函数将返回“true”或“false”来表示函数执行是否成功。布尔值“true”和“false”可以分别用整数“1”和“0”来表示,虽然它们是不同的数据类型。
 
5.12 实现递归
递归函数就是函数调用自己本身。这些函数特别适用于浏览动态数据结构,例如列表和树。
但是,几乎没有基于Web的应用程序要求使用如此复杂的数据结构,所以我们很少使用递归函数。在很多情况下,递归可以用来取代循环,因为二者都是重复做一些事情。递归函数比循环慢而且要占用更多的内存,所以应该尽可能多用些循环
在递归方法中有一些非常优美而精确的东西。但是,在大多数情况下,最好还是使用循环方法。请注意,它没有递归方法长(虽然循环函数并不总是这样),但却也能实现相同的功能。最主要的不同在于,递归函数将在内存中创建几个自身的拷贝,而且将产生多次函数调用的开销。
当递归方法的代码比循环方法的代码更简短、更美观的时候,我们可能会选择使用递归,但是在应用领域通常不会这样。

虽然递归看上去更美观,但程序员常会忘记给出递归的终止条件。这意味着函数会一直重复下去直到服务器内存耗尽,或者达到了最大调用次数。
 
5.13 进一步学习
 
5.14 下一章

TAG: PHP和MySQL Web开发 学习笔记

引用 删除 坤仔   /   2007-09-04 16:17:54
3
很到位,谢谢!理解吸收中。。。。。
 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

数据统计

  • 访问量: 73476
  • 日志数: 265
  • 书签数: 19
  • 建立时间: 2007-05-03
  • 更新时间: 2008-04-15

RSS订阅

Open Toolbar