문제

일반적인 상속을 사용하지 않고 클래스 나 그 방법을 재정의하는 방법이 있습니까? 예를 들어:

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 라이브러리 언어에 대한 지원을 추가 할 수 있으며 후임자입니다. 클래스 키트. 그리고 그랬지만 버려진 제작자가 (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 $_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($objPatch->getCode());

약간 조잡하지만 작동합니다!

완전성을 위해 -PHP에서 원숭이 패치를 사용할 수 있습니다. runkit. 자세한 내용은 참조하십시오 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)에는 일부 내장 굴절 도구가 있습니다. 그러나이 작업을 수행하는 방법은 없습니다.

또한 시스템에 전혀 나쁜 코드를 갖고 싶지 않을 것입니다. 실수로 부름을받을 수 있기 때문입니다.

If the library is explicitly creating the bad class and not using a locater or dependency system you are out of luck. There is no way to override a method on another class unless you subclass. The solution might be to create a patch file that fixes the library, so you can upgrade the library and re-apply the patch to fix that specific method.

You might be able to do this with runkit. http://php.net/runkit

There's alway extending the class with a new, proper, method and calling that class instead of the buggy one.

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

(Sorry for the crappy formatting)

You can make a copy of the library class, with everything the same except the class name. Then override that renamed class.

It's not perfect, but it does improve the visibility of the extending class's changes. If you fetch the library with something like Composer, you'll have to commit the copy to source control and update it when you update the library.

In my case it was an old version of https://github.com/bshaffer/oauth2-server-php. I modified the library's autoloader to fetch my class file instead. My class file took on the original name and extended a copied version of one of the files.

Since you always have access to the base code in PHP, redefine the main class functions you want to override as follows, this should leave your interfaces intact:

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

You may for some reason use a private variable but then you will only be able to access the function by extending the class or logic inside the class. Similarly it's possible you'd want to have different objects of the same class have different functions. If so, do't use static, but usually you want it to be static so you don't duplicate the memory use for each object made. The 'ranOnce' code just makes sure you only need to initialize it once for the class, not for every $myObject = new third_party_library()

Now, later on in your code or another class - whenever the logic hits a point where you need to override the function - simply do as follows:

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

As a side note, if you do all your class functions this way and use a string-based key/value store like public static $functions['function_name'] = function(...){...}; this can be useful for reflection. Not as much in PHP as other languages though because you can already grab the class and function names, but you can save some processing and future users of your class can use overrides in PHP. It is however, one extra level of indirection, so I would avoid using it on primitive classes wherever possible.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top