词法结构

脚本

一个 脚本 是一个有序的字符序列。通常,脚本与文件系统中的文件一一对应,但这种对应关系不是必需的。PHP 脚本被解析为一系列 8 位字节,而不是 Unicode 或任何其他字符集中的代码点。在本规范中,字节由其 ASCII 解释表示,如果它们是可打印字符。

从概念上讲,脚本使用以下步骤进行翻译

  1. 词法分析,将输入字符流转换为标记流。

  2. 语法分析,将标记流转换为可执行代码。

符合规范的实现必须接受使用 UTF-8 编码形式(如 Unicode 标准所定义)编码的脚本,并将它们转换为字符序列。实现可以选择接受和转换其他字符编码方案。

语法

本规范使用两种语法来展示 PHP 编程语言的语法。词法语法 定义了如何将源字符组合成空格、注释和标记。语法语法 定义了如何将生成的标记组合成 PHP 程序。

语法使用语法产生式来呈现,每个产生式定义一个非终结符以及该非终结符扩展成非终结符或终结符序列的可能方式。在产生式中,非终结符以斜体显示像这样,终结符以等宽字体显示像这样

语法产生式的第一行是所定义的非终结符的名称,后跟语法产生式的冒号,以及词法产生式的两个冒号。每个缩进的后续行都包含一个可能的扩展,该扩展作为非终结符或终结符序列给出。例如,产生式

single-line-comment-example::
   //   input-charactersopt
   #   input-charactersopt

将词法语法产生式单行注释示例定义为终结符//#,后跟可选的输入字符。每个扩展都在单独的行上列出。

虽然备选方案通常在单独的行上列出,但当备选方案数量众多时,短语“之一”可能会先于在一行上给出的扩展列表。例如,

hexadecimal-digit-example:: one of
   0   1   2   3   4   5   6   7   8   9
   a   b   c   d   e   f
   A   B   C   D   E   F

词法分析

一般

产生式输入文件是脚本词法结构的根。每个脚本都必须符合此产生式。

语法

input-file::
   input-element
   input-file   input-element

input-element::
   comment
   white-space
   token

语义

脚本的基本元素是注释、空格和标记。

脚本的词法处理涉及将该脚本缩减为一系列标记,这些标记成为语法分析的输入。标记可以用空格分隔,并用注释分隔。

词法处理总是生成最长的可能的词法元素。(例如,$a+++++$b必须解析为$a++ ++ +$b,这在语法上是无效的)。

注释

支持两种形式的注释:分隔注释单行注释

语法

comment::
   single-line-comment
   delimited-comment

single-line-comment::
   //   input-charactersopt
   #   input-charactersopt

input-characters::
   input-character
   input-characters   input-character

input-character::
   Any source character except   new-line

new-line::
   Carriage-return character (0x0D)
   Line-feed character (0x0A)
   Carriage-return character (0x0D) followed by line-feed character (0x0A)

delimited-comment::
   /*   No characters or any source character sequence except */   */

语义

除了在字符串文字或注释中,字符/*开始分隔注释,以字符*/结束。除了在字符串文字或注释中,字符//#开始单行注释,以换行符结束。该换行符不是注释的一部分。但是,如果单行注释是嵌入脚本中的最后一个源元素,则可以省略尾部的换行符。(注意:这允许像<?php ... // ... ?>这样的用法)。

分隔注释可以出现在脚本中任何空格可以出现的位置。(例如;/*...*/$c/*...*/=/*...*/567/*...*/;/*...*/解析为$c=567;,而$k = $i+++/*...*/++$j;解析为$k = $i+++ ++$j;)。

实现说明

在标记化期间,实现可以将分隔注释视为空格。

空格

空格由一个或多个换行符、空格和水平制表符字符的任意组合组成。

语法

white-space::
   white-space-character
   white-space   white-space-character

white-space-character::
   new-line
   Space character (0x20)
   Horizontal-tab character (0x09)

语义

空格和水平制表符字符被认为是水平空格字符

标记

一般

有几种类型的源标记

语法

token::
   variable-name
   name
   keyword
   integer-literal
   floating-literal
   string-literal
   operator-or-punctuator

名称

语法

variable-name::
   $   name

namespace-name::
   name
   namespace-name   \   name

namespace-name-as-a-prefix::
   \
   \opt   namespace-name   \
   namespace   \
   namespace   \   namespace-name   \

qualified-name::
   namespace-name-as-a-prefixopt   name

name::
   name-nondigit
   name   name-nondigit
   name   digit

name-nondigit::
   nondigit
   one of the characters 0x80–0xff

nondigit:: one of
   _
   a   b   c   d   e   f   g   h   i   j   k   l   m
   n   o   p   q   r   s   t   u   v   w   x   y   z
   A   B   C   D   E   F   G   H   I   J   K   L   M
   N   O   P   Q   R   S   T   U   V   W   X   Y   Z

语义

名称用于标识以下内容:常量变量标签函数类成员接口特性命名空间,以及heredocnowdoc 注释中的名称。

一个名称以下划线 (_) 、名称非数字或范围为 0x80--0xff 的扩展名称字符开头。后续字符还可以包括数字。一个变量名是一个以美元符号 ($) 开头的名称。

除非另有说明(函数方法接口特性命名空间),名称区分大小写,名称中的每个字符都有意义。

以两个下划线 (__) 开头的名称由 PHP 语言保留,用户代码不应定义这些名称。

以下名称不能用作类、接口或特性的名称:boolFALSEfloatintNULLstringTRUEiterablevoid

以下名称保留供将来使用,不应将其用作类、接口或特性的名称:mixednumericobjectresource

除了class之外,所有关键字都可以用作类、接口或特性的成员的名称。但是,class可以用作属性或方法的名称。

变量名和函数名(在函数调用上下文中使用时)不需要定义为源标记;它们也可以在运行时使用简单变量表达式创建。(例如,给定$a = "Total"; $b = 3; $c = $b + 5;${$a.$b.$c} = TRUE;等效于$Total38 = TRUE;,而${$a.$b.$c}()等效于Total38())。

示例

const MAX_VALUE = 100;
function getData() { /*...*/ }
class Point { /*...*/ }
interface ICollection { /*...*/ }

实现说明

不鼓励实现对名称长度施加任意限制。

关键字

一个关键字是一个类似名称的字符序列,它是保留的,不能用作名称。

语法

keyword:: one of
   abstract   and   array   as   break   callable   case   catch   class   clone
   const   continue   declare   default   die   do   echo   else   elseif   empty
   enddeclare   endfor   endforeach   endif   endswitch   endwhile   eval   exit
   extends   final   finally   for   foreach   function   global
   goto   if   implements   include   include_once   instanceof
   insteadof   interface   isset   list   namespace   new   or   print   private
   protected   public   require   require_once   return   static   switch
   throw   trait   try   unset   use   var   while   xor   yield   yield from

语义

关键字不区分大小写。

请注意,yield from是一个包含空格的单个标记。但是,注释在该空格中不允许。

此外,所有魔术常量也被视为关键字。

文字

值的源代码表示称为文字

整数文字

语法

integer-literal::
   decimal-literal
   octal-literal
   hexadecimal-literal
   binary-literal

decimal-literal::
   nonzero-digit
   decimal-literal   digit

octal-literal::
   0
   octal-literal   octal-digit

hexadecimal-literal::
   hexadecimal-prefix   hexadecimal-digit
   hexadecimal-literal   hexadecimal-digit

hexadecimal-prefix:: one of
   0x   0X

binary-literal::
   binary-prefix   binary-digit
   binary-literal   binary-digit

binary-prefix:: one of
   0b   0B

digit:: one of
   0   1   2   3   4   5   6   7   8   9

nonzero-digit:: one of
   1   2   3   4   5   6   7   8   9

octal-digit:: one of
   0   1   2   3   4   5   6   7

hexadecimal-digit:: one of
   0   1   2   3   4   5   6   7   8   9
   a   b   c   d   e   f
   A   B   C   D   E   F

binary-digit:: one of
   0   1

语义

十进制整数文字的值使用基数 10 计算;八进制整数文字使用基数 8 计算;十六进制整数文字使用基数 16 计算;二进制整数文字使用基数 2 计算。

如果整数文字表示的值可以放入类型 int 中,则该值将是结果值的类型;否则,类型将是 float,如下所述。

由于负数在 PHP 中表示为正数的否定,因此最小的负值(对于 32 位为 -2147483648,对于 64 位为 -9223372036854775808)不能表示为十进制整数文字。如果非负值太大而不能表示为int,它将变为float,然后取反。

使用十六进制、八进制或二进制表示法编写的文字被认为具有非负值。

整数文字始终是常量表达式。

示例

$count = 10;      // decimal 10

0b101010 >> 4;    // binary 101010 and decimal 4

0XAF << 023;      // hexadecimal AF and octal 23

在使用 32 位 int 表示的实现上

2147483648 -> 2147483648 (too big for int, so is a float)

-2147483648 -> -2147483648 (too big for int, so is a float, negated)

-2147483647 - 1 -> -2147483648 fits in int

0x80000000 -> 2147483648 (too big for int, so is a float)
浮点文字

语法

floating-literal::
   fractional-literal   exponent-partopt
   digit-sequence   exponent-part

fractional-literal::
   digit-sequenceopt   .   digit-sequence
   digit-sequence   .

exponent-part::
   e   signopt   digit-sequence
   E   signopt   digit-sequence

sign:: one of
   +   -

digit-sequence::
   digit
   digit-sequence   digit

约束

浮点文字的值必须可以用其类型表示。

语义

浮点文字的类型是float

常量INFNAN分别提供对无穷大和非数字的浮点值的访问。

浮点文字始终是常量表达式。

示例

$values = array(1.23, 3e12, 543.678E-23);
字符串文字

语法

string-literal::
   single-quoted-string-literal
   double-quoted-string-literal
   heredoc-string-literal
   nowdoc-string-literal

语义

字符串文字是由一些方式分隔的零个或多个字符的序列。分隔符不是文字内容的一部分。

字符串文字的类型是string

单引号字符串文字

语法

single-quoted-string-literal::
   b-prefixopt   '   sq-char-sequenceopt   '

sq-char-sequence::
   sq-char
   sq-char-sequence   sq-char

sq-char::
   sq-escape-sequence
   \opt   any member of the source character set except single-quote (') or backslash (\)

sq-escape-sequence:: one of
   \'   \\

b-prefix:: one of
   b   B

语义

单引号字符串文字是由单引号 (', 0x27) 分隔的字符串文字。文字可以包含除单引号 (') 和反斜杠 (\\) 之外的任何源字符,它们只能用其对应的转义序列表示。

可选的b-前缀保留供将来用于处理所谓的二进制字符串。目前,带有b-前缀单引号字符串文字等效于不带前缀的文字。

单引号字符串文字始终是常量表达式。

示例

'This text is taken verbatim'

'Can embed a single quote (\') and a backslash (\\) like this'
双引号字符串文字

语法

double-quoted-string-literal::
   b-prefixopt   "   dq-char-sequenceopt   "

dq-char-sequence::
   dq-char
   dq-char-sequence   dq-char

dq-char::
   dq-escape-sequence
   any member of the source character set except double-quote (") or backslash (\)
   \   any member of the source character set except "\$efnrtvxX or   octal-digit

dq-escape-sequence::
   dq-simple-escape-sequence
   dq-octal-escape-sequence
   dq-hexadecimal-escape-sequence
   dq-unicode-escape-sequence

dq-simple-escape-sequence:: one of
   \"   \\   \$   \e   \f   \n   \r   \t   \v

dq-octal-escape-sequence::
   \   octal-digit
   \   octal-digit   octal-digit
   \   octal-digit   octal-digit   octal-digit

dq-hexadecimal-escape-sequence::
   \x   hexadecimal-digit   hexadecimal-digitopt
   \X   hexadecimal-digit   hexadecimal-digitopt

dq-unicode-escape-sequence::
   \u{   codepoint-digits   }

codepoint-digits::
   hexadecimal-digit
   hexadecimal-digit   codepoint-digits

语义

双引号字符串文字是由双引号 (", 0x22) 分隔的字符串文字。文字可以包含除双引号 (") 和反斜杠 (\\) 之外的任何源字符,它们只能用其对应的转义序列表示。某些其他(有时不可打印)字符也可以表示为转义序列。

可选的b-前缀保留供将来用于处理所谓的二进制字符串。目前,带有b-前缀双引号字符串文字等效于不带前缀的文字。

转义序列表示单个字符编码,如下表所述

转义序列字符名称Unicode 字符
$美元符号0x24
"双引号0x22
\反斜杠0x5C
\e转义0x1B
\f换页0x0C
\n换行0x0A
\r回车符0x0D
\t水平制表符0x09
\v垂直制表符0x0B
\ooo1–3 位八进制数字值 ooo
\xhh 或 \Xhh1–2 位十六进制数字值 hh
\u{xxxxxx}Unicode 码位 U+xxxxxx 的 UTF-8 编码U+xxxxxx

在双引号字符串字面量中,反斜杠 (\) 除非被识别为转义序列的开头,否则将被保留 verbatim。

在双引号字符串字面量中,没有被反斜杠 (\) 转义的美元符号 ($) 将使用下面描述的变量替换规则进行处理。

\u{xxxxxx} 转义序列生成 Unicode 码位为花括号内指定十六进制数字的 UTF-8 编码。实现 MUST NOT 允许超出 U+10FFFF 的 Unicode 码位,因为这超出了 UTF-8 可以编码的范围(参见 RFC 3629)。如果指定了大于 U+10FFFF 的码位,实现 MUST 发生错误。实现 MUST 将 \u 原样传递,并且如果后面没有跟着 {,则不要将其解释为转义序列,但是如果后面跟着 {,则如果后面没有跟着 } 或内容不是有效的码位,则实现 MUST 产生错误。实现 MUST 支持前导零,但 MUST NOT 支持码位之间花括号的开头或结尾的空格。实现 MUST 允许 Unicode 码位不是 Unicode 标量值,例如高低代理。

Unicode 转义序列不能通过变量替换创建。例如,给定 $v = "41""\u{$v}" 导致 "\u41",长度为 4 的字符串,而 "\u{0$v}""\u{{$v}}" 包含格式错误的 Unicode 转义序列。

变量替换

变量替换接受以下语法

string-variable::
   variable-name   offset-or-propertyopt
   ${   expression   }

offset-or-property::
   offset-in-string
   property-in-string

offset-in-string::
   [   name   ]
   [   variable-name   ]
   [   integer-literal   ]

property-in-string::
   ->   name

表达式 的工作方式与 简单变量表达式 相同。

在评估上面语法定义的变量之后,其值将根据 字符串转换 的规则转换为字符串,并替换到字符串中,以代替变量替换表达式。

字符串中的偏移量字符串中的属性 定义的下标或属性访问将根据 下标运算符成员访问运算符 的规则分别解析。例外情况是,字符串中的偏移量 中的 名称 被解释为字符串字面量,即使它没有被引用。

如果 $ 之后的字符序列无法解析为 名称 且不以 { 开头,则 $ 字符将被解释为 verbatim,并且不会执行变量替换。

变量替换还提供对表达式求值的有限支持。这是通过将表达式括在匹配的花括号对 ({ ... }) 中来完成的。左花括号后面必须紧跟着美元符号 ($),之间没有任何空格,并且该美元符号必须开始一个变量名。如果不是这种情况,花括号将被解释为 verbatim。如果左花括号 ({) 被转义,则不会将其解释为嵌入式表达式的开头,而是将其解释为 verbatim。

表达式的值将根据 字符串转换 的规则转换为字符串,并替换到字符串中,以代替替换表达式。

如果双引号字符串字面量不包含任何变量替换,则它是一个常量表达式。

示例

$x = 123;
echo ">\$x.$x"."<"; // → >$x.123<
// -----------------------------------------
$colors = array("red", "white", "blue");
$index = 2;
echo "\$colors[$index] contains >$colors[$index]<\n";
  // → $colors[2] contains >blue<
// -----------------------------------------
class C {
    public $p1 = 2;
}
$myC = new C();
echo "\$myC->p1 = >$myC->p1<\n";  // → $myC->p1 = >2<
Heredoc 字符串字面量

语法

heredoc-string-literal::
   b-prefixopt   <<<   hd-start-identifier   new-line   hd-bodyopt   hd-end-identifier   ;opt   new-line

hd-start-identifier::
   name
   "   name   "

hd-end-identifier::
   name

hd-body::
   hd-char-sequenceopt   new-line

hd-char-sequence::
   hd-char
   hd-char-sequence   hd-char

hd-char::
   hd-escape-sequence
   any member of the source character set except backslash (\)
   \ any member of the source character set except \$efnrtvxX or   octal-digit

hd-escape-sequence::
   hd-simple-escape-sequence
   dq-octal-escape-sequence
   dq-hexadecimal-escape-sequence
   dq-unicode-escape-sequence

hd-simple-escape-sequence:: one of
   \\   \$   \e   \f   \n   \r   \t   \v

约束

开始和结束标识符名称必须相同。<<< 和开始标识符之间仅允许水平空格。开始标识符和后面的换行符之间不允许任何空格。换行符和后面的结束标识符之间不允许任何空格。除了可选的分号 (;) 之外,结束标识符和终止该源行的换行符之间不允许任何字符,即使是注释或空格。

语义

Heredoc 字符串字面量是由“<<< name”和“name”分隔的字符串字面量。字面量可以包含任何源字符。某些其他(有时是不可打印的)字符也可以表示为转义序列。

Heredoc 字面量支持 双引号字符串字面量 中定义的变量替换。

如果 Heredoc 字符串字面量不包含任何变量替换,则它是一个常量表达式。

可选的 b-前缀 没有任何效果。

示例

$v = 123;
$s = <<<    ID
S'o'me "\"t e\txt; \$v = $v"
Some more text
ID;
echo ">$s<";
// → >S'o'me "\"t e  xt; $v = 123"
// Some more text<
Nowdoc 字符串字面量

语法

nowdoc-string-literal::
   b-prefixopt   <<<   '   name   '   new-line   hd-bodyopt   name   ;opt   new-line

约束

开始和结束标识符名称必须相同。开始标识符名称与其包含的单引号 (') 之间不允许任何空格。另请参阅 heredoc 字符串字面量

语义

Nowdoc 字符串字面量看起来像 heredoc 字符串字面量,除了前者中的开始标识符名称被括在单引号 (') 中。两种形式的字符串字面量具有相同的语义和约束,除了 Nowdoc 字符串字面量不受变量替换(如单引号字符串)的影响。

Nowdoc 字符串字面量是一个常量表达式。

可选的 b-前缀 没有任何效果。

示例

$v = 123;
$s = <<<    'ID'
S'o'me "\"t e\txt; \$v = $v"
Some more text
ID;
echo ">$s<\n\n";
// → >S'o'me "\"t e\txt; \$v = $v"
// Some more text<

运算符和标点符号

语法

operator-or-punctuator:: one of
   [   ]   (   )   {   }   .   ->   ++   --   **   *   +   -   ~   !
   $   /   %   <<   >>   <   >   <=   >=   ==   ===   !=   !==   ^   |
   &   &&   ||   ?   :   ;   =   **=   *=   /=   %=   +=   -=   .=   <<=
   >>=   &=   ^=   |=   ,   ??   <=>   ...   \

语义

运算符和标点符号是具有独立句法和语义意义的符号。运算符 用于表达式中描述涉及一个或多个操作数 的操作,并产生结果值、产生副作用或两者兼而有之。标点符号 用于分组和分隔。