PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.3
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.3
3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 2.3.0 2.3.1 All 196 releases
convertkit / vendor / wordpress / php-mcp-schema / src / Common / Traits / ValidatesRequiredFields.php

ValidatesRequiredFields.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.3, at vendor/wordpress/php-mcp-schema/src/Common/Traits/ValidatesRequiredFields.php

413 lines 11.2 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 * Returns the entries of $data whose keys the caller does not model.
189 *
190 * Used by types the MCP schema declares open (`[key: string]: unknown`),
191 * so that unrecognized fields survive a fromArray()/toArray() round trip
192 * instead of being silently discarded.
193 *
194 * @param array<string, mixed> $data
195 * @param array<int, string> $known
196 * @return array<string, mixed>|null
197 */
198 protected static function additionalFields(array $data, array $known): ?array
199 {
200 $additional = array_diff_key($data, array_flip($known));
201
202 return $additional === [] ? null : $additional;
203 }
204
205 /**
206 * Asserts a value is an object and returns it.
207 *
208 * Accepts both PHP arrays and objects, auto-converting arrays to objects.
209 * This aligns with MCP spec where JSON objects can be PHP arrays or objects.
210 *
211 * @param mixed $value
212 * @return object
213 * @phpstan-assert object $value
214 */
215 protected static function asObject($value): object
216 {
217 if (is_array($value)) {
218 return (object) $value;
219 }
220 if (!is_object($value)) {
221 throw new \InvalidArgumentException(sprintf(
222 'Expected array or object, got %s',
223 gettype($value)
224 ));
225 }
226 return $value;
227 }
228
229 /**
230 * Returns a value as object or null.
231 *
232 * @param mixed $value
233 * @return object|null
234 */
235 protected static function asObjectOrNull($value): ?object
236 {
237 return $value === null ? null : self::asObject($value);
238 }
239
240 /**
241 * Asserts a value is an array of strings and returns it.
242 *
243 * @param mixed $value
244 * @return array<int, string>
245 */
246 protected static function asStringArray($value): array
247 {
248 if (!is_array($value)) {
249 throw new \InvalidArgumentException(sprintf(
250 'Expected array, got %s',
251 gettype($value)
252 ));
253 }
254 /** @var array<int, string> */
255 return array_values(array_map(static fn($item): string => (string) $item, $value));
256 }
257
258 /**
259 * Returns a value as array of strings or null.
260 *
261 * @param mixed $value
262 * @return array<int, string>|null
263 */
264 protected static function asStringArrayOrNull($value): ?array
265 {
266 return $value === null ? null : self::asStringArray($value);
267 }
268
269 /**
270 * Asserts a value is an associative array with string values only.
271 *
272 * Used for MCP types like { [key: string]: string } index signatures.
273 *
274 * @param mixed $value
275 * @return array<string, string>
276 * @phpstan-assert array<string, string> $value
277 */
278 protected static function asStringMap($value): array
279 {
280 if (!is_array($value)) {
281 throw new \InvalidArgumentException(sprintf(
282 'Expected array, got %s',
283 gettype($value)
284 ));
285 }
286 foreach ($value as $key => $v) {
287 if (!is_string($v)) {
288 throw new \InvalidArgumentException(sprintf(
289 'Expected string value for key "%s", got %s',
290 (string) $key,
291 gettype($v)
292 ));
293 }
294 }
295 /** @var array<string, string> */
296 return $value;
297 }
298
299 /**
300 * Returns a value as string map or null.
301 *
302 * Used for optional MCP types like { [key: string]: string } | null.
303 *
304 * @param mixed $value
305 * @return array<string, string>|null
306 */
307 protected static function asStringMapOrNull($value): ?array
308 {
309 return $value === null ? null : self::asStringMap($value);
310 }
311
312 /**
313 * Asserts a value is an associative array with object values only.
314 *
315 * Used for MCP types like { [key: string]: object } index signatures.
316 * Accepts both PHP arrays and objects as values, auto-converting arrays to objects.
317 * This aligns with MCP spec where JSON objects can be PHP arrays or objects.
318 *
319 * @param mixed $value
320 * @return array<string, object>
321 * @phpstan-assert array<string, object> $value
322 */
323 protected static function asObjectMap($value): array
324 {
325 if (!is_array($value)) {
326 throw new \InvalidArgumentException(sprintf(
327 'Expected array, got %s',
328 gettype($value)
329 ));
330 }
331
332 $result = [];
333 foreach ($value as $key => $v) {
334 if (is_array($v)) {
335 $result[$key] = (object) $v;
336 } elseif (is_object($v)) {
337 $result[$key] = $v;
338 } else {
339 throw new \InvalidArgumentException(sprintf(
340 'Expected array or object for key "%s", got %s',
341 (string) $key,
342 gettype($v)
343 ));
344 }
345 }
346
347 /** @var array<string, object> */
348 return $result;
349 }
350
351 /**
352 * Returns a value as object map or null.
353 *
354 * Used for optional MCP types like { [key: string]: object } | null.
355 *
356 * @param mixed $value
357 * @return array<string, object>|null
358 */
359 protected static function asObjectMapOrNull($value): ?array
360 {
361 return $value === null ? null : self::asObjectMap($value);
362 }
363
364 /**
365 * Asserts a value is a scalar (string, int, float, or bool) for sprintf.
366 *
367 * @param mixed $value
368 * @return string|int|float|bool
369 */
370 protected static function asScalar($value)
371 {
372 if (!is_scalar($value)) {
373 throw new \InvalidArgumentException(sprintf(
374 'Expected scalar value, got %s',
375 gettype($value)
376 ));
377 }
378 return $value;
379 }
380
381 /**
382 * Asserts a value is a string or number (int/float) and returns it.
383 *
384 * Used for MCP types like ProgressToken that accept string | number.
385 *
386 * @param mixed $value
387 * @return string|int|float
388 */
389 protected static function asStringOrNumber($value)
390 {
391 if (!is_string($value) && !is_int($value) && !is_float($value)) {
392 throw new \InvalidArgumentException(sprintf(
393 'Expected string or number, got %s',
394 gettype($value)
395 ));
396 }
397 return $value;
398 }
399
400 /**
401 * Returns a value as string or number (int/float), or null.
402 *
403 * Used for optional MCP types like ProgressToken that accept string | number | null.
404 *
405 * @param mixed $value
406 * @return string|int|float|null
407 */
408 protected static function asStringOrNumberOrNull($value)
409 {
410 return $value === null ? null : self::asStringOrNumber($value);
411 }
412 }
413