PluginProbe ʕ •ᴥ•ʔ
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). / trunk
CommerceBird – AI Command Center, ERP Integrations & B2B for WooCommerce (Zoho, Exact Online). vtrunk
3.0.3 3.0.2 3.0.1 trunk 2.2.14 2.2.15 2.2.16 2.2.17 2.2.18 2.2.19 2.3.0 2.3.1 2.3.10 2.3.11 2.3.12 2.3.13 2.3.14 2.3.2 2.3.3 2.3.4 2.3.5 2.3.6 2.3.7 2.3.8 2.3.9 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.5.0 2.5.1 2.5.2 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.7.91 2.7.92 2.7.93 2.8.0 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0
commercebird / vendor / wordpress / php-mcp-schema / src / Common / Traits / ValidatesRequiredFields.php
commercebird / vendor / wordpress / php-mcp-schema / src / Common / Traits Last commit date
ValidatesRequiredFields.php 3 months ago
ValidatesRequiredFields.php
395 lines
1 <?php
2
3 declare(strict_types=1);
4
5 namespace WP\McpSchema\Common\Traits;
6
7 /**
8 * Trait for validating required fields in fromArray() methods.
9 *
10 * Use this trait in DTOs that need to validate required fields from input arrays.
11 * Reports ALL missing fields at once for better developer experience.
12 *
13 * @mcp-version 2025-11-25
14 */
15 trait ValidatesRequiredFields
16 {
17 /**
18 * Validates that all required fields are present in the data array.
19 *
20 * @param array<string, mixed> $data The input data array
21 * @param string[] $requiredFields List of required field names
22 * @return void
23 * @throws \InvalidArgumentException If any required fields are missing
24 */
25 protected static function assertRequired(array $data, array $requiredFields): void
26 {
27 $missing = array_filter(
28 $requiredFields,
29 static fn(string $field): bool => !array_key_exists($field, $data)
30 );
31
32 if (count($missing) > 0) {
33 throw new \InvalidArgumentException(sprintf(
34 '%s: missing required field(s): %s',
35 static::class,
36 implode(', ', $missing)
37 ));
38 }
39 }
40
41 /**
42 * Asserts a value is a string and returns it.
43 *
44 * @param mixed $value
45 * @return string
46 * @phpstan-assert string $value
47 */
48 protected static function asString($value): string
49 {
50 if (!is_string($value)) {
51 throw new \InvalidArgumentException(sprintf(
52 'Expected string, got %s',
53 gettype($value)
54 ));
55 }
56 return $value;
57 }
58
59 /**
60 * Asserts a value is an int and returns it.
61 *
62 * @param mixed $value
63 * @return int
64 * @phpstan-assert int $value
65 */
66 protected static function asInt($value): int
67 {
68 if (!is_int($value)) {
69 throw new \InvalidArgumentException(sprintf(
70 'Expected int, got %s',
71 gettype($value)
72 ));
73 }
74 return $value;
75 }
76
77 /**
78 * Asserts a value is a float and returns it.
79 *
80 * @param mixed $value
81 * @return float
82 * @phpstan-assert float $value
83 */
84 protected static function asFloat($value): float
85 {
86 if (!is_float($value) && !is_int($value)) {
87 throw new \InvalidArgumentException(sprintf(
88 'Expected float, got %s',
89 gettype($value)
90 ));
91 }
92 return (float) $value;
93 }
94
95 /**
96 * Asserts a value is a bool and returns it.
97 *
98 * @param mixed $value
99 * @return bool
100 * @phpstan-assert bool $value
101 */
102 protected static function asBool($value): bool
103 {
104 if (!is_bool($value)) {
105 throw new \InvalidArgumentException(sprintf(
106 'Expected bool, got %s',
107 gettype($value)
108 ));
109 }
110 return $value;
111 }
112
113 /**
114 * Asserts a value is an array and returns it.
115 *
116 * @param mixed $value
117 * @return array<string, mixed>
118 * @phpstan-assert array<string, mixed> $value
119 */
120 protected static function asArray($value): array
121 {
122 if (!is_array($value)) {
123 throw new \InvalidArgumentException(sprintf(
124 'Expected array, got %s',
125 gettype($value)
126 ));
127 }
128 /** @var array<string, mixed> */
129 return $value;
130 }
131
132 /**
133 * Returns a value as string or null.
134 *
135 * @param mixed $value
136 * @return string|null
137 */
138 protected static function asStringOrNull($value): ?string
139 {
140 return $value === null ? null : self::asString($value);
141 }
142
143 /**
144 * Returns a value as int or null.
145 *
146 * @param mixed $value
147 * @return int|null
148 */
149 protected static function asIntOrNull($value): ?int
150 {
151 return $value === null ? null : self::asInt($value);
152 }
153
154 /**
155 * Returns a value as float or null.
156 *
157 * @param mixed $value
158 * @return float|null
159 */
160 protected static function asFloatOrNull($value): ?float
161 {
162 return $value === null ? null : self::asFloat($value);
163 }
164
165 /**
166 * Returns a value as bool or null.
167 *
168 * @param mixed $value
169 * @return bool|null
170 */
171 protected static function asBoolOrNull($value): ?bool
172 {
173 return $value === null ? null : self::asBool($value);
174 }
175
176 /**
177 * Returns a value as array or null.
178 *
179 * @param mixed $value
180 * @return array<string, mixed>|null
181 */
182 protected static function asArrayOrNull($value): ?array
183 {
184 return $value === null ? null : self::asArray($value);
185 }
186
187 /**
188 * Asserts a value is an object and returns it.
189 *
190 * Accepts both PHP arrays and objects, auto-converting arrays to objects.
191 * This aligns with MCP spec where JSON objects can be PHP arrays or objects.
192 *
193 * @param mixed $value
194 * @return object
195 * @phpstan-assert object $value
196 */
197 protected static function asObject($value): object
198 {
199 if (is_array($value)) {
200 return (object) $value;
201 }
202 if (!is_object($value)) {
203 throw new \InvalidArgumentException(sprintf(
204 'Expected array or object, got %s',
205 gettype($value)
206 ));
207 }
208 return $value;
209 }
210
211 /**
212 * Returns a value as object or null.
213 *
214 * @param mixed $value
215 * @return object|null
216 */
217 protected static function asObjectOrNull($value): ?object
218 {
219 return $value === null ? null : self::asObject($value);
220 }
221
222 /**
223 * Asserts a value is an array of strings and returns it.
224 *
225 * @param mixed $value
226 * @return array<int, string>
227 */
228 protected static function asStringArray($value): array
229 {
230 if (!is_array($value)) {
231 throw new \InvalidArgumentException(sprintf(
232 'Expected array, got %s',
233 gettype($value)
234 ));
235 }
236 /** @var array<int, string> */
237 return array_values(array_map(static fn($item): string => (string) $item, $value));
238 }
239
240 /**
241 * Returns a value as array of strings or null.
242 *
243 * @param mixed $value
244 * @return array<int, string>|null
245 */
246 protected static function asStringArrayOrNull($value): ?array
247 {
248 return $value === null ? null : self::asStringArray($value);
249 }
250
251 /**
252 * Asserts a value is an associative array with string values only.
253 *
254 * Used for MCP types like { [key: string]: string } index signatures.
255 *
256 * @param mixed $value
257 * @return array<string, string>
258 * @phpstan-assert array<string, string> $value
259 */
260 protected static function asStringMap($value): array
261 {
262 if (!is_array($value)) {
263 throw new \InvalidArgumentException(sprintf(
264 'Expected array, got %s',
265 gettype($value)
266 ));
267 }
268 foreach ($value as $key => $v) {
269 if (!is_string($v)) {
270 throw new \InvalidArgumentException(sprintf(
271 'Expected string value for key "%s", got %s',
272 (string) $key,
273 gettype($v)
274 ));
275 }
276 }
277 /** @var array<string, string> */
278 return $value;
279 }
280
281 /**
282 * Returns a value as string map or null.
283 *
284 * Used for optional MCP types like { [key: string]: string } | null.
285 *
286 * @param mixed $value
287 * @return array<string, string>|null
288 */
289 protected static function asStringMapOrNull($value): ?array
290 {
291 return $value === null ? null : self::asStringMap($value);
292 }
293
294 /**
295 * Asserts a value is an associative array with object values only.
296 *
297 * Used for MCP types like { [key: string]: object } index signatures.
298 * Accepts both PHP arrays and objects as values, auto-converting arrays to objects.
299 * This aligns with MCP spec where JSON objects can be PHP arrays or objects.
300 *
301 * @param mixed $value
302 * @return array<string, object>
303 * @phpstan-assert array<string, object> $value
304 */
305 protected static function asObjectMap($value): array
306 {
307 if (!is_array($value)) {
308 throw new \InvalidArgumentException(sprintf(
309 'Expected array, got %s',
310 gettype($value)
311 ));
312 }
313
314 $result = [];
315 foreach ($value as $key => $v) {
316 if (is_array($v)) {
317 $result[$key] = (object) $v;
318 } elseif (is_object($v)) {
319 $result[$key] = $v;
320 } else {
321 throw new \InvalidArgumentException(sprintf(
322 'Expected array or object for key "%s", got %s',
323 (string) $key,
324 gettype($v)
325 ));
326 }
327 }
328
329 /** @var array<string, object> */
330 return $result;
331 }
332
333 /**
334 * Returns a value as object map or null.
335 *
336 * Used for optional MCP types like { [key: string]: object } | null.
337 *
338 * @param mixed $value
339 * @return array<string, object>|null
340 */
341 protected static function asObjectMapOrNull($value): ?array
342 {
343 return $value === null ? null : self::asObjectMap($value);
344 }
345
346 /**
347 * Asserts a value is a scalar (string, int, float, or bool) for sprintf.
348 *
349 * @param mixed $value
350 * @return string|int|float|bool
351 */
352 protected static function asScalar($value)
353 {
354 if (!is_scalar($value)) {
355 throw new \InvalidArgumentException(sprintf(
356 'Expected scalar value, got %s',
357 gettype($value)
358 ));
359 }
360 return $value;
361 }
362
363 /**
364 * Asserts a value is a string or number (int/float) and returns it.
365 *
366 * Used for MCP types like ProgressToken that accept string | number.
367 *
368 * @param mixed $value
369 * @return string|int|float
370 */
371 protected static function asStringOrNumber($value)
372 {
373 if (!is_string($value) && !is_int($value) && !is_float($value)) {
374 throw new \InvalidArgumentException(sprintf(
375 'Expected string or number, got %s',
376 gettype($value)
377 ));
378 }
379 return $value;
380 }
381
382 /**
383 * Returns a value as string or number (int/float), or null.
384 *
385 * Used for optional MCP types like ProgressToken that accept string | number | null.
386 *
387 * @param mixed $value
388 * @return string|int|float|null
389 */
390 protected static function asStringOrNumberOrNull($value)
391 {
392 return $value === null ? null : self::asStringOrNumber($value);
393 }
394 }
395