Собственно кеширование вывода MVC фреймворков является абсолютно классической и рутинной задачей, все фреймворки в той или иной степени реализуют его, тут я в общих словах накидаю разработанную мной методологию.


Что потребуется



  1. Нужна реализация общего механизма кеша, но не совсем обычная — секрет поддержка тегов
  2. Нужен базовый ORM в каком-либо виде, все что от него требуется это на все сущности, на сохранение и удаление повесить определенные события
  3. Нужна поддержка отложенных запросов — основная идея в том что запрос формируется в контроллере но выполняется при разборе шаблона
  4. Быстрый компилируемый шаблон с поддержкой кеширования — тут от шаблонизатора требуется что-бы он мог полностью заменять обработку куска шаблона содержимым кеша по указанному ключу


Ограничения



данная система подразумевает что фреймворк работает с БД в эксклюзивном режиме — т.е, грубо говоря админка данных реализована в текущем проекте и в базу ручками не лазят.

Реализация



Для затравки, укажу используемые мной инструменты и укажу направдение в котором я двигался:

Система кеширования — просто синглтон который по ключу из драйвера получает значение — все просто как молоток
Теги кеша — базовый наследуемый класс Tag:
class Tag
{
    function __contruct($name)
    {
         $this->name = $name;
    }

    public function buildKey()
    {
         return sprintf('tag_%s_%s', get_class($this), $this->name);
    }

    public function clear()
    {
         $ver = microtime(true).'.'.rand(0, 1000000);
         Cache::GetInstance()->update($this->buildKey(), $ver);
    }
}


Теги реализуются наследованием данного класса и реализацией конструктора «по вкусу».

Теперь можно некоторую запись в кеше пометить набором тегов (тут предполагается что читатель включит фантазию) и сделав clear() тега принудительно сделать все записи кеша содержащие данный тег — не валидными.

Отложенные запросы отлично реализуются с помощью ORM Propel 1.3 — все что вам нужно это запихивать в шаблон объект реализующий SPL Iterator и включающий сформированный объект класса Propel Criteria ну и всякая OOП магия пыха тоже полезна, в данном случае.

На тот-же Propel, путем написания своих билдеров в сущности запихиваются триггеры обнуления тегов:
 class A
 {
     function save(PropelPDO $con)
     {
        ....
        $tag1 = new TagEntity(APeer::CLASS_NAME);
        $tag1->clear();

        if (!$this->isNew())
        {
            $tag2 = new TagPk(APeer::CLASS_NAME, $this->getPrimaryKey());
            $tag2->clear();
        }
        ....
     }
 }


В качетве шаблонизатора использую PHPTAL, к нему пишется кеширующий триггер. Контроллеры имеют примерно следующий вид:
  function executeAction(Context $context)
  {
     $context->addTag('test_tag1', new TagEntity(APeer::CLASS_NAME)); // описываем тег и добавляем ему метку в контексте

     $context->as = APeer::Lazy()->descByDate()->limit(10); // готовим выборку

     $this->renderTemplate('template_name', $context); // рендерим шаблон - да такой у меня API
  }


А шаблон:
 .....
 <div phptal:id="test_tag1">
    <ul>
      <li tal:repeat="a as" tal:content="a/getTitle" />
    </ul>
 </div>
 .....


Вот теперь, при первом запросе сформированное содержимое елемента div попадет в кеш c тегом new TagEntity(APeer::CLASS_NAME), определенным в контроллере (магические триггер щаблонизатора) и пока не будут произведены какие-либо изменения таблицы, описываемой APeer, будет браться из кеша и запросов в БД не будет совсем.

Вместо заключения



Все описанное выше — работает в реальном фреймворке и если хабрасообщество меня приймет то думаю буду писать еще.