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