Регистрация хуков до запуска системы хуков WordPress

Обычно хуки в WordPress можно использовать только после инициализации ядра, когда глобальный объект $wp_filter уже создан. Однако иногда возникает необходимость создать хук для события раньше — ещё до того, как работает система хуков. Для этого WordPress позволяет определить глобальную переменную $wp_filter заранее.

Как это работает

В WP есть функция WP_Hook::build_preinitialized_hooks(), которая нормализует массив $wp_filter, определённый заранее, и превращает его в полноценные объекты WP_Hook.

Таким образом, вы можете заранее подготовить $GLOBALS['wp_filter'] так, будто хуки уже зарегистрированы. А WP затем в свое время превратит ваш массив в объект WP_Hook и при выполнении хука, отработает ваш коллбэк.

  • Хуки обычно регистрируются после запуска WordPress, но можно сделать это раньше, вручную определив $GLOBALS['wp_filter'].
  • Для этого используется метод WP_Hook::build_preinitialized_hooks().
  • Такой код работает только в wp-config.php или раньше.
  • Подходит для временных решений и низкоуровневых фиксов.

Формат массива $wp_filter

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

$filters = [
	'wp_fatal_error_handler_enabled' => [
		10 => [
			[
				'accepted_args' => 0,
				'function'      => function() {
					return false;
				},
			],
		],
	],
];

Хук wp_fatal_error_handler_enabled вызывается ещё до старта системы хуков. Благодаря этому его можно переопределить уже в wp-config.php.

Пример: редирект с главной страницы на поддомен

Рассмотрим реальный случай. Есть мультисайт: / (английская версия) и /ru/ (русская версия). На время разработки английской версии требовалось временно перенаправлять главную страницу //ru/.

Чтобы такое нестандартное поведение было очевидно, а код было легко найти и удалить, он был вынесен в wp-config.php. Но поскольку система хуков там ещё не работает, хук был зарегистрирован через $GLOBALS['wp_filter'].

/// Temp fix for prod site while EN version is under development.
defined( 'WP_CLI' ) || defined( 'DOING_CRON' ) || defined( 'DOING_AJAX' ) || temp_fix_for_multisite_migration_for_prod();

function temp_fix_for_multisite_migration_for_prod() {
	$host = 'example.com';
	if( $host !== $_SERVER['HTTP_HOST'] ){
		return;
	}

	// ! до изменения REQUEST_URI
	if( preg_match( '~^/ru/?$~', $_SERVER['REQUEST_URI'] ) ){
		header( "Location: https://$host", true, 307 );
		exit;
	}

	// подмена запроса
	if( '/' === $_SERVER['REQUEST_URI'] ){
		$_SERVER['REQUEST_URI'] = '/ru/';
	}

	$temp_ru_home_url_fix = static function( $url, $path, $orig_scheme, $blog_id ){
		if( ! $path || '/' === $path ){
			$url = preg_replace( '~/ru/?~', '/', $url );
		}

		return $url;
	};

	// добавляем хук на событие wp
	$GLOBALS['wp_filter'] = [
		'wp' => [
			0 => [
				[
					'function' => static fn() => add_filter( 'home_url', $temp_ru_home_url_fix, 99, 4 ),
					'accepted_args' => 0,
				],
			],
		],
	];
}

Пример: отключение экрана восстановления

$GLOBALS['wp_filter'] = [
	'wp_fatal_error_handler_enabled' => [
		10 => [[
			'function'      => static fn() => false,
			'accepted_args' => 0,
		]],
	],
];
2 комментария