55

典型的な継承を使用せずに、クラスまたはそのメソッドの一部を再定義する方法はありますか? 例えば:

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_librarytoなどに変更できないことを明確にする必要がありました。magical_third_party_library

私の目的のために、クラスに関数を追加することは可能でしょうか? 「部分クラス」と呼ばれるものを使用して、C# でこれを行うことができると思います。

4

13 に答える 13

44

モンキーパッチといいます。ただし、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\''
);
于 2008-09-26T00:07:31.737 に答える
14

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

少し粗いですが、うまくいきます!

于 2011-06-10T21:20:58.320 に答える
11

完全を期すために、PHP ではrunkitを介してモンキー パッチを利用できます。詳細については、 を参照してくださいrunkit_method_redefine()

于 2008-12-07T12:24:05.833 に答える
9

次のような別のクラスでラップするのはどうですか

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);
 }
}
于 2010-12-08T07:40:12.240 に答える
4

はい、それは呼ばれ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はそれについてかなり冗長です。

于 2008-09-26T00:08:00.313 に答える
1

Zend Studio と PDT (Eclipse ベースの IDE) には、屈折ツールが組み込まれています。しかし、これを行う組み込みのメソッドはありません。

また、システムに悪いコードをまったく入れたくないでしょう。間違って呼び出される可能性があるため。

于 2008-09-26T00:04:10.497 に答える
0

runkitを使用してこれを実行できる場合があります。http://php.net/runkit

于 2008-09-26T13:08:20.147 に答える
0

ライブラリが明示的に不正なクラスを作成していて、ロケーターまたは依存関係システムを使用していない場合は、運が悪いです。サブクラス化しない限り、別のクラスのメソッドをオーバーライドする方法はありません。解決策は、ライブラリを修正するパッチファイルを作成することです。これにより、ライブラリをアップグレードし、パッチを再適用して、その特定の方法を修正できます。

于 2008-09-26T00:14:59.847 に答える
0

クラス名以外はすべて同じで、ライブラリ クラスのコピーを作成できます。次に、名前が変更されたクラスをオーバーライドします。

完全ではありませんが、拡張クラスの変更の可視性が向上します。Composer などでライブラリをフェッチする場合は、コピーをソース管理にコミットし、ライブラリを更新するときにそれを更新する必要があります。

私の場合、それはhttps://github.com/bshaffer/oauth2-server-phpの古いバージョンでした。ライブラリのオートローダーを変更して、代わりにクラス ファイルをフェッチしました。私のクラス ファイルは元の名前を引き継ぎ、ファイルの 1 つのコピー バージョンを拡張しました。

于 2017-05-24T21:56:08.710 に答える
-1

新しい適切なメソッドでクラスを拡張し、バグのあるクラスの代わりにそのクラスを呼び出すことが常にあります。

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

(下手な書式ですみません)

于 2008-09-26T00:10:12.103 に答える