source: trunk/library/PEAR/PEAR/PackageFile/v2.php @ 7655

Revision 7655, 68.1 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Melhorias de performance no codigo do Expresso.

Line 
1<?php
2/**
3 * PEAR_PackageFile_v2, package.xml version 2.0
4 *
5 * PHP versions 4 and 5
6 *
7 * @category   pear
8 * @package    PEAR
9 * @author     Greg Beaver <cellog@php.net>
10 * @copyright  1997-2009 The Authors
11 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
12 * @version    CVS: $Id: 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
15 */
16/**
17 * For error handling
18 */
19require_once 'PEAR/ErrorStack.php';
20/**
21 * @category   pear
22 * @package    PEAR
23 * @author     Greg Beaver <cellog@php.net>
24 * @copyright  1997-2009 The Authors
25 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
26 * @version    Release: 1.9.4
27 * @link       http://pear.php.net/package/PEAR
28 * @since      Class available since Release 1.4.0a1
29 */
30class PEAR_PackageFile_v2
31{
32
33    /**
34     * Parsed package information
35     * @var array
36     * @access private
37     */
38    var $_packageInfo = array();
39
40    /**
41     * path to package .tgz or false if this is a local/extracted package.xml
42     * @var string|false
43     * @access private
44     */
45    var $_archiveFile;
46
47    /**
48     * path to package .xml or false if this is an abstract parsed-from-string xml
49     * @var string|false
50     * @access private
51     */
52    var $_packageFile;
53
54    /**
55     * This is used by file analysis routines to log progress information
56     * @var PEAR_Common
57     * @access protected
58     */
59    var $_logger;
60
61    /**
62     * This is set to the highest validation level that has been validated
63     *
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
70     * @var int
71     * @access private
72     */
73    var $_isValid = 0;
74
75    /**
76     * True if the filelist has been validated
77     * @param bool
78     */
79    var $_filesValid = false;
80
81    /**
82     * @var PEAR_Registry
83     * @access protected
84     */
85    var $_registry;
86
87    /**
88     * @var PEAR_Config
89     * @access protected
90     */
91    var $_config;
92
93    /**
94     * Optional Dependency group requested for installation
95     * @var string
96     * @access private
97     */
98    var $_requestedGroup = false;
99
100    /**
101     * @var PEAR_ErrorStack
102     * @access protected
103     */
104    var $_stack;
105
106    /**
107     * Namespace prefix used for tasks in this package.xml - use tasks: whenever possible
108     */
109    var $_tasksNs;
110
111    /**
112     * Determines whether this packagefile was initialized only with partial package info
113     *
114     * If this package file was constructed via parsing REST, it will only contain
115     *
116     * - package name
117     * - channel name
118     * - dependencies
119     * @var boolean
120     * @access private
121     */
122    var $_incomplete = true;
123
124    /**
125     * @var PEAR_PackageFile_v2_Validator
126     */
127    var $_v2Validator;
128
129    /**
130     * The constructor merely sets up the private error stack
131     */
132    function PEAR_PackageFile_v2()
133    {
134        $this->_stack = new PEAR_ErrorStack('PEAR_PackageFile_v2', false, null);
135        $this->_isValid = false;
136    }
137
138    /**
139     * To make unit-testing easier
140     * @param PEAR_Frontend_*
141     * @param array options
142     * @param PEAR_Config
143     * @return PEAR_Downloader
144     * @access protected
145     */
146    function &getPEARDownloader(&$i, $o, &$c)
147    {
148        $z = &new PEAR_Downloader($i, $o, $c);
149        return $z;
150    }
151
152    /**
153     * To make unit-testing easier
154     * @param PEAR_Config
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
159     * @access protected
160     */
161    function &getPEARDependency2(&$c, $o, $p, $s = PEAR_VALIDATE_INSTALLING)
162    {
163        if (!class_exists('PEAR_Dependency2')) {
164            require_once 'PEAR/Dependency2.php';
165        }
166        $z = &new PEAR_Dependency2($c, $o, $p, $s);
167        return $z;
168    }
169
170    function getInstalledBinary()
171    {
172        return isset($this->_packageInfo['#binarypackage']) ? $this->_packageInfo['#binarypackage'] :
173            false;
174    }
175
176    /**
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
181     */
182    function installBinary(&$installer)
183    {
184        if (!OS_WINDOWS) {
185            $a = false;
186            return $a;
187        }
188        if ($this->getPackageType() == 'extsrc' || $this->getPackageType() == 'zendextsrc') {
189            $releasetype = $this->getPackageType() . 'release';
190            if (!is_array($installer->getInstallPackages())) {
191                $a = false;
192                return $a;
193            }
194            foreach ($installer->getInstallPackages() as $p) {
195                if ($p->isExtension($this->_packageInfo['providesextension'])) {
196                    if ($p->getPackageType() != 'extsrc' && $p->getPackageType() != 'zendextsrc') {
197                        $a = false;
198                        return $a; // the user probably downloaded it separately
199                    }
200                }
201            }
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);
208                }
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());
213                    }
214                }
215                $dl = &$this->getPEARDownloader($installer->ui, $installer->getOptions(),
216                    $installer->config);
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)) {
224                        break;
225                    }
226                }
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);
243                            return $a;
244                        }
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');
249                    }
250                }
251            }
252        }
253        $a = false;
254        return $a;
255    }
256
257    /**
258     * @return string|false Extension name
259     */
260    function getProvidesExtension()
261    {
262        if (in_array($this->getPackageType(),
263              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
264            if (isset($this->_packageInfo['providesextension'])) {
265                return $this->_packageInfo['providesextension'];
266            }
267        }
268        return false;
269    }
270
271    /**
272     * @param string Extension name
273     * @return bool
274     */
275    function isExtension($extension)
276    {
277        if (in_array($this->getPackageType(),
278              array('extsrc', 'extbin', 'zendextsrc', 'zendextbin'))) {
279            return $this->_packageInfo['providesextension'] == $extension;
280        }
281        return false;
282    }
283
284    /**
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
288     * @return bool
289     */
290    function isEquivalent($pf1)
291    {
292        if (!$pf1) {
293            return true;
294        }
295        if ($this->getPackageType() == 'bundle') {
296            return false;
297        }
298        $this->_stack->getErrors(true);
299        if (!$pf1->validate(PEAR_VALIDATE_NORMAL)) {
300            return false;
301        }
302        $pass = true;
303        if ($pf1->getPackage() != $this->getPackage()) {
304            $this->_differentPackage($pf1->getPackage());
305            $pass = false;
306        }
307        if ($pf1->getVersion() != $this->getVersion()) {
308            $this->_differentVersion($pf1->getVersion());
309            $pass = false;
310        }
311        if (trim($pf1->getSummary()) != $this->getSummary()) {
312            $this->_differentSummary($pf1->getSummary());
313            $pass = false;
314        }
315        if (preg_replace('/\s+/', '', $pf1->getDescription()) !=
316              preg_replace('/\s+/', '', $this->getDescription())) {
317            $this->_differentDescription($pf1->getDescription());
318            $pass = false;
319        }
320        if ($pf1->getState() != $this->getState()) {
321            $this->_differentState($pf1->getState());
322            $pass = false;
323        }
324        if (!strstr(preg_replace('/\s+/', '', $this->getNotes()),
325              preg_replace('/\s+/', '', $pf1->getNotes()))) {
326            $this->_differentNotes($pf1->getNotes());
327            $pass = false;
328        }
329        $mymaintainers = $this->getMaintainers();
330        $yourmaintainers = $pf1->getMaintainers();
331        for ($i1 = 0; $i1 < count($yourmaintainers); ++$i1) {
332            $reset = false;
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']);
338                        $pass = false;
339                    }
340                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
341                        $this->_differentEmail($mymaintainers[$i2]['handle'],
342                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
343                        $pass = false;
344                    }
345                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
346                        $this->_differentName($mymaintainers[$i2]['handle'],
347                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
348                        $pass = false;
349                    }
350                    unset($mymaintainers[$i2]);
351                    $mymaintainers = array_values($mymaintainers);
352                    unset($yourmaintainers[$i1]);
353                    $yourmaintainers = array_values($yourmaintainers);
354                    $reset = true;
355                    break;
356                }
357            }
358            if ($reset) {
359                $i1 = -1;
360            }
361        }
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);
367                $pass = false;
368            }
369        }
370        return $pass;
371    }
372
373    function _differentPackage($package)
374    {
375        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
376            'self' => $this->getPackage()),
377            'package.xml 1.0 package "%package%" does not match "%self%"');
378    }
379
380    function _differentVersion($version)
381    {
382        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
383            'self' => $this->getVersion()),
384            'package.xml 1.0 version "%version%" does not match "%self%"');
385    }
386
387    function _differentState($state)
388    {
389        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
390            'self' => $this->getState()),
391            'package.xml 1.0 state "%state%" does not match "%self%"');
392    }
393
394    function _differentRole($handle, $role, $selfrole)
395    {
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%"');
399    }
400
401    function _differentEmail($handle, $email, $selfemail)
402    {
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%"');
406    }
407
408    function _differentName($handle, $name, $selfname)
409    {
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%"');
413    }
414
415    function _unmatchedMaintainers($my, $yours)
416    {
417        if ($my) {
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%"');
421        }
422        if ($yours) {
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%"');
426        }
427    }
428
429    function _differentNotes($notes)
430    {
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%"');
437    }
438
439    function _differentSummary($summary)
440    {
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%"');
447    }
448
449    function _differentDescription($description)
450    {
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%"');
457    }
458
459    function _missingFile($file)
460    {
461        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
462            'package.xml 1.0 file "%file%" is not present in <contents>');
463    }
464
465    /**
466     * WARNING - do not use this function unless you know what you're doing
467     */
468    function setRawState($state)
469    {
470        if (!isset($this->_packageInfo['stability'])) {
471            $this->_packageInfo['stability'] = array();
472        }
473        $this->_packageInfo['stability']['release'] = $state;
474    }
475
476    /**
477     * WARNING - do not use this function unless you know what you're doing
478     */
479    function setRawCompatible($compatible)
480    {
481        $this->_packageInfo['compatible'] = $compatible;
482    }
483
484    /**
485     * WARNING - do not use this function unless you know what you're doing
486     */
487    function setRawPackage($package)
488    {
489        $this->_packageInfo['name'] = $package;
490    }
491
492    /**
493     * WARNING - do not use this function unless you know what you're doing
494     */
495    function setRawChannel($channel)
496    {
497        $this->_packageInfo['channel'] = $channel;
498    }
499
500    function setRequestedGroup($group)
501    {
502        $this->_requestedGroup = $group;
503    }
504
505    function getRequestedGroup()
506    {
507        if (isset($this->_requestedGroup)) {
508            return $this->_requestedGroup;
509        }
510        return false;
511    }
512
513    /**
514     * For saving in the registry.
515     *
516     * Set the last version that was installed
517     * @param string
518     */
519    function setLastInstalledVersion($version)
520    {
521        $this->_packageInfo['_lastversion'] = $version;
522    }
523
524    /**
525     * @return string|false
526     */
527    function getLastInstalledVersion()
528    {
529        if (isset($this->_packageInfo['_lastversion'])) {
530            return $this->_packageInfo['_lastversion'];
531        }
532        return false;
533    }
534
535    /**
536     * Determines whether this package.xml has post-install scripts or not
537     * @return array|false
538     */
539    function listPostinstallScripts()
540    {
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);
546        }
547        $taskfiles = array();
548        foreach ($contents as $file) {
549            $atts = $file['attribs'];
550            unset($file['attribs']);
551            if (count($file)) {
552                $taskfiles[$atts['name']] = $file;
553            }
554        }
555        $common = new PEAR_Common;
556        $common->debug = $this->_config->get('verbose');
557        $this->_scripts = array();
558        $ret = array();
559        foreach ($taskfiles as $name => $tasks) {
560            if (!isset($filelist[$name])) {
561                // ignored files will not be in the filelist
562                continue;
563            }
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'];
570                }
571            }
572        }
573        if (count($ret)) {
574            return $ret;
575        }
576        return false;
577    }
578
579    /**
580     * Initialize post-install scripts for running
581     *
582     * This method can be used to detect post-install scripts, as the return value
583     * indicates whether any exist
584     * @return bool
585     */
586    function initPostinstallScripts()
587    {
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);
593        }
594        $taskfiles = array();
595        foreach ($contents as $file) {
596            $atts = $file['attribs'];
597            unset($file['attribs']);
598            if (count($file)) {
599                $taskfiles[$atts['name']] = $file;
600            }
601        }
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
608                continue;
609            }
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
616                }
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']);
621                if (!$res) {
622                    continue; // skip this file
623                }
624                if (PEAR::isError($res)) {
625                    return $res;
626                }
627                $assign = &$task;
628                $this->_scripts[] = &$assign;
629            }
630        }
631        if (count($this->_scripts)) {
632            return true;
633        }
634        return false;
635    }
636
637    function runPostinstallScripts()
638    {
639        if ($this->initPostinstallScripts()) {
640            $ui = &PEAR_Frontend::singleton();
641            if ($ui) {
642                $ui->runPostinstallScripts($this->_scripts, $this);
643            }
644        }
645    }
646
647
648    /**
649     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
650     * <file> tags.
651     */
652    function flattenFilelist()
653    {
654        if (isset($this->_packageInfo['bundle'])) {
655            return;
656        }
657        $filelist = array();
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];
662            }
663            $this->_packageInfo['contents']['dir']['file'] = $filelist;
664            unset($this->_packageInfo['contents']['dir']['dir']);
665        } else {
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'])) {
671                            continue;
672                        }
673                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
674                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
675                    }
676                } else {
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'];
680                    }
681                }
682            }
683        }
684    }
685
686    /**
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
691     * @return array
692     * @access protected
693     */
694    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
695    {
696        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
697            $baseinstall = $dir['attribs']['baseinstalldir'];
698        }
699        if (isset($dir['dir'])) {
700            if (!isset($dir['dir'][0])) {
701                $dir['dir'] = array($dir['dir']);
702            }
703            foreach ($dir['dir'] as $subdir) {
704                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
705                    $name = '*unknown*';
706                } else {
707                    $name = $subdir['attribs']['name'];
708                }
709                $newpath = empty($path) ? $name :
710                    $path . '/' . $name;
711                $this->_getFlattenedFilelist($files, $subdir,
712                    $baseinstall, $newpath);
713            }
714        }
715        if (isset($dir['file'])) {
716            if (!isset($dir['file'][0])) {
717                $dir['file'] = array($dir['file']);
718            }
719            foreach ($dir['file'] as $file) {
720                $attrs = $file['attribs'];
721                $name = $attrs['name'];
722                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
723                    $attrs['baseinstalldir'] = $baseinstall;
724                }
725                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
726                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
727                    $attrs['name']);
728                $file['attribs'] = $attrs;
729                $files[] = $file;
730            }
731        }
732    }
733
734    function setConfig(&$config)
735    {
736        $this->_config = &$config;
737        $this->_registry = &$config->getRegistry();
738    }
739
740    function setLogger(&$logger)
741    {
742        if (!is_object($logger) || !method_exists($logger, 'log')) {
743            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
744        }
745        $this->_logger = &$logger;
746    }
747
748    /**
749     * WARNING - do not use this function directly unless you know what you're doing
750     */
751    function setDeps($deps)
752    {
753        $this->_packageInfo['dependencies'] = $deps;
754    }
755
756    /**
757     * WARNING - do not use this function directly unless you know what you're doing
758     */
759    function setCompatible($compat)
760    {
761        $this->_packageInfo['compatible'] = $compat;
762    }
763
764    function setPackagefile($file, $archive = false)
765    {
766        $this->_packageFile = $file;
767        $this->_archiveFile = $archive ? $archive : $file;
768    }
769
770    /**
771     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
772     * @param boolean determines whether to purge the error stack after retrieving
773     * @return array
774     */
775    function getValidationWarnings($purge = true)
776    {
777        return $this->_stack->getErrors($purge);
778    }
779
780    function getPackageFile()
781    {
782        return $this->_packageFile;
783    }
784
785    function getArchiveFile()
786    {
787        return $this->_archiveFile;
788    }
789
790
791    /**
792     * Directly set the array that defines this packagefile
793     *
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
796     * @param array
797     */
798    function fromArray($pinfo)
799    {
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']);
805        }
806        $this->_incomplete = false;
807        $this->_packageInfo = $pinfo;
808    }
809
810    function isIncomplete()
811    {
812        return $this->_incomplete;
813    }
814
815    /**
816     * @return array
817     */
818    function toArray($forreg = false)
819    {
820        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
821            return false;
822        }
823        return $this->getArray($forreg);
824    }
825
826    function getArray($forReg = false)
827    {
828        if ($forReg) {
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';
839            return $arr;
840        } else {
841            $info = $this->_packageInfo;
842            unset($info['dirtree']);
843            if (isset($info['_lastversion'])) {
844                unset($info['_lastversion']);
845            }
846            if (isset($info['#binarypackage'])) {
847                unset($info['#binarypackage']);
848            }
849            return $info;
850        }
851    }
852
853    function packageInfo($field)
854    {
855        $arr = $this->getArray(true);
856        if ($field == 'state') {
857            return $arr['stability']['release'];
858        }
859        if ($field == 'api-version') {
860            return $arr['version']['api'];
861        }
862        if ($field == 'api-state') {
863            return $arr['stability']['api'];
864        }
865        if (isset($arr['old'][$field])) {
866            if (!is_string($arr['old'][$field])) {
867                return null;
868            }
869            return $arr['old'][$field];
870        }
871        if (isset($arr[$field])) {
872            if (!is_string($arr[$field])) {
873                return null;
874            }
875            return $arr[$field];
876        }
877        return null;
878    }
879
880    function getName()
881    {
882        return $this->getPackage();
883    }
884
885    function getPackage()
886    {
887        if (isset($this->_packageInfo['name'])) {
888            return $this->_packageInfo['name'];
889        }
890        return false;
891    }
892
893    function getChannel()
894    {
895        if (isset($this->_packageInfo['uri'])) {
896            return '__uri';
897        }
898        if (isset($this->_packageInfo['channel'])) {
899            return strtolower($this->_packageInfo['channel']);
900        }
901        return false;
902    }
903
904    function getUri()
905    {
906        if (isset($this->_packageInfo['uri'])) {
907            return $this->_packageInfo['uri'];
908        }
909        return false;
910    }
911
912    function getExtends()
913    {
914        if (isset($this->_packageInfo['extends'])) {
915            return $this->_packageInfo['extends'];
916        }
917        return false;
918    }
919
920    function getSummary()
921    {
922        if (isset($this->_packageInfo['summary'])) {
923            return $this->_packageInfo['summary'];
924        }
925        return false;
926    }
927
928    function getDescription()
929    {
930        if (isset($this->_packageInfo['description'])) {
931            return $this->_packageInfo['description'];
932        }
933        return false;
934    }
935
936    function getMaintainers($raw = false)
937    {
938        if (!isset($this->_packageInfo['lead'])) {
939            return false;
940        }
941        if ($raw) {
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;
949            return $ret;
950        } else {
951            $ret = array();
952            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
953                array($this->_packageInfo['lead']);
954            foreach ($leads as $lead) {
955                $s = $lead;
956                $s['handle'] = $s['user'];
957                unset($s['user']);
958                $s['role'] = 'lead';
959                $ret[] = $s;
960            }
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) {
966                    $s = $maintainer;
967                    $s['handle'] = $s['user'];
968                    unset($s['user']);
969                    $s['role'] = 'developer';
970                    $ret[] = $s;
971                }
972            }
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) {
978                    $s = $maintainer;
979                    $s['handle'] = $s['user'];
980                    unset($s['user']);
981                    $s['role'] = 'contributor';
982                    $ret[] = $s;
983                }
984            }
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) {
990                    $s = $maintainer;
991                    $s['handle'] = $s['user'];
992                    unset($s['user']);
993                    $s['role'] = 'helper';
994                    $ret[] = $s;
995                }
996            }
997            return $ret;
998        }
999        return false;
1000    }
1001
1002    function getLeads()
1003    {
1004        if (isset($this->_packageInfo['lead'])) {
1005            return $this->_packageInfo['lead'];
1006        }
1007        return false;
1008    }
1009
1010    function getDevelopers()
1011    {
1012        if (isset($this->_packageInfo['developer'])) {
1013            return $this->_packageInfo['developer'];
1014        }
1015        return false;
1016    }
1017
1018    function getContributors()
1019    {
1020        if (isset($this->_packageInfo['contributor'])) {
1021            return $this->_packageInfo['contributor'];
1022        }
1023        return false;
1024    }
1025
1026    function getHelpers()
1027    {
1028        if (isset($this->_packageInfo['helper'])) {
1029            return $this->_packageInfo['helper'];
1030        }
1031        return false;
1032    }
1033
1034    function setDate($date)
1035    {
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');
1044        }
1045        $this->_packageInfo['date'] = $date;
1046        $this->_isValid = 0;
1047    }
1048
1049    function setTime($time)
1050    {
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,
1055                    array('version',
1056                    'stability', 'license', 'notes', 'contents', 'compatible',
1057                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
1058                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
1059                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
1060        }
1061        $this->_packageInfo['time'] = $time;
1062    }
1063
1064    function getDate()
1065    {
1066        if (isset($this->_packageInfo['date'])) {
1067            return $this->_packageInfo['date'];
1068        }
1069        return false;
1070    }
1071
1072    function getTime()
1073    {
1074        if (isset($this->_packageInfo['time'])) {
1075            return $this->_packageInfo['time'];
1076        }
1077        return false;
1078    }
1079
1080    /**
1081     * @param package|api version category to return
1082     */
1083    function getVersion($key = 'release')
1084    {
1085        if (isset($this->_packageInfo['version'][$key])) {
1086            return $this->_packageInfo['version'][$key];
1087        }
1088        return false;
1089    }
1090
1091    function getStability()
1092    {
1093        if (isset($this->_packageInfo['stability'])) {
1094            return $this->_packageInfo['stability'];
1095        }
1096        return false;
1097    }
1098
1099    function getState($key = 'release')
1100    {
1101        if (isset($this->_packageInfo['stability'][$key])) {
1102            return $this->_packageInfo['stability'][$key];
1103        }
1104        return false;
1105    }
1106
1107    function getLicense($raw = false)
1108    {
1109        if (isset($this->_packageInfo['license'])) {
1110            if ($raw) {
1111                return $this->_packageInfo['license'];
1112            }
1113            if (is_array($this->_packageInfo['license'])) {
1114                return $this->_packageInfo['license']['_content'];
1115            } else {
1116                return $this->_packageInfo['license'];
1117            }
1118        }
1119        return false;
1120    }
1121
1122    function getLicenseLocation()
1123    {
1124        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
1125            return false;
1126        }
1127        return $this->_packageInfo['license']['attribs'];
1128    }
1129
1130    function getNotes()
1131    {
1132        if (isset($this->_packageInfo['notes'])) {
1133            return $this->_packageInfo['notes'];
1134        }
1135        return false;
1136    }
1137
1138    /**
1139     * Return the <usesrole> tag contents, if any
1140     * @return array|false
1141     */
1142    function getUsesrole()
1143    {
1144        if (isset($this->_packageInfo['usesrole'])) {
1145            return $this->_packageInfo['usesrole'];
1146        }
1147        return false;
1148    }
1149
1150    /**
1151     * Return the <usestask> tag contents, if any
1152     * @return array|false
1153     */
1154    function getUsestask()
1155    {
1156        if (isset($this->_packageInfo['usestask'])) {
1157            return $this->_packageInfo['usestask'];
1158        }
1159        return false;
1160    }
1161
1162    /**
1163     * This should only be used to retrieve filenames and install attributes
1164     */
1165    function getFilelist($preserve = false)
1166    {
1167        if (isset($this->_packageInfo['filelist']) && !$preserve) {
1168            return $this->_packageInfo['filelist'];
1169        }
1170        $this->flattenFilelist();
1171        if ($contents = $this->getContents()) {
1172            $ret = array();
1173            if (!isset($contents['dir'])) {
1174                return false;
1175            }
1176            if (!isset($contents['dir']['file'][0])) {
1177                $contents['dir']['file'] = array($contents['dir']['file']);
1178            }
1179            foreach ($contents['dir']['file'] as $file) {
1180                $name = $file['attribs']['name'];
1181                if (!$preserve) {
1182                    $file = $file['attribs'];
1183                }
1184                $ret[$name] = $file;
1185            }
1186            if (!$preserve) {
1187                $this->_packageInfo['filelist'] = $ret;
1188            }
1189            return $ret;
1190        }
1191        return false;
1192    }
1193
1194    /**
1195     * Return configure options array, if any
1196     *
1197     * @return array|false
1198     */
1199    function getConfigureOptions()
1200    {
1201        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
1202            return false;
1203        }
1204
1205        $releases = $this->getReleases();
1206        if (isset($releases[0])) {
1207            $releases = $releases[0];
1208        }
1209
1210        if (isset($releases['configureoption'])) {
1211            if (!isset($releases['configureoption'][0])) {
1212                $releases['configureoption'] = array($releases['configureoption']);
1213            }
1214
1215            for ($i = 0; $i < count($releases['configureoption']); ++$i) {
1216                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
1217            }
1218
1219            return $releases['configureoption'];
1220        }
1221
1222        return false;
1223    }
1224
1225    /**
1226     * This is only used at install-time, after all serialization
1227     * is over.
1228     */
1229    function resetFilelist()
1230    {
1231        $this->_packageInfo['filelist'] = array();
1232    }
1233
1234    /**
1235     * Retrieve a list of files that should be installed on this computer
1236     * @return array
1237     */
1238    function getInstallationFilelist($forfilecheck = false)
1239    {
1240        $contents = $this->getFilelist(true);
1241        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
1242            $base = $contents['dir']['attribs']['baseinstalldir'];
1243        }
1244        if (isset($this->_packageInfo['bundle'])) {
1245            return PEAR::raiseError(
1246                'Exception: bundles should be handled in download code only');
1247        }
1248        $release = $this->getReleases();
1249        if ($release) {
1250            if (!isset($release[0])) {
1251                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
1252                    if ($forfilecheck) {
1253                        return $this->getFilelist();
1254                    }
1255                    return $contents;
1256                }
1257                $release = array($release);
1258            }
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);
1270                            }
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
1276                                }
1277                            }
1278                        }
1279                        PEAR::popErrorHandling();
1280                    }
1281                }
1282                // this is the release to use
1283                if (isset($instance['filelist'])) {
1284                    // ignore files
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']]);
1291                        }
1292                    }
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'];
1301                        }
1302                    }
1303                }
1304                if ($forfilecheck) {
1305                    foreach ($contents as $file => $attrs) {
1306                        $contents[$file] = $attrs['attribs'];
1307                    }
1308                }
1309                return $contents;
1310            }
1311        } else { // simple release - no installconditions or install-as
1312            if ($forfilecheck) {
1313                return $this->getFilelist();
1314            }
1315            return $contents;
1316        }
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');
1320    }
1321
1322    /**
1323     * This is only used at install-time, after all serialization
1324     * is over.
1325     * @param string file name
1326     * @param string installed path
1327     */
1328    function setInstalledAs($file, $path)
1329    {
1330        if ($path) {
1331            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
1332        }
1333        unset($this->_packageInfo['filelist'][$file]['installed_as']);
1334    }
1335
1336    function getInstalledLocation($file)
1337    {
1338        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
1339            return $this->_packageInfo['filelist'][$file]['installed_as'];
1340        }
1341        return false;
1342    }
1343
1344    /**
1345     * This is only used at install-time, after all serialization
1346     * is over.
1347     */
1348    function installedFile($file, $atts)
1349    {
1350        if (isset($this->_packageInfo['filelist'][$file])) {
1351            $this->_packageInfo['filelist'][$file] =
1352                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
1353        } else {
1354            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
1355        }
1356    }
1357
1358    /**
1359     * Retrieve the contents tag
1360     */
1361    function getContents()
1362    {
1363        if (isset($this->_packageInfo['contents'])) {
1364            return $this->_packageInfo['contents'];
1365        }
1366        return false;
1367    }
1368
1369    /**
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
1376     */
1377    function setFileAttribute($filename, $attr, $value, $index = false)
1378    {
1379        $this->_isValid = 0;
1380        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
1381            $this->_filesValid = false;
1382        }
1383        if ($index !== false &&
1384              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
1385            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
1386            return true;
1387        }
1388        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
1389            return false;
1390        }
1391        $files = $this->_packageInfo['contents']['dir']['file'];
1392        if (!isset($files[0])) {
1393            $files = array($files);
1394            $ind = false;
1395        } else {
1396            $ind = true;
1397        }
1398        foreach ($files as $i => $file) {
1399            if (isset($file['attribs'])) {
1400                if ($file['attribs']['name'] == $filename) {
1401                    if ($ind) {
1402                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
1403                    } else {
1404                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
1405                    }
1406                    return true;
1407                }
1408            }
1409        }
1410        return false;
1411    }
1412
1413    function setDirtree($path)
1414    {
1415        if (!isset($this->_packageInfo['dirtree'])) {
1416            $this->_packageInfo['dirtree'] = array();
1417        }
1418        $this->_packageInfo['dirtree'][$path] = true;
1419    }
1420
1421    function getDirtree()
1422    {
1423        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
1424            return $this->_packageInfo['dirtree'];
1425        }
1426        return false;
1427    }
1428
1429    function resetDirtree()
1430    {
1431        unset($this->_packageInfo['dirtree']);
1432    }
1433
1434    /**
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
1438     * @return boolean
1439     */
1440    function isCompatible($pf)
1441    {
1442        if (!isset($this->_packageInfo['compatible'])) {
1443            return false;
1444        }
1445        if (!isset($this->_packageInfo['channel'])) {
1446            return false;
1447        }
1448        $me = $pf->getVersion();
1449        $compatible = $this->_packageInfo['compatible'];
1450        if (!isset($compatible[0])) {
1451            $compatible = array($compatible);
1452        }
1453        $found = false;
1454        foreach ($compatible as $info) {
1455            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
1456                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
1457                    $found = true;
1458                    break;
1459                }
1460            }
1461        }
1462        if (!$found) {
1463            return false;
1464        }
1465        if (isset($info['exclude'])) {
1466            if (!isset($info['exclude'][0])) {
1467                $info['exclude'] = array($info['exclude']);
1468            }
1469            foreach ($info['exclude'] as $exclude) {
1470                if (version_compare($me, $exclude, '==')) {
1471                    return false;
1472                }
1473            }
1474        }
1475        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
1476            return true;
1477        }
1478        return false;
1479    }
1480
1481    /**
1482     * @return array|false
1483     */
1484    function getCompatible()
1485    {
1486        if (isset($this->_packageInfo['compatible'])) {
1487            return $this->_packageInfo['compatible'];
1488        }
1489        return false;
1490    }
1491
1492    function getDependencies()
1493    {
1494        if (isset($this->_packageInfo['dependencies'])) {
1495            return $this->_packageInfo['dependencies'];
1496        }
1497        return false;
1498    }
1499
1500    function isSubpackageOf($p)
1501    {
1502        return $p->isSubpackage($this);
1503    }
1504
1505    /**
1506     * Determines whether the passed in package is a subpackage of this package.
1507     *
1508     * No version checking is done, only name verification.
1509     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1510     * @return bool
1511     */
1512    function isSubpackage($p)
1513    {
1514        $sub = array();
1515        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
1516            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
1517            if (!isset($sub[0])) {
1518                $sub = array($sub);
1519            }
1520        }
1521        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
1522            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
1523            if (!isset($sub1[0])) {
1524                $sub1 = array($sub1);
1525            }
1526            $sub = array_merge($sub, $sub1);
1527        }
1528        if (isset($this->_packageInfo['dependencies']['group'])) {
1529            $group = $this->_packageInfo['dependencies']['group'];
1530            if (!isset($group[0])) {
1531                $group = array($group);
1532            }
1533            foreach ($group as $deps) {
1534                if (isset($deps['subpackage'])) {
1535                    $sub2 = $deps['subpackage'];
1536                    if (!isset($sub2[0])) {
1537                        $sub2 = array($sub2);
1538                    }
1539                    $sub = array_merge($sub, $sub2);
1540                }
1541            }
1542        }
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())) {
1547                        return true;
1548                    }
1549                } else {
1550                    if ($dep['uri'] == $p->getURI()) {
1551                        return true;
1552                    }
1553                }
1554            }
1555        }
1556        return false;
1557    }
1558
1559    function dependsOn($package, $channel)
1560    {
1561        if (!($deps = $this->getDependencies())) {
1562            return false;
1563        }
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]);
1569                    }
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) {
1574                            return true;
1575                        }
1576                    }
1577                }
1578            }
1579            if (isset($deps['group'])) {
1580                if (!isset($deps['group'][0])) {
1581                    $dep['group'] = array($deps['group']);
1582                }
1583                foreach ($deps['group'] as $group) {
1584                    if (isset($group[$type])) {
1585                        if (!is_array($group[$type])) {
1586                            $group[$type] = array($group[$type]);
1587                        }
1588                        foreach ($group[$type] as $dep) {
1589                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
1590                            if (strtolower($dep['name']) == strtolower($package) &&
1591                                  $depchannel == $channel) {
1592                                return true;
1593                            }
1594                        }
1595                    }
1596                }
1597            }
1598        }
1599        return false;
1600    }
1601
1602    /**
1603     * Get the contents of a dependency group
1604     * @param string
1605     * @return array|false
1606     */
1607    function getDependencyGroup($name)
1608    {
1609        $name = strtolower($name);
1610        if (!isset($this->_packageInfo['dependencies']['group'])) {
1611            return false;
1612        }
1613        $groups = $this->_packageInfo['dependencies']['group'];
1614        if (!isset($groups[0])) {
1615            $groups = array($groups);
1616        }
1617        foreach ($groups as $group) {
1618            if (strtolower($group['attribs']['name']) == $name) {
1619                return $group;
1620            }
1621        }
1622        return false;
1623    }
1624
1625    /**
1626     * Retrieve a partial package.xml 1.0 representation of dependencies
1627     *
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
1635     */
1636    function getDeps($raw = false, $nopearinstaller = false)
1637    {
1638        if (isset($this->_packageInfo['dependencies'])) {
1639            if ($raw) {
1640                return $this->_packageInfo['dependencies'];
1641            }
1642            $ret = array();
1643            $map = array(
1644                'php' => 'php',
1645                'package' => 'pkg',
1646                'subpackage' => 'pkg',
1647                'extension' => 'ext',
1648                'os' => 'os',
1649                'pearinstaller' => 'pkg',
1650                );
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])) {
1655                    continue;
1656                }
1657                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
1658                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
1659                        continue;
1660                    }
1661                    if (!isset($deps[0])) {
1662                        $deps = array($deps);
1663                    }
1664                    foreach ($deps as $dep) {
1665                        if (!isset($map[$dtype])) {
1666                            // no support for arch type
1667                            continue;
1668                        }
1669                        if ($dtype == 'pearinstaller') {
1670                            $dep['name'] = 'PEAR';
1671                            $dep['channel'] = 'pear.php.net';
1672                        }
1673                        $s = array('type' => $map[$dtype]);
1674                        if (isset($dep['channel'])) {
1675                            $s['channel'] = $dep['channel'];
1676                        }
1677                        if (isset($dep['uri'])) {
1678                            $s['uri'] = $dep['uri'];
1679                        }
1680                        if (isset($dep['name'])) {
1681                            $s['name'] = $dep['name'];
1682                        }
1683                        if (isset($dep['conflicts'])) {
1684                            $s['rel'] = 'not';
1685                        } else {
1686                            if (!isset($dep['min']) &&
1687                                  !isset($dep['max'])) {
1688                                $s['rel'] = 'has';
1689                                $s['optional'] = $optional;
1690                            } elseif (isset($dep['min']) &&
1691                                  isset($dep['max'])) {
1692                                $s['rel'] = 'ge';
1693                                $s1 = $s;
1694                                $s1['rel'] = 'le';
1695                                $s['version'] = $dep['min'];
1696                                $s1['version'] = $dep['max'];
1697                                if (isset($dep['channel'])) {
1698                                    $s1['channel'] = $dep['channel'];
1699                                }
1700                                if ($dtype != 'php') {
1701                                    $s['name'] = $dep['name'];
1702                                    $s1['name'] = $dep['name'];
1703                                }
1704                                $s['optional'] = $optional;
1705                                $s1['optional'] = $optional;
1706                                $ret[] = $s1;
1707                            } elseif (isset($dep['min'])) {
1708                                if (isset($dep['exclude']) &&
1709                                      $dep['exclude'] == $dep['min']) {
1710                                    $s['rel'] = 'gt';
1711                                } else {
1712                                    $s['rel'] = 'ge';
1713                                }
1714                                $s['version'] = $dep['min'];
1715                                $s['optional'] = $optional;
1716                                if ($dtype != 'php') {
1717                                    $s['name'] = $dep['name'];
1718                                }
1719                            } elseif (isset($dep['max'])) {
1720                                if (isset($dep['exclude']) &&
1721                                      $dep['exclude'] == $dep['max']) {
1722                                    $s['rel'] = 'lt';
1723                                } else {
1724                                    $s['rel'] = 'le';
1725                                }
1726                                $s['version'] = $dep['max'];
1727                                $s['optional'] = $optional;
1728                                if ($dtype != 'php') {
1729                                    $s['name'] = $dep['name'];
1730                                }
1731                            }
1732                        }
1733                        $ret[] = $s;
1734                    }
1735                }
1736            }
1737            if (count($ret)) {
1738                return $ret;
1739            }
1740        }
1741        return false;
1742    }
1743
1744    /**
1745     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
1746     */
1747    function getPackageType()
1748    {
1749        if (isset($this->_packageInfo['phprelease'])) {
1750            return 'php';
1751        }
1752        if (isset($this->_packageInfo['extsrcrelease'])) {
1753            return 'extsrc';
1754        }
1755        if (isset($this->_packageInfo['extbinrelease'])) {
1756            return 'extbin';
1757        }
1758        if (isset($this->_packageInfo['zendextsrcrelease'])) {
1759            return 'zendextsrc';
1760        }
1761        if (isset($this->_packageInfo['zendextbinrelease'])) {
1762            return 'zendextbin';
1763        }
1764        if (isset($this->_packageInfo['bundle'])) {
1765            return 'bundle';
1766        }
1767        return false;
1768    }
1769
1770    /**
1771     * @return array|false
1772     */
1773    function getReleases()
1774    {
1775        $type = $this->getPackageType();
1776        if ($type != 'bundle') {
1777            $type .= 'release';
1778        }
1779        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
1780            return $this->_packageInfo[$type];
1781        }
1782        return false;
1783    }
1784
1785    /**
1786     * @return array
1787     */
1788    function getChangelog()
1789    {
1790        if (isset($this->_packageInfo['changelog'])) {
1791            return $this->_packageInfo['changelog'];
1792        }
1793        return false;
1794    }
1795
1796    function hasDeps()
1797    {
1798        return isset($this->_packageInfo['dependencies']);
1799    }
1800
1801    function getPackagexmlVersion()
1802    {
1803        if (isset($this->_packageInfo['zendextsrcrelease'])) {
1804            return '2.1';
1805        }
1806        if (isset($this->_packageInfo['zendextbinrelease'])) {
1807            return '2.1';
1808        }
1809        return '2.0';
1810    }
1811
1812    /**
1813     * @return array|false
1814     */
1815    function getSourcePackage()
1816    {
1817        if (isset($this->_packageInfo['extbinrelease']) ||
1818              isset($this->_packageInfo['zendextbinrelease'])) {
1819            return array('channel' => $this->_packageInfo['srcchannel'],
1820                         'package' => $this->_packageInfo['srcpackage']);
1821        }
1822        return false;
1823    }
1824
1825    function getBundledPackages()
1826    {
1827        if (isset($this->_packageInfo['bundle'])) {
1828            return $this->_packageInfo['contents']['bundledpackage'];
1829        }
1830        return false;
1831    }
1832
1833    function getLastModified()
1834    {
1835        if (isset($this->_packageInfo['_lastmodified'])) {
1836            return $this->_packageInfo['_lastmodified'];
1837        }
1838        return false;
1839    }
1840
1841    /**
1842     * Get the contents of a file listed within the package.xml
1843     * @param string
1844     * @return string
1845     */
1846    function getFileContents($file)
1847    {
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));
1855            }
1856        } else { // tgz
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;
1861            }
1862            $file = $tar->extractInString($file);
1863            $tar->popErrorHandling();
1864            if (PEAR::isError($file)) {
1865                return PEAR::raiseError("Cannot locate file '$file' in archive");
1866            }
1867            return $file;
1868        }
1869    }
1870
1871    function &getRW()
1872    {
1873        if (!class_exists('PEAR_PackageFile_v2_rw')) {
1874            require_once 'PEAR/PackageFile/v2/rw.php';
1875        }
1876        $a = new PEAR_PackageFile_v2_rw;
1877        foreach (get_object_vars($this) as $name => $unused) {
1878            if (!isset($this->$name)) {
1879                continue;
1880            }
1881            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
1882                  $name == '_stack') {
1883                $a->$name = &$this->$name;
1884            } else {
1885                $a->$name = $this->$name;
1886            }
1887        }
1888        return $a;
1889    }
1890
1891    function &getDefaultGenerator()
1892    {
1893        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
1894            require_once 'PEAR/PackageFile/Generator/v2.php';
1895        }
1896        $a = &new PEAR_PackageFile_Generator_v2($this);
1897        return $a;
1898    }
1899
1900    function analyzeSourceCode($file, $string = false)
1901    {
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';
1906            }
1907            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1908        }
1909        return $this->_v2Validator->analyzeSourceCode($file, $string);
1910    }
1911
1912    function validate($state = PEAR_VALIDATE_NORMAL)
1913    {
1914        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
1915            return false;
1916        }
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';
1921            }
1922            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1923        }
1924        if (isset($this->_packageInfo['xsdversion'])) {
1925            unset($this->_packageInfo['xsdversion']);
1926        }
1927        return $this->_v2Validator->validate($this, $state);
1928    }
1929
1930    function getTasksNs()
1931    {
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);
1937                        break;
1938                    }
1939                }
1940            }
1941        }
1942        return $this->_tasksNs;
1943    }
1944
1945    /**
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>
1948     *
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
1952     * @param string
1953     * @return boolean
1954     */
1955    function getTask($task)
1956    {
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";
1964        }
1965        $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
1966        if ($fp) {
1967            fclose($fp);
1968            require_once "PEAR/Task/$taskfile.php";
1969            return "PEAR_Task_$task";
1970        }
1971        return false;
1972    }
1973
1974    /**
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
1979     */
1980    function _ksplice($array, $key, $value, $newkey)
1981    {
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);
1987    }
1988
1989    /**
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
1993     * @access private
1994     */
1995    function _insertBefore($array, $keys, $contents, $newkey)
1996    {
1997        foreach ($keys as $key) {
1998            if (isset($array[$key])) {
1999                return $array = $this->_ksplice($array, $key, $contents, $newkey);
2000            }
2001        }
2002        $array[$newkey] = $contents;
2003        return $array;
2004    }
2005
2006    /**
2007     * @param subsection of {@link $_packageInfo}
2008     * @param array|string tag contents
2009     * @param array format:
2010     * <pre>
2011     * array(
2012     *   tagname => array(list of tag names that follow this one),
2013     *   childtagname => array(list of child tag names that follow this one),
2014     * )
2015     * </pre>
2016     *
2017     * This allows construction of nested tags
2018     * @access private
2019     */
2020    function _mergeTag($manip, $contents, $order)
2021    {
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);
2027                }
2028                if (count($order) > 1) {
2029                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
2030                    return $manip;
2031                }
2032            }
2033        } else {
2034            return $manip;
2035        }
2036        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
2037            $manip[$tag][] = $contents;
2038        } else {
2039            if (!count($manip[$tag])) {
2040                $manip[$tag] = $contents;
2041            } else {
2042                $manip[$tag] = array($manip[$tag]);
2043                $manip[$tag][] = $contents;
2044            }
2045        }
2046        return $manip;
2047    }
2048}
2049?>
Note: See TracBrowser for help on using the repository browser.