Objektorientiertes automatisches Laden in WordPress, Teil 2

In dem vorherigen Tutorial haben wir eine Handvoll Konzepte behandelt, die alle notwendig sein werden, um vollständig zu verstehen, was wir in diesem Tutorial tun.

Konkret behandelten wir die folgenden Themen:

  • objektorientierte Schnittstellen
  • das Prinzip der Einzelverantwortung
  • wie diese in PHP aussehen
  • wohin gehen wir mit unserem plugin

In manchen Serien ist es einfach, Tutorials zu überspringen, die möglicherweise nicht aufeinander aufbauen. Diese Serie soll jedoch nicht so sein. Stattdessen ist es dazu gedacht, in sequentieller Reihenfolge gelesen zu werden, und es soll auf den Inhalt jedes vorherigen Tutorials aufbauen.

Ich nehme an, Sie sind alle auf dem Laufenden. 

Fertig machen

Auch wenn ich dies vielleicht im ersten Tutorial erwähnt habe, möchte ich trotzdem sicherstellen, dass wir alle auf der gleichen Seite sind, was wir in jedem Tutorial tun und welche Software Sie benötigen.

Unsere Roadmap

In diesem Lernprogramm sieht der Plan wie folgt aus:

  1. Untersuchen Sie den Code, den wir bisher geschrieben haben.
  2. Bestimmen Sie, wie wir es möglicherweise mithilfe objektorientierter Techniken ändern können.
  3. Geben Sie die übergeordnete Übersicht für unsere Implementierung an.

Letztendlich werden wir nicht schreiben viel Code in diesem Tutorial, aber wir werden etwas schreiben. Es ist jedoch ein praktisches Tutorial, in dem wir objektorientierte Analyse und Design durchführen. Dies ist eine notwendige Phase für viele große Projekte (und etwas, das für kleine Projekte geschehen sollte)..

Was du brauchst

Wenn Sie mitverfolgt haben, sollten Sie dies bereits eingerichtet haben. Aber um sicher zu gehen, hier ist die kurze Version von allem, was Sie brauchen:

  • eine lokale Entwicklungsumgebung, die für Ihr Betriebssystem geeignet ist
  • ein Verzeichnis, in dem WordPress 4.6.1 gehostet wird
  • ein Texteditor oder eine IDE
  • Kenntnis der WordPress Plugin API

Wenn all dies vorhanden ist, können wir an dem Code arbeiten, der im vorherigen Tutorial verwendet wurde. Also lasst uns anfangen.

Code analysieren

Als erstes wollen wir den aktuellen Zustand unseres Autoloaders analysieren. Es mag wie eine Menge Code aussehen, den Sie in einen einzelnen Codeblock einfügen können, aber das zeigt schon, dass wir noch einiges zu tun haben.

Hier ist der aktuelle Stand unseres Autoloaders:

 0; $ i--) // Liest die aktuelle Komponente des Dateiteils. $ current = strtolower ($ file_parts [$ i]); $ current = str_ireplace ('_', '-', $ current); // Wenn wir beim ersten Eintrag sind, dann beim Dateinamen. if (count ($ file_parts) - 1 === $ i) / * Wenn 'interface' in den Teilen des Dateinamens enthalten ist, * definieren Sie den $ file_name anders, damit er ordnungsgemäß geladen wird. * Andernfalls setzen Sie einfach $ file_name auf die Struktur der Klasse * Dateiname. * / if (strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'interface')) // Ergreifen Sie den Namen der Schnittstelle aus ihrem qualifizierten Namen. $ interface_name = explodieren ('_', $ file_parts [Anzahl ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; $ file_name = "interface- $ interface_name.php";  else $ file_name = "class- $ current.php";  else $ namespace = '/'. $ current $ namespace;  // Erstellen Sie nun einen Pfad zur Datei, indem Sie den Dateispeicherort zuordnen. $ filepath = trailingslashit (dirname (dirname (__FILE__)). $ namespace); $ filepath. = $ dateiname; // Wenn die Datei im angegebenen Pfad vorhanden ist, schließen Sie sie ein. if (file_exists ($ filepath)) include_once ($ filepath);  else wp_die (esc_html ("Die Datei, die versucht, unter $ filepath geladen zu werden, ist nicht vorhanden.")); 

Denken Sie an dieser Stelle daran, dass das Prinzip der Einzelverantwortung Folgendes festlegt:

Eine Klasse sollte nur einen Grund haben, sich zu ändern.

Im Moment haben wir nicht einmal eine Klasse, geschweige denn mehrere einzelne Methoden, die nur einen einzigen Grund haben, um sie zu ändern.

Und obwohl es sinnvoll sein könnte, diese Autoloader-Methode in kleinere, einzelne Methoden zu zerlegen, beginnen wir auf einer höheren Ebene und denken über einen Autoloader in Bezug auf eine Schnittstelle nach. Dann werden wir einen Drilldown machen, um eine Klasse (oder Klassen) zu erstellen..

Objektorientierte Analyse: Verantwortlichkeiten

Erinnern Sie sich aus dem vorherigen Tutorial, dass eine Schnittstelle im PHP-Handbuch wie folgt definiert ist:

Mit Objektschnittstellen können Sie Code erstellen, der angibt, welche Methoden eine Klasse implementieren muss, ohne festzulegen, wie diese Methoden behandelt werden.

Lassen Sie uns angesichts des Codes und der obigen Definitionen darüber nachdenken, was ein Autoloader aus einer modularen Perspektive tun muss. Lassen Sie uns es in Punkte unterteilen, die repräsentieren, was möglicherweise genug ist, um sich zu ändern. Nein, wir können nicht alle diese Punkte verwenden, aber deshalb wird es Analyse genannt. Wir werden später am Entwurf arbeiten.

Der Code bewirkt Folgendes:

  1. Überprüft, dass wir explizit mit unserem Namespace arbeiten.
  2. Teilt den eingehenden Klassennamen in Teile, um zu bestimmen, ob es sich um eine Klasse oder ein Interface handelt (so $ class_name ist ein schlechter Variablenname).
  3. Überprüft, ob wir mit einer Schnittstellendatei arbeiten.
  4. Überprüft, ob wir mit einer Klassendatei arbeiten.
  5. Überprüft, ob wir mit einer Schnittstelle arbeiten.
  6. Erzeugt basierend auf dem Ergebnis der obigen Bedingungen einen Dateinamen.
  7. Erstellt einen Dateipfad basierend auf dem generierten Dateinamen.
  8. Wenn die Datei unter dem generierten Namen vorhanden ist, wird sie hinzugefügt.
  9. Andernfalls generiert der Code einen Fehler.

Der obige Code funktioniert also neun Dinge-das ist es wenigstens Neun Gründe zu ändern, bevor die Arbeit abgeschlossen ist. 

Dies sollte selbstverständlich sein, aber diese besondere Funktion ist ein perfektes Beispiel, das wir umgestalten können, um objektorientierte Analyse, Design, Schnittstellen und Implementierung zu demonstrieren.

Und das wirft eine Frage auf: Wo fangen wir überhaupt an??

Objektorientierte Analyse

An diesem Punkt ist es fair zu sagen, dass wir mit der objektorientierten Analyse beginnen können. Das heißt, wir schauen uns an, welche potenziellen Klassen wir haben könnten und wie sie miteinander interagieren, wenn wir alles, was wir oben aufgelistet haben, zusammenbringen. Denken Sie daran, wir möchten auch, dass der Grundsatz der Einzelverantwortung uns bei der Entscheidungsfindung unterstützt.

An diesem Punkt sind wir nicht besonders besorgt darüber, wie die Klassen miteinander kommunizieren werden. Stattdessen konzentrieren wir uns mehr auf das Erstellen von Klassen, die aus einem einzigen Grund geändert werden müssen.

Nachdem dies gesagt wurde, werde ich ein Beispiel an Klassen anbieten, die meiner Meinung nach funktionieren könnten. Bevor Sie fortfahren, schauen Sie sich an, was wir getan haben und versuchen Sie, Ihre eigene Liste aufzustellen. Dann können wir Notizen vergleichen.

Ein Wort über Fähigkeiten

Beachten Sie, dass Sie möglicherweise eine bessere Idee haben als die unten aufgeführten, oder Sie nehmen etwas von dem, was wir geteilt haben. Unabhängig davon ist dies eine Lernübung. Wir versuchen, unseren Code und unsere Organisation zu verbessern und letztendlich bessere Programmierer zu werden.

Unsere potentiellen Klassen

In Anbetracht dessen, was ich oben aufgeführt habe, habe ich folgende Klassen gefunden:

  1. Autoloader. Dies ist die Hauptklasse, die letztendlich unsere Klasse, unseren Namespace oder unser Interface umfasst. Wir nennen diese Klasse. Der Rest sind Klassen, die sich um die notwendige Arbeit kümmern, die diese eine Klasse zum Einschließen der Datei benötigt. 
  2. NamespaceValidator. Diese Datei prüft die eingehende Klasse, die Schnittstelle oder was Sie haben und bestimmt, ob sie gültig ist. Dies gibt uns den entscheidenden Faktor, wenn wir mit dem Rest unseres Codes nicht fortfahren können. 
  3. FileInvestigator. Diese Klasse untersucht den Dateityp, der an den Autoloader übergeben wird. Es ermittelt, ob es sich um eine Klasse, eine Schnittstelle oder einen Namespace handelt, und gibt den vollständig qualifizierten Pfadnamen an die Datei zurück, damit er eingeschlossen werden kann.
  4. FileRegistry. Dabei wird der vollständig qualifizierte Dateipfad verwendet, der letztendlich von den anderen Klassen zurückgegeben wird, und wird in das Plugin aufgenommen.

Und das ist es. Jetzt müssen Drittanbieter-Klassen in unserem Plugin nur etwas über die Autoloader-Klasse wissen, aber der Autoloader benötigt Kenntnisse über eine andere Klasse und andere Klassen benötigen Kenntnisse über andere Klassen.

Dort sind Möglichkeiten, dies zu handhaben (mithilfe von Abhängigkeitseinspritzungscontainern, aber das würde den Rahmen dieses Projekts sprengen). Was wir jedoch mit unserem Code erreichen möchten, ist die Minimierung, wie viele Klassen voneinander wissen.

Objektorientiertes Design

Zu diesem Zeitpunkt werden verschiedene Entwickler, Firmen, Agenturen und Teams einen anderen Ansatz wählen, wie sie das System entwerfen, an dem sie arbeiten.

Eine der gebräuchlichsten Methoden, dies zu tun, ist die Verwendung eines sogenannten UML-Diagramms. Obwohl es nützlich ist, lohnt es sich nicht, im Rahmen dieses Tutorials etwas zu tun, da es ein ganz anderes Tutorial erfordert, um alle Teile zu erklären.

Für die Zwecke unseres Tutorials und da wir mit so wenig Code arbeiten, versuchen wir herauszufinden, wie die einzelnen Klassen funktionieren können, bevor wir sie implementieren. Auf diese Weise erhalten wir eine Vorstellung davon, wie wir unseren Code organisieren können.

Beachten Sie, dass wir noch keinen Namensraum für diesen Code haben werden, und dieser Code sollte noch nicht mit WordPress implementiert oder getestet werden. Wir werden im nächsten Tutorial darauf eingehen.

Beginnen wir mit dem Autoloader und von dort aus arbeiten.

Autoloader

Denken Sie daran, dass diese Klasse für die Aufnahme der erforderlichen Datei verantwortlich ist. Dies ist die Datei, die beim registriert wird spl_autoload_register Funktion. 

namespace_validator = neuer NamespaceValidator (); $ this-> file_registry = new FileRegistry ();  öffentliche Funktion load ($ filename) if ($ this-> namespace_validator-> is_valid ($ filename)) $ this-> file_registry-> load ($ filename);  

Beachten Sie, dass diese Klasse von der Klasse abhängt NamespaceValidator und das FileRegistry Klasse. Wir werden jeden von ihnen in einem Moment genauer betrachten.

NamespaceValidator

Diese Datei prüft den eingehenden Dateinamen und ermittelt, ob er gültig ist. Dies geschieht durch Betrachten des Namespaces im Dateinamen.

Wenn die Datei tut In der Tat gehören wir zu unserem Namensraum, dann können wir davon ausgehen, dass es sicher ist, unsere Datei zu laden.

FileInvestigator

Diese Klasse macht ziemlich viel Arbeit, obwohl ein Teil davon mit sehr einfachen, sehr kleinen Hilfsmethoden durchgeführt wird. Im Verlauf der Ausführung wird der Typ der übergebenen Datei geprüft. 

Anschließend wird der vollständig qualifizierte Dateiname für den Dateityp abgerufen.

get_file_name ($ file_parts, $ current, $ i); if (count ($ file_parts) - 1! == $ i) $ filepath = trailingslashit ($ filepath);  return $ filepath;  private Funktion get_file_name ($ file_parts, $ current, $ i) $ filename = "; if (count ($ file_parts) - 1 === $ i) if ($ this-> is_interface ($ file_parts)) $ Dateiname = $ this-> get_interface_name ($ file_parts); else $ dateiname = $ this-> get_class_name ($ current); else $ Dateiname = $ this-> get_namespace_name ($ current); return $ filename;  private Funktion is_interface ($ file_parts) return strpos (strtolower ($ file_parts [Anzahl ($ file_parts) - 1]), 'interface'); private Funktion get_interface_name ($ file_parts) $ interface_name = explode ('_', $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; return "interface- $ interface_name.php"; private Funktion get_class_name ($ current) return "class- $ current.php" ; private Funktion get_namespace_name ($ current) return '/'. $ current;

Wenn es eine Datei gibt, die ein wenig mehr überarbeitet werden kann, dann ist dies die richtige. Schließlich versucht es festzustellen, ob wir mit einer Klasse, einem Interface oder einer Klasse arbeiten. Eine einfache Fabrik könnte dafür besser geeignet sein.

Wenn es an der Zeit ist, unseren Code zu implementieren, werden wir dies möglicherweise weiter überarbeiten. Bis dahin ist dies ein vorläufiger Entwurf, der gut genug funktionieren kann.

FileRegistry

Dabei wird der vollständig qualifizierte Dateipfad verwendet und die Datei eingeschlossen. Andernfalls wird die WordPress-API verwendet, um eine Fehlermeldung anzuzeigen.

Klasse FileRegistry privater $ investigator; öffentliche Funktion __construct () $ this-> investigator = new FileInvestigator ();  public function load ($ filepath) $ filepath = $ this-> investigator-> get_filetype ($ filepath); $ filepath = rtrim (plugin_dir_path (dirname (__FILE__)), '/'). $ filepath; if (file_exists ($ filepath)) include_once ($ filepath);  else wp_die (esc_html ('Die angegebene Datei ist nicht vorhanden.'));  

Eine andere Alternative zur Verwendung der WordPress-API wäre das Auslösen einer benutzerdefinierten Ausnahmemeldung. Auf diese Weise können wir unseren Code vollständig von WordPress trennen oder entkoppeln.

Dieser Code ist wieder eine Verschleppung vom ursprünglichen Autoloader. Während der Implementierung können wir dies auch ändern.

Fazit

In Ordnung, also haben wir uns den vorhandenen Code für unseren Autoloader angesehen, und dann haben wir einige potenzielle Codes verdrängt, die wir basierend auf objektorientierter Analyse und Design verwenden können.

Ist die Lösung, an der wir arbeiten, wartbarer als die, die wir haben? Absolut. Funktioniert dies im Kontext von WordPress und unserem vorhandenen Plugin? Wir werden es nicht wissen, bis wir damit beginnen, unser Plugin zu nutzen.

Wie bereits erwähnt, gibt es immer noch einige Bereiche, in denen wir diesen Code möglicherweise umgestalten könnten. Wenn wir bei der Implementierung unseres Codes in der endgültigen Version unseres Plugins auf diese Art von Problemen stoßen, schauen wir uns genau das an.

Wie auch immer, der Code, den wir jetzt haben, sollte besser lesbar sein (obwohl wir noch DocBlocks und einige Inline-Kommentare einführen müssen) und wartungsfreundlicher und sogar noch testbarer.

Ich hoffe, dass dies Ihnen eine Vorstellung davon vermittelt, wie Sie eine lange Methode anwenden und sie in zweckorientiertere Klassen unterteilen können. Sicher, mehrere Klassen zu haben, mag sich auf den ersten Blick komisch anfühlen, aber das bedeutet nicht, dass es eine schlechte Sache ist. Habe mehr Dateien (und damit Klassen) mit weniger Code als eine Datei mit viel Code ist besser.

Umfassen Sie in dieser Hinsicht den nicht eingängigen Charakter der objektorientierten Programmierung. Im nächsten Tutorial kehren wir zu unserem Plugin zurück und arbeiten an der Implementierung einer Variation des obigen Codes. Wir werden wahrscheinlich auch etwas davon debuggen. Denn wir machen es beim ersten Mal selten richtig

Wenn Sie bis dahin mehr über die objektorientierte Programmierung in WordPress erfahren möchten, finden Sie alle meine vorherigen Tutorials auf meiner Profilseite. Fühlen Sie sich frei, meinem Blog zu folgen oder folgen Sie mir auf Twitter, wo ich häufig über beides spreche.

Ressourcen

  • Objektorientiertes automatisches Laden in WordPress, Teil 1
  • Namensräume
  • Autoloading
  • Schnittstellen
  • Die WordPress Plugin-API
  • Grundsatz der Einzelverantwortung