Fork me on GitHub

PHP进阶基础知识剖析(一)之魔术方法

成功的唯一秘诀——坚持到最后一分钟。
柏拉图

前言

笔者近期开始一个专栏----PHP进阶基础知识剖析,专门对于那些比较生僻但很重要的语法内容,核心用法,进阶函数等等进行整理和总结,欢迎大家阅读!

那么本期要讲述的是关于魔术方法的知识

介绍

魔术方法是PHP中一类很特殊的系统内置方法,将所有以 __(两个下划线)开头的类方法保留为魔术方法。可以看作PHP的“语法糖”。

分类

魔术方法种类繁多,今天也不过多赘述,就简单讲解几种重要的

  • __set和__get方法
  • __construct、__destruct、__clone方法
  • __call和__callStatic方法
  • __sleep和__wakeup方法
  • __toString和__debugInfo方法
  • __invoke方法
  • __isset和__unset方法
__set和__get方法

作用:用于给对象属性赋值或者取值时,即使这个属性不存在或者这个属性有访问限制,也会能够正确执行。(请看如下示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Account
{
private $user;
public function __set($name, $value)
{
//注:此处name是外来变量不属于成员变量,因此$this->$name,否则$this->name
$this->$name = $value;
}
public function __get($name)
{
if (!isset($this->$name)) {
$this->$name = "默认值";
}
return $this->$name;
}
}

分析:

正如代码所示,有了这两个函数之后,不用向其他语言一样需要创建set方法和get方法获取访问限制的变量,其实本身这两个函数都是public的,只不过这样操作更加简单,属于PHP属性重载的一类。

__construct、__destruct、__clone方法

作用:分别是PHP的构造函数、析构函数和对象复制函数


注意点:

析构函数的执行时间:

1、对象的所有引用都被删除时
2、对象被显式销毁时(可以使用unset,或者赋值NULL,或者改变对象的值)
3、页面加载完成时(此时内存已经被GC机制自动释放,所有实例化的类自然也被销毁,最后实例化的类先销毁,最先实例化的类最后被销毁)


析构函数作用场景:

一个需要循环执行的脚本,这其中可能会涉及到频繁的创建某个对象,它可以将对象打开的一些资源及时的释放,以防止内存溢出或单个进程占用过多内存。


__clone函数:

其是对对象实例进行的浅复制,当调用clone方法是会自动调用此__clone方法,对象内的基本数值类型进行的是传值复制,而对象内的对象型成员变量,如果不重写__clone方法,显式的clone这个对象成员变量的话,这个成员变量就是传引用复制,而不是生成一个新的对象。所有的引用属性仍然会是一个指向原来的变量的引用。

复制完成
时,如果定义了 __clone() 方法,则新创建的对象(复制生成的对象)中的 __clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。

其实,clone方法克隆出来的对象是一个新的对象,除了其中引用属性的成员变量外,其他的与原对象都无关,因此一般解决断开引用属性的成员变量和原对象关系的方法就是在__clone函数中再调用clone方法(如示例)

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
33
34
35
36
37
38
39
40
class SubObject
{
static $instances = 0;
public $instance;

public function __construct() {
$this->instance = ++self::$instances;
}

public function __clone() {
$this->instance = ++self::$instances;
}
}

class MyCloneable
{
public $object1;
public $object2;

function __clone()
{

// 强制复制一份this->object, 否则仍然指向同一个对象
$this->object1 = clone $this->object1;
}
}

$obj = new MyCloneable();

$obj->object1 = new SubObject();
$obj->object2 = new SubObject();

$obj2 = clone $obj;


print("Original Object:\n");
print_r($obj);

print("Cloned Object:\n");
print_r($obj2);

__call和__callStatic方法

作用:当访问对象一个不可访问的方法或未定义的方法时,__call会被调用。
静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。属于方法重载

其实,设置这两个方法的本意是让动态创建变成可能(即根据参数的不同后期创建不同的方法),另外__callStatic还可以实现延迟绑定。(请看下方示例)

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
class MethodTest 
{
public function __call($name, $arguments)
{
echo "Calling object method '$name' "
. implode(', ', $arguments). "\n";
}
public static function __callStatic($name, $arguments)
{
//根据参数实现不同的方法后期动态创建
$method = preg_replace('/^run(\w*)$/','${1}',$name);
return self::$method();
}
private static function Test()
{
echo "test\n";
}
private static function date()
{
echo "date\n";
}
}

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

__sleep和__wakeup方法

作用:
用于序列化的时候调用,即serialize方法时会自动检测类中是否包含__sleep方法,__sleep方法会先被调用,然后才执行序列化操作,同理,__wakeup方法如果存在类中,会先于unserialize执行


注意:

__sleep() 方法常用于提交未提交的数据,或类似的清理操作。但必须返回所有应被序列化的变量名称的数组

__wakeup() 经常用在反序列化操作中,例如重新建立数据库连接,或执行其它初始化操作。(请看下方示例!)

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
33
34
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();
}
}
$conn = new Connection();
$arr = serialize($conn);
$e = unseralize($arr);

__toString和__debugInfo方法

作用:

格式化echo输出某个对象时(因为本身对象无法用echo输出),而__debugInfo是在格式化var_dump和print_r输出。


用了这两个方法,可以更好的格式化输出(请看下方代码示例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class TestClass
{
public $foo;
public $a;
public function __construct($foo)
{
$this->foo = $foo;
}

public function __toString() {
return "这个对象的属性是$this->foo";
}
public function __debugInfo()
{
return [
'foo' => $this->foo,
'a'=> $this->a
];
}
}

$class = new TestClass('Hello');
echo $class;
print_r($class);

__invoke方法

作用:

当尝试用函数的方式调用一个对象是触发此方法。(请看下方代码示例!)

1
2
3
4
5
6
7
8
9
class CallableClass 
{
function __invoke($x) {
var_dump($x);
}
}
$obj = new CallableClass;
$obj(5); // int(5)
var_dump(is_callable($obj)) // 检测变量内容是否可当作函数调用bool(true)

__isset和__unset方法

作用:
也属于属性重载的一类,与__set、__get类似


用法:
当对不可访问属性调用 isset() 或 empty() 时,__isset() 会被调用,是属于检查属性的用法

当对不可访问属性调用 unset() 时,__unset() 会被调用,是属于删除属性的用法。(请看下方代码示例!)

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
class Person {  
private $name;
private $sex;
private $age;
public function __get($property_name) {
if(isset($this->$property_name))
{
return ($this->$property_name);
} else {
return (NULL);
}
}
public function __set($property_name, $value) {
$this->$property_name = $value;
}
public function __isset($param) {
echo "isset()函数测定私有成员时,自动调用<br>";
return isset($this->$param);
}
public function __unset($param) {
echo "当在类外部使用unset()函数来删除私有成员时自动调用的<br>";
unset($this->$param);
}
}
$p = new Person();
$p->name = "LinDD";
echo var_dump(isset($p->name))."<br>";
echo $p->name."<br>";
unset($p->name);
echo $p->name;
-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%