流程控制
简介
任何 PHP 脚本都是由一系列语句构成的。一条语句可以是一个赋值语句,一个函数调用,一个循环,一个条件语句或者甚至是一个什么也不做的语句(空语句)。语句通常以分号结束。此外,还可以用花括号将一组语句封装成一个语句组。语句组本身可以当作是一行语句。本章介绍了各种语句类型。
if
<?php
if ($a > $b)
echo "a is bigger than b";
?>
else
<?php
if ($a > $b) {
echo "a is greater than b";
} else {
echo "a is NOT greater than b";
}
?>
elseif/else if
<?php
if ($a > $b) {
echo "a is bigger than b";
} elseif ($a == $b) {
echo "a is equal to b";
} else {
echo "a is smaller than b";
}
/* 不正确的使用方法: */
if ($a > $b):
echo $a." is greater than ".$b;
else if ($a == $b): // 将无法编译
echo "The above line causes a parse error.";
endif;
/* 正确的使用方法: */
if ($a > $b):
echo $a." is greater than ".$b;
elseif ($a == $b): // 注意使用了一个单词的 elseif
echo $a." equals ".$b;
else:
echo $a." is neither greater than or equal to ".$b;
endif;
?>
流程控制的替代语法
(PHP 4, PHP 5, PHP 7)
PHP 提供了一些流程控制的替代语法,包括 if,while,for,foreach 和 switch。替代语法的基本形式是把左花括号({)换成冒号(:),把右花括号(})分别换成 endif;,endwhile;,endfor;,endforeach; 以及 endswitch;。
Note:
不支持在同一个控制块内混合使用两种语法。
while
<?php
/* example 1 */
$i = 1;
while ($i <= 10) {
echo $i++; /* the printed value would be
$i before the increment
(post-increment) */
}
/* example 2 */
$i = 1;
while ($i <= 10):
print $i;
$i++;
endwhile;
?>
do-while
<?php
$i = 0;
do {
echo $i;
} while ($i > 0);
?>
for
<?php
$people = Array(
Array('name' => 'Kalle', 'salt' => 856412),
Array('name' => 'Pierre', 'salt' => 215863)
);
for($i = 0, $size = count($people); $i < $size; ++$i)
{
$people[$i]['salt'] = rand(000000, 999999);
}
?>
foreach
foreach仅能够应用于数组和对象
Note:
foreach 不支持用“@”来抑制错误信息的能力。
用 list() 给嵌套的数组解包
(PHP 5 >= 5.5.0, PHP 7)
PHP 5.5 增添了遍历一个数组的数组的功能并且把嵌套的数组解包到循环变量中,只需将 list() 作为值提供。
例如:
<?php
$array = [
[1, 2],
[3, 4],
];
foreach ($array as list($a, $b)) {
// $a contains the first element of the nested array,
// and $b contains the second element.
echo "A: $a; B: $b\n";
}
?>
以上例程会输出:
A: 1; B: 2
A: 3; B: 4
list() 中的单元可以少于嵌套数组的,此时多出来的数组单元将被忽略
如果 list() 中列出的单元多于嵌套数组则会发出一条消息级别的错误信息
break
结束循环语句,执行下面的语句
continue
跳过本次循环中剩余的代码,进行下一次循环
switch
<?php
switch ($i) {
case 0:
echo "i equals 0";
break;
case 1:
echo "i equals 1";
break;
case 2:
echo "i equals 2";
break;
default:
echo "i is not equal to 0, 1 or 2";
}
?>
match
(PHP 8)
match 表达式基于值的一致性进行分支计算。 match表达式和 switch 语句类似, 都有一个表达式主体,可以和多个可选项进行比较。 与 switch 不同点是,它会像三元表达式一样求值。 与 switch 另一个不同点,它的比较是严格比较( ===)而不是松散比较(==)。 Match 表达式从 PHP 8.0.0 起可用。
示例 #1 match 表达式结构
<?php
$return_value = match (subject_expression) {
single_conditional_expression => return_expression,
conditional_expression1, conditional_expression2 => return_expression,
};
?>
示例 #2 match 的基础用法
<?php
$food = 'cake';
$return_value = match ($food) {
'apple' => 'This food is an apple',
'bar' => 'This food is a bar',
'cake' => 'This food is a cake',
};
var_dump($return_value);
?>
以上例程会输出: string(19) "This food is a cake"
info
不一定要使用 match 表达式的结果。 match 表达式必须使用分号 ; 结尾。
match 表达式跟 switch 语句相似,但是有以下关键区别:
- match 比较分支值,使用了严格比较 (===), 而 switch 语句使用了松散比较。
- match 表达式会返回一个值。
- match 的分支不会像 switch 语句一样, 落空时执行下个 case。
- match 表达式必须彻底列举所有情况。
match 表达式和 switch 语句类似, 逐个检测匹配分支。一开始不会执行代码。 只有在所有之前的条件不匹配主体表达式时,才会执行剩下的条件表达式。 只会执行返回的表达式所对应的匹配条件表达式。 举例:
<?php
$result = match ($x) {
foo() => ...,
$this->bar() => ..., // 如果 foo() === $x,不会执行 $this->bar()
$this->baz => beep(), // 只有 $x === $this->baz 时才会执行 beep()
// 等等
};
?>
match 表达式分支可以通过逗号分隔,包含多个表达式。 这是一个逻辑 OR,当多个分支表达式右侧相同时,就可以用这种缩写。
<?php
$result = match ($x) {
// 匹配分支:
$a, $b, $c => 5,
// 等同于以下三个分支:
$a => 5,
$b => 5,
$c => 5,
};
?>
default 模式是个特殊的条件。 当之前的条件都不匹配时,会匹配到该模式。 For example:
<?php
$expressionResult = match ($condition) {
1, 2 => foo(),
3, 4 => bar(),
default => baz(),
};
?>
danger
多个 default 模式将会触发 E_FATAL_ERROR 错误。 match 表达式必须详尽列出所有情况。 如果主体表达式不能被任意分支条件处理, 会抛出 UnhandledMatchError。
使用 match 表达式处理非一致性检查
可以使用 match 表达式将 true 作为主项表达式来处理非一致性条件的情况。
示例 #4 针对整数范围,使用宽泛的表达式匹配分支
<?php
$age = 23;
$result = match (true) {
$age >= 65 => 'senior',
$age >= 25 => 'adult',
$age >= 18 => 'young adult',
default => 'kid',
};
var_dump($result);
?>
示例 #5 针对字符串内容,使用宽泛的表达式匹配分支
<?php
$text = 'Bienvenue chez nous';
$result = match (true) {
str_contains($text, 'Welcome') || str_contains($text, 'Hello') => 'en',
str_contains($text, 'Bienvenue') || str_contains($text, 'Bonjour') => 'fr',
// ...
};
var_dump($result);
?>
以上例程会输出:string(2) "fr"
declare
declare 结构用来设定一段代码的执行指令。declare 的语法和其它流程控制结构相似:
declare (directive)
statement
directive 部分允许设定 declare 代码段的行为。 目前只认识三个指令:
因为本指令是在文件编译时处理的,所以指令只接受字面量的值。 无法使用变量和常量。下面为你演示:
案例1
<?php
// 这样是有效的:
declare(ticks=1);
// 这样是无效的:
const TICK_VALUE = 1;
declare(ticks=TICK_VALUE);
?>
案例2
<?php
declare(encoding='ISO-8859-1');
// code
?>
案例3
<?php
declare(strict_types=1);
function sum($a, $b): int {
return $a + $b;
}
var_dump(sum(1, 2));
var_dump(sum(1, 2.5));
?>
return
return 将程序控制返还给调用模块。 将在调用模块中执行的下一句表达式中继续。
如果在一个函数中调用 return 语句,将立即结束此函数的执行并将它的参数作为函数的值返回。return 也会终止 eval() 语句或者脚本文件的执行。
如果在全局范围中调用,则当前脚本文件中止运行。如果当前脚本文件是被 include 的或者 require 的,则控制交回调用文件。此外,如果当前脚本是被 include 的,则 return 的值会被当作 include 调用的返回值。如果在主脚本文件中调用 return,则脚本中止运行。如果当前脚本文件是在 php.ini 中的配置选项 auto_prepend_file 或者 auto_append_file 所指定的,则此脚本文件中止运行。
更多信息见返回值。
注意: 注意既然 return 是语言结构而不是函数,因此其参数没有必要用括号将其括起来,也不推荐这样用。
注意: 如果没有提供参数,则一定不能用括号,此时返回 null。如果调用 return 时加上了括号却又没有参数会导致解析错误。
自 PHP 7.1.0 起,如果返回类型需要是 void 而带了返回的参数, 将导致 E_COMPILE_ERROR; 相反返回类型需要而未带参数也会同样导致该错误。
require
require 和 include 几乎完全一样,除了处理失败的方式不同之外。require 在出错时产生 E_COMPILE_ERROR 级别的错误。换句话说将导致脚本中止而 include 只产生警告(E_WARNING),脚本会继续运行。
require确实实在zend预编译时,将其引入的文件内容复制到当前,然后执行预编译和执行。而include在预编译的被编译成opcode了,当执行倒这一指令时,会像函数调用一样进入一个栈的环境,在这里完成对引入文件的内容的复制和编译,进而执行(这里就是我的理解)。如果这时候出错了,直接跳出栈,并不影响主流程的进展,这也就是为什么include出错只是warning,而由于require是在主流程里面,所以一旦出错就是致命的(这里的结果呼应了,我的理解)。 总结:include包含的文件在程序已开始的时候就包含了,而require是在运行到require语句时才包含对应的文件。
include
被包含文件先按参数给出的路径寻找,如果没有给出目录(只有文件名)时则按照 include_path 指定的目录寻找。如果在 include_path 下没找到该文件则 include 最后才在调用脚本文件所在的目录和当前工作目录下寻找。如果最后仍未找到文件则 include 结构会发出一条警告;这一点和 require 不同,后者会发出一个致命错误。
当一个文件被包含时,其中所包含的代码继承了 include 所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用。不过所有在包含文件中定义的函数和类都具有全局作用域。
require_once
require_once 语句和 require 语句完全相同,唯一区别是 PHP 会检查该文件是否已经被包含过,如果是则不会再次包含。
include_once
goto
(PHP 5 >= 5.3.0, PHP 7)
goto 操作符可以用来跳转到程序中的另一位置。该目标位置可以用目标名称加上冒号来标记,而跳转指令是 goto 之后接上目标位置的标记。PHP 中的 goto 有一定限制,目标位置只能位于同一个文件和作用域,也就是说无法跳出一个函数或类方法,也无法跳入到另一个函数。也无法跳入到任何循环或者 switch 结构中。可以跳出循环或者 switch,通常的用法是用 goto 代替多层的 break。
Example #1 *goto* 示例
<?php
goto a;
echo 'Foo';
a:
echo 'Bar';
?>
以上例程会输出:
Bar