WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru

Хуки в WordPress (фильтры и события)

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

Есть два типа хуков:

Фильтр (filter)
Изменяет значение переменной — получает значение и возвращает его (изменённое или нет).
Событие (action)
Запускает произвольный код — колбэк функция прикрепленный к хуку сработает в момент срабатывания хука в коде.

К хукам (фильтрам, событиям) прикрепляются PHP функции (callbacks), затем эти функции сработают, в момент срабатывания хука. Такое поведение в программировании называется перехват.

В функцию прикрепленную к хуку могут передаваться параметры (дополнительные данные). На основе этих данных в функции можно создавать какую-либо логику. Читайте ниже.

Простой пример хука-события (action):

// создадим функцию для хука - события
function echo_1(){ echo 1; }

// привяжем функцию к хуку
add_action( 'my_hook', 'echo_1' );

// создаем хук
do_action( 'my_hook' ); //> выведет 1

Пример с передачей параметра:

// создадим функции для событий
function echo_1( $data ){
	echo $data[0] .' '. $data[1] .'!';
}

// привяжем функции к хуку
add_action( 'my_hook', 'echo_1' );

// создаем хук
do_action( 'my_hook', [ 'Привет', ' мир' ] ); //> выведет "Привет мир!"

В программной части фильтры и события — это одно и тоже, т.е. в коде работает и то и другое одинаково, можно например заменять функцию add_filter() на add_action() — все будет работать! Разделение нужно, потому что это разные вещи по смыслу.

Список всех хуков WP смотрите на странице «Все хуки WordPress».

Всего в WordPress их около 2000. Некоторые важные хуки я описал и продолжаю описывать, они находятся тут.

Как работают Фильтры в WordPress

Для работы фильтра используются две функции:

  1. add_filter( hook_name, function, priority, args_num ) — добавляет/прикрепляет функцию к фильтру, которая будет вызвана в момент срабатывания фильтра. Такая функция называется «коллбэк-функция».

  2. apply_filters( hook_name, value, args... ) — создает хук-фильтр. Запускает прикрепленные к фильтру коллбэк-функцию.

На момент срабатывания хука в нём отработают только те коллбэки, которые уже были добавлены. Т.е. прикреплять коллбэк нужно до того как фильтр будет вызван с помощью apply_filters().

Пример

// коллбэк-функция
function my_filter_function( $str ){
	return 'Здравствуйте '. $str;
}

// Прикрепим коллбэк к фильтру.
add_filter( 'my_filter', 'my_filter_function' );

// Вызов фильтра
$name = apply_filters( 'my_filter', 'Владимир' );

echo $name; //> Здравствуйте Владимир

Еще пример

Допустим, у нас есть функция text(), которая возвращает текст. Нам нужно сделать так, чтобы этот текст можно было изменять за пределами этой функции. Для этого назовем наш фильтр my_filter_name и создадим хук:

function text( $text ){

	// удалим html теги из текста
	$text = strip_tags( $text );

	// возвращаем текст через фильтр.
	return apply_filters( 'my_filter_name', $text );
}

// коллбэк функция
function my_filter_function( $text ){

	// обрежем текст до 30 знаков
	return mb_substr( $text, 0, 30 ) .'...';
}

// Прикрепим функцию к фильтру
add_filter( 'my_filter_name', 'my_filter_function' );

// Вызываем text()
// функция удалит из текста html теги — это сделает она сама с помощью strip_tags().
// А дальше обрежет текст — это сделает наш фильтр

$text = 'Lorem <b>Ipsum</b> is simply dummy text of the printing and typesetting industry.';

echo text( $text );

// выведет:
// Lorem Ipsum is simply dummy te...

Фильтров в WordPress много и вы наверняка с ними встречались в темах и плагинах. Популярные из них: the_content, body_class, sanitize_user, comment_form.

меню

Как работают События в WordPress

Для работы события используются две функции:

  1. add_action( hook_name, function, priority, args_num ) — добавляет/прикрепляет функции к событию, которое вызывается с помощью do_action(). Функция должна прикрепляться к событию до того, как событие произойдет. Нужно это, чтобы во время срабатывания события PHP функции уже были прикреплены к событию.

  2. do_action( hook_name, args... ) — это и есть событие. Запускает/вызывает добавленные к событию функции. Вызывается там, где должно сработать событие.

do_action() должна вызываться после add_action(), т.е. после того, как к событию добавлена функция, что логично...

Пример

Допустим, мы делаем тему и в каком-то месте темы нам нужно вызвать функцию, но мы заранее не знаем как функция будет называться. Т.е. пользователи сами будут добавлять функцию в это место.

Для этого в этом месте шаблона мы может вызвать не функцию а событие, назовем его my_action и затем прикрепить к этому событию функцию:

// создадим функцию для события
function my_action_function( $text ){
	echo 'Событие "my_action" сработало сейчас.';
}
// Прикрепим функцию к событию 'my_action'. Это можно сделать в другом файле
// главное до того как событие сработает.
add_action( 'my_action', 'my_action_function' );

// Вызов самого события. Вставляем эту строку в то место темы где нужно,
// чтобы сработала прикрепленная к нему функция
do_action( 'my_action' ); //> на месте этого кода будет выведена надпись:
						  // 'Событие "my_action" сработало сейчас.'

С событиями вы тоже встречались их много и одни из самых популярных это: wp_head, wp_footer.

меню

Приоритет хука

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

Приоритет указывается в третьем параметре для функции которая прикрепляет функцию к хуку:

add_action( 'init', 'hook_callback1', 8 );
add_action( 'init', 'hook_callback2', 9 );

Функция с приоритетом 8 выполнится раньше функции с приоритетом 9. Если приоритет не указан, то он равен 10 — приоритет по умолчанию.

Если две функции прикреплены к одном хуку с одинаковым приоритетом, то они будут выполняться в порядке их прикрепления.

Рассмотрим как работают приоритеты на примере:

add_action( 'init', 'run_me_late', 11 );
add_action( 'init', 'run_me_early', 9 );
add_action( 'init', 'run_me_normal' );     // приоритет 10 по умолчанию
add_action( 'init', 'run_me_normal_two' ); // приоритет 10 по умолчанию

В момент срабатывания хука init функции выполняться в следующем порядке:

  1. run_me_early()
  2. run_me_normal()
  3. run_me_normal_two()
  4. run_me_late()
  • Как видно, чем больше число приоритета, тем позднее выполниться хук.

  • Если приоритет не указан, как в первом примере, то он равен 10.

  • Для фильтров приоритеты работают точно также.
меню

Параметры хука

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

Например, когда WordPress сохраняет запись, запускается событие save_post. Этот хук может передать два параметра: ID записи и объект записи:

do_action( 'save_post', $post->ID, $post );

По умолчанию передается только первый параметр $post->ID:

add_action( 'save_post', 'my_save_post_callback' );

function my_save_post_callback( $post_id ) {
	// доступен один параметр
}

Однако можно указать что мы хотим получить 2 параметра. Для этого укажем четвертый параметр add_action():

add_action( 'save_post', 'my_save_post_callback', 10, 2 );

function my_save_post_callback( $post_id, $post ) {
	// доступны 2 параметра
}

Ограничение в один параметр по умолчанию сделано для быстродействия системы хуков.

Для фильтров все работает аналогично

При создании фильтра в него можно передать сколько угодно параметров через запятую:

$str = apply_filters( 'my_filter', $str, $data1, $data2 );

Теперь, при добавлении коллбэка. Мы можем использовать эти параметры. Для этого нужно указать количество передаваемых параметров:

add_filter( 'my_filter', 'my_filter_function', 10, 3 );

3 - количество параметров, которые получит функция my_filter_function() в момент вызова.

Функция фильтра будет выглядеть так:

function my_filter_function( $str, $data1, $data2 ){

	// Можем использовать $data1, $data2

	return $str;
}
меню

Удаление хука

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

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

Для справки. Это полностью одинаковые функции и обе они удаляют хук, а фильтр это или событие, в данном случае, значения не имеет. Так например, событии и фильтр мы можем удалить используя только функцию remove_filter() или только remove_action(). Но для понятности кода, все же лучше фильтры удалять с помощью remove_filter(), а события с помощью remove_action().

Для удаления хука нужно знать:

  • название хука, например wp_footer;
  • название прикрепленной функции, например my_action_function из примера выше;
  • приоритет выполнения хука, если он был установлен при создании хука. Если при создании приоритет не был установлен, то он равен 10 и при удалении его указывать не обязательно

Если нужно удалить хук с приоритетом отличным от 10 и вы его не указали, то хук НЕ будет удален!

Пример удаления хука

Допустим где-то в плагине, к событию wp_footer прикреплена функция my_action_function, которая выводит текст в подвале темы:

add_action( 'wp_footer', 'my_action_function' );

function my_action_function( $text ){
	echo 'Это текст в подвале!';
}

В теме нам нужно удалить это событие, чтобы текст в подвале не выводился. Для этого в файле темы functions.php можно прописать такой код:

remove_action( 'wp_footer', 'my_action_function' );

Важный момент: удалять хук нужно после того как он добавлен, но еще не отработал.

Еще пример удаления хука

Демонстрация добавления и удаления хука в одном коде:

// добавляем функцию к событию my_action
add_action( 'my_action', 'my_action_function' );

function my_action_function( $text ){
	echo 'Привет!';
}

// создаем событие
do_action( 'my_action' ); //> Привет!

// удаляем добавленное ранее событие
remove_action( 'my_action', 'my_action_function' );

// создаем событие еще раз
do_action( 'my_action' ); // ничего не выведет...

Удаление с приоритетом (приоритет должен совпадать):

add_filter( 'my_filter', 'function_name', 99 );    // так добавлен
remove_filter( 'my_filter', 'function_name', 99 ); // так нужно удалять

Удаление статического метода класса:

add_filter( 'my_filter', [ 'My_Class', 'method_name' ], 15 );    // так добавлен
remove_filter( 'my_filter', [ 'My_Class', 'method_name' ], 15 ); // так нужно удалять

Удаление не статического метода класса, который добавлен с использованием $this:

class A {

	function __construct(){
		add_action( 'my_action', [ $this, 'method_name' ], 15 );
	}

	function method_name(){
		echo 'Привет!';
	}
}

$class = new A; // экземпляр

// для удаления нужно найти переменную в которую был сохранен
// экземпляр класса при создании, сейчас это $class
remove_action( 'my_action', [ $class, 'method_name' ], 15 );

Удаление хука добавленного анонимной функцией

Удалить хук с замыканием не так просто, например такой код не сработает:

add_action( 'my_action', function(){  echo 'Привет!';  } );

remove_action( 'my_action', function(){  echo 'Привет!';  } ); // не работает!

Надежно удалить хук с использованием замыкания, можно только если замыкание создавалось в переменную и у нас есть доступ к этой переменной:

$my_func = function(){
	echo 'Привет!';
};

add_action( 'my_action', $my_func );

remove_action( 'my_action', $my_func ); // Работает!

Ненадежный, но все же способ удалить хук с замыканием, когда у нас нет доступа к замыканию:

/**
 * Removes the hook when it has been added by a closure.
 * The accuracy of the function is not guaranteed - the first hook
 * that matches the priority and the number of hook arguments will be removed.
 *
 * @param string $name
 * @param int    $priority
 * @param int    $accepted_args
 */
function kama_remove_closure_hook( $name, $priority = 10, $accepted_args = 1 ){
	global $wp_filter;

	$callbacks = & $wp_filter[ $name ]->callbacks[ $priority ];

	// find our hook
	// it is not always possible to identify it unambiguously
	// at least we know that it was created with a closure
	// and we know it's priority
	foreach( $callbacks as $key => $cb ){

		if( ! ( $cb['function'] instanceof Closure ) )
			continue;

		if( $cb['accepted_args'] !== $accepted_args )
			continue;

		// remove
		unset( $callbacks[ $key ] );

		// first suitable only
		break;
	}

}

Пример её использования:

add_action( 'my_action', function(){
	echo 'Привет!';
} );

do_action( 'my_action' ); // Привет!

kama_remove_closure_hook( 'my_action', 10, 1 );

do_action( 'my_action' ); // (пусто)
меню

Создание своего хука

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

Чтобы создать событие или фильтр в коде, к которому потом можно будет добавлять функции, используется функция

Синтаксис у этих функций такой:

do_action( $tag, $arg_a, $arg_b, ... );

apply_filters( $tag, $value, $arg_a, $arg_b, ... );
$tag(строка) (обязательный)
Название хука.
$value(строка/массив/число/объект/логический) (обязательный)
Значение, которое будет передаваться функции в её первом аргументе — значение, которое нужно отфильтровать.
$arg_a(строка/массив/число/объект/логический)
Значение аргумента.
$arg_b(строка/массив/число/объект/логический)
Значение еще одного аргумента.
Пример создания хука-события

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

Итак, у нас есть функция плагина add_vote() - записывает голос, в конец этой функции добавим событие:

function add_vote(){

	// код функции
	// тут определяется ID опроса - $poll_id и выбранный ответ - $answer
	// передадим эти данные в хук.

	// создаем хук
	do_action( 'my_plugin_add_vote', $poll_id, $answer );
}

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

add_action( 'my_plugin_add_vote', 'my_add_vote_logic', 10, 2 );

function my_add_vote_logic( $poll_id, $answer ){
	global $wpdb;

	// запишем в таблицу логов, заметку что кто-то проголосовал
	$wpdb->insert( $wpdb->log_table, [
		'user_id' => 5,
		'log_massage' => 'Проголосовали за опрос '. $get_poll( $poll_id )->name
	] );

}
меню

Функции хуков

Функция Описание
do_action() Создает событие (возможность запустить php функции в нужный момент работы кода). Подключать php функцию к событию нужно с помощью add_action().
do_action_ref_array() Создает событие (хук), на который затем можно прикрепить PHP функции. Аргументы передаются в виде массива.
do_action_deprecated() Fires functions attached to a deprecated action hook.
add_action() Прикрепляет указанную PHP функцию на указанный хук. Указанная функция сработает в момент события, которое в свою очередь запускается с помощью do_action().
remove_action() Удаляет хук (событие или фильтр). Удаляет функцию прикрепленную к указанному хуку.
remove_all_actions() Removes all of the callback functions from an action hook.
did_action() Получает число, сколько раз было вызвано указанное событие (хук).
current_action() Retrieves the name of the current action hook.
doing_action() Проверяет обрабатывается ли в текущий момент указанный хук (фильтр, событие).
has_action() Проверяет была ли зарегистрирована функция для хука-события.
apply_filters() Применяет прикрепленную к указанному фильтру PHP функцию.
apply_filters_ref_array() Выполняет функции прикрепленные к указанному хуку (фильтру). Параметры передаются в массиве. Позволяет передать параметр по ссылке.
apply_filters_deprecated() Fires functions attached to a deprecated filter hook.
add_filter() Прикрепляет указанную PHP функцию к указанному хуку-фильтру. Так, во время срабатывания фильтра значение будет обработано указанной PHP функцией.
remove_filter() Удаляет указанную функцию прикрепленную к указанному фильтру.
remove_all_filters() Удаляет все хуки у указанного фильтра.
current_filter() Получает название текущего фильтра.
has_filter() Проверяет была ли прикреплена к указанному фильтру какая-нибудь функция. Можно указать название конкретной функции.
doing_filter() Проверяет обрабатывается ли в текущий момент указанный хук (фильтр, событие).
меню

Вспомогательные WordPress функции для фильтров

В WordPress есть специальные функции, которые упрощают работу с фильтрами.

Функция Описание
__return_true() Просто возвращает true. Вспомогательная функция WordPress.
__return_false() Просто возвращает false. Вспомогательная функция WordPress.
__return_zero() Просто возвращает 0. Вспомогательная функция WordPress.
__return_empty_string() Просто возвращает пустую строку: ''. Вспомогательная функция WordPress.
__return_empty_array() Просто возвращает пустой массив: array(). Вспомогательная функция WordPress.
__return_null() Просто возвращает null. Вспомогательная функция WordPress.

Например, мы может отключить все стандартные виджеты WordPress с помощью фильтра load_default_widgets, так:

function is_load_default_widgets(){
	return false;
}
add_filter( 'load_default_widgets', 'is_load_default_widgets' );

Или можно не создавать отдельную функцию которая вернет false, а использовать уже готовую функцию из ядра WordPress: __return_false():

add_filter( 'load_default_widgets', '__return_false' );

Чтобы было понятнее как это работает, давайте посмотрим на вызов фильтра load_default_widgets:

function wp_maybe_load_widgets(){

	if ( ! apply_filters( 'load_default_widgets', true ) )
		return;

	require_once( ABSPATH . WPINC . '/default-widgets.php' );

	add_action( '_admin_menu', 'wp_widgets_add_menu' );
}

При вызове функции wp_maybe_load_widgets() срабатывает фильтр. По умолчанию он всегда возвращает true и условие не выполняется — виджет подключаются. А в примерах выше мы возвращаем false и условие выполняется — виджеты не подключаются.

Это демонстративный пример из кода WordPress, чтобы объяснить принцип работы. В практике его лучше не использовать, для отключения виджетов используйте код из описания функции unregister_widget().

Аналогичный пример

Аналогичным образом можно выключить возможность публиковать записи по протоколу xmlrpc. Которой, кстати по умолчанию включен, но им мало кто пользуется.

// отключим публикацию по xmlrpc
add_filter( 'xmlrpc_enabled', '__return_false' );

Еще один пример

Закроем возможность сброса пароля, с помощью фильтра allow_password_reset:

add_filter( 'allow_password_reset', '__return_false' );

Теперь все пользователи не смогут сбрасывать пароли на сайте.

меню

Список всех хуков WordPress

Получить список всех хуков может понадобится. В процессе разработки или при поиске ошибок.

Как я упоминал выше все хуки записываются в общий массив, а точнее в глобальную переменную $wp_filters. К слову, если удалить из $wp_filters элемент массива (хук), то хук перестанет работать...

Следующий код выведет список всех хуков (фильтров и событий), которые зарегистрированы на момент вызова этого кода:

/**
 * Выводит на экран список всех хуков WordPress и функций к ним прикрепленных.
 *
 * @param строка $hook_name Название хука список фукнций которого нужно вывести.
 *
 * @version 2.1
 */
function hooks_list( $hook_name = '' ){
	global $wp_filter;
	$wp_hooks = $wp_filter;

	// для версии 4.4 - переделаем в массив
	if( is_object( reset( $wp_hooks ) ) ){
		foreach( $wp_hooks as & $object ) $object = $object->callbacks;
		unset( $object );
	}

	if( $hook_name ){
		$hooks[ $hook_name ] = @ $wp_hooks[ $hook_name ];

		if( ! is_array($hooks[$hook_name]) ){
			trigger_error( "Nothing found for '$hook_name' hook", E_USER_WARNING );
			return;
		}
	}
	else {
		$hooks = $wp_hooks;
		ksort( $wp_hooks );
	}

	$out = '';
	foreach( $hooks as $name => $funcs_data ){
		ksort( $funcs_data );
		$out .= "\nхук\t<b>$name</b>\n";
		foreach( $funcs_data as $priority => $functions ){
			$out .= "$priority";
			foreach( array_keys($functions) as $func_name ) $out .= "\t$func_name\n";
		}
	}

	echo '<'.'pre>'. $out .'</pre'.'>';
}

Пример использования:

hooks_list();

/* Получим список такого формата:

хук wp_enqueue_scripts
10  lambda_2
99  theme_scripts_styles

хук wp_footer
0   0000000072b7ec0d00002b862c2211a0enqueue_jquery_if_need
10  0000000072b7edb400002b862c2211a0footer_scripts
	0000000072b7eb7b00002b862c2211a0main_js
20  wp_print_footer_scripts
99  0000000072b7ec0d00002b862c2211a0add_script_to_footer
	0000000072b7eb1600002b862c208180
	question_close_ajax_js
	add_question_rating_ajax_js
	add_comment_rating_ajax_js
999 0000000072b7edb600002b862c2211a0footer_script
1000    wp_admin_bar_render
9999999999  0000000072b7eb0b00002b862c208180

... и т.д. ...
*/

А так выглядит элемент массива в самой переменной $wp_filter:

global $wp_filter;

print_r( $wp_filter );

/*
	[wp_title] => WP_Hook Object
		(
			[callbacks] => Array
				(
					[10] => Array
						(
							[wptexturize] => Array
								(
									[function] => wptexturize
									[accepted_args] => 1
								)

							[convert_chars] => Array
								(
									[function] => convert_chars
									[accepted_args] => 1
								)

							[esc_html] => Array
								(
									[function] => esc_html
									[accepted_args] => 1
								)

						)

					[11] => Array
						(
							[capital_P_dangit] => Array
								(
									[function] => capital_P_dangit
									[accepted_args] => 1
								)

						)

				)

			[iterations:WP_Hook:private] => Array
				(
				)

			[current_priority:WP_Hook:private] => Array
				(
				)

			[nesting_level:WP_Hook:private] => 0
			[doing_action:WP_Hook:private] =>
		)

	[widget_title] => WP_Hook Object
		(
		............... и т.д.
*/
меню

Как получить функции прикрепленные к указанному хуку?

Часто бывает нужно посмотреть, какие функции прикреплены к отдельному фильтру или событию.

Код выше позволит это сделать, для этого нужно указать в нем параметр $hook_name:

hooks_list( 'the_title' );

/* Получим:

хук the_title
10  wptexturize
	convert_chars
	trim
	func_title_add_brackets
11  capital_P_dangit

*/

Другой вариант - это просто получить данные массива

Используем глобальную переменную $wp_filters. В ней хранятся данные всех хуков.

Данные в $wp_filters добавляются по ходу обработки кода, поэтому имеет большое значение когда используется переменная $wp_filters: например, если её использовать в начале файла темы functions.php, то в ней будут все функции хуков который были добавлены до подключения этого файла, но не будет добавленных в этом файле или позднее.

/**
 * Выводит список функций прикрепленных к указанному хуку
 * @param (str) $hook Название хука, функции которого нужно вывести.
 */
function print_filters_for( $hook ){
	global $wp_filter;

	$data = isset( $wp_filter[$hook] ) ? $wp_filter[$hook] : "Хук `$hook` не найден...";

	echo '<pre>', print_r( $data, 1 ) .'</pre>';
}

Теперь, чтобы вывести список всех функций/методов прикрепленных к фильтру или событию нужно вызвать эту функцию, и указать ей название хука.

Например, получим все функции добавленные к событию wp_footer. В индексе callbacks мы получим все функции прикрепленные к хуку, в порядке приоритета их вызова.

print_filters_for( 'wp_footer' );

/* Выведет:

WP_Hook Object (

	[callbacks] => Array (

		[0] => Array (
			[echo_ads_tpls] => Array (
				[function] => echo_ads_tpls
				[accepted_args] => 1
			)
		)

		[20] => Array (
			[wp_print_footer_scripts] => Array (
				[function] => wp_print_footer_scripts
				[accepted_args] => 1
			)
		)

		[99] => Array (
			[question_close_ajax_js] => Array (
				[function] => question_close_ajax_js
				[accepted_args] => 1
			)

			[add_question_rating_ajax_js] => Array (
				[function] => add_question_rating_ajax_js
				[accepted_args] => 1
			)
		)

		[1000] => Array (
			[wp_admin_bar_render] => Array (
				[function] => wp_admin_bar_render
				[accepted_args] => 1
			)
		)
	)

	[iterations:WP_Hook:private] => Array()
	[current_priority:WP_Hook:private] => Array()
	[nesting_level:WP_Hook:private] => 0
	[doing_action:WP_Hook:private] =>
)
*/

Быстрые и дешевые лайки на любые посты в Instagram можно приобрести на сайте Doctor SMM с гарантией на выполнение заказа. Только здесь Вы найдете ресурс на свой вкус! На сайте представлен широкий выбор формата лайков: русские, англоязычные, автолайки, тв-лайки, микс и пр. Делайте заказ сейчас и получайте выгодные оптовые скидки!

39 комментов
Полезные 4 Вопросы 1 Все
    Войти