Skip to content
Open
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 98 additions & 34 deletions src/Theme_Mod_Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,49 +85,113 @@ public function get( $args = array(), $assoc_args = array() ) {
$args = array();
}

$list = array();
$mods = get_theme_mods();
// This array will hold the list of theme mods in a format suitable for the WP CLI Formatter.
$mod_list = array();

// 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();
if ( ! is_array( $mods ) ) {
// If no mods are set (perhaps new theme), make sure foreach still works.
// If no mods are set (perhaps new theme), make sure array_intersect_key still works.
Comment thread
philipjohn marked this conversation as resolved.
Outdated
$mods = array();
}
foreach ( $mods as $k => $v ) {
// If mods were given, skip the others.
if ( ! empty( $args ) && ! in_array( $k, $args, true ) ) {
continue;
}

if ( is_array( $v ) ) {
$list[] = [
'key' => $k,
'value' => '=>',
];
foreach ( $v as $_k => $_v ) {
$list[] = [
'key' => " $_k",
'value' => $_v,
];
}
} else {
$list[] = [
'key' => $k,
'value' => $v,
];
// Generate the list of items ready for output. We use an initial separator that we can replace later depending on format.
$separator = '!!!';
Comment thread
philipjohn marked this conversation as resolved.
Outdated
array_walk(
$mods,
function ( $value, $key ) use ( &$mod_list, $separator ) {
Comment on lines +96 to +103
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.
$this->mod_to_string( $key, $value, $mod_list, $separator );
}
}
);

// For unset mods, show blank value.
foreach ( $args as $mod ) {
if ( ! isset( $mods[ $mod ] ) ) {
$list[] = [
'key' => $mod,
'value' => '',
];
}
// Take our Formatter-friendly list and adjust it according to the requested format.
switch ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'format' ) ) {
Comment thread
philipjohn marked this conversation as resolved.
Outdated
// For tables we use a double space to indent child items.
case 'table':
$mod_list = array_map(
function ( $item ) {
Comment thread
philipjohn marked this conversation as resolved.
Outdated
$parts = explode( '!!!', $item['key'] );
$new_key = array_pop( $parts );
if ( ! empty( $parts ) ) {
$new_key = str_repeat( ' ', count( $parts ) ) . $new_key;
}
return [
'key' => $new_key,
'value' => $item['value'],
];
},
$mod_list
);
break;

// 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(
function ( $item ) {
return [
'key' => str_replace( '!!!', '.', $item['key'] ),
'value' => $item['value'],
];
},
$mod_list
),
function ( $item ) {
return ! empty( $item['value'] );
Comment thread
swissspidy marked this conversation as resolved.
Outdated
}
);
break;
Comment on lines +128 to +146
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.
}

// Output the list using the WP CLI Formatter.
$formatter = new \WP_CLI\Formatter( $assoc_args, $this->fields, 'thememods' );
$formatter->display_items( $list );
$formatter->display_items( $mod_list );
}

/**
* Convert the theme mods to a flattened array with a string representation of the keys.
*
* @param string $key The mod key
* @param mixed $value The value of the mod.
* @param array $mod_list The list so far, passed by reference.
* @param string $separator A string to separate keys to denote their place in the tree.
*/
private function mod_to_string( $key, $value, &$mod_list, $separator ) {
if ( is_array( $value ) || is_object( $value ) ) {
// Convert objects to arrays for easier handling.
$value = (array) $value;

// Explicitly handle empty arrays to ensure they are displayed.
if ( empty( $value ) ) {
$mod_list[] = array(
'key' => $key,
'value' => '[empty array]',
);
return;
}

// Arrays get their own entry in the list to allow for sensible table output.
$mod_list[] = array(
'key' => $key,
'value' => '',
);

foreach ( $value as $child_key => $child_value ) {
$this->mod_to_string( $key . $separator . $child_key, $child_value, $mod_list, $separator );
}
} else {
// Explicitly handle boolean values to ensure they are displayed correctly.
if ( is_bool( $value ) ) {
$value = $value ? '[true]' : '[false]';
}

$mod_list[] = array(
'key' => $key,
'value' => $value,
);
}
}

/**
Expand Down
Loading