Frage

Gibt es eine Möglichkeit, eine Klasse oder einige seiner Methoden neu zu definieren, ohne typische Vererbung? Zum Beispiel:

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

Was kann ich tun buggy_function() zu ersetzen? Offensichtlich ist es das, was ich tun möchte

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

Das ist mein genaues Dilemma: Ich habe eine dritte Partei Bibliothek aktualisiert, die meinen Code bricht. Ich will nicht die Bibliothek direkt modifizieren, da zukünftige Updates den Code erneut brechen könnte. Ich bin für eine nahtlose Art und Weise der Klassenmethode zu ersetzen.

Ich habe diese rel="noreferrer">, die sagt, es kann es tun, aber ich bin vorsichtig wie es ist 4 Jahre alt.

EDIT:

Ich soll klargestellt, dass ich nicht die Klasse von third_party_library umbenennen magical_third_party_library oder irgendetwas anderes, weil der Rahmen Einschränkungen.

Für meine Zwecke wäre es möglich, nur eine Funktion der Klasse hinzufügen? Ich glaube, Sie können dies tun, in C # mit etwas ein als „partielle Klasse.“

War es hilfreich?

Lösung

Es ist Affe Patchen genannt. Aber, PHP nicht haben native Unterstützung für sie.

Obwohl, wie andere auch darauf hingewiesen haben, die runkit Bibliothek ist für das Hinzufügen von Unterstützung für die Sprache verfügbar und ist der Nachfolger von classkit . Und obwohl es zu sein schien gewesen aufgegeben von seinem Schöpfer (angegeben hat, dass es nicht vereinbar mit PHP 5.2 und höher), wird das Projekt nun erscheint eine rel="noreferrer">.

nicht sagen, dass ich ein Fan seines Ansatzes bin. Sie Modifikationen durch Code-Strings Auswertung haben immer zu mir scheinen potentiell gefährlich und schwierig zu debuggen zu sein.

Dennoch runkit_method_redefine erscheint, was Sie zu sein sind suchen, und ein Beispiel für seine Verwendung finden Sie in /tests/runkit_method_redefine.phpt im Repository:

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

Andere Tipps

runkit scheint wie eine gute Lösung, aber es ist nicht standardmäßig aktiviert und Teile davon sind noch experimentell aktiviert. So gehackt ich eine kleine Klasse zusammen, die Funktionsdefinitionen in einer Klassendatei ersetzt. Beispiel für die Verwendung:

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

Dann ist die Klasse geändert werden, und seine Methoden neu zu definieren:

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

eval dann den neuen Code:

eval($objPatch->getCode());

Ein wenig grob, aber es funktioniert!

Aus Gründen der Vollständigkeit - Affe Patchen in PHP durch verfügbar runkit . Weitere Einzelheiten finden Sie runkit_method_redefine().

Wie wäre es in einer anderen Klasse wie

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

Ja, es heißt extend:

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

ich mit dem Präfix "sd". ; -)

Beachten Sie, dass, wenn Sie eine Klasse erweitern Methoden außer Kraft zu setzen, Signatur der Methode der das Original entspricht. So zum Beispiel, wenn die ursprüngliche buggy_function($foo, $bar) sagte, hat sie die Parameter in der Klasse passen sie erstrecken.

PHP ist ziemlich ausführliche Informationen.

Für Menschen, die für diese Antwort suchen noch.

Sie sollten verwenden erstreckt in Kombination mit Namespaces .

wie folgt aus:

namespace MyCustomName;

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

Dann benutzt es wie folgt zu tun:

use MyCustomName\third_party_library;

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

Zend Studio und PDT (Eclipse-basierte IDE) haben in refractoring Tool einige gebaut. Aber es gibt keine in Methoden gebaut, dies zu tun.

Auch Sie würden gar nicht schlecht Code in Ihrem System haben. Da es sich bei irrtümlich genannt werden könnte.

Wenn die Bibliothek explizit die schlechte Klasse erstellen und keine locater oder Abhängigkeitssystem verwenden Sie kein Glück. Es gibt keine Möglichkeit, eine Methode auf eine andere Klasse außer Kraft zu setzen, wenn Sie eine Unterklasse. Die Lösung könnte darin bestehen, eine Patch-Datei zu erstellen, die die Bibliothek behebt, so können Sie die Bibliothek aktualisieren und erneut anwenden das Patch, das spezifische Verfahren zu beheben.

Möglicherweise können Sie dies mit runkit tun. http://php.net/runkit

Es alway ist die Klasse mit einem neuen, richtigen, Verfahren erstreckt, und diese Klasse anstelle des Buggys eines Aufruf.

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

(Sorry für die crappy Formatierung)

Sie können eine Kopie der Bibliothek Klasse machen, mit allem, was das gleiche, außer dem Klassennamen. Dann überschreibt, die umbenannte Klasse.

Es ist nicht perfekt, aber es hat die Sichtbarkeit der erweiterten Klasse der Änderungen zu verbessern. Wenn Sie die Bibliothek mit so etwas wie Komponisten holen, müssen Sie, um die Kopie verpflichten zu Quellcodeverwaltung und aktualisieren, wenn Sie die Bibliothek aktualisieren.

In meinem Fall war es eine alte Version von https://github.com/bshaffer/ oauth2-server-php . Ich änderte die Bibliothek des Autoloader meine Klassendatei anstatt zu holen. Meine Klassendatei auf dem ursprünglichen Namen hat und erweitert, um eine kopierte Version von einem der Dateien.

Da Sie immer Zugriff auf den Basiscode in PHP haben, neu zu definieren, die Hauptklasse Funktionen, die Sie außer Kraft setzen mögen, wie folgt, soll dies Ihre Schnittstellen intakt lassen:

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

Sie aus irgendeinem Grund kann eine private Variable verwenden, aber dann werden Sie nur in der Lage sein, die Funktion für den Zugriff durch die Klasse oder die Logik innerhalb der Klasse erstreckt. Ebenso ist es möglich, dass Sie verschiedene Objekte der gleichen Klasse haben unterschiedliche Funktionen haben wollen würden. Wenn ja, do't statisch verwenden, aber in der Regel wollen Sie es statisch sein, so dass Sie die Speicherverwendung nicht duplizieren für jedes Objekt gemacht. Der ‚ranOnce‘ Code nur macht Sie sicher, dass es nur für die Klasse einmal initialisiert werden müssen, nicht für jede $myObject = new third_party_library()

Nun, später in Ihrem Code oder einer anderen Klasse - immer dann, wenn die Logik einen Punkt trifft, wo Sie die Funktion außer Kraft setzen müssen - einfach wie folgt vor:

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

Als Seite beachten, wenn Sie alle haben Ihre Klassenfunktionen auf diese Weise und verwenden Sie einen String-basierten Schlüssel / Wert-Speicher wie public static $functions['function_name'] = function(...){...}; kann dies für die Reflexion von Nutzen sein. Nicht so viel in PHP als andere Sprachen aber, weil Sie bereits die Klassen- und Funktionsnamen greifen, aber Sie können einige Verarbeitung und zukünftigen Benutzer Ihrer Klasse speichern Überschreibungen in PHP verwenden können. Es ist jedoch eine zusätzliche Dereferenzierungsebene, also würde ich vermeiden, dass es auf primitive Klassen, wo immer möglich verwendet wird.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top