PHP - 重载
在 C++ 或 Java 中,这个术语表示一个 class 可以定义同名的 class method 多次,但参数和/或返回值类型不同。在 PHP 中,overloading 的含义有所不同。它是一种特性,可以动态创建 properties 和 methods。PHP 的 magic methods(以双下划线开头的 method 名称)用于设置动态 properties 和 methods。
用于 overloading 目的的 magic methods 在与未声明或当前 scope 中不可见的 properties 或 methods 交互时会被调用。
因此在本章中,我们涵盖了以下主题 −
Property Overloading
Method Overloading
Overloading 中的访问控制
处理未定义的 Properties 和 Methods
继承与 Overloading
Property Overloading
PHP 的 magic methods 示例包括 __construct()、__destruct()、__tostring() 等。PHP 使用以下 magic methods 来实现 properties 的 overloading。
public __set ( string $name , mixed $value ) : void public __get ( string $name ) : mixed public __isset ( string $name ) : bool public __unset ( string $name ) : void
这里,
__set() 在向受保护的、private 的或不存在的不可访问 properties 写入数据时运行。
__get() 从不可访问的 properties 读取数据。
__isset() 在对不可访问的 properties 调用 isset() 或 empty() 时被调用。
__unset() 在对不可访问的 properties 调用 unset() 时被调用。
上面使用的 $name 参数是要设置或获取的 property 的名称。__set() method 的 $value 参数指定要赋给 property 的值。
__isset() method 检查某个 property 是否已被设置。__unset() method 删除该 property。
Property overloading 仅在 object context 中有效。在任何 static context 中,这些 magic methods 不会被触发。因此它们不应被声明为 static。
示例
在以下代码中,设置并获取了一个在 class 中未声明的动态 property,名为 myprop。
<?php
class myclass {
public function __set($name, $value) {
echo "setting $name property to $value \n";
$this->$name = $value;
}
public function __get($name) {
echo "value of $name property is ";
return $this->$name;
}
}
$obj = new myclass();
# 这会调用 __set() method
$obj->myproperty="Hello World!";
# 这会调用 __get() method
echo "Retrieving myproperty: " . $obj->myproperty . PHP_EOL;
?>
它将产生以下 输出 −
setting myproperty property to Hello World! Retrieving myproperty: Hello World!
__set() 和 __get() magic methods 也可以设置和获取声明为 private 的 property。在 myclass 中(在 function 定义之前)添加以下语句
private $myproperty;
你可以通过在 myclass 中定义 __isset() method 来检查 property 是否存在 −
public function __isset($name) {
return isset($this->$name);
}
使用此语句检查 property 是否已设置 −
var_dump (isset($obj->myproperty));
在这种情况下返回 true。
要使用在 myclass 中定义的 __unset() method 取消动态创建的 property −
public function __unset($name) {
unset($this->$name);
}
以下代码将返回 false −
var_dump (isset($obj->myproperty));
方法重载
用于动态设置方法的两个魔术方法是 __call() 和 __callStatic()。
public __call (string $name , array $arguments) : mixed public static __callStatic (string $name , array $arguments) : mixed
当在对象上下文中调用不可访问的(未定义或 private)方法时,会触发 __call()。另一方面,当在静态上下文中调用不可访问的方法时,会触发 __callStatic()。
示例
以下示例演示了 PHP 中的方法重载
<?php
class myclass {
public function __call($name, $args) {
// $name 的值区分大小写。
echo "Calling object method $name with " . implode(" ", $args). "\n";
}
public static function __callStatic($name, $args) {
echo "Calling static method $name with " . implode(" ", $args). "\n";
}
}
$obj = new myclass();
# 这会调用 __call() 魔术方法
$obj->mymethod("Hello World!");
# 这会调用 __callStatic() 方法
myclass::mymethod("Hello World!");
?>
它将产生以下 输出 −
Calling object method mymethod with Hello World! Calling static method mymethod with Hello World!
重载中的访问控制
这里的 __get 和 __set 方法可以访问 private 或 protected 属性,这可能会绕过封装。
<?php
class Example {
private $name;
public function __set($property, $value) {
$this->$property = $value;
}
public function __get($property) {
return $this->$property;
}
}
$obj = new Example();
// 访问 private 属性
$obj->name = "Radhika";
echo $obj->name;
?>
输出
以上代码的输出如下 −
Radhika
处理未定义的属性和方法
为了避免意外行为,请在魔术方法中添加测试或异常。请看下面的示例 −
<?php
class SafeExample {
public function __get($name) {
if (!property_exists($this, $name)) {
throw new Exception("Property '$name' does not exist.");
}
}
}
$obj = new SafeExample();
try {
echo $obj->unknownProperty;
} catch (Exception $e) {
echo $e->getMessage();
}
?>
输出
这将产生以下输出 −
Property 'unknownProperty' does not exist.
继承和重载
子类可以重写魔术方法,因此您可能需要使用 parent::__call() 来保持父类功能。
class ParentClass {
public function __call($name, $arguments) {
echo "Parent method: $name\n";
}
}
class ChildClass extends ParentClass {
public function __call($name, $arguments) {
echo "Child method: $name\n";
parent::__call($name, $arguments);
}
}
$obj = new ChildClass();
$obj->testMethod();
输出
这将生成以下输出 −
Child method: testMethod Parent method: testMethod
注意,使用 "->" 操作符表示该方法是 实例方法,而 "::" 操作符表示该方法是 静态方法。