Add JSON handling for search/replace in custom tables and nested JSON#226
Add JSON handling for search/replace in custom tables and nested JSON#226ratneshjais wants to merge 2 commits intowp-cli:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for searching and replacing strings within JSON-encoded data, including nested structures and escaped URLs. It adds automatic detection of JSON columns and updates the recursive processing logic to handle JSON decoding and encoding. The review feedback identifies a malformed regular expression in the JSON detection logic and suggests a performance optimization to avoid unnecessary json_decode calls by performing a preliminary character check.
| // URLs that a simple SQL REPLACE cannot reach. | ||
| if ( null === $serial_row ) { | ||
| // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- escaped through self::esc_sql_ident | ||
| $serial_row = $wpdb->get_row( "SELECT * FROM $table_sql WHERE $col_sql REGEXP '^[\\\\[{]' LIMIT 1" ); |
There was a problem hiding this comment.
The regular expression ^[\\[{] appears to be malformed as it opens a character class [ but does not close it with a corresponding ]. This will likely result in a database error or fail to match any rows, preventing the automatic detection of JSON columns. A more reliable and readable approach for detecting strings starting with { or [ is to use an alternation.
$serial_row = $wpdb->get_row( "SELECT * FROM $table_sql WHERE $col_sql REGEXP '^(\\\\[|{)' LIMIT 1" );| // Try to decode as a JSON object or array and recurse into the | ||
| // decoded structure. This properly handles URLs stored inside | ||
| // JSON-encoded columns (e.g. Gravity Forms confirmations, block | ||
| // editor font data), including nested JSON where slashes are | ||
| // double-escaped. | ||
| $json_decoded = json_decode( $data, true ); | ||
| if ( null !== $json_decoded && is_array( $json_decoded ) ) { | ||
| $json_decoded = $this->run_recursively( $json_decoded, false, $recursion_level + 1, $visited_data ); | ||
| $json_result = json_encode( $json_decoded ); | ||
| if ( false !== $json_result ) { | ||
| $data = $json_result; | ||
| } | ||
| } elseif ( $this->regex ) { |
There was a problem hiding this comment.
Attempting to json_decode() every string in a column detected as JSON can lead to significant performance degradation, especially for large text fields like post_content. Since valid JSON objects and arrays must start with { or [, adding a quick character check before calling json_decode() will avoid unnecessary parsing for the majority of non-JSON strings.
// Try to decode as a JSON object or array and recurse into the
// decoded structure. This properly handles URLs stored inside
// JSON-encoded columns (e.g. Gravity Forms confirmations, block
// editor font data), including nested JSON where slashes are
// double-escaped.
if ( '' !== $data && ( '{' === $data[0] || '[' === $data[0] ) ) {
$json_decoded = json_decode( $data, true );
if ( null !== $json_decoded && is_array( $json_decoded ) ) {
$json_decoded = $this->run_recursively( $json_decoded, false, $recursion_level + 1, $visited_data );
$json_result = json_encode( $json_decoded );
if ( false !== $json_result ) {
$data = $json_result;
}
}
} elseif ( $this->regex ) {…refix for custom tables - Update JSON post content test to expect PHP type (JSON detection now routes to PHP mode) - Add --all-tables-with-prefix flag for custom table tests so WP-CLI can find them - Fix double replacement by making JSON decode path and str_replace/regex mutually exclusive Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Which issue is this for? Some additional context would be helpful. |
|
Hello! 👋 Thanks for opening this pull request! Please check out our contributing guidelines. We appreciate you taking the initiative to contribute to this project. Contributing isn't limited to just code. We encourage you to contribute in the way that best fits your abilities, by writing tutorials, giving a demo at your local meetup, helping other users with their support questions, or revising our documentation. Here are some useful Composer commands to get you started:
To run a single Behat test, you can use the following command: # Run all tests in a single file
composer behat features/some-feature.feature
# Run only a specific scenario (where 123 is the line number of the "Scenario:" title)
composer behat features/some-feature.feature:123You can find a list of all available Behat steps in our handbook. |
No description provided.