Über Hooks und Funktionen in WordPress

Über Hooks und Funktionen in WordPress 1

In der Entwicklung mit WordPress kann man sehr schnell Ergebnisse vorweisen. Das liegt daran, dass neben einer relativ guten Dokumentation auf niedrige Hürden gesetzt wird: Es gibt weder eine Templatesprache, noch eine eigene Scriptsprache. WordPress setzt auf einfaches PHP und bietet für die Entwicklung sehr viele gute Werkzeuge an.

Eines der Werkzeuge sind sogenannte Hooks. Ein Hook ist eine Einsprungmarke, an welche der eigene Code gehangen wird. So erlangt man eine volle Kontrolle, wann welches Script ausgeführt werden soll. Der Vorteil liegt klar auf der Hand: Code wird nicht unabsichtlich oder unbeabsichtigt ausgeführt. Das erhöht die Performance und Sicherheit.

Innerhalb von WordPress gibt es zwei verschiedene Arten von Hooks: Actions und Filter. Der Hauptunterschied zwischen diesen beiden Prinzipien ist die Semantik. Beide Hook-Arten machen im Grunde das Gleiche, jedoch in einem anderen Kontext, welcher für die Programmierung interessant ist. Bei Actions werden Dinge ausgeführt. In den Filtern werden Daten und Ausgaben manipuliert.

Actions

Wenn WordPress geladen wird, werden verschiedene Actions an verschiedenen Punkten ausgeführt. In der /wp-settings.php finden wir zum Beispiel Folgendes:

do_action( 'plugins_loaded' );

Diese Action (Link zum Source) wird ausgeführt, nachdem das WordPress Grundsystem hochgefahren und alle Plugins eingebunden wurden. Normalerweise hängt man sich mit den Plugins an diese Stelle in WordPress rein.

do_action( 'after_setup_theme' );

Diese Action (Link zum Source) wird ausgeführt, nachdem die Theme-Dateien geladen und alle relevanten Daten verarbeitet worden sind. Sollten Themes irgendwelche Features, wie Widgets oder Menüs, mitbringen, dann werden diese normalerweise in diesem Kontext ausgeführt.

Möchte man sich jetzt an eine dieser Hooks hängen, dann kann man die Funktion add_action() benutzen. Diese registriert die eigene Funktion an einer bestimmten Action. Wenn wir in die /wp-includes/plugin.php schauen, dann sehen wir Folgendes:

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {
	return add_filter($tag, $function_to_add, $priority, $accepted_args);
}

Die Funktion (Link zum Source) add_action() ist – wie oben bereits geschrieben – eigentlich das Gleiche wie ein Filter, nur in einer anderen Semantik. Hier haben wir es schwarz auf weiß. Dennoch sollte add_action() verwendet werden, da so gleich der Kontext mitgegeben wird. Die einzelnen Parameter kann man hier getrost ignorieren.

Wollen wir uns an einen dieser Hooks hängen, kann der Code so aussehen:

add_action( 'plugins_loaded', 'my_plugin_init' );

Wir haben hier das Plugin an die Action ‘plugins_loaded’ gehangen. Wirft man einen Blick (Link zum Source) in die Funktion do_action(), dann sieht man, dass alle registrierten Aktionen geholt und in einer Schleife über den Befehl call_user_func_array() ausgeführt werden. Sobald WordPress an unserer Action ankommt, wird es gestoppt.

WordPress selbst hat im Core noch wesentlich mehr Actions registriert. Zu guter Letzt ein praktisches Beispiel: Wollen wir im Theme innerhalb des -Bereiches einen weiteren Meta-Tag ausgeben, dann sieht der Code dafür so aus:


Wie wir sehen, starten wir erst innerhalb der Aktion after_setup_theme. So können wir sicher gehen, dass alle Daten geladen wurden. In der Funktion wpc_my_theme_init() registrieren wir die Aktion, welche dann den Meta-Tag ausgibt.

Filter

Filter sind in WordPress dazu da, bereits existierende Daten zu manipulieren. Sehr viele Variablen in WordPress lassen sich filtern. Das fängt bei kleinen einfachen Variablen an und kann so weit gehen, dass der komplette Inhalt eines Blogbeitrages geändert wird.

Filter werden in WordPress über den Befehl add_filter() registriert. Im Gegensatz zu den Actions sollte man hier die einzelnen Parameter (Link zum Source) nicht ignorieren.

function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
	global $wp_filter, $merged_filters;

	$idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
	$wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args);
	unset( $merged_filters[ $tag ] );
	return true;
}

Die Parameter $tag und $function_to_add haben wir oben schon in dem Beispiel für Actions benutzt. Dabei repräsentiert $tag den eigentlichen Filter, zum Beispiel the_title und $function_to_add die Funktion, die ausgeführt werden soll, zum Beispiel wpc_manipulate_the_title(). Der optionale Parameter $priority zeigt an, in welcher Reihenfolge Aktionen ausgeführt werden sollen. Der Wert 10 ist für die meisten Filter ausreichend. Mit dem Parameter $accepted_args legen wir fest, wie viele Argumente eine Funktion mitbekommt. Dieser Wert ist für uns interessant, da einige Filter mehr als einen Parameter liefern können. Dazu gleich mehr.

Filter werden in WordPress mit dem Befehl (Link zum Source) apply_filters() ausgeführt. Wie auch in do_action() werden hier alle registrierten Filter durchlaufen und über call_user_func_array() geladen. Haben wir im Core einen Filter, dann kann das so (Link zum Source) aussehen:

return apply_filters( 'the_title', $title, $id );

Wie wir sehen, werden zwei Parameter registriert, zum einen $title (im Grunde ist das der fertige String für einen Beitragstitel kurz vor der Ausgabe) und $id (das ist die ID des Beitrages, für welchen der Titel bestimmt ist). Da innerhalb von apply_filters() der Befehl call_user_func_array() steht werden auch alle Parameter mitgeliefert, aber interessanterweise auch nur so viele, wie wir im Parameter $accepted_args mitgeben. Das heisst, dass wir uns aussuchen können, ob wir alle Parameter brauchen, oder nur einen.

Möchten wir zum Beispiel nur den Titel eines Beitrages manipulieren, dann kann der Code so aussehen:

add_filter( 'the_title', 'my_the_title' );

Wie wir hier sehen können, geben wir an add_filter() keine weiteren Parameter mit, da die Default-Werte für unsere Zwecke völlig ausreichend sind. Möchten wir jedoch noch einen Wert zum Beispiel aus den Post-Metas laden, um diesen am Titel zu zeigen, dann sieht das so aus:

add_filter( 'the_title', 'my_the_title', 10, 2 );

Da wir jetzt die beiden möglichen Parameter aus den apply_filters() verwenden, müssen wir den Parameter $accepted_args auf zwei setzen. (Da wir die Priorität nicht ändern wollen, setzen wir diesen Wert auf den Standard-Wert, also 10.) In der Funktion call_user_func_array() wird nun ein Parameter-Array übergeben, welches genau zwei Werte enthält. Das Ganze kann dann so erweitert werden, dass zum Beispiel (Link zum Source) im Filter request_filesystem_credentials bis zu sechs Parameter verwendet werden können.

Eigene Filter und Actions definieren

Was WordPress anfängt, sollte in eigenen Plugins und Themes unbedingt fortgeführt werden. Ich selbst finde es sehr hilfreich, wenn eigene Actions und Filter definiert werden. Somit ist es möglich, über zusätzliche Plugins das eigene Plugin zu erweitern. Das Plugin WooCommerce ist dafür eines der schönsten Beispiele. Hier werden im ganzen Source-Code eigene Actions und Filter registriert, die dann über Plugins manipuliert werden können. Nur so ist es möglich, dass es mehrere hundert Plugins allein für WooCommerce gibt.

Als erstes Beispiel für eigene Actions möchte ich den Template-Part eines Themes zeigen (etwas gekürzt). Für den Kontext: Dieser Template Part wird innerhalb der single.php via get_template_part() aufgerufen.

>
', '' ); ?>
<

Hier werden zwei Actions definiert: wpc_my_theme_single_post_before_content und wpc_my_theme_single_post_after_content. Schreiben wir ein Plugin für das Theme, dann können wir eine dieser beiden Actions benutzen. Natürlich können wir uns auch an the_content hängen. Aber vielleicht gibt es ja den einen oder anderen Anwendungsfall, bei dem diese beiden Actions gebraucht werden.


In diesem Beispiel geht es um ein Plugin. Hier haben wir eine Action registriert, nachdem alle Arbeiten im Plugin abgeschlossen sind. Damit ist es möglich, dass weitere Plugins sich genau hinter diese Action klemmen und so Code ausführen, der für das Plugin interessant wäre.

Für Filter sieht die Sache ähnlich aus. Wo auch immer wir mit definierten Variablen arbeiten, kann es Sinn machen, eigene Filter zu registrieren:


Hier haben wir einen Filter auf die Variable $meta gelegt, welcher zwei Parameter mitbringt. Damit ist es möglich, den Wert über verschiedene Plugins zu beeinflussen.

Komplexe Funktionen sauber gestalten

Neben einem ordentlichen Code-Style (Präfixe, richtige Benennung von Funktionen und Variablen) ist es wichtig, dass komplexere Funktionen mit mehreren Parametern gut gestaltet werden. Anhand dieser Beispielfunktion können wir die Möglichkeiten einschätzen:

 
function my_plugin_wp_list_categories( $output, $args ) {

    $default_args = array(
        'show_option_none' => __('No categories'),
        'orderby' => 'name',
        'order' => 'ASC',
        'style' => 'list',
        'show_count' => 0,
        'hide_empty' => 1,
        'use_desc_for_title' => 1,
        'child_of' => 0,
        'feed' => '',
        'feed_type' => '',
        'feed_image' => '',
        'exclude' => '',
        'exclude_tree' => '',
        'current_category' => 0,
        'hierarchical' => true,
        'title_li' => __( 'Categories' ),
        'echo' => 1,
        'depth' => 0,
        'taxonomy' => 'category'
    );

    /**
     * Pre-Filter to bypass this whole function
     *
     * @param	string $output the finished HTML output
     * @param	array $args the arrays which was given with this function
     * @param	array $default_args the default arguments
     */
    $rtn = apply_filters( 'pre_my_plugin_wp_list_categories', FALSE, $output, $args, $default_args );
    if ( $rtn !== FALSE )
        return $rtn;

    // Fallback with default Args if there is no pre breakup
    $args = wp_parse_args( $args, $default_args );
    /**
     * Filter the arguments of this function
     *
     * @param	array $args the final merged arguments
     */
    $args = apply_filters( 'my_plugin_wp_list_categories_args', $args );

    // Process
    $markup = '';

    // …

    /**
     * Filter the return value of this function
     *
     * @param	string $markup the finished HTML output
     * @param	array $args the final $args arrays
     */
    return apply_filters( 'my_plugin_wp_list_categories', $markup, $args );

}

add_filter( 'get_term_list', 'my_plugin_wp_list_categories', 10, 2 );

Innerhalb dieser Funktion werden insgesamt drei Filter registriert: pre_my_plugin_wp_list_categories, my_plugin_wp_list_categories_args und my_plugin_wp_list_categories. All diese Filter haben eine bestimmte Aufgabe:

  • pre_my_plugin_wp_list_categories - Der Pre-Filter dient dazu, die komplette Funktion auszuhebeln, um einen eigenen return zu definieren. Der erste Parameter 'FALSE’ dient als Überprüfung für den möglichen break der gesamten restlichen Funktion.
  • my_plugin_wp_list_categories_args - Der Args-Filter ist für Funktionen mit mehr als einem Parameter gedacht. Hier können vor dem eigentlichen Programmcode noch die Argumente manipuliert werden. Als Übergabewert dient das Array $args, welches schon mit den $default_args zusammengeführt wurde.
  • my_plugin_wp_list_categories - Der Return-Filter dient dazu, das letztendliche Ergebnis nochmals filtern zu können, um etwaige Features anzuhängen. Im Return-Filter können weitere Attribute zu der $return_value und den $args hinzugefügt werden, um diese sinnvoll zu ergänzen.

Allein mit diesen drei Filtern ist es möglich, das komplette Plugin von einem anderen Plugin ändern zu lassen - Pluginception sozusagen.

Fazit

Hooks sind eine tolle Sache und sollten verwendet werden, um WordPress zu erweitern. Auch eigene Plugins und Themes sollten eigene Filter mitbringen. Entwicklerinnen bieten wir so die Möglichkeit, unsere Sachen zu ändern, ohne umständlich zu Forken, das Support-Forum zu bemühen oder überhaupt irgendeinen zeitaufwendigen Kontakt herzustellen. So können Dinge mit dem eigenen Plugin entstehen, an welche bei der Entwicklung gar nicht gedacht worden sind.
 
Der Beitrag wurde ursprünglich auf wpcoding.de veröffentlicht.

Newsletter abonnieren

Das könnte dich auch interessieren

WooCommerce-Bestellwert erhöhen

Die Umsätze in WooCommerce erhöhen, ohne mehr Pakete zu versenden? Ist das Zauberei? Nein, das geht ganz ohne Hokuspokus – und zwar mit der Erhöhung d ...

Mehr erfahren

WooCommerce SEO - Produktbilder und Produktgalerie

Im letzten WooCommerce SEO Beitrag hier im MarketPress-Blog ging es um Produktseiten, Produktbeschreibungen und Produktkategorien. Heute sind die Produktbi ...

Mehr erfahren

WooCommerce SEO - Produktseiten, Produktbeschreibungen & Produktkategorien

Mit der Suchmaschinenoptimierung für WooCommerce kann man gar nicht früh genug anfangen. Startvorteile im Rennen um die besten Plätze sichert sich, wer ...

Mehr erfahren

Kommentare

5 Kommentare

  1. Max Schmitt

    Hallo!

    Kann es sein, dass mit der Darstellung der Code-Beispiele auf der Seite irgendwas nicht funktioniert? Ich lese nur was von [crayon-591c471361f6d157639670 inline=”true” ] etc. …

    Gruß, Max

    1. @Max: Danke für den Hinweis, das muss bei einem Relaunch verloren gegangen sein. Ich versuche, die fehlenden Teile zu rekonstruieren. Wenn es mir gelingt, melde ich mich hier noch einmal (Thomas, der Autor, arbeitet mittlerweile nicht mehr bei uns).

      Ich frage unseren neuen Entwickler, ob er es ansonsten neu schreibt, das kann aber durch ein aktuelles Release etwas dauern.

      1. Max Schmitt

        Ok, danke für die Rückmeldung!

      2. Hallo Max,

        unsere Entwickler haben den Artikel nun überarbeitet.

        Herzliche Grüße
        Tino

  2. […] noch ein sehr flexibles System von Hooks, welches Thomas Herzog drüben bei MarketPress recht gut beschreibt. In der Regel kommt man für die meisten Anpassungen im Child-Theme jedoch ohne Hooks […]

Schreibe eine Antwort

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

Du kannst folgende HTML Tags verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>