PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / trunk
Pods – Custom Content Types and Fields vtrunk
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / src / Pods / CLI / Commands / Base.php
pods / src / Pods / CLI / Commands Last commit date
Base.php 4 months ago Field.php 4 months ago Group.php 4 months ago Playbook.php 4 months ago Pod.php 4 months ago Tools.php 4 months ago
Base.php
606 lines
1 <?php
2
3 namespace Pods\CLI\Commands;
4
5 // Don't load directly.
6 if ( ! defined( 'ABSPATH' ) ) {
7 die( '-1' );
8 }
9
10 use Pods\REST\V1\Endpoints\Base as Base_Endpoint;
11 use WP_CLI;
12 use WP_Error;
13 use WP_REST_Request;
14
15 /**
16 * Class Base
17 *
18 * @since 2.8.0
19 */
20 abstract class Base {
21
22 /**
23 * @var string
24 */
25 protected $namespace = 'pods';
26
27 /**
28 * @var string
29 */
30 protected $command = '';
31
32 /**
33 * @var Base_Endpoint
34 */
35 protected $endpoint_single;
36
37 /**
38 * @var Base_Endpoint
39 */
40 protected $endpoint_single_slug;
41
42 /**
43 * @var Base_Endpoint
44 */
45 protected $endpoint_archive;
46
47 /**
48 * Handle setup of things needed by command.
49 *
50 * @since 2.8.0
51 */
52 public function hook() {
53 // Permissions are relaxed for WP-CLI context.
54 add_filter( 'pods_is_admin', '__return_true' );
55
56 $this->add_commands();
57 }
58
59 /**
60 * Add commands based on endpoint object.
61 *
62 * @since 2.8.0
63 */
64 public function add_commands() {
65 if ( $this->endpoint_archive && method_exists( $this->endpoint_archive, 'get' ) ) {
66 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'list' );
67
68 WP_CLI::add_command( $command, [
69 $this,
70 'list_items',
71 ], $this->build_command_args( 'list', $this->endpoint_archive ) );
72 }
73
74 if ( $this->endpoint_archive && method_exists( $this->endpoint_archive, 'create' ) ) {
75 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'add' );
76
77 WP_CLI::add_command( $command, [
78 $this,
79 'add',
80 ], $this->build_command_args( 'add', $this->endpoint_archive ) );
81 }
82
83 if ( $this->endpoint_single && method_exists( $this->endpoint_single, 'get' ) ) {
84 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'get' );
85
86 WP_CLI::add_command( $command, [
87 $this,
88 'get',
89 ], $this->build_command_args( 'get', $this->endpoint_single ) );
90 }
91
92 if ( $this->endpoint_single_slug && method_exists( $this->endpoint_single_slug, 'get' ) ) {
93 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'get-by-slug' );
94
95 WP_CLI::add_command( $command, [
96 $this,
97 'get_by_slug',
98 ], $this->build_command_args( 'get', $this->endpoint_single_slug ) );
99 }
100
101 if ( $this->endpoint_single && method_exists( $this->endpoint_single, 'update' ) ) {
102 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'update' );
103
104 WP_CLI::add_command( $command, [
105 $this,
106 'update',
107 ], $this->build_command_args( 'update', $this->endpoint_single ) );
108 }
109
110 if ( $this->endpoint_single_slug && method_exists( $this->endpoint_single_slug, 'update' ) ) {
111 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'update-by-slug' );
112
113 WP_CLI::add_command( $command, [
114 $this,
115 'update_by_slug',
116 ], $this->build_command_args( 'update', $this->endpoint_single_slug ) );
117 }
118
119 if ( $this->endpoint_single && method_exists( $this->endpoint_single, 'delete' ) ) {
120 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'delete' );
121
122 WP_CLI::add_command( $command, [
123 $this,
124 'delete',
125 ], $this->build_command_args( 'delete', $this->endpoint_single ) );
126 }
127
128 if ( $this->endpoint_single_slug && method_exists( $this->endpoint_single_slug, 'delete' ) ) {
129 $command = sprintf( '%1$s %2$s %3$s', $this->namespace, $this->command, 'delete-by-slug' );
130
131 WP_CLI::add_command( $command, [
132 $this,
133 'delete_by_slug',
134 ], $this->build_command_args( 'delete', $this->endpoint_single_slug ) );
135 }
136 }
137
138 /**
139 * List items.
140 *
141 * @since 2.8.0
142 *
143 * @param array $args List of positional arguments.
144 * @param array $assoc_args List of associative arguments.
145 *
146 * @throws WP_CLI\ExitException
147 */
148 public function list_items( array $args, array $assoc_args ) {
149 return $this->run_endpoint_method( $args, $assoc_args, 'get', $this->endpoint_archive );
150 }
151
152 /**
153 * Add an item.
154 *
155 * @since 2.8.0
156 *
157 * @param array $args List of positional arguments.
158 * @param array $assoc_args List of associative arguments.
159 *
160 * @throws WP_CLI\ExitException
161 */
162 public function add( array $args, array $assoc_args ) {
163 return $this->run_endpoint_method( $args, $assoc_args, 'create', $this->endpoint_archive );
164 }
165
166 /**
167 * Get an item by ID.
168 *
169 * @since 2.8.0
170 *
171 * @param array $args List of positional arguments.
172 * @param array $assoc_args List of associative arguments.
173 *
174 * @throws WP_CLI\ExitException
175 */
176 public function get( array $args, array $assoc_args ) {
177 return $this->run_endpoint_method( $args, $assoc_args, 'get', $this->endpoint_single );
178 }
179
180 /**
181 * Get an item by slug.
182 *
183 * @since 2.8.0
184 *
185 * @param array $args List of positional arguments.
186 * @param array $assoc_args List of associative arguments.
187 *
188 * @throws WP_CLI\ExitException
189 */
190 public function get_by_slug( array $args, array $assoc_args ) {
191 return $this->run_endpoint_method( $args, $assoc_args, 'get', $this->endpoint_single_slug );
192 }
193
194 /**
195 * Update an item by ID.
196 *
197 * @since 2.8.0
198 *
199 * @param array $args List of positional arguments.
200 * @param array $assoc_args List of associative arguments.
201 *
202 * @throws WP_CLI\ExitException
203 */
204 public function update( array $args, array $assoc_args ) {
205 return $this->run_endpoint_method( $args, $assoc_args, 'update', $this->endpoint_single );
206 }
207
208 /**
209 * Update an item by slug.
210 *
211 * @since 2.8.0
212 *
213 * @param array $args List of positional arguments.
214 * @param array $assoc_args List of associative arguments.
215 *
216 * @throws WP_CLI\ExitException
217 */
218 public function update_by_slug( array $args, array $assoc_args ) {
219 return $this->run_endpoint_method( $args, $assoc_args, 'update', $this->endpoint_single_slug );
220 }
221
222 /**
223 * Delete an item by ID.
224 *
225 * @since 2.8.0
226 *
227 * @param array $args List of positional arguments.
228 * @param array $assoc_args List of associative arguments.
229 *
230 * @throws WP_CLI\ExitException
231 */
232 public function delete( array $args, array $assoc_args ) {
233 return $this->run_endpoint_method( $args, $assoc_args, 'delete', $this->endpoint_single );
234 }
235
236 /**
237 * Delete an item by slug.
238 *
239 * @since 2.8.0
240 *
241 * @param array $args List of positional arguments.
242 * @param array $assoc_args List of associative arguments.
243 *
244 * @throws WP_CLI\ExitException
245 */
246 public function delete_by_slug( array $args, array $assoc_args ) {
247 return $this->run_endpoint_method( $args, $assoc_args, 'delete', $this->endpoint_single_slug );
248 }
249
250 /**
251 * Run endpoint method using args provided.
252 *
253 * @since 2.8.0
254 *
255 * @param array $args List of positional arguments.
256 * @param array $assoc_args List of associative arguments.
257 * @param string $method Method name.
258 * @param Base_Endpoint $endpoint Endpoint object.
259 *
260 * @throws WP_CLI\ExitException
261 */
262 public function run_endpoint_method( array $args, array $assoc_args, $method, Base_Endpoint $endpoint ) {
263 if ( ! method_exists( $endpoint, $method ) ) {
264 return;
265 }
266
267 $assoc_args = $this->json_or_args( $assoc_args );
268 $assoc_args = $this->validate_args( $args, $assoc_args, $method, $endpoint );
269
270 if ( is_wp_error( $assoc_args ) ) {
271 return $this->output_error_response( $assoc_args );
272 }
273
274 $attributes = [
275 'args' => $assoc_args,
276 ];
277
278 $method_mapping = [
279 'list' => 'GET',
280 'add' => 'POST',
281 'get' => 'GET',
282 'update' => 'POST',
283 'delete' => 'DELETE',
284 ];
285
286 $rest_method = 'GET';
287
288 if ( isset( $method_mapping[ $method ] ) ) {
289 $rest_method = $method_mapping[ $method ];
290 }
291
292 $permissions_mapping = [
293 'list' => 'can_read',
294 'add' => 'can_create',
295 'get' => 'can_read',
296 'update' => 'can_edit',
297 'delete' => 'can_delete',
298 ];
299
300 if ( isset( $permissions_mapping[ $method ] ) ) {
301 $permissions_method = $permissions_mapping[ $method ];
302
303 if ( method_exists( $endpoint, $permissions_method ) && ! $endpoint->$permissions_method() ) {
304 \WP_CLI::error( __( 'The current user does not have access to this endpoint.', 'pods' ) );
305 }
306 }
307
308 $route = $endpoint->get_route();
309
310 // Add numeric args.
311 if ( ! empty( $args ) ) {
312 $route = sprintf( $route, ...$args );
313 }
314
315 $request = new WP_REST_Request( $rest_method, '/' . rest_get_url_prefix() . $route, $attributes );
316
317 if ( 'POST' === $rest_method ) {
318 $request->set_body_params( $assoc_args );
319 } else {
320 $request->set_query_params( $assoc_args );
321 }
322
323 $response = $endpoint->$method( $request );
324
325 if ( is_wp_error( $response ) ) {
326 return $this->output_error_response( $response );
327 }
328
329 if ( null !== $response ) {
330 if ( is_object( $response ) || is_array( $response ) ) {
331 $response = wp_json_encode( $response, JSON_PRETTY_PRINT );
332 }
333
334 WP_CLI::line( $response );
335 }
336
337 WP_CLI::success( __( 'Command successful', 'pods' ) );
338 }
339
340 /**
341 * Get the list of arguments with JSON expanded if provided.
342 *
343 * @since 2.8.0
344 *
345 * @param array $assoc_args List of associative arguments.
346 *
347 * @return array List of arguments with JSON expanded if provided.
348 */
349 public function json_or_args( array $assoc_args ) {
350 if ( isset( $assoc_args['json'] ) ) {
351 $assoc_args = array_merge( $assoc_args, json_decode( $assoc_args['json'], true ) );
352
353 unset( $assoc_args['json'] );
354 }
355
356 return $assoc_args;
357 }
358
359 /**
360 * Determine whether the args validated.
361 *
362 * @since 2.8.0
363 *
364 * @param array $args List of positional arguments.
365 * @param array $assoc_args List of associative arguments.
366 * @param string $method Method name.
367 * @param Base_Endpoint $endpoint Endpoint object.
368 *
369 * @return array|WP_Error The associative args that validated or the WP_Error object with what failed.
370 */
371 public function validate_args( array $args, array $assoc_args, $method, Base_Endpoint $endpoint ) {
372 $rest_args = $this->get_rest_args( $method, $endpoint );
373
374 if ( empty( $rest_args ) ) {
375 return $assoc_args;
376 }
377
378 foreach ( $rest_args as $param => $arg ) {
379 // Handle path args.
380 if ( isset( $arg['in'] ) && 'path' === $arg['in'] ) {
381 if ( empty( $args ) ) {
382 if ( empty( $arg['required'] ) ) {
383 continue;
384 }
385
386 // translators: %s is the parameter name.
387 return new WP_Error( 'cli-missing-positional-argument', sprintf( __( 'Missing positional argument: %s', 'pods' ), $param ) );
388 }
389
390 $value = array_shift( $args );
391
392 $value = $this->validate_arg( $value, $arg, $param );
393
394 if ( is_wp_error( $value ) ) {
395 return $value;
396 }
397
398 $assoc_args[ $param ] = $value;
399
400 continue;
401 }
402
403 // Handle normal args.
404 $value = null;
405
406 if ( isset( $assoc_args[ $param ] ) ) {
407 $value = $assoc_args[ $param ];
408 }
409
410 $value = $this->validate_arg( $value, $arg, $param );
411
412 if ( is_wp_error( $value ) ) {
413 return $value;
414 }
415
416 if ( null !== $value ) {
417 $assoc_args[ $param ] = $value;
418 }
419 }
420
421 return $assoc_args;
422 }
423
424 /**
425 * Determine whether the arg validates.
426 *
427 * @since 2.8.0
428 *
429 * @param mixed $value CLI value provided.
430 * @param array $arg REST API argument options.
431 * @param string $param Parameter name.
432 *
433 * @return mixed|WP_Error The argument value or the WP_Error object with what failed to validate.
434 */
435 public function validate_arg( $value, array $arg, $param ) {
436 $is_required = ! empty( $arg['required'] );
437 $is_null = null === $value;
438
439 if ( $is_null ) {
440 if ( ! $is_required ) {
441 return $value;
442 }
443
444 // translators: %s is the parameter name.
445 return new WP_Error( 'cli-argument-required', sprintf( __( 'Argument is required: %s', 'pods' ), $param ) );
446 }
447
448 if ( isset( $arg['type'] ) && 'integer' === $arg['type'] ) {
449 $value = (int) $value;
450 }
451
452 if ( ! empty( $arg['validate_callback'] ) && is_callable( $arg['validate_callback'] ) ) {
453 $valid = call_user_func( $arg['validate_callback'], $value );
454
455 if ( ! $valid ) {
456 $callable_name = null;
457
458 if ( is_array( $arg['validate_callback'] ) ) {
459 $callable_name = '';
460
461 if ( is_object( $arg['validate_callback'][0] ) ) {
462 $callable_name = get_class( $arg['validate_callback'][0] ) . '::';
463 } elseif ( is_string( $arg['validate_callback'][0] ) ) {
464 $callable_name = $arg['validate_callback'][0] . '::';
465 }
466
467 $callable_name .= $arg['validate_callback'][1];
468 } elseif ( is_string( $arg['validate_callback'] ) ) {
469 $callable_name = $arg['validate_callback'];
470 }
471
472 if ( empty( $callable_name ) ) {
473 // translators: %s is the parameter name.
474 return new WP_Error( 'cli-argument-not-valid', sprintf( __( 'Argument not provided as expected: %s', 'pods' ), $param ) );
475 }
476
477 // translators: %1$s is the validation callback name, %2$s is the parameter name.
478 return new WP_Error( 'cli-argument-not-valid-with-callback', sprintf( __( 'Argument not provided as expected (%1$s): %2$s', 'pods' ), $callable_name, $param ) );
479 }
480
481 if ( is_wp_error( $valid ) ) {
482 return $valid;
483 }
484 }
485
486 $valid = rest_validate_value_from_schema( $value, $arg, $param );
487
488 if ( ! $valid ) {
489 return '';
490 }
491
492 if ( is_wp_error( $valid ) ) {
493 return $valid;
494 }
495
496 return $value;
497 }
498
499 /**
500 * Get list of REST API arguments from endpoint.
501 *
502 * @since 2.8.0
503 *
504 * @param string $command Command name.
505 * @param Base_Endpoint $endpoint Endpoint object.
506 *
507 * @return array List of REST API arguments.
508 */
509 public function get_rest_args( $command, Base_Endpoint $endpoint ) {
510 $command_mapping = [
511 'list' => 'READ_args',
512 'add' => 'CREATE_args',
513 'create' => 'CREATE_args',
514 'get' => 'READ_args',
515 'update' => 'EDIT_args',
516 'delete' => 'DELETE_args',
517 ];
518
519 if ( ! isset( $command_mapping[ $command ] ) ) {
520 return [];
521 }
522
523 $method = $command_mapping[ $command ];
524
525 if ( ! method_exists( $endpoint, $method ) ) {
526 return [];
527 }
528
529 $rest_args = $endpoint->$method();
530
531 if ( empty( $rest_args ) ) {
532 return [];
533 }
534
535 return $rest_args;
536 }
537
538 /**
539 * Get list of properly formatted CLI command arguments.
540 *
541 * @since 2.8.0
542 *
543 * @param string $command Command name.
544 * @param Base_Endpoint $endpoint Endpoint object.
545 *
546 * @return array List of properly formatted CLI command arguments.
547 */
548 public function build_command_args( $command, Base_Endpoint $endpoint ) {
549 $rest_args = $this->get_rest_args( $command, $endpoint );
550
551 if ( empty( $rest_args ) ) {
552 return [];
553 }
554
555 $cli_args = [
556 'synopsis' => [],
557 ];
558
559 foreach ( $rest_args as $param => $arg ) {
560 $cli_arg = [
561 'type' => 'assoc',
562 'name' => $param,
563 'optional' => empty( $arg['required'] ),
564 ];
565
566 if ( ! empty( $arg['description'] ) ) {
567 $cli_arg['description'] = $arg['description'];
568 }
569
570 if ( ! empty( $arg['default'] ) ) {
571 $cli_arg['default'] = $arg['default'];
572 }
573
574 if ( isset( $arg['in'] ) && 'path' === $arg['in'] ) {
575 // Handle path args.
576 $cli_arg['type'] = 'positional';
577 } elseif ( isset( $arg['cli_boolean'] ) && $arg['cli_boolean'] ) {
578 // Handle flag args.
579 $cli_arg['type'] = 'flag';
580 } elseif ( ! empty( $arg['enum'] ) ) {
581 // Handle enum options.
582 $cli_arg['options'] = $arg['enum'];
583 }
584
585 $cli_args['synopsis'][] = $cli_arg;
586 }
587
588 return $cli_args;
589 }
590
591 /**
592 * Output the CLI error response from the WP_Error object.
593 *
594 * @since 2.8.0
595 *
596 * @param WP_Error $error The error object.
597 *
598 * @throws WP_CLI\ExitException
599 */
600 public function output_error_response( WP_Error $error ) {
601 $error_message = sprintf( '%1$s [%2$s]', $error->get_error_message(), $error->get_error_code() );
602
603 WP_CLI::error( $error_message );
604 }
605 }
606