Хуки в WordPress: взгляд изнутри

Ключевая особенность гибкого фундамента WordPress состоит в умелом использовании хуков. Применяемые в темах, плагинах и ядре, хуки позволяют добиться беспрецедентного расширения при сохранении совместимости с последующими версиями WordPress. Отсюда вытекает, что понимание хуков должно составлять обязательную часть «репертуара» разработчиков. Присоединяйтесь к нам – сегодня мы рассмотрим скрытые стороны этой простой, но в то же время мощной системы. О чем пойдет речь: мы пробежимся по концепции хуков, их применению и, естественно, покажем примеры их использования.

Концепция: что такое хуки и почему они используются?

1

За любым качественно написанным программным обеспечением кроется мощная концепция, которая отвечает на вопросы «что такое?» и «почему?». Хуки – не исключение. Говоря простыми словами, хук – это заполнитель для действия. Когда происходит определенное событие, такое как публикация записи, хук активируется, позволяя реализовать соответствующую реакцию. В терминах разработчиков хуки представляют собой пример системы, управляемой событиями.

Концепция хуков требует более подробного объяснения, нежели простое определение термина – нам нужно понять, чем они полезны. Хуки играют в WordPress ключевую роль вследствие постоянного развития данной системы. Сотни разработчиков постоянно обновляют систему, а значит нет никакой возможности отредактировать базовые файлы каждого плагина, темы или отдельной настройки, так как они будут часто меняться. Вместо этого для расширения системы требуется фреймворк, позволяющий подключать внешнюю функциональность, чтобы добиться такого же мощного эффекта, как и в случае с внутренними манипуляциями. Хуки – ключевая особенность данного фреймворка.

Как применить хуки?

Как разработчик, вы должны понимать не только то, что делают хуки или почему они введены, но и знать, как их создавать. Иными словами, чтобы полностью понять систему хуков, нужно разобраться в том, как они применяются.

По аналогии с письменным тестом, взгляд в ответы – не всегда самая лучшая идея. Как утверждают многие стратегии прохождения тестов, сначала надо прочесть вопрос, сформулировать свои собственные мысли по поводу ответа, и затем уже принять решение, которое ближе всего отвечает вашему обоснованию. Подобный метод может быть применен в процессе изучения разработки программного обеспечения; вместо того чтобы заглядывать в чей-либо код, чтобы понять, как реализуется та или иная особенность, зачастую полезнее сначала написать его самостоятельно, и затем уже вернуться назад и изучить его работу. Именно так мы и поступим.

Работа с документацией

Чтобы понять то, как система реализована на практике, зачастую полезно посмотреть в документацию. Давайте изучим статьи о двух базовых функциях подцепления, представленных в кодексе:

  • add_action( $hook, $function [, $priority [, $numArgs ] ] ) – задает обработчик функции $function, который вызывается в том случае, если определенный хук $hook активирован в результате возникновения события. $priority определяет, будет ли данный обработчик функции вызван до или после других обработчиков функций (по умолчанию переменная равна 10). Чем меньше этот показатель, тем раньше будет вызвана функция. $numArgs – количество аргументов, принимаемых обработчиком функции (по умолчанию 1).
  • do_action( $hook [, $arg1 [, $arg2 [, $arg3 [, … ] ] ] ] ) – активирует определенный хук $hook путем вызова всех обрабатываемых функций с необязательными аргументами $arg1, $arg2, $arg3 и т.д.

Теперь, когда у нас в одной руке находится документация, мы можем сделать некоторые выводы по поводу этих двух функций. add_action просто связывает функцию, приоритет и количество аргументов со строкой. Это напоминает идеальную задачу для PHP массивов, которые также работают как хэш-карты, хранящие пары ключ-значение. do_action более тривиальная функция – она обращается к этому массиву, разыскивает функциональные обработчики и вызывает их впоследствии. Держа в памяти нашу схему, самое время перейти к применению хуков.

Применяем полученные знания на практике

2

Поскольку мы делаем это для получения знаний о системе хуков WordPress, нам не нужно применять эти две функции в полном соответствии с документацией. Вместо этого давайте сфокусируемся на их применении без лишних параметров, чтобы сэкономить свое время и быстро понять суть.

Перед тем как начать, давайте создадим план действий:

  • Нам понадобится глобальный массив, который будет доступен для обеих функций.
  • add_action сохранит в массив заданное имя хука и набор соответствующих обработчиков в виде пары ключ-значение.
  • do_action получит обработчики функций из массива для заданного хука и вызовет каждый из них.

Код будет выглядеть следующим образом:

$actions = array();  
  
function add_action( $hook, $function )  
{  
    global $actions;  
  
    // create an array of function handlers if it doesn't already exist  
    if( !isset( $actions[ $hook ] ) )  
        $actions[ $hook ] = array();  
  
    // append the current function to the list of function handlers  
    $actions[ $hook ][] = $function;  
}  
  
function do_action( $hook )  
{  
    global $actions;  
  
    if( isset( $actions[ $hook ] ) )  
    {  
        // call each function handler associated with this hook  
        foreach( $actions[ $hook ] as $function )  
            call_user_func( $function );  
    }  
}  

Отлично; мы создали универсальную систему хуков с помощью примерно 20 строк кода. Теперь, когда у нас есть идея того, как работают хуки, давайте погрузимся в код ядра WordPress, чтобы подтвердить наши гипотезы.

Быстро перемещаться по коду можно с помощью разных инструментов – таких как, к примеру, Yoast’s PHP Cross Reference of the WordPress Source.

Поиск по «add_action» выдает следующий код:

function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1) {  
    return add_filter($tag, $function_to_add, $priority, $accepted_args);  
}  
  
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;  
}  

add_action вызывает add_filter, который, в свою очередь, добавляет заданную функцию в массив, связанный с хуком $tag. Несмотря на то, чтобы здесь мы также используем параметры $priority и $accepted_args, данная функция полностью отвечает нашим предположениям. do_action будет чуть длиннее и сложнее:

function do_action($tag, $arg = '') {  
    [Omitted code that records statistics, processes arguments, and deals with filters]  
  
    do {  
        foreach ( (array) current($wp_filter[$tag]) as $the_ )  
            if ( !is_null($the_['function']) )  
                call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));  
  
    } while ( next($wp_filter[$tag]) !== false );  
  
    [Omitted code that cleans up]  
}  

Цикличное прохождение по связанным функциям и вызов каждой из них не является для нас чем-то странным; фактически это именно то, о чем мы говорили в нашей реализации. Следовательно, то, что мы начали с мыслей о коде, не только помогло нам лучше понять его, но и пробудило критическое мышление для решения проблемы, что очень важно в разработке программного обеспечения.

Примеры

Обладая пониманием работы хуков, давайте взглянем на два следующих примера. Первый пример – работа с RSS; вместо того чтобы рассылать уведомления через синдикацию, почему бы не использовать для этого электронную почту?

Система почтовых уведомлений

3

Чтобы реализовать данную систему, нам нужно получить дату публикации записи. Однако какой хук мы должны использовать для этого? Описание API действий предлагает список хуков наряду со связанными с ними событиями, что поможет нам получить ответ на поставленный выше вопрос. Описание хука publish_post подходит под наши требования, потому давайте добавим обработчик функций к нему:

add_action( 'publish_post', 'notify_via_email' );  

Все, что нам осталось сделать, – это отправить уведомление по электронной почте в обработчике функции notify_via_email. Обратите внимание, что хук publish_post передает ID записи как аргумент в нашу функцию. Это позволит нам получить информацию о записи с помощью функции get_post:

function notify_via_email( $post_id ) {  
    $post = get_post( $post_id );  
    $to = '[email protected]<script type="text/javascript"> 
/* <![CDATA[ */ 
(function(){try{var s,a,i,j,r,c,l,b=document.getElementsByTagName("script");l=b[b.length-1].previousSibling;a=l.getAttribute('data-cfemail');if(a){s='';r=parseInt(a.substr(0,2),16);for(j=2;a.length-j;j+=2){c=parseInt(a.substr(j,2),16)^r;s+=String.fromCharCode(c);}s=document.createTextNode(s);l.parentNode.replaceChild(s,l);}}catch(e){}})(); 
/* ]]> */ 
</script>';      
  
    $subject = 'Post Published on ' . get_bloginfo( 'name' );  
    $message = $post->post_title . ' was published on ' . get_bloginfo( 'name' ) . ' as of ' . $post->post_date . '. You may view it at ' . get_permalink( $post_id ) . '.';  
  
    wp_mail( $to, $subject, $message );  
}  

После получения записи мы применим ее заголовок, дату и постоянную ссылку в нашем почтовом сообщении. Оно затем будет отправлено с помощью функции wp_mail, которая требует адрес получателя, тему письма и сообщение в качестве своих параметров. Со всем этим наша система отправки почтовых уведомлений готова. Обратите внимание, что не рекомендуется вызывать сразу несколько функций wp_mail за один раз, поскольку это ведет к заметной задержке загрузки страниц после того, как нажата кнопка публикации записи.

Плагин Google Analytics

4

Наше первое приложение, пусть несколько надуманное, выступило кратким введением в использование хуков. Теперь мы приведем второй пример, который будет уже более близок к реальному использованию на практике. Мы создадим простой плагин Google Analytics, который автоматически вставит код отслеживания в футер страницы.

Сначала может показаться, что тема является более подходящей средой для такого кода. Но это лишь мнимое представление, ибо тема сайта восприимчива к версиям и изменениям. Код аналитики может легко потеряться при переходе от одной темы к другой. Создание плагина позволяет избежать этой проблемы, поскольку он будет оставаться активным вне зависимости от используемой темы. Вам не придется вносить какие-либо изменения в тему всякий раз после ее замены.

Но как добавить код в футер сайта? Все так же – с помощью хуков. Если вы работали с WordPress темами раньше, вы, вероятно, вызывали функции wp_head и wp_footer в хэдере и футере вашего сайта соответственно. Обе эти функции просто активируют хуки, чтобы плагины могли легко добавлять свой код в эти жизненно важные области страницы. Следовательно, мы просто добавим действие к хуку wp_footer:

add_action( 'wp_footer', 'add_google_analytics_code' );  

Наша функция add_google_analytics_code, как и предполагает ее название, печатает код Google Analytics:

<?php function add_google_analytics_code() { ?>  
<script>  
    var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];  
    (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];  
    g.async=g.src='//www.google-analytics.com/ga.js';  
    s.parentNode.insertBefore(g,s)}(document,'script'))  
</script>  
<?php } ?>  

Обязательно измените UA-XXXXX-X на ваш личный ID, зависящий от сайта. В итоге все заработает. Просто добавьте указанный код в файл и загрузите его в вашу папку с плагинами. Убедитесь в том, что плагин содержит заголовок — к примеру, такой:

<?php  
/* 
Plugin Name: Wptuts+ Google Analytics Plugin 
Plugin URI: [Insert a link to this article.] 
Description: This plugin adds Google Analytics tracking code to the footer of a WordPress site. 
Version: 1.0 
Author: Your Name 
Author URI: http://example.org 
License: GPL2 
*/  
?>  

Поменяйте имя автора, URI, ну и не забудьте активировать плагин!

Источник: wp.tutsplus.com

Блог про WordPress
Комментарии: 8
  1. samsim

    Очень интересная статейка. Очень долго пользуюсь этим CMS но до сих пор не знал что такое хуки. Теперь можно попробовать их в действии.

  2. andrew

    Здравствуйте.
    Интересует вопрос области действия переменных созданных в хуках.
    Проверил, что если в файле functions.php объявить переменную, она будет доступна как глобальная во всех хуках одного файла.
    Как сделать переменную доступной одновременно в хуках разных файлов?

  3. Дмитрий (автор)
  4. Мария

    Здравствуйте, скажите могу ли я через do_action выполнить только одну функцию прицепленную к этому экшну?
    Пример:
    вот экшн со списком хуков
    /**
    * woocommerce_single_product_summary hook.
    *
    * @hooked woocommerce_template_single_title — 5
    * @hooked woocommerce_template_single_rating — 10
    * @hooked woocommerce_template_single_price — 10
    * @hooked woocommerce_template_single_excerpt — 20
    * @hooked woocommerce_template_single_add_to_cart — 30
    * @hooked woocommerce_template_single_meta — 40
    * @hooked woocommerce_template_single_sharing — 50
    */
    do_action( ‘woocommerce_single_product_summary’ );

    а мне нужно вызвать только woocommerce_template_single_title как это сделать?

    1. Дмитрий (автор)

      Если вам нужно, чтобы выполнялось только одно действие из этого хука, то в таком случае вам нужно их все удалить с помощью remove_action, а потом подключить только одно действие с помощью add_action.

      Вот тут есть подробный пример с перемещением элементов (сменой их приоритета):

      http://wpbackoffice.com/reorder-product-page-contents-woocommerce/

  5. Игорь

    Здравствуйте Дмитрий! Скажите пожалуйста add_action(); это самостоятельная функция не связанная с add_filter(); ? Если это так , то почему в функции add_action(){

    add_filter()

    } и пожалуйста объясните, может я что не понимаю?

    1. Дмитрий (автор)

      add_action() и add_filter() — примерно одно и то же. Разница только в смысле, которым мы их наполняем. Add_filter() обязательно что-то возвращает. Add_action() срабатывает в определенный момент при событии и может ничего не возвращать. Но вообще можно использовать одно вместо другого.

  6. Дмитрий

    Здравствуйте, Дмитрий! Подскажите не могу сообразить нужно немного изменить код плагина yoast, но в код плагина лезть не охота (по понятным причинам). Вот и возник вопрос можно ли это сделать с помощью хука. Вот сам кусок кода который хотелось бы изменить
    /**
    * Take the crumbs array and convert each crumb to a single breadcrumb string.
    *
    * @link http://support.google.com/webmasters/bin/answer.py?hl=en&answer=185417 Google documentation on RDFA
    */
    private function prepare_links() {
    if ( ! is_array( $this->crumbs ) || $this->crumbs === array() ) {
    return;
    }

    foreach ( $this->crumbs as $i => $crumb ) {
    $link_info = $crumb; // Keep pre-set url/text combis.

    if ( isset( $crumb[‘id’] ) ) {
    $link_info = $this->get_link_info_for_id( $crumb[‘id’] );
    }
    if ( isset( $crumb[‘term’] ) ) {
    $link_info = $this->get_link_info_for_term( $crumb[‘term’] );
    }
    if ( isset( $crumb[‘ptarchive’] ) ) {
    $link_info = $this->get_link_info_for_ptarchive( $crumb[‘ptarchive’] );
    }

    $this->links[] = $this->crumb_to_link( $link_info, $i );
    }
    }
    хотелось бы как то убрать деактивировать часть кода отвечающего за вывод ссылки ptarchive
    Заранее благодарю!

Добавить комментарий

Получать новые комментарии по электронной почте.