Skip to content

Conversation

@philipjohn
Copy link

Fixes #96

I maintained but improved the space-indented formatting for the table format as folks might be using this (as per @schlessera's comment) and then fixed the other formats by using dot notation to represent each field, given the formatter needs arrays with key and value.

For the table format, I opted to remove the => for the value to represent an array. I felt it was harder to read than simply leaving the value blank and letting the indent do the visual work.

For other formats I stripped out anything without a value.

For all formats booleans and empty arrays are represented by a string (e.g. [empty array]. There may be a better way to represent these, and I did consider var_export() but others may have better ideas.

Here are some examples of the new output:

$ wp eval 'print_r(get_theme_mods());'
Array
(
    [0] => 
    [nav_menu_locations] => Array
        (
        )

    [sidebars_widgets] => Array
        (
            [time] => 1749114163
            [data] => Array
                (
                    [wp_inactive_widgets] => Array
                        (
                            [0] => block-2
                            [1] => block-3
                            [2] => block-4
                            [3] => block-5
                            [4] => block-6
                        )

                    [sidebar-1] => Array
                        (
                            [0] => block-7
                        )

                )

        )

)
$ wp theme mod get --all --format=table
+-------------------------+---------------+
| key                     | value         |
+-------------------------+---------------+
| 0                       | [false]       |
| nav_menu_locations      | [empty array] |
| sidebars_widgets        |               |
|   time                  | 1749114163    |
|   data                  |               |
|     wp_inactive_widgets |               |
|       0                 | block-2       |
|       1                 | block-3       |
|       2                 | block-4       |
|       3                 | block-5       |
|       4                 | block-6       |
|     sidebar-1           |               |
|       0                 | block-7       |
+-------------------------+---------------+
$ wp theme mod get --all --format=csv
key,value
0,[false]
nav_menu_locations,"[empty array]"
sidebars_widgets.time,1749114163
sidebars_widgets.data.wp_inactive_widgets.0,block-2
sidebars_widgets.data.wp_inactive_widgets.1,block-3
sidebars_widgets.data.wp_inactive_widgets.2,block-4
sidebars_widgets.data.wp_inactive_widgets.3,block-5
sidebars_widgets.data.wp_inactive_widgets.4,block-6
sidebars_widgets.data.sidebar-1.0,block-7
$ wp theme mod get --all --format=yaml
---
- 
  key: "0"
  value: '[false]'
- 
  key: nav_menu_locations
  value: '[empty array]'
- 
  key: sidebars_widgets.time
  value: 1749114163
- 
  key: sidebars_widgets.data.wp_inactive_widgets.0
  value: block-2
- 
  key: sidebars_widgets.data.wp_inactive_widgets.1
  value: block-3
- 
  key: sidebars_widgets.data.wp_inactive_widgets.2
  value: block-4
- 
  key: sidebars_widgets.data.wp_inactive_widgets.3
  value: block-5
- 
  key: sidebars_widgets.data.wp_inactive_widgets.4
  value: block-6
- 
  key: sidebars_widgets.data.sidebar-1.0
  value: block-7
$ wp theme mod get --all --format=json | jq .
[
  {
    "key": "0",
    "value": "[false]"
  },
  {
    "key": "nav_menu_locations",
    "value": "[empty array]"
  },
  {
    "key": "sidebars_widgets.time",
    "value": 1749114163
  },
  {
    "key": "sidebars_widgets.data.wp_inactive_widgets.0",
    "value": "block-2"
  },
  {
    "key": "sidebars_widgets.data.wp_inactive_widgets.1",
    "value": "block-3"
  },
  {
    "key": "sidebars_widgets.data.wp_inactive_widgets.2",
    "value": "block-4"
  },
  {
    "key": "sidebars_widgets.data.wp_inactive_widgets.3",
    "value": "block-5"
  },
  {
    "key": "sidebars_widgets.data.wp_inactive_widgets.4",
    "value": "block-6"
  },
  {
    "key": "sidebars_widgets.data.sidebar-1.0",
    "value": "block-7"
  }
]

@philipjohn philipjohn requested a review from a team as a code owner June 5, 2025 14:04
Copy link
Member

@schlessera schlessera left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, only minor nitpicks for the code. Also, it would be good to have at least 1 feature test covering this.

@philipjohn philipjohn requested a review from schlessera June 6, 2025 08:14
@philipjohn
Copy link
Author

Oh, I will do tests by the way! Just probably not until after this week :)

@github-actions github-actions bot added command:theme-mod-get Related to 'theme mod get' command scope:distribution Related to distribution scope:testing Related to testing labels Dec 22, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the wp theme mod get implementation to produce well-structured output across table, CSV, JSON, and YAML formats, particularly for nested option values, addressing malformed output reported in issue #96.

Changes:

  • Introduces a recursive mod_to_string() helper to flatten nested theme mod arrays/objects into a list of { key, value } pairs, with special handling for booleans and empty arrays.
  • Adjusts table formatting to use indentation for nested keys and uses dot-notation for hierarchical keys in CSV/JSON/YAML formats, while filtering out items deemed to have “no value” in non-tabular formats.
  • Updates usage of WP_CLI\Utils\get_flag_value() via an imported Utils alias and reuses the new formatter logic for both get and list_ subcommands.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +96 to +103
// If specific mods are requested, filter out any that aren't requested.
$mods = ! empty( $args ) ? array_intersect_key( get_theme_mods(), array_flip( $args ) ) : get_theme_mods();

// For unset mods, show blank value.
foreach ( $args as $mod ) {
if ( ! isset( $mods[ $mod ] ) ) {
$list[] = [
'key' => $mod,
'value' => '',
];
// Generate the list of items ready for output. We use an initial separator that we can replace later depending on format.
$separator = '\t';
array_walk(
$mods,
function ( $value, $key ) use ( &$mod_list, $separator ) {
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous implementation added rows for requested mods that are not present in get_theme_mods() (see the old "For unset mods, show blank value." logic), which is relied on by the theme-mod.feature example for header_textcolor. With this new $mods construction and no subsequent pass over $args, requested-but-unset mods are now silently omitted from the output for all formats, changing the CLI behavior; to preserve backward compatibility you should add entries with an empty value for any requested mod that is not present in $mods (at least for the table format, and optionally for others).

Copilot uses AI. Check for mistakes.
$mod_list
),
function ( $item ) {
return ! empty( $item['value'] );
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using empty( $item['value'] ) here will also filter out legitimate values like '0' or 0 from the CSV/YAML/JSON output, not just missing values. If the intent is to strip only items with no value, consider checking explicitly for null or an empty string instead of relying on empty(), so that falsy-but-meaningful values are still emitted.

Suggested change
return ! empty( $item['value'] );
return $item['value'] !== '' && $item['value'] !== null;

Copilot uses AI. Check for mistakes.
@@ -1,5 +1,7 @@
<?php

use \WP_CLI\Utils;
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The import here uses a leading backslash, whereas other commands in this repository consistently use use WP_CLI\Utils; without it (for example, src/Theme_Command.php:5 and src/Plugin_Command.php:5). For consistency with established codebase conventions, it would be better to drop the leading backslash from this use statement.

Copilot uses AI. Check for mistakes.
Comment on lines +128 to +146
// For JSON, CSV, and YAML formats we use dot notation to show the hierarchy.
case 'csv':
case 'yaml':
case 'json':
$mod_list = array_filter(
array_map(
static function ( $item ) use ( $separator ) {
return [
'key' => str_replace( $separator, '.', $item['key'] ),
'value' => $item['value'],
];
},
$mod_list
),
function ( $item ) {
return ! empty( $item['value'] );
}
);
break;
Copy link

Copilot AI Feb 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new flattening and dot-notation logic for CSV/YAML/JSON formats introduces non-trivial behavior but currently has no dedicated acceptance tests (unlike features/theme-mod-list.feature, which covers those formats for the list subcommand). Given that issue #96 was caused by malformed non-tabular output, it would be valuable to add Behat coverage for wp theme mod get --all --format=csv|json|yaml with nested theme-mod structures to ensure this formatter behavior is exercised and guarded against regressions.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

command:theme-mod-get Related to 'theme mod get' command scope:distribution Related to distribution scope:testing Related to testing

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wp theme mod get --all produces malformed output for non-tabular formats

3 participants