第5章逻辑组:函数和文件

当你在编写计算机程序时,懒惰是一种美德。重用已编写的代码可以更轻松地完成尽可能少的工作。函数是代码重用的关键。一个 功能是一组命名的,你可以通过调用函数名,而不是重新输入的语句只是执行语句。这可以节省时间并防止错误。此外,函数可以更轻松地使用其他人编写的代码(正如您通过使用PHP引擎作者编写的内置函数所发现的那样)。

定义自己的函数和使用它们的基础知识将在下一节“声明和调用函数”中进行介绍。当你调用一个函数时,你可以给它一些值来操作它。例如,如果您编写了一个函数来检查是否允许用户访问当前网页,则需要为该函数提供用户名和当前网页名称。这些值称为 参数。 “将参数传递给函数”解释了如何编写接受参数的函数以及如何使用函数内部的参数。

有些功能是单行道。你可以传递他们的论点,但你没有得到任何回报。一 print_header(),打印的HTML页面的顶部功能可以采取含有页面标题的说法,但它执行后它不会给你任何信息。它只显示输出。大多数功能在两个方向上移动信息。前面提到的访问控制功能就是一个例子。该函数返回一个值:( true 访问被授予)或false(访问被拒绝)。这个值是称为返回值。您可以像使用任何其他值或变量一样使用函数的返回值。返回值在“从函数返回值”中讨论。

函数内部的语句可以像函数外部的语句一样使用变量。但是,函数内部和函数外部的变量存在于两个不同的世界中。PHP引擎将一个$name在函数内部调用的变量和一个$name在函数外部调用的变量视为两个不相关的变量。“了解变量范围”解释了哪些变量可用于程序的哪些部分的规则。理解这些规则很重要 – 弄错了,你的代码依赖于未初始化或不正确的变量。这是一个很难追查的错误。

因为函数非常适合重用,所以创建完整的函数定义的单独文件然后从程序中引用这些文件很方便。这使得不同的程序(以及同一程序的不同部分)共享功能而不会重复。“在另一个文件中运行代码”解释了PHP在程序中将多个文件捆绑在一起的工具。

声明和调用函数
创造一个新函数,使用 function关键字,紧随其后通过函数名称,然后,在花括号内,功能体。例5-1声明了一个名为的新函数 page_header()。1

例5-1。声明一个函数

函数名称遵循相同的规则作为变量名称:它们必须以字母或下划线开头,名称中的其余字符可以是字母,数字或下划线。PHP引擎不会阻止您使用具有相同名称的变量和函数,但如果可以,则应该避免使用它。许多具有相似名称的东西使得程序难以理解。

该page_header()功能在示例5-1中定义的内容可以像内置函数一样调用。 例5-2用于page_header() 打印完整页面。

例5-2。调用一个函数

可以在调用函数之前或之后定义函数。PHP引擎读取整个程序文件并在运行文件中的任何命令之前处理所有函数定义。例5-3中的page_header()和page_footer()函数都成功执行,即使它在调用之前定义并在调用之后定义。page_header()page_footer()

例5-3。在调用它们之前或之后定义函数

将参数传递给函数

虽然某些功能(例如 page_header()在上一节中)总是做同样的事情,但其他功能对可以改变的输入进行操作。 提供给函数的输入值称为 参数。参数增加了函数的功能,因为它们使函数更加灵活。您可以修改 page_header()以获取包含页面颜色的参数。修改后的函数声明如例5-4所示。

例5-4。使用参数声明函数

在函数声明中,$color 在函数名后面的括号之间添加。这使得函数内部的代码使用一个名为的变量$color,该变量保存调用时传递给函数的值。例如,您可以像这样调用函数:

这设置$color为cc00cc 内部page_header2(),因此它打印:

如示例5-4所示定义一个带参数的函数时,必须在调用时将参数传递给函数。如果在没有参数值的情况下调用函数,PHP引擎会发出警告。例如,如果您这样调用page_header2():

引擎打印一条如下所示的消息:

要避免此警告,请通过在函数声明中指定缺省值来定义一个函数以获取可选参数。如果在调用函数时提供了值,则该函数使用提供的值。如果在调用函数时未提供值,则该函数使用默认值。要指定默认值,请将其放在参数名称后面。例5-5设置$colorto 的默认值 cc3399。

例5-5。指定默认值

调用page_header3(‘336699’)产生与调用相同的结果page_header2(‘336699′)。当每个函数的主体执行时,$color具有值336699,该值是为标签的bgcolor属性 打印的颜色<body>。但是,page_header2()如果没有参数产生警告,您可以在page_header3()没有参数的情况下运行 ,$color设置为 cc3399。

对于参数的默认值必须是文字,如 12,cc3399或 Shredded Swiss Chard。它们不能是变量。以下是不行的,将导致PHP引擎停止运行您的程序:

要定义接受多个参数的函数,请在函数声明中使用逗号分隔每个参数。在例5-6中,page_header4()有两个参数:$color和$title。

例5-6。定义一个双参数函数

要在调用函数时传递多个参数,请在函数调用中用逗号分隔参数值。实施例5-7调用page_header4()与值$color和$title。

例5-7。调用双参数函数

例5-7打印:

在例5-6中,两个参数都是必需的。您可以在带有多个参数的函数中使用相同的语法来表示默认参数值,就像在带有一个参数的函数中一样。但是,所有可选参数都必须在任何必需参数之后。例5-8显示了定义具有的三参数函数的正确方法 一个,两个或三个可选参数。

例5-8。多个可选参数

所有可选参数必须位于参数列表的末尾,以避免歧义。如果page_header7()可以使用强制性的第一个参数$color,可选的第二个参数$title和强制的第三个参数来定义$header,那么page_header7(’33cc66′,’Good Morning’)意味着什么?该’Good Morning’参数可以是一个值$title或 $header。在任何强制参数之后放置所有可选参数可以避免这种混淆。

对作为参数传递给函数的变量所做的任何更改都不会影响函数外部的变量。2在例5-9中,$counter函数外部的值 不会改变。

例5-9。改变参数值

例5-9打印:

传递$counter作为参数 countdown()告诉PHP引擎在函数的开头复制$counterinto 的值$top,因为$top是参数的名称。无论$top 函数内部发生什么都不会影响 $counter。一旦将值 $counter复制到函数的持续时间内$top, $counter就会超出图片。

即使参数与函数外部的变量具有相同的名称,修改参数也不会影响函数外部的变量。如果countdown()在示例5-9中更改了以便调用其参数 $counter而不是$top,$counter则函数外部的值不会更改。函数外部的参数和变量碰巧具有相同的名称。它们完全没有连接。

从函数返回值

标题打印您在本章中看到的函数通过显示一些输出来执行操作。除了打印数据或将信息保存到数据库之类的操作之外,函数还可以计算一个称为 返回值的值,该值可以在以后的程序中使用。要捕获函数的返回值,请将函数调用分配给变量。例如5-10存储内置函数的返回值number_format()的变量 $number_to_display。

例5-10。捕获返回值

就像例1-6,例5-10 打印:

将函数的返回值赋给变量就像为变量赋值或字符串一样。该语句 $number = 57 表示“存储57在变量中 $number。”该语句 $number_to_display = number_format(321442019)表示“ number_format() 使用参数调用函数321442019并将返回值存储在其中 $number_to_display。”一旦将函数的返回值放入变量,就可以使用该变量和值它包含与程序中的任何其他变量一样。

要从您编写的函数返回值,请使用return带有值的 关键字返回。当函数执行时,一旦遇到 return关键字,它就会停止运行并返回相关的值。示例5-11定义了一个函数,该函数在添加税和小费后返回餐馆支票的总金额。

例5-11。从函数返回值

restaurant_check()返回的值可以像程序中的任何其他值一样使用。例5-12 在if()语句中使用返回值。

例5-12。在if()语句中使用返回值

特定return语句只能返回一个值。你不能用类似的东西返回多个值return 15, 23。如果要从函数返回多个值,可以将不同的值放入一个数组中,然后返回该数组。

例5-13显示了一个修改版本, restaurant_check()它返回一个双元素数组,其中包含添加提示之前和添加提示之后的总量。

例5-13。从函数返回数组

例5-14使用返回的数组 restaurant_check2()。

例5-14。使用从函数返回的数组

虽然您只能使用return语句返回单个值 ,你可以return在函数中有多个 语句。return函数内部程序流到达的第一个 语句导致函数停止运行并返回一个值。这不一定是return最接近函数开头的 语句。例5-15将示例5-12中的现金或信用卡逻辑移动到确定适当支付方法的新功能中。

例5-15。函数中的多个return语句

例5-16payment_method()通过传递它来使用新函数结果来自restaurant_check()。

例5-16。将返回值传递给另一个函数

例5-16打印以下内容:

这是因为量restaurant_check() 回报是小于20。这被传递到payment_method()在$total参数。在第一比较payment_method(),之间 $amount和$cash_on_hand,是 false,所以代码在else 块内payment_method()执行。这会导致函数返回字符串cash。

第3章讨论的关于真值的规则适用于像其他值一样返回函数的值。您可以利用此功能在if()语句和其他控制流构造中使用函数。例5-17 通过restaurant_check()从if()语句的测试表达式中调用函数来决定要做什么 。

例5-17。使用if()返回值

为了评估例5-17中的测试表达式,PHP引擎首先调用该 restaurant_check()函数。然后将函数的返回值与其进行比较20,就像它是变量或文字值一样。如果 restaurant_check()返回小于20的数字(在这种情况下它执行),则执行第一个print 语句。否则,第二个print 语句运行。

测试表达式也可以只包含一个没有比较的函数调用或其他运算符。在这样的测试表达式中,函数的返回值将转换为true或 false根据“理解true和false”中概述的规则。如果返回值为 true,则测试表达式为 true。如果返回值是false,则测试表达式也是 如此。函数可以显式返回true或false 使其更明显应该在测试表达式中使用。例5-18中的can_pay_cash()功能可以确定我们是否可以支付现金。

例5-18。返回true或false的函数

在例5-18中,can_pay_cash()函数比较了它的两个参数。如果 $amount更大,则函数返回 true。否则,它返回 false。该if()功能一心一意之外声明追求其作为使命 if()陈述调查其测试表达式的真值。由于本次测试表达式是一个函数调用,它调用can_pay_cash()与两个参数 20和$total。函数的返回值是测试表达式的真值,并控制打印哪条消息。

就像您可以将变量放在测试表达式中一样,您可以将函数的返回值放在测试表达式中。在您调用返回值的函数的任何情况下,您可以考虑调用该函数的代码,例如 restaurant_check(15.22,8.25,15),在程序运行时由函数的返回值替换。

一个常见的捷径是在测试表达式中使用带赋值运算符的函数调用,并依赖于这一事实分配的结果是分配的值。这使您可以调用函数,保存其返回值,并true在一个步骤中检查返回值是否全部。例5-19演示了如何执行此操作。

例5-19。在测试表达式中分配和函数调用

在示例5-19中,如果计算出的帐单(包括税和小费)大于 ,则complete_bill()函数返回。如果账单小于或等于,则返回账单金额。当函数外部的语句评估其测试表达式时,会发生以下情况:false$cash_on_hand$cash_on_handif()

  1. complete_bill()用参数调用 15.228.25和 15,和20
  2. 返回值complete_bill()分配给$total
  3. 的分配(其,请记住,是相同的值被分配)的结果被转换为任一true 或false并用作测试表达的最终结果。

了解可变范围

正如您在例5-9中看到的,内部发生了变化一个功能 保存参数的变量不会影响函数外部的变量。这是因为函数内部的活动发生在不同的范围内。在函数外定义的变量是称为 全局变量。它们存在于一个范围内。函数内部定义的变量称为局部变量。每个函数都有自己的范围。

想象一下,每个功能都是大公司的一个分支机构,任何功能之外的代码都是公司总部。在费城分公司,同事们用他们的名字互相称呼:“爱丽丝在这份报告上做了很多工作”,或者“鲍勃从未在我的咖啡中加入适量的糖。”这些陈述谈到了费城(一个函数的局部变量),并且对在另一个分支机构(另一个函数的局部变量)或公司总部(全局变量)工作的Alice或Bob一无所知。

本地和全局变量的工作方式类似 $dinner函数内部调用的变量 ,无论它是否是该函数的参数,都与函数$dinner 外部调用的变量和 $dinner另一个函数内部调用的变量完全断开。例5-20说明了不同范围内变量的不相关性。

例5-20。可变范围

例5-20打印:

在这两个函数中,before $dinner设置为函数内部的值,它没有值。全局变量 $dinner在函数内部没有任何影响。$dinner但是,一旦 在函数内部设置,它不会影响$dinner 任何函数外的全局集或$dinner另一个函数中的变量。在每个函数内部,$dinner 指的是本地版本,$dinner并且与在另一个函数中碰巧具有相同名称的变量完全分开。

但是,与所有类比一样,可变范围与公司组织之间的类比并不完美。在公司,您可以轻松地参考其他地方的员工; 费城的人们可以谈论“总部的爱丽丝”或“亚特兰大的鲍勃”,而总部的领主可以决定“费城的爱丽丝”或“查尔斯顿的鲍勃”的未来。但是,根据变量,你可以访问全球来自函数内部的变量,但是您无法从该函数外部访问函数的局部变量。这相当于分支机构的人们能够谈论总部的人而不是其他分支机构的任何人,以及总部的人们无法在任何分支机构谈论任何人。

有两种方法可以从函数内部访问全局变量。最直接的方法是在一个名为的特殊数组中查找它们$GLOBALS。每个全局变量都可以作为该数组中的元素访问。例5-21演示了如何使用该$GLOBALS数组。

例5-21。$ GLOBALS数组

例5-21打印:

例5-21$dinner从函数内部 访问全局 $GLO⁠BALS⁠[​’din⁠ner’]。该 $GLOBALS数组还可以修改全局变量。 例5-22显示了如何做到这一点。

例5-22。使用$ GLOBALS修改变量

例5-22打印:

在hungry_dinner()函数内部, $GLOBALS[‘dinner’]可以像任何其他变量一样进行修改,并且修改会更改全局变量 $dinner。在这种情况下, $GLOBALS[‘dinner’]使用示例2-19中的连接运算符附加一个字符串。

访问函数内部全局变量的第二种方法是使用global关键字。这告诉PHP引擎在函数内进一步使用命名变量应该引用具有给定名称的全局变量,而不是局部变量。这称为“将变量带入本地范围。” 示例5-23显示了 global关键字在起作用。

例5-23。全局关键字

例5-23打印:

第一个print语句显示全局变量的未修改值$dinner。该 global $dinner行 vegetarian_dinner()意味着$dinner函数内部的任何使用都是 指全局的 $dinner,而不是具有相同名称的局部变量。因此,print函数中的第一个语句打印已设置的全局值,下一行的赋值会更改全局值。由于函数内部的全局值已更改,因此函数print外部的最后一个语句也会打印更改的值。

该global关键字可以同时与多个变量名一起使用。只需用逗号分隔每个变量名称即可。例如:

小费
通常,使用$GLOBALS 数组来访问函数内的全局变量而不是 global关键字。使用$GLOBALS 提供有关您正在处理全局变量的每个变量访问的提醒。除非你正在编写一个非常短的函数,否则很容易忘记你正在处理一个全局变量,global并且为什么你的代码行为不端而感到困惑。依赖于 $GLOBALS数组需要一些额外的输入,但它确实为您的代码的可懂度提供了奇迹。

您可能已经注意到使用该$GLOBALS数组的示例有些奇怪。这些示例 $GLOBALS在函数内部使用,但不$GLOBALS使用global关键字引入本地范围。$GLOBALS无论是在函数内部还是外部使用, 数组始终在范围内。这是因为它 $GLOBALS是一种特殊的预定义变量,称为自动全局变量 。自动全局变量是变量 可以在PHP程序中的任何位置使用,而无需将它们纳入范围。他们就像一个知名的员工,总部或分公司的每个人都用他的名字来称呼他们。

自动全局变量始终是自动填充数据的数组。它们包含诸如提交的表单数据,cookie值和会话信息之类的内容。章7和10分别描述了汽车专用的全局变量是在不同背景下是有用的。

执行关于参数和返回值的规则

除非你告诉PHP引擎 否则,函数参数 和返回值对其类型或值没有任何约束。例5-9中的countdown()函数假定它的参数是一个数字,但你可以传递一个字符串,例如作为参数,PHP引擎不会抱怨。”Caramel”

类型声明是一种表达参数值约束的方法。这些告诉PHP引擎参数允许的值是什么类型,因此它可以在提供错误类型时向您发出警告。表5-1显示了PHP引擎理解的不同类型的声明以及PHP引入的版本支持。

表5-1。输入声明

DeclarationArgument ruleMinimum PHP version
arrayMust be an array5.1.0
boolMust be boolean: true or false7.0.0
callableMust be something representing a function or method that can be calleda5.4.0
floatMust be a floating-point number7.0.0
intMust be an integer7.0.0.
stringMust be a string7.0.0.
Name of a classMust be an instance of that class (see Chapter 6 for more information about classes and instances).5.0.0

a这可以是一个包含有效函数名的字符串,一个双元素数组,其中第一个元素是一个对象实例,第二个元素是一个包含方法名称的字符串,或者其他一些东西。有关所有详细信息,请参见 http://www.php.net/language.types.callable。

在定义函数时, 类型声明在参数名称之前。例5-24显示了示例5-9中的函数,其中包含适当的int类型声明。

例5-24。声明参数类型

之间的唯一区别实施例5-9和实施例5-24是在int后countdown(和前$top。当countdown()传递一个有效的整数(例如5)时,代码运行得很好。如果传递了另一种类型的值,则PHP引擎会抱怨。例如,如果你打电话的countdown(“grunt”);时候 使用PHP 7,然后您收到类似于以下内容的错误消息:

在错误消息中,PHP引擎告诉你一个TypeError,指示哪个argument(1)传递给哪个function(countdown())具有类型不匹配,包括参数类型应该是integer什么()以及参数类型实际是什么(string)。您还可以获得有问题的函数调用的位置以及定义被调用函数的位置的信息。

在PHP 7中,这TypeError是一个可以使用异常处理程序捕获的异常。“指示异常问题”提供了有关如何捕获程序中的异常的详细信息。3

PHP 7还支持函数返回的值类型的类型声明。要强制检查函数的返回类型,请:在)关闭参数列表之后放置一个,然后返回返回类型声明。例如,示例5-25显示了使用返回类型声明扩充的示例5-26中的restaurant_check()函数。

例5-25。声明返回类型

如果示例5-25中的函数返回除a之外的任何内容float,则PHP引擎生成一个TypeError。

警告
对于PHP 7中的标量类型声明,默认情况下声明的强制执行并不是绝对严格的。

即使使用类型声明,PHP 7也会尝试转换参数的类型或返回值,该值实际上与类型声明不匹配但可以匹配它。数值以静默方式转换为字符串,包含数字的字符串将以静默方式转换为相应的数字类型。

你可以在特定的情况下关闭这个松散的goosey默认值 通过放在文件declare(strict_types=1);的顶部文件。然后,该文件中任何函数调用的参数和返回值必须与类型声明匹配(但您仍然可以将整数作为声明为的参数传递float)。

您无法全局强制执行严格的输入。您必须在要使用它的每个文件中声明它。

在另一个文件中运行代码

到目前为止我们看到的PHP代码示例大多是自包含的单个文件。使用的任何变量或函数也在同一文件中定义。随着程序的增长,当您将代码拆分为不同的文件时,它们更易于管理。该require指令告诉PHP引擎加载位于不同文件中的代码,这使得在许多地方重用该代码变得容易。

例如,考虑本章前面定义的一些函数。我们可以将它们组合成一个文件并将其保存为restaurant-functions.php,如例5-26所示。

例5-26。在自己的文件中定义函数

假设示例5-26保存为restaurant-functions.php,则另一个文件可能引用它,如图实施例5-27,与require ‘restaurant-functions.php’;。

例5-27。引用单独的文件

例5-27中的require ‘restaurant-functions.php’;行告诉PHP引擎停止读取当前正在读取的文件中的命令,去读取restaurant-functions.php文件中的所有命令,然后返回到第一个文件并继续。在这个例子中,restaurant-functions.php只定义了一些函数,但是加载的文件可以包含任何有效的PHP代码。如果加载的文件包含语句,那么PHP引擎将打印出打印输出的内容。requireprint

如果require语句无法找到要加载的文件,或者它确实找到了该文件但它不包含有效的PHP代码,则PHP引擎会停止运行您的程序。该include语句还从另一个文件加载代码,但如果加载的文件有问题,它将继续运行。

PHP引擎如何查找文件
如果require或者include给出一个绝对路径名 – 一个以/OS X或Linux或驱动器号或\Windows 开头的路径名 – 那么PHP引擎只会查找该文件的特定位置。

同样,如果一个 提供相对路径 – 从./当前目录或当前目录../的父目录开始 – 然后PHP仅在该位置查找该文件。

但是,对于提供的其他文件名或路径名,PHP引擎会查询配置指令include_path。它的值是在需要或包含文件时要查看的目录列表。如果在任何这些目录中找不到该文件,则PHP引擎将检查包含执行要求或包含的文件的目录。

因为在单独的文件中组织代码可以很容易地重用常用函数和定义,所以本书在后续章节中经常使用它。使用require并include打开大门,轻松使用其他人编写的代码库,这将在第16章中讨论。

章节总结

本章包括:

  • 定义函数并在程序中调用它们
  • 使用强制参数定义函数
  • 使用可选参数定义函数
  • 从函数返回值
  • 了解变量范围
  • 在函数内使用全局变量
  • 理解类型声明
  • 使用参数类型声明
  • 使用返回类型声明
  • 在单独的文件中组织PHP代码

演习

1. 编写一个函数来返回HTML <img />标记。该函数应接受图像的网址强制参数和可选参数alt文字,height和width。

2. 修改上一练习中的函数,以便只将文件名传递给URL参数中的函数。在函数内部,在文件名前添加一个全局变量以生成完整的URL。例如,如果将photo.png传递给函数,并且全局变量包含 /images/,那么src 返回的<img>标记的属性将是 /images/photo.png。即使图像移动到新路径或服务器,这样的功能也是保持图像标记正确的简便方法。只需更改全局变量 – 例如,从/images/更改 http://images.example.com/。

3. 将上一个练习中的函数放在一个文件中。然后创建另一个加载第一个文件并使用它打印出一些<img />标签的文件。

4. 以下代码打印出来的是什么?

 

5.  网页的颜色,如#ffffff与 #cc3399由红,绿,蓝串接十六进制颜色值制成。编写一个接受十进制红色,绿色和蓝色参数的函数,并返回一个包含适当颜色的字符串,以便在网页中使用。例如,如果参数是255,0和255,那么返回的字符串应该是#ff00ff。您可能会发现使用内置函数很有帮助,该函数dechex()在http://www.php.net/dechex中有记录。

如果有不懂的地方请留言,SKY8G网站编辑者专注于研究IT源代码研究与开发。希望你下次光临,你的认可和留言是对我们最大的支持,谢谢!

上一篇: 第4章数据组:使用数组

下一篇: PHP | strrev()函数

登录 评论
avatar