3 * PEAR_PackageFile_v2, package.xml version 2.0
9 * @author Greg Beaver <cellog@php.net>
10 * @copyright 1997-2009 The Authors
11 * @license http://opensource.org/licenses/bsd-license.php New BSD License
12 * @version CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
13 * @link http://pear.php.net/package/PEAR
14 * @since File available since Release 1.4.0a1
19 require_once 'PEAR/ErrorStack.php';
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.0a1
30 class PEAR_PackageFile_v2
34 * Parsed package information
38 var $_packageInfo = array();
41 * path to package .tgz or false if this is a local/extracted package.xml
48 * path to package .xml or false if this is an abstract parsed-from-string xml
55 * This is used by file analysis routines to log progress information
62 * This is set to the highest validation level that has been validated
64 * If the package.xml is invalid or unknown, this is set to 0. If
65 * normal validation has occurred, this is set to PEAR_VALIDATE_NORMAL. If
66 * downloading/installation validation has occurred it is set to PEAR_VALIDATE_DOWNLOADING
67 * or INSTALLING, and so on up to PEAR_VALIDATE_PACKAGING. This allows validation
68 * "caching" to occur, which is particularly important for package validation, so
69 * that PHP files are not validated twice
76 * True if the filelist has been validated
79 var $_filesValid = false;
94 * Optional Dependency group requested for installation
98 var $_requestedGroup = false;
101 * @var PEAR_ErrorStack
107 * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
112 * Determines whether this packagefile was initialized only with partial package info
114 * If this package file was constructed via parsing REST, it will only contain
122 var $_incomplete = true;
125 * @var PEAR_PackageFile_v2_Validator
130 * The constructor merely sets up the private error stack
132 function PEAR_PackageFile_v2()
134 $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
135 $this->_isValid = false;
139 * To make unit-testing easier
140 * @param PEAR_Frontend_*
141 * @param array options
143 * @return PEAR_Downloader
146 function &getPEARDownloader(&$i, $o, &$c)
148 $z = &new PEAR_Downloader($i, $o, $c);
153 * To make unit-testing easier
155 * @param array options
156 * @param array package name as returned from {@link PEAR_Registry::parsePackageName()}
157 * @param int PEAR_VALIDATE_* constant
158 * @return PEAR_Dependency2
161 function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
163 if (!class_exists('PEAR_Dependency2')) {
164 require_once 'PEAR/Dependency2.php';
166 $z = &new PEAR_Dependency2($c, $o, $p, $s);
170 function getInstalledBinary()
172 return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
177 * Installation of source package has failed, attempt to download and install the
178 * binary version of this package.
179 * @param PEAR_Installer
180 * @return array|false
182 function installBinary(&$installer)
188 if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
189 $releasetype = $this->getPackageType() . 'release';
190 if (!is_array($installer->getInstallPackages())) {
194 foreach ($installer->getInstallPackages() as $p) {
195 if ($p->isExtension($this->_packageInfo['providesextension'])) {
196 if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
198 return $a; // the user probably downloaded it separately
202 if (isset($this->_packageInfo[$releasetype]['binarypackage'])) {
203 $installer->log(0, 'Attempting to download binary version of extension "' .
204 $this->_packageInfo['providesextension'] . '"');
205 $params = $this->_packageInfo[$releasetype]['binarypackage'];
206 if (!is_array($params) || !isset($params[0])) {
207 $params = array($params);
209 if (isset($this->_packageInfo['channel'])) {
210 foreach ($params as $i => $param) {
211 $params[$i] = array('channel' => $this->_packageInfo['channel'],
212 'package' => $param, 'version' => $this->getVersion());
215 $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
217 $verbose = $dl->config->get('verbose');
218 $dl->config->set('verbose', -1);
219 foreach ($params as $param) {
220 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
221 $ret = $dl->download(array($param));
222 PEAR::popErrorHandling();
223 if (is_array($ret) && count($ret)) {
227 $dl->config->set('verbose', $verbose);
228 if (is_array($ret)) {
229 if (count($ret) == 1) {
230 $pf = $ret[0]->getPackageFile();
231 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
232 $err = $installer->install($ret[0]);
233 PEAR::popErrorHandling();
234 if (is_array($err)) {
235 $this->_packageInfo['#binarypackage'] = $ret[0]->getPackage();
236 // "install" self, so all dependencies will work transparently
237 $this->_registry->addPackage2($this);
238 $installer->log(0, 'Download and install of binary extension "' .
239 $this->_registry->parsedPackageNameToString(
240 array('channel' => $pf->getChannel(),
241 'package' => $pf->getPackage()), true) . '" successful');
242 $a = array($ret[0], $err);
245 $installer->log(0, 'Download and install of binary extension "' .
246 $this->_registry->parsedPackageNameToString(
247 array('channel' => $pf->getChannel(),
248 'package' => $pf->getPackage()), true) . '" failed');
258 * @return string|false Extension name
260 function getProvidesExtension()
262 if (in_array($this->getPackageType(),
263 array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
264 if (isset($this->_packageInfo['providesextension'])) {
265 return $this->_packageInfo['providesextension'];
272 * @param string Extension name
275 function isExtension($extension)
277 if (in_array($this->getPackageType(),
278 array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
279 return $this->_packageInfo['providesextension'] == $extension;
285 * Tests whether every part of the package.xml 1.0 is represented in
286 * this package.xml 2.0
287 * @param PEAR_PackageFile_v1
290 function isEquivalent($pf1)
295 if ($this->getPackageType() == 'bundle') {
298 $this->_stack->getErrors(true);
299 if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
303 if ($pf1->getPackage() != $this->getPackage()) {
304 $this->_differentPackage($pf1->getPackage());
307 if ($pf1->getVersion() != $this->getVersion()) {
308 $this->_differentVersion($pf1->getVersion());
311 if (trim($pf1->getSummary()) != $this->getSummary()) {
312 $this->_differentSummary($pf1->getSummary());
315 if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
316 preg_replace('/\s+/', '', $this->getDescription())) {
317 $this->_differentDescription($pf1->getDescription());
320 if ($pf1->getState() != $this->getState()) {
321 $this->_differentState($pf1->getState());
324 if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
325 preg_replace('/\s+/', '', $pf1->getNotes()))) {
326 $this->_differentNotes($pf1->getNotes());
329 $mymaintainers = $this->getMaintainers();
330 $yourmaintainers = $pf1->getMaintainers();
331 for ($i1 = 0; $i1 < count($yourmaintainers); $i1++) {
333 for ($i2 = 0; $i2 < count($mymaintainers); $i2++) {
334 if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
335 if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
336 $this->_differentRole($mymaintainers[$i2]['handle'],
337 $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
340 if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
341 $this->_differentEmail($mymaintainers[$i2]['handle'],
342 $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
345 if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
346 $this->_differentName($mymaintainers[$i2]['handle'],
347 $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
350 unset($mymaintainers[$i2]);
351 $mymaintainers = array_values($mymaintainers);
352 unset($yourmaintainers[$i1]);
353 $yourmaintainers = array_values($yourmaintainers);
362 $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
363 $filelist = $this->getFilelist();
364 foreach ($pf1->getFilelist() as $file => $atts) {
365 if (!isset($filelist[$file])) {
366 $this->_missingFile($file);
373 function _differentPackage($package)
375 $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
376 'self' => $this->getPackage()),
377 'package.xml 1.0 package "%package%" does not match "%self%"');
380 function _differentVersion($version)
382 $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
383 'self' => $this->getVersion()),
384 'package.xml 1.0 version "%version%" does not match "%self%"');
387 function _differentState($state)
389 $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
390 'self' => $this->getState()),
391 'package.xml 1.0 state "%state%" does not match "%self%"');
394 function _differentRole($handle, $role, $selfrole)
396 $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
397 'role' => $role, 'self' => $selfrole),
398 'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
401 function _differentEmail($handle, $email, $selfemail)
403 $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
404 'email' => $email, 'self' => $selfemail),
405 'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
408 function _differentName($handle, $name, $selfname)
410 $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
411 'name' => $name, 'self' => $selfname),
412 'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
415 function _unmatchedMaintainers($my, $yours)
418 array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
419 $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
420 'package.xml 2.0 has unmatched extra maintainers "%handles%"');
423 array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
424 $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
425 'package.xml 1.0 has unmatched extra maintainers "%handles%"');
429 function _differentNotes($notes)
431 $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
432 $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
433 substr($this->getNotes(), 0, 24) . '...';
434 $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
435 'self' => $truncmynotes),
436 'package.xml 1.0 release notes "%notes%" do not match "%self%"');
439 function _differentSummary($summary)
441 $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
442 $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
443 substr($this->getsummary(), 0, 24) . '...';
444 $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
445 'self' => $truncmysummary),
446 'package.xml 1.0 summary "%summary%" does not match "%self%"');
449 function _differentDescription($description)
451 $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
452 $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
453 substr($this->getdescription(), 0, 24) . '...');
454 $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
455 'self' => $truncmydescription),
456 'package.xml 1.0 description "%description%" does not match "%self%"');
459 function _missingFile($file)
461 $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
462 'package.xml 1.0 file "%file%" is not present in <contents>');
466 * WARNING - do not use this function unless you know what you're doing
468 function setRawState($state)
470 if (!isset($this->_packageInfo['stability'])) {
471 $this->_packageInfo['stability'] = array();
473 $this->_packageInfo['stability']['release'] = $state;
477 * WARNING - do not use this function unless you know what you're doing
479 function setRawCompatible($compatible)
481 $this->_packageInfo['compatible'] = $compatible;
485 * WARNING - do not use this function unless you know what you're doing
487 function setRawPackage($package)
489 $this->_packageInfo['name'] = $package;
493 * WARNING - do not use this function unless you know what you're doing
495 function setRawChannel($channel)
497 $this->_packageInfo['channel'] = $channel;
500 function setRequestedGroup($group)
502 $this->_requestedGroup = $group;
505 function getRequestedGroup()
507 if (isset($this->_requestedGroup)) {
508 return $this->_requestedGroup;
514 * For saving in the registry.
516 * Set the last version that was installed
519 function setLastInstalledVersion($version)
521 $this->_packageInfo['_lastversion'] = $version;
525 * @return string|false
527 function getLastInstalledVersion()
529 if (isset($this->_packageInfo['_lastversion'])) {
530 return $this->_packageInfo['_lastversion'];
536 * Determines whether this package.xml has post-install scripts or not
537 * @return array|false
539 function listPostinstallScripts()
541 $filelist = $this->getFilelist();
542 $contents = $this->getContents();
543 $contents = $contents['dir']['file'];
544 if (!is_array($contents) || !isset($contents[0])) {
545 $contents = array($contents);
547 $taskfiles = array();
548 foreach ($contents as $file) {
549 $atts = $file['attribs'];
550 unset($file['attribs']);
552 $taskfiles[$atts['name']] = $file;
555 $common = new PEAR_Common;
556 $common->debug = $this->_config->get('verbose');
557 $this->_scripts = array();
559 foreach ($taskfiles as $name => $tasks) {
560 if (!isset($filelist[$name])) {
561 // ignored files will not be in the filelist
564 $atts = $filelist[$name];
565 foreach ($tasks as $tag => $raw) {
566 $task = $this->getTask($tag);
567 $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
568 if ($task->isScript()) {
569 $ret[] = $filelist[$name]['installed_as'];
580 * Initialize post-install scripts for running
582 * This method can be used to detect post-install scripts, as the return value
583 * indicates whether any exist
586 function initPostinstallScripts()
588 $filelist = $this->getFilelist();
589 $contents = $this->getContents();
590 $contents = $contents['dir']['file'];
591 if (!is_array($contents) || !isset($contents[0])) {
592 $contents = array($contents);
594 $taskfiles = array();
595 foreach ($contents as $file) {
596 $atts = $file['attribs'];
597 unset($file['attribs']);
599 $taskfiles[$atts['name']] = $file;
602 $common = new PEAR_Common;
603 $common->debug = $this->_config->get('verbose');
604 $this->_scripts = array();
605 foreach ($taskfiles as $name => $tasks) {
606 if (!isset($filelist[$name])) {
607 // file was not installed due to installconditions
610 $atts = $filelist[$name];
611 foreach ($tasks as $tag => $raw) {
612 $taskname = $this->getTask($tag);
613 $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
614 if (!$task->isScript()) {
615 continue; // scripts are only handled after installation
617 $lastversion = isset($this->_packageInfo['_lastversion']) ?
618 $this->_packageInfo['_lastversion'] : null;
619 $task->init($raw, $atts, $lastversion);
620 $res = $task->startSession($this, $atts['installed_as']);
622 continue; // skip this file
624 if (PEAR::isError($res)) {
628 $this->_scripts[] = &$assign;
631 if (count($this->_scripts)) {
637 function runPostinstallScripts()
639 if ($this->initPostinstallScripts()) {
640 $ui = &PEAR_Frontend::singleton();
642 $ui->runPostinstallScripts($this->_scripts, $this);
649 * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
652 function flattenFilelist()
654 if (isset($this->_packageInfo['bundle'])) {
658 if (isset($this->_packageInfo['contents']['dir']['dir'])) {
659 $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
660 if (!isset($filelist[1])) {
661 $filelist = $filelist[0];
663 $this->_packageInfo['contents']['dir']['file'] = $filelist;
664 unset($this->_packageInfo['contents']['dir']['dir']);
666 // else already flattened but check for baseinstalldir propagation
667 if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
668 if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
669 foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
670 if (isset($file['attribs']['baseinstalldir'])) {
673 $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
674 = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
677 if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
678 $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
679 = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
687 * @param array the final flattened file list
688 * @param array the current directory being processed
689 * @param string|false any recursively inherited baeinstalldir attribute
690 * @param string private recursion variable
694 function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
696 if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
697 $baseinstall = $dir['attribs']['baseinstalldir'];
699 if (isset($dir['dir'])) {
700 if (!isset($dir['dir'][0])) {
701 $dir['dir'] = array($dir['dir']);
703 foreach ($dir['dir'] as $subdir) {
704 if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
707 $name = $subdir['attribs']['name'];
709 $newpath = empty($path) ? $name :
711 $this->_getFlattenedFilelist($files, $subdir,
712 $baseinstall, $newpath);
715 if (isset($dir['file'])) {
716 if (!isset($dir['file'][0])) {
717 $dir['file'] = array($dir['file']);
719 foreach ($dir['file'] as $file) {
720 $attrs = $file['attribs'];
721 $name = $attrs['name'];
722 if ($baseinstall && !isset($attrs['baseinstalldir'])) {
723 $attrs['baseinstalldir'] = $baseinstall;
725 $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
726 $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
728 $file['attribs'] = $attrs;
734 function setConfig(&$config)
736 $this->_config = &$config;
737 $this->_registry = &$config->getRegistry();
740 function setLogger(&$logger)
742 if (!is_object($logger) || !method_exists($logger, 'log')) {
743 return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
745 $this->_logger = &$logger;
749 * WARNING - do not use this function directly unless you know what you're doing
751 function setDeps($deps)
753 $this->_packageInfo['dependencies'] = $deps;
757 * WARNING - do not use this function directly unless you know what you're doing
759 function setCompatible($compat)
761 $this->_packageInfo['compatible'] = $compat;
764 function setPackagefile($file, $archive = false)
766 $this->_packageFile = $file;
767 $this->_archiveFile = $archive ? $archive : $file;
771 * Wrapper to {@link PEAR_ErrorStack::getErrors()}
772 * @param boolean determines whether to purge the error stack after retrieving
775 function getValidationWarnings($purge = true)
777 return $this->_stack->getErrors($purge);
780 function getPackageFile()
782 return $this->_packageFile;
785 function getArchiveFile()
787 return $this->_archiveFile;
792 * Directly set the array that defines this packagefile
794 * WARNING: no validation. This should only be performed by internal methods
795 * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
798 function fromArray($pinfo)
800 unset($pinfo['old']);
801 unset($pinfo['xsdversion']);
802 // If the changelog isn't an array then it was passed in as an empty tag
803 if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
804 unset($pinfo['changelog']);
806 $this->_incomplete = false;
807 $this->_packageInfo = $pinfo;
810 function isIncomplete()
812 return $this->_incomplete;
818 function toArray($forreg = false)
820 if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
823 return $this->getArray($forreg);
826 function getArray($forReg = false)
829 $arr = $this->_packageInfo;
830 $arr['old'] = array();
831 $arr['old']['version'] = $this->getVersion();
832 $arr['old']['release_date'] = $this->getDate();
833 $arr['old']['release_state'] = $this->getState();
834 $arr['old']['release_license'] = $this->getLicense();
835 $arr['old']['release_notes'] = $this->getNotes();
836 $arr['old']['release_deps'] = $this->getDeps();
837 $arr['old']['maintainers'] = $this->getMaintainers();
838 $arr['xsdversion'] = '2.0';
841 $info = $this->_packageInfo;
842 unset($info['dirtree']);
843 if (isset($info['_lastversion'])) {
844 unset($info['_lastversion']);
846 if (isset($info['#binarypackage'])) {
847 unset($info['#binarypackage']);
853 function packageInfo($field)
855 $arr = $this->getArray(true);
856 if ($field == 'state') {
857 return $arr['stability']['release'];
859 if ($field == 'api-version') {
860 return $arr['version']['api'];
862 if ($field == 'api-state') {
863 return $arr['stability']['api'];
865 if (isset($arr['old'][$field])) {
866 if (!is_string($arr['old'][$field])) {
869 return $arr['old'][$field];
871 if (isset($arr[$field])) {
872 if (!is_string($arr[$field])) {
882 return $this->getPackage();
885 function getPackage()
887 if (isset($this->_packageInfo['name'])) {
888 return $this->_packageInfo['name'];
893 function getChannel()
895 if (isset($this->_packageInfo['uri'])) {
898 if (isset($this->_packageInfo['channel'])) {
899 return strtolower($this->_packageInfo['channel']);
906 if (isset($this->_packageInfo['uri'])) {
907 return $this->_packageInfo['uri'];
912 function getExtends()
914 if (isset($this->_packageInfo['extends'])) {
915 return $this->_packageInfo['extends'];
920 function getSummary()
922 if (isset($this->_packageInfo['summary'])) {
923 return $this->_packageInfo['summary'];
928 function getDescription()
930 if (isset($this->_packageInfo['description'])) {
931 return $this->_packageInfo['description'];
936 function getMaintainers($raw = false)
938 if (!isset($this->_packageInfo['lead'])) {
942 $ret = array('lead' => $this->_packageInfo['lead']);
943 (isset($this->_packageInfo['developer'])) ?
944 $ret['developer'] = $this->_packageInfo['developer'] :null;
945 (isset($this->_packageInfo['contributor'])) ?
946 $ret['contributor'] = $this->_packageInfo['contributor'] :null;
947 (isset($this->_packageInfo['helper'])) ?
948 $ret['helper'] = $this->_packageInfo['helper'] :null;
952 $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
953 array($this->_packageInfo['lead']);
954 foreach ($leads as $lead) {
956 $s['handle'] = $s['user'];
961 if (isset($this->_packageInfo['developer'])) {
962 $leads = isset($this->_packageInfo['developer'][0]) ?
963 $this->_packageInfo['developer'] :
964 array($this->_packageInfo['developer']);
965 foreach ($leads as $maintainer) {
967 $s['handle'] = $s['user'];
969 $s['role'] = 'developer';
973 if (isset($this->_packageInfo['contributor'])) {
974 $leads = isset($this->_packageInfo['contributor'][0]) ?
975 $this->_packageInfo['contributor'] :
976 array($this->_packageInfo['contributor']);
977 foreach ($leads as $maintainer) {
979 $s['handle'] = $s['user'];
981 $s['role'] = 'contributor';
985 if (isset($this->_packageInfo['helper'])) {
986 $leads = isset($this->_packageInfo['helper'][0]) ?
987 $this->_packageInfo['helper'] :
988 array($this->_packageInfo['helper']);
989 foreach ($leads as $maintainer) {
991 $s['handle'] = $s['user'];
993 $s['role'] = 'helper';
1004 if (isset($this->_packageInfo['lead'])) {
1005 return $this->_packageInfo['lead'];
1010 function getDevelopers()
1012 if (isset($this->_packageInfo['developer'])) {
1013 return $this->_packageInfo['developer'];
1018 function getContributors()
1020 if (isset($this->_packageInfo['contributor'])) {
1021 return $this->_packageInfo['contributor'];
1026 function getHelpers()
1028 if (isset($this->_packageInfo['helper'])) {
1029 return $this->_packageInfo['helper'];
1034 function setDate($date)
1036 if (!isset($this->_packageInfo['date'])) {
1037 // ensure that the extends tag is set up in the right location
1038 $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
1039 array('time', 'version',
1040 'stability', 'license', 'notes', 'contents', 'compatible',
1041 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
1042 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
1043 'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
1045 $this->_packageInfo['date'] = $date;
1046 $this->_isValid = 0;
1049 function setTime($time)
1051 $this->_isValid = 0;
1052 if (!isset($this->_packageInfo['time'])) {
1053 // ensure that the time tag is set up in the right location
1054 $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
1056 'stability', 'license', 'notes', 'contents', 'compatible',
1057 'dependencies', 'providesextension', 'srcpackage', 'srcuri',
1058 'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
1059 'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
1061 $this->_packageInfo['time'] = $time;
1066 if (isset($this->_packageInfo['date'])) {
1067 return $this->_packageInfo['date'];
1074 if (isset($this->_packageInfo['time'])) {
1075 return $this->_packageInfo['time'];
1081 * @param package|api version category to return
1083 function getVersion($key = 'release')
1085 if (isset($this->_packageInfo['version'][$key])) {
1086 return $this->_packageInfo['version'][$key];
1091 function getStability()
1093 if (isset($this->_packageInfo['stability'])) {
1094 return $this->_packageInfo['stability'];
1099 function getState($key = 'release')
1101 if (isset($this->_packageInfo['stability'][$key])) {
1102 return $this->_packageInfo['stability'][$key];
1107 function getLicense($raw = false)
1109 if (isset($this->_packageInfo['license'])) {
1111 return $this->_packageInfo['license'];
1113 if (is_array($this->_packageInfo['license'])) {
1114 return $this->_packageInfo['license']['_content'];
1116 return $this->_packageInfo['license'];
1122 function getLicenseLocation()
1124 if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
1127 return $this->_packageInfo['license']['attribs'];
1132 if (isset($this->_packageInfo['notes'])) {
1133 return $this->_packageInfo['notes'];
1139 * Return the <usesrole> tag contents, if any
1140 * @return array|false
1142 function getUsesrole()
1144 if (isset($this->_packageInfo['usesrole'])) {
1145 return $this->_packageInfo['usesrole'];
1151 * Return the <usestask> tag contents, if any
1152 * @return array|false
1154 function getUsestask()
1156 if (isset($this->_packageInfo['usestask'])) {
1157 return $this->_packageInfo['usestask'];
1163 * This should only be used to retrieve filenames and install attributes
1165 function getFilelist($preserve = false)
1167 if (isset($this->_packageInfo['filelist']) && !$preserve) {
1168 return $this->_packageInfo['filelist'];
1170 $this->flattenFilelist();
1171 if ($contents = $this->getContents()) {
1173 if (!isset($contents['dir'])) {
1176 if (!isset($contents['dir']['file'][0])) {
1177 $contents['dir']['file'] = array($contents['dir']['file']);
1179 foreach ($contents['dir']['file'] as $file) {
1180 $name = $file['attribs']['name'];
1182 $file = $file['attribs'];
1184 $ret[$name] = $file;
1187 $this->_packageInfo['filelist'] = $ret;
1195 * Return configure options array, if any
1197 * @return array|false
1199 function getConfigureOptions()
1201 if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
1205 $releases = $this->getReleases();
1206 if (isset($releases[0])) {
1207 $releases = $releases[0];
1210 if (isset($releases['configureoption'])) {
1211 if (!isset($releases['configureoption'][0])) {
1212 $releases['configureoption'] = array($releases['configureoption']);
1215 for ($i = 0; $i < count($releases['configureoption']); $i++) {
1216 $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
1219 return $releases['configureoption'];
1226 * This is only used at install-time, after all serialization
1229 function resetFilelist()
1231 $this->_packageInfo['filelist'] = array();
1235 * Retrieve a list of files that should be installed on this computer
1238 function getInstallationFilelist($forfilecheck = false)
1240 $contents = $this->getFilelist(true);
1241 if (isset($contents['dir']['attribs']['baseinstalldir'])) {
1242 $base = $contents['dir']['attribs']['baseinstalldir'];
1244 if (isset($this->_packageInfo['bundle'])) {
1245 return PEAR::raiseError(
1246 'Exception: bundles should be handled in download code only');
1248 $release = $this->getReleases();
1250 if (!isset($release[0])) {
1251 if (!isset($release['installconditions']) && !isset($release['filelist'])) {
1252 if ($forfilecheck) {
1253 return $this->getFilelist();
1257 $release = array($release);
1259 $depchecker = &$this->getPEARDependency2($this->_config, array(),
1260 array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
1261 PEAR_VALIDATE_INSTALLING);
1262 foreach ($release as $instance) {
1263 if (isset($instance['installconditions'])) {
1264 $installconditions = $instance['installconditions'];
1265 if (is_array($installconditions)) {
1266 PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1267 foreach ($installconditions as $type => $conditions) {
1268 if (!isset($conditions[0])) {
1269 $conditions = array($conditions);
1271 foreach ($conditions as $condition) {
1272 $ret = $depchecker->{"validate{$type}Dependency"}($condition);
1273 if (PEAR::isError($ret)) {
1274 PEAR::popErrorHandling();
1275 continue 3; // skip this release
1279 PEAR::popErrorHandling();
1282 // this is the release to use
1283 if (isset($instance['filelist'])) {
1285 if (isset($instance['filelist']['ignore'])) {
1286 $ignore = isset($instance['filelist']['ignore'][0]) ?
1287 $instance['filelist']['ignore'] :
1288 array($instance['filelist']['ignore']);
1289 foreach ($ignore as $ig) {
1290 unset ($contents[$ig['attribs']['name']]);
1293 // install files as this name
1294 if (isset($instance['filelist']['install'])) {
1295 $installas = isset($instance['filelist']['install'][0]) ?
1296 $instance['filelist']['install'] :
1297 array($instance['filelist']['install']);
1298 foreach ($installas as $as) {
1299 $contents[$as['attribs']['name']]['attribs']['install-as'] =
1300 $as['attribs']['as'];
1304 if ($forfilecheck) {
1305 foreach ($contents as $file => $attrs) {
1306 $contents[$file] = $attrs['attribs'];
1311 } else { // simple release - no installconditions or install-as
1312 if ($forfilecheck) {
1313 return $this->getFilelist();
1317 // no releases matched
1318 return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
1319 'system, extensions installed, or architecture, cannot install');
1323 * This is only used at install-time, after all serialization
1325 * @param string file name
1326 * @param string installed path
1328 function setInstalledAs($file, $path)
1331 return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
1333 unset($this->_packageInfo['filelist'][$file]['installed_as']);
1336 function getInstalledLocation($file)
1338 if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
1339 return $this->_packageInfo['filelist'][$file]['installed_as'];
1345 * This is only used at install-time, after all serialization
1348 function installedFile($file, $atts)
1350 if (isset($this->_packageInfo['filelist'][$file])) {
1351 $this->_packageInfo['filelist'][$file] =
1352 array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
1354 $this->_packageInfo['filelist'][$file] = $atts['attribs'];
1359 * Retrieve the contents tag
1361 function getContents()
1363 if (isset($this->_packageInfo['contents'])) {
1364 return $this->_packageInfo['contents'];
1370 * @param string full path to file
1371 * @param string attribute name
1372 * @param string attribute value
1373 * @param int risky but fast - use this to choose a file based on its position in the list
1374 * of files. Index is zero-based like PHP arrays.
1375 * @return bool success of operation
1377 function setFileAttribute($filename, $attr, $value, $index = false)
1379 $this->_isValid = 0;
1380 if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
1381 $this->_filesValid = false;
1383 if ($index !== false &&
1384 isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
1385 $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
1388 if (!isset($this->_packageInfo['contents']['dir']['file'])) {
1391 $files = $this->_packageInfo['contents']['dir']['file'];
1392 if (!isset($files[0])) {
1393 $files = array($files);
1398 foreach ($files as $i => $file) {
1399 if (isset($file['attribs'])) {
1400 if ($file['attribs']['name'] == $filename) {
1402 $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
1404 $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
1413 function setDirtree($path)
1415 if (!isset($this->_packageInfo['dirtree'])) {
1416 $this->_packageInfo['dirtree'] = array();
1418 $this->_packageInfo['dirtree'][$path] = true;
1421 function getDirtree()
1423 if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
1424 return $this->_packageInfo['dirtree'];
1429 function resetDirtree()
1431 unset($this->_packageInfo['dirtree']);
1435 * Determines whether this package claims it is compatible with the version of
1436 * the package that has a recommended version dependency
1437 * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
1440 function isCompatible($pf)
1442 if (!isset($this->_packageInfo['compatible'])) {
1445 if (!isset($this->_packageInfo['channel'])) {
1448 $me = $pf->getVersion();
1449 $compatible = $this->_packageInfo['compatible'];
1450 if (!isset($compatible[0])) {
1451 $compatible = array($compatible);
1454 foreach ($compatible as $info) {
1455 if (strtolower($info['name']) == strtolower($pf->getPackage())) {
1456 if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
1465 if (isset($info['exclude'])) {
1466 if (!isset($info['exclude'][0])) {
1467 $info['exclude'] = array($info['exclude']);
1469 foreach ($info['exclude'] as $exclude) {
1470 if (version_compare($me, $exclude, '==')) {
1475 if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
1482 * @return array|false
1484 function getCompatible()
1486 if (isset($this->_packageInfo['compatible'])) {
1487 return $this->_packageInfo['compatible'];
1492 function getDependencies()
1494 if (isset($this->_packageInfo['dependencies'])) {
1495 return $this->_packageInfo['dependencies'];
1500 function isSubpackageOf($p)
1502 return $p->isSubpackage($this);
1506 * Determines whether the passed in package is a subpackage of this package.
1508 * No version checking is done, only name verification.
1509 * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1512 function isSubpackage($p)
1515 if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
1516 $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
1517 if (!isset($sub[0])) {
1521 if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
1522 $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
1523 if (!isset($sub1[0])) {
1524 $sub1 = array($sub1);
1526 $sub = array_merge($sub, $sub1);
1528 if (isset($this->_packageInfo['dependencies']['group'])) {
1529 $group = $this->_packageInfo['dependencies']['group'];
1530 if (!isset($group[0])) {
1531 $group = array($group);
1533 foreach ($group as $deps) {
1534 if (isset($deps['subpackage'])) {
1535 $sub2 = $deps['subpackage'];
1536 if (!isset($sub2[0])) {
1537 $sub2 = array($sub2);
1539 $sub = array_merge($sub, $sub2);
1543 foreach ($sub as $dep) {
1544 if (strtolower($dep['name']) == strtolower($p->getPackage())) {
1545 if (isset($dep['channel'])) {
1546 if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
1550 if ($dep['uri'] == $p->getURI()) {
1559 function dependsOn($package, $channel)
1561 if (!($deps = $this->getDependencies())) {
1564 foreach (array('package', 'subpackage') as $type) {
1565 foreach (array('required', 'optional') as $needed) {
1566 if (isset($deps[$needed][$type])) {
1567 if (!isset($deps[$needed][$type][0])) {
1568 $deps[$needed][$type] = array($deps[$needed][$type]);
1570 foreach ($deps[$needed][$type] as $dep) {
1571 $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
1572 if (strtolower($dep['name']) == strtolower($package) &&
1573 $depchannel == $channel) {
1579 if (isset($deps['group'])) {
1580 if (!isset($deps['group'][0])) {
1581 $dep['group'] = array($deps['group']);
1583 foreach ($deps['group'] as $group) {
1584 if (isset($group[$type])) {
1585 if (!is_array($group[$type])) {
1586 $group[$type] = array($group[$type]);
1588 foreach ($group[$type] as $dep) {
1589 $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
1590 if (strtolower($dep['name']) == strtolower($package) &&
1591 $depchannel == $channel) {
1603 * Get the contents of a dependency group
1605 * @return array|false
1607 function getDependencyGroup($name)
1609 $name = strtolower($name);
1610 if (!isset($this->_packageInfo['dependencies']['group'])) {
1613 $groups = $this->_packageInfo['dependencies']['group'];
1614 if (!isset($groups[0])) {
1615 $groups = array($groups);
1617 foreach ($groups as $group) {
1618 if (strtolower($group['attribs']['name']) == $name) {
1626 * Retrieve a partial package.xml 1.0 representation of dependencies
1628 * a very limited representation of dependencies is returned by this method.
1629 * The <exclude> tag for excluding certain versions of a dependency is
1630 * completely ignored. In addition, dependency groups are ignored, with the
1631 * assumption that all dependencies in dependency groups are also listed in
1632 * the optional group that work with all dependency groups
1633 * @param boolean return package.xml 2.0 <dependencies> tag
1634 * @return array|false
1636 function getDeps($raw = false, $nopearinstaller = false)
1638 if (isset($this->_packageInfo['dependencies'])) {
1640 return $this->_packageInfo['dependencies'];
1646 'subpackage' => 'pkg',
1647 'extension' => 'ext',
1649 'pearinstaller' => 'pkg',
1651 foreach (array('required', 'optional') as $type) {
1652 $optional = ($type == 'optional') ? 'yes' : 'no';
1653 if (!isset($this->_packageInfo['dependencies'][$type])
1654 || empty($this->_packageInfo['dependencies'][$type])) {
1657 foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
1658 if ($dtype == 'pearinstaller' && $nopearinstaller) {
1661 if (!isset($deps[0])) {
1662 $deps = array($deps);
1664 foreach ($deps as $dep) {
1665 if (!isset($map[$dtype])) {
1666 // no support for arch type
1669 if ($dtype == 'pearinstaller') {
1670 $dep['name'] = 'PEAR';
1671 $dep['channel'] = 'pear.php.net';
1673 $s = array('type' => $map[$dtype]);
1674 if (isset($dep['channel'])) {
1675 $s['channel'] = $dep['channel'];
1677 if (isset($dep['uri'])) {
1678 $s['uri'] = $dep['uri'];
1680 if (isset($dep['name'])) {
1681 $s['name'] = $dep['name'];
1683 if (isset($dep['conflicts'])) {
1686 if (!isset($dep['min']) &&
1687 !isset($dep['max'])) {
1689 $s['optional'] = $optional;
1690 } elseif (isset($dep['min']) &&
1691 isset($dep['max'])) {
1695 $s['version'] = $dep['min'];
1696 $s1['version'] = $dep['max'];
1697 if (isset($dep['channel'])) {
1698 $s1['channel'] = $dep['channel'];
1700 if ($dtype != 'php') {
1701 $s['name'] = $dep['name'];
1702 $s1['name'] = $dep['name'];
1704 $s['optional'] = $optional;
1705 $s1['optional'] = $optional;
1707 } elseif (isset($dep['min'])) {
1708 if (isset($dep['exclude']) &&
1709 $dep['exclude'] == $dep['min']) {
1714 $s['version'] = $dep['min'];
1715 $s['optional'] = $optional;
1716 if ($dtype != 'php') {
1717 $s['name'] = $dep['name'];
1719 } elseif (isset($dep['max'])) {
1720 if (isset($dep['exclude']) &&
1721 $dep['exclude'] == $dep['max']) {
1726 $s['version'] = $dep['max'];
1727 $s['optional'] = $optional;
1728 if ($dtype != 'php') {
1729 $s['name'] = $dep['name'];
1745 * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
1747 function getPackageType()
1749 if (isset($this->_packageInfo['phprelease'])) {
1752 if (isset($this->_packageInfo['extsrcrelease'])) {
1755 if (isset($this->_packageInfo['extbinrelease'])) {
1758 if (isset($this->_packageInfo['zendextsrcrelease'])) {
1759 return 'zendextsrc';
1761 if (isset($this->_packageInfo['zendextbinrelease'])) {
1762 return 'zendextbin';
1764 if (isset($this->_packageInfo['bundle'])) {
1771 * @return array|false
1773 function getReleases()
1775 $type = $this->getPackageType();
1776 if ($type != 'bundle') {
1779 if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
1780 return $this->_packageInfo[$type];
1788 function getChangelog()
1790 if (isset($this->_packageInfo['changelog'])) {
1791 return $this->_packageInfo['changelog'];
1798 return isset($this->_packageInfo['dependencies']);
1801 function getPackagexmlVersion()
1803 if (isset($this->_packageInfo['zendextsrcrelease'])) {
1806 if (isset($this->_packageInfo['zendextbinrelease'])) {
1813 * @return array|false
1815 function getSourcePackage()
1817 if (isset($this->_packageInfo['extbinrelease']) ||
1818 isset($this->_packageInfo['zendextbinrelease'])) {
1819 return array('channel' => $this->_packageInfo['srcchannel'],
1820 'package' => $this->_packageInfo['srcpackage']);
1825 function getBundledPackages()
1827 if (isset($this->_packageInfo['bundle'])) {
1828 return $this->_packageInfo['contents']['bundledpackage'];
1833 function getLastModified()
1835 if (isset($this->_packageInfo['_lastmodified'])) {
1836 return $this->_packageInfo['_lastmodified'];
1842 * Get the contents of a file listed within the package.xml
1846 function getFileContents($file)
1848 if ($this->_archiveFile == $this->_packageFile) { // unpacked
1849 $dir = dirname($this->_packageFile);
1850 $file = $dir . DIRECTORY_SEPARATOR . $file;
1851 $file = str_replace(array('/', '\\'),
1852 array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
1853 if (file_exists($file) && is_readable($file)) {
1854 return implode('', file($file));
1857 $tar = &new Archive_Tar($this->_archiveFile);
1858 $tar->pushErrorHandling(PEAR_ERROR_RETURN);
1859 if ($file != 'package.xml' && $file != 'package2.xml') {
1860 $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
1862 $file = $tar->extractInString($file);
1863 $tar->popErrorHandling();
1864 if (PEAR::isError($file)) {
1865 return PEAR::raiseError("Cannot locate file '$file' in archive");
1873 if (!class_exists('PEAR_PackageFile_v2_rw')) {
1874 require_once 'PEAR/PackageFile/v2/rw.php';
1876 $a = new PEAR_PackageFile_v2_rw;
1877 foreach (get_object_vars($this) as $name => $unused) {
1878 if (!isset($this->$name)) {
1881 if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
1882 $name == '_stack') {
1883 $a->$name = &$this->$name;
1885 $a->$name = $this->$name;
1891 function &getDefaultGenerator()
1893 if (!class_exists('PEAR_PackageFile_Generator_v2')) {
1894 require_once 'PEAR/PackageFile/Generator/v2.php';
1896 $a = &new PEAR_PackageFile_Generator_v2($this);
1900 function analyzeSourceCode($file, $string = false)
1902 if (!isset($this->_v2Validator) ||
1903 !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
1904 if (!class_exists('PEAR_PackageFile_v2_Validator')) {
1905 require_once 'PEAR/PackageFile/v2/Validator.php';
1907 $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1909 return $this->_v2Validator->analyzeSourceCode($file, $string);
1912 function validate($state = PEAR_VALIDATE_NORMAL)
1914 if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
1917 if (!isset($this->_v2Validator) ||
1918 !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
1919 if (!class_exists('PEAR_PackageFile_v2_Validator')) {
1920 require_once 'PEAR/PackageFile/v2/Validator.php';
1922 $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1924 if (isset($this->_packageInfo['xsdversion'])) {
1925 unset($this->_packageInfo['xsdversion']);
1927 return $this->_v2Validator->validate($this, $state);
1930 function getTasksNs()
1932 if (!isset($this->_tasksNs)) {
1933 if (isset($this->_packageInfo['attribs'])) {
1934 foreach ($this->_packageInfo['attribs'] as $name => $value) {
1935 if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
1936 $this->_tasksNs = str_replace('xmlns:', '', $name);
1942 return $this->_tasksNs;
1946 * Determine whether a task name is a valid task. Custom tasks may be defined
1947 * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
1949 * Note that this method will auto-load the task class file and test for the existence
1950 * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
1951 * PEAR_Task_mycustom_task
1955 function getTask($task)
1957 $this->getTasksNs();
1958 // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
1959 $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
1960 $taskfile = str_replace(' ', '/', ucwords($task));
1961 $task = str_replace(array(' ', '/'), '_', ucwords($task));
1962 if (class_exists("PEAR_Task_$task")) {
1963 return "PEAR_Task_$task";
1965 $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
1968 require_once "PEAR/Task/$taskfile.php";
1969 return "PEAR_Task_$task";
1975 * Key-friendly array_splice
1976 * @param tagname to splice a value in before
1977 * @param mixed the value to splice in
1978 * @param string the new tag name
1980 function _ksplice($array, $key, $value, $newkey)
1982 $offset = array_search($key, array_keys($array));
1983 $after = array_slice($array, $offset);
1984 $before = array_slice($array, 0, $offset);
1985 $before[$newkey] = $value;
1986 return array_merge($before, $after);
1990 * @param array a list of possible keys, in the order they may occur
1991 * @param mixed contents of the new package.xml tag
1992 * @param string tag name
1995 function _insertBefore($array, $keys, $contents, $newkey)
1997 foreach ($keys as $key) {
1998 if (isset($array[$key])) {
1999 return $array = $this->_ksplice($array, $key, $contents, $newkey);
2002 $array[$newkey] = $contents;
2007 * @param subsection of {@link $_packageInfo}
2008 * @param array|string tag contents
2009 * @param array format:
2012 * tagname => array(list of tag names that follow this one),
2013 * childtagname => array(list of child tag names that follow this one),
2017 * This allows construction of nested tags
2020 function _mergeTag($manip, $contents, $order)
2022 if (count($order)) {
2023 foreach ($order as $tag => $curorder) {
2024 if (!isset($manip[$tag])) {
2025 // ensure that the tag is set up
2026 $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
2028 if (count($order) > 1) {
2029 $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
2036 if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
2037 $manip[$tag][] = $contents;
2039 if (!count($manip[$tag])) {
2040 $manip[$tag] = $contents;
2042 $manip[$tag] = array($manip[$tag]);
2043 $manip[$tag][] = $contents;