How to check 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 your plugin’s dependencies are installed and active to be sure you can use their functions.

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 for the purpose.

How to check if another plugin is running

WordPress provides a function that allows us to check if a plugin is running. 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 plugin file' ) ) {
	// Plugin is active
}

What you can do instead 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
}

Checking if all required plugins are active

We can now create a class that will allow us to check if all required plugins are running and throw an exception (that we will catch later) if they are not:

<?php
// my-plugin-name/classes/Dependency_Checker.php
class My_Plugin_Name_Dependency_Checker {

	/**
	 * Define the plugins that our plugin requires to function.
	 * Array format: 'Plugin Name' => 'Path to main plugin file'
	 */
	const REQUIRED_PLUGINS = array(
		'Akismet'     => 'akismet/akismet.php',
		'WooCommerce' => 'woocommerce/woocommerce.php',
	);

	/**
	 * Check if all required plugins are active, otherwise throw an exception.
	 *
	 * @throws My_Plugin_Name_Missing_Dependencies_Exception
	 */
	public function check() {
		$missing_plugins = $this->get_missing_plugins_list();
		if ( ! empty( $missing_plugins ) ) {
			throw new My_Plugin_Name_Missing_Dependencies_Exception( $missing_plugins );
		}
	}

	/**
	 * @return string[] Names of plugins that we require, but that are inactive.
	 */
	private function get_missing_plugins_list() {
		$missing_plugins = array();
		foreach ( self::REQUIRED_PLUGINS as $plugin_name => $main_file_path ) {
			if ( ! $this->is_plugin_active( $main_file_path ) ) {
				$missing_plugins[] = $plugin_name;
			}
		}
		return $missing_plugins;
	}

	/**
	 * @param string $main_file_path Path to main plugin file, as defined in self::REQUIRED_PLUGINS.
	 *
	 * @return bool
	 */
	private function is_plugin_active( $main_file_path ) {
		return in_array( $main_file_path, $this->get_active_plugins() );
	}

	/**
	 * @return string[] Returns an array of active plugins' main files.
	 */
	private function get_active_plugins() {
		return apply_filters( 'active_plugins', get_option( 'active_plugins' ) );
	}

}

And here is the exception that our class throws:

<?php
// my-plugin-name/classes/exceptions/Missing_Dependencies_Exception.php
class My_Plugin_Name_Missing_Dependencies_Exception extends My_Plugin_Name_Exception {

	/** @var string[] */
	private $missing_plugin_names;

	/**
	 * @param string[] $missing_plugin_names Names of the plugins that our plugin depends on,
	 *                                       that were found to be inactive.
	 */
	public function __construct( $missing_plugin_names ) {
		$this->missing_plugin_names = $missing_plugin_names;
	}

	/**
	 * @return string[]
	 */
	public function get_missing_plugin_names() {
		return $this->missing_plugin_names;
	}

}

… and the abstract class it extends:

<?php
// my-plugin-name/classes/exceptions/Exception.php
abstract class My_Plugin_Name_Exception extends Exception {

}

Suppressing plugin activation if dependencies are not met

If our dependencies are not met, we can prevent our plugin from executing any actual functionality. In a class that sets up our plugin, we can do the following:

<?php
// my-plugin-name/classes/Setup.php
class My_Plugin_Name_Setup {

	/** @var My_Plugin_Dependency_Checker */
	private $dependency_checker;

	public function init() {
		$this->load_classes();
		$this->create_instances();
		
		try {
			$this->dependency_checker->check();
		} catch ( My_Plugin_Name_Missing_Dependencies_Exception $e ) {
			return;
		}
		
		// Do actual plugin functionality registration here - add_action(), add_filter() etc.
	}

	private function load_classes() {
		// Exceptions
		require_once dirname( __FILE__ ) . '/exceptions/Exception.php';
		require_once dirname( __FILE__ ) . '/exceptions/Missing_Dependencies_Exception.php';

		// Dependency checker
		require_once dirname( __FILE__ ) . '/Dependency_Checker.php';
	}

	private function create_instances() {
		$this->dependency_checker = new My_Plugin_Name_Dependency_Checker();
	}

}

Reporting missing dependencies to admins

The code we wrote so far will check if all required plugins are running and only bind any 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.

To achieve this, we will create another class that will take care of reporting missing plugins to users with the activate_plugins capability (so that only user roles who can actually do something about it – and probably the only ones that should know about it – know about the issue). Only administrators have the activate_plugins capability by default.

<?php
// my-plugin-name/classes/Missing_Dependency_Reporter.php
class My_Plugin_Name_Missing_Dependency_Reporter {

	const REQUIRED_CAPABILITY = 'activate_plugins';

	/** @var string[] */
	private $missing_plugin_names;

	/**
	 * @param string[] $missing_plugin_names
	 */
	public function __construct( $missing_plugin_names ) {
		$this->missing_plugin_names = $missing_plugin_names;
	}

	public function bind_to_admin_hooks() {
		add_action( 'admin_notices', array( $this, 'display_admin_notice' ) );
	}

	public function display_admin_notice() {
		if ( ! current_user_can( self::REQUIRED_CAPABILITY ) ) {
			// If the user does not have the "activate_plugins" capability, do nothing.
			return;
		}

		$missing_plugin_names = $this->missing_plugin_names;
		include dirname( __FILE__ ) . '/../views/missing-dependencies-admin-notice.php';
	}

}

And the view to display the actual message:

<?php
// my-plugin-name/views/missing-dependencies-admin-notice.php
if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly
}

/** @var string[] $missing_plugin_names */
?>

<div class="error notice">
    <p>
        <strong>Error:</strong>
        The <em>My Plugin Name</em> plugin won't execute
        because the following required plugins are not active:
		<?php echo esc_html( implode( ', ', $missing_plugin_names ) ) ?>.
        Please activate these plugins.
    </p>
</div>

Now we have to make our plugin setup class run the Missing_Dependency_Reporter class whenever the dependencies are missing. Let’s rewrite our Setup class:

<?php
// my-plugin-name/classes/Setup.php (updated)
class My_Plugin_Name_Setup {

	/** @var My_Plugin_Dependency_Checker */
	private $dependency_checker;

	public function init() {
		$this->load_classes();
		$this->create_instances();
		
		try {
			$this->dependency_checker->check();
		} catch ( My_Plugin_Name_Missing_Dependencies_Exception $e ) {
			// The exception contains the names of missing plugins.
			$this->report_missing_dependencies( $e->get_missing_plugin_names() );
			return;
		}
		
		// Do actual plugin functionality registration here - add_action(), add_filter() etc.
	}

	private function load_classes() {
		// Exceptions
		require_once dirname( __FILE__ ) . '/exceptions/Exception.php';
		require_once dirname( __FILE__ ) . '/exceptions/Missing_Dependencies_Exception.php';

		// Dependency checker
		require_once dirname( __FILE__ ) . '/Dependency_Checker.php';
		require_once dirname( __FILE__ ) . '/Missing_Dependency_Reporter.php';
	}

	private function create_instances() {
		$this->dependency_checker = new My_Plugin_Name_Dependency_Checker();
	}
	
	/**
	 * @param string[] $missing_plugin_names
	 */
	private function report_missing_dependencies( $missing_plugin_names ) {
		$missing_dependency_reporter = new My_Plugin_Name_Missing_Dependency_Reporter( $missing_plugin_names );
		$missing_dependency_reporter->bind_to_admin_hooks();
	}

}

And finally, to execute our plugin’s Setup class:

<?php
// my-plugin-name/my-plugin-name.php
if ( ! class_exists( 'My_Plugin_Name_Setup' ) ) {
	require_once dirname( __FILE__ ) . '/classes/Setup.php';
	$my_plugin_name_setup = new My_Plugin_Name_Setup();
	$my_plugin_name_setup->init();
}

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

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!

Header photo by Pixabay