WordPress и домены-синонимы (алиасы)
{% import 'macros/index.njk' as macro with context %}
{{ macro.task ('сделать так, чтобы один сайт на WordPress с одной БД открывался по множеству разных доменов и не сойти с ума.') }}
Ситуация такая: есть сайт. Внутри — WordPress и свой мир. У сайта есть команда разработки. Для каждого разработчика придумано своё окружение и работы ведутся на dev-домене. dev0.*.tld
, dev2.*.tld
, ну и пока не закончатся разработчики или цифры.
Ещё есть обычные зеркала сайта — маркетинг придумал домен в другой доменной зоне, глобальные, региональные и какие угодно. И при этом всё это живёт на одной БД, ну, да. Не спрашивайте. Так бывает. Реальность достаточна сурова и объясняется контекстом (который до конца никому не понятен) или фразой типа: «так устроена жизнь».
И вот я совсем не понимаю, зачем в WordPress в базе данных в пугающе многих местах, захардкожен домен сайта. При варианте разработки «сделал локально → перенёс на удалённый сервер», человечество изобрело для этого костыля другие костыли или просто в лоб заменяют SQL-запросом все совпадения домена. Потрясающе.
Константы WP_SITEURL
и WP_HOME
¶
Решение (трюк?) с переопределением констант WP_SITEURL
и WP_HOME
работают не так, как нужно: они переопределяются, но не изменяются. И чаще всего вы получите при обращении к dev0.*.tld
запрос ресурсов, статики и всего, с абсолютным урлом основного домена. На dev-окружениях такое и бесит и недопустимо.
// wp-config.php define( 'WP_HOME', 'https://' . $_SERVER['HTTP_HOST'] ); define( 'WP_SITEURL', 'https://' . $_SERVER['HTTP_HOST'] );
«Умная переопределялка» для хоста через $_SERVER['HTTP_HOST']
будет вести себя точно также из-за абсолютных ссылок в БД.
/** * Вторая попытка определения хоста для дев- и прод-доменов */ switch ( strtolower( $_SERVER['HTTP_HOST'] ) ) { case 'dev.7site.tld': define('WP_SITEURL', 'https://dev.7site.tld'); define('WP_HOME', 'https://dev.7site.tld'); break; case '7site.tld': define('WP_SITEURL', 'https://7site.tld'); define('WP_HOME', 'https://7site.tld'); break; default: define('WP_SITEURL', 'https://site.tld'); define('WP_HOME', 'https://site.tld'); break; }
Разумеется, при этом менять что-то в самой БД — нельзя. При этом хочется. А нельзя.
Кажется, логичным: заменять то, что нужно и там, где надо (дорогостоящая операция по производительности). Так уже работает, например, для медиафайлов на dev-доменах: медиа загружены на основной домен, менеджер при тестировании смотрит окружение разработчика, но урл для картинки будет заменён на урл боевого домена:
<img
class="gallery__img"
alt="<?= $item['alt']; ?>"
draggable="false"
src="<?= str_replace( 'dev1.', '', $item['src'] ); ?>">
Что же делать ¶
Выкинуть из кода все переопределения констант.
Создать файл wp-domains.php
в корне. Внутри описать магию с ob_start
+ str_replace
.
<?php
/**
* https://github.com/MihanEntalpo/WordpressGitTools/blob/master/wp-domains.php
*/
//Проверим, не запускался ли данный скрипт ранее:
if (defined("WP_DOMAINS_INITIALIZED")) return;
const WP_DOMAINS_INITIALIZED = 1;
//Домены на котором может работать сайт, локальные и удалённые,
$domains_enabled = array(
"site.tld",
"7site.tld",
"dev.7site.tld"
);
//Добавим в массив $_SERVER все необходимые для проверки ключи,
//которых там может не быть
$_SERVER = array_replace(
array(
"HTTPS" => "", "HTTP_HTTPS" => "", "REQUEST_SCHEME" => "", "SERVER_PORT" => ""
),
$_SERVER
);
//Проверим, используется ли https?
$https =
(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
|| (!empty($_SERVER['HTTP_HTTPS']) && $_SERVER['HTTP_HTTPS'] != 'off')
|| $_SERVER['REQUEST_SCHEME'] == 'https'
|| $_SERVER['SERVER_PORT'] == 443;
//Составим адрес хоста
$site_addr = ($https ? "https://" : "http://") . $_SERVER['HTTP_HOST'] . "/";
//Заменим адрес хоста на тот, что мы вычислили
if (!defined("WP_HOME")) define("WP_HOME", $site_addr);
if (!defined("WP_SITEURL")) define("WP_SITEURL", $site_addr);
//Добавим обработчик выдачи сайта, который будет заменять во всех ссылках
//домены на текущий домен
ob_start(function ($data) use ($domains_enabled, $https)
{
$current_host = $_SERVER['HTTP_HOST'];
$replace = array();
$scheme = $https ? "https" : "http";
foreach ($domains_enabled as $domain) {
if ($current_host == $domain) continue;
$replace["http://$domain/"] = "$scheme://$current_host/";
$replace["https://$domain"] = "$scheme://$current_host";
$replace["//$domain/"] = "//$current_host/";
$replace["//$domain"] = "//$current_host";
}
return str_replace(array_keys($replace), array_values($replace), $data);
});
Подключить файл wp-domains.php
в файлы wp-config.php
и wp-load.php
.
require_once __DIR__ . '/wp-domains.php';
Проверено на хостинге timeweb.ru, четырёх доменах-синонимов и трёх поддоменах.