Алексей Валиков - Технология XSLT
<td><input class="flat" type="text" name="email"/></td>
</tr>
<tr>
<td colspan="2">
<xsl:text>Subject</xsl:text><BR/>
<input type="text" name="subject"/><br/>
<!-- В скрытом поле posted помещаем текущую дату -->
<input type="hidden" name="posted" value="{page/date}"/><br/>
<textarea rows="10" cols="50" name="msg"/><br/><br/>
<input type="submit" value="Post"/>
</td>
</tr>
</table>
</form>
<!-- Обрабатываем страницу -->
<xsl:apply-templates select="page"/>
</xsl:template>
<!-- Обработка страницы -->
<xsl:template match="page">
<xsl:apply-templates select="messages"/>
</xsl:template>
<!-- Обработка сообщений -->
<xsl:template match="messages">
<xsl:apply-templates select="message"/>
</xsl:template>
<!-- Вывод сообщения -->
<xsl:template match="message">
<xsl:text>From: </xsl:text>
<xsl:choose>
<!-- Если e-mail не указан, выводим просто имя -->
<xsl:when test="not(EMAIL)">
<xsl:value-of select="PERSON"/>
</xsl:when>
<!-- Если e-mail указан, выводим гиперссылку -->
<xsl:otherwise>
<A href="mailto:{EMAIL}"><xsl:value-of select="PERSON"/></A>
</xsl:otherwise>
</xsl:choose>
<!-- Выводим дату записи -->
<xsl:value-of select="concat(', ', POSTED)"/><br/>
<!-- Если была указана тема, выводим ее -->
<xsl:if test="SUBJECT">
<xsl:text>Subject: </xsl:text>
<xsl:value-of select="SUBJECT"/><BR/>
</xsl:if>
<HR/>
<!-- Выводим текст сообщения -->
<xsl:value-of select="MSG"/>
</xsl:template>
</xsl:stylesheet>
Теперь займемся самим php-скриптом.
Листинг 9.5. Скрипт guestbook.php<html>
<head>
<title>Guestbook</title>
<META
http-equiv="Content-Type"
content="text/html; charset=windows-1251">
<link rel="stylesheet" type="text/css" href="style.css"/>
</head>
<body>
<?php
// Загружаем входящий документ
$sourcefile = "source.xml";
$sourcehandle = fopen($sourcefile, "r")
or die("Невозможно открыть входящий документ.");
$source = fread($sourcehandle, filesize($sourcefile));
// Загружаем преобразование
$stylesheetfile = "stylesheet.xsl";
$stylesheethandle = fopen($stylesheetfile, "r")
or die("Невозможно открыть файл преобразования");
$stylesheet = fread($stylesheethandle, filesize($stylesheetfile));
// Инициализируем XSLT-процессор
$xslt = @xslt_create() or die("Can't create XSLT handle!");
// Выполняем преобразование
@xslt_process($stylesheet, $source, $result);
// Выводим результат
echo $result;
// Освобождаем ресурсы
@xslt_free($xslt);
?>
</body>
</html>
Приблизительный результат выполнения этого скрипта можно видеть на рис. 9.9.
Рис. 9.9. Сгенерированная из PHP-скрипта страница гостевой книги
Выполнение XSLT-преобразований в JavaScript
JavaScript является одним из наиболее популярных скриптовых языков, которые применяются при программировании для Web. В этой главе мы покажем, как при помощи JavaScript и MSXML создать интерактивный каталог, основанный на XML и XSLT.
Предположим, что каталог организован в виде иерархии категорий приблизительно следующим образом.
Листинг 9.6. XML-документ каталога<?xml version="1.0" encoding="windows-1251"?>
<catalog>
<category title="Компьютеры">
<category title="Настольные компьютеры"/>
<category title="Серверы"/>
</category>
<category title="Комплектующие">
<category title="Процессоры"/>
<category title="Материнские платы"/>
</category>
<category title="Расходные материалы">
<category title="Картриджи">
<category title="Картриджи для плоттеров"/>
<category title="Картриджи для принтеров"/>
</category>
<category title="Тонеры"/>
<category title="Бумага"/>
</category>
</catalog>
При отображении этого дерева мы будем раскрывать только определенную выбранную ветвь категорий. Скажем, если пользователь выбрал категорию "Расходные материалы", показывать информацию о компьютерах мы ему не будем. Иными словами, мы будем показывать только те категории, которые являются надкатегориями выбранной. Для того чтобы сделать это как можно эффективнее, мы выполним следующие шаги.
□ При помощи ключа и уникального идентификатора, сгенерированного функцией generate-id, мы найдем в дереве требуемую категорию и присвоим ее переменной $category.
□ Воспользовавшись осью ansector-or-self, мы найдем все надкатегории данной, то есть все категории, которые прямо или косвенно содержат найденную. Путь выборки будет иметь вид $category/ancestor-or-self::category. Найденное множество мы присвоим переменной $path.
□ При обработке каждой из категорий мы будем обрабатывать ее подкатегории только в том случае, если она является надкатегорией выбранной; иначе говоря — только в том случае, когда ее узел принадлежит множеству узлов $path. Проверять это мы будем при помощи условия count(.|$path)=count($path).
Искомое преобразование в итоге запишется в виде.
Листинг 9.7. Преобразование обрабатывающее наш каталог<?xml version="1.0" encoding="windows-1251"?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Выводим документ в формате html и кодировке windows-1251 -->
<xsl:output method="html" encoding="windows-1251"/>
<!--
| Переменная, которая содержит уникальный
| идентификатор выбранного узла дерева
+-->
<xsl:param name="current" select="''"/>
<!-- Определение ключа категории -->
<xsl:key name="cat" match="category" use="generate-id(.)"/>
<!-- Находим текущую категорию -->
<xsl:variable name="category" select="key('cat',$current)"/>
<!--
| Находим надкатегории текущей категории, узлы которых
| мы будем раскрывать в дереве
+-->
<xsl:variable name="path"
select="$category/ancestor-or-self::category"/>
<!-- Шаблон обработки каталога -->
<xsl:template match="catalog">
<xsl:apply-templates select="category"/>
</xsl:template>
<!-- Шаблон обработки категории-->
<xsl:template match="category">
<!-- Параметр, указывающий отступ -->
<xsl:param name="indent"/>
<!-- Выводим отступ -->
<xsl:value-of select="$indent"/>
<!-- Выводим информацию о категории в виде ссылки -->
<а href="javascript:expand('{generate-id(.)}')">
<!-- Перед названием категории выводим соответствующую иконку -->
<img height="11" width="11" border="0">
<xsl:choose>
<!--
| Если категория не содержит субэлементов,
| выводим иконку с точкой
+-->
<xsl:when test="not(*)">
<xsl:attribute name="src">images/dot.gif</xsl:attribute>
</xsl:when>
<!--
| Если категория принадлежит ветке выбранной категории,
| выводим иконку с минусом, что означает раскрытую ветку
+-->
<xsl:when test="count(.|$path)=count($path)">
<xsl:attribute name="src">images/minus.gif</xsl:attribute>
</xsl:when>
<!--
| Если категория не принадлежит ветке выбранной категории,
| выводим иконку с плюсом, что означает нераскрытую ветку
+-->
<xsl:otherwise>
<xsl:attribute name="src">images/plus.gif</xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</img>
<!--
| Выводим неразрывный пробел.
|   в Unicode соответствует
+-->
<xsl:text> </xsl:text>
<!-- Выводим название категории -->
<xsl:value-of select="@title"/>
</a>