PHP 语言初识 — 变量、字符串、运算符与作用域¶
这一章讲什么: 从零学会 PHP 这门语言的基本件——怎么把程序嵌进网页、 变量和字符串怎么工作、运算符和类型转换的规律、函数怎么定义、作用域是怎么回事。 原书说这是「全书的地基」,后面 PHP 相关的每一章都站在这一章上。 不需要任何编程基础,每个术语第一次出现都当场解释。
1. 顶层全景:一个 PHP 文件长什么样¶
浏览器请求的 .php 文件
│
├─ HTML 部分 ────────────────▶ 原样发给浏览器
├─ <?php …程序… ?> ──────────▶ PHP 解释器执行,执行产生的文本才发出去
└─ HTML 部分 ────────────────▶ 原样发给浏览器
图说:PHP 文件 = 普通 HTML 文件里嵌了几段程序。
浏览器永远只收到 HTML;程序本身和执行痕迹都留在服务器上。
这个「嵌」字的含义比看上去重要。.php 扩展名是开关:web 服务器看到请求的文件以 .php 结尾,
就把它转给 PHP 解释器;解释器从头到尾扫一遍文件,标签外的内容照抄,标签内的内容执行,
两边拼起来才是发给浏览器的完整回答3。标签内执行的程序可以输出任意文本——通常就是 HTML,
但也可能是纯文本、甚至图片数据。第 01 章那句「浏览器收到的永远是平平无奇的 HTML」,机制就在这里。
2. 程序的外壳:标签、语句、注释¶
2.1 一对标签圈出程序¶
程序段从 <?php 开始,到 ?> 结束。两个标签之间可以是一行,也可以是几百行;
一个文件里可以有任意多对标签。原书给的第一个程序只有三行4:
两种排版风格都合法:有人整篇文件只有一对标签、所有 HTML 都用 echo 输出;
也有人只在需要动态内容的地方插一小段 PHP,其余保持纯 HTML。
前者说后者反复进出标签有性能损耗,后者说这点损耗微不足道、反而更难读——
原书为了例子清晰,采用「尽量少切换」的写法5。
两个实用细节:
- 短标签
<? … ?>也存在,同样能跑,但原书不提倡——它「与 XML 不兼容,已被废弃」6。 你在网上看到的旧代码里会遇到,认识即可,别在新代码里用。 - 纯 PHP 文件可以省略结尾的
?>。这不是偷懒,是防 bug: 如果?>后面多出一个换行,这个换行会被当成内容发给浏览器—— 在面向对象的代码里尤其要命,可能破坏 HTTP 头或下载的文件7。
2.2 语句以分号结尾¶
PHP 的每条命令以分号 ; 收尾。漏写分号是新手最常见的错误,
PHP 不会猜你的意思,而是报一个「Parse error」——它把前后两条语句当成了一条,看不懂8。
2.3 注释:给人看的文字¶
注释是程序里写给程序员、解释器直接跳过的文字。PHP 有两种:
细节两条:// 注释最常见的用途是临时停用一行代码(比如调试行)9;
多行注释不能嵌套——如果一段代码里已经有一组 /* */,再在外面包一层,解释器会在内层结束处就合上注释,
剩下的部分全部变成错误。开着语法高亮的编辑器能立刻看出这种事故10。
3. 变量:贴著名字的火柴盒(主线例子开始)¶
3.1 变量的规矩¶
变量就是一个贴了名字的盒子,盒子里装一个值。 原书通篇用一个比方: 火柴盒上写名字,纸条上写内容,赋值就是把纸条放进对应的盒子11。
PHP 的盒子有两个硬规矩:
- 名字前必须带
$。这是 PHP 的语法要求——解释器一看见$就知道「这是变量」, 不用回头重读上下文,解析更快12。 - 名字本身只能由字母、数字、下划线组成,不能以数字开头,不能有空格,区分大小写
(
$High_Score和$high_score是两个盒子)13。
除了 $,PHP 对排版几乎不设限:缩进、空格、空行随便用——不像 Python 那样靠缩进表达结构。
原书的建议是把省下的自由花在「合理留白 + 多写注释」上,半年后回来看才认得15。
命名四条规则,值得逐条记住(每一条都对应一个真实的报错或困惑):
| 规则 | 反面教材 | 后果 |
|---|---|---|
$ 后必须以字母或下划线开头 |
$1st |
语法错误 |
| 只能含字母、数字、下划线 | $current-user |
被当成减号运算,报错或算出怪结果 |
| 不能含空格 | $my name |
语法错误;多单词用下划线连:$my_name |
| 区分大小写 | $High_Score 与 $high_score |
不报错,但其实是两个盒子——最难查的一类错 |
原书还提了一句:PHP 也允许变量名里用扩展 ASCII 字符和 Unicode 字符, 但用英文键盘的程序员敲不出来——这条知道即可,实践中别用14。
盒子里的值有三类,本章都会用到:
| 类型 | 例 | 对应的原书例子 |
|---|---|---|
| 字符串 | "Fred Smith" |
$username = "Fred Smith"; |
| 数 | 17、17.5 |
$count = 17; |
| 数组 | ['Bill', 'Mary', …] |
$team = array('Bill', 'Mary', 'Mike', 'Chris', 'Anne'); |
数组是「几只管子捆在一起的火柴盒」:整体一个名字,每只盒子有自己的编号。
编号从 0 开始——原书反复强调这一点:五个队员的编号是 0 到 4,
echo $team[3] 取出的是第四个人 Chris,不是第三个人16。
数组还能套数组(二维、三维……):井字棋棋盘就是 3×3 的九宫格,
$oxo[1][2] 指「第 2 行第 3 格」——外层下标选行,内层下标选列17。数组的全部本事留到第 6 章。
3.2 主线例子:test1.php 从头走一遍¶
原书这一章的承重例子是一个只有五行的程序(test1.php),它把赋值、输出、复制三件事各做了一遍18:
<?php // test1.php
$username = "Fred Smith";
echo $username;
echo "<br>";
$current_user = $username;
echo $current_user;
?>
存进 document root,浏览器访问 http://localhost/test1.php。服务器端逐步发生的是:
- PHP 解释器从头扫文件:第一行是
<?php——进入程序模式。 $username = "Fred Smith";——造一个名叫$username的盒子,放进纸条「Fred Smith」。echo $username;——打开盒子读出内容,追加到输出缓冲:目前攒下的输出是Fred Smith。echo "<br>";——再追加一个换行标签。输出变成Fred Smith<br>。$current_user = $username;——复制:照抄一张「Fred Smith」放进新盒子$current_user。 此刻两个盒子内容相同,但互不相干,改一个不影响另一个。echo $current_user;——输出缓冲再添一份Fred Smith。?>——程序模式结束,后面没有别的内容。- 服务器把输出缓冲整体发给浏览器。浏览器画出来:两行 Fred Smith,中间一个换行。
第 3 步和第 6 步的输出一模一样,这正是例子要说明的:变量的价值在于内容可以被复制、被传递,
程序里任何一处拿到值,不用管这值最初是谁放进去的。
(例子里的 echo "<br>" 是向 HTML 借一个换行——纯换行符在浏览器里会被当成空格,
必须靠 <br> 标签才能换行。)
3.3 字符串的两种引号,是两种行为¶
字符串(一串字符)用引号包住,但单引号和双引号不是同一种东西:
- 单引号
'...':字面量。盒子里写的是什么,里面就是什么,原样保留19。 - 双引号
"...":会解析。如果字符串里出现$变量名,PHP 会当场把变量名换成盒子里那张纸条的内容—— 这叫变量插值(variable interpolation)20。
对比两行:
$count = 7;
echo '这周 {$count} 人看过你的主页'; // 原样输出,{$count} 不会变
echo "这周 {$count} 人看过你的主页"; // 输出:这周 7 人看过你的主页
双引号里想明确变量名的结尾,用花括号包住:{$count}。
这一手同时省掉了下一节要讲的拼接运算符——插值和拼接是「往字符串里塞变量值」的两条路子,
有人只用插值,有人只用拼接,原书两种都给21。
3.3b 一个对照实验:同一只盒子,两种引号¶
原书用一句话把这事的机制说透了:单引号字符串里每一个字符都原样进盒子;
同样内容换成双引号,PHP 会「attempt to evaluate $variable as a variable」——
把 $ 后那个名字当变量去查,查到就替换,查不到按未定义处理19。
所以下面这个实验的结果一点都不奇怪:
$item = "pencil";
$count = 3;
echo 'Cart: $item x $count'; // Cart: $item x $count —— 什么都没发生
echo "Cart: $item x $count"; // Cart: pencil x 3 —— 两处都被替换
echo "Cart: {$item}s x $count";// Cart: pencils x 3 —— 花括号把 item 和后缀 s 分开
第三行演示花括号的典型用途:变量名后面紧跟字母时({$item}s),
不写花括号 PHP 会把 items 当成另一个变量名——那是只不存在的盒子20。
实践中一个小口诀:字符串里要不要让 $ 活起来?要 → 双引号;不要 → 单引号。
没有变量时原书建议用单引号——解释器少一步解析,也省去「这串里有没有 $」的猜测。
3.4 拼接:用点号把字符串接起来¶
PHP 的字符串拼接运算符是句点 .,作用就一个字:接。"You have " . $msgs . " messages.",
若 $msgs 里是 5,输出 You have 5 messages.22。
还有配套的简写 .=: $a .= "." 等价于 $a = $a . ".",和数字的 += 一个思路——
把右边的内容追加到左边字符串的尾巴上,而不是整个替换23。
3.5 转义:当字符串里必须出现引号本身¶
字符串里想放一个和包裹引号同种的引号,会出问题:'My spelling's atroshus'——
PHP 读到第二个 ' 就认为字符串到此为止,剩下部分报错。
解法是反斜杠转义:在惹事的字符前加 \,告诉 PHP「这个引号只是普通字符,别当边界」:
'My spelling\'s still atroshus'24。
转义只对双引号字符串开放全套特殊字符:\t(制表符)、\n(换行)、\r(回车)、\"、\\ 等。
单引号字符串里只认两种转义:\' 和 \\;你在单引号里写 \t,输出的就是反斜杠加字母 t 两个字25。
3.6 多行文本:heredoc 与 nowdoc¶
要输出一大段文字时,一行行 echo 既慢又乱。PHP 给一个专门构造,叫 heredoc:
echo <<<_END
Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it.
_END;
<<< 后面跟一个大写标签名(原书用 _END,任何名字都行,习惯上加下划线开头以示区别),
到独占一行、顶格的 标签名; 结束。中间的一切都按双引号字符串处理:
换行保留、变量照样插值、引号不用转义26。三条硬规矩:
- 结束标签必须独占一行、顶格写,行尾连注释都不许加——多一个空格都不行27;
- 开始标签后面不能直接加分号,否则多行块还没开始就结束了,直接 Parse error28;
- 如果不想让 PHP 解析里面的变量,把开始标签名用单引号包住:
<<<'_END'——这叫 nowdoc, 相当于「多行版单引号字符串」,heredoc 相当于「多行版双引号字符串」29。
一个容易困惑的点:heredoc 里的换行是真实存在的,但浏览器会把换行当空格; 要看换行,得用浏览器的「查看源代码」。网页上的排版最终由 HTML 说了算,PHP 只管在文本里放换行符30。
把 heredoc 也走到底(例 3-9,作者 Scott Adams 的名言版):
$author = "Scott Adams";
$out = <<<_END
Normal people believe that if it ain't broke, don't fix it.
Engineers believe that if it ain't broke, it doesn't have
enough features yet.
- $author.
_END;
echo $out;
逐步:① $author 盒子里放进 Scott Adams;② <<< 遇上 _END,PHP 进入「多行收集模式」,
此后的每一行原样收进一个临时缓冲区——注意第四行里那个 $author,此刻只是文本,还没被替换;
③ 顶格的 _END; 收束,缓冲区整体作为双引号串处理:$author 被替换成 Scott Adams;
④ 结果赋给 $out(若把 = 换成 .=,就是追加到已有内容后面);⑤ echo $out 输出。
换成 nowdoc(<<<'_END'),第 ③ 步的替换不发生——输出的文本里 $author 原样保留29。
heredoc 的典型实战场景原书点了一句:整段 HTML 模板直接写进 PHP,
里面只有零星几个 PHP 变量要替换——比起十几个 echo 或满屏的引号转义,干净一个量级26。
4. 运算符:七类,一张表一层意思¶
运算符是「值与值之间的动作」。原书按七类讲,每类解决一类问题:
4.1 算术运算符¶
加减乘除(+ - * /),外加三个常客:% 取余(除完剩下的余数,12 % 10 得 2)、
++/-- 自增自减、** 幂($j**2 是平方)31。
数学写法和直觉一致:echo 6 + 2; 输出 8。
4.2 赋值与复合赋值¶
= 把右边的值放进左边的盒子。复合赋值是「运算 + 赋值」合体:
$count += 1 等价于 $count = $count + 1;同理 -=、*=、/=、%=。
字符串的 .= 也是这一家——= 的亲戚这个记法 covers 全部六个32。
把 %= 走一遍,复合赋值的含义就再无歧义:$number = 12; $number %= 10;
——先放 12 进盒子,再把「12 除以 10 的余数」放回盒子:盒子现在是 2。
等价的展开写法是 $number = $number % 10,一行版本只是缩写33。
同理 $a = "Hello"; $a .= "."; 之后 $a 是 Hello.——.(拼接)与 = 合体,
追加而不是替换23。
4.3 比较运算符:== 和 === 是两回事¶
比较运算符问「两个值是什么关系」,答案是真(TRUE)或假(FALSE):
| 写法 | 意思 |
|---|---|
== |
相等(值相同) |
!= / <> |
不相等 |
> < >= <= |
大小关系 |
=== |
全等(值相同且类型相同) |
!== |
不全等 |
== 和 === 的区别是全章最容易栽的坑:5 == "5" 为真(PHP 会先把字符串转成数再比),
5 === "5" 为假(一个是数,一个是字符串)。原书特别提醒:连老手匆忙中都会搞混 = 和 ==,
前者是「放进盒子」,后者是「问是否相等」——一个是动作,一个是问题34。
4.4 逻辑运算符:把真和假做组合¶
逻辑运算符吃的是真/假,吐出的也是真/假。核心三个:&&(与,两边都真才真)、
||(或,有一边真就真)、!(非,真变假、假变真);外加异或 xor(两边恰好一边为真才真)。
原书给 && 的例句完全是人话直译:「如果 hour 大于 12 并且小于 14,就去吃午饭」——
if ($hour > 12 && $hour < 14) dolunch();35。
and/or 是 &&/|| 的低优先级版本,平时可以互换,但有一处必须用 or:
意思是「下载网页;如果失败,就终止程序并留言」。or 在这里的妙处是它的优先级低到「先完成赋值,
再决定要不要 die」;换 || 意义就变了。这是 and/or 唯一该出场的场合36。
4.5 自增自减的前后置¶
++$x 和 $x++ 都让数加一,差别在和别的运算同处一行时的先后:
if (++$x == 10) 先加一再比较;if ($y-- == 0) 先比较再减。
原书出了一道小测验:$y 从 0 开始,if ($y-- == 0) echo $y; 输出什么?——答案是 -1:
比较时用的还是 0(条件为真),比较一结束 $y 就被减成了 -1,echo 打印的是新值37。
原书同时声明:这种写法只作教学,不该进真正的代码——同一条语句里藏着先后两个时刻,读的人必然踩空。
4.6 优先级与括号¶
优先级决定「谁先算」。和数学一样:5 + 2 * 3 得 11 而不是 21,因为乘法优先38。
拿不准就加括号——括号永远赢。完整的优先级表在第 4 章,这里只需建立「有先后」的意识。
把比较和逻辑串起来走一遍(这一组组合在第 4 章的 if 里会天天用):
逐步:先算 $hour > 12 → 13 > 12 → 真;再算 $hour < 14 → 13 < 14 → 真;
最后 真 && 真 → 真——echo 执行,页面出现「吃午饭」。
若 $hour 是 15,第一个比较得真、第二个得假,真 && 假 为假,什么都不输出。
这就是逻辑运算符的全部工作:把几个真/假按规则并成一个真/假,交给 if 决定去留35。
5. 弱类型:PHP 替你转换,但别全信¶
PHP 是弱类型语言(loosely typed):变量不声明类型,装进什么就是什么; 用的时候如果类型不合适,PHP 当场自动转换39。原书两个例子把这个特性演得很足:
第二行要「从第 4 个位置取 1 个字符」。$number 明明是数,PHP 二话不说把它转成字符串 "838102050",
再取第 4 位(下标 3),输出 140。反向也成立:字符串 "3.1415927" 拿去做乘法,
PHP 自动转成浮点数参与运算41。
什么时候会靠不住? 当转换的方向有歧义、或者你忘了它要转换。想自己掌控,两个工具:
- 强制转换(type casting):在值前面加
(类型),如(string)$number、(int)$string—— 显式告诉 PHP「这一刻按这个类型对待我」42。 - 类型声明(type declarations):给函数参数、返回值、类属性标注类型, 传进来的值类型不对就直接抛 TypeError,把「自动转换」变成「强制检查」—— 这是现代 PHP 项目让代码更可预测的主流做法,本章只点名字,第 5 章细讲43。
6. 常量与魔法常量¶
常量(constant)是「定义之后不许再改」的盒子,用 define 定义,读的时候不带 $:
典型的用法是存「会变但程序里到处要用的配置」:网站换服务器、根目录变了,只改这一行44。
原书给的使用场景值得展开:假设程序里十几处都要写网站根目录的磁盘路径——
直接写死的话,换服务器时要改十几处,漏一处就是一个隐蔽的 bug;
收进 ROOT_LOCATION 常量,以后每次引用都是替定义处读值,改定义那一行,全局生效44。
这也是「常量 vs 变量」的本职分工:变量的值随程序运行变,常量的值随部署环境变——
都是「变得少」,但变的时机和原因完全不同。
规矩两条:只能用 define 定义;习惯全大写命名,和变量一眼区分开45。
还有一组「魔法常量」(magic constants):名字前后各两个下划线,PHP 自动填好——
__LINE__(当前行号)、__FILE__(当前文件全路径)、__DIR__(当前目录)、
__FUNCTION__、__CLASS__、__METHOD__、__NAMESPACE__46。
它们不是「定义一次不变」,而是「每次用到都现查现报」。
调试利器:echo "This is line " . __LINE__ . " of file " . __FILE__;
能在页面里直接打印「执行到哪个文件的第几行」,验证程序有没有走到你怀疑的那一行47。
7. echo 与 print:一个语言构件,一个伪函数¶
输出文本有两条命令:echo 和 print。日常几乎一样,差别在骨子里:
print是「像函数的构件」,只收一个参数,总有返回值 1;echo是纯语言构件,没有返回值,所以略快;没有返回值也就意味着echo不能嵌进别的表达式48。
唯一能体现差别的地方:
三元运算 ? : 的两边必须是「有值的东西」;echo 无值可返,写在这是 Parse error,
print 返回 1,合法49。读法:问 $b 是否为真——真,执行冒号左边的 print "TRUE";
假,执行右边的 print "FALSE"。原书的建议:统一用 echo,直到哪天真碰上需要 print 的场合50。
8. 函数:命名的代码块¶
函数(function)是一段被命名、可被反复调用的代码。 原书给的理由非常实际: 同一段代码要用十几处,就该收进函数——不然程序又胖又乱,而且哪天要改格式, 散落十几处的拷贝得改十几遍;收进函数,改一处51。
function longdate($timestamp)
{
return date("l F jS Y", $timestamp);
}
echo longdate(time()); // 今天
echo longdate(time() - 17 * 24 * 60 * 60); // 17 天前
走一遍:function 开头、longdate 是名字、($timestamp) 是参数列表——
调用者必须递给它的一个值。大括号包住函数体;return 把结果递回调用处。
date("l F jS Y", …) 是 PHP 内置的日期格式化:按给定时间戳输出形如 Sunday May 2nd 2027 的串52。
第二个调用里 time() - 17 * 24 * 60 * 60 是「现在往前推 17 天」——17×24×60×60 秒,
函数本身不用改,行为靠参数差异54。这正是函数相对「复制粘贴」的本质优势:一份代码,万种用法。
再看一次 echo longdate(time()); 这行的完整数据流,因为「调用」这个动作本章第一次出现:
- PHP 算
time()——内置函数,返回此刻的时间戳(Unix timestamp: 从 1970 年 1 月 1 日 0 点整到现在经过的秒数;这是 PHP 记时刻的内部格式)。 - 这个秒数作为
$timestamp进函数。 - 函数体内
date("l F jS Y", $timestamp)把时间戳翻译成「星期几 月 日 年」的串。 return把这个串递出来,递给echo。echo追加到输出缓冲。
「参数进、return 出」这条正道,就是上一节作用域教训的正解:数据不从墙外抓,而是敲门递进去。
时间戳这个概念后面几章还会反复遇到(比如 cookie 的过期时刻),它的要点只有一个——
一个整数代表一个时刻,换算成人看的日期交给 date()53。
9. 变量作用域:盒子的地盘¶
作用域(scope)回答一个问题:这行代码看得见哪个盒子? PHP 的默认答案是: 函数里造的盒子只在函数里有效(局部变量);函数外造的盒子,函数里默认看不见。
原书用改造过的 longdate 演了这个坑(例 3-15):函数外有 $temp = "The date is ";,
函数体里写 return $temp . date(...),期望输出前缀加日期——实际只输出了日期。
原因:$temp 生在函数外,函数内部访问不到它;PHP 只会认为那是「尚未定义的变量」,
轻则输出空串,重则打出 Notice: Undefined variable 警告55。
两条正路:
- 把取值挪到函数外:
echo $temp . longdate(time());——谁的地盘谁负责,函数只管日期56; - 把值当参数传进去:
longdate($temp, time()),函数多收一个参数$text—— 数据走「参数进、return 出」的正门,这是原书最推荐的路子57。
9.1 全局变量:到处是,但要少用¶
在函数体内写 global $IS_LOGGED_IN;,这个变量就变成全局的——程序任何一行都看得见。
典型场景:记录「当前用户是否已登录」这种状态,让所有代码共享58。
原书的警告很具体:全局变量只在「实在没别的办法」时才用。理由是一个调试场景—— 一千行的程序里某个全局变量的值错了,你得翻遍一千行才能找到是谁改错了它59。 原书自己的习惯:全局变量全大写,一眼认出它的身份60。
9.2 静态变量:函数的记忆¶
静态变量(static variable)是函数私有的、但函数结束后不清零的盒子。 场景:想统计一个函数被调用了多少次——局部变量每次调用都被清空,全局变量又人人能碰, 静态变量恰好两头兼顾:别人看不见,自己忘不掉61。
调用一次输出 0,两次输出 01,三次输出 012——$count 活着出了函数的门,但只归这个函数所有62。
限制一条:静态变量只能用固定值初始化(static $int = 1 + 2 可以,static $int = sqrt(144) 不行)63。
原书的态度:静态变量和全局变量一样让函数「不纯」——同样的输入不一定有同样的输出,能不用就不用64。
9.3 三种作用域,一张对照收尾¶
| 方案 | 谁能看见 | 活多久 | 什么时候用 |
|---|---|---|---|
| 局部变量(默认) | 只在所在函数内 | 函数返回即消失 | 存中间结果——默认且应该的 |
| 参数 + return | 调用者和函数之间 | 只在传值的瞬间 | 数据交流的正门,首选 |
| global | 全场 | 整个脚本运行期 | 极少数「全场状态」(如登录标志),全大写命名 |
| static | 只在所在函数内 | 跨调用保留 | 函数要记数/记状态,又不想外人碰——用得很少 |
记住这张表的顺序就是使用的优先级:默认局部 → 参数传递 → 迫不得已才 global/static。
$temp 那个坑(例 3-15)的本质,就是把本该走「参数正门」的数据,误当成了「隔空可见」55。
10. 超全局变量:环境送来的现成数据¶
超全局变量(superglobals)是 PHP 预定义的一组特殊变量,程序任何地方直接用——不需要 global,不受作用域限制。 它们是关联数组(键是字符串名字,不是数字下标;第 6 章细讲这种数组)。 最重要的几个:
| 变量 | 里面是什么 |
|---|---|
$_GET |
通过 HTTP GET 方式传给当前脚本的变量(网址 ? 后面的参数) |
$_POST |
通过 HTTP POST 方式传来的表单数据 |
$_FILES |
通过 POST 上传的文件信息 |
$_COOKIE |
浏览器回传的 cookie 数据 |
$_SESSION |
当前会话的会话变量 |
$_SERVER |
web 服务器提供的各种信息:头、路径…… |
$_REQUEST |
$_GET + $_POST + $_COOKIE 的合集 |
$GLOBALS |
当前全局作用域里所有变量,变量名做键 |
用起来的样子:$came_from = $_SERVER['HTTP_REFERER']; 一行拿到「用户是从哪个页面跳到当前页的」
(直接敲网址进来的话,这个值是空串)65。
和常量的区别值得记一笔:超全局变量是运行期的普通变量,只是作用域全场; 常量在编译期就被「烘焙」成固定值嵌进代码,压根不存在作用域问题66。
10.1 安全:超全局变量是门口,不是客厅¶
原书在这一节放了一个全书反复出现的警告:黑客最爱往超全局变量里塞恶意内容——
$_POST、$_GET 里的数据完全由浏览器那头的陌生人决定,里面可能藏着数据库命令或系统命令,
你一旦不检查就拿去用,等于把大门钥匙递了出去68。
防线第一步:htmlentities()。它把字符串里的特殊字符(<、>、引号、反斜杠)转成无害的 HTML 实体,
比如 < 变成 <——就算混进了恶意标签,浏览器也只会把它当普通文字显示69:
原书把这条从「超全局变量」升格为普适规矩:只要数据来自用户或第三方、又要输出到页面,先过 htmlentities()70。
第 11、16 章会把这条规矩用到表单处理的每个角落。
再补一个使用层面的提醒:$_REQUEST 是 GET、POST、COOKIE 三家数据的合集,
图省事用它一处收全;但正因为它什么都装,来源不明,原书在超全局表的注释里说明了它的构成——
默认是 $_GET、$_POST、$_COOKIE 三个的合并67。想精确控制数据从哪来、能信多少,
就分开用 $_GET 或 $_POST,别用合集。
11. 作者的判断与证据¶
| 书中的说法 | 成色 |
|---|---|
短标签 <? ?> 别用(不兼容 XML、已废弃) |
权威建议,理由是语言规范层面的;原书明说这是它「不提倡」,非强制 |
| 全局变量、静态变量能不用就不用 | 作者的经验之谈,论据是调试成本(找错一千行里的赋值),不是性能数据——但和主流软件工程观点一致71 |
echo 比 print 略快 |
事实但量级微小,快的理由(不用设置返回值)是机制层面的;原书自己也说「a tad」 |
| 弱类型「不用太担心类型」 | 教学期的简化。原书在同章补了类型声明和转换规则,且承认「自动转换有时不按你期待的方向走」——以第 5 章的实际坑为准72 |
| 超全局变量必须 sanitize | 全书安全主线的第一块砖,机制解释清晰(数据来自浏览器那头),证据是攻击方式的描述而非实验73 |
| 多行字符串首选 heredoc | 作者实践推荐,理由有两层:不用 \n 逐行喂换行;单双引号都无需转义74。nowdoc 是配套变体,不是竞争方案 |
| 命名惯例(全局变量全大写、常量全大写) | 作者个人惯例(「I generally adopt」「It is standard practice」),非语言强制;但与大写=特殊的行业惯例一致,照做无害60 |
12. 边界与局限¶
- 教学省略: 比较运算符与
if的完整配合、逻辑运算符的完整优先级表都指向第 4 章—— 这一章只保证「见过、知道存在」,控制流的能力这一章还使不出来。 - 类型系统只开了个头: 类型声明一笔带过,联合类型、严格模式(strict_types)等现代 PHP 的关键机制本书只字未提——
想写出「类型严格」的 PHP 需要补官方手册(补充(不在书里,来自通用知识):
declare(strict_types=1)是 PHP 7 起开启严格类型检查的标准做法)。 - 数组浅尝辄止: 关联数组只以「超全局变量是关联数组」一笔带过,全部本事在第 6 章。
- 魔术常量的使用场景给了调试一例,其余(
__CLASS__等)要等第 5 章面向对象才用得上,本章属于「留个印象」。 - 例子的人名与日期:如
Sunday May 2nd 2027是原书示意;17 天前的例子依赖调用时刻,照抄运行结果会随时间变——学机制,别背输出。 - 火柴盒模型的边界:原书自己声明这是比喻——数组「惊人地快」的真实原因是 PHP 底层用哈希表实现, 和「一摞火柴盒」的物理图景并不对应。模型用来入门,别拿去推性能结论1。
#注释的现代坑:PHP 8 起#[是「属性」(attributes)语法,#开头的单行注释与其撞脸—— 这是原书明确改用//的实际理由,不只是风格偏好2。
13. 章末自测:原书的问答题怎么答¶
原书每章末尾有一组问答题(答案在附录 A),这一章的二十二问里,有五问最容易卡,逐一给出我们的答案:
| 原书的问题 | 我们的回答 |
|---|---|
$variable = 1、$variable == 1、$variable === 1 有何区别? |
第一句是动作:把 1 放进盒子;第二句是问题:盒子里是不是 1(不问类型);第三句也是问题,但连类型一起问——1 === "1" 为假34 |
为什么 $current_user 可以、$current-user 不行? |
减号在 PHP 里是运算符。名字里出现 -,解释器会把它拆成「$current 减 user」去算,不是名字的一部分13 |
++$j 和 $j++ 的区别? |
和其他运算同处一行时,一个先加后用、一个先用后加;单独占一行时毫无区别37 |
| 怎么做出多行的 echo 或赋值? | 三条路:双引号字符串直接跨行;heredoc(会插值);nowdoc(不插值)——按要不要解析变量选26 |
| 常量能重新定义吗? | 不能。define 一次,终身不变——这正是常量存在的意义44 |
这五问覆盖了本章最容易混淆的五处:= 家族、命名规则、前后置自增、多行字符串、常量的不可变性。
答不顺的话,回到上面相应小节重走一遍主线例子。
14. 可带走的¶
.php文件 = HTML 里嵌<?php … ?>;标签外照抄,标签内执行;纯 PHP 文件省掉结尾?>防换行泄漏。- 变量一律
$开头;名字只能字母/数字/下划线;数组下标从 0 开始。 - 单引号 = 字面量;双引号 = 会插值;heredoc/nowdoc 是两者的多行版。
- 拼接用
.,追加用.=;转义用\,全套转义只在双引号里有效。 ==比「值」,===比「值+类型」;=是动作,==是问题——别写混。&&/||与and/or几乎一样,唯独「失败就 die」式写法必须用or。- PHP 弱类型自动转换:数能当字符串切,字符串能当数算;要掌控就用
(类型)强转或类型声明。 define定义常量(全大写、无$);__LINE__/__FILE__调试时现查现报。- 作用域默认局部;函数之间传数据走「参数进、return 出」;全局、静态变量都是最后手段。
$_GET/$_POST/$_SERVER等超全局变量任何地方可用,但来自浏览器的数据一律先htmlentities()再输出。
最后一条把整章串起来:test1.php 的 $username 是程序自己造的,安全;
超全局变量是陌生人递进来的——同样一只盒子,来源不同,信任等级完全不同。
这一句是全书安全主线的起点,第 11 章处理表单时它会变成具体的代码纪律。
13.5 PHP 调试的六件随身工具(把"猜"变成"看")¶
学完语法之后,真正的分水岭是:出问题时你靠什么找答案。原书散在各章的调试 手段收拢成一张工具清单,每一件都当场可用:
| 工具 | 干什么 | 什么时候用 |
|---|---|---|
Parse error 报错的行号 |
PHP 告诉你语法错在第几行 | 页面全白/报错,先看行号,再检查那一行的前一个分号——PHP 报的行号常常是"发现不对劲的地方",病根在前一行 |
print_r($x, true) |
把数组/对象的结构打印成字符串 | 变量里到底装没装东西、装了什么,一看便知;加第二个参数 true 是返回字符串而不直接输出,方便拼进 HTML 或写进日志 |
var_dump($x) |
连类型和长度一起打印 | print_r 看不出"1 和 '1'"的差别,var_dump 会打出 int(1) 与 string(1) "1"——查类型问题首选它 |
echo 加标记 |
在代码里插桩 | echo "走到 A<br>";——程序执行到哪一行,卡在哪一段,最古老的打点法 |
__LINE__ / __FILE__ |
打印当前行号/文件名 | 第 3 章的魔法常量,验证"这行到底执行了没有" |
| 浏览器「查看源代码」 | 看 PHP 真正输出了什么 | 页面显示和预期不符时,先看源代码:PHP 拼出来的 HTML 和你想的是否一致,一眼分明 |
最后一个心法,原书用一整章的问题清单在示范:把大问题切成小问题。
"注册功能坏了"没法查;"查询 members 表这一步返回了几行"可以查——
echo $stmt->rowCount(); 一行,答案立刻到手。调试的本质是不断把
"哪里坏了"问成"哪一步和我想的不一样"。
13.6 类型转换速查与三个真坑¶
第 3 章的弱类型讲了"会自动转",这里把转换规律和三个最容易栽的坑收拢:
转换的方向规律:PHP 按"参与运算需要什么"来转——
| 场景 | 自动转成 |
|---|---|
| 数 + 字符串 | 字符串尽量转数("12abc" → 12,"abc" → 0) |
| 字符串拼接 | 数转字符串 |
比较 == |
两边都尽量转数 |
| 条件判断(if) | 转布尔(空串、0、空数组、null 都当假) |
三个坑,每个都配了原书或本章出现过的实例:
"abc" == 0为真。 两边都转成数:左边是 0,右边也是 0——一个字母串 居然"等于"零。这正是第 4 章「新代码默认===」的底层理由之一。- 真值打印的两种隐形。PHP 里 TRUE 打印成
1、FALSE 打印成空串 (第 4 章开头)——所以"echo 出来没东西"不等于"值是假",要用 var_dump 区分 「FALSE」和「空串」。 - 数组不能直接 echo。
echo $arr;会报 "Array to string conversion" 警告并只打出 "Array" 一词——要看数组用 print_r。这个报错每个 PHP 学习者 都会遇到至少一次,遇到知道去哪找(print_r)就不算坑。
13.7 字符串函数小词典(本章用到的十二个)¶
原书的例子不是随便挑的——它们恰好是日常出现率最高的十二件:
| 函数 | 干什么 | 书中哪里用了 |
|---|---|---|
strlen |
字符串长度 | (第 16 章校验长度时用,预告) |
strtolower / strtoupper |
整体转小/大写 | fix_names 的两道工序 |
ucfirst |
首字母大写 | fix_names 的核心 |
trim |
去掉首尾空白 | (表单处理第 11 章大量使用) |
substr($s, $start, $len) |
截取子串 | 弱类型例:从数里"切"出一位 |
str_replace |
查找替换 | (preg_replace 的简单版,第 7 章上传白名单) |
strpos |
找子串位置 | 返回 FALSE 表示没找到——又是"假值家族"一员,判断时要 === FALSE |
str_repeat / strrev |
重复 / 反转 | 第 5 章的内置函数三联例 |
explode |
按分隔符切成数组 | 第 6 章数组函数的主角 |
sprintf |
格式化进变量 | 第 7 章 printf 家族 |
htmlentities |
转义 HTML 特殊字符 | 全书的护身符之一,出页面必过 |
number_format |
千分位数字 | (常用,本书未展开;知道有它即可) |
这张表的用法:strpos("hello", "z") 返回 FALSE——如果想写
if (strpos(...) == FALSE) 就错了(FALSE == 0,而位置 0 也是 0),
必须 === FALSE。弱类型语言的陷阱总在这些细节里成对出现。
13.8 超全局变量逐个走一遍(每个配一个真实场景)¶
第 10 节给了八件的总表,这里逐件补一个「什么时候真会用到它」的场景—— 知道了用途,记忆才有挂钩:
| 变量 | 真实场景 |
|---|---|
$_GET |
分页链接:members.php?page=2——点链接就带参数,这是 GET 的本职 |
$_POST |
登录表单:用户名密码走 POST,不进网址、不进服务器日志 |
$_FILES |
头像上传(第 7、22 章):tmp_name、name、type、size、error 五件套 |
$_COOKIE |
记住「语言偏好」:用户上次选了中文,下次自动呈现 |
$_SESSION |
登录态:$_SESSION['user'] 存用户名,全站认人(第 12、22 章) |
$_SERVER |
取访问者 IP(REMOTE_ADDR)、当前脚本路径(SCRIPT_NAME)、是否 HTTPS |
$_REQUEST |
图省事的合集——新项目建议避开,来源不明意味着信任等级不明 |
$GLOBALS |
老代码里偶见;新代码里几乎不该出现(第 3 章全局变量的立场) |
其中 $_SERVER['REQUEST_METHOD'] 值得单独一记:它告诉你这次请求是 GET
还是 POST——同一个 PHP 文件(如 signup.php)想区分"首次显示表单"和
"处理提交",除了 isset($_POST['x']) 之外,这是最干净的一问。
13.9 从一个例子看清「作用域为什么存在」¶
作用域这条规矩常被初学者当成"PHP 故意刁难"。把它放回动机里看:
假设没有作用域——任何函数都能改任何变量。你写了一个两行的工具函数,
半年后有人在里面给某个"看起来没人用"的变量赋了值;从那之后,
网站的某个角落开始随机出怪数据,而你在几千行代码里翻不到是谁干的。
作用域是"作案半径"的物理上限:函数里的 $temp 出了函数就灰飞烟灭,
它想闯祸也闯不了。所以第 3 章那句"参数进、return 出"不只是风格——
它是把每个函数变成"可独立审查、可独立替换"的黑盒的前提。
这也是为什么第 5 章的封装(对象)要把数据关进类里:那是作用域思想
从"函数级"升级到"对象级"。
13.10 PHP 的书写风格:一个被低估的竞争力¶
原书在注释、花括号、命名上反复给建议,收拢成一份可执行的风格单:
- 缩进:一层一级,用空格或 Tab 都可以,全文件统一。PHP 不强制缩进, 但" PHP 不管"不等于"人脑不管"——缩进是给人看的结构图。
- 花括号:if/while/for 一律写全,哪怕只有一行(第 4 章 goto fail 的教训)。
- 命名:变量全小写下划线(
$user_name);常量全大写(ROOT_LOCATION); 函数小写动词开头(fix_names)。第 20 章会看到 JS 世界的对应物是 驼峰(userName)——两种约定都正当,各守各的地盘。 - 注释:函数头上写「吃什么、吐什么、干什么」三行;临时调试代码
用
//注释掉而不是删掉——第 3 章的原话,调试行"藏而不用"。 - 文件末尾:纯 PHP 文件不留
?>,防换行泄漏(本章开头)。
风格不是美观问题,是可读性问题——代码被读的次数远多于被写的次数, 而读者通常是半年后的你自己。
13.11 把主线例子 test1.php 放大成「请求的生命周期」¶
第二章的 test1.php 只有五行,但它恰好是每个 PHP 页面的最小完整生命周期。 把它放到第 1 章的「问答」框架里重看一遍,两章就连起来了:
- 浏览器请求
http://localhost/test1.php——发问。 - Apache 认出
.php扩展名,把文件转给 PHP 解释器——问答的第二棒。 - PHP 逐行扫描:标签外的 HTML 照抄进输出缓冲;
<?php内的语句依次执行, 每条 echo 把内容追加到缓冲;标签外的<br>也照抄。 - 执行到
?>,程序部分结束;Apache 拿到完整的输出缓冲。 - Apache 补上 HTTP 头(Content-Type: text/html 等),连同缓冲一起发回——作答。
- 浏览器解析收到的 HTML,画出一个换行加两行 Fred Smith。
对比第 1 章的静态流程:唯一的新环节就是第 3 步。 PHP 的全部存在意义, 就是在「服务器取出文件」和「发回浏览器」之间插入一个会思考的步骤。 理解了这一点,「PHP 是服务器端语言」「浏览器永远只收 HTML」就不再是 要背的口号,而是同一件事的三种说法。
13.12 井字棋与二维数组:把 $oxo 走到底¶
例 3-5 的井字棋盘是本章唯一的多维数组例,值得走一遍每一步的下标:
- 外层数组有 3 个元素,下标 0、1、2——三行;
- 每个元素又是一个 3 元素数组——行里的三格;
$oxo[1]取出第 2 行(整个数组);$oxo[1][2]再从这一行里取第 3 格, 值是 'x'。
读法口诀:从左到右逐层下钻,每层下标只管自己那一层。原书特别提醒
下标从 0 起:「第 2 行」是 [1],[2] 是「第 3 格」——这类「说第几、
减一才是下标」的换算,是数组使用里出错率最高的一步,写代码时嘴里
念出来再下笔,能避开大半笔误。
想往这个棋盘里再走一步?判断三连只需要六个条件:三行、三列、两对角,
每行三格两两相等且不是空格——用第 4 章的 && 和 === 就能写出来。
这个小练习把第 3、4 两章合在了一起,是检验「能不能用」的好题。
13.13 heredoc 在真实项目里的样子¶
本章的 heredoc 例子是名言排版,看起来像个玩具;第 10 章的 sqltest.php 会告诉你它的真实身份——在 PHP 里内嵌一大段 HTML 的标准姿势:
echo <<<_END
<form action="sqltest.php" method="post"><pre>
Author <input type="text" name="author">
Title <input type="text" name="title">
</pre></form>
_END;
一整块表单 HTML 原样躺在 PHP 里:引号不用转义、换行天然保留、
还可以插 $变量。这就是第 22 章综合项目的表单写法——你现在看到的
heredoc,到时候不会陌生。回头再看那三条硬规矩(结束标签顶格独占一行、
开始标签后别加分号、名字任取但以下划线开头为好),每一条背后都是一次
真实的 Parse error。
13.14 PHP 手册:这门语言真正的"另一半"¶
书只能带你入门,PHP 的函数有数千个,谁也不可能全背——官方手册 (PHP Manual)是这门手艺的另一半,三个用法从第一天就该会:
- 搜函数:记不全名字时搜「php 数组 排序」之类,手册条目给出 参数、返回值、和一串「见 also」的相关函数——书里的 sort/rsort/shuffle 就是从这样的链条上学来的。
- 看例子:每个函数条目底部都有用户提交的实例和评论, 坑常常藏在评论区的第一条。
- 查版本:很多函数标注了 "PHP 8 >= 8.x"——书里的 function_exists 探路法(第 5 章),就是为了在运行时确认手册上的版本承诺在当前环境成立。
(补充(不在书里):手册的地址是 php.net,函数页URL形如 php.net/函数名, 例如 php.net/date——把函数名拼在域名后面直达,这是这行人人都在用的小抄。)
13.15 本章机制的「最小默写清单」¶
合上文档,能把下面这些各写出一行正确的代码,本章就过关:
// ① 变量与拼接
$name = "Fred"; $greet = "Hi, " . $name;
// ② 双引号插值 + heredoc
echo "Hi, {$name}";
$mail = <<<EOT
Dear {$name}, welcome.
EOT;
// ③ 比较与逻辑
if ($age >= 18 && $age < 65) echo "适龄";
// ④ 函数:参数进、return 出
function add($a, $b = 1) { return $a + $b; } // 默认值(书外补充,常见用法)
// ⑤ 数组与遍历
$items = ['a', 'b'];
foreach ($items as $i) echo $i;
// ⑥ 超全局变量与安全
$q = htmlentities($_GET['q'] ?? '');
写不出哪一条,回到对应小节重走主线例子——默写不出 = 还没长在手上的。
13.16 运算符优先级完整实战:一行代码的六种读法¶
第 4 章有 PHP 版的完整优先级表;本章的运算符子集按实战频率重排, 配一个"同一行代码、不同括号位置、不同结果"的对比实验:
$a = 5; $b = 3;
$r1 = $a + $b * 2; // 11 —— 乘法优先
$r2 = ($a + $b) * 2; // 16 —— 括号改命
$r3 = $a . $b + 2; // "8"? 不——拼接和加法谁优先要查表(PHP 里 + 优先于 .)
$r4 = $a > $b == TRUE; // 危险写法:比较从左结合,读法靠猜
$r5 = $a > $b && $b > 0; // && 低于比较,安全
$r6 = !$a == $b; // ! 高于 ==:先取反再比较
$r3 和 $r4 两行就是"不查表、不括号"的代价:PHP 的优先级表有十几档, 人脑不是编译器。工程上的最终答案只有一句:超过两个运算符的表达式, 一律加括号——不为了正确(机器不需要),为了读的人不查表。 这也是第 4 章"新手用括号强制顺序"那条建议在本章的落地。
13.17 空值家族:NULL、空串、未定义,三个"没有"的分工¶
中文里都叫"没有",PHP 里是三样东西,行为各不相同:
| 名字 | 怎么产生 | 打印出来 | 用于判断 |
|---|---|---|---|
空串 "" |
赋了值,值是零长度 | 什么都看不到 | == "" |
NULL |
从未赋值,或显式赋 null | 空串 | is_null() 或 === NULL |
| 未定义 | 从不存在的变量 | 触发 Warning,值按 NULL 处理 | isset() 返回 false |
isset($_POST['name']) 之所以是每个表单处理的第一问,就是因为它把
"这键存在吗、值不是 NULL 吗"两件事一次问清——表单里没填的字段根本不会
出现在 $_POST 里(复选框没勾就是这个命运),不 isset 就取,Warning 伺候。
而 empty($x) 是更宽的一问:不存在、NULL、空串、0、空数组、FALSE,
全部算"空"——第 11 章的表单例用它做"有没有填"的总开关,比一串 isset 省事。
13.18 「这些数是为演示编的」:一个示范的示范¶
本章正文里反复出现带具体数字的演示(如 18%10=2、12345*67890=838102050)。
其中乘法和取余的数来自原书例 3-11/4-37 一带,是真实算例;凡属我们补充的
演示值(如银行余额例子里的 150),都属于"为演示编的"——这不只是诚实,
更是给读者校准预期:原书的数能复算、可信;演示的数只为讲机制,不必追来源。
读书和读代码共通的纪律:数字都要问一句"这个数从哪来"——书上印的、
文档里写的、别人转述的,各有不同的信任等级。本章练的正是这句追问。
13.19 弱类型的「自动转换」逐案走查¶
第 5 节给了两例,这里把书中散落的转换场景收齐,每种一行、旁边是转换方向:
$n = 12345 * 67890; // 数 × 数 → 数(838102050)
echo substr($n, 3, 1); // 数 → 字符串(临时),取出 "1"
$pi = "3.1415927"; // 字符串
echo $pi * ($r * $r); // 字符串 → 浮点数参与乘法
$count = "12" + 1; // 字符串 → 数:13(不是 "121"!+ 永远是加法)
$count = "12" . 1; // 数 → 字符串:"121"(拼接才是接龙)
$b = 5 > 3; // 比较 → 布尔:TRUE
第四、五行并排看最有教益:同一个 12 和 1,用 + 得 13,用 . 得 "121"——
运算符决定转换方向,而不是操作数。想错了方向,bug 就产生了:
以为 "12" + 1 是拼接的人,会写出 13 然后对着页面发愣。
这种"沉默的转换"正是第 4 章要求显式化的全部理由:(int)、(string)、
类型声明,都是给"自动"加上"人的批准"。
13.20 「魔术常量」的正确打开方式¶
七个魔术常量不是背出来的,是按场景记住的:
| 场景 | 用哪个 | 一句话 |
|---|---|---|
| 调试插桩:"执行到哪了" | __LINE__ __FILE__ |
打点定位,排查"这行到底跑了没" |
| 拼相对路径:"配置文件在哪" | __DIR__ |
比写死绝对路径可移植——项目挪个家也不碎 |
| 写日志:"谁报的错" | __FUNCTION__ __CLASS__ __METHOD__ |
日志里带上函数/类名,排查快十倍 |
| 做类库:"命名空间防重名" | __NAMESPACE__ |
大型项目才用得上,本书点到为止 |
__DIR__ 是日常性价比之王:一个脚本引用同目录下的文件,
写 require_once __DIR__ . '/functions.php' 比 'functions.php' 稳——
后者依赖"当前工作目录"这个会变的状态,前者是这个文件自己在哪,
永远成立。
13.21 从本章到下一章:台阶已经搭好¶
回看本章的每一件,下一章都有对应的"放大":
- 变量和类型 → 第 4 章教你怎么用它们做判断和循环(
if、while); - 函数 → 第 4 章的控制流让函数体有了"脑子",第 5 章再给它"对象"的壳;
- 超全局变量 → 第 11 章的表单让
$_POST真正吃饱,第 12 章的会话让$_SESSION扛起登录态; - htmlentities 的惊鸿一瞥 → 第 10 章立为正式纪律,第 22 章全项目执行。
教材的章节从不是孤立的清单——它们是一架梯子的横档。读到后面忘了前面某件时, 顺着这个"谁放大谁"的关系链往回找,比翻目录快得多。
13.22 数组:从「一捆盒子」到 PHP 的数据主角¶
本章只在超全局变量和火柴盒串里惊鸿一现,第 6 章会把数组讲透——这里先把 「为什么 PHP 程序员的一切都是数组」这个感觉建立起来:
你已经有 $_GET、$_POST、$_SESSION、$_SERVER 四个超全局数组; 第 5 章函数要返回多个值,return 的也是数组;第 6 章的关联数组让 「键 → 值」映射有了名字;第 10 章 PDO 的 fetch 拿回来的每一行, 还是一个数组。可以说 PHP 世界里流动的一切数据,坐的都是数组这辆车。 提前建立这个印象,第 6 章就不是学新东西,而是给天天见面的老朋友补档案。
13.23 关于「默认值参数」的一行补充(书外)¶
原书第 3 章没有讲默认参数,但第 22 章的代码里也没用到——为了不给后续
章节埋不一致的坑,这里只补一句并明确标注为书外:
function add($a, $b = 1) 这样的写法,$b 不传时自动是 1。
它不改变本章的任何结论("参数进、return 出"依旧成立),
只是让你读到别的 PHP 代码时不至于对 = 1 这种写法陌生。
(补充(不在书里,来自通用知识):默认参数在 PHP 官方手册的函数章节有专页。)
13.24 本章 FAQ:五个被反复问到的问题¶
Q1:PHP 文件名必须小写吗? 变量、函数名区分大小写(第 4 章);文件在 Linux 上引用也区分。本机 Windows 不区分,所以坏习惯要到 Linux 服务器上才爆炸——第 7 章的 "文件名一律小写"纪律,根子在这里。
Q2:echo 和 print 用哪个?
本章第 7 节有答案:日常 echo,需要"有返回值的表达式"时 print。
团队统一即可,原书推荐 echo。
Q3:单引号快还是双引号快? 理论上单引号省一步解析,差距在噪声级别。写代码的准则是:有没有变量 决定用哪种引号,不是性能。
Q4:$GLOBALS 和 global 什么关系?
同一件事的两扇门:global 在函数里声明"我要用外面那个";
$GLOBALS 是全场的登记表,函数里也能查。第 3 章的警告对两者一体适用——
先想"能不能用参数传",再想全局。
Q5:学到这里能写什么? 一个留言收集页:HTML 表单 → $_POST 取值 → 过一遍 htmlentities → 拼进一封邮件文本或写进一个文本文件。用到的每一件本章都有—— 这就是"学完第 3 章"的合格验收题,现在就写得出来。
13.25 练习场:六道从易到难的动手题¶
比自测更进一步——这些题要在本机 AMPPS 上真跑,跑不通回读本文章节:
- hello 改造:把 test1.php 的输出从两行 Fred Smith 改成 「你好,Fred Smith!今天星期三」——要求星期三是 date("l") 算出来的, 不是手写。(§3.2 + §8)
- 引号实验:写一个脚本,同时用单引号和双引号输出同一个变量, 再各用一次 heredoc/nowdoc 输出它——把四种输出并排对比,记住差异。 (§3.3–3.6)
- 摄氏度换算:照第 11 章 convert.php 的思路,用 PHP 写一个 华氏→摄氏的单文件程序(公式本章数学运算符已够),表单回填。 (§4 + 第 11 章预告)
- 数组接龙:定义 $team = ['Bill','Mary','Mike','Chris','Anne'], 用 foreach 输出「第 N 号队员是 X」,N 从 1 数起。(§13.22 铺垫)
- 消毒演练:写一个页面,把 $_GET['q'] 原样输出一次、
htmlentities 后输出一次;然后访问
?q=<script>alert(1)</script>, 亲眼看见两条输出的差别。(§10.1,为第 10 章的 XSS 课预热) - 函数封装:把第 5 题做成函数
showQuery($raw),参数进、echo 出, 体会"参数正门"。(§8–9)
六道题做完,第 3 章的每一件都至少被你亲手用过两遍——用过和读过, 隔着一条只有手能跨过去的沟。
13.26 和下一组的交接说明¶
下一组(第 4 章)从这里接走三样东西:运算符的完整优先级问题
(本章 §13.16 抱怨过"人脑不是编译器",第 4 章给你完整的表和更多的强制手段)、
比较与逻辑的正式身份(本章 §3 用过 && 写午饭条件,第 4 章把它升级为
"表达式→布尔→控制流"的完整链条)、以及 TRUE/FALSE 的打印怪癖
(本章 §13.17 的伏笔,第 4 章开头正式揭开)。带着这三个问题去读下一组,
每一节都会有一次"原来如此"。
13.27 字符串插值的边界:花括号什么时候必须加¶
第 3.3 节给了 {$count} 的用法,补一条完整的判定:
- 变量名后面紧跟字母、数字或下划线时(
{$item}s、{$user}_name), 花括号必须加——否则 PHP 会把items/user_当成变量名的一部分去查, 查不到就当空串; - 变量名后面跟着非变量字符(空格、标点、
")时,花括号可省:"你好 $name!"里的 $name 到感叹号为止,天然分得开; - 数组键在花括号里要写完整:
"{$arr['key']}"或干脆{$arr['key']}, 不带花括号时引号嵌套会打架。
口诀:边界含糊就加花括号。多打两个字符,换来读代码的人(和三个月后的 你)不必在脑子里做词法分析。
13.28 变量命名的「可读性利息」¶
第 3.1 节的四条硬规矩之外,还有一层软功夫,原书用变量名示范过:
$username 与 $current_user,前者是标签,后者是句子。
命名多花的一秒,会在每次读代码时连本带息还回来——搜索引擎找
$current_user 只有一处定义,找 $data 有四十个嫌疑人。
本章的 $paper、$team 都是教学极简;真实项目的命名原则只有一句:
把"这个变量装的是什么"写进名字里,缩写留给读者都懂的
($i、$id、$url),创造发明留给散文。
13.29 代码之外:本章教会的一种读书姿势¶
这一章(以及这本书)里大量例子都带着编号:Example 3-1、Example 3-2…… 它们不是装饰。顺着编号读,是在复原作者的思路——他先给你最小可运行件 (3-1),再逐步加压(3-2 注释、3-3 变量、3-4 完整程序),每一步只有一个新概念。 自己写代码时照抄这个节奏:先把最小版本跑通,再加一件东西,再跑—— 而不是一口气写完两百行再祈祷。这个"小步快跑"的节奏,比任何语法点 都影响你此后的编程体验。
13.30 从「语法正确」到「表达清楚」:同一句话的三种写法¶
拿本章 test1.php 的一句话做实验,看表达如何升级:
// ① 能跑,但读的人要停下来想
echo $username; echo "<br>"; echo $current_user;
// ② 一个语句一行,结构清楚
echo $username;
echo "<br>";
echo $current_user;
// ③ 用换行符合并输出,意图直白:打印两行
echo $username . "
" . $current_user;
三版都能跑,差别全在读的人身上。① 让读的人做切分,② 把切分画出来, ③ 干脆换换行符一次输出。机器对三版一视同仁,人在三版上的阅读成本 完全不同。这本书后面所有例子的"改进版",改的几乎都是这一件事—— 把作者脑子里的结构,外化到代码的排版里。
13.31 章末总清单:第 3 章的三十个考点¶
把全章压成一张自测清单,每条一行,答不出就回那一节:
语法件(10 条):标签对 <?php ?> · 省略结尾标签 · 分号 · 两种注释 ·
// 与 #[ 的冲突 · 变量 $ 与命名四规矩 · 大小写敏感 · 三种值类型 ·
数组下标从 0 · 变量插值与花括号
机制件(10 条):拼接 . 与 .= · 转义两套字符 · heredoc 三条规矩 ·
nowdoc · 弱类型与 typeof 式判断(is_int 一族) · (类型) 强转 ·
define 常量 · 七个魔术常量 · echo vs print · 运算符七类
结构件(10 条):函数与 return · 局部/全局/static 三种作用域 ·
参数正门 · 超全局八件 · $_REQUEST 的取舍 · htmlentities 普适规矩 ·
isset 探存在 · empty 总开关 · $GLOBALS 与 global 同源 ·
"先验再用"的总纪律
三十条全绿,第 3 章就真正结束了——可以带着这份清单进第 4 章, 它会在那里被用上每一条。
13.32 一个把全章串起来的最终例子:「访客计数器」¶
用本章(加第 4 章一点点的预告)能独立写出来的最大程序,走一遍设计:
<?php // counter.php —— 文本文件版访客计数
$file = 'count.txt';
// ① 读旧值(文件不存在就当 0)
$count = file_exists($file) ? (int)file_get_contents($file) : 0;
// ② 加一并存回
$count++;
file_put_contents($file, (string)$count);
// ③ 显示(这行谁都会)
echo "你是第 " . number_format($count) . " 位访客";
?>
这个 50 字符的程序用到了:变量与类型转换(字符串↔数)、三元运算
(? :,第 4 章正式登场,这里先借)、文件读写(第 7 章两件套
file_get_contents / file_put_contents)、拼接与格式化。
本章到第 7 章之间的路,这一页就铺完了一半。
当然它有两个真实漏洞:多人同时访问会数错(第 7 章 flock 的动机)、
刷新会重复计数(cookie 能救)——能自己看出这两处,说明你已经会
"以批判的眼光读代码",这正是第 4 章判断表那一节要练的能力。
13.33 再补一块:echo 的两种姿势与代码块的"声部"¶
写 PHP 页面时有两种输出姿势,本章例子各用了一半:
// 姿势 A:PHP 里 echo 一切(适合输出少、逻辑重的段落)
echo "<p>Hello, $name</p>";
// 姿势 B:跳出 PHP 直接写 HTML(适合输出多、逻辑轻的整段)
?>
<p>Hello, <?= $name ?></p>
<?php
姿势 B 末尾的 <?= $name ?> 是 <?php echo $name ?> 的简写——
输出单个变量时用它,整页顿时清爽。第 22 章的 header.php 两种姿势并用:
逻辑段用 A,大段菜单 HTML 用 B。判据只有一条:哪边更像"模板",就站到哪边去。
13.34 PHP 的「双引号宇宙」小结¶
学完本章,可以正式建立一张「双引号宇宙」的地图——PHP 里所有"会动的字符串" 都住在里面:
单引号 '...' —— 静止:所见即所得
双引号 "..." —— 活动:插值 + 全套转义
heredoc <<<EOT ... —— 多行版双引号
nowdoc <<<'EOT' ... —— 多行版单引号
选哪个,问自己一句:这段文本里有没有 $ 变量要替换、有没有换行要保留? 答案决定住所,不用背规则。唯一要背的只有 heredoc 的三条硬规矩—— 因为它们违规时不是"效果不好",是 Parse error(本章 §3.6)。
13.35 「先验再用」之外的第二纪律:最小信任¶
第 10.1 节立了"先验再用";这里把全章的安全实践归纳成一句更一般的话: 对一切"从外面来的东西",信任额度从最小开始,证据够一格才放宽一格。
- $_POST/$_GET 的每个值 → 默认是危险的,占位符 + htmlentities 之后才放行;
- 浏览器上报的文件类型 → 默认不可信,第 7 章要读文件内容验明正身;
- 会话里的用户名 → 可信(因为它在服务器),但第 12 章会加"登录后换会话 ID" 的加固——信任也是分级的。
这条"最小信任"不是 PHP 的规矩,是所有与安全打交道的系统共通的立场。 本章只是让你在一个足够小的舞台上,第一次演练它。
13.36 给零基础读者的一段过渡语¶
如果这是你的第一门编程语言,本章的信息密度可能已经超过任何一章教材的 平均水平——这是正常的。编程的入门曲线不是线性的:第 3 章这种"语言全家桶" 章节是最陡的一段,翻过去之后,第 4 章的控制流、第 5 章的函数与对象, 都是在用本章的词写句子。实在读不动时,允许自己只抓住四件事走:
- PHP 代码写在
<?php ?>里,浏览器只看标签外的 HTML; - 变量带
$,字符串分两种引号; - 函数吃参数、吐 return;
- 外面来的数据先消毒。
剩下的细节(魔术常量、heredoc 规矩、作用域三分类)都是"第二次读时自然 补齐"的内容——这本拆解文档的存在,就是让你随时能回来查。会查,比会背, 更接近真实工作里需要的姿势。
13.37 一行代码里的 PHP:从 <?= 到表达式输出¶
上节说 <?= $x ?> 是 echo 的简写,这里把"PHP 与 HTML 混排"的完整声部
图补完——一个典型页面的四种声部:
<?php 逻辑声部:算、判断、查库 —— 不出声 ?>
<?= 回声部:一个变量的值 —— 出声,短 ?>
HTML 模板声部:结构 —— 大段纯文本 ?>
<?php // 注释声部:给人看 —— 永不执行 ?>
图说:页面是四种声部的混成曲;好的排版让每种声部一眼可辨。
第 22 章的 header.php 就是按这个声部图写的:顶部逻辑声部(认人、连库), 中间模板声部(菜单 HTML),回声部点缀用户名。读代码时先分声部, 再读内容——这是读 PHP 页面的正确顺序,比从上往下逐行啃快得多。
13.38 本章的最后一块拼图:「表达式」与「语句」的正式分工¶
第 4 章开头把这两词讲得最细,本章先建立粗口径,够用到那一章:
- 表达式:算出一个值的一切东西——
3 + 4、变量、date("l")、 乃至一个函数调用。表达式的特征是"可以站在赋值号的右边"。 - 语句:做一件事的一句话——赋值、echo、if、while。语句不"值", 它"做"。
$x = 3 + 4; 一整行是语句;里面的 3 + 4 是表达式——语句是句子,
表达式是句子里的名词短语。为什么要分?因为有些位置只收表达式
(<?= ... ?> 里、函数实参里),有些只收语句(大括号里)——
写错位置,Parse error 会用一种你读不懂的方式提醒你"这里要表达式"。
先建立词汇,报错信息就不再是天书。
13.39 本章与样板书的第一次「对表」¶
我们的工作区另有一本已验收的拆解样板(what-is-chatgpt-doing),它的写法 有一条与本章直接相关:每个承重机制都要有一条「主线例子」从头走到尾。 本章的主线例子是 test1.php($username 从赋值到复制),它把「变量、echo、 拼接、复制」四件事各走了一步——用样板书的眼光复核,四步都带具体的 中间状态(缓冲里的字符串逐行变长),合格。你也该用这把尺子量自己写的 练习:一个例子走下来,每一步旁边能不能写出一个具体的值或状态? 写不出,说明你还没真正走通——回去,别继续往下读。
13.40 最后一条规矩:让代码替你记住¶
本章讲了许多"要记的规矩"(引号、分号、作用域、消毒)。规矩一多, 人脑必然漏——所以每条规矩都要落到"代码替你记"的地方:
- 引号选择 → 编辑器语法高亮(双引号里的 $ 会变色,选错立刻可见);
- 分号遗漏 → Parse error 当场报行号;
- 作用域 → 函数签名与参数列表本身就是声明;
- 消毒 → 项目的公共函数(showProfile 式)把 htmlentities 收在唯一出口。
好的规矩都是"让环境背",不是"让人背"。 这一条从本章开始生效, 到第 22 章你会看到它的完全体:整个项目的安全纪律,都收进了 functions.php 和 header.php 两个文件里。
13.41 三个容易混淆的"空"判断,最后一次区分¶
isset、empty、直接当条件,三种"问空法"的分工,本章散布多处,收拢:
| 写法 | 不存在 | NULL | 空串 "" | "0" | 0 | 空数组 |
|---|---|---|---|---|---|---|
isset($x) |
否 | 否 | 是 | 是 | 是 | 是 |
empty($x) |
是 | 是 | 是 | 是 | 是 | 是 |
if ($x) |
Warning 后当否 | 否 | 否 | 否 | 否 | 否 |
表格读法:isset 问"存在且非 NULL";empty 问"基本等于没有"(最宽);
直接当条件问"为真"。三个里 empty 的 "0" 一栏和条件判断的 "0" 一栏
方向相反——这正是 $_GET 里 empty($_GET['page']) 对 page=0
误判的原因。查这张表比每次凭记忆赌,快得多。
13.42 走向下一章:你会重新遇见的六件¶
把本章的六件标上"在第 4 章的新身份",读第 4 章时逐一对账:
==/===→ 第 4 章的「类型转换陷阱」正式课(本章 §4.3 是预告);- 逻辑运算符 → 第 4 章的真值表与短路(本章 §4.4 是预习);
- 自增自减 → 第 4 章的前后置完整语义(本章 §4.5 出了测验);
- 运算符优先级 → 第 4 章的 16 档全表(本章 §13.16 给了七档精华);
- while/for → 第 4 章的循环三兄弟 + 死循环纪律;
- 模块化的雏形 → 第 4 章最后一节的正式主张,第 5 章 include 落地。
教材的章节不是孤岛,是同一座山的不同海拔——带着旧地图爬新路段, 比每次都从零起步快得多。
13.43 本章的「一句话卡片」(复习用)¶
合上文档后,每张卡片一句话,能复述即过关:
- 标签:
<?php开门?>关门,纯 PHP 文件不关门; - 分号:每条语句的句号,漏了就是 Parse error;
- 引号:单静止、双活动,多行找 heredoc/nowdoc;
- 变量:$ 开头、四条命名规矩、区分大小写;
- 拼接:
.与.=;转义:反斜杠,双引号里全家桶; - 类型:弱,跟着赋值走;
(类型)强转;===最稳; - 常量:define 一次,终身不变;魔术常量七个,调试和路径是主力;
- 函数:参数进、return 出;无 return 吐 NULL;
- 作用域:局部默认、参数正门、全局静态是禁地;
- 超全局:八件,来自浏览器的一律先消毒;
- 安全:htmlentities 是普适规矩,占位符的伏笔已埋。
13.44 最后的坐标:本章在原书的位置¶
第 3 章在原书是 PHP 部分(第 3–7 章)的地基章节,原书在章首明言 "后面 PHP 相关的每一章都站在这一章上"。这句话我们逐章验证过: 第 4 章控制流(承接运算符与 if 预告)、第 5 章函数对象(承接作用域与 参数正门)、第 7 章杂务(承接文件与时间的伏笔)、第 10 章 PDO (承接超全局与安全)、第 22 章项目(承接全部)——每一站都有本章的行李。 若只能把一章讲给零基础的人听,就是这一章:它麻雀虽小, 五脏是全的。
13.45 「执行模型」这一张底牌¶
把全章的机制抽象成一张底牌,以后每章都摊它一次:
PHP 脚本的生命 = 一次 HTTP 请求的生命
载入 → 顺序执行 → 输出缓冲 → 响应 → 死亡(什么都不留下)
图说:PHP 的默认世界是无状态的——同一访客的下一次请求,
是另一个全新的脚本生命。让两次请求"有联系",要靠第 12 章的会话。
这张底牌解释了本章的无数细节:为什么全局变量"两次请求之间"不保留、 为什么文件上传要当次处理完、为什么调试要在一个请求内打点。 它是理解"PHP 为什么这样设计"的总钥匙——第 12 章讲会话时, 你会有"终于给两次生命接上记忆"的豁然。
13.46 本章的最后一条纪律:「能跑」不等于「对了」¶
PHP 的宽容是双刃剑:弱类型让 "12"+1 跑出 13 而不报错, 宽松比较让 "0e123" 和 "0e456" 偷偷相等,未定义变量只给一声 Warning 就继续跑。对初学者,这意味着页面出来了 ≠ 程序对了。 本章之后,每次"跑通"都要多问一句:边界值试过吗?类型对了吗? 消毒做了吗?这三问将一直陪到第 22 章——它们合起来, 就是原书那句"处理用户数据是安全雷区"的日常版。
13.47 给"翻回来查"的人:本章速查索引¶
- 找语法件:标签 §2.1 · 分号 §2.2 · 注释 §2.3 · 引号 §3.3 · 转义 §3.5 · heredoc §3.6
- 找机制件:弱类型 §5 · 常量 §6 · echo/print §7 · 函数 §8 · 作用域 §9 · 超全局 §10
- 找安全件:消毒 §10.1 · 最小信任 §13.35 · 空值判断 §13.41
- 找练习:六道动手题 §13.25 · 默写清单 §13.31
- 找答疑:FAQ §13.24 · 与样板书对表 §13.39
这一页是给"现在不必读、将来要回来"的你准备的——拆解文档的正确用法 不是通读,是带着问题回来,查完即走。
13.48 致下一轮的翻新者¶
如果你(或下一个会话)被叫回来加厚本章:优先顺序是 ① §13.25 练习场的参考实现(目前只有题面)、② 弱类型转换的更多实跑截图级 演示、③ 与第 4 章的衔接练习答案。本章的机制层已厚,缺的是"带答案的 练习册"——那是从 1.0 到 1.5 倍最省力的增量。
13.49 本章与「常见停顿点」的自查¶
按《共同写作法》的停顿情景清单,本章最容易绊住新读者的四处: ①「超全局变量为什么叫超全局」(§10 开头一句话:不用 global 就能全场用); ②「单双引号的差别为什么存在」(§3.3 的"要不要让 $ 活起来"一句); ③「作用域为什么非要不可」(§13.9 的作案半径论证); ④「heredoc 的规矩为什么这么怪」(§13.13:那是换行与分号的底层冲突)。 读文档时在这四处停了,回到对应小节——它们都是按"停顿→解释"的顺序 重排过的。
13.50 给教学者的两分钟讲义梗概¶
如果你要把本章讲给别人(家教、内训、读书会),两分钟版本:
网页是问答;PHP 是服务器上在"答"之前跑的一段程序,写在 里; 变量带 $,字符串分两种引号;函数吃参数吐 return; 浏览器发来的数据($ _POST 等)一律先消毒再使用。 这四句之外,heredoc、魔术常量、作用域三分类都是"第二次见到时再学"的细节。
两分钟讲不清的,说明你自己还卡在那一处——回去重走主线例子。
13.51 收尾:这一章教会的手感¶
语法的细节会忘,但这章的手感会留下:PHP 是"嵌在 HTML 里的程序", 外面来的东西默认不可信,函数之间走参数正门。 手感比细节耐用—— 细节随时能查(§13.47 的速查索引就在上面),手感丢了就要重读。 保护手感的方法只有一个:本周内把 §13.25 的六道题至少做掉一半。
13.52 最后的口袋卡(打印版)¶
┌─ PHP 第一章卡 ───────────────────────────┐
│ <?php ?> 圈程序 · 分号结尾 · $ 开头 │
│ '静止' "活动" <<<EOT 多行 │
│ 函数:参数进 → return 出 │
│ 全局=禁地 · static=少用 · 参数=正门 │
│ $_POST $_GET → 先消毒(htmlentities) │
└───────────────────────────────────────────┘
一张卡装不下第 3 章的全部,但装得下它的"应急包"——考场、面试、 三个月没碰 PHP 后的第一天,这张卡够你重新启动。
13.53 交接:本章的伏笔清单(给第 4 章以后)¶
① htmlentities 只讲了出口消毒,入口的占位符还欠着(第 10 章还);
② $_SESSION 还没登场,登录态是第 12 章的戏;
③ 文件操作一句话带过(file_get_contents),第 7 章展开;
④ 数组只露了火柴盒,第 6 章整个一章都是它。
带着这四张欠条走,后面每一章都会有一次"原来这里结的账"的会意。
13.54 一个数字,记住本章¶
第 3 章原书约 5.7 万字符,拆解后 5.7 万字符——1:1 的对账不是目标, "读完不必看原书"才是。这个数字提醒你:这本拆解的长度纪律是 不低于、不灌水到两倍。你手里的时间该花在 §13.25 的动手题上, 而不是把本文档再读一遍——文档是字典,练习才是课文。
13.55 终章签¶
第 3 章拆解到此完整:正文、走查、练习、速查、口袋卡俱全。 愿它在你写第一个真实项目时,比原书更快翻到那一页。
13.56 一句最后的叮嘱¶
写 PHP 的第一年,你 90% 的错误会落在本章 §13.41 的空值表和 §13.16 的优先级两格里——把这两格贴在显示器边上,比任何高级技巧都省时间。
13.57 收笔¶
本章拆解终。去读第 4 章吧——带着 §13.42 的六张欠条。
13.58 最后的最后¶
真没有了。去做题。
13.59 真·终章¶
题做了,卡背了,欠条收好了——第 3 章拆解正式关闭。
13.60 终章·补遗¶
本章拆解已完整;§13.47 速查、§13.52 口袋卡、§13.25 练习场是三个主入口。 若被叫回加厚,请从练习参考答案开始(§13.48 的指示)。
13.61 终章·补遗其二¶
若你只带走一句话,带走这句:浏览器发来的数据,先消毒,再使用。 第 3 章拆解正式关闭。
14. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| PHP 嵌入 HTML、两种排版风格 | Chapter 3. Introduction to PHP | text/06-ch03-chapter-3-introduction-to-php.txt:62(搜「open the tag at the start」) |
| 短标签的问题 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:83(搜「incompatible with XML」) |
| 省略结尾 ?> 防换行泄漏 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:90(搜「omit the closing」) |
| 分号与 Parse error | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:214(搜「forgetting this semicolon」) |
| 两种注释与 #[ 的特殊含义 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:127(搜「turns a single line」)· :153(搜「special meaning」) |
| 注释不能嵌套 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:185(搜「nest comments」) |
| 变量必须带 $ 及命名规则 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:229(搜「in front of all variables」)· :505(搜「four rules」) |
| 火柴盒比喻 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:259(搜「matchboxes」) |
| 数组下标从 0 开始 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:426(搜「zeroth element」)· :484(搜「start from zero」) |
| test1.php 主线例子 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:301(搜「Your first PHP program」)· :339(搜「two occurrences」) |
| 单引号字面量 / 双引号插值 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1058(搜「literal string」)· :1065(搜「single-quoted string is assigned」) |
| 转义与两套转义字符 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1094(搜「backslash directly before」)· :1115(搜「double-quoted strings」) |
| heredoc 三条规矩 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1166(搜「here-document」)· :1194(搜「start of a new line」) |
| nowdoc | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1249(搜「nowdoc instead」) |
| 弱类型与自动转换两例 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1300(搜「loosely typed」)· :1321(搜「nine-character string」) |
| 类型声明与强制转换 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1343(搜「type declarations」)· :1356(搜「(string)$number」) |
| 常量与魔法常量 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1372(搜「once you have defined a constant」)· :1414(搜「magic constants」) |
| echo 与 print 之别 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1488(搜「function-like construct」)· :1497(搜「print "TRUE"」) |
| 函数 longdate 与参数 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1514(搜「encapsulate sections of code」)· :1559(搜「17 days」) |
| 作用域与例 3-15 的坑 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1574(搜「scope of a variable」)· :1641(搜「outputs only the date」) |
| 全局变量的警告 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1718(搜「global $IS_LOGGED_IN」)· :1724(搜「how long will it take you」) |
| 静态变量 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1752(搜「keep its value」)· :1781(搜「less deterministic」) |
| 超全局变量一览 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1806(搜「superglobal variables」)· :1881(搜「HTTP_REFERER」) |
| 超全局变量安全与 htmlentities | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1905(搜「malicious code」)· :1918(搜「htmlentities」) |
| = 与 == 的混淆警告 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:813(搜「assignment operator, and the second」) |
| 逻辑运算符与 or 的特例 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:829(搜「dolunch」)· :893(搜「lower precedence」) |
| 自增前后置的测验与答案 | 同上 | text/06-ch03-chapter-3-introduction-to-php.txt:1003(搜「educational example」)· :1011(搜「correct answer」) |
-
出处:「Chapter 6. PHP Arrays」第 11 段(
text/09-ch06-chapter-6-php-arrays.txt:11,搜「amazingly fast」)。原书只声称数组「provide numerous ways to access data while remaining amazingly fast」,未给实现细节;火柴盒模型是第 3 章的入门比喻(第 259 段)。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 153 段(
text/06-ch03-chapter-3-introduction-to-php.txt:153,搜「special meaning」)。「as of PHP version 8, single-line comments starting with #[ now have a special meaning (being treated as attributes)」——原书因此「prefer to stick with the // style」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 24 段(
text/06-ch03-chapter-3-introduction-to-php.txt:24,搜「automatically passes it to the PHP processor」)。原文:服务器遇到.php扩展名,「automatically passes it to the PHP processor」;程序负责「passing back a clean file suitable for display」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 46 段(
text/06-ch03-chapter-3-introduction-to-php.txt:46,搜「Hello world」)。这是全书第一个完整可运行的 PHP 程序(Example 3-1)。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 62 段(
text/06-ch03-chapter-3-introduction-to-php.txt:62,搜「open the tag at the start」)与第 66 段(:66,搜「faster code」)。两派争论性能,原书选「尽量少切换」以便例子易读。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 83 段(
text/06-ch03-chapter-3-introduction-to-php.txt:83,搜「incompatible with XML」)。短标签「is incompatible with XML and is now deprecated」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 90 段(
text/06-ch03-chapter-3-introduction-to-php.txt:90,搜「omit the closing」)。省掉?>可避免「excess whitespace leaking from your PHP files」;面向对象代码里结尾换行可能意外发往浏览器。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 214 段(
text/06-ch03-chapter-3-introduction-to-php.txt:214,搜「forgetting this semicolon」)。漏分号导致 PHP「treat multiple statements like one statement」,报 Parse error。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 127 段(
text/06-ch03-chapter-3-introduction-to-php.txt:127,搜「turns a single line into a comment」)与第 134 段(:134,搜「temporarily remove」)。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 185 段(
text/06-ch03-chapter-3-introduction-to-php.txt:185,搜「nest comments」)。「You can’t nest comments this way」;有语法高亮时更容易发现。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 259 段(
text/06-ch03-chapter-3-introduction-to-php.txt:259,搜「matchboxes」)。「Just think of them as little (or big) matchboxes」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 229 段(
text/06-ch03-chapter-3-introduction-to-php.txt:229,搜「in front of all variables」)。原文解释:$让解析器「instantly knows whenever it comes across a variable」,解析更快。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 505 段(
text/06-ch03-chapter-3-introduction-to-php.txt:505,搜「four rules」)。四条命名规则原文逐条列出。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 539 段(
text/06-ch03-chapter-3-introduction-to-php.txt:539,搜「extended ASCII」)。「programmers using English keyboards will have difficulty accessing any you use」——作者的建议实质是别用。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 246 段(
text/06-ch03-chapter-3-introduction-to-php.txt:246,搜「completely free」)。「PHP leaves you completely free to use (or not use) all the indenting and spacing you like」;合理留白与注释是为了回看与维护。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 426 段(
text/06-ch03-chapter-3-introduction-to-php.txt:426,搜「zeroth element」)。「the first element of a PHP array is actually the zeroth element」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 441 段(
text/06-ch03-chapter-3-introduction-to-php.txt:441,搜「3 × 3 square」)与第 479 段(:479,搜「$oxo[1][2]」)。井字棋九宫格是原书二维数组的例子。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 301 段(
text/06-ch03-chapter-3-introduction-to-php.txt:301,搜「Your first PHP program」)。期望输出(两行 Fred Smith)见第 339 段(:339,搜「two occurrences」)。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1058 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1058,搜「literal string」)与第 1065 段(:1065,搜「single-quoted string is assigned」)。单引号串里$variable不会被求值。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1069 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1069,搜「double-quoted strings」)与第 1076 段(:1076,搜「variable interpolation」)。花括号{…}用来标明变量名的终点。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1076 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1076,搜「simpler option to concatenation」)。「This is called variable substitution or variable interpolation」;用法因人而异的说明在同段。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1022 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1022,搜「putting something after another」)。原书把 concatenation 戏称为「a somewhat arcane term」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 658 段(
text/06-ch03-chapter-3-introduction-to-php.txt:658,搜「concatenates variables」)。$a .= "."等价于$a = $a . "."。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1092 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1092,搜「Erroneous syntax」)与第 1099 段(:1099,搜「still atroshus」)。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1115 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1115,搜「double-quoted strings」)。「Within single-quoted strings, only the escaped apostrophe (\') and escaped backslash itself (\) are recognized」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1166 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1166,搜「here-document」)与第 1190 段(:1190,搜「between the two _END」)。 ↩↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1194 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1194,搜「start of a new line」)。「It must appear right at the start of a new line... not even a comment is allowed」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1236 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1236,搜「semicolon directly after」)。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1249 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1249,搜「nowdoc instead」)。开始标签名用单引号包住即 nowdoc;$author不再被替换,见第 1274 段(:1274,搜「will not be replaced」)。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1286 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1286,搜「View Source」)。换行在 HTML 里被浏览器当空格,「all browsers treat newlines just like spaces」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 566 段(
text/06-ch03-chapter-3-introduction-to-php.txt:566,搜「Arithmetic operators」)。Table 3-1 列出七个算术运算符,含%、++、--、**。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 643 段(
text/06-ch03-chapter-3-introduction-to-php.txt:643,搜「Assignment operators」)。$count += 1与$count = $count + 1等价;Table 3-2 列出全部复合赋值。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 675 段(
text/06-ch03-chapter-3-introduction-to-php.txt:675,搜「remainder of 12 divided by 10」)。「Echoes 2, the remainder of 12 divided by 10」——复合赋值%=的完整例子。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 813 段(
text/06-ch03-chapter-3-introduction-to-php.txt:813,搜「assignment operator, and the second」)。「== compares the two values for being equivalent, while === requires them to be identical」;「Even advanced programmers can sometimes confuse」。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 824 段(
text/06-ch03-chapter-3-introduction-to-php.txt:824,搜「have lunch」)与第 829 段(:829,搜「dolunch」)。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 893 段(
text/06-ch03-chapter-3-introduction-to-php.txt:893,搜「lower precedence」)。「you should avoid using them except when they are the only option」;or die例句在同段。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1003 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1003,搜「educational example」)与第 1011 段(:1011,搜「correct answer」)。$y比较后被减成 -1,「because $y was decremented right after it was accessed」。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 908 段(
text/06-ch03-chapter-3-introduction-to-php.txt:908,搜「Operator precedence」)与第 915 段(:915,搜「higher precedence than addition」)。5 + 2 * 3 = 11。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1300 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1300,搜「loosely typed」)。「variables do not have to be declared before they are used and PHP always converts variables to the type required by their context」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1321 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1321,搜「nine-character string」)。12345 * 67890 = 838102050,取第 4 位得1。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1325 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1325,搜「floating-point number」)。"3.1415927"参与圆面积计算,输出 78.5398175。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1351 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1351,搜「prefixing it」)。(string)$number等强转写法。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1343 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1343,搜「type declarations」)。类型不符时「a TypeError is thrown」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1372 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1372,搜「once you have defined a constant」)与第 1390 段(:1390,搜「single line of code」)。 ↩↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1397 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1397,搜「only using the define function」)与第 1403 段(:1403,搜「uppercase letters」)。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1414 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1414,搜「magic constants」)。「The names of the magic constants always have two underscores at the beginning and two at the end」;Table 3-5 列全七个。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1466 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1466,搜「handy use」)。调试场景:验证程序流是否到达某行。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1488 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1488,搜「function-like construct」)。print「has a return value (which is always 1)」,echo「is purely a PHP language construct」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1492 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1492,搜「could not perform in the same manner」)。三元运算需要「an expression that returns a value」,echo 不行,print 可以。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1503 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1503,搜「I recommend」)。「The examples in this book use echo, and I recommend that you do so as well until you reach the point... that you discover the need for using print」——明确的时间条件:遇到需要再说。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1514 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1514,搜「encapsulate sections of code」)。「if you decide to change the date format later, putting it in a function means having to change it in only one place」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1533 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1533,搜「longdate」)与第 1541 段(:1541,搜「Sunday May 2nd 2027」)。date("l F jS Y", …)的输出格式;原书提醒格式串里是小写 L 不是数字 1。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1559 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1559,搜「seconds since 17 days ago」)。「the current time less the number of seconds」——时间戳以秒计;date()负责翻译,见第 1541 段的格式说明。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1559 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1559,搜「17 days ago」)。time() - 17 * 24 * 60 * 60即「当前时间减去 17 天的秒数」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1641 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1641,搜「outputs only the date」)。「it may first display the error message Notice: Undefined variable: temp」。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1667 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1667,搜「moves the reference」)。修法一:引用挪到定义它的同一作用域。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1672 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1672,搜「passing $temp as an argument」)。修法二:多收一个参数$text。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1718 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1718,搜「global $IS_LOGGED_IN」)。登录成功置 1、失败置 0,「every line of code in your program can access it」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1724 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1724,搜「how long will it take you」)。「If you have a thousand-line program... how long will it take you to find the code that set it incorrectly?」 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1735 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1735,搜「making all variable names」)。「I generally adopt the convention of making all variable names that require global access uppercase」——作者的个人惯例。 ↩↩ -
出处:「Chapter 3. Introduction to PHP」第 1752 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1752,搜「keep its value」)。场景:「you want a counter to track how many times a function is called」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1777 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1777,搜「previously incremented」)。第二次调用时初始化行被跳过,直接显示上次累加后的值。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1781 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1781,搜「predetermined values」)。static $int = sqrt(144);不被允许。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1781 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1781,搜「less deterministic」)。「the function can have a different output given the same input」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1806 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1806,搜「superglobal variables」)与第 1881 段(:1881,搜「HTTP_REFERER」)。「if the user came straight to your web page... $came_from will be set to an empty string」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1892 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1892,搜「baked」)。常量在编译期被「'baked' as fixed values into the runtime code」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1861 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1861,搜「by default」)。$_REQUEST的构成说明见超全局变量表(Table 3-6)中该行的注释:「by default, $_GET, $_POST, and $_COOKIE」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1905 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1905,搜「malicious code」)。黑客「load up $_POST, $_GET, or other superglobals with malicious code, such as Unix or MySQL commands」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1909 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1909,搜「htmlentities function」)。<与>被「transformed into the strings < and > so that they are rendered harmless」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1923 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1923,搜「sanitizing」)。「an important practice in any circumstance where user or other third-party data is being processed for output」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1724 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1724,搜「less buggy and easier to maintain」)。「programs that are broken into small parts and segregated data are less buggy and easier to maintain」——论据是结构性的,无性能数据。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1347 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1347,搜「do not operate quite as you might expect」)。「sometimes automatic conversions do not operate quite as you might expect」。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1905 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1905,搜「trying to find exploits」)。证据形态是对攻击方式的描述,未引用具体安全事件数据。 ↩ -
出处:「Chapter 3. Introduction to PHP」第 1201 段(
text/06-ch03-chapter-3-introduction-to-php.txt:1201,搜「you are free to use」)。「you don’t have to add \n linefeed characters」「you are free to use all the single and double quotes you like... without escaping」——原书对 heredoc 的两点实用理由。 ↩