1
0
Files
breadcrumb-the-shire/bin/module-cli-bootstrap.php
fs c7b8fd516a feat: extend module platform with UI slots, runtime components, CLI tooling and {{userId}} search support
Completes the generic module platform that enables modules to contribute
UI elements, runtime JS components, and search resources without any
core hardcoding.

New generic UI slot types:
- topbar.right_item: module-contributed topbar buttons
- layout.body_end_template: module-contributed dialog/overlay templates
- layout.head_style: module-contributed global CSS
- runtime.component: declarative JS component registration with phase ordering

New infrastructure:
- ModuleAutoloader: PSR-4 autoloading for module-local PHP classes
- ModuleRuntimePageBuilder: symlinks module pages into runtime directory
- ModuleRuntimeAssetPublisher: publishes module CSS/JS to web/modules/
- ModulePermissionSynchronizer: syncs module permissions to DB
- CLI scripts: module-runtime-sync, module-build, module-migrate,
  module-permissions-sync, module-assets-sync
- {{userId}} placeholder in SearchDataService for user-scoped search queries
- Component runtime with phased initialization (early/default/late)
- AppContainer.protectExistingBindings() to prevent module→core overwrites
- Architecture tests: ModuleStructureContractTest, CoreTemplateIsolationTest,
  FrontendComponentRuntimeContractTest, AppContainerIsolationContractTest

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 22:19:56 +01:00

117 lines
2.8 KiB
PHP

<?php
declare(strict_types=1);
/**
* Shared CLI bootstrap and error policy for module runtime scripts.
*
* Policy:
* - first-party warnings/notices/deprecations => fail-fast (RuntimeException)
* - vendor warnings/notices/deprecations => ignored and counted
*/
function moduleCliProjectRoot(): string
{
return dirname(__DIR__);
}
function moduleCliVendorWarningsIgnoredTotal(): int
{
$count = $GLOBALS['module_cli_vendor_warnings_ignored'] ?? 0;
return is_int($count) ? $count : 0;
}
function moduleCliVendorWarningsIgnoredSince(int $before): int
{
return max(0, moduleCliVendorWarningsIgnoredTotal() - $before);
}
function moduleCliIncrementVendorWarningsIgnored(): void
{
$current = moduleCliVendorWarningsIgnoredTotal();
$GLOBALS['module_cli_vendor_warnings_ignored'] = $current + 1;
}
function moduleCliSeverityName(int $severity): string
{
return match ($severity) {
E_WARNING => 'E_WARNING',
E_NOTICE => 'E_NOTICE',
E_USER_WARNING => 'E_USER_WARNING',
E_USER_NOTICE => 'E_USER_NOTICE',
E_DEPRECATED => 'E_DEPRECATED',
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
default => 'E_' . $severity,
};
}
function moduleCliInstallErrorPolicy(): void
{
static $installed = false;
if ($installed) {
return;
}
$installed = true;
error_reporting(E_ALL);
ini_set('display_errors', '0');
set_error_handler(static function (
int $severity,
string $message,
string $file = '',
int $line = 0
): bool {
$handledSeverities = [
E_WARNING,
E_NOTICE,
E_USER_WARNING,
E_USER_NOTICE,
E_DEPRECATED,
E_USER_DEPRECATED,
];
if (!in_array($severity, $handledSeverities, true)) {
return false;
}
if ((error_reporting() & $severity) === 0) {
return true;
}
$normalizedFile = str_replace('\\', '/', $file);
if (str_contains($normalizedFile, '/vendor/')) {
moduleCliIncrementVendorWarningsIgnored();
return true;
}
throw new RuntimeException(sprintf(
"First-party runtime warning (%s) in %s:%d: %s",
moduleCliSeverityName($severity),
$file,
$line,
$message
));
});
}
function moduleCliBootstrapApp(): void
{
static $booted = false;
if ($booted) {
return;
}
moduleCliInstallErrorPolicy();
$projectRoot = moduleCliProjectRoot();
chdir($projectRoot);
require_once $projectRoot . '/vendor/autoload.php';
require_once $projectRoot . '/config/config.php';
require_once $projectRoot . '/lib/Support/helpers.php';
$container = require $projectRoot . '/lib/App/registerContainer.php';
setAppContainer($container);
$booted = true;
}