3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
13 * Metadata for an extension (e.g. the extension's "info.xml" file)
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 class CRM_Extension_Info
{
21 * Extension info file name.
23 const FILENAME
= 'info.xml';
36 * Each item is a specification like:
37 * array('type'=>'psr4', 'namespace'=>'Foo\Bar', 'path'=>'/foo/bar').
39 public $classloader = [];
43 * Each item is they key-name of an extension required by this extension.
45 public $requires = [];
49 * List of strings (tag-names).
56 * Ex: [0 => ['name' => 'Alice', 'email' => 'a@b', 'homepage' => 'https://example.com', 'role' => 'Person']]
62 * The current maintainer at time of publication.
63 * This is deprecated in favor of $authors.
66 public $maintainer = NULL;
70 * The name of a class which handles the install/upgrade lifecycle.
71 * @see \CRM_Extension_Upgrader_Interface
73 public $upgrader = NULL;
76 * Load extension info an XML file.
80 * @throws CRM_Extension_Exception_ParseException
81 * @return CRM_Extension_Info
83 public static function loadFromFile($file) {
84 list ($xml, $error) = CRM_Utils_XML
::parseFile($file);
86 throw new CRM_Extension_Exception_ParseException("Failed to parse info XML: $error");
89 $instance = new CRM_Extension_Info();
90 $instance->parse($xml);
95 * Load extension info a string.
97 * @param string $string
100 * @throws CRM_Extension_Exception_ParseException
101 * @return CRM_Extension_Info
103 public static function loadFromString($string) {
104 list ($xml, $error) = CRM_Utils_XML
::parseString($string);
105 if ($xml === FALSE) {
106 throw new CRM_Extension_Exception_ParseException("Failed to parse info XML: $string");
109 $instance = new CRM_Extension_Info();
110 $instance->parse($xml);
115 * Build a reverse-dependency map.
117 * @param array $infos
118 * The universe of available extensions.
119 * Ex: $infos['org.civicrm.foobar'] = new CRM_Extension_Info().
121 * If "org.civicrm.api" is required by "org.civicrm.foo", then return
122 * array('org.civicrm.api' => array(CRM_Extension_Info[org.civicrm.foo])).
123 * Array(string $key => array $requiredBys).
125 public static function buildReverseMap($infos) {
127 foreach ($infos as $info) {
128 foreach ($info->requires
as $key) {
129 $revMap[$key][] = $info;
142 public function __construct($key = NULL, $type = NULL, $name = NULL, $label = NULL, $file = NULL) {
146 $this->label
= $label;
151 * Copy attributes from an XML document to $this
153 * @param SimpleXMLElement $info
155 public function parse($info) {
156 $this->key
= (string) $info->attributes()->key
;
157 $this->type
= (string) $info->attributes()->type
;
158 $this->file
= (string) $info->file
;
159 $this->label
= (string) $info->name
;
160 $this->upgrader
= (string) $info->upgrader
;
162 // Convert first level variables to CRM_Core_Extension properties
163 // and deeper into arrays. An exception for URLS section, since
164 // we want them in special format.
165 foreach ($info as $attr => $val) {
166 if (count($val->children()) == 0) {
167 $this->$attr = trim((string) $val);
169 elseif ($attr === 'urls') {
171 foreach ($val->url
as $url) {
172 $urlAttr = (string) $url->attributes()->desc
;
173 $this->urls
[$urlAttr] = (string) $url;
177 elseif ($attr === 'classloader') {
178 $this->classloader
= [];
179 foreach ($val->psr4
as $psr4) {
180 $this->classloader
[] = [
182 'prefix' => (string) $psr4->attributes()->prefix
,
183 'path' => (string) $psr4->attributes()->path
,
186 foreach ($val->psr0
as $psr0) {
187 $this->classloader
[] = [
189 'prefix' => (string) $psr0->attributes()->prefix
,
190 'path' => (string) $psr0->attributes()->path
,
194 elseif ($attr === 'tags') {
196 foreach ($val->tag
as $tag) {
197 $this->tags
[] = (string) $tag;
200 elseif ($attr === 'requires') {
201 $this->requires
= $this->filterRequirements($val);
203 elseif ($attr === 'maintainer') {
204 $this->maintainer
= CRM_Utils_XML
::xmlObjToArray($val);
206 'name' => (string) $val->author
,
207 'email' => (string) $val->email
,
208 'role' => 'Maintainer',
211 elseif ($attr === 'authors') {
212 foreach ($val->author
as $author) {
213 $this->authors
[] = $thisAuthor = CRM_Utils_XML
::xmlObjToArray($author);
214 if ('maintainer' === strtolower($thisAuthor['role'] ??
'')) {
215 $this->maintainer
= ['author' => $thisAuthor['name'], 'email' => $thisAuthor['email'] ??
NULL];
220 $this->$attr = CRM_Utils_XML
::xmlObjToArray($val);
226 * Filter out invalid requirements, e.g. extensions that have been moved to core.
228 * @param SimpleXMLElement $requirements
231 public function filterRequirements($requirements) {
233 $compatInfo = CRM_Extension_System
::getCompatibilityInfo();
234 foreach ($requirements->ext
as $ext) {
235 $ext = (string) $ext;
236 if (empty($compatInfo[$ext]['obsolete'])) {