На протяжение последних двух лет я считал неразрешимой одну задачу. Рад сообщить, что решение найдено, и,
что важно, его можно реализовать на чистом PHP.
Библиотеки Dklab_DOMDocument является инструментом, с помощью которого вы можете реализовывать
вызываемый "на лету" компилятор с придуманного вами упрощенного диалекта XSLT
в чистый XSLT. Примером такого компилятора является, например,
система Dklab_ShortXSLT, поддерживающая сокращенный синтаксис
работы с XSLT-шаблонами: {if}, {else}, {foreach}, вставка констант и т. д.
Главная проблема, решенная в Dklab_DOMDocument, как отслеживать
инструкции xsl:include и xsl:import, автоматически пропуская через компилятор файлы, которые в них
подключаются.
Внимание! Если вы ищите библиотеку работы с упрощенным синтаксисом XSLT, то
вам нужно использовать Dklab_ShortXSLT,
построенную на основе Dklab_DOMDocument.
Возможности библиотеки
С технической точки зрения Dklab_DOMDocument это расширенная версия стандартного PHP-класса
DOMDocument, написанная на чистом PHP.
Она позволяет назначать функции-фильтры (или "компиляторы"), которые будут
вызваны перед загрузкой XSLT- или XML-документа для предварительной обработки его тела.
Библиотека предназначена, главным образом, для работы с XSLT-шаблонами. Почему же тогда в ее имени
присутствует слово "DOMDocument"? Потому, что любой XSLT-шаблон является корректным
XML-документом. А значит, хотя библиотека работает с XML, ее можно использовать для предварительной
обработки XSLT-шаблонов.
Возможности библиотеки:
Запуск фильтров для каскадно включаемых XSLT-шаблонов и XML-файлов, на которые ссылается исходный документ.
Особенно это удобно при работе с XSLT, т.к. XSLT-шаблон является XML-документом.
Итак, фильтры запускаются автоматически для шаблонов, подключаемых через
xsl:include,
xsl:import,
а также на XInclude.
Кэширование результата компиляции во временных файлах с отслеживанием
достоверности кэша. Это полезно, если компиляция занимает много времени,
а обрабатываемый XSLT- или XML-документ меняется редко.
Совместимость со стандартным интерфейсом DOMDocument. Везде, где вы применяете DOMDocument,
можно использовать и Dklab_DOMDocument.
Назначение нескольких фильтров, вызываемых последовательно.
Пример компилятора: замена имен констант на значения
Т.к. интерфейс библиотеки повторяет стандартный DOMDocument, рекомендую вначале прочитать
документацию по DOMDocument.
Прочитали? Тогда сразу перейдем к примеру.
Главный XSLT-шаблон: page.xsl
Представьте, что мы хотим автоматически заменять все вхождения вида
{{HELLO}}, {{SITE_NAME}} и т. д. на соответствующие
языковые значения в XSLT-шаблонах нашей системы. Рассмотрим следующий шаблон:
Обратите внимание на директиву <xsl:import href="1_layout.xsl" /> в предыдущем листинге.
Очевидно, что конструкции вида {{HELLO}} могут встретиться и в нем:
Но и это еще не все. Конструкция <xsl:for-each select="document('1_menu.xml')/menu/item">
выше ссылается на внешний XML-файл, в котором также могут встретиться подстановочные константы:
<?php
require dirname(__FILE__) . '/../../lib/Dklab/DOMDocument.php';
$replace = intval(@$_GET['replace']);
// Load XSLT template; assign the preprocessor and cache dir.
$xslDoc = new Dklab_DOMDocument();
$xslDoc->addPreprocessor("preprocessor");
$xslDoc->setCacheDir("/tmp/Dklab_DOMDocument_$replace");
$xslDoc->load('page.xsl');
// Initialize the processor and assign the template.
$xsl = new XSLTProcessor();
$xsl->setParameter("", "replace", $replace);
$xsl->importStyleSheet($xslDoc);
// Run the transformation.
$doc = new DOMDocument();
$doc->loadXML('<root><name>Vasily Pupkin</name></root>');
echo $xsl->transformToXML($doc);
/**
* This function is an XSLT preprocessor.
* It is called before XSLT or XML document is loaded,
* EVEN IF xsl:include, xsl:import or document() are used.
* This is a true magic, but it really works.
*/
function preprocessor($content, $file)
{
if (@$_GET['replace']) {
$content = strtr($content, array(
'{{MAIN}}' => "Main page",
'{{PAGE}}' => "Test page",
'{{HELLO}}' => "Good morning",
'{{SITE_NAME}}' => "Test site",
));
}
return $content;
}
echo "<br><br><hr>";
show_source(__FILE__);
Рассмотрим функцию-компилятор, которую мы назвали preprocessor().
Она использует strtr()
для замены входжений вида {{SITE_NAME}} на строковые значения.
Конечно, в реальной практике строки будут взяты из какого-то внешнего источника,
а не "зашиты" напрямую, как в примере.
Методы класса Dklab_DOMDocument
Вся специфика Dklab_DOMDocument заключается в следующем коде:
$xslDoc = new Dklab_DOMDocument();
$xslDoc->addPreprocessor("preprocessor");
$xslDoc->setCacheDir("/tmp/Dklab_DOMDocument_$replace");
// ...
function preprocessor($content, $file) { ... }
Метод addPreprocessor() добавляет в список фильтров указанную функцию или
метод объекта (в последнем случае синтаксис должен быть таким: addPreprocessor(array($obj, "methodName"))).
Эта функция будет вызываться каждый раз, когда в объект $xslDoc загружается XML-файл, а также
когда срабатывают инструкции xsl:include, xsl:import и даже XSLT-функция document().
Метод setCacheDir() устанавливает директорию для хранения кэша обработанных
документов. Если файл найдет в кэше, фильтры для него вызваны не будут. Заметьте, что
мы варьируем имя кэш-директории в зависимости от параметра $replace, т.к. результаты
компиляции зависят от него (будут ли подстановлены языковые константы или нет).
Методы getDocumentURI() и setDocumentURI() предназначены для изменения свойства объекта
DOMDocument $xslDoc->documentURI.
В случае необходимости смены базового URI документа не присваивайте напрямую $xslDoc->documentURI:
используйте только $xslDoc->setDocumentURI(). Иначе компиляция включаемых файлов будет невозможной.
Резюме
Библиотека Dklab_DOMDocument это низкоуровневое средство, не
компилятор с языка шаблонов, а скорее инструмент для построения такого компилятора.
Поэтому самостоятельное ее использование довольно редкое явление.
Если вы ищите систему поддержки упрощенного синтаксиса XSLT, обратите внимание
на библиотеку Dklab_ShortXSLT. Она построена с
использованием Dklab_DOMDocument и поддерживает конструкции {if}, {else}, {foreach},
вставку констант, инструкцию {} вне XML-атрибутов и т. д.