Точки расширения модуля¶
Основные точки расширения, к которым подключается модуль: контейнер DI, потоковое промежуточное программное обеспечение, задачи cron, миграции версий и типизированные события. Чтобы создать модуль, смотрите Создание модуля; для загрузки/жизненного цикла смотрите Жизненный цикл модуля.
Оформление контейнеров и сервизов DI¶
Сервисы регистрируются в boot() через ServiceContainer. Контейнер поддерживает:
set(id, factory)— отложенный синглтон с помощью вызываемого или прямого значенияfactory(id, callable)— новый экземпляр для каждогоget()decorate(id, callable, priority)— завершение существующей службы
// Decorate a service (adds behaviour around the original)
$container->decorate('stream.encoder', function (mixed $inner, ServiceContainer $c): MyEncoder {
return new MyEncoder($inner, $c->get('settings'));
}, priority: 20);
Декораторы объединены в цепочки по приоритету (самый высокий и самый внешний). Защищенные сервисы
(db, settings, config, auth) не удается оформить — любая попытка приводит к результату RuntimeException.
Соответствие требованиям стандарта PSR-11¶
ServiceContainer реализует ContainerInterface:
public function get(string $id): mixed; // throws NotFoundException if missing
public function has(string $id): bool;
NotFoundException реализует NotFoundExceptionInterface extends ContainerExceptionInterface.
События PSR-14¶
Модули подписываются на типизированные события с помощью атрибута getEventSubscribers() или #[ListensTo]. Это описано в полном объеме — диспетчеризация, регистрация слушателей, приоритеты, события, которые можно остановить, и встроенный каталог событий - на специальной странице Система событий.
Потоковое промежуточное программное обеспечение¶
Модули могут внедрять промежуточное программное обеспечение в потоковый конвейер, реализуя
StreamMiddlewareProviderInterface (отдельно от ModuleInterface):
class MyStreamMiddleware implements StreamMiddlewareInterface {
public function getPriority(): int {
return 50;
}
public function handle(StreamContext $ctx, callable $next): StreamContext {
// before — read or set attributes
$ctx->set('my.key', 'value');
$ctx = $next($ctx);
// after
return $ctx;
}
}
StreamContext - это набор атрибутов (get, set, has, abort, isAborted). StreamPipeline
выполняет промежуточное программное обеспечение, отсортированное по убыванию getPriority().
Приоритеты трубопровода¶
| Диапазон | Владелец |
|---|---|
80–100 |
Ядро (авторизация, разрешение, ограничение подключения) |
0–79 |
Модули |
Зарезервированные слоты на панели навигации¶
| Родительский узел | Гнезда для модулей |
|---|---|
management.service_setup |
order ≥ 60 |
management.logs |
order ≥ 170 |
Задача Cron¶
Логика Cron (MyCron.php) — только бизнес-логика, без подключения к интерфейсу командной строки.
Обертка от CronJob (MyCronJob.php) — реализует CommandInterface, использует CronTrait:
class MyCronJob implements CommandInterface {
use CronTrait;
public function getName(): string { return 'cron:my_task'; }
public function getDescription(): string { return 'Cron: my task'; }
public function execute(array $rArgs): int {
if (!$this->assertRunAsXcVm()) {
return 1;
}
require INCLUDES_PATH . 'admin.php';
require_once __DIR__ . '/MyCron.php';
$this->initCron('XC_VM[MyTask]');
MyCron::run();
return 0;
}
}
Регистрация в модуле:
public function registerCommands(CommandRegistry $registry): void {
$registry->register(new MyCronJob());
}
Объявите запись crontab, переопределив getCronEntries() в классе module:
ModuleLoader::collectCronEntries() объединяет записи всех модулей и StartupCommand /
StatusCommand автоматически записывайте их в системный crontab — никаких изменений в основных файлах не требуется.
Формат: ключ = выражение cron, значение = имя консольной команды, зарегистрированное с помощью registerCommands().
Версионные миграции (MigratableInterface)¶
Два механизма, оба аддитивные. файловая схема, описанный в разделе Структура каталогов модулей (
database.sqlмастер +database_drop.sqlразборка +migrations/<semver>.sqlдельты) используется по умолчанию для обычный DDL/seed.MigratableInterfaceниже приведен программный путь для обновления шаги, требующие логики PHP (повторное заполнение данных, условные изменения). Модуль может использовать один из них или оба;ModuleManager::updateModule()сначала запускает файл delta, затем вызываемые миграции.
Модули, для обновления которых требуется PHP логическая реализация MigratableInterface:
namespace XcVm\Module\MyModule;
use BaseModule;
use MigratableInterface;
use ServiceContainer;
class MyModuleModule extends BaseModule implements MigratableInterface {
public function getMigrations(): array {
return [
'1.1.0' => function (): void {
// runs when upgrading from any version < 1.1.0 to >= 1.1.0
global $db;
$db->query("ALTER TABLE xc_my_table ADD COLUMN new_col INT DEFAULT 0");
},
'1.2.0' => function (): void {
// runs when upgrading from < 1.2.0 to >= 1.2.0
},
];
}
}
ModuleManager::updateModule() считывает installed_version из хранилища переопределений, фильтрует
сопоставляет только записи > fromVersion && <= toVersion, сортирует по полу и запускает каждую из них.
вызываемый в своей собственной транзакции базы данных. installModule() записи installed_version после
успешная установка; uninstallModule() удаляет ее.
Key rules:
- Ключи - это полустрочные строки (
'1.1.0','2.0.0') —version_compareиспользуется упорядочение - Каждая миграция выполняется в рамках своей собственной транзакции — сбой откатывает только этот шаг
BaseModuleпредоставляет значение по умолчаниюgetMigrations(): array { return []; }, поэтому реализацияMigratableInterfaceявляется необязательным