Zend Framework: оптимизация Zend_View, и нужен ли нам Zend_Cache? // PHP
Для тех кто уже хоть чуть-чуть знаком с 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, день прошёл не зря, но у меня ощущение, что сделал я что-то не так…