Abilities left registered after a failed atomic module registration. */ private static array $quarantinedAbilities = []; /** * Whether the AI Client exists and AI is allowed on this request. * * @since 0.1.0 */ public static function aiClient(): bool { return function_exists('wp_ai_client_prompt') && function_exists('wp_supports_ai') && class_exists('WordPress\AiClient\AiClient') && wp_supports_ai(); } /** * Whether the Abilities API exists (listing and running abilities). * * @since 0.1.0 */ public static function abilities(): bool { return function_exists('wp_get_abilities') && function_exists('wp_get_ability') && class_exists('WP_Ability'); } /** * Whether abilities can be registered (the plugin's own tools). * * @since 0.1.0 */ public static function abilityRegistration(): bool { return function_exists('wp_register_ability') && function_exists('wp_register_ability_category'); } /** @return list|null Registered Ability ids, or null when the discovery API is unavailable. */ public static function abilityNames(): ?array { if (!self::abilities()) { return null; } return array_values(array_filter(array_keys(wp_get_abilities()), 'is_string')); } /** @param list $before Remove and quarantine Abilities added after the snapshot. */ public static function rollbackAbilities(array $before): void { if (!self::abilities()) { return; } $existing = array_fill_keys($before, true); foreach (array_keys(wp_get_abilities()) as $name) { if (is_string($name) && !isset($existing[$name])) { self::quarantineAbilities([$name]); self::unregisterAbilityIfSupported($name); } } } /** @param list $names */ public static function quarantineAbilities(array $names): void { foreach ($names as $name) { if ($name !== '') { self::$quarantinedAbilities[$name] = true; } } } public static function abilityQuarantined(string $name): bool { return isset(self::$quarantinedAbilities[$name]); } public static function unregisterAbilityIfSupported(string $name): void { if (function_exists('wp_unregister_ability')) { try { wp_unregister_ability($name); } catch (\Throwable) { // Quarantine is authoritative when WordPress cannot remove the registration. } } } /** Whether the classic navigation menu APIs are available. */ public static function navigationMenus(): bool { return function_exists('wp_get_nav_menus') && function_exists('wp_get_nav_menu_items'); } /** Whether block theme templates can be enumerated. */ public static function blockTemplates(): bool { return function_exists('get_block_templates'); } /** Whether the block pattern registry is present. */ public static function blockPatterns(): bool { return class_exists('WP_Block_Patterns_Registry'); } /** * Whether the privacy policy guide accepts suggested text. * * @since 0.1.0 */ public static function privacyPolicyText(): bool { return function_exists('wp_add_privacy_policy_content'); } }