Git Product home page Git Product logo

search-replace-command's Introduction

wp-cli/search-replace-command

Searches/replaces strings in the database.

Testing

Quick links: Using | Installing | Contributing | Support

Using

wp search-replace <old> <new> [<table>...] [--dry-run] [--network] [--all-tables-with-prefix] [--all-tables] [--export[=<file>]] [--export_insert_size=<rows>] [--skip-tables=<tables>] [--skip-columns=<columns>] [--include-columns=<columns>] [--precise] [--recurse-objects] [--verbose] [--regex] [--regex-flags=<regex-flags>] [--regex-delimiter=<regex-delimiter>] [--regex-limit=<regex-limit>] [--format=<format>] [--report] [--report-changed-only] [--log[=<file>]] [--before_context=<num>] [--after_context=<num>]

Searches through all rows in a selection of tables and replaces appearances of the first string with the second string.

By default, the command uses tables registered to the $wpdb object. On multisite, this will just be the tables for the current site unless --network is specified.

Search/replace intelligently handles PHP serialized data, and does not change primary key values.

OPTIONS

<old>
	A string to search for within the database.

<new>
	Replace instances of the first string with this new string.

[<table>...]
	List of database tables to restrict the replacement to. Wildcards are
	supported, e.g. `'wp_*options'` or `'wp_post*'`.

[--dry-run]
	Run the entire search/replace operation and show report, but don't save
	changes to the database.

[--network]
	Search/replace through all the tables registered to $wpdb in a
	multisite install.

[--all-tables-with-prefix]
	Enable replacement on any tables that match the table prefix even if
	not registered on $wpdb.

[--all-tables]
	Enable replacement on ALL tables in the database, regardless of the
	prefix, and even if not registered on $wpdb. Overrides --network
	and --all-tables-with-prefix.

[--export[=<file>]]
	Write transformed data as SQL file instead of saving replacements to
	the database. If <file> is not supplied, will output to STDOUT.

[--export_insert_size=<rows>]
	Define number of rows in single INSERT statement when doing SQL export.
	You might want to change this depending on your database configuration
	(e.g. if you need to do fewer queries). Default: 50

[--skip-tables=<tables>]
	Do not perform the replacement on specific tables. Use commas to
	specify multiple tables. Wildcards are supported, e.g. `'wp_*options'` or `'wp_post*'`.

[--skip-columns=<columns>]
	Do not perform the replacement on specific columns. Use commas to
	specify multiple columns.

[--include-columns=<columns>]
	Perform the replacement on specific columns. Use commas to
	specify multiple columns.

[--precise]
	Force the use of PHP (instead of SQL) which is more thorough,
	but slower.

[--recurse-objects]
	Enable recursing into objects to replace strings. Defaults to true;
	pass --no-recurse-objects to disable.

[--verbose]
	Prints rows to the console as they're updated.

[--regex]
	Runs the search using a regular expression (without delimiters).
	Warning: search-replace will take about 15-20x longer when using --regex.

[--regex-flags=<regex-flags>]
	Pass PCRE modifiers to regex search-replace (e.g. 'i' for case-insensitivity).

[--regex-delimiter=<regex-delimiter>]
	The delimiter to use for the regex. It must be escaped if it appears in the search string. The default value is the result of `chr(1)`.

[--regex-limit=<regex-limit>]
	The maximum possible replacements for the regex per row (or per unserialized data bit per row). Defaults to -1 (no limit).

[--format=<format>]
	Render output in a particular format.
	---
	default: table
	options:
	  - table
	  - count
	---

[--report]
	Produce report. Defaults to true.

[--report-changed-only]
	Report changed fields only. Defaults to false, unless logging, when it defaults to true.

[--log[=<file>]]
	Log the items changed. If <file> is not supplied or is "-", will output to STDOUT.
	Warning: causes a significant slow down, similar or worse to enabling --precise or --regex.

[--before_context=<num>]
	For logging, number of characters to display before the old match and the new replacement. Default 40. Ignored if not logging.

[--after_context=<num>]
	For logging, number of characters to display after the old match and the new replacement. Default 40. Ignored if not logging.

EXAMPLES

# Search and replace but skip one column
$ wp search-replace 'http://example.test' 'http://example.com' --skip-columns=guid

# Run search/replace operation but dont save in database
$ wp search-replace 'foo' 'bar' wp_posts wp_postmeta wp_terms --dry-run

# Run case-insensitive regex search/replace operation (slow)
$ wp search-replace '\[foo id="([0-9]+)"' '[bar id="\1"' --regex --regex-flags='i'

# Turn your production multisite database into a local dev database
$ wp search-replace --url=example.com example.com example.test 'wp_*options' wp_blogs wp_site --network

# Search/replace to a SQL file without transforming the database
$ wp search-replace foo bar --export=database.sql

# Bash script: Search/replace production to development url (multisite compatible)
#!/bin/bash
if $(wp --url=http://example.com core is-installed --network); then
    wp search-replace --url=http://example.com 'http://example.com' 'http://example.test' --recurse-objects --network --skip-columns=guid --skip-tables=wp_users
else
    wp search-replace 'http://example.com' 'http://example.test' --recurse-objects --skip-columns=guid --skip-tables=wp_users
fi

Installing

This package is included with WP-CLI itself, no additional installation necessary.

To install the latest version of this package over what's included in WP-CLI, run:

wp package install [email protected]:wp-cli/search-replace-command.git

Contributing

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.

For a more thorough introduction, check out WP-CLI's guide to contributing. This package follows those policy and guidelines.

Reporting a bug

Think you’ve found a bug? We’d love for you to help us get it fixed.

Before you create a new issue, you should search existing issues to see if there’s an existing resolution to it, or if it’s already been fixed in a newer version.

Once you’ve done a bit of searching and discovered there isn’t an open or fixed issue for your bug, please create a new issue. Include as much detail as you can, and clear steps to reproduce if possible. For more guidance, review our bug report documentation.

Creating a pull request

Want to contribute a new feature? Please first open a new issue to discuss whether the feature is a good fit for the project.

Once you've decided to commit the time to seeing your pull request through, please follow our guidelines for creating a pull request to make sure it's a pleasant experience. See "Setting up" for details specific to working on this package locally.

Support

GitHub issues aren't for general support questions, but there are other venues you can try: https://wp-cli.org/#support

This README.md is generated dynamically from the project's codebase using wp scaffold package-readme (doc). To suggest changes, please submit a pull request against the corresponding part of the codebase.

search-replace-command's People

Contributors

andyexeter avatar brandonpayton avatar cjhaas avatar clemens-tolboom avatar cyberhobo avatar danielbachhuber avatar ernilambar avatar francescolaffi avatar gitlost avatar itsananderson avatar jpry avatar jrfnl avatar lc43 avatar lkwdwrd avatar markberube avatar marksabbath avatar miya0001 avatar mpeshev avatar mullnerz avatar mwilliamson avatar natewr avatar nyordanov avatar schlessera avatar scribu avatar sidsector9 avatar swissspidy avatar szepeviktor avatar villevuor avatar wesm87 avatar wojsmol avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

search-replace-command's Issues

String not found in serialized object

I'm not sure if I'm using the search-replace command incorrectly or if this is a bug. When I search for the string using wp db search, it finds occurrences:

image

But search-replace doesn't:

image

It happens in the wpml_language_switcher_template_objects option, it looks like this:

MySQL [e3pjiwcm]> select * from ds_options where option_id=7020 \G
*************************** 1. row ***************************
   option_id: 7020
 option_name: wpml_language_switcher_template_objects
option_value: a:6:{s:20:"wpml-legacy-dropdown";O:16:"WPML_LS_Template":10:{s:26:" WPML_LS_Template template";a:12:{s:4:"path";a:1:{i:0;s:104:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown";}s:7:"version";s:1:"1";s:4:"name";s:8:"Dropdown";s:4:"slug";s:20:"wpml-legacy-dropdown";s:8:"base_uri";s:110:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown/";s:3:"css";a:1:{i:0;s:119:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown/style.css";}s:2:"js";a:1:{i:0;s:119:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown/script.js";}s:14:"flags_base_uri";N;s:14:"flag_extension";N;s:7:"is_core";b:1;s:3:"for";a:2:{i:0;s:8:"sidebars";i:1;s:17:"shortcode_actions";}s:14:"force_settings";a:1:{s:29:"display_link_for_current_lang";i:1;}}s:23:" WPML_LS_Template model";a:0:{}s:24:" WPML_LS_Template prefix";s:8:"wpml-ls-";s:38:" WPML_Templates_Factory custom_filters";a:0:{}s:40:" WPML_Templates_Factory custom_functions";a:0:{}s:17:" * template_paths";a:1:{i:0;s:104:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown";}s:39:" WPML_Templates_Factory cache_directory";N;s:18:" * template_string";N;s:30:" WPML_Templates_Factory wp_api";N;s:28:" WPML_Templates_Factory twig";N;}s:26:"wpml-legacy-dropdown-click";O:16:"WPML_LS_Template":10:{s:26:" WPML_LS_Template template";a:12:{s:4:"path";a:1:{i:0;s:110:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown-click";}s:7:"version";s:1:"1";s:4:"name";s:14:"Dropdown click";s:4:"slug";s:26:"wpml-legacy-dropdown-click";s:8:"base_uri";s:116:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown-click/";s:3:"css";a:1:{i:0;s:125:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown-click/style.css";}s:2:"js";a:1:{i:0;s:125:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown-click/script.js";}s:14:"flags_base_uri";N;s:14:"flag_extension";N;s:7:"is_core";b:1;s:3:"for";a:2:{i:0;s:8:"sidebars";i:1;s:17:"shortcode_actions";}s:14:"force_settings";a:1:{s:29:"display_link_for_current_lang";i:1;}}s:23:" WPML_LS_Template model";a:0:{}s:24:" WPML_LS_Template prefix";s:8:"wpml-ls-";s:38:" WPML_Templates_Factory custom_filters";a:0:{}s:40:" WPML_Templates_Factory custom_functions";a:0:{}s:17:" * template_paths";a:1:{i:0;s:110:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-dropdown-click";}s:39:" WPML_Templates_Factory cache_directory";N;s:18:" * template_string";N;s:30:" WPML_Templates_Factory wp_api";N;s:28:" WPML_Templates_Factory twig";N;}s:27:"wpml-legacy-horizontal-list";O:16:"WPML_LS_Template":10:{s:26:" WPML_LS_Template template";a:12:{s:4:"path";a:1:{i:0;s:111:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-horizontal";}s:7:"version";s:1:"1";s:4:"name";s:15:"Horizontal List";s:4:"slug";s:27:"wpml-legacy-horizontal-list";s:8:"base_uri";s:117:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-horizontal/";s:3:"css";a:1:{i:0;s:126:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-horizontal/style.css";}s:2:"js";a:0:{}s:14:"flags_base_uri";N;s:14:"flag_extension";N;s:7:"is_core";b:1;s:3:"for";a:3:{i:0;s:8:"sidebars";i:1;s:6:"footer";i:2;s:17:"shortcode_actions";}s:14:"force_settings";a:0:{}}s:23:" WPML_LS_Template model";a:0:{}s:24:" WPML_LS_Template prefix";s:8:"wpml-ls-";s:38:" WPML_Templates_Factory custom_filters";a:0:{}s:40:" WPML_Templates_Factory custom_functions";a:0:{}s:17:" * template_paths";a:1:{i:0;s:111:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-horizontal";}s:39:" WPML_Templates_Factory cache_directory";N;s:18:" * template_string";N;s:30:" WPML_Templates_Factory wp_api";N;s:28:" WPML_Templates_Factory twig";N;}s:25:"wpml-legacy-vertical-list";O:16:"WPML_LS_Template":10:{s:26:" WPML_LS_Template template";a:12:{s:4:"path";a:1:{i:0;s:109:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-vertical";}s:7:"version";s:1:"1";s:4:"name";s:13:"Vertical List";s:4:"slug";s:25:"wpml-legacy-vertical-list";s:8:"base_uri";s:115:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-vertical/";s:3:"css";a:1:{i:0;s:124:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-vertical/style.css";}s:2:"js";a:0:{}s:14:"flags_base_uri";N;s:14:"flag_extension";N;s:7:"is_core";b:1;s:3:"for";a:3:{i:0;s:8:"sidebars";i:1;s:6:"footer";i:2;s:17:"shortcode_actions";}s:14:"force_settings";a:0:{}}s:23:" WPML_LS_Template model";a:0:{}s:24:" WPML_LS_Template prefix";s:8:"wpml-ls-";s:38:" WPML_Templates_Factory custom_filters";a:0:{}s:40:" WPML_Templates_Factory custom_functions";a:0:{}s:17:" * template_paths";a:1:{i:0;s:109:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-list-vertical";}s:39:" WPML_Templates_Factory cache_directory";N;s:18:" * template_string";N;s:30:" WPML_Templates_Factory wp_api";N;s:28:" WPML_Templates_Factory twig";N;}s:29:"wpml-legacy-post-translations";O:16:"WPML_LS_Template":10:{s:26:" WPML_LS_Template template";a:12:{s:4:"path";a:1:{i:0;s:113:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-post-translations";}s:7:"version";s:1:"1";s:4:"name";s:17:"Post translations";s:4:"slug";s:29:"wpml-legacy-post-translations";s:8:"base_uri";s:119:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-post-translations/";s:3:"css";a:1:{i:0;s:128:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-post-translations/style.css";}s:2:"js";a:0:{}s:14:"flags_base_uri";N;s:14:"flag_extension";N;s:7:"is_core";b:1;s:3:"for";a:1:{i:0;s:17:"post_translations";}s:14:"force_settings";a:0:{}}s:23:" WPML_LS_Template model";a:0:{}s:24:" WPML_LS_Template prefix";s:8:"wpml-ls-";s:38:" WPML_Templates_Factory custom_filters";a:0:{}s:40:" WPML_Templates_Factory custom_functions";a:0:{}s:17:" * template_paths";a:1:{i:0;s:113:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/legacy-post-translations";}s:39:" WPML_Templates_Factory cache_directory";N;s:18:" * template_string";N;s:30:" WPML_Templates_Factory wp_api";N;s:28:" WPML_Templates_Factory twig";N;}s:14:"wpml-menu-item";O:16:"WPML_LS_Template":10:{s:26:" WPML_LS_Template template";a:12:{s:4:"path";a:1:{i:0;s:98:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/menu-item";}s:7:"version";s:1:"1";s:4:"name";s:9:"Menu Item";s:4:"slug";s:14:"wpml-menu-item";s:8:"base_uri";s:104:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/menu-item/";s:3:"css";a:1:{i:0;s:113:"//prime.wpvyvoj.cz/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/menu-item/style.css";}s:2:"js";a:0:{}s:14:"flags_base_uri";N;s:14:"flag_extension";N;s:7:"is_core";b:1;s:3:"for";a:1:{i:0;s:5:"menus";}s:14:"force_settings";a:0:{}}s:23:" WPML_LS_Template model";a:0:{}s:24:" WPML_LS_Template prefix";s:8:"wpml-ls-";s:38:" WPML_Templates_Factory custom_filters";a:0:{}s:40:" WPML_Templates_Factory custom_functions";a:0:{}s:17:" * template_paths";a:1:{i:0;s:98:"/var/www/html/wp-content/plugins/sitepress-multilingual-cms/templates/language-switchers/menu-item";}s:39:" WPML_Templates_Factory cache_directory";N;s:18:" * template_string";N;s:30:" WPML_Templates_Factory wp_api";N;s:28:" WPML_Templates_Factory twig";N;}}
    autoload: yes

skipping entries in the wp_posts table

Describe the current, buggy behavior
search-replace is not properly updating values in the wp_posts table.

Steps to reproduce

wp search-replace 'from.com' 'to.com' \
  --recurse-objects \
  --skip-columns=guid \
  --skip-tables=wp_users \
  --path=$WP_PATHP_PATH

Here is the first occurrence of from.com

INSERT INTO `wp_posts` VALUES (1,1,'2020-02-15 00:42:07','2020-02-15 00:42:07','<!-- wp:paragraph -->\n<p>Welcome to WordPress    . This is your first post. Edit or delete it, then start writing!</p>\n<!-- /wp:paragraph -->','Hello world!','','trash','open    ','open','','hello-world__trashed','','','2020-02-17 15:54:14','2020-02-17 15:54:14','',0,'http://from.com/?p=1    ',0,'post','',1),(2,1,'2020-02-15 00:42:07','2020-02-15 00:42:07'...

Here is the entire export of the wp_posts table after I ran the wp search-replace command
search-error.sql.zip

Describe what you would expect as the correct outcome
I would expect all occurrences of the string to be replaced

Let us know what environment you are running this on
OS

lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.4 LTS
Release:	18.04
Codename:	bionic

MySQL

mysql --version
mysql  Ver 14.14 Distrib 5.7.29, for Linux (x86_64) using  EditLine wrapper

WP CLI

OS:	Linux 4.15.0-88-generic #88-Ubuntu SMP Tue Feb 11 20:11:34 UTC 2020 x86_64
Shell:	/bin/bash
PHP binary:	/usr/bin/php7.2
PHP version:	7.2.24-0ubuntu0.18.04.3
php.ini used:	/etc/php/7.2/cli/php.ini
WP-CLI root dir:	phar://wp-cli.phar/vendor/wp-cli/wp-cli
WP-CLI vendor dir:	phar://wp-cli.phar/vendor
WP_CLI phar path:	/home/sc
WP-CLI packages dir:
WP-CLI global config:
WP-CLI project config:
WP-CLI version:	2.4.0

search-replace export breaking permalinks

In testing wp search-replace --export=database.sql, I got some different results than using wp db export database.sql and then doing wp search-replace to the export later. Namely, in the wp_options table, the --export version breaks my permalinks:

('33', 'permalink_structure', '/{7267a97bdafb7aebecb895314b7351152b3b34ec93cfd83ec25869556c15d230}year{7267a97bdafb7aebecb895314b7351152b3b34ec93cfd83ec25869556c15d230}/{7267a97bdafb7aebecb895314b7351152b3b34ec93cfd83ec25869556c15d230}monthnum{7267a97bdafb7aebecb895314b7351152b3b34ec93cfd83ec25869556c15d230}/{7267a97bdafb7aebecb895314b7351152b3b34ec93cfd83ec25869556c15d230}postname{7267a97bdafb7aebecb895314b7351152b3b34ec93cfd83ec25869556c15d230}/', 'yes'),

When it should be:

(33,'permalink_structure','/%year%/%monthnum%/%postname%/','yes')

So it appears the % characters are breaking the mysqldump. Any ideas?

Better communicate failure to search-replace JSON-serialized URLs

From Post Status Slack:

javorszky [5 hours ago] Gravity forms confirmation: goes to https://livesite.com/confirmation-page
I'm on dev. Dev is https://site.test
`wp search-replace https://livesites.com https://site.text`
It does not turn gravity form's form confirmation setting
javorszky [5 hours ago] because the setting is stored in the `rg_form_meta` table in the `confirmations` column as `json`, so the url will look like
javorszky [5 hours ago] `https:\/\/livesite.com\/confirmation-page`

We should better communicate this scenario in our documentation. In fact, it may be time for a dedicated document on search-replacing URLs in the database. Or even, create a dedicated wp search-replace url command, because replacing URLs is such a common use case.

cc @javorszky

GUID updating in search-replace

I've opened this issue after commenting from issue #92

I'm not a contributor, but I can say that when running a search/replace, more often than not I see guid table does get updated or shows that it's a table that is updated when I run

wp search-replace "http://olddom" "https://newdom" --all-tables --skip-columns=guid --skip-columns=user_email --precise --skip-plugins --skip-themes --report-changed-only

+-------------------------+---------------+--------------+------+
| Table                   | Column        | Replacements | Type |
+-------------------------+---------------+--------------+------+
| wp_options              | option_value  | 9            | PHP  |
| wp_postmeta             | meta_value    | 2            | PHP  |
| wp_posts                | post_content  | 67           | PHP  |
| wp_posts                | post_excerpt  | 28           | PHP  |
| wp_posts                | guid          | 600          | PHP  |
| wp_users                | user_url      | 1            | PHP  |
| wp_woocommerce_sessions | session_value | 1            | PHP  |
+-------------------------+---------------+--------------+------+
Success: Made 708 replacements. Please remember to flush your persistent object cache with `wp cache flush`

I will see if I can gather more data on this, but it appears the guid is being updated in posts table. I'll have to snag some info from another search/replace when I run it.

The report prints a message when the table's primary key isn't found

Currently, when doing a search and replace with --report-changed-only, if the table has no primary key, it still prints the name of the table and the string 'skipped'.

It should only add to report when this flag isn't present.

example:

wp search-replace 'aaaaaaaaa' 'bbbbbbb' --report-changed-only --url="example.com" --network

will print:

+------------+--------+--------------+------+
| Table      | Column | Replacements | Type |
+------------+--------+--------------+------+
| wp_example |        | skipped      |      |
+------------+--------+--------------+------+

Clarify "please flush object cache" message in multisite

After running search-replace on multisite with a persistent object cache enabled, you can see a message like this:

Success: Made 2419 replacements. Please remember to flush your persistent object cache with `wp cache flush`

However, running wp cache flush may produce a "Error: site not found" message if the old lookup value is still in cache.

Et voila! You've entered an indeterminate state where you'd expect you could flush cache against the new URL, but the cache still has the old URL, so you need to flush cache against the old URL before the new URL works.

We should clarify the message accordingly when this scenario occurs.

Search-replace command do not work properly

I think, the search-replace command do not work properly.

Post content code contains:
some text <a href="[page_url 363]" class="btn">anchor text</a> some text

This wp-cli command found only 14 posts:
search-replace '<a\s+href="\[page_url\s+363\]"([^>]+)>([^<]+)<\/a>' 'ReplaceText' wp_posts --regex --allow-root --regex-flags='i' --regex-delimiter='/' --log --dry-run

Should find 73 posts.

If I make get_posts and execute preg_replace function on post content, the php script found every occurences into 73 posts (there is one occurence in one post).

Pattern, delimiters and flags are the same.

--url and --all-tables being used together in multisite

Feature Request

Describe your use case and the problem you are facing

In a multisite environment, passing in both --url and --all-tables together ends up searching and replacing on all tables instead of the associated tables in the subsite passed in through the --url flag.

Describe the solution you'd like

A) It would be nice to warn the user before executing that passing in --url with --all-tables in a multisite environment will ignore the --url parameter and all tables in the multisite will be affected
Or..
B) Perform the search-replace on only the associated tables of the subsite passed in via --url.

PHP Warning: PHP Startup: Unable to load dynamic library

The following modules are causing issue, even though they are enabled

[/]# wp search-replace 'http://xxx/wordpress/' 'http://xxx/' --all-tables
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/pspell.so' - libpspell.so.15: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/tidy.so' - libtidy-0.99.so.0: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/wddx.so' - /opt/alt/php55/usr/lib64/php/modules/wddx.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/xmlreader.so' - /opt/alt/php55/usr/lib64/php/modules/xmlreader.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/xmlrpc.so' - /opt/alt/php55/usr/lib64/php/modules/xmlrpc.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/xmlwriter.so' - /opt/alt/php55/usr/lib64/php/modules/xmlwriter.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/xsl.so' - /opt/alt/php55/usr/lib64/php/modules/xsl.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php55/usr/lib64/php/modules/zip.so' - /opt/alt/php55/usr/lib64/php/modules/zip.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0

[/]# php -m
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php71/usr/lib64/php/modules/intl.so' - libicudata.so.57: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php71/usr/lib64/php/modules/pgsql.so' - /opt/alt/php71/usr/lib64/php/modules/pgsql.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php71/usr/lib64/php/modules/phar.so' - /opt/alt/php71/usr/lib64/php/modules/phar.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php71/usr/lib64/php/modules/pspell.so' - /opt/alt/php71/usr/lib64/php/modules/pspell.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php71/usr/lib64/php/modules/soap.so' - /opt/alt/php71/usr/lib64/php/modules/soap.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php71/usr/lib64/php/modules/sockets.so' - /opt/alt/php71/usr/lib64/php/modules/sockets.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0
PHP Warning: PHP Startup: Unable to load dynamic library '/opt/alt/php71/usr/lib64/php/modules/tidy.so' - /opt/alt/php71/usr/lib64/php/modules/tidy.so: failed to map segment from shared object: Cannot allocate memory in Unknown on line 0

Option to exlude tables when using --all-tables

I usually use --export to get a file that I can just import from dev to live. But as the development process can sometimes take some time, the live env does have new entries in some tables like the Gravity Forms Entry tables, that get overwritten on import.
So I'd just like to replace everything everywhere but export it to a file without the tables I don't want to have in the sql file.

Or maybe add an option to not include the tables, where nothing was replaced, in the exported file.

Explain the purpose of the --precise flag

The description of the --precise flag suggests that default results are not precise. Is this the case?

I think the description of this flag needs to be improved, and possibly an example added to the Examples section demonstrating when it should be used.

search-replace doesn't quote SQL values

Original issue in wp-cli/wp-cli#4631 by @miraline:

The wp search-replace command with --regex and --all-tables-with-prefix generates invalid SQL queries.

Reproducing on a fresh install:

an@an:~/tmp/wp $ lsb_release -a
No LSB modules are available.
Distributor ID:    Ubuntu
Description:    Ubuntu 14.04.5 LTS
Release:    14.04
Codename:    trusty
an@an:~ $ mkdir tmp/wp
an@an:~ $ cd tmp/wp
an@an:~/tmp/wp $ wp cli version
WP-CLI 1.4.1
an@an:~/tmp/wp $ wp core download 
Downloading WordPress 4.9.2 (en_US)...
md5 hash verified: 6961904477ab2b7a53374326a07cb819
Success: WordPress downloaded.
an@an:~/tmp/wp $ mysql -u root -p -e 'create database wp_test'
Enter password: 
an@an:~/tmp/wp $ wp config create --dbname=wp_test --dbuser=root --dbpass='' 
Success: Generated 'wp-config.php' file.
an@an:~/tmp/wp $ wp core install --skip-email --url=wptest.local --title=test --admin_user=admin --admin_password=123456 [email protected]
Success: WordPress installed successfully.
an@an:~/tmp/wp $ cat /tmp/tbl.sql 
DROP TABLE IF EXISTS `wp_123_test`;
CREATE TABLE `wp_123_test` (
  `name` varchar(50),
  `value` varchar(5000),
  `created_at` datetime NOT NULL,
  `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`name`)
) ENGINE=InnoDB;
INSERT INTO `wp_123_test` VALUES ('test_val','off','2016-11-15 14:41:33','2016-11-15 21:41:33');
an@an:~/tmp/wp $ mysql -u root wp_test < /tmp/tbl.sql
an@an:~/tmp/wp $ wp search-replace --dry-run --regex 'mytestdomain.com\/' 'mytestdomain2.com/' --skip-themes --skip-plugins --all-tables-with-prefix --skip-columns=guid,domain 2>&1 | head -c 1000
WordPress database error Unknown column 'test_val' in 'where clause' for query SELECT `name` FROM `wp_123_test` WHERE `name` = test_val made by include('phar:///usr/local/bin/wp/php/boot-phar.php'), include('phar:///usr/local/bin/wp/php/wp-cli.php'), WP_CLI\bootstrap, WP_CLI\Bootstrap\LaunchRunner->process, WP_CLI\Runner->start, WP_CLI\Runner->_run_command_and_exit, WP_CLI\Runner->run_command, WP_CLI\Dispatcher\Subcommand->invoke, call_user_func, WP_CLI\Dispatcher\CommandFactory::WP_CLI\Dispatcher\{closure}, call_user_func, Search_Replace_Command->__invoke, Search_Replace_Command->php_handle_col
WordPress database error Unknown column 'test_val' in 'where clause' for query SELECT `value` FROM `wp_123_test` WHERE `name` = test_val made by include('phar:///usr/local/bin/wp/php/boot-phar.php'), include('phar:///usr/local/bin/wp/php/wp-cli.php'), WP_CLI\bootstrap, WP_CLI\Bootstrap\LaunchRunner->process, WP_CLI\Runner->start, WP_CLI\Runner->_run_command_and_exit, WP_CLI\Runner->run_command,an@an:~/tmp/wp $ ^C

It looks like test_val is not quoted and thus is treated like a table name, not a value.

In the same time, it works just fine without --regex and/or --all-tables-with-prefix:

an@an:~/tmp/wp $ wp search-replace --dry-run 'mytestdomain.com\/' 'mytestdomain2.com/' --skip-themes --skip-plugins --all-tables-with-prefix --skip-columns=guid,domain 2>&1 | head -c 1000
Table    Column    Replacements    Type
wp_123_test    name    0    SQL
wp_123_test    value    0    SQL
wp_commentmeta    meta_key    0    SQL
wp_commentmeta    meta_value    0    SQL
wp_comments    comment_author    0    SQL
wp_comments    comment_author_email    0    SQL
wp_comments    comment_author_url    0    SQL
wp_comments    comment_author_IP    0    SQL
wp_comments    comment_content    0    SQL
wp_comments    comment_approved    0    SQL
...

Add --regex-limit

When using the search-replace command with the --regex option, it uses preg_replace() / preg_replace_callback() to replace all occurrences in a row.

There might be situations where you only want that replacement to happen a certain amount of times (e.g. only once per row).

preg_replace_callback() and preg_replace() have a $limit parameter that could be used for this and defaults to -1 (no limit).

Suggestion: Allow modifying this $limit parameter using a new --regex-limit argument. That should be rather trivial (just pass the value to the function), but needs some testing obviously.

Better error message on regex check.

The regex check at Search_Replace_Command.php#L191 just returns a fails message on false. It should return the PCRE error also, which can be got with error_get_last() (and confusingly not with preg_last_error()). Also sometimes gives an offset so it would be groovy to have it highlight or point to where in the regex that is.

Edit: maybe add this as a function in Utils as will be useful elsewhere also (eg db search).

Pass current `$col` and `$primary_keys` down to the SearchReplacer

Feature Request

Describe your use case and the problem you are facing

When using the (to-be-merged) callback feature, I'd like to access current information about the data being replaced (post-ID, meta-ID, ...)

Describe the solution you'd like

This is possible if run_recursively / run functions of the SearchReplacer are given the information about current $col and $primary_keys

Move command over to new v2 structure

The following changes need to be made to move the command over to the v2 structure:

  • Make sure the correct framework is required:
    composer require wp-cli/wp-cli:^2
    
  • Require the testing framework as a dev dependency:
    composer require --dev wp-cli/wp-cli-tests:^0
    
  • Use the .travis.yml file from wp-cli/wp-cli:
    wget https://raw.githubusercontent.com/wp-cli/wp-cli/master/.travis.yml
    
  • Add the default script configuration to Composer file:
      "scripts": {
          "lint": "run-linter-tests",
          "phpcs": "run-phpcs-tests",
          "phpunit": "run-php-unit-tests",
          "behat": "run-behat-tests",
          "prepare-tests": "install-package-tests",
          "test": [
              "@lint",
              "@phpcs",
              "@phpunit",
              "@behat"
          ]
      },
    
  • Remove scaffolded binary files:
    git rm bin/install-package-tests.sh
    git rm bin/test.sh
    
  • Remove scaffolded Behat setup:
    git rm features/bootstrap/*
    git rm features/extra/*
    git rm features/steps/*
    
  • Remove scaffolded Behat tags util script:
    git rm utils/behat-tags.php
    
  • Add command packages that are needed for Behat tests as --dev dependencies.
    The following commands are already available, anything else needs to be explicitly required:
    • cli *
    • config *
    • core *
    • eval
    • eval-file
    • help
  • Update all dependencies:
    composer update
    
  • Optional - Add PHPCS rule set to enable CS & compatibility sniffing:
    wget https://raw.githubusercontent.com/wp-cli/wp-cli/master/phpcs.xml.dist
    
  • Run and adapt tests to make sure they all pass:
    composer test
    

Run search-replace without bootstrapping WordPress multisite entirely when --network is used

One annoying, and even confusing, idiosyncrasy of wp search-replace on multisite:

wp search-replace old.com new.com --network --url=old.com

If you forget --url=old.com and DOMAIN_CURRENT_SITE=new.com, you'll get a confusing "Site not found" error because WordPress fails to look up the site before wp search-replace can execute.

If --network, --all-tables, --all-tables-with-prefix, or any tables are provided, it's not necessary to know the blog context for wp search-replace. In this case, we should be able to run wp search-replace without bootstrapping WordPress multisite entirely.

Related wp-cli/cache-command#17

Problems with Views - "ERROR 1449 ... The user specified as a definer ('xxx'@'xxx') does not exist"

This is a suggest / query / pre-PR question.

I am pulling a db from serverA to serverB and running a search-replace on it. It is failing because the dB contains a view. The view references the serverA MySQL user which doesn't exist on serverB.

(presuming I'm not missing something / haven't messed something up in my setup...)

I don't know of any reason to run a search-replace on a View as they are dynamic by nature. Would a PR to skip them be accepted? Is there any reason Views should be included?

These would work on MySQL/MariaDB (but haven't been extensively tested):

SHOW TABLE STATUS
# WHERE `comment` != 'VIEW'
WHERE `Rows` IS NOT NULL

It would mean modifying wp_get_table_names() in wp-cli/php/utils-wp.php fairly extensively, unless there's an easier way to identify Views.

I think this is going to get more widely applicable if the use of a View in core goes ahead (wasn't there talk of a View for taxonomy data?)

While I'm obviously talking about modifying a function in core wp-cli I thought I'd post this hear first as the most obvious (to me) place that it would affect and use case for the change.

Refactor `WP_CLI\SearchReplacer` functionality.

See #62 (comment)

The WP_CLI\SearchReplacer class now takes a load of args on construction which just mirror those being set in Search_Replace_Command which doesn't make much sense to me.

I think it should be deprecated (and left around unused for BC maybe) and its functionality either put straight into Search_Replace_Command (as it once was 29454e3), or put into a utils function (as it once was aa7df69) where it would now take a closure callback that would have all the info and do the actual replacement.

Note also that the phpunit tests tests/test-search-replace.php were never moved from wp-cli/wp-cli.

Allow changing database name

Search-replace command could get option to let it work on other database than is read from WP configuration. When I do development I always have dev, staging and prod environments. Every one has its own domain, so running search-replace is a must. I often prepare databases for staging and prod on my dev machine. For example when my current Wordpress instance has db named wp_dbase I add two more dbs, its wp_dbase_staging and wp_dbase_prod. Then I copy wp_dbase -> wp_dbase_staging and run search and replace on wp_dbase_staging, Then it is prepared to upload on staging mysql server. Please consider additional option eg. --dbname=name_of_database_to_work_on

Explain why certain tables can be skipped

We should have a more concise version of this in the command description:

One of the keys in each database table has to be marked as the primary key, or else WP-CLI will skip over it and output ‘skipped’ as shown above. To see if that’s your problem, use DESCRIBE to view the index data for one of the skipped tables, like this:

$ wp db query 'DESCRIBE wp_17_comments'

+----------------------+---------------------+------+-----+---------------------+-------+
| Field                | Type                | Null | Key | Default             | Extra |
+----------------------+---------------------+------+-----+---------------------+-------+
| comment_ID           | bigint(20) unsigned | NO   |     | NULL                |       |
| comment_post_ID      | bigint(20) unsigned | NO   |     | 0                   |       |
| comment_author       | tinytext            | NO   |     | NULL                |       |
| comment_author_email | varchar(100)        | NO   |     |                     |       |
| comment_author_url   | varchar(200)        | NO   |     |                     |       |
| comment_author_IP    | varchar(100)        | NO   |     |                     |       |
| comment_date         | datetime            | NO   |     | 0000-00-00 00:00:00 |       |
| comment_date_gmt     | datetime            | NO   |     | 0000-00-00 00:00:00 |       |
| comment_content      | text                | NO   |     | NULL                |       |
| comment_karma        | int(11)             | NO   |     | 0                   |       |
| comment_approved     | varchar(20)         | NO   |     | 1                   |       |
| comment_agent        | varchar(255)        | NO   |     |                     |       |
| comment_type         | varchar(20)         | NO   |     |                     |       |
| comment_parent       | bigint(20) unsigned | NO   |     | 0                   |       |
| user_id              | bigint(20) unsigned | NO   |     | 0                   |       |
+----------------------+---------------------+------+-----+---------------------+-------+

‘PRI’ should be listed in the Key column somewhere, but since it isn’t, WP-CLI doesn’t know which is the primary key and is consequently skipping the table. Since the example above is a WordPress comments table, comment_ID should have ‘PRI’ in the Key column. By looking at the table index data above (and thanks to help from @JPry), I was able to see that my table was missing much more than just the primary key and that an error had occurred when the database was exported/imported. So to fix the issue, I needed to get another copy of it and try the search-replace again.

From https://kellenmace.com/wp-cli-search-replace-tables-skipped/

Related https://wordpress.org/support/topic/wp-search-replace-skipped-tables/

Alert the user of db table permissions errors

Feature Request

Use Case and user story

When there are database table permissions errors, search-replace should be able to alert the user.

I was working with an older DB that I had manually imported the wp_posts table with the wrong DB permissions. (the table was read-only)

When I did a dry-run of the search-replace it worked great, showed me all the replacements to be made, etc.

Then when I did a live run, it went through the process and still reported that there were 527 changes to make, but instead of making them it returned Success: 0 Replacements made

I downloaded a more blunt tool for forcing the DB updates through, and that's when I discovered that the table in question is read-only. Once I knew that, the workaround was simple: export the DB to a fresh DB using wp db export and then import that fresh database, which resets the permissions, then search-replace worked like a charm.

I also tried with --verbose and --debug but the critical piece (when the actual SQL is running) is not outputting anything useful.

Output of the Dry Run

527 Replacements to be made

Output of the --debug argument on a live run

Success: made 0 replacements

Proposed solution

It would be more helpful if table permission errors are preventing the actual changing, that this is output.

I'd propose an additional argument of --errors or adding this output to the existing --debug argument.

Show actual transformations taking place

From wp-cli/wp-cli#3777

There should be a way to see the changes that a search-replace would make. This would be especially valuable to use with --regex flag, where your changes can get out of control if you don't see what's going on.

The current export feature is a bit underpowered because it generates a huge SQL file for the site. There's no easy way to see what exactly is changing.

I think we need a report feature that generates a from/to report for each table (that's changing). I would suggest using something like WP's built-in Text Diff or PHP-diff for a diff column.

There are lots of false-positives. For example, plugins that have had their PHP classes change (new properties). The unserialize will add with the newer properties from the PHP class. It will be "incorrectly" reported as a replacement. This is probably unavoidable since this has to happen at some point. However, number 1 (above) could alleviate the confusion from this.

[...]

The summary is not sufficient. It raises eyebrows for falsepositives (rare case though). I'm always wary of what it is going to replace before I execute the replace.

The idea is coming from in-house BU search-replace script that does diffs using php-diff and outputs them into CSV files. It lacks regex searching. We're in the mode to adopt wp-cli more in the future, hence the feature request.

search-replace --regex line breaks not working

when using:
wp search-replace "\r\n\r\n" "\r\n" --regex wp_posts --include-columns="post_content,post_excerpt"

it finds (matches) the double line breaks, but it will replace it with literal \r\n instead of line breaks.

Progress bar

Issue: I can't see the status of any search-replace commands as they are executing

Right now I'm running a search-replace command (as a regular expression) against the postmeta table which has around 1 million rows. This is pretty slow (which obviously has nothing to do with this command in itself) but I'd love some kind of feedback as to the progress of the command.

For reference the command is:

wp search-replace '\/vid-dodsfall\/(.)+' '/begravning/$1' --regex wp_postmeta

Solution: I'd love to see a progress bar indicator for this command

I would guess that the majority of use cases for search-replace are something along the lines of

wp search-replace https://live.com https://local.com

This can take a wee while on a larger database and I'd love to have some indication of progress.

Clarify the allowed values for --skip-columns and --include-columns

It's not clear from the documentation whether a fully qualified column name is allowed in the --skip-columns and --include-columns arguments, for example --skip-columns=wp_posts.guid.

This format is not supported, so I think the documentation should be updated to make this clear.

Filter by post types and post status in post table

Feature Request

Describe your use case and the problem you are facing

I currently use search-replace for batch editing content (when the name of an event that we do has changed for example...)

There's been a couple instances where I would have preferred search-replace to only
edit the content that is currently published (post_status=published) not in past revisions; or only to only change in specified post_types (we updated the url structure for only one kind of custom post_type).

Describe the solution you'd like

I was wondering if it's possible to add an additional parameter to the search-replace command so that when search and replaces are made; the user can specify the
only affect the current revision of content in the wordpress database.
(Thinking aloud this could be done by filtering the post_status or post_type columns for specified values.

(I'd add an extra parameter like post_status=publish which would not update currently published content) using a parameter like --skip-post-status.
Example usage:
wp search-replace 'before' 'after' --skip-post-status=publish --recurse-objects --skip-columns=guid --skip-tables=wp_users

or to only edit certain post types like
--include-post-type-values=my_custom_post_type
Example usage:
wp search-replace 'before' 'after' --include-post-type-values=my_custom_post_type --recurse-objects --skip-columns=guid --skip-tables=wp_users

Filtering out the post_status and post_type columns for their values can be done using a SQL statement but adding this to wp-cli hopefully would be worth doing.

Automatically rerun failed scenarios

The following changes need to be made:

  1. In the .travis.yml file, the - composer behat line in the script: section needs to be changed into the following:
- composer behat || composer behat-rerun
  1. In the composer-json file, the requirement on wp-cli/wp-cli-tests needs to be adapted to require at least v2.0.7:
"wp-cli/wp-cli-tests": "^2.0.7"
  1. In the composer-json file, the "scripts" section needs to be extended. Immediately after the line "behat": "run-behat-tests",, the following line needs to be inserted:
"behat-rerun": "rerun-behat-tests",

Here's an example of how this should look like:

Run search-replace command in file

For me, it would be a great feature to run a search-replace command inside a file.

When I move a site from one server to another (assuming both servers are FTP only, no SSH access) I download the site + db to my local machine and upload both of them to the new server.

To make replacements (for example when the site is a clone on another domainname), I have to import the sql dump into my own local database and setup a Wordpress installation, to be able to use the WP CLI search-replace command and create a new (rewrited) export.

It would be a great feature to do search-replace replacements straight into a file. For example with a --sql-file= argument (the --export= argument should then also be required)

NULL values are exported as empty strings ('')

Running wp db export export.sql generates this for one of the tables (formatted with newlines for easier comparison):

INSERT INTO `wpl_itsec_lockouts` VALUES (1,'four_oh_four','2018-05-03 11:02:41','2018-05-03 11:02:41','1970-01-01 00:00:01','1970-01-01 00:00:01','127.0.0.1',NULL,NULL,1),
(2,'four_oh_four','2018-05-03 11:02:52','2018-05-03 11:02:52','1970-01-01 00:00:01','1970-01-01 00:00:01','127.0.0.1',NULL,NULL,1),
(3,'four_oh_four','2018-05-03 13:15:29','2018-05-03 13:15:29','1970-01-01 00:00:01','1970-01-01 00:00:01','127.0.0.1',NULL,NULL,1),
(4,'four_oh_four','2018-05-03 13:16:01','2018-05-03 13:16:01','1970-01-01 00:00:01','1970-01-01 00:00:01','127.0.0.1',NULL,NULL,1),
(5,'four_oh_four','2018-05-03 13:16:10','2018-05-03 13:16:10','1970-01-01 00:00:01','1970-01-01 00:00:01','127.0.0.1',NULL,NULL,1);

Whilst running wp search-replace "foo" "bar" --all-tables --export=search-replace.sql generates this:

INSERT INTO `wpl_itsec_lockouts` (`lockout_id`, `lockout_type`, `lockout_start`, `lockout_start_gmt`, `lockout_expire`, `lockout_expire_gmt`, `lockout_host`, `lockout_user`, `lockout_username`, `lockout_active`) VALUES 
('1', 'four_oh_four', '2018-05-03 11:02:41', '2018-05-03 11:02:41', '1970-01-01 00:00:01', '1970-01-01 00:00:01', '127.0.0.1', '', '', '1'),
('2', 'four_oh_four', '2018-05-03 11:02:52', '2018-05-03 11:02:52', '1970-01-01 00:00:01', '1970-01-01 00:00:01', '127.0.0.1', '', '', '1'),
('3', 'four_oh_four', '2018-05-03 13:15:29', '2018-05-03 13:15:29', '1970-01-01 00:00:01', '1970-01-01 00:00:01', '127.0.0.1', '', '', '1'),
('4', 'four_oh_four', '2018-05-03 13:16:01', '2018-05-03 13:16:01', '1970-01-01 00:00:01', '1970-01-01 00:00:01', '127.0.0.1', '', '', '1'),
('5', 'four_oh_four', '2018-05-03 13:16:10', '2018-05-03 13:16:10', '1970-01-01 00:00:01', '1970-01-01 00:00:01', '127.0.0.1', '', '', '1');

When trying to import the sql dump from the above search-replace command this happens:

$ wp db import search-replace.sql
ERROR 1366 (HY000) at line 57 in file: 'search-replace.sql': Incorrect integer value: '' for column 'lockout_user' at row 1

Granted this is not a standard WordPress table, but I'm hoping you could look into this anyway.

I'm running WP-CLI 1.5.1 through cygwin on Windows 10.

Multiple Search/Replaces

Maybe I am missing something obvious, but I have not found a way to perform multiple search-replaces on a single database export file. When generating an SQL export file from a staging or production instance to be used in any local environment, there often requires at least two search-replace operations (one for the domain, the second for the paths).

Does this functionality exist? Or is it strictly limited to one search-replace? I have found myself just writing more sed commands in the meantime to operate on the dumped file. Is this within the scope of wp-cli search-replace?

Flag --no-recurse-objects is ignored

Bug Report

Describe the current, buggy behavior

Aloha, although the docs describe a --no-recurse-objects flag:

[--recurse-objects]
Enable recursing into objects to replace strings. Defaults to true; pass –no-recurse-objects to disable.

https://developer.wordpress.org/cli/commands/search-replace/

it doesn't seem to be respected. We tried variants of --recurse-objects=false but there doesn't seem to be a way to get a falsey value into the flag.

Describe how other contributors can replicate this bug

On vanilla WordPress:

$ wp search-replace "This is an example" "Replacement" wp_posts --report-changed-only --dry-run --no-recurse-objects
+----------+--------------+--------------+------+
| Table    | Column       | Replacements | Type |
+----------+--------------+--------------+------+
| wp_posts | post_content | 2            | PHP  |
+----------+--------------+--------------+------+
Success: 2 replacements to be made.

Describe what you would expect as the correct outcome

Note the replacement type is still PHP; it should be SQL since the --no-recurse-objects flag is present.

Let us know what environment you are running this on

$ wp cli info
OS:	Linux 4.15.0-91-generic #92-Ubuntu SMP Fri Feb 28 11:09:48 UTC 2020 x86_64
Shell:	/bin/bash
PHP binary:	/usr/bin/php7.2
PHP version:	7.2.24-0ubuntu0.18.04.3
php.ini used:	/etc/php/7.2/cli/php.ini
WP-CLI root dir:	phar://wp-cli.phar/vendor/wp-cli/wp-cli
WP-CLI vendor dir:	phar://wp-cli.phar/vendor
WP_CLI phar path:	/var/www/html
WP-CLI packages dir:
WP-CLI global config:
WP-CLI project config:
WP-CLI version:	2.4.0

Provide a possible solution

Probably just add the flag in Search_Replace_Command.php:invoke(), below the flag vars:

		if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'no-recurse-objects' ) ) {
			$this->recurse_objects = false;
		}

Though this isn't tested.

Adopt and enforce new `WP_CLI_CS` standard

We have a new PHPCS standard for WP-CLI called WPCliCS (props @jrfnl). It is part of the wp-cli/wp-cli-tests package starting with version v2.1.0.

To adopt & enforce this new standard, the following actions need to be taken for this repository:

  • Create a PR that adds a custom ruleset phpcs.xml.dist to the repository

    • Add phpcs.xml.dist file
    • Adapt .distignore to ignore phpcs.xml.dist & phpunit.xml.dist
    • Adapt .gitignore to ignore phpunit.xml, phpcs.xml & .phpcs.xml
    • Require version ^2.1 of the wp-cli/wp-cli-tests as a dev dependency
  • Make any required changes to the code that fail the checks from the above ruleset in separate PRs

  • Merge thre ruleset once all required changes have been processed and merged

A sample PR for a simple repository can be seen here: https://github.com/wp-cli/maintenance-mode-command/pull/3/files

Related wp-cli/wp-cli#5179

Specify Key value when Search and Replace in Sterilized Data.

I came across the scenario of moving my S3 bucket and while doing that also changed the path.

I upload an image, It is sent to Kraken.io, and Uploaded to S3 using Media Offload.

I made a copy locally of the database and ran wp search-replace 'old-bucket 'new-bucket. To my joy, the serialized string was also updated.

My next task was to update the media path. Previously it would have been old-bucket/year/month

Now it would be new-bucket/media/year/month

If I were to search and replace based off of /2015 I am going to affect the _wp_attached_file and _wp_attachment_metadata

Is it possible to search inside of two columns where one column value is equal to something else?

Like look in meta_value where meta_key == amazonS3_info?

Add option to only show count

I'm trying to use this in an automated workflow for moving sites between various environments (live/dev/stage) and it would be really nice to get only the count of replacements for reporting purposes, something like --display-count-only. Right now I'm just grepping for the Success line which isn't the end of the world, just not the best solution.

I could create a quick PR for this, something like the below to be placed right before the table code

if ( \WP_CLI\Utils\get_flag_value( $assoc_args, 'display-count-only' ) ) {
    WP_CLI::line( $total );
    return;
}

The option --quite would obviously have precedence over this and this would also be compatible with --dry-run.

Search-replace percentages replaced by hash

If I use the search-replace command (with the export method), percentages are replaced by aa5581ad8d0dd2197104583347fac867d4b40a5088c41ff8c927244934459f6a

Anyone else with this problem or a solutions?

New feature: hide 0 changed results

Can we add an option to hide non changed results? When dealing with dbs that have 1000+ tables it becomes hard to filter which tables were affected.

Warning: unserialize() expects parameter 1 to be string, array given

Bug Report

Describe the current, buggy behavior

[Suppressed] Warning: unserialize() expects parameter 1 to be string, array given in wp-cli/search-replace-command/src/WP_CLI/SearchReplacer.php on line 85

Additionally, this results in warnings like this:

[Suppressed] Notice: unserialize(): Error at offset 0 of 24 bytes in wp-cli/search-replace-command/src/WP_CLI/SearchReplacer.php on line 85

Describe how other contributors can replicate this bug

I'm not 100% sure the conditions that trigger this, we've just noticed it while running search/replaces like this:

wp --path=/var/www/ search-replace http://foo.com https://foo.bar --all-tables --dry-run

Describe what you would expect as the correct outcome

No suppressed warnings because only strings get passed into unserialize().

Let us know what environment you are running this on

wp cli info
OS:    Linux 4.9.0-9-amd64 #1 SMP Debian 4.9.168-1 (2019-04-12) x86_64
Shell:    /bin/bash
PHP binary:    /<redacted>/php
PHP version:    7.3.5
php.ini used:    /<redacted>/php.ini
WP-CLI root dir:    phar://wp-cli.phar/vendor/wp-cli/wp-cli
WP-CLI vendor dir:    phar://wp-cli.phar/vendor
WP_CLI phar path:    /home/<redacted>
WP-CLI packages dir:    
WP-CLI global config:    
WP-CLI project config:    
WP-CLI version:    2.1.0

Provide a possible solution

This bug was introduced here:

babbfbf

It would seem that while trying to address PHPCS styles, the logic was changed.

Was:

if ( is_string( $data ) && false !== ( $unserialized = @unserialize( $data ) ) ) {

Is now:

$unserialized = @unserialize( $data );
if ( is_string( $data ) && false !== $unserialized ) {

The is_string() check needs to happen before unserialize() is called, not after.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.