重新定义类方法或类(Redefine Class Methods or Class)

2019-06-21 13:18发布

有没有什么办法重新定义一个类或者它的一些方法,而无需使用典型的继承? 例如:

class third_party_library {
    function buggy_function() {
        return 'bad result';
    }
    function other_functions(){
        return 'blah';
    }
}

我能做些什么来取代buggy_function() 显然,这是我想要做的是什么

class third_party_library redefines third_party_library{
    function buggy_function() {
        return 'good result';
    }
    function other_functions(){
        return 'blah';
    }
}

这是我确切的困境:我更新,打破我的代码第三方库。 我不想直接修改库,为未来的更新可能会再次打破代码。 我正在寻找一种无缝的方式来代替类方法。

我发现这个库 ,指出它可以做到这一点,但我持谨慎态度,因为它是4岁。

编辑:

我应该澄清,我不能从类重命名third_party_librarymagical_third_party_library或其他任何东西,因为框架的限制。

对于我而言,这将有可能只是增加一个函数的类? 我想你可以用一种叫做为此在C#“部分类”。

Answer 1:

这就是所谓的猴子修补 。 但是,PHP没有为它的原生支持。

不过,正如其他人也指出,在runkit库可用于添加支持的语言,是对继任者classkit 。 而且,尽管它似乎已放弃了它的创建者(已经表示,它不与PHP 5.2和更高版本兼容),该项目确实现在似乎有一个新的家庭和维护者 。

我还不能说我是一个球迷的做法。 通过评估的代码串进行修改似乎总是对我来说是潜在的危险和难以调试。

尽管如此, runkit_method_redefine似乎是你要找的是什么,它的使用的例子中可以找到/tests/runkit_method_redefine.phpt在仓库中:

runkit_method_redefine('third_party_library', 'buggy_function', '',
    'return \'good result\''
);


Answer 2:

runkit似乎是一个很好的解决方案,但它不是默认启用和它的部分仍然是实验性的。 所以我砍死在一起的小类替换类文件中的函数定义。 实例:

class Patch {

private $_code;

public function __construct($include_file = null) {
    if ( $include_file ) {
        $this->includeCode($include_file);
    }
}

public function setCode($code) {
    $this->_code = $code;
}

public function includeCode($path) {

    $fp = fopen($path,'r');
    $contents = fread($fp, filesize($path));
    $contents = str_replace('<?php','',$contents);
    $contents = str_replace('?>','',$contents);
    fclose($fp);        

    $this->setCode($contents);
}

function redefineFunction($new_function) {

    preg_match('/function (.+)\(/', $new_function, $aryMatches);
    $func_name = trim($aryMatches[1]);

    if ( preg_match('/((private|protected|public) function '.$func_name.'[\w\W\n]+?)(private|protected|public)/s', $this->_code, $aryMatches) ) {

        $search_code = $aryMatches[1];

        $new_code = str_replace($search_code, $new_function."\n\n", $this->_code);

        $this->setCode($new_code);

        return true;

    } else {

        return false;

    }

}

function getCode() {
    return $this->_code;
}
}

然后包括类进行修改,并重新定义它的方法:

$objPatch = new Patch('path_to_class_file.php');
$objPatch->redefineFunction("
    protected function foo(\$arg1, \$arg2)
    {   
        return \$arg1+\$arg2;
    }");

然后EVAL新的代码:

eval($objPatch->getCode());

有点粗糙,但它的工程!



Answer 3:

为了完整起见-猴子修补是提供PHP通过runkit 。 有关详细信息,请参阅runkit_method_redefine()



Answer 4:

如何在另一个类包装它像

class Wrapper {
 private $third_party_library;
 function __construct() { $this->third_party_library = new Third_party_library(); }
 function __call($method, $args) {
  return call_user_func_array(array($this->third_party_library, $method), $args);
 }
}


Answer 5:

是的,这就是所谓的extend

<?php
class sd_third_party_library extends third_party_library
{
    function buggy_function() {
        return 'good result';
    }
    function other_functions(){
        return 'blah';
    }
}

我前面加上“SD”。 ;-)

请记住,当你扩展一个类覆盖的方法,该方法的签名必须匹配原始。 因此,例如,如果原来说buggy_function($foo, $bar) ,它以匹配类扩展它的参数。

PHP是相当详细了解它。



Answer 6:

对于人们仍在寻找这个答案。

您应该使用延伸与组合的命名空间 。

像这样:

namespace MyCustomName;

class third_party_library extends \third_party_library {
  function buggy_function() {
      return 'good result';
  }
  function other_functions(){
      return 'blah';
  }
}

然后用它这样做:

use MyCustomName\third_party_library;

$test = new third_party_library();
$test->buggy_function();
//or static.
third_party_library::other_functions();


Answer 7:

Zend Studio的和PDT(基于Eclipse的IDE)有一些内置的refractoring工具。 但目前还没有内置的方法来做到这一点。

你也不会希望在你的系统有恶意代码的。 因为它可以在被错误地调用。



Answer 8:

如果库被明确创建坏类,而不是使用定位器或依赖系统你是出于运气。 有没有办法覆盖另一个类的方法,除非你的子类。 该解决方案可能是创建,修复库中的补丁文件,这样你就可以升级库,并重新应用补丁来修复具体方法。



Answer 9:

您也许能runkit做到这一点。 http://php.net/runkit



Answer 10:

还有的送花儿给人扩大与新的,正确的,方法的类并调用类,而不是马车之一。

class my_better_class Extends some_buggy_class {
    function non_buggy_function() {
        return 'good result';
    }
}

(很抱歉的蹩脚格式化)



Answer 11:

你可以使图书馆类的副本,与一切,除了类名相同。 然后重写改名类。

它并不完美,但它确实提高了扩展类的变化的可见性。 如果你喜欢的东西作曲家取库,你必须提交一份到源代码管理和更新它,当你更新库。

就我而言,这是一个旧版本的https://github.com/bshaffer/oauth2-server-php 。 我修改了库的自动加载磁带机来迎接我的类文件代替。 我的类文件承担了原来的名称和扩展的一个文件的复制版本。



Answer 12:

因为你总是有机会在PHP中的基本代码,重新定义要覆盖如下,这应该离开你的接口,完整的主类功能:

class third_party_library {
    public static $buggy_function;
    public static $ranOnce=false;

    public function __construct(){
        if(!self::$ranOnce){
            self::$buggy_function = function(){ return 'bad result'; };
            self::$ranOnce=true;
        }
        .
        .
        .
    }
    function buggy_function() {
        return self::$buggy_function();
    }        
}

您可能会因为某种原因使用私有变量,但那么你将只能通过扩展类中的类或逻辑访问功能。 同样有可能你会希望有同一类的不同的对象有不同的功能。 如果是这样,do't使用静态的,但通常你希望它是静态的,所以你不重复的内存使用由每个对象。 该“ranOnce”代码只是确保你只需要一次初始化它的类,而不是为每$myObject = new third_party_library()

现在,后来在你的代码或其他类 - 每当逻辑命中,你需要重写功能的点 - 简单地做如下:

$backup['buggy_function'] = third_party_library::$buggy_function;
third_party_library::$buggy_function = function(){
    //do stuff
    return $great_calculation;
}
.
.
.  //do other stuff that needs the override
.  //when finished, restore the original function
.
third_party_library::$buggy_function=$backup['buggy_function'];

作为一个侧面说明,如果你把你所有的类函数这种方式并使用基于字符串的键/值存储像public static $functions['function_name'] = function(...){...}; 这可以是用于反射有用。 没有那么多的PHP的其他语言,但因为你已经可以抢班和函数名,但你可以节省一些加工类的未来用户可以在PHP中使用替代。 但是它是一个间接额外的水平,所以我会尽量避免使用它原始的类尽可能。



文章来源: Redefine Class Methods or Class