Введение
wbDom — это мощный шаблонизатор для Server-Side Rendering (SSR) и работы с HTML шаблонами в PHP. Библиотека предоставляет jQuery-подобный API для поиска, модификации и компиляции HTML-шаблонов с поддержкой переменных, условий (if/else), циклов (foreach) и кастомных тегов. Идеально подходит для создания компонентных шаблонов и реализации паттерна Template Engine в PHP-приложениях.
Основные возможности:
- 🎯 jQuery-подобный API - интуитивные методы find(), findAll(), findOne() для DOM-селекторов
- 📝 Работа с переменными - поддержка {{variable}} синтаксиса с точечной нотацией (data binding)
- 🔄 Условия и циклы - встроенная поддержка if/else атрибутов (conditional rendering)
- 🧩 Кастомные теги - модульная система компонентов для Server-Side Rendering
- 🔧 DOM-манипуляции - замена, изменение содержимого элементов (template manipulation)
- ⚡ Производительность - кеширование DOM-документа для быстрой работы (template caching)
- 🌐 SSR-ready - готовые решения для Server-Side Rendering без JavaScript фреймворков
Производительность:
wbDom автоматически кеширует распарсенный DOM-документ, что обеспечивает высокую скорость обработки шаблонов даже при многократном использовании. Оптимизирован для работы в production-окружении с поддержкой template precompilation и hot reload в режиме разработки.
Современный подход:
wbDom поддерживает компонентную архитектуру, декларативные шаблоны и Server-Side Rendering для создания масштабируемых веб-приложений на PHP.
Установка
Шаг 1: Клонирование репозитория
Клонируйте репозиторий wbDom на ваш сервер:
git clone https://gitflic.ru/project/digiport/wbdom.gitИли добавьте как подмодуль в существующий проект:
git submodule add https://gitflic.ru/project/digiport/wbdom.git wbdomШаг 2: Подключение библиотеки
Подключите файл wbdom.php в начале вашего приложения:
require_once __DIR__ . '/wbdom/wbdom.php';💡 Путь к файлу:
Убедитесь, что путь к файлу соответствует структуре вашего проекта. Если вы клонировали репозиторий в корневую директорию проекта, используйте путь выше.
Шаг 3: Обновление кода
Для обновления библиотеки до последней версии выполните:
cd wbdom
git pull origin masterЕсли используете подмодуль:
git submodule update --remote wbdomБыстрый старт
💡 SSR в действии: Все примеры ниже демонстрируют Server-Side Rendering - HTML генерируется на сервере PHP и отправляется клиенту уже готовым, без необходимости JavaScript для первоначального рендеринга.
Пример 1: Простая компиляция шаблона
<?php
require_once __DIR__ . '/wbdom/wbdom.php';
// Создаем экземпляр с шаблоном
$dom = new wbDom('<h1>Привет, {{name}}!</h1>');
// Устанавливаем переменные
$dom->variables = ['name' => 'Мир'];
// Получаем результат
echo $dom->outer();
// Вывод: <h1>Привет, Мир!</h1>Пример 2: Загрузка шаблона из файла
<?php
require_once __DIR__ . '/wbdom/wbdom.php';
// Загружаем шаблон из файла
$dom = new wbDom();
$dom->fromFile(__DIR__ . '/templates/index.html');
// Устанавливаем данные
$dom->variables = [
'title' => 'Главная страница',
'user' => [
'name' => 'Иван',
'email' => 'ivan@example.com'
]
];
// Компилируем и выводим
echo $dom->outer();Пример 3: Загрузка шаблона из текста
<?php
require_once __DIR__ . '/wbdom/wbdom.php';
$html = '<div class="card">
<h2>{{title}}</h2>
<p>{{description}}</p>
</div>';
$dom = new wbDom();
$dom->fromText($html);
$dom->variables = [
'title' => 'Заголовок',
'description' => 'Описание карточки'
];
echo $dom->outer();Работа с переменными (Data Binding)
💡 Data Binding: Система переменных в wbDom реализует концепцию одностороннего data binding (one-way data binding). Данные передаются из PHP в шаблон и автоматически подставляются в нужные места.
Базовое использование
$dom = new wbDom('<p>Имя: {{name}}, Возраст: {{age}}</p>');
$dom->variables = [
'name' => 'Иван',
'age' => 25
];
echo $dom->outer();
// Вывод: <p>Имя: Иван, Возраст: 25</p>Точечная нотация (вложенные массивы)
$dom = new wbDom('<div>
<h1>{{user.name}}</h1>
<p>Email: {{user.email}}</p>
<p>Город: {{user.address.city}}</p>
</div>');
$dom->variables = [
'user' => [
'name' => 'Иван Иванов',
'email' => 'ivan@example.com',
'address' => [
'city' => 'Москва',
'street' => 'Ленина, 1'
]
]
];
echo $dom->outer();Доступ к элементам массива
$dom = new wbDom('<ul>
<li>{{colors[0]}}</li>
<li>{{colors[1]}}</li>
<li>{{colors[2]}}</li>
</ul>');
$dom->variables = [
'colors' => ['Красный', 'Зеленый', 'Синий']
];
echo $dom->outer();Метод vars() для установки переменных
$dom = new wbDom('<p>{{user.name}} - {{user.age}} лет</p>');
// Установка переменных через метод vars()
$dom->vars('user.name', 'Иван');
$dom->vars('user.age', 30);
echo $dom->outer();Метод fetch() для добавления данных
$dom = new wbDom('<p>{{name}} - {{role}}</p>');
// Добавляем данные постепенно
$dom->fetch(['name' => 'Иван']);
$dom->fetch(['role' => 'Разработчик']);
echo $dom->outer();Работа с DOM
💡 Работа с DOM: wbDom предоставляет полный набор методов для работы с DOM-структурой, включая поиск элементов, фильтрацию, обход дерева и модификацию элементов. Все методы совместимы с jQuery API.
Содержание раздела:
Поиск элементов
find() - поиск коллекции элементов
$dom = new wbDom('<div>
<p class="text">Первый параграф</p>
<p class="text">Второй параграф</p>
<p class="text">Третий параграф</p>
</div>');
// find() возвращает WbNodeList (коллекцию элементов)
$paragraphs = $dom->find('.text');
// Можно применять операции к коллекции
foreach ($paragraphs as $p) {
$p->setAttribute('data-index', $p->getIndex());
}findOne() - поиск первого элемента
$dom = new wbDom('<div>
<h1 id="title">Заголовок</h1>
<p>Текст</p>
</div>');
// Находим первый элемент
$title = $dom->findOne('#title');
if ($title) {
// Изменяем содержимое
$title->text('Новый заголовок');
}
echo $dom->outer();findAll() - поиск всех элементов
$dom = new wbDom('<ul>
<li>Элемент 1</li>
<li>Элемент 2</li>
<li>Элемент 3</li>
</ul>');
// Находим все элементы li
$items = $dom->findAll('li');
foreach ($items as $index => $item) {
$item->text("Элемент " . ($index + 1));
}Фильтрация элементов
💡 Фильтрация: wbDom поддерживает полный набор методов фильтрации из jQuery API. Методы фильтрации работают с коллекциями элементов (WbNodeList) и позволяют сужать набор совпавших элементов по различным критериям.
eq() - элемент по индексу
Сужает набор элементов до элемента с указанным индексом (отрицательные индексы отсчитываются с конца).
$dom = new wbDom('<ul>
<li>Первый</li>
<li>Второй</li>
<li>Третий</li>
</ul>');
$items = $dom->find('li');
$first = $items->eq(0); // Первый элемент
$last = $items->eq(-1); // Последний элемент
echo $first->text(); // Вывод: Первый
echo $last->text(); // Вывод: Третийfirst() - первый элемент
Сужает набор элементов до первого элемента в наборе.
$dom = new wbDom('<div>
<p class="text">Первый параграф</p>
<p class="text">Второй параграф</p>
</div>');
$paragraphs = $dom->find('.text');
$first = $paragraphs->first();
echo $first->text(); // Вывод: Первый параграфlast() - последний элемент
Сужает набор элементов до последнего элемента в наборе.
$dom = new wbDom('<ul>
<li>Элемент 1</li>
<li>Элемент 2</li>
<li>Элемент 3</li>
</ul>');
$items = $dom->find('li');
$last = $items->last();
echo $last->text(); // Вывод: Элемент 3filter() - фильтрация по селектору или функции
Сужает набор элементов до тех, которые соответствуют селектору или проходят тест функции.
$dom = new wbDom('<div>
<p class="active">Активный</p>
<p class="inactive">Неактивный</p>
<p class="active">Активный 2</p>
</div>');
$paragraphs = $dom->find('p');
// Фильтрация по селектору
$active = $paragraphs->filter('.active');
// Фильтрация по функции
$filtered = $paragraphs->filter(function($index, $element) {
return strpos($element->text(), 'Активный') !== false;
});
echo $active->count(); // Вывод: 2not() - исключение элементов
Удаляет элементы из набора совпавших элементов.
$dom = new wbDom('<div>
<p class="visible">Видимый</p>
<p class="hidden">Скрытый</p>
<p class="visible">Видимый 2</p>
</div>');
$paragraphs = $dom->find('p');
$visible = $paragraphs->not('.hidden');
echo $visible->count(); // Вывод: 2has() - элементы с потомками
Сужает набор элементов до тех, которые имеют потомка, соответствующего селектору.
$dom = new wbDom('<div>
<div class="container">
<span class="badge">Новое</span>
</div>
<div class="container">
<p>Текст</p>
</div>
</div>');
$containers = $dom->find('.container');
$withBadge = $containers->has('.badge');
echo $withBadge->count(); // Вывод: 1is() - проверка соответствия
Проверяет текущий набор элементов на соответствие селектору и возвращает true, если хотя бы один элемент соответствует.
$dom = new wbDom('<div>
<p class="text">Параграф</p>
<span>Спан</span>
</div>');
$elements = $dom->find('p, span');
if ($elements->is('.text')) {
echo "Найден элемент с классом 'text'";
}
if ($elements->is('p')) {
echo "Найден параграф";
}even() - четные элементы
Сужает набор элементов до четных элементов в наборе (индексация с нуля).
$dom = new wbDom('<ul>
<li>Элемент 0</li>
<li>Элемент 1</li>
<li>Элемент 2</li>
<li>Элемент 3</li>
</ul>');
$items = $dom->find('li');
$evenItems = $items->even(); // Элементы с индексами 0 и 2
foreach ($evenItems as $item) {
echo $item->text() . "\n";
}
// Вывод:
// Элемент 0
// Элемент 2odd() - нечетные элементы
Сужает набор элементов до нечетных элементов в наборе (индексация с нуля).
$dom = new wbDom('<ul>
<li>Элемент 0</li>
<li>Элемент 1</li>
<li>Элемент 2</li>
<li>Элемент 3</li>
</ul>');
$items = $dom->find('li');
$oddItems = $items->odd(); // Элементы с индексами 1 и 3
foreach ($oddItems as $item) {
echo $item->text() . "\n";
}
// Вывод:
// Элемент 1
// Элемент 3slice() - диапазон элементов
Сужает набор элементов до подмножества, указанного диапазоном индексов.
$dom = new wbDom('<ul>
<li>Элемент 0</li>
<li>Элемент 1</li>
<li>Элемент 2</li>
<li>Элемент 3</li>
<li>Элемент 4</li>
</ul>');
$items = $dom->find('li');
$slice = $items->slice(1, 4); // Элементы с индексами 1, 2, 3
foreach ($slice as $item) {
echo $item->text() . "\n";
}
// Вывод:
// Элемент 1
// Элемент 2
// Элемент 3map() - преобразование элементов
Пропускает каждый элемент через функцию, создавая новый массив с возвращаемыми значениями.
$dom = new wbDom('<ul>
<li data-price="100">Товар 1</li>
<li data-price="200">Товар 2</li>
<li data-price="300">Товар 3</li>
</ul>');
$items = $dom->find('li');
$prices = $items->map(function($index, $element) {
return (int)$element->attr('data-price');
});
print_r($prices);
// Вывод: Array([0] => 100, [1] => 200, [2] => 300)💡 Особенности:
- Все методы фильтрации возвращают новый объект WbNodeList (не изменяют исходную коллекцию)
- Методы можно комбинировать в цепочки:
$dom->find('li')->filter('.active')->first() - Метод
filter()поддерживает как CSS-селекторы, так и callback-функции - Метод
not()может принимать селектор, элемент или callback-функцию - Метод
has()проверяет наличие потомков, соответствующих селектору - Методы
even()иodd()используют индексацию с нуля (как в jQuery) - Метод
slice()работает аналогично PHP функцииarray_slice()
Обход DOM-дерева
💡 Обход DOM-дерева: wbDom поддерживает полный набор методов обхода DOM-дерева из jQuery API. Эти методы позволяют перемещаться по структуре DOM, находить родительские элементы, дочерние элементы, соседние элементы и предков.
children() - дочерние элементы
Получает прямые дочерние элементы каждого элемента в наборе, опционально отфильтрованные по селектору.
$dom = new wbDom('<div class="container">
<p>Параграф 1</p>
<span class="highlight">Спан</span>
<p>Параграф 2</p>
</div>');
$container = $dom->findOne('.container');
$children = $container->children();
// Все дочерние элементы: p, span, p
$highlighted = $container->children('.highlight');
// Только элементы с классом highlight: span
foreach ($children as $child) {
echo $child->tag() . "\n";
}
// Вывод:
// p
// span
// pparent() - родительский элемент
Получает родительский элемент каждого элемента в наборе, опционально отфильтрованный по селектору.
$dom = new wbDom('<div class="wrapper">
<div class="container">
<p id="text">Текст</p>
</div>
</div>');
$text = $dom->findOne('#text');
$parent = $text->parent();
// Родитель: div.container
$wrapper = $text->parent('.wrapper');
// Родитель с классом wrapper: null (не прямой родитель)
echo $parent->attr('class'); // Вывод: containerparents() - все предки
Получает все предки каждого элемента в наборе, опционально отфильтрованные по селектору.
$dom = new wbDom('<div class="wrapper">
<div class="container">
<div class="inner">
<p id="text">Текст</p>
</div>
</div>
</div>');
$text = $dom->findOne('#text');
$allParents = $text->parents();
// Все предки: div.inner, div.container, div.wrapper
$containerParents = $text->parents('.container');
// Только предки с классом container: div.container
foreach ($allParents as $parent) {
echo $parent->attr('class') . "\n";
}
// Вывод:
// inner
// container
// wrapperclosest() - ближайший предок
Получает первый элемент, соответствующий селектору, при обходе вверх от текущего элемента (включая сам элемент).
$dom = new wbDom('<div class="wrapper">
<div class="container">
<div class="inner">
<p id="text">Текст</p>
</div>
</div>
</div>');
$text = $dom->findOne('#text');
$closestContainer = $text->closest('.container');
// Ближайший предок с классом container: div.container
$closestWrapper = $text->closest('.wrapper');
// Ближайший предок с классом wrapper: div.wrapper
echo $closestContainer->attr('class'); // Вывод: containersiblings() - соседние элементы
Получает все соседние элементы (братья и сестры) каждого элемента в наборе, опционально отфильтрованные по селектору.
$dom = new wbDom('<div>
<p>Параграф 1</p>
<span id="target">Целевой элемент</span>
<p class="highlight">Параграф 2</p>
<span>Спан 2</span>
</div>');
$target = $dom->findOne('#target');
$allSiblings = $target->siblings();
// Все соседи: p, p.highlight, span
$highlightedSiblings = $target->siblings('.highlight');
// Только соседи с классом highlight: p.highlight
foreach ($allSiblings as $sibling) {
echo $sibling->tag() . "\n";
}
// Вывод:
// p
// p
// spannext() - следующий сосед
Получает непосредственно следующий соседний элемент каждого элемента в наборе, опционально отфильтрованный по селектору.
$dom = new wbDom('<div>
<p id="first">Первый</p>
<span>Средний</span>
<p>Третий</p>
</div>');
$first = $dom->findOne('#first');
$next = $first->next();
// Следующий сосед: span
$nextParagraph = $first->next('p');
// Следующий сосед-параграф: p (третий)
echo $next->text(); // Вывод: СреднийnextAll() - все следующие соседи
Получает все следующие соседние элементы каждого элемента в наборе, опционально отфильтрованные по селектору.
$dom = new wbDom('<div>
<p id="first">Первый</p>
<span>Второй</span>
<p>Третий</p>
<span>Четвертый</span>
</div>');
$first = $dom->findOne('#first');
$allNext = $first->nextAll();
// Все следующие соседи: span, p, span
$nextParagraphs = $first->nextAll('p');
// Только следующие параграфы: p
foreach ($allNext as $sibling) {
echo $sibling->text() . "\n";
}
// Вывод:
// Второй
// Третий
// Четвертыйprev() - предыдущий сосед
Получает непосредственно предыдущий соседний элемент каждого элемента в наборе, опционально отфильтрованный по селектору.
$dom = new wbDom('<div>
<p>Первый</p>
<span>Второй</span>
<p id="third">Третий</p>
</div>');
$third = $dom->findOne('#third');
$prev = $third->prev();
// Предыдущий сосед: span
$prevParagraph = $third->prev('p');
// Предыдущий сосед-параграф: p (первый)
echo $prev->text(); // Вывод: ВторойprevAll() - все предыдущие соседи
Получает все предыдущие соседние элементы каждого элемента в наборе, опционально отфильтрованные по селектору.
$dom = new wbDom('<div>
<p>Первый</p>
<span>Второй</span>
<p>Третий</p>
<span id="fourth">Четвертый</span>
</div>');
$fourth = $dom->findOne('#fourth');
$allPrev = $fourth->prevAll();
// Все предыдущие соседи: p, span, p
$prevParagraphs = $fourth->prevAll('p');
// Только предыдущие параграфы: p, p
foreach ($allPrev as $sibling) {
echo $sibling->text() . "\n";
}
// Вывод:
// Третий
// Второй
// Первый💡 Особенности:
- Все методы обхода возвращают объект WbNodeList (коллекцию элементов)
- Методы можно комбинировать в цепочки:
$dom->find('p')->parent('.container')->children() - Методы
children(),parent(),parents(),siblings()поддерживают опциональный селектор для фильтрации - Метод
closest()проверяет сам элемент перед обходом вверх по дереву - Методы
next()иprev()возвращают только один элемент (или null) - Методы
nextAll()иprevAll()возвращают все соответствующие соседние элементы - Методы работают как с отдельными элементами (WbElement), так и с коллекциями (WbNodeList)
Модификация элементов
replace() - замена элемента
$dom = new wbDom('<div id="content">Старое содержимое</div>');
$element = $dom->findOne('#content');
// Заменяем элемент на новый HTML
$dom->replace($element, '<section class="new-content">Новое содержимое</section>');
echo $dom->outer();html() - получение и установка HTML содержимого
Метод html() может использоваться для получения HTML содержимого (без параметров) или для его установки (с параметром).
Получение HTML содержимого:
$dom = new wbDom('<div id="container"><h2>Заголовок</h2><p>Текст</p></div>');
$container = $dom->findOne('#container');
// Получаем HTML содержимое (без параметров)
$innerHtml = $container->html();
echo $innerHtml;
// Вывод: <h2>Заголовок</h2><p>Текст</p>
// Для WbNodeList объединяет HTML всех элементов
$items = $dom->find('p');
$combinedHtml = $items->html();
echo $combinedHtml;
// Вывод: <p>Текст</p>Установка HTML содержимого:
$dom = new wbDom('<div id="container"></div>');
$container = $dom->findOne('#container');
// Устанавливаем HTML содержимое (со строкой)
$container->html('<h2>Заголовок</h2><p>Текст параграфа</p>');
echo $dom->outer();
// Вывод: <div id="container"><h2>Заголовок</h2><p>Текст параграфа</p></div>Метод html() также поддерживает установку содержимого из элементов, объектов wbDom или WbNodeList:
// Установка из другого элемента
$source = $dom->findOne('#source');
$container->html($source);
// Установка из объекта wbDom
$otherDom = new wbDom('<section>Контент</section>');
$container->html($otherDom);
// Установка из WbNodeList
$items = $dom->find('.item');
$container->html($items);text() - получение и установка текстового содержимого
Метод text() может использоваться для получения текстового содержимого (без параметров) или для его установки (с параметром).
Получение текстового содержимого:
$dom = new wbDom('<div id="content">Начало <strong>жирный</strong> конец</div>');
$content = $dom->findOne('#content');
// Получаем текстовое содержимое (без параметров)
// Извлекает только текст, без HTML тегов
$text = $content->text();
echo $text;
// Вывод: Начало жирный конец
// Для WbNodeList объединяет текст всех элементов
$items = $dom->find('strong');
$combinedText = $items->text();
echo $combinedText;
// Вывод: жирныйУстановка текстового содержимого:
$dom = new wbDom('<div id="message"></div>');
$message = $dom->findOne('#message');
// Устанавливаем текстовое содержимое (HTML будет экранирован)
$message->text('<script>alert("XSS")</script>');
echo $dom->outer();
// Вывод: <div id="message"><script>alert("XSS")</script></div>Метод text() также поддерживает установку текста из элементов, объектов wbDom или WbNodeList (извлекается только текст без HTML тегов):
// Установка текста из другого элемента
$source = $dom->findOne('#source');
$message->text($source); // Извлекает только текст из #source
// Установка текста из объекта wbDom
$otherDom = new wbDom('<section>Текст <strong>контента</strong></section>');
$message->text($otherDom); // Результат: "Текст контента"
// Установка текста из WbNodeList
$items = $dom->find('.item');
$message->text($items); // Объединяет текст всех элементовouter() - получение полного HTML элемента
Метод outer() возвращает полный HTML элемента, включая сам тег и все его атрибуты.
$dom = new wbDom('<div id="container" class="box"><h2>Заголовок</h2><p>Текст</p></div>');
$container = $dom->findOne('#container');
// Получаем полный HTML элемента (включая сам тег)
$fullHtml = $container->outer();
echo $fullHtml;
// Вывод: <div id="container" class="box"><h2>Заголовок</h2><p>Текст</p></div>
// Для WbNodeList объединяет outer HTML всех элементов
$items = $dom->find('p');
$combinedOuter = $items->outer();
echo $combinedOuter;
// Вывод: <p>Текст</p>Отличие от html():
outer()- возвращает полный HTML элемента включая сам тег и атрибутыhtml()- возвращает только внутреннее содержимое элемента (без самого тега)
$dom = new wbDom('<div id="box" class="container">Контент</div>');
$box = $dom->findOne('#box');
$outerHtml = $box->outer();
// Результат: <div id="box" class="container">Контент</div>
$innerHtml = $box->html();
// Результат: КонтентУсловия (Conditional Rendering)
💡 Conditional Rendering: wbDom поддерживает условное отображение элементов на основе данных шаблона. Элементы рендерятся или скрываются в зависимости от значений переменных.
wbDom имеет встроенную поддержку атрибута if для HTML-элементов
и тега <if> для условного рендеринга блоков контента.
Атрибут if
wbDom имеет встроенную поддержку атрибута if для любого HTML-элемента.
Элемент отображается только если условие истинно.
$dom = new wbDom('<div>
<p if="{{showMessage}}">Сообщение видно</p>
<p if="{{!showMessage}}">Сообщение скрыто</p>
</div>');
$dom->variables = ['showMessage' => true];
$dom->fetch();
echo $dom->outer();
// Вывод: <div><p>Сообщение видно</p></div>Тег <if>
wbDom имеет встроенную поддержку тега <if> для условного рендеринга блоков контента.
Тег поддерживает атрибуты true и false, а также вложенный тег <else>.
$dom = new wbDom('<div>
<if true="{{isLoggedIn}}">
<p>Добро пожаловать, {{user.name}}!</p>
</if>
</div>');
$dom->variables = [
'isLoggedIn' => true,
'user' => ['name' => 'Иван']
];
$dom->fetch();
echo $dom->outer();
// Вывод: <div><p>Добро пожаловать, Иван!</p></div>Атрибуты:
true- условие для отображения блока (если условие истинно)false- условие для отображения блока (если условие ложно)
Тег <if> с <else>
$dom = new wbDom('<div>
<if true="{{isLoggedIn}}">
<p>Добро пожаловать, {{user.name}}!</p>
<else>
<p>Вы не авторизованы</p>
</else>
</if>
</div>');
$dom->variables = [
'isLoggedIn' => false,
'user' => ['name' => 'Иван']
];
$dom->fetch();
echo $dom->outer();
// Вывод: <div><p>Вы не авторизованы</p></div>Использование атрибута false
$dom = new wbDom('<div>
<if false="{{isGuest}}">
<p>Контент для авторизованных пользователей</p>
<else>
<p>Пожалуйста, войдите в систему</p>
</else>
</if>
</div>');
$dom->variables = ['isGuest' => true];
$dom->fetch();
echo $dom->outer();
// Вывод: <div><p>Пожалуйста, войдите в систему</p></div>Атрибут else
$dom = new wbDom('<div>
<p if="{{isLoggedIn}}" else="Вы не авторизованы">
Добро пожаловать, {{user.name}}!
</p>
</div>');
$dom->variables = [
'isLoggedIn' => false,
'user' => ['name' => 'Иван']
];
$dom->fetch();
echo $dom->outer();
// Вывод: <div><p>Вы не авторизованы</p></div>Сложные условия
$dom = new wbDom('<div>
<p if="{{age >= 18}}">Вы совершеннолетний</p>
<p if="{{age < 18}}">Вы несовершеннолетний</p>
<p if="{{role == \'admin\'}}">Вы администратор</p>
</div>');
$dom->variables = [
'age' => 25,
'role' => 'admin'
];
$dom->fetch();
echo $dom->outer();💡 Особенности:
- Атрибут
ifработает на любом HTML-элементе - Тег
<if>поддерживает атрибутыtrueиfalse - Можно использовать вложенный тег
<else>внутри<if> - Поддерживаются сложные условия с операторами сравнения (
==,!=,>=,<и т.д.) - Работает с переменными, включая строковые литералы в кавычках
- Встроенная поддержка - не требует дополнительных модулей
Циклы (Loops)
💡 Циклы: wbDom имеет встроенную поддержку тега <foreach>
для итерации по массивам и объектам. Циклы работают без дополнительных модулей.
Базовое использование foreach
$dom = new wbDom('<ul>
<foreach from="users" item="user">
<li>{{user.name}} - {{user.email}}</li>
</foreach>
</ul>');
$dom->variables = [
'users' => [
['name' => 'Иван', 'email' => 'ivan@example.com'],
['name' => 'Мария', 'email' => 'maria@example.com'],
['name' => 'Петр', 'email' => 'petr@example.com']
]
];
$dom->fetch();
echo $dom->outer();Атрибуты:
from- имя переменной с массивом для итерацииitem- имя переменной для текущего элемента (по умолчанию:item)key- имя переменной для ключа/индекса (по умолчанию:key)
Использование ключа в цикле
$dom = new wbDom('<ul>
<foreach from="colors" item="color" key="index">
<li>{{index + 1}}. {{color}}</li>
</foreach>
</ul>');
$dom->variables = [
'colors' => ['Красный', 'Зеленый', 'Синий']
];
$dom->fetch();
echo $dom->outer();
// Вывод: <ul>
// <li>1. Красный</li>
// <li>2. Зеленый</li>
// <li>3. Синий</li>
// </ul>Вложенные циклы
$dom = new wbDom('<div>
<foreach from="categories" item="category">
<h2>{{category.name}}</h2>
<ul>
<foreach from="category.items" item="item">
<li>{{item.title}}</li>
</foreach>
</ul>
</foreach>
</div>');
$dom->variables = [
'categories' => [
[
'name' => 'Категория 1',
'items' => [['title' => 'Товар 1'], ['title' => 'Товар 2']]
],
[
'name' => 'Категория 2',
'items' => [['title' => 'Товар 3']]
]
]
];
$dom->fetch();
echo $dom->outer();Условный рендеринг в цикле
$dom = new wbDom('<ul>
<foreach from="products" item="product">
<li if="{{product.inStock}}">
{{product.name}} - {{product.price}} руб.
</li>
</foreach>
</ul>');
$dom->variables = [
'products' => [
['name' => 'Товар 1', 'price' => 100, 'inStock' => true],
['name' => 'Товар 2', 'price' => 200, 'inStock' => false],
['name' => 'Товар 3', 'price' => 300, 'inStock' => true]
]
];
$dom->fetch();
echo $dom->outer();💡 Особенности:
- Поддерживает массивы и объекты, реализующие интерфейс
Traversable - Локальные переменные
itemиkeyдоступны только внутри цикла - Можно использовать вложенные циклы с разными именами переменных
- Работает совместно с атрибутом
ifдля условного рендеринга
Работа с атрибутами
Проверка наличия класса
$dom = new wbDom('<div class="active item">Элемент</div>');
$element = $dom->findOne('div');
if ($dom->hasClass($element, 'active')) {
echo "Элемент имеет класс 'active'";
}Проверка наличия атрибута
$dom = new wbDom('<input type="text" id="username" required>');
$input = $dom->findOne('#username');
if ($dom->hasAttribute($input, 'required')) {
echo "Поле обязательно для заполнения";
}Методы вывода
outer() - полный HTML
$dom = new wbDom('<div class="card">{{content}}</div>');
$dom->variables = ['content' => 'Текст карточки'];
echo $dom->outer();
// Вывод: <div class="card">Текст карточки</div>html() - алиас для outer()
$dom = new wbDom('<p>{{text}}</p>');
$dom->variables = ['text' => 'Привет'];
echo $dom->html(); // То же самое что outer()inner() - внутреннее содержимое
$dom = new wbDom('<div><p>Текст</p></div>');
echo $dom->inner();
// Вывод: <p>Текст</p>text() - только текст без HTML
$dom = new wbDom('<div><h1>Заголовок</h1><p>Текст</p></div>');
echo $dom->text();
// Вывод: ЗаголовокТекстРабота с API на уровне шаблонов
💡 API Integration: wbDom позволяет получать данные из API напрямую в шаблонах. Это может быть как внутренний API вашего приложения, так и внешние REST API. Данные загружаются на сервере во время рендеринга шаблона, что обеспечивает Server-Side Data Fetching.
Пример 1: Загрузка данных из внутреннего API
Используйте класс Api для получения данных из вашего внутреннего API:
<?php
require_once __DIR__ . '/wbdom/wbdom.php';
require_once __DIR__ . '/api/api.php';
// Создаем экземпляр API
$api = new Api();
// Получаем данные из API
$users = $api->list(['@table' => 'users', '@limit' => 10]);
// Создаем шаблон
$template = '<div class="users-list">
<h2>Список пользователей</h2>
{{#users}}
<div class="user-item">
<h3>{{name}}</h3>
<p>{{email}}</p>
</div>
{{/users}}
</div>';
$dom = new wbDom($template);
$dom->variables = ['users' => $users];
echo $dom->outer();Пример 2: Кастомный тег для работы с API
Создайте кастомный тег, который автоматически загружает данные из API:
// Кастомный тег для загрузки данных из API
class tagApiData extends tagBase {
public function getTagName() {
return 'apidata';
}
public function render($element, $data) {
$attrs = $element->attrs();
$table = $attrs['table'] ?? null;
$id = $attrs['id'] ?? null;
if (!$table) {
return '<div class="error">Не указана таблица</div>';
}
// Загружаем данные из API
require_once __DIR__ . '/../api/api.php';
$api = new Api();
if ($id) {
// Получаем одну запись
$item = $api->read(['@table' => $table, 'id' => $id]);
$items = $item ? [$item] : [];
} else {
// Получаем список записей
$items = $api->list(['@table' => $table, '@limit' => 10]);
}
// Рендерим шаблон с данными
$template = $element->inner();
$dom = new wbDom($template);
$dom->variables = ['items' => $items];
return $dom->outer();
}
}
// Регистрируем тег
$dom = new wbDom();
$dom->registerTag('apidata', new tagApiData($dom));
// Используем в шаблоне
$dom->fromText('<apidata table="users">
{{#items}}
<div class="user">{{name}} - {{email}}</div>
{{/items}}
</apidata>');
echo $dom->outer();Пример 3: Загрузка данных из внешнего REST API
Используйте file_get_contents() или curl для загрузки данных из внешних API:
<?php
require_once __DIR__ . '/wbdom/wbdom.php';
// Загружаем данные из внешнего API
$apiUrl = 'https://api.example.com/users';
$context = stream_context_create([
'http' => [
'method' => 'GET',
'header' => 'Content-Type: application/json'
]
]);
$response = file_get_contents($apiUrl, false, $context);
$users = json_decode($response, true);
// Используем данные в шаблоне
$template = '<div class="users">
{{#users}}
<div class="user-card">
<h3>{{name}}</h3>
<p>{{email}}</p>
</div>
{{/users}}
</div>';
$dom = new wbDom($template);
$dom->variables = ['users' => $users ?? []];
echo $dom->outer();Пример 4: Кастомный тег для внешнего API
Создайте переиспользуемый компонент для работы с внешними API:
// Кастомный тег для внешнего API
class tagExternalApi extends tagBase {
public function getTagName() {
return 'external-api';
}
public function render($element, $data) {
$attrs = $element->attrs();
$url = $attrs['url'] ?? '';
$method = $attrs['method'] ?? 'GET';
if (!$url) {
return '<div class="error">Не указан URL API</div>';
}
// Выполняем HTTP-запрос
$context = stream_context_create([
'http' => [
'method' => $method,
'header' => 'Content-Type: application/json'
]
]);
$response = @file_get_contents($url, false, $context);
if ($response === false) {
return '<div class="error">Ошибка загрузки данных</div>';
}
$apiData = json_decode($response, true);
// Рендерим шаблон с данными
$template = $element->inner();
$dom = new wbDom($template);
$dom->variables = ['data' => $apiData ?? []];
return $dom->outer();
}
}
// Использование
$dom = new wbDom();
$dom->registerTag('external-api', new tagExternalApi($dom));
$dom->fromText('<external-api url="https://api.example.com/posts">
{{#data}}
<article>
<h2>{{title}}</h2>
<p>{{body}}</p>
</article>
{{/data}}
</external-api>');
echo $dom->outer();💡 Преимущества Server-Side Data Fetching:
- Быстрая загрузка - данные загружаются на сервере до отправки HTML клиенту
- SEO-оптимизация - поисковые системы видят полный контент
- Кеширование - можно кешировать результаты API-запросов
- Безопасность - API ключи и токены остаются на сервере
- Производительность - меньше запросов от клиента, меньше нагрузка
⚠️ Рекомендации:
- Используйте кеширование для часто запрашиваемых данных
- Обрабатывайте ошибки API-запросов
- Ограничивайте количество параллельных запросов
- Используйте таймауты для внешних API
- Логируйте ошибки для отладки
Практические примеры
Пример 1: Карточка пользователя
$template = '<div class="user-card">
<img src="{{user.avatar}}" alt="{{user.name}}" if="{{user.avatar}}">
<h2>{{user.name}}</h2>
<p>{{user.email}}</p>
<p if="{{user.bio}}">{{user.bio}}</p>
<div if="{{user.isPremium}}">
<span class="badge">Premium</span>
</div>
</div>';
$dom = new wbDom($template);
$dom->variables = [
'user' => [
'name' => 'Иван Иванов',
'email' => 'ivan@example.com',
'avatar' => '/images/avatar.jpg',
'bio' => 'Разработчик PHP',
'isPremium' => true
]
];
echo $dom->outer();Пример 2: Модификация существующего HTML
// Загружаем существующий HTML
$html = file_get_contents('template.html');
$dom = new wbDom($html);
// Находим элементы и модифицируем их
$title = $dom->findOne('h1');
if ($title) {
$title->text('Новый заголовок');
}
$links = $dom->findAll('a');
foreach ($links as $link) {
$href = $link->getAttribute('href');
if ($href) {
$link->setAttribute('target', '_blank');
}
}
// Выводим модифицированный HTML
echo $dom->outer();Пример 3: Форма с условными полями
$template = '<form>
<input type="text" name="name" placeholder="Имя" required>
<div if="{{showEmail}}">
<input type="email" name="email" placeholder="Email">
</div>
<div if="{{isCompany}}">
<input type="text" name="company" placeholder="Название компании">
</div>
<button type="submit">Отправить</button>
</form>';
$dom = new wbDom($template);
$dom->variables = [
'showEmail' => true,
'isCompany' => false
];
echo $dom->outer();Пример 4: Математические выражения
$dom = new wbDom('<p>Сумма: {{a + b}}, Произведение: {{a * b}}</p>');
$dom->variables = [
'a' => 10,
'b' => 5
];
echo $dom->outer();
// Вывод: <p>Сумма: 15, Произведение: 50</p>