Джино: хостинг и веб-сервисы

Система Orphus
Russian version
Добавить на Del.icio.us
English version
Добавить на Digg.com

 dkLab | Конструктор | Dklab_DOMDocument: создайте свой собственный диалект XSLT 

Карта сайта :: Форум «Лаборатории» :: Проект «Денвер»
Проект «Orphus» :: Куроводство: наблы :: Конструктор


2009-02-28
Обсудить на форуме

Чайник 

На протяжение последних двух лет я считал неразрешимой одну задачу. Рад сообщить, что решение найдено, и, что важно, его можно реализовать на чистом 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-шаблонах нашей системы. Рассмотрим следующий шаблон:

Листинг 1: Главный шаблон: page.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:import href="1_layout.xsl" />
<xsl:template name="body">
    {{HELLO}}, <xsl:value-of select="//name" />!
    <h2>
    <xsl:choose>
        <xsl:when test="$replace = 0">
            <a href="?replace=1">Replace keys by TEXTS</a>
        </xsl:when>
        <xsl:otherwise>
            <a href="?replace=0">Show the KEYS back</a>
        </xsl:otherwise>
    </xsl:choose>
    </h2>
</xsl:template>
</xsl:stylesheet>

Включаемый файл: 1_layout.xsl

Обратите внимание на директиву <xsl:import href="1_layout.xsl" /> в предыдущем листинге. Очевидно, что конструкции вида {{HELLO}} могут встретиться и в нем:

Листинг 2: Включаемый макет: 1_layout.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.1"
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/">
    <html>
    <head><title>{{SITE_NAME}}</title></head>
    <body>
        <div style="padding:6px; border: 2px solid black">
            Menu:
            <xsl:for-each select="document('1_menu.xml')/menu/item">
                <a href="{@url}"><xsl:value-of select="." /></a>
                <xsl:text> </xsl:text>
            </xsl:for-each>
        </div>
        <br/>
        <xsl:call-template name="body" />
    </body>
    </html>
</xsl:template>
</xsl:stylesheet>

Внешний загружаемый XML-документ: 1_menu.xml

Но и это еще не все. Конструкция <xsl:for-each select="document('1_menu.xml')/menu/item"> выше ссылается на внешний XML-файл, в котором также могут встретиться подстановочные константы:

Листинг 3: Файл, подключаемый через document(): 1_menu.xml
<?xml version="1.0" encoding="UTF-8"?>
<menu>
  <item url=".">{{MAIN}}</item>
  <item url="page.php">{{PAGE}}</item>
</menu>

PHP-скрипт работы с XSLT-шаблонами: page.php

Напишем теперь на PHP скрипт, который будет подключать указанные XSLT-файлы, компилируя вхождения вида {{SITE_NAME}} в текстовые подстановки.

Листинг 4: PHP-скрипт: page.php
<?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 заключается в следующем коде:

Листинг 5: Пример инициализации библиотеки
$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-атрибутов и т. д.







Дмитрий Котеров, Лаборатория dk. ©1999-2016
GZip
Добавить на Del.icio.us   Добавить на Digg.com   Добавить на reddit.com