WordPress - how to disable plugins on certain environment

Ever wondered how to disable WordPress plugins based on your environment? If yes - read this article.

WordPress - how to disable plugins on certain environment

Tired of your caching plugin which ruins your experience while developing your next plugin or theme? If you want to learn how to disable WordPress plugins based on the current server environment continue below!

5 years have passed since I initially wrote the tip regarding disabling the WordPress plugin on certain page. Time passed by and this is still a valid method but it's not enough anymore, we would like to have even more control over our plugins.

Nowadays each of our projects uses plenty of environments. Let's forget complex setups for a minute (local->dev->stage/qa/test->prod or whatever floats your boat). Almost all of us have at least two environments - local and production.

What if your fancy cache plugin is driving you nuts when you try to develop that next template? You disable it but then it puts define( 'WP_CACHE', false ) everywhere and now you are afraid to commit it along with your "real" changes.

What if your analytics or backup plugin should not run on certain environment?

We need a way to be able to tell the plugins "go away" in certain situations and unfortunately WordPress doesn't have any solution out of the box (and this is actually a good thing, less code => better product).

The solution

As mentioned in my previous posts I am a big fan of Roots/Bedrock architecture so I will propose two solutions. First one is for the guys who still do WP development in old fashion way and second one for all of us, fancy Composer kids.

Stage 1 - define your needs.

First of all we have to define the plugins we would like to disable. Let's use an array stored in constant for that purpose.

In my case I would like to disable Autoptimize (file minification) and Wp Super Cache (page caching) on my local environment.

define('DEV_DISABLED_PLUGINS', serialize([
	'autoptimize/autoptimize.php',
	'wp-super-cache/wp-cache.php'
]));

Disclaimer: I know that in PHP7 you can store arrays in a constant without serialization but many WP folks are still on PHP 5.x.

Stage 2A - editing wp-config.php

If you have a regular WordPress architecture:

1.Edit your wp-config.php file (or local-config.php, if you follow that pattern).
2.Paste the define snippet from above. The best way would be to do it over the line saying: /* That's all, stop editing! Happy blogging. */.
3.Be sure to not push that file to production as in its bare form it wont protect you from disabling these plugins on prod.

Stage 2B - editing application configuration of Roots/Bedrock

If you use roots/bedrock:

1.Navigate to config/environments directory.
2.Choose the environment you want to apply your changes to. (It can be for example development.php)
3.Paste the define snippet at the end of the file.

Stage 3 - define MU-Plugin

Now we need to actually do the disabling part.

There are few ways to do it but the most common one is to leverage option_active_plugins filter. This method is still valid even though it was used first time many years ago.

Let's define our mu-plugin first
You can read what are the mu-plugins here. If you do not use them today think about incorporating them into your codebase.

Go to the:
A) wp-content/mu-plugins (regular WP)
or to
B)web/app/mu-plugins (Roots/Bedrock)
and create a file my-plugin-disabler.php there.

Contents of my-plugin-disabler.php

<?php
/*
Plugin Name: Simple Environment plugin disabler
Description:  Disables plugins based on environment settings.
Author: Kamil Grzegorczyk
Version: 0.1
*/
    
if ( defined( 'DEV_DISABLED_PLUGINS' ) ) {
    $plugins_to_disable = unserialize( DEV_DISABLED_PLUGINS );
    
    if ( ! empty( $plugins_to_disable ) && is_array( $plugins_to_disable ) ) {

        require_once( dirname( __FILE__ ) . '/vendor/DisablePlugins.php' );
        $utility = new DisablePlugins( $plugins_to_disable );
        
        //part below is optional but for me it is crucial
        error_log( 'Locally disabled plugins: ' . var_export( $plugins_to_disable, true ) );
	}
}

So far so good right? We created mu-plugin and checked if we have defined any plugins to be disabled.

Inside the IF statement we use the magical utility called DisablePlugins but we did not define that anywhere so let's fix that.

Add DisablePlugins utility
Years ago Mark Jaquith, one of WP Core developers, wrote a small utility to disable plugins using option_active_plugins filter.

Even though it was created like 7 years ago it will be more than enough in our scenario. And we want to be a smart developer who does not need to reinvent the wheel, right?

While still being in the mu-plugins directory please create vendor directory there and paste the following contents to DisablePlugins.php file:

<?php
	/**
	 * Plugin disabling engine class
	 * Author: Mark Jaquith
	 * Author URI: http://markjaquith.com/
	 * Plugin URI: https://gist.github.com/markjaquith/1044546
	 * Using fork: https://gist.github.com/Rarst/4402927
	 */

	class DisablePlugins {
		static $instance;
		private $disabled = [];

		/**
		 * Sets up the options filter, and optionally handles an array of plugins to disable
		 * @param array $disables Optional array of plugin filenames to disable
		 */
		public function __construct( Array $disables = NULL ) {
			/**
			 * Handle what was passed in
			 */
			if ( is_array( $disables ) ) {
				foreach ( $disables as $disable ) {
					$this->disable( $disable );
				}
			}

			/**
			 * Add the filters
			 */
			add_filter( 'option_active_plugins', [ $this, 'do_disabling' ] );
			add_filter( 'site_option_active_sitewide_plugins', [ $this, 'do_network_disabling' ] );

			/**
			 * Allow other plugins to access this instance
			 */
			self::$instance = $this;
		}

		/**
		 * Adds a filename to the list of plugins to disable
		 */
		public function disable( $file ) {
			$this->disabled[] = $file;
		}

		/**
		 * Hooks in to the option_active_plugins filter and does the disabling
		 * @param array $plugins WP-provided list of plugin filenames
		 * @return array The filtered array of plugin filenames
		 */
		public function do_disabling( $plugins ) {
			if ( count( $this->disabled ) ) {
				foreach ( (array)$this->disabled as $plugin ) {
					$key = array_search( $plugin, $plugins );
					if ( false !== $key ) {
						unset( $plugins[ $key ] );
					}
				}
			}

			return $plugins;
		}

		/**
		 * Hooks in to the site_option_active_sitewide_plugins filter and does the disabling
		 *
		 * @param array $plugins
		 *
		 * @return array
		 */
		public function do_network_disabling( $plugins ) {

			if ( count( $this->disabled ) ) {
				foreach ( (array)$this->disabled as $plugin ) {

					if ( isset( $plugins[ $plugin ] ) ) {
						unset( $plugins[ $plugin ] );
					}
				}
			}

			return $plugins;
		}
	}

Conclusion

It wasn't so scary was it?

Now you can use that solution on any environment you wish, you can even place it into your production config file if that fits your scenario.

Obviously only in situation where you do not share configs across the environments (another advantage of Roots/Bedrock set up which has separate configs out of the box). If you have one file though you can always rely on IF statements and request_uri detection but this is whole another story.

Take care!