forked from fa/breadcrumb-the-shire
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>
117 lines
2.8 KiB
PHP
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;
|
|
}
|