Ist es möglich, eine Funktion in PHP zu überschreiben?

73

Können Sie eine Funktion wie diese deklarieren ...

function ihatefooexamples(){
  return "boo-foo!";
};

Und dann etwas wie folgt neu deklarieren ...

if ($_GET['foolevel'] == 10){
  function ihatefooexamples(){
    return "really boo-foo";
  };
};

Ist es möglich, eine Funktion auf diese Weise zu überschreiben?

Wie auch immer?

Mark Lalor
quelle
Es wäre wunderbar, wenn PHP so erweitert würde, dass 'unset' verwendet werden könnte, um eine Funktionsdefinition zu löschen, die zur Neudefinition mit 'function' vorbereitet wurde.
David Spector

Antworten:

102

Bearbeiten

Um Kommentare zu adressieren, die in dieser Antwort nicht direkt auf die ursprüngliche Frage eingehen. Wenn Sie über eine Google-Suche hierher gekommen sind, beginnen Sie hier

Es gibt eine Funktion namens override_function , die tatsächlich zur Rechnung passt. Da diese Funktion jedoch Teil der Advanced PHP Debugger- Erweiterung ist, ist es schwierig, ein Argument zu liefern, override_function()das für die Verwendung in der Produktion vorgesehen ist. Daher würde ich "Nein" sagen, es ist nicht möglich, eine Funktion mit der Absicht zu überschreiben, die der ursprüngliche Fragesteller im Sinn hatte.

Ursprüngliche Antwort

Hier sollten Sie OOP nutzen, insbesondere Polymorphismus.

interface Fooable
{
    public function ihatefooexamples();
}

class Foo implements Fooable
{
    public function ihatefooexamples()
    {
        return "boo-foo!";
    }
}

class FooBar implements Fooable
{
    public function ihatefooexamples()
    {
        return "really boo-foo";
    }
}

$foo = new Foo();

if (10 == $_GET['foolevel']) {
    $foo = new FooBar();
}

echo $foo->ihatefooexamples();
Peter Bailey
quelle
18
+1 für die Lösung des eigentlichen Problems, anstatt nur zu versuchen, die genaue Frage zu beantworten ...
ircmaxell
2
Die einzige Änderung, die ich vornehmen würde, wäre, das $foo = new Foo()in einen elseBlock zu setzen, falls entweder der Konstruktor (a) teuer ist oder (b) Nebenwirkungen hat.
Austin Hyde
2
@ Gordon Du analysierst zu viel. Ich denke, es ist ziemlich klar, dass dies nur ein Beispiel ist.
Peter Bailey
7
Ich bin nicht einverstanden, mit dieser Antwort sagt die Frage eindeutig "Funktion", nicht "Methode", es ist klar, dass der Kontext keine Klasse ist. Ich bin von einer Google-Suche hierher gekommen. Momentan verwende ich eine Nicht-OO-Legacy-Bibliothek und diese Antwort ist einfach nutzlos.
Mastazi
4
@mastazi Vielen Dank, dass Sie mich darauf hingewiesen haben, dass dies eine Top-Antwort von Google Search auf diese Frage ist. Ich habe die Antwort mit Informationen aktualisiert, die zukünftigen Antwortsuchenden mit ähnlichen Zielen wie Ihren eigenen helfen sollen.
Peter Bailey
72

Affen-Patch im Namespace php> = 5.3

Eine weniger ausweichende Methode als das Ändern des Interpreters ist das Affenfeld.

Das Patchen von Affen ist die Kunst, die eigentliche Implementierung durch ein ähnliches "Patch" zu ersetzen.

Ninja-Fähigkeiten

Bevor Sie wie ein PHP-Ninja Affen-Patches erstellen können, müssen Sie zunächst die PHP-Namespaces verstehen.

Seit PHP 5.3 haben wir Namespaces kennengelernt, die Sie auf den ersten Blick als äquivalent zu Java-Paketen bezeichnen könnten, aber es ist nicht ganz dasselbe. Namespaces in PHP sind eine Möglichkeit, den Bereich zu kapseln, indem eine Fokushierarchie erstellt wird, insbesondere für Funktionen und Konstanten. Wie dieses Thema, Rückgriff auf globale Funktionen , zu erklären versucht.

Wenn Sie beim Aufrufen einer Funktion keinen Namespace angeben , sucht PHP zuerst im aktuellen Namespace und bewegt sich dann in der Hierarchie nach unten, bis die erste in diesem vorangestellten Namespace deklarierte Funktion gefunden und ausgeführt wird. Wenn Sie in unserem Beispiel print_r();von namespace My\Awesome\Namespace;PHP aus aufrufen , suchen Sie zuerst nach einer aufgerufenen Funktion My\Awesome\Namespace\print_r();und My\Awesome\print_r();dann, My\print_r();bis die in PHP integrierte Funktion im globalen Namespace gefunden wird \print_r();.

Sie können a function print_r($object) {}im globalen Namespace nicht definieren, da dies zu einer Namenskollision führt, da bereits eine Funktion mit diesem Namen vorhanden ist.

Erwarten Sie einen schwerwiegenden Fehler wie:

Fatal error: Cannot redeclare print_r()

Nichts hindert Sie jedoch daran, genau das im Rahmen eines Namespace zu tun.

Den Affen flicken

Angenommen, Sie haben ein Skript mit mehreren print_r();Aufrufen.

Beispiel:

<?php
     print_r($some_object);
     // do some stuff
     print_r($another_object);
     // do some other stuff
     print_r($data_object);
     // do more stuff
     print_r($debug_object);

Aber Sie ändern später Ihre Meinung und möchten, dass die Ausgabe <pre></pre>stattdessen in Tags eingeschlossen wird. Ist dir schon mal was passiert?

Bevor Sie loslegen und jeden Anruf ändern, sollten Sie print_r();stattdessen das Patchen von Affen in Betracht ziehen.

Beispiel:

<?php
    namespace MyNamespace {
        function print_r($object) 
        {
            echo "<pre>", \print_r($object, true), "</pre>"; 
        }

        print_r($some_object);
        // do some stuff
        print_r($another_object);
        // do some other stuff
        print_r($data_object);
        // do more stuff
        print_r($debug_object);
    }

Ihr Skript wird jetzt MyNamespace\print_r();anstelle des globalen Skripts verwendet\print_r();

Funktioniert hervorragend zum Verspotten von Unit-Tests.

nJoy!

Nickl-
quelle
3
Ein wirklich wertvoller Ansatz! Vielen Dank für das Teilen
Nico Haase
1
Dies ist eine unglaublich hilfreiche Antwort für diejenigen, die nicht mehr mit alten Bibliotheken zu tun haben!
Mastazi
1
Ich kann dies nicht zum Laufen bringen, wenn die Funktionen A, B und C in einer vorhandenen großen Include-Datei definiert sind (ich möchte diese nicht ändern, da andere Seiten sie verwenden) und ich nur die Funktion B im Hauptmenü überschreiben möchte Datei, wobei B in einer separaten Include-Datei definiert ist. Ich denke, Namespaces funktionieren in diesem Fall nicht.
David Spector
@DavidSpector Sie müssen den Namespace neu deklarieren, den Sie in einer separaten Include-Datei ex deklarieren möchten. namespace SeparateInclude {}Dann können Sie in der Hauptdatei wieder einen Namespace namespace MainFile {}erstellen und die neue Datei zum Erstellen eines Affen-Patches function B()erneut in die aufrufende Hauptdatei aufnehmen \SeparateInclude\B();. Wenn Sie B();jetzt \MainFile\B();die Hauptdatei aufrufen, wird aufgerufen, welche Aufrufe \SeparateInclude\B();erfolgreich \B();aus der großen / ursprünglichen Include-Datei vermieden werden.
Nickl
Bewegt es sich tatsächlich in den Namespaces nach unten, weil es nicht so scheint? Es scheint, dass A \ B \ C, das versucht, ein "f" zu verwenden, nur nach \ A \ B \ C \ f oder \ f ("oberste Ebene f") sucht. Die \ sind buchstäblich Namen, wobei \ am Anfang nur vorhanden ist spezielle Bedeutung (wie das Verketten oder Nichtverketten des aktuellen Namespace als Präfix oder das Nicht-Tun) - können Sie dies bestätigen? Weil es Sinn machen würde, wenn es so wäre! Aber es ist PHP, also werden sie das nicht tun (ich habe es ausgiebig gelesen und gelesen, das gab mir einen Hoffnungsschimmer - zerquetsche oder oder zeig mir, was ich vermisst habe, bitte! PHP5 wäre großartig für die Antwort BTW)
Alec Teal
25

Schauen Sie sich an override_function, um die Funktionen zu überschreiben.

override_function - Überschreibt integrierte Funktionen

Beispiel:

override_function('test', '$a,$b', 'echo "DOING TEST"; return $a * $b;');
Sarfraz
quelle
2
Dies ist ein guter Punkt, aber wenn die Funktion groß ist, ist es nicht schön, so lange Strings zu übergeben! :(, ein weiterer Punkt ist, dass dies nur für eingebaute Funktionen und nicht benutzerdefiniert verwendet werden kann!
RobertPitt
@RobertPitt: Einverstanden, dass dies das ist, was wir im Moment haben und Ihre Lösung gut aussieht, aber das hängt von der verwendeten PHP-Version ab.
Sarfraz
Warum sollten Sie nicht auf einer PHP-Version <5.3.0 sein wollen? Wenn Sie kein Skript erstellen, das im Einzelhandel erhältlich ist, haben Sie einen gültigen Punkt.
RobertPitt
@ RobertPitt: Ich benutze 5.3 und hoffe, dass OP das gleiche auch verwendet :)
Sarfraz
1
Ich habe versucht, die Override-Funktion zu verwenden, aber sie ist Teil von PECL, und PECL muss zuerst konfiguriert werden.
13

Die kurze Antwort lautet "Nein". Sie können eine Funktion nicht mehr überschreiben, wenn sie sich im PHP-Funktionsumfang befindet.

Sie können anonyme Funktionen wie diese am besten nutzen

$ihatefooexamples = function()
{
  return "boo-foo!";
}

//...
unset($ihatefooexamples);
$ihatefooexamples = function()
{
   return "really boo-foo";
}

http://php.net/manual/en/functions.anonymous.php

RobertPitt
quelle
Benötigen Sie das wirklich unset? Ich weiß, dass es nicht schlecht ist, nur neugierig (ich habe diesen speziellen Fall nicht ausprobiert) ...
ircmaxell
2
@ircmax Sie müssen nicht verwendenunset
NullUserException
Ich habe keine Ahnung von anonymen Funktionen. Ich weiß nur, wie man sie erstellt, aber ich habe nie mit ihnen gespielt. Ich verstehe, dass das Deaktivieren bei Überbeanspruchung teuer sein kann, aber je nach Größe der Funktion ist es möglicherweise besser!
RobertPitt
1
Ich weiß nicht, warum dies nicht den erforderlichen Verdienst hatte. Es ist der sauberste, benutzerfreundlichste und vernünftigste Ansatz. Das OP hat nicht nach einem OOP-Ansatz gefragt (im Gegenteil), und die Funktionalität von Drittanbietern funktioniert einfach nicht, daher ist dies die einzige Lösung.
Christian
Dies überschreibt keine Funktion, sondern ändert lediglich den Wert einer Variablen. Sie hätten das gleiche Ergebnis erzielt, wenn Sie den Teil der Schließung (anonyme Funktion) vollständig weggelassen hätten. Dies beantwortet die Frage nicht!
Nickl
11

Sie können keine Funktionen in PHP neu deklarieren. Sie können sie jedoch überschreiben. Überprüfen Sie die überschreibenden Funktionen sowie die Umbenennungsfunktionen , um die Funktion zu speichern, die Sie überschreiben, wenn Sie möchten.

Denken Sie also daran, dass Sie eine Funktion verlieren, wenn Sie sie überschreiben. Vielleicht möchten Sie es behalten, aber unter einem anderen Namen. Ich sage nur.

Wenn es sich um Funktionen in Klassen handelt, die Sie überschreiben möchten, müssen Sie lediglich eine Unterklasse erstellen und die Funktion in Ihrer Klasse neu deklarieren, ohne rename_function und override_function ausführen zu müssen.

Beispiel:

rename_function('mysql_connect', 'original_mysql_connect' );
override_function('mysql_connect', '$a,$b', 'echo "DOING MY FUNCTION INSTEAD"; return $a * $b;');
Tomlikestorock
quelle
16
Dies erfordert eine APD PECL-Erweiterung und ist veraltet. Neueste Version ist von 2004. pecl.php.net/package/apd
jperelli
5

Ich würde alle Funktionen eines Falls in eine includeDatei und die anderen in eine andere aufnehmen include.

Zum Beispiel simple.incwürde enthalten function boofoo() { simple }und really.incwürde enthaltenfunction boofoo() { really }

Es hilft bei der Lesbarkeit / Wartung Ihres Programms, da alle Funktionen der gleichen Art in der gleichen sind inc.

Dann oben in Ihrem Hauptmodul

  if ($_GET['foolevel'] == 10) {
    include "really.inc";
  }
  else {
    include "simple.inc";
  }
e2-e4
quelle
Wenn es erforderlich ist (scheint nicht so zu sein), kann man ein $iam = "really"oder $iam = "simple"oben in die Include-Dateien einfügen.
e2-e4
1
Ich denke, dies ist die beste Nicht-OOP-Antwort auf diese Frage, da sie eine prozedurale Umsetzung des Staatsmusters zeigt. (Obwohl ich eine Implementierung bevorzugen würde, bei der die ifs durch eine foreach-Schleife ersetzt werden.) Ich habe dieses Muster erfolgreich verwendet. Sie können durch Protokollierung herausfinden, welche Funktion verwendet wird. Die Protokollnachrichten haben eine Ebene, die in Produktions- oder Akzeptanzumgebungen nicht mehr verwendet wird.
Loek Bergman
3

Sie können die PECL-Erweiterung verwenden

Aber das ist meiner Meinung nach eine schlechte Praxis. Sie verwenden Funktionen, sehen sich jedoch das Designmuster des Dekorateurs an . Kann die Grundidee daraus ausleihen.

Gordon
quelle
1

Eine Lösung für den verwandten Fall, in dem Sie eine Include-Datei A haben, die Sie bearbeiten können und einige ihrer Funktionen in einer Include-Datei B (oder der Hauptdatei) überschreiben möchten:

Hauptdatei:

<?php
$Override=true; // An argument used in A.php
include ("A.php");
include ("B.php");
F1();
?>

Datei A einschließen:

<?php
if (!@$Override) {
   function F1 () {echo "This is F1() in A";}
}
?>

Datei B einschließen:

<?php
   function F1 () {echo "This is F1() in B";}
?>

Wenn Sie zur Hauptdatei blättern, wird " Dies ist F1 () in B " angezeigt .

David Spector
quelle
0

Abhängig von der Situation, in der Sie dies benötigen, können Sie möglicherweise anonyme Funktionen wie die folgenden verwenden:

$greet = function($name)
{
    echo('Hello ' . $name);
};

$greet('World');

... dann können Sie jederzeit eine neue Funktion für die angegebene Variable einstellen

esud
quelle