fixturesDir = sys_get_temp_dir() . '/layout-provider-guard-' . uniqid(); mkdir($this->fixturesDir, 0777, true); } protected function tearDown(): void { $this->removeDir($this->fixturesDir); $this->restoreAppContainer(); } public function testReservedLayoutKeyIsRejected(): void { $registry = $this->createRegistryWithProvider(DummyReservedLayoutProvider::class); $container = new AppContainer(); $container->set(ModuleRegistry::class, static fn (): ModuleRegistry => $registry); $this->pushAppContainer($container); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage("must not override reserved layout key 'moduleSlots'"); appBuildLayoutNavContext([], [], []); } public function testNamespacedLayoutProviderKeyIsAccepted(): void { $registry = $this->createRegistryWithProvider(DummyNamespacedLayoutProvider::class); $container = new AppContainer(); $container->set(ModuleRegistry::class, static fn (): ModuleRegistry => $registry); $this->pushAppContainer($container); $layoutNav = appBuildLayoutNavContext([], [], []); self::assertSame('ok', $layoutNav['dummy.value'] ?? null); } private function createRegistryWithProvider(string $providerClass): ModuleRegistry { $moduleDir = $this->fixturesDir . '/dummy-module'; mkdir($moduleDir, 0777, true); file_put_contents( $moduleDir . '/module.php', ' 'dummy-module', 'layout_context_providers' => [$providerClass], ], true) . ';' ); return ModuleRegistry::boot($this->fixturesDir, ['dummy-module']); } private function removeDir(string $dir): void { if (!is_dir($dir)) { return; } $items = new \RecursiveIteratorIterator( new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS), \RecursiveIteratorIterator::CHILD_FIRST ); foreach ($items as $item) { if ($item->isDir()) { rmdir($item->getPathname()); } else { unlink($item->getPathname()); } } rmdir($dir); } } final class DummyReservedLayoutProvider implements LayoutContextProvider { public function provide(array $session, AppContainer $container): array { return [ 'moduleSlots' => ['bad' => true], ]; } } final class DummyNamespacedLayoutProvider implements LayoutContextProvider { public function provide(array $session, AppContainer $container): array { return [ 'dummy.value' => 'ok', ]; } }