php中的猴子补丁

php中的猴子补丁,php,monkeypatching,Php,Monkeypatching,我正在试图弄清楚猴子补丁是如何工作的,以及如何让它在我自己的对象/方法上工作 我一直在看这个库,它正是我想自己做的: 使用它,您可以从对象重新定义方法。它使用了“猴子补丁”技术。但我无法通过查看来源来真正了解到底发生了什么 假设我有以下对象: //file: MyClass.php namespace MyClass; class MyClass { public function say() { echo 'Hi'; } } 我想这样做: Mon

我正在试图弄清楚猴子补丁是如何工作的,以及如何让它在我自己的对象/方法上工作

我一直在看这个库,它正是我想自己做的:

使用它,您可以从对象重新定义方法。它使用了“猴子补丁”技术。但我无法通过查看来源来真正了解到底发生了什么

假设我有以下对象:

//file: MyClass.php
namespace MyClass;

class MyClass {

    public function say()
    {
        echo 'Hi';
    }
}
我想这样做:

Monkeypatch\replace('MyClass', 'say', function() {
    echo 'Hello';
});

$obj = new MyClass();
$obj->say();  // Prints: 'Hello'
但我不知道如何编写实际的补丁部分。我知道在这种情况下名称空间很重要。但这到底是如何让我修补某个方法的呢?我是否需要在某个地方使用eval()(如果需要,如何使用)

我真的找不到任何关于这件事的好例子,除了:


但我真的不知道如何将其应用于我自己的对象/方法。我希望得到一个好的解释或示例。

您可以使用。更具体地说,您可以使用。

在实际情况下,区别在于第二个strlen前面使用的\字符

使用名称空间时,您可以使用名称空间,并直接调用名称空间中声明的方法/类:

use TheNamespace;
$var = new TheClass();
或者通过使用以下命令显式调用该类:

$var=new\TheNamespace\TheClass()

因此,通过调用
\strlen()
而不是
strlen()
,您明确要求PHP使用默认strlen,而不是为此命名空间定义的strlen


至于猴子补丁,您可以使用runkit()。关于拼凑,他们的网站上也有大量的例子()。您可以查看正在替换类中函数的magic方法示例。

我使用eval()和名称空间修补了一个类。 不过,这可能不是您的答案,因为如果您正在进行monkey修补的类已经在命名空间中,则这不起作用。 除了从eval字符串中删除名称空间声明之外,我还没有找到解决方法。但是,这样做可能会破坏类方法中任何与命名空间相关的代码

在我的例子中,我正在为一个依赖于数据库交互的类进行单元测试而对核心PDO类进行修补。但是,也许看到我的技术会帮助你找出如何让它适合你的情况

我在这里的一篇博客文章中看到了代码片段:

从PHP5.6开始,仍然不支持monkey补丁;然而,PHP5.3引入了匿名函数。这个答案并不完全是您想要的,并且可能会有所改进,但总体思路是使用数组,并创建一个自包含、自引用的数组(“对象”),如果您愿意的话:

test.php

$inner = require('test2.php');
$inner['say'](); // Hi!

$inner['data']['say'] = 'Bye!';
$inner['say'](); // still says Hi!

$inner['set_say']('Bye!');
$inner['say'](); // Bye!

$inner = require('test2.php');
$inner['say'](); // Hi!
$class = array(
    'data' => array(
        'say' => 'Hi!'
    ),

    'say' => function() use (&$class){
        echo $class['data']['say'].'<br />';
    },

    'set_say' => function($msg) use (&$class){
        $class['data']['say'] =& $msg; 
    }
);

return $class;
test2.php

$inner = require('test2.php');
$inner['say'](); // Hi!

$inner['data']['say'] = 'Bye!';
$inner['say'](); // still says Hi!

$inner['set_say']('Bye!');
$inner['say'](); // Bye!

$inner = require('test2.php');
$inner['say'](); // Hi!
$class = array(
    'data' => array(
        'say' => 'Hi!'
    ),

    'say' => function() use (&$class){
        echo $class['data']['say'].'<br />';
    },

    'set_say' => function($msg) use (&$class){
        $class['data']['say'] =& $msg; 
    }
);

return $class;
$class=array(
“数据”=>数组(
“说”=>“嗨!”
),
'say'=>function()使用(&$class){
echo$class['data']['say'].
; }, 'set_say'=>函数($msg)使用(&$class){ $class['data']['say']=&$msg; } ); 返回$class;

另外,这里有一个免责声明说上面的代码(以及PHP中的猴子补丁)几乎总是一个糟糕的想法,但有时这是绝对必要的。

您可能已经了解到了这一点,但是,仅供参考,他们使用的是流包装器


基本上,他们在文件和phar上注册流包装器,因此当加载代码时,他们可以对其进行操作,它对从opcache加载的代码不起作用

我不想使用runkit,它是一个pecl,不能安装在很多主机上。此外,我对猴子补丁的工作原理很感兴趣。如果我只是想要一个图书馆,我会在补丁上使用runkit。我只是想知道猴子补丁如何与我自己的对象进行补丁。我从webstie中了解strlen示例。但不是如何在我自己的对象上应用它。它没有说明如何像拼凑中那样重新定义方法。我也不是在寻找任何类似runkit等的ohter库。我只是想知道如何使用普通PHP进行monkey补丁。@w00网站中提到的示例并不是您可以理解的monkey补丁,因为您已经了解了名称空间的全部要点。这就是为什么在类中没有任何重写函数的示例。如果您对monkey补丁的兴趣完全是为了单元测试,那么为什么不在开发服务器上使用runkit呢?否则你为什么不试试拼凑呢?但我不知道它是否有那么好的伸缩性。