Sollten alle Plugins in einer Klasse gekapselt sein?

28

Sollten die Funktionen bei der Entwicklung eines Plugins zu einer Klasse zusammengefasst werden, um Namespace-Konflikte zu vermeiden?

Verursacht die Verwendung von Klassen einen Performance-Overhead für PHP?

Sollten bei einem Leistungstreffer stattdessen Funktionsnamen vorab festgelegt werden?

Jamie
quelle
8
Wahrscheinlich mehr PHP-Fragen als eine WordPress- Frage. Prüfen Sie , ob diese Stackoverflow-Frage Ihre Frage angemessen beantwortet.
t31os

Antworten:

24

Sollten die Funktionen bei der Entwicklung eines Plugins zu einer Klasse zusammengefasst werden, um Namespace-Konflikte zu vermeiden?

Ja, aber das ist nur eines der kleinen Argumente. Tatsächlich ist das nicht die "wahre" Natur einer Klasse in OOAD .

Verursacht die Verwendung von Klassen einen Performance-Overhead für PHP?

Nein, nicht besonders. Schlechtes Design und / oder schlecht geschriebener Code oder vorzeitige Optimierung verursachen weitaus mehr Leistungsprobleme als tatsächliche Sprachfunktionen.

Sollten bei einem Leistungstreffer stattdessen Funktionsnamen vorab festgelegt werden?

Wie geschrieben, gibt es keine Leistungseinbußen. Schlecht geschriebener Code ist eher ein Leistungsverlust als gut geschriebener Code, der mehr Codezeilen enthält, Sie jedoch nicht zu schlechten Dingen zwingt.


Endeffekt:

Sie können Klassen für Plugins unterschiedlich nutzen. Sie können sie nur verwenden, um eine Art Namespace zu haben, und sie "nur" für globale Funktionen verwenden. Die direkteste Form davon sind statische Klassenfunktionen. Das folgende Codebeispiel zeigt beide, zuerst globale Funktionen, dann globale statische Klassenfunktionen:

/* global function */
function myplug_hook()
{
}

add_filter('the_hook', 'myplug_hook');


/* global static function */
class myplug
{
    public static function hook()
    {
    }
}

add_filter('the_hook', 'myplug::hook');

Dies ist nur ein kleines Beispiel, das zeigt, dass Sie mehr für den einzelnen Haken eingeben müssen. Außerdem wird gezeigt, wie der Namespace funktioniert: Sie können den Namen einer einzelnen Klasse einfacher ersetzen, um alle statischen Funktionen umzubenennen und dann zu suchen und zu ersetzen, myplug::was myplug_aufgrund von Fehlalarmen schwieriger sein könnte . Aber am Ende gibt es keinen großen Unterschied.

Der entscheidende Punkt ist: Statische Klassenfunktionen Docs sind nicht wirklich viel anderes als globale Funktionen Docs .

Und dieses Beispiel zeigt auch: Namespacing ist in Ordnung, aber bei worpdress hört der Namespacing bei Verwendung von Hooks auf: Die Callback-Funktion ist hartcodiert, daher der Vorteil des Namespacing mit der Klasse (eine Stelle für den Basisnamen, den Klassennamen) nicht Hilfe, wenn Sie Ihren Code mit WordPress für die Hook-Namen eingreifen.

Der eigentliche Vorteil beginnt mit der Verwendung von tatsächlichen Klasseninstanzen und nicht statischen Funktionen. Dies hat den Vorteil, dass Sie anfangen können, OO-Prinzipien anzuwenden, und Ihren Code rationalisieren können. Statische Klassenfunktionen sind eher ein Problem als eine Lösung.

Dann ist es mehr als nur syntaktischer Zucker.

Entscheidend ist: Machen Sie etwas, das Ihnen hilft, den Code zu schreiben, mit dem Sie problemlos umgehen und den Sie warten können. Überbewerten Sie die Leistung nicht, das ist ein häufiger Fehler. Wichtiger ist, dass Sie Code schreiben, der einfach zu lesen und zu verstehen ist und der genau das tut, was Sie brauchen. Vielleicht ist diese Frage und Antwort hilfreich für ein größeres Bild in diesem Zusammenhang: Multiple Custom Metabox Help .

Ein gängiger Ansatz, den ich auch bei kleineren Plugins habe, ist die Verwendung einer statischen Hilfsfunktion, um das Plugin zu instanziieren. Der Rest befindet sich dann in der Plugin-Instanz. Dies hilft, die Haupt-Plug-in-Logik zu kapseln, und es hat den Vorteil, dass Namespaces mit den Hooks verwendet werden können, was mit globalen Standardfunktionen nicht möglich ist. Das folgende Codebeispiel zeigt das Muster:

<?php
/** Plugin Headers ... */

return MyPlugin::bootstrap(); 

class MyPlugin
{
    /** @var MyPlugin */
    static $instance;
    static public function bootstrap() {
        if (NULL === self::$instance) {
            self::$instance = new __CLASS__;
        }
        return self::$instance;
    }
    # ...
}

Dies ist ein allgemeines Muster, das ich für die Basis-Plugin-Datei verwende. Die Plugin-Klasse repräsentiert einerseits das Plugin für WordPress und ermöglicht andererseits, objektorientierte Paradigmen für den eigenen Code zu verwenden, die sogar vollständig objektorientiert sein können (aber nicht müssen). Es ist eine Art Controller, der mit der gesamten WordPress-API als Anfrage (n) verbunden ist.

Wie das Beispiel zeigt, wird eine Instanz des Plugins erstellt. Auf diese Weise können Sie bekannte Commons wie Constructor Docs ( __construct) verwenden , um das eigentliche Plugin zu initialisieren:

# ...
class MyPlugin
{
    # ...
    public function __construct()
    {
        add_filter('the_hook', array($this, 'hook'));
    }

    public function hook()
    {
    }
    # ...
}

Zum Zeitpunkt der Registrierung des Hooks profitiert dieses Plugin-Objekt bereits von seinem Design: Sie haben aufgehört, die eigentliche Hook-Funktion mit dem konkreten Plugin- Klassennamen fest zu codieren . Dies ist möglich, weil die Klasse für den Rückruf an die Objektinstanz gebunden ist. Klingt kompliziert, nur zu sagen: $this ist das Plugin. Kann in Hook-Rückrufen verwendet werden. Vergleichen Sie die Methoden von Registering Class als Hook-Rückrufe .

Dieses Muster ermöglicht eine einfachere Schnittstelle mit WordPress: Die Injektion wird auf die Namen der Hooks und die von ihnen bereitgestellten Daten reduziert. Sie können dann beginnen, direkt in diese Plugin-Klasse zu implementieren oder Ihre Implementierung dahingehend umzugestalten, dass nur Code in die Plugin-Klasse eingefügt wird, der das absolute Minimum für die Definition der Plugin-Schnittstelle gegen WordPress darstellt. Hier beginnt der Spaß und höchstwahrscheinlich das, was jeder Plugin-Autor auf lange Sicht erreichen möchte.

Also nicht mit Wurmkleid programmieren, sondern dagegen. Da worpdress sehr flexibel ist, gibt es keine gemeinsame oder einfach zu beschreibende Programmierschnittstelle. Eine Basis-Plug-in-Klasse kann diese Rolle übernehmen und bietet Ihnen mehr Flexibilität für Ihren eigenen Code, was zu einfacherem Code und einer besseren Leistung führt.

Es gibt also mehr als nur einen Vorteil für den Namensabstand. Der beste Vorschlag, den ich machen kann, ist: Versuchen Sie es selbst. Es gibt nicht viel zu verlieren, nur Neues zu entdecken.

Sie werden höchstwahrscheinlich Unterschiede bemerken, nachdem Sie einige größere Aktualisierungen von WordPress durchgeführt haben, während Ihr Plugin kompatibel bleibt.

Vorsichtsmaßnahme : Wenn Ihr Plugin direkt in WordPress integriert ist, um die Aufgabe zu erledigen, ist es möglicherweise besser, eine oder zwei öffentliche Funktionen zu verwenden. Nehmen Sie das richtige Werkzeug für den Job.

hakre
quelle
1
Wenn sich statische Klassenfunktionen wirklich nicht von globalen Funktionen unterscheiden und Sie Namensraumkonflikte vermeiden möchten, habe ich die Notwendigkeit, Plugins als Klassen zu schreiben, (noch) nicht verstanden. Ich bin auch verwirrt von Ihrer Helfer-Bootstrap-Funktion. Warum deklarieren Sie das neue Objekt nicht einfach als $ new_object = new MyClass () ;?
AlxVallejo
@AlxVallejo: Nur für Namespaces gibt es keine wirkliche Notwendigkeit (wie ich in der Antwort geschrieben habe, sind statische Klassenmethoden so ziemlich die gleichen wie globale Funktionen). Sie können den Namespace also selbst erstellen (der Namespace vor PHP 5.3). Das hast du also richtig gemerkt. Ähnlich return $myPlugin = new MyPlugin(); verhält es sich mit der statischen Bootstrap-Funktion: Technisch ist es nicht notwendig, eine einfache macht es auch. Für das Gesamtbild reicht ein einfaches neues Plugin jedoch möglicherweise nicht aus. Vergleichen Sie das WordPress-Plugin: Wie vermeide ich eine „enge Kopplung“? .
Hakre
9

Klassen VS-Funktionssatz


Performance

Allgemein: Afaik, es gibt keinen Unterschied in der "Leistung" zwischen Klassen und Funktionssätzen.

Detail:

  • Es gibt einen großen Unterschied, wenn Sie function_exists()vs. fragen. class_exists()Normalerweise haben Sie eine Menge Funktionen (~ 1.800 (?) Im WP-Kern) vs. Klassen (~ 100 (?) Im WP-Kern). Es ist also ein Unterschied in der Ausführungszeit, Dinge "steckbar" zu machen und somit die Existenz in Frage zu stellen .
  • Klassen bieten einen großen Vorteil gegenüber Funktionssätzen: Sie können es viel einfacher vermeiden, sie bei einer Anfrage aufzurufen, wenn Sie sie nicht benötigen, als mit Funktionen. Sie müssen nur bedingte Prüfungen für die Klasse und nicht für jede Funktion durchführen. Wenn Sie es also nicht bei jedem Seitenaufruf benötigen und nicht viele if / else-Anweisungen aufrufen müssen, ist eine Funktion "leistungsfähiger".

Architektur - So funktioniert's:

Funktionssatz: Im Allgemeinen werden Funktionen in der von Ihnen aufgerufenen Zeile ausgeführt. Jedes Mal, wenn Sie etwas anrufen, müssen Sie es erneut schreiben, wenn Sie es mehr als einmal aufrufen müssen.

Klasse: Es gibt verschiedene Herangehensweisen an Klassen. Die Klasse, die einem Funktionssatz am nächsten kommt, ist die Klasse "factory" ( wikipedia / google ). Imo es ist fast das gleiche wie eine Reihe von Funktionen, aber in einer Klasse eingekapselt. Es gibt aber auch andere "Arten" von Klassen. Sie können beispielsweise eine abstrakte oder übergeordnete Klasse schreiben , die Sie um eine untergeordnete Klasse erweitern. In einem realen Beispiel: Nehmen wir an, Sie haben eine Klasse, die einige statische Textfelder erstellt. In Ihrer __construct()Funktion haben Sie eine Reihe von Szenarien wie "left_column", "right_column" und "footer_field". Dann rufen Sie so etwas wie $text_field = new TextFieldClass();die Klasse zu instanziieren. Und später rufst du einfach an $text_field->add( $case => 'left_column', 'case' => 'foo text' );und$text_field->add( $case => 'footer_field', 'case' => 'bar text' );. Dann wurden alle Ihre Bedingungen und andere bereits ausgeführt, als Sie die Klasse instanziierten, und nur die beiden Klassenfunktionen wurden aufgerufen, als Sie die Textfelder erstellten. In diesem Szenario hätten Sie einige ms Ausführungszeit sparen können.


Persönliche Meinung

Wenn Sie Ihre Klassen mit Bedacht schreiben, haben Sie einen geringen Leistungsvorteil. Aber Sie haben eine gut organisierte Struktur, an der Sie arbeiten können. Soweit nichts Spektakuläres. Wenn Sie jedoch die folgenden "geteilten" Anwendungsfälle für Klassen und Funktionen in einem Plugin berücksichtigen, erhalten Sie meinen letzten Punkt: Klasse ist intern, Funktionen sind API . Solange Sie API nur über öffentlich nutzbare Funktionen (die dann Klassen oder Klassenfunktionen aufrufen) anbieten, sind Sie auf der sicheren Seite und entwickeln Ihr Plugin weiter. Sie haben die Freiheit erlangt, die interne Struktur oder sogar die Möglichkeiten Ihres Plugins zu ändern, ohne die Benutzer jederzeit und überall zu beeinträchtigen.

Beispiel:

// construction of object
if ( ! class_exists( 'WPSE_HelloWorld' ) )
{

class WPSE_HelloWorld
{
    function __construct( $args = array( 'text', 'html', 'echo' ) )
    {
        // call your object building procedures here
        $this->hello_world( 'text', 'html', 'echo' );
    }

    function hello_world( 'text', 'html', 'echo' )
    {
        $start_el = '<{$html}>';
        $end_el = '</{$html}>';
        if ( $echo )
        {
            return print "{$start_el}{$some}{$end_el}";
        }

        return "{$start_el}{$some}{$end_el}";
    }
} // END Class 

}

// API: public functions
function the_hello_world( $args( 'echo' => true ) )
{
    $new = new WPSE_HelloWorld();
    return $new->hello_world( $args );
}

function get_hello_world( array( $args( 'echo' => false) ) )
{
    $new = new WPSE_HelloWorld();
    return $new->hello_world( $args );
}

// then you can call it like get_the_title() or the_title(), which you know from the WP API:
// 'echo' is set to false per default:
$some_var = get_hello_world( array( 'text' => 'hello reader', 'html' => 'strong' ) );
# *returns* "<strong>hello reader</strong>"

// 'echo' is set to true per default:
the_hello_world( array( 'text' => 'hello reader', 'html' => 'strong' ) );
# *prints/echos* "<strong>hello reader</strong>"

Hinweis: Bitte lesen Sie auch den Link @ t310s im Kommentar zum Q.

Kaiser
quelle
nur neugierig, warum soll deine Plugin-Datei mehrmals in WordPress enthalten sein?
Hakre
@hakre Wo genau habe ich das gesagt? sry, ziemlich müde bei der mama.
Kaiser
1
@kaiser, ich nehme an, @hakre bezieht sich auf die if( ! class_exists )Zeile, die Sie am Anfang haben?
jjeaton
1
@hakre Ich nehme an, dass @kaiser den class_existsCheck nicht macht, weil er mehr als einmal enthalten sein könnte, sondern um einen Konflikt mit einer anderen Klasse zu vermeiden?
Michal Mau
Ja, ich habe mich über die class_exists gewundert.
Hakre
4

Es ist eine rein stilistische Entscheidung des Plugin-Autors. Es gibt keinen wirklichen Geschwindigkeitsunterschied.

Otto
quelle
1

Der Unterricht bietet in der Regel keine Vorteile in Bezug auf die Leistung, aber er hat auch nur sehr selten negative Auswirkungen. Ihr eigentlicher Vorteil besteht darin, den Code klarer zu gestalten und Namespace-Konflikte zu vermeiden.

Bainternet
quelle
Wie @hakre bereits erwähnt hat, unterscheiden sich die Namespace-Konflikte bei der Verwendung von Präfixen für globale Funktionen nicht wesentlich. "Cleaner" -Code und Verhindern von Namespace-Konflikten sind in diesem Fall auch nicht?
AlxVallejo
@AlxVallejo Ich denke schon :)
Bainternet
0

Wenn Sie Funktionen verwenden, werden Sie in der Regel den Namen des Plugins in jeden Funktionsnamen einfügen, sodass Sie diesen Namen effektiv ein Dutzend Mal duplizieren werden, wenn das Plugin ein Dutzend Funktionen enthält, was ein bisschen schwierig ist .

Bei Klassen ist der Name des Plugins wahrscheinlich nur einmal im Klassennamen enthalten.

Darüber hinaus können Sie Vererbung oder andere OO-Konstrukte verwenden, um Verhalten auf sehr saubere Weise zu implementieren. Hier ist eine Ex:

class animalplugin{
  //plugin functions...
  function talk(){print "animalnoise";}
}
class animalplugin_with_cat_mods extends abcplugin{
  //cat functions overrides
  function talk(){print "meow";}
}
if (iscat()){
  new animalplugin_with_cat_mods();
} else {
  new animalplugin();
}
zeedre
quelle