Wie du dir eine Klassen-Instanz in PHP erstellst

Du arbeitest viel mit PHP-Klassen und hast noch nichts von Instanzen gehört? Hier erfährst du, wie du deine Klassen-Instanzen erstellst. Du kannst diese Methode auch für bestehende Projekte einsetzen, da sich am ursprünglichen Code nichts verändert. Außerdem erfährst du, wie du mit einer Vererbung die wiederholte Tipperei für Instanzen sparen kannst.

PHP-Klasse erstellen

class Books
{
    public function __construct()
    {
        add_action('init', [ & $this, 'register_post_type']);
    }

    public function register_post_type()
    {
        register_post_type('book', [
            'public' => true,
            'label' => 'Bücher',
            'menu_icon' => 'dashicons-book',
        ]);
    }
}Code-Sprache: PHP (php)

In diesem Beispiel haben wir eine Books-Klasse. Diese erstellt für uns den Post Type book. Im Normalfall würden wir die Klasse dann mit new Books(); starten. Das Problem ist, dass jedes Mal, wenn du die Klasse so aufrufst, die Klasse neu initialisiert wird. Das bedeutet, dass du die Klasse im schlimmsten Fall, mehr als einmal initialisierst.

Um das Problem zu lösen, erstellen wir Instanzen. Eine Instanz speichert die eigene Klasse in einer statischen Variable ab, die man Eigenschaft nennt. Die Eigenschaft wird aber immer nur dann neu befüllt, wenn die Eigenschaft null ist. Also eine Klasse hat keine Variablen, sondern Eigenschaften. Das immer schön merken.

PHP-Klasse mit Instanz

class Books
{
    private static $instance;

    public static function instance()
    {
        if (!self::$instance) {
            self::$instance = new static();
        }

        return self::$instance;
    }

    public function __construct()
    {
        add_action('init', [&$this, 'register_post_type']);
    }

    public function register_post_type()
    {
        register_post_type('book', [
            'public' => true,
            'label' => 'Bücher',
            'menu_icon' => 'dashicons-book',
        ]);
    }
}Code-Sprache: PHP (php)

Hier haben wir jetzt die Möglichkeit geschaffen, eine Instanz der Klasse zu erstellen. Wie du siehst, wird die $instance -Eigenschaft nur dann gefüllt, wenn sie auf null steht. Wenn die Klasse also mehr als einmal abgerufen wird, wird sich immer auf die Instanz bezogen, denn die Eigenschaft $instance wäre dann nicht null.

Du kannst die Instanz der Klasse nun initialisieren:

Books::instance();Code-Sprache: CSS (css)

Da die Klasse jetzt in einer Instanz gespeichert und diese Instanz statisch ist, kannst du die Klasse jetzt auch mit der init-Hook von WordPress starten. Da freut sich WordPress, denn es kann dann selbst bestimmen, wann etwas abgerufen wird.

add_action('init', ['Books', 'instance']);Code-Sprache: JavaScript (javascript)

Wenn du jetzt ganz viele Klassen hast, die nur einmal abgerufen werden soll, dann schreibst du also in jede Klasse die Instanz. Da dieser Code immer gleich ist (mit Ausnahme von Klassen, in denen du Argumente im Konstruktor weitergibst), kannst du jetzt eine abstrakte Klasse erstellen. Diese abstrakte Klasse vererbt dann die Instanz an deine Klassen, sodass du das nicht jedes Mal tippen musst.

Abstrakte Klasse für Vererbungen

abstract class Module
{
    private static $instance;

    public static function instance()
    {
        if (!self::$instance) {
            self::$instance = new static();
        }

        return self::$instance;
    }

    public function __construct() {}
}Code-Sprache: PHP (php)

In diesem Beispiel nenne ich die Klasse jetzt Modul. Sie ist für mich das Modul, welches die Eigenschaften vererbt, eine Instanz zu erstellen.

Um jetzt von der Klasse etwas erben zu können, müssen wir unseren Code für die Bücher, etwas anpassen. Das Ergebnis sieht dann so aus:

class Books extends Module
{
    public function __construct()
    {
        add_action('init', [&$this, 'register_post_type']);
    }

    public function register_post_type()
    {
        register_post_type('book', [
            'public' => true,
            'label' => 'Bücher',
            'menu_icon' => 'dashicons-book',
        ]);
    }
}Code-Sprache: PHP (php)

Hier haben wir unserer Books-Klasse nun gesagt, dass sie die Eigenschaften von Module erben soll, indem wir extends Module verwendet haben. Jetzt musst du nicht überall die Instanz setzen. Du kannst deine Books-Klasse jetzt wieder mit der init-Hook von WordPress initialisieren:

add_action('init', ['Books', 'instance']);Code-Sprache: JavaScript (javascript)

Obwohl du die statische Methode instance nicht mehr in der Books-Klasse hast, kannst du die Instanz so setzen, da sie diese Methode von der Module-Klasse geerbt hat. Das kannst du jetzt für alle deine Klassen übernehmen. Du musst also nichts großartig an deinem Code ändern, lediglich die Klasse erweitern mit der Module-Klasse und dann die Instanz starten.

Die bessere Alternative: Traits

Wie der Titel bereits sagt, ist die bessere Alternative zu einer abstrakten Klasse ein trait. Das liegt hauptsächlich daran, dass es mit abstrakten Klassen zu Problemen kommen kann, wenn man dort die Instanzen setzt. Manchmal denkt das System, dass die Instanz für eine Klasse bereits gesetzt ist, weil sie in der abstrakten Klasse an anderer Stelle gesetzt wurde.

Deshalb nutzen wir lieber Traits anstelle von Klassen. Traits sind im Grunde Klassen, die für einen recyceln. Also dort speicherst du deinen Code, den du häufiger verwenden willst. Du setzt dann auch kein extends, sondern ein use, direkt in die Klasse.

trait Module
{
    private static $instance;

    public static function instance()
    {
        if (!self::$instance) {
            self::$instance = new static();
        }

        return self::$instance;
    }
}Code-Sprache: PHP (php)

Damit haben wir Module zu einem trait gemacht. Jetzt müssen wir noch unsere Klasse anpassen, damit das funktioniert:

class Books 
{
    use Module;

    public function __construct()
    {
        add_action('init', [&$this, 'register_post_type']);
    }

    public function register_post_type()
    {
        register_post_type('book', [
            'public' => true,
            'label' => 'Bücher',
            'menu_icon' => 'dashicons-book',
        ]);
    }
}Code-Sprache: PHP (php)

Wie du siehst, wird hier nicht mehr mit extends gearbeitet, sondern Module direkt innerhalb der Klasse mit use verwendet. Ähnlich, wie man das mit Klassen macht, die in einem Namespace sind. Dann funktioniert das auch, egal wo.

Achtung

Beachte immer, dass du bestimmte Klassen nicht instanziierst, wenn du sie häufiger einsetzen willst. Wenn du jetzt bspw. eine Book-Klasse hast, in der du im Konstruktor eine ID vergibst, dann sollte dies keine Instanz sein. Du kannst die Instanz aber verändern, damit zumindest diese Klasse, so wie sie ist, nur einmal abgerufen wird. Dafür nutzen wir wp_cache_set() und wp_cache_get(). Mit den beiden Funktionen speichern wir etwas in den Objekt-Cache. Damit wird die Klasse so wie sie ist, abgespeichert, zumindest für diesen Moment. Lädst du die Seite neu, wird sie wieder initialisiert. Hier ein Beispiel mit einer Book-Klasse:

class Book
{
    public $book_id;
    private static $instance;

    public static instance(int $book_id)
    {
        $cache_name = 'book/' . $book_id;
    
        if (!wp_cache_get($cache_name)) {
            self::$instance = new static($book_id);
            wp_cache_set($cache_name, self::$instance);
        }

        return self::$instance;
    }

    public function __construct(int $book_id)
    {
        $this->book_id = $book_id;
    }

    public function get_book()
    {
        return get_post($this->book_id);
    }

    public function get_book_meta(string $meta)
    {
        return maybe_unserialize(get_post_meta($this->book_id, $meta, true));
    }
}Code-Sprache: PHP (php)

Wenn du die Klassen-Instanz jetzt aufrufst, wird sie im Objekt-Cache von WordPress gespeichert. Du kannst die Klasse jetzt also so häufig auf einer Seite verwenden, wie du möchtest. WordPress sorgt dafür, dass sie wiederverwendet wird. Sehr cool!

$book_id = get_query_var('p');

$book = Book::instance($book_id);Code-Sprache: PHP (php)