Do czego służą i jak działają hooki w WordPressie (część I)

Hooki to temat, który każdy kto poważnie myśli o tworzeniu stron internetowych w WordPress musi opanować. Hooki pozwalają zmieniać funkcjonalności WordPressa bez modyfikowania jego podstawowych plików. Przejdźmy zatem do rzeczy i ogarnijmy podstawowe informacje na tematów hooków w WordPressie.

Dobre miejsce żeby zobaczyć jak działają hooki w WordPressie, znajduje się w pliku wp-includes/default-filters.php. Zawartość tego pliku składa się głównie z wywołań funkcji add_filter oraz add_action. Pytanie co tam się dzieje i jak to wszystko w ogóle działa. Poszukajmy sobie czegoś co się nam z czymkolwiek kojarzy. Wydaje się, że wp_head wygląda znajomo. Możliwe, że chodzi o funkcję wp_head.

...
add_action( 'wp_head', 'wp_print_styles', 8 );
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
add_action( 'wp_head', 'wp_generator' );
add_action( 'wp_head', 'rel_canonical' );
...

Na pewno każdy z nas spotkał się z wywołaniem tej funkcji w jakimś motywie WordPressa. Wywołanie zazwyczaj znajduje się w pliku header.php i wygląda, tak jak w przykładowym kodzie poniżej:

<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="profile" href="https://gmpg.org/xfn/11">
<?php wp_head(); ?>
</head>
...

Nawet nie znając żadnej mądrej definicji hooków w WordPressie, możemy dostrzec, że istnieje jakiś związek pomiędzy powyższymi fragmentami kodu. Zbadajmy temat trochę bardziej szczegółowo. Zobaczymy czy gdzieś istnieje definicja funkcji wp_head. Wyniki wyszukiwania najprawdopodobniej pokażą coś takiego:

Searching 7742 files for "function wp_head()"

/wp-includes/general-template.php:
 2871   * @since 1.2.0
 2872   */
 2873: function wp_head() {
 2874  	/**
 2875  	 * Prints scripts or data in the head tag on the front end.

1 match in 1 file

Zobaczmy w związku z tym, co się dzieje w definicji funkcji wp_head, która znajduje się w pliku wp-includes/general-template.php. Znajdziemy tam następujący kod:

function wp_head() {
	/**
	 * Prints scripts or data in the head tag on the front end.
	 *
	 * @since 1.5.0
	 */
	do_action( 'wp_head' );
}

Chyba zaczynamy rozumieć co się tu dzieje. Funkcja wp_head nie robi nic poza wywołaniem innej funkcji do_action z jednym argumentem w postaci ciągu znaków wp_head. Intuicyjnie możemy zgadywać, że istnieje jakiś związek pomiędzy funkcjami do_action i add_action. Wróćmy na chwilę do pliku wp-includes/default-filters.php i zobaczymy jak wyglądają wywołania add_action, które się tam znajdują. Weźmy sobie jedno z nich i rozbierzmy na części. Dla przykładu niech będzie to wywołanie znajdujące się w linii 294:

...
add_action( 'wp_head', 'wp_print_styles', 8 );
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
add_action( 'wp_head', 'wp_generator' );
add_action( 'wp_head', 'rel_canonical' );
...

Widzimy, że funkcja add_action wywoływana jest z dwoma argumentami wp_head i wp_generator będącymi ciągami znaków. Jeżeli chodzi o pierwszy argument, to już sprawdziliśmy, że istnieje funkcja wp_head, która wywołuje funkcję do_action. Zobaczymy w takim razie co znajdziemy na temat argumentu wp_generator. Prawdopodobnie będzie to jakaś funkcja. Poszukajmy jej zatem:

Searching 7742 files for "function wp_generator()"

/wp-includes/general-template.php:
 4520   * @since 2.5.0
 4521   */
 4522: function wp_generator() {
 4523  	/**
 4524  	 * Filters the output of the XHTML generator tag.

1 match in 1 file

Mamy definicję funkcji wp_generator. Widać że argumentami funkcji add_action są po prostu nazwy innych funkcji. Popatrzmy na funkcję wp_generator. Widać, że wp_generator wywołuje inną funkcję onazwie the_generator, która z kolei coś wyświetla.

/**
 * Displays the XHTML generator that is generated on the wp_head hook.
 *
 * See {@see 'wp_head'}.
 *
 * @since 2.5.0
 */
function wp_generator() {
	/**
	 * Filters the output of the XHTML generator tag.
	 *
	 * @since 2.5.0
	 *
	 * @param string $generator_type The XHTML generator.
	 */
	the_generator( apply_filters( 'wp_generator_type', 'xhtml' ) );
}

/**
 * Display the generator XML or Comment for RSS, ATOM, etc.
 *
 * Returns the correct generator type for the requested output format. Allows
 * for a plugin to filter generators overall the {@see 'the_generator'} filter.
 *
 * @since 2.5.0
 *
 * @param string $type The type of generator to output - (html|xhtml|atom|rss2|rdf|comment|export).
 */
function the_generator( $type ) {
	/**
	 * Filters the output of the XHTML generator tag for display.
	 *
	 * @since 2.5.0
	 *
	 * @param string $generator_type The generator output.
	 * @param string $type           The type of generator to output. Accepts 'html',
	 *                               'xhtml', 'atom', 'rss2', 'rdf', 'comment', 'export'.
	 */
	echo apply_filters( 'the_generator', get_the_generator( $type ), $type ) . "\n";
}

Pomińmy na razie widoczne wywołanie funkcji apply_filters i zobaczmy co wyświetla funkcja the_generator. Funkcja the_generator używa konstrukcji językowej echo, a więc wyniku jej działania nie możemy sobie po prostu zapisać w jakieś zmiennej i wyświetlić. Zbuforujmy sobie wynik działania funkcji wp_generator i zobaczmy co otrzymamy. Poniższy kod można sobie tymczasowo umieścić w pliku functions.php znajdującym się w każdym motywie WordPress.

ob_start();
wp_generator();
$var = ob_get_clean();
var_dump($var);
die;

Po przeładowaniu przeglądarki dostaniemy coś takiego:

'<meta name="generator" content="WordPress 5.3.2" />
' (length=52)

Bingo! Wydaje się, że wiemy już co się dzieje. Ten meta tag znajduje się w sekcji <head> naszej strony internetowej. Możemy łatwo wywnioskować, że kod:

add_action( 'wp_head', 'wp_generator' );

odpowiada za wywołanie funkcji wp_generator w miejscu, w którym wywoływana jest funkcja wp_head. Zobaczmy teraz jak wygląda to od strony teoretycznej. Mamy tu do czynienia z jednym z rodzajów hooków WordPressa zwanym akcją. Akcje działają w ten sposób, że w jakimś miejscu w kodzie, deklarowane jest za pomocą wywołania funkcji do_action wykonanie akcji o określonej nazwie np.:

do_action( 'wp_head' );

Oznacza to, że do każdej takiej akcji można za pomocą wywołania funkcji add_action dodać inną funkcję, która zostanie wykonana w momencie, w którym zadeklarowano wywołanie do_action. Widać to doskonale we fragmencie kodu który pokazałem na samym początku.

...
add_action( 'wp_head', 'wp_print_styles', 8 );
add_action( 'wp_head', 'wp_print_head_scripts', 9 );
add_action( 'wp_head', 'wp_generator' );
add_action( 'wp_head', 'rel_canonical' );
...

Kolejno dodawane są tu funkcje, które zostaną wykonane w ramach akcji wp_head. Trzeci argument funkcji add_action, to liczba całkowita określająca priorytet wywołania funkcji wykonywanej w ramach danej akcji. Domyślny priorytet to 10. Dzięki temu możemy bardzo łatwo dodać naszą własną funkcję, która zostanie wykonana w ramach danej akcji. Robimy to poprzez przekazanie nazwy naszej własnej funkcji jako drugi argument funkcji add_action.

add_action( 'wp_head', 'my_custom_function' );

function my_custom_function() {
  // do something
}

Równie łatwo możemy stworzyć swoją własną akcję w dowolnym miejscu kodu. Nazwa akcji może być dowolna. Możemy ją opakować w jakąś funkcję tak jak to ma miejsce w przypadku wp_head lub wywołać do_action bezpośrednio np:

<!doctype html>
<html <?php language_attributes(); ?>>
<head>
<meta charset="<?php bloginfo( 'charset' ); ?>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="profile" href="https://gmpg.org/xfn/11">
<?php wp_head(); ?>
<?php do_action('custom_wp_head_after'); ?>
</head>
...

Następnie do akcji dopinamy jakąś inną naszą funkcję:

add_action( 'custom_wp_head_after', 'my_custom_function' );

function my_custom_function() {
  // do something
}

Jeżeli chcemy usunąć jakąś funkcję z kolejki funkcji wykonywanych w ramach danej akcji używamy do tego funkcji remove_action. Funkcja przyjmuje trzy argumenty: nazwę akcji, nazwę funkcji którą chcemy usunąć oraz priorytet funkcji którą chcemy usunąć. Jeżeli podczas wywołania add_action określony został priorytet, to podczas usuwania również należy podać ten sam priorytet. W ramach ćwiczenia usuńmy sobie funkcję wp_generator, która wyświetla aktualną wersję WordPressa. W pliku functions.php umieszczamy następujący kod:

remove_action( 'wp_header', 'wp_generator');

Drugi rodzaj hooków stanowią filtry. Filtry działają bardzo podobnie z tym, że umożliwiają ingerencję ingerencję w dane zanim zostaną one użyte przez WordPressa. Na tym ogólnym określeniu czym są filtry zakończę ten trochę przydługi wpis. Mam nadzieję, że na podstawie lektury dowiedziałeś się czegoś ciekawego. Z filtrami WordPressa zmierzymy się w części drugiej.

Do góry strony