OpenCart 2+ описание процесса загрузки приложения
- Подробности
- Категория: Opencart (OCstore)
- Обновлено 23.01.2024
Для того, чтобы хорошо разбираться в том, с чем работаешь, необходимо знать процесс "изнутри". В данной статье я распишу пошагово процесс работы ядра фреймворка или CMS (кому как нравится) OpenCart версии 2+. Тем более, что OpenCart построен с учетом концепции MVC (модель-вод-контроллер), а значит имеет довольно понятную структуру с которой удобно работать и удобно расширять. Чего пока не хватает - так это документации и русскоязычных статей в интернете.
Файл index.php
Файл находится в корне сайта.
Это точка входа для всего приложения. Все запросы к серверу проходят через этот файл, который подключает нужные библиотеки и координирует дальнейшее выполнение скрипта.
Рассмотрим этапы его работы.
- Устанавливается константа с версией OC
define('VERSION', '2.3.0.2.2');
- При наличии файла config.php в корневой папке, он подключается и устанавливает ряд констант, устанавливающих пути к служебным (system) и рабочим (catalog) папкам, а так же константы доступа к базе данных.
if (is_file('config.php')) { require_once('config.php'); }
- В случае отсутствия файла config.php в корневой папке, константа DIR_APPLICATION оказывается не установленной. Тогда отсылается заголовок на переадресацию в папку install, которая запускает процесс установки OC и завершает на этом этапе работу скрипта
if (!defined('DIR_APPLICATION')) { header('Location: install/index.php'); exit; }
- Если дошли до этой строки - конфигурация загружена, подключается файл из папки system/startup.php
require_once(DIR_SYSTEM . 'startup.php');
- Последняя строка файла index.php
start('catalog');
выполнится лишь после подключения файла 'startup.php', о чем ниже.
Файл system/startup.php
- Устанавливается протоколирование всех ошибок
error_reporting(E_ALL);
- Проверяется версия php, должна быть не ниже 5.4
if (version_compare(phpversion(), '5.4.0', '<') == true) { exit('PHP5.4+ Required'); }
- Устанавливается временная зона по-умолчанию
if (!ini_get('date.timezone')) { date_default_timezone_set('UTC'); }
- Далее устанавливаются значения суперглобального массива $_SERVER
$_SERVER['DOCUMENT_ROOT'] - содержит путь к корневой директории сервера, если скрипт выполняется в виртуальном хосте, в данном элементе указывается путь к корневой директории виртуального хоста.
$_SERVER['REQUEST_URI'] - содержит имя скрипта, начиная от корневой директории виртуального хоста и параметры. Например: /index.php?route=extension/feed/google_sitemapy
$_SERVER['HTTP_HOST'] - доменное имя сайта.
$_SERVER['HTTPS'] - сохраняет в массив признак защищенного соединения HTTPS
- Объявление функции modification(), которая используется для получения пути к
модифицированным файлам CMS (например при подключении дополнений).
function modification($filename) {…}
При этом в начале функции прописаны проверки на область видимости от куда выполняется скрипт – админка, папка install или catalog. Каждая из этих областей видимости определяет свои константы, на основе их и построены данные проверки.
В переменную $file сохраняется путь к файлу начиная от папки system/storage/modification. Если переданный функции файл там найден, то возвращается данный путь. Если нет – значит файл не модифицировался и возвращается тот же путь к файлу, который передан функции в качестве аргумента.
- Подключение файла autoload.php, которого нет по-умолчанию в OpenCart 2.3.
// Autoloader if (is_file(DIR_SYSTEM . '../../vendor/autoload.php')) { require_once(DIR_SYSTEM . '../../vendor/autoload.php'); }
В данном файле можно подключить сторонние автозагрузчики или что-то еще.
- Далее код автозагрузки системных классов OC.
Функция spl_autoload_register() при каждом запросе неизвестного класса вызывает функцию library(), которая ищет и загружает запрашиваемый класс из папки system/library или же system/storage/modification/system/library если есть модификация запрашиваемого файла класса, т.к. используется функция modification() для такой проверки.
- Подключается ряд важных системных классов, нужных для работы CMS из папки system/engine.
Экземпляры данных классов будут созданы чуть позже в главном файле фреймворка system/framework.php (описано ниже) и сохранены в реестр для доступа к ним из любого места приложения через загрузчик engine/loader.php.
Предварительно наличие файла проверяется среди модифицированных с помощью функции modification()
require_once(modification(DIR_SYSTEM . 'engine/action.php')); …
- Из папки system/helper подключается несколько файлов содержащие функции-помошники
- general.php – Файл для обеспечения безопастности. Содержит функцию token() для генерации случайной строки (токена), функцию hash_equals() для сравнения хеш-строк
- utf8.php – устанавливает кодировку
- json.php – кодирует/раскодирует данные в формате json
- В конце данного файла объявление функции start(), которая должна подключить главный файл фреймворка- framework.php, реализующий дальнейшую работу приложения согласно модели MVC.
function start($application_config) { require_once(DIR_SYSTEM . 'framework.php'); }
После этого, выполнение скрипта возвращается к файлу index.php в котором выполняется последняя строка, запускающая подключение к фалу framework.php
В качестве аргумента, функции передается указатель на рабочий каталог- backend(admin) или frontend(catalog)
start('catalog');
Файл system/framework.php
Не забываем, что все классы в данном файле загружаются с помощью автозагрузчика, с проверкой на модификацию (сначала поиск в папке system/storage/modification/system/…)
- Первой строкой создается объект класса Registry. Файл system\engine\registry.php
$registry = new Registry();
Это самый важный объект приложения. Данный класс используется для хранения(получения, проверки на наличие) в массиве $data служебных объектов (Config, Event, Loader, Request, Response, Database, Session, Cache, Url, Language, Document). Далее в данном файле (framework.php) создаются эти самые служебные объекты и с помощью метода set() сохраняются в закрытой переменной $data класса Registry. Так же, многие из этих объектов, получают Registry в качестве аргумента конструктора, при их создании, для получения доступа из них к реестру, а из него к другим служебным объектам.
Пример.
Любой контроллер наследует от абстрактного класса Controller, который получает объект Registry при создании:
abstract class Controller { protected $registry; public function __construct($registry) { $this->registry = $registry; } ...
так же есть магические методы
public function __get($key) { return $this->registry->get($key); } public function __set($key, $value) { $this->registry->set($key, $value); }
Таким образом, при запросе свойства класса, которое у него отсутствует, идет передача вызова на объект Registry (который был передан в protected $registry).
Таким образом, при вызове в контроллере
$this->load->model('...');
после того, как не будет найдено свойство load контроллера, будет вызвано $registry->load.
То есть, объект Registry используется, в последствии, многими другими объектами для получения доступа к хранимым в нем служебным объектам (их свойствам и методам).
- Config. Файл system\library\config.php
Данный объект собирает массив настроек (private $data) для различных частей приложения. Он имеет три метода для сохранения, получения и проверки наличия определенной настройки.
Сразу после создания объекта, вызывается метод загрузки конфигурации из файла system\config\default.php
$config->load('default');
в данном файле хранятся настройки по умолчанию касающиеся БД, эл.почты, вывода ошибок, сессии, кэша, кодировки и др. (см. файл system\config\default.php)
Следующей строкой в массив настроек добавляются настройки из файла
- system\config\catalog.php (если фреймворк запущен из frontend – в index.php строкой start('catalog');)
или
- system\config\admin.php (для админки)
Затем, экземпляр класса Config сохраняется в массив $data класса Registry
Данные одного из этих файлов (который загрузится) дополняет и где-то меняет настройки по-умолчанию из файла default.php
- Event. Файл system\engine\event.php
Объект данного класса работает с событиями OC. События используются для вставки своего кода в существующий на определенном этапе его выполнения.
Строкой
$event = new Event($registry);
передается объект Registry в конструктор класса Event, для использования в методе trigger() при вызове нужных функций.
- Event Register. Регистрируюся все события, которые были получены объектом Config и сохранены в массиве action_event. Они записываются в массив data[$trigger] объекта Event.
if ($config->has('action_event')) { foreach ($config->get('action_event') as $key => $value) { $event->register($key, new Action($value)); } }
- Loader. Файл system\engine\loader.php
Объект позволяет загрузить нужные классы controller, model, view, language, а так же служебные классы из папок library, helper, config
Экземпляр класса Loader сохраняется в массив объекта Registry с ключем 'load', поэтому доступен как load.
Пример загрузки из класса контроллера:
$this->load->model('account/custom_field');
Делает он это, опять таки, с помощью переданного ему, в качестве аргумента для конструктора, объекта Registry, хранящему все служебные объекты с их свойствами (читать выше).
- Request (запрос браузера на сервер). Файл system\library\request.php
Данный класс сохраняет в своих свойствах элементы глобальных массивов (_GET _POST _REQUEST _COOKIE _FILES _SERVER), пропуская, предварительно, ключи и значения через метод clean(), которая преобразует специальные символы в HTML-сущности.
С помощью данного класса, обращаться к элементам суперглобальных массивов можно так:
$this->request->post['address_id']
в результате будет получено значение массива $_POST по ключу 'address_id'.
- Response (ответ сервера браузеру). Файл system\library\response.php
После создания объекта Response, следующей строкой, создается заголовок с кодировкой по-умолчанию.
$response->addHeader('Content-Type: text/html; charset=utf-8');
В данный объект можно сохранять свои заголовки методом addHeader($header), которые будут добавляться в private $headers.
Есть так же метод для переадресации redirect($url, $status = 302), метод для сжатия страницы сервером перед отправкой браузеру compress().
Метод setOutput($output) вызывается во многих контроллерах для сохранения страницы (после формирования файла представления) в private $output объекта Response, для последующего вывода:
$this->response->setOutput($this->load->view('checkout/checkout', $data));
Метод output() – передает браузеру для вывода заголовки и все содержимое страницы. Он выполняется в самом конце файла framework.php
- DB. Класс для работы с базой данных. Файл system\library\db.php
При создании объекта проверяется существование класса, отвечающего за работу с указанным расширением (указывается в конфиге, в массиве «db_type» - например mysqli). Если класс найден, на основании других данных из конфига создается соединение с БД.
Данный объект используется в моделях:
$this->db->query("INSERT INTO…
- Session. Класс для работы с сессиями. Файл system\library\session.php
В настройках проверяется нужно ли сразу стартовать сессию (для каталога admin стартует сразу)
В конструкторе класса Session есть строка определяющая параметры для куки сессии
session_set_cookie_params(0, '/');
ноль значит, что при закрытии браузера куки удалятся (например товары из корзины). Поэтому можно установить свое значение в секундах.
Так же с сессиями работают:
- класс Session\DB из файла system\library\session\db.php который сохраняет определенные данные в БД устанавливая в одно из полей идентификатор текущей сессии (нужно предварительно раскомментировать код создания таблицы session в начале файла);
- класс Session\File из файла system\library\session\file.php который создает файл имеющий название '/sess_' + идентификатор сессии и сохраняет в него данные.
Идентификатор сессии, данные классы, получают из класса Session с помощью метода getId()
- Cache. Класс для работы с кэшем. Файл system\library\cache.php
Данный класс подключает один из файлов, который определяет место хранения кэша. Место хранения передается конструктору при создании объекта в качестве аргумента $adaptor.
Возможные способы хранения кэша:
- APC (system\library\cache\apc.php)
- Файл (system\library\cache\file.php)
- Оперативная память (system\library\cache\mem.php)
По умолчанию в настройках стоит хранение кэша в файле и срок 3600 (1 час)
Методы работы с кэшем get() set() delete(), в зависимости от указанного места хранения, перенаправляются к одному из соответствующих классов.
- Url. Объект данного класса генерирует URL (ссылки). Файл system\library\url.php
Методу link() передаются параметры из которых он формирует и возвращает полный URL.
$data['customlink'] = $this->url->link('custom/custom');
- Language. Файл подключающий языковые файлы и получающий из них запрашиваемую строку. Файл system\library\language.php
Язык используемый по-умолчанию, прописывается в файле конфигурации system\config\default.php:
$_['language_default'] = 'en-gb';
Метод load данного класса, принимает, в качестве первого аргумента путь к файлу перевода, который подключается:
$this->load->language('checkout/checkout');
В данной строке, load является не методом класса language, а объектом класса Loader, который содержит метод language(), вызывающий (через регистратор объектов- Registry) метод load уже объекта language, передавая ему параметры в скобках.
Получение данных из массива подключаемого языкового файла осуществляется с помощью метода get():
$this->language->get('heading_title')
- Document. Объект данного класса содержит данные (собирает и возвращает) выводимые в основном в шапке HTML документа, такие как заголовок страницы, описание, ключевые слова, стили, js скрипты и др. Файл system\library\document.php
Методы класса состоят из get – получающих значение элемента и set(add) устанавливающих значение или массив значений. Таким образом можно, например добавить свой файл стилей в массив:
$this->document->addStyle('catalog/view/mystyle.css');
далее, в контроллере получаем все стили и передаем в представление (header.tpl), где в цикле они извлекаются.
- Config Autoload. Производится автозагрузка конфигурационных файлов, которые можно добавить в system\config\admin.php
Данные файлы нужно указать в system\config\default.php в массиве
// Autoload Configs $_['config_autoload'] = array();
- Language Autoload. Загружает автоматически языковые файлы, указанные в system\config\default.php
$_['language_autoload'] = array('en-gb');
- Library Autoload. Позволяет автоматически загрузить указанные классы библиотек. Например для frontend загружается дополнительно библиотека openbay, указанная в файле system\config\catalog.php
$_['library_autoload'] = array( 'openbay' );
- Model Autoload. Позволяет автоматически загрузить модель из списка указанном в конфигурационных файлах, например в system\config\default.php
$_['model_autoload'] = array();
- Front Controller. Объект используется для выполнения переданных ему действий (вызов определенного метода нужного контроллера) до выполнения метода контроллера переданного в route (полученного из URL).
$controller = new Front($registry);
Создается объект класса Front хранящий обработчики событий в массиве (private $pre_action) и вызывающий для их обработки метод execute объекта Action. Файл system\engine\front.php
Метод addPreAction() формирует массив объектов класса Action для последующего выполнения.
Метод dispatch() сначала выполняет все элементы из массива $pre_action, а затем переданный в качестве аргумента Action $action. Для выполнения он передает объекты методу execute(), где вызывается одноименный метод объекта Action, который и выполняет метод (по-умолчанию index) переданного ему контроллера.
- Pre Actions. В данном блоке, в фронт-контроллер устанавливаются действия, которые должны выполняться до выполнения любых других действий.
if ($config->has('action_pre_action')) { foreach ($config->get('action_pre_action') as $value) { $controller->addPreAction(new Action($value)); } }
В конфигурационном файле проверяется наличие действий (контроллеров), методы (index) которых нужно выполнить сразу же. Например в system\config\catalog.php:
$_['action_pre_action'] = array( 'startup/session', 'startup/startup', 'startup/error', 'startup/event', 'startup/maintenance', 'startup/'.$seo_type );
В цикле, данные массива передаются в метод addPreAction() объекта Front, где они сохраняются в массив $pre_action для дальнейшего выполнения.
- Dispatch. Данная строка кода:
$controller->dispatch(new Action($config->get('action_router')), new Action($config->get('action_error')));
отправляет на выполнение объект Action с аргументом из конфигурационного файла (startup/router.php) и вторым аргументом объект с указанием файла который должен обработать ошибки, если будут. В данном случае это «error/not_found»
То есть вызов метода $controller->dispatch() в OC начинает процесс работы контроллера/метода указанных в route в данный момент (из URL), перед этим выполнив методы (index по-умолчанию если не указаны другие) данных классов (из 'action_pre_action'):
'startup/session', 'startup/startup', 'startup/error', 'startup/event', 'startup/maintenance', 'startup/'.$seo_type
Указанные контроллеры находятся в папке catalog\controller\startup
Выполнив предварительные методы из списка, последним в списке вызывается catalog\controller\startup\router.php
он из URL отбирает только значение «route»- контроллер/действие и создает объект класса Action передав ему их в качестве аргумента для конструктора. Далее вызывается метод execute() объекта Action, который и выполняет переданные ему контроллер/метод. Остальные GET параметры используются уже в этих контроллерах.
- Далее
$response->setCompression($config->get('config_compression'));
Считывается из конфигурации и сохраняется в свойстве объекта Response уровень сжатия для функции gzencode(). По умолчанию – 0 (без сжатия).
Завершающая строка выполняемая фреймворком
$response->output();
Вызывает метод output() объекта Response, в котором производится вызов метода сжатия compress() если до этого был выбран уровень сжатия, затем отсылаются заголовки и выводится контент строкой echo $output;
Стоит еще вспомнить класс Action, который был подключен в файле system/startup.php. Файл system\engine\action.php
Объект его создается в основном в данном файле (framework.php) когда нужно выполнить какой-то метод определенного контроллера. При создании его объекта, конструктору передается $route – путь к выполняемому контроллеру/метод. Если метод не передан – будет выполняться метод index.
В файле framework.php объект класса Action передается
- для выполнения методов служебных объектов (предзагрузки) массива 'action_pre_action' файла конфигурации
- методу dispatch() объекта Front Controller, который сначала в цикле выполняет действия добавленные в предзагрузку и затем действие(контроллер) startup/router, который разбирает текущий URL и выполняет указанный там контроллер/действие.