Открываю цикл рассказов о второй версии cogear следующим материалом.Рабочий процесс движка также называют итерацией. Это процесс, который происходит во времени от вашего запроса к движку до получения ответа виде страницы сайта в браузере. Именно на него и тратятся ресурсы системы, и именно по нему и меряется производительность любого движка.
Весь жизненный цикл одной итерации cogear² расположен в главном файле — index.php.
Установка постоянных
<?php
/**
* Cogear — simple and fast site management system.
*
* Created by Dmitriy Belyaev at the year of 2011.
*
* "Life is like the development. But the last one never ends." — Dmitriy Belyaev
*/
// Version
define('COGEAR', '2.0');
// Constants
define('DS', DIRECTORY_SEPARATOR);
define('PS', PATH_SEPARATOR);
define('EXT', '.php');
define('ROOT', realpath(dirname(__FILE__)));
define('GEARS_FOLDER', 'gears');
// Core gears
define('ENGINE', ROOT . DS . 'engine');
// Gears for all sites
define('GEARS', ROOT . DS . 'gears');
define('SITES', ROOT . DS . 'sites');
define('DEFAULT_SITE', SITES . DS . 'default');
define('PHP_FILE_PREFIX', '<?php ' . "\n");
define('IGNITE', time());
// Define error reporting level
error_reporting(E_ALL | E_STRICT);
Для начала следует определить все пути и задать важные константы.Автозагрузка
Еще пользуетесь прожорливыми функциями include и require? Тогда мы идем к вам.В cogear² все классы загружаются автоматически при использовании правил и наименования пространства имен.
/**
* Autoload
*
* @param $class Class name.
* @return boolean
*/
function autoload($class) {
$filename = str_replace('_', DS, $class);
if ($path = find($filename.EXT)) {
include $path;
return TRUE;
}
return FALSE;
}
// Register with autoload
spl_autoload_register('autoload');
Помним, что include внутри автозагрузчика оптимизирован в ядре PHP > 5.2, и потребляет много меньше ресурсов.Слоеный пирог
Задумка данной концепции состоит в том, чтобы разделить ядро системы (/engine), шестеренки для всех сайтов (/gears) и шестеренки для текущего сайта (/sites/адрес_сайта/gears).Когда автозагрузчик ищет класс, то он обходит папки в обратной последовательности. Таким образом вы можете переназначить шестеренку ядра только для определенного сайта или же ставить и обновлять шестеренки для всех сайтов сразу.
/**
* Search for file — layerd pancake ideology
*
* @param string $file
* @return string|array
*/
function find($file) {
$paths = array(
ENGINE . DS . $file,
ENGINE . DS . 'Core' . DS . $file,
GEARS . DS . $file,
);
defined('SITE') && $paths[] = SITE . DS . GEARS_FOLDER . DS . $file;
$result = array();
while ($path = array_pop($paths)) {
if (strpos($path, '*') !== FALSE && $files = glob($path)) {
foreach ($files as $file) {
$result[str_replace($path, '', $file)] = $file;
}
} elseif (file_exists($path)) {
return $path;
}
}
return $result ? $result : FALSE;
}
Скромный дебаг
Грешно, каюсь, но простяцкий дебаг полезен везде./**
* Simple debug
*
* @param mixed $data
*/
function debug() {
echo '<pre>';
$args = func_get_args();
call_user_func_array('var_dump', $args);
echo '</pre>';
}
Поехали!
Гагарин с нами. Запускаем cogear².$cogear = Cogear::getInstance();
$cogear->request = new Request();
// Set host
$host = $cogear->request->get('HTTP_HOST');
// Defince site folder
// Check if main
if (substr_count($host, '.') > 1) {
if (!is_dir(SITES . DS . $host)) {
list($subdomain, $host) = preg_split('#[\.]+#', $host, 2, PREG_SPLIT_NO_EMPTY);
define('SUBDOMAIN', $subdomain);
}
}
defined('SITE') OR is_dir(SITES . DS . $host) && define('SITE', SITES . DS . $host) OR define('SITE', DEFAULT_SITE);
define('SITE_GEARS', SITE . DS . GEARS_FOLDER);
Инициализируем базовый класс запроса. Определяем:
- хост,
- поддомен, если он есть,
- текущий сайт,
- папку с шестеренками сайта.
$cogear->config = new Config(SITE . DS . 'settings' . EXT);
define('DEVELOPMENT', $cogear->config->development);
Тут же читаем конфиг сайт (класс загружается сам). Здесь стоит обратить внимание, что классы ядра расположены в папке /engine/Core, но могут в своем название префикс Core_ не носить. Это сделано специально для сокращения печатаемого кода.
Помним, что конфиги хранятся в виде обычного массива, а читать мы их можем следующим образом:
config('user.profile.avatar.size','64x64'); // последним можно значение по-умолчанию приткнуть
//Внутри шестеренки
$this->get('user.profile.avatar.size','64x64');
//Там где у нас нет прямого доступа к ядру, а функцию config мы не используем по религиозным убеждениям
$cogear = getInstance();
$cogear->get('user.profile.avatar.size','64x64');
//Или
cogear()->get('user.profile.avatar.size','64x64');
Писать в конфиги можно как строки, так и массивы и делается это одним способом://Внутри шестеренки
$this->set('user.profile.avatar.size','200x200');
//Вне шестеренки
cogear()->set('user.profile.avatar.size','200x200');
Обратите внимание, что точки — это показатели вложенности массива. В итоге подобной манипуляции с конфигом внтури его файла вы увидите такой код:…
'user' =>
array (
'profile' =>
array (
'avatar' =>
array (
'size' => '32x32',
),
),
),
…
Едем дальше. Еще немного констант.
$folder = basename(ROOT);
if (!in_array($folder, array($host,'www', 'public_html', 'htdocs', SITE))) {
define('SUBDIR', $folder);
$host .= '/' . SUBDIR;
}
if (($port = $cogear->request->get('SERVER_PORT')) != 80) {
$host .= ':' . $port;
}
define('SITE_URL', $host);
// Define uploads folder
defined('UPLOADS') OR define('UPLOADS', SITE . DS . 'uploads');
Наконец-то корректно определение поддиректории и порта сайта. Можно вешать сайты на IP-шники и класть в подпапки.Обратите внимание, все константы можно изменить в одном файле под любой нестандартный конфиг. Например, можно иметь одно ядро для всех сайтов на сервере и хранить его отдельно, оставив лишь алиас(ссылку в файловой системе).
Далее грузим кеш (системный и обычный), запускаем роутер, харвестр файлов JS и СSS, класс ответа и сессии. Одним словом то, без чего нельзя обойтись.
$cogear->system_cache = new Cache(array('adapter' => 'Cache_Adapter_File', 'path' => SITE . DS . 'cache' . DS . 'system', 'enabled' => !DEVELOPMENT));
$cogear->cache = $cogear->config->cache ? new Cache($cogear->config->cache) : $cogear->system_cache;
if (!$options = $cogear->config->cookies) {
$options = array(
'name' => 'session',
'cookie_domain' => '.' . SITE_URL,
);
}
$cogear->router = new Router();
$cogear->assets = new Assets();
$cogear->response = new Response();
$cogear->session = Session::factory('session', $options);
Системный кеш отличается от обычного тем, что его можно не стирать, т.к. он используется как постоянное хранилище системных данных.Грузим конфиг настроек.
// Load current site settings if file exists
$cogear->config->load(SITE.DS.'config'.EXT);
Да, да, у вас не двоится в глазах. В settings.php хранятся базовые настройки, такие как соединение с БД. После установки движка на домен он заливается бетоном (права 0644), чтобы никто не мог напортачить, положив сайт.Настройки же шестеренок дампятся в config.php, таким образом вы можете легко переносить настройки с сайта на сайт.
Финал. Грузим шестеренки. Цепляем события и сохраняет отработанный цикл ядра (сохранение переменых в файл, если таковые были добавлены за итерацию).
// Load gears
$cogear->loadGears();
event('ignite');
event('done');
$cogear->save();
event('exit'); Вуаля. Стоит сказать, что на локальной машинe в комплекте Denwer PHP5.3 + eAccelerator движок потребляет сейчас около 150Кб за итерацию.
Количество включенных шестеренок оцените сами:
В следующий раз расскажу про систему хуков и событий.


Одно только настораживает, хранить конфиг в массиве… Редактирование конфигов вручную для многих приобретет зловещий смысл.
По-сути, хранить в виде массива в одном файле — оптимальное решение. Потому как конфиг всегда грузится, каждый раз. Пихать его в базу и перекешировать — зачем? И потом намного проще разобраться в массиве при хардкодинге, чем править таблички через phpMyAdmin.
Напишу об этом статью.
Читабельно?
<?php return array ( 'theme' => array ( 'logo' => '/theme/logo/logo.png', 'favicon' => '/theme/icon/favicon.ico', ), 'wysiwyg' => array ( 'editor' => 'redactor', ), 'cron' => array ( 'last_run' => 1311627030, ), 'user' => array ( 'profile' => array ( 'avatar' => array ( 'size' => '32x32', ), ), ), 'image' => array ( 'presets' => array ( 'avatar' => array ( 'small' => array ( 'size' => '24x24', 'actions' => array ( 0 => 'sizecrop', ), ), 'profile' => array ( 'size' => '32x32', 'actions' => array ( 0 => 'sizecrop', ), ), ), ), ), );Но все же ini'шники по мне более юзерфрендли.
$file OR $file = $this->file;
, раньше такого вроде не встречал.
Аналогично тому:
if(!$file){ $file = $this->file; }Если первая часть до OR будет положительной (не 0, FALSE, NULL), то до второй интерпретатор не дойдет.
Если же первая часть нулевая, то исполняется вторая проверка.