Гостевая
Форум
Разделы
Главная страница
Js скрипты
Php скрипты
Html шаблоны
Книги по Web дизайну
Статьи


Главная страница статей --> Советы по фотошопу, графике и хитрости в построении php кода

Преобразование XML + XSLT с помощью Sablotron

Источник: realcoding.net

Альтернативное введение в использование XSL Transformations в PHP при помощи Sablotron.

Данный материал следует воспринимать как альтернативное введение в использование XSLT с Sablotron в PHP.

Термины XSL и XSLT близки друг к другу, и новичкам их можно считать синонимами. Подробности, в чём же различия, описаны в спецификации XSL Transformations W3C.

Все, кто интересовался возможностями XSLT, читал стандартный пример из мануала, либо примеры, приводимые в статьях, посвящённых XSLT, на разных сайтах. Работающий пример из этой же серии:

<?php

$xmlData
= <?xml version=1.0 encoding=Windows-1251?>
<document>
<game>
    <title>Railroad Tycoon II Platinum</title>
    <genre>экономическая стратегия</genre>
    <designer>PopTop software</designer>
    <publisher>G.O.D. games</publisher>
    <year>2001</year>
</game>
<game>
    <title>Grand Prix 4</title>
    <genre>автосимулятор</genre>
    <designer>Geoff Crammond & Simergy</designer>
    <publisher>Infogrames Entertainment</publisher>
    <year>2002</year>
</game>
</document>
;

$xslData = <?xml version=1.0 encoding=windows-1251?>
<!DOCTYPE xsl:stylesheet>
<xsl:stylesheet version=1.0 xmlns:xsl=http://www.w3.org/1999/XSL/Transform>
<xsl:output method=html indent=yes encoding=Windows-1251/>

<xsl:template match=/>
    <xsl:apply-templates/>
</xsl:template>

<xsl:template match=document>
    <html><head>
    <title>Игры</title>
    </head>
    <body>
    <h1>Игры</h1>
    <table cellpadding=2 cellspacing=2 border=1>
    <tr>
        <td>Название</td>
        <td>жанр</td>
        <td>год</td>
        <td>разработчик</td>
        <td>издатель</td>
    </tr>
        <xsl:apply-templates select=game/>
    </table>
    </body></html>
</xsl:template>

<xsl:template match=game>
    <tr>
        <td><b><xsl:value-of select=title/></b></td>
        <td><xsl:value-of select=genre/></td>
        <td><xsl:value-of select=year/></td>
        <td><xsl:value-of select=designer/></td>
        <td><xsl:value-of select=publisher/></td>
    </tr>
</xsl:template>

</xsl:stylesheet>
;

$xh = xslt_create();

   
$arguments = array(
       
/_xml => $xmlData,
       
/_xsl => $xslData
   
);

   
$result = @xslt_process($xh, arg:/_xml, arg:/_xsl, NULL, $arguments);
   

if (
$result)
    print (
$result);
else {
    print (
There was an error that occurred in the XSL transformation...\n);
    print (
\tError number: . xslt_errno($xh) . \n);
    print (
\tError string: . xslt_error($xh) . \n);
    exit;
}

?>

Подобных примеров в Сети полно. Все они хорошо показывают, что XSL-трансформация в php работает, но после их прочтения остаётся неясным, зачем XSL нужен, скорее даже наоборот — почему XSL не нужен.

"Действительно", — подумает читатель, — "если данные лежат в базе, зачем городить огород, формируя сперва XML, а затем ещё преобразовывать через XSL? С тем же успехом это сделает класс HTML-шаблона."

После этого разочарованный программист напрочь теряет интерес к XSL и вешает на технологию ярлык "ненужная заумь".

Вам, уважаемые читатели, повезло найти такой замечательный сайт, как "php в деталях". Здесь вы прочитаете о том, что XSL может не только преобразовывать XML в HTML, но и то, как можно при помощи XSL облегчить работу с php-скриптами.

Начало работы


Приведённый выше пример, хоть и слишком прост, хорошо иллюстрирует, каким образом делается XSL-преобразование в php.

Чтобы этот код работал, нужно установить XSLT-процессор Sablotron. На виндовой машине это делается так:

1. положить iconv(-1.3).dll, expat.dll и sablot.dll в C:\windows\System (все файлы есть в стандартном дистрибутиве php)
2. открыть C:\windows\php.ini и в нём найти параметр extension_dir. Если значение параметра — "." или нечто вроде "./", исправить на, скажем, "f:\usr\local\php\extension" (или адрес директории, в которой у вас лежат/будут лежать расширения php). Теперь это будет директория расширений php.
3. положить в директорию расширений файл php_xslt.dll (это для php версии 4.2.x), либо php_sablot.dll (для версии 4.0.x)
4. в php.ini раскомментируйте строчку extension=php_xslt.dll (4.2.x) или extension=php_sablot.dll (4.0.x)

Теория


Использование XSLT позволяет отделить от php-скриптов работу по форматированию и представлению данных. Это не только уменьшение объёма кода, но и вынос большого количества логических конструкций (if, else, switch), а следовательно, облегчение работы по написанию и отладке программ. Смею утверждать, что тот, кто не пробовал работать с XSLT, не представляет себе, насколько php-кодирование облегчится.

Впрочем, не надо обольщаться: если у вас было несколько конструкций if ... else в php-скрипте, они, скорее всего, появятся в том же количестве в XSL-файле.

Теперь к примерам.

Вывод списков


Все усложнения, происходящие от необходимости выводить список в удобочитаемом виде, переносятся на плечи XSL. Пример #2. Список статей на сайте с подсветкой статьи, которую читают сейчас, чередование цвета в строках и нумерация списка.

XML:

<current-date>2002-05-30</current-date>

<list-
article date=2002-10-03>Ловля ошибок в PHP</list-article>
<list-
article date=2002-10-02>Живой проект и мёртвый журнал</list-article>
<list-
article date=2002-06-03>Работа с MySQL. Часть 7. Деревья</list-article>
<list-
article date=2002-05-30>Ручная сортировка в веб-интерфейсе</list-article>
<list-
article date=2002-05-29>Как поладить дизайнеру с программистом</list-article>
<list-
article date=2002-05-27>Relax this is PHP</list-article>

XSLT:

...
<
table>
<
xsl:apply-templates select=list-article/>
</
table>
...

<
xsl:template match=list-article>
    <
tr>
    <
xsl:if test=position() mod 2 = 1>
        <
xsl:attribute name=bgcolor>#cccccc</xsl:attribute>
   
</xsl:if>
    <
td>
    <
xsl:value-of select=position()>
    <
a href=/{@date}.htm><xsl:value-of select=./></a>
    <
xsl:if test=@date = .. /current-date>&nbsp;&lt;</xsl:if>
    </
td>
    </
tr>
</
xsl:template>

Произвольная разметка


Переводя на XML сайт с текстами (как этот), естественно хотеть сделать собственную разметку статей. Например, в контейнером important выделять очень важные места и иметь возможность выделять их не обязательно жирным шрифтом, но, может быть, цветом, CSS-стилем. Или писать цитаты как <quote>текст цитаты<quote> и иметь возможность менять стиль их оформления вместе с дизайном сайта.

Медленно продвигаясь от самого простого первого примера, многие натыкаются на эту проблему и не могут найти решения. Ведь если выделить абзац в тег <para> и делать для него шаблон, на первый взгляд, существуют три способа вывода содержимого:

1. тег xsl:value-of выводит текст, но удаляет все теги в абзаце
2 .тег xsl:copy-of выводит копию всего содержимого (без возможности применять шаблоны к детям — внутренним тегам) и самого контейнера <para>...</para> (что не очень красиво в HTML).
3. наконец, xsl:apply-templates применит шаблоны к детям, но пропустит текст

Проблема кажется безвыходной, но решение есть. Я использую "магические" шаблоны, которые выводят и текст и теги в нём со всеми атрибутами и без изменений. Пример #3:

XML:

<text>
    <
para>Данный пример использует <strong>магические шаблоны</strong>
для разбора произвольной разметки. Это позволяет избежать таких жалоб:
    </
para>
    <
quote>Люди, памажите сами мы не местные! Не могу вывести теги в тексте
при помощи value
-of!
    </
quote>
    <
hr/>
    <
strong>Запомните эти шаблоны раз и навсегда!</strong>
    <
para>Тогда вы сможете обрабатывать <u>любой</u> <a href=http://www.txt.ru>текст</a>
Почти любой.
    </
para>
</
text>

XSLT:

<xsl:template match=text><xsl:apply-templates/></xsl:template>

<
xsl:template match=strong>
    <
font color=#cc0000><b><xsl:apply-templates/></b></font>
</
xsl:template>

<!--
три магических шаблона -->
<!--
1. общий -->
<
xsl:template match=*>
    <
xsl:copy>
        <
xsl:apply-templates select=@* />
        <
xsl:apply-templates/>
    </
xsl:copy>
</
xsl:template>

<!--
2. для текста -->
<
xsl:template match=text()>
    <
xsl:value-of select=. disable-output-escaping=yes/>
</
xsl:template>

<!--
3. для тегов и аттрибутов -->
<
xsl:template match=@*node()>
    <
xsl:copy>
        <
xsl:apply-templates select=@*node()/>
    </
xsl:copy>
</
xsl:template>

Первым делом XSLT-процессор при вызове инструкции apply-templates ищет шаблон для каждого элемента. Для элемента strong шаблон есть, и именно в соответствии с ним такие элементы будут обработаны. Для гиперссылки шаблона нет, поэтому она будет выведена, как есть. Можно добавить в XSL шаблон и для ссылки, который бы выводил рядом с каждой текстовой ссылкой картинку для открытия её в новом окне:

<xsl:template match=a[@href]>
<
xsl:copy-of select=./> <a href={@href}
target=_blank><img src=/window.gif width=15 height=15
alt=открыть в новом окне/></a>
</
xsl:template>

* в шаблоне использован параметр match="a[@href]" — этот шаблон будет применён только к тем тегам ссылок, в которых есть поле href и пропустит якоря (<a name="xxx"></a>).

Невалидный код и  


Кажущаяся необходимость писать валидный XML-код так же отпугивает многих неофитов XSLT. Хорошо, с завтрашнего дня будем писать статьи только валидно, благо дома можно проверить, нет ли в тексте XML-ошибки — mismatched tag или invalid token, — с этим как-нибудь справимся. Но ведь, по-хорошему, нужно и весь архив перевести в валидный код! И я так тоже думал, когда появилась возможность переделывать сайт на XML.

Решение проблемы довольно простое: не хочешь — не пиши валидно. Пиши, как привык, — без кавычек в атрибутах тегов, используй простой <br> и прочее. Достаточно заключить текст в контейнер <![CDATA[ ... ]]> (пример ниже).

Что касается &nbsp;, то здесь дела такие: элемента nbsp в XML нет. Есть lt, gt, quot, но не nbsp (вполне логично — это ведь non-braking space, который относится к форматированию и придуман для HTML). Поэтому его нужно объявить в документе, либо использовать только внутри <![CDATA[...]]>.

Пример #4:

XML:

<text>
    <
bad-markup><![CDATA[В этом <a href=http://detail.phpclub.net>тексте</a> применена
невалидная разметка. <br> И ничего страшного.]]></bad-markup>
    <
quote>Люди, памажите, сами мы не местные!</quote>
    <
hr/>
    <
strong>Запомните и эти шаблоны тоже!</strong>
</
text>

XSLT:

<xsl:template match=text><![CDATA[ >>> и в XSL можно делать то же самое! <<< ]]>
<
xsl:apply-templates/></xsl:template>

<
xsl:template match=bad-markup>
    <
xsl:value-of select=. disable-output-escaping=yes/>
</
xsl:template>

<
xsl:template match=*>
    <
xsl:copy>
        <
xsl:apply-templates select=@* />
        <
xsl:apply-templates/>
    </
xsl:copy>
</
xsl:template>

<
xsl:template match=text()>
    <
xsl:value-of select=. disable-output-escaping=yes/>
</
xsl:template>

<
xsl:template match=@*node()>
    <
xsl:copy>
        <
xsl:apply-templates select=@*node()/>
    </
xsl:copy>
</
xsl:template>

Очень удобно! Большие изменения в архив вносить не придётся. Можно начать писать валидно, а продолжать как попало. А можно комбинировать эти два подхода. Чтобы не писать в архивные файлы тег CDATA, я сделал простое преобразование при помощи регулярных выражений (важно так же помнить, что один тег CDATA не должен содержать в себе другой).

$doc = preg_replace(~<(ph[1-3]pre)>(.*?)</\\1>~, <\\1>\\2</\\1>, $doc);

Циклы


Допустим, нам нужно сделать форму для редактирования статьи, в том числе её даты. Для удобства пользования надо сделать три раскрывающихся списка (далее — "крутилки") — дата от 1 до 31, месяц, год. Первое решение, которое приходит в голову — сделать HTML-код крутилок в php, вставить в XML в контейнере CDATA, а затем вывести в XSL с параметром disable-output-escaping="yes".

На самом деле, XSLT может и это. Достаточно вставить в данные XML число, номер месяца и год. Крутилки можно нарисовать сразу в XSLT.

Напишем шаблон, не предназначенный ни для какого элемента документа. Он будет вызываться командой xsl:call-template и получать два параметра: значение счётчика и максимум. Сперва он будет выводить нужные нам данные со значением счётчика, затем вызывать самого себя с параметрами максимум и счётчик, увеличенный на 1. Пример #5:

XML:

<month-name>Февраль</month-name>
<
month-name>Март</month-name>
<
month-name>Апрель</month-name>
<
month-name>Май</month-name>
<
month-name>Июнь</month-name>
<
month-name>Июль</month-name>
<
month-name>Август</month-name>
<
month-name>Сентябрь</month-name>
<
month-name>Октябрь</month-name>
<
month-name>Ноябрь</month-name>
<
month-name>Декабрь</month-name>

<
article>
    ...
    <
day>7</day>
    <
month>10</month>
    <
year>2002</year>
</
article>

XSLT:

<xsl:template match=article>
    ...
    <
select name=d>
    <
xsl:call-template name=day>
        <
xsl:with-param name=count>1</xsl:with-param>
    </
xsl:call-template>
    </
select>
    <
select name=m>
    <
xsl:call-template name=month>
        <
xsl:with-param name=count>1</xsl:with-param>
    </
xsl:call-template>
    </
select>
    ...
</
xsl:template>

<
xsl:template name=day>
    <
xsl:param name=count/>
    <
option value={$count}>
        <
xsl:if test=$count = //artcile/day>
            <
xsl:attribute name=selected>yes</xsl:attribute>
        <
xsl:if>
        <
xsl:value-of select=$count/>
    </
option>
    <
xsl:if test=$count &lt; 31>
        <
xsl:call-template name=day>
            <
xsl:with-param name=count>
                <
xsl:value-of select=$count + 1/>
            </
xsl:with-param>
        </
xsl:call-template>
    </
xsl:if>
</
xsl:template>

<
xsl:template name=month>
    <
xsl:param name=count/>
    <
option value={$count}>
        <
xsl:if test=$count = //artcile/month>
            <
xsl:attribute name=selected>yes</xsl:attribute>
        <
xsl:if>
        <
xsl:value-of select=//month-name[position() = $count]/>
    </
option>
    <
xsl:if test=$count &lt; 12>
        <
xsl:call-template name=month>
            <
xsl:with-param name=count>
                <
xsl:value-of select=$count + 1/>
            </
xsl:with-param>
        </
xsl:call-template>
    </
xsl:if>
</
xsl:template>

Оставляю вам в качестве домашнего задания шаблон для вывода крутилки с годом.

Резюме


Как видите, многое из того, что пишется в php-скриптах, даже при использовании класса шаблона, можно успешно спустить в XSLT. Но стОит ли заниматься этим?

Ответ зависит от условий работы в вашем проекте. Второй момент — технологичность работы.

Допустим, я захочу сделать на этом сайте меню быстрой навигации — раскрывающийся список со всеми статьями, — чтобы пользователь мог выбрать статью из списка и сразу перейти к ней. Ещё я захочу оставить список последних материалов (сейчас он находится справа вверху).

Если делать это при помощи класса шаблона типа FastTemplate, нужно два специальных блока и дополнительный код в php, который бы объявлял в шаблоне блок для списка всех статей и отдельно блок для списка 10 последних. Аналогичные действия необходимы в таком случае и при работе без класса шаблона. При работе с XML достаточно всего лишь одного набора данных "Дата => Статья", из которого в XSL-документе строятся и листбокс быстрого перехода, и список последних статей.

А если вдруг понадобится неважно для чего сделать другое оформление сайта (например, версия для WAP, или просто редизайн), в котором будет решено отказаться от списка 10 последних материалов. В случае первых двух технологий — класс шаблона и смешанный код — нужно будет убрать часть php кода, в случае XSLT изменения коснутся только XSL-файла. Такой процесс более технологичен, поскольку невозможно сделать новые ошибки в php-скриптах (а теперь представьте обратный случай — списка 10 последних статей не было, но его решили добавить!).

Итак, выбор остаётся за вами, а я как мог привёл сильные стороны технологии и доводы в пользу использования XML в проектах.



Похожие статьи:
- Модуль CGI.pm
- *.JS, когда грузиться думаешь?
- Работа с сессиями в perl
- Image::Magick или фотошоп для perl
- Приватность в Интернет
- Подпись или аватар на пхп
- Сбор статистики на PHP
- Классы и объекты в РНР со Штирлицом и Мюллером
- Как назвать Web-сайт?
- Дядя Гугл — великан
- Разработка фирменного стиля
- Сверхдинамичные веб-интерфейсы
- Оптимизация для MSN


Оглавление | Обсудить на форуме | Главная страница сайта | Карта сайта |
[0.002]