Перейти к содержанию

Подключение и регистрация сердечника

Как панель собирается при загрузке: один служебный контейнер, как он заполняется и как модули помещают свои маршруты, события, команды, записи cron и элементы навигационной панели в основные реестры.

Эта страница является сквозное повествование и потребительская сторона одним из основных реестров. То авторская сторона каждой точки расширения задокументирована в другом месте и связана с What lives elsewhere — this page does not repeat it.


Один контейнер

Все зависит от одного процесса в масштабах всего процесса ServiceContainer (src/Core/Container/ServiceContainer.php), синглтон PSR-11 ContainerInterface, полученный с помощью ServiceContainer::getInstance(). XC_Bootstrap::boot() создает его, заполняет его, и каждый последующий потребитель (XC_Bootstrap::getContainer(), модуль boot(), разрешение обработчика маршрута) считывает то же самое пример. Тесты сбрасывают его с помощью ServiceContainer::resetInstance().

Для основных служб есть значение автоматическое обнаружение поставщика услуг отсутствует: зарегистрирован канонический набор обязательно с помощью bootstrap (см. ниже), а модули добавляют свои собственные сервисы в свои boot(). То, что вы видите зарегистрированным, в точности соответствует коду set() — ничто не подключено с помощью сканирование условных обозначений или аннотаций.


Заполнение контейнера при загрузке

Это основа системы. XC_Bootstrap::boot() регистрация услуг осуществляется в два этапа.

Early (in boot() itself), before any subsystem loads:

Ключ Ценность Источник
context активное строковое значение BootContext src/bootstrap.php
options массив $options, переданный в boot() src/bootstrap.php
config ConfigReader::getAll() (проанализировано config.ini) src/bootstrap.php

Набор канонических служб — XC_Bootstrap::populateContainer() (выполняется для каждого контекста, за исключением Minimal, т.е. как только база данных будет запущена):

Ключ Ценность Записи
db дескриптор Database защищенный (см. ниже)
settings SettingsManager::getAll() защищенный
servers ServerRepository::getAll()
bouquets BouquetService::getAll()
categories CategoryService::getFromDatabase()
redis RedisManager::instance() только тогда, когда для этого контекста был загружен Redis
translator Translator::class
events новый экземпляр EventDispatcher также подключен к статическому фасаду (см. События)

Сразу после этого XC_Bootstrap::assertContainerHealth() жестко требует, чтобы events было присутствует (плюс db/redis, когда они были загружены) и выдает ошибку, если нет — гарантия громкий сбой этот более поздний код может предполагать, что эти службы существуют, вместо того, чтобы проверять каждую из них на нулевой уровень.

Записи context/config/settings/servers/bouquets/categories представляют собой простые данные снимки, сделанные при загрузке, а не на ленивых заводах — они считываются, а не пересчитываются, на протяжении всего срока службы устройства. запрос.


Ссылка на сервисный контейнер

Все установщики могут быть объединены в цепочку (return $this).

Метод Цель
set(id, value) Зарегистрируйте сервис. Значение Closure становится ленивая фабрика синглетов (сначала вызывается один раз для get(), затем кэшируется); все остальное — скаляр, объект или массив [Class, 'method'] — сохраняется как готовое значение.
factory(id, callable) Зарегистрируйте фабрику, которая возвращает значение новый экземпляр для каждого get() (без кэширования).
register(array) Скопируйте set() с карты id => value.
get(id) Разрешить службу. Возвращает кэшированный синглтон, если он присутствует; в противном случае запускает фабрику один раз, кэширует ее и применяет декораторы. Защищает от циклических фабрик и выдает CircularDependencyException; выдает NotFoundException для неизвестного идентификатора.
getOrDefault(id, default) Как get(), но возвращает default вместо того, чтобы выбрасывать при отсутствии.
has(id) / keys() / remove(id) / dump() Самоанализ и разрушение.
decorate(id, decorator, priority) Завершите работу с существующей службой, наивысший приоритет которой был применен последним. Запрещенный в защищенных службах ['db', 'settings', 'config', 'auth'] — выберите один из вариантов оформления. Смотрите Пункты расширения модуля.
tag(id, tag) / getTagged(tag) Сгруппируйте службы под одной меткой для пакетного поиска.

Метки в настоящее время не используются основной проводкой. tag()/getTagged() существует, но путь загрузки собирает вклады модулей путем проверки instanceof по списку загруженных модулей (см. bootAll), нет по тегу. Исходный документированный блок все еще описывает теги как механизм сбора данных для подписчиков/cron/маршрутов, который описывает предполагаемый дизайн, а не текущий код. Не полагайтесь на коллекцию на основе тегов, пока она не будет создана на самом деле. реализованный.

Resolving a [Class, 'method'] handler (used by the Router) goes through the container, so классы-обработчики разграничиваются при регистрации, а в противном случае возвращаются к new.


ModuleLoader::bootAll — организатор

После того, как ModuleLoader::loadAll() обнаружил, отфильтровал и топологически отсортированный отобрал модули (see Module Lifecycle), bootAll() is the single place that pushes each вклад модуля в основные реестры. Он проверяет каждое значение подинтерфейс на instanceof таким образом, модуль реализует только те перехватчики, которые ему нужны (ModuleInterface - это их совокупность).

Порядок для каждого модуля внутри bootAll(ServiceContainer $container, ?Router $router, ?StreamPipeline $pipeline):

  1. Сначала основная навигационная панель, один раз(new CoreNavbarProvider())->registerNavbar(...) перед любым модулем, поэтому узлы основного меню существуют как родительские.
  2. ServiceProviderInterfaceboot($container) затем registerEventSubscribers() — службы регистрируются до подключения слушателей этого модуля.
  3. StreamMiddlewareProviderInterfaceregisterStreamMiddleware($pipeline)только в том случае, если было передано значение $pipeline.
  4. RouteProviderInterfaceregisterRoutes($router)только в том случае, если было передано значение $router ($router !== null).
  5. NavbarProviderInterfaceregisterNavbar(...).

Два вклада равны отдельные проходы, не являющиеся частью bootAll:

  • registerAllCommands($registry) — Команды CLI, каждый модуль которых заключен в try / catch, поэтому один сломанный модуль не может заблокировать весь CLI.
  • collectCronEntries() — строки crontab, собранные из CronProviderInterface::getCronEntries(), используемые командами запуска/состояния.

Потому что маршруты и потоковое промежуточное программное обеспечение стробируются на основе необязательных аргументов $router/$pipeline, одно и то же значение bootAll() выполняет различную работу в зависимости от точки входа — интерфейс командной строки не передает ни, таким образом, к нему подключены только подписчики services + event (и безвредная навигационная панель).


События

EventDispatcher (src/Core/Events/EventDispatcher.php) - это синглтон со статическим фасадом. populateContainer() выполняет new EventDispatcher()EventDispatcher::setInstance($d)$container->set('events', $d), таким образом, запись контейнера events и статический EventDispatcher::dispatch()/listen() общий доступ к хранилищу прослушивателей один. Модули регистрируют прослушиватели во время шага 2 из bootAll, описанного выше, с помощью атрибута getEventSubscribers() или #[ListensTo].

Полная информация - регистрационные формы, приоритеты, мероприятия, которые можно отменить, встроенный каталог мероприятий — приведена ниже. в системе событий; на этой странице описывается только где в последовательности загрузки прослушиватели подключись к сети.


Регистрация команд CLI

Путь к интерфейсу CLI (src/console.php) создает свой набор команд в два этапа:

  1. Автоматическое обнаружение ядра. new CommandRegistry(), затем глобус Cli/Commands/*.php и Cli/CronJobs/*.php, сопоставьте каждый каталог с его пространством имен и с помощью отражения register() каждый неабстрактный класс, реализующий CommandInterface. Таким образом, добавление основной команды - это просто удаление класса в одном из этих каталогов — никакой ручной регистрации.
  2. Команды модуля. ModuleLoader::registerAllCommands($registry) вызывает каждый CommandProviderInterface::registerCommands().

CommandRegistry (src/Cli/CommandRegistry.php) - это простая карта name → CommandInterface: register(), dispatch($argv) (обрабатывает --list/--help, группирует справку по префиксу group: в название команды), get($name), getAll().


Сквозная загрузка — администрирование (web)

src/Public/index.php:

  1. XC_Bootstrap::boot(BootContext::Admin) — создает контейнер; устанавливает context/options/config; загружает константы; выполняет проверку флуда/хостинга; bootAdmin() (сессия, база данных, LegacyInitializer, Redis, API администратора/реселлера, транслятор, обработчик завершения работы, константы состояния); populateContainer(); assertContainerHealth().
  2. Router::getInstance(), затем require основные файлы маршрутов routes/{scope}.php (+ routes/api.php).
  3. Блок загрузки модуля: router->beginModuleRegistration()new ModuleLoader; loadAll(); bootAll($container, $router)router->endModuleRegistration()drainRouteCollisions(). Режим регистрации модуля выполняет выигрывают основные маршруты по любому маршруту модуля с одинаковым путем; коллизии фиксируются, а не перезаписываются автоматически.
  4. Router::dispatch() / dispatchApi() обрабатывает запрос, разрешая [Class, 'method'] обработчики через контейнер.

Смотрите Контексты начальной загрузки, чтобы узнать, какие именно подсистемы инициализируются в каждом контексте, и Обработка HTTP-запросов для API маршрутизатора и диспетчеризации.


Сквозная загрузка — CLI

src/console.php:

  1. require bootstrap.php; XC_Bootstrap::boot(BootContext::Cli) — DB, LegacyInitializer, необязательно Redis, заголовок процесса, затем такой же populateContainer() (таким образом, events и friends также существуют в CLI).
  2. new CommandRegistry(); автоматическое обнаружение ядра Cli/Commands + Cli/CronJobs (глобус + отражение) → register().
  3. new ModuleLoader; loadAll(); registerAllCommands($registry) (команды модуля, до bootAll), затем bootAll(getContainer()) без маршрутизатора и трубопровода — таким образом, маршруты и потоковое промежуточное программное обеспечение пропускаются; подключаются только службы модуля + подписчики событий.
  4. registry->dispatch($argv) выполняет запрошенную команду.

Что живет в другом месте

Чтобы избежать дублирования, информация об авторе и каждой подсистеме размещается на отдельных страницах:

Тема Страница
Which subsystems each context initialises; boot() options; idempotency Контексты начальной загрузки
Формы регистрации на мероприятия, приоритеты, мероприятия, которые можно отменить, каталог мероприятий Система событий
API маршрутизатора, begin/endModuleRegistration, диспетчеризация, разрешение обработчика Обработка HTTP-запросов
Конструктор элементов навигационной панели, правила видимости, рендеринг Рендеринг навигационной панели
Обнаружение модулей, фильтрация env, топосортировка, включение/ выключение, установка / обновление Жизненный цикл модуля
DI—оформление, потоковое промежуточное программное обеспечение, cron, миграции - хуки автора модуля Точки расширения модуля
Написание модуля (манифест, контракт класса, макет каталога) Разработка модуля

Связанные файлы

Файл Роль
src/Core/Container/ServiceContainer.php Контейнер DI: set/factory/get/decorate/tag
src/bootstrap.php XC_Bootstrap::boot, populateContainer, assertContainerHealth
src/Core/Module/ModuleLoader.php bootAll, registerAllCommands, collectCronEntries
src/Core/Events/EventDispatcher.php Диспетчер событий + статический фасад, объединенный в events
src/Cli/CommandRegistry.php Карта команд CLI + dispatch
src/console.php Точка входа в интерфейс командной строки: автоматическое обнаружение основной команды + загрузка модуля
src/Public/index.php Веб-точка входа: файлы маршрута + загрузочный блок модуля