XSLT-шаблонизатор для PHP
Привіт! Мене звати Артем, я — PHP-програміст. У цій статті розглянемо основи XSLT і приклади його використання як шаблонизатора для веб-сайтів, які створено мовою PHP.
Зазвичай, коли створюють сайти з PHP, для динамічного формування HTML-сторінок виведення використовують один з багатьох Smarty-подібних шаблонизаторів. На моє глибоке переконання, XSLT у цій ролі дуже недооцінили, і я спробую виправити цю прикру помилку.
Коли я лише починав вивчати PHP, шаблонизатори не використовували взагалі, а виведення даних з PHP у HTML було заведено здійснювати безпосередньо. Згодом з’явився та швидко набув популярності шаблонизатор Smarty, тож створювати представлення згідно з архітектурним шаблоном MVC стало набагато простіше й цікавіше. Але коли я випадково, читаючи все поспіль у посібнику про PHP, познайомився з XSL-трансформацією, то зрозумів, що підсів на неї й це надовго.
Схема роботи XSLT
Зізнаюся відверто, що не відразу зрозумів цю технологію, а тим паче оцінив її переваги. Насамперед мене спантеличувала парадигма програмування, яка істотно відрізняється від звичної для PHP об’єктно-орієнтованої, або навіть процедурної. До того ж мова Extensible Stylesheet складніша, як порівняти з тим же Smarty. І це не дивно, адже XSLT — цілком незалежна самодостатня мова, розроблена W3C як міжнародний стандарт для перетворення структур даних. Але згодом, коли мій мозок звик до нової парадигми, я зрозумів, що мої перестороги були марними.
Документ у форматі XML за допомогою шаблонів XSL можна змінити на
Шаблони
Як на мене, шаблони найскладніші й водночас найцікавіші в XSLT, і тому дуже важливо зрозуміти, як вони працюють. Саме тому, перед тим як переходити до інших простіших інструкцій, потрібно приділити якнайбільше уваги вивченню цього питання.
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/*"> ... <xsl:apply-templates select="country/city" /> ... <xsl:call-template name="map" /> ... </xsl:template> <xsl:template match="country/city"> ... </xsl:template> <xsl:template name="map"> ... </xsl:template> </xsl:stylesheet>
XSL-файл складається зі щонайменше одного або кількох шаблонів template
. У них за допомогою простих інструкцій у вигляді XSLT-елементів, створюють нову структуру даних на основі вхідних даних. Або, інакше кажучи, шаблон XSLT описує, які дані з вхідного
Шаблони XSLT, так само як і функції в програмуванні, мають два призначення: основне й додаткове. Основне — це уникнення дублювання коду шляхом його повторного використання. А додаткове — поділ великих шматків коду на менші, зручніші для сприйняття.
Шаблон <xsl:template match="/*">...</xsl:template>
обробляє головний елемент вхідних даних, який розташовано в самому корені
Шаблони в XSLT викликаються двома різними способами та мають різні підходи до області видимості вхідних даних XML у самому шаблоні. Я називаю їх, за аналогією з умовним переходом в програмуванні, шаблонами з умовним або безумовним викликом.
Шаблон з умовним викликом застосовують за допомогою елемента apply-templates
з атрибутом select
, у якому вказано умову виклику шаблона. Виклик зактивізується лише тоді, коли хоч якась частина вхідного документа відповідає умові виклику. Цей тип шаблонів, по-моєму, є родзинкою XSLT і робить його таким, яким він є. Він за своєю природою реалізує принцип обробки потокових даних, а саме тут — ще й ієрархічної структури.
<!-- Застосувати шаблон, якщо на поточному рівні дерева присутній елемент country --> <xsl:apply-templates select="country" /> <!-- Застосувати шаблон, якщо на поточному рівні дерева присутній елемент country, який містить дочірній елемент city --> <xsl:apply-templates select="country/city" /> <!-- Застосувати шаблон, якщо на поточному рівні дерева присутній елемент country, який містить дочірній елемент city, який містить атрибут size --> <xsl:apply-templates select="country/city[@size]" /> <!-- Застосувати шаблон, якщо на поточному рівні дерева присутній елемент country, який містить дочірній елемент city, який містить атрибут size, який має значення 'big' --> <xsl:apply-templates select="country/city[@size='big']" /> <!-- Застосувати шаблон з атрибутом mode, який має значення article, якщо на поточному рівні дерева присутній елемент country, який містить дочірній елемент city, який містить атрибут size, який має значення 'big' --> <xsl:apply-templates select="country/city[@size='big']" mode="article" />
Особливо зауважте, що звернення до вхідних даних XML у шаблоні, який викликали в такий спосіб, здійснюється відносно того елемента, який зазначено останнім в умові виклику шаблона. У першому прикладі звертання в самому шаблоні до даних здійснюватиметься відносно елемента country
. У решті чотирьох випадків — відносно елемента city
.
Якщо ви хочете отримати дані з елементів чи атрибутів, які містяться за межами кореневого елемента цього шаблона, то до них можна звернутися або за допомогою абсолютного шляху (наприклад: /document/country/@title
), або за допомогою відносного (наприклад: ../@title
).
Шаблон з безумовним викликом викликають за допомогою інструкції call-template
і, на противагу попередньому, абсолютно без жодних умов. Як наслідок, в інструкції й у шаблоні, який вона викликає, указують власну назву шаблона — name
, а не умову його застосування.
За своєю суттю він — аналог виклику функції в програмуванні, за винятком підходу до області видимості даних. У разі виклику шаблона за допомогою цього способу доступ до вхідних даних відбувається так само, як і в місці виклику шаблона. Тобто цей метод виклику під час виконання ніби вставляє весь код із шаблона в те місце, де його викликають, замість інструкції виклику.
Для того щоб краще зрозуміти шаблони, методи їхнього виклику й інші пов’язані із цим речі, пропоную створити невеликий практичний приклад. Створімо спочатку простенький templates.xml
лише з кількома елементами.
<?xml version="1.0" encoding="UTF-8" ?> <?xml-stylesheet type="text/xsl" href="/templates.xsl" ?> <document> <element attribute="Значення атрибута елемента"> <subelement>Значення дочірнього елемента</subelement> </element> </document>
Хоча виклик шаблонів у різних режимах і використовують на практиці дуже рідко, але це дає можливість запускати два різних шаблони для однієї умови. Передавання параметрів у шаблон використовують частіше й, на відміну від режиму, можуть застосовувати для обох способів виклику шаблона.
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="yes" encoding="UTF-8" media-type="text/html" /> <xsl:template match="/document"> <html> <head> <title>Шаблони</title> </head> <body> <h1>Приклад головного шаблону</h1> <div class="class1">...</div> <!-- Приклад умовного виклику шаблону --> <xsl:apply-templates select="element" /> <!-- Приклад безумовного виклику шаблону --> <xsl:call-template name="name1" /> <!-- Приклад безумовного виклику шаблону з параметром --> <xsl:call-template name="name2"> <xsl:with-param name="title" select="'Приклад параметра до шаблона'" /> </xsl:call-template> </body> </html> </xsl:template> <xsl:template match="element"> <div class="class2"> <h2>Приклад шаблону з умовним викликом</h2> <em><xsl:value-of select="@attribute" /></em> <p>...</p> <!-- Приклад умовного виклику вкладеного шаблону в режимі mode1 --> <xsl:apply-templates select="subelement" mode="mode1" /> <!-- Приклад умовного виклику вкладеного шаблону в режимі mode2 --> <xsl:apply-templates select="subelement" mode="mode2" /> </div> </xsl:template> <xsl:template match="element/subelement" mode="mode1"> <div class="class3"> <h3>Приклад вкладеного шаблону з умовним викликом в режимі mode1</h3> <em><xsl:value-of select="." /> (mode1)</em> <p>...</p> </div> </xsl:template> <xsl:template match="element/subelement" mode="mode2"> <div class="class4"> <h3>Приклад вкладеного шаблону з умовним викликом в режимі mode2</h3> <em><xsl:value-of select="." /> (mode2)</em> <p>...</p> </div> </xsl:template> <xsl:template name="name1"> <div class="class5"> <h2>Приклад шаблону з безумовним викликом</h2> <p>...</p> </div> </xsl:template> <xsl:template name="name2"> <xsl:param name="title" /> <div class="class6"> <h2>Приклад шаблону з безумовним викликом з параметром</h2> <em><xsl:value-of select="$title" /></em> <p>...</p> </div> </xsl:template> </xsl:stylesheet>
А тепер найцікавіше — процес трансформації. Запускаємо створений нами
Приклад роботи шаблонів в XSLT
Якщо подивитися на початковий код сторінки, то побачимо там не HTML-теги, як ми звикли, а дані XML. І це правильно, тому що саму HTML-сторінку створює веб-оглядач, а
Для того щоб усе-таки побачити HTML-код новоствореної сторінки, потрібно скористатися внутрішніми інструментами розроблення, які надає веб-оглядач (наприклад Chrome DevTools). Якщо ви зробили все правильно, то код вашої сторінки матиме приблизно такий вигляд:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Шаблони</title>
</head>
<body>
<h1>Приклад головного шаблону</h1>
<div class="class1">...</div>
<div class="class2">
<h2>Приклад шаблону з умовним викликом</h2>
<em>Значення атрибута елемента</em>
<p>...</p>
<div class="class3">
<h3>Приклад вкладеного шаблону з умовним викликом в режимі mode1</h3>
<em>Значення дочірнього елемента (mode1)</em>
<p>...</p>
</div>
<div class="class4">
<h3>Приклад вкладеного шаблону з умовним викликом в режимі mode2</h3>
<em>Значення дочірнього елемента (mode2)</em>
<p>...</p>
</div>
</div>
<div class="class5">
<h2>Приклад шаблону з безумовним викликом</h2>
<p>...</p>
</div>
<div class="class6">
<h2>Приклад шаблону з безумовним викликом з параметром</h2>
<em>Приклад параметра до шаблона</em>
<p>...</p>
</div>
</body>
</html>
Решта інструкцій
Для демонстрації решти поширених XSLT-інструкцій я підготував новий приклад, який, на відміну від попередніх штучних, хоча б віддалено нагадує шаблон з реального проєкту.
XML
Але спочатку для демонстрації можливостей шаблона потрібно створити файл index.xml
з певними структурою й даними.
<?xml version="1.0" encoding="UTF-8" ?> <?xml-stylesheet type="text/xsl" href="/index.xsl" ?> <root title="Новини України та світу" copyright="2019" description="Самі свіжі та точні новини України та світу"> <menu> <item title="Домашня" description="Домашня сторінка сайту" section="home" /> <item title="Новини" description="Стрічка новин" section="news" /> <item title="Категорії" description="Перелік категорій публікацій" section="category" /> <item title="Мітки" description="Список міток публікацій" section="tag" /> <item title="Автори" description="Перелік авторів публікацій" section="user" /> </menu> <main /> <notifications> <p>Редакція може не погоджуватись з автором публікації</p> <p>Передрук частини публікації дозволено тільки за наявності посилання</p> <p>Рекламні матеріали публікуються виключно з відповідною позначкою</p> </notifications> </root>
Стандарт XML, як і решта поширених форматів даних (CSV, YAML, JSON), настільки простий, що я не витрачатиму час на його опис. Лише в таблиці нижче наведу кілька прикладів найпоширеніших способів здійснення навігації в ієрархічній структурі за допомогою мови запитів XPath, яку, до речі, теж створили в W3C. Якщо вам цих прикладів буде замало, завжди можна підглянути в розділі «Синтаксис XPath».
/root | Пряме звернення до кореневого елемента root |
/root/@title | Пряме звернення до атрибута title кореневого елемента root |
/root/menu | Звернення до дочірніх елементів menu батьківського кореневого елементу root |
/root/menu/* | Звернення до нащадків батьківського елемента menu кореневого елемента root |
menu/*/@title | Відносне звернення до атрибутів title нащадків батьківського menu |
menu/item[1]/@section | Відносне звернення до атрибута section першого дочірнього елемента item батьківського елемента menu |
XSL
А тепер створимо файл шаблона index.xsl
, у якому з метою ознайомлення використаємо найпоширеніші XSL-інструкції.
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="html" indent="yes" encoding="UTF-8" media-type="text/html" /> <xsl:variable name="section" select="/root/@section" /> <xsl:template match="/*"> <xsl:text disable-output-escaping="yes"><!DOCTYPE html ></xsl:text> <html xml:lang="uk" lang="uk" dir="ltr"> <head> <title><xsl:value-of select="@title" /></title> <meta name="description" content="{@description}" /> <link href="/index.css" rel="stylesheet" /> <script src="/index.js" type="application/javascript" /> </head> <body> <header><xsl:call-template name="menu" /></header> <main> <h1><xsl:value-of select="@title" /></h1> <div class="body {name(main/.)}"> <xsl:apply-templates select="main/*" /> </div> </main> <footer> <div class="copyright"> <p>Всі права застережено © <xsl:value-of select="@copyright" /></p> </div> <div class="notifications"> <xsl:value-of select="notifications" disable-output-escaping="yes" /> </div> </footer> </body> </html> </xsl:template> <xsl:template name="menu"> <nav> <ul> <xsl:for-each select="menu/item"> <li> <xsl:if test="@section=$section"> <xsl:attribute name="class">active</xsl:attribute> </xsl:if> <a href="/?section={@section}" title="{@title}"> <xsl:value-of select="@title" /> </a> </li> </xsl:for-each> </ul> </nav> </xsl:template> <xsl:template match="home">Домашня ...</xsl:template> <xsl:template match="news">Новини ...</xsl:template> <xsl:template match="category"> <div id="index" class="index index-{name(.)}"> <xsl:choose> <xsl:when test="items/item"> <xsl:for-each select="items/item"> <p> <strong><xsl:value-of select="@title" /></strong> - <xsl:value-of select="@description" /> </p> </xsl:for-each> </xsl:when> <xsl:otherwise> <p>Записів не знайдено</p> </xsl:otherwise> </xsl:choose> </div> </xsl:template> <xsl:template match="tag">Мітки ...</xsl:template> <xsl:template match="user">Автори ...</xsl:template> </xsl:stylesheet>
Перше, що впадає в око тим, хто раніше не працював зі XSLT, — це те, як гармонійно його інструкції вписуються поміж HTML-розмітками. Цьому є просте пояснення: XML, HTML, XSLT і багато інших цікавих речей розробили World Wide Web Consortium, і їх реалізують у схожий спосіб. Тобто XSLT для HTML, на відміну від інших поширених шаблонизаторів, — споріднене середовище.
Нижче я спробую якомога стисліше й водночас досить зрозуміло описати доволі об’ємний матеріал. Заразом я не відволікатимуся на загальні речі на кшталт версій, простору імен або опцій налаштування перетворення, про які ви без проблем зможете дізнатися з довідника самостійно.
<meta name="description" content="{@description}" />
Фігурні дужки (шаблон значення атрибута) — дуже зручний спосіб для виведення рядкових типів даних (елементів, атрибутів, змінних, убудованих функцій тощо) зі вхідного документа саме в значенні атрибута елемента шаблона.
<xsl:value-of select="@title" />
<xsl:value-of select="notifications" disable-output-escaping="yes"/>
Ця інструкція схожа на попередню, але, на відміну від неї, здійснює виведення за межами значення атрибута елемента. Зверніть увагу на її дуже корисний атрибут disable-output-escaping
, який дозволяє виводити текст зі службовими символами на кшталт < та >
. Це інколи потрібно, наприклад, коли в тексті, що виводимо, присутні теги з форматуванням тексту, посилання тощо.
<xsl:attribute name="class">active</xsl:attribute>
Згідно з документацією, ця інструкція створює атрибут з певним значенням. Та на практиці в шаблонизаторі її використовують для динамічного створення атрибута або зміни його значення залежно від якоїсь умови, тому що створювати атрибути без умов простіше відразу в шаблоні статично, прописуючи його в елементі.
<xsl:variable name="section" select="/root/@section" />
Створює змінну зі значенням, область видимості якої залежить від місця її створення. Якщо її створили на самому початку файлу, то це глобальна змінна, до якої є доступ з будь-якого місця. А якщо в самому шаблоні, її видно тільки там.
<xsl:text disable-output-escaping="yes"><!DOCTYPE html></xsl:text>
А ось цей елемент використовують доволі рідко, але я змушений його згадати принаймні через цей випадок. Річ у тому, що стандарт XSLT версії 1.0, який ми зараз використовуємо, розробили аж далекого disable-output-escaping
.
<!-- Приклад інструкції одинарного умовного переходу --> <xsl:if test="@section=$section">...</xsl:if> <!-- Приклад інструкції множинного умовного переходу --> <xsl:choose> <xsl:when test="a < b">...</xsl:when> <xsl:when test="a > b">...</xsl:when> <xsl:otherwise>...</xsl:otherwise> </xsl:choose> <!-- Приклад інструкції перебору елементів з вхідного документа --> <xsl:for-each select="menu/item">...</xsl:for-each>
Останні три інструкції я об’єднав в одному прикладі, оскільки ви їх і так добре знаєте: вони аналогічні операторам з інших мов програмування. Однак варто зазначити, що знаки «менше» чи «більше» (< чи >
) водночас службові і їх використовують для позначення тегів. Тому в умовах інструкцій їх варто замінити на їхні HTML-сутності (< чи >
).
Зверніть увагу, що звернення до елементів select
, так само, як і в шаблонах з умовним викликом.
Якщо я нічого не забув, то описав усі поширені інструкції, за допомогою яких ви вже зараз зможете використовувати XSLT як шаблонизатор. А якщо їх вам буде замало, ось вам посилання на повний перелік елементів з описом і прикладами щодо кожного: XSLT Elements Reference.
XML + XSLT = HTML
Перед тим як запустити процес трансформації, раджу витратити трохи часу на створення двох додаткових маленьких файлів. До самого перетворення вони жодного стосунку не мають, але своєю чергою вможливлять ознайомлення з шаблонизатором відразу в комплексі з іншими технологіями, потрібними для створення сайтів.
Спочатку створимо index.css
із трьома рядками, щоб надати меню більш-менш людського вигляду.
header nav ul {list-style: none;padding: 0;} header nav ul li {display: inline;} header nav ul li.active {font-weight: bold;}
Потім раджу створити файл index.js
і додати в нього мінімум два рядки, щоб перевірити роботу JavaScript.
let time=new Date().toLocaleTimeString('uk-UA'); console.log('JavaScript завантажено успішно ['+time+']');
Якщо ми все робили правильно, то в кореневій теці нашого тестового сайту, окрім створених у попередніх розділах файлів templates.xml
і templates.xsl
, мають бути ще 4 файли: index.xml
, index.xsl
, index.css
та index.js
.
Тоді запускаємо раніше створений
Приклад роботи інших інструкцій XSLT
Цього разу вихідний HTML-файл має трохи цікавіший вигляд.
<!DOCTYPE html>
<html xml:lang="uk" lang="uk" dir="ltr" data-lt-installed="true">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Новини України та світу</title>
<meta name="description" content="Самі свіжі та точні новини України та світу">
<link href="/index.css" rel="stylesheet">
<script src="/index.js" type="application/javascript"></script>
</head>
<body>
<header>
<nav>
<ul>
<li><a href="/?section=home" title="Домашня">Домашня</a></li>
<li><a href="/?section=news" title="Новини">Новини</a></li>
<li><a href="/?section=category" title="Категорії">Категорії</a></li>
<li><a href="/?section=tag" title="Мітки">Мітки</a></li>
<li><a href="/?section=user" title="Автори">Автори</a></li>
</ul>
</nav>
</header>
<main>
<h1>Новини України та світу</h1>
<div class="body main"></div>
</main>
<footer>
<div class="copyright"><p>Всі права застережено © 2019</p></div>
<div class="notifications">
<p>Редакція може не погоджуватись з автором публікації</p>
<p>Передрук частини публікації дозволено тільки за наявності посилання</p>
<p>Рекламні матеріали публікуються виключно з відповідною позначкою</p>
</div>
</footer>
</body>
</html>
PHP + XML + XSLT = HTML
Якщо у вас все вийшло й ви побачили HTML-файл, гадаю, ви неодмінно спробували перевірити роботу меню. Але під час переходів по його пунктах на сторінці нічого не відбувається, бо XSLT відповідає тільки за виведення інформації. А якщо ви хочете її змінювати залежно від дій користувача, потрібно створити хоча б якийсь примітивний контролер.
PHP має дуже багато різних інструментів для роботи з XML, і найпростіший з них — це SimpleXML
. За його допомогою можна працювати з
Ось невеличкий приклад того, як просто з ним працювати:
$xml = '<?xml version="1.0" encoding="UTF-8" ?><document time="" />'; $node = new SimpleXMLElement($xml); $node['time'] = time(); $nodeCountry = $node->addChild('country'); $nodeCountry->addAttribute('title', 'Україна'); $nodeCountry->addAttribute('image', '/images/ua.jpg'); $nodeCity = $nodeCountry->addChild('city'); $nodeCity->addAttribute('title', 'Львів'); $nodeCity->addAttribute('image', '/images/lviv.jpg'); $nodeObject = $nodeCity->addChild('object', 'Опис ...'); $nodeObject->addAttribute('title', 'Львівська Опера'); $nodeObject->addAttribute('image', '/images/lviv-opera.jpg'); echo $node->asXML();
Спочатку за допомогою SimpleXMLElement
і встановили значення атрибута time в кореневому елементі. Потім за допомогою методів addChild
та addAttribute
додали елементи й атрибути в потрібному нам місці ієрархічної структури документа. І, нарешті, за допомогою методу asXML
здійснили конвертацію об’єкта SimpleXMLElement
у рядок та вивели його на перегляд.
<?xml version="1.0" encoding="UTF-8"?> <document time="1566411359"> <country title="Україна" image="/images/ua.jpg"> <city title="Львів" image="/images/lviv.jpg"> <object title="Львівська Опера" image="/images/lviv-opera.jpg">Опис ...</object> </city> </country> </document>
Тут все настільки просто, що я навіть не знаю, що ще можна додати. Зрідка трапляється, коли обмеженого функціонала SimpleXMLElement
бракує. Тоді по допомогу треба звертатися до надзвичайно просунутого й, відповідно, набагато складнішого розширення DOM (Document Object Model)
. Також варто звернути увагу на те, що для роботи з XML треба під’єднати в PHP розширення php_xml
.
Нарешті, як приклад я створив маленький скрипт на PHP, який надасть сторінці динаміки.
// Отримаємо обраний розділ сайту define('SECTION', ($_GET['section']) ?? ''); // Імітація отриманих даних з БД $categories = [ ['id' => '1', 'title' => 'Політика', 'description' => 'Політика опис ...'], ['id' => '2', 'title' => 'Економіка', 'description' => 'Економіка опис ...'], ['id' => '3', 'title' => 'Соціум', 'description' => 'Соціум опис ...'], ['id' => '4', 'title' => 'Культура', 'description' => 'Культура опис ...'], ['id' => '5', 'title' => 'Спорт', 'description' => 'Спорт опис ...'] ]; // Відкриваємо XML-файл $xml = file_get_contents('index.xml'); $node = new SimpleXMLElement($xml); // Додаємо початкові дані до XML-файлу $node->addAttribute('host', $_SERVER['HTTP_HOST']); $node->addAttribute('request', $_SERVER['REQUEST_URI']); try { // Оновлюємо, при потребі, дані поточної сторінки $node->addAttribute('section', SECTION); foreach($node->menu->item as $item) { if ($item['section'] == SECTION) { $node['title'] = $item['title']; $node['description'] = $item['description']; } } // Імпровізований міні-контролер switch(SECTION) { case '': case 'home': { $nodeHome = $node->main->addChild('home'); // ... }; break; case 'news': { $nodeNews = $node->main->addChild('news'); // ... }; break; case 'category':{ $nodeCategory = $node->main->addChild('category'); $nodeItems = $nodeCategory->addChild('items'); foreach($categories as $category) { $xmlItem = $nodeItems->addChild('item'); $xmlItem->addAttribute('id', $category['id']); $xmlItem->addAttribute('title', $category['title']); $xmlItem->addAttribute('description', $category['description']); } }; break; case 'tag': { $nodeTag = $node->main->addChild('tag'); // ... }; break; case 'user': { $nodeUser = $node->main->addChild('user'); // ... }; break; default: { $message = sprintf('Невідомий розділ сайту "%s"', SECTION); throw new Exception($message); } } } catch (Exception $exception) { header('HTTP/1.x 404 Not Found'); throw $exception; } header("Content-type: text/xml; charset=utf-8'"); echo $node->asXML();
А зараз, виконавши скрипт, отримаємо модифікований
Приклад роботи XSLT разом з PHP
Виконувати перетворення XSLT з боку клієнта для розробника дуже зручно: це частково зменшує навантаження на сервер. А для веб-оглядача клієнта ця трансформація не надто обтяжлива й відбувається абсолютно непомітно. Але водночас цей спосіб має один типовий недолік: невідомо, як саме різні веб-оглядачі здійснюватимуть це перетворення.
Хоча XSLT — це єдиний, добре описаний і відкритий стандарт, але, як це часто трапляється, кожен тлумачить його на власний розсуд. До речі, так само, як і HTML та багато інших відкритих стандартів. Якщо поширені веб-оглядачі більш-менш добре впораються із цим завданням, то як відбуватиметься цей процес у менш поширених веб-оглядачах, передбачити неможливо. І якщо ви не маєте бажання ризикувати, то доведеться виконати це перетворення на стороні сервера за допомогою, наприклад, того ж PHP.
Здійснити XSLT-перетворення PHP ми зможемо за допомогою об’єкта XSLTProcessor
, який приймає XML як об’єкт DOMDocument
. Тому перед XSLT-перетворенням треба здійснити конвертацію із SimpleXML
у DOMDocument
(SimpleXML -> DOMDocument -> XSLTProcessor
). Як наслідок, доведеться замінити два останні рядки в нашому останньому прикладі на такий код:
$xmlDOM = new DOMDocument('1.0', 'UTF-8'); $xmlDOM->loadXML($node->asXML()); $xsl = new DOMDocument('1.0', 'UTF-8'); $xsl->load('index.xsl', LIBXML_NOCDATA); $xslt = new XSLTProcessor(); $xslt->importStylesheet($xsl); header('Content-Type: text/html; charset=utf-8'); echo $xslt->transformToXML($xmlDOM);
Сам процес трансформації має дещо заплутаний вигляд, але його достатньо раз прописати в кінці скриптів, і ви до нього не повертатиметеся. А для роботи з XML треба використовувати суперпростий SimpleXMLElement
.
До речі, робота з XSLT у середовищі PHP реалізується засобами скомпільованої системної бібліотеки libxslt
. А це, зокрема, дуже позитивно позначається на швидкодії, як порівняти з іншими шаблонизаторами, які здійснюють таке перетворення за допомогою власних скриптів мовою PHP.
У цій статті я намагався якомога більше спростити опис і приклади, щоб знизити поріг входження в цю дивовижну технологію. Маю надію, що мені це вдалося та згодом кількість адептів XSLT поступово, але невпинно збільшуватиметься. Так само, як і кількість сайтів, генерацію HTML-сторінок у яких зреалізували за допомогою цієї мови.