PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 28.5
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v28.5
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
← All changes | src/myyoast-client/user-interface/auth-command.php +178 -69 27.728.5 View file →
@@ -4,18 +4,21 @@
4 4
5 5 use Exception;
6 6 use WP_CLI;
7 7 use WP_CLI\ExitException;
8 +use WP_CLI\Utils;
8 9 use Yoast\WP\SEO\Commands\Command_Interface;
9 10 use Yoast\WP\SEO\Conditionals\MyYoast_Connection_Conditional;
10 -use Yoast\WP\SEO\General\User_Interface\General_Page_Integration;
11 11 use Yoast\WP\SEO\Loadable_Interface;
12 12 use Yoast\WP\SEO\Main;
13 +use Yoast\WP\SEO\MyYoast_Client\Application\Exceptions\Registration_Temporarily_Unavailable_Exception;
13 14 use Yoast\WP\SEO\MyYoast_Client\Application\MyYoast_Client;
14 15 use Yoast\WP\SEO\MyYoast_Client\Application\MyYoast_Client_Cleanup;
15 16 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\Client_Registration_Interface;
16 17 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\Token_Storage_Interface;
17 18 use Yoast\WP\SEO\MyYoast_Client\Application\Ports\User_Token_Storage_Interface;
19 +use Yoast\WP\SEO\MyYoast_Client\Domain\Exceptions\Invalid_Resource_Exception;
20 +use Yoast\WP\SEO\MyYoast_Client\Domain\Resource_Indicator;
18 21 use Yoast\WP\SEO\MyYoast_Client\Domain\Token_Set;
19 22 use Yoast\WP\SEO\MyYoast_Client\Infrastructure\OIDC\Issuer_Config;
20 23
21 24 /**
@@ -120,8 +123,14 @@
120 123 * a specific user's token status.
121 124 *
122 125 * ## OPTIONS
123 126 *
127 + * [--resource=<uri>]
128 + * : Show status for a specific RFC 8707 resource indicator. Omit to target the default resource. Cannot be combined with --all-resources.
129 + *
130 + * [--all-resources]
131 + * : Show status for every stored resource bucket. Cannot be combined with --resource.
132 + *
124 133 * [--format=<format>]
125 134 * : Output format.
126 135 * ---
127 136 * default: table
@@ -133,8 +142,10 @@
133 142 * ## EXAMPLES
134 143 *
135 144 * wp yoast auth status
136 145 * wp yoast auth status --user=admin
146 + * wp yoast auth status --resource=https://ai.yoa.st
147 + * wp yoast auth status --all-resources
137 148 * wp yoast auth status --format=json
138 149 *
139 150 * @when after_wp_load
140 151 *
@@ -156,14 +167,34 @@
156 167 if ( $registered_client !== null ) {
157 168 $client_id = $registered_client->get_client_id();
158 169 }
159 170
160 - $site_token = $this->token_storage->get();
161 - $site_token_info = $this->build_token_info( $site_token );
171 + $has_all = (bool) Utils\get_flag_value( $assoc_args, 'all-resources', false );
172 + $resource = Utils\get_flag_value( $assoc_args, 'resource' );
162 173
163 - $user_token = ( $user_id > 0 ) ? $this->user_token_storage->get( $user_id ) : null;
164 - $user_token_info = $this->build_token_info( $user_token );
174 + if ( $has_all && $resource !== null && $resource !== '' ) {
175 + WP_CLI::error( '--all-resources and --resource cannot be combined.' );
176 + }
165 177
178 + if ( $has_all ) {
179 + $user_tokens = ( $user_id > 0 ) ? $this->user_token_storage->get_all( $user_id ) : [];
180 + $site_tokens = $this->token_storage->get_all();
181 + }
182 + else {
183 + try {
184 + $resource_filter = new Resource_Indicator( ( $resource !== null && $resource !== '' ) ? (string) $resource : null );
185 + }
186 + catch ( Invalid_Resource_Exception $e ) {
187 + WP_CLI::error( 'Invalid resource indicator: ' . $e->getMessage() );
188 + return;
189 + }
190 +
191 + $user_tokens = ( $user_id > 0 ) ? \array_filter( [ $this->user_token_storage->get( $user_id, $resource_filter ) ] ) : [];
192 + $site_tokens = \array_filter( [ $this->token_storage->get( $resource_filter ) ] );
193 + }
194 +
195 + $format = Utils\get_flag_value( $assoc_args, 'format', 'table' );
196 +
166 197 $data = [
167 198 'issuer_url' => $issuer_url,
168 199 'software_statement' => ( $has_software ) ? 'configured' : 'not configured',
169 200 'initial_access_token' => ( $has_iat ) ? 'configured' : 'not configured',
@@ -168,19 +199,14 @@
168 199 'software_statement' => ( $has_software ) ? 'configured' : 'not configured',
169 200 'initial_access_token' => ( $has_iat ) ? 'configured' : 'not configured',
170 201 'registered' => ( $is_registered ) ? 'yes' : 'no',
171 202 'client_id' => ( $client_id ?? '-' ),
172 - 'site_token' => $site_token_info['status'],
173 - 'site_token_expires' => $site_token_info['expires'],
174 - 'site_token_scopes' => $site_token_info['scopes'],
175 203 'user_id' => ( $user_id > 0 ) ? $user_id : 'none (use --user flag)',
176 - 'user_token' => $user_token_info['status'],
177 - 'user_token_expires' => $user_token_info['expires'],
178 - 'user_token_scopes' => $user_token_info['scopes'],
179 - 'user_token_errors' => $user_token_info['error_count'],
204 + 'user_tokens' => $this->build_token_inventory( $user_tokens ),
205 + 'site_tokens' => $this->build_token_inventory( $site_tokens ),
180 206 ];
181 207
182 - $this->output( $data, $assoc_args['format'] );
208 + $this->output( $data, $format );
183 209 }
184 210
185 211 /**
186 212 * Registers the site as an OAuth client.
@@ -216,16 +242,20 @@
216 242 *
217 243 * @throws ExitException When registration fails.
218 244 */
219 245 public function register( $args = null, $assoc_args = null ): void {
220 - if ( isset( $assoc_args['force'] ) ) {
246 + if ( Utils\get_flag_value( $assoc_args, 'force', false ) ) {
221 247 $this->myyoast_client->deregister();
222 248 WP_CLI::log( 'Deregistered existing client.' );
223 249 }
224 250
225 251 try {
226 - $redirect_uri = \get_admin_url( null, 'admin.php?page=' . General_Page_Integration::PAGE . '&yoast_myyoast_oauth_callback=1' );
227 - $client = $this->myyoast_client->ensure_registered( [ $redirect_uri ] );
252 + $client = $this->myyoast_client->ensure_registered();
253 + } catch ( Registration_Temporarily_Unavailable_Exception $e ) {
254 + $retry_after = $e->get_retry_after_seconds();
255 + $retry_hint = ( $retry_after !== null ) ? \sprintf( ' Try again in %d seconds.', $retry_after ) : ' Try again later.';
256 + WP_CLI::error( 'Registration is temporarily unavailable.' . $retry_hint );
257 + return;
228 258 } catch ( Exception $e ) {
229 259 WP_CLI::error( 'Registration failed: ' . $e->getMessage() );
230 260 return;
231 261 }
@@ -234,9 +264,9 @@
234 264 [
235 265 'client_id' => $client->get_client_id(),
236 266 'status' => 'registered',
237 267 ],
238 - $assoc_args['format'],
268 + Utils\get_flag_value( $assoc_args, 'format', 'table' ),
239 269 );
240 270
241 271 WP_CLI::success( 'Client registered: ' . $client->get_client_id() );
242 272 }
@@ -241,9 +271,9 @@
241 271 WP_CLI::success( 'Client registered: ' . $client->get_client_id() );
242 272 }
243 273
244 274 /**
245 - * Verifies the client registration with the server.
275 + * Refreshes the client registration status against the server.
246 276 *
247 277 * Reads the current registration from the authorization server to
248 278 * confirm it is still valid and shows the registration metadata.
249 279 *
@@ -259,11 +289,13 @@
259 289 * ---
260 290 *
261 291 * ## EXAMPLES
262 292 *
263 - * wp yoast auth verify
264 - * wp yoast auth verify --format=json
293 + * wp yoast auth refresh-status
294 + * wp yoast auth refresh-status --format=json
265 295 *
296 + * @subcommand refresh-status
297 + *
266 298 * @when after_wp_load
267 299 *
268 300 * @param array<int, string>|null $args The arguments.
269 301 * @param array<string, string>|null $assoc_args The associative arguments.
@@ -269,19 +301,19 @@
269 301 * @param array<string, string>|null $assoc_args The associative arguments.
270 302 *
271 303 * @return void
272 304 *
273 - * @throws ExitException When verification fails.
305 + * @throws ExitException When the status refresh fails.
274 306 */
275 - public function verify( $args = null, $assoc_args = null ): void {
307 + public function refresh_status( $args = null, $assoc_args = null ): void {
276 308 if ( ! $this->myyoast_client->is_registered() ) {
277 309 WP_CLI::error( 'Not registered. Run "wp yoast auth register" first.' );
278 310 }
279 311
280 312 try {
281 - $metadata = $this->myyoast_client->verify_registration();
313 + $metadata = $this->myyoast_client->refresh_registration_status();
282 314 } catch ( Exception $e ) {
283 - WP_CLI::error( 'Verification failed: ' . $e->getMessage() );
315 + WP_CLI::error( 'Status refresh failed: ' . $e->getMessage() );
284 316 return;
285 317 }
286 318
287 319 // Redact sensitive fields.
@@ -286,9 +318,9 @@
286 318
287 319 // Redact sensitive fields.
288 320 unset( $metadata['registration_access_token'] );
289 321
290 - $this->output( $this->flatten_for_display( $metadata ), $assoc_args['format'] );
322 + $this->output( $metadata, Utils\get_flag_value( $assoc_args, 'format', 'table' ) );
291 323
292 324 WP_CLI::success( 'Registration is valid.' );
293 325 }
294 326
@@ -326,17 +358,17 @@
326 358 }
327 359
328 360 WP_CLI::confirm( 'This will deregister this site from MyYoast and clear all cached tokens. Proceed?', $assoc_args );
329 361
330 - if ( isset( $assoc_args['local-only'] ) ) {
362 + if ( Utils\get_flag_value( $assoc_args, 'local-only', false ) ) {
331 363 $this->client_registration->delete_local_data();
332 - $this->myyoast_client->clear_site_token();
364 + $this->myyoast_client->clear_all_site_tokens();
333 365 WP_CLI::success( 'Local registration data cleared.' );
334 366 return;
335 367 }
336 368
337 369 $result = $this->myyoast_client->deregister();
338 - $this->myyoast_client->clear_site_token();
370 + $this->myyoast_client->clear_all_site_tokens();
339 371
340 372 if ( $result ) {
341 373 WP_CLI::success( 'Client deregistered.' );
342 374 }
@@ -397,8 +429,11 @@
397 429 *
398 430 * [--scopes=<scopes>]
399 431 * : Comma-separated scopes to request.
400 432 *
433 + * [--resource=<uri>]
434 + * : RFC 8707 resource indicator to bind the token to (e.g. https://ai.yoa.st). Omit for the default resource.
435 + *
401 436 * [--code=<code>]
402 437 * : Authorization code from the callback URL (user flow phase 2).
403 438 *
404 439 * [--state=<state>]
@@ -420,8 +455,11 @@
420 455 *
421 456 * # Site-level token (client_credentials):
422 457 * wp yoast auth authorize --site --scopes=service:analytics
423 458 *
459 + * # Site-level token for a non-default resource:
460 + * wp yoast auth authorize --site --resource=https://ai.yoa.st --scopes=service:ai:consume
461 + *
424 462 * # User authorization code flow, phase 1 - get the URL:
425 463 * wp yoast auth authorize --user=admin --scopes=openid,profile
426 464 *
427 465 * # User authorization code flow, phase 2 - exchange the code:
@@ -436,16 +474,19 @@
436 474 *
437 475 * @throws ExitException When authorization fails.
438 476 */
439 477 public function authorize( $args = null, $assoc_args = null ): void {
440 - $scopes = $this->parse_scopes( $assoc_args );
478 + $scopes = $this->parse_scopes( $assoc_args );
479 + $format = Utils\get_flag_value( $assoc_args, 'format', 'table' );
480 + $resource = Utils\get_flag_value( $assoc_args, 'resource' );
481 + $resource_indicator = ( $resource !== null && $resource !== '' ) ? (string) $resource : null;
441 482
442 - if ( isset( $assoc_args['site'] ) ) {
443 - $this->authorize_site( $scopes, $assoc_args['format'] );
483 + if ( Utils\get_flag_value( $assoc_args, 'site', false ) ) {
484 + $this->authorize_site( $scopes, $resource_indicator, $format );
444 485 return;
445 486 }
446 487
447 - $this->authorize_user( $assoc_args, $scopes, $assoc_args['format'] );
488 + $this->authorize_user( $assoc_args, $scopes, $resource_indicator, $format );
448 489 }
449 490
450 491 /**
451 492 * Revokes tokens for the current user and/or the site.
@@ -458,8 +499,14 @@
458 499 *
459 500 * [--site]
460 501 * : Clear the cached site-level token.
461 502 *
503 + * [--resource=<uri>]
504 + * : Limit revocation to a single RFC 8707 resource indicator. Omit to target the default resource.
505 + *
506 + * [--all-resources]
507 + * : Revoke every stored token across all resource indicators. Cannot be combined with --resource.
508 + *
462 509 * [--yes]
463 510 * : Skip confirmation prompt.
464 511 *
465 512 * ## EXAMPLES
@@ -466,8 +513,10 @@
466 513 *
467 514 * wp yoast auth revoke --user=admin
468 515 * wp yoast auth revoke --site
469 516 * wp yoast auth revoke --user=admin --site --yes
517 + * wp yoast auth revoke --user=admin --resource=https://ai.yoa.st
518 + * wp yoast auth revoke --user=admin --site --all-resources
470 519 *
471 520 * @when after_wp_load
472 521 *
473 522 * @param array<int, string>|null $args The arguments.
@@ -475,26 +524,62 @@
475 524 *
476 525 * @return void
477 526 */
478 527 public function revoke( $args = null, $assoc_args = null ): void {
479 - $user_id = \get_current_user_id();
480 - $has_site = isset( $assoc_args['site'] );
481 - $has_user = ( $user_id > 0 );
528 + $user_id = \get_current_user_id();
529 + $has_user = ( $user_id > 0 );
530 + $has_site = (bool) Utils\get_flag_value( $assoc_args, 'site', false );
531 + $has_all_resources = (bool) Utils\get_flag_value( $assoc_args, 'all-resources', false );
532 + $resource = Utils\get_flag_value( $assoc_args, 'resource' );
482 533
483 534 if ( ! $has_site && ! $has_user ) {
484 535 WP_CLI::error( 'Specify --site and/or use the global --user flag.' );
485 536 }
486 537
538 + if ( $has_all_resources && $resource !== null && $resource !== '' ) {
539 + WP_CLI::error( '--all-resources and --resource cannot be combined.' );
540 + }
541 +
542 + $resource_indicator = null;
543 + if ( $resource !== null && $resource !== '' ) {
544 + $resource_indicator = (string) $resource;
545 + try {
546 + new Resource_Indicator( $resource_indicator );
547 + }
548 + catch ( Invalid_Resource_Exception $e ) {
549 + WP_CLI::error( 'Invalid resource indicator: ' . $e->getMessage() );
550 + return;
551 + }
552 + }
553 +
487 554 WP_CLI::confirm( 'This will revoke the specified tokens. Proceed?', $assoc_args );
488 555
489 - if ( $has_user ) {
490 - $this->myyoast_client->revoke_user_token( $user_id );
491 - WP_CLI::log( \sprintf( 'User %d tokens revoked.', $user_id ) );
556 + try {
557 + if ( $has_user ) {
558 + if ( $has_all_resources ) {
559 + $this->myyoast_client->revoke_all_user_tokens( $user_id );
560 + WP_CLI::log( \sprintf( 'User %d tokens revoked across all resources.', $user_id ) );
561 + }
562 + else {
563 + $this->myyoast_client->revoke_user_token( $user_id, $resource_indicator );
564 + WP_CLI::log( \sprintf( 'User %d tokens revoked.', $user_id ) );
565 + }
566 + }
567 +
568 + if ( $has_site ) {
569 + if ( $has_all_resources ) {
570 + $this->myyoast_client->clear_all_site_tokens();
571 + WP_CLI::log( 'All site tokens cleared.' );
572 + }
573 + else {
574 + $this->myyoast_client->clear_site_token( $resource_indicator );
575 + WP_CLI::log( 'Site token cleared.' );
576 + }
577 + }
492 578 }
493 -
494 - if ( $has_site ) {
495 - $this->myyoast_client->clear_site_token();
496 - WP_CLI::log( 'Site token cleared.' );
579 + catch ( Exception $e ) {
580 + WP_CLI::error( 'Revocation failed: ' . $e->getMessage() );
581 + return;
497 582 }
498 583
499 584 WP_CLI::success( 'Done.' );
500 585 }
@@ -534,10 +619,11 @@
534 619 *
535 620 * @throws ExitException When key rotation fails.
536 621 */
537 622 public function rotate_keys( $args = null, $assoc_args = null ): void {
538 - $rotate_registration = isset( $assoc_args['registration'] ) || isset( $assoc_args['all'] );
539 - $rotate_dpop = isset( $assoc_args['dpop'] ) || isset( $assoc_args['all'] );
623 + $rotate_all = (bool) Utils\get_flag_value( $assoc_args, 'all', false );
624 + $rotate_registration = ( $rotate_all || (bool) Utils\get_flag_value( $assoc_args, 'registration', false ) );
625 + $rotate_dpop = ( $rotate_all || (bool) Utils\get_flag_value( $assoc_args, 'dpop', false ) );
540 626
541 627 if ( ! $rotate_registration && ! $rotate_dpop ) {
542 628 WP_CLI::error( 'Specify --registration, --dpop, or --all.' );
543 629 }
@@ -568,18 +654,19 @@
568 654
569 655 /**
570 656 * Performs a client_credentials grant for a site-level token.
571 657 *
572 - * @param string[] $scopes The scopes to request.
573 - * @param string $format The output format.
658 + * @param string[] $scopes The scopes to request.
659 + * @param string|null $resource_indicator The resource indicator (RFC 8707), or null for the default resource.
660 + * @param string $format The output format.
574 661 *
575 662 * @return void
576 663 *
577 664 * @throws ExitException When the token request fails.
578 665 */
579 - private function authorize_site( array $scopes, string $format ): void {
666 + private function authorize_site( array $scopes, ?string $resource_indicator, string $format ): void {
580 667 try {
581 - $token_set = $this->myyoast_client->get_site_token( $scopes );
668 + $token_set = $this->myyoast_client->get_site_token( $scopes, $resource_indicator );
582 669 } catch ( Exception $e ) {
583 670 WP_CLI::error( 'Site token request failed: ' . $e->getMessage() );
584 671 return;
585 672 }
@@ -591,32 +678,33 @@
591 678
592 679 /**
593 680 * Handles the user authorization code flow.
594 681 *
595 - * @param array<string, string> $assoc_args The associative arguments.
596 - * @param string[] $scopes The scopes to request.
597 - * @param string $format The output format.
682 + * @param array<string, string> $assoc_args The associative arguments.
683 + * @param string[] $scopes The scopes to request.
684 + * @param string|null $resource_indicator The resource indicator (RFC 8707), or null for the default resource.
685 + * @param string $format The output format.
598 686 *
599 687 * @return void
600 688 *
601 689 * @throws ExitException When authorization fails.
602 690 */
603 - private function authorize_user( array $assoc_args, array $scopes, string $format ): void {
691 + private function authorize_user( array $assoc_args, array $scopes, ?string $resource_indicator, string $format ): void {
604 692 $user_id = \get_current_user_id();
605 693 if ( $user_id <= 0 ) {
606 694 WP_CLI::error( 'User authorization requires the global --user flag.' );
607 695 }
608 696
609 - $has_code = isset( $assoc_args['code'] );
610 - $has_state = isset( $assoc_args['state'] );
697 + $code = Utils\get_flag_value( $assoc_args, 'code' );
698 + $state = Utils\get_flag_value( $assoc_args, 'state' );
611 699
612 - // Phase 2: exchange the code.
613 - if ( $has_code && $has_state ) {
700 + // Phase 2: exchange the code. The resource was persisted in the flow state during phase 1.
701 + if ( $code !== null && $state !== null ) {
614 702 try {
615 703 $token_set = $this->myyoast_client->exchange_authorization_code(
616 704 $user_id,
617 - $assoc_args['code'],
618 - $assoc_args['state'],
705 + (string) $code,
706 + (string) $state,
619 707 );
620 708 } catch ( Exception $e ) {
621 709 WP_CLI::error( 'Code exchange failed: ' . $e->getMessage() );
622 710 return;
@@ -627,23 +715,25 @@
627 715 WP_CLI::success( 'User authorized.' );
628 716 return;
629 717 }
630 718
631 - if ( $has_code || $has_state ) {
719 + if ( $code !== null || $state !== null ) {
632 720 WP_CLI::error( 'Both --code and --state are required for code exchange.' );
633 721 }
634 722
635 - // Phase 1: generate the authorization URL.
636 - $redirect_uri = \get_admin_url( null, 'admin.php?page=' . General_Page_Integration::PAGE . '&yoast_myyoast_oauth_callback=1' );
723 + // Phase 1: generate the authorization URL. Registration is a prerequisite.
724 + if ( ! $this->myyoast_client->is_registered() ) {
725 + WP_CLI::error( 'Not registered. Run "wp yoast auth register" first.' );
726 + }
637 727
638 728 try {
639 - $url = $this->myyoast_client->get_authorization_url( $user_id, $redirect_uri, $scopes );
729 + $url = $this->myyoast_client->get_authorization_url( $user_id, $scopes, $resource_indicator );
640 730 } catch ( Exception $e ) {
641 731 WP_CLI::error( 'Failed to generate authorization URL: ' . $e->getMessage() );
642 732 return;
643 733 }
644 734
645 - if ( isset( $assoc_args['url-only'] ) ) {
735 + if ( Utils\get_flag_value( $assoc_args, 'url-only', false ) ) {
646 736 WP_CLI::log( $url );
647 737 return;
648 738 }
649 739
@@ -671,8 +761,9 @@
671 761 */
672 762 private function build_token_info( ?Token_Set $token_set ): array {
673 763 if ( $token_set === null ) {
674 764 return [
765 + 'resource' => '-',
675 766 'status' => 'none',
676 767 'expires' => '-',
677 768 'scopes' => '-',
678 769 'error_count' => '-',
@@ -679,8 +770,9 @@
679 770 ];
680 771 }
681 772
682 773 return [
774 + 'resource' => ( $token_set->get_resource_indicator()->is_default() ? '(default)' : $token_set->get_resource_indicator()->value() ),
683 775 'status' => ( $token_set->is_expired() ) ? 'expired' : 'valid',
684 776 'expires' => \gmdate( 'Y-m-d H:i:s', $token_set->get_expires_at() ) . ' UTC',
685 777 'scopes' => ( $token_set->get_scope() ?? '-' ),
686 778 'error_count' => $token_set->get_error_count(),
@@ -687,8 +779,23 @@
687 779 ];
688 780 }
689 781
690 782 /**
783 + * Builds a display-safe inventory of tokens across resource buckets.
784 + *
785 + * @param Token_Set[] $token_sets The token sets.
786 + *
787 + * @return array<int, array<string, string|int>> The inventory.
788 + */
789 + private function build_token_inventory( array $token_sets ): array {
790 + $inventory = [];
791 + foreach ( $token_sets as $token_set ) {
792 + $inventory[] = $this->build_token_info( $token_set );
793 + }
794 + return $inventory;
795 + }
796 +
797 + /**
691 798 * Parses the comma-separated scopes option.
692 799 *
693 800 * @param array<string, string>|null $assoc_args The associative arguments.
694 801 *
@@ -694,13 +801,14 @@
694 801 *
695 802 * @return string[] The parsed scopes.
696 803 */
697 804 private function parse_scopes( $assoc_args ): array {
698 - if ( ! isset( $assoc_args['scopes'] ) || $assoc_args['scopes'] === '' ) {
805 + $scopes = Utils\get_flag_value( $assoc_args, 'scopes', '' );
806 + if ( $scopes === '' ) {
699 807 return [];
700 808 }
701 809
702 - return \array_values( \array_filter( \array_map( 'trim', \explode( ',', $assoc_args['scopes'] ) ) ) );
810 + return \array_values( \array_filter( \array_map( 'trim', \explode( ',', (string) $scopes ) ) ) );
703 811 }
704 812
705 813 /**
706 814 * Flattens nested arrays for table display by JSON-encoding array values.
@@ -713,9 +821,9 @@
713 821 $result = [];
714 822 foreach ( $data as $key => $value ) {
715 823 if ( \is_array( $value ) ) {
716 824 // phpcs:ignore Yoast.Yoast.JsonEncodeAlternative.Found -- WP-CLI display output, not user-facing HTML.
717 - $result[ $key ] = ( \wp_json_encode( $value ) ?? '[]' );
825 + $result[ $key ] = ( \wp_json_encode( $value ) ?? 'err' );
718 826 }
719 827 elseif ( \is_bool( $value ) ) {
720 828 $result[ $key ] = ( $value ) ? 'true' : 'false';
721 829 }
@@ -741,13 +849,14 @@
741 849 WP_CLI::log( ( $encoded !== false ) ? $encoded : '{}' );
742 850 return;
743 851 }
744 852
745 - $items = [];
746 - foreach ( $data as $key => $value ) {
853 + $flat_data = $this->flatten_for_display( $data );
854 + $items = [];
855 + foreach ( $flat_data as $key => $value ) {
747 856 $items[] = [
748 857 'field' => $key,
749 - 'value' => (string) $value,
858 + 'value' => $value,
750 859 ];
751 860 }
752 861
753 862 WP_CLI\Utils\format_items( 'table', $items, [ 'field', 'value' ] );