Yii - перехват запросов
Все фреймворки, с которыми мне приходилось работать, при всей своей гибкости, все же слегка «деревянные». Например, ни один из них не позволяет динамически менять конфигурацию приложения, без редактирования конфигурационных файлов. Это создает определенные проблемы, если вы хотите, чтобы ваше приложение было по-настоящему модульным.
Представим, что вы хотите создать CMS, которая будет иметь кучу модулей, созданных сторонними разработчиками. И каждый пользователь вашей CMS должен иметь возможность сам решать, какие именно модули ему устанавливать. При этом установка/удаление модуля должны происходит по принципу «одного клика», т.е. пользователь должен лишь выбрать нужный модуль и нажать кнопку «Установить» (или «Удалить»), всю остальную работу, включая изменение настроек, система должна сделать сама.
Вот тут и начинаются проблемы. Скопировать (скачать) файлы модуля в определенную папку сервера не представляет труда, но как обновить «hard-coded» конфигурацию? Писать парсер php-файлов ради добавления пары строк в конфиг - далеко не самое умное решение. Гораздо лучше (и удобнее) работать с конфигурацией, находящейся в каком-нибудь хранилище (например в БД) и считывать ее при запуске приложения. Осталось только придумать как успеть загрузить настройки ДО начала работы приложения (обработки запроса)...
Описанная выше ситуация — это только один из случаев, когда приложение должно по каким-то причинам «перехватить управление» до начала обработки запроса самим фреймворком. Другая частая необходимость в таком поведении — проверка прав доступа. К примеру некоторые из установленных модулей сейчас отключены и хорошо бы чтобы фреймворк об этом узнал и соответственно отреагировал.
Предлагаемое ниже решение прекрасно работает в Yii и позволяет одним махом «закрыть» все подобные вопросы.
Yii имеет механизм временной блокировки сайта. Суть его сводится к следующему: в настройках можно указать маршрут (контроллер/действие), который будет вызываться при любом обращении к сайту. В самом действии обычно прописан код вывода сообщения типа «На сайте ведутся технические работы. Зайдите позже».
Как оказалось, этот механизм прекрасно подходит для решения вышеописанных задач, нужно лишь добавить пару строк, чтобы заставить фреймворк продолжить обработку запроса, когда все настройки и проверки уже выполнены. Таким образом мы получим систему, в которой перед обработкой любого запроса будет вызываться созданное нами действие (метод класса контроллера).
Перейдем к практической реализации.
Код до смешного прост. Создадим контроллер и обзовем его Privratnik:
class PrivratnikController extends Controller
{
public function actionIndex()
{
// здесь будем добавлять код
// возвращаем управление фреймворку
$_app = Yii::app();
$route = $_app->getUrlManager()->parseUrl($_app->getRequest());
$_app->runController($route);
}
}
Сохраним текст класса в файле PrivratnikController.php в папке protected/controllers. Затем активируем его, установив в конфигурации фреймворка значение параметра catchAllRequest равным маршруту к нашему контроллеру:
'catchAllRequest' => array('privratnik/index'),
Проверив работу сайта можно убедиться, что ничего не изменилось — все страницы открываются так же как и раньше. Но теперь все вызовы проходят через наш контроллер. Убедиться в этом проще простого: например, измените метод actionIndex() вот так:
public function actionIndex()
{
echo '<h1>Hello World</h1>';
$_app = Yii::app();
$route = $_app->getUrlManager()->parseUrl($_app->getRequest());
$_app->runController($route);
}
и вверху каждой страницы вашего сайты вы увидите надпись Hello World 
Как применять описанную возможность думайте сами. Например, можно динамически подменять параметры генерации URL'ов страниц и получить возможность указания элиасов страниц. Или можно проверить авторизацию пользователя и, в зависимости от нее, блокировать доступ к некоторым частям сайта. Так же здесь можно вставить анализ запросов для сбора статистики или защиты от DdoS-атак...
В общем применений масса.
Статичные файлы vs скрипты. Невидимое зло.
Читая всякие форумы по CMS-строительству, наблюдаю странную на мой взгляд, картину - люди стараются нагрузить вэб-сервера дурной бесполезной работой, не имея на это никаких существенных оснований. Речь веду о запихивании в БД всего, что под руку попадется. Например, многие современные CMS грешат тем, что хранят в БД те данные, которым там совсем не место - шаблоны страниц, стили и т.д.
Мне кажется, что такой подход - результат того, что вэб-приложения разрабатывают люди, привыкшие создавать софт для настольных систем, для которых хорош принцип "компьютер железный, вот пусть и работает!". Но в вэб такой подход совершенно не приемлем. Имхо.
Если вы создаете настольное приложение, то вам нет необходимости заботиться о расходе памяти (ее навалом в любом современном компе) равно как и о нагрузке на процессор и т.д. Ведь система однопользовательская и кроме вашей софтины (грубо говоря) ничего на данный момент не выполняется. Работа в фоне всяких торрент-клиентов и винампов не в счет - нагрузка, создаваемая ими минимальна.
Совсем другое дело, когда код выполняется на виртуальном хостинге, где размещена пара сотен сайтов, каждый из которых обслуживает десятки клиентов одновременно. Получается, что параллельно с вашим скриптом сервер обрабатывает еще сотни (а то и тысячи) запросов для других сайтов. Тут уж и кажущиеся несущественные операции дают в сумме дополнительный нагруз, который совсем не так мал, как кажется.
Вот четыре основные вещи, дающие абсолютно лишнюю нагрузку:
- хранение стилей в БД
- склеивание стилей в один большой блок
- хранение шаблонов в БД
- использование "кросс-БДшного" кода.
Рассмотрим их более подробно.
Проблема хранения стилей в БД.
Модная нынче тенденция хранить стили в БД оборачивается тем, что при каждом запросе клиентского приложения (броузера) серверу приходится запускать PHP-скрипт, который в свою очередь должен пропарсить запрос, определить какие именно стили требуются клиенту, затем "вынуть" их из БД и только потом отправить клиенту, предварительно создав все необходимые заголовки. Казалось бы нагрузка не такая уж и существенная, но все же вариант, когда стили хранятся в отдельных файлах гораздо предпочтительнее, т.к. в этом случае вэб-сервер тупо отсылает клиенту готовый файл и никаких запусков скриптов, парсингов, обращений к БД и прочей лабуды не выполняется. Ну так зачем делать лишнюю работу?
Сторонники хранения стилей в базе данных приводят два (весьма спорных) аргумента в защиту такого подхода.
Первый основывается на том, что пользователи их продукта (CMS) имеют возможность редактировать стили прямо в админке сайта. Круто, конечно, но во-первых редактирование стилей (как и скриптов, шаблонов и прочего не-контекстного барахла) в textarea не идет ни в какое сравнение даже с виндовым "блокнотом". Про всякие подсветки синтаксиса, отступы, автоформатирование, автодополнение и исправление ошибок тут вообще речь не идет. Мазохизм в чистом виде.
Во-вторых, возможность редактирования файлов через админку вовсе не обязует хранить данные в БД — дайте пользователю возможность редактирования через админку стилевых файлов на сервере и обращения к БД (как и бесполезные вызовы скриптов) больше не нужны. Если результат одинаковый, то зачем платить больше?
Опять же, сама возможность такого редактирования может понадобиться только при смене дизайна сайта, а такая необходимость (в реальных условиях серьезного проекта) возникает только на этапе разработки. И для ее выполнения, как ни крути, нужны специалисты (по крайней мере люди, знающие CSS, HTML и т.д.) и уж пользоваться FTP-клиентами и нормальными редакторами такие люди точно умеют. Выходит, что пользы тут ноль.
Второй аргумент — якобы упрощение переноса сайта с одного хоста на другой. Типа просто экспортировали БД на старом хосте и импортировали на новом и все само собой правильно настроилось. Но ведь при переносе сайта вам все равно придется переносить и скрипты (сам код CMS), так какая разница переносить только файлы CMS или файлы CMS плюс файлы дизайна??? Один фиг копировать придется. 
Склеивание стилей в один большой блок.
В сети существует (опять же весьма спорное) мнение, что отправка стилей одним файлом происходит быстрее, чем отправка пачки мелких стилевых файлов. Сторонники этого подхода видимо забывают, что сетевые протоколы по-сути вещи асинхронные и зачастую отправка пачки мелких блоков происходит быстрее, чем отправка одного большого блока, т.к. несколько файлов будут передаваться параллельно и, если какой-то из них где-то вдруг застрянет, то клиенту не придется ждать — он в это время будет скачивать остальные файлы.
В тоже время склейка тоже не происходит сама собой. Снова вэб-сервер, вместо тупой отправки готовых файлов, будет вынужден запускать PHP-скрипты, которым придется производить кучу работы (почти такой же объем вычислений, как при генерации страницы!) — парсить запрос, определять из каких именно шаблонов состоит страница (а для этого придется вызывать все модули, формирующие контент данной страницы, которые, в свою очередь, будут парсить запрос или шаблоны, рыться в БД и т.д.), собирать в кучу (опять же скорее всего из БД) отдельные куски стилевых файлов, клеить их в один и только потом отправлять клиенту. Огромная и совершенно тупая работа и бесполезная нагрузка на сервер.
Хранение шаблонов в БД.
Это наверное наибольший из маразмов, т.к. ни один вменяемый дизайнер/верстальщик ни за какое бабло не станет проводить редактирование шаблонов в броузере. А других аргументов в пользу хранения шаблонов в БД просто нет. Про перенос с хоста на хост я уже сказал, тут тоже никакого выигрыша нет.
Проблема ужесточается тем, что времена когда страница сайта строилась из одного шаблона, давно прошли и ныне страницы современных сайтов собираются из кучи мелких шаблончиков - под каждый блок сайта свой шаблон и, возможно, не один. В результате имеем по одному лишнему обращению к БД на каждый из микро-шаблончиков, а то и более. Зачем? Только чтобы потешить свое ЧСВ, типа идем в ногу со временем и активно используем передовую идею запихивания хлама в БД? Смешно. 
Кросс-БДшные CMS.
Конечно офигенно круто, если ваша CMS может, практически без каких-либо изменений в коде, работать с почти любой, установленной на сервере, СУБД. MySQL? Пожалуйста. Postgre? Легко! MSSQL? Да не вопрос!!! Глобальная победа универсальности в рамках отдельно взятого сайта!
На практике же 80% всех сайтов сидят под MySQL и большего им не нужно. А там где нужны транзакции и прочие мега-фишки, требующие более взрослых СУБД, используются и более другие CMS, заточенные под соответствующие задачи. Зато БД-шная универсальность обходится совсем не так дешево, как кажется. И для программистов и для серверов.
Холиварить тут можно очень долго, поскольку найдется не одна сотня разработчиков, которые будут утверждать, что строить запросы, используя какую-нить ActiveRecord в стопиццот раз проще, чем вручную писать SQL-запросы. Но они лукавят, т.к. если запрос использует пяток таблиц, завязанных перекрестными ссылками в тугой узел, обильно приправленный полу-десятком условий с сортировками, группировками и прочими JOIN'ами, то объем кода, который требуется написать, чтобы все это добро впихнуть в ту же ActiveRecord становится в несколько раз больше, чем сам чистый SQL-запрос. И самое противное, что разобраться в этой мешанине уже совсем не так просто, как в чистом SQL.
В качестве примера, для разминки мозга, можете сами попробовать запрограммировать в ZendFramework или CakePhp вот это (совершенно реальный запрос из моего блога):
SELECT
s.id, inner_uri, category_id, created, last_modified,
title, menu_alt, link_target, author, editor,
COUNT(c.id) as comments,
ua.user_name AS author_name, ua.user_email as author_email,
ua.user_avatar as author_avatar,
ue.user_name AS editor_name, ue.user_email as editor_email,
cat.cat_title AS category , p.content,
p.commenting, p.comment_thru
FROM
t1_module_blog_posts p
LEFT JOIN t1_sitemap s ON s.id=p.map_id
LEFT JOIN t1_module_blog_comments c ON c.map_id = s.id
LEFT JOIN t1_users ua ON ua.user_id = s.author
LEFT JOIN t1_users ue ON ue.user_id = s.editor
LEFT JOIN t1_categories cat ON s.category_id = cat.cat_id
WHERE
visible=1
AND (s.category_id=0 OR cat.cat_visible=1)
AND (valid_thru=0 OR valid_thru>=1279452356)
AND s.created<1279452356
AND (s.permission='' OR s.permission IN
('admin_panel_access', 'authorized-user', 'developers-only', 'manage_categories',
'manage_content', 'manage_groups', 'manage_menus', 'manage_modules',
'manage_perms', 'manage_prefs', 'manage_settings', 'manage_users', 'module_settings'))
AND (s.category_id IN ('25', '21', '18', '19')
OR s.category_id=0)
GROUP BY p.map_id
ORDER BY created DESC
LIMIT 20, 140
Сделали? Сравнили читаемость? А заработало? И с первого раза??? 
А теперь прикиньте, что каждый запрос (от простейшего до монстрообразного) требует таких же "напрягов" от вас, как разработчика, а потом еще движок, при каждом обращении к БД будет всю эту вашу писанину парсить, сверять с реальной структурой БД и строить тот самый чистый запрос, от которого вы так долго пытались избавиться который вы так долго пытались "зашифровать" для фреймворка. Работа ради работы?
Ну да, CMS может работать с туевой хучей разных СУБД, но нужно ли вам это в реале? Согласны ли вы платить тормозами за то, чем реально не пользуетесь, ведь из десятка поддерживаемых БД вы все равно пользуетесь только одной?
Итог
Задумайтесь. (С) National Geographic.
Набор передовых PHP фреймворков
PHP - скриптовый язык программирования, который позволяет быстро и относительно легко создавать серверные приложения для сайтов любого уровня, будь то домашняя страничка, блог, форум или огромный портал.
И уж коль вы работаете с этим языком, значит вам пригодятся размещенные в этом посте ссылки. Все они ведут на сайты различных PHP-фреймворков, которые сделают вашу работу в десятки раз проще, помогут решить многие типичные проблемы, избавят от множества ошибок и съэкономят вам сотни часов времени разработки.
Akelos
Cake PHP
Code Igniter

Kohana
Prado

Seagull

Symfony

Yii

Zend Framework

Zoop


