表达式

一般

一个表达式包含一个或多个项和零个或多个运算符。

一个完整表达式是一个不属于其他表达式的表达式。

一个副作用是指改变执行环境状态的操作。(例如,修改变量、写入设备或文件,或者调用执行此类操作的函数)。

当表达式被求值时,它会产生一个结果。它也可能产生副作用。只有少数运算符会产生副作用。(例如,给定表达式语句$v = 10; 表达式 10 被求值为结果 10,并且没有副作用。然后赋值运算符被执行,这会导致$v被修改的副作用。整个表达式的结果是赋值完成后$v的值。但是,该结果从未被使用。类似地,给定表达式语句++$v; 表达式被求值为结果$v的增量值,副作用是$v实际上被增量。同样,结果从未被使用)。

值计算和副作用的发生由序列点分隔,序列点是程序执行中的位置,在该位置,之前承诺的所有计算和副作用都已完成,并且未来操作的任何计算或副作用尚未开始。每个完整表达式的末尾都有一个序列点。逻辑与逻辑或条件合并函数调用运算符都包含一个序列点。(例如,在以下表达式语句系列中,$a = 10; ++$a; $b = $a;,每个完整表达式的末尾都有序列点,因此对$a的赋值在$a被增量之前完成,增量在对$b的赋值之前完成)。

当表达式包含多个运算符时,这些运算符的优先级控制这些运算符的应用顺序。(例如,表达式$a - $b / $c被求值为$a - ($b / $c),因为/运算符的优先级高于二元-运算符)。运算符的优先级由其关联的语法产生式定义决定。

如果一个操作数出现在两个具有相同优先级的运算符之间,则执行操作的顺序由这些运算符的结合性决定。对于左结合运算符,操作从左到右执行。(例如,$a + $b - $c被求值为($a + $b) - $c)。对于右结合运算符,操作从右到左执行。(例如,$a = $b = $c被求值为$a = ($b = $c))。

优先级和结合性可以使用分组括号控制。(例如,在表达式($a - $b) / $c中,减法在除法之前完成。如果没有分组括号,则除法将首先执行)。

虽然优先级、结合性和分组括号控制运算符的应用顺序,但它们控制项本身的求值顺序。除非本规范中明确说明,否则表达式中操作数相对于彼此的求值顺序是不确定的。请参阅上面关于包含序列点的运算符的讨论。(例如,在完整表达式$list1[$i] = $list2[$i++]中,左侧$i的值是旧的还是新的$i是不确定的。类似地,在完整表达式$j = $i + $i++中,$i的值是旧的还是新的$i是不确定的。最后,在完整表达式f() + g() * h()中,三个函数的调用顺序是不确定的)。

实现说明

不包含副作用并且其结果值没有被使用的表达式不必被求值。例如,表达式语句6;$i + 6;$i/$j; 形式良好,但它们不包含副作用,并且它们的結果不被使用。

如果可以确定没有其他程序代码依赖于副作用的发生,则不必执行副作用。(例如,在return $a++;return ++$a;的情况下,很明显每个情况下必须返回什么值,但如果$a是封闭函数的局部变量,则$a实际上不必被增量)。

基本表达式

一般

语法

primary-expression:
   variable
   class-constant-access-expression
   constant-access-expression
   literal
   array-creation-expression
   intrinsic
   anonymous-function-creation-expression
   object-creation-expression
   postfix-increment-expression
   postfix-decrement-expression
   prefix-increment-expression
   prefix-decrement-expression
   byref-assignment-expression
   shell-command-expression
   (   expression   )

语义

带括号表达式的类型和值与不带括号的表达式相同。

简单变量

语法

simple-variable:
   variable-name
   $   simple-variable
   $   {   expression   }

约束

最后两个变体中的简单变量表达式必须指定一个标量值或可转换为字符串的对象。

语义

一个简单变量表达式指定一个变量,其名称由变量名简单变量表达式结果的字符串表示决定,具体取决于哪种情况适用。在后两种情况下,变量名可能包含词法变量名中不允许的字符。

简单变量在不同上下文中和针对不同类型的变量的行为如变量部分所述。

变量$this在任何非静态实例方法(包括构造函数)内部预定义,当该方法从对象上下文中调用时。$this的值是调用对象或正在构造的对象。

示例

$color = "red";
$$color = 123;    // equivalent to $red = 123
// -----------------------------------------
$x = 'ab'; $ab = 'fg'; $fg = 'xy';
$$ $ $x = 'Hello';  // equivalent to $xy = Hello
// -----------------------------------------
$v1 = 3;
$$v1 = 22;        // equivalent to ${3} = 22, variable name is "3"
$v2 = 9.543;
$$v2 = TRUE;    // equivalent to ${9.543} = TRUE
$v3 = NULL;
$$v3 = "abc";   // equivalent to ${NULL} = "abc", here we create a variable with empty name
// -----------------------------------------
function f1 () { return 2.5; }
${1 + f1()} = 1000;   // equivalent to ${3.5} = 1000

可解引用表达式

语法

dereferencable-expression:
   variable
   (   expression   )
   array-creation-expression
   string-literal

callable-expression:
   callable-variable
   (   expression   )
   array-creation-expression
   string-literal

约束

字符串字面量不能使用变量插值,并且不能是 heredoc 或 nowdoc 字符串字面量。

语义

一个可解引用表达式可以作为解引用运算符的左侧使用,例如[]->::。一个可调用表达式可以作为函数调用运算符的左侧使用。

变量

语法

callable-variable:
   simple-variable
   subscript-expression
   member-call-expression
   scoped-call-expression
   function-call-expression

variable:
   callable-variable
   scoped-property-access-expression
   member-access-expression

语义

一个变量是一个表达式,它原则上可以用作左值。但是,各个可能的表达式可能会进一步限制它们是否可以充当左值。不是变量的表达式永远不能充当左值。

常量访问表达式

constant-access-expression:
   qualified-name

语义

一个常量访问表达式求值为具有名称限定名常量的值。

字面量

语法

literal:
   integer-literal
   floating-literal
   string-literal

语义

一个字面量求值为其值,如词法规范中针对字面量的规定。

内在函数

一般

语法

intrinsic:
   empty-intrinsic
   eval-intrinsic
   exit-intrinsic
   isset-intrinsic

语义

本系列部分中的名称具有特殊含义,称为内在函数,但它们不是关键字;它们也不是函数,它们是引擎解释的语言结构。

empty

语法

empty-intrinsic:
   empty   (   expression   )

语义

如果由表达式指定的变量或值为空,则此内在函数返回TRUE,其中表示指定的变量不存在,或者存在并且其值与FALSE相等。否则,内在函数返回FALSE

以下值被认为为空:FALSE00.0""(空字符串)、"0"NULL、空数组以及任何未初始化的变量。

如果此内在函数与指定动态属性的表达式一起使用,则如果该属性的类具有__isset,则调用该方法。如果该方法返回TRUE,则检索属性的值(如果定义了,则可能调用__get)并与FALSE进行比较,如上所述。否则,结果为FALSE

示例

empty("0");  // results in TRUE
empty("00"); // results in FALSE
$v = [10, 20];
empty($v);   // results in FALSE

eval

语法

eval-intrinsic:
   eval   (   expression   )

约束

表达式必须指定一个字符串,或者可以转换为字符串。字符串的内容必须是有效的 PHP 源代码。如果源代码格式错误,则会抛出类型为ParseError的异常。

字符串中的 PHP 源代码不能用开始和结束的PHP 标签分隔。但是,源代码本身可以包含这些标签。

语义

此内在函数将由表达式指定的字符串的内容作为 PHP 脚本代码进行求值。

从源代码内部执行return语句将终止执行,返回的值将成为eval返回的值。如果源代码格式错误,则eval返回FALSE;否则,eval返回NULL

源代码在调用eval的位置的范围内执行。

示例

$str = "Hello";
eval("echo \$str . \"\\n\";");  // → echo $str . "\n"; → prints Hello

exit/die

语法

exit-intrinsic:
   exit
   exit   (   expressionopt   )
   die
   die   (   expressionopt   )

约束

表达式指定一个整数时,其值必须在 0–254 范围内。

语义

exitdie等效。

此内在函数终止当前脚本。如果表达式指定一个字符串,则该字符串将写入STDOUT。如果表达式指定一个整数,则表示脚本的退出状态代码。代码 255 由 PHP 保留。代码 0 代表“成功”。退出状态代码可用于执行环境。如果表达式被省略或是一个字符串,则退出状态代码为零。exit没有结果值。

exit按顺序执行以下操作

示例

exit ("Closing down");
exit (1);
exit;

isset

语法

isset-intrinsic:
   isset   (   variable-list   ,opt   )

variable-list:
   variable
   variable-list   ,   variable

语义

如果由变量s 指定的所有变量都已设置并且它们的值不为NULL,则此内在函数返回TRUE。否则,它返回FALSE

如果此内在函数与指定动态属性的表达式一起使用,则如果该属性的类具有__isset,则调用该方法。如果该方法返回TRUE,则检索属性的值(如果定义了,则可能调用__get),如果它不为NULL,则结果为TRUE。否则,结果为FALSE

示例

$v = TRUE;
isset($v);     // results in TRUE
$v = NULL;
isset($v);     // results in FALSE
$v1 = TRUE; $v2 = 12.3; $v3 = NULL;
isset($v1, $v2, $v3);  // results in FALSE

匿名函数创建

语法

anonymous-function-creation-expression:
   staticopt   function   &opt   (   parameter-declaration-listopt   )   anonymous-function-use-clauseopt   return-typeopt   compound-statement

anonymous-function-use-clause:
   use   (   use-variable-name-list   )

use-variable-name-list:
   &opt   variable-name
   use-variable-name-list   ,   &opt   variable-name

语义

此运算符返回类型为Closure或其派生类型的对象,该对象封装了匿名函数。匿名函数的定义方式和行为类似于命名函数,只是前者没有名称,并且有一个可选的匿名函数使用子句

表示匿名函数的表达式与伪类型callable兼容。

use-variable-name-list是来自封闭作用域的变量列表,这些变量将通过名称提供给匿名函数的主体。这些变量可以根据需要按值或按引用传递。用于这些变量的值是在创建Closure对象时使用的值,而不是在使用它来调用它封装的函数时使用的值。

在实例或静态方法内部定义的匿名函数的作用域设置为它定义所在的类。否则,匿名函数是无作用域的

在实例方法内部定义的匿名函数绑定到调用该方法的对象,而静态方法内部定义的匿名函数,或者在前面加可选的static修饰符的匿名函数是静态的,否则匿名函数是未绑定的

示例

function doit($value, callable $process)
{
  return $process($value);
}
$result = doit(5, function ($p) { return $p * 2; });  // doubles a value
$result = doit(5, function ($p) { return $p * $p; }); // squares a value
// -----------------------------------------
class C
{
  public function compute(array $values)
  {
    $count = 0;
    $callback1 = function () use (&$count) // has C as its scope
    {
      ++$count;
      //...
    };
    //...
    $callback2 = function()   // also has C as its scope
    {
      //...
    };
    //...
  }
  //...
}

new运算符

语法

object-creation-expression:
   new   class-type-designator   (   argument-expression-listopt   )
   new   class-type-designator   (   argument-expression-list   ,opt   )
   new   class-type-designator
   new   class   (   argument-expression-listopt   )   class-base-clauseopt   class-interface-clauseopt   {   class-member-declarationsopt   }
   new   class   class-base-clauseopt   class-interface-clauseopt   {   class-member-declarationsopt   }

class-type-designator:
   qualified-name
   new-variable

new-variable:
   simple-variable
   new-variable   [   expressionopt   ]
   new-variable   {   expression   }
   new-variable   ->   member-name
   qualified-name   ::   simple-variable
   relative-scope   ::   simple-variable
   new-variable   ::   simple-variable

约束

qualified-name必须命名一个类。

new-variable必须是类型为string的值,其中包含类的名称或对象。

class-type-designator不能指定抽象类

argument-expression-list中的参数数量必须至少与为类的构造函数定义的非可选参数的数量一样多。

语义

new class-type-designator形式创建由class-type-designator指定的类类型对象。new class形式创建匿名类类型对象,这种类型没有指定名称。但是,在所有其他方面,匿名类具有与命名类类型相同的功能。

如果class-type-designator是导致字符串值的new-variable,则该字符串用作类名。如果表达式导致对象,则对象的类用作新对象的类。new-variablevariable具有相同的语义,但语法被限制为排除调用。

qualified-name根据作用域解析运算符中描述的规则进行解析,包括对selfparentstatic的支持。

创建对象后,每个实例属性将使用属性定义中指定的值进行初始化,如果没有提供初始化值,则使用值NULL

然后通过调用类的构造函数并传递可选的argument-expression-list来初始化对象。如果类没有构造函数,则使用该类继承的构造函数(如果有)。类也可以不指定构造函数定义,在这种情况下,构造函数调用将被省略。

命名类型object-creation-expression的结果是class-type-designator指定的类型的对象。匿名类object-creation-expression的结果是未指定类型的对象。但是,此类型将是class-base-clauseclass-interface-clause提供的类型的所有子类型,并且class-members定义应遵循与常规类声明相同的继承和实现规则。

形式为new class的每个不同的源代码表达式都会导致与所有其他匿名类类型不同的类类型。但是,对形式为new class的相同源代码表达式的多次求值会导致同一个类类型的实例。

由于构造函数调用是函数调用,因此函数调用运算符部分的相关部分也适用。

示例

class Point
{
  public function __construct($x = 0, $y = 0)
  {
    ...
  }
  ...
}
$p1 = new Point;     // create Point(0, 0)
$p1 = new Point(12);   // create Point(12, 0)
$cName = 'Point';
$p1 = new $cName(-1, 1); // create Point(-1, 1)
// -----------------------------------------
$v2 = new class (100) extends C1 implements I1, I2 {
	public function __construct($p) {
	    echo "Inside class " . __CLASS__ . " constructor with parameter $p\n";
	}
};

数组创建运算符

语法

array-creation-expression:
   array   (   array-initializeropt   )
   [   array-initializeropt   ]

array-initializer:
   array-initializer-list   ,opt

array-initializer-list:
   array-element-initializer
   array-element-initializer   ,   array-initializer-list

array-element-initializer:
   &opt   element-value
   element-key   =>   &opt   element-value

element-key:
   expression

element-value:
   expression

约束

如果array-element-initializer包含&,则element-value中的expression必须指定变量

语义

如果省略array-initializer,则数组将具有零个元素。为了方便起见,array-initializer可以有尾随逗号;但是,此逗号将被忽略。array-initializer-list由一个或多个array-element-initializer项组成的逗号分隔列表组成,每个项用于提供element-value和可选的element-key

如果element-key的类型既不是int也不是string,则具有floatbool值的键,或者内容完全匹配decimal-literal模式的字符串,将转换为整数,所有其他类型的值将转换为字符串

如果从array-element-initializer中省略element-key,则与相应的element-value关联的元素键类型为int。关联的键比该数组先前分配的最大的非负int键大 1,无论该键是显式提供还是默认提供。如果数组没有非负int键,则使用键0。如果先前分配的最大的int键是可表示的最大整数,则不会添加新元素。

一旦元素键被转换为intstring,并且默认关联了省略的元素键,如果array-initializer中的两个或多个array-element-initializer元素包含相同的键,则词法上最右边的元素是其element-value用于初始化该元素的元素。

此运算符的结果是新创建的数组值。

如果array-element-initializer包含&,则element-value的值将使用按引用赋值存储。

示例

$v = [];      // array has 0 elements, i.e. empty array
$v = array(TRUE);   // array has 1 element, the Boolean TRUE
$v = [123, -56];  // array of two ints, with implicit int keys 0 and 1
$v = [0 => 123, 1 => -56]; // array of two ints, with explicit int keys 0 and 1
$i = 10;
$v = [$i - 10 => 123, $i - 9 => -56]; // key can be a runtime expression
$v = [NULL, 1 => FALSE, 123, 3 => 34e12, "Hello"];  // implicit & explicit keys
$i = 6; $j = 12;
$v = [7 => 123, 3 => $i, 6 => ++$j];  // keys are in arbitrary order
$v[4] = 99;   // extends array with a new element
$v = [2 => 23, 1 => 10, 2 => 46, 1.9 => 6];
     // array has 2, with keys 2 and 1, values 46 and 6, respectively
$v = ["red" => 10, "4" => 3, 9.2 => 5, "12.8" => 111, NULL => 1];
     // array has 5 elements, with keys “red”, 4, 9, “12.8”, and “”.
$c = array("red", "white", "blue");
$v = array(10, $c, NULL, array(FALSE, NULL, $c));
$v = array(2 => TRUE, 0 => 123, 1 => 34.5, -1 => "red");
foreach($v as $e) { /* ... */ } // iterates over keys 2, 0, 1, -1
for ($i = -1; $i <= 2; ++$i) { echo $v[$i]; } // retrieves via keys -1, 0, 1, 2

下标运算符

语法

subscript-expression:
   dereferencable-expression   [   expressionopt   ]
   dereferencable-expression   {   expression   }   <b>[Deprecated form]</b>

约束

如果dereferencable-expression指定字符串,则expression不能指定字符串。

expression只能在subscript-expression在可修改的左值上下文中使用,并且dereferencable-expression不指定字符串的情况下省略。这种情况的例外是当dereferencable-expression是空字符串时 - 然后它将转换为空数组。

如果subscript-expression在非左值上下文中使用,则被指定的元素必须存在。

语义

subscript-expression指定数组或字符串的(可能不存在的)元素。当subscript-expression指定实现ArrayAccess类型的对象时,最小语义定义如下;但是,它们可以通过该对象的offsetGetoffsetSet方法进行扩展。

元素键由expression指定。如果element-key的类型既不是int也不是string,则具有floatbool值的键,或者内容完全匹配decimal-literal模式的字符串,将转换为整数,所有其他类型的值将转换为字符串

如果dereferencable-expressionexpression都指定字符串,则expression将被视为指定了int键零,并产生一个非致命错误。

subscript-expression指定可修改的左值当且仅当dereferencable-expression指定可修改的左值。

dereferencable-expression指定数组

如果存在expression,如果指定的元素存在,则结果的类型和值是该元素的类型和值;否则,结果是NULL

如果省略了expression,则将插入一个新元素。它的键类型为int,比该数组先前分配的最高的int键大 1。如果这是第一个具有int键的元素,则使用键0。如果先前分配的最大的int键是可表示的最大整数,则不会添加新元素。结果是添加的新元素,或者如果未添加元素,则为NULL

dereferencable-expression指定字符串

expression将转换为int,结果是字符串中对应于该整数的位置处的字符。如果整数为负数,则从字符串末尾反向计数位置。如果位置引用了不存在的偏移量,则结果为空字符串。

如果运算符用作simple-assignment-expression的左侧,

  • 如果分配的字符串为空,或者在不存在的负偏移量(绝对值大于字符串长度)的情况下,将发出警告,并且不会执行分配。
  • 如果偏移量大于当前字符串长度,则字符串将扩展到与偏移量值相等的长度,使用空格(0x20)填充字符。
  • 将要分配的值将转换为字符串,并将指定偏移量处的字符替换为字符串的第一个字符。

下标运算符不能在按引用上下文中或作为后缀或前缀递增或递减运算符的操作数或在compound-assignment-expression的左侧使用字符串值,这样做会导致致命错误。

dereferencable-expression指定实现ArrayAccess类型的对象

如果存在expression

  • 如果subscript-expression在非左值上下文中使用,则对象的offsetGet方法将使用expression作为参数进行调用。offsetGet的返回值是结果。
  • 如果使用上下文是作为simple-assignment-expression的左侧,则对象的offsetSet方法将使用expression作为第一个参数,并使用该simple-assignment-expression右侧的值作为第二个参数进行调用。右侧的值是结果。
  • 如果使用上下文是作为compound-assignment-expression的左侧,则表达式e1[e] op= e2将被评估为e1[e] = e1->offsetGet(e) op (e2),然后根据上面简单的赋值规则进行处理。
  • 如果使用上下文是作为后缀或前缀递增或递减运算符的操作数,则对象的offsetGet方法将使用expression作为参数进行调用。但是,此方法无法知道是否使用了递增或递减运算符,或者它是否是前缀或后缀运算符。为了使值通过递增/递减进行修改,offsetGet必须按引用返回。下标运算符结果是offsetGet返回的值。

如果省略了expression

  • 如果使用上下文是作为simple-assignment-expression的左侧,则对象的offsetSet方法将使用NULL作为第一个参数,并使用该simple-assignment-expression右侧的值作为第二个参数进行调用。结果的类型和值是该simple-assignment-expression右侧的类型和值。
  • 如果使用上下文是作为compound-assignment-expression的左侧:表达式e1[] op= e2将被评估为e1[] = e1->offsetGet(NULL) op (e2),然后根据上面简单的赋值规则进行处理。
  • 如果使用上下文是作为 后置或前置递增或递减运算符 的操作数,则对象的 offsetGet 方法将使用 NULL 作为参数调用。但是,该方法无法知道使用了递增或递减运算符,也不知道是前置运算符还是后置运算符。为了使值被递增/递减修改,offsetGet 必须返回引用。下标运算符返回的值由 offsetGet 返回。

注意:此运算符的括号 ({...}) 形式已弃用。

示例

$v = array(10, 20, 30);
$v[1] = 1.234;    // change the value (and type) of element [1]
$v[-10] = 19;   // insert a new element with int key -10
$v["red"] = TRUE; // insert a new element with string key "red"
[[2,4,6,8], [5,10], [100,200,300]][0][2]  // designates element with value 6
["black", "white", "yellow"][1][2]  // designates substring "i" in "white"
function f() { return [1000, 2000, 3000]; }
f()[2];      // designates element with value 3000
"red"[1.9];    // designates "e"
"red"[-2];    // designates "e"
"red"[0][0][0];    // designates "r"
// -----------------------------------------
class MyVector implements ArrayAccess { /* ... */ }
$vect1 = new MyVector(array(10, 'A' => 2.3, "up"));
$vect1[10] = 987; // calls Vector::offsetSet(10, 987)
$vect1[] = "xxx"; // calls Vector::offsetSet(NULL, "xxx")
$x = $vect1[1];   // calls Vector::offsetGet(1)

函数调用运算符

语法

function-call-expression:
   qualified-name   (   argument-expression-listopt   )
   qualified-name   (   argument-expression-list   ,   )
   callable-expression   (   argument-expression-listopt   )
   callable-expression   (   argument-expression-list   ,   )

argument-expression-list:
   argument-expression
   argument-expression-list   ,   argument-expression

argument-expression:
   variadic-unpacking
   expression

variadic-unpacking:
   ...   expression

约束

可调用表达式必须指定一个函数,方法是成为包含函数名称的字符串类型的值,或者成为实现 __invoke 方法(包括 Closure 对象)的类型的对象。

函数调用中出现的参数数量必须至少与为该函数定义的非可选参数数量一样多。

在该函数存在之前,不能对 条件定义的函数 进行任何调用。

任何与通过引用传递的参数匹配的参数都应该(但不必)指定一个左值。

如果使用可变参数解包,则表达式的结果必须是数组或 Traversable。如果提供不兼容的值,则忽略该参数并发出非致命错误。

语义

形式为函数调用表达式的表达式是一个函数调用。该表达式指定被调用函数,而参数表达式列表指定要传递给该函数的参数。参数可以是任何值。在函数调用中,首先计算可调用表达式,然后按从左到右的顺序计算每个表达式。在每个参数计算完并函数被调用之前,都有一个 序列点。有关函数调用结果的详细信息,请参阅 return 语句。只有当函数通过引用返回可修改的值时,函数调用的值才是可修改的左值。

当调用函数时,传递给它的每个参数的值都将分配给该函数定义中的相应参数(如果存在该参数)。参数值到参数的分配是根据 简单通过引用分配 来定义的,具体取决于参数的声明方式。参数数量可能比参数多,在这种情况下,可以使用库函数 func_num_argsfunc_get_argfunc_get_args 来访问传递的完整参数列表。如果函数调用中出现的参数数量少于为该函数定义的参数数量,则任何没有对应参数的参数都被认为是未定义的(如果它没有 默认参数值);否则,它被认为是用该默认参数值定义的。

如果通过引用传递了一个未定义的变量,则该变量将被定义,其初始值为 NULL

允许直接和间接递归函数调用。

如果可调用表达式是字符串,则这是一个 可变函数调用

如果使用可变参数解包操作,则操作数被认为是参数列表。操作数中包含的值将逐个提取(与 foreach 的方式相同),并用于调用的后续参数。迭代中的键被忽略。

可以在同一个函数调用中使用多个解包操作,并且解包和常规参数可以以任何顺序混合使用。

示例

function square($v) { return $v * $v; }
square(5);     // call square directly; it returns 25
$funct = square;  // assigns the string "square" to $funct
$funct(-2.3)    // call square indirectly; it returns 5.29
strlen($lastName); // returns the # of bytes in the string
// -----------------------------------------
function f1() { ... }  function f2() { ... }  function f3() { ... }
for ($i = 1; $i <= 2; ++$i) { $f = 'f' . $i;  $f(); }
// -----------------------------------------
function f($p1, $p2, $p3, $p4, $p5) { ... }
function g($p1, $p2, $p3, $p4, $p5) { ... }
function h($p1, $p2, $p3, $p4, $p5) { ... }
$funcTable = array(f, g, h);  // list of 3 function designators
$i = 1;
$funcTable[$i++]($i, ++$i, $i, $i = 12, --$i); // calls g(2,3,3,12,11)
// -----------------------------------------
function f4($p1, $p2 = 1.23, $p3 = "abc") { ... }
f4(); // inside f4, $p1 is undefined, $p2 is 1.23, $p3 is "abc"
// -----------------------------------------
function f(&$p) { ... }
$a = array(10, 20, 30);
f($a[5]); // non-existent element going in, but element exists afterwards
// -----------------------------------------
function factorial($int)  // contains a recursive call
{
  return ($int > 1) ? $int * factorial($int - 1) : $int;
}
// -----------------------------------------
$anon = function () { ... };  // store a Closure in $anon
$anon();  // call the anonymous function encapsulated by that object

成员访问运算符

语法

member-access-expression:
   dereferencable-expression   ->   member-name

member-name:
   name
   simple-variable
   {   expression   }

约束

可解引用表达式必须指定一个对象,或者为 NULLFALSE 或空字符串。

表达式必须是字符串类型的变量(但不是字符串文字),其中包含实例属性的名称(**不包括** 前导 $)或该实例的类类型的实例或静态方法。

语义

成员访问表达式指定由可解引用表达式指定的对象的实例属性,其名称由成员名称的字符串表示形式给出。该值为属性的值,并且如果可解引用表达式是可修改的左值,则该值也是可修改的左值。

当在可修改左值上下文中使用 -> 运算符并且成员名称指定不可见的属性时,该属性将被视为 动态属性。如果可解引用表达式的类类型定义了一个 __set 方法,则该方法将被调用以存储属性的值。当在非左值上下文中使用 -> 运算符并且成员名称指定不可见的属性时,该属性将被视为动态属性。如果可解引用表达式的类类型定义了一个 __get 方法,则该方法将被调用以检索属性的值。

如果可解引用表达式NULLFALSE 或空字符串,则形式为 $p->x = 10 的表达式将导致创建一个 stdClass 实例,其中具有值为 10 的动态属性 x。然后使 $p 引用该实例。

示例

class Point
{
  private $x;
  private $y;
  public function move($x, $y)
  {
    $this->x = $x;  // sets private property $x
    $this->y = $y;  // sets private property $x
  }
  public function __toString()
  {
    return '(' . $this->x . ',' . $this->y . ')';
  }
  // get private properties $x and $y
  public function __set($name, $value) { ... }
  public function __get($name) { ... }
}
$p1 = new Point;
$p1->move(3, 9);  // calls public instance method move by name
$n = "move";
$p1->$n(-2, 4);   // calls public instance method move by variable
$p1->color = "red"; // turned into $p1->__set("color", "red");
$c = $p1->color;  // turned into $c = $p1->__get("color");

成员调用运算符

语法

member-call-expression:
   dereferencable-expression   ->   member-name   (   argument-expression-listopt   )
   dereferencable-expression   ->   member-name   (   argument-expression-list   ,   )

约束

可解引用表达式必须指定一个对象。

此外,一般 函数调用约束 适用。

语义

成员调用表达式调用由可解引用表达式指定的对象的实例或静态方法,方法名称由成员名称的字符串表示形式给出,参数由参数表达式列表给出。可解引用表达式的值用作被调用方法中 $this 的值。

一般 函数调用语义 适用。

如果被调用方法不存在或在当前作用域中不可见,则会抛出异常,除非存在 __call 方法,在这种情况下,将改为调用该方法。

示例

请参阅 成员访问示例

后置递增和递减运算符

语法

postfix-increment-expression:
   variable   ++

postfix-decrement-expression:
   variable   --

约束

后置 ++ 和 – 运算符的操作数必须是具有标量兼容类型的可修改左值。

语义

这些运算符的行为类似于其 前置对应运算符,只是后置 ++ 或 – 表达式的值是在执行任何递增或递减之前的值。

示例

$i = 10; $j = $i-- + 100;   // old value of $i (10) is added to 100
$a = array(100, 200); $v = $a[1]++; // old value of $ia[1] (200) is assigned

前置递增和递减运算符

语法

prefix-increment-expression:
   ++   variable

prefix-decrement-expression:
   --   variable

约束

前置 ++-- 运算符的操作数必须是具有标量兼容类型的可修改左值。

语义

算术操作数

对于与算术操作数一起使用的前置 ++ 运算符,该运算符的 副作用 是将操作数的值增加 1。结果是操作数在递增后的值。如果 int 操作数的值是该类型可表示的最大值,则将操作数递增,就像它是 float 一样。

对于与算术操作数一起使用的前置 -- 运算符,该运算符的副作用是将操作数的值减少 1。结果是操作数在递减后的值。如果 int 操作数的值是该类型可表示的最小值,则将操作数递减,就像它是 float 一样。

对于与具有值 INF-INFNAN 的操作数一起使用的前置 ++-- 运算符,没有副作用,结果是操作数的值。

布尔操作数

对于与布尔值操作数一起使用的前置 ++-- 运算符,没有副作用,结果是操作数的值。

NULL 值操作数

对于与 NULL 值操作数一起使用的前置 – 运算符,没有副作用,结果是操作数的值。对于与 NULL 值操作数一起使用的前置 ++ 运算符,副作用是操作数的类型将更改为 int,操作数的值将设置为零,并将该值增加 1。结果是操作数在递增后的值。

字符串操作数

对于与值为空字符串的操作数一起使用的前置 -- 运算符,副作用是操作数的类型将更改为 int,操作数的值将设置为零,并将该值减少 1。结果是操作数在递增后的值。

对于与值为空字符串的操作数一起使用的前置 ++ 运算符,副作用是操作数的值将更改为字符串“1”。操作数的类型保持不变。结果是操作数的新值。

对于与数字字符串一起使用的前置 --++ 运算符,数字字符串将被视为相应的 intfloat 值。

对于与非数字字符串值操作数一起使用的前置 -- 运算符,没有副作用,结果是操作数的值。

对于仅包含字母数字字符的非数字字符串值操作数,对于前置 ++ 运算符,操作数被认为是 36 进制数字的表示形式(即,数字 0–9 后跟 A–Z 或 a–z),其中字母大小写对于值目的被忽略。最右边的数字增加 1。对于数字 0–8,这意味着变成 1–9。对于字母“A”–“Y”(或“a”–“y”),这意味着变成“B”–“Z”(或“b”–“z”)。对于数字 9,数字变成 0,并且进位将加到下一个最左边的数字,依此类推。对于数字“Z”(或“z”),结果字符串将附加一个额外的数字“A”(或“a”)。例如,当递增时,“a” -> “b”,“Z” -> “AA”,“AA” -> “AB”,“F29” -> “F30”,“FZ9” -> “GA0” 以及“ZZ9” -> “AAA0”。包含数字的数字位置将进行模 10 循环,而包含字母的数字位置将进行模 26 循环。

对于包含任何非字母数字字符的非数字字符串值操作数,对于前置 ++ 运算符,直到最右边的非字母数字字符(包括该字符)的所有字符都将不变地传递到结果字符串中。最右边的非字母数字字符右侧的字符将被视为仅包含字母数字字符的非数字字符串值操作数,只是结果字符串不会扩展。相反,包含数字的数字位置将进行模 10 循环,而包含字母的数字位置将进行模 26 循环。

对象操作数

如果操作数具有支持该操作的对象类型,则对象语义定义结果。否则,该操作没有效果,结果是操作数。

示例

$i = 10; $j = --$i + 100;   // new value of $i (9) is added to 100
$a = array(100, 200); $v = ++$a[1]; // new value of $a[1] (201) is assigned
$a = "^^Z"; ++$a; // $a is now "^^A"
$a = "^^Z^^"; ++$a; // $a is now "^^Z^^"

Shell 命令运算符

语法

shell-command-expression:
   `   dq-char-sequenceopt   `

其中 ` 是重音符字符 0x60,通常称为反引号

语义

此运算符将双引号字符序列传递给命令 shell 以执行,就像它被传递给库函数 shell_exec 一样。如果该命令执行的输出被写入 STDOUT,则该输出将作为字符串成为此运算符的结果。如果输出被重定向到 STDOUT 以外,或者双引号字符序列为空或仅包含空白,则该运算符的结果为 NULL

如果 shell_exec 被禁用,则此运算符也被禁用。

示例

$result = `ls`;           // result is the output of command ls
$result = `ls >dirlist.txt`;  // result is NULL
$d = "dir"; $f = "*.*";
$result = `$d {$f}`;      // result is the output of command dir *.*

作用域解析运算符

语法

scoped-property-access-expression:
   scope-resolution-qualifier   ::   simple-variable

scoped-call-expression:
   scope-resolution-qualifier   ::   member-name   (   argument-expression-listopt   )
   scope-resolution-qualifier   ::   member-name   (   argument-expression-list   ,   )

class-constant-access-expression:
   scope-resolution-qualifier   ::   name

scope-resolution-qualifier:
   relative-scope
   qualified-name
   dereferencable-expression

relative-scope:
   self
   parent
   static

约束

限定名称必须是类或接口类型的名称。

可解引用表达式必须是字符串类型的值,该字符串包含类或接口类型的名称,或一个对象。

语义

在类或接口内部或外部,运算符::允许选择常量。在类内部或外部,该运算符允许选择静态属性、静态方法或实例方法。在类内部,它还允许选择重写的属性或方法。

如果使用作用域属性访问表达式形式,则该运算符正在访问由简单变量给出的静态属性,并且可以用作左值。

如果使用类常量访问表达式形式,则该运算符正在访问由名称给出的类常量。此形式不能用作左值。

如果使用作用域调用表达式形式,则运算符正在调用由成员名给出的方法,该方法在对象上下文之外被视为静态方法调用。

在对象上下文内部,当$this被定义并且调用的方法不是static并且调用的类与$this类的父类相同,则方法调用是非静态的,具有相同的$this。否则,它是一个静态方法调用。

相对作用域指定与当前类作用域相关的类。在类内部,self指的是同一个类,parent指的是当前类扩展的类。在方法内部,static指的是与调用方法的类继承上下文相对应的类。这允许延迟静态绑定,此时类解析取决于动态调用上下文。

class Base
{
  public function b()
  {
    static::f();  // calls the most appropriate f()
  }
  public function f() { ... }
}
class Derived extends Base
{
  public function f() { ... }
}
$b1 = new Base;
$b1->b(); // as $b1 is an instance of Base, Base::b() calls Base::f()
$d1 = new Derived;
$d1->b(); // as $d1 is an instance of Derived, Base::b() calls Derived::f()

::class结尾的作用域解析表达式形式的值是一个字符串,包含当前类的完全限定名,对于static限定符,表示当前类上下文。

示例

final class MathLibrary
{
  public static function sin() { ... }
  ...
}
$v = MathLibrary::sin(2.34);  // call directly by class name
$clName = 'MathLibrary';
$v = $clName::sin(2.34);    // call indirectly via string
// -----------------------------------------
class MyRangeException extends Exception
{
  public function __construct($message, ...)
  {
    parent::__construct($message);
    ...
  }
  ...
}
// -----------------------------------------
class Point
{
  private static $pointCount = 0;
  public static function getPointCount()
  {
    return self::$pointCount;
  }
  ...
}

clone运算符

语法

clone-expression:
   primary-expression
   clone   primary-expression

约束

主表达式必须指定一个对象。

语义

clone运算符创建一个新的对象,该对象是主表达式指定的对象的浅拷贝。然后,如果主表达式的类类型有一个名为__clone的方法,则调用它来执行深拷贝。结果是新对象。

示例

考虑一个类Employee,从它派生出一个类Manager。让我们假设这两个类都包含作为对象的属性。clone用于制作Manager对象的副本,在幕后,Manager对象使用clone来复制基类Employee的属性。

class Employee
{
  //...
  public function __clone()
  {
    // make a deep copy of Employee object
  }
}
class Manager extends Employee
{
  //...
  public function __clone()
  {
    $v = parent::__clone();
    // make a deep copy of Manager object

  }
}
$obj1 = new Manager("Smith", 23);
$obj2 = clone $obj1;  // creates a new Manager that is a deep copy

指数运算符

语法

exponentiation-expression:
   clone-expression
   clone-expression   **   exponentiation-expression

语义

**运算符产生将左侧操作数的值提升到右侧操作数的幂的结果。

如果任何一个操作数具有支持**运算的对象类型,则对象语义定义结果。首先检查左侧操作数。

如果任何一个或两个操作数具有非数字类型,则将它们的值转换为intfloat类型,具体情况视情况而定。如果两个操作数都具有非负整数类型,并且结果可以表示为int,则结果类型为int;否则,结果类型为float。如果任何一个或两个操作数都是前导数字或非数字字符串,则必须为每个操作数产生一个非致命错误。

示例

2**3;   // int with value 8
2**3.0;   // float with value 8.0
"2.0"**"3"; // float with value 8.0

一元运算符

一般

语法

unary-expression:
   exponentiation-expression
   unary-op-expression
   error-control-expression
   cast-expression

语义

这些运算符从右到左结合。

一元算术运算符

语法

unary-op-expression:
   unary-operator   unary-expression

unary-operator: one of
   +   -   ~

约束

一元+和一元-的操作数必须具有标量兼容类型。

一元~运算符的操作数必须具有算术或字符串类型,或者是一个支持~的对象。

语义

算术操作数

对于与算术操作数一起使用的一元+运算符,结果的类型和值是操作数的类型和值。

对于与算术操作数一起使用的一元-运算符,结果的值是操作数的负值。但是,如果int操作数的原始值是最小可表示该类型的可表示值,则操作数被视为float,结果将是float

对于与int操作数一起使用的一元~运算符,结果的类型是int。结果的值是操作数值的按位补码(也就是说,结果中的每个位只有在操作数中的对应位为清除时才会被设置)。对于与float操作数一起使用的一元~运算符,在计算按位补码之前,操作数的值首先被转换为int

布尔操作数

对于与TRUE值操作数一起使用的一元+运算符,结果的值是1,类型是int。当与FALSE值操作数一起使用时,结果的值是零,类型是int

对于与TRUE值操作数一起使用的一元-运算符,结果的值是-1,类型是int。当与FALSE值操作数一起使用时,结果的值是零,类型是int

NULL 值操作数

对于与NULL值操作数一起使用的一元+或一元-运算符,结果的值是零,类型是int

字符串操作数

对于与数字字符串或前导数字字符串一起使用的一元+-运算符,字符串首先被转换为intfloat,具体情况视情况而定,然后被视为算术操作数。前导数字字符串中的尾随非数字字符被忽略。对于非数字字符串,结果类型为int,值为0。如果字符串是前导数字或非数字字符串,则必须产生一个非致命错误。

对于与字符串一起使用的一元~运算符,结果是字符串,其中每个字节都是源字符串中对应字节的按位补码。

对象操作数

如果操作数具有支持该运算的对象类型,则对象语义定义结果。否则,对于~,将发出致命错误,对于+-,将对象转换为int

示例

$v = +10;
if ($v1 > -5) // ...
$v = ~0b1010101;
$s = "\x86\x97"; $s = ~$s; // $s is "yh"

错误控制运算符

语法

error-control-expression:
   @   unary-expression

语义

运算符@抑制对一元表达式求值时生成的任何错误消息的报告。

如果使用库函数set_error_handler建立了自定义错误处理程序,则该处理程序仍然会被调用。

示例

$infile = @fopen("NoSuchFile.txt", 'r');

在打开失败的情况下,fopen返回的值是FALSE,这足以知道要处理错误。fopen调用可能生成的错误消息被抑制(不会显示,也不会记录)。

实现说明

给定以下示例

function f() {
  $ret = $y;
  return $ret;
}

$x = @f();  // without @, get "Undefined variable: y"

以下代码展示了此语句的处理方式

$origER = error_reporting();
error_reporting(0);
$tmp = f();
$curER = error_reporting();
if ($curER === 0) error_reporting($origER);
$x = $tmp;

强制转换运算符

语法

cast-expression:
   (   cast-type   )   unary-expression

cast-type: one of
   array   binary   bool   boolean   double   int   integer   float   object
   real   string   unset

约束

unset强制转换类型不再受支持,会导致编译时错误。

语义

强制转换类型unset和二进制(见下文)外,强制转换表达式操作数的值被转换为强制转换类型指定的类型,这就是结果的类型和值。这种结构被称为强制转换,并用作动词,“强制转换”。如果没有进行转换,则结果的类型和值与强制转换表达式的类型和值相同。

强制转换会导致信息丢失。

array强制转换类型会导致转换为数组类型

binary强制转换类型保留用于将来在处理所谓的二进制字符串时使用。目前,它完全等同于string强制转换。

boolboolean强制转换类型会导致转换为bool类型

intinteger强制转换类型会导致转换为int类型

floatdoublereal强制转换类型会导致转换为float类型

object强制转换类型会导致转换为object类型

string强制转换类型会导致转换为string类型

示例

(int)(10/3)          // results in the int 3 rather than the float 3.333...
(array)(16.5)      // results in an array of 1 float; [0] = 16.5
(int)(float)"123.87E3" // results in the int 123870

instanceof运算符

语法

instanceof-expression:
   unary-expression
   instanceof-subject   instanceof   class-type-designator

instanceof-subject:
   instanceof-expression

语义

运算符instanceof如果instanceof-subject指定的值是一个具有class-type-designator指定类型的对象,是一个类型派生自该类型的对象,或者是一个类型实现class-type-designator指定的接口的对象,则返回TRUE。否则,它返回FALSE

类型可以通过class-type-designator以三种形式之一指定

  1. 限定名直接指定类型名。
  2. 当使用新变量形式时,新变量可以具有包含类或接口名称的字符串值。
  3. 或者,新变量可以指定一个对象,在这种情况下,对象的类型将用作指定的类型。请注意,接口不能用此形式指定。

请注意,如果给定类型的名称不对应于现有的类或接口,则instanceof不会调用自动加载器,而是返回FALSE

示例

class C1 {  }
$c1 = new C1;
class C2 {  }
$c2 = new C2;
class D extends C1 { };
$d = new D;
var_dump($d instanceof C1);      // TRUE
var_dump($d instanceof C2);      // FALSE
var_dump($d instanceof D);       // TRUE
// -----------------------------------------
interface I1 { }
interface I2 { }
class E1 implements I1, I2 { }
$e1 = new E1;
var_dump($e1 instanceof I1);       // TRUE
$iName = "I2";
var_dump($e1 instanceof $iName);   // TRUE
$e2 = new E1;
var_dump($e2 instanceof $e1);      // TRUE

逻辑非运算符

logical-NOT-expression:
   instanceof-expression
   !   instanceof-expression

语义

操作数的值被转换为bool类型,如果它是TRUE,则运算符的结果是FALSE。否则,结果是TRUE

示例

$t = TRUE;
if (!$t) // ...

乘法运算符

语法

multiplicative-expression:
   logical-NOT-expression
   multiplicative-expression   *   logical-NOT-expression
   multiplicative-expression   /   logical-NOT-expression
   multiplicative-expression   %   logical-NOT-expression

约束

运算符/和运算符%的右侧操作数不能为零。

语义

如果任何一个操作数是支持该运算的对象,则结果由该对象的语义定义,首先检查左侧操作数。

二元*运算符产生其操作数的乘积。如果任何一个或两个操作数具有非数字类型,则将它们的值转换为intfloat类型,具体情况视情况而定。如果任何一个或两个操作数都是前导数字或非数字字符串,则必须为每个操作数产生一个非致命错误。然后,如果任何一个操作数具有float类型,则将另一个操作数转换为该类型,结果类型为float。否则,两个操作数都具有int类型,在这种情况下,如果结果值可以表示为int类型,那就是结果类型。否则,结果类型将为float

除以零会导致非致命错误。如果分子的值为正,则结果值为INF。如果分子的值为负,则结果值为-INF。如果分子的值为零,则结果值为NAN

二元/运算符产生将左侧操作数除以右侧操作数的商。如果任何一个或两个操作数具有非数字类型,则将它们的值转换为intfloat类型,具体情况视情况而定。如果任何一个或两个操作数都是前导数字或非数字字符串,则必须为每个操作数产生一个非致命错误。然后,如果任何一个操作数具有float类型,则将另一个操作数转换为该类型,结果类型为float。否则,两个操作数都具有int类型,在这种情况下,如果计算的数学值可以使用int类型保留,那就是结果类型;否则,结果类型是float

二元%运算符产生将左侧操作数除以右侧操作数的余数。如果两个操作数的类型不是int,则将它们的值转换为该类型。如果任何一个或两个操作数都是前导数字或非数字字符串,则必须为每个操作数产生一个非致命错误。结果类型为int。如果右侧操作数的值为零,则将抛出DivisionByZeroError类型的异常。

这些运算符从左到右结合。

示例

-10 * 100;       // int with value -1000
100 * -3.4e10;   // float with value -3400000000000
"123" * "2e+5;   // float with value 24600000
100 / 100;       // int with value 1
100  / "123";    // float with value 0.8130081300813
"123" % 100;     // int with value 23
100 / 0;         // results in a diagnostic followed by bool with value false
100 / 0.0;       // results in a diagnostic followed by bool with value false
1.3 / 0;         // results in a diagnostic followed by bool with value false
1.3 / 0.0;       // results in a diagnostic followed by bool with value false
100 / "a";       // results in a diagnostic followed by bool with value false (a is converted to 0)

加法运算符

语法

additive-expression:
   multiplicative-expression
   additive-expression   +   multiplicative-expression
   additive-expression   -   multiplicative-expression
   additive-expression   .   multiplicative-expression

约束

如果+的任何一个操作数是数组类型,另一个操作数也必须是数组类型。

二元-运算符不能应用于数组。

语义

如果任何一个操作数是支持该运算的对象,则结果由该对象的语义定义,首先检查左侧操作数。

对于非数组操作数,二元+运算符生成这些操作数的总和,而二元-运算符在从左操作数中减去右操作数时生成其操作数的差。如果任一操作数或两个操作数都具有非数组、非数字类型,则其值将根据需要转换为intfloat类型。如果任一操作数或两个操作数是领先数字或非数字字符串,则必须针对每个操作数生成一个非致命错误。然后,如果任一操作数具有float类型,则另一个操作数将转换为该类型,并且结果具有float类型。否则,两个操作数都具有int类型,在这种情况下,如果结果值可以表示为int类型,则该类型为结果类型。否则,结果将具有float类型。

如果两个操作数都是数组类型,二元+运算符将生成一个新的数组,该数组是两个操作数的并集。结果是左操作数的副本,在其末尾插入元素,按顺序,对于右操作数中的每个元素,其键在左操作数中不存在。右操作数中任何键在左操作数中存在的元素都会被忽略。

二元.运算符创建一个字符串,该字符串是左操作数和右操作数的串联,按此顺序。如果任一操作数或两个操作数的类型不是string,则其值将转换为string类型。结果具有string类型。

这些运算符从左到右结合。

示例

-10 + 100;        // int with value 90
100 + -3.4e10;    // float with value -33999999900
"123" + "2e+5";   // float with value 200123
100 - "123";      // int with value 23
-3.4e10 - "abc";  // float with value -34000000000
// -----------------------------------------
[1, 5 => FALSE, "red"] + [4 => -5, 1.23]; // [1, 5 => FALSE, "red", 4 => -5]
  // dupe key 5 (value 1.23) is ignored
[NULL] + [1, 5 => FALSE, "red"];          // [NULL, 5 => FALSE, "red"]
  // dupe key 0 (value 1) is ignored
[4 => -5, 1.23] + [NULL];                 // [4 => -5, 1.23, 0 => NULL]
// -----------------------------------------
-10 . NAN;        // string with value "-10NAN"
INF . "2e+5";     // string with value "INF2e+5"
TRUE . NULL;      // string with value "1"
10 + 5 . 12 . 100 - 50;  // int with value 1512050; ((((10 + 5).12).100)-50)

位移运算符

语法

shift-expression:
   additive-expression
   shift-expression   <<   additive-expression
   shift-expression   >>   additive-expression

约束

每个操作数必须具有可标量类型。

语义

如果任何一个操作数是支持该运算的对象,则结果由该对象的语义定义,首先检查左侧操作数。

给定表达式e1 << e2e1的值中的位向左移动e2位。从左侧移出的位被丢弃,从右侧移入零位。给定表达式e1 >> e2e1的值中的位向右移动e2位。从右侧移出的位被丢弃,符号位从左侧传播。

如果任一操作数没有int类型,则其值首先转换为该类型。如果任一操作数或两个操作数是领先数字或非数字字符串,则必须针对每个操作数生成一个非致命错误。

结果的类型是int,结果的值是在移位完成后。e1e2的值不变。

左移,其中移位计数大于整数类型的位宽(例如 32 或 64),必须始终导致 0,即使没有针对此操作的本机处理器支持。

右移,其中移位计数大于整数类型的位宽(例如 32 或 64),当e1为正时必须始终导致 0,当e1为负时必须始终导致 -1,即使没有针对此操作的本机处理器支持。

如果移位计数为负,则抛出ArithmeticError类型的异常。

这些运算符从左到右结合。

示例

1000 >> 2   // 0x3E8 is shifted right 2 places
-1000 << 2  // 0xFFFFFC18 is shifted left 5 places
123 >> 128  // Shift count larger than bit width => result 0
123 << 33   // For 32-bit integers the result is zero, otherwise
            // it is 0x7B shifted left 33 places

关系运算符

语法

relational-expression:
   shift-expression
   relational-expression   <   shift-expression
   relational-expression   >   shift-expression
   relational-expression   <=   shift-expression
   relational-expression   >=   shift-expression
   relational-expression   <=>   shift-expression

语义

运算符<=>表示两个表达式之间的比较运算符,如果左侧表达式小于右侧表达式(即如果$a < $b返回TRUE),结果将是小于0的整数,如下所述由运算符<的语义定义,整数0如果这些表达式相等(由==运算符的语义定义),否则为大于0的整数。

运算符<表示小于,运算符>表示大于,运算符<=表示小于或等于,运算符>=表示大于或等于

结果的类型是bool

注意,大于语义实现为小于的逆运算,即$a > $b$b < $a相同。如果操作数没有良好排序,这可能会导致令人困惑的结果,例如比较两个没有比较语义的对象,或比较数组。

下表显示了不同类型的比较结果,左侧操作数垂直显示,右侧操作数水平显示。转换根据类型转换规则执行。

NULLboolintfloatstringarrayobjectresource
NULL=->->->->-><<
bool<-1<-<-<-<-<-<-
int<-->22<-<3<-
float<-->22<-<3<-
string<-->->->2, 4<32
array<-->>>>53>
object>->333363
resource>->->->2<32
  • =表示结果始终为“等于”,即严格比较始终为FALSE,相等比较始终为TRUE
  • <表示左侧操作数始终小于右侧操作数。
  • >表示左侧操作数始终大于右侧操作数。
  • ->表示左侧操作数将转换为右侧操作数的类型。
  • <-表示右侧操作数将转换为左侧操作数的类型。
  • 数字表示以下情况之一
  1. 如果任一操作数具有bool类型,则另一个操作数将转换为该类型。结果是两个操作数在转换后的逻辑比较,其中FALSE定义为小于TRUE
  2. 如果其中一个操作数具有算术类型,是一个资源,或是一个可以表示为intfloat而不会丢失精度的数字字符串,则操作数将转换为相应的算术类型,float优先于int,资源转换为int。结果是两个操作数在转换后的数值比较。
  3. 如果只有一个操作数具有对象类型,如果该对象具有比较处理程序,则该处理程序将定义结果。否则,如果该对象可以转换为另一个操作数的类型,则它将被转换,并且结果用于比较。否则,该对象与任何其他操作数类型相比都更大。
  4. 如果两个操作数都是非数字字符串,则结果是两个操作数的词法比较。具体来说,字符串从它们的第一个字节开始逐字节比较。如果两个字节比较相等,并且两个字符串中都没有更多字节,则两个字符串相等,比较结束;否则,如果这是其中一个字符串的最后一个字节,则较短的字符串比较长的字符串更小,比较结束。如果两个字节比较不相等,则具有较低值的字节的字符串比另一个字符串更小,比较结束。如果字符串中还有更多字节,则对下一对字节重复此过程。
  5. 如果两个操作数都是数组类型,如果数组的元素数量不同,则元素数量较少的数组被认为比另一个数组更小,无论每个数组的键和值如何,比较结束。对于具有相同元素数量的数组,将逐个考虑左侧操作数中的键,如果左侧操作数中的下一个键存在于右侧操作数中,则比较相应的 value。如果它们不相等,则包含较小值的数组被认为比另一个数组更小,比较结束;否则,使用下一个元素重复此过程。如果左侧操作数中的下一个键在右侧操作数中不存在,则无法比较数组,并且返回FALSE。如果所有值都相等,则认为两个数组相等。
  6. 在比较两个对象时,如果任何对象类型具有自己的比较语义,那么这将定义结果,左侧操作数优先。否则,如果对象类型不同,则比较结果为FALSE。如果对象类型相同,则使用上面描述的数组比较来比较对象的属性。

这些运算符从左到右结合。

示例

"" < "ab"       // result has value TRUE
"a" > "A"       // result has value TRUE
"a0" < "ab"     // result has value TRUE
"aA <= "abc"    // result has value TRUE
// -----------------------------------------
NULL < [10,2.3] // result has value TRUE
TRUE > -3.4     // result has value FALSE
TRUE < -3.4     // result has value FALSE
TRUE >= -3.4    // result has value TRUE
FALSE < "abc"   // result has value TRUE
// -----------------------------------------
10 <= 0         // result has value FALSE
10 >= "-3.4"    // result has value TRUE
"-5.1" > 0      // result has value FALSE
// -----------------------------------------
[100] < [10,20,30] // result has value TRUE (LHS array is shorter)
[10,20] >= ["red"=>0,"green"=>0] // result has value FALSE, (key 10 does not exists in RHS)
["red"=>0,"green"=>0] >= ["green"=>0,"red"=>0] // result has value TRUE (order is irrelevant)
// ------------------------------------
function order_func($a, $b) {
    return ($a->$x <=> $b->x) ?: ($a->y <=> $b->y) ?: ($a->z <=> $b->z);
}

相等运算符

语法

equality-expression:
   relational-expression
   equality-expression   ==   relational-expression
   equality-expression   !=   relational-expression
   equality-expression   <>   relational-expression
   equality-expression   ===   relational-expression
   equality-expression   !==   relational-expression

语义

运算符==表示值相等,运算符!=<>等效,表示值不相等

对于运算符==!=<>,不同类型的操作数将根据关系运算符中的相同规则进行转换和比较。两个不同类型的对象永远不相等。

运算符===表示相同类型和值相等,或同一性比较,运算符!==表示===的反面。如果值具有相同的类型并且比较相等,则它们被视为相同,以及以下附加条件

  • 在比较两个对象时,同一性运算符检查两个操作数是否为完全相同的对象,而不是两个类型和值相同的不同对象。
  • 数组必须具有相同顺序的相同元素才能被视为相同。
  • 字符串相同,如果它们包含相同的字符,与值比较运算符不同,不会对数字字符串执行任何转换。

结果的类型是bool

这些运算符从左到右结合。

示例

"a" <> "aa" // result has value TRUE
// -----------------------------------------
NULL == 0   // result has value TRUE
NULL === 0  // result has value FALSE
TRUE != 100  // result has value FALSE
TRUE !== 100  // result has value TRUE
// -----------------------------------------
"10" != 10  // result has value FALSE
"10" !== 10 // result has value TRUE
// -----------------------------------------
[10,20] == [10,20.0]  // result has value TRUE
[10,20] === [10,20.0] // result has value FALSE
["red"=>0,"green"=>0] === ["red"=>0,"green"=>0] // result has value TRUE
["red"=>0,"green"=>0] === ["green"=>0,"red"=>0] // result has value FALSE

按位与运算符

语法

bitwise-AND-expression:
   equality-expression
   bitwise-AND-expression   &   equality-expression

约束

每个操作数必须具有可标量类型。

语义

如果任何一个操作数是支持该运算的对象,则结果由该对象的语义定义,首先检查左侧操作数。

如果任一操作数没有int类型,则其值首先转换为该类型。如果任一操作数或两个操作数是领先数字或非数字字符串,则必须针对每个操作数生成一个非致命错误。

此运算符的结果是两个操作数的按位与,该结果的类型是int

但是,如果两个操作数都是字符串,则结果是字符串,该字符串由字节序列组成,这些字节序列是在匹配位置的操作数字符串的字节上执行按位与运算的结果(result[0] = s1[0] & s2[0],等等)。如果其中一个字符串比另一个字符串更长,则它将被截断为较短的字符串的长度。

此运算符从左到右结合。

示例

0b101111 & 0b101          // 0b101
$lLetter = 0x73;          // letter 's'
$uLetter = $lLetter & ~0x20;  // clear the 6th bit to make letter 'S'

按位异或运算符

语法

bitwise-exc-OR-expression:
   bitwise-AND-expression
   bitwise-exc-OR-expression   ^   bitwise-AND-expression

约束

每个操作数必须具有可标量类型。

语义

如果任何一个操作数是支持该运算的对象,则结果由该对象的语义定义,首先检查左侧操作数。

如果任一操作数没有int类型,则其值首先转换为该类型。如果任一操作数或两个操作数是领先数字或非数字字符串,则必须针对每个操作数生成一个非致命错误。

此运算符的结果是两个操作数的按位异或,该结果的类型是int

但是,如果两个操作数都是字符串,则结果是字符串,该字符串由字节序列组成,这些字节序列是在匹配位置的操作数字符串的字节上执行按位异或运算的结果(result[0] = s1[0] ^ s2[0],等等)。如果其中一个字符串比另一个字符串更长,则它将被截断为较短的字符串的长度。

此运算符从左到右结合。

示例

0b101111 ^ 0b101    // 0b101010
$v1 = 1234; $v2 = -987; // swap two integers having different values
$v1 = $v1 ^ $v2;
$v2 = $v1 ^ $v2;
$v1 = $v1 ^ $v2;    // $v1 is now -987, and $v2 is now 1234

按位或运算符

语法

bitwise-inc-OR-expression:
   bitwise-exc-OR-expression
   bitwise-inc-OR-expression   |   bitwise-exc-OR-expression

约束

每个操作数必须具有可标量类型。

语义

如果任何一个操作数是支持该运算的对象,则结果由该对象的语义定义,首先检查左侧操作数。

如果任一操作数没有int类型,则其值首先转换为该类型。如果任一操作数或两个操作数是领先数字或非数字字符串,则必须针对每个操作数生成一个非致命错误。

此运算符的结果是两个操作数的按位或,该结果的类型是int

但是,如果两个操作数都是字符串,则结果是字符串,该字符串由字节序列组成,这些字节序列是在匹配位置的操作数字符串的字节上执行按位或运算的结果(result[0] = s1[0] | s2[0],等等)。如果其中一个字符串比另一个字符串更短,则它将用零字节扩展。

此运算符从左到右结合。

示例

0b101111 | 0b101      // 0b101111
$uLetter = 0x41;      // letter 'A'
$lLetter = $upCaseLetter | 0x20;  // set the 6th bit to make letter 'a'

逻辑与运算符(形式 1)

语法

logical-AND-expression-1:
   bitwise-inc-OR-expression
   logical-AND-expression-1   &&   bitwise-inc-OR-expression

语义

给定表达式e1 && e2e1首先被计算。如果e1转换为boolFALSE,则不会计算e2,结果具有bool类型,值为FALSE。否则,将计算e2。如果e2转换为boolFALSE,则结果具有bool类型,值为FALSE;否则,它具有bool类型,值为TRUE。在e1的计算之后有一个顺序点。

此运算符从左到右结合。

除了优先级不同之外,运算符&&与运算符and具有完全相同的语义。

示例

if ($month > 1 && $month <= 12) ...

逻辑或运算符(形式 1)

语法

logical-inc-OR-expression-1:
   logical-AND-expression-1
   logical-inc-OR-expression-1   ||   logical-AND-expression-1

语义

给定表达式e1 || e2e1首先被计算。如果e1转换为boolTRUE,则不会计算e2,结果具有bool类型,值为TRUE。否则,将计算e2。如果e2转换为boolTRUE,则结果具有bool类型,值为TRUE;否则,它具有bool类型,值为FALSE。在e1的计算之后有一个顺序点。

此运算符从左到右结合。

示例

if ($month < 1 || $month > 12) ...

空合并运算符

语法

coalesce-expression:
   logical-inc-OR-expression-1
   logical-inc-OR-expression-1   ??   coalesce-expression

语义

给定表达式 e1 ?? e2,如果 e1 已设置且不为 NULL(即对于 issetTRUE),则结果为 e1。否则,仅当且仅当 e2 被评估时,结果才会变为整个表达式的结果。在评估 e1 后存在一个顺序点。

请注意,?? 的语义类似于 isset,因此未初始化的变量在 e1 中使用时不会产生警告。

此运算符从右到左结合。

示例

$arr = ["foo" => "bar", "qux" => NULL];
$obj = (object)$arr;

$a = $arr["foo"] ?? "bang"; // "bar" as $arr["foo"] is set and not NULL
$a = $arr["qux"] ?? "bang"; // "bang" as $arr["qux"] is NULL
$a = $arr["bing"] ?? "bang"; // "bang" as $arr["bing"] is not set

$a = $obj->foo ?? "bang"; // "bar" as $obj->foo is set and not NULL
$a = $obj->qux ?? "bang"; // "bang" as $obj->qux is NULL
$a = $obj->bing ?? "bang"; // "bang" as $obj->bing is not set

$a = NULL ?? $arr["bing"] ?? 2; // 2 as NULL is NULL, and $arr["bing"] is not set

function foo() {
    echo "executed!", PHP_EOL;
}
var_dump(true ?? foo()); // outputs bool(true), "executed!" does not appear as it short-circuits

条件运算符

语法

conditional-expression:
   coalesce-expression
   conditional-expression   ?   expressionopt   :   coalesce-expression

语义 给定表达式 e1 ? e2 : e3,首先评估 e1,如果它具有其他类型,则将其 转换为 bool。如果结果为 TRUE,则仅当且仅当 e2 被评估时,结果及其类型才会变为整个表达式的结果和类型。否则,仅当且仅当 e3 被评估时,结果及其类型才会变为整个表达式的结果和类型。在评估 e1 后存在一个顺序点。如果省略了 e2,则整个表达式的结果和类型是 e1 的值和类型(在转换为 bool 之前)。

此运算符从左到右结合。

示例

for ($i = -5; $i <= 5; ++$i)
  echo "$i is ".(($i & 1 == TRUE) ? "odd\n" : "even\n");
// -----------------------------------------
$a = 10 ? : "Hello";  // result is int with value 10
$a = 0 ? : "Hello";     // result is string with value "Hello"
$i = PHP_INT_MAX;
$a = $i++ ? : "red";  // result is int with value 2147483647 (on a 32-bit
                // system) even though $i is now the float 2147483648.0
// -----------------------------------------
$i++ ? f($i) : f(++$i); // the sequence point makes this well-defined
// -----------------------------------------
function factorial($int)
{
  return ($int > 1) ? $int * factorial($int - 1) : $int;
}

赋值运算符

一般

语法

assignment-expression:
   conditional-expression
   simple-assignment-expression
   compound-assignment-expression

约束

赋值运算符的左操作数必须是可修改的左值。

语义

这些运算符从右到左结合。

简单赋值

语法

simple-assignment-expression:
   variable   =   assignment-expression
   list-intrinsic   =   assignment-expression

约束

如果左操作数指定的地址是字符串元素,则键必须不是负值的 int,并且右操作数必须具有 string 类型。

语义

如果赋值表达式指定具有值类型的表达式,请参见 标量类型的赋值 如果赋值表达式指定具有句柄类型的表达式,请参见 对象和资源类型的赋值。如果赋值表达式指定具有数组类型的表达式,请参见 数组类型的赋值

结果的类型和值是在存储(如果有 [见下文])发生后左操作数的类型和值。结果不是左值。

如果左操作数指定的地址是非存在的数组元素,则会插入一个新的元素,该元素具有指定的键,并且其值为右操作数的值。

如果左操作数指定的地址是字符串元素,则如果键是负值的 int,则不会产生副作用。否则,如果键是非负值的 int,则来自右操作数的左端单个字符将存储在指定的地址;右操作数字符串中的所有其他字符都将被忽略。如果指定的地址超出目标字符串的末尾,则该字符串将扩展到新长度,并在新添加的字符之前使用空格 (0x20) 作为填充添加到旧的末尾。如果右操作数是空字符串,则存储空字符 \0 (0x00)。

示例

$a = $b = 10    // equivalent to $a = ($b = 10)
$v = array(10, 20, 30);
$v[1] = 1.234;    // change the value (and type) of an existing element
$v[-10] = 19;   // insert a new element with int key -10
$v["red"] = TRUE; // insert a new element with string key "red"
$s = "red";
$s[1] = "X";    // OK; "e" -> "X"
$s[-5] = "Y";   // warning; string unchanged
$s[5] = "Z";    // extends string with "Z", padding with spaces in [3]-[5]
$s = "red";
$s[0] = "DEF";    // "r" -> "D"; only 1 char changed; "EF" ignored
$s[0] = "";       // "D" -> "\0"
$s["zz"] = "Q";   // warning; defaults to [0], and "Q" is stored there
// -----------------------------------------
class C { ... }
$a = new C; // make $a point to the allocated object

list 内置函数

语法

list-intrinsic:
   list   (   list-expression-list   )

list-expression-list:
   unkeyed-list-expression-list
   keyed-list-expression-list   ,opt

unkeyed-list-expression-list:
   list-or-variable
   ,
   unkeyed-list-expression-list   ,   list-or-variableopt

keyed-list-expression-list:
   expression   =>   list-or-variable
   keyed-list-expression-list   ,   expression   =>   list-or-variable

list-or-variable:
   list-intrinsic
   &opt   variable

约束

list-intrinsic 必须用作 simple-assignment-expression 中的左操作数,其右操作数必须是指定实现 ArrayAccess 接口的数组或对象的表达式(称为源数组)。

list-or-variable 中的每个变量必须指定一个可写的变量(称为目标变量)。

list-expression-list 中的至少一个元素必须是非空的。

语义

此内置函数将源数组中的一个或多个元素分配给目标变量。目标变量可以按引用分配。成功后,它将返回源数组的副本。如果源数组不是实现 ArrayAccess 的数组或对象,则不会执行任何分配,返回值为 NULL

对于unkeyed-list-expression-list,源数组中所有具有 string 类型键的元素都将被忽略。具有 int 键为 0 的元素被分配给第一个目标变量,具有 int 键为 1 的元素被分配给第二个目标变量,依此类推,直到所有目标变量都被分配。任何其他数组元素都被忽略。如果具有 int 键的源数组元素少于目标变量,则未分配的目标变量将被设置为 NULL,并将生成一个非致命错误。

对于keyed-list-expression-list,每个键-变量对依次处理,键和变量由 => 符号分隔。具有第一个键的元素(该键已使用与 下标运算符 相同的规则转换)被分配给第一个目标变量。如果存在,则对第二个 => 对重复此过程,依此类推。任何其他数组元素都被忽略。如果没有具有给定键的数组元素,则未分配的目标变量将被设置为 NULL,并将生成一个非致命错误。

分配必须按此顺序进行。

任何目标变量都可以是列表,在这种情况下,预期相应的元素是一个数组。

如果源数组元素和目标变量以任何方式重叠,则行为未定义。

示例

list($min, $max, $avg) = array(0, 100, 67);
  // $min is 0, $max is 100, $avg is 67
list($min, $max, $avg) = array(2 => 67, 1 => 100, 0 => 0);
  // same as example above
list($min, , $avg) = array(0, 100, 67);
  // $min is 0, $avg is 67
list($min, $max, $avg) = array(0, 2 => 100, 4 => 67);
  // $min is 0, $max is NULL, $avg is 100
list($min, list($max, $avg)) = [0, [1 => 67, 99, 0 => 100], 33];
  // $min is 0, $max is 100, $avg is 67

list($arr[1], $arr[0]) = [0, 1];
  // $arr is [1 => 0, 0 => 1], in this order
list($arr2[], $arr2[]) = [0, 1];
  // $arr2 is [0, 1]

$a = [1, 2];
list(&$one, $two) = $a;
  // $a[0] is 1, $a[1] is 2
$one++;
  // $a[0] is 2, $a[1] is 2

list("one" => $one, "two" => $two) = ["one" => 1, "two" => 2];
  // $one is 1, $two is 2
list(
    "one" => $one,
    "two" => $two,
) = [
    "one" => 1,
    "two" => 2,
];
  // $one is 1, $two is 2

$a = ['one' => 1, 'two' => 2];
list('one' => &$one, 'two' => $two) = $a;
  // $a['one'] is 1, $a['two'] is 2
$one++;
  // $a['one'] is 2, $a['two'] is 2

list(list("x" => $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = [
    ["x" => 1, "y" => 2],
    ["x" => 3, "y" => 4]
];
  // $x1 is 1, $y1 is 2, $x2 is 3, $y2 is 4
list(0 => list($x1, $x2), 1 => list($x2, $y2)) = [[1, 2], [3, 4]];
  // $x1 is 1, $y1 is 2, $x2 is 3, $y2 is 4

按引用赋值

语法

byref-assignment-expression:
   variable   =   &   variable

约束

右操作数变量必须是左值,或者是对返回按引用值的函数的调用。

语义

一元表达式成为赋值表达式的别名。如果赋值表达式指定具有值类型的表达式,请参见 标量类型的按引用赋值 如果赋值表达式指定具有句柄类型的表达式,请参见 非标量类型的按引用赋值。如果赋值表达式指定具有数组类型的表达式,请参见 延迟数组复制

示例

$a = 10;
$b =& $a;   // make $b an alias of $a
++$a;       // increment $a/$b to 11
$b = -12;   // sets $a/$b to -12
$a = "abc";     // sets $a/$b to "abc"
unset($b);      // removes $b's alias to $a
// -----------------------------------------
function &g2() { $t = "xxx"; return $t; } // return byRef
$b =& g2();     // make $b an alias to "xxx"

复合赋值

语法

compound-assignment-expression:
   variable   compound-assignment-operator   assignment-expression

compound-assignment-operator: one of
   **=   *=   /=   %=   +=   -=   .=   <<=   >>=   &=   ^=   |=

约束

对相应的二元运算符适用的任何约束也适用于复合赋值形式。

语义

表达式 e1 op= e2 等效于 e1 = e1 op (e2),除了 e1 仅被评估一次。

示例

$v = 10;
$v += 20;   // $v = 30
$v -= 5;    // $v = 25
$v .= 123.45  // $v = "25123.45"
$a = [100, 200, 300];
$i = 1;
$a[$i++] += 50; // $a[1] = 250, $i → 2

yield 运算符

语法

yield-from-expression:
   yield from   assignment-expression

yield-expression:
   yield-from-expression
   yield
   yield   yield-expression
   yield   yield-from-expression   =>   yield-expression

语义

任何包含yield-expression 的函数都是生成器函数。生成器函数生成零个或多个键/值对的集合,其中每个对代表某个系列中的下一个。例如,生成器可能会yield 随机数或斐波那契数列。当显式调用生成器函数时,它将返回一个类型为 Generator 的对象,该对象实现接口 Iterator。因此,这允许使用 foreach 语句 对该对象进行迭代。在每次迭代期间,引擎会隐式调用生成器函数以获取下一个键/值对。然后引擎会保存生成器状态以备后续键/值对请求使用。

yield 运算符会生成结果 NULL,除非调用了方法 Generator->send 来提供结果值。此运算符具有生成集合中下一个值的副作用。

如果从yield-expression 中省略键,则与相应值关联的元素键类型为 int。关联的键比此集合中之前分配的 int 键多一。但是,如果这是此集合中第一个具有 int 键的元素,则使用零。

如果值也被省略,则将使用 NULL 代替。

如果生成器函数定义声明它按引用返回,则键/值对中的每个值都将按引用yield。

以下内容仅适用于 yield from 形式

生成器函数(称为委派生成器)可以委派给另一个生成器函数(称为子生成器)、可遍历对象或数组,每个都由表达式指定。

赋值表达式yield 的每个值都直接传递给委派生成器的调用方。

发送到委派生成器的 send 方法的每个值都传递给子生成器的 send 方法。如果赋值表达式不是生成器函数,则会忽略任何发送的值。

赋值表达式抛出的异常会向上传播到委派生成器。

在遍历完成时,如果可遍历对象不是生成器,则会向委派生成器返回 NULL。如果可遍历对象是生成器,则其返回值将作为 yield from 表达式的值发送到委派生成器。

如果赋值表达式的值为先前以未捕获的异常终止的生成器,或者其值为既不可遍历又不可数组的东西,则会抛出类型为 Error 的异常。

示例

function getTextFileLines($filename)
{
  $infile = fopen($filename, 'r');
  if ($infile == FALSE) { /* deal with the file-open failure */ }

  try
  {
    while ($textLine = fgets($infile))  // while not EOF
    {
      $textLine = rtrim($textLine, "\r\n"); // strip off terminator
      yield $textLine;
    }
  }
  finally
  {
    fclose($infile);
  }
}
foreach (getTextFileLines("Testfile.txt") as $line) { /* process each line */ }
// -----------------------------------------
function series($start, $end, $keyPrefix = "")
{
  for ($i = $start; $i <= $end; ++$i)
  {
    yield $keyPrefix . $i => $i;  // generate a key/value pair
  }
}
foreach (series(1, 5, "X") as $key => $val) { /* process each key/val pair */ }
// -----------------------------------------
function gen()
{
    yield 1;
    yield from gen2();
    yield 4;
}
function gen2()
{
    yield 2;
    yield 3;
}
foreach (gen() as $val)
{
    echo $val . "\n";   // Produces the values 1, 2, 3, and 4
}
// -----------------------------------------
function g() {
  yield 1;
  yield from [2, 3];
  yield 4;
}
$g = g();
foreach ($g as $yielded) {
    echo $yielded . "\n";   // Produces the values 1, 2, 3, and 4
}

语法

print-expression:
   yield-expression
   print   print-expression

约束

print-expression 值必须是 可转换为字符串。特别是,它不应是数组,如果它是对象,它必须实现 __toString 方法

语义

在将print-expression 的值必要时转换为字符串后,print 会将生成的字符串写入 STDOUT。与 echo 不同,print 可以在任何允许表达式的上下文中使用。它始终返回 1 值。

另请参见:双引号字符串字面量heredoc 文档转换为字符串

示例

$v1 = TRUE;
$v2 = 123;
print  '>>' . $v1 . '|' . $v2 . "<<\n";   // outputs ">>1|123<<"
print ('>>' . $v1 . '|' . $v2 . "<<\n");  // outputs ">>1|123<<"
$v3 = "qqq{$v2}zzz";
print "$v3\n";            // outputs "qqq123zzz"
$a > $b ? print "..." : print "...";

逻辑与运算符(形式 2)

语法

logical-AND-expression-2:
   print-expression
   logical-AND-expression-2   and   yield-expression

语义

除了优先级不同之外,运算符与 运算符 && 的语义完全相同。

逻辑异或运算符

语法

logical-exc-OR-expression:
   logical-AND-expression-2
   logical-exc-OR-expression   xor   logical-AND-expression-2

语义

如果任一操作数没有 bool 类型,则首先将其值转换为该类型。

给定表达式 e1 xor e2,首先评估 e1,然后评估 e2。如果 e1e2 转换为 boolTRUE,但两者都不为 TRUE,则结果具有 bool 类型,值为 TRUE。否则,结果具有 bool 类型,值为 FALSE。在评估 e1 后存在一个顺序点。

此运算符从左到右结合。

示例

f($i++) xor g($i) // the sequence point makes this well-defined

逻辑或运算符(形式 2)

语法

logical-inc-OR-expression-2:
   logical-exc-OR-expression
   logical-inc-OR-expression-2   or   logical-exc-OR-expression

语义

除了优先级不同之外,运算符与 运算符 || 的语义完全相同。

脚本包含运算符

一般

语法

expression:
   logical-inc-OR-expression-2
   include-expression
   include-once-expression
   require-expression
   require-once-expression

语义

在创建大型应用程序或构建组件库时,能够将源代码分解成小巧、易于管理的部分(每个部分执行一些特定的任务,并且可以以某种方式共享、单独测试、维护和部署)非常有用。例如,程序员可以定义一系列有用的常量并在众多可能不相关的应用程序中使用它们。同样,一组类定义可以在需要创建这些类型的对象的众多应用程序之间共享。

包含文件是一个脚本,适合由另一个脚本包含。进行包含的脚本是包含文件,而被包含的脚本是被包含文件。一个脚本可以是包含文件、被包含文件,两者都是,或者两者都不是。

使用一系列常量示例,一个名为Positions.php的包含文件可能在它自己的命名空间Positions中定义常量TOPBOTTOMLEFTRIGHT。使用一组类示例,为了支持二维几何应用程序,一个名为Point.php的包含文件可能定义类Point。一个名为Line.php的包含文件可能定义类Line(其中Line表示为一对Point)。一个名为Circle.php的包含文件可能定义类Circle(其中Circle表示为一个表示原点的Point和一个半径)。

如果构成应用程序的多个脚本都使用一个或多个Position常量,它们可以通过include运算符分别包含相应的包含文件。然而,大多数包含文件在每次被包含时都以相同的方式运行,因此将同一个包含文件多次包含到同一个作用域中通常是浪费时间。在几何示例中,任何尝试多次包含同一个包含文件的操作都将导致致命错误“试图重新定义类类型”。但是,可以使用include_once运算符来避免这种情况。

require运算符include运算符的一种变体,而require_once运算符include_once运算符的一种变体。

重要的是要理解,与C/C++(或类似语言)的预处理器不同,PHP中的脚本包含不是一个文本替换过程。也就是说,包含文件的内容不会被视为直接替换包含文件中包含操作的源代码。有关更多信息,请参见下面的示例。

包含表达式可以写成类似函数调用的形式;然而,即使包含文件可以向其包含文件返回值,情况也并非如此。

用于指定包含文件的名称可以包含绝对路径或相对路径。在后一种情况下,实现可能会使用配置指令include_path来解析包含文件的路径。

示例

如上所述,PHP中的脚本包含不是一个文本替换过程(与C/C++的预处理器等不同)。这允许人们在包含文件中指定命名空间,即使不允许在一个文件中嵌套命名空间

include.php

namespace foo;
$x = 'hello';
foo();

index.php

namespace bar {
  include 'include.php'; // this is fine does not result in a nested namespace
  echo $x;               // hello
  \foo\foo();            // function foo is still member of the foo namespace

  //namespace baz{}      // would fail, nesting namespaces are not allowed
}

此外,不允许在一个文件中嵌套类,而包含文件中定义的类不会导致嵌套类(在条件定义的类中除外)——接口或特性也是如此。

include.php

namespace foo;
class Foo{}

index.php

class Bar{
  function bar(){
    include 'include.php'; // this is fine, does not result in a nested class
  }
  //class Foo1{}       // would fail, nested classes are not allowed
  //interface Foo2{}   // would fail as well
  //trait Foo3{}       // and would fail as well
}
new Foo();             // fails, \Foo could not be found
new \foo\Foo();        // fails, definition for class Foo was not loaded yet
$bar = new Bar();
$bar->bar();
new Foo();             // still fails, include != use statement
new \foo\Foo();        // succeeds, definition for class Foo was loaded

c-常量不能在函数或方法中定义(与d-常量不同。与上面其他示例一样,当常量通过文件包含发生时,它不会丢失其作用域,这是完全合法的。考虑以下示例

include.php

namespace foo;
const X = 2;

index.php

class Bar{
  function bar(){
    include 'include.php';
  }
}
echo X;                // emits a warning: Use of undefined constant X ...
echo \foo\X;           // same as above since the inclusion did not happen yet
$bar = new Bar();
$bar->bar();
echo X;                // still fails, include != use statement
echo \foo\X;           // succeeds, X was defined through the inclusion

与常量、函数、类、接口和特性不同,在文件顶层定义的变量在被另一个文件包含时,其含义可能会发生变化(成为全局变量)。当包含发生在局部作用域中时,就会出现这种情况。在这种情况下,变量将成为相应作用域的局部变量。以下示例为例

include.php

namespace foo;
$x = 'hello';

index.php

function bar(){
  include 'include.php';  // introduces the local variable $x
  $x = 'hi';              // modification is only local
  return $x;
}
echo bar();               // hi
echo $x;                  // emits a notice: Undefined variable: x ...

include 'include.php';    // introduces the global variable $x
echo $x;                  // hello

include运算符

语法

include-expression:
   include   expression

约束

表达式必须可以转换为字符串,该字符串指定一个文件名。

语义

include运算符导致解析和执行指定的包含文件。如果文件名无效或未指定可读文件,则会生成一个非致命错误。

当打开包含文件时,解析从文件开头的HTML模式开始。包含文件解析完成后,立即执行。

在包含文件中定义的变量将采用包含文件中包含发生的行号的作用域。但是,包含文件中定义的函数和类始终处于全局作用域。

如果包含发生在包含文件中的函数定义内部,则包含文件的全部内容将被视为在该函数内部定义。

此运算符产生的结果如下:

  1. 如果包含文件返回任何值,则该值为结果。
  2. 如果包含文件没有返回任何值,则结果为整数1
  3. 如果包含由于任何原因失败,则结果为FALSE

库函数get_included_files提供所有由四个包含运算符中的任何一个包含的文件的名称。

示例

$fileName = 'limits' . '.php'; include $fileName;
$inc = include('limits.php');
If ((include 'Positions.php') == 1) ...

include_once运算符

语法

include-once-expression:
   include_once   expression

语义

此运算符与include运算符相同,只是在include_once的情况下,同一个包含文件在每次程序执行时只包含一次。

一旦包含文件被包含,随后对该包含文件的include_once使用将返回TRUE,但不会发生其他操作。

文件通过完整的路径名进行识别,因此文件名不同的形式(如完整路径和相对路径)仍然被视为同一个文件。

示例

Point.php

\\ Point.php:
<?php ...
class Point { ... }

\\ Circle.php:
<?php ...
include_once 'Point.php';
class Circle { /* uses Point somehow */ }

\\ MyApp.php
include_once 'Point.php';   // Point.php included directly
include_once 'Circle.php';    // Point.php now not included indirectly
$p1 = new Point(10, 20);
$c1 = new Circle(9, 7, 2.4);

require运算符

语法

require-expression:
   require   expression

语义

此运算符与include运算符相同,只是在require的情况下,找不到或打开指定的包含文件会导致致命错误。

require_once运算符

语法

require-once-expression:
   require_once   expression

语义

此运算符与require运算符相同,只是在require_once的情况下,包含文件在每次程序执行时只包含一次。

一旦包含文件被包含,随后对该包含文件的require_once使用将返回TRUE,但不会发生其他操作。

文件通过完整的路径名进行识别,因此文件名不同的形式(如完整路径和相对路径)仍然被视为同一个文件。

常量表达式

语法

constant-expression:
   expression

约束

表达式只能使用以下语法元素

语义

常量表达式计算为组成表达式的值。