Для ускорения 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. Моя жизнь изменилась к лучшему!
Теперь — расскажите о своих бест практицес для решения подобной проблемы :) И давайте сделаем нормальное, разумное ОО-решение без лишних хаков?
