Programmatically checking WordPress plugin dependencies

Sometimes a WordPress plugin you are writing extends the functionality of another plugin or simply relies on it to work. In these cases, you need to make sure the plugins your plugin depends on are installed and active to be sure you can use their functionality.

There are some existing libraries that you can use to make sure that a plugin is running – one such library is TGM Plugin Activation. Checking if another plugin is running, however, is fairly simple and you can implement it yourself if you don’t want to pull an external library into your project for the purpose.

UPDATE (April 2020): Since publishing this article in October 2018, I have updated the plugin code and moved the entirety of it to a GitHub repository for ease of access and improved readability. I have also updated this article to only include the key points of the implementation, leaving housing the rest of it to the repository.

The complete code

The complete code of the implementation described here is available in this GitHub repository.

How to check if another plugin is running

WordPress provides a function that allows us to check if a plugin is running. However, it requires you to load extra code that is only available in the admin dashboard by default:

include_once ABSPATH . 'wp-admin/includes/plugin.php';
if ( is_plugin_active( 'path to main plugin file' ) ) {
	// Plugin is active
}

What you can do to bypass this is what this WooCommerce guide advises:

$active_plugins = apply_filters( 'active_plugins', get_option( 'active_plugins' ) );
if ( in_array( 'woocommerce/woocommerce.php', $active_plugins ) ) {
    // Plugin is active
}

Now that we know how to check if a plugin is running, we can implement this in our plugin. Here is how our plugin is going to work:

  • First, check if all of the plugins that our plugin requires to function are installed and activated.
  • If they are, run our plugin’s actual logic.
  • If they are not, do not run the actual plugin logic and display a notice to the administrators.

The plugin file

As with every plugin, we need a file to serve as an entry point for the plugin. Its only task will be to load a setup class called Check_Plugin_Dependencies that will take care of setting up the rest of the plugin (please see the GitHub repository for the full implementation with PHPDoc documentation rather than excerpts from the code).

<?php
// check-plugin-dependencies.php

// Load the required class files here, etc.
$check_plugin_dependencies = new Check_Plugin_Dependencies();
$check_plugin_dependencies->setup();

The main class: Check_Plugin_Dependencies

Let’s create the class that control what happens based on whether the plugins we require are running.

Here is the structure of our class’ setup() method that is called by our plugin’s main file:

<?php
// includes/Check_Plugin_Dependencies.php

class Check_Plugin_Dependencies {

	public function setup() {
		try {
			$this->check_dependencies();
			$this->run();
		} catch ( Missing_Dependencies_Exception $e ) {
			$this->display_missing_dependencies_notice( $e );
		}
	}

	// Other methods will be defined here later.

}

$this->check_dependencies() is the method that orders the check to be executed.

If no exception is thrown inside $this->check_dependencies(), the $this->run() method will be called. This is the method that will execute the actual plugin logic which requires some plugins to be active.

If, however, Missing_Dependencies_Exception is thrown, control will be passed to the catch block that then calls $this->display_missing_dependencies_notice().

Here is the code of the other methods in the class:

<?php
// includes/Check_Plugin_Dependencies.php (again)

class Check_Plugin_Dependencies {

	public function setup() {
		try {
			$this->check_dependencies();
			$this->run();
		} catch ( Missing_Dependencies_Exception $e ) {
			$this->display_missing_dependencies_notice( $e );
		}
	}

	private function check_dependencies() {
		$dependency_checker = new Dependency_Checker();
		$dependency_checker->check();
	}

	private function run() {
		// Execute the actual plugin functionality here - maybe define some hooks using add_action(), add_filter() etc.
	}

	private function display_missing_dependencies_notice( Missing_Dependencies_Exception $e ) {
		$missing_dependency_reporter = new Missing_Dependency_Reporter( $e->get_missing_plugin_names() );
		$missing_dependency_reporter->init();
	}

}

As you can see above, both check_dependencies() and display_missing_dependencies_notice() use other objects to do the dependency check and to display the notice. The main class we have just created only takes care of controlling the flow of things.

The class running the check: Dependency_Checker

This is the class that will define what plugins need to be active and then check them against the active_plugins option in the database as mentioned at the beginning of this article.

Please consult the example in the repository for a documented version of each class, including this one.

<?php
// includes/Dependency_Checker.php

class Dependency_Checker {

	/**
	 * Define the plugins our plugin requires to function.
	 *
	 * Example:
	 *
	 *    const REQUIRED_PLUGINS = array(
	 *        'Some Plugin'    => 'some-plugin/some-plugin.php',
	 *        'Another Plugin' => 'another-plugin/another-plugin.php',
	 *    );
	 */
	const REQUIRED_PLUGINS = array(
		'Hello Dolly' => 'hello-dolly/hello.php',
		'WooCommerce' => 'woocommerce/woocommerce.php',
	);

	public function check() {
		$missing_plugins = $this->get_missing_plugin_list();

		if ( ! empty( $missing_plugins ) ) {
			// The exception holds the names of missing plugins.
			throw new Missing_Dependencies_Exception( $missing_plugins );
		}
	}

	private function get_missing_plugin_list() {
		// Only get the plugins that are *not* active.
		$missing_plugins = array_filter(
			self::REQUIRED_PLUGINS,
			array( $this, 'is_plugin_inactive' ),
			ARRAY_FILTER_USE_BOTH
		);

		return array_keys( $missing_plugins );
	}

	private function is_plugin_inactive( $main_plugin_file_path ) {
		return ! in_array( $main_plugin_file_path, $this->get_active_plugins() );
	}

	private function get_active_plugins() {
		return apply_filters( 'active_plugins', get_option( 'active_plugins' ) );
	}

}

The class displaying the notice: Missing_Dependency_Reporter

The code we wrote so far will check if all required plugins are running and only run actual functionality if they are. It would also be nice if we got a notice in the admin dashboard if the required plugins are not running, but only if we have the appropriate permissions to do something about it (or should know about it).

To achieve this, we will create another class that will take care of reporting missing plugins to users with the activate_plugins capability. Only administrators have this capability by default.

<?php
// includes/Admin/Missing_Dependency_Reporter.php

class Missing_Dependency_Reporter {

	const CAPABILITY_REQUIRED_TO_SEE_NOTICE = 'activate_plugins';

	/**
	 * Stores the missing plugin names extracted from the exception.
	 */
	private $missing_plugin_names;

	public function __construct( $missing_plugin_names ) {
		$this->missing_plugin_names = $missing_plugin_names;
	}

	/**
	 * Main method that hooks into the 'admin_notices' hook that only
	 * runs in the admin dashboard.
	 */
	public function init() {
		add_action( 'admin_notices', array( $this, 'display_admin_notice' ) );
	}

	public function display_admin_notice() {
		if ( current_user_can( self::CAPABILITY_REQUIRED_TO_SEE_NOTICE ) ) {
			$this->render_template();
		}
	}

	private function render_template() {
		// This allows us to access the $missing_plugin_names variable in the view template.
		$missing_plugin_names = $this->missing_plugin_names;

		/**
		 * The notice informing of plugin dependencies not being met.
		 */
		include dirname( CHECK_PLUGIN_DEPENDENCIES_PLUGIN_FILE ) . '/views/admin/missing-dependencies-notice.php';
	}

}

Finally, this is the notice that will be displayed to the administrator if some required plugins are not active:

<?php
// views/admin/missing-dependencies-notice.php
?>

<div class="error notice">
	<p>
		<?php
			printf(
				wp_kses(
					__(
						'<strong>Error:</strong> The <em>Check Plugin Dependencies</em> plugin cannot execute because'
						. ' the following required plugins are not active: %s. Please activate these plugins.',
						'check-plugin-dependencies'
					),
					array(
						'strong' => array(),
						'em'     => array(),
					)
				),
				esc_html( implode( ', ', $missing_plugin_names ) )
			);
		?>
	</p>
</div>

And that’s it! We have a fully-functional plugin dependency checker.

Some final remarks

Another way of handling unmet dependencies would be to immediately deactivate the plugin. I decided it might be more useful to keep the plugin active but prevent its logic from running. This way the notice can be displayed on each page of the admin dashboard instead of just once as deactivating the plugin would also deactivate the notice-displaying code.

The code could be extended to also check other things, possibly using the version_compare() function, such as:

Please let me know if you liked this post and if it was useful for you. Maybe you know of a better approach? I would love to hear about it!

Comments