Klassenmethoden oder Klasse neu definieren

Lesezeit: 7 Minuten

Klassenmethoden oder Klasse neu definieren
SeanDowney

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

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

Was kann ich tun, um zu ersetzen buggy_function()? Das möchte ich natürlich gerne tun

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

Das ist genau mein Dilemma: Ich habe eine Bibliothek eines Drittanbieters aktualisiert, die meinen Code bricht. Ich möchte die Bibliothek nicht direkt ändern, da zukünftige Updates den Code erneut beschädigen könnten. Ich suche nach einer nahtlosen Möglichkeit, die Klassenmethode zu ersetzen.

Ich habe das gefunden Bücherei das sagt, es kann es tun, aber ich bin vorsichtig, da es 4 Jahre alt ist.

BEARBEITEN:

Ich hätte klarstellen sollen, dass ich die Klasse nicht umbenennen kann third_party_library zu magical_third_party_library oder irgendetwas anderes aufgrund von Framework-Einschränkungen.

Wäre es für meine Zwecke möglich, der Klasse einfach eine Funktion hinzuzufügen? Ich denke, Sie können dies in C # mit einer sogenannten “partiellen Klasse” tun.

  • PHP unterstützt das nicht. Sie können die Klasse erweitern und wiederverwenden. Das ist es. Es tut uns leid.

    – Bis

    26. September 2008 um 0:48 Uhr

  • Können Sie die ursprüngliche Buggy-Klasse umbenennen? Benennen Sie es in buggy_third_party um und erweitern Sie es selbst, indem Sie Ihrer Klasse den ursprünglichen Namen geben.

    – Gnud

    7. Dezember 2008 um 13:16 Uhr

1646316849 548 Klassenmethoden oder Klasse neu definieren
Jonathan Lonowski

Es heißt Affen patchen. Aber PHP hat keine native Unterstützung dafür.

Obwohl, wie andere auch darauf hingewiesen haben, die Runkit-Bibliothek steht zur Unterstützung der Sprache zur Verfügung und ist der Nachfolger von Klassensatz. Und obwohl es so aussah verlassen von seinem Ersteller (nachdem angegeben wurde, dass es nicht mit PHP 5.2 und höher kompatibel war), scheint das Projekt jetzt eine zu haben neues Zuhause und Betreuer.

Ich kann immer noch nicht sagen, dass ich ein Fan seines Ansatzes bin. Das Vornehmen von Modifikationen durch Auswerten von Code-Strings schien mir immer potenziell gefährlich und schwierig zu debuggen.

Immer noch, runkit_method_redefine scheint das zu sein, wonach Sie suchen, und ein Beispiel für seine Verwendung finden Sie in /tests/runkit_method_redefine.phpt im Depot:

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

  • Dies wurde als Antwort akzeptiert: Bedeutet das, dass PHP es unterstützt? ein paar mehr infos wären schön.

    – Nickf

    7. Dezember 2008 um 13:04 Uhr

  • @nickf: “Ich glaube nicht” ist eine Verneinung; Das OP bedeutet also “in PHP nicht unterstützt”. Soweit ich sagen kann, PHP unterstützt kein Monkey-Patching.

    – Piskvor verließ das Gebäude

    17. Februar 2010 um 9:16 Uhr


  • @Piskvor, na ja – ich kann lesen, aber es war merkwürdig, dass diese Antwort gegenüber den anderen akzeptiert wurde, die eine Art Anleitung boten.

    – Nickf

    17. Februar 2010 um 13:06 Uhr

Runkit scheint eine gute Lösung zu sein, aber es ist standardmäßig nicht aktiviert und Teile davon sind noch experimentell. Also habe ich eine kleine Klasse gehackt, die Funktionsdefinitionen in einer Klassendatei ersetzt. Beispielnutzung:

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

Fügen Sie dann die zu ändernde Klasse ein und definieren Sie ihre Methoden neu:

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

Dann den neuen Code auswerten:

eval($objPatch->getCode());

Ein wenig grob, aber es funktioniert!

  • das ist eine Reise! die eval-Anweisung definiert dann die Klasse mit der ersetzten Funktion? Also muss ich nur das Objekt instanziieren und damit ausführen?

    – SeanDowney

    10. Juni 2011 um 21:55 Uhr

  • Jawohl! Das ist alles, was Sie tun müssen.

    – JPhilly

    11. August 2011 um 21:06 Uhr

  • @SeanDowney, das scheint fast zu einfach, um wahr zu sein. Funktioniert diese Klasse die Box? Haben Sie die erwarteten Ergebnisse @SeanDowney erhalten? Würde mein Leben leichter machen.

    – Theunis

    9. Februar 2016 um 7:57 Uhr

  • @JPhilly Ich habe diesen Ansatz ausprobiert, bekomme aber einen schwerwiegenden Fehler: Klasse kann nicht neu deklariert werden. Wo soll ich den Evaluierungscode ausführen?

    – Theunis

    9. Februar 2016 um 8:38 Uhr

  • Sie sollten die Klasse überhaupt nicht laden lassen, also müssen Sie immer noch die Kontrolle darüber haben, ob Sie die Klasse benötigen oder nicht (wenn sie automatisch geladen wird, haben Sie Glück, wenn nicht, funktioniert das nicht). Auch diese Klasse könnte stark verbessert werden (bei Regex fehlen mehrere Leerzeichenprüfungen) und es würde nicht funktionieren, wenn die Klasse HTML-Teile in ihren Methoden oder PHP-Tags in der Mitte der Klasse offen/geschlossen hätte

    – Tofandel

    7. November 2019 um 14:18 Uhr

Für Leute, die immer noch nach dieser Antwort suchen.

Du solltest benutzen erweitert in Kombination mit Namensräume.

so was:

namespace MyCustomName;

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

Um es dann zu verwenden, gehen Sie folgendermaßen vor:

use MyCustomName\third_party_library;

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

  • das ist eine interessante Lösung. Der einzige Nachteil, den ich sehe, ist, wenn Sie das Verhalten der ursprünglichen Klasse an den Stellen ändern müssen, an denen sie verwendet wird.

    – Garet Claborn

    18. Oktober 2017 um 20:55 Uhr


  • Du hast meinem Projekt das Leben gerettet :). Danke für die Idee! In meinem Framework habe ich FrameworkFunction\RestrictContent definiert, und ich erlaube den anderen Entwicklern, CustomFunction\RestrictContent auszuführen, wenn sie etwas überschreiben müssen, ohne den Kernframeworkcode zu berühren. Dann, wenn ich ein if(class_exists()) verwende, um zu prüfen, ob der neue Namespace implementiert wurde, und um ihn zu verwenden, andernfalls zurück zur Klasse des Frameworks.

    – Zach Voss

    2. Mai 2020 um 14:58 Uhr


1646316850 366 Klassenmethoden oder Klasse neu definieren
Bis

Der Vollständigkeit halber – Affen-Patching ist in PHP durchgängig verfügbar Runkit. Einzelheiten finden Sie unter runkit_method_redefine().

Wie wäre es, es in eine andere Klasse zu packen

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

  • Dies ist perfekt zum Verpacken einer großen Klasse. Sie können auch die ursprüngliche PHP-Variable an den Konstruktor übergeben und mit $this austauschen. (praktisch für eine Hauptklasse, nicht so praktisch für viele Instanzen)

    – GDmac

    31. Dezember 2013 um 12:17 Uhr


  • Ich würde gerne ein Beispiel sehen, wie man das benutzt

    – Gerfried

    23. Juni 2020 um 16:08 Uhr

1646316850 366 Klassenmethoden oder Klasse neu definieren
Bis

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 habe “sd” vorangestellt. 😉

Denken Sie daran, dass beim Erweitern einer Klasse zum Überschreiben von Methoden die Signatur der Methode mit der des Originals übereinstimmen muss. Also zum Beispiel wenn das Original sagte buggy_function($foo, $bar)muss es mit den Parametern in der Klasse übereinstimmen, die es erweitert.

PHP ist ziemlich ausführlich darüber.

  • Dies ist perfekt zum Verpacken einer großen Klasse. Sie können auch die ursprüngliche PHP-Variable an den Konstruktor übergeben und mit $this austauschen. (praktisch für eine Hauptklasse, nicht so praktisch für viele Instanzen)

    – GDmac

    31. Dezember 2013 um 12:17 Uhr


  • Ich würde gerne ein Beispiel sehen, wie man das benutzt

    – Gerfried

    23. Juni 2020 um 16:08 Uhr

Klassenmethoden oder Klasse neu definieren
Ólafur Waage

Zend Studio und PDT (Eclipse-basierte IDE) haben einige eingebaute Refraktoring-Tools. Aber es gibt keine eingebauten Methoden, um dies zu tun.

Außerdem möchten Sie überhaupt keinen schlechten Code in Ihrem System haben. Da es versehentlich aufgerufen werden könnte.

924210cookie-checkKlassenmethoden oder Klasse neu definieren

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy