Для ускорения Zend Framework очень действенен такой финт: собираем все классы, которые нам нужны, в один фаил, а потом включаем eAccelerator и инклудим его в самом начале. Один фаил + еАкселератор круче, чем много фаилов.
Под катом — рассказ, как я это сделал. Это не самое умное, лучшее и красивое решение, поэтому я рад послушать ваши советы и замечания. В общем, топик этот — ради ваших советов и замечаний — тех, что по делу, а не по поводу всякой херни типа орфографических ошибок. Спасибо!
Я сначала сам с собой договорился, что буду использовать Zend_Loader (точнее, Zend_Loader_Autoloader — ну что-то такое, чего там использует Zend_Application), а не грузить фаилы инклудами.
После чего я решил действовать так:
Итак, первое: сбор фаилов в data.txt.
Оказалось, что Zend_Loader_PluginLoader умеет составлять такой список сам (но немного криво), а Zend_Loader_Autoloader — не умеет. Но это не беда. Тут стоило бы унаследовать автолоадер и сделать все по честному, но мне было впадлу и я похачил сам ZF. Благо, на продакшн все равно не нужно будет лить хаченую версию: HotPlug.php можно собирать и дома:
Я открыл свой сайт и побегал по нему некоторое время. Сайт мне нравился, а разрастающийся APPLICATION_PATH. '/../data/files.txt' — нет. Но это ничего страшного, подумал я, и набросал скрипт для сборки. Опять же, стоило сделать его умно, красиво, объектно-ориентированно и консольно, но мне было впадлу и я тупо создал combine.php в /htdocs/
После чего я радостно заинклудил этот фаил прямо в индексе. И знаете, что оно мне сказало? Что ему не хватает кучи классов. «Wtf?!» — подумал я и начал ловить недостающие классы, дописывая их ручками в files.txt, а потом пересобирая HotPlug.php :)
И тут до меня дошло, что Loader не знал о тех фаилах, которые инклудятся require_once в начале зендовских классов, а поэтому они не собираются. Но они и не инклудятся %)
Пришлось написать еще один скрипт, который комментирует все эти require_once:
Теперь я пересобрал HotPlug.php и все было просто чудесно!
— Шаг второй
Помимо Zend_Loader_Autoloader, инклудами ведает еще и Zend_Loader_PluginLoader, и инклудит он сам, без Zend_Loader'а. Зато он умеет собирать список. Вот так:
Этот cache.php после пары запусков содержит много include_once'сов, которые нужны, чтобы автоматом грузить эти плагины. Я так и не понял, какой от этого прирост и решил попросить его пихать эти фаилы в мой files.txt
Чтобы не парсить строку include_once '...'; я похачил (какой я гад все-таки, а!) Zend/Loader/PluginLoader.php
Опять же, на продакшн это посылать не надо, так что ничего страшного. А дома — ну дома, пусть будет дома. Надо будет обновить ZF, а потом снова пересобрать HotPlug.php — ну похачу еще разок, или там сделаю все по человечески как-нибудь.
Теперь я еще побегал по сайту, собирая plugin-ы, а потом пересобрал HotPlug.php. Моя жизнь изменилась к лучшему!
Теперь — расскажите о своих бест практицес для решения подобной проблемы :) И давайте сделаем нормальное, разумное ОО-решение без лишних хаков?
Под катом — рассказ, как я это сделал. Это не самое умное, лучшее и красивое решение, поэтому я рад послушать ваши советы и замечания. В общем, топик этот — ради ваших советов и замечаний — тех, что по делу, а не по поводу всякой херни типа орфографических ошибок. Спасибо!
Я сначала сам с собой договорился, что буду использовать Zend_Loader (точнее, Zend_Loader_Autoloader — ну что-то такое, чего там использует Zend_Application), а не грузить фаилы инклудами.
После чего я решил действовать так:
- Собираем все волшебные фаилы, которые нам нужны, в APPLICATION_PATH. '/../data/files.txt'
- Открываем APPLICATION_PATH. '/../data/files.txt' и собираем их в один волшебный APPLICATION_PATH. '/../data/HotPlug.php', по дороге вырезая инклуды и комментарии
Итак, первое: сбор фаилов в data.txt.
Оказалось, что Zend_Loader_PluginLoader умеет составлять такой список сам (но немного криво), а Zend_Loader_Autoloader — не умеет. Но это не беда. Тут стоило бы унаследовать автолоадер и сделать все по честному, но мне было впадлу и я похачил сам ZF. Благо, на продакшн все равно не нужно будет лить хаченую версию: HotPlug.php можно собирать и дома:
/* library/Zend/Loader.php */ public static function loadClass($class, $dirs = null) { if (class_exists($class, false) || interface_exists($class, false)) { return; } if ((null !== $dirs) && !is_string($dirs) && !is_array($dirs)) { //require_once 'Zend/Exception.php'; throw new Zend_Exception('Directory argument must be a string or an array'); } // autodiscover the path from the class name $file = str_replace('_', DIRECTORY_SEPARATOR, $class) . '.php'; if (!empty($dirs)) { // use the autodiscovered path $dirPath = dirname($file); if (is_string($dirs)) { $dirs = explode(PATH_SEPARATOR, $dirs); } foreach ($dirs as $key => $dir) { if ($dir == '.') { $dirs[$key] = $dirPath; } else { $dir = rtrim($dir, '\\/'); $dirs[$key] = $dir . DIRECTORY_SEPARATOR . $dirPath; } } $file = basename($file); self::loadFile($file, $dirs, true); } else { self::_securityCheck($file); include $file; } // добавляем отсюда $files = file( APPLICATION_PATH . '/../data/files.txt' ); $files[] = $file; $files = array_unique($files); file_put_contents( APPLICATION_PATH . '/../data/files.txt', implode("\n", $files) ); // досюда if (!class_exists($class, false) && !interface_exists($class, false)) { //require_once 'Zend/Exception.php'; throw new Zend_Exception("File \"$file\" does not exist or class \"$class\" was not found in the file"); } }
Я открыл свой сайт и побегал по нему некоторое время. Сайт мне нравился, а разрастающийся APPLICATION_PATH. '/../data/files.txt' — нет. Но это ничего страшного, подумал я, и набросал скрипт для сборки. Опять же, стоило сделать его умно, красиво, объектно-ориентированно и консольно, но мне было впадлу и я тупо создал combine.php в /htdocs/
<? $skip = array( T_COMMENT, T_OPEN_TAG, T_CLOSE_TAG, T_DOC_COMMENT, T_ML_COMMENT // нафиг комменты из HotPlug! и всякие <? и ?> тоже нафиг ); $dir = "d:\work\нескажуназвание\library\\"; $files = file('d:\work\нескажуназвание\app\data\files.txt'); $res = '<?'; foreach ($files as $file) { if (substr(trim($file), -4) != '.php') $file = str_replace('_', '\\', trim($file)) . ".php"; // если там имя класса, а не фаила - переделываем if (is_file($fileName = trim($dir . $file))) { $res .= "\n/* $file */\n"; $tokens = token_get_all(file_get_contents($fileName)); $was_require_once = 0; $was_shit_require_once = 0; foreach($tokens as $token) { if (is_array($token)) { if (in_array($token[0], $skip)) continue; if ($token[0] == T_WHITESPACE) { $res .= ' '; // поменьше места на всякие табы continue; } if ($was_require_once) { if ($token[0] == T_CONSTANT_ENCAPSED_STRING) { // скипаем require_once, после которых идет строка в кавычках. если потом идет что-то вроде $file - скипать не надо! тоже достаточно грязный метод, стоило бы подумать $was_shit_require_once = 1; } else { $res .= 'require_once ' . $token[1]; } $was_require_once = 0; continue; } if ($token[0] == T_REQUIRE_ONCE) { $was_require_once = 1; } else { $res .= $token[1]; } } else { if (!$was_shit_require_once) // чтобы ";" после удаленных require_once удалять $res .= $token; $was_shit_require_once = 0; } } $res .= "\n"; } } file_put_contents("d:\work\нескажуназвание\app\data\HotPlug.php", $res);
После чего я радостно заинклудил этот фаил прямо в индексе. И знаете, что оно мне сказало? Что ему не хватает кучи классов. «Wtf?!» — подумал я и начал ловить недостающие классы, дописывая их ручками в files.txt, а потом пересобирая HotPlug.php :)
И тут до меня дошло, что Loader не знал о тех фаилах, которые инклудятся require_once в начале зендовских классов, а поэтому они не собираются. Но они и не инклудятся %)
Пришлось написать еще один скрипт, который комментирует все эти require_once:
<? function getDirectoryTree( $outerDir ){ $dirs = array_diff( scandir( $outerDir ), Array( ".", ".." ) ); $dir_array = Array(); foreach( $dirs as $d ){ if( is_dir($outerDir."/".$d) ) $dir_array[ $d ] = getDirectoryTree( $outerDir."/".$d ); else $dir_array[ $d ] = $d; } return $dir_array; } $dirs = getDirectoryTree("d:\work\пыщь\library\Zend"); function gotcha($fname, $key, $dir) { if (is_array($fname)) { array_walk($fname, 'gotcha', $dir . DIRECTORY_SEPARATOR . $key); return; } $fname = $dir . DIRECTORY_SEPARATOR . $fname; file_put_contents( $fname, preg_replace("/require_once\\s+\'Zend/", "//require_once \'Zend", file_get_contents( $fname )) ); // стоило бы написать /(require|include)_once\\s+(\'|\")/ или типа того, но мне было - ну вы догадались - впадлу тестировать этот прег и я запускал скрипт просто 4 раза подряд. Благо, после этого он больше не нужен вообще :) } array_walk($dirs, 'gotcha', "d:\work\пыщь\library\Zend");
Теперь я пересобрал HotPlug.php и все было просто чудесно!
— Шаг второй
Помимо Zend_Loader_Autoloader, инклудами ведает еще и Zend_Loader_PluginLoader, и инклудит он сам, без Zend_Loader'а. Зато он умеет собирать список. Вот так:
// Где-нибудь в Bootstrap.php, или - у меня - в /library/R00/Bootstrap.php, от которого наследуются бутстрапы моих проектов Zend_Loader_PluginLoader::setIncludeFileCache( APPLICATION_PATH . '/../data/cache.php');
Этот cache.php после пары запусков содержит много include_once'сов, которые нужны, чтобы автоматом грузить эти плагины. Я так и не понял, какой от этого прирост и решил попросить его пихать эти фаилы в мой files.txt
Zend_Loader_PluginLoader::setIncludeFileCache( APPLICATION_PATH . '/../data/files.txt');
Чтобы не парсить строку include_once '...'; я похачил (какой я гад все-таки, а!) Zend/Loader/PluginLoader.php
protected static function _appendIncFile($incFile) { if (!file_exists(self::$_includeFileCache)) { $file = ''; // раз изменение } else { $file = file_get_contents(self::$_includeFileCache); } if (!strstr($file, $incFile)) { $file .= "\n$incFile\n"; // два изменение file_put_contents(self::$_includeFileCache, $file); } }
Опять же, на продакшн это посылать не надо, так что ничего страшного. А дома — ну дома, пусть будет дома. Надо будет обновить ZF, а потом снова пересобрать HotPlug.php — ну похачу еще разок, или там сделаю все по человечески как-нибудь.
Теперь я еще побегал по сайту, собирая plugin-ы, а потом пересобрал HotPlug.php. Моя жизнь изменилась к лучшему!
Теперь — расскажите о своих бест практицес для решения подобной проблемы :) И давайте сделаем нормальное, разумное ОО-решение без лишних хаков?