Изменение контейнеров изображений на стороне сервера в WordPress
Всякий раз, когда вы создаете собственные решения для других людей, вы можете столкнуться с нюансами того, как WordPress отображает контент.
Обычно это связано с темой, как минимум одним плагином или их комбинацией. А если вам нужно работать с изображениями по отдельности, это может стать проблемой. Проблема даже с попыткой написать такой пост в том, что сложно даже описать ситуацию, в которой вам может понадобиться что-то подобное.
Тем не менее, я собираюсь сделать все, что в моих силах. То есть я хочу поделиться тем, как изменять контейнеры изображений на стороне сервера перед их рендерингом на стороне клиента и делать это с помощью библиотеки PHP DOMDocument.
Звучит как много? Надеюсь, я смогу сломать это достаточно легко.
Прежде чем приступить к коду, предположим, что у вас есть объект, который отображает изображения в одном элементе абзаца (или любом типе элемента блочного уровня), и вам нужно обернуть каждый элемент в некоторый тип класса, чтобы вы могли получить доступ через CSS или JavaScript.
Где мы
Итак, для начала предположим, что изображения визуализируются следующим образом:
И вам нужно сделать их так:
Теперь вам может понадобиться или не понадобиться пространство между ними. На самом деле это не имеет значения, потому что вы можете контролировать это через CSS. Но у вас есть контроль над тем, как это отправляется по сети на сторону клиента.
Для этого вам понадобится несколько вещей:
- Доступ к библиотеке PHP DOMDocument,
- Понимание того, как манипулировать контентом с помощью хука WordPress the_content ,
- Стратегия того, как вы хотите обернуть изображения.
Я пройдусь по каждому из них в коде, но достаточно сказать, что я собираюсь обернуть каждое изображение элементом абзаца. Очевидно, вы можете делать все, что захотите.
Как это сделать
Во-первых, убедитесь, что вы объявили, что используете содержимое документа DOM над своим классом :
<?php
namespace Acme;
use DOMDocument;
class AcmeContentSubscriber
{
// ...
}
Затем создайте хук для the_content. Как вы это сделаете, будет зависеть от того, как вы организовали свой код (будь то ООП или нет). В этом примере я покажу очень простой пример того , как это сделать :
<?php
public function contentSubscriber()
{
add_action( 'the_content', [$this, 'addImageAttributes']);
}
После этого вам нужно будет немного поработать (все это ниже, но выходит за рамки этого поста). Это включает:
- преобразование кодировки объектов HTML,
- создание экземпляра документа DOM,
- загрузка HTML поста из входящего контента
В коде это должно выглядеть так :
<?php
public function addImageAttributes($content)
{
$content = mb_convert_encoding($content, 'HTML-ENTITIES', "UTF-8");
$document = new DOMDocument();
libxml_use_internal_errors(true);
$document->loadHTML(utf8_decode($content));
// ...
}
Затем вам нужно перебрать элементы img и убедиться, что вы устанавливаете правильный атрибут. Вы можете использовать класс, атрибут данных или что-то другое. Эта часть не имеет значения.
Обратите внимание, что для данного изображения вы захотите проверить, что следующий элемент не является элементом абзаца, поскольку это то, что я решил обернуть изображением. Другими словами, если следующий элемент не является абзацем, мы обернем этот элемент в элемент абзаца.
Для этого скелет функции main должен выглядеть так :
<?php
public function addImageAttributes($content)
{
$content = mb_convert_encoding($content, 'HTML-ENTITIES', "UTF-8");
$document = new DOMDocument();
libxml_use_internal_errors(true);
$document->loadHTML(utf8_decode($content));
$images = $document->getElementsByTagName('img');
foreach ($images as $image) {
$image->setAttribute('class', 'acme-iamge');
if ($image->nextSibling->tagName !== 'p') {
$this->wrapImage($document, $image);
}
}
// ...
}
Тогда функция, отвечающая за перенос элемента в абзац, должна выглядеть так :
<?php
/**
* This function is used to wrap individual images in a paragraph element.
*
* @param $document The DOM Document which is to be rendered.
* @param $image The image to wrap with the new paragraph element.
*/
private function wrapImage($document, $image)
{
if (null === $image) {
return;
}
$wrapper = $document->createElement('p');
$wrapper->setAttribute('class', 'acme-image');
$image->parentNode->replaceChild($wrapper, $image);
if (null !== $image) {
$wrapper->appendChild($image);
}
}
Обязательно прочитайте DocBlock кода, чтобы понять, как работает функция. Проще говоря:
- он принимает экземпляр документа и элемент изображения,
- создает элемент абзаца,
- добавляет атрибут класса
- заменяет исходный элемент изображения абзацем,
- и добавляет изображение в качестве дочернего элемента
И поскольку мы создали объект документа в методе, нам не нужно ничего возвращать.
Окончательная версия исходной функции должна выглядеть так :
<?php
public function addImageAttributes($content)
{
$content = mb_convert_encoding($content, 'HTML-ENTITIES', "UTF-8");
$document = new DOMDocument();
libxml_use_internal_errors(true);
$document->loadHTML(utf8_decode($content));
$images = $document->getElementsByTagName('img');
foreach ($images as $image) {
$image->setAttribute('class', 'acme-image');
if ($image->nextSibling->tagName !== 'p') {
$this->wrapImage($document, $image);
}
}
return $document->saveHTML();
}
И ваш вывод должен выглядеть как на изображении выше. Однако помните; вам нужно вернуть результаты экземпляра документа в WordPress, чтобы он правильно отображал HTML. Именно это и делает функция saveHTML в приведенном выше коде.