Answers.Body: <p>The following will return an array keyed by the long extension 'key', whose values are one of <code>installed</code> <code>uninstalled</code> <code>disabled</code> (or, less commonly, <code>unknown</code>, <code>installed-missing</code>, <code>disabled-missing</code>)</p>
  <pre><code>&lt;?php
  $list = CRM_Extension_System::singleton()-&gt;getManager()-&gt;getStatuses();
  </code></pre>
  <p>You can find enabled ones with:</p>
  <pre><code>&lt;?php
  $list = array_keys(preg_grep('/^installed$/',
          CRM_Extension_System::singleton()-&gt;getManager()-&gt;getStatuses()));
  </code></pre>
  <p>There's also <code>getStatus()</code>: to directly discover whether a particular extension is enabled, you might do</p>
  <pre><code>&lt;?php
  $extensionIsInstalled = 'installed' ===
    CRM_Extension_System::singleton()-&gt;getManager()-&gt;getStatus($extensionKey);
  if ($extensionIsInstalled) { ... }
  </code></pre>
  <p>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 <em>want</em> to do this a million times quickly, that is :-)</p>
  <h2>You <em>can</em> also do it with an api3 call...</h2>
  <pre><code>&lt;?php
  if ('installed' ===
    civicrm_api3('Extension', 'get', 
       ['key' =&gt; 'aah', 'sequential' =&gt; 1])['values'][0]['status'] ?? '') { ... }
  </code></pre>
  <p>But this is <em>hideously</em> inefficient for this purpose (it does a lot more), taking 40minutes to do it a million times!</p>
  <h2>Bonus extra</h2>
  <p>Once you know it's installed you can check the version like:</p>
  <pre><code>&lt;?php
  
  $recentEnough = version_compare(CRM_Extension_System::singleton()
    -&gt;getMapper()-&gt;keyToInfo($extensionKey)-&gt;version,
    $minVersionString) &gt;= 0;
  </code></pre>
  <h2>Bonus: check if a particular <em>component</em> is installed</h2>
  <pre><code>&lt;?php
  $memberIsInstalled = isset(\CRM_Core_Component::getEnabledComponents()['CiviMember']);
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-22T03:38:52.933Z
Answers.Text: 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
Answers.UserDisplayName: Luke Stewart
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-22T09:17:18.467Z
Answers.Text: @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).
Answers.UserDisplayName: artfulrobot
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-17T09:23:14.270Z
Answers.LastActivityDate: 2024-02-13T10:48:36.560Z
Answers.LastEditDate: 2024-02-13T10:48:36.560Z
Answers.UserDisplayName: artfulrobot
Answers.Body: <p>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....</p>
  <pre><code>$query = &quot;SELECT COUNT(*) FROM civicrm_extension WHERE full_name = %1 AND is_active = TRUE&quot;;
  $countExt = CRM_Core_DAO::singleValueQuery($query, [1 =&gt; [&quot;aah&quot;, &quot;String&quot;]]);
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-17T09:44:47.060Z
Answers.Text: 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.
Answers.UserDisplayName: artfulrobot
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-17T09:30:50.170Z
Answers.LastActivityDate: 2021-11-17T09:30:50.170Z
Answers.UserDisplayName: ErikH - CiviCooP
Body: <p>I want to <em>progressively enhance</em> 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.</p>
  <p>How can I check if an extension is installed?</p>
  
Tags: <extension><developer>
Title: How to programmatically check if an extension is installed
ThreadText: # How to programmatically check if an extension is installed
  
  (Asked by: username_0 on 2021-11-17)
  
  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?
  
  **Tags:** extension, developer
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_0 on 2021-11-17. Score: 2 | Accepted answer
  
  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`)
  
  ```
  <?php
  $list = CRM_Extension_System::singleton()->getManager()->getStatuses();
  
  ```
  
  You can find enabled ones with:
  
  ```
  <?php
  $list = array_keys(preg_grep('/^installed$/',
          CRM_Extension_System::singleton()->getManager()->getStatuses()));
  
  ```
  
  There's also `getStatus()`: to directly discover whether a particular extension is enabled, you might do
  
  ```
  <?php
  $extensionIsInstalled = 'installed' ===
    CRM_Extension_System::singleton()->getManager()->getStatus($extensionKey);
  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...
  
  ```
  <?php
  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:
  
  ```
  <?php
  
  $recentEnough = version_compare(CRM_Extension_System::singleton()
    ->getMapper()->keyToInfo($extensionKey)->version,
    $minVersionString) >= 0;
  
  ```
  
  ## Bonus: check if a particular *component* is installed
  
  ```
  <?php
  $memberIsInstalled = isset(\CRM_Core_Component::getEnabledComponents()['CiviMember']);
  
  ```
  
  > **Comments:**
  > **username_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  
  > **username_0**: @username_1 +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).  
  
  ---
  
  ### Answer by username_2 on 2021-11-17. Score: 1
  
  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"]]);
  
  ```
  
  > **Comments:**
  > **username_0**: 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.  
  
  ---
  
  