有没有办法在不使用典型继承的情况下重新定义类或其某些方法?例如:

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_library 重命名为 magical_third_party_library 或其他任何内容。

就我的目的而言,是否可以在课堂上添加一个功能?我认为你可以在C#中用一种叫做“部分类”的东西来做这件事。

有帮助吗?

解决方案

它叫做猴子补丁。但是,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\''
);

其他提示

runkit似乎是一个很好的解决方案,但它默认情况下没有启用,部分仍然是实验性的。所以我一起攻击了一个小类,它取代了类文件中的函数定义。用法示例:

class Patch {

private 

runkit似乎是一个很好的解决方案,但它默认情况下没有启用,部分仍然是实验性的。所以我一起攻击了一个小类,它取代了类文件中的函数定义。用法示例:

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

然后包括要修改的类并重新定义其方法:

eval($objPatch->getCode());

然后评估新代码:

<*>

有点原油但它有效!

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

然后包括要修改的类并重新定义其方法:

<*>

然后评估新代码:

<*>

有点原油但它有效!

为了完整起见,可以通过 runkit 在PHP中获得猴子修补程序。有关详细信息,请参阅 runkit_method_redefine()

如何将它包装在另一个类中,如

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);
 }
}

是的,它被称为 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非常冗长。

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

您应该将扩展名称空间

像这样:

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();

Zend Studio和PDT(基于eclipse的ide)有一些内置的refractoring工具。但是没有内置的方法可以做到这一点。

此外,您根本不希望系统中存在错误的代码。因为它可能被错误地调用。

如果库显式创建了坏类而没有使用定位器或依赖系统,那你就不走运了。除非你是子类,否则无法覆盖另一个类的方法。 解决方案可能是创建修复库的补丁文件,以便您可以升级库并重新应用补丁来修复该特定方法。

您可以使用runkit执行此操作。 http://php.net/runkit

总是使用一个新的,正确的方法扩展该类,并调用该类而不是错误的类。

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

(抱歉格式糟糕)

您可以复制库类,除了类名之外的所有内容都相同。然后覆盖重命名的类。

它并不完美,但确实提高了扩展类更改的可见性。如果您使用Composer之类的东西获取库,则必须将副本提交给源代码控制并在更新库时更新它。

就我而言,它是旧版本的 https://github.com/bshaffer/的oauth2服务器的PHP 。我修改了库的自动加载器来取代我的类文件。我的类文件采用了原始名称,并扩展了其中一个文件的复制版本。

由于您始终可以访问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();
    }        
}

您可能由于某种原因使用私有变量,但之后您只能通过扩展类中的类或逻辑来访问该函数。同样地,您可能希望同一类的不同对象具有不同的功能。如果是这样,不要使用静态,但通常你希望它是静态的,所以你不要复制每个对象的内存使用。 '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中使用覆盖。然而,它是一个额外的间接层,所以我尽量避免在原始类上使用它。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top