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