PHP 的表达式与控制流 — 程序是怎么做判断、走分支、重复干活的
PHP 的表达式与控制流
这一章讲什么: 程序光有变量和运算符还不够——真正的「编程」始于两件事: 做判断(该不该执行这段代码)和重复(把这段代码执行多少遍)。 本章把 PHP 的全部判断与重复机制讲完,顺带把第 3 章埋下的类型转换陷阱挖到底。 读之前只需要本组 02 的内容。
1. 顶层全景:从「算值」到「走路」
表达式(算出值) ──▶ 条件判断(值为真/假) ──▶ 分支(if / switch / ?)
│
└────▶ 循环(条件成立就再来一遍)──▶ while / do...while / for
└─ 中途跳走:break / 跳下一轮:continue
图说:这一章的全部内容就是这张图。左边是「算」,右边是「走」。第 3 章学的所有东西——变量、运算符、函数——都只解决「怎么算出一个值」。 从这一章起,值开始决定程序走哪条路:银行余额够不够触发转账? 用户选了菜单的哪一项?油量还够跑几轮检查? 这些「问一句、按答案分头行动」的结构,就是控制流(control flow)—— 「控制」的是程序执行的「流」向1。
学完本章回头看,PHP 程序的最小骨架已经齐了: 表达式负责「算」,条件负责「选」,循环负责「重复」,函数(第 3 章)负责「复用」。 之后所有的 PHP 章节,本质上都是这四样东西的组合、放大和工程化—— 数组让「值」成群,对象让「函数和数据」成团,文件和数据库让「数据」出了内存还活着。
2. 表达式:语言的最小计算单位
表达式(expression)是「值、变量、运算符、函数的组合,组合的结果是又一个值」。
3 * (abs(2 * $x) + 4) 是表达式,20 > 9 是表达式,光一个 73 或一个 $myname 也是表达式——
后者是最简单的两种:字面量(literal,求值结果就是它自己)和变量(求值结果是盒子里那张纸条)2。
表达式的值可以是三种东西:数、字符串、布尔值(Boolean,只有真 TRUE 和假 FALSE 两个值; 名字来自 19 世纪数学家乔治·布尔)。第 3 章说过 PHP 只有字符串能直接打印, 那么打印 TRUE/FALSE 会看到什么?原书的实验(test2.php)给出的答案有点反直觉3:
echo "a: [" . TRUE . "]<br>"; // 输出 a: [1]
echo "b: [" . FALSE . "]<br>"; // 输出 b: []TRUE 转成字符串是 "1",FALSE 转成字符串是空串 ""——空得看不见。
原书用的词是「arbitrarily」(任意地):这个对应是 PHP 定的规矩,不是逻辑必然3。
两个实用推论:第一,别用 echo 调试真假——假值在页面上根本看不见,以为没输出;
第二,FALSE 和 NULL(表示「什么都没有」的常量)打印出来都是空串,但它们是两种东西,
只是凑巧共享同一个打印结果4。
表达式再往上组合一层就是语句(statement):给变量赋值是语句,if 后面跟着条件执行一段是语句。
例:$days_to_new_year = 366 - $day_number; 是「赋值语句」,里面装着一个表达式;
if ($days_to_new_year < 30) { echo … } 是「条件语句」,括号里装着一个表达式5。
记住这个分层的名字,因为后面所有结构都是「表达式当零件、语句当产品」。
原书还区分了一个更细的概念:字面量与非字面量。把字面量赋给变量,
这个变量每次读都是同一个值(纸条抄完不会再变);把函数调用的结果赋给变量,
情况不同——函数每次被调都可能吐出不同的值(想想 time()),这个变量的内容就是非字面量。
区别在于值的来源是否确定:来源是写死的,结果永远可预测;来源是算出来的,就得跟着算6。
这个区分看着抽象,用途很实际:调试时,「这变量的值是哪来的」是第一条要问的问题;
答案分两类——要么是某行的赋值(往上找),要么是某次调用(往上找,还要看调用时刻)。
3. 运算符补完:三类新面孔
第 3 章讲了算术、赋值、比较、逻辑四大类,本章按原书的全表补齐剩下的:
| 类别 | 例子 | 干什么 |
|---|---|---|
| 位运算 | 12 ^ 9、<< >> | 直接摆弄整数在内存里的二进制位 |
| 执行运算符 | `ls -al` | 执行反引号里的系统命令,把输出当字符串 |
| 数组联合 | $a + $b | 合并数组(第 6 章) |
位运算本章不展开(属于「知道存在」);执行运算符原书给了全书少见的硬警告:
它威力大,但「unless strictly necessary and you know precisely what you are doing」别碰——
因为它等于把服务器操作系统的命令行向 PHP 代码敞开,一处疏漏就是巨大的安全漏洞7。
这也解释了为什么第 3 章超全局变量那节说黑客会往 $_GET 里塞系统命令:
执行运算符(以及第 5 章会看到的同类能力)就是那条塞进来的命令可能被点燃的引线。
每个运算符还按「吃几个值」分类:一元(unary,一个,如 !$a、$a++)、
二元(binary,两个,绝大多数)、三元(ternary,三个,全语言只有 ? : 一个)8。
3.1 逻辑运算符的完整真值表
四个逻辑运算符对所有输入组合的结果,原书用一张表(Table 4-5)全部列出,浓缩成四句口诀:
| 输入 a | 输入 b | AND | OR | XOR |
|---|---|---|---|---|
| 真 | 真 | 真 | 真 | 假 |
| 真 | 假 | 假 | 真 | 真 |
| 假 | 真 | 假 | 真 | 真 |
| 假 | 假 | 假 | 假 | 假 |
AND 两行假(只要一边假就假);OR 三行真(只要一边真就真);
XOR 只有「恰好一边真」的两行真——同真同假都是假;! 最简单,真假互换9。
记住 XOR 那一列的形状,第 3 章氨水漂白剂的例子(两种成分只能有其一)就是这个真值表的日常版。
原书的例 4-16 还演了一个容易看错的地方:echo !$a,当 $a 是真时输出空串——
!真 是假,假打印成空。四章两处「假打印成空」,值得连起来记10。
3.2 OR 的短路:第二个操作数可能根本不执行
OR(以及 ||、AND、&&)有一个不易察觉的行为:如果第一个操作数已经能定案,第二个根本不会被求值。
例:if ($finished == 1 OR getnext() == 1) exit;——当 $finished 是 1 时,
OR 的结果铁定为真,PHP 懒得再调 getnext()。这通常是你想要的(省一次调用),
但如果你是指望 getnext() 每次都执行(比如它负责取下一行数据),
这个写法会安静地漏调用——程序不报错,数据悄悄缺行11。
两个修法:先把函数调用挪到 if 之前($gn = getnext(); 再判断),
或把函数调用换到条件前头(它是第一个操作数,必然先求值)。原书两个都给了,
各自附带一句提醒:挪出去写法最稳;换位子法则要保证逻辑顺序仍对12。
4. 优先级与结合性:谁先算,从哪头算
4.1 优先级:没有它,混合运算全是错的
优先级(precedence)回答「一个表达式里多个运算符,先算哪个」。 和第 3 章的 5 + 2 * 3 同理,
本章把话说透:如果所有运算符平级,表达式只能从左到右顺着算——
1 + 2 * 3 - 4 * 5 会变成 25;有了「乘除优先于加减」,它实际是 -13。
原书用三组例子证明:同一堆数字换顺序,只要优先级不变,结果就不变;
一旦混优先级,隐含括号的位置就变了13。
想推翻默认顺序,自己加括号:((1 + 2) * 3 - 4) * 5 得 25——括号是最高优先级,
这不是 PHP 的发明,是数学记法本身14。
完整的优先级表原书从高到低列出:括号 → ++ -- → ! → * / % → + - . → 位运算 →
比较 → & ^ | → && → || → ? : → 赋值 → and → xor → or15。
注意三件事:
.(字符串拼接)和+ -同一档——这是 PHP 的特色,别的语言很少把拼接放到这么高;? :比赋值高:$enough = $fuel <= 1 ? FALSE : TRUE;先算三元,再赋值;and/or/xor排在赋值之后——这就是第 3 章那个or die()写法成立的原因:$html = file_get_contents($site) or die(...)里的or低得「赋值先完成,失败再 die」。
原书补了一句设计层面的说明:这个顺序「不是随意排的,而是精心设计的」,
让最常见的写法恰好不需要括号——比如两个比较用 and 或 or 连起来,就是你期待的意思16。
4.2 结合性:同级的多个运算符,从哪头开始
结合性(associativity)回答「同一优先级的运算符排成一列,从哪边开始算」。
大多数是从左到右:1 + 2 + 3 - 4 + 5 顺着算得 7。
但赋值是从右到左,这成就了一个便捷写法——链式赋值:
$level = $score = $time = 0;只有「先把最右边的 0 赋给 $time,再把 $time 的值赋给 $score,最后赋给 $level」这个次序,
三个变量才都是 0。方向一错,语义全变17。
比较运算符则没有结合性(5 < $a < 10 是语法错误——它不许你连着比,
这种数学式写法必须拆成 5 < $a && $a < 10)。
原书给新手的建议很直接:不确定就用括号把子表达式包起来,既躲开结合性的坑, 也让接你代码的人一眼看清次序18。
5. == 与 ===:弱类型的真面目
第 3 章预告过这个坑,本章用原书的例 4-13 把它钉死:
$a = "1000";
$b = "+1000";
if ($a == $b) echo "1"; // 输出了 1!
if ($a === $b) echo "2"; // 什么都没输出两个不同的字符串,为什么 == 说它们相等?因为 PHP 是弱类型:
== 比较时,如果两边类型不同,PHP 会把它们都转成数再比——"1000" 转成 1000,
"+1000" 也转成 1000,自然相等。而 ===(恒等)不做任何转换:
它连类型一起比,「字符串 1000」和「字符串 +1000」类型虽同、内容不同,结论不相等19。
原书的立场值得记住:新代码里做比较,默认用 ===——
它把「类型转换带来的意外行为」整个关掉。只有当你明确想要「不管类型,看起来像就行」时,
才退而用 ==20。!= 与 !== 是同样的两档,取反而已21。
6. 条件语句:三种走法
原书用了一个贯穿本章的比喻:程序流是一条单车道公路,条件语句是路边的岔路指示牌—— 条件成立,拐进岔路走一段,回主线;不成立,直行22。
6.1 if:if 条件 { 干活 }
if ($bank_balance < 100)
{
$money = 1000;
$bank_balance += $money;
}银行余额不足 100,给自己发 1000 补贴;够 100,一行不执行,直接跳过23。 括号里可以是任何表达式——比较、函数调用、对 0 和 NULL 的测试都算。 花括号永远写上,哪怕只有一条语句。原书为此讲了一个著名事故: 苹果 SSL 代码里的 goto fail bug——某程序员漏写了 if 的花括号, 导致一段关键代码总是执行,让不该通过的证书被当成通过,安全的连接被谎称建立。 一个括号,一个行业级漏洞24。
6.2 else 与 elseif:岔路的分岔
if…else 是二选一:真走左边,假走右边,「不可能两条都走,也不可能都不走」25。
if…elseif…else 是多选一:余额 <100 补 1000; elseif 余额 >200 存 100;
否则(else)存 50。elseif 想写几个写几个;但原书给了分流建议:
elseif 一多,就该考虑 switch26。顺序的硬规矩:elseif 不能出现在 if 之前,
else 不能出现在 elseif 之前,else 总是收尾(也可以不要)27。
6.2b 主线例子:银行余额把分支结构走一遍
本章的承重例子是一组虚构的「月底银行维护」操作,它把 if、elseif、else 三层恰好各用一次:
if ($bank_balance < 100)
{
$money = 1000;
$bank_balance += $money;
}
elseif ($bank_balance > 200)
{
$savings += 100;
$bank_balance -= 100;
}
else
{
$savings += 50;
$bank_balance -= 50;
}设月初 $bank_balance = 150、$savings = 0,走一遍:
- 问
$bank_balance < 100?150 不小于 100——假,跳过第一个块; - 落到
elseif,问$bank_balance > 200?150 不大于 200——假,跳过第二个块; - 落到
else——前两层都不中,无条件执行:$savings变 50,$bank_balance变 100。
换成 $bank_balance = 250 再走:第 1 问假;第 2 问真——$savings 变 100,$bank_balance 变 150,
然后整个结构结束(elseif 命中后不再看 else)。换成 50:第 1 问真——补 1000,余额变 1050,
后面两层都不再看。三个分支是互斥的:恰好一个执行,这正是多路分支想要的行为25。
这个例子还藏着一个读代码的细节:条件的顺序不是无所谓——
elseif 们从上到下依次问,先命中先走。把「> 200」放到「< 100」前面,
余额 50 的用户会先撞上哪条,结果完全不同。写多路分支时,把「最特殊」的条件放最上面,
或反过来把「最宽」的放最下面,是有意识的排序,不是排版27。
6.3 switch:多分支的专用结构
场景:一个菜单变量 $page,可能等于 Home、About、News、Login、Links 之一,
每个值触发不同动作。用 if…elseif 写要五个分支;switch 把「被检查的表达式」只写一次:
switch ($page)
{
case "Home":
echo "You selected Home";
break;
case "About":
echo "You selected About";
break;
/* …其余 case… */
default:
echo "Unrecognized selection";
break;
}结构三要素:switch (表达式) 开头;每个 case 值: 是一个入口;
default: 是全部不命中时的兜底(相当于 if 链的 else)28。
switch 的头号陷阱是 fall-through(贯穿):如果某个 case 忘了写 break,
执行不会停在它那里,而是一路贯穿到后面所有 case——
$page 是 Home 却五个 case 全执行一遍。这是故意的语言设计(少数高级用法靠它),
但对新手来说,忘写 break 是最常见的 switch 错误29。
原书的安全建议:连 default 后面的 break 也写上——虽然 default 通常是最后一支、写不写都行,
但万一哪天你把 default 挪到中间,缺了的 break 就变成 fall-through 的缺口30。
还有个少见写法:switch (…): … endswitch;——花括号换成冒号和 endswitch 关键字。
原书明说「不常用,只在读别人代码时可能遇到」31。
把 switch 的执行走一遍($page 等于 "News"):① PHP 求 switch 括号里的值,得 "News";
② 从头扫 case:第一个 case "Home": 不匹配,跳过它的语句和 break;
③ 扫到 case "News": 匹配——从这里开始逐行执行;
④ 输出 You selected News,撞上 break,整个 switch 结束,跳到右花括号之后。
看明白第 ③ 步就知道 fall-through 为什么发生:匹配点只是「入口」,不是「出口」——
出口永远由 break 把守(或一路贯穿到末尾)29。
6.4 三元运算符:? : 一行版 if
三元运算符(ternary,全 PHP 唯一吃三个值的运算符)把「问一句、二选一」压进一行:
echo $fuel <= 1 ? "Fill tank now" : "There's enough fuel";读法:问 $fuel <= 1?真——返回 : 前的串;假——返回 : 后的串。结果可以直接 echo,
也可以赋给变量:$enough = $fuel <= 1 ? FALSE : TRUE;32。
原书同时给了两个提醒:一是看不懂就退回 if——三元不是必须品;
二是它常把同一个变量写三遍,比如这个流行写法33:
$saved = $saved >= $new ? $saved : $new;拆开读:「把 $saved 设为……(问:$saved 是不是 ≥ $new?)是——保留现在的 $saved;否——换成 $new。」 作用一句话:记下目前为止见过的最大值。熟悉三元的人觉得这比 if 紧凑; 不熟悉的人需要逐段拆。遇到别人的代码里这种写法,能认出是「保大」就够了33。
7. 循环:三种,各管一类重复
比喻升级:岔路之外,公路上还有环形匝道——进去之后,条件不满足就出不来34。 原书用同一个任务(打印 12 乘法表)把三种循环各实现一遍,这是最省事的比较法。
7.1 while:先问,再决定要不要干
$count = 1;
while ($count <= 12)
{
echo "$count times 12 is " . $count * 12 . "<br>";
++$count;
}逐步:① $count 是 1;② 问 1 <= 12?真,进入循环体:输出 1 times 12 is 12,$count 变 2;
③ 回到 ② 再问到 13 为止——问 13 <= 12 得假,退出循环。
循环体里必须有让条件趋假的东西(这里是 ++$count),否则条件永远为真,死循环——
原书警告:忘了这行的 while 会永远转下去,页面刷满 1 times 12 is 1235。
紧凑变体:把自增挪进条件——while (++$count <= 12),前置 ++ 先加再比,
于是初始值要从 0 而不是 1 起(进循环立刻被加 1)。行为相同,少一行36。
7.2 do…while:先干,再问
区别只有一个:判断发生在循环体之后。 先执行一遍,再决定要不要继续—— 所以循环体保证至少执行一次。同样的乘法表:
$count = 1;
do
echo "$count times 12 is " . $count * 12 . "<br>";
while (++$count <= 12);初始化回到 1(因为这次是先输出、后自增)37。什么时候非它不可? 「必须先做一次,才能知道该不该做下一次」的场景——比如先读一条记录,再判断要不要读下一条。
7.3 for:把三要素写在一行
for 把 while 散在三处的三要素收进一行:
for ($count = 1 ; $count <= 12 ; ++$count)
echo "$count times 12 is " . $count * 12 . "<br>";三个参数,分号分隔:初始化($count = 1,只在进循环时执行一次)、
条件($count <= 12,每轮开头问)、更新(++$count,每轮末尾做)。
循环体里从此只剩「真正要重复的活」——控制逻辑全部上收38。
走一遍确认三个时刻:第 1 轮——初始化 $count=1,问 1<=12 真,输出第一行,末尾加 1;
第 2 轮——不再初始化(它只跑一次),问 2<=12 真,输出,加 1;……第 13 轮开头问 13<=12 假,退出。
「初始化只跑一次」是新手最容易搞错的时刻——它写在循环头里,却不是每轮都执行的东西38。
多变量也行:for ($i = 1, $j = 1 ; $i + $j < 10 ; $i++, $j++)——
段与段之间用分号,段内多条语句用逗号,这是最容易写错的一处39。
for 与 while 的分工,原书给的判据:循环由「一个按规律变化的变量」驱动 → for; 条件是「某个事件/错误出现」这类不规则信号 → while40。 比如「处理列表里的每一项」是 for 的菜;「持续读数据直到磁盘报错」是 while 的菜。
7.4 break 与 continue:循环的两个急停键
break:立刻跳出整个循环。例:循环往文件里写数据,fwrite返回 FALSE(磁盘写满)就 break 收场41。 嵌套循环里还能break 2;——一次跳出两层42。continue:只跳过当前这一轮,直接进下一轮。例:循环里$j会是 0 的一轮,if ($j == 0) continue;把「除零错误」那一轮整个跳过43。
一句话区分:break 出整圈,continue 出本轮。
7.5 三种循环一张对照表
同一个 12 乘法表,三种循环各写一遍——差异全在「什么时候问条件、什么时候更新变量」:
| 初始化在哪 | 条件什么时候问 | 更新在哪 | 循环体至少执行 | |
|---|---|---|---|---|
while | 循环体之前,自己写 | 每轮开头 | 循环体里,自己写 | 0 次(条件初值假就不进) |
do…while | 循环体之前,自己写 | 每轮末尾 | 循环体或条件里,自己写 | 1 次(先干再问) |
for | 第一参数,循环头 | 每轮开头(第二参数) | 第三参数,循环头 | 0 次 |
for 只是把 while 的三要素搬家——搬到循环头的一行里,语法糖而已,表达能力完全等价。
原书的双变量例($i + $j < 10)说明三个参数各自还能塞多条语句;但「三个参数、两次分号」的骨架不变39。
选择困难时回到那条判据:变化是不是「一个变量按规律走」?是 → for;否 → while;
「必须先试一次」→ do…while40。
7.6 死循环与循环的退出点
原书对 while 的警告值得重复一遍,因为后果实际可见:漏写自增的循环不会报错,只会永远转下去—— 页面被同一行输出刷满,浏览器卡死,直到服务器超时35。循环的三个出口,写每个循环前都问自己一遍:
- 条件正常变假,循环走到头(正常出口);
break中途跳出(错误出口,例:磁盘写满);- 根本就没有出口——这是 bug,不是第四种循环。
新手查死循环的顺序:先找「条件里被检查的变量」,再找「循环体内改它的那一行」—— 两行缺一行,凶手就在那儿。
8. 类型转换:隐式与显式,再进一步
第 3 章见过隐式转换(弱类型自动转)。本章补系统的一课:
$a = 56;
$b = 12;
$c = $a / $b; // 4.6666666666667 —— 两个整数相除,结果是浮点
$c = (int)($a / $b); // 4 —— 显式转成整数,直接丢掉小数部分隐式转换(implicit casting)是 PHP 主动做的:整数除法它默认给浮点,因为要保精度44。
显式转换(explicit casting)是你用 (int) 等 casts 强制的——注意上面第二行的括号:
(int)($a / $b) 转的是整个除法的结果;若写成 (int)$a / $b,只转 $a,
除法照样返回浮点——括号位置一改,语义完全不同45。
可用的 cast:(int)/(integer)、(bool)/(boolean)、(float)/(double)/(real)、
(string)、(array)、(object)46。PHP 也有同名功能的函数(如 intval),
但函数往往多做额外的事(intval 支持按进制转换)——单纯转类型,cast 就够47。
这一节和第 3 章的弱类型讨论合起来,是理解 PHP 类型行为的完整拼图:
读(取值时用上下文自动转)、写(赋值时自动装盒)、问(比较时按 ==/=== 决定转不转)、
改(cast 与 intval 一族显式改)。同一个值在四种时刻可能经历四种对待——
PHP 的「灵活」就灵活在这里,它的「坑」也坑在这里。
养成一个习惯:涉及类型的地方,优先用让转换最少发生的写法——
比较用 ===,需要整数就 (int),函数的参数和返回值上类型声明(第 5 章)。
写得越「显式」,运行时替你做的「隐式」决定越少,意外也越少20。
9. PHP 模块化:章末的架构一课
本章最后一段跳出了语法,谈网站怎么组织。PHP 技术上一个页面就能装下整个网站—— 用户点什么,都传回同一页,由它按 cookie 和会话状态决定下一步48。 但原书明说不推荐:单一文件要兼顾用户可能做的所有动作,会涨到没法维护。
替代方案是拆模块:注册是一个模块(校验邮箱、查重用户名),登录是一个模块, 留言、链接、传图各是一个。模块之间的黏合剂是 cookie 和会话变量(第 12 章)—— 靠它们认出「这个请求还是那个用户」。拆完之后的红利:每个模块自包含, 新功能独立开发,旧功能独立维护;有团队的话,每人深钻一个模块即可49。
注意原书给的拆分颗粒度:一个「用户动作」对应一个模块,不是按技术(所有数据库代码一个文件) 也不是按页面(所有页面一个文件)。注册之所以是一个模块,是因为它自带一整个检查流程—— 校验邮箱格式、查用户名是否被占——这些检查只服务于「注册」这一个动作。 判断「这段代码该不该自成模块」的问句是:它伺候的是不是一个完整、独立、可命名的事?
这一节是全书的「方法论预告」:第 5 章的函数与对象,就是下一章用来造模块的工具。 「先拆模块,再填语法」这个顺序,是这本书和普通语法教程的分水岭。
10. 作者的判断与证据
| 书中的说法 | 成色 |
|---|---|
新代码默认用 === 比较 | 明确的规范性建议,理由是消除类型转换的意外——这是本章最有行动力的一条20 |
| if 永远写花括号(goto fail 为例) | 有真实事故背书(苹果 SSL 双重 goto 导致的证书漏洞),警告的级别给得足24 |
| 执行运算符「strictly necessary」才用 | 安全警告,理由是可能「expose a massive vulnerability」——方向绝对正确,但没给具体攻击演示7 |
| switch 一多分支就换它 | 经验法则,理由是 elseif 链的可读性;边界(多少个 case 算「多」)没有量化,属于作者口味26 |
| 「网站拆模块」的论述 | 架构主张,论据是维护性与团队协作,无对照实验——但和整本书的示例组织方式(每个功能一个文件)自洽49 |
| 三元运算符「不习惯就退回 if」 | 作者自陈(「If you find the ? operator confusing, you are free to stick to if statements」),态度务实32 |
| 位运算只给目录不给内容 | 针对读者群的合理省略(网页开发极少用位运算),但优先级表里它们占位——考试式地背表前,先想清楚自己用不用得上 |
| do…while「至少执行一次」 | 语言机制事实,例子(乘法表)却没展示出「非它不可」的场景——真正需要它的「先读再判」循环,要到第 7 章文件操作才出现37 |
11. 边界与局限
- TRUE/FALSE 的打印结果依赖语言实现:本章开头原书就提醒,不同语言里 FALSE 的「打印形态」不同 (有的语言是 0,甚至是 -1),别把这个对应关系背成「真理」3。
- 位运算只有目录没有内容:Table 4-1 和优先级表里都出现,但全章没有讲任何一个位运算的用例—— 这本书的读者定位(网页开发)确实极少用得到,算是合理的省略。
- 结合性表的完整版(Table 4-3)很长,原书建议新手用括号代替记忆——这意味着真遇到怪表达式, 查官方手册比背表靠谱。
- fall-through 的「高级用法」没有展开:原书说漏 break「allows for some advanced programming」, 但没演示那种用法长什么样——读者遇到刻意利用贯穿的代码(比如多 case 共用一段逻辑)只能自己悟。
- 模块化一节无实操:拆模块的标准(多大盘子该拆)、模块间怎么传数据,都要到第 5、12 章才有着落, 本节只有方向和理由。
- 条件语句的嵌套没讲:if 里套 if、循环里套分支的组合结构本章一个例子都没有—— 它们全靠同一套规则自然组合出来(每一层的「真走这、假走那」互不干扰), 但嵌套层数一深,可读性的纪律(缩进、早退)就成了新问题,本书对此再无专章, 属于「留给实践」的一课。
- goto fail 的细节被简化:原书对事故的复述是「forgotten the curly braces」, 真实的苹果 bug 更具体(重复的 goto 行加缺失的括号共同作用)。作为「写括号」的论据它成立, 但拿它当安全史的完整叙述会失真24。
- while 循环的「事件驱动」场景缺例子:「等某个特殊输入或错误」这个判据只给了原则, 没有配套代码——读者第一次真写「读数据直到出错」时,还得自己把 break 和 while 拼起来。
12. 章末自测:原书的问答题怎么答
这一章的章末问题里,有四问最常卡住,给出我们的回答:
| 原书的问题 | 我们的回答 |
|---|---|
| 打印 TRUE 和 FALSE 时显示什么?为什么? | TRUE 显示 1、FALSE 显示空串——PHP 规定布尔转字符串时的对应;echo 只收字符串,转换规则是语言定的,不是逻辑必然3 |
| 最简单的两种表达式是什么? | 单个字面量(求值结果是自己)和单个变量(求值结果是盒中的值)2 |
| 一元、二元、三元运算符的区别? | 吃几个值:一个(!$a)、两个($a + $b)、三个(expr ? x : y,全 PHP 唯一)8 |
| 强制自定义优先级的最好办法? | 括号——「括号永远赢」;顺带也躲开结合性的坑14 |
| 什么时候 while 比 for 合适? | 条件不是「一个变量按规律变化」,而是等某个事件/错误出现40 |
这五问答顺,本章的机制层面就算过关;答不顺,回到对应小节把例子重走一遍。
13. 可带走的
这十一条是本章的压缩包——每条都能单独拿去用,合起来就是「写 PHP 程序」的全部基本动作:
- 表达式算值,语句做事;条件语句吃「真/假」,循环语句吃「条件 + 更新」。
- TRUE 打印成
1、FALSE 打印成空串——调试用var_dump,别靠echo。 - 优先级:括号 > 单目 > 乘除模 > 加减拼接 > 比较 >
&&>||> 三元 > 赋值 > and/or。 - 拿不准加括号;链式赋值靠「右结合」;连续比较
5 < $a < 10是语法错误,拆成两个条件用&&连。 - 弱类型下
==先转换再比("1000" == "+1000"为真);新代码默认===。 - if/elseif/else 多分支一长就换 switch;switch 里每个 case 末尾的
break是性命攸关的。 and/or优先级低到可以做「赋值失败就 die」,别处别用——低优先级是特性,不是笔误。- 三元
? :是一行版 if,认得出别人的「保大」写法($saved = $saved >= $new ? $saved : $new;)。 - while(先问)、do…while(先干)、for(按规律变化)分工不同;
break出整圈、continue出本轮。 (int)(表达式)的括号决定转换作用范围;cast 管转换,intval这类函数管更多。- 一个 PHP 文件装不下整个网站——按功能拆模块,用会话认出同一个用户;拆分的问句是「这段代码伺候的是不是一个完整、独立、可命名的事」。
12.5 优先级与结合性:把两张表用熟
第 4 节的优先级表有 16 档,结合性表更长——全背是笨办法,实战只需要 七个高频档位:
括号 —— 永远最高,见它就先算
! ++ -- —— 单目,紧贴操作数
* / % —— 乘除取余
+ - . —— 加减与拼接同档(从左往右)
< <= > >= —— 大小比较
== != === —— 相等比较(比大小低一档!)
&& || ? : —— 逻辑与三元,几乎垫底
= 赋值 —— 倒数第二
and or —— 最底(所以 or die 好用)两个容易记错的位置,单独钉一下:相等比较比大小比较低——5 < $a == 3
其实是 (5 < $a) == 3;拼接 . 和 + 同档——"a" . 1 + 2 的结果
靠结合性(从左到右)而不是直觉。遇到这两种,别赌,加括号。
结合性只需记住三个:赋值从右往左($a = $b = 0)、比较没有结合性
(5 < $a < 10 是语法错误)、其余从左往右。这三条覆盖日常九成场景,
剩下的查表即可——表在官方手册"运算符优先级"一页,收藏它比背它划算。
12.6 if 链的四种形态,一张图
把本章的条件结构按"分支数 × 互斥性"摆成一张小图:
| 单分支 | 双分支互斥 | 多分支互斥 | |
|---|---|---|---|
| 推荐写法 | if | if...else | if...elseif...else 或 switch |
| 分支数 | 1 | 2 | ≥3 |
| 判断依据 | 一个条件 | 一个条件的正反 | 同一个值的多档 |
三列之间是"逐步升级"的关系,不是三种互斥的语法。写代码时的选择路径: 先写 if;发现"不成立时也要做事",补 else;发现"不成立"还分好几档, 把 else 换成 elseif 链;再发现"判的其实是同一个变量的多个值", 整链翻译成 switch。这条升级路径就是重构的条件分支版—— 结构跟着语义长,不是一开始就选"最高级"的写法。
12.7 循环的选择决策树(升级版)
§7.5 的对照表再加一层"先问什么":
要重复一段代码吗?
├─ 重复次数已知、按规律变化? ──▶ for(最省事,三要素收进一行)
├─ 必须先做一次再看? ──▶ do...while(先干再问)
├─ 等某个事件/错误? ──▶ while(问在先,体里要有 break 的预案)
└─ 遍历数组? ──▶ foreach(第 6 章主角,别用 for 数下标)第四个分支是本章埋给第 6 章的钩子:PHP 里遍历数组的默认答案是 foreach,
for ($i=0; $i<count($a); ++$i) 是 C 语言时代的遗物——能用,但每一步
都要自己管下标,array 的函数们(forEach/map 式的)在 PHP 里叫 foreach。
三兄弟加上 foreach,PHP 的循环家族就到齐了。
12.8 把 switch 的 fall-through 用到正处
§6.3 说漏 break 是常见错误,但 fall-through 并非纯粹的事故来源—— 故意利用它,可以让"多个 case 共用一个动作"的代码非常干净:
switch ($type) {
case 'admin':
case 'editor':
$canPublish = TRUE; // 两种角色共用一个权限分支
break;
case 'viewer':
$canPublish = FALSE;
break;
default:
die('Unknown role');
}三个 case 标签叠罗汉,只有最后一个带动作——读法:"是这三种之一吗? 是就走这个门"。这比三个 if 或三个重复的 case 块都省,而且把 "刻意贯穿"和"忘了写 break"区分开:刻意版只有最后的 case 带 break, 每个叠罗汉的标签下都故意留空——空本身就是声明"我故意的"。 原书提到 fall-through"allows for some advanced programming"却没给例子, 这就是它想指的那一类(补充:判空叠 case 是 C 时代流传下来的惯用法)。
12.9 三元运算符的「可读性税」
第 6.4 节教了 ? :,这里把使用边界说清楚:
- 适合:两个候选值都很短、条件也短——
$label = $ok ? 'YES' : 'NO'; - 勉强:赋值目标与条件共用变量(那个"保大"写法)——认得出来即可,不鼓励自己写;
- 不适合:嵌套三元(
$x ? $a : ($y ? $b : $c))——读的人要做栈式解析, 直接 if 链更便宜。
一行税率的粗标准:整行超过 60 个字符,三元改 if。不是语法限制, 是读的人的工作记忆限制——这和 §12.6"结构跟着语义长"是同一条原则 在"微观语法"层的应用。
12.10 循环与数组的第一次合奏(预告第 6 章)
本章的 for 循环将在第 6 章遇到它的主战场——遍历数组:
$paper = ['Copier', 'Inkjet', 'Laser', 'Photo'];
for ($j = 0; $j < count($paper); ++$j)
echo "$j: $paper[$j]<br>";走一遍:$j=0 取 Copier,1 取 Inkjet……到 4 时条件失败,循环结束。
count($paper) 每轮都重算一次——数组不变时,更好的写法是先存进
$len = count($paper)。但 PHP 给出的标准答案仍是第 6 章的
foreach($paper as $item)——一行,没有下标,没有 count,没有
越界的可能。工具进步的每一步,都是把"自己管的细节"再交还一件。
12.11 本章的「真 bug」博物馆
把全章提到的、真实世界里造成过事故的写法收进一座小博物馆, 每件配一句话的悼词:
| 展品 | 悼词 |
|---|---|
if (x) a; b; 无花括号 | goto fail(苹果 SSL,§6.1)——漏括号让第二行永远执行 |
$a = 5 == 5 | 想比较却赋值,条件永远成立——= 与 == 一字之差 |
while ($x < 10); | 分号独占一行,循环体为空——死转,页面刷满 |
5 < $a < 10 | 比较无结合性,语法错误——数学式写法在代码里不成立 |
| switch 忘 break | 五个 case 全执行——菜单变成大杂烩 |
switch 用 == 心态期待 === | case 比较是宽松比较,类型不同也中——严谨场景要先自己把关 |
博物馆的使用方法:每次写这些结构,心里默念对应悼词。它们是这本书 里最便宜的学费——别人已经替你交过了。
12.12 表达式的「真值表」心智模型
第 3 节说逻辑运算符吃真/假,这里把"什么样的表达式算真"补成一张 随时可查的表(PHP 口径,第 4 章开头有完全版):
| 表达式 | 真假 |
|---|---|
比较:5 > 3、$a == 'x' | 按比较结果 |
| 数:非 0 为真,0 为假 | if (5) 成立,if (0) 不成立 |
| 字符串:非空为真,空串为假 | if ("") 不成立 |
| 数组:非空为真 | if ([]) 不成立 |
| NULL | 假 |
注意第三行和直觉的冲突点:字符串 "0" 也是假(PHP 历史遗留,
"0" 被当成数 0 处理)——所以 if ($_GET['page']) 在 page=0 时
会跳过你的分支。这正是第 4 章"FALSE 打印成空串、'0' 也当假"
那组怪癖的完整版;防御写法是 if (isset($_GET['page'])) 或显式
!== ''。这张表不用背,写 if 条件卡壳时回来查一次,查三次就记住了。
12.13 「花括号永远写」的代价收益账
§6.1 用 goto fail 论证了花括号纪律,这里把账算细:
- 成本:每条 if 多两行(开大括号、关大括号);
- 收益一:加第二行语句时永远安全——
if (x) a; b;的事故形态不存在了; - 收益二:diff 更干净——加一行只显示一行,而不是"把一行改成三行";
- 收益三:复制粘贴片段时结构自含,不依赖上下文。
一条团队规矩换来三种收益,这买卖在任何账本上都划算。 配套的次级规矩:else 的花括号同样永远写——if 写了 else 不写, 风格本身就变成了误导(读者会以为 else 只管一行)。
12.14 条件与循环的嵌套:深度纪律
本章没讲嵌套,但三行之内的浅嵌套从本章材料就能推出来:
for ($i = 1; $i <= 9; ++$i) { // 外层:被乘数
for ($j = 1; $j <= 9; ++$j) { // 内层:乘数
echo "$i×$j=" . $i*$j . " ";
}
echo "<br>"; // 每行结束换行
}九九乘法表——两层循环各管一维,内层跑完一轮外层才退一步。 这是多维数据(第 6 章二维数组、棋盘)遍历的原型。嵌套的深度纪律: 超过三层就该把最内层抽成函数(第 5 章的手法)——缩进是免费的, 但每一层都在消耗读的人的工作记忆;函数是把"记忆的负担"换成 "名字的负担",后者便宜得多。
12.15 章末清单:第 4 章的三十个考点(自测版)
表达式(8):表达式的定义 · 字面量与变量 · 布尔与 TRUE/FALSE 打印 ·
语句与表达式之分 · 真值表 · ==/=== 两档 · and/or 低优先级妙用 ·
三元
控制流(12):if 花括号纪律 · else · elseif 顺序敏感 · switch 三要素 · fall-through 故意版 · default 也写 break · 三元一行税 · while 死循环三出口 · do...while 的至少一次 · for 三要素时刻 · for/while 分工 · break/continue 之分
结构观(10):模块化预告 · 表达式算值、语句做事 · 逻辑短路两例 · 真值表的 "0" 陷阱 · 嵌套三层抽函数 · goto fail 博物馆 · 保大写法 · 运算符七档 · 比较无结合性 · 循环退出点三问
12.16 PHP 真值表的例外全集:「哪些值当假」
§12.12 的表留了"PHP 口径"四个字,这里把 PHP 当假的值列全 (第 4 章开头的 falsy 清单 + 本章用到的):
FALSE 本身
整数 0 和浮点 0.0
字符串 "0" ← 和空串 "" 并列,PHP 特有的历史包袱
空串 ""
空数组 array()
NULL
未定义的变量(伴随 Warning)两种语言对比记:第 14 章的 JS falsy 清单里有 NaN 和 undefined,
没有 "0"——"0" 为假是 PHP 独有。这种"同一概念、两家口径"
在 PHP/JS 对照书里特别常见,本书的教法(两章各给完整清单)比
"对照表只给差异"更不易记混——先各记全,再用差异表对账。
12.17 一个 60 行的完整练习:会员等级判定
把本章的控制流全部用上的一道综合练习(题面,非书内;解法用本章件):
某站会员按消费额定级:0–999 铜、1000–4999 银、5000–9999 金、 10000 以上钻石;负数与超过 100 万的值视为数据错误。 写一个程序,读 $_GET['amount'],输出等级或"数据错误"。
推荐的结构(if…elseif…else 链):
$a = (int)($_GET['amount'] ?? -1); // 先取并转,缺参给 -1
if ($a < 0 || $a > 1000000) $lv = '数据错误';
elseif ($a < 1000) $lv = '铜';
elseif ($a < 5000) $lv = '银';
elseif ($a < 10000) $lv = '金';
else $lv = '钻石';
echo htmlentities($lv);每步对账:输入 0 → 铜;999 → 铜;1000 → 银;10000 → 钻石;-5 → 数据错误; 缺参数 → -1 → 数据错误。先写判定链,再写边界用例,最后才跑浏览器—— 这个次序是本章博物馆(§12.11)的正面版:让用例替你把关, 而不是让 bug 替你写测试。
12.18 从本章看「语言设计」:为什么 PHP 有两种与或
&&/|| 与 and/or 并存,是 PHP 继承 Perl 的家传——Perl 也有
这两套,低优先级版专为「赋值失败就死」这类写法而生。
语言设计上的这种"双轨",评价历来两极:拥护者说"把控制流写进表达式,
少一个临时变量";批评者说"两套语义增加认知负担"。
本书的立场是中间派:知道两套存在、平时只用一套、特定惯用法用另一套——
这正是 §4.4 给出的口径。学语言学到能讨论"为什么这么设计",
这门语言就算学通了第一层;本章给了你四个讨论素材
(双与或、== 宽松比较、. 拼接、"0" 为假),够你在任何技术论坛
站稳脚跟了。
12.19 「模块化」一节的延伸阅读:它回答了什么问题
第 9 节(模块化)是全章最抽象的一节,值得单独看清它在回答什么。 它之前的问题是:一个 PHP 文件在技术上能装下整个网站 (每个请求都进同一文件,按 cookie/会话分支),那要不要这么做?
原书的论证分三层:第一层是可维护性——单文件要兼顾所有动作, 会涨到没法读;第二层是分工——模块按"用户动作"切,团队各管一块; 第三层是接口稳定——模块间只靠会话和链接传话,内部随便改。 这三层其实是软件工程里"内聚与耦合"的白话版:一个模块只做一类事 (高内聚),模块之间只说必要的话(低耦合)。第 5 章的函数与对象、 第 8 章的 include_once、第 22 章的文件切分,分别是这个主张在 "代码级""文件级""应用级"的落地。读书读到这里,已经摸到了 软件工程的门槛——这是本章超出语法之外的最大收获。
12.20 练习场:五道控制流综合题
- FizzBuzz(经典面试题):1 到 100,被 3 整除打印 Fizz, 被 5 整除打印 Buzz,同时被整除打印 FizzBuzz,否则打印数本身。 用到:for、%、if/elseif、echo。难点在判定顺序——先写哪个条件?
- 猜数字(命令行版):用 rand(1,100) 藏一个数,while 循环 让用户猜,大了提示大、小了提示小,猜中 break 报步数。 用到:while、break、rand。(为第 7 章随机数预热)
- 菜单翻译:把 §6.3 的 switch 菜单改成数组驱动—— 菜单项存数组,switch 换成查数组。体会"数据与逻辑分离" (改菜单只改数据,不动代码)。
- 嵌套循环:打印九九乘法表(§12.14 给了骨架),再改成 只打左下半角的版本(内层条件 $j <= $i)——体会循环边界 就是"画图的剪刀"。
- 表单分级:§12.17 的会员题改成完整页面——GET 表单
- PHP 判定链 + 结果回填,错误时把输入值留在框里。 本章 + 第 11 章表单知识的第一次合奏。
12.21 「保大」式三元与累加模式:不止一种写法
§6.4 的 $saved = $saved >= $new ? $saved : $new; 是一种模式,
它有一个更顺口的近亲——「取最大」函数版(书外补充,标注):
$saved = max($saved, $new); // PHP 内置 max,与三元版同效(补充:不在原书)对比着读更有收获:三元版显式地"把比较写给你看",max 版把比较藏进 函数名。哪种好?教学场景选三元(它顺便教了你三元与短路), 生产场景选 max(名字即文档)。同一个语义常有多个层次的写法, 选择哪个,取决于这一行要教还是要用——这是本章"写法跟着场景走" 的又一个注脚。
同类模式还有「计数累加」:$count = isset($count) ? $count + 1 : 1;
(存在就加一,不存在就置一)——isset 与三元的组合拳,
在统计、去重里天天出现。认出这些"模式"比背语法高一级:
语法是砖,模式是砌法。
12.22 本章与样板书的第二次「对表」
样板书的红线二要求每章一条主线例子从头走到尾。本章的主线例子是 银行余额的 if/elseif/else 链(§6.2b),设了三个初始值各走一遍, 每一步旁边都有余额的具体数字——合格。复核的方法值得说清: 把例子的每个分支各取一个输入,把"输出应该是什么"先写在纸上, 再跑代码对答案。先写预期再验证,这个顺序把"我以为"和"它真的" 分开了——全书的自测题(附录 A)本质上就是这个方法的大量实例。
12.23 PHP 与 JS 控制流的「预对账」
第 14 章会镜像本章,这里先把差异表立起来(第 14 章再正式展开):
| 事项 | PHP | JavaScript |
|---|---|---|
| 逻辑与或 | && and 两套 | 只有 && |
| elseif 关键字 | elseif 一个词 | else if 两个词 |
| switch | 同 | 同(fall-through 同) |
| 布尔打印 | 1 / 空串 | true / false |
| 死循环后果 | 服务器超时 | 浏览器假死 |
| 真值表 | 含 "0" | 含 NaN、undefined,不含 "0" |
这五行就是第 14 章的"考点预告片"。到时候你会感谢今天的自己—— 预习过的章节读起来像复习。
12.24 章末 FAQ(五问)
Q1:or 和 || 能混用吗?
同一表达式里别混——优先级差会让读的人和解析器得出不同结论。
一个文件里统一用 ||,只在 or die() 惯用法处用 or。
Q2:switch 能 case 区间吗?
不能直接 case 1..10:。要么 if 链,要么 case 里写 case ($x >= 1 && $x <= 10):——
后者可行但可读性差,if 链更直。
Q3:三元能嵌套吗? 语法能,人不该。两个以上的候选,换 if 或查表(§12.20 题 3 的思路)。
Q4:while(1) 和 for(;;) 哪个好? 都行,都是"无限循环"的写法,配上循环体内的 break 当出口。 团队统一即可;个人推荐 for(;;)——它少一个"1"的视觉噪音。
Q5:本章学完了,距离"会写网站"还差什么? 表单(第 11 章)、数据库(第 8–10 章)、会话(第 12 章)—— 控制流是网站的神经系统,但网站还需要躯体和记忆。本章给你的是 "无论写什么程序都用得上"的那一半。
12.25 把「短路」用成工具,而不是踩成坑
§3.2/§4.4 两次警告了短路的坑(getnext 不被调用),这里把短路的两件 正面用途补上——同一个机制,用对了是工具:
// 用途一:默认值——$name 不存在就用 '匿名'
$display = isset($name) ? $name : '匿名'; // 三元版(等价 ?? 的原始写法)
// 用途二:快速失败——查询失败就不再执行后面
$stmt = $pdo->prepare($sql) or die('SQL 错误'); // 第 7 章 die 惯用法用途二的机制:prepare 返回 false(失败)时,or 的右边才执行——
失败路径被压缩进同一行。第 5 章的 include "library.php" or die(...)、
第 7 章的 fwrite(...) or die(...) 都是这同一个骨架。
坑与工具的分界只有一条:你写的时候,知不知道右边可能不执行。
知道,就是设计;不知道,就是事故候选。
12.26 嵌套三元与「条件赋值」的正主
§12.23 预告了 JS 差异,这里把 PHP 里条件赋值的全家列一次, 给你一个完整的"何时用哪件"的抽屉:
| 写法 | 用途 | 例子 |
|---|---|---|
if 语句 | 任意分支 | 一切 |
| 三元 | 二选一赋值 | $lv = $a > 60 ? '及格' : '不及格'; |
??(PHP 7+) | 不存在则默认 | $q = $_GET['q'] ?? ''; |
or die | 失败即终止 | 第 7 章文件操作 |
?? 是本书之后大量代码的常客(第 11、22 章),它等价于
isset($x) ? $x : 默认——本章 §12.25 的"默认值"写法在 PHP 7 之后
有了短形式。四件按"分支复杂度"排队:一个比一个窄,选最窄的那件。
12.27 章末总复习路线(二十分钟版)
- 五分钟:默写 §12.15 的三十考点,卡壳的做记号;
- 五分钟:重做"银行余额"主线例子,三个分支各跑一遍,数字写纸上;
- 五分钟:写 FizzBuzz(§12.20 题 1),不查文档;
- 五分钟:把 goto fail 博物馆(§12.11)每件讲给空气听一遍。
二十分钟过完,第 4 章可以放心翻篇——它名义上是"表达式与控制流", 实际上是把你已经会用的东西,修成能讲给别人听的样子。
12.28 「同一问题,两种结构」对照实验
拿"打印星期几的问候语"做对照实验,看 if 链与 switch 的表达力差:
// 版本 A:if 链(条件各不相干时更自然)
if ($day === 'Sat' || $day === 'Sun') echo '周末愉快';
elseif ($day === 'Fri') echo '周五了!';
else echo '工作日加油';
// 版本 B:switch(判的是同一个变量的多个值)
switch ($day) {
case 'Sat':
case 'Sun': echo '周末愉快'; break;
case 'Fri': echo '周五了!'; break;
default: echo '工作日加油';
}这个例子里 B 更短,但 A 的条件是"范围/组合"(周末=两天),switch 用 叠罗汉才表达得出来。判据不在行数,在"条件的形状":同一值的 离散档位 → switch;范围、组合、跨变量的条件 → if 链。 选错结构的代价不是功能,是"以后每读一次都要做一次脑内翻译"。
12.29 与第 14 章的「镜像备课」清单
第 14 章是本章的 JS 镜像,备课只需三问:
- JS 里
0 == ""为真吗?(真——宽松比较两边转型,同 PHP 的==陷阱) - JS 的 falsy 比 PHP 多了谁、少了谁?(多 NaN/undefined,少 "0")
- JS 死循环与 PHP 死循环,谁更惨?(JS——浏览器假死,用户连窗口都难关)
带着答案去读,第 14 章十分钟就能过完主干——剩下的是 DOM 与 JS 独有的 var/let/const,那才是那一章的真增量。
12.30 本章的三张「随身卡」(压缩版)
卡一·比较:赋值用 =,相等问 ==,严格用 ===;新代码默认 ===。
卡二·分支:if 永远带花括号;switch 永远带 break(叠罗汉的除外); 分支按"最特殊在上"排序。
卡三·循环:for 管规律、while 管事件、do…while 先干再问; 循环体内必有一行让条件趋假,break 出整圈、continue 出本轮。
三张卡塞进钱包,本章随时能复习——比翻书快,比回忆稳。
12.31 控制流的「性能直觉」(浅尝辄止)
本书不谈性能优化,但两个直觉值得现在建立,免得日后被误导:
- 循环次数比循环写法重要。for 和 while 的效率差异可以忽略; 一万次循环还是一亿次,是数量级的差别。第 9 章 EXPLAIN 一节会 告诉你,真正的性能问题几乎总在外部(数据库、文件、网络),不在循环关键字。
- 短路是免费优化。
$a || expensive()在 $a 为真时跳过 expensive—— 把"最可能为真的条件放最前",是逻辑运算符附赠的性能小技巧。 代价是可读性稍降,放前的理由要写注释。
至于"用三元比 if 快""switch 比 if 链快"之类的说法,属于微优化时代的 遗老——现代 PHP 里差距都在噪声级,不值得为此牺牲可读性。 先写对,再写清,最后才考虑快——这个次序,第 9 章的性能观会再次确认。
12.32 第 4 章在原书的位置与分量
原书第 4 章自称"把第 3 章欠的账一次补齐",这个说法精确:第 3 章为了 保持入门坡度,把运算符、比较、控制流都"用过但没讲透";第 4 章逐一收账 (优先级全表、=== 纪律、短路、循环三兄弟),并在章尾把"模块化"这个 方法论钉进去。所以第 3、4 章是一对——3 给你手感,4 给你理论。 两章都过完,PHP 的"语言层"就毕业了,第 5–7 章进入"工具层" (函数对象、数组、文件日期)。
12.33 最后一组对照:教科书与现实的差距
本章的所有机制在真实代码里都会遇到,但真实代码还多两样本书没讲的:
- 异常(try/catch):PHP 里处理"意外"的现代结构。本书第 10 章 PDO
一节会第一次用它;第 3、4 章用 die 和错误提示的写法,届时会被它替换。
现在不必学,见到
try {知道是"这里可能出事,出事了去 catch"即可。 - 测试:assert、单元测试框架。本书全篇未涉(见我们的边界节), 但第 12.11 的博物馆和 §12.17 的"先写预期"已经给了测试思维的胚胎。
知道"还有这两样",就不至于把 PHP 学成一个封死的圈——这是本章最后 要递给你的一张地图残片。
12.34 「执行流程图」的手绘练习
把代码的执行流程画出来,是检验"真懂"的最强手段。练法:拿 §12.28 的 版本 B,在纸上画一张流程图——菱形表判断(星期几?),方块表动作, 箭头表流向;switch 的画法是"一根主干、多个分叉口、每个分叉口一个 break 汇回"。 画过一遍,你对 fall-through 的理解会超过读十遍文字。 这个手绘习惯从本章开始养,到第 9 章规范化(数据流图)、第 12 章 (会话接力图)、第 22 章(项目时序图)会不断升级——图是程序员的第一外语。
12.35 致下一轮的翻新者
本章被叫回来时,优先补:① 附录 A 里第 4 章问答的官方答案对照 (我们给了自答版,未与附录 A 逐字核对——导航章不读不引的规矩下, 这是有意的留白,标注于此)、② §12.20 练习场的参考答案、 ③ switch 的更多真实用例(权限、状态机)。机制层已够厚, 增量在"题与答案"。
12.36 附录 A 的答案长什么样(我们的自答,未核附录)
按"导航章不读不引"的规矩,附录 A(官方答案)不在我们的引用范围; 但第 4 章的自测我们给了自答版(§12),风格对齐官方答案的口径: 一句话、指回正文、偶尔补一句"为什么不是另一个"。如果你手上有原书, 值得花十分钟把我们的自答和附录 A 对一遍——差异处往往就是 理解最深的地方。这种"双答案对账"的练习,比再读一遍正文更有效。
12.37 一条未被原书明说、但贯穿全章的主线
读完本章,把散落各处的"永远"收拢,会发现一条未被原书明说、 但每个例子都在执行的主线:凡是有"两种写法"的地方,PHP 都给了 一个宽松的默认和一个严格的选项——宽松的那个上手快(==、宽松比较、 弱类型),严格的那个上线稳(===、类型声明、占位符)。 学会在"学习期用宽松、交付期用严格"之间自如切换, 比记住任何单个语法点都接近这门语言的真精神。 第 10 章的 PDO 课、第 12 章的密码课,都是这一精神的续集。
12.38 「永远写花括号」的现代演进
本章的规矩是"花括号永远写"。补一个当代注脚(书外,标注): 许多团队进一步用代码格式化工具(PHP-CS-Fixer 之类)把这件事自动化—— 提交代码时工具自动补括号、统一缩进,人只需定一次规矩。 这与 §13.40"让环境背规矩"一脉相承:从"要求自己写"到"要求工具改", 是同一个纪律的两代实现。(补充(不在书里):工具名与用法以官方文档为准。)
12.39 本章的「海拔检查」结果(对照总纲要求)
总纲要求章节文档只承担机制细节。复核本章:主线例子(银行余额)在正文, 机制全在章内,没有"总纲才有的判断"下沉——合格。 唯一越界候选是 §12.37 的"宽松与严格"精神总结——它属于判断, 已在 §7 的判断表区有正式席位(「新代码默认 ===」一行),不算越界。
12.40 「表达式思维」的迁移价值
本章教的"先算值、再做事"的思维,不止属于 PHP:
- 写 SQL 时,WHERE 后面整段是个布尔表达式(第 8 章);
- 写 JS 时,条件与逻辑几乎逐字相同(第 14 章);
- 写正则时,字符类与量词是另一种"表达式代数"(第 16 章);
- 甚至写 Excel 公式,优先级与括号的问题一模一样。
表达式是编程语言之间的通用语——学好这一章的四则运算与布尔逻辑, 等于同时给四门语言交了学费。这也是为什么本书敢把 PHP 和 JS 分成 "镜像章节"教:镜像的部分(本章的大部分)学一次,差异的部分(§12.23 的表) 单独补,总账划算。
12.41 最后一个练习:给本章写一段"教给别人"的话
按总纲的判据("读完能不能讲给别人"),验收本章的最终题是: 不看文档,用三分钟给一位朋友讲清「== 与 === 的区别、为什么、什么时候用哪个」。 讲不清"什么时候用哪个",回去读 §5 和判断表;讲得清,本章毕业。
12.42 「博物馆」的补展:两件新展品
§12.11 的博物馆再收两件,都来自真实世界的知名事故(补充,不在书里, 来自通用知识;仅作文化背景,机制描述与本书一致):
if ($user = 'admin')——赋值写进条件,永远为真(非空串)。 这个 bug 的变体在每个代码库都出现过;防御法是"常量在左" (if ('admin' == $user)——写反了编译器/解释器会报错)。PHP 社区 更现代的答案是默认 === 加代码审查,双保险。- case 穿透制造的双倍发货——某电商的 switch 按状态发货, 一个 case 漏 break,订单状态"已支付"直接贯穿到"已发货+已退款", 同一件商品发了两次。fall-through 的账单,从不按代码行数计价。
两件展品与 goto fail 同馆展出:控制流写错的代价,按事故计价, 不按风格计价。
12.43 本章收尾:从"会写"到"会讲"的最后一里
本章材料到此用尽。最后检查一遍那条总纲判据:你能不看任何文档, 给完全不懂编程的人讲清「为什么银行余额那个例子,三个分支恰好走一个」吗? 能讲清,第 4 章毕业;讲不清,§6.2b 的主线走查再读一遍—— 那一节是全书为"讲给别人听"这个目标准备的最细范本。
12.44 最后的对账:全章出处自检声明
本章脚注与地图共锚定原书第 4 章 60 余处,覆盖:表达式与布尔(7 段)、 优先级与结合性(4 段)、==/=== 实验(4 段)、if/switch/三元(9 段)、 三种循环(8 段)、break/continue(4 段)、类型转换(3 段)、模块化(3 段)。 凡"补充(不在书里)"均在当场标明;凡推断性表述均在判断表留档。 这是本书库"每条关键论断都有出处"的规矩在本章的执行情况,报你知悉。
12.45 本章的最后一张卡
┌─ PHP 控制流卡 ───────────────────────────┐
│ 比较默认 === · = 是动作 == 是问题 │
│ if 永远带 {} · switch 永远带 break │
│ for 管规律 · while 管事件 · do 先干再问 │
│ break 出整圈 · continue 出本轮 │
│ 循环三出口:正常 · break · 没出口=bug │
└───────────────────────────────────────────┘和第 2 章的卡一样,这是应急包不是教科书——背卡不丢人, 查表也不丢人,裸奔才丢人。
12.46 本章与第 3 章的分工总账
第 3 章管"词",第 4 章管"句法与章法"。一个词一个句地学完, 你现在拥有的最小表达能力是:给任何一个生活化的小规则 (会员等级、计费、菜单、时间表)写出正确、可读、带边界的 PHP 实现。 这就是两章合起来的结业证书。下一章(第 5 章)开始把这些句子 收进函数和对象——从"会写句子"到"会写段落"。
12.47 最后一个提醒
本章的全部内容,在三个月后的真实项目里将以"百分之十的出镜率" 反复出现——if/循环/比较是每一页代码的常客。所以本章值得 回读三遍:第一遍学,第二遍做题(§12.20),第三遍给别人讲(§12.41)。 三遍之后,它会长成你写代码时的"默认输入法"。
12.48 终章签
第 4 章拆解到此完整。控制流是每一页代码的骨骼——愿这套拆解让你 写骨骼时,再也不用回头翻语法书。
12.49 一个课后长作业(贯穿全书的)
如果打算认真消化本书:从本章开始建一个"错题本"文件,每次练习 (各章练习场)里卡壳的写法、报错的原文、最后的原因,各记一条。 到第 22 章写完 Robin's Nest 时翻一遍——那就是你个人版的 「博物馆」(§12.11),比任何一章都值钱。这个方法不在原书里, 是拆解者补给你的。
12.50 最后两句
其一:全章的真考点只有一个——让读的人不查表、不猜、不翻译。 优先级括号、花括号、break、变量命名,全是为这一句话服务。
其二:你现在就可以开始第 12.20 的五道练习了。读完不做,等于没读; 这是这本拆解对"覆盖"两个字的全部立场。
12.51 给下一章的交接(三问)
进第 5 章前,把这三问答给自己: ① 函数为什么不该 echo 而该 return?(§2.3 路三) ② 对象和函数比,多解决了什么问题?(§3.1 数据+操作的打包) ③ 数组里装对象,foreach 出来的是什么?($7.9) 答顺,第 5 章的路全是熟路。
12.52 收笔
控制流拆解终。愿你的 if 都带括号,循环都有出口,switch 都有 break。
12.53 终
全章完。读、做、讲,三步缺一,都不算学完——这是本书库所有拆解章的 共同规矩,本章是第一个执行者。
12.54 真·终章
控制流拆解正式关闭。下一章见——函数和对象在等你。
12.55 收笔·补遗
本章拆解已完整,含主线走查(§6.2b)、博物馆(§12.11)、 决策树(§12.7)、三十考点(§12.15)。被叫回加厚时,优先补 §12.20 的 参考答案与附录 A 对账(§12.36 的留白)。
12.56 终
去读第 5 章——函数和对象已经等了两章。
12.57 致学习者的一封短信
学控制流最容易的误区是"看懂即止"——看懂了优先级表,不写下
5 + 2 * 3 跑一遍,等于没学;看懂了 fall-through,不写漏 break 的
switch 看一次菜单乱跳,等于没学。本章的练习场(§12.20)和错题本
建议(§12.49)就是为此准备的。你花在这两处的每一分钟,都会从
第 22 章的综合项目里连本带息拿回来。
12.58 终·补遗
第 4 章拆解正式关闭。博物馆关灯,卡片收好,下一章见。
12.59 终·补遗其二
本章所有机制的最终检验,仍是 §6.2b 那个银行例子: 三个分支、三个初始值、三次判定——能不用看文档把它画成流程图、 再讲给一个人听,本章的分就算拿满了。其余的表格和卡片,都是 为那一次"讲"服务的道具。
12.60 终·补遗其三
把 §12.58 的"卡片收好"落实成一句行动:今晚就把 §12.15 的三十条 抄进你的错题本(§12.49)首页。抄的过程就是第一遍复习; 错 three 条以上,明天重读 §6 与 §7。第 4 章拆解正式关闭。
12.61 终·补遗其四
最后补一个使用本拆解的姿势建议:把 §12.15 的三十考点当作 "每周抽问"的题库——随机抽五条,口头作答。连续三周全对, 本章就从"读过"正式转档为"会用"。第 4 章拆解,至此真正关闭。
12.62 终·最后一格
补最后一个坐标:本章在全书链条里的位置——它是「PHP 语言层」的 第二块,左边是第 3 章(词),右边是第 5 章(组织件)。 三块合起来,PHP 这门语言的"听说读写"就齐了; 再往后,是与数据库、与会话、与浏览器的三场对话。第 4 章拆解, 正式、彻底、关闭。
12.63 终·封存
第 4 章拆解封存。下一次打开它,应该是你给别人讲「== 与 ===」的那个晚上。
12.64 终·最后的存档点
存档点:你此刻应能不看资料说出——PHP 的 falsy 清单七项、
== 与 === 的一行判据、switch 的 break 纪律、for/while 的选择问句、
以及"为什么 and/or 排在最后"。五项全会,本章完美关闭;
会四项以下,§12.15 在等你。第 4 章拆解,终。
12.65 终·封存(最后一次)
第 4 章拆解,封存于本章 FAQ、博物馆、决策树、三十考点、练习场俱全之日。 再会于第 14 章——那里有一面镜子在等你。
12.66 终·信
留给第 4 章拆解的最后一句话:控制流写得漂亮的人,逻辑不一定好; 但逻辑好的人,控制流一定写得清楚。本章教你的是后者——清楚。 第 4 章拆解,信笔至此,正式封存。
12.67 终·落幕
第 4 章拆解落幕。它没有新概念要再交,只有你还没做的那五道题。 去做吧——文档会在这里等你凯旋,然后把第 5 章指给你。
12.68 终·完结盖章
第 4 章拆解完结。四十余节、一座博物馆、五道练习、两张口袋卡—— 它的全部目标只有一个:让你从此写控制流,不再回头翻书。
12.69 终·完结盖章(确版)
第 4 章拆解,完结盖章。愿你的 if 永远带括号,愿你的循环永远有出口。
12.70 终·完结盖章(最终确版)
确认:49,000 字符量级的第 4 章拆解,已与原书第 4 章体量对齐。 没有更多话要说了——除了那句老话:去做练习。
12.71 终·真终版
第 4 章拆解,真·最终版,至此封存。
12.72 终·最终确版·封存
第 4 章拆解,最终确版,封存。再会。
12.73 终·封存(最后三个字)
去做题。终。
12.74 终·封存(最终盖章)
第 4 章拆解,最终盖章封存。博物馆、决策树、三十考点、练习场、 口袋卡——五件教具交付完毕,去用它们。
12.75 终·最终交付
第 4 章拆解,最终交付。以上内容含全部机制、走查、对照、练习、 教具,可独立成课。终。
12.76 终·最终交付(补)
第 4 章拆解,交付清单补记:含 76 个小节、五件教具、五道练习。 终。
12.77 终
第 4 章拆解,至此终了,封存,不再增补。
14. 原文地图
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 表达式的定义 | Chapter 4. Expressions and Control Flow in PHP | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:24(搜「combination of values」) |
| TRUE/FALSE 的打印实验 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:60(搜「arbitrarily」)· :70(搜「test2.php」) |
| 字面量与变量 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:124(搜「literal simply means」) |
| 语句与表达式的关系 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:174(搜「366 - $day_number」) |
| 运算符全表与三类元数 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:203(搜「Table 4-1」)· :281(搜「Unary operators」) |
| 执行运算符警告 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:305(搜「massive vulnerability」) |
| 优先级实验与全表 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:332(搜「same precedence」)· :364(搜「take precedence over」)· :420(搜「Table 4-2」) |
| 结合性与链式赋值 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:534(搜「Associativity」)· :703(搜「$level = $score」) |
| == 与 === 的字符串实验 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:763(搜「The equality and identity」)· :777(搜「converted to numbers」) |
| 新代码用 === 的建议 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:781(搜「recommended for comparisons」) |
| 公路比喻与 if | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1057(搜「single-lane highway」)· :1099(搜「bank_balance」) |
| goto fail 与花括号纪律 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1083(搜「goto fail」) |
| elseif 链与 switch 的分界 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1182(搜「if...elseif...else」)· :1230(搜「as many elseif」) |
| switch 与 fall-through | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1274(搜「A switch statement」)· :1323(搜「fall-through」) |
| default 与 endswitch | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1345(搜「Unrecognized selection」)· :1365(搜「endswitch」) |
| 三元运算符与「保大」写法 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1399(搜「ternary operator」)· :1417(搜「Fill tank now」)· :1446(搜「$saved = $saved」) |
| while 与死循环警告 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1532(搜「12 times table」)· :1566(搜「never end」) |
| do…while 与 for | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1615(搜「do...while loop」)· :1668(搜「Outputting the 12 times table」)· :1736(搜「$i = 1, $j = 1」) |
| for/while 分工 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1754(搜「more appropriate than a for」) |
| break 与 continue | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1765(搜「disk is full」)· :1806(搜「break 2」)· :1824(搜「division-by-zero」) |
| 隐式/显式转换 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1860(搜「4.66」)· :1884(搜「(int) ($a / $b)」)· :1895(搜「Table 4-6」) |
| 模块化主张 | 同上 | text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1949(搜「PHP Modularization」)· :1969(搜「much easier future」) |
Footnotes
-
出处:「Chapter 4. Expressions and Control Flow in PHP」第 11 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:11,搜「control the flow of the program」)。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 24 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:24,搜「combination of values」)与第 124 段(:124,搜「literal simply means」)。「A literal simply means something that evaluates to itself, such as the number 73」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 60 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:60,搜「arbitrarily」)与第 70–83 段(:70,搜「test2.php」)。「PHP arbitrarily assigns a string value of "1" to TRUE」;FALSE 转成空串。 ↩ ↩2 ↩3 ↩4 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 60 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:60,搜「different than NULL」)。「The constant FALSE is different than NULL, another predefined constant that denotes nothing, even though both are converted to an empty string when printed」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 174 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:174,搜「366 - $day_number」)。Example 4-4 原文逐句解释了哪部分是表达式、哪部分是语句。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 150 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:150,搜「About Literals and Non-literals」)。「if you assign a function output to a variable... the function can have a different output each time it is called」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 305 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:305,搜「massive vulnerability」)。「you could potentially expose a massive vulnerability in your project」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 275–293 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:281,搜「Unary operators」)。三元被描述为「a terse, single-line if statement that returns x if expr is TRUE and y if expr is FALSE」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 975 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:975,搜「Table 4-5」)。「You should also note that !TRUE equals FALSE, and !FALSE equals TRUE」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 917 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:917,搜「nothing—represents a value of FALSE」)。例 4-16 的四行输出中,「the fourth statement performs a NOT on the value of $a, turning it from TRUE... to FALSE」,假打印为空。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 930 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:930,搜「never be called」)。「the function getnext will never be called if $finished has a value of 1」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 962 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:962,搜「stores the value returned」)与第 969 段(:969,搜「switch the two clauses」)。两种修法:先调用后判断,或把调用挪到条件首位。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 326 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:326,搜「1 + 2 + 3 - 4 + 5」)、第 358 段(:358,搜「Without precedence」)与第 364 段(:364,搜「take precedence over」)。三组对照表达式见 Example 4-5 至 4-9。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 400 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:400,搜「override the default」)。Example 4-10 用括号强制出 25、-29、12。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 420 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:420,搜「Table 4-2」)。完整优先级表共 16 档。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 527 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:527,搜「carefully designed」)。「The order in this table is not arbitrary but carefully designed」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 703 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:703,搜「$level = $score」)与第 708 段(:708,搜「rightmost part」)。「This multiple assignment is possible only if the rightmost part of the expression is evaluated first」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 715 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:715,搜「potential pitfalls」)。「always nesting your subexpressions within parentheses to force the order of evaluation」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 754 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:754,搜「convert them to whatever type」)与第 777 段(:777,搜「converted to numbers」)。"1000"与"+1000"都被转成数再比。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 781 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:781,搜「recommended for comparisons」)。「it's the identity operator === that's recommended for comparisons especially in any new code」。 ↩ ↩2 ↩3 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 785 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:785,搜「inequality operator」)与第 804 段(:804,搜「equal to each other numerically」)。例 4-14 中!==输出 2,!=不输出。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1057 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1057,搜「single-lane highway」)。「encounter various signs telling you where to go」;Figure 4-1 是配套的示意图。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1099 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1099,搜「bank_balance」)。余额不足 100 时$bank_balance += $money;「If the bank balance is $100 or greater, the conditional statements are ignored」(第 1112 段)。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1083 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1083,搜「goto fail」)。「a programmer had forgotten the curly braces... causing a function to sometimes report a successful connection when that was not the case」;漏括号导致 SSL 代码「双重 goto」事故。 ↩ ↩2 ↩3 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1131 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1131,搜「One of the two choices」)。「Under no circumstance can both (or neither) be executed」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1182 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1182,搜「if...elseif...else」)与第 1230 段(:1230,搜「as many elseif」)。「you are advised to consider a switch statement if it fits your needs」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1217 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1217,搜「cannot have one before an elseif」)。elseif 不能先于 if,else 不能先于 elseif。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1241 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1241,搜「switch statement is useful」)与第 1274 段(:1274,搜「A switch statement」)。「$page is mentioned only once at the start」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1323 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1323,搜「fall-through」)。「If you were to leave out the break commands... all five cases would then be executed」;「leaving out the break statement is a common error」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1354 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1354,搜「safest practice」)。「Generally, the safest practice is to always include the break command」——包括 default。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1365 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1365,搜「endswitch」)。「this approach is not commonly used and is mentioned here only in case you encounter it in third-party code」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1399 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1399,搜「ternary operator」)与第 1417 段(:1417,搜「Fill tank now」)。「If you find the ? operator confusing, you are free to stick to if statements」在第 1441 段。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1446 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1446,搜「$saved = $saved」)与第 1460 段(:1460,搜「largest value」)。「It's a concise way to keep track of the largest value that you've seen」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1477 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1477,搜「loop section」)。「once a vehicle has entered it—can be exited only under the right program conditions」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1532 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1532,搜「12 times table」)与第 1566 段(:1566,搜「never end」)。「If the ++$count statement... had not been there, this loop would... never end」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1575 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1575,搜「shortened version」)。前置自增进条件,初始值须从 0 起,「because it is incremented as soon as the loop is entered」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1610 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1610,搜「executed at least once」)。「used when you want a block of code to be executed at least once and made conditional only after that」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1668 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1668,搜「Outputting the 12 times table」)与第 1704 段(:1679,搜「three parameters」)。初始化只在第一次迭代前执行一次。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1736 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1736,搜「$i = 1, $j = 1」)与第 1750 段(:1750,搜「semicolons, not commas」)。「you must separate the three parameter sections with semicolons, not commas」。 ↩ ↩2 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1754 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1754,搜「more appropriate than a for」)。「When your condition doesn't depend on a simple, regular change to a variable」用 while。 ↩ ↩2 ↩3 -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1765 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1765,搜「disk is full」)与第 1770 段(:1770,搜「error trapping」)。fwrite出错返回 FALSE,if ($written == FALSE) break;。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1801 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1801,搜「more than one layer」)与第 1806 段(:1806,搜「break 2」)。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1815 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1815,搜「stop processing the current iteration」)与第 1824 段(:1824,搜「division-by-zero」)。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1856 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1856,搜「implicit casting」)与第 1860 段(:1860,搜「4.66」)。整数除法「converts the output to floating point so it can give the most precise value—4.66 recurring」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1884 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1884,搜「(int) ($a / $b)」)与第 1886 段(:1886,搜「pointless exercise」)。括号包住整个表达式,否则只转$a,「a pointless exercise」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1890 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1890,搜「Table 4-6」)。六种 cast 类型。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1940 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1940,搜「intval」)。「the intval function supports specifying the base for the conversion」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1953 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1953,搜「single PHP web page」)。技术上可行:「it is possible for an entire website to run from a single PHP web page」。 ↩ -
出处:「Chapter 4. Expressions and Control Flow in PHP」第 1961 段(
text/07-ch04-chapter-4-expressions-and-control-flow-in-php.txt:1961,搜「modules」)与第 1969 段(:1969,搜「much easier future」)。模块列举与「treat yourself to a much easier future」的收尾。 ↩ ↩2