*/ private static array $schemaCache = []; public static function assertValid( array $rawManifest, string $moduleId, ?string $manifestFile = null, ?string $schemaFile = null ): void { $schemaPath = self::resolveSchemaPath($schemaFile); $schema = self::loadSchema($schemaPath); $payload = json_decode((string) json_encode($rawManifest), false); if (!is_object($payload)) { throw new RuntimeException("Manifest schema validation failed for module '{$moduleId}': manifest payload is not serializable."); } $validator = new Validator(); $result = $validator->validate($payload, $schema); if ($result->isValid()) { return; } $location = $manifestFile ?? ("modules/{$moduleId}/module.php"); $message = self::formatValidationErrors($result->error()); throw new RuntimeException( "Manifest schema validation failed for module '{$moduleId}' ({$location}): {$message}" ); } private static function resolveSchemaPath(?string $schemaFile): string { if ($schemaFile !== null && trim($schemaFile) !== '') { return $schemaFile; } return dirname(__DIR__, 3) . '/' . self::DEFAULT_SCHEMA_PATH; } private static function loadSchema(string $schemaPath): object { $normalizedPath = str_replace('\\', '/', $schemaPath); if (isset(self::$schemaCache[$normalizedPath])) { return self::$schemaCache[$normalizedPath]; } if (!is_file($schemaPath)) { throw new RuntimeException("Module manifest schema not found: {$schemaPath}"); } $rawSchema = file_get_contents($schemaPath); if ($rawSchema === false) { throw new RuntimeException("Unable to read module manifest schema: {$schemaPath}"); } $decodedSchema = json_decode($rawSchema); if (!is_object($decodedSchema)) { throw new RuntimeException("Invalid JSON schema in {$schemaPath}"); } self::$schemaCache[$normalizedPath] = $decodedSchema; return $decodedSchema; } private static function formatValidationErrors(?ValidationError $error): string { if ($error === null) { return 'unknown schema validation error'; } $formatter = new ErrorFormatter(); $messages = $formatter->formatFlat( $error, static function (ValidationError $validationError) use ($formatter): string { $path = JsonPointer::pathToString($validationError->data()->fullPath()); $normalizedPath = $path === '' ? '/' : $path; $message = $formatter->formatErrorMessage($validationError); return "{$normalizedPath}: {$message}"; } ); $messages = array_values(array_unique(array_filter( $messages, static fn ($message): bool => is_string($message) && trim($message) !== '' ))); if ($messages === []) { return 'unknown schema validation error'; } return implode('; ', array_slice($messages, 0, 3)); } }