I want to progressively enhance one of my extensions by altering the code paths to use another extension, if it's installed, and fall back to my own extension's code if not.

How can I check if an extension is installed?


The following will return an array keyed by the long extension 'key', whose values are one of installed uninstalled disabled (or, less commonly, unknown, installed-missing, disabled-missing)

$list = CRM_Extension_System::singleton()->getManager()->getStatuses();

You can find enabled ones with:

$list = array_keys(preg_grep('/^installed$/',

There's also getStatus(): to directly discover whether a particular extension is enabled, you might do

$extensionIsInstalled = 'installed' ===
if ($extensionIsInstalled) { ... }

This appears to be cached as it takes my test server 1.2s to do it a million times, though because of all the stack-work, you can cut this time by a third by implementing your own isXInstalled() function with a static var. Should you want to do this a million times quickly, that is :-)

You can also do it with an api3 call...

if ('installed' ===
  civicrm_api3('Extension', 'get', 
     ['key' => 'aah', 'sequential' => 1])['values'][0]['status'] ?? '') { ... }

But this is hideously inefficient for this purpose (it does a lot more), taking 40minutes to do it a million times!

Bonus extra

Once you know it's installed you can check the version like:


$recentEnough = version_compare(CRM_Extension_System::singleton()
  $minVersionString) >= 0;
  • 1
    Is another option to use a try/catch or use function_exists or class_exists? How does that compare when you run it 1 million times Nov 22 '21 at 3:38
  • @LukeStewart +1 for good suggestion. The problem with the class_exists is that it will trigger all the zillions of classloaders, which will all do various file system hunts for the file. So there's an overhead for the first call. After that I would expect class_exists to be as fast as a function with a static variable, i.e. pretty damn fast. I've done that in the past definitely, and in a way it's kinda better if you're depending on that particular implementation within an extension (but a class is not necessarily an API, of course). Nov 22 '21 at 9:17

You can also do it with a query. Obviously it is better to do it with the API but as explained above it takes a while....

$query = "SELECT COUNT(*) FROM civicrm_extension WHERE full_name = %1 AND is_active = TRUE";
$countExt = CRM_Core_DAO::singleValueQuery($query, [1 => ["aah", "String"]]);
  • Yep. But for completeness' sake: on the speed comparisons, this is also very slow (~4min for 1M lookups), as it's not cached and a lot of work goes in to building a query, quoting strings etc. Nov 17 '21 at 9:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.