函数

一般

当函数被调用时,信息可以通过参数列表传递给它,参数列表包含一个或多个参数表达式,或者更简单的说,参数。它们按位置对应于被调用的函数定义中的参数列表中的参数

无条件定义的函数是指其定义位于脚本顶层的函数。有条件定义的函数是指其定义位于复合语句中的函数,例如另一个函数(嵌套函数)、条件语句等。函数嵌套的深度没有限制。考虑外部函数和在其内部定义的内部函数的情况。在外部函数至少被调用一次之前,其内部函数不存在。即使外部函数被调用,如果其运行时逻辑绕过了内部函数的定义,那么内部函数仍然不存在。有条件定义的函数在执行流程到达函数定义的位置时才会出现。

任何包含yield的函数都是生成器函数

示例

ucf1(); // can call ucf1 before its definition is seen
function ucf1() { ... }
ucf1(); // can call ucf1 after its definition is seen
cf1(); // Error; call to non-existent function
$flag = TRUE;
if ($flag) { function cf1() { ... } } // cf1 now exists
if ($flag) { cf1(); } // can call cf1 now
// -----------------------------------------
function ucf2() { function cf2() { ... } }
cf2(); // Error; call to non-existent function
ucf2(); // now cf2 exists
cf2(); // so we can call it

函数调用

函数通过函数调用运算符()调用。

函数定义

语法

function-definition:
   function-definition-header   compound-statement

function-definition-header:
   function   &opt   name   (   parameter-declaration-listopt   )   return-typeopt

parameter-declaration-list:
   simple-parameter-declaration-list
   variadic-declaration-list

simple-parameter-declaration-list:
   parameter-declaration
   parameter-declaration-list   ,   parameter-declaration

variadic-declaration-list:
   simple-parameter-declaration-list   ,   variadic-parameter
   variadic-parameter

parameter-declaration:
   type-declarationopt   &opt   variable-name   default-argument-specifieropt

variadic-parameter:
   type-declarationopt   &opt   ...   variable-name

return-type:
   :   type-declaration
   :   void

type-declaration:
   ?opt   base-type-declaration

base-type-declaration:
   array
   callable
   iterable
   scalar-type
   qualified-name

scalar-type:
   bool
   float
   int
   string

default-argument-specifier:
   =   constant-expression

约束

函数定义中的每个参数名称必须不同。

有条件定义的函数必须在对该函数进行任何调用之前存在。

构造函数、析构函数和克隆方法的函数定义不得包含返回类型

对于生成器函数,如果指定了返回类型,则只能是以下之一:GeneratorIteratorTraversable

语义

函数定义定义了一个名为name的函数。函数名区分大小写。函数可以定义零个或多个参数,每个参数都在其参数声明列表中的参数声明中指定。每个参数都有一个名称,变量名称,以及可选的默认参数说明符参数声明中的&表示参数是按引用传递,而不是按值传递。name之前的&表示从该函数返回的值将按引用返回。返回值在return 语句描述中描述。

当函数被调用时,如果存在一个参数与其对应参数,则使用值赋值将参数分配给参数变量,而对于按引用传递,则使用按引用赋值将参数分配给参数变量。如果该参数没有对应参数,但参数具有默认参数值,则对于按值传递或按引用传递,将使用值赋值将默认值分配给参数变量。否则,如果参数没有对应参数并且参数没有默认值,则参数变量不存在,并且没有对应的VSlot存在。在所有可能的参数都被分配初始值或与参数关联之后,函数体,复合语句,被执行。此执行可能会正常终止,通过return 语句异常终止。

每个参数都是父函数的局部变量,并且是可以修改的左值。

函数定义可以存在于脚本的顶层,任何复合语句中,在这种情况下,函数是有条件定义的,或者存在于类的方法声明部分中。

如果定义了可变参数,则传递给函数的每个参数(未与前面的参数匹配)都将作为数组元素存储在此参数中。第一个这样的参数获得索引 0,下一个获得 1,依此类推。如果没有提供额外的参数,则该参数的值为空数组。请注意,如果对可变参数提供类型和/或按引用规范,则这些规范将应用于它捕获的每个额外参数。

默认情况下,参数将接受任何类型的参数。但是,通过指定类型声明,可以限制接受的参数类型。通过指定array,仅接受array 类型的参数。通过指定callable,仅接受指定函数(见下文)的参数。通过指定iterable,仅接受array 类型或实现Traversable 接口的对象的参数。通过指定限定名称,仅接受具有该类型的类的实例,或者从该类型派生的类的实例,或者仅接受直接或间接实现该接口类型的类的实例。检查与instanceof 运算符的检查相同。

callable 伪类型接受以下内容

  • 包含在调用时定义的函数名称的字符串值。
  • 包含索引01 下两个元素的数组值。第一个元素可以是字符串或对象。如果第一个元素是字符串,则第二个元素必须是命名第一个元素指定的类中的方法的字符串。如果第一个元素是对象,则第二个元素必须是命名可以在第一个元素指定的对象上调用的方法的字符串,来自被调用函数的上下文。
  • Closure 类的实例。
  • 实现__invoke的类的实例。

库函数is_callable 报告变量的内容是否可以作为函数调用。

使用标量类型键入的参数,如果它们通过了此标量类型的类型检查,则会被接受,如下所述。一旦通过检查,参数类型始终为指定的标量类型(或NULL,如果允许NULL)。

如果参数具有类型声明,则除非类型是可空的,否则不会接受NULL。如果类型以? 为前缀或参数具有计算结果为NULL 的默认值,则类型是可空的。

键入参数的默认值必须为指定的类型,或NULL,并且无论模式如何,都不会对默认值执行转换。

返回类型

如果函数使用返回类型声明定义,则函数返回的值应与定义的类型兼容,使用与参数类型检查相同的规则。NULL 值不允许用于键入的返回值。如果return 语句的值没有通过类型检查,则会产生致命错误。

void 类型是一种特殊类型,它只能用作返回类型,而不能在其他上下文中使用。它在运行时没有影响,请参阅return 语句

类型检查模式

类型检查可以在两种模式下执行:严格模式和强制模式(默认)。两种模式之间的差异仅存在于标量键入的参数(intfloatstringbool)中。

对于强制模式,如果传递的值与参数的类型相同,则接受该值。如果不是,则尝试转换。如果转换成功,则转换后的值是分配给参数的值。如果转换失败,则会产生致命错误。

对于严格模式,参数必须完全与声明的类型相同(例如,字符串"1" 不会被接受作为键入为int 的参数的值)。唯一的例外是,int 值将被接受为float 键入的参数并转换为float。请注意,严格模式不仅适用于用户定义的函数,还适用于内部函数,请参阅手册以了解适当的参数类型。如果类型不匹配,则会抛出类型为TypeError 的异常。

请注意,如果参数是按引用传递的,并且发生了转换,则该值将使用新转换的值重新分配。

模式由declare 语句设置。

请注意,类型检查模式用于调用者控制的函数调用,而不是被调用者。虽然检查是在被调用函数中执行的,但调用者定义检查是严格的还是强制的。同一函数可以从不同上下文中以严格模式和强制模式检查调用。

返回类型的检查始终由定义函数的脚本定义。

示例

// coercive mode by default
function accept_int(int $a) { return $a+1; }
accept_int(1); // ok
accept_int("123"); // ok
accept_int("123.34"); // ok
accept_int("123.34 and some"); // ok + notice
accept_int("not 123"); // fatal error!
accept_int(null); // fatal error

function accept_int_or_not(int $a = null) { return $a+1; }
accept_int_or_not(null); // ok

function convert_int(int &$a) { return $a+1; }
$a = "12";
convert_int($a);
var_dump($a); // $a is now int

// Now in strict mode
declare(strict_types=1);
function accept_int(int $a) { return $a+1; }
function accept_float(float $a) { return $a+1; }
accept_int(1); // ok
accept_float(1); // ok
accept_int(1.5); // fatal error
accept_int("123"); // fatal error
echo substr("123", "1"); // fatal error

变量函数

如果变量名称后跟函数调用运算符(),并且该变量的值指定当前定义的函数(见上文描述),则该函数将被执行。如果该变量未指定函数或该函数无法调用,则会产生致命错误。

匿名函数

匿名函数,也称为闭包,是定义时没有名称的函数。因此,它必须在表达式的上下文中定义,该表达式的值立即用于调用该函数,或者被保存到变量中以供以后执行。匿名函数通过匿名函数创建运算符定义。

对于__FUNCTION____METHOD__,匿名函数的名称报告为{closure}