Ga naar inhoud

Service container & bootstrap

Dit document beschrijft hoe de applicatie opstart (bootstrap) en hoe services worden aangemaakt en opgehaald via ServiceRegistry.


Bootstrap — _www/appconfig/bootstrap.php

bootstrap.php is de eerste stap bij elk HTTP-request en bij CLI-uitvoering. Het bestand wordt geladen door zowel _www/index.php:3, _www/api.php:3 (via require_once) als cli.php:15. _api/index.php:18 laadt het via het bare require-keyword (niet require_once).

Stap-voor-stap (_www/appconfig/bootstrap.php)

// bootstrap.php:8
require_once __DIR__ . '/../../vendor/autoload.php';
Laadt de Composer-autoloader (PSR-0 voor src/, alle files-entries).

// bootstrap.php:9
require_once __DIR__ . '/constants.php';
Laadt alle APP_*-constanten, AJAX_FUNCTION, database-constanten en omgevingsconstanten (via environment_constants.php). Zie Configuratie & environment.

// bootstrap.php:10
require_once APP_CORE_PATH . '/config/autoload_classes.php';
Registreert de legacy atx\-suffix-autoloader via spl_autoload_register. Zie Autoloading & namespaces.

// bootstrap.php:13–22
\Sentry\init([
    'dsn'                  => SENTRY_DSN,
    'environment'          => ENVIRONMENT,
    'release'              => require APP_PROJECT_DIR . '/.version.php',
    'max_value_length'     => 2048,
    'max_request_body_size'=> 'always',
    'traces_sample_rate'   => 1.0,
    'profiles_sample_rate' => 1.0,
    'enable_logs'          => is_prod(),
]);
Initialiseert Sentry met de DSN uit constants.php, de huidige omgeving en de releasetag. Tracing en profiling zijn op 100% ingesteld.

// bootstrap.php:24–35
register_shutdown_function(function() {
    $error = error_get_last();
    if ($error !== null && in_array($error['type'], [E_ERROR, E_CORE_ERROR, ...], true)) {
        \Sentry\captureMessage(...);
    }
    \Sentry\logger()->flush();
});
Een shutdown-handler vangt fatale PHP-fouten op en stuurt ze naar Sentry.

// bootstrap.php:37–44
if (!is_cli() && !(isset($_SESSION['username']) && $_SESSION['username'] === 'updating')) {
    \atx\SessionDAO::startSession();
    if (!isset($_SESSION['taal'])) {
        $_SESSION['taal'] = \atx\system\Language::DEFAULT_LOCALE;
    }
} else {
    $_SESSION['taal'] = \atx\system\Language::DEFAULT_LOCALE;
    $_SESSION['username'] = 'updating';
}
Start de sessie (tenzij CLI of een HTTP-request met $_SESSION['username'] === 'updating'). Stelt de standaardtaal in als er nog geen sessietaal is. De else-tak simuleert $_SESSION['taal'] en $_SESSION['username'] — dit geldt voor zowel CLI-uitvoering als voor HTTP-requests waarbij de sessie al op 'updating' staat.

// bootstrap.php:47–49
if (checkManage()) {
    ini_set('display_errors', '1');
}
Schakelt foutweergave in voor beheerder-requests (manage.atraxion.com).


ServiceRegistry — service-locator met League Container

_core/model/classes/atx/system/ServiceRegistry.php is de centrale dependency injection-laag. Het combineert een service-locator-patroon met een League Container als onderliggend IoC-systeem.

Structuur

// ServiceRegistry.php:12
final class ServiceRegistry
{
    private static $instance;      // singleton
    private $container;            // League\Container\Container
    private array $instances = []; // instantiecache
}

Initialisatie (eerste aanroep van ServiceRegistry::get())

Bij de eerste aanroep roept de private methode getInstance() (ServiceRegistry.php:176) new self() aan, wat de constructor (ServiceRegistry.php:29) triggert. Alle initialisatiestappen gebeuren in die constructor:

  1. Maak een League\Container\Container aan en stel defaultToShared() in (ServiceRegistry.php:31–32): alle services zijn standaard singletons.

  2. Laad service-providers uit _core/config/service-providers.php (ServiceRegistry.php:34–36):

foreach (require APP_CORE_PATH . '/config/service-providers.php' as $className) {
    $this->container->addServiceProvider(new $className);
}

De service-providers.php bevat 57 actieve service-providers (o.a. DoctrineServiceProvider, TwigServiceProvider, MailServiceProvider, ExchangeServiceProvider, EdiServiceProvider, …).

  1. Bij CLI-uitvoering: laad ook command-providers uit _core/config/command-providers.php (ServiceRegistry.php:38–41). Deze registreren Symfony Console-commands (o.a. BirthdayEmailQueueCommandProvider, OrderDocumentsCommandProvider, …). CacheWarmupCommand heeft geen eigen entry in dit bestand en wordt via een generieke provider geregistreerd (bijv. CommonCommandProvider).

  2. Delegeer onbekende services aan een fallback-container (ServiceRegistry.php:44–73): als een klasse niet via een service-provider geregistreerd is, maakt de delegate container gewoon new $id() aan en voert hydrateService() uit.

getInstance() zelf is alleen een singleton-bewaker: als self::$instance al bestaat, retourneert het die; anders roept het new self() aan en slaat het op (ServiceRegistry.php:176–183).

Ophalen van een service

$service = ServiceRegistry::get(SomeClass::class);

ServiceRegistry::get() (ServiceRegistry.php:88–98):

  1. Kijkt in de interne $instances-cache.
  2. Zo niet: roept createService($service, false) aan → $container->get($service).
  3. Roept hydrateService($instance) aan.
  4. Slaat op in $instances.

Hydratatie (hydrateService)

ServiceRegistry::hydrateService() (ServiceRegistry.php:188–197) zorgt voor automatische dependency-injectie:

  • Als de service HasDependencies implementeert: roept resolveDependencies() aan, die via getDependencyList() de afhankelijkheden opsomt en injecteert (via setter of property).
  • Als de service EntityManagerAware implementeert: injecteert de Doctrine EntityManager.

Aanmaken van een nieuwe (niet-gedeelde) instantie

$service = ServiceRegistry::createService(SomeClass::class); // new = true

createService() roept $container->getNew($service) aan (slaat niet op in de cache).

Handige statische snelkoppelingen

Methode Wat
ServiceRegistry::getEntityManager() Retourneert de Doctrine EntityManager via DoctrineService
ServiceRegistry::getRepository($fqcn) Retourneert een Doctrine-repository
ServiceRegistry::getEventManager() Retourneert de EventManager
ServiceRegistry::getSettings() Retourneert de SettingService
ServiceRegistry::getContainer() Retourneert de rauwe League\Container\Container

Service-providers

Elke service-provider implementeert League\Container\ServiceProvider\ServiceProviderInterface en registreert één of meer services in de container. Providers worden één keer geladen bij de eerste ServiceRegistry::get()-aanroep.

Voorbeelden van service-providers (_core/config/service-providers.php):

Provider Wat hij registreert
DoctrineServiceProvider DoctrineService, EntityManager
TwigServiceProvider Twig Environment
MailServiceProvider SendMailFactory, maildiensten
ExchangeServiceProvider Pay.nl exchange-controller
EdiServiceProvider EDI-orderverwerking
TranssmartServiceProvider Transsmart-verzendings-client
ArteelServiceProvider Arteel loyaliteits-client
LoyaltyServiceProvider Loyaliteitsmodule
OrderDocumentsServiceProvider Orderdocumenten + virusscan
BirthdayEmailQueueServiceProvider Verjaardags-e-mailwachtrij
VirusTotalServiceProvider VirusTotal-client

CLI-bootstrap — cli.php

cli.php deelt dezelfde bootstrap als de webapplicatie:

// cli.php:15
require_once __DIR__ . '/_www/appconfig/bootstrap.php';

Daarna haalt het de Symfony Console Application op uit de ServiceRegistry:

// cli.php:38
$app = ServiceRegistry::get(Application::class);
$app->setDispatcher($dispatcher);
$app->run();

De Application wordt geregistreerd door Atraxion\Container\ServiceProvider\ConsoleServiceProvider (één van de command-providers in _core/config/command-providers.php:4). Alle individuele commands worden door hun eigen command-provider-klasse aan de Application toegevoegd.

Een foutlistener (ConsoleEvents::ERROR) logt console-fouten via LogUpdateDAO (cli.php:21–36). De doeldatabase wordt bepaald door LogUpdateDAO::Table() (vermoedelijk atx_log, maar niet rechtstreeks in cli.php zichtbaar).


Samenvatting bootstrap-volgorde

graph TD
    A[index.php / api.php / _api/index.php / cli.php] --> B[bootstrap.php]
    B --> C[vendor/autoload.php]
    B --> D[constants.php + environment_constants.php]
    B --> E[autoload_classes.php — spl_autoload_register]
    B --> F[Sentry::init]
    B --> G[SessionDAO::startSession]
    C --> H[ServiceRegistry — eerste get]
    H --> I[League Container initialiseren]
    I --> J[service-providers.php — 57 actieve providers]
    I -->|CLI| K[command-providers.php — CLI commands]
    H --> L[delegate container — fallback new $id]

Zie ook: Tech stack · Autoloading & namespaces · Request lifecycle & routing · Configuratie & environment