这些'魔术'方法拥有者特殊的名字,以两个下划线开始,表示这些方法在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() 方法没有定义,那么正常情况下我们会看到:

  通过定义 __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;

PHP开发者必须了解的9个魔术方法的更多相关文章

  1. PHP魔术变量和魔术方法

    基础知识:魔术变量和魔术方法 魔术变量:最初PHP魔术变量的出现主要是为了方便开发者调试PHP的代码;当然也可以利用这个实现特殊需求.在写法上魔术变量前后都有两个下划线. 如:_LINE_:返回文件中 ...

  2. python的__call__、__str__、__repr__、__init__、__class__、__name___、__all__、__doc__、__del__等魔术方法的作用

    python中,一切都是对象 在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”--魔术方法 1.__call__:作用是把类实例变成一个可调用对象 在Pyth ...

  3. python基础知识09-继承,多继承和魔术方法

    1.继承 class Father: def init(self,age,sex): self.age = age self.sex = sex class Son(Father): 类名后面写括号, ...

  4. php魔术方法小结

    php魔术方法 __construct() __construct(mixed ...$values = ""): void PHP 允许开发者在一个类中定义一个方法作为构造函数. ...

  5. PHP基础知识之魔术方法

    __construct(), __destruct(), __call(), __callStatic(), __get(), __set(), __isset(), __unset(), __sle ...

  6. 前端学PHP之面向对象系列第二篇——魔术方法

    × 目录 [1]构造方法 [2]析构方法 [3]不可访问属性[4]对象复制[5]字符串[6]对象不存在[7]自动加载类[8]串行化[9]函数调用 前面的话 php在面向对象部分有很多相关的魔术方法,这 ...

  7. PHP中的魔术方法(2)

    1.__get.__set这两个方法是为在类和他们的父类中没有声明的属性而设计的__get( $property ) 当调用一个未定义的属性时访问此方法__set( $property, $value ...

  8. 魔术方法__sleep 和 __wakeup

    感觉序列化和反序列化用得倒是比较少了,而json_encode和json_decode用得相对多,都是转化成串,进行入库.传输等.json更方便,但是序列化和反序列化结合这两个魔术方法使用倒还行< ...

  9. Python魔术方法-Magic Method

    介绍 在Python中,所有以"__"双下划线包起来的方法,都统称为"Magic Method",例如类的初始化方法 __init__ ,Python中所有的魔 ...

随机推荐

  1. 201521123066 《java程序设计》第一周学习总结

    本周学习总结 (1)学习了Java的跨平台运行是因为有虚拟机,其特点是具有简单性,结构中立. (2)老师使用了新的作业模式,要学会发现其中的优势并好好学习使用. 书面作业 (1)为什么java程序可以 ...

  2. DAU新解

    写在回归之时 --------------------------- 抱歉,已经超过三年没有回到这里了. 浮躁,虚荣,一颗心没有落到地上,也没有好好总结一下自己.怀念最开始那个时候的状态,安静的做点事 ...

  3. 关于args的一个小bug

    我在开始学习Java的时候就有点疑惑,到底main方法中的args到底是什么?经过我的一些思考,然后结合代码写一点自己的看法. 下面来看一段代码: /** * @author 薛定谔的猫 * 关于ar ...

  4. 前端基础之HTML

    一.HTML 段落是通过 <p> 标签进行定义的 如: <p> hello world! </p> <html> 与 </html> 之间的 ...

  5. maven web 项目中启动报错 Java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet

    主要原因是maven项目里面的jar包吗,没有导入到项目中 maven web 项目中启动报错 Java.lang.ClassNotFoundException: org.springframewor ...

  6. 怎样使用自定义标签简化 js、css 引入?

    国庆将至,工作兴致全无,来总结点项目里平时不起眼干货. 前端引入 js .css 一般是这样: <script type="text/javascript" src=&quo ...

  7. 分享基于分布式Http长连接框架--代码模型

    好的代码应该是方便客户端使用,代码能够自描述,规范化,大众标准化. 而且我相信代码也是有生命的,需要不断的维护它,你以什么样的态度对待它,它就会以同样的态度回敬你,所以在写代码前,先摆好自己的态度(一 ...

  8. Count Color 线段树

    Count Color Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit  ...

  9. php版本的选择

    简单来说non-thread-safe 非 线程安全 与IIS 搭配环境,thread-safe 线程安全 与apache 搭配的 环境这个大家一定要注意,否则用错了版本,apache是无法启动的,另 ...

  10. 记住密码"功能的正确设计

    Web上的用户登录功能应该是最基本的功能了,可是在我看过一些站点的用户登录功能后,我觉得很有必要写一篇文章教大家怎么来做用户登录功能.下面的文章告诉大家这个功能可能并没有你所想像的那么简单,这是一个关 ...