Easyweb — это верстальный движок общего назначения, написанный на PHP, и предназначенный для выдачи XML-ответа по HTTP-запросу. В его основе лежит шаблонизатор XSLT, а сам движок представляет собой набор решений основных задач в вебе: роутинг запросов, разграничение прав доступа, разделение способов получения данных и способов их представления, локализация, вспомогательные PHP классы и фасилити, и так далее. Проект является сборником архитектурных и концептуальных идей, накопившихся за шесть-семь лет разработки для веба.
Если говорить о классификации, то Easyweb, вероятно, больше всего похож на Content Management Framework. Он, в некотором смысле, немного выше уровнем, чем большинство программных фреймворков, поскольку основная часть продукта, созданного на Easyweb, выстроена на XML-технологиях. В то же время он гораздо ниже уровнем, чем привычные CMS — в нем нет встроенного пользовательского интерфейса, готовых шаблонов страниц или предустановленного набора SQL-запросов.
В двух словах, сайт или XML-сервис на Easyweb — это XML-конфиг, набор шаблонов страниц, база данных, а также скрипты для обработки POST-запросов. Все перечисленное создается вебмастером самостоятельно в его любимом текстовом редакторе, а также админке его любимой СУБД. Easyweb будет интересен тем, для кого выполняются все перечисленные условия:
Мое знакомство с разработкой для веба началось достаточно стандартно: сайты, выходившие из-под моей клавиатуры, были кашей из HTML-я вперемешку с PHP-вставками и прямыми обращениями к базе данных через mysql_* API. Однако, поскольку я уже достаточно давно занимался программированием на самых разнообразных языках, начиная от ассемблера, и заканчивая SQL, шейдерами и Александрескуподобным C++, я довольно быстро понял, что такой веб никому не интересен, и нужно что-то менять. Прошло совсем немного времени, и я узнал от моего товарища о существовании XSLT. Изучив матчасть, я понял: вот она, технология моей мечты! Однако XSLT — это просто шаблонизатор, в то время как разработка для веба состоит из более широкого спектра задач. Поэтому я решил, что нужно делать полноценный движок.
После этого разработка Easyweb-а пошла по следующему сценарию: я писал движок и пользовался им до того момента, пока не накапливалась критическая масса новых идей (обычно это занимало немногим более года), после чего выбрасывал, и писал с нуля новую версию, в которой реализовывал все то, что накопилось и наболело за время работы с предыдущей реинкарнацией Easyweb-а.
Между третьей и четвертой версией у меня возник целый ряд технологических затыков, в связи с чем я даже всерьез задумывался о том, чтобы написать четвертую версию Easyweb-а на другом языке. В той или иной мере были предприняты определенные попытки на C++, Java, Scala, Ruby, Perl, Erlang, и даже технологиях Microsoft-а с их .NET-ами и прочими IIS-ами. Однако, после более чем полугода рассматривания одних и тех же проблем с разных сторон, в итоге таки удалось преодолеть все препятствия в рамках PHP. Ура, решение найдено!
Дописывая четвертую версию движка, я пришел к выводу, что мне удалось разрешить все ключевые проблемы, возникавшие лично у меня за все время разработки в вебе, в связи с чем было решено, что Easyweb дозрел до того, чтобы стать публичным проектом.
Четвертая версия движка все еще находится на стадии тестирования и периодического подправления некоторых мелочей. Планируется, что первая версия, которую можно будет назвать стабильной и полностью юзабельной, появится до конца года.
Документация написана на английском языке, однако, пока что не корректировалась носителем языка, поэтому постарайтесь не очень сильно смеяться с моего рязанского акцента.
Я не буду приводить подробную документацию в данной статье, поскольку она есть в Wiki-разделе проекта на GitHub-е. Напротив, я постараюсь изложить тот минимум, которого должно быть достаточно, чтобы вы поняли, интересен ли вам Easyweb и вы хотите заняться его изучением, или же вас устраивают технологии, с которыми вы работаете в данный момент, и этот проект вам не нужен.
Easyweb — это движок, в котором все нужно делать самому. Вообще все: спроектировать базу данных, написать SQL-процедуры, сверстать XHTML, прописать CSS-стили, JavaScript-ы, а также обработчики POST-запросов. Цель и смысл Easyweb-а заключается в том, чтобы позволить сделать это «всё» максимально просто, компактно, читабельно, интуитивно понятно, безопасно, ну и еще с десяток всяких красивых наречий.
Как было сказано выше, сайт, созданный на Easyweb-е, состоит из базы данных, XML-конфига, шаблонов страниц, локализации, и обработчиков POST-запросов. Обсудим каждую из этих сущностей, и приведем примеры XML- и PHP-сниппетов.
Easyweb не накладывает каких-либо ограничений на структуру базы данных, поэтому к ее проектированию следует подойти с учетом всего того хорошего, что вам известно про базы данных: ключи, индексы, foreign key constraints, триггеры, вьюхи, ну и так далее. Открываем, к примеру, phpMyAdmin, и — вперед.
В конфиге описывается, какие страницы есть на сайте, какие есть источники данных, какие процедуры используются для доступа к этим данным (и как они выглядят), а также как регулируются права доступа к страницам, процедурам и фрагментам шаблонов.
Страница:
Источник данных:
Процедура:
Членство в группе:
Разрешение:
Все шаблоны страниц являются XSL-валидными документами, внутри которых страница верстается с помощью стандартных XSL- и XPath-сущностей, а также предоставляемых движком расширений. Расширения Easyweb-а находятся в пространстве имен «www». Пример простого корневого шаблона:
Пример верстки меню (menu.xsl из примера выше):
Верстка списка книг, получаемого процедурой. XSL-шаблон хранится в отдельном файле:
Верстка сразу в текущем шаблоне:
Для хранения текстов мультиязычных сайтов в Easyweb-е предусмотрен XML-файл специального формата. Пример такого файла:
Для запроса фрагментов данных из это XML-я предусмотрено специальное XPath-расширение и строковые альясы, представляющие собой упрощенную форму записи пути к соответствующей ноде. Пример вывода текста в XSL-шаблоне:
Конфигурирование страницы в конфиге:
Содержимое book-edit.php:
Пример сохранения загруженных POST-запросом картинок:
Среди PHP-классов, предоставляемых движком, имеется класс session, предназначенный для хранения объектов в сессии и их извлечения из нее же. Easyweb позволяет хранить в сессии как простые значения вроде строк и чисел, так и более сложные сущности, а именно: индексные массивы, ассоциативные массивы, объекты Easyweb-класса xml, а также любые PHP-объекты, сериализуемые в JSON и обратно:
Все объекты, хранимые в сессии, доступны их XSL-шаблонов в виде XML-представления через специальное XPath-расширениe:
На данный момент движок работает через PHP-класс XSLTProcessor, который, в свою очередь, использует libxslt. Это значит, что возможно использование только XSLT 1.0 и XPath 1.0. Хорошей новостью является то, что XSLTProcessor в стандартной сборке поддерживает основную часть EXSLT. Как только / если в PHP появится стандартный модуль для работы с XSLT/XPath 2.0, он сразу же будет встроен в Easyweb.
В качестве API для доступа к СУБД используется PHP PDO, поэтому, теоретически, Easyweb поддерживает 12 типов СУБД (http://php.net/manual/ru/pdo.drivers.php), а также может быть легко адаптирован под любую другую СУБД, имеющую PDO-драйвер (достаточно засабмитить feature request с названием СУБД и ссылкой на ее PDO-драйвер). Кроме того, архитектура Easyweb-а легко позволяет (автору движка, то есть — мне) встраивать поддержку и других СУБД, не имеющих PDO-драйвера (Например — MongoDB), что, собственно, и планируется сделать в будущем.
Академически-подробных тестов по производительности не проводилось, поэтому могу поделиться результатами лишь визуальных наблюдений, наколенных замеров таймингов, и анализом логов апача с параллельным заглядыванием в htop. Итак, вот что известно:
На данный момент на некоторое неопределенное будущее запланированы следующие большие задачи (разработка собственного, либо подбор готового решения и интеграция с ним):
Репозиторий Easyweb: github.com/nyan-cat/easyweb
Документация: github.com/nyan-cat/easyweb/wiki
Учебный пример «гостевая книга»: github.com/nyan-cat/easybook
Спасибо за внимание. В комментариях отвечу на ваши вопросы.
Если говорить о классификации, то Easyweb, вероятно, больше всего похож на Content Management Framework. Он, в некотором смысле, немного выше уровнем, чем большинство программных фреймворков, поскольку основная часть продукта, созданного на Easyweb, выстроена на XML-технологиях. В то же время он гораздо ниже уровнем, чем привычные CMS — в нем нет встроенного пользовательского интерфейса, готовых шаблонов страниц или предустановленного набора SQL-запросов.
В двух словах, сайт или XML-сервис на Easyweb — это XML-конфиг, набор шаблонов страниц, база данных, а также скрипты для обработки POST-запросов. Все перечисленное создается вебмастером самостоятельно в его любимом текстовом редакторе, а также админке его любимой СУБД. Easyweb будет интересен тем, для кого выполняются все перечисленные условия:
- Если вас по каким-либо причинам не устраивают существующие CMS;
- Если вы задумываетесь о том, что нужно «написать все самому»;
- Если вас пугает сложность существующих программных фреймворков;
- Если вы любите XHTML, а также дружное семейство XML-технологий.
Предыстория
Мое знакомство с разработкой для веба началось достаточно стандартно: сайты, выходившие из-под моей клавиатуры, были кашей из HTML-я вперемешку с PHP-вставками и прямыми обращениями к базе данных через mysql_* API. Однако, поскольку я уже достаточно давно занимался программированием на самых разнообразных языках, начиная от ассемблера, и заканчивая SQL, шейдерами и Александрескуподобным C++, я довольно быстро понял, что такой веб никому не интересен, и нужно что-то менять. Прошло совсем немного времени, и я узнал от моего товарища о существовании XSLT. Изучив матчасть, я понял: вот она, технология моей мечты! Однако XSLT — это просто шаблонизатор, в то время как разработка для веба состоит из более широкого спектра задач. Поэтому я решил, что нужно делать полноценный движок.
После этого разработка Easyweb-а пошла по следующему сценарию: я писал движок и пользовался им до того момента, пока не накапливалась критическая масса новых идей (обычно это занимало немногим более года), после чего выбрасывал, и писал с нуля новую версию, в которой реализовывал все то, что накопилось и наболело за время работы с предыдущей реинкарнацией Easyweb-а.
Между третьей и четвертой версией у меня возник целый ряд технологических затыков, в связи с чем я даже всерьез задумывался о том, чтобы написать четвертую версию Easyweb-а на другом языке. В той или иной мере были предприняты определенные попытки на C++, Java, Scala, Ruby, Perl, Erlang, и даже технологиях Microsoft-а с их .NET-ами и прочими IIS-ами. Однако, после более чем полугода рассматривания одних и тех же проблем с разных сторон, в итоге таки удалось преодолеть все препятствия в рамках PHP. Ура, решение найдено!
Дописывая четвертую версию движка, я пришел к выводу, что мне удалось разрешить все ключевые проблемы, возникавшие лично у меня за все время разработки в вебе, в связи с чем было решено, что Easyweb дозрел до того, чтобы стать публичным проектом.
Текущее состояние
Четвертая версия движка все еще находится на стадии тестирования и периодического подправления некоторых мелочей. Планируется, что первая версия, которую можно будет назвать стабильной и полностью юзабельной, появится до конца года.
Документация написана на английском языке, однако, пока что не корректировалась носителем языка, поэтому постарайтесь не очень сильно смеяться с моего рязанского акцента.
Ближе к делу
Я не буду приводить подробную документацию в данной статье, поскольку она есть в Wiki-разделе проекта на GitHub-е. Напротив, я постараюсь изложить тот минимум, которого должно быть достаточно, чтобы вы поняли, интересен ли вам Easyweb и вы хотите заняться его изучением, или же вас устраивают технологии, с которыми вы работаете в данный момент, и этот проект вам не нужен.
Easyweb — это движок, в котором все нужно делать самому. Вообще все: спроектировать базу данных, написать SQL-процедуры, сверстать XHTML, прописать CSS-стили, JavaScript-ы, а также обработчики POST-запросов. Цель и смысл Easyweb-а заключается в том, чтобы позволить сделать это «всё» максимально просто, компактно, читабельно, интуитивно понятно, безопасно, ну и еще с десяток всяких красивых наречий.
Как было сказано выше, сайт, созданный на Easyweb-е, состоит из базы данных, XML-конфига, шаблонов страниц, локализации, и обработчиков POST-запросов. Обсудим каждую из этих сущностей, и приведем примеры XML- и PHP-сниппетов.
База данных
Easyweb не накладывает каких-либо ограничений на структуру базы данных, поэтому к ее проектированию следует подойти с учетом всего того хорошего, что вам известно про базы данных: ключи, индексы, foreign key constraints, триггеры, вьюхи, ну и так далее. Открываем, к примеру, phpMyAdmin, и — вперед.
XML-конфиг
В конфиге описывается, какие страницы есть на сайте, какие есть источники данных, какие процедуры используются для доступа к этим данным (и как они выглядят), а также как регулируются права доступа к страницам, процедурам и фрагментам шаблонов.
Страница:
<page name="book" url="/book/(book_id -> natural)/">
<template src="/tpl/main.xsl" xml="book:info(book_id -> $book_id)">
<template name="head" src="/tpl/home/head.xsl" />
<template name="body" src="/tpl/home/body.xsl" />
</template>
</page>
Источник данных:
<datasource
name="library"
type="postgresql"
server="192.168.10.230"
username="guest"
password="mZrCe3qlj92H"
database="library"
charset="UNICODE" />
Процедура:
<procedure name="book:list" datasource="library" root="books" item="book" empty="true">
<param name="author_id" type="natural" />
select id, title, description from book where author_id = $author_id;
</procedure>
Членство в группе:
<group name="editor">
<param name="account_id" type="natural" />
account:type(account_id -> $account_id) = 'editor'
</group>
Разрешение:
<permission name="book:edit">
<param name="account_id" type="natural" />
<param name="book_id" type="natural" />
editor(account_id -> $account_id) and public(book_id -> $book_id)
</permission>
Шаблоны страниц
Все шаблоны страниц являются XSL-валидными документами, внутри которых страница верстается с помощью стандартных XSL- и XPath-сущностей, а также предоставляемых движком расширений. Расширения Easyweb-а находятся в пространстве имен «www». Пример простого корневого шаблона:
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:php="http://php.net/xsl"
xmlns:www="https://github.com/nyan-cat/easyweb"
exclude-result-prefixes="xi php www">
<xsl:template match="/">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<xi:include href="head.xml" />
<www:template name="head" />
</head>
<body>
<www:xslt xsl="/xsl/menu.xsl" xml="/xml/menu.xml" />
<www:template name="body" />
<footer>
<xsl:value-of select="www:local('footer')" />
</footer>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Пример верстки меню (menu.xsl из примера выше):
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:php="http://php.net/xsl"
xmlns:www="https://github.com/nyan-cat/easyweb"
exclude-result-prefixes="php www">
<xsl:template match="menu">
<xsl:for-each select="item">
<a href="{@href}">
<xsl:value-of select="www:local(@name)" />
</a><xsl:if test="position() != last()"> | </xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Верстка списка книг, получаемого процедурой. XSL-шаблон хранится в отдельном файле:
<www:xslt xsl="/xsl/book-list.xsl" xml="book:list(author_id -> $author_id)" />
Верстка сразу в текущем шаблоне:
<xsl:for-each select="www:query('book:list', 'author_id -> $author_id')/books/book">
<h3><xsl:value-of select="title" /></h3>
<xsl:copy-of select="description" />
</xsl:for-each>
Локализация
Для хранения текстов мультиязычных сайтов в Easyweb-е предусмотрен XML-файл специального формата. Пример такого файла:
<?xml version="1.0" encoding="utf-8" ?>
<local>
<home>
<title>
<en>Chinese Cuisine</en>
<ru>Китайская кухня</ru>
<zh>中国菜</zh>
</title>
<description>
<en>
Welcome to Nannan Zhang personal website about chinese cuisine.
Feel free to contact me at <a href="mailto:nannan@zhang.com">nannan@zhang.com</a>.
</en>
<ru>
Добро пожаловать на персональный сайт Наннан Джанг,
посвященный китайской кухне. Вы можете связаться со мной
по адресу <a href="mailto:nannan@zhang.com">nannan@zhang.com</a>.
</ru>
<zh>
关于中式美食,欢迎访问张南南个人网站。
垂询邮箱:<a href="mailto:nannan@zhang.com">nannan@zhang.com</a>
</zh>
</description>
</home>
</local>
Для запроса фрагментов данных из это XML-я предусмотрено специальное XPath-расширение и строковые альясы, представляющие собой упрощенную форму записи пути к соответствующей ноде. Пример вывода текста в XSL-шаблоне:
<xsl:copy-of select="www:local('home:description')" />
Обработка POST-запросов
Конфигурирование страницы в конфиге:
<page
name="book:edit:post"
url="/book/edit/post/"
action="/php/book-edit.php"
permission="book:edit(account_id -> $session:account_id, book_id -> $post:id)" />
Содержимое book-edit.php:
<?php
function action($www, $response)
{
$www->query('book:edit', array
(
'book_id' => post::hidden('id'),
'title' => post::text('title'),
'description' => post::textarea('description')
));
$response->location('/book/' . post::hidden('id') . /);
}
?>
Пример сохранения загруженных POST-запросом картинок:
<?php
function action($www, $response)
{
foreach(files::get('picture.+') as $file)
{
$image = image::load($file['name']);
$scaled = $image->fit_to_width_copy(800);
$image_id = $www->evaluate('image:create');
$scaled->save(fs::normalize('/images/pictures/' . $image_id));
}
}
?>
Работа с сессией
Среди PHP-классов, предоставляемых движком, имеется класс session, предназначенный для хранения объектов в сессии и их извлечения из нее же. Easyweb позволяет хранить в сессии как простые значения вроде строк и чисел, так и более сложные сущности, а именно: индексные массивы, ассоциативные массивы, объекты Easyweb-класса xml, а также любые PHP-объекты, сериализуемые в JSON и обратно:
<?php
session::start();
session::value('account_id', $account_id);
var_dump( session::value('account_id') );
session::vector('numbers', array(1, 2, 3));
var_dump( session::vector('numbers') );
session::map('food', array('Apple' => 'Fruit', 'Potato' => 'Vegitable'));
var_dump( session::map('food') );
$xml = xml::load('/xml/menu.xml');
session::xml('menu', $xml);
var_dump( session::xml('menu') );
session::object('user', new user('Mark', user::type_editor, 35));
var_dump( session::object('user') );
?>
Все объекты, хранимые в сессии, доступны их XSL-шаблонов в виде XML-представления через специальное XPath-расширениe:
<xsl:for-each select="www:session('xml', 'menu')/menu/item">
<a href="{@url}"><xsl:value-of select="@name" /></a>
</xsl:for-each>
Технические вопросы
На данный момент движок работает через PHP-класс XSLTProcessor, который, в свою очередь, использует libxslt. Это значит, что возможно использование только XSLT 1.0 и XPath 1.0. Хорошей новостью является то, что XSLTProcessor в стандартной сборке поддерживает основную часть EXSLT. Как только / если в PHP появится стандартный модуль для работы с XSLT/XPath 2.0, он сразу же будет встроен в Easyweb.
В качестве API для доступа к СУБД используется PHP PDO, поэтому, теоретически, Easyweb поддерживает 12 типов СУБД (http://php.net/manual/ru/pdo.drivers.php), а также может быть легко адаптирован под любую другую СУБД, имеющую PDO-драйвер (достаточно засабмитить feature request с названием СУБД и ссылкой на ее PDO-драйвер). Кроме того, архитектура Easyweb-а легко позволяет (автору движка, то есть — мне) встраивать поддержку и других СУБД, не имеющих PDO-драйвера (Например — MongoDB), что, собственно, и планируется сделать в будущем.
Академически-подробных тестов по производительности не проводилось, поэтому могу поделиться результатами лишь визуальных наблюдений, наколенных замеров таймингов, и анализом логов апача с параллельным заглядыванием в htop. Итак, вот что известно:
- Простейшая гостевая книга из учебного примера на GitHub-е, страница с лентой сообщений генерируется за 0.005 — 0.01 секунд на недорогой VDS-ке. Конфигурация VDS-ки, однако, достоверно неизвестна;
- Сайт формата «доска объявлений». Выделенный сервер уровня Intel Xeon E3 выдерживает ~10 миллионов запросов от поисковых ботов в сутки, и, судя по htop-у, у сервера еще имеется запас в несколько раз;
- Сайт формата «доска объявлений» на предыдущей версии движка и выделенном сервере уровня Intel i7 в свое время столкнулся с бурным наплывом посетителей, порядка 5000 — 10000 уникальных пользователей за 15 минут. Замедления работы сайта не наблюдалось. В то же время, текущая версия движка работает быстрее, чем предыдущая.
Дальнейшие планы и размышления
На данный момент на некоторое неопределенное будущее запланированы следующие большие задачи (разработка собственного, либо подбор готового решения и интеграция с ним):
- Поддержка различных СУБД, не имеющих PDO-драйвера, по мере возникновения чьих-либо пожеланий;
- Перевод некоторых частей парсинга с регулярок на полноценный LL(1);
- ORM-система или ее подобие;
- Фасетный поиск;
- Полнотекстовый поиск;
- Фреймворк для облегчения решения определенных задач на клиенте (AJAX или WebSockets на стороне клиента, и, вероятно, JSON-over-HTTP на стороне сервера).
Ссылки
Репозиторий Easyweb: github.com/nyan-cat/easyweb
Документация: github.com/nyan-cat/easyweb/wiki
Учебный пример «гостевая книга»: github.com/nyan-cat/easybook
Спасибо за внимание. В комментариях отвечу на ваши вопросы.