marcus.kober_abstract_representation_of_modern_PHP_techniques_c_18872fab-ded8-41a4-8dd4-4bd9556baa9e

Autoloading, Coding Standards und Ordnerstruktur in der WordPress-Plugin-Entwicklung

Im letzen Artikel habe ich die Verwendung von Namespaces erklärt, diese benötigen wir heute, um das Autoloading zu etablieren, das gleichzeitig unsere Ordner-Struktur definiert.

Klassen in eigenen PHP-Dateien deklarieren

Um unseren Code übersichtlich zu halten, verteilen wir den Code sinnvoll auf mehrere PHP-Dateien. Jede Klasse sollte in einer eigenen Datei deklariert werden. Diese Datei muss dann im Haupt-Code zunächst geladen werden (z.B. über require_once()), bevor die Klasse instanziiert werden kann.

Gehen wir einmal von folgender Ordernstruktur eines Beispiel-Plugins aus:

my-plugin
├── class-01.php
├── class-02.php
└── my-plugin.php

Inhalt class-01.php:

<?php
class class01
{
    public function __construct()
    {
        echo 'class01 instantiated.';
    }
}

Möchten wir nun in der Plugin-Datei my-plugin.php die Klasse class01 instanziieren, müssen wir die Datei zunächst einbinden und können dann die Klasse instanziieren:

<?php
/**
 * Plugin Name: MyPlugin
 */

require_once __DIR__ . '/class-01.php';

$klasse01 = new class01(

Benötigen wir auch class02 in diesem Skript, müssen wir ebenfalls zunächst die Datei einbinden und können die Klasse dann instanziieren:

<?php
/**
 * Plugin Name: MyPlugin
 */

require_once __DIR__ . '/class-01.php';
require_once __DIR__ . '/class-02.php';

$klasse01 = new class01();
$klasse02 = new class02();

Dies kann schon bei einer geringen Anzahl von Klassen schnell unĂĽbersichtlich werden und zu langen require_once()-Listen fĂĽhren.

Nun ändert sich eine Anforderung an unser Plugin und wir müssen einen Service einführen, der z.B. von class02 verwendet werden muss.

Wir fĂĽgen unseren Service als neue Klasse in die Datei service-01.php ein:

my-plugin
├── class-01.php
├── class-02.php
├── my-plugin.php
└── service-01.php
<?php
class service01
{
    public function __construct()
    {
        echo 'service01 instantiated.';
    }
}

Und ändern class02:

<?php
class class01
{
    protected service01 $service01;

    public function __construct(service01 $service01)
    {
        $this->service01 = $service01;

        echo 'class02 and service01 instantiated.';
    }
}

Nun müssen wir in der Hauptdatei des Plugins zunächst die Datei des Service einbinden:

<?php
/**
* Plugin Name: MyPlugin
*/

require_once __DIR__ . '/class-01.php';
require_once __DIR__ . '/class-02.php';
require_once __DIR__ . '/service-01.php';

$service01 = new service01();

$klasse01 = new class01();
$klasse02 = new class02($service01);

Und spätestens hier wird es unübersichtlich, es folgt zunächst eine lange Liste mit require_once() und wir müssen bei jeder neuen Klasse, die wir benötigen, diese Liste noch länger machen. Darüber hinaus sieht unsere Ordnerstruktur auch nicht mehr sehr übersichtlich aus. Im nächsten Schritt könnte also ein inc-Ordner eingeführt werden, in dem alle Datein liegen, die wir per require_once() einfügen möchten:

my-plugin
├── inc
│   ├── class-01.php
│   ├── class-02.php
│   └── servie-01.php
└── my-plugin.php

Im Grunde ist an dieser Stelle auch noch nichts gegen einen solchen Aufbau einzuwenden. Wird das Plugin umfangreicher und wir haben unterschiedliche Klassen wie Models, Services und Klassen mit Hooks, brauchen wir auch eine Ordnerstruktur, die all die neuen Datein sinnvoll und ĂĽbersichtlich gliedert.

Darüber hinaus möchten wir von der dann sehr langen Liste an require_once()-Aufrufen wegkommen.

Vorbereitung auf das Autoloading und Code-Standards

Autoloading bezeichnet das automatische Laden von benötigten Dateien und um dies verlässlich nutzen zu können, müssen wir unseren bisherigen Code zum einen mit Namespaces ausrüsten und zum anderen sollten wir bestimmten Coding-Standards folgen. Coding-Standards betreffen hier sowohl den Code selbst, als auch die Ordner-Struktur, die wir verwenden werden. Diese Standards sorgen dafür, ein wenig mehr Ordnung in das Chaos zu bringen, das PHP generell erlaubt. Es gibt Standards für das Einrücken von Code (der ewige Krieg: Tabs oder Spaces?), die Benennung von Variablen, Funktionen, Klassen und Methoden usw.

Wozu sind Standards wichtig?

Standards im Coding fĂĽhren zu mehr Ăśbersichtlichkeit und sie geben generell ein Schema vor, wie etwas aussehen sollte, damit sich Entwickler*innen nicht auch noch darum Gedanken machen mĂĽssen und damit sich – vor allem im Team – jeder schnell und einfach in fremden Code einfinden kann.

Coding-Standards

Weil es ja sonst zu einfach wäre, gibt es unterschiedliche Standards. 🙂 Coding-Standards werden von unterschiedlicher Stelle definiert. Es gibt Standards, die WordPress selbst festlegt, Standards, die von der PHP Framework Interoperability Group (PHP-FIG) festgelegt werden und auch einzelne Firmen und Agenturen können natürlich ihre eigenen Standards definieren.

Uns interessieren an dieser Stelle primär zwei Definitionen:

An welche Standards halten wir uns denn nun?

Die Antwort, die dir nicht gefallen wird: it depends.

Es kommt auf deinen eigenen Geschmack an und es gibt hier kein richtig und kein falsch. Du kannst auch deine eigenen Standards definieren (was ich aber nicht empfehle). Wichtig ist, sich tatsächlich an eine Sache zu halten.

An welche Standards werden wir uns in dieser Serie halten?

Ich möchte dir nicht vorschreiben, an welche Standards du dich hältst, aber ich halte mich an die PSR, da sie auch von anderen PHP-Frameworks wie Symfony und Laravel eingehalten werden. Wenn du auf PSR setzt, bist du nicht im WordPress-Universum gefangen, sondern kannst nahtlos auch an Symfony- oder Lavarel-Projekten arbeiten. Hier gehen allerdings die Meinungen auseinander, es gibt zahlreicher Verfechter des WordPress-Standards, wenn schon für WordPress programmiert wird. Möchtest du einmal Core-Entwickler werden, solltest du auf jeden Fall wissen, dass es unterschiedliche Coding-Standards gibt.

Bitte nimm dir – falls du PSR noch nicht kennst – die Zeit, und sieh dir die folgenden PSRs einmal an:

  • PSR-1: Basic Coding Standard
  • PSR-2: Coding Style Guide
  • PSR-12: Extended Coding Style

WICHTIG:

PHP-Dateien, die entweder reinen PHP-Code enthalten, oder aber mit einem PHP-Code-Block enden, dĂĽrfen keinen schlieĂźenden PHP-Tag (?>) enthalten (siehe PSR-2, 2.2 Files)! Dies kann vor allem in der Plugin-Entwicklung zu einem Headers already sent-Fehler fĂĽhren, wenn nach dem ?> noch Whitespace oder eine Leerzeile folgt.

Standard fĂĽr die Ordnungstruktur

Da wir für das Autoloading in der Ordner-Benennung den Namspaces folgen müssen, möchte ich für die Ordnerstruktur keine festen Standards definieren. Das ist letztlich gerade in der WordPress-Plugin-Entwicklung etwas mehr Geschmackssache. Frameworks sind da allerdings wesentlich strenger.

Für später (und auch für das PSR-4-Autoloading, siehe weiter unten) ist es allerding wichtig, dass ausser der Plugin-Hauptdatei alle PHP-Dateien innerhalb eines src-Ordners liegen. Dies wird vor allem dann wichtig, wenn wir zu den Unit Tests kommen, da so der eigentliche Sourcecode in src und alle zugehörigen Tests in tests liegen. Aber auch für das Autoloading ist dies, wie wir gleich sehen werden, wichtig.

Unser bisheriger Beispiel-Code wird nun standardisiert

Um das Autoloading gut etablieren zu können, richten wir unseren Code nun komplett an den Standards aus und führen Namespaces ein. Folgende Änderungen führen wir durch:

  • Klassennamen werden in StudlyCaps geschrieben, also Klasse01 statt klasse01
  • die Datei-Namen sind identisch mit den Klassennamen, also Klasse01.php statt klasse-01.php
  • alle Dateien in src erhalten Namespaces, dabei ist der Basis-Namespace MKMyPlugin nach dem Muster HerstellerPlugin und unsere Klassen 1 und 2 liegen im Subnamespace Main, während der Service in Service liegt

Ordernstruktur

Wir etablieren nun die Ordnerstruktur, die der Benennung der Namespaces folgt:

my-plugin
├── src
│   ├── Main
│   │   ├── Class01.php
│   │   └── Class02.php
│   └── Service
│       └── Service01.php
└── my-plugin.php

Die Dateien

src/Main/Class01.php

<?php

namespace MK\MyPlugin\Main;

class Class01
{
    public function __construct()
    {
        echo 'Class01 instantiated.';
    }
}

src/Main/Class02.php

<?php

namespace MK\MyPlugin\Main;

use MK\MyPlugin\Service\Service01;

class Class02
{
    protected Service01 $service01;

    public function __construct(Service01 $service01)
    {
        $this->$service01 = $service01;

        echo 'Class02 instantiated.';
    }
}

src/Service/Service01.php

<?php

namespace MK\MyPlugin\Main;

class Service01
{
    public function __construct()
    {
        echo 'Service01 instantiated.';
    }
}

my-plugin.php

<?php
/**
* Plugin Name: MyPlugin
*/

require_once __DIR__ . '/src/Main/Class01.php';
require_once __DIR__ . '/src/Main/Class02.php';
require_once __DIR__ . '/src/Service/Service01.php';

use MK\MyPlugin\Main\Class01;
use MK\MyPlugin\Main\Class02;
use MK\MyPlugin\Main\Service01;

$service01 = new Service01();

$class01 = new Class01();
$class02 = new Class02($service01);

Autoloading in WordPress-Plugins nutzen

Grundsätzlich gibt es zwei Wege, Autoloading zu verwenden:

  • selbst eingerichtetes Autoloading ĂĽber spl_autoload_register()
  • PSR-4-Autoloading ĂĽber composer

Ich verwende grundsätzlich das Autoloading über composer und empfehle dies sehr. Um hier aber die wichtigsten Dinge abzudecken, behandle ich auch spl_autoload_register().

Autoloading mit spl_autoload_register()

Mit der PHP-Funktion spl_autoload_register() können wir unser eigenes Autoloading implementieren. Dazu sehen wir uns einmal den Funktionsaufruf an:

spl_autoload_register(?callable $callback = null, bool $throw = true, bool $prepend = false): bool

Die Funktion erwartet also mindests eine Callback-Funktion, und dieser Funktion wird der Name der zu ladenden Klasse mitgegeben:

callback(string $class): void

Wir können nun in unserer Hauptdatei my-plugin.php die require_once()-Aufrufe auskommentieren und testweise einmal spl_autoload_register() einfügen, als Callback verwenden wir eine anonyme Funktion:

<?php
/**
* Plugin Name: MyPlugin
*/

// require_once __DIR__ . '/src/Main/Class01.php';
// require_once __DIR__ . '/src/Main/Class02.php';
// require_once __DIR__ . '/src/Service/Service01.php';

use MK\MyPlugin\Main\Class01;
use MK\MyPlugin\Main\Class02;
use MK\MyPlugin\Main\Service01;

spl_autoload_register(function(string $className) {
    var_dump($className);
});

$service01 = new Service01();

$class01 = new Class01();
$class02 = new Class02($service01);

Dadurch, dass wir die require_once()-Aufrufe deaktiviert und spl_autoload_register integriert haben, versucht PHP beim Aufruf von new Service01() ĂĽber den Autoloader die passende PHP-Datei zu erhalten. Dazu ĂĽbergibt PHP den voll qualifizierten Klassennamen an die Callback-Funktion, die wir an spl_autoload_register ĂĽbergeben haben.

Alles, was unser Callback momentan macht, ist per var_dump() den Klassennamen auszugeben. Unser obiger Code erzeugt also folgende Meldung:

string(29) "MK\MyPlugin\Service\Service01"
Fatal error: Uncaught Error: Class "MK\MyPlugin\Service\Service01" not found in 
[(...)/wp-content/plugins]/autoload-plugin-02/autoload-plugin-01.php:18 
Stack trace: #0 (...)/wp-settings.php(453): include_once() #1 (...)/wp-config.php(93): 
require_once '...' #2 (...)/wp-load.php(50): require_once '...' #3 (...)/wp-admin/admin.php(34): 
require_once '...' #4 (...)/wp-admin/plugins.php(10): require_once '...' #5 {main} 
thrown in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php on line 18

Ich habe in diesem Code meine lokalen Pfade durch (…) ersetzt und manuelle UmbrĂĽche eingefĂĽgt, um die Meldung nicht zu lang zu machen.

Zunächst erscheint also die Ausgabe von var_dump():

string(29) "MK\MyPlugin\Service\Service01"

Dies ist der voll qualifizierte Name der Klasse, die als erstes instanziiert wird.

Dann folgt der Fatal Error, da die Klasse nicht gefunden werden konnnte. Wir mĂĽssen nun also in unserem Callback dafĂĽr sorgen, dass die entsprechende Datei geladen wird.

Da wir es uns mit den Namespaces und der entsprechenden Ordnerstruktur schon sehr einfach gemacht haben, gelingt es uns durch ein paar simple Anpassungen des voll qualifizierten Klassennamens den Dateinamen zu erhalten:

<?php
/**
* Plugin Name: MyPlugin
*/

// require_once __DIR__ . '/src/Main/Class01.php';
// require_once __DIR__ . '/src/Main/Class02.php';
// require_once __DIR__ . '/src/Service/Service01.php';

use MK\MyPlugin\Main\Class01;
use MK\MyPlugin\Main\Class02;
use MK\MyPlugin\Main\Service01;

spl_autoload_register(function(string $className) {
    // MKMyPlugin vom Klassennamen durch den Pfad zu src ersetzen:
    $className = str_replace('MK\\MyPlugin\\', __DIR__ . '/src/', $className);

    // Die restlichen Backslashes durch Verzeichnis-Trenner (Slashes) ersetzen und .php anhängen
    $classFile =  str_replace('\\', '/', $className) . '.php';

    // Klassen-Datei laden
    require_once $classFile;
});

$service01 = new Service01();

$class01 = new Class01();
$class02 = new Class02($service01);

Im Beispiel der Servie01-Klasse lautet der Klassenname MKMyPluginServiceService01. Der Teil MKMyPlugin kann dabei als Alias zum src-Verzeichnisses unseres Plugins gesehen werden.

Um daraus also den Pfad für die Klassen-Datei zu erhalten, müssen wir im ersten Schritt nur den Teil MKMyPlugin durch __DIR__ . '/src/ ersetzen. Im Anschluss tauschen wir alle Backslashes () durch den Verzeichnis-Trenner (Slash, /) und hängen die Endung .php an.

Aus MKMyPluginServiceService01 wird so also (...)/wp-content/plugins/my-plugin/src/Service/Service01.php, was genau unserer Datei entspricht. Der Teil (...) entspricht deinem lokalen Pfad zur WordPress-installation.

Mit dem obigen Code sollte unser Plugin also nun alle Klassen-Datein automatisch nachladen. Wir mĂĽssen nun also nur noch unseren ĂĽberflĂĽssigen, auskommentierten Code entfernen:

<?php
/**
* Plugin Name: MyPlugin
*/

use MK\MyPlugin\Main\Class01;
use MK\MyPlugin\Main\Class02;
use MK\MyPlugin\Main\Service01;

spl_autoload_register(function(string $className) {
    // MKMyPlugin vom Klassennamen durch den Pfad zu src ersetzen:
    $className = str_replace('MK\\MyPlugin\\', __DIR__ . '/src/', $className);

    // Die restlichen Backslashes durch Verzeichnis-Trenner (Slashes) ersetzen und .php anhängen
    $classFile =  str_replace('\\', '/', $className) . '.php';

    // Klassen-Datei laden
    require_once $classFile;
});

$service01 = new Service01();

$class01 = new Class01();
$class02 = new Class02($service01);

Doch wenn wir diesen Code nun so umsetzen und in unserer WordPress-Installation die Seite neu laden, werden wir mit einem neuen Fehler konfrontiert:

Warning: require_once(WP_Site_Health.php): Failed to open stream: No such file or directory 
in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php on line 27

Fatal error: Uncaught Error: Failed opening required 'WP_Site_Health.php' (include_path='.:/usr/share/php:/www/wp-content/pear') 
in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php:27 
Stack trace: #0 [internal function]: {closure}('WP_Site_Health') 
#1 (...)/wp-settings.php(604): class_exists('WP_Site_Health') #2 (...)/wp-config.php(93): 
require_once('...') #3 (...)/wp-load.php(50): require_once('...') 
#4 (...)/wp-admin/admin.php(34): require_once('...') #5 (...)/wp-admin/plugins.php(10): 
require_once('...') #6 {main} thrown in (...)/wp-content/plugins/autoload-plugin-02/autoload-plugin-01.php on line 27

Was passiert hier?

PHP versucht, die WordPress-Core-Klasse WP_Site_Health zu laden, woraus unser Callback-Code die Datei WP_Site_Health.php macht, und diese ist so natĂĽrlich nicht zu finden.

Der Autoloader-Callback, den wir mit spl_autoload_register() registrieren, wird im globalen Namespace deklariert. Wir müssen also dafür sorgen, dass wir nur Klassennamen verarbeiten werden, die auch tatsächlich in unserem Namespace vorhanden sind, da PHP den Callback bei jeder geladenen Klasse verwendet, die nach der Registrierung geladen wird. So also auch für WordPress-Core-Klassen.

Um zu gewährleisten, dass nur Klassennamen in unserem Autoloader behandelt werden, die tatsächlich in unserem Namespace liegen, fügen wir folgende Überprüfung ein:

<?php
/**
* Plugin Name: MyPlugin
*/

use MK\MyPlugin\Main\Class01;
use MK\MyPlugin\Main\Class02;
use MK\MyPlugin\Main\Service01;

spl_autoload_register(function(string $className) {
    if (false === strpos($className, 'MK\\MyPlugin')) {
        return;
    }

    // MKMyPlugin vom Klassennamen durch den Pfad zu src ersetzen:
    $className = str_replace('MK\\MyPlugin\\', __DIR__ . '/src/', $className);

    // Die restlichen Backslashes durch Verzeichnis-Trenner (Slashes) ersetzen und .php anhängen
    $classFile =  str_replace('\\', '/', $className) . '.php';

    // Klassen-Datei laden
    require_once $classFile;
});

$service01 = new Service01();

$class01 = new Class01();
$class02 = new Class02($service01);

Mit if (false === strpos($className, 'MK\MyPlugin')) ĂĽberprĂĽfen wir also, ob der Anfang unseres Namespaces im Klassennamen vorkommt. Falls nicht (strpos liefert false), beenden wir den Callback mit return.

Jetzt haben wir einen funktionierenden Autoloader, der nur die Klassennamen unseres Plugins behandelt.

Autoloading mit Composer

Die manuelle Umformung des Klassennamens im Callback von spl_autoload_register() und die Ausnahme-Behandlung von Klassennamen außerhalb des Namespaces des Plugins sind einerseits aufwändig und andererseits natürlich fehleranfällig.

Auch im Sinne der Code-Wiederverwendbarkeit schneidet diese Version des Autoloadings nicht gut ab, da wir hier immer manuell an mindestens zwei Stellen den Namespace anpassen mĂĽssen.

Es wäre doch viel schöner, wenn wir uns hier einer etablierten Lösung bedienen könnten. Und hier kommt Composer ins Spiel. Composer ist ein plattformübergreifendes Dependency-Management-Tool für PHP, das Entwickler*innen hilft, benötigte Bibliotheken und Pakete für ihre Projekte einfach zu verwalten und automatisch herunterzuladen. Zugleich bietet Composer auch ein auf PSR-4 basierendes Autoloading, das wir ab jetzt auch für unser Plugin einsetzen möchten.

Composer einrichten

Um Composer im Plugin verwenden zu können, müssen wir Composer zunächst einrichten. Dafür muss Composer bereits auf deinem Gerät installiert sein, dazu kannst du diesem Tutorial folgen.

Ist Composer installiert, öffnest du bitte ein Terminal (ich kann Warp empfehlen, wenn du auf einem Mac unterwegs bist, ansonsten hyper.js) und wechselst in dein Plugin-Verzeichnis.

Dann gibst du folgenden Befehl ein: composer init. Der Composer config generator wird dich dann durch die Einrichtung führen. In den meisten Fällen kannst du die Standard-Angaben verwenden.

Da wir noch keine Abhängigkeiten definieren möchten, kannst du die Fragen Would you like to define your dependencies (require) interactively und Would you like to define your dev dependencies (require-dev) interactively mit no beantworten.

Dann folgt die Frage, die wir benötigen:

Add PSR-4 autoload mapping? Maps namespace “MarcuskoberMyPlugin” to the entered relative path. [src/, n to skip]:

Hier generiert Composer automatisch einen Namespace aus dem zurest angegebenen Package Name, der sich standardmäßig aus dem von dir gewählten Namen bei der Einrichtung von Composer und dem Verzeichnisnamen zusammensetzt. Wir bestätigen die Auswahl hier mit enter, um den Namen und das vorgeschlagene src-Verzeichnis zu wählen.

Im Anschluss muss die Einrichtung noch mit einem yes bestätigt werden.

Composer legt nun ein vendor-Verzeichnis für dein Plugin an und die Config-Datei composer.json. Im vendor-Verzeichnis liegen nun bereits die Dateien, die Composer für das Autoloading benötigt und in diesem Verzeichnis landen später auch Pakete, falls du welche über Composer installierst.

Die Config-Datei sieht bei mir nun wie folgt aus:

{
    "name": "marcuskober/my-plugin",
    "autoload": {
        "psr-4": {
            "Marcuskober\\MyPlugin\\": "src/"
        }
    },
    "authors": [
        {
            "name": "Marcus Kober",
            "email": "marcus.kober@gmail.com"
        }
    ]
}

In meinem Fall muss ich hier unter "psr-4"noch den Namespace korrigieren, damit er zu unserem gewählten Namespace passt. Das kann bei dir – je nach Wahl des Namens, Verzeichnisses und Namespaces – auch nötig sein.

Wir ändern dies (Marcuskober zu MK) also entsprechend ab:

"psr-4": {
    "MK\\MyPlugin\\": "src/"
}

Haben wir den Namespace in der composer.json geändert, müssen wir Composer dies noch mitteilen, damit er seine Autoload-Dateien entsprechend anpasst. Dazu geben wir im Terminal den folgenden Befehl ein: composer dump-autoload. Meldet Composer im Terminal Generated autoload files zurück, haben wir Composer korrekt eingerichtet.

Composer Autoload in unserem Plugin verwenden

Das einzige, was wir tun mĂĽssen, um Composer Autoloading in unserem Plugin zu verwenden, ist die require_once()-Aufrufe zu entfernen und die Datei vender/autoload.php zu laden:

<?php
/**
* Plugin Name: MyPlugin
*/

use MK\MyPlugin\Main\Class01;
use MK\MyPlugin\Main\Class02;
use MK\MyPlugin\Main\Service01;

require __DIR__ . '/vendor/autoload.php';

$service01 = new Service01();

$class01 = new Class01();
$class02 = new Class02($service01);

Und lediglich mit dem EinfĂĽgen der Zeile require __DIR__ . '/vendor/autoload.php'; funktionert das Autoloading mit Composer direkt out of the box!

Fazit und Ausblick

Wie du siehst, ist das Autoloading mit Composer kein Hexenwerk und im Gegensatz zu spl_autoload_register() schnell und unkompliziert umgesetzt.

Haben deine Plugins einen gewissen Umfang erreicht, empfehle ich dir, das Autoloading ĂĽber Composer zu verwenden. Auch wenn du die Einstellungen frei anpassen kannst, zwingt dich das Autoloading doch zu einer sauberen Benennung der Klassen, einer logischen Ordner-Struktur und die Verwendung eines einzelnen src-Verzeichnisses fĂĽr deine PHP-Dateien. Und glaube mir, der Zwang ist an dieser Stelle rein positiv zu betrachten.

In den kommenden Artikeln dieser Serie werden wir uns tiefer mit der Entwicklung und Strukturierung umfangreicher Plugins befassen und dabei wird der Code immer anschaulicher werden, da wir nach diesen recht theoretischen ersten Artikeln tatsächlich verwendbaren Code sehen werden!

Im nächsten Artikel befassen wir uns mit Hooks und deren Registrierung aus deinem Plugin heraus.

Abonniere meinen Newsletter

Abonniere jetzt meinen Newsletter und verpasse nie mehr einen neuen Artikel. Kein Mist, kein Spam – ich sende dir nur eine Mail, sobald ein neuer Artikel erscheint.

Formular ĂĽberspringen
Die E-Mail-Adresse sollte ein ‘@’-Zeichen und eine gĂĽltige Domain mit einem Punkt enthalten.
Formular ĂĽbersprungen

Ist dir ein Fehler oder ein Problem in diesem Artikel aufgefallen, oder möchtest du sonst etwas zum Thema sagen? Schreib mir gerne!

Datum:

9. Mai 2023

Kategorie:

Artikelserie modernes PHP in Plugins