9

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

我一直在看这个库,它完全符合我自己想做的事情: https ://github.com/antecedent/patchwork

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

所以假设我有以下对象:

//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() (如果需要,如何)?

除了: http ://till.klampaeckel.de/blog/archives/105-Monkey-patching-in-PHP.html

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

4

5 回答 5

4

您可以使用runkit进行运行时类修改。更具体地说,您可以使用runkit_method_redefine

于 2012-04-08T18:35:48.637 回答
3

http://till.klampaeckel.de/blog/archives/105-Monkey-patching-in-PHP.html的情况下,真正不同的是第二个 strlen 前面使用的\字符。

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

use TheNamespace;
$var = new TheClass();

或者使用类似的方法显式调用该类:

$var = new \TheNamespace\TheClass();

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

至于猴子补丁,您可以使用 runkit ( http://ca.php.net/runkit )。此外,关于拼凑,他们的网站( http://antecedent.github.com/patchwork/docs/examples.html)中有大量示例。您可以检查正在替换类中的函数的魔术方法示例。

于 2012-04-08T18:41:13.103 回答
1

从 PHP 5.6 开始,仍然不支持猴子补丁;然而 PHP 5.3 引入了匿名函数。这个答案并不完全是您正在寻找的,并且可能会有所改进,但总体思路是使用数组、匿名函数引用来创建一个自包含、自引用的数组(一个“对象”,如果你会):

测试.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!

test2.php

$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;

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

于 2015-04-23T19:13:30.370 回答
0

我使用 eval() 和命名空间对一个类进行了猴子修补。但是,这可能不是您的答案,因为如果您正在猴子修补的类已经在命名空间中,这将不起作用。除了从 eval 字符串中修剪命名空间声明之外,我还没有想出如何解决这个问题。但是,这样做可能会破坏类方法中任何依赖于命名空间的代码。

就我而言,我正在对核心 PDO 类进行猴子修补,以对依赖于数据库交互的类进行单元测试。但是,也许看到我的技术会帮助你弄清楚如何让它适合你的情况。

我在这里的博客文章中有代码片段:http: //chrisgriffing.com/coding/php/2012/04/12/how-to-mock-pdo-and-other-objects/

于 2012-04-16T23:33:29.450 回答
-1

您可能已经想通了,但是,仅供参考,他们使用的是流包装器,

http://php.net/manual/es/function.stream-wrapper-register.php

basically, they register an stream wrapper on file and phar, so when the code is loaded, thay can manipulate it, it doesnt work on code loaded from an opcache

于 2016-04-08T20:55:05.047 回答