PHP this, self, static 详解
在 PHP 面向对象编程中,
$this、self和static是三个至关重要的关键词,它们用于在类内部访问成员属性和方法。理解它们之间的区别和用法是掌握 PHP 对象模型,特别是后期静态绑定 (Late Static Binding) 的关键。
核心思想:$this 指向当前对象实例,self 指向当前类,static 结合后期静态绑定机制,根据运行时调用者来确定指向的类。
一、$this 关键字
1.1 定义与用途
$this 关键字用于引用当前对象实例。它只能在非静态方法中使用,用于访问当前对象实例的非静态属性和非静态方法。
当一个类被实例化为一个对象后,$this 就代表了该对象。
1.2 访问方式
通过 -> 运算符访问:$this->propertyName 或 $this->methodName()。
1.3 示例
1 |
|
1.4 总结 $this
- 代表:当前对象实例。
- 使用范围:非静态方法。
- 访问类型:非静态属性和非静态方法。
- 操作符:
->。
二、self 关键字
2.1 定义与用途
self 关键字用于引用当前类。它可以在静态方法和非静态方法中使用,用于访问当前类的静态属性、静态方法以及类常量。
self 在编译时就确定了它所引用的类,因此它总是指向定义该常量的类,即使是在继承链中。这被称为早期绑定 (Early Binding)。
2.2 访问方式
通过 :: (Scope Resolution Operator,范围解析操作符) 访问:self::$staticProperty 或 self::staticMethod() 或 self::CLASS_CONSTANT。
2.3 示例
1 |
|
2.4 self 与继承
self 总是指向它被定义时所在的类,而不是运行时调用的类。
1 |
|
从上面的例子可以看出,self::getClassName() 在 ChildClass 中调用时,仍然返回 “Parent”,因为它绑定到了 ParentClass。
2.5 总结 self
- 代表:当前类。
- 使用范围:静态方法和非静态方法。
- 访问类型:静态属性、静态方法、类常量。
- 操作符:
::。 - 绑定:早期绑定 (Early Binding),总是指向代码编写时所在的类。
三、static 关键字 (后期静态绑定)
3.1 定义与用途
static 关键字在上下文中使用时,提供了一种后期静态绑定 (Late Static Binding) 的机制。与 self 不同,static 关键字指向的是运行时实际调用该方法或属性的类,而不是声明该方法或属性的类。
这使得 static 在处理继承和多态时更加灵活,它允许子类重写父类的静态成员,并在运行时正确地引用到子类的实现。
3.2 访问方式
通过 :: 访问:static::$staticProperty 或 static::staticMethod()。
3.3 示例 (延续 self 示例中的 ParentClass 和 ChildClass)
1 |
|
通过这个例子,我们可以清晰地看到 static 在继承链中如何动态地根据调用者来解析静态成员。
3.4 static 的其他应用
工厂模式:当工厂方法需要根据子类类型创建实例时,
static配合new static()可以很方便地实现。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Product {
public static function factory() {
return new static(); // 创建调用者的实例
}
}
class ConcreteProductA extends Product {}
class ConcreteProductB extends Product {}
$a = ConcreteProductA::factory();
$b = ConcreteProductB::factory();
var_dump($a); // object(ConcreteProductA)#1 (...)
var_dump($b); // object(ConcreteProductB)#2 (...)链式调用中的
return static:在实现链式调用时,返回static而不是$this可以确保在继承链中保持正确的类型提示。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class QueryBuilder {
protected $query;
public function select($fields) {
$this->query .= "SELECT " . implode(', ', $fields);
return $this; // 返回当前对象实例
}
public function where($condition) {
$this->query .= " WHERE " . $condition;
return $this;
}
public function getQuery() {
return $this->query;
}
}
class ExtendedQueryBuilder extends QueryBuilder {
public function limit($count) {
$this->query .= " LIMIT " . $count;
return $this; // 如果这里返回的是 $this,但类型提示是 QueryBuilder,可能影响 IDE 自动补全
// 更好的做法是返回 static,即使是子类方法,也能返回子类实例
// return static;
}
}
$query = new ExtendedQueryBuilder();
// $query->select(['id', 'name'])->where('age > 18')->limit(10); // 可以链式调用
3.5 总结 static
- 代表:运行时实际调用该静态成员的类。
- 使用范围:静态方法和非静态方法。
- 访问类型:静态属性、静态方法。
- 操作符:
::。 - 绑定:后期静态绑定 (Late Static Binding),动态地根据运行时调用上下文确定类。
四、三者的对比总结
| 特性 | $this |
self |
static |
|---|---|---|---|
| 代表 | 当前对象实例 | 当前类 (定义时的类) | 运行时调用者所在的类 |
| 绑定 | 对象绑定 (Instance Binding) | 早期绑定 (Early Binding) / 编译时绑定 | 后期静态绑定 (Late Static Binding) / 运行时绑定 |
| 用途 | 访问实例属性和方法 | 访问类静态属性、静态方法、类常量 | 访问类静态属性、静态方法,实现多态 |
| 使用范围 | 非静态方法 | 静态方法和非静态方法 | 静态方法和非静态方法 |
| 操作符 | -> |
:: |
:: |
| 继承中的行为 | 指向实例化对象本身,不受继承影响 | 无论谁调用,都指向代码定义时的类 (ParentClass::someMethod()) |
动态指向调用者所在的类 (ChildClass::someMethod()) |
五、在实际开发中的选择
- 访问对象实例成员:始终使用
$this->propertyName和$this->methodName()。 - 访问类常量:始终使用
self::CONSTANT_NAME。类常量是不会被子类重写的,因此使用self是正确的选择。 - 访问静态属性和静态方法:
- 如果你确定需要引用当前定义这些静态成员的类,并且不希望子类重写时影响到这里,就使用
self::$staticProperty或self::staticMethod()(早期绑定)。 - 如果你希望在继承链中能够动态地引用到调用者(子类)的静态成员,实现多态性,那么就使用
static::$staticProperty或static::staticMethod()(后期静态绑定)。这通常是更灵活、更推荐的做法,特别是在需要子类行为覆盖父类静态行为的场景。
- 如果你确定需要引用当前定义这些静态成员的类,并且不希望子类重写时影响到这里,就使用
理解这三个关键字,特别是 self 和 static 在继承中的区别,是编写健壮、可扩展的 PHP 面向对象代码的关键。
