PluginProbe
ZIP AI – AI Website Builder & AI Agent / 0.0.12
ZIP AI – AI Website Builder & AI Agent v0.0.12
0.0.13 0.0.12 0.0.11 0.0.10 0.0.9 trunk 0.0.4 0.0.5 0.0.6 0.0.7 0.0.8
zip-ai / lib / php-mcp-schema / src / Common / Traits / ValidatesRequiredFields.php

ValidatesRequiredFields.php in ZIP AI – AI Website Builder & AI Agent 0.0.12, at lib/php-mcp-schema/src/Common/Traits/ValidatesRequiredFields.php

395 lines 10.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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