Unser zweites Türchen in unserem Adventskalender!

Es gibt viele Dinge, die man tun oder nutzen kann, um WordPress Beine zu machen. Eins davon ist Caching. Aber Caching ist keine konkrete Sache an sich. Es ist vielmehr eine Technik, die man auf verschiedene Weisen, in verschiedenen Ebenen und bei verschiedenen Arten von Daten einsetzen kann. Dieser Beitrag stellt das Plugin Inpsyde Menu Cache vor. Es erlaubt dir, WordPress-Navigationsmenüs zu cachen und wiederzuverwenden. Das Plugin ermöglicht daher das Cachen von HTML-Fragmenten, und zwar recht gut anpassbar.

Warum einen Menü-Cache verwenden?

Sofern du nicht bereits einen HTML-Fragment- oder gar Full-Page-Cache verwendest, wird jede Frontend-Anfrage deiner Website die Markup-Generierung all deiner Navigationsmenüs anstoßen. Je nachdem, was diese Menüs beinhalten, ist dies eine äußerst kostspielige Sache, die, wie erwähnt, für jeden einzelnen Besuch der Seite erneut durchgeführt wird.

Zeitverschwendung

WordPress rendert Navigationsmenüs mit Hilfe der Funktion wp_nav_menu(). Dies bewirkt einen internen Aufruf von _wp_menu_item_classes_by_context(). Je nach Kontext, verwendet die Funktion wiederum wp_get_object_terms()für jedes Menüelement—was eine Menge an Datanbankanfragen verursacht, die nicht gecachet sind.

Demzufolge wird jedes einzelne Menüelement von Neuem generiert, für jeden einzelnen Seitenaufruf. Mal angenommen, du änderst deine Menüs nicht alle fünf Minuten, ist dies eine große Zeitverschwendung, die so überhaupt nicht nötig ist.

Realbeispiel

Im Rahmen eines größeren WooCommerce-Projektes wurde mir die Aufgabe zuteil, die Frontend Experience zu messen und zu verbessern. Mit Hilfe des spitzenmäßigen Plugins Query Monitor (insbesondere dessen hilfreiche Datenbankanalysefunktionalität) und des Xdebug-Profilers konnte ich zahlreiche doppelte Anfragen eliminieren; und ich konnte kostspielige und ungecachete Anfragen umgehen (z.B. indem ich innerhalb des Themes WP_PostObjekte anstatt IDs weiterreiche, denn Letztere wurden von allen empfangenden Funktionen sowieso nur dafür genutzt, selbst an die jeweiligen Objekte heranzukommen).

Ebenfalls viel Zeitaufwand verursachte das Rendern von Navigationsmenüs. Das Hauptmenü war übrigens ein Mega-Menü mit mehr als 70 Elementen, und für jedes davon haben Third-Party-Plugins mehrere benutzerdefinierte Datenbankanfragen abgeschickt; ungecachet, natürlich.

So etwas ist leicht zu entdecken, und zum Glück auch leicht zu beheben. Also habe ich genau das getan.

Was macht Inpsyde Menu Cache?

Das Plugin macht sich zwei Filter zunutze, die von der Funktion wp_nav_menu() angeboten werden. Einen um ein frisch erzeugtes Menü (d.h. das gerenderte HTML) im Cache zu speichern, und einen um auf ein zuvor gecachetes Menü zu überprüfen, bevor es (erneut) generiert wird. Tatsächlich würde uns ein Action Hook nach erfolgreicher Generierung des Markups genügen, aber einen solchen gibt es nicht, daher müssen wir einen Filter ge- bzw. missbrauchen.

Für jedes Menü, das gerendert werden soll, fängt das Plugin den Programmfluss ab und überprüft, ob im Cache bereits ein entsprechendes Element wartet. Falls dem so ist, wird es ohne weitere Fragen verwendet.

Jedes Mal, wenn WordPress ein neues Menü gerendert hat, wird es im Cache gespeichert, sodass es später wiederverwendet werden kann und somit nachfolgende Seitenaufrufe schneller sind.

Jedes Element im Cache besitzt einen eindeutigen Schlüssel, aber es liegt an dir, was genau als individuelles Element gespeichert wird. Standardmäßig cachet das Plugin jede unterschiedliche Verwendung eines Menüs. Das bedeutet, dass ein zwei- oder mehrfach gerendertes Menü mit der exakt gleichen Konfiguration (d.h. exakt gleiche Argumente beim Aufruf von wp_nav_menu()) nur ein Zimmer im Cache benötigt, unabhängig von der aktuellen URL, des Benutzers oder anderer möglicher Kontextvariablen.

Wie kann ich Inpsyde Menu Cache verwenden?

Der folgende Abschnitt hilft dir bei der Verwendung des Plugins, und zeigt ein paar Anwendungsbeispiele der verfügbaren Filter.

Installation

Inpsyde Menu Cache benötigt PHP 5.4 oder höher, aber wie wir alle wissen, solltest du PHP 7 verwenden, richtig? 😉

Aktuell kann das Plugin nicht über das WordPress Plugin Directory bezogen werden. Wir haben uns dafür entschieden, es als Composer Package anzubieten. Das bedeutet, dass du Composer brauchst, um das Plugin zu installieren:

$ composer require inpsyde/menu-cache

Übrigens, falls du komplette WordPress-Websites mit Composer verwalten möchtest, können wir dir nur wärmstens das tolle WP Starter Package empfehlen.

Was ist mit dynamischen Menüs?

Wie zuvor erwähnt ignoriert das Plugin die jeweils aktuelle URL. Falls du einen Walker verwendest, der unterschiedlichen Output je nach URL (oder Queried Object etc.) erzeugt, oder falls dein Theme spezielle Styles für besondere Menü-Elemente (z.B. das aktuelle Element oder einen der Vorgänger) beinhaltet, solltest du die Schlüsselerzeugung entsprechend anpassen.

Hier ein einfaches Beispiel, das individuelle Element für jede einzelne URL erzeugt:

<?php

namespace My\MenuCacheCustomization;

use Inpsyde\MenuCache\MenuCache;

add_action( 'plugins_loaded', function () {

    if ( ! class_exists( MenuCache::class ) ) {
        return;
    }

    add_filter( MenuCache::FILTER_KEY, function( $key ) {

        $suffix = md5( $_SERVER['REQUEST_URI'] );

        return "{$key}_{$suffix}";
    } );
} );

Was ist mit Invalidierung?

Standardmäßig erfolgt keine Invalidierung des Caches seitens des Plugins, sondern wird mit Ablauf (engl. expiration) gearbeitet, welche 5 Minuten beträgt. Obwohl möglich, ist die Invalidierung von Caches relativ schwer zu realisiere, und umso mehr in diesem Fall, bei dem die Erzeugung der Schlüssel filterbar ist. Falls du wirklich Invalidierung benötigst, könnte das bedeuten, dass du auf die falsche Weise cachest, oder aber das entsprechende Menü gar nicht cachen solltest…

Trotzdem, Invalidierung nach nachgerüstet werden, indem etwas in die Schlüsselerzeugung einbaust, das du wahlweise von Hand oder automatisch invalidieren kannst. Ein sehr rudimentäres Beispiel könnte wie folgt aussehen:

<?php

namespace My\MenuCacheCustomization;

use Inpsyde\MenuCache\MenuCache;

const OPTION = '_latest_menu_update';

add_action( 'plugins_loaded', function () {

    add_action( 'wp_update_nav_menu', function () {

        update_option( OPTION, time() );
    } );

    if ( ! class_exists( MenuCache::class ) ) {
        return;
    }

    add_filter( MenuCache::FILTER_KEY, function( $key ) {

        $timestamp = get_option( OPTION, time() );

        return "{$key}_{$timestamp}";
    } );
} );

Im obigen Code benutzen wir eine eigene Option zur Speicherung der Zeit des letzten Menü-Updates; irgendein Menü. Dieser Zeitstempel wird anschließend in der Schlüsselerzeugung verwendet, und so ändert sich der Schlüssel für einunddasselbe Menü mit jedem Menü-Update. Jetzt könnten wir sogar die Ablaufzeit auf mehrere Stunden oder gar Tage anheben, wenn wir wollten.

Aber noch einmal: dies mag nicht die beste Herangehensweise der Invalidierung sein, und gute hängen allein schon immer von der Schlüsselerzeugung ab.

Kann ich den Cache wg. $reason ausschalten?

Manchmal kann der Cache auch nerven. Angenommen, du bist dabei dein Stage- oder Produktivsystem zu debuggen, oder du möchtest bei bestimmten Benutzern nicht, dass sie Daten aus dem Cache erhalten. Zum Glück kannst du das Caching komplett ausknipsen, entsprechend jeder erdenkbaren Bedingung.

Hier das Debugging-Beispiel:

<?php

namespace My\MenuCacheCustomization;

use Inpsyde\MenuCache\MenuCache;

add_action( 'plugins_loaded', function () {

    if ( ! class_exists( MenuCache::class ) ) {
        return;
    }

    add_filter( MenuCache::FILTER_SHOULD_CACHE_MENU, function( $should_cache ) {

        return defined( 'WP_DEBUG' ) && WP_DEBUG
            ? false
            : $should_cache;
    } );
} );

Inpsyde Menu Cache ist Open Source!

Als Unterstützer der WordPress-Community sowie Open Source im Allgemeinen, haben wir dafür gesorgt, dass das Plugin Inpsyde Menu Cache frei nutzbar ist und man es zugleich einfach mitgestalten kann.

Falls du eine Idee für ein neues Feature hast, einen möglichen Bug gefunden hast, oder etwas bezüglich des Plugins einfach nicht verstehst, dann ab zum GitHub-Repository und ein neues Issue erstellen. Unnötig zu sagen, aber wir freuen uns natürlich auch sehr über jegliche Art von Pull Request von dir, sei es, um einen Fheler Fehler zu beheben, die Dokumentation zu verbessern, weitere Tests zu ergänzen, oder sogar ein dir wichtiges Feature zu implementieren. Schaue dir zuvor jedoch bitte an, ob bereits etwas in der Art diskutiert wird/wurde.

Auch Kommentare sind möglich. 😉

Fröhliches Cachen!

Öffne das dritte Türchen unseres WordPress Adventskalenders 2017!

Antwort abgeben

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.