PluginProbe
The Innovative Form Builder – IvyForms / 1.5
The Innovative Form Builder – IvyForms v1.5
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
← All changes | backend/src/Factory/Security/CaptchaProviderResolver.php +241 -0 0.6.1 → 1.5 View file →
@@ -1,0 +1,241 @@
1 +<?php
2 +
3 +/**
4 + * @copyright © Melograno Venture Studio. All rights reserved.
5 + * @licence See COPYING.md for license details.
6 + */
7 +
8 +namespace IvyForms\Factory\Security;
9 +
10 +use IvyForms\Services\Settings\SettingsService;
11 +
12 +/**
13 + * Resolver for determining which CAPTCHA provider to use
14 + *
15 + * Handles the business logic of provider selection based on form fields,
16 + * configuration validation, and conflict detection.
17 + *
18 + * @package IvyForms\Factory\Security
19 + */
20 +class CaptchaProviderResolver
21 +{
22 + // Provider constants
23 + public const PROVIDER_RECAPTCHA = 'recaptcha';
24 + public const PROVIDER_HCAPTCHA = 'hcaptcha';
25 + public const PROVIDER_TURNSTILE = 'turnstile';
26 + public const PROVIDER_NONE = 'none';
27 + private SettingsService $settingsService;
28 +
29 + /** @var array<string, array<string>> */
30 + private array $requiredKeys;
31 +
32 + /** @var array<string, string> */
33 + private array $fieldTypeMapping;
34 +
35 + /**
36 + * @param SettingsService $settingsService
37 + * @param array<string, array<string>> $requiredKeys Provider required keys mapping
38 + * @param array<string, string> $fieldTypeMapping Field type to provider mapping
39 + */
40 + public function __construct(
41 + SettingsService $settingsService,
42 + array $requiredKeys = [],
43 + array $fieldTypeMapping = []
44 + ) {
45 + $this->settingsService = $settingsService;
46 + $this->requiredKeys = $requiredKeys ?: $this->getDefaultRequiredKeys();
47 + $this->fieldTypeMapping = $fieldTypeMapping ?: $this->getDefaultFieldTypeMapping();
48 + }
49 +
50 + /**
51 + * Get the first configured provider
52 + *
53 + * @return string
54 + */
55 + public function getActiveProvider(): string
56 + {
57 + $providers = [
58 + self::PROVIDER_RECAPTCHA,
59 + self::PROVIDER_HCAPTCHA,
60 + self::PROVIDER_TURNSTILE,
61 + ];
62 +
63 + foreach ($providers as $provider) {
64 + if ($this->isConfigured($provider)) {
65 + return $provider;
66 + }
67 + }
68 +
69 + return self::PROVIDER_NONE;
70 + }
71 +
72 + /**
73 + * Determine CAPTCHA provider based on form fields
74 + *
75 + * @param array<object> $formFields Array of form field objects
76 + * @return string
77 + */
78 + public function resolveProviderForForm(array $formFields): string
79 + {
80 + foreach ($formFields as $field) {
81 + $fieldType = $field->getType();
82 + if (isset($this->fieldTypeMapping[$fieldType])) {
83 + $provider = $this->fieldTypeMapping[$fieldType];
84 + if ($this->isConfigured($provider)) {
85 + return $provider;
86 + }
87 + }
88 + }
89 +
90 + return self::PROVIDER_NONE;
91 + }
92 +
93 + /**
94 + * Check if a provider is properly configured
95 + *
96 + * @param string $provider
97 + * @return bool
98 + */
99 + public function isConfigured(string $provider): bool
100 + {
101 + if (!isset($this->requiredKeys[$provider])) {
102 + return false;
103 + }
104 +
105 + $settings = $this->settingsService->getSetting('security', $provider);
106 + if (!is_array($settings)) {
107 + return false;
108 + }
109 +
110 + $requiredKeys = $this->requiredKeys[$provider];
111 + foreach ($requiredKeys as $key) {
112 + if (empty($settings[$key])) {
113 + return false;
114 + }
115 + }
116 +
117 + return true;
118 + }
119 +
120 + /**
121 + * Get all configured providers
122 + *
123 + * @return array<string>
124 + */
125 + public function getConfiguredProviders(): array
126 + {
127 + $configured = [];
128 + $allProviders = [
129 + self::PROVIDER_RECAPTCHA,
130 + self::PROVIDER_HCAPTCHA,
131 + self::PROVIDER_TURNSTILE,
132 + ];
133 +
134 + foreach ($allProviders as $provider) {
135 + if ($this->isConfigured($provider)) {
136 + $configured[] = $provider;
137 + }
138 + }
139 +
140 + return $configured;
141 + }
142 +
143 + /**
144 + * Check if form has multiple CAPTCHA field types (conflict detection)
145 + *
146 + * @param array<object> $formFields Array of form field objects
147 + * @return array{hasConflict: bool, foundTypes: array<string>}
148 + */
149 + public function checkFormCaptchaConflicts(array $formFields): array
150 + {
151 + $foundTypes = [];
152 +
153 + foreach ($formFields as $field) {
154 + $fieldType = $field->getType();
155 + if (isset($this->fieldTypeMapping[$fieldType])) {
156 + if (!in_array($fieldType, $foundTypes, true)) {
157 + $foundTypes[] = $fieldType;
158 + }
159 + }
160 + }
161 +
162 + return [
163 + 'hasConflict' => count($foundTypes) > 1,
164 + 'foundTypes' => $foundTypes,
165 + ];
166 + }
167 +
168 + /**
169 + * Get provider status summary
170 + *
171 + * @param array<string> $supportedProviders List of supported providers from factory
172 + * @return array<string, array{supported: bool, configured: bool}>
173 + */
174 + public function getProviderStatus(array $supportedProviders = []): array
175 + {
176 + $status = [];
177 + $allProviders = [
178 + self::PROVIDER_RECAPTCHA,
179 + self::PROVIDER_HCAPTCHA,
180 + self::PROVIDER_TURNSTILE,
181 + ];
182 +
183 + foreach ($allProviders as $provider) {
184 + $status[$provider] = [
185 + 'supported' => in_array($provider, $supportedProviders, true),
186 + 'configured' => $this->isConfigured($provider),
187 + ];
188 + }
189 +
190 + return $status;
191 + }
192 +
193 + /**
194 + * Get configuration requirements for a provider
195 + *
196 + * @param string $provider
197 + * @return array<string>
198 + */
199 + public function getProviderRequiredKeys(string $provider): array
200 + {
201 + return $this->requiredKeys[$provider] ?? [];
202 + }
203 +
204 + /**
205 + * Get field type mapping
206 + *
207 + * @return array<string, string>
208 + */
209 + public function getFieldTypeMapping(): array
210 + {
211 + return $this->fieldTypeMapping;
212 + }
213 +
214 + /**
215 + * Get default required keys for providers
216 + *
217 + * @return array<string, array<string>>
218 + */
219 + private function getDefaultRequiredKeys(): array
220 + {
221 + return [
222 + self::PROVIDER_RECAPTCHA => ['siteKey', 'secretKey'],
223 + self::PROVIDER_HCAPTCHA => ['siteKey', 'secretKey'],
224 + self::PROVIDER_TURNSTILE => ['siteKey', 'secretKey'],
225 + ];
226 + }
227 +
228 + /**
229 + * Get default field type to provider mapping
230 + *
231 + * @return array<string, string>
232 + */
233 + private function getDefaultFieldTypeMapping(): array
234 + {
235 + return [
236 + 'recaptcha' => self::PROVIDER_RECAPTCHA,
237 + 'hcaptcha' => self::PROVIDER_HCAPTCHA,
238 + 'turnstile' => self::PROVIDER_TURNSTILE,
239 + ];
240 + }
241 +}