Merge pull request #21294 from eileenmcnaughton/cont_form
[civicrm-core.git] / CRM / Extension / Info.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
bc77d7c0 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
bc77d7c0
TO
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 |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035
TO
11
12/**
13 * Metadata for an extension (e.g. the extension's "info.xml" file)
4faa436b
SB
14 *
15 * @package CRM
ca5cec67 16 * @copyright CiviCRM LLC https://civicrm.org/licensing
6a488035
TO
17 */
18class CRM_Extension_Info {
19
20 /**
fe482240 21 * Extension info file name.
6a488035
TO
22 */
23 const FILENAME = 'info.xml';
24
7b966967
SL
25 /**
26 * @var string
27 */
6a488035
TO
28 public $key = NULL;
29 public $type = NULL;
30 public $name = NULL;
31 public $label = NULL;
32 public $file = NULL;
33
4025c773
TO
34 /**
35 * @var array
36 * Each item is a specification like:
37 * array('type'=>'psr4', 'namespace'=>'Foo\Bar', 'path'=>'/foo/bar').
38 */
be2fb01f 39 public $classloader = [];
4025c773 40
f21a105f
TO
41 /**
42 * @var array
43 * Each item is they key-name of an extension required by this extension.
44 */
be2fb01f 45 public $requires = [];
f21a105f 46
9bb46670
TO
47 /**
48 * @var array
49 * List of strings (tag-names).
50 */
51 public $tags = [];
52
72657f71
TO
53 /**
54 * @var array
55 * List of authors.
56 * Ex: [0 => ['name' => 'Alice', 'email' => 'a@b', 'homepage' => 'https://example.com', 'role' => 'Person']]
57 */
58 public $authors = [];
59
60 /**
61 * @var array|null
62 * The current maintainer at time of publication.
63 * This is deprecated in favor of $authors.
64 * @deprecated
65 */
66 public $maintainer = NULL;
67
941c0926
TO
68 /**
69 * @var string|null
70 * The name of a class which handles the install/upgrade lifecycle.
71 * @see \CRM_Extension_Upgrader_Interface
72 */
73 public $upgrader = NULL;
74
6a488035 75 /**
fe482240 76 * Load extension info an XML file.
6a488035 77 *
6c8f6e67
EM
78 * @param $file
79 *
80 * @throws CRM_Extension_Exception_ParseException
6a488035 81 * @return CRM_Extension_Info
6a488035
TO
82 */
83 public static function loadFromFile($file) {
84 list ($xml, $error) = CRM_Utils_XML::parseFile($file);
85 if ($xml === FALSE) {
86 throw new CRM_Extension_Exception_ParseException("Failed to parse info XML: $error");
87 }
88
89 $instance = new CRM_Extension_Info();
90 $instance->parse($xml);
91 return $instance;
92 }
93
94 /**
fe482240 95 * Load extension info a string.
6a488035 96 *
f41911fd
TO
97 * @param string $string
98 * XML content.
77b97be7
EM
99 *
100 * @throws CRM_Extension_Exception_ParseException
6a488035
TO
101 * @return CRM_Extension_Info
102 */
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");
107 }
108
109 $instance = new CRM_Extension_Info();
110 $instance->parse($xml);
111 return $instance;
112 }
113
f21a105f
TO
114 /**
115 * Build a reverse-dependency map.
116 *
117 * @param array $infos
118 * The universe of available extensions.
119 * Ex: $infos['org.civicrm.foobar'] = new CRM_Extension_Info().
120 * @return array
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).
124 */
125 public static function buildReverseMap($infos) {
be2fb01f 126 $revMap = [];
f21a105f
TO
127 foreach ($infos as $info) {
128 foreach ($info->requires as $key) {
129 $revMap[$key][] = $info;
130 }
131 }
132 return $revMap;
133 }
134
e0ef6999
EM
135 /**
136 * @param null $key
137 * @param null $type
138 * @param null $name
139 * @param null $label
140 * @param null $file
141 */
00be9182 142 public function __construct($key = NULL, $type = NULL, $name = NULL, $label = NULL, $file = NULL) {
353ffa53
TO
143 $this->key = $key;
144 $this->type = $type;
145 $this->name = $name;
146 $this->label = $label;
147 $this->file = $file;
6a488035
TO
148 }
149
150 /**
151 * Copy attributes from an XML document to $this
152 *
153 * @param SimpleXMLElement $info
6a488035
TO
154 */
155 public function parse($info) {
353ffa53
TO
156 $this->key = (string) $info->attributes()->key;
157 $this->type = (string) $info->attributes()->type;
158 $this->file = (string) $info->file;
6a488035 159 $this->label = (string) $info->name;
941c0926 160 $this->upgrader = (string) $info->upgrader;
6a488035
TO
161
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) {
d8b46e13 167 $this->$attr = trim((string) $val);
6a488035
TO
168 }
169 elseif ($attr === 'urls') {
be2fb01f 170 $this->urls = [];
6a488035
TO
171 foreach ($val->url as $url) {
172 $urlAttr = (string) $url->attributes()->desc;
173 $this->urls[$urlAttr] = (string) $url;
174 }
175 ksort($this->urls);
176 }
4025c773 177 elseif ($attr === 'classloader') {
be2fb01f 178 $this->classloader = [];
4025c773 179 foreach ($val->psr4 as $psr4) {
be2fb01f 180 $this->classloader[] = [
4025c773
TO
181 'type' => 'psr4',
182 'prefix' => (string) $psr4->attributes()->prefix,
183 'path' => (string) $psr4->attributes()->path,
be2fb01f 184 ];
4025c773 185 }
a76a800b
TO
186 foreach ($val->psr0 as $psr0) {
187 $this->classloader[] = [
188 'type' => 'psr0',
189 'prefix' => (string) $psr0->attributes()->prefix,
190 'path' => (string) $psr0->attributes()->path,
191 ];
192 }
4025c773 193 }
9bb46670
TO
194 elseif ($attr === 'tags') {
195 $this->tags = [];
196 foreach ($val->tag as $tag) {
197 $this->tags[] = (string) $tag;
198 }
199 }
f21a105f 200 elseif ($attr === 'requires') {
e4c4f267 201 $this->requires = $this->filterRequirements($val);
f21a105f 202 }
72657f71
TO
203 elseif ($attr === 'maintainer') {
204 $this->maintainer = CRM_Utils_XML::xmlObjToArray($val);
205 $this->authors[] = [
206 'name' => (string) $val->author,
207 'email' => (string) $val->email,
208 'role' => 'Maintainer',
209 ];
210 }
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];
216 }
217 }
218 }
6a488035
TO
219 else {
220 $this->$attr = CRM_Utils_XML::xmlObjToArray($val);
221 }
222 }
223 }
224
e4c4f267
CW
225 /**
226 * Filter out invalid requirements, e.g. extensions that have been moved to core.
227 *
228 * @param SimpleXMLElement $requirements
229 * @return array
230 */
231 public function filterRequirements($requirements) {
232 $filtered = [];
233 $compatInfo = CRM_Extension_System::getCompatibilityInfo();
234 foreach ($requirements->ext as $ext) {
235 $ext = (string) $ext;
236 if (empty($compatInfo[$ext]['obsolete'])) {
237 $filtered[] = $ext;
238 }
239 }
240 return $filtered;
241 }
242
6a488035 243}