详解php魔术方法(Magic methods)的使用方法


Posted in PHP onFebruary 14, 2016

PHP中把以两个下划线__开头的方法称为魔术方法,这些方法在PHP中充当了举足轻重的作用。 魔术方法包括:

  • __construct(),类的构造函数
  • __destruct(),类的析构函数
  • __call(),在对象中调用一个不可访问方法时调用
  • __callStatic(),用静态方式中调用一个不可访问方法时调用
  • __get(),获得一个类的成员变量时调用
  • __set(),设置一个类的成员变量时调用
  • __isset(),当对不可访问属性调用isset()或empty()时调用
  • __unset(),当对不可访问属性调用unset()时被调用。
  • __sleep(),执行serialize()时,先会调用这个函数
  • __wakeup(),执行unserialize()时,先会调用这个函数
  • __toString(),类被当成字符串时的回应方法
  • __invoke(),调用函数的方式调用一个对象时的回应方法
  • __set_state(),调用var_export()导出类时,此静态方法会被调用。
  • __clone(),当对象复制完成时调用

__construct()和__destruct()

构造函数和析构函数应该不陌生,他们在对象创建和消亡时被调用。例如我们需要打开一个文件,在对象创建时打开,对象消亡时关闭

<?php 
class FileRead
{
 protected $handle = NULL;

 function __construct(){
  $this->handle = fopen(...);
 }

 function __destruct(){
  fclose($this->handle);
 }
}
?>

这两个方法在继承时可以扩展,例如:

<?php 
class TmpFileRead extends FileRead
{
 function __construct(){
  parent::__construct();
 }

 function __destruct(){
  parent::__destruct();
 }
}
?>

__call()和__callStatic()

在对象中调用一个不可访问方法时会调用这两个方法,后者为静态方法。这两个方法我们在可变方法(Variable functions)调用中可能会用到。

<?php
class MethodTest 
{
 public function __call ($name, $arguments) {
  echo "Calling object method '$name' ". implode(', ', $arguments). "\n";
 }

 public static function __callStatic ($name, $arguments) {
  echo "Calling static method '$name' ". implode(', ', $arguments). "\n";
 }
}

$obj = new MethodTest;
$obj->runTest('in object context');
MethodTest::runTest('in static context');
?>

__get(),__set(),__isset()和__unset()

当get/set一个类的成员变量时调用这两个函数。例如我们将对象变量保存在另外一个数组中,而不是对象本身的成员变量

<?php 
class MethodTest
{
 private $data = array();

 public function __set($name, $value){
  $this->data[$name] = $value;
 }

 public function __get($name){
  if(array_key_exists($name, $this->data))
   return $this->data[$name];
  return NULL;
 }

 public function __isset($name){
  return isset($this->data[$name])
 }

 public function unset($name){
  unset($this->data[$name]);
 }
}
?>

__sleep()和__wakeup()

当我们在执行serialize()和unserialize()时,会先调用这两个函数。例如我们在序列化一个对象时,这个对象有一个数据库链接,想要在反序列化中恢复链接状态,则可以通过重构这两个函数来实现链接的恢复。例子如下:

<?php
class Connection 
{
 protected $link;
 private $server, $username, $password, $db;

 public function __construct($server, $username, $password, $db)
 {
  $this->server = $server;
  $this->username = $username;
  $this->password = $password;
  $this->db = $db;
  $this->connect();
 }

 private function connect()
 {
  $this->link = mysql_connect($this->server, $this->username, $this->password);
  mysql_select_db($this->db, $this->link);
 }

 public function __sleep()
 {
  return array('server', 'username', 'password', 'db');
 }

 public function __wakeup()
 {
  $this->connect();
 }
}
?>

__toString()

对象当成字符串时的回应方法。例如使用echo $obj;来输出一个对象

<?php
// Declare a simple class
class TestClass
{
 public function __toString() {
  return 'this is a object';
 }
}

$class = new TestClass();
echo $class;
?>

这个方法只能返回字符串,而且不可以在这个方法中抛出异常,否则会出现致命错误。

__invoke()

调用函数的方式调用一个对象时的回应方法。如下

<?php
class CallableClass 
{
 function __invoke() {
  echo 'this is a object';
 }
}
$obj = new CallableClass;
var_dump(is_callable($obj));
?>

__set_state()

调用var_export()导出类时,此静态方法会被调用。

<?php
class A
{
 public $var1;
 public $var2;

 public static function __set_state ($an_array) {
  $obj = new A;
  $obj->var1 = $an_array['var1'];
  $obj->var2 = $an_array['var2'];
  return $obj;
 }
}

$a = new A;
$a->var1 = 5;
$a->var2 = 'foo';
var_dump(var_export($a));
?>

__clone()

当对象复制完成时调用。例如在设计模式详解及PHP实现:单例模式一文中提到的单例模式实现方式,利用这个函数来防止对象被克隆。

<?php 
public class Singleton {
 private static $_instance = NULL;

 // 私有构造方法 
 private function __construct() {}

 public static function getInstance() {
  if (is_null(self::$_instance)) {
   self::$_instance = new Singleton();
  }
  return self::$_instance;
 }

 // 防止克隆实例
 public function __clone(){
  die('Clone is not allowed.' . E_USER_ERROR);
 }
}
?>

魔术常量(Magic constants)

PHP中的常量大部分都是不变的,但是有8个常量会随着他们所在代码位置的变化而变化,这8个常量被称为魔术常量。

  • __LINE__,文件中的当前行号
  • __FILE__,文件的完整路径和文件名
  • __DIR__,文件所在的目录
  • __FUNCTION__,函数名称
  • __CLASS__,类的名称
  • __TRAIT__,Trait的名字
  • __METHOD__,类的方法名
  • __NAMESPACE__,当前命名空间的名称

这些魔术常量常常被用于获得当前环境信息或者记录日志。

以上就是本文的全部内容,希望对大家的学习有所帮助。

PHP 相关文章推荐
由php if 想到的些问题
Mar 22 PHP
php中日期加减法运算实现代码
Dec 08 PHP
php 目录遍历、删除 函数的使用介绍
Apr 28 PHP
使用PHP导出Word文档的原理和实例
Oct 21 PHP
PHP实现对文本数据库的常用操作方法实例演示
Jul 04 PHP
php生成不重复随机数、数组的4种方法分享
Mar 30 PHP
php支持中文字符串分割的函数
May 28 PHP
使用phpexcel类实现excel导入mysql数据库功能(实例代码)
May 12 PHP
CI框架整合smarty步骤详解
May 19 PHP
Thinkphp微信公众号支付接口
Aug 04 PHP
php 读取文件夹下所有图片、文件的实例
Oct 17 PHP
浅谈PHP进程管理
Mar 08 PHP
PHP浮点比较大小的方法
Feb 14 #PHP
PHP魔术方法使用方法汇总
Feb 14 #PHP
PHP函数超时处理方法
Feb 14 #PHP
PHP使用file_get_content设置头信息的方法
Feb 14 #PHP
PHP下使用mysqli的函数连接mysql出现warning: mysqli::real_connect(): (hy000/1040): ...
Feb 14 #PHP
PHP缓冲区用法总结
Feb 14 #PHP
PHP二维数组排序简单实现方法
Feb 14 #PHP
You might like
PHP输入流php://input介绍
2012/09/18 PHP
php将文本文件转换csv输出的方法
2014/12/31 PHP
php简单实现多字节字符串翻转的方法
2015/03/31 PHP
给WordPress中的留言加上楼层号的PHP代码实例
2015/12/14 PHP
php一个文件搞定微信jssdk配置
2016/12/12 PHP
使javascript也能包含文件
2006/10/26 Javascript
javascript之ESC(第二类混淆)
2007/05/06 Javascript
5款Javascript颜色选择器
2009/10/25 Javascript
利用jquery的获取JS文件中的字符串内容
2012/02/14 Javascript
JavaScript实现GriwView单列全选(自写代码)
2013/05/13 Javascript
Tab切换组件(选项卡功能)实例代码
2013/11/21 Javascript
JavaScript支持的最大递归调用次数分析
2014/06/24 Javascript
基于JavaScript实现移动端TAB触屏切换效果
2015/10/20 Javascript
JavaScript实现字符串与日期的互相转换及日期的格式化
2016/03/07 Javascript
jQuery使用serialize()表单序列化时出现中文乱码问题的解决办法
2016/07/27 Javascript
Angular 页面跳转时传参问题
2016/08/01 Javascript
详解Vue This$Store总结
2018/12/17 Javascript
微信小程序封装自定义弹窗的实现代码
2019/05/08 Javascript
Vue+Element实现网页版个人简历系统(推荐)
2019/12/31 Javascript
Vue作用域插槽实现方法及作用详解
2020/07/08 Javascript
[04:49]期待西雅图之战 2016国际邀请赛中国区预选赛WINGS战队赛后采访
2016/06/29 DOTA
[48:00]完美世界DOTA2联赛循环赛 Forest vs Inki BO2第二场 11.04
2020/11/04 DOTA
Python中自定义函数的教程
2015/04/27 Python
Python松散正则表达式用法分析
2016/04/29 Python
python多线程方式执行多个bat代码
2016/06/07 Python
深入理解Python中的内置常量
2017/05/20 Python
Python 中 Virtualenv 和 pip 的简单用法详解
2017/08/18 Python
python pandas中对Series数据进行轴向连接的实例
2018/06/08 Python
浅析python 中大括号中括号小括号的区分
2019/07/29 Python
新手学习Python2和Python3中print不同的用法
2020/06/09 Python
python3.6中anaconda安装sklearn踩坑实录
2020/07/28 Python
Baby Tulai澳大利亚:美国婴儿背带品牌
2018/10/15 全球购物
安全的后院和健身蹦床:JumpSport
2019/07/15 全球购物
线程问题:wait()方法是定义在哪个类里面
2015/07/07 面试题
《童年》教学反思
2014/02/18 职场文书
《葡萄沟》教学反思
2016/02/23 职场文书