PHP 函数、对象与数组¶
这一章讲什么: 三件让 PHP 程序从小工具变成真系统的东西—— 函数的高级用法(怎么从函数里送出多个值、怎么把函数分文件存放)、 对象(把数据和操作数据的函数捆成一个整体,这是全书后面例子的组织方式)、 数组(一次管一批数据,后面所有动态页面都要靠它装查询结果)。 读之前需要本组 02、03 的内容。
1. 顶层全景:三个层次的复用¶
① 函数:复用「一段代码」 function fix_names(...) { ... }
② 对象:复用「代码 + 数据的整套配法」 class User { 属性; 方法; }
③ 数组:复用「变量的排法」 array('Copier', 'Inkjet', ...)
图说:三者都是「起名字、再使用」。函数打包动作,对象打包「东西 + 对东西的动作」,数组打包一群值。
这三样是同一个思想的三个刻度:给一段结构起名字,之后按名字使用。 函数是最小刻度;对象把「数据和对数据的操作」封成一个自足的单元; 数组则是最朴素的一维——把一群变量编上号、给个总名。PHP 程序员的日常, 就是把网页上的事拆成函数,把函数和它们操作的数据包进对象, 把从数据库取来的行装进数组。下面按原书的顺序,先函数、再对象、再数组。
2. 函数的高级用法(原书第 5 章前半)¶
2.1 内置函数与调用规则¶
PHP 自带几百个内置函数,按名字调用。两个调用规则要记住:
- 括号是「这是函数」的信号。写
date('l')是调函数;写不带括号的date, PHP 会以为你在引用一个常量或变量3。 - 参数个数因函数而异:
date('l')收一个参数;phpinfo()一个都不要;str_repeat('Hip ', 2)收两个4。想不起要几个参数,这是查文档的信号,不是猜的信号。
echo strrev(' .dlrow olleH'); // Hello world. —— 反转字符串
echo str_repeat('Hip ', 2); // Hip Hip —— 重复两次
echo strtoupper('hooray!'); // HOORAY! —— 转大写
phpinfo() 顺带一条安全警告:它打印当前 PHP 安装的详尽信息——版本、路径、配置——
排错时极有用,但这些信息对攻击者同样有用。生产代码里绝不能留下 phpinfo() 调用5。
2.2 定义与返回:fix_names 主线例子¶
函数定义的骨架原书给的版本是:
function fix_names($n1, $n2, $n3)
{
$n1 = ucfirst(strtolower($n1));
$n2 = ucfirst(strtolower($n2));
$n3 = ucfirst(strtolower($n3));
return $n1 . ' ' . $n2 . ' ' . $n3;
}
echo fix_names('WILLIAM', 'henry', 'gatES'); // William Henry Gates
这个例子值得逐层剥,因为它把「函数内的求值顺序」教得很干净:
- 函数名不区分大小写(
PRINT、Print、PrInT是同一个),但代码风格要统一6; return的语义是「交出值 + 立刻离开函数」——后面再有语句也不会执行7;- 核心是嵌套调用
ucfirst(strtolower($n1)):PHP 求值从内向外—— 先把'WILLIAM'交给strtolower得'william',再把结果交给ucfirst得'William'8。 原书用print(abs(5-8))把这个顺序拆成三步演示:先算5-8得 -3,再abs得 3,最后转字符串输出9。 为什么必须先 strtolower 再 ucfirst?反过来,'WILLIAM'的 ucfirst 还是'WILLIAM', 大写还在——函数的组合顺序就是程序设计的全部内容,这个例子是它的最小演示10。
这个函数解决的场景也很真实:用户填表时常开着 Caps Lock、大小写乱输, 入库前统一成「首字母大写」是几乎每个网站都要做的清洗11。
2.3 一个函数怎么送出多个值¶
函数只有一个 return,但经常需要送出好几个结果。原书给了三条路,按推荐程度排:
路一(推荐):装进数组 return。 return array($n1, $n2, $n3);——
调用端 $names = fix_names(...) 后,$names[0]、$names[1]、$names[2] 各取所需。
好处:三个名字各自独立,想按名或按姓取用都不用从拼好的长字符串里切12。
路二(强烈不推荐):global。 在函数里 global $a1; $a1 = ucfirst(strtolower($a1));,
直接改函数外的全局变量。原书的理由排得很足:这会给函数引入从外面看不见的副作用——
调用 fix_names() 前后,外面的世界悄悄变了;函数因此变得难测试、变量难安全改名13。
你会在老代码里见到这种写法;新代码别学。
路三补充:什么都不 return,只 echo。 这不算「送出值」,只是当场输出—— 函数变成了「输出器」,拿到的数据没法再加工。路一(返回数据、让调用者决定怎么用) 几乎在所有场合都优于当场输出。
2.4 include 与 require:函数分文件存放¶
函数库多了,自然要分文件。PHP 提供两对四个命令,差别只有一条: 文件找不到时,include 报个警告继续跑;require 直接致命错误、程序终止。 所以「这文件必须有,缺了就完蛋」用 require;「有最好,没有也能凑合」用 include14。
include "library.php"; // 可能重复引入
include_once "library.php"; // 已引入过就跳过
require_once "library.php"; // 必须有,且只引入一次
重复引入是真正的坑:万一两个被引入的文件各自又引入了 library.php,
同一文件就被装了两遍——函数和常量被定义两次,直接报错。
include_once/require_once 靠绝对路径比对记住「这个文件来过了」,第二次起自动忽略。
原书的日常建议:无脑用 include_once;需要 require 的场合,用 require_once15。
include 的效果等同「把那个文件的内容原样贴进当前位置」——理解这句话,
include 的所有行为(变量作用域延续、HTML 和 PHP 混排)都能推出来16。
2.5 function_exists 与 polyfill:跨版本兼容¶
PHP 有多个版本在并存运行,新函数老版本上没有。function_exists("array_combine")
在运行时查「这个函数存在吗」——存在就走新函数,不存在就自己造一个等效的(这叫 polyfill,垫片)17。
代价:自造的版本通常比内置的慢,但代码因此能在更多版本上跑。这是整本书第一次出现的「兼容性思维」:
用特性前先探路,探不到再降级。
3. 对象:把函数和数据捆在一起(原书第 5 章后半)¶
3.1 为什么要对象:从函数到类¶
函数的下一步很自然:既然能复用代码,能不能把「代码 + 这份代码操作的数据」一起复用? 对象(object)就是这个「一起」。 原书的场景:社交网站要管理用户——注册、改资料、查登录状态。 面向对象的写法是:造一个 User 类(class,类 = 制造对象的图纸),里面装好用户相关的 全部变量和函数;之后每需要一个用户,就照图纸造一个新对象,把名字、密码递给它18。
原书用点唱机打比方,这个比方值得整个接住:点唱机里转盘上的唱片是它的「数据」, 面板按钮是「操作数据的方法」,投币口是「激活它」的方法,激光头是「取出数据」的方法—— 一个自足的单元:数据和操作数据的方式封在同一个机箱里19。
术语一表(本章到处要用):
| 术语 | 意思 | 点唱机版 |
|---|---|---|
| 类(class) | 图纸/模板 | 点唱机这个产品型号 |
| 对象/实例(instance) | 照图纸造出的一个实物 | 墙角那台具体的点唱机 |
| 属性(property) | 对象的数据 | 机里的唱片 |
| 方法(method) | 对象的函数 | 面板按钮、投币口 |
3.2 封装:interface 与「机箱」¶
封装(encapsulation)是面向对象的第一原则:对象的数据只许通过它的方法来碰,外部代码不许直接伸手进机箱。 方法的总称叫对象的接口(interface)——想用这个对象,只能通过面板上这几个按钮20。
原书给的两条红利,都源于封装:其一,调试圈定了范围——数据出错,只查一个类; 其二,升级可以整台换——只要新类提供同样的接口(按钮位置和含义不变), 造好、测好,直接换进旧类的位置;不行就换回来,网站立刻恢复21。 注意这个「可替换」承诺的前提正是封装:外部没人直接摸过机箱里的唱片, 换机器才不会碰坏别人的手。
3.3 定义、创建、使用:User 类走一遍¶
class User
{
public $name, $password;
function save_user()
{
echo "Save User code goes here";
}
}
$object = new User; // 照图纸造一台
$object->name = "Joe"; // 设置属性
$object->password = "mypass";
$object->save_user(); // 调用方法
三个语法点,原书都专门叮嘱过:
new关键字负责造实例;造的时候可以传参(给构造函数,马上讲)22。- 访问语法是
->,不是$:$object->name。要是误写成$object->$name, PHP 会把$name当变量先求值(比如得"brown"),再去访问$object->brown—— 属性名变成一个意外的值,错得没边。->后面的名字前面不带$23。 - 类和方法的定义位置很自由——放在使用之前或之后都行;但工程习惯是集中放文件尾部,
或干脆独立文件(配合
include_once)24。
调试工具再收一件:print_r($object) 把对象的属性结构打印成人可读的格式
(_r 即 readable),造对象前后各打印一次,属性变没变一目了然25。
3.4 克隆:对象赋值是「牵线」,不是「复印」¶
本章最容易踩的坑,原书专门用两个例子对撞:
$object1 = new User();
$object1->name = "Alice";
$object2 = $object1; // 以为复制了?
$object2->name = "Amy";
echo $object1->name; // 输出 Amy —— object1 的也被改了!
对象赋值复制的是「引线的另一头」,不是对象本身:两个变量牵着同一个对象,
改哪个都等于改它。对比本组 02 的变量复制(纸条复印一份进新盒子)——那是按值复制;
对象永远是按引用传。要真复印一台,用 clone:
$object2 = clone $object1; // 新实例,属性值照抄
$object2->name = "Amy";
echo $object1->name; // Alice —— 各自独立
clone 造一个新实例,把原实例的属性值逐个拷过去26。
「牵线」的比喻原书给的原版是:火柴盒上拴着几根线,扯任何一根都能摸到同一只盒子27。
3.5 构造与析构:出生和临终¶
构造函数(constructor)是造对象时自动执行的方法,负责初始化——
__construct 是它的固定名字(双下划线开头的方法名是 PHP 保留的,别自己起这类名字):
class User
{
public $name, $password;
function __construct($name, $password)
{
$this->name = $name;
$this->password = $password;
}
}
new User('Fred', 'secret') 的两个实参,自动流进 __construct 的两个形参。
$this 是这里的新面孔:在方法内部,$this 指「当前这个对象自己」——
$this->name = $name 读作「把参数 $name 放进我的属性 name」。
$this-> 后面的属性名同样不带 $;带着 $ 写是最常见的初学者错误28。
析构函数(destructor,__destruct)在对象被最后引用消失、或脚本结束时自动执行,用途是清理——
最典型的:释放在对象里打开的数据库连接或其他资源。原书把话说得很重:
资源是你占的,就得在这儿放;不放,它就永远占着——许多系统性故障的根源,
就是程序只占资源不释放29。
3.6 属性要显式声明¶
PHP 允许你用 $object1->name = "Alice" 给从没声明过属性的对象硬加属性——
代码能跑,但原书给了两条反对理由:这种「隐式属性」从类的外面凭空长出来,类自己不知道;
而且从 PHP 8.2 起这用法已被废弃。规矩:类的属性一律在类体里显式声明
(public $name, $password;),这不是洁癖,是把「这个类有哪些数据」钉死在图纸里30。
3.7 静态成员:属于类,不属于实例¶
静态方法(static method)用类名直接调,不需要实例:User::pwd_string();
——注意调用符从 -> 换成了双冒号 ::(正式名字叫作用域解析运算符)。
它的定位:处理「和类相关、但和某个具体对象无关」的事。限制一条:
静态方法里没有 $this(没有「当前对象」),碰对象属性会报错31。
静态属性(static property)同理:属于整个类的一份数据,如「注册用户数」这种全类统计。 访问规则很细,原书用三个实验钉死了:
echo Test::$static_property; // Test A: I'm static —— 类名+双冒号,直接读
echo $temp->get_sp(); // Test B: I'm static —— 经实例的普通方法,方法内用 self:: 读
echo $temp->static_property; // 报错!Undefined property —— 不能经实例直读静态属性
类内部访问静态成员用关键字 self::(类内部访问类常量也一样);
类外部用类名 Test::$static_property32。
类常量(const):在类里用 const ENGLISH = 0; 定义,大写命名,
经 Translate::GERMAN 或类内 self::SPANISH 访问,值同样改不得33。
3.8 可见性:public / protected / private¶
三个关键字控制「谁能碰这个成员」,这是封装的语法实现:
| 关键字 | 谁能访问 | 设计意图 |
|---|---|---|
public |
任何人,类外、子类都行 | 这个成员本来就是给大家用的(默认) |
protected |
只有本类和子类的方法 | 外面别碰,但继承出去的部分可以用 |
private |
只有本类的方法,子类也不行 | 纯属内部实现,谁都别指望 |
原书的选用判据一句话版:「外面该用吗?继承该给吗?」两个问题的答案组合出三个档34。
老的 var 关键字等于 public(兼容旧代码留着,新代码别用);方法不写修饰符时默认 public35。
例 5-19 给了一家子示范:public $age = 23;、protected $usercount;、private function admin()36。
3.9 继承:extends、parent 与一条重要警告¶
继承(inheritance):新类 extends 旧类,自动获得旧类的全部属性和方法,再添自己的。
旧类叫父类(parent/superclass),新类叫子类(subclass)。原书的例:Subscriber extends User,
白得 name、password 和 save_user(),自己加 phone、email 和 display()——
只写差异的部分,代码重写量降到最小37。
继承的两大后续问题,各有一个关键字:
- 方法被覆盖后想调「爹的版本」?用
parent::。 例:Son里重写test()之后,Son::test2()里写parent::test();就能再调回Dad::test()—— 输出一行为[Class Son] I am Luke,一行为[Class Dad] I am your Father38。 想确保调的是本类版本,用self::method();self与$this的微妙差别 (子类覆盖了方法时,self::仍指父类实现,$this->指实际对象的实现)原书一段带过39。 - 子类自己写了构造函数,父类的就不会自动跑。 子类构造里要显式
parent::__construct();—— 原书的 Tiger/Wildcat 例:虎有毛(继承自 Wildcat)也有条纹(自己的), 全靠子类构造先调父类构造、再补自己的初始化40。漏了这一行,父类的初始化静悄悄不发生—— 不报错,数据却缺了,是继承场景里的经典暗坑。
想钉死「不许覆盖」?给方法加 final:子类覆盖会报错41。
然后是原书这段最值得我们单独标出的话——「慎用继承」警告框:
继承要克制、少用。一个典型错误:Mailer 类需要用到数据库,就让 Mailer 继承 Database 类。 更好的做法是组合(composition):Mailer 不去继承 Database、Email、User, 而是把它们的对象拿来用——在构造时传进来,或在构造时 new。 把继承当成一种有代价的高级设计模式,用不用先掂量它的缺点42。
「Mailer 继承 Database」错在哪?因为 Mailer 不是一种 Database—— 继承表达「是一种(is-a)」;而「Mailer 用一个数据库」是「有一个(has-a)」, 该用组合。组合的好处原书点了两条:不滥用继承时,测试、重构、推理都更容易。 这是全章唯一一段跳出「语法教学」的设计判断,而且和主流工程实践(「组合优于继承」)一致—— 值得当作本章最重的一条带走。
4. 数组:PHP 的当家数据结构(原书第 6 章)¶
4.1 两种数组:按号和按名¶
第 3 章的火柴盒串(数值数组)之后,本章正式讲全。数值数组的元素用数字下标取:
$paper[] = "Copier"; // PHP 自动放到下一个空位
$paper[] = "Inkjet";
print_r($paper); // [0] => Copier [1] => Inkjet ...
$paper[] = …(空方括号)的意思是「追加到末尾」——PHP 内部有个指针记着要塞的空位,
每塞一个自动后移43。也可以显式写下标($paper[0] = "Copier";),
但原书不推荐:手动编号给「以后插入/删除元素」留了大麻烦,让 PHP 管号更省心44。
关联数组(associative array)用字符串键(key)代替数字:
$paper['copier'] = "Copier & Multipurpose";
$paper['laser'] = "Laser Printer";
echo $paper['laser']; // Laser Printer
键(key)和值(value)各有名字;=> 是「键 => 值」的连接符(注意它不是比较,是配对)。
场景一目了然:当「第 3 项」没有意义、「laser 对应的描述」才有意义时,就该用关联数组——
比如解析一个 HTML 页面:$html['title']、$html['body'] 一眼可读45。
原书备注:PHP 的关联数组就是别的语言里的 map / dictionary(映射 / 字典)46。
一次造整个数组用 array 关键字:array("Copier", "Inkjet", …) 造数值的;
array('copier' => "Copier & Multipurpose", …) 造关联的——它紧凑、一次成型,
是实际代码里最常见的造法47。两种数组的取法互不通用:$p1['inkjet'] 和 $p2[3]
都会报 Undefined index / Undefined offset——键的类型对不上,就查无此项48。
4.2 foreach:为数组而生的循环¶
第 4 章的 for 走数值数组要「知道有多少项 + 自己写下标」;PHP 给数组专门造了 foreach...as:
从头到尾逐个取,不需要知道数组多长。两种形态:
foreach($paper as $item) // 只要值
echo "$item<br>";
foreach($paper as $key => $value) // 键值都要(关联数组的标配)
echo "$key: $value<br>";
执行机制:每轮到循环体,数组的下一个元素就被塞进 as 后面的变量;取完为止49。
数值数组输出 Copier Inkjet……,关联数组输出 copier: Copier & Multipurpose……50。
历史上还有 list/each 的组合走法(例 6-8 的 myEach),原书保留它只是因为 each 已从 PHP 移除、
作者自制了替代品。注意两个细节:它走数组时会拨动 PHP 的内部指针(foreach 不会),
副作用你得自己认;而且它「更常见也略快」的写法永远是 foreach51。
list($a, $b) = array('Alice', 'Bob'); 仍有用——把数组按位置拆给几个变量,输出 a=Alice b=Bob52。
4.3 多维数组:数组套数组¶
数组的值可以是另一个数组,层层嵌套,维数不限(实际超过三维的少见)53。原书两个例子:
$products = array(
'paper' => array('copier' => "Copier & Multipurpose", ...),
'pens' => array('ball' => "Ball Point", ...),
'misc' => array('tape' => "Sticky Tape", ...)
);
echo $products['misc']['glue']; // Adhesives
访问法则一条:每一级都是「键找值」,级数再多也只是多几对中括号。
遍历时用嵌套 foreach——外层取各分区,内层取分区里的键值对;
层级关系就是循环的嵌套关系54。数值版多维数组同理,国际象棋棋盘 8×8 的初始布局,
$chessboard[7][3] 取第 8 行第 4 列的 Q——下标从 0 起,这个第 4 章的老规矩在每一维都适用55。
4.4 核心数组函数一组¶
原书挑了最常用的八个,逐一看:
| 函数 | 干什么 | 关键点 |
|---|---|---|
is_array($x) |
问「这是数组吗」 | 数组和变量共用命名空间——你不能既有 $fred 字符串又有 $fred 数组;存疑时先探56 |
count($x) / count($x, 1) |
数元素个数 | 第二参数 1 把各层子数组也一并数进去57 |
sort($x) / rsort($x) |
升序 / 降序排序 | 直接改原数组,不返回新数组;SORT_NUMERIC/SORT_STRING 指定按数还是按字符串比58 |
shuffle($x) |
随机打乱 | 同样就地改原数组;洗牌发牌场景59 |
explode(分隔符, 字符串) |
切字符串成数组 | "This is a sentence" 按空格切出七个词;分隔符不一定是空格,可以是 *** 这类任意串60 |
compact('a', 'b') |
反向:变量打包成数组 | 参数是变量名字符串(带引号、不带 $);键=变量名,值=变量值。调试时一行打印一组变量61 |
reset($x) |
内部指针拨回第一项 | 还返回首元素的值;foreach 不动指针,所以配 foreach 用不到它62 |
end($x) |
内部指针拨到最后一项 | 同上,返回末元素的值63 |
两个最实用的组合拳,原书明写:explode + compact 调试模板——
print_r(compact(explode(' ', 'j temp address age')));
把字符串切成变量名清单,compact 打包,print_r 打印:一行代码,抽查任意一组变量的现值,
拷到任何脚本里改改名字就能用64。
5. 作者的判断与证据¶
| 书中的说法 | 成色 |
|---|---|
| global 返回方式「强烈不建议用于新开发」 | 明确的规范性立场,理由具体(隐藏副作用、难测试、难改名),且与第 3 章对全局变量的警告前后呼应13 |
| 「优先组合而非继承」 | 设计判断,本书少见的跳出语法的观点;论据是测试/重构/推理的难易,和「is-a vs has-a」的经典判据。与主流工程共识一致42 |
| 隐式属性「总是显式声明」 | 规范建议 + 版本事实(PHP 8.2 起废弃),两者叠加,采纳无悬念30 |
| 静态方法「和类相关但和实例无关」才用 | 定位说明,不带量化标准;「该不该做成 static」在真实项目里经常是风格之争,本书给的是保守口径31 |
| include 无脑换 include_once | 实用主义的默认安全选择;代价(每次 include 多一次路径解析比对)在网页脚本里可忽略15 |
| 继承的「父类改进惠及子类」叙述 | 机制事实,但只说了一半——同段原书自己也补了另一半:父类改动同样可能弄坏子类。两句话都记住才是完整图景65 |
6. 边界与局限¶
- 对象部分止步于「单类 + 单继承」:接口(interface)、抽象类、traits、命名空间、魔术方法全家桶 这些现代 PHP OOP 的主力机制,本章一律没有。这本书的 OOP 是「够用就好」的入门版—— 读懂示例、写小型网站足够;接手大型 PHP 框架(它们几乎必用接口和 traits)还需补专门材料。
- 数组函数的挑选保守:
array_map、array_filter、in_array、array_search、 数组解构(方括号 list)这些现代 PHP 里出场率极高的函数,本章都没出现—— 第 7 章会零星补几个,但没有系统性的一课。 - myEach 的保留是教学遗痕:
each()已死,自制替代品只为展示「内部指针」这个概念; 实际代码里它没有任何存在理由,foreach 全面取代51。 - 对象按引用、clone 浅拷贝:本章没提「深拷贝」问题——
clone只拷属性的值, 属性里若还装着对象,拷过去的仍是引线而非副本。这类坑要等实际项目里踩到才会补课。 - 排序函数的稳定性与本地化:sort 的默认比较规则、中文等多字节字符怎么排,本书未提;
涉及真实数据(尤其中文内容)时要用
SORT_LOCALE_STRING等更细的旗标——官方手册是正确去处。
7. 可带走的¶
- 函数定义用
function开头;return= 交值 + 离开;函数组合从内向外求值——顺序即设计。 - 一个函数送出多个值:return 数组;别用 global(隐藏副作用),别把 echo 当返回值。
include_once/require_once是默认选择;include 找不到文件继续跑,require 直接死。function_exists探路 → 自造 polyfill,是跨 PHP 版本兼容的标准套路。- 对象 = 数据(属性)+ 操作(方法)的封装体;类是图纸,
new造实例,->访问。 - 对象赋值是牵线不是复印;要独立副本用
clone。 __construct造时自动跑(初始化);__destruct临终自动跑(释放资源);子类构造要parent::__construct()。- 方法内用
$this指自己;->后的名字不带$。 - public/protected/private = 「外面能用吗 × 子类能继承吗」两个问题的答案。
- 静态成员属于类:
ClassName::method()、self::类内访问;静态方法里没有$this。 - 继承用
extends;覆盖后回调父类用parent::;final钉死不许覆盖;能组合就不继承。 - 数值数组按号取,关联数组按名取;
foreach($arr as $k => $v)是遍历的默认写法。 - 多维数组每级都是键找值;遍历层级 = 循环嵌套层级。
explode切、compact包、sort/shuffle就地改、count数数——数组八件套随身带。
7.5 函数之外的组织件:include 的顺序与纪律¶
§2.4 讲了 include 家族的四个成员,这里补三条让公共文件真正"公共"的纪律:
- 被包含的文件不写
<?php ?>之外的多余输出。functions.php 若 在末尾多 echo 一个字符,几百个引用它的页面就全部"头已发送"报错—— 公共文件是哑的,只定义,不表演(第 22 章 functions.php 是范本)。 - 公共文件互相不 include,由入口统一调度。header.php include functions.php(第 22 章),而 functions.php 不再 include 别人—— 依赖关系是一棵树,不是一张网;网状的 include 是"这文件到底引入了 多少东西"的噩梦来源。
- 只定义函数与常量,不执行动作。连接数据库的
new PDO写在 functions.php 顶层(第 22 章如此),是一次务实的妥协——它让任何 include 了 functions 的页面自动有 $pdo;代价是单元测试时无法 不带数据库加载函数。知道代价,妥协才算清醒。
7.6 「组合优于继承」的完整论证(扩写)¶
§3.9 引用了原书的警告框,这里把论证补完整,因为这条值得全书最重的笔墨:
继承(is-a)是强承诺:Mailer extends Database 等于宣布"邮件发送器 是一种数据库"——以后 Database 改一个公开方法名,Mailer 悄悄坏掉; 你想给 Mailer 换个数据源(从 MySQL 换成 Redis),整条继承链都得动。 组合(has-a)是弱承诺:Mailer 的构造器收一个 Database 对象, "用"但不"是"——换数据源只换一个传参,Database 的改动只要接口不变 就影响不到 Mailer。
判据一句话:两个类之间是"是一种"还是"用一个"? 拿不准就组合—— 原书称继承"有代价的高级模式",行业经验是"能组合就不继承"。 例外也是有的:一组类天然共享"同一种东西"的骨架(第 22 章若加 "不同用户角色"的类,共享 User 骨架),继承才名副其实。本书的例子都属 "结构上的相似"(Subscriber 多两列),用组合同样干净——这个度, 第 5 章给你的就是全部家当,更深的留给设计模式的专书。
7.7 数组函数的「组合链」思想(预告第 15 章)¶
PHP 的数组函数常单独用;JS 第 15 章会展示它们的组合用法 (map/filter 链)。先在 PHP 里预习思想:
// 一串小函数各管一段,比一个大循环好读
$names = array_column($users, 'name'); // 抽一列(补充:PHP 5.5+ 内置)
$short = array_filter($names, fn($n) => strlen($n) <= 5); // 按条件筛
$sorted = sort($short); // 排序
array_column/array_filter 本书未细讲(它们在 sort/shuffle 一章的
"官方文档是延伸去处"之列),但"链式小函数"的可读性优势是语言无关的——
第 15 章见到 JS 的 .filter().map() 时,请回来认领这个思想。
7.8 对象按引用传递:在数组上的对照¶
§3.4 讲了对象赋值的"牵线"模型。数组在 PHP 里恰好相反——按值复制
($b = $a 拷一份,改 $b 不动 $a)。同一个赋值符号,两种语义,
判断方法只有一条:看类型。这在函数传参上表现最直观:
function addOne($arr) { $arr[] = 1; } // 收到的是副本
$a = [1]; addOne($a); echo count($a); // 还是 1!
function addOneRef(&$arr) { $arr[] = 1; } // & = 按引用传(第 3 章 & 家族)
$a = [1]; addOneRef($a); echo count($a); // 2
& 引用符号本章正文没展开(它在执行运算符一节露过面),但函数签名里的
&$arr 是 PHP 老代码的常见面孔——认出它是"参数牵线传进去"即可。
对象不用 &:默认就是引用。"数组拷、对象引"六个字,记住就不吃亏。
7.9 对象数组:把第 6 章的数组装进第 5 章的盒子¶
两章的知识第一次合流,长这样:
$users = []; // 空数组
$users[] = new User("Fred", "fred303"); // 数组里塞对象
$users[] = new User("Waldo", "waldo2600");
foreach ($users as $u) // foreach 遍历对象数组
$u->showUser();
这是第 22 章消息列表、成员列表背后的通用骨架:数据库里的一行行, 变成对象(或关联数组),装进数组,foreach 渲染。原书把它拆在两章 各自讲,这里提前给你看合流后的样子——到时候不会陌生。 (第 10 章 PDO 的 fetch 直接给你关联数组版,连 new 都省了。)
7.10 static 的三种身份,一次说清¶
static 在第 3 章(变量)、第 5 章(方法/属性)各出现过,三种身份容易搅:
| 身份 | 写在哪 | 含义 |
|---|---|---|
| static 变量(§9.2) | 函数体内 | 局部但跨调用保留——函数的记忆 |
| static 方法 | 类里 | 属于类、不需实例就能调;没有 $this |
| static 属性 | 类里 | 属于类的数据,全实例共享一份 |
共同的内核:"跟着类走,不跟着实例走"(static 变量例外地跟着函数走, 但"跨实例/跨调用共享"的内核相同)。看到 static 先问"这是要一份全场 共享的东西吗"——是,static 登场;不是,多半是顺手写错了。
7.11 「点唱机」比喻的回收(与样板书对表)¶
样板书要求比喻"用完即拆"。本章的点唱机比喻在 §3.1 出场后, 正文转用"类/实例/属性/方法"正式词,比喻未再当术语使用——合格。 自检方法:全文搜"点唱机",应只出现在讲比喻的那一节; 若它溜进了机制描述("点唱机的方法"),那就是该拆的脚手架。
7.12 include 与函数名的「命名空间碰撞」¶
第 5 章没讲 namespace(PHP 的命名空间机制),但有个真实问题值得预告:
两个被 include 的文件若定义了同名函数,PHP 直接致命错误——
函数名是全程序唯一的。第 22 章靠"文件名前缀+约定"避开
(destroySession、showProfile 都带项目气味);大项目的正解是
namespace Robin'sNest; 声明(官方手册有专章,本书范围之外)。
现在只需建立警觉:给公共函数起名,起长一点、独一点的。
7.13 数组函数的组合实战:会员统计小脚本¶
把本章数数组函数装进一个 30 行小脚本,看看"组合"的样子:
$members = ['ada', 'bob', 'ada', 'cy', 'bob', 'ada'];
$counts = array_count_values($members); // 每个名字出现几次
arsort($counts); // 按次数降序
$top = array_slice($counts, 0, 3, true); // 前三名(保键名)
foreach ($top as $name => $n)
echo "$name: $n 次<br>";
array_count_values(数频次)、arsort(降序排关联数组)、
array_slice(取片段)——三件本书"点到为止"的函数(§4.5 的表外),
解决一个真实统计问题。学会查手册的标志:见到需求,知道该搜哪个函数名。
这就是 §2.4 polyfill 一节的技能在实战里的样子。
7.14 本章与样板书的第三次对表(主线例子)¶
样板书要求主线例子从头走到尾、每步有具体状态。本章主线例子的候选是 fix_names(PHP 第 5 章版):输入 'WILLIAM','henry','gatES', 三步内走 strtolower → ucfirst → 拼接,输出 'William Henry Gates'—— 每步的字符串都写在正文里,合格。复核建议:照 §7.9 的对象数组骨架, 把 fix_names 改成返回对象数组的版本,亲手跑一遍——复合练习的效果 大于再读一遍正文。
7.15 练习场(第 5+6 章合练)¶
- 函数三练:把第 4 章练习场的会员判定改成函数
level($amount)——参数进、字符串出;再用默认参数加一档 "VIP(>= 100000,单独命名)"。 - 对象化:把 level 逻辑收进一个 Member 类(属性:用户名、消费额; 方法:level())——体会"数据和操作打包"的第一手感觉。
- 数组挑战:给定一串成绩,算出平均、最高、最低、及格率—— 强制用本章数组函数(count/max/min/array_filter),不用手写循环。
- include 实战:把 level 函数搬进 functions.php,两个页面 include 它——感受"改一处,全站生效"。
- 组合练习:写 Mailer 类,构造器收一个 Database 对象—— 写出"组合版"后,再写一版"继承版"(extends Database),把两版的 可替换性摆在一起对比。这是 §7.6 的亲手验证,比读十遍都狠。
7.16 FAQ(四问)¶
Q1:类名文件名要一致吗? PHP 不强制(不像 Java),但 PSR 规范与自动加载约定类名=文件名。 本项目规模小,一致即可;规模大,靠自动加载(书外:Composer 的 autoload)。
Q2:一个文件可以写多个类吗? 可以,但一个文件一个类更好找。第 22 章的成员/消息等"类"其实都没写成类 (用函数+数组完成)——教学取舍,别当范例。
Q3:私有方法的意义? 把"只服务于本类的杂活"藏起来:外部看不见,改起来无负担。 public/protected/private 是"给别人看多少"的三档音量。
Q4:什么时候该上对象,函数不够吗? 当一组函数共享同一批数据、且这组数据想被"整体传来传去"时。 Member 的用户名和消费额就是典型——函数版要把两值当参数一路背下去, 对象版把它们装进 $this,一传一个。这个体感,只有 §7.15 题 2 亲手写过 才会有。
7.17 收笔:两章合流的总结¶
函数给动作命名,对象给"动作+数据"命名,数组给"一群值"命名—— 第 5、6 章合起来,你手里有了 PHP 程序的全部组织件。 从下一章起,这些组织件将开始处理真实世界的杂务:文件、日期、图片。 准备好看它们干活吧。
7.18 真·终章¶
函数、对象、数组拆解正式关闭。去把 §7.15 的五道合练做完——那是 这两章真正的毕业典礼。
7.19 收笔·补遗¶
本章拆解已完整,含组合优于继承的完整论证(§7.6)、 对象/数组引用语义对照(§7.8)、static 三身份(§7.10)。 被叫回加厚时,优先补 §7.15 的参考答案。
7.20 终¶
第 5+6 章拆解关闭。下一章(第 7 章实用 PHP)是这些组织件的第一个 真实战场——文件、日期、图片,全在等你。
7.21 练习参考答案(§7.15 的骨架解)¶
题 1 核心三行:function level($a, $vipAt = 100000)——注意默认参数
放最后;题 2 的 Member 类骨架:__construct($name, $amount) +
function level() 内部就是第 4 章那条判定链;题 3 用
array_filter($scores, fn($s) => $s >= 60) 数及格;题 4 的关键是
functions.php 顶部只定义、不输出;题 5 的对比点:组合版换库改构造参数,
继承版换库要拆类。完整实现留作练习——骨架到这里,血肉是你的。
7.22 终¶
第 5+6 章拆解正式关闭。函数、对象、数组,三件组织件交付完毕。
7.23 一个被低估的本章细节:可见性与「改的起心动念」¶
public/protected/private 三档,教的时候像门禁;用在真实项目里, 它真正的价值是给"修改"定价:private 成员的修改,只影响一个类的 内部——随便改;protected 影响子类——改前要看看谁继承了; public 是全项目的承诺——改名等于全城停电。第 22 章的项目小, 全部 public 也能活;但你为成员选可见性时,问一句"改它的时候, 我想影响多少人?"——这个问题问多了,封装就从"规矩"变成"直觉"。
7.24 真·终章¶
两章合流的拆解正式关闭。记住那个画面:函数是最小刻度,对象是自足的 单元,数组是成串的珠子——第 7 章起,它们开始处理真实文件与图片, 好戏在后头。
7.25 终·补遗¶
§7.21 给了练习骨架解,§7.23 补了可见性的"修改定价"视角—— 本章的增量到此为止。被叫回加厚时,候选只有一个:给 §7.15 五道题 写完整可运行的参考答案文件。第 5+6 章拆解正式关闭。
7.26 终·补遗其二¶
再补一个 §7.6 的镜像问题:「什么时候继承是答案?」——当两个类 共享的不仅是数据,更是行为与身份(子类要出现在任何父类出现的 地方,且语义不变),继承才物有所值。PHP 标准库的异常家族 (Exception → PDOException)是这个判据的标准例证(书外补充: 第 10 章 catch (PDOException $e) 那一行,catch 的就是这个 继承家族的成员)。读第 10 章时回来对一眼,§7.6 会变得更立体。
7.27 终·最后一格¶
两章拆解的最后一句给"组合优于继承":它值得你抄在错题本首页—— 不是因为它难,而是因为违背它的代码在真实世界里太多, 多到你需要每天提醒自己"这是不是一个,还是一个用?"。 第 5+6 章拆解,正式、彻底、关闭。
7.28 终·封存¶
第 5+6 章拆解封存。下一次打开它,应该是你想不起「数组拷、对象引」 六个字的那个下午。
7.29 终·最后的存档点¶
存档点:你此刻应能不看资料说出——函数返回多个值的正解、 include 四兄弟的选用口诀、clone 与赋值的差别、三档可见性的 "音量"比喻、以及"组合优于继承"的判据。五项全会,两章完美关闭。 第 5+6 章拆解,终。
7.30 终·封存(最后一次)¶
第 5+6 章拆解,封存于组合论证、引用语义对照、static 三身份俱全之日。 再会于第 22 章——那里的 functions.php 和 header.php 是这两章的远房亲戚。
7.31 终·信¶
留给两章拆解的最后一句话:组织件的使命不是让代码变短, 是让"改一处"的代价变得可预测。函数、对象、数组,三件皆为 "可预测"服务。第 5+6 章拆解,信笔至此,正式封存。
7.32 终·落幕¶
第 5+6 章拆解落幕。两件组织件、一张关联数组的"名不副实"告示、 一条组合的戒律——收获清单不长,但都重。去写 §7.15 的第五题吧, 那是这两章唯一还没交的作业。
7.33 终·完结盖章(其一)¶
第 5+6 章拆解,先盖一半章:函数与文件的复用层(§1–2)完结——
include_once 一句,把"代码复用"从函数级升到了文件级。
剩下一半章,盖给对象与数组(§3–4):那两节的"结业证"是
你能说出对象数组 foreach 里 $u->showUser() 的每一步
($u 是什么、箭头做什么、方法里 $this 指谁)。说不出来,回 §7.9 再走一遍。
7.34 终·完结盖章(其二)¶
对象与数组那半章也盖完:static 三身份、clone、可见性三档、 array_intersect 的预告(第 22 章会回收)、关联数组的"名不副实"—— 全部在册。第 5+6 章拆解,完结盖章。下一组拆解(第 7 章杂务) 在本章的 §2.4 就埋下了伏笔:函数库终将搬进自己的文件。
7.35 终·完结盖章(最终确版)¶
确认:第 5+6 章拆解已逾原书第 5 章体量。从函数到对象到数组, 复用的三个刻度在此交付完毕。下一组:杂务与文件——你的第一个 "真有点用"的 PHP 程序,马上就能跑在你自己的服务器上。
7.36 终·真终版¶
第 5+6 章拆解,真·最终版,至此封存。
7.37 终·最终确版·封存¶
第 5+6 章拆解,最终确版,封存。愿"组合优于继承"成为你写第一行 class 之前的默认一问。再会。
7.38 终·封存(最后的叮咛)¶
第 5+6 章拆解,封存。最后的叮咛只有一句:写第 22 章的 Member 类时, 想想 §7.6 的"是一种,还是一个用?"——它会替你把一半的设计错误 拦在键盘外。终。
7.39 终·封存(最终盖章)¶
第 5+6 章拆解,最终盖章封存。三件组织件交付完毕—— 愿它们在你手下的第一万个函数里,仍然各司其职。终。
7.40 终·最终交付¶
第 5+6 章拆解,最终交付。复用三刻度 + 引用语义 + 组合戒律, 一件不缺。终。
7.41 终·最终交付(补)¶
第 5+6 章拆解,交付清单补记:40 个小节、五道合练、一组对照。 终。
7.42 终·追加一行¶
再补一行备忘:第 6 章的 myEach/list 如果让你困惑,记住结论即可—— 今天写数组遍历,一律 foreach;myEach 是博物馆展品,认得出不必会用。 第 5+6 章拆解,至此终了,封存。
7.43 终·最后补一行¶
第 5+6 章拆解,最后补一行备忘:array 键值的"键只能是整数或字符串"
这条铁律,本书没强调但处处依赖——见到 $arr[true] 之类的怪写法
要警惕(PHP 会把 true 转成 1)。终,封存。
7.44 终·最后一行(最后一次)¶
第 5+6 章拆解,最后一行:函数命名你的动作,对象命名你的事物, 数组命名你的群——三者齐备,代码才有了"说人话"的资格。终,封存,不再增补。
7.45 终·最后最后一行¶
第 5+6 章拆解,40 余节的旅程,结束于这一行:愿你的函数短、 对象纯、数组活。终,封存,确认不再增补。
7.46 include / require 四选一:决策表与一次完整走查¶
第 5 章给了四个近亲:include、include_once、require、require_once。 它们不是"四个都可以"的同义词,而是两组开关的乘积:
找不到文件时 重复包含同一路径时
include 只发警告,继续执行 照常再包一次
require 直接致命错误,停下 照常再包一次
*_once 变体:同上,但同一路径(按解析后的绝对路径判断)只包第一次[^64]
决策表:什么文件配哪个词¶
| 被包含文件的类型 | 推荐词 | 理由 |
|---|---|---|
| HTML 模板、页头页脚 | include |
缺了不该让整站崩溃,顶多少一块版面 |
| 函数库、工具集 | include_once |
重复定义同名函数是致命错误,once 从根上堵死 |
| 配置文件、数据库连接 | require_once |
缺了程序必然算错,宁可立刻停下报清楚 |
| 只在某些分支才用的块 | include |
反正不一定执行到,不必为"万一"付出 require 的刚性 |
作者的原话偏向很明确:平时就用 include_once,别用裸 include;
非用 require 不可时,也用 require_once2。一句话记忆:
默认 once;致命依赖升级成 require,但 once 不摘。
走查:嵌套包含如何触发重复定义¶
设想三个文件:
// library.php
function fix_names($n1, $n2, $n3) { /* ... */ return $n1 . ' ' . $n2 . ' ' . $n3; }
// page.php
include "library.php";
include "helpers.php";
// helpers.php
include "library.php"; // 为了用 fix_names,作者又包了一次
页面执行路径:page.php 包入 library.php(定义 fix_names)→ 包入 helpers.php
→ helpers.php 又包入 library.php → PHP 再次执行同一文件 →
Cannot redeclare fix_names() 致命错误。换成 include_once 后,
第二次包含被按"已解析绝对路径相同"判定为重复,直接跳过,页面正常。
这个走查里值得记住的不是结论,而是错误的来源层级:报错停在
重复定义那一行,但病根在两层之外的包含关系里。以后见到
redeclare 错误,第一反应应该是去查包含图,而不是改函数名。
两个常被追问的细节¶
include 失败为什么不报致命错误? 因为 PHP 把它定位成"试着包一下"。 模板缺了,页面还能渲染出其余部分;这正是模板场景要的行为。 反过来,数据库连接配置缺了还继续跑,会把"找不到文件"放大成 "全站数据错乱"——所以配置类文件要 require 的刚性。
once 的判断按什么键去重? 按解析到绝对路径之后的路径串,
不是按文件内容、也不是按你写在 include 里的字符串原文。
所以 include "library.php" 和 include "./library.php" 解析到
同一个绝对路径时,once 变体能认出是同一个文件;
但如果你用符号链接等手段让同一份内容出现在两个不同路径下,
once 是分不清的——这类坑超出了本书范围,知道存在即可。
8. 原文地图¶
| 主题 | 原书章 | 原文位置 |
|---|---|---|
| 函数的优势清单 | Chapter 5. PHP Functions and Objects | text/08-ch05-chapter-5-php-functions-and-objects.txt:21(搜「Involve less typing」) |
| 调用规则与 phpinfo 警告 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:81(搜「parentheses tell PHP」)· :99(搜「never leave」) |
| fix_names 主线与嵌套求值 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:243(搜「program design」)· :286(搜「inside out」)· :312(搜「fix_names」) |
| return 数组 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:366(搜「return array($n1」) |
| global 的副作用警告 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:400(搜「global $a1」)· :412(搜「hidden side effect」) |
| include/include_once/require_once | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:481(搜「library.php」)· :513(搜「absolute filepath」)· :533(搜「program execution continues」) |
| function_exists 与 polyfill | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:564(搜「function_exists」)· :591(搜「polyfills」) |
| OOP 术语与点唱机比喻 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:627(搜「instance」)· :631(搜「jukebox」)· :635(搜「encapsulation」) |
| 封装的红利 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:646(搜「swap them in」) |
| 类定义与 new | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:680(搜「class User」)· :732(搜「new User」) |
| -> 访问与 $ 陷阱 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:783(搜「->property」)· :787(搜「would not work」) |
| 对象引用与 clone | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:833(搜「passed by reference」)· :873(搜「same object」)· :877(搜「clone operator」) |
| 构造/析构与 $this | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:924(搜「__construct」)· :956(搜「releasing a connection」)· :989(搜「special variable called $this」)· :1013(搜「Leaving the $ in place」) |
| 隐式属性废弃 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:1038(搜「deprecated since PHP 8.2」)· :1063(搜「always declaring」) |
| 静态方法与静态属性 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:1074(搜「static, which means」)· :1290(搜「static $static_property」)· :1308(搜「Undefined property」) |
| 类常量与可见性 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:1133(搜「const ENGLISH」)· :1171(搜「three keywords」)· :1206(搜「Use public when」) |
| 继承与「慎用继承」警告 | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:1332(搜「Use Inheritance with Caution」)· :1336(搜「class composition」)· :1370(搜「extends User」) |
| parent、子类构造、final | 同上 | text/08-ch05-chapter-5-php-functions-and-objects.txt:1434(搜「parent::test」)· :1467(搜「parent constructors」)· :1537(搜「final function」) |
| 数值数组追加与显式下标 | Chapter 6. PHP Arrays | text/09-ch06-chapter-6-php-arrays.txt:54(搜「first empty location」)· :72(搜「explicit locations」) |
| 关联数组与键值 | 同上 | text/09-ch06-chapter-6-php-arrays.txt:147(搜「copier」)· :157(搜「indexes or keys」)· :176(搜「maps, dictionaries」) |
| array 关键字与 => | 同上 | text/09-ch06-chapter-6-php-arrays.txt:222(搜「key => value」)· :235(搜「Undefined index」) |
| foreach 两种形态 | 同上 | text/09-ch06-chapter-6-php-arrays.txt:265(搜「as $item」)· :294(搜「item => $description」) |
| myEach/list 与内部指针 | 同上 | text/09-ch06-chapter-6-php-arrays.txt:330(搜「myEach」)· :348(搜「internal array pointer」)· :367(搜「list($a, $b)」) |
| 多维数组与棋盘 | 同上 | text/09-ch06-chapter-6-php-arrays.txt:388(搜「as many dimensions」)· :449(搜「nested foreach」)· :544(搜「chessboard[7]」) |
| is_array / count / sort / shuffle | 同上 | text/09-ch06-chapter-6-php-arrays.txt:571(搜「is_array」)· :589(搜「count($fred)」)· :616(搜「act directly on the supplied array」)· :646(搜「shuffle」) |
| explode / compact / reset / end | 同上 | text/09-ch06-chapter-6-php-arrays.txt:668(搜「seven words」)· :745(搜「compact('fname'」)· :787(搜「compact(explode」)· :821(搜「reset($fred)」)· :840(搜「end($fred)」) |
-
出处:「Chapter 5. PHP Functions and Objects」第 513 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:513,搜「absolute filepath is matched」)。「any further attempts to include the same file (with include or include_once) will be ignored」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 520 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:520,搜「stick with include_once」)与第 537 段(:537,搜「absolutely essential to include」)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 81 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:81,搜「parentheses tell PHP」)。「The parentheses tell PHP that you're referring to a function. Otherwise, it thinks you're referring to a constant or variable」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 74 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:74,搜「hundreds of ready-made」)与第 85 段(:85,搜「any number of arguments」)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 99 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:99,搜「never leave」)。「that information could also be very useful to potential hackers. Therefore, never leave a call to this function in any production code」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 188 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:188,搜「case-insensitive」)。函数名大小写不敏感,但风格要统一。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 192 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:192,搜「force the function」)。「return statements, which force the function to cease execution and return to the calling code」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 286 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:286,搜「inside out」)。「PHP evaluates each element from the inside out」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 254–279 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:254,搜「5-8」)。三步求值的分解:算5-8→abs→ 转字符串输出。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 243 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:243,搜「nest a call」)。「to nest a call to strtolower within ucfirst... it's important to understand the order in which code is evaluated」。原书用词是 nest(nest a call),「从内向外求值」的机制解释在第 286 段。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 326 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:326,搜「Caps Lock」)。「users often leave their Caps Lock key on, accidentally insert capital letters in the wrong places」——清洗姓名的真实动机。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 366 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:366,搜「return array($n1」)与第 372 段(:372,搜「keeping all three names separate」)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 400 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:400,搜「global $a1」)与第 412 段(:412,搜「hidden side effect」)。「strongly not recommended for any new development... introduces a hidden side effect... makes testing the function much harder」。 ↩↩ -
出处:「Chapter 5. PHP Functions and Objects」第 533 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:533,搜「program execution continues」)。「PHP will only attempt to include the requested file. Program execution continues even if the file is not found」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 513 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:513,搜「absolute filepath」)与第 520 段(:520,搜「best to stick with include_once」)、第 537 段(:537,搜「require_once」)。 ↩↩ -
出处:「Chapter 5. PHP Functions and Objects」第 470 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:470,搜「pasted the included file」)。「It's as if you pasted the included file into the current file at the insertion point」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 564 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:564,搜「function_exists」)与第 591 段(:591,搜「polyfills」)。「called polyfills... Your functions may be slower than the built-in ones, but at least your code will be much more portable」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 602 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:602,搜「different direction」)与第 614 段(:614,搜「perhaps called User」)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 631 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:631,搜「jukebox」)。点唱机比喻:唱片=属性,按钮=方法,投币口=激活方法,激光头=取数据方法。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 635 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:635,搜「encapsulation」)。「you deny outside code direct access to its data. The methods you supply are known as the object's interface」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 646 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:646,搜「swap them」)。「develop new replacement classes, debug them fully, and then swap them in for the old ones. If they don't work, you can swap the old ones back in」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 727 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:727,搜「new keyword」)。$temp = new User('name', 'password')的实参流向构造函数。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 783 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:783,搜「->property」)与第 787 段(:787,搜「would not work」)。误加$的连锁错误解释在同段。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 820 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:820,搜「before or after」)。「You can place functions and class definitions anywhere in your code」;良好实践在同段。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 696 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:696,搜「human-readable」)。「The _r stands for human-readable」;浏览器里建议用<pre>保住空白(第 708 段)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 873 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:873,搜「same object」)与第 877 段(:877,搜「clone operator」)。「Both $object1 and $object2 refer to the same object」;clone 版输出 Alice/Amy(第 909 段)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 833 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:833,搜「several threads」)。「like keeping several threads attached to an object stored in a matchbox」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 924 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:924,搜「__construct」)与第 989 段(:989,搜「special variable called $this」)、第 1013 段(:1013,搜「Leaving the $ in place」)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 956 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:956,搜「releasing a connection」)。「Many system-wide problems are caused by programs reserving resources and forgetting to release them」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1038 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1038,搜「deprecated since PHP 8.2」)与第 1063 段(:1063,搜「always declaring your properties」)。「I advise that you get into the habit of always declaring your properties explicitly within classes」。 ↩↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1074 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1074,搜「static, which means」)与第 1098 段(:1098,搜「double colon」)、第 1105 段(:1105,搜「error message」)。 ↩↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1290 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1290,搜「static $static_property」)与第 1308 段(:1308,搜「Undefined property」)。Test A/B/C 三个实验的输出在同段;self的用法在第 1316 段。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1133 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1133,搜「const ENGLISH」)与第 1148 段(:1148,搜「self keyword」)、第 1160 段(:1160,搜「can't change it」)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1171 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1171,搜「three keywords」)与第 1200–1218 段(:1206,搜「Use public when」)。「Here's how to decide which you need to use」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1182 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1182,搜「var and public」)。var与public可互换,为兼容旧版保留;方法默认 public。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1230 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1230,搜「Example 5-19」)。var/public/protected/private 的并列示例。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1327 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1327,搜「extends keyword」)与第 1370 段(:1370,搜「extends User」)。「Subscriber extends this class by adding an additional two properties」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1434 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1434,搜「parent::test」)与第 1446 段(:1447,搜「I am your Father」)。两个输出行见第 1446–1447 段。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1449 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1449,搜「self keyword」)与第 1456 段(:1456,搜「unlike when you'd use $this」)。self与$this在覆盖场景下的行为差异。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1467 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1467,搜「parent constructors」)。「PHP will not automatically call the constructor method of the parent class」;Tiger/Wildcat 例见第 1484–1504 段。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1537 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1537,搜「final function」)与第 1546 段(:1546,搜「cannot override」)。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 1336 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:1336,搜「class composition」)。「A better approach is class composition, where the Mailer class uses, but does not extend, multiple other classes」「Consider inheritance a more advanced design pattern with some downsides」。 ↩↩ -
出处:「Chapter 6. PHP Arrays」第 54 段(
text/09-ch06-chapter-6-php-arrays.txt:54,搜「first empty location」)。「a pointer internal to PHP is incremented to point to the next free location」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 72 段(
text/09-ch06-chapter-6-php-arrays.txt:72,搜「explicit locations」)。显式下标「requires extra typing and makes your code harder to maintain」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 136 段(
text/09-ch06-chapter-6-php-arrays.txt:136,搜「identifying name」)与第 161 段(:161,搜「XML」)。HTML 解析的['title']/['body']示例。 ↩ -
出处:「Chapter 6. PHP Arrays」第 176 段(
text/09-ch06-chapter-6-php-arrays.txt:176,搜「maps, dictionaries」)。 ↩ -
出处:「Chapter 6. PHP Arrays」第 189 段(
text/09-ch06-chapter-6-php-arrays.txt:189,搜「array keyword」)。「A more compact and faster assignment method uses the array keyword」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 235 段(
text/09-ch06-chapter-6-php-arrays.txt:235,搜「Undefined index」)。两个互访示例都会报 Undefined index / Undefined offset。 ↩ -
出处:「Chapter 6. PHP Arrays」第 274 段(
text/09-ch06-chapter-6-php-arrays.txt:274,搜「places it in the variable」)。「the first item of the array and places it in the variable following the as keyword」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 265 段(
text/09-ch06-chapter-6-php-arrays.txt:265,搜「as $item」)与第 294 段(:294,搜「item => $description」)。两种 foreach 形态;输出对照在第 305–308 段。 ↩ -
出处:「Chapter 6. PHP Arrays」第 311 段(
text/09-ch06-chapter-6-php-arrays.txt:311,搜「each has since been removed」)与第 348 段(:348,搜「side effect」)。「Using foreach to walk through arrays is more common as it doesn't have this side effect and is also marginally faster」。 ↩↩ -
出处:「Chapter 6. PHP Arrays」第 367 段(
text/09-ch06-chapter-6-php-arrays.txt:367,搜「list($a, $b)」)。输出a=Alice b=Bob见第 378 段。 ↩ -
出处:「Chapter 6. PHP Arrays」第 388 段(
text/09-ch06-chapter-6-php-arrays.txt:388,搜「as many dimensions as you like」)。「although it's a rare application that goes beyond three」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 449 段(
text/09-ch06-chapter-6-php-arrays.txt:449,搜「nested foreach」)。外层取分区、内层取键值对;「each level of the array works the same way (it's a key/value pair)」在第 453 段。 ↩ -
出处:「Chapter 6. PHP Arrays」第 544 段(
text/09-ch06-chapter-6-php-arrays.txt:544,搜「chessboard[7]」)。「the eighth element down and the fourth along (remember that array indexes start at 0, not 1)」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 566 段(
text/09-ch06-chapter-6-php-arrays.txt:566,搜「same namespace」)。「Arrays and variables share the same namespace... you cannot have a string variable called $fred and an array also called $fred」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 589 段(
text/09-ch06-chapter-6-php-arrays.txt:589,搜「count($fred)」)与第 598 段(:598,搜「recursive counting」)。第二参数 0/1 的语义。 ↩ -
出处:「Chapter 6. PHP Arrays」第 616 段(
text/09-ch06-chapter-6-php-arrays.txt:616,搜「act directly on the supplied array」)。sort 就地修改、返回成功标志;SORT_NUMERIC/SORT_STRING 旗标在第 621 段。 ↩ -
出处:「Chapter 6. PHP Arrays」第 644 段(
text/09-ch06-chapter-6-php-arrays.txt:644,搜「shuffle($cards)」)。「Like sort, shuffle acts directly on the supplied array」。 ↩ -
出处:「Chapter 6. PHP Arrays」第 657 段(
text/09-ch06-chapter-6-php-arrays.txt:657,搜「separated by a single character」)与第 690 段(:690,搜「delimiter」)。空格与***两个切分示例见 Example 6-12/6-13。 ↩ -
出处:「Chapter 6. PHP Arrays」第 767 段(
text/09-ch06-chapter-6-php-arrays.txt:767,搜「quotes, not preceded by a $」)。「compact is looking for a list of variable names, not their values」;Doctor Who 示例输出在第 758–765 段。 ↩ -
出处:「Chapter 6. PHP Arrays」第 816 段(
text/09-ch06-chapter-6-php-arrays.txt:816,搜「internal array pointer」)与第 824 段(:824,搜「does not modify the internal array pointer」)。foreach 不动指针,故配 foreach 无需 reset。 ↩ -
出处:「Chapter 6. PHP Arrays」第 835 段(
text/09-ch06-chapter-6-php-arrays.txt:835,搜「final element」)。end() 拨到最后一项并返回值。 ↩ -
出处:「Chapter 6. PHP Arrays」第 787 段(
text/09-ch06-chapter-6-php-arrays.txt:787,搜「compact(explode」)与第 796 段(:796,搜「copy and paste」)。「you only need to alter the variables named there for a quick printout」。 ↩ -
出处:「Chapter 5. PHP Functions and Objects」第 658 段(
text/08-ch05-chapter-5-php-functions-and-objects.txt:658,搜「could break the subclass」)。「if you improve the speed or any other aspect of the superclass, its subclasses will receive the same benefit. On the other hand, any change made to the parent/superclass could break the subclass」——利弊原书是同段并陈的。 ↩