Skip to content

Make Formatter extensible with custom format handlers#6238

Open
Copilot wants to merge 18 commits intomainfrom
copilot/make-formats-extensible
Open

Make Formatter extensible with custom format handlers#6238
Copilot wants to merge 18 commits intomainfrom
copilot/make-formats-extensible

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 12, 2026

Commands providing --format outputs lacked a standardized extension mechanism. Extensions couldn't add custom formats (e.g., XML, Nagios-compatible output) without modifying core.

Changes

Format Registration API

  • Added Formatter::add_format($name, $handler) for registering custom format handlers
  • Handlers receive ($items, $fields, $context) and output directly
  • Custom formatters stored in static $custom_formatters array
  • Built-in formats can now be overridden by registering custom handlers with the same name

Built-in Formats Refactored

  • All built-in formats (table, json, csv, yaml, count, ids) now use the same add_format() API
  • Built-in formats are registered via InitializeFormatter bootstrap step during startup
  • Removed hardcoded switch statements in favor of unified format handler mechanism
  • Built-in formats are treated identically to custom formats, enabling consistent override behavior

Format Discovery

  • Added Formatter::get_available_formats() returning all registered formats
  • Added formatter_available_formats hook for filtering the format list
  • Format list is built dynamically from registered handlers

Single-Item Display Support

  • Format handlers accept optional third parameter $context for contextual information
  • JSON/YAML handlers check for $context['single_item'] flag to output without array wrappers
  • Enables proper single-item display (e.g., {...} instead of [{...}]) through unified API
  • Eliminates need for special-case handling in single-item display methods

Format Resolution

  • Updated format() and show_multiple_fields() to use registered format handlers exclusively
  • Custom formatters receive proper field resolution with prefix support via validate_fields()
  • Custom handlers receive properly filtered items matching requested fields with resolved field names
  • Special handling for table/csv formats (convert to rows) and count/ids formats (work with raw items)

Example

// Register XML format
WP_CLI\Formatter::add_format( 'xml', function( $items, $fields, $context = [] ) {
    // Check if single-item display
    $is_single = isset( $context['single_item'] ) && $context['single_item'];
    
    echo "<?xml version=\"1.0\"?>\n<items>\n";
    foreach ( $items as $item ) {
        echo "  <item>\n";
        foreach ( $item as $key => $value ) {
            echo "    <{$key}>" . htmlspecialchars( $value ) . "</{$key}>\n";
        }
        echo "  </item>\n";
    }
    echo "</items>\n";
});

// Override built-in JSON format
WP_CLI\Formatter::add_format( 'json', function( $items, $fields, $context = [] ) {
    $is_single = isset( $context['single_item'] ) && $context['single_item'] && count( $items ) === 1;
    $output = $is_single ? reset( $items ) : $items;
    echo json_encode( $output, JSON_PRETTY_PRINT );
});

// Use in commands with prefix support
$formatter = new WP_CLI\Formatter( [ 'format' => 'xml' ], [ 'title', 'status' ], 'post' );
$formatter->display_items( $items ); // Resolves 'title' to 'post_title', 'status' to 'post_status'

All built-in formats (table, json, csv, yaml, count, ids) work as before and can be overridden by registering a handler with the same name.

Original prompt

This section details on the original issue you should resolve

<issue_title>Make formats extensible</issue_title>
<issue_description>For the commands that provide multiple --format outputs, we should discuss providing a standardized extension mechanism.

  • Make the array of available formats filterable.
  • Provide a way to map a format to an implementation that transforms the provided data into that format.

In this way, one could provide optional packages that add a specific format (like a Nagios-compatible output or an XML output) across the board.</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@github-actions github-actions bot added command:cli Related to 'cli' command command:cli-param-dump Related to 'cli param-dump' command command:cli-utils labels Feb 12, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 12, 2026

Codecov Report

❌ Patch coverage is 31.70732% with 56 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
php/WP_CLI/Formatter.php 33.33% 52 Missing ⚠️
php/WP_CLI/Bootstrap/InitializeFormatter.php 0.00% 3 Missing ⚠️
php/bootstrap.php 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copilot AI and others added 3 commits February 12, 2026 22:33
- Add static $custom_formatters array to track custom format handlers
- Add Formatter::add_format() method to register custom formatters
- Add Formatter::get_available_formats() method with filterable format list
- Update format() and show_multiple_fields() to support custom handlers
- Add PHPUnit tests for custom format functionality
- Add Behat feature tests for custom format scenarios

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
- Update Formatter class docblock to mention extensibility
- Add detailed examples to add_format() method
- Add usage examples to get_available_formats() method
- Document filter hook 'formatter_available_formats'

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
- Add BUILTIN_FORMATS constant for better maintainability
- Add debug message for skipped items in custom formatters
- Improve test coverage for parameter passing and field filtering
- Add type assertions to satisfy PHPStan

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement extensible format options for commands Make Formatter extensible with custom format handlers Feb 12, 2026
Copilot AI requested a review from swissspidy February 12, 2026 22:40
@swissspidy

This comment was marked as resolved.

gemini-code-assist[bot]

This comment was marked as resolved.

@swissspidy

This comment was marked as resolved.

@swissspidy swissspidy requested a review from Copilot March 10, 2026 21:14

This comment was marked as resolved.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ix field resolution for custom formatters

- Fix handler signatures in tests to accept ($items, $fields) parameters
- Remove restriction preventing override of built-in formats
- Add field validation and prefix resolution for custom formatters
- Check custom formatters before built-in switch statement to enable overriding
- Add test for prefix field resolution with custom formatters
- Add test for overriding built-in formats

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>

This comment was marked as resolved.

@swissspidy swissspidy marked this pull request as ready for review March 11, 2026 16:47
@swissspidy swissspidy requested a review from a team as a code owner March 11, 2026 16:47
@swissspidy

This comment was marked as resolved.

- Add static flag to track built-in format registration
- Create register_builtin_formats() method to register all built-in formats using add_format()
- Register count, ids, json, yaml, csv, and table formats via the API
- Remove switch statements from format() and show_multiple_fields() methods
- All built-in formats now use the same registration mechanism as custom formats
- Built-in formats can be overridden by calling add_format() with the same name
- Special handling for ids/count formats (work with raw items) and table format (needs formatter instance)

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/wp-cli/wp-cli/sessions/e94ee5b4-3181-4bf6-b2d2-3ba9a27776c6

This comment was marked as resolved.

- Remove BUILTIN_FORMATS constant, use array_keys($custom_formatters) instead
- Remove defensive type check for hook return value (trust the hook)
- Create InitializeFormatter bootstrap step to register built-in formats
- Move format registration from lazy initialization to bootstrap
- Remove special handling for json/yaml in show_multiple_fields
- Make register_builtin_formats() public and remove flag check
- Add setUpBeforeClass to FormatterTest for test initialization

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/wp-cli/wp-cli/sessions/44e4d3bc-205c-4f5f-b5f4-892faf288cf9
Copilot AI requested a review from swissspidy March 23, 2026 10:54
@swissspidy

This comment was marked as resolved.

Fixes test failures where single-item display was incorrectly wrapping
items in an array. WP_CLI::print_value() properly handles single items
by outputting them without array wrappers, which is required for json/yaml
formats in single-item mode (display_item).

Multi-item display continues to use registered format handlers directly.

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/wp-cli/wp-cli/sessions/747ee921-fbd9-4706-97a9-551bbfc7849b

This comment was marked as resolved.

@swissspidy

This comment was marked as resolved.

- Add optional context parameter to json/yaml format handlers
- Handlers check for 'single_item' flag to output without array wrapper
- Remove WP_CLI::print_value() usage in show_multiple_fields
- Pass context flag when calling formatters for single-item display
- Maintains backward compatibility for custom format handlers

Co-authored-by: swissspidy <841956+swissspidy@users.noreply.github.com>
Agent-Logs-Url: https://github.com/wp-cli/wp-cli/sessions/2e99076d-2ca2-4d77-a5a8-eb3b535c9c25

This comment was marked as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

command:cli Related to 'cli' command command:cli-param-dump Related to 'cli param-dump' command command:cli-utils

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make formats extensible

3 participants