major update

This commit is contained in:
2026-03-04 15:56:58 +01:00
parent 16759a2732
commit 8f4dd5840d
478 changed files with 24313 additions and 8201 deletions

View File

@@ -2,10 +2,10 @@
namespace MintyPHP\Http;
use MintyPHP\Http\RequestContext;
use MintyPHP\Service\Audit\ApiAuditService;
use MintyPHP\Service\Security\RateLimiterService;
use MintyPHP\Service\Security\SecurityServicesFactory;
use MintyPHP\Service\Settings\SettingGateway;
use MintyPHP\Service\Settings\SettingServicesFactory;
class ApiBootstrap
{
@@ -18,10 +18,26 @@ class ApiBootstrap
private static bool $initialized = false;
private static bool $shutdownRegistered = false;
private static ?SecurityServicesFactory $securityServicesFactory = null;
private static ?RateLimiterService $rateLimiterService = null;
private static ?SettingServicesFactory $settingServicesFactory = null;
private static ?SettingGateway $settingGateway = null;
/** @var (callable(): ApiAuditService)|null */
private static $apiAuditServiceResolver = null;
/** @var (callable(): SettingGateway)|null */
private static $settingGatewayResolver = null;
/** @var (callable(): RateLimiterService)|null */
private static $rateLimiterServiceResolver = null;
/** @var (callable(): ApiSystemAuditReporter)|null */
private static $apiSystemAuditReporterResolver = null;
public static function configure(
callable $apiAuditServiceResolver,
callable $settingGatewayResolver,
callable $rateLimiterServiceResolver,
callable $apiSystemAuditReporterResolver
): void {
self::$apiAuditServiceResolver = $apiAuditServiceResolver;
self::$settingGatewayResolver = $settingGatewayResolver;
self::$rateLimiterServiceResolver = $rateLimiterServiceResolver;
self::$apiSystemAuditReporterResolver = $apiSystemAuditReporterResolver;
}
/**
* Initialize the API request context.
@@ -40,8 +56,16 @@ class ApiBootstrap
define('MINTY_ALLOW_OUTPUT', true);
}
RequestContext::start([
'channel' => 'api',
'method' => strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')),
'path' => (string) parse_url((string) ($_SERVER['REQUEST_URI'] ?? ''), PHP_URL_PATH),
]);
header('X-Request-Id: ' . RequestContext::requestHeaderValue());
self::registerShutdownHandler();
\auditServicesFactory()->createApiAuditService()->startRequestContext();
self::apiAuditService()->startRequestContext();
self::startSystemAuditReporter();
self::setCorsHeaders();
if (strtoupper($_SERVER['REQUEST_METHOD'] ?? '') === 'OPTIONS') {
@@ -96,7 +120,8 @@ class ApiBootstrap
if ($statusCode <= 0) {
$statusCode = 200;
}
\auditServicesFactory()->createApiAuditService()->finish($statusCode);
self::apiAuditService()->finish($statusCode);
self::finishSystemAuditReporter($statusCode);
return;
}
@@ -107,7 +132,8 @@ class ApiBootstrap
if ($statusCode <= 0) {
$statusCode = 200;
}
\auditServicesFactory()->createApiAuditService()->finish($statusCode);
self::apiAuditService()->finish($statusCode);
self::finishSystemAuditReporter($statusCode);
return;
}
@@ -115,7 +141,8 @@ class ApiBootstrap
if ($statusCode < 400) {
$statusCode = 500;
}
\auditServicesFactory()->createApiAuditService()->finish($statusCode, 'fatal_error');
self::apiAuditService()->finish($statusCode, 'fatal_error');
self::finishSystemAuditReporter($statusCode, 'fatal_error');
});
}
@@ -208,29 +235,53 @@ class ApiBootstrap
private static function settings(): SettingGateway
{
if (self::$settingGateway instanceof SettingGateway) {
return self::$settingGateway;
}
if (!(self::$settingServicesFactory instanceof SettingServicesFactory)) {
self::$settingServicesFactory = new SettingServicesFactory();
}
self::$settingGateway = self::$settingServicesFactory->createSettingGateway();
return self::$settingGateway;
return self::resolveDependency(self::$settingGatewayResolver, SettingGateway::class, 'ApiBootstrap');
}
private static function rateLimiter(): RateLimiterService
{
if (self::$rateLimiterService instanceof RateLimiterService) {
return self::$rateLimiterService;
return self::resolveDependency(self::$rateLimiterServiceResolver, RateLimiterService::class, 'ApiBootstrap');
}
private static function apiAuditService(): ApiAuditService
{
return self::resolveDependency(self::$apiAuditServiceResolver, ApiAuditService::class, 'ApiBootstrap');
}
private static function systemAuditReporter(): ApiSystemAuditReporter
{
return self::resolveDependency(self::$apiSystemAuditReporterResolver, ApiSystemAuditReporter::class, 'ApiBootstrap');
}
private static function startSystemAuditReporter(): void
{
try {
self::systemAuditReporter()->start();
} catch (\Throwable) {
// fail-open
}
}
private static function finishSystemAuditReporter(int $statusCode, ?string $errorCode = null): void
{
try {
self::systemAuditReporter()->finish($statusCode, $errorCode);
} catch (\Throwable) {
// fail-open
}
}
private static function resolveDependency(mixed $resolver, string $expectedClass, string $consumer): mixed
{
if (!is_callable($resolver)) {
throw new \RuntimeException($consumer . ' is not configured for dependency: ' . $expectedClass);
}
if (!(self::$securityServicesFactory instanceof SecurityServicesFactory)) {
self::$securityServicesFactory = new SecurityServicesFactory();
$service = $resolver();
if (!$service instanceof $expectedClass) {
throw new \RuntimeException($consumer . ' resolver returned invalid dependency: ' . $expectedClass);
}
self::$rateLimiterService = self::$securityServicesFactory->createRateLimiterService();
return self::$rateLimiterService;
return $service;
}
}