Merge pull request #17253 from mattwire/utf8convertblocksize
[civicrm-core.git] / CRM / Extension / Info.php
1 <?php
2 /*
3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
5 | |
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 +--------------------------------------------------------------------+
10 */
11
12 /**
13 * Metadata for an extension (e.g. the extension's "info.xml" file)
14 *
15 * @package CRM
16 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 */
18 class CRM_Extension_Info {
19
20 /**
21 * Extension info file name.
22 */
23 const FILENAME = 'info.xml';
24
25 /**
26 * @var string
27 */
28 public $key = NULL;
29 public $type = NULL;
30 public $name = NULL;
31 public $label = NULL;
32 public $file = NULL;
33
34 /**
35 * @var array
36 * Each item is a specification like:
37 * array('type'=>'psr4', 'namespace'=>'Foo\Bar', 'path'=>'/foo/bar').
38 */
39 public $classloader = [];
40
41 /**
42 * @var array
43 * Each item is they key-name of an extension required by this extension.
44 */
45 public $requires = [];
46
47 /**
48 * @var array
49 * List of strings (tag-names).
50 */
51 public $tags = [];
52
53 /**
54 * Load extension info an XML file.
55 *
56 * @param $file
57 *
58 * @throws CRM_Extension_Exception_ParseException
59 * @return CRM_Extension_Info
60 */
61 public static function loadFromFile($file) {
62 list ($xml, $error) = CRM_Utils_XML::parseFile($file);
63 if ($xml === FALSE) {
64 throw new CRM_Extension_Exception_ParseException("Failed to parse info XML: $error");
65 }
66
67 $instance = new CRM_Extension_Info();
68 $instance->parse($xml);
69 return $instance;
70 }
71
72 /**
73 * Load extension info a string.
74 *
75 * @param string $string
76 * XML content.
77 *
78 * @throws CRM_Extension_Exception_ParseException
79 * @return CRM_Extension_Info
80 */
81 public static function loadFromString($string) {
82 list ($xml, $error) = CRM_Utils_XML::parseString($string);
83 if ($xml === FALSE) {
84 throw new CRM_Extension_Exception_ParseException("Failed to parse info XML: $string");
85 }
86
87 $instance = new CRM_Extension_Info();
88 $instance->parse($xml);
89 return $instance;
90 }
91
92 /**
93 * Build a reverse-dependency map.
94 *
95 * @param array $infos
96 * The universe of available extensions.
97 * Ex: $infos['org.civicrm.foobar'] = new CRM_Extension_Info().
98 * @return array
99 * If "org.civicrm.api" is required by "org.civicrm.foo", then return
100 * array('org.civicrm.api' => array(CRM_Extension_Info[org.civicrm.foo])).
101 * Array(string $key => array $requiredBys).
102 */
103 public static function buildReverseMap($infos) {
104 $revMap = [];
105 foreach ($infos as $info) {
106 foreach ($info->requires as $key) {
107 $revMap[$key][] = $info;
108 }
109 }
110 return $revMap;
111 }
112
113 /**
114 * @param null $key
115 * @param null $type
116 * @param null $name
117 * @param null $label
118 * @param null $file
119 */
120 public function __construct($key = NULL, $type = NULL, $name = NULL, $label = NULL, $file = NULL) {
121 $this->key = $key;
122 $this->type = $type;
123 $this->name = $name;
124 $this->label = $label;
125 $this->file = $file;
126 }
127
128 /**
129 * Copy attributes from an XML document to $this
130 *
131 * @param SimpleXMLElement $info
132 */
133 public function parse($info) {
134 $this->key = (string) $info->attributes()->key;
135 $this->type = (string) $info->attributes()->type;
136 $this->file = (string) $info->file;
137 $this->label = (string) $info->name;
138
139 // Convert first level variables to CRM_Core_Extension properties
140 // and deeper into arrays. An exception for URLS section, since
141 // we want them in special format.
142 foreach ($info as $attr => $val) {
143 if (count($val->children()) == 0) {
144 $this->$attr = (string) $val;
145 }
146 elseif ($attr === 'urls') {
147 $this->urls = [];
148 foreach ($val->url as $url) {
149 $urlAttr = (string) $url->attributes()->desc;
150 $this->urls[$urlAttr] = (string) $url;
151 }
152 ksort($this->urls);
153 }
154 elseif ($attr === 'classloader') {
155 $this->classloader = [];
156 foreach ($val->psr4 as $psr4) {
157 $this->classloader[] = [
158 'type' => 'psr4',
159 'prefix' => (string) $psr4->attributes()->prefix,
160 'path' => (string) $psr4->attributes()->path,
161 ];
162 }
163 }
164 elseif ($attr === 'tags') {
165 $this->tags = [];
166 foreach ($val->tag as $tag) {
167 $this->tags[] = (string) $tag;
168 }
169 }
170 elseif ($attr === 'requires') {
171 $this->requires = $this->filterRequirements($val);
172 }
173 else {
174 $this->$attr = CRM_Utils_XML::xmlObjToArray($val);
175 }
176 }
177 }
178
179 /**
180 * Filter out invalid requirements, e.g. extensions that have been moved to core.
181 *
182 * @param SimpleXMLElement $requirements
183 * @return array
184 */
185 public function filterRequirements($requirements) {
186 $filtered = [];
187 $compatInfo = CRM_Extension_System::getCompatibilityInfo();
188 foreach ($requirements->ext as $ext) {
189 $ext = (string) $ext;
190 if (empty($compatInfo[$ext]['obsolete'])) {
191 $filtered[] = $ext;
192 }
193 }
194 return $filtered;
195 }
196
197 }