|null */ private static ?array $cache = null; /** * Returns every provider that can be chosen. * * @return array Providers keyed by slug. */ public static function providers(): array { if ( null !== self::$cache ) { return self::$cache; } $providers = array(); foreach ( array( new RecaptchaV2(), new RecaptchaV3(), new Turnstile() ) as $provider ) { $providers[ $provider->id() ] = $provider; } /** * Filters the available captcha providers. * * @since 1.0.0 * * @param array $providers Providers keyed by slug. */ $providers = (array) apply_filters( 'formpilot_captcha_providers', $providers ); self::$cache = array_filter( $providers, static fn( $provider ): bool => $provider instanceof CaptchaProvider ); return self::$cache; } /** * Returns the labels for the settings screen. * * @return array Labels keyed by slug, "none" included. */ public static function choices(): array { $choices = array( self::NONE => __( 'None', 'devplugins-custom-form-builder' ) ); foreach ( self::providers() as $id => $provider ) { $choices[ $id ] = $provider->label(); } return $choices; } /** * Returns the provider a form should use, if any. * * A provider that is chosen but missing a key is treated as switched off. The * alternative — rendering a widget that cannot be verified — locks every * visitor out of the form, which is a worse failure than accepting spam. * * @return CaptchaProvider|null The provider, or null when none applies. */ public static function active(): ?CaptchaProvider { $provider = self::providers()[ Settings::text( self::SETTING ) ] ?? null; if ( ! $provider instanceof CaptchaProvider ) { return null; } return $provider->is_configured() ? $provider : null; } /** * Returns the label of a provider that is selected but cannot be used. * * Falling back to "off" when a key is missing is deliberate and stays — * rendering a widget that cannot be verified would lock every visitor out of * the form, which is worse than accepting spam. Doing it *silently* was the * defect: a mistyped secret switched the site's spam protection off entirely, * the forms carried on working, and nothing anywhere said so. * * @return string The provider's label, or an empty string when all is well. */ public static function misconfigured(): string { $provider = self::providers()[ Settings::text( self::SETTING ) ] ?? null; return $provider instanceof CaptchaProvider && ! $provider->is_configured() ? $provider->label() : ''; } /** * Forgets the resolved providers. */ public static function flush(): void { self::$cache = null; } }