Initial repo created
[timetracker.git] / WEB-INF / lib / pear / PEAR / PackageFile / v2 / rw.php
1 <?php
2 /**
3  * PEAR_PackageFile_v2, package.xml version 2.0, read/write version
4  *
5  * PHP versions 4 and 5
6  *
7  * @category   pear
8  * @package    PEAR
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: rw.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.0a8
15  */
16 /**
17  * For base class
18  */
19 require_once 'PEAR/PackageFile/v2.php';
20 /**
21  * @category   pear
22  * @package    PEAR
23  * @author     Greg Beaver <cellog@php.net>
24  * @copyright  1997-2009 The Authors
25  * @license    http://opensource.org/licenses/bsd-license.php New BSD License
26  * @version    Release: 1.9.4
27  * @link       http://pear.php.net/package/PEAR
28  * @since      Class available since Release 1.4.0a8
29  */
30 class PEAR_PackageFile_v2_rw extends PEAR_PackageFile_v2
31 {
32     /**
33      * @param string Extension name
34      * @return bool success of operation
35      */
36     function setProvidesExtension($extension)
37     {
38         if (in_array($this->getPackageType(),
39               array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
40             if (!isset($this->_packageInfo['providesextension'])) {
41                 // ensure that the channel tag is set up in the right location
42                 $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
43                     array('usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
44                     'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
45                     'bundle', 'changelog'),
46                     $extension, 'providesextension');
47             }
48             $this->_packageInfo['providesextension'] = $extension;
49             return true;
50         }
51         return false;
52     }
53
54     function setPackage($package)
55     {
56         $this->_isValid = 0;
57         if (!isset($this->_packageInfo['attribs'])) {
58             $this->_packageInfo = array_merge(array('attribs' => array(
59                                  'version' => '2.0',
60                                  'xmlns' => 'http://pear.php.net/dtd/package-2.0',
61                                  'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
62                                  'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
63                                  'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
64     http://pear.php.net/dtd/tasks-1.0.xsd
65     http://pear.php.net/dtd/package-2.0
66     http://pear.php.net/dtd/package-2.0.xsd',
67                              )), $this->_packageInfo);
68         }
69         if (!isset($this->_packageInfo['name'])) {
70             return $this->_packageInfo = array_merge(array('name' => $package),
71                 $this->_packageInfo);
72         }
73         $this->_packageInfo['name'] = $package;
74     }
75
76     /**
77      * set this as a package.xml version 2.1
78      * @access private
79      */
80     function _setPackageVersion2_1()
81     {
82         $info = array(
83                                  'version' => '2.1',
84                                  'xmlns' => 'http://pear.php.net/dtd/package-2.1',
85                                  'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
86                                  'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
87                                  'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
88     http://pear.php.net/dtd/tasks-1.0.xsd
89     http://pear.php.net/dtd/package-2.1
90     http://pear.php.net/dtd/package-2.1.xsd',
91                              );
92         if (!isset($this->_packageInfo['attribs'])) {
93             $this->_packageInfo = array_merge(array('attribs' => $info), $this->_packageInfo);
94         } else {
95             $this->_packageInfo['attribs'] = $info;
96         }
97     }
98
99     function setUri($uri)
100     {
101         unset($this->_packageInfo['channel']);
102         $this->_isValid = 0;
103         if (!isset($this->_packageInfo['uri'])) {
104             // ensure that the uri tag is set up in the right location
105             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
106                 array('extends', 'summary', 'description', 'lead',
107                 'developer', 'contributor', 'helper', 'date', 'time', 'version',
108                 'stability', 'license', 'notes', 'contents', 'compatible',
109                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
110                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
111                 'extbinrelease', 'bundle', 'changelog'), $uri, 'uri');
112         }
113         $this->_packageInfo['uri'] = $uri;
114     }
115
116     function setChannel($channel)
117     {
118         unset($this->_packageInfo['uri']);
119         $this->_isValid = 0;
120         if (!isset($this->_packageInfo['channel'])) {
121             // ensure that the channel tag is set up in the right location
122             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
123                 array('extends', 'summary', 'description', 'lead',
124                 'developer', 'contributor', 'helper', 'date', 'time', 'version',
125                 'stability', 'license', 'notes', 'contents', 'compatible',
126                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
127                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
128                 'extbinrelease', 'bundle', 'changelog'), $channel, 'channel');
129         }
130         $this->_packageInfo['channel'] = $channel;
131     }
132
133     function setExtends($extends)
134     {
135         $this->_isValid = 0;
136         if (!isset($this->_packageInfo['extends'])) {
137             // ensure that the extends tag is set up in the right location
138             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
139                 array('summary', 'description', 'lead',
140                 'developer', 'contributor', 'helper', 'date', 'time', 'version',
141                 'stability', 'license', 'notes', 'contents', 'compatible',
142                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
143                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
144                 'extbinrelease', 'bundle', 'changelog'), $extends, 'extends');
145         }
146         $this->_packageInfo['extends'] = $extends;
147     }
148
149     function setSummary($summary)
150     {
151         $this->_isValid = 0;
152         if (!isset($this->_packageInfo['summary'])) {
153             // ensure that the summary tag is set up in the right location
154             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
155                 array('description', 'lead',
156                 'developer', 'contributor', 'helper', 'date', 'time', 'version',
157                 'stability', 'license', 'notes', 'contents', 'compatible',
158                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
159                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
160                 'extbinrelease', 'bundle', 'changelog'), $summary, 'summary');
161         }
162         $this->_packageInfo['summary'] = $summary;
163     }
164
165     function setDescription($desc)
166     {
167         $this->_isValid = 0;
168         if (!isset($this->_packageInfo['description'])) {
169             // ensure that the description tag is set up in the right location
170             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
171                 array('lead',
172                 'developer', 'contributor', 'helper', 'date', 'time', 'version',
173                 'stability', 'license', 'notes', 'contents', 'compatible',
174                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
175                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
176                 'extbinrelease', 'bundle', 'changelog'), $desc, 'description');
177         }
178         $this->_packageInfo['description'] = $desc;
179     }
180
181     /**
182      * Adds a new maintainer - no checking of duplicates is performed, use
183      * updatemaintainer for that purpose.
184      */
185     function addMaintainer($role, $handle, $name, $email, $active = 'yes')
186     {
187         if (!in_array($role, array('lead', 'developer', 'contributor', 'helper'))) {
188             return false;
189         }
190         if (isset($this->_packageInfo[$role])) {
191             if (!isset($this->_packageInfo[$role][0])) {
192                 $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
193             }
194             $this->_packageInfo[$role][] =
195                 array(
196                     'name' => $name,
197                     'user' => $handle,
198                     'email' => $email,
199                     'active' => $active,
200                 );
201         } else {
202             $testarr = array('lead',
203                     'developer', 'contributor', 'helper', 'date', 'time', 'version',
204                     'stability', 'license', 'notes', 'contents', 'compatible',
205                     'dependencies', 'providesextension', 'usesrole', 'usestask',
206                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
207                     'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog');
208             foreach (array('lead', 'developer', 'contributor', 'helper') as $testrole) {
209                 array_shift($testarr);
210                 if ($role == $testrole) {
211                     break;
212                 }
213             }
214             if (!isset($this->_packageInfo[$role])) {
215                 // ensure that the extends tag is set up in the right location
216                 $this->_packageInfo = $this->_insertBefore($this->_packageInfo, $testarr,
217                     array(), $role);
218             }
219             $this->_packageInfo[$role] =
220                 array(
221                     'name' => $name,
222                     'user' => $handle,
223                     'email' => $email,
224                     'active' => $active,
225                 );
226         }
227         $this->_isValid = 0;
228     }
229
230     function updateMaintainer($newrole, $handle, $name, $email, $active = 'yes')
231     {
232         $found = false;
233         foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
234             if (!isset($this->_packageInfo[$role])) {
235                 continue;
236             }
237             $info = $this->_packageInfo[$role];
238             if (!isset($info[0])) {
239                 if ($info['user'] == $handle) {
240                     $found = true;
241                     break;
242                 }
243             }
244             foreach ($info as $i => $maintainer) {
245                 if ($maintainer['user'] == $handle) {
246                     $found = $i;
247                     break 2;
248                 }
249             }
250         }
251         if ($found === false) {
252             return $this->addMaintainer($newrole, $handle, $name, $email, $active);
253         }
254         if ($found !== false) {
255             if ($found === true) {
256                 unset($this->_packageInfo[$role]);
257             } else {
258                 unset($this->_packageInfo[$role][$found]);
259                 $this->_packageInfo[$role] = array_values($this->_packageInfo[$role]);
260             }
261         }
262         $this->addMaintainer($newrole, $handle, $name, $email, $active);
263         $this->_isValid = 0;
264     }
265
266     function deleteMaintainer($handle)
267     {
268         $found = false;
269         foreach (array('lead', 'developer', 'contributor', 'helper') as $role) {
270             if (!isset($this->_packageInfo[$role])) {
271                 continue;
272             }
273             if (!isset($this->_packageInfo[$role][0])) {
274                 $this->_packageInfo[$role] = array($this->_packageInfo[$role]);
275             }
276             foreach ($this->_packageInfo[$role] as $i => $maintainer) {
277                 if ($maintainer['user'] == $handle) {
278                     $found = $i;
279                     break;
280                 }
281             }
282             if ($found !== false) {
283                 unset($this->_packageInfo[$role][$found]);
284                 if (!count($this->_packageInfo[$role]) && $role == 'lead') {
285                     $this->_isValid = 0;
286                 }
287                 if (!count($this->_packageInfo[$role])) {
288                     unset($this->_packageInfo[$role]);
289                     return true;
290                 }
291                 $this->_packageInfo[$role] =
292                     array_values($this->_packageInfo[$role]);
293                 if (count($this->_packageInfo[$role]) == 1) {
294                     $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
295                 }
296                 return true;
297             }
298             if (count($this->_packageInfo[$role]) == 1) {
299                 $this->_packageInfo[$role] = $this->_packageInfo[$role][0];
300             }
301         }
302         return false;
303     }
304
305     function setReleaseVersion($version)
306     {
307         if (isset($this->_packageInfo['version']) &&
308               isset($this->_packageInfo['version']['release'])) {
309             unset($this->_packageInfo['version']['release']);
310         }
311         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
312             'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
313                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
314                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
315                 'extbinrelease', 'bundle', 'changelog'),
316             'release' => array('api')));
317         $this->_isValid = 0;
318     }
319
320     function setAPIVersion($version)
321     {
322         if (isset($this->_packageInfo['version']) &&
323               isset($this->_packageInfo['version']['api'])) {
324             unset($this->_packageInfo['version']['api']);
325         }
326         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $version, array(
327             'version' => array('stability', 'license', 'notes', 'contents', 'compatible',
328                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
329                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
330                 'extbinrelease', 'bundle', 'changelog'),
331             'api' => array()));
332         $this->_isValid = 0;
333     }
334
335     /**
336      * snapshot|devel|alpha|beta|stable
337      */
338     function setReleaseStability($state)
339     {
340         if (isset($this->_packageInfo['stability']) &&
341               isset($this->_packageInfo['stability']['release'])) {
342             unset($this->_packageInfo['stability']['release']);
343         }
344         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
345             'stability' => array('license', 'notes', 'contents', 'compatible',
346                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
347                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
348                 'extbinrelease', 'bundle', 'changelog'),
349             'release' => array('api')));
350         $this->_isValid = 0;
351     }
352
353     /**
354      * @param devel|alpha|beta|stable
355      */
356     function setAPIStability($state)
357     {
358         if (isset($this->_packageInfo['stability']) &&
359               isset($this->_packageInfo['stability']['api'])) {
360             unset($this->_packageInfo['stability']['api']);
361         }
362         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $state, array(
363             'stability' => array('license', 'notes', 'contents', 'compatible',
364                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
365                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
366                 'extbinrelease', 'bundle', 'changelog'),
367             'api' => array()));
368         $this->_isValid = 0;
369     }
370
371     function setLicense($license, $uri = false, $filesource = false)
372     {
373         if (!isset($this->_packageInfo['license'])) {
374             // ensure that the license tag is set up in the right location
375             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
376                 array('notes', 'contents', 'compatible',
377                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
378                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
379                 'extbinrelease', 'bundle', 'changelog'), 0, 'license');
380         }
381         if ($uri || $filesource) {
382             $attribs = array();
383             if ($uri) {
384                 $attribs['uri'] = $uri;
385             }
386             $uri = true; // for test below
387             if ($filesource) {
388                 $attribs['filesource'] = $filesource;
389             }
390         }
391         $license = $uri ? array('attribs' => $attribs, '_content' => $license) : $license;
392         $this->_packageInfo['license'] = $license;
393         $this->_isValid = 0;
394     }
395
396     function setNotes($notes)
397     {
398         $this->_isValid = 0;
399         if (!isset($this->_packageInfo['notes'])) {
400             // ensure that the notes tag is set up in the right location
401             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
402                 array('contents', 'compatible',
403                 'dependencies', 'providesextension', 'usesrole', 'usestask', 'srcpackage', 'srcuri',
404                 'phprelease', 'extsrcrelease', 'zendextsrcrelease', 'zendextbinrelease',
405                 'extbinrelease', 'bundle', 'changelog'), $notes, 'notes');
406         }
407         $this->_packageInfo['notes'] = $notes;
408     }
409
410     /**
411      * This is only used at install-time, after all serialization
412      * is over.
413      * @param string file name
414      * @param string installed path
415      */
416     function setInstalledAs($file, $path)
417     {
418         if ($path) {
419             return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
420         }
421         unset($this->_packageInfo['filelist'][$file]['installed_as']);
422     }
423
424     /**
425      * This is only used at install-time, after all serialization
426      * is over.
427      */
428     function installedFile($file, $atts)
429     {
430         if (isset($this->_packageInfo['filelist'][$file])) {
431             $this->_packageInfo['filelist'][$file] =
432                 array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
433         } else {
434             $this->_packageInfo['filelist'][$file] = $atts['attribs'];
435         }
436     }
437
438     /**
439      * Reset the listing of package contents
440      * @param string base installation dir for the whole package, if any
441      */
442     function clearContents($baseinstall = false)
443     {
444         $this->_filesValid = false;
445         $this->_isValid = 0;
446         if (!isset($this->_packageInfo['contents'])) {
447             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
448                 array('compatible',
449                     'dependencies', 'providesextension', 'usesrole', 'usestask',
450                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
451                     'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
452                     'bundle', 'changelog'), array(), 'contents');
453         }
454         if ($this->getPackageType() != 'bundle') {
455             $this->_packageInfo['contents'] =
456                 array('dir' => array('attribs' => array('name' => '/')));
457             if ($baseinstall) {
458                 $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'] = $baseinstall;
459             }
460         } else {
461             $this->_packageInfo['contents'] = array('bundledpackage' => array());
462         }
463     }
464
465     /**
466      * @param string relative path of the bundled package.
467      */
468     function addBundledPackage($path)
469     {
470         if ($this->getPackageType() != 'bundle') {
471             return false;
472         }
473         $this->_filesValid = false;
474         $this->_isValid = 0;
475         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $path, array(
476                 'contents' => array('compatible', 'dependencies', 'providesextension',
477                 'usesrole', 'usestask', 'srcpackage', 'srcuri', 'phprelease',
478                 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
479                 'bundle', 'changelog'),
480                 'bundledpackage' => array()));
481     }
482
483     /**
484      * @param string file name
485      * @param PEAR_Task_Common a read/write task
486      */
487     function addTaskToFile($filename, $task)
488     {
489         if (!method_exists($task, 'getXml')) {
490             return false;
491         }
492         if (!method_exists($task, 'getName')) {
493             return false;
494         }
495         if (!method_exists($task, 'validate')) {
496             return false;
497         }
498         if (!$task->validate()) {
499             return false;
500         }
501         if (!isset($this->_packageInfo['contents']['dir']['file'])) {
502             return false;
503         }
504         $this->getTasksNs(); // discover the tasks namespace if not done already
505         $files = $this->_packageInfo['contents']['dir']['file'];
506         if (!isset($files[0])) {
507             $files = array($files);
508             $ind = false;
509         } else {
510             $ind = true;
511         }
512         foreach ($files as $i => $file) {
513             if (isset($file['attribs'])) {
514                 if ($file['attribs']['name'] == $filename) {
515                     if ($ind) {
516                         $t = isset($this->_packageInfo['contents']['dir']['file'][$i]
517                               ['attribs'][$this->_tasksNs .
518                               ':' . $task->getName()]) ?
519                               $this->_packageInfo['contents']['dir']['file'][$i]
520                               ['attribs'][$this->_tasksNs .
521                               ':' . $task->getName()] : false;
522                         if ($t && !isset($t[0])) {
523                             $this->_packageInfo['contents']['dir']['file'][$i]
524                                 [$this->_tasksNs . ':' . $task->getName()] = array($t);
525                         }
526                         $this->_packageInfo['contents']['dir']['file'][$i][$this->_tasksNs .
527                             ':' . $task->getName()][] = $task->getXml();
528                     } else {
529                         $t = isset($this->_packageInfo['contents']['dir']['file']
530                               ['attribs'][$this->_tasksNs .
531                               ':' . $task->getName()]) ? $this->_packageInfo['contents']['dir']['file']
532                               ['attribs'][$this->_tasksNs .
533                               ':' . $task->getName()] : false;
534                         if ($t && !isset($t[0])) {
535                             $this->_packageInfo['contents']['dir']['file']
536                                 [$this->_tasksNs . ':' . $task->getName()] = array($t);
537                         }
538                         $this->_packageInfo['contents']['dir']['file'][$this->_tasksNs .
539                             ':' . $task->getName()][] = $task->getXml();
540                     }
541                     return true;
542                 }
543             }
544         }
545         return false;
546     }
547
548     /**
549      * @param string path to the file
550      * @param string filename
551      * @param array extra attributes
552      */
553     function addFile($dir, $file, $attrs)
554     {
555         if ($this->getPackageType() == 'bundle') {
556             return false;
557         }
558         $this->_filesValid = false;
559         $this->_isValid = 0;
560         $dir = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'), $dir);
561         if ($dir == '/' || $dir == '') {
562             $dir = '';
563         } else {
564             $dir .= '/';
565         }
566         $attrs['name'] = $dir . $file;
567         if (!isset($this->_packageInfo['contents'])) {
568             // ensure that the contents tag is set up
569             $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
570                 array('compatible', 'dependencies', 'providesextension', 'usesrole', 'usestask',
571                 'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease',
572                 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
573                 'bundle', 'changelog'), array(), 'contents');
574         }
575         if (isset($this->_packageInfo['contents']['dir']['file'])) {
576             if (!isset($this->_packageInfo['contents']['dir']['file'][0])) {
577                 $this->_packageInfo['contents']['dir']['file'] =
578                     array($this->_packageInfo['contents']['dir']['file']);
579             }
580             $this->_packageInfo['contents']['dir']['file'][]['attribs'] = $attrs;
581         } else {
582             $this->_packageInfo['contents']['dir']['file']['attribs'] = $attrs;
583         }
584     }
585
586     /**
587      * @param string Dependent package name
588      * @param string Dependent package's channel name
589      * @param string minimum version of specified package that this release is guaranteed to be
590      *               compatible with
591      * @param string maximum version of specified package that this release is guaranteed to be
592      *               compatible with
593      * @param string versions of specified package that this release is not compatible with
594      */
595     function addCompatiblePackage($name, $channel, $min, $max, $exclude = false)
596     {
597         $this->_isValid = 0;
598         $set = array(
599             'name' => $name,
600             'channel' => $channel,
601             'min' => $min,
602             'max' => $max,
603         );
604         if ($exclude) {
605             $set['exclude'] = $exclude;
606         }
607         $this->_isValid = 0;
608         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
609                 'compatible' => array('dependencies', 'providesextension', 'usesrole', 'usestask',
610                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
611                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
612             ));
613     }
614
615     /**
616      * Removes the <usesrole> tag entirely
617      */
618     function resetUsesrole()
619     {
620         if (isset($this->_packageInfo['usesrole'])) {
621             unset($this->_packageInfo['usesrole']);
622         }
623     }
624
625     /**
626      * @param string
627      * @param string package name or uri
628      * @param string channel name if non-uri
629      */
630     function addUsesrole($role, $packageOrUri, $channel = false) {
631         $set = array('role' => $role);
632         if ($channel) {
633             $set['package'] = $packageOrUri;
634             $set['channel'] = $channel;
635         } else {
636             $set['uri'] = $packageOrUri;
637         }
638         $this->_isValid = 0;
639         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
640                 'usesrole' => array('usestask', 'srcpackage', 'srcuri',
641                     'phprelease', 'extsrcrelease', 'extbinrelease',
642                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
643             ));
644     }
645
646     /**
647      * Removes the <usestask> tag entirely
648      */
649     function resetUsestask()
650     {
651         if (isset($this->_packageInfo['usestask'])) {
652             unset($this->_packageInfo['usestask']);
653         }
654     }
655
656
657     /**
658      * @param string
659      * @param string package name or uri
660      * @param string channel name if non-uri
661      */
662     function addUsestask($task, $packageOrUri, $channel = false) {
663         $set = array('task' => $task);
664         if ($channel) {
665             $set['package'] = $packageOrUri;
666             $set['channel'] = $channel;
667         } else {
668             $set['uri'] = $packageOrUri;
669         }
670         $this->_isValid = 0;
671         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $set, array(
672                 'usestask' => array('srcpackage', 'srcuri',
673                     'phprelease', 'extsrcrelease', 'extbinrelease',
674                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')
675             ));
676     }
677
678     /**
679      * Remove all compatible tags
680      */
681     function clearCompatible()
682     {
683         unset($this->_packageInfo['compatible']);
684     }
685
686     /**
687      * Reset dependencies prior to adding new ones
688      */
689     function clearDeps()
690     {
691         if (!isset($this->_packageInfo['dependencies'])) {
692             $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
693                 array(
694                     'dependencies' => array('providesextension', 'usesrole', 'usestask',
695                         'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
696                         'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog')));
697         }
698         $this->_packageInfo['dependencies'] = array();
699     }
700
701     /**
702      * @param string minimum PHP version allowed
703      * @param string maximum PHP version allowed
704      * @param array $exclude incompatible PHP versions
705      */
706     function setPhpDep($min, $max = false, $exclude = false)
707     {
708         $this->_isValid = 0;
709         $dep =
710             array(
711                 'min' => $min,
712             );
713         if ($max) {
714             $dep['max'] = $max;
715         }
716         if ($exclude) {
717             if (count($exclude) == 1) {
718                 $exclude = $exclude[0];
719             }
720             $dep['exclude'] = $exclude;
721         }
722         if (isset($this->_packageInfo['dependencies']['required']['php'])) {
723             $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
724             $this->_packageInfo['dependencies']['required']['php']),
725                 'warning: PHP dependency already exists, overwriting');
726             unset($this->_packageInfo['dependencies']['required']['php']);
727         }
728         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
729             array(
730                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
731                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
732                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
733                 'required' => array('optional', 'group'),
734                 'php' => array('pearinstaller', 'package', 'subpackage', 'extension', 'os', 'arch')
735             ));
736         return true;
737     }
738
739     /**
740      * @param string minimum allowed PEAR installer version
741      * @param string maximum allowed PEAR installer version
742      * @param string recommended PEAR installer version
743      * @param array incompatible version of the PEAR installer
744      */
745     function setPearinstallerDep($min, $max = false, $recommended = false, $exclude = false)
746     {
747         $this->_isValid = 0;
748         $dep =
749             array(
750                 'min' => $min,
751             );
752         if ($max) {
753             $dep['max'] = $max;
754         }
755         if ($recommended) {
756             $dep['recommended'] = $recommended;
757         }
758         if ($exclude) {
759             if (count($exclude) == 1) {
760                 $exclude = $exclude[0];
761             }
762             $dep['exclude'] = $exclude;
763         }
764         if (isset($this->_packageInfo['dependencies']['required']['pearinstaller'])) {
765             $this->_stack->push(__FUNCTION__, 'warning', array('dep' =>
766             $this->_packageInfo['dependencies']['required']['pearinstaller']),
767                 'warning: PEAR Installer dependency already exists, overwriting');
768             unset($this->_packageInfo['dependencies']['required']['pearinstaller']);
769         }
770         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
771             array(
772                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
773                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
774                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
775                 'required' => array('optional', 'group'),
776                 'pearinstaller' => array('package', 'subpackage', 'extension', 'os', 'arch')
777             ));
778     }
779
780     /**
781      * Mark a package as conflicting with this package
782      * @param string package name
783      * @param string package channel
784      * @param string extension this package provides, if any
785      * @param string|false minimum version required
786      * @param string|false maximum version allowed
787      * @param array|false versions to exclude from installation
788      */
789     function addConflictingPackageDepWithChannel($name, $channel,
790                 $providesextension = false, $min = false, $max = false, $exclude = false)
791     {
792         $this->_isValid = 0;
793         $dep = $this->_constructDep($name, $channel, false, $min, $max, false,
794             $exclude, $providesextension, false, true);
795         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
796             array(
797                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
798                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
799                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
800                 'required' => array('optional', 'group'),
801                 'package' => array('subpackage', 'extension', 'os', 'arch')
802             ));
803     }
804
805     /**
806      * Mark a package as conflicting with this package
807      * @param string package name
808      * @param string package channel
809      * @param string extension this package provides, if any
810      */
811     function addConflictingPackageDepWithUri($name, $uri, $providesextension = false)
812     {
813         $this->_isValid = 0;
814         $dep =
815             array(
816                 'name' => $name,
817                 'uri' => $uri,
818                 'conflicts' => '',
819             );
820         if ($providesextension) {
821             $dep['providesextension'] = $providesextension;
822         }
823         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
824             array(
825                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
826                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
827                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
828                 'required' => array('optional', 'group'),
829                 'package' => array('subpackage', 'extension', 'os', 'arch')
830             ));
831     }
832
833     function addDependencyGroup($name, $hint)
834     {
835         $this->_isValid = 0;
836         $this->_packageInfo = $this->_mergeTag($this->_packageInfo,
837             array('attribs' => array('name' => $name, 'hint' => $hint)),
838             array(
839                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
840                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
841                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
842                 'group' => array(),
843             ));
844     }
845
846     /**
847      * @param string package name
848      * @param string|false channel name, false if this is a uri
849      * @param string|false uri name, false if this is a channel
850      * @param string|false minimum version required
851      * @param string|false maximum version allowed
852      * @param string|false recommended installation version
853      * @param array|false versions to exclude from installation
854      * @param string extension this package provides, if any
855      * @param bool if true, tells the installer to ignore the default optional dependency group
856      *             when installing this package
857      * @param bool if true, tells the installer to negate this dependency (conflicts)
858      * @return array
859      * @access private
860      */
861     function _constructDep($name, $channel, $uri, $min, $max, $recommended, $exclude,
862                            $providesextension = false, $nodefault = false,
863                            $conflicts = false)
864     {
865         $dep =
866             array(
867                 'name' => $name,
868             );
869         if ($channel) {
870             $dep['channel'] = $channel;
871         } elseif ($uri) {
872             $dep['uri'] = $uri;
873         }
874         if ($min) {
875             $dep['min'] = $min;
876         }
877         if ($max) {
878             $dep['max'] = $max;
879         }
880         if ($recommended) {
881             $dep['recommended'] = $recommended;
882         }
883         if ($exclude) {
884             if (is_array($exclude) && count($exclude) == 1) {
885                 $exclude = $exclude[0];
886             }
887             $dep['exclude'] = $exclude;
888         }
889         if ($conflicts) {
890             $dep['conflicts'] = '';
891         }
892         if ($nodefault) {
893             $dep['nodefault'] = '';
894         }
895         if ($providesextension) {
896             $dep['providesextension'] = $providesextension;
897         }
898         return $dep;
899     }
900
901     /**
902      * @param package|subpackage
903      * @param string group name
904      * @param string package name
905      * @param string package channel
906      * @param string minimum version
907      * @param string maximum version
908      * @param string recommended version
909      * @param array|false optional excluded versions
910      * @param string extension this package provides, if any
911      * @param bool if true, tells the installer to ignore the default optional dependency group
912      *             when installing this package
913      * @return bool false if the dependency group has not been initialized with
914      *              {@link addDependencyGroup()}, or a subpackage is added with
915      *              a providesextension
916      */
917     function addGroupPackageDepWithChannel($type, $groupname, $name, $channel, $min = false,
918                                       $max = false, $recommended = false, $exclude = false,
919                                       $providesextension = false, $nodefault = false)
920     {
921         if ($type == 'subpackage' && $providesextension) {
922             return false; // subpackages must be php packages
923         }
924         $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
925             $providesextension, $nodefault);
926         return $this->_addGroupDependency($type, $dep, $groupname);
927     }
928
929     /**
930      * @param package|subpackage
931      * @param string group name
932      * @param string package name
933      * @param string package uri
934      * @param string extension this package provides, if any
935      * @param bool if true, tells the installer to ignore the default optional dependency group
936      *             when installing this package
937      * @return bool false if the dependency group has not been initialized with
938      *              {@link addDependencyGroup()}
939      */
940     function addGroupPackageDepWithURI($type, $groupname, $name, $uri, $providesextension = false,
941                                        $nodefault = false)
942     {
943         if ($type == 'subpackage' && $providesextension) {
944             return false; // subpackages must be php packages
945         }
946         $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
947             $providesextension, $nodefault);
948         return $this->_addGroupDependency($type, $dep, $groupname);
949     }
950
951     /**
952      * @param string group name (must be pre-existing)
953      * @param string extension name
954      * @param string minimum version allowed
955      * @param string maximum version allowed
956      * @param string recommended version
957      * @param array incompatible versions
958      */
959     function addGroupExtensionDep($groupname, $name, $min = false, $max = false,
960                                          $recommended = false, $exclude = false)
961     {
962         $this->_isValid = 0;
963         $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
964         return $this->_addGroupDependency('extension', $dep, $groupname);
965     }
966
967     /**
968      * @param package|subpackage|extension
969      * @param array dependency contents
970      * @param string name of the dependency group to add this to
971      * @return boolean
972      * @access private
973      */
974     function _addGroupDependency($type, $dep, $groupname)
975     {
976         $arr = array('subpackage', 'extension');
977         if ($type != 'package') {
978             array_shift($arr);
979         }
980         if ($type == 'extension') {
981             array_shift($arr);
982         }
983         if (!isset($this->_packageInfo['dependencies']['group'])) {
984             return false;
985         } else {
986             if (!isset($this->_packageInfo['dependencies']['group'][0])) {
987                 if ($this->_packageInfo['dependencies']['group']['attribs']['name'] == $groupname) {
988                     $this->_packageInfo['dependencies']['group'] = $this->_mergeTag(
989                         $this->_packageInfo['dependencies']['group'], $dep,
990                         array(
991                             $type => $arr
992                         ));
993                     $this->_isValid = 0;
994                     return true;
995                 } else {
996                     return false;
997                 }
998             } else {
999                 foreach ($this->_packageInfo['dependencies']['group'] as $i => $group) {
1000                     if ($group['attribs']['name'] == $groupname) {
1001                     $this->_packageInfo['dependencies']['group'][$i] = $this->_mergeTag(
1002                         $this->_packageInfo['dependencies']['group'][$i], $dep,
1003                         array(
1004                             $type => $arr
1005                         ));
1006                         $this->_isValid = 0;
1007                         return true;
1008                     }
1009                 }
1010                 return false;
1011             }
1012         }
1013     }
1014
1015     /**
1016      * @param optional|required
1017      * @param string package name
1018      * @param string package channel
1019      * @param string minimum version
1020      * @param string maximum version
1021      * @param string recommended version
1022      * @param string extension this package provides, if any
1023      * @param bool if true, tells the installer to ignore the default optional dependency group
1024      *             when installing this package
1025      * @param array|false optional excluded versions
1026      */
1027     function addPackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
1028                                       $recommended = false, $exclude = false,
1029                                       $providesextension = false, $nodefault = false)
1030     {
1031         if (!in_array($type, array('optional', 'required'), true)) {
1032             $type = 'required';
1033         }
1034         $this->_isValid = 0;
1035         $arr = array('optional', 'group');
1036         if ($type != 'required') {
1037             array_shift($arr);
1038         }
1039         $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
1040             $providesextension, $nodefault);
1041         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
1042             array(
1043                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
1044                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
1045                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
1046                 $type => $arr,
1047                 'package' => array('subpackage', 'extension', 'os', 'arch')
1048             ));
1049     }
1050
1051     /**
1052      * @param optional|required
1053      * @param string name of the package
1054      * @param string uri of the package
1055      * @param string extension this package provides, if any
1056      * @param bool if true, tells the installer to ignore the default optional dependency group
1057      *             when installing this package
1058      */
1059     function addPackageDepWithUri($type, $name, $uri, $providesextension = false,
1060                                   $nodefault = false)
1061     {
1062         $this->_isValid = 0;
1063         $arr = array('optional', 'group');
1064         if ($type != 'required') {
1065             array_shift($arr);
1066         }
1067         $dep = $this->_constructDep($name, false, $uri, false, false, false, false,
1068             $providesextension, $nodefault);
1069         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
1070             array(
1071                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
1072                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
1073                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
1074                 $type => $arr,
1075                 'package' => array('subpackage', 'extension', 'os', 'arch')
1076             ));
1077     }
1078
1079     /**
1080      * @param optional|required optional, required
1081      * @param string package name
1082      * @param string package channel
1083      * @param string minimum version
1084      * @param string maximum version
1085      * @param string recommended version
1086      * @param array incompatible versions
1087      * @param bool if true, tells the installer to ignore the default optional dependency group
1088      *             when installing this package
1089      */
1090     function addSubpackageDepWithChannel($type, $name, $channel, $min = false, $max = false,
1091                                          $recommended = false, $exclude = false,
1092                                          $nodefault = false)
1093     {
1094         $this->_isValid = 0;
1095         $arr = array('optional', 'group');
1096         if ($type != 'required') {
1097             array_shift($arr);
1098         }
1099         $dep = $this->_constructDep($name, $channel, false, $min, $max, $recommended, $exclude,
1100             $nodefault);
1101         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
1102             array(
1103                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
1104                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
1105                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
1106                 $type => $arr,
1107                 'subpackage' => array('extension', 'os', 'arch')
1108             ));
1109     }
1110
1111     /**
1112      * @param optional|required optional, required
1113      * @param string package name
1114      * @param string package uri for download
1115      * @param bool if true, tells the installer to ignore the default optional dependency group
1116      *             when installing this package
1117      */
1118     function addSubpackageDepWithUri($type, $name, $uri, $nodefault = false)
1119     {
1120         $this->_isValid = 0;
1121         $arr = array('optional', 'group');
1122         if ($type != 'required') {
1123             array_shift($arr);
1124         }
1125         $dep = $this->_constructDep($name, false, $uri, false, false, false, false, $nodefault);
1126         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
1127             array(
1128                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
1129                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
1130                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
1131                 $type => $arr,
1132                 'subpackage' => array('extension', 'os', 'arch')
1133             ));
1134     }
1135
1136     /**
1137      * @param optional|required optional, required
1138      * @param string extension name
1139      * @param string minimum version
1140      * @param string maximum version
1141      * @param string recommended version
1142      * @param array incompatible versions
1143      */
1144     function addExtensionDep($type, $name, $min = false, $max = false, $recommended = false,
1145                              $exclude = false)
1146     {
1147         $this->_isValid = 0;
1148         $arr = array('optional', 'group');
1149         if ($type != 'required') {
1150             array_shift($arr);
1151         }
1152         $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
1153         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
1154             array(
1155                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
1156                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
1157                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
1158                 $type => $arr,
1159                 'extension' => array('os', 'arch')
1160             ));
1161     }
1162
1163     /**
1164      * @param string Operating system name
1165      * @param boolean true if this package cannot be installed on this OS
1166      */
1167     function addOsDep($name, $conflicts = false)
1168     {
1169         $this->_isValid = 0;
1170         $dep = array('name' => $name);
1171         if ($conflicts) {
1172             $dep['conflicts'] = '';
1173         }
1174         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
1175             array(
1176                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
1177                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
1178                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
1179                 'required' => array('optional', 'group'),
1180                 'os' => array('arch')
1181             ));
1182     }
1183
1184     /**
1185      * @param string Architecture matching pattern
1186      * @param boolean true if this package cannot be installed on this architecture
1187      */
1188     function addArchDep($pattern, $conflicts = false)
1189     {
1190         $this->_isValid = 0;
1191         $dep = array('pattern' => $pattern);
1192         if ($conflicts) {
1193             $dep['conflicts'] = '';
1194         }
1195         $this->_packageInfo = $this->_mergeTag($this->_packageInfo, $dep,
1196             array(
1197                 'dependencies' => array('providesextension', 'usesrole', 'usestask',
1198                     'srcpackage', 'srcuri', 'phprelease', 'extsrcrelease', 'extbinrelease',
1199                     'zendextsrcrelease', 'zendextbinrelease', 'bundle', 'changelog'),
1200                 'required' => array('optional', 'group'),
1201                 'arch' => array()
1202             ));
1203     }
1204
1205     /**
1206      * Set the kind of package, and erase all release tags
1207      *
1208      * - a php package is a PEAR-style package
1209      * - an extbin package is a PECL-style extension binary
1210      * - an extsrc package is a PECL-style source for a binary
1211      * - an zendextbin package is a PECL-style zend extension binary
1212      * - an zendextsrc package is a PECL-style source for a zend extension binary
1213      * - a bundle package is a collection of other pre-packaged packages
1214      * @param php|extbin|extsrc|zendextsrc|zendextbin|bundle
1215      * @return bool success
1216      */
1217     function setPackageType($type)
1218     {
1219         $this->_isValid = 0;
1220         if (!in_array($type, array('php', 'extbin', 'extsrc', 'zendextsrc',
1221                                    'zendextbin', 'bundle'))) {
1222             return false;
1223         }
1224
1225         if (in_array($type, array('zendextsrc', 'zendextbin'))) {
1226             $this->_setPackageVersion2_1();
1227         }
1228
1229         if ($type != 'bundle') {
1230             $type .= 'release';
1231         }
1232
1233         foreach (array('phprelease', 'extbinrelease', 'extsrcrelease',
1234                        'zendextsrcrelease', 'zendextbinrelease', 'bundle') as $test) {
1235             unset($this->_packageInfo[$test]);
1236         }
1237
1238         if (!isset($this->_packageInfo[$type])) {
1239             // ensure that the release tag is set up
1240             $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('changelog'),
1241                 array(), $type);
1242         }
1243
1244         $this->_packageInfo[$type] = array();
1245         return true;
1246     }
1247
1248     /**
1249      * @return bool true if package type is set up
1250      */
1251     function addRelease()
1252     {
1253         if ($type = $this->getPackageType()) {
1254             if ($type != 'bundle') {
1255                 $type .= 'release';
1256             }
1257             $this->_packageInfo = $this->_mergeTag($this->_packageInfo, array(),
1258                 array($type => array('changelog')));
1259             return true;
1260         }
1261         return false;
1262     }
1263
1264     /**
1265      * Get the current release tag in order to add to it
1266      * @param bool returns only releases that have installcondition if true
1267      * @return array|null
1268      */
1269     function &_getCurrentRelease($strict = true)
1270     {
1271         if ($p = $this->getPackageType()) {
1272             if ($strict) {
1273                 if ($p == 'extsrc' || $p == 'zendextsrc') {
1274                     $a = null;
1275                     return $a;
1276                 }
1277             }
1278             if ($p != 'bundle') {
1279                 $p .= 'release';
1280             }
1281             if (isset($this->_packageInfo[$p][0])) {
1282                 return $this->_packageInfo[$p][count($this->_packageInfo[$p]) - 1];
1283             } else {
1284                 return $this->_packageInfo[$p];
1285             }
1286         } else {
1287             $a = null;
1288             return $a;
1289         }
1290     }
1291
1292     /**
1293      * Add a file to the current release that should be installed under a different name
1294      * @param string <contents> path to file
1295      * @param string name the file should be installed as
1296      */
1297     function addInstallAs($path, $as)
1298     {
1299         $r = &$this->_getCurrentRelease();
1300         if ($r === null) {
1301             return false;
1302         }
1303         $this->_isValid = 0;
1304         $r = $this->_mergeTag($r, array('attribs' => array('name' => $path, 'as' => $as)),
1305             array(
1306                 'filelist' => array(),
1307                 'install' => array('ignore')
1308             ));
1309     }
1310
1311     /**
1312      * Add a file to the current release that should be ignored
1313      * @param string <contents> path to file
1314      * @return bool success of operation
1315      */
1316     function addIgnore($path)
1317     {
1318         $r = &$this->_getCurrentRelease();
1319         if ($r === null) {
1320             return false;
1321         }
1322         $this->_isValid = 0;
1323         $r = $this->_mergeTag($r, array('attribs' => array('name' => $path)),
1324             array(
1325                 'filelist' => array(),
1326                 'ignore' => array()
1327             ));
1328     }
1329
1330     /**
1331      * Add an extension binary package for this extension source code release
1332      *
1333      * Note that the package must be from the same channel as the extension source package
1334      * @param string
1335      */
1336     function addBinarypackage($package)
1337     {
1338         if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
1339             return false;
1340         }
1341         $r = &$this->_getCurrentRelease(false);
1342         if ($r === null) {
1343             return false;
1344         }
1345         $this->_isValid = 0;
1346         $r = $this->_mergeTag($r, $package,
1347             array(
1348                 'binarypackage' => array('filelist'),
1349             ));
1350     }
1351
1352     /**
1353      * Add a configureoption to an extension source package
1354      * @param string
1355      * @param string
1356      * @param string
1357      */
1358     function addConfigureOption($name, $prompt, $default = null)
1359     {
1360         if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
1361             return false;
1362         }
1363
1364         $r = &$this->_getCurrentRelease(false);
1365         if ($r === null) {
1366             return false;
1367         }
1368
1369         $opt = array('attribs' => array('name' => $name, 'prompt' => $prompt));
1370         if ($default !== null) {
1371             $opt['attribs']['default'] = $default;
1372         }
1373
1374         $this->_isValid = 0;
1375         $r = $this->_mergeTag($r, $opt,
1376             array(
1377                 'configureoption' => array('binarypackage', 'filelist'),
1378             ));
1379     }
1380
1381     /**
1382      * Set an installation condition based on php version for the current release set
1383      * @param string minimum version
1384      * @param string maximum version
1385      * @param false|array incompatible versions of PHP
1386      */
1387     function setPhpInstallCondition($min, $max, $exclude = false)
1388     {
1389         $r = &$this->_getCurrentRelease();
1390         if ($r === null) {
1391             return false;
1392         }
1393         $this->_isValid = 0;
1394         if (isset($r['installconditions']['php'])) {
1395             unset($r['installconditions']['php']);
1396         }
1397         $dep = array('min' => $min, 'max' => $max);
1398         if ($exclude) {
1399             if (is_array($exclude) && count($exclude) == 1) {
1400                 $exclude = $exclude[0];
1401             }
1402             $dep['exclude'] = $exclude;
1403         }
1404         if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
1405             $r = $this->_mergeTag($r, $dep,
1406                 array(
1407                     'installconditions' => array('configureoption', 'binarypackage',
1408                         'filelist'),
1409                     'php' => array('extension', 'os', 'arch')
1410                 ));
1411         } else {
1412             $r = $this->_mergeTag($r, $dep,
1413                 array(
1414                     'installconditions' => array('filelist'),
1415                     'php' => array('extension', 'os', 'arch')
1416                 ));
1417         }
1418     }
1419
1420     /**
1421      * @param optional|required optional, required
1422      * @param string extension name
1423      * @param string minimum version
1424      * @param string maximum version
1425      * @param string recommended version
1426      * @param array incompatible versions
1427      */
1428     function addExtensionInstallCondition($name, $min = false, $max = false, $recommended = false,
1429                                           $exclude = false)
1430     {
1431         $r = &$this->_getCurrentRelease();
1432         if ($r === null) {
1433             return false;
1434         }
1435         $this->_isValid = 0;
1436         $dep = $this->_constructDep($name, false, false, $min, $max, $recommended, $exclude);
1437         if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
1438             $r = $this->_mergeTag($r, $dep,
1439                 array(
1440                     'installconditions' => array('configureoption', 'binarypackage',
1441                         'filelist'),
1442                     'extension' => array('os', 'arch')
1443                 ));
1444         } else {
1445             $r = $this->_mergeTag($r, $dep,
1446                 array(
1447                     'installconditions' => array('filelist'),
1448                     'extension' => array('os', 'arch')
1449                 ));
1450         }
1451     }
1452
1453     /**
1454      * Set an installation condition based on operating system for the current release set
1455      * @param string OS name
1456      * @param bool whether this OS is incompatible with the current release
1457      */
1458     function setOsInstallCondition($name, $conflicts = false)
1459     {
1460         $r = &$this->_getCurrentRelease();
1461         if ($r === null) {
1462             return false;
1463         }
1464         $this->_isValid = 0;
1465         if (isset($r['installconditions']['os'])) {
1466             unset($r['installconditions']['os']);
1467         }
1468         $dep = array('name' => $name);
1469         if ($conflicts) {
1470             $dep['conflicts'] = '';
1471         }
1472         if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
1473             $r = $this->_mergeTag($r, $dep,
1474                 array(
1475                     'installconditions' => array('configureoption', 'binarypackage',
1476                         'filelist'),
1477                     'os' => array('arch')
1478                 ));
1479         } else {
1480             $r = $this->_mergeTag($r, $dep,
1481                 array(
1482                     'installconditions' => array('filelist'),
1483                     'os' => array('arch')
1484                 ));
1485         }
1486     }
1487
1488     /**
1489      * Set an installation condition based on architecture for the current release set
1490      * @param string architecture pattern
1491      * @param bool whether this arch is incompatible with the current release
1492      */
1493     function setArchInstallCondition($pattern, $conflicts = false)
1494     {
1495         $r = &$this->_getCurrentRelease();
1496         if ($r === null) {
1497             return false;
1498         }
1499         $this->_isValid = 0;
1500         if (isset($r['installconditions']['arch'])) {
1501             unset($r['installconditions']['arch']);
1502         }
1503         $dep = array('pattern' => $pattern);
1504         if ($conflicts) {
1505             $dep['conflicts'] = '';
1506         }
1507         if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
1508             $r = $this->_mergeTag($r, $dep,
1509                 array(
1510                     'installconditions' => array('configureoption', 'binarypackage',
1511                         'filelist'),
1512                     'arch' => array()
1513                 ));
1514         } else {
1515             $r = $this->_mergeTag($r, $dep,
1516                 array(
1517                     'installconditions' => array('filelist'),
1518                     'arch' => array()
1519                 ));
1520         }
1521     }
1522
1523     /**
1524      * For extension binary releases, this is used to specify either the
1525      * static URI to a source package, or the package name and channel of the extsrc/zendextsrc
1526      * package it is based on.
1527      * @param string Package name, or full URI to source package (extsrc/zendextsrc type)
1528      */
1529     function setSourcePackage($packageOrUri)
1530     {
1531         $this->_isValid = 0;
1532         if (isset($this->_packageInfo['channel'])) {
1533             $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
1534                 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
1535                 'bundle', 'changelog'),
1536                 $packageOrUri, 'srcpackage');
1537         } else {
1538             $this->_packageInfo = $this->_insertBefore($this->_packageInfo, array('phprelease',
1539                 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease', 'zendextbinrelease',
1540                 'bundle', 'changelog'), $packageOrUri, 'srcuri');
1541         }
1542     }
1543
1544     /**
1545      * Generate a valid change log entry from the current package.xml
1546      * @param string|false
1547      */
1548     function generateChangeLogEntry($notes = false)
1549     {
1550         return array(
1551             'version' =>
1552                 array(
1553                     'release' => $this->getVersion('release'),
1554                     'api' => $this->getVersion('api'),
1555                     ),
1556             'stability' =>
1557                 $this->getStability(),
1558             'date' => $this->getDate(),
1559             'license' => $this->getLicense(true),
1560             'notes' => $notes ? $notes : $this->getNotes()
1561             );
1562     }
1563
1564     /**
1565      * @param string release version to set change log notes for
1566      * @param array output of {@link generateChangeLogEntry()}
1567      */
1568     function setChangelogEntry($releaseversion, $contents)
1569     {
1570         if (!isset($this->_packageInfo['changelog'])) {
1571             $this->_packageInfo['changelog']['release'] = $contents;
1572             return;
1573         }
1574         if (!isset($this->_packageInfo['changelog']['release'][0])) {
1575             if ($this->_packageInfo['changelog']['release']['version']['release'] == $releaseversion) {
1576                 $this->_packageInfo['changelog']['release'] = array(
1577                     $this->_packageInfo['changelog']['release']);
1578             } else {
1579                 $this->_packageInfo['changelog']['release'] = array(
1580                     $this->_packageInfo['changelog']['release']);
1581                 return $this->_packageInfo['changelog']['release'][] = $contents;
1582             }
1583         }
1584         foreach($this->_packageInfo['changelog']['release'] as $index => $changelog) {
1585             if (isset($changelog['version']) &&
1586                   strnatcasecmp($changelog['version']['release'], $releaseversion) == 0) {
1587                 $curlog = $index;
1588             }
1589         }
1590         if (isset($curlog)) {
1591             $this->_packageInfo['changelog']['release'][$curlog] = $contents;
1592         } else {
1593             $this->_packageInfo['changelog']['release'][] = $contents;
1594         }
1595     }
1596
1597     /**
1598      * Remove the changelog entirely
1599      */
1600     function clearChangeLog()
1601     {
1602         unset($this->_packageInfo['changelog']);
1603     }
1604 }