3 * package.xml parsing class, package.xml version 1.0
9 * @author Greg Beaver <cellog@php.net>
10 * @copyright 1997-2009 The Authors
11 * @license http://opensource.org/licenses/bsd-license.php New BSD License
12 * @version CVS: $Id: v1.php 313023 2011-07-06 19:17:11Z dufuz $
13 * @link http://pear.php.net/package/PEAR
14 * @since File available since Release 1.4.0a1
17 * package.xml abstraction class
19 require_once 'PEAR/PackageFile/v1.php';
21 * Parser for package.xml version 1.0
24 * @author Greg Beaver <cellog@php.net>
25 * @copyright 1997-2009 The Authors
26 * @license http://opensource.org/licenses/bsd-license.php New BSD License
27 * @version Release: @PEAR-VER@
28 * @link http://pear.php.net/package/PEAR
29 * @since Class available since Release 1.4.0a1
31 class PEAR_PackageFile_Parser_v1
37 * BC hack to allow PEAR_Common::infoFromString() to sort of
38 * work with the version 2.0 format - there's no filelist though
39 * @param PEAR_PackageFile_v2
41 function fromV2($packagefile)
43 $info = $packagefile->getArray(true);
44 $ret = new PEAR_PackageFile_v1;
45 $ret->fromArray($info['old']);
48 function setConfig(&$c)
51 $this->_registry = &$c->getRegistry();
54 function setLogger(&$l)
60 * @param string contents of package.xml file, version 1.0
61 * @return bool success of parsing
63 function &parse($data, $file, $archive = false)
65 if (!extension_loaded('xml')) {
66 return PEAR::raiseError('Cannot create xml parser for parsing package.xml, no xml extension');
68 $xp = xml_parser_create();
70 $a = &PEAR::raiseError('Cannot create xml parser for parsing package.xml');
73 xml_set_object($xp, $this);
74 xml_set_element_handler($xp, '_element_start_1_0', '_element_end_1_0');
75 xml_set_character_data_handler($xp, '_pkginfo_cdata_1_0');
76 xml_parser_set_option($xp, XML_OPTION_CASE_FOLDING, false);
78 $this->element_stack = array();
79 $this->_packageInfo = array('provides' => array());
80 $this->current_element = false;
81 unset($this->dir_install);
82 $this->_packageInfo['filelist'] = array();
83 $this->filelist =& $this->_packageInfo['filelist'];
84 $this->dir_names = array();
85 $this->in_changelog = false;
88 $this->_isValid = true;
90 if (!xml_parse($xp, $data, 1)) {
91 $code = xml_get_error_code($xp);
92 $line = xml_get_current_line_number($xp);
94 $a = &PEAR::raiseError(sprintf("XML error: %s at line %d",
95 $str = xml_error_string($code), $line), 2);
101 $pf = new PEAR_PackageFile_v1;
102 $pf->setConfig($this->_config);
103 if (isset($this->_logger)) {
104 $pf->setLogger($this->_logger);
106 $pf->setPackagefile($file, $archive);
107 $pf->fromArray($this->_packageInfo);
113 * Unindent given string
115 * @param string $str The string that has to be unindented.
119 function _unIndent($str)
121 // remove leading newlines
122 $str = preg_replace('/^[\r\n]+/', '', $str);
123 // find whitespace at the beginning of the first line
124 $indent_len = strspn($str, " \t");
125 $indent = substr($str, 0, $indent_len);
127 // remove the same amount of whitespace from following lines
128 foreach (explode("\n", $str) as $line) {
129 if (substr($line, 0, $indent_len) == $indent) {
130 $data .= substr($line, $indent_len) . "\n";
131 } elseif (trim(substr($line, 0, $indent_len))) {
132 $data .= ltrim($line);
138 // Support for package DTD v1.0:
139 // {{{ _element_start_1_0()
142 * XML parser callback for ending elements. Used for version 1.0
145 * @param resource $xp XML parser resource
146 * @param string $name name of ending element
152 function _element_start_1_0($xp, $name, $attribs)
154 array_push($this->element_stack, $name);
155 $this->current_element = $name;
156 $spos = sizeof($this->element_stack) - 2;
157 $this->prev_element = ($spos >= 0) ? $this->element_stack[$spos] : '';
158 $this->current_attributes = $attribs;
162 if ($this->in_changelog) {
165 if (array_key_exists('name', $attribs) && $attribs['name'] != '/') {
166 $attribs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
168 if (strrpos($attribs['name'], '/') === strlen($attribs['name']) - 1) {
169 $attribs['name'] = substr($attribs['name'], 0,
170 strlen($attribs['name']) - 1);
172 if (strpos($attribs['name'], '/') === 0) {
173 $attribs['name'] = substr($attribs['name'], 1);
175 $this->dir_names[] = $attribs['name'];
177 if (isset($attribs['baseinstalldir'])) {
178 $this->dir_install = $attribs['baseinstalldir'];
180 if (isset($attribs['role'])) {
181 $this->dir_role = $attribs['role'];
185 if ($this->in_changelog) {
188 if (isset($attribs['name'])) {
190 if (count($this->dir_names)) {
191 foreach ($this->dir_names as $dir) {
195 $path .= preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
197 unset($attribs['name']);
198 $this->current_path = $path;
199 $this->filelist[$path] = $attribs;
200 // Set the baseinstalldir only if the file don't have this attrib
201 if (!isset($this->filelist[$path]['baseinstalldir']) &&
202 isset($this->dir_install))
204 $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
207 if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
208 $this->filelist[$path]['role'] = $this->dir_role;
213 if (!$this->in_changelog) {
214 $this->filelist[$this->current_path]['replacements'][] = $attribs;
218 $this->_packageInfo['maintainers'] = array();
219 $this->m_i = 0; // maintainers array index
222 // compatibility check
223 if (!isset($this->_packageInfo['maintainers'])) {
224 $this->_packageInfo['maintainers'] = array();
227 $this->_packageInfo['maintainers'][$this->m_i] = array();
228 $this->current_maintainer =& $this->_packageInfo['maintainers'][$this->m_i];
231 $this->_packageInfo['changelog'] = array();
232 $this->c_i = 0; // changelog array index
233 $this->in_changelog = true;
236 if ($this->in_changelog) {
237 $this->_packageInfo['changelog'][$this->c_i] = array();
238 $this->current_release = &$this->_packageInfo['changelog'][$this->c_i];
240 $this->current_release = &$this->_packageInfo;
244 if (!$this->in_changelog) {
245 $this->_packageInfo['release_deps'] = array();
249 // dependencies array index
250 if (!$this->in_changelog) {
252 isset($attribs['type']) ? ($attribs['type'] = strtolower($attribs['type'])) : false;
253 $this->_packageInfo['release_deps'][$this->d_i] = $attribs;
256 case 'configureoptions':
257 if (!$this->in_changelog) {
258 $this->_packageInfo['configure_options'] = array();
261 case 'configureoption':
262 if (!$this->in_changelog) {
263 $this->_packageInfo['configure_options'][] = $attribs;
267 if (empty($attribs['type']) || empty($attribs['name'])) {
270 $attribs['explicit'] = true;
271 $this->_packageInfo['provides']["$attribs[type];$attribs[name]"] = $attribs;
274 if (isset($attribs['version'])) {
275 $this->_packageInfo['xsdversion'] = trim($attribs['version']);
277 $this->_packageInfo['xsdversion'] = '1.0';
279 if (isset($attribs['packagerversion'])) {
280 $this->_packageInfo['packagerversion'] = $attribs['packagerversion'];
287 // {{{ _element_end_1_0()
290 * XML parser callback for ending elements. Used for version 1.0
293 * @param resource $xp XML parser resource
294 * @param string $name name of ending element
300 function _element_end_1_0($xp, $name)
302 $data = trim($this->cdata);
305 switch ($this->prev_element) {
307 $this->_packageInfo['package'] = $data;
310 $this->current_maintainer['name'] = $data;
315 $this->_packageInfo['extends'] = $data;
318 $this->_packageInfo['summary'] = $data;
321 $data = $this->_unIndent($this->cdata);
322 $this->_packageInfo['description'] = $data;
325 $this->current_maintainer['handle'] = $data;
328 $this->current_maintainer['email'] = $data;
331 $this->current_maintainer['role'] = $data;
334 if ($this->in_changelog) {
335 $this->current_release['version'] = $data;
337 $this->_packageInfo['version'] = $data;
341 if ($this->in_changelog) {
342 $this->current_release['release_date'] = $data;
344 $this->_packageInfo['release_date'] = $data;
348 // try to "de-indent" release notes in case someone
349 // has been over-indenting their xml ;-)
350 // Trim only on the right side
351 $data = rtrim($this->_unIndent($this->cdata));
352 if ($this->in_changelog) {
353 $this->current_release['release_notes'] = $data;
355 $this->_packageInfo['release_notes'] = $data;
359 if ($this->in_changelog) {
360 $this->current_release['release_warnings'] = $data;
362 $this->_packageInfo['release_warnings'] = $data;
366 if ($this->in_changelog) {
367 $this->current_release['release_state'] = $data;
369 $this->_packageInfo['release_state'] = $data;
373 if ($this->in_changelog) {
374 $this->current_release['release_license'] = $data;
376 $this->_packageInfo['release_license'] = $data;
380 if ($data && !$this->in_changelog) {
381 $this->_packageInfo['release_deps'][$this->d_i]['name'] = $data;
385 if ($this->in_changelog) {
388 array_pop($this->dir_names);
391 if ($this->in_changelog) {
396 if (count($this->dir_names)) {
397 foreach ($this->dir_names as $dir) {
402 $this->filelist[$path] = $this->current_attributes;
403 // Set the baseinstalldir only if the file don't have this attrib
404 if (!isset($this->filelist[$path]['baseinstalldir']) &&
405 isset($this->dir_install))
407 $this->filelist[$path]['baseinstalldir'] = $this->dir_install;
410 if (!isset($this->filelist[$path]['role']) && isset($this->dir_role)) {
411 $this->filelist[$path]['role'] = $this->dir_role;
416 if (empty($this->_packageInfo['maintainers'][$this->m_i]['role'])) {
417 $this->_packageInfo['maintainers'][$this->m_i]['role'] = 'lead';
422 if ($this->in_changelog) {
427 $this->in_changelog = false;
430 array_pop($this->element_stack);
431 $spos = sizeof($this->element_stack) - 1;
432 $this->current_element = ($spos > 0) ? $this->element_stack[$spos] : '';
437 // {{{ _pkginfo_cdata_1_0()
440 * XML parser callback for character data. Used for version 1.0
443 * @param resource $xp XML parser resource
444 * @param string $name character data
450 function _pkginfo_cdata_1_0($xp, $data)
452 if (isset($this->cdata)) {
453 $this->cdata .= $data;