Explanation of a Python Script for Automatically Replacing URLs in a WordPress Database

  1. Issues Arising from Domain Changes

When migrating a WordPress site to another server or changing the domain, unexpected issues may arise throughout the site. In particular, if the embedded links and image URLs in the site continue to reference the old domain, certain parts of the site may not display correctly.

For example, the following issues commonly occur:

  • Images not displaying: Images inserted in posts or pages may stop appearing because they still use the old domain name. If the image links are not correctly updated, visitors will encounter “Image not found” errors.
  • Links not functioning: Internal and external links within the site may still reference the old domain, preventing users from navigating to the intended pages when clicked. This harms the user experience and negatively impacts SEO (Search Engine Optimization).
  • Unable to access the admin dashboard: If the WordPress admin dashboard is still operating based on the old domain, it may become inaccessible after migration. This issue can prevent you from making site settings or editing content, disrupting site management.
  • Themes and plugins not functioning properly: Some themes and plugins may have hardcoded URLs. If these settings are not updated, the themes and plugins may not function as expected.

These issues cannot be solved merely by moving the WordPress files because WordPress stores URLs in the database, and these must be properly updated. However, manually updating the database is time-consuming and prone to errors.

To address these issues efficiently, a more streamlined approach is required. One such tool is WP-CLI, which allows you to easily replace old URLs in the database with the new domain in bulk. However, the focus of this article is on a custom script, so please continue reading.

  1. What is WP-CLI?

To address the issues caused by domain changes during site migration, WP-CLI is a tool that can be very helpful. Beginners might wonder, “What is a CLI?” or “What is WP-CLI?” So let’s take a step-by-step approach to explain it clearly.

What is a CLI?

First, let’s briefly discuss the term CLI (Command Line Interface). A CLI is a system where you input commands into a text-based interface to interact with the computer. Typically, we operate by clicking on a browser or pressing buttons in an app, but with a CLI, you operate by “typing commands.”

For example, tasks such as opening folders or deleting files are performed using the keyboard instead of a mouse.

What is WP-CLI?

Now, let’s explain WP-CLI. WP-CLI is a tool that allows you to manage WordPress through the command line. Normally, WordPress is managed using a browser, but with WP-CLI, you can easily perform various operations on WordPress by simply entering commands.

For example:

  • You can install plugins or update themes instantly without opening a browser.
  • You can bulk-replace data in the database or delete specific posts.

In short, WP-CLI helps you streamline WordPress management tasks. It’s particularly useful when you need to replace URLs in the database after a domain change, as in this case.

Benefits of Using WP-CLI

Beginners may ask, “Is this really necessary?” But WP-CLI offers several advantages:

  • Speed: Instead of manually changing each item in the browser, WP-CLI allows you to complete multiple tasks at once with a single command.
  • Accuracy: WP-CLI supports serialized data, which can be easily corrupted with manual updates. This means it reduces the risk of data corruption.
  • Automation: Even if you manage multiple sites, you can apply the same commands across all of them, greatly improving efficiency.

How to Use WP-CLI

Now, let’s look at how to use WP-CLI with a simple example.

  1. Install WP-CLI

First, you need to install WP-CLI. Depending on your server environment, WP-CLI may not be pre-installed, so in that case, check with your server administrator or verify if your server supports WP-CLI.

For installation instructions, refer to the official WP-CLI website.

How to Install WP-CLI

We will explain how to install WP-CLI, a tool that makes managing WordPress more efficient, in a way that is easy for beginners to understand. Here, we will introduce the basic installation method using a Phar file.

  1. Check System Requirements

Before installing WP-CLI, check the system requirements. If the following conditions are met, you can install and use WP-CLI smoothly.

  • A UNIX-based environment (e.g., OS X, Linux, FreeBSD, Cygwin): WP-CLI can also be used on Windows, but some features may be limited.
  • PHP 5.6 or higher: Since WP-CLI runs on PHP, PHP must be installed. If your PHP version is outdated, update to the latest version.
  • WordPress version 3.7 or higher: If you’re using an older version of WordPress, some WP-CLI features may not work properly.
  1. Download WP-CLI

To install WP-CLI, you need to download the Phar file (an executable PHP archive). Using this file, you can easily install and use WP-CLI.

Run the following command in your terminal (command line) to download the WP-CLI Phar file:

curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

This command uses curl to download the WP-CLI Phar file from the internet. The file will be saved in your current directory under the name wp-cli.phar.

  1. Verify the Phar File is Working

Next, verify that the downloaded Phar file works correctly. Run the following command:

php wp-cli.phar --info

By running this command, you can check whether WP-CLI is functioning. If PHP and WP-CLI versions are displayed correctly, it means the installation was successful.

The command ‘php’ is not found. You can install it using the following method:
sudo apt install php8.3-cli # version 8.3.6-0ubuntu0.24.04.1, or
sudo apt install php-cli # version 2:8.2+93ubuntu1
sudo apt install php8.2-cli # version 8.2.12-1ubuntu2

WP-CLI requires PHP to run, but since PHP is not yet installed in my environment, this message is displayed. Let me explain the available options for Ubuntu 24.04.

Options:

  • php8.3-cli: This option installs the command line interface (CLI) for PHP 8.3. PHP 8.3 is the latest version, and it is expected to receive future updates and support. However, some WordPress plugins and themes may not yet be compatible with PHP 8.3.
  • php-cli (version 8.2): This option installs the CLI for PHP 8.2, which is provided as the default on Ubuntu 24.04. PHP 8.2 is stable and supported by WordPress and many plugins, making it an ideal recommendation.
  • php8.2-cli: This option installs only the CLI for PHP 8.2. It is similar to php-cli, but explicitly specifies the version to be installed.

Recommendation:

The safest choice is to install php-cli (version 8.2). PHP 8.2 is stable and highly compatible with many WordPress plugins and themes, so it’s less likely to cause any issues. Simply run the following command to install it:

sudo apt install php-cli

Once installed, you can run the php wp-cli.phar --info command again to confirm, and the output should appear as expected.

OS: Linux 6.8.0-41-generic #41-Ubuntu SMP PREEMPT_DYNAMIC Fri Aug 2 20:41:06 UTC 2024 x86_64
Shell: /bin/bash
PHP binary: /usr/bin/php8.3
PHP version: 8.3.6
php.ini used: /etc/php/8.3/cli/php.ini
MySQL binary: /usr/bin/mysql
MySQL version: mysql Ver 8.0.39-0ubuntu0.24.04.2 for Linux on x86_64 ((Ubuntu))
SQL modes:
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/mamu/mysql
WP-CLI packages dir:
WP-CLI cache dir: /home/mamu/.wp-cli/cache
WP-CLI global config:
WP-CLI project config:
WP-CLI version: 2.11.0

Even though I installed version 8.2, as shown in the result above, PHP 8.3 is being used. I looked into this issue.

Why does PHP version 8.3 show up even though I selected the second option (sudo apt install php-cli)?

Conclusion

When you install the php-cli package, it installs the system’s default PHP version’s CLI (in this case, PHP 8.3). As a result, when you run the php command, PHP 8.3 is used.

Detailed Explanation

About the php-cli Package

The php-cli package is a meta-package in Ubuntu that provides the CLI for the system’s default PHP version.

The default PHP version depends on your system’s repository configuration and what has been installed.

The installation options presented:

  • sudo apt install php8.3-cli # version 8.3.6-0ubuntu0.24.04.1
  • sudo apt install php-cli # version 2:8.2+93ubuntu1
  • sudo apt install php8.2-cli # version 8.2.12-1ubuntu2
  1. The first option explicitly installs the CLI for PHP 8.3.
  2. The second option installs the CLI via the php-cli meta-package, which provides the system’s default PHP version.
  3. The third option installs the CLI for PHP 8.2 explicitly.

Confusion over Version Numbers

The version number for the php-cli package (e.g., 2:8.2+93ubuntu1) refers to the package itself, not the PHP version it installs.

If PHP 8.3 is the system’s default version, then installing php-cli will install the CLI for PHP 8.3.

How the php Command Works

The php command points to the default version of PHP on the system.

Therefore, when you run php wp-cli.phar --info, PHP 8.3 is used because it is the system’s default version.

Why This Happens

Since the system’s default PHP version is PHP 8.3, installing the php-cli package installs the CLI for PHP 8.3, and the php command points to PHP 8.3.

Difference Between Package Version and PHP Version

The package version number is used for version control within the package management system and may not directly correspond to the version of the software itself.

Actions and Considerations

If everything is working correctly with the current setup, you can continue using it as is.

If PHP 8.3 is functioning properly and WP-CLI works without issues, there is no need to change anything.

If You Want to Use a Specific PHP Version

If you want to use PHP 8.2, follow these steps:

  1. Install the CLI for PHP 8.2bashコードをコピーするsudo apt install php8.2-cli
  2. Switch the default PHP version using the update-alternatives commandbashコードをコピーするsudo update-alternatives --config php From the list that appears, select the path for PHP 8.2.

Additional Information

  • update-alternatives Command
    If multiple PHP versions are installed, you can use this command to switch the default PHP version.
  • Check PHP Version
    You can verify the current PHP version by running the following command:bashコードをコピーするphp -v

The php-cli package installs the CLI for the system’s default PHP version, which in this case is PHP 8.3.

The reason the chosen option did not install the expected version is that php-cli is a meta-package, and it does not explicitly specify the PHP version.

If necessary, you can install a specific version’s CLI package and switch the default PHP version using the update-alternatives command.


  1. Making WP-CLI Usable as a Command

Once you have verified that the Phar file works correctly, you can make WP-CLI easier to use by setting it up to be executed with the wp command.

First, make the Phar file executable by running the following command:

chmod +x wp-cli.phar

Next, move the Phar file to a directory in your system’s PATH so that it can be run from anywhere:

sudo mv wp-cli.phar /usr/local/bin/wp

Now you can easily run WP-CLI using the wp command.


  1. Verifying WP-CLI Installation

To check if WP-CLI is installed correctly, run the following command:

wp --info

This command will display environment information used by WP-CLI, such as the PHP and MySQL versions. With this, the WP-CLI installation is complete.

WP-CLI Updates

WP-CLI is regularly updated, so it’s necessary to keep it up to date to use the latest features. To update WP-CLI, use the following command:

wp cli update

If WP-CLI was installed with administrative privileges, you will need to use sudo when updating:

sudo wp cli update

If you want to try the latest developer version, you can also update to the nightly build with the following command:

wp cli update --nightly

This way, you can work with the latest version of WP-CLI.

1. Setting Up Tab Completion

When using WP-CLI, you can improve your workflow by enabling “tab completion,” which allows you to automatically complete commands as you type. If you use Bash or zsh, follow these steps to set up tab completion:

For Bash:

Download the tab completion script:

curl -O https://raw.githubusercontent.com/wp-cli/wp-cli/master/utils/wp-completion.bash

Add the script to your ~/.bash_profile to load it:

source /FULL/PATH/TO/wp-completion.bash

To apply the changes, run the following command:

source ~/.bash_profile

For zsh:

To enable Bash tab completion in zsh, you need to add additional settings. Append the following lines to your ~/.zshrc file:

autoload bashcompinit
bashcompinit
source /FULL/PATH/TO/wp-completion.bash

Now, tab completion is enabled. When typing WP-CLI commands, you can press the Tab key to complete the command automatically.

2. Replacing Domain Names with WP-CLI

WP-CLI allows you to replace old domain names with new ones across your database when migrating. Use the following command:

wp search-replace 'http://old-domain.com' 'http://new-domain.com' --all-tables

This command will replace the old domain with the new one across all tables in the database.

Example Command:

wp search-replace '192.168.0.50' '192.168.0.91' --all-tables

wp search-replace: This part instructs WP-CLI to search and replace.

'http://old-domain.com': Specify the old domain here.

'http://new-domain.com': Input the new domain here.

--all-tables: This ensures the replacement runs across all WordPress tables.

3. Taking a Backup

Before using WP-CLI, always take a backup of your database to avoid issues in case of mistakes.

Example Backup Command:

wp db export backup.sql

This command exports the current database to a file called backup.sql.

WP-CLI is a highly useful tool, even for beginners, and can be a great help when performing tasks like domain changes during migration. It may feel a bit daunting at first, but once you try it, you’ll be surprised at how convenient it is. With WP-CLI, you’ll be able to manage databases, plugins, and more in no time, making it an essential tool for anyone looking to manage WordPress efficiently.

What is WP-CLI’s wp db export?

wp db export is a database export feature provided by WP-CLI. With this command, you can easily back up your WordPress database without needing to use mysqldump. While it functions similarly to mysqldump, it handles the database in a way that’s optimized for WordPress when executed through WP-CLI.

Command Explanation

wp db export backup.sql
  • wp db export: This is WP-CLI’s database export command. It exports the entire database used by the current WordPress installation.
  • backup.sql: This specifies the filename where the exported database will be saved. In this case, it will be saved as backup.sql.

Unlike mysqldump, this command is tailored for the WordPress environment, making it more convenient for those familiar with managing WordPress sites.

Comparison: mysqldump vs. wp db export

  • mysqldump: A general-purpose database export tool for MySQL. It can export databases beyond WordPress, making it a widely-used backup tool for various databases. I personally use this tool.
    mysqldump -u username -p database_name > backup.sql
  • wp db export: A command that simplifies exporting a WordPress database via WP-CLI. Unlike mysqldump, you don’t need to configure detailed options, and it integrates seamlessly with WordPress’s management environment, making it easier to back up.

Usability

  • For Beginners: wp db export is easier to use than mysqldump, especially in environments where WP-CLI is already installed, making it more user-friendly for beginners.
  • Integrated Tool: Using WP-CLI allows for consistent operations with other management tasks, making it convenient for users familiar with WordPress.

If WP-CLI is available in your environment, using this command for WordPress site backups makes it easy to back up your database.

How to Replace URLs with WP-CLI

When migrating a WordPress site or changing the domain name, you need to replace the URLs in the database. Doing this manually can be very tedious, but WP-CLI makes it easy, safe, and efficient to replace URLs.

Here, we’ll explain in detail how to replace URLs from the old domain to the new domain using WP-CLI.

Preparation Before Starting the Replacement

First, be sure to back up your database. If any issues arise during the replacement process, having a backup will allow you to easily restore the database.

As explained in the previous section, here’s a reminder on how to back up your database:

wp db export backup.sql

This command saves the current WordPress database to a file named backup.sql. This way, you can have peace of mind in case anything goes wrong. When you execute the wp db export backup.sql command, the backup file will be created in the directory where the command was run.

For example, if your current working directory is /home/user/, the backup.sql file will be saved as /home/user/backup.sql.

If you want to save the backup file to a specific location, you can specify the path. For instance, if you want to save the backup to the /home/user/backups/ folder, you would specify it like this:

wp db export /home/user/backups/backup.sql

This will create the backup file in the specified location.

However, the following error occurred.

Error: This does not seem to be a WordPress installation.
The used path is: /home/mamu/mysql/
Pass –path=`path/to/wordpress` or run `wp core download`.

This error message occurs because the command is being executed in a directory that is not the one where WordPress is installed. WP-CLI commands must be run in the directory where WordPress is installed.

Cause of the Error

The wp db export command expects to be run in the location where the WordPress files are present. If the current directory is not the WordPress directory, an error will occur.

Solution

1. Run the command in the WordPress installation directory

First, navigate to the directory where WordPress is installed before running the command. For example, if WordPress is installed in /var/www/html/wordpress, you can navigate to that directory with the following command:

cd /var/www/html/wordpress

Afterward, run the following command to take a backup:

wp db export backup.sql

2. Use the --path option

If you prefer not to leave your current directory, you can use the --path option to specify the path to the WordPress installation.

For example, if WordPress is installed in /var/www/html/wordpress, you can run the following command:

wp db export backup.sql --path=/var/www/html/wordpress

This will execute the command while specifying the path where WordPress is installed, and the backup will be created.

Solution 1: Navigate to the WordPress installation directory and execute the command.

Solution 2: Use the --path option to specify the WordPress installation directory.

Using WP-CLI inside Docker containers

If you’re running WordPress in a Docker environment, executing WP-CLI from the host may feel a bit more complex, but it can be handled appropriately. Even if you have mounted directories on the host, using WP-CLI within the Docker container is convenient.

Solution: Use WP-CLI inside the Docker container

When running WordPress in Docker, it is common to execute WP-CLI directly inside the container. This allows you to access the WordPress files and database directly.

Steps:

  1. Install WP-CLI inside the container
    If WP-CLI is not installed in the Docker container, log into the container with the following command:
    docker exec -it <container_name> bash
    Then, download and install WP-CLI inside the container:
    curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
    chmod +x wp-cli.phar
    mv wp-cli.phar /usr/local/bin/wp
  2. Take a backup using WP-CLI
    Run the wp db export command inside the container to back up the database:
    wp db export /var/www/html/backup.sql
    In this case, /var/www/html/ is the directory where the WordPress files are located. The backup file will be saved in /var/www/html/ within the container.

Sharing files between the host and container

If you have a mounted directory on the host, you can save the backup file to the host by using the mount point. For example, if the container’s /var/www/html/ is mounted to /home/user/wordpress/ on the host, the backup file will be saved as /home/user/wordpress/backup.sql on the host.

Example:

docker exec -it <container_name> wp db export /var/www/html/backup.sql

This will create a backup file in the folder mounted on the host.

Understanding the process

The docker exec command is used to execute commands inside the Docker container, so you can run it from any directory on the host. However, if WP-CLI is not installed in the container or if it is not correctly registered in the $PATH environment variable, an error will occur. It can be helpful to create a Dockerfile that installs WP-CLI.

# Base the image on the official WordPress image
FROM wordpress:latest

# Install curl and download WP-CLI
RUN apt-get update && apt-get install -y curl \
&& curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \
&& chmod +x wp-cli.phar \
&& mv wp-cli.phar /usr/local/bin/wp

# Install mariadb-client to use mysqldump
RUN apt-get install -y mariadb-client

However, you will also need to modify the docker-compose.yml file. Below is an example of a configuration that does not use a Dockerfile and relies on the official image (image: wordpress:latest), so no build is necessary. In this case, running docker compose build will not build anything since the configuration does not include custom build settings.

wordpress:
container_name: wp
depends_on:
- db
image: wordpress:latest
ports:
- "8080:80"

Enabling the build process

If you want to create a custom image, you need to add the build section to the docker-compose.yml file, instead of using image: wordpress:latest.

Example: Adding a build section to docker-compose.yml

wordpress:
container_name: wp
depends_on:
- db
build: .
image: custom-wordpress-wpcli # Use the custom-built image from the Dockerfile
ports:
- "8080:80"
  • build: .: This section tells Docker to build the image using the Dockerfile in the current directory.
  • image: custom-wordpress-wpcli: This specifies the name of the custom-built image. By naming the image, you can use it in the future.

Now, you’re ready to back up your database.

docker exec -it <container_name> wp db export /var/www/html/backup.sql

What to do if an error occurs?

Error: YIKES! It looks like you’re running this as root. You probably meant to run this as the user that your WordPress installation exists under.

If you REALLY mean to run this as root, we won’t stop you, but just bear in mind that any code on this site will then have full control of your server, making it quite DANGEROUS.

If you’d like to continue as root, please run this again, adding this flag: –allow-root

If you’d like to run it as the user that this site is under, you can run the following to become the respective user:

sudo -u USER -i — wp

This error occurs because WP-CLI is issuing a warning about executing commands as the root user. For security reasons, WP-CLI typically recommends running commands as the user who installed WordPress, rather than as root.

Solution

1. Use the --allow-root flag (simple solution)

By using this flag, you can execute WP-CLI as the root user. However, be aware that running commands as root carries security risks. If you’re in an environment where security isn’t a concern, this method should work without issues.

docker exec -it wp wp db export /var/www/html/backup.sql --allow-root

2. Execute the command as the appropriate user

Another solution is to switch to the user under which WordPress is installed and run WP-CLI as that user. In many WordPress installations, the www-data user is used. You can switch to the www-data user and run the command as follows:

docker exec -it wp sudo -u www-data wp db export /var/www/html/backup.sql

This will allow you to execute WP-CLI as the appropriate user, avoiding any security warnings.

  • Option 1: Using the --allow-root flag lets you run WP-CLI as root but be cautious about the security risks.
  • Option 2: Switching to the appropriate user (e.g., www-data) helps avoid security risks by running the command under the correct user.

What if the following error occurs?

OCI runtime exec failed: exec failed: unable to start container process: exec: “sudo”: executable file not found in $PATH: unknown

This error message indicates that the sudo command is not installed inside the container. Often, lightweight container images don’t include sudo, so you need to execute commands as the www-data user without using sudo.

Solution: Use the su command

To switch to the www-data user inside the container, use the su command:

docker exec -it --user www-data wp wp db export /var/www/html/backup.sql

This command uses Docker’s --user option to directly execute the command as the www-data user inside the container. This allows you to run WP-CLI with the correct user permissions without needing sudo.

  • If sudo is not available in the container, use the --user option to specify the user directly.

Running WP-CLI as the www-data user allows you to avoid security warnings while executing the command.

Backup File Location

The above command saves the backup.sql file to /var/www/html/ inside the Docker container. If this directory is mounted to /home/user/wordpress/ on the host, the backup file will also appear as /home/user/wordpress/backup.sql on the host.

Why you don’t need to be in the host directory

When using docker exec, you are executing commands inside the container. This means that no matter which directory you are in on the host, the operation is performed within the container. There is no need to be in a specific directory on the host.

The docker exec command can be run from any directory on the host, and there is no need to navigate to a specific directory on the host.

While the backup file is saved inside the container, if the directory is mounted to the host, the file will also be accessible on the host.

Using WP-CLI inside Docker containers is highly convenient. By installing WP-CLI within the container and running commands inside the container, you can easily perform tasks such as database operations and backups for WordPress.

2. Basic Flow of Replacing URLs

Next, let’s execute the basic command to replace old URLs with new ones. To do this, we use WP-CLI’s search-replace command. This command searches for the specified string (in this case, the old domain URL) and replaces it with a new string (the new domain URL).

Here is the basic command:

wp search-replace 'http://old-domain.com' 'http://new-domain.com' --all-tables

3. Detailed Explanation of the Command

Let’s break down each part of the command:

  • wp search-replace: This is the WP-CLI command that searches for a specified string and replaces it with another string in the WordPress database.
  • 'http://old-domain.com': This specifies the old URL that you want to search for. Here, you input the old domain name before migration.
  • 'http://new-domain.com': This is where you input the new domain name. This will be the URL that replaces the old one.
  • --all-tables: This option tells WP-CLI to search through all tables in the WordPress database. Since WordPress stores URLs in multiple tables, this ensures that all instances of the old URL are replaced.

4. Options for Safer Replacements

If you want to verify which parts will be replaced before making changes, you can use the --dry-run option. This option simulates the replacement process, showing what will be replaced without actually changing the data.

wp search-replace 'http://old-domain.com' 'http://new-domain.com' --all-tables --dry-run

This allows you to check which tables and columns will be modified, ensuring that you can proceed with the actual replacement confidently. If you are using Docker, you can run the following command:

wp search-replace '192.168.0.50' '192.168.0.91' --all-tables

You want to run this without entering the container. It is possible to execute this command directly from the host using WP-CLI inside the Docker container. However, you must run it as the appropriate user and ensure that mysqldump or mysql works properly within the environment.

How to Execute the Command

To execute the wp search-replace command without entering the container, use Docker’s exec command. As in the previous examples, run it as the www-data user:

docker exec -it --user www-data wp wp search-replace '192.168.0.50' '192.168.0.91' --all-tables

This allows you to execute the replacement directly from the host inside the container.

Important Notes

Running commands as the www-data user avoids the security warnings associated with running as the root user.

If mariadb-client or mysqldump is installed inside the container, the command should execute without issues.

If you encounter errors indicating that mysqldump or mysql is missing, follow the earlier steps to install mariadb-client.

By using docker exec, you can run the wp search-replace command without entering the container.

Once you’ve confirmed that the necessary user permissions and mysqldump are available inside the container, the command should run without errors.

5. Performing the Actual Replacement

Once the simulation runs successfully, you can proceed with the actual replacement by removing the --dry-run option:

wp search-replace 'http://old-domain.com' 'http://new-domain.com' --all-tables

This command will replace the old domain with the new one across the entire database. It targets all tables, ensuring that URLs in posts, images, and settings files are replaced in one go.

6. After the Replacement

After the replacement is complete, check your WordPress site to ensure that all URLs have been correctly updated to the new domain. Pay special attention to links in posts, image displays, and access to the admin panel to make sure there are no issues.

The wp search-replace 'http://old-domain.com' 'http://new-domain.com' --all-tables command replaces all instances of the old domain URL with the new domain URL across all tables in the WordPress database. Specifically, the following items will be replaced.

Replacement Targets

  • wp_posts table, post_content column: This includes URLs for links and images within the content of posts and pages. In other words, if the old domain is present in the article content, it will also be replaced with the new domain.
  • wp_options table, siteurl and home columns: The basic URL of WordPress and the URL of the front page will also be replaced.
  • wp_posts table, guid column: This includes the permalink (fixed link) URLs for posts and pages, which will also be replaced.
  • Other tables: The entire database, including tables created by plugins and themes, will be subject to replacement.

Including the Post Content (post_content)

By using the --all-tables option, all tables and columns in the database become replacement targets, including the post_content column. This ensures that URLs within the content of articles and pages, such as links and images, will also be replaced.

Caution

This command handles serialized data correctly, so even fields containing serialized data are safely replaced. However, because all tables are included, there is a risk that unintended replacements may occur. For example, external links or URLs stored in configuration files by certain plugins may also be changed.

The wp search-replace command performs replacements across the entire database, including the post content (post_content).

Since all tables are included, it is important to use this command carefully. It is highly recommended to back up the database before executing the command, to prevent any accidental data loss.

WP-CLI Allows Safe and Efficient URL Replacements

WP-CLI makes it easy to replace URLs across the entire database, saving a significant amount of time compared to manually changing each URL one by one. Additionally, since it handles serialized data properly, the risk of data corruption is minimized.

Although the process may seem a bit complex, following each command step by step will make it manageable, even for beginners. Always take a backup first and use the --dry-run option to proceed safely.

Importing a Database with WP-CLI

WP-CLI also provides a database import feature. Specifically, you can use the wp db import command to import a database backup file (usually an SQL file) into the WordPress database.

How to Use the wp db import Command

Here is the basic usage:

wp db import <filename>.sql

Replace <filename>.sql with the name of the SQL file you want to import.

Before running this command, make sure you are in the WordPress installation directory.

Example

For example, if you want to import a backup file named backup.sql:

wp db import backup.sql

Running this command will import the contents of backup.sql into the current database.

Important Notes

  • Database Backup: It is strongly recommended to back up the current database before performing an import. This will protect against data loss in case of any issues.
  • Verify Database Connection Info: Make sure the correct database connection information is set in the wp-config.php file.
  • Sufficient Permissions: Ensure that the user running WP-CLI has sufficient permissions to access the database.

Other Database-Related Commands

WP-CLI provides several other useful commands for database management:

  • wp db reset: Resets the database and deletes all tables.
    wp db reset
  • wp db query: Runs custom SQL queries.
    wp db query "SELECT * FROM wp_posts WHERE post_status='publish';"

By using these commands, you can efficiently manage your WordPress database.

What to Do When WP-CLI is Unavailable

While WP-CLI is a highly useful tool, there are situations where it may not be available or cannot be installed on certain servers. In such cases, you still need a method to replace URLs in the WordPress database. Here, we will introduce a method to replace URLs using a custom script when WP-CLI is not available on the server.

Why a Custom Script is Necessary

URLs are stored in various locations in the WordPress database. Finding and replacing all these URLs manually would be a tedious task. If WP-CLI is available, it can handle the replacement in one go. However, if WP-CLI is unavailable, you can create your own script to efficiently operate on the database.

Special care needs to be taken when dealing with serialized data, as manual replacement can be risky. Using a script ensures that the replacement process is safe and accurate.

Using a Python Script to Replace URLs

Here, we will introduce a Python script to replace URLs in a MySQL database. Python is a versatile programming language that makes it easy to connect to and manipulate databases. This script is especially useful in environments where WP-CLI cannot be used.

Preparation: Installing Python and Necessary Modules

First, check if Python is installed on your server. If it is not installed, you can either ask your server administrator to install it or download it from the official Python website.

Next, install the Python module called MySQL Connector. This allows Python to connect to a MySQL database. Use the following command to install MySQL Connector:

pip install mysql-connector-python

Additionally, install any other required modules.

Creating a Custom Script

Next, create a script that safely operates on specific tables and columns, replacing the old URL with the new one in bulk.

import mysql.connector
import logging
import os
import argparse
from dotenv import load_dotenv
from concurrent.futures import ThreadPoolExecutor, as_completed
from tqdm import tqdm
import phpserialize

# Load environment variables from the .env file
load_dotenv()

# Log file settings
logging.basicConfig(
    level=logging.DEBUG,  # Set log level to DEBUG
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('wpurl_replace.log', encoding='utf-8'),
        logging.StreamHandler()
    ]
)

# Parse command-line arguments
parser = argparse.ArgumentParser(description='WordPress URL replacement script')
parser.add_argument('--dry-run', action='store_true', help='Perform a dry run without making actual replacements')
parser.add_argument('--old-url', required=True, help='The URL to be replaced')
parser.add_argument('--new-url', required=True, help='The new URL to replace the old one')
args = parser.parse_args()
logging.debug(f"Command-line arguments: {args}")

# Database connection settings
db_config = {
    'user': os.getenv('DB_USER'),
    'password': os.getenv('DB_PASSWORD'),
    'host': os.getenv('DB_HOST'),
    'database': os.getenv('DB_NAME'),
}
logging.debug(f"Database settings: {db_config}")

# Function definitions (all included, no omissions)

# Function to retrieve the primary key for each table
def get_primary_key(table_name):
    primary_keys = {
        'wp_options': 'option_id',
        'wp_postmeta': 'meta_id',
        'wp_users': 'ID',
        'wp_posts': 'ID',
        'wp_commentmeta': 'meta_id',
        'wp_comments': 'comment_ID',
        'wp_links': 'link_id',
        'wp_term_taxonomy': 'term_taxonomy_id',
        'wp_termmeta': 'meta_id',
        'wp_terms': 'term_id',
        'wp_usermeta': 'umeta_id',
    }
    return primary_keys.get(table_name, 'id')  # Default is 'id'

# Function to establish a database connection
def connect_to_database():
    try:
        conn = mysql.connector.connect(**db_config, autocommit=True)
        logging.debug("Connected to the database")
        return conn
    except mysql.connector.Error as err:
        logging.error(f"Database connection error: {err}")
        raise

# Function to retrieve all tables
def get_all_tables(cursor):
    cursor.execute("SHOW TABLES")
    return [table[0] for table in cursor.fetchall()]

# Function to retrieve all columns for a specific table
def get_columns(cursor, table_name):
    cursor.execute(f"SHOW COLUMNS FROM `{table_name}`")
    return cursor.fetchall()

# Function to check if a column contains serialized data
def is_column_serialized(table_name, column_name):
    serialized_columns = {
        'wp_options': ['option_value'],
        'wp_postmeta': ['meta_value'],
        # Add other tables and columns containing serialized data
    }
    return table_name in serialized_columns and column_name in serialized_columns[table_name]

# Function to replace URLs in serialized data and re-serialize it
def unserialize_replace_serialize(data, old_url, new_url):
    try:
        unserialized = phpserialize.loads(data.encode())
        if isinstance(unserialized, dict):
            for key, value in unserialized.items():
                if isinstance(value, bytes):
                    value_str = value.decode()
                    unserialized[key] = value_str.replace(old_url, new_url).encode()
        return phpserialize.dumps(unserialized).decode()
    except Exception:
        return data.replace(old_url, new_url)

# Function to replace URLs in specific columns of a table
def replace_url_in_table(table_name, column_name, old_url, new_url, dry_run):
    conn = connect_to_database()  # Connect to the database per thread
    cursor = conn.cursor()
    try:
        primary_key = get_primary_key(table_name)  # Retrieve primary key
        if is_column_serialized(table_name, column_name):
            if dry_run:
                logging.info(f"Would replace serialized data in {table_name}.{column_name}")
                return 0
            else:
                sql = f"SELECT {primary_key}, `{column_name}` FROM `{table_name}` WHERE `{column_name}` LIKE %s"
                cursor.execute(sql, (f'%{old_url}%',))
                rows = cursor.fetchall()
                logging.debug(f"Found {len(rows)} rows in {table_name}.{column_name}")
                update_count = 0
                for row in rows:
                    id, data = row
                    new_data = unserialize_replace_serialize(data, old_url, new_url)
                    if new_data != data:
                        update_sql = f"UPDATE `{table_name}` SET `{column_name}` = %s WHERE {primary_key} = %s"
                        cursor.execute(update_sql, (new_data, id))
                        logging.debug(f"Updated row with ID {id} in {table_name}.{column_name}")
                        update_count += 1
                return update_count
        else:
            if dry_run:
                sql = f"SELECT COUNT(*) FROM `{table_name}` WHERE `{column_name}` LIKE %s"
                cursor.execute(sql, (f'%{old_url}%',))
                count = cursor.fetchone()[0]
                logging.info(f"Would replace {count} occurrences in {table_name}.{column_name}")
                return count
            else:
                sql_update = f"""
                UPDATE `{table_name}`
                SET `{column_name}` = REPLACE(`{column_name}`, %s, %s)
                WHERE `{column_name}` LIKE %s
                """
                cursor.execute(sql_update, (old_url, new_url, f'%{old_url}%'))
                affected_rows = cursor.rowcount
                logging.debug(f"Updated {affected_rows} rows in {table_name}.{column_name}")
                return affected_rows
    except mysql.connector.Error as err:
        logging.error(f"Error in {table_name}.{column_name}: {err}")
        return 0
    finally:
        cursor.close()
        conn.close()

def main():
    try:
        logging.debug("Starting the main function")
        conn = connect_to_database()
        cursor = conn.cursor()

        tables = get_all_tables(cursor)
        logging.debug(f"Retrieved tables: {tables}")
        total_updates = 0

        with ThreadPoolExecutor(max_workers=5) as executor:
            futures = []
            for table_name in tables:
                columns = get_columns(cursor, table_name)
                logging.debug(f"Columns in table {table_name}: {columns}")
                for (column_name, column_type, _, _, _, _) in columns:
                    if 'char' in column_type or 'text' in column_type or 'varchar' in column_type:
                        logging.debug(f"Processing {table_name}.{column_name}")
                        future = executor.submit(
                            replace_url_in_table,
                            table_name,
                            column_name,
                            args.old_url,
                            args.new_url,
                            args.dry_run
                        )
                        futures.append(future)

            with tqdm(total=len(futures), desc="Processing") as pbar:
                for future in as_completed(futures):
                    try:
                        result = future.result()
                        total_updates += result
                        pbar.update(1)
                    except Exception as e:
                        logging.error(f"Error during URL replacement: {e}", exc_info=True)

        print(f"Total updates: {total_updates}")
        if args.dry_run:
            print("Dry run completed. No changes were made.")
        else:
            print("URL replacement completed.")

    except Exception as e:
        logging.error(f"An unexpected error occurred: {e}", exc_info=True)
    finally:
        if 'conn' in locals() and conn.is_connected():
            cursor.close()
            conn.close()
            logging.debug("Closed the database connection")

if __name__ == "__main__":
    main()

Introduction

This script is a Python script for bulk replacing URLs in a WordPress database. When migrating a site to a new domain or server, it is necessary to replace old URLs with new ones, but doing this manually is a difficult task. By using this script, you can efficiently replace specified URLs from all text-type columns in the database.

Key Features

  • Database Connection: Connects to a MySQL database using information from a .env file.
  • Scanning All Tables and Columns: Automatically detects all tables and text-type columns in the database.
  • URL Replacement: Replaces the specified old URL with the new URL.
  • Handling Serialized Data: Correctly processes columns containing serialized data, such as the wp_options and wp_postmeta tables.
  • Dry Run Feature: By using the --dry-run option, you can check which data will be replaced without making actual changes.
  • Detailed Logging: Progress and results are recorded in the wpurl_replace.log file.

Environment and Preparation

Required Python Packages

  • Python 3.7 or higher
  • mysql-connector-python
  • python-dotenv
  • tqdm
  • phpserialize

Installing Packages

Use the following command to install the necessary packages:

pip install mysql-connector-python python-dotenv tqdm phpserialize

Creating the .env File

Create a .env file in the same directory as the script with the following contents to store database connection information:

DB_USER=your_database_username
DB_PASSWORD=your_database_password
DB_HOST=your_database_host_or_IP_address
DB_NAME=your_database_name

How to Use the Script

Command-Line Arguments

  • --old-url: The old URL to be replaced (required)
  • --new-url: The new URL to replace the old one (required)
  • --dry-run: Enable dry run mode (optional)

Example Usage

To actually replace the data:

python wpurl_replace.py --old-url http://oldsite.com --new-url http://newsite.com

To run in dry run mode (no changes will be made):

python wpurl_replace.py --old-url http://oldsite.com --new-url http://newsite.com --dry-run

Detailed Explanation of the Script

1. Connecting to the Database

The script reads the database connection information from the .env file and connects to the MySQL database.

The connection uses the autocommit option, ensuring that changes are automatically reflected in the database.

2. Detecting Tables and Columns

The script retrieves all tables in the database.

It then retrieves the column information for each table and identifies text-type columns (such as char, varchar, and text).

3. Replacing URLs

Handling Serialized Data:

For columns that contain serialized data, such as the option_value column in the wp_options table or the meta_value column in the wp_postmeta table, special handling is required.

The data is unserialized, the old URL is recursively replaced with the new URL, and then the data is re-serialized before being saved back into the database.

Handling Regular Text Data:

For text-type columns, the SQL REPLACE function is used to perform a bulk replacement.

Dry Run Mode:

When the --dry-run option is specified, no actual data changes are made. Instead, the script logs the number of records that would be replaced.

4. Logging

The script logs the progress and any errors to the wpurl_replace.log file.

Progress and results are also displayed in the console.

Important Notes

  • Backups: Be sure to back up the database before running the script to prevent any data loss in case of an issue.
  • Testing in a Staging Environment: First run the script in a staging environment to verify that it works as expected before running it in production.
  • Appropriate Permissions: Ensure that the user connecting to the database has sufficient read and write permissions.
  • Checking Logs: After running the script, check the wpurl_replace.log file for any errors.

Troubleshooting

If the log file is not generated:

  • Ensure that the script has write permissions in the directory where it is being executed.
  • Check that the logging settings are configured correctly in the script.

If an error occurs:

  • Review the error messages in the wpurl_replace.log file to identify the issue.
  • If necessary, adjust the logging level in the script to obtain more detailed information.

If no replacements are made:

  • Confirm that the old URL exists in the database.
  • Ensure that the --dry-run option is not enabled.

This script automates the process of replacing URLs during WordPress site migrations or domain changes, significantly reducing the time and effort required. By using it correctly, you can safely and efficiently update URLs in the database.

FAQ

Q1. When should this script be used?

This script is used when migrating a WordPress site to a new domain or server to replace old URLs with new ones in the database all at once.

Q2. What is serialized data?

Serialized data refers to data that has been serialized using PHP’s serialize() function. Arrays or objects are converted into a string and stored in the database. This script correctly deserializes and replaces the URLs in such data.

Q3. Is there anything I should be aware of before running the script?

Yes, be sure to back up your database. It is also recommended to verify the script’s behavior in a test environment before running it in production.


Support

If you have any questions or encounter issues, follow these steps for troubleshooting:

  • Check the log file: Review the wpurl_replace.log file for any error messages or warnings.
  • Verify your environment: Make sure that the correct version of Python and all necessary packages are installed.
  • Check your configuration: Confirm that the database connection information in the .env file and the command-line arguments are correct.

What if I’m not comfortable using the command line?

For those who are not familiar with the command line, web-based database management tools like PhpMyAdmin or Adminer are very useful. These tools allow you to manage databases intuitively through a web browser without needing to memorize commands, making them ideal for beginners or those who prefer GUI operations.


How to Import a Database Using PhpMyAdmin

  1. Access PhpMyAdmin: Open your browser and go to http://yourdomain.com/phpmyadmin to access PhpMyAdmin on your server.
  2. Select the database: From the list on the left, choose the database where you want to import the data.
  3. Click the “Import” tab: In the top menu, select the “Import” tab.
  4. Choose the file: Under the “File to import” section, select the local SQL file you want to import.
  5. Set options: Adjust the character set and format options as needed.
  6. Run the import: Click the “Go” button at the bottom of the page to start the import.

Benefits of PhpMyAdmin

  • User-friendly: The intuitive interface means you don’t need to memorize commands.
  • Immediate feedback: Results are displayed immediately, allowing you to easily track the progress of your operations.
  • Multi-functional: In addition to importing, PhpMyAdmin also allows you to edit tables, run queries, and more.

Things to Watch Out For

  • Large file imports: Depending on the server settings, there may be limits on the file size you can upload. In such cases, consider adjusting the server settings or using command-line tools.
  • Security: Since PhpMyAdmin is a powerful tool, it’s important to take proper security measures to prevent unauthorized access. Strengthen admin passwords and restrict access as necessary.

Alternative Tools

  • Adminer: A lightweight, single-file database management tool. It’s easier to set up than PhpMyAdmin and offers the essential features in a compact form.
  • MySQL Workbench: An official MySQL management tool available as a desktop application, offering advanced database management through a GUI.

For those who are uncomfortable with the command line or prefer a GUI, tools like PhpMyAdmin and Adminer are ideal. These tools make it easy to perform tasks such as importing/exporting databases and managing tables.


Key Points:

  • Choose the tool that suits you: Select the most appropriate tool based on your task and skill level.
  • Importance of backups: Always back up your database, regardless of the tool you are using.
  • Security measures: Ensure proper security practices when using web-based tools.

https://github.com/superdoccimo/wprep

Please share if you like it!
TOC