《PHP类的基础概念:从零开始学面向对象》
什么是"类"?------ 你的设计图纸
想象你要开一家机器人制造工厂。你需要一张"设计图纸",上面画着机器人该有什么"部件"(比如名字、型号)和"功能"(比如行走、说话)。在 PHP 里, "类"(Class)就是这张设计图纸。
kotlin
// Robot 类:机器人设计图纸
class Robot {
// ... 在这里定义部件和功能
}
类本身不是机器人,它是制造机器人的模板。
类里有什么?------ 你的三大法宝
一张完整的图纸包含三样东西:
-
常量 (Constants) :固定不变的设定。
const MAX_SPEED = 100;
// 所有机器人的最高速度上限- 一旦定义,永远不能改变。
-
属性 (Properties) :机器人的"部件 "或"特征"。
public $name;
// 机器人的名字public $color;
// 机器人的颜色- 属性是机器人的"身份证信息"。
-
方法 (Methods) :机器人的"功能 "或"动作"。
public function walk() { ... }
// 机器人走路的功能public function speak($words) { ... }
// 机器人说话的功能- 方法是机器人的"技能包"。
📌 重要提示 :在方法内部定义的变量(如
function walk() { $step = 1; }
中的$step
)是局部变量 ,只在该方法执行时存在,用完就消失,它不是类结构的一部分。
创建机器人(实例化)------ 用图纸造机器
有了图纸,就可以造机器人了!这叫"实例化 "。使用 new
关键字。
ini
$myRobot = new Robot(); // 造一个机器人
$myRobot
就是根据 Robot
类创造出来的具体机器人 ,我们称之为"对象 "或"实例"。
new
操作符的进化:PHP 8.0+ 的任意表达式支持
在 PHP 8.0 之前,new
后面只能跟固定的类名。
ini
// PHP 8.0 之前
$className = 'Robot';
$instance = new $className(); // 需要变量,写法稍显别扭
从 PHP 8.0.0 起,new
可以用在任意表达式中,这带来了巨大的灵活性和简洁性!
php
// ✅ PHP 8.0+ 新写法:直接在表达式中使用 new
// 1. 从函数返回类名
function getRobotClass(): string {
return 'Robot';
}
$robot1 = new (getRobotClass());
// 2. 字符串拼接
$robot2 = new ('Rob' . 'ot');
// 3. 使用 ::class 常量
class AdvancedRobot {}
$robot3 = new (AdvancedRobot::class);
// 4. 创建后立即调用方法
echo (new DateTime())->format('Y-m-d'); // 创建 DateTime 对象并调用 format
// 注意:从 PHP 8.4 起,括号 ( ) 可以省略:echo new DateTime()->format('Y-m-d');
💡 关键点:
new ($className)
这种写法在 PHP 8.1+ 被标记为"软弃用",强烈推荐使用new ($expression)
的新语法。- 给构造函数传参 :如果类有构造函数需要参数,直接在表达式后的括号里传入即可:
new (<expression>)(<arg1>, <arg2>)
。
php// 假设 Robot 构造函数需要 name 和 age class Robot { public function __construct(string $name, int $age) { ... } } // 正确传参方式 $robot = new ('Rob' . 'ot')('小助手', 5);
对象的"引用"特性 ------ 一个机器人,多个遥控器
记住核心规则:对象变量默认是"引用" ,而不是"值"。
ini
$robot1 = new Robot();
$robot2 = $robot1; // $robot2 不是新机器人,而是指向 $robot1 的"遥控器"
$robot2->name = "小助手"; // 通过 $robot2 的遥控器改名字
echo $robot1->name; // 输出:小助手 ❗
为什么? 因为 $robot1
和 $robot2
指向的是同一个机器人实体。改一个,另一个也跟着变。
继承:机器人升级 ------ extends
PHP 支持"机器人升级"。新机器人可以继承旧机器人的所有部件和功能,然后添加自己的新东西。
scala
class SuperRobot extends Robot { // SuperRobot 继承 Robot
public $laser; // 新增激光武器
public function fly() { // 新增飞行功能
echo "起飞!";
}
}
extends
表示继承。- 重要限制 :PHP 不支持多重继承,一个机器人只能有一个"爸爸"(基类)。
覆盖 (Override) 与 Final
新机器人可以改进旧功能。
scala
class SuperRobot extends Robot {
// 覆盖父类的 speak 方法
public function speak($words) {
echo "超级机器人说:" . $words;
}
}
-
覆盖规则 (LSP - 里氏替换原则) :
- 方法签名(参数)必须兼容。不能删除父类方法的必需参数。
- 可以增加可选参数。
- 不能收紧访问权限(比如父类
public
,子类不能改成private
)。
-
覆盖的例外:
- 构造函数 (
__construct
) :子类构造函数的签名不需要 与父类兼容。子类可以有自己独立的参数列表(但仍需通过parent::__construct()
调用父类构造函数来初始化父类部分)。 private
方法 :子类可以定义一个与父类private
方法同名的方法,但这不是覆盖 ,而是完全独立的方法 ,因为private
方法在子类中不可见。
- 构造函数 (
-
Final 关键字 :如果父类方法或常量被标记为
final
,子类绝对不能覆盖它,这是"最终设定"。
::class
------ 获取类的全名
需要知道一个类的完整名称(尤其带命名空间时)?用 ::class
。
php
namespace Factory\Robots;
class WorkerRobot {}
echo WorkerRobot::class; // 输出: Factory\Robots\WorkerRobot
// 对象也可以用 (PHP 8.0+)
$robot = new WorkerRobot();
echo $robot::class; // 输出: Factory\Robots\WorkerRobot
Nullsafe 操作符 ?->
------ 安全调用
不确定机器人是否存在或有某个功能?用 ?->
安全调用。
ini
// 自 PHP 8.0.0 起可用
$result = $repository?->getUser(5)?->name;
// 上边那行代码等价于以下代码
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}