'Composition root: builds DiviIntegration and wires its ports (DiviFeatureGate) into the core.', ]; public function testTheCoreUsesNoDiviClass(): void { $offenders = []; foreach (self::coreFiles() as $relative => $path) { if (isset(self::EXCEPTIONS[$relative])) { continue; } $names = self::diviNames((string) file_get_contents($path)); if ($names !== []) { $offenders[] = $relative . ': ' . implode(', ', $names); } } self::assertSame([], $offenders, "Der Kern darf keine Divi-Klasse benutzen (Port über Plugin.php verdrahten):\n" . implode("\n", $offenders)); } public function testEveryExceptionExistsAndStillNeedsIt(): void { $files = self::coreFiles(); foreach (array_keys(self::EXCEPTIONS) as $relative) { self::assertArrayHasKey($relative, $files, $relative . ' gibt es nicht mehr – Ausnahme streichen'); self::assertNotSame( [], self::diviNames((string) file_get_contents($files[$relative])), $relative . ' braucht die Ausnahme nicht mehr – streichen' ); } } public function testTheScanFindsImportsQualifiedNamesGroupUsesAliasesAndClassStrings(): void { $source = <<<'PHP' Sorted, unique names of Divi classes the source uses. */ private static function diviNames(string $source): array { $names = []; foreach (token_get_all($source) as $token) { if (! is_array($token)) { continue; } $text = match ($token[0]) { T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED => ltrim($token[1], '\\'), T_CONSTANT_ENCAPSED_STRING => ltrim(str_replace('\\\\', '\\', substr($token[1], 1, -1)), '\\'), default => '', }; $diviClass = '/^(?:LIT\\\\MCP\\\\Integrations\\\\)?Divi\\\\[A-Za-z0-9_\\\\]+$/'; if (preg_match($diviClass, $text) === 1 || $text === 'LIT\\MCP\\Integrations\\Divi') { $names[] = $text; } } $names = array_values(array_unique($names)); sort($names); return $names; } public function testEveryScannedPathExists(): void { $root = self::root(); foreach ([...self::CORE_PATHS, ...self::CORE_ROOTS] as $directory) { if (! in_array($directory, self::OPTIONAL_PATHS, true)) { self::assertDirectoryExists($root . $directory, $directory . ' fehlt – Scan-Liste anpassen'); } } } public function testTheDiviUiLivesInTheDiviIntegration(): void { $files = self::coreFiles(); self::assertArrayHasKey('Plugin.php', $files, 'root files are scanned'); self::assertArrayHasKey('Integrations/IntegrationRegistry.php', $files, 'Integrations root files are scanned'); self::assertArrayNotHasKey('Admin/DiviAdminExtras.php', $files, 'the Divi additions belong to Integrations/Divi/Admin'); self::assertFileExists(self::root() . 'Integrations/Divi/Admin/DiviAdminExtras.php'); self::assertFileExists(self::root() . 'Integrations/Divi/Cli/DiviCheckCommand.php'); } private static function root(): string { return dirname(__DIR__, 3) . '/src/'; } /** * @return array Path below src/ => absolute path of every PHP file in CORE_PATHS (recursively) and * directly in CORE_ROOTS. */ private static function coreFiles(): array { $root = self::root(); $files = []; foreach (self::CORE_PATHS as $directory) { if (! is_dir($root . $directory)) { continue; // Only an OPTIONAL_PATHS entry may be missing (testEveryScannedPathExists). } $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($root . $directory, FilesystemIterator::SKIP_DOTS) ); foreach ($iterator as $file) { if ($file->getExtension() === 'php') { $files[substr((string) $file->getPathname(), strlen($root))] = (string) $file->getPathname(); } } } foreach (self::CORE_ROOTS as $directory) { foreach (glob($root . ($directory === '' ? '' : $directory . '/') . '*.php') ?: [] as $path) { $files[substr($path, strlen($root))] = $path; } } ksort($files); return $files; } }