语法/表达式/语句与操作符

[TOC]

基本语法

PHP解释器解析一段文本时,只会将所有由特定标记——<?php?>包裹的文本当作PHP代码,标记之外的文本(除了紧接着?>的换行符)将作为程序的输出。唯一的例外是,解释器会略过那些出现在PHP条件语句中的非PHP代码文本(如下示例代码中,两行非PHP代码的文本中某一行将根据判断语句的具体结果充当输出):

<?php if ($expression == true): ?>
  This will show if the expression is true.
<?php else: ?>
  Otherwise this will show.
<?php endif; ?>

这一特性使得PHP代码可以被嵌入在各种文本之中,充当一种模板语言。

如果一个文本文件的内容是纯PHP代码,则建议省略结尾处的?>

PHP解释器还能识别除<?php...?>其他三种PHP代码文本标记,但不建议使用。“短标记”风格从 PHP5.4 起成为默认有效且推荐使用的语法:<?= expreesion ?>等价于<?php echo expression; ?>

语句与表达式

PHP的语句、表达式等概念同常见语言类似(可参考《JS的操作符与语句》)。一个 PHP 程序是由一系列语句直接构成的。语句有流程控制语句、表达式语句、空语句等类型。可以用花括号将一组语句封装成一个语句组。语句组本身可以当作是一行语句。每个PHP语句以分号结尾,结束标记?>隐含表示了一个分号。以下将介绍结构化的流程控制语句。

if/else/elseif语句

其形式为

if (expr)
  statement

表达式expr的值将被转化为一个布尔值。具体规则见《PHP中的数据类型》一文。

if语句可搭配else语句与elseif语句。其中elseif语句使用关键字elseif,也可以写作else if(后者无法用于使用了“流程控制替代语法”的情况)

switch语句

同C、JS的switch语句在语法和表现上都一样(除了PHP中switch使用的是松散比较)。包括可以使用default关键字,以及fall-through现象。

while/do-while/for语句

同其它语言类似。注意对循环条件表达式的到布尔类型的转换。同C一样,for的三个表达式都是可省略的。

foreach语句

foreach 仅能够用于迭代数组和对象,如果尝试应用于其他数据类型的值,将触发一个错误。其语法有两种:

foreach (array_expression as $value)
    statement

foreach (array_expression as $key => $value)
    statement

分别将被迭代对象当作列表和字典。其中as是一个隐含的赋值操作,因此可以使用引用赋值与解构赋值功能(见下文“赋值操作符”一节)。

当遍历一个object类型的实例时,默认情况下所有可见属性都将被用于遍历。更多讨论见object相关章节。

注意点:

  • 当 foreach 开始执行时,数组内部的指针会自动指向第一个单元。这意味着不需要在 foreach 循环之前调用 reset()。
  • 由于 foreach 依赖内部数组指针,在循环中修改其值将可能导致意外的行为
  • 数组最后一个元素的 $value 引用在 foreach 循环之后仍会保留。建议使用 unset() 来将其销毁
  • foreach 不支持用“@”来抑制错误信息的能力

break/continue/goto语句

break/continue的用法同JS类似, 可用于for,foreach,while,do-while 或者 switch 结构中,特殊之处在于:

  • 关键词后均可跟一数字(不支持使用表达式,包括变量表达式),用来支持跳出几重循环。默认为1
  • switch 语句可以使用 continue,效果同break

goto 语句在 5.3之后引入,可以让控制流跳转到程序中的另一位置。该目标位置可以用目标名称加上冒号来标记,而跳转指令是 goto 之后接上目标位置的标记。目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中。可以跳出循环或者 switch,通常的用法是用 goto 代替多层的 break。

declare语句

declare语句用于控制PHP解释器解释某段代码时的行为。其语法为:

declare (directive)
    statement

或者不跟任何语句,这样的declare语句将指示PHP解释器解释其后所有代码的行为(但如果有 declare 结构的文件被其它文件包含,则对包含它的父文件不起作用):

declare (directive);

目前只有两(三)个directive:

  • encoding:指示解释器对某段代码的编码方式
  • ticks:设定解释器在解释某段代码时的时钟周期
  • strict_types:设定是否开启严格模式。严格模式开启后设定了对标量类型的type hint的函数在收到不正确的类型时不会试图进行类型转换

详见http://php.net/manual/en/control-structures.declare.php

include/require/includeonce/requireonce

使用include语句,在一个PHP文件中“引入”另外一个PHP文件中的代码。这是一个运行时过程。一个代码文件被引入时,会被解释执行一遍,这一过程中,被引入的代码像是原本就处在include语句的位置一样,这意味着 在被引入的代码中,能够使用在include语句处能够使用的符号。反之,被引入的代码也能创建符号,它们的作用域情况同“创建它们的代码原本就处在include语句的位置”时的情况一样。此规则的一个例外是魔术常量,它们是在发生“引入”之前就已被解析器设定的。

文件寻找路径

include关键字后跟一字符串(不必非得是字符串字面量),该字符串如果是文件路径的形式——相对路径(以...开头)或绝对路径(linux中以/开头,win中以\或盘符开头)——出现,则解释器按照该路径寻找文件。否则字符串将被视为不包含路径的文件名,此时按照如下顺序在文件系统中查找指定文件:

  1. php.ini文件中includepath选项指定的目录,以及使用函数setincludepath为本次程序执行过程指定的一个或多个目录(通常像这样使用该函数:`setincludepath(getinclude_path() . 'your/path')`)
  2. 当前代码文件,即本条include语句所在的文件的目录
  3. 当前工作目录,可以理解为解释器本次执行过程的“入口文件”的目录

都查找不到,则产生一个 E_WARNING 级别的错误警告(非致命运行时错误)。

也支持通过URL从远程加载文件。本文不讨论。

其它关键字

  • require:文件加载出错时将产生ECOMPILEERROR 级别的错误(但实际上是运行时才发生的),执行过程立刻中止
  • include_once:如果指定文件在整个程序执行期间已经被引入过一次,则不会被再次引入
  • require_once

return

return语句可以使用于函数内或函数外。前一情况见《PHP函数详解》一文;对于后一情况,则当前PHP文件(指使用了该语句的PHP文件)中止运行后续代码。如果当前PHP文件是被 include 的或者 require 的,则控制交回调用文件。此外,如果当前代码文件是被 include 的,则 return 的值会被当作 include 调用的返回值。如果在入口文件中调用 return,则本次程序执行过程立刻中止。

流程控制替代语法

if,while,for,foreach 和 switch语句具有替代语法,基本形式是把左花括号({)换成冒号(:),把右花括号(})分别换成 endif;,endwhile;,endfor;,endforeach; 以及 endswitch;。 这一设计旨在方便PHP代码混杂在一般文本中时的情况。

操作符

PHP中的操作符经常进行隐式类型转换以确保运算能够进行,实在无法确保类型正确的情况下会触发致命错误。

+、-、*、/、%、**

算术运算符

  • +和-可充当一元运算符,其余均为二元运算符
  • 指数运算符**在PHP5.6后可用
  • 除法运算符总是返回浮点数。只有在下列情况例外:两个操作数都是整数(或字符串转换成的整数)并且正好能整除,这时它返回一个整数
  • 取模运算符的操作数在运算之前都会转换成整数,结果的正负号被除数的相同
  • 算数运算符会尽可能将所有类型的操作数都隐式转换为可以用来计算的类型(int或float)。但对于+运算符,如果两个操作数均为array,则表示array的联合: 运把右边的数组元素附加到左边的数组后面,两个数组中都有的键名,则只用左边数组中的,右边的被忽略

=

赋值运算符。赋值运算表达式的值就是所赋的值。有+=/-=/.=等一系列组合运算符。

赋值时可以针对(从0开始索引的)数组进行解构赋值,例如:

<?php
list($a, $b, $c) = [1, 2, 3];

关于赋值与变量的话题,见《PHP中的变量与函数》一文。

位操作符

位操作符包括&、|、^、~、<<、>>,作用与JS中的无异。对于操作数,位操作符总是将它们隐式转换为integer,计算结果也为integer。例外情况是,对于&、|、~和^,若所有操作数均为string,则位操作符将利用组成string值的ascii值进行操作,得到的结果也是string类型。

==/===/!=/<>/!==/</>/<=/>=/<=>/?? ??

比较运算符返回一个boolean值。

  • ==/!=将两个操作数通过隐式类型转换得到同一个类型的值之后再进行比较,===/!==不会这么做
  • <>就是!=
  • 【PHP7】<=>:$a <=> $b表示当$a小于、等于、大于$b时 分别返回一个小于、等于、大于0的integer 值
  • 【PHP7】?? ??:$a ?? $b ?? $c表示 从左往右第一个存在且不为 NULL 的操作数。如果都没有定义且不为 NULL,则返回 NULL

除了===/!==和?? ??,比较运算符对操作数进行下列隐式类型转换或比较流程: | 运算数 1 类型 | 运算数 2 类型 | 结果| |---------------------------------------| | null 或 string | string | 将 NULL 转换为 "",进行数字或词汇比较| |bool 或 null | 任何其它类型 | 转换为 bool,FALSE < TRUE| |object | object | 内置类可以定义自己的比较,不同类不能比较,相同类和数组同样方式比较属性(PHP 4 中),PHP 5 有其自己的说明:在相等性方面的默认规则是, 当使用比较运算符(==)比较两个对象变量时,比较的原则是:如果两个对象的属性和属性值 都相等,而且两个对象是同一个类的实例,那么这两个对象变量相等;而如果使用全等运算符(===),这两个对象变量一定要指向某个类的同一个实例(即同一个对象)| |string,resource 或 number | string,resource 或 number | 将字符串和资源转换成数字,按普通数学比较| | array| array | 大小比较规则:具有较少成员的数组较小,如果运算数 1 中的键不存在于运算数 2 中则数组无法比较,否则挨个值比较;相等性比较规则:具有松散相等的键值对的两个数组松散相等,具有相同键值对且顺序和类型都相同的两个数组全等| | object | 任何其它类型 | object 总是更大| | array | 任何其它类型| array 总是更大,除了object|

?:

三元运算符,用法包括(expr1) ? (expr2) : (expr3)或 expr1 ?: expr3,后者意为“ 在 expr1 求值为 TRUE 时返回 expr1,否则返回 expr3”。总是将expr1的值隐式转换为boolean。

@

错误抑制运算符,出现于任何PHP表达式之前,该表达式可能产生的任何错误信息都将不被输出。

如果用 seterrorhandler() 设定了自定义的错误处理函数,仍然会被调用,但是此错误处理函数可以(并且也应该)调用 error_reporting(),而该函数在出错语句前有 @ 时将返回 0。

如果激活了 trackerrors 特性,表达式所产生的任何错误信息都被存放在变量 $phperrormsg 中。此变量在每次出错时都会被覆盖,所以如果想用它的话就要尽早检查。

执行运算符

执行运算符是一对反引号,反引号中的内容将被当作shell命令来执行,其输出将成为本次执行运算符操作的值。等效于使用函数shell_exec。在激活了安全模式时是无效的。

++/--

自增/减操作符用于变量前后。

  • 自增操作可用于值为string类型的变量,但必须是由字符[a-zA-Z]组成的字符串,其余情况无任何效果
  • 自增自减对值是布尔值的变量没有效果
  • 自减对值是NULL的变量没有效果,而自增的结果是1

and/or/xor/!/&&/||

逻辑操作符总是将操作数隐式转换到boolean值。xor异或的效果为,如果两个操作数任一为TRUE但不同时为TRUE,则返回TRUE,否则返回FALSE

  • 存在短路特性
  • ||和or同义,但|| 的优先级比or高;&&和and同理

.

字符串运算符.用于得到将两个字符串连接后的结果。该操作符试图对所有操作数隐式转换到string。

instanceof

检测一个值是否是一个类的实例,对父类和接口都有效。如果右侧的操作数是字符串(但不能是以字面量的形式给出),将把字符串的内容当作类名。如果被检测的变量不是一个object,instanceof 并不发出任何错误信息而是返回 FALSE。 instanceof 运算符是 PHP 5 引进的。在此之前用 isa(),但是后来 isa() 被废弃而用 instanceof 替代了。注意自 PHP 5.3.0 起,又恢复使用 is_a() 了。