Антон Шевчук // Web-разработчик

Zend Framework: оптимизация Zend_View, и нужен ли нам Zend_Cache? // PHP

Zend Framework

Для тех кто уже хоть чуть-чуть знаком с Zend_View не будет секретом существование Helper’ов – классов которые облегчают генерацию HTML кода. Написать свой helper не составит труда даже для начинающего разработчика, но я решил покопаться внутри и понять, а все ли коту масленица.

Zend_View: Helpers

И так, для начала поясню как работает механизм вызова helper’ов:

В классе Zend_View_Abstract существует магический метод __call (о принципах работы см. документацию) , при вызове данного метода, происходит следующее:

  • Проверяется не был ли загружен класс вызываемого helper’a до этого
  • Если не был загружен – пытаемся найти и прочитать файл во всех путях к helper’ам (мы их можем добавлять используя метод addHelperPath)
  • Нашли, проинклудили, создали сущность класса, сохранили для потомков
  • У объекта helper’a вызываем необходимый нам метод

Ну как Вам сила ООП? И все это сверху прикрыто еще множеством проверок, т.е. для вызова одного метода выполняется очень много телодвижений.

Что я предлагаю? Создайте свой класс View, который будет наследоваться от Zend_View_Abstract, и добавьте все необходимые методы в данный класс. Этот вариант более предпочтителен если Ваши хэлперы не слишком громоздки.

Вот что у меня получилось когда я helper для translate перекинул в свой XCore_View объект (данные для 10000 идентичных вызовов):

Time (s) Memory (b)
Метод во View 0.3108 0122416
View Helper 0.5231 0124116
Разница 0.2123 0001700

Как видим различия в использованной памяти ничтожно малы, но вот время выполнения скрипта страдает, конечно я понимаю что 10 000 вызовов на страницы не будет, но осадочек то остается…

Причина более медленной работы ZF кроется во множестве проверок, т.е. мой код по идеи менее дуракоустойчив…

Далее – в версии 1.5 добавили еще несколько хэлперов для формирования заголовков в HTML документе:

  • Doctype
  • HeadLink
  • HeadMeta
  • HeadScript
  • HeadStyle
  • HeadTitle
  • InlineScript

Ранее для этой цели мной был написан класс Document, но поскольку стремление быть ближе к ZF было велико, решил я использовать хэлперы и забыть о классе Document. Но я опять столкнулся с проблемой производительности (Init – время и память потраченные на инициализацию, Output – генерация HTML):

Time (s) Memory (b)
Init Output Init Output
Старый Document 0.0003 0.0083 0004288 0108008
View Helpers 0.0054 0.0105 0166624 0122984
Разница 0.0051 0.0022 0162336 0014976

Немного поясню результаты – при инициализации helper’ов происходит поиск и загрузка нужных файлов, и так же создается сущность объекта для каждого из helper’ов. При использовании же класса Document, мы оперируем только одним файлом, одним классом, одним объектом. При выводе результаты не отличаются так сильно – что и не удивительно – происходит всего-лишь вызов методов уже существующих в памяти объектов, которые выполняют практически идентичные задачи.

Zend_Cache

Еще хотел бы похвалить удобство работы с Zend_Cache, но вот скорость работы опять же уступает кастомному решению:

У нас есть следующая задача – в системе существует несколько XML конфигурационных файлов, необходимо закешировать их, т.к. парсить каждый раз XML не очень хорошо. Для этого мы воспользовались Zend_Cache:

function getConfig() {
  require_once 'Zend/Cache.php';
  $frontendOptions = array('master_file' => 'config.xml' , 'automatic_serialization' => true , 'lifetime' => null , 'write_control' => false);
  $backendOptions  = array('cache_dir' => 'config' , 'file_locking' => false);// FIXME: it's ZF error

  $cache = Zend_Cache::factory('File', 'File', $frontendOptions, $backendOptions);

  if (! ($parsed = $cache->load(md5('config.xml')))) {
            // load configuration file
            $parsed = simplexml_load_file($xml_path, "XCore_XML_Element", LIBXML_NOCDATA);
            $cache->save($parsed);
        }
  }
  return $parsed;
}

Опять же заглянув внутрь – не все то самолет, что Zend Framework:

Для контроля изменений мастер-файла в кэш директории Zend создает 2 файла – сам кэш данных – представляющий собой серелизованный массив и еще один файл содержащий мета данные о оригинальном файле включая настройки кэширования. Т.е. когда у нас выполняется проверка на попадание в кэш – всегда считывается файл с метаданными и десерелизуется, а на это тратиться время. Попробуем написать свой механизм кэширования файлов, используя в качестве средства контроля изменений лишь дату модификации файла, вот каков результат (тестирование при попадании в кэш):

Time (s) Memory (b)
Xcore_Cache_Config 0.0015 0057076
Zend_Cache 0.0030 0101096
Разница 0.0015 0044020

Да уж – тут становиться грустно – разница почти в два раза, радует что в системе зачастую не бывает более одного конфигурационного файла, хотя у кого как – у нас в phpXcore их 5, хорошо это или плохо, не знаю…

А в целом использовать Zend_Cache удобно, и зачастую необходимо…

P.S. Продуктивный день – покритиковал Zend, похвалил Microsoft, день прошёл не зря, но у меня ощущение, что сделал я что-то не так…

© Антон Шевчук 2007-2017