php的魔术方法

魔术方法魔术方法是PHP面向对象中特有的特性。它们在特定的情况下被触发,都是以双下划线开头,你可以把它们理解为钩子,利用模式方法可以轻松实现PHP面向对象中重载(Overloading即动态创建类属性和方法)。魔术方法很多还是成对出现的,以下列出目前PHP中所有的模式方法。

  以两个下划线开始,表示这些方法在php特定事件下将会被触发。这可能听起来有点自动魔法但是它真的很酷的,我们已经看过一个简单的例子在 last post,即我们使用一个构造器-使用这个作为我们第一个例子。


_construct

构造器是一个魔术方法,当对象被实例化时它会被调用。在一个类声明时它常常是第一件做的事但是没得必要他也像其他任何方法在类中任何地方都可以声明,构造器也能像其他方法样继承。如果我们想到以前继承例子从介绍到oop,我们能添加构造方法到Animal 类中,如:

class Animal{
  
  public function __construct() {
    $this->created = time();
    $this->logfile_handle = fopen('/tmp/log.txt', 'w');
  }
  
}


现在我们创建一个类来继承Animal类 - Penguin类!不添加任何属性和方法在Penguin类中,我们能申明并定义它继承自Animal类,如:

class Penguin extends Animal {
  
}
  
$tux = new Penguin;
echo $tux->created;
如果我们定义一个构造方法在Penguin类中,然后Penguin对象将会运行当它被实例化后。由于并没有构造方法,PHP 会参考父类方法定义  信息来使用它因此我们能覆盖父类方法,或者不,在我们的新类中-很便利。


_destruct

你发现文件句柄也是构造器一部分吗?当我们使用完一个对象时真不想把事情放一边,因此析构方法做着与构造方法相反的事情。当对象被销毁时,析构方法会运行,或者明确的说当我们不再使用它时,php会为我们清理掉。Animal类中,我们的析构方法像这样,如:

class Animal{
  
  public function __construct() {
    $this->created = time();
    $this->logfile_handle = fopen('/tmp/log.txt', 'w');
  }
  
  public function __destruct() {
    fclose($this->logfile_handle);
  }
}
析构器让我们关闭任何额外的资源比如被使用过的对象。在php中由于我们有这样运行时间短的脚本(留意在更新的php版本中增强的垃圾回收机制),通常讨论内存溢出根本不需要。然而它仍是好的推行方法来清理而且总体上让程序运行起来更高效。


_get

这个魔术方法是一个非常灵巧的小技巧 - 它使实际上不存在的属性如同存在一半。让我们举个小企鹅的例子:

class Penguin extends Animal {
  
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
}
现在,如果我们的小企鹅有一个 "name" 属性,而在此之后加载的属性为 "age",那么我们可以这样处理:

$tux = new Penguin(3);
echo $tux->name . " is " . $tux->age . " years old\n";

然而,设想一下,后端数据库或数据供应者发生了改变,"name"没有了,变味了"username"。并且设想这是一个非常复杂的应用,而需要修改的调用"name"的地方非常多。我们可以使用 __get 方法,使得"name"属性如同存在一样:

class Penguin extends Animal {
  
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
  
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
}

这并不是编写整个系统的好方法,因为它会让调试工作变得更困难,但它是一个非常有价值的工具。它允许如同属性一样使用或者展示需要经过计算的数据,以及无数我都想不到的地方。


_set

那么,我们将所有对 $this->name 的调用都更改为返回 $this->username的值,那么,如果我们想要设置这个值呢?也许我们有一个账户界面允许用户修改他们的名字。这时我们就需要 __set 方法的帮助了,举例说明:

class Penguin extends Animal {
  
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
  
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
  }
  
  public function __set($field, $value) {
    if($field == 'name') {
      $this->username = $value;
    }
  }
}
这样,我们就针对大量的调用伪造对象的属性,正如我说的,这并不是一个正统的方法,但却是一个很有用的技巧,值得记住。


_call

这里有两种近似的方法,我并没有单独列出来,而是一起说明。一个是 _call 方法,如果定义,它将在调用未定义过的方法时被调用;另一个是 _callStatic 方法,工作方式与第一个相同,但却是在调用未定义的静态方法时生效(PHP 5.3 加入).通常我使用 __call 进行友善的错误处理,这在需要别人整合调用你的方法的库代码中非常有用。例如,如果一段脚本拥有一个企鹅对象,名为 $penguin ,它包含一个 $penguin->speak() 方法...假设 speak() 方法没有定义,那么正常情况下我们会看到:


PHP Fatal error: Call to undefined method Penguin::speak() in ...

通过定义 __call 方法,我们可以使用一些更友善的提示信息来代替 PHP 的错误提示:

class Animal {
}
class Penguin extends Animal {
  
  public function __construct($id) {
    $this->getPenguinFromDb($id);
  }
  
  public function getPenguinFromDb($id) {
    // elegant and robust database code goes here
  }
  
  public function __get($field) {
    if($field == 'name') {
      return $this->username;
    }
  }
  
  public function __set($field, $value) {
    if($field == 'name') {
      $this->username = $value;
    }
  }
  
  public function __call($method, $args) {
      echo "unknown method " . $method;
      return false;
  }
}

这将捕获的错误并回应。在实际应用中,更合适的方法是依据你的需要纪录消息日志·,将用户重定向,或者抛出一个异常,但概念是相同的。在这里你可以处理任何你需要处理的不当调用,你可以检测方法的名称,并一一处理——例如,你可以同上面我们重命名属性一些样重命名方法。


_sleep

_sleep()方法会被调用当对象被序列化后,并允许你处理序列化。这有各种各样的程序,一个很好的例子如果一个对象包含某种类型的指针,例如文件句柄或引用另一个对象。当对象被序列化然后解序列化,这些引用类型是无用的,因为这些类型的引用的目标可能不再存在或有效。因此,最好是来取消这些信息在存储它们之前。


_wakeup

__wakeup()是与__sleep()方法相反的,允许您更改对象解序列化的行为。和__sleep()一起使用,可以用来恢复被删除的句柄和对象当对象被序列化时。一个很好的例子程序是数据库句柄被取消设置当该项被序列化,然后恢复到当前配置中设置项目时,解序列化一个数据库句柄。


_clone

我们看过一个使用clone关键字的例子,在我的介绍从入门到oop的第二部分,创建对象的副本,而不是有两个变量指向同一个实际的数据。在一个类中重写此方法,我们可以观察发生了什么当在对象上使用clone关键字时,。虽然这是不是我们每一天能遇到的,一个漂亮的用例是创建一个真正的单例模式通过添加private访问修饰符给这个方法。


_toString

无疑把最好的始终留到最后,__toString方法是一个非常方便的附加方法对于我们的工具包。该方法可以声明覆盖对象的行为,当作为一个字符串输出时,例如,当它被输出时。如果你想能输出对象到模板中,你可以使用此方法来控制输出结果。让我们再来看看在Penguin类中: 

class Penguin {
  
  public function __construct($name) {
      $this->species = 'Penguin';
      $this->name = $name;
  }
  
  public function __toString() {
      return $this->name . " (" . $this->species . ")\n";
  }
}

在适当的位置,输出该对象通过调用echo输出它,如:
$tux = new Penguin('tux');
echo $tux;

总结一下:

1.__construct,__destruct
__constuct构建对象的时被调用;
__destruct明确销毁对象或脚本结束时被调用;
2.__get,__set
__set当给不可访问或不存在属性赋值时被调用
__get读取不可访问或不存在属性时被调用
3.__isset,__unset
__isset对不可访问或不存在的属性调用isset()或empty()时被调用
__unset对不可访问或不存在的属性进行unset时被调用
4.__call,__callStatic
__call调用不可访问或不存在的方法时被调用
__callStatic调用不可访问或不存在的静态方法时被调用
5.__sleep,__wakeup
__sleep当使用serialize时被调用,当你不需要保存大对象的所有数据时很有用
__wakeup当使用unserialize时被调用,可用于做些对象的初始化操作
6.__clone
进行对象clone时被调用,用来调整对象的克隆行为
7.__toString
当一个类被转换成字符串时被调用
8.__invoke
当以函数方式调用对象时被调用
9.__set_state
当调用var_export()导出类时,此静态方法被调用。用__set_state的返回值做为var_export的返回值。
10.__debuginfo
当调用var_dump()打印对象时被调用(当你不想打印所有属性)适用于PHP5.6版本

魔术方法大全
http://php.net/manual/en/language.oop5.magic.php




### PHP 魔术方法概述 PHP魔术方法是一组特殊的类成员函数,这些方法的名字以双下划线 `__` 开头。它们的主要作用是在特定条件下被触发执行,从而提供了一种灵活的方式来处理各种场景下的需求。 #### 常见的魔术方法及其功能 以下是几个常见的魔术方法以及其具体用途: 1. **`__construct()`** - 当创建一个新的对象实例会自动调用方法。 - 它通常用于初始化对象的状态或设置默认属性值。 - 示例代码如下: ```php class Example { public function __construct() { echo "对象已创建"; } } $obj = new Example(); // 输出: 对象已创建 ``` 2. **`__destruct()`** - 此方法会在对象销毁之前被调用。 - 主要用来清理资源或者保存数据到文件等操作。 - 示例代码如下: ```php class Example { public function __destruct() { echo "对象即将销毁"; } } $obj = new Example(); unset($obj); // 输出: 对象即将销毁 ``` 3. **`__get()` 和 `__set()`** - 这两个方法分别用于访问和修改未定义或不可达的私有/受保护属性。 - 如果试图读取不存在的属性,则会触发 `__get()`;如果尝试写入这样的属性则会触发 `__set()`。 - 示例代码如下: ```php class Example { private $data; public function __set($key, $value) { $this->data[$key] = $value; } public function __get($key) { return isset($this->data[$key]) ? $this->data[$key] : null; } } $example = new Example(); $example->test = 'hello'; // 调用了 __set() echo $example->test; // 调用了 __get(), 输出: hello ``` 4. **`__call()` 和 `__callStatic()`** - 当调用一个不存在的方法,这两个方法会被激活。 - `__call()` 处理的是实例方法调用,而 `__callStatic()` 是静态方法调用的情况。 - 示例代码如下: ```php class Example { public function __call($method, $args) { echo "$method was called with arguments: "; print_r($args); } public static function __callStatic($method, $args) { echo "$method was statically called with arguments: "; print_r($args); } } $example = new Example(); $example->nonExistentMethod(1, 2, 3); // 动态方法调用 Example::anotherNonExistentMethod('a', 'b'); // 静态方法调用 ``` 5. **`__toString()`** - 将对象转换成字符串表示形式的候会调用方法。 - 特别适用于打印调试信息或是将对象嵌套于其他字符串之中。 - 示例代码如下: ```php class Example { public function __toString() { return "这是一个Example类的对象"; } } $example = new Example(); echo $example; // 输出: 这是一个Example类的对象 ``` 6. **`__invoke()`** - 如同引用所描述的一样,在尝试像函数一样调用某个对象,这个方法就会被执行[^2]。 - 示例代码已在引用中给出。 7. **`__clone()`** - 创建当前对象副本之后立即调用它。 - 可在此处实现深拷贝逻辑或者其他必要的克隆后动作[^2]。 - 示例代码如下: ```php class Example { protected $property; public function __clone() { $this->property = clone $this->property; } } ``` 8. **`__sleep()` 和 `__wakeup()`** - 序列化过程中涉及到了这两个方法。 - `__sleep()` 返回一个数组指定哪些变量应该序列化存储下来; - `__wakeup()` 则负责反序列化后的恢复工作。 9. **`__debugInfo()` (自 PHP 5.6 起可用)** - 提供定制化的 var_dump 结果展示方式。 ### 总结 利用好这些魔术方法可以极大地提升程序设计的质量与效率,同也让代码更加简洁易懂。然而需要注意过度依赖可能会降低可维护性和清晰度,因此应当谨慎选用合适的场合应用之[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值