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

Revision 7673, 68.2 KB checked in by douglasz, 11 years ago (diff)

Ticket #3236 - Correcoes para Performance: Function Within Loop Declaration.

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        $yourmaintainers_count = count($yourmaintainers);
332        for ($i1 = 0; $i1 < $yourmaintainers_count; ++$i1) {
333            $reset = false;
334            $mymaintainers_count = count($mymaintainers);
335            for ($i2 = 0; $i2 < $mymaintainers_count; ++$i2) {
336                if ($mymaintainers[$i2]['handle'] == $yourmaintainers[$i1]['handle']) {
337                    if ($mymaintainers[$i2]['role'] != $yourmaintainers[$i1]['role']) {
338                        $this->_differentRole($mymaintainers[$i2]['handle'],
339                            $yourmaintainers[$i1]['role'], $mymaintainers[$i2]['role']);
340                        $pass = false;
341                    }
342                    if ($mymaintainers[$i2]['email'] != $yourmaintainers[$i1]['email']) {
343                        $this->_differentEmail($mymaintainers[$i2]['handle'],
344                            $yourmaintainers[$i1]['email'], $mymaintainers[$i2]['email']);
345                        $pass = false;
346                    }
347                    if ($mymaintainers[$i2]['name'] != $yourmaintainers[$i1]['name']) {
348                        $this->_differentName($mymaintainers[$i2]['handle'],
349                            $yourmaintainers[$i1]['name'], $mymaintainers[$i2]['name']);
350                        $pass = false;
351                    }
352                    unset($mymaintainers[$i2]);
353                    $mymaintainers = array_values($mymaintainers);
354                    unset($yourmaintainers[$i1]);
355                    $yourmaintainers = array_values($yourmaintainers);
356                    $reset = true;
357                    break;
358                }
359            }
360            if ($reset) {
361                $i1 = -1;
362            }
363        }
364        $this->_unmatchedMaintainers($mymaintainers, $yourmaintainers);
365        $filelist = $this->getFilelist();
366        foreach ($pf1->getFilelist() as $file => $atts) {
367            if (!isset($filelist[$file])) {
368                $this->_missingFile($file);
369                $pass = false;
370            }
371        }
372        return $pass;
373    }
374
375    function _differentPackage($package)
376    {
377        $this->_stack->push(__FUNCTION__, 'error', array('package' => $package,
378            'self' => $this->getPackage()),
379            'package.xml 1.0 package "%package%" does not match "%self%"');
380    }
381
382    function _differentVersion($version)
383    {
384        $this->_stack->push(__FUNCTION__, 'error', array('version' => $version,
385            'self' => $this->getVersion()),
386            'package.xml 1.0 version "%version%" does not match "%self%"');
387    }
388
389    function _differentState($state)
390    {
391        $this->_stack->push(__FUNCTION__, 'error', array('state' => $state,
392            'self' => $this->getState()),
393            'package.xml 1.0 state "%state%" does not match "%self%"');
394    }
395
396    function _differentRole($handle, $role, $selfrole)
397    {
398        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
399            'role' => $role, 'self' => $selfrole),
400            'package.xml 1.0 maintainer "%handle%" role "%role%" does not match "%self%"');
401    }
402
403    function _differentEmail($handle, $email, $selfemail)
404    {
405        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
406            'email' => $email, 'self' => $selfemail),
407            'package.xml 1.0 maintainer "%handle%" email "%email%" does not match "%self%"');
408    }
409
410    function _differentName($handle, $name, $selfname)
411    {
412        $this->_stack->push(__FUNCTION__, 'error', array('handle' => $handle,
413            'name' => $name, 'self' => $selfname),
414            'package.xml 1.0 maintainer "%handle%" name "%name%" does not match "%self%"');
415    }
416
417    function _unmatchedMaintainers($my, $yours)
418    {
419        if ($my) {
420            array_walk($my, create_function('&$i, $k', '$i = $i["handle"];'));
421            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $my),
422                'package.xml 2.0 has unmatched extra maintainers "%handles%"');
423        }
424        if ($yours) {
425            array_walk($yours, create_function('&$i, $k', '$i = $i["handle"];'));
426            $this->_stack->push(__FUNCTION__, 'error', array('handles' => $yours),
427                'package.xml 1.0 has unmatched extra maintainers "%handles%"');
428        }
429    }
430
431    function _differentNotes($notes)
432    {
433        $truncnotes = strlen($notes) < 25 ? $notes : substr($notes, 0, 24) . '...';
434        $truncmynotes = strlen($this->getNotes()) < 25 ? $this->getNotes() :
435            substr($this->getNotes(), 0, 24) . '...';
436        $this->_stack->push(__FUNCTION__, 'error', array('notes' => $truncnotes,
437            'self' => $truncmynotes),
438            'package.xml 1.0 release notes "%notes%" do not match "%self%"');
439    }
440
441    function _differentSummary($summary)
442    {
443        $truncsummary = strlen($summary) < 25 ? $summary : substr($summary, 0, 24) . '...';
444        $truncmysummary = strlen($this->getsummary()) < 25 ? $this->getSummary() :
445            substr($this->getsummary(), 0, 24) . '...';
446        $this->_stack->push(__FUNCTION__, 'error', array('summary' => $truncsummary,
447            'self' => $truncmysummary),
448            'package.xml 1.0 summary "%summary%" does not match "%self%"');
449    }
450
451    function _differentDescription($description)
452    {
453        $truncdescription = trim(strlen($description) < 25 ? $description : substr($description, 0, 24) . '...');
454        $truncmydescription = trim(strlen($this->getDescription()) < 25 ? $this->getDescription() :
455            substr($this->getdescription(), 0, 24) . '...');
456        $this->_stack->push(__FUNCTION__, 'error', array('description' => $truncdescription,
457            'self' => $truncmydescription),
458            'package.xml 1.0 description "%description%" does not match "%self%"');
459    }
460
461    function _missingFile($file)
462    {
463        $this->_stack->push(__FUNCTION__, 'error', array('file' => $file),
464            'package.xml 1.0 file "%file%" is not present in <contents>');
465    }
466
467    /**
468     * WARNING - do not use this function unless you know what you're doing
469     */
470    function setRawState($state)
471    {
472        if (!isset($this->_packageInfo['stability'])) {
473            $this->_packageInfo['stability'] = array();
474        }
475        $this->_packageInfo['stability']['release'] = $state;
476    }
477
478    /**
479     * WARNING - do not use this function unless you know what you're doing
480     */
481    function setRawCompatible($compatible)
482    {
483        $this->_packageInfo['compatible'] = $compatible;
484    }
485
486    /**
487     * WARNING - do not use this function unless you know what you're doing
488     */
489    function setRawPackage($package)
490    {
491        $this->_packageInfo['name'] = $package;
492    }
493
494    /**
495     * WARNING - do not use this function unless you know what you're doing
496     */
497    function setRawChannel($channel)
498    {
499        $this->_packageInfo['channel'] = $channel;
500    }
501
502    function setRequestedGroup($group)
503    {
504        $this->_requestedGroup = $group;
505    }
506
507    function getRequestedGroup()
508    {
509        if (isset($this->_requestedGroup)) {
510            return $this->_requestedGroup;
511        }
512        return false;
513    }
514
515    /**
516     * For saving in the registry.
517     *
518     * Set the last version that was installed
519     * @param string
520     */
521    function setLastInstalledVersion($version)
522    {
523        $this->_packageInfo['_lastversion'] = $version;
524    }
525
526    /**
527     * @return string|false
528     */
529    function getLastInstalledVersion()
530    {
531        if (isset($this->_packageInfo['_lastversion'])) {
532            return $this->_packageInfo['_lastversion'];
533        }
534        return false;
535    }
536
537    /**
538     * Determines whether this package.xml has post-install scripts or not
539     * @return array|false
540     */
541    function listPostinstallScripts()
542    {
543        $filelist = $this->getFilelist();
544        $contents = $this->getContents();
545        $contents = $contents['dir']['file'];
546        if (!is_array($contents) || !isset($contents[0])) {
547            $contents = array($contents);
548        }
549        $taskfiles = array();
550        foreach ($contents as $file) {
551            $atts = $file['attribs'];
552            unset($file['attribs']);
553            if (count($file)) {
554                $taskfiles[$atts['name']] = $file;
555            }
556        }
557        $common = new PEAR_Common;
558        $common->debug = $this->_config->get('verbose');
559        $this->_scripts = array();
560        $ret = array();
561        foreach ($taskfiles as $name => $tasks) {
562            if (!isset($filelist[$name])) {
563                // ignored files will not be in the filelist
564                continue;
565            }
566            $atts = $filelist[$name];
567            foreach ($tasks as $tag => $raw) {
568                $task = $this->getTask($tag);
569                $task = &new $task($this->_config, $common, PEAR_TASK_INSTALL);
570                if ($task->isScript()) {
571                    $ret[] = $filelist[$name]['installed_as'];
572                }
573            }
574        }
575        if (count($ret)) {
576            return $ret;
577        }
578        return false;
579    }
580
581    /**
582     * Initialize post-install scripts for running
583     *
584     * This method can be used to detect post-install scripts, as the return value
585     * indicates whether any exist
586     * @return bool
587     */
588    function initPostinstallScripts()
589    {
590        $filelist = $this->getFilelist();
591        $contents = $this->getContents();
592        $contents = $contents['dir']['file'];
593        if (!is_array($contents) || !isset($contents[0])) {
594            $contents = array($contents);
595        }
596        $taskfiles = array();
597        foreach ($contents as $file) {
598            $atts = $file['attribs'];
599            unset($file['attribs']);
600            if (count($file)) {
601                $taskfiles[$atts['name']] = $file;
602            }
603        }
604        $common = new PEAR_Common;
605        $common->debug = $this->_config->get('verbose');
606        $this->_scripts = array();
607        foreach ($taskfiles as $name => $tasks) {
608            if (!isset($filelist[$name])) {
609                // file was not installed due to installconditions
610                continue;
611            }
612            $atts = $filelist[$name];
613            foreach ($tasks as $tag => $raw) {
614                $taskname = $this->getTask($tag);
615                $task = &new $taskname($this->_config, $common, PEAR_TASK_INSTALL);
616                if (!$task->isScript()) {
617                    continue; // scripts are only handled after installation
618                }
619                $lastversion = isset($this->_packageInfo['_lastversion']) ?
620                    $this->_packageInfo['_lastversion'] : null;
621                $task->init($raw, $atts, $lastversion);
622                $res = $task->startSession($this, $atts['installed_as']);
623                if (!$res) {
624                    continue; // skip this file
625                }
626                if (PEAR::isError($res)) {
627                    return $res;
628                }
629                $assign = &$task;
630                $this->_scripts[] = &$assign;
631            }
632        }
633        if (count($this->_scripts)) {
634            return true;
635        }
636        return false;
637    }
638
639    function runPostinstallScripts()
640    {
641        if ($this->initPostinstallScripts()) {
642            $ui = &PEAR_Frontend::singleton();
643            if ($ui) {
644                $ui->runPostinstallScripts($this->_scripts, $this);
645            }
646        }
647    }
648
649
650    /**
651     * Convert a recursive set of <dir> and <file> tags into a single <dir> tag with
652     * <file> tags.
653     */
654    function flattenFilelist()
655    {
656        if (isset($this->_packageInfo['bundle'])) {
657            return;
658        }
659        $filelist = array();
660        if (isset($this->_packageInfo['contents']['dir']['dir'])) {
661            $this->_getFlattenedFilelist($filelist, $this->_packageInfo['contents']['dir']);
662            if (!isset($filelist[1])) {
663                $filelist = $filelist[0];
664            }
665            $this->_packageInfo['contents']['dir']['file'] = $filelist;
666            unset($this->_packageInfo['contents']['dir']['dir']);
667        } else {
668            // else already flattened but check for baseinstalldir propagation
669            if (isset($this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'])) {
670                if (isset($this->_packageInfo['contents']['dir']['file'][0])) {
671                    foreach ($this->_packageInfo['contents']['dir']['file'] as $i => $file) {
672                        if (isset($file['attribs']['baseinstalldir'])) {
673                            continue;
674                        }
675                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs']['baseinstalldir']
676                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
677                    }
678                } else {
679                    if (!isset($this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir'])) {
680                       $this->_packageInfo['contents']['dir']['file']['attribs']['baseinstalldir']
681                            = $this->_packageInfo['contents']['dir']['attribs']['baseinstalldir'];
682                    }
683                }
684            }
685        }
686    }
687
688    /**
689     * @param array the final flattened file list
690     * @param array the current directory being processed
691     * @param string|false any recursively inherited baeinstalldir attribute
692     * @param string private recursion variable
693     * @return array
694     * @access protected
695     */
696    function _getFlattenedFilelist(&$files, $dir, $baseinstall = false, $path = '')
697    {
698        if (isset($dir['attribs']) && isset($dir['attribs']['baseinstalldir'])) {
699            $baseinstall = $dir['attribs']['baseinstalldir'];
700        }
701        if (isset($dir['dir'])) {
702            if (!isset($dir['dir'][0])) {
703                $dir['dir'] = array($dir['dir']);
704            }
705            foreach ($dir['dir'] as $subdir) {
706                if (!isset($subdir['attribs']) || !isset($subdir['attribs']['name'])) {
707                    $name = '*unknown*';
708                } else {
709                    $name = $subdir['attribs']['name'];
710                }
711                $newpath = empty($path) ? $name :
712                    $path . '/' . $name;
713                $this->_getFlattenedFilelist($files, $subdir,
714                    $baseinstall, $newpath);
715            }
716        }
717        if (isset($dir['file'])) {
718            if (!isset($dir['file'][0])) {
719                $dir['file'] = array($dir['file']);
720            }
721            foreach ($dir['file'] as $file) {
722                $attrs = $file['attribs'];
723                $name = $attrs['name'];
724                if ($baseinstall && !isset($attrs['baseinstalldir'])) {
725                    $attrs['baseinstalldir'] = $baseinstall;
726                }
727                $attrs['name'] = empty($path) ? $name : $path . '/' . $name;
728                $attrs['name'] = preg_replace(array('!\\\\+!', '!/+!'), array('/', '/'),
729                    $attrs['name']);
730                $file['attribs'] = $attrs;
731                $files[] = $file;
732            }
733        }
734    }
735
736    function setConfig(&$config)
737    {
738        $this->_config = &$config;
739        $this->_registry = &$config->getRegistry();
740    }
741
742    function setLogger(&$logger)
743    {
744        if (!is_object($logger) || !method_exists($logger, 'log')) {
745            return PEAR::raiseError('Logger must be compatible with PEAR_Common::log');
746        }
747        $this->_logger = &$logger;
748    }
749
750    /**
751     * WARNING - do not use this function directly unless you know what you're doing
752     */
753    function setDeps($deps)
754    {
755        $this->_packageInfo['dependencies'] = $deps;
756    }
757
758    /**
759     * WARNING - do not use this function directly unless you know what you're doing
760     */
761    function setCompatible($compat)
762    {
763        $this->_packageInfo['compatible'] = $compat;
764    }
765
766    function setPackagefile($file, $archive = false)
767    {
768        $this->_packageFile = $file;
769        $this->_archiveFile = $archive ? $archive : $file;
770    }
771
772    /**
773     * Wrapper to {@link PEAR_ErrorStack::getErrors()}
774     * @param boolean determines whether to purge the error stack after retrieving
775     * @return array
776     */
777    function getValidationWarnings($purge = true)
778    {
779        return $this->_stack->getErrors($purge);
780    }
781
782    function getPackageFile()
783    {
784        return $this->_packageFile;
785    }
786
787    function getArchiveFile()
788    {
789        return $this->_archiveFile;
790    }
791
792
793    /**
794     * Directly set the array that defines this packagefile
795     *
796     * WARNING: no validation.  This should only be performed by internal methods
797     * inside PEAR or by inputting an array saved from an existing PEAR_PackageFile_v2
798     * @param array
799     */
800    function fromArray($pinfo)
801    {
802        unset($pinfo['old']);
803        unset($pinfo['xsdversion']);
804        // If the changelog isn't an array then it was passed in as an empty tag
805        if (isset($pinfo['changelog']) && !is_array($pinfo['changelog'])) {
806          unset($pinfo['changelog']);
807        }
808        $this->_incomplete = false;
809        $this->_packageInfo = $pinfo;
810    }
811
812    function isIncomplete()
813    {
814        return $this->_incomplete;
815    }
816
817    /**
818     * @return array
819     */
820    function toArray($forreg = false)
821    {
822        if (!$this->validate(PEAR_VALIDATE_NORMAL)) {
823            return false;
824        }
825        return $this->getArray($forreg);
826    }
827
828    function getArray($forReg = false)
829    {
830        if ($forReg) {
831            $arr = $this->_packageInfo;
832            $arr['old'] = array();
833            $arr['old']['version'] = $this->getVersion();
834            $arr['old']['release_date'] = $this->getDate();
835            $arr['old']['release_state'] = $this->getState();
836            $arr['old']['release_license'] = $this->getLicense();
837            $arr['old']['release_notes'] = $this->getNotes();
838            $arr['old']['release_deps'] = $this->getDeps();
839            $arr['old']['maintainers'] = $this->getMaintainers();
840            $arr['xsdversion'] = '2.0';
841            return $arr;
842        } else {
843            $info = $this->_packageInfo;
844            unset($info['dirtree']);
845            if (isset($info['_lastversion'])) {
846                unset($info['_lastversion']);
847            }
848            if (isset($info['#binarypackage'])) {
849                unset($info['#binarypackage']);
850            }
851            return $info;
852        }
853    }
854
855    function packageInfo($field)
856    {
857        $arr = $this->getArray(true);
858        if ($field == 'state') {
859            return $arr['stability']['release'];
860        }
861        if ($field == 'api-version') {
862            return $arr['version']['api'];
863        }
864        if ($field == 'api-state') {
865            return $arr['stability']['api'];
866        }
867        if (isset($arr['old'][$field])) {
868            if (!is_string($arr['old'][$field])) {
869                return null;
870            }
871            return $arr['old'][$field];
872        }
873        if (isset($arr[$field])) {
874            if (!is_string($arr[$field])) {
875                return null;
876            }
877            return $arr[$field];
878        }
879        return null;
880    }
881
882    function getName()
883    {
884        return $this->getPackage();
885    }
886
887    function getPackage()
888    {
889        if (isset($this->_packageInfo['name'])) {
890            return $this->_packageInfo['name'];
891        }
892        return false;
893    }
894
895    function getChannel()
896    {
897        if (isset($this->_packageInfo['uri'])) {
898            return '__uri';
899        }
900        if (isset($this->_packageInfo['channel'])) {
901            return strtolower($this->_packageInfo['channel']);
902        }
903        return false;
904    }
905
906    function getUri()
907    {
908        if (isset($this->_packageInfo['uri'])) {
909            return $this->_packageInfo['uri'];
910        }
911        return false;
912    }
913
914    function getExtends()
915    {
916        if (isset($this->_packageInfo['extends'])) {
917            return $this->_packageInfo['extends'];
918        }
919        return false;
920    }
921
922    function getSummary()
923    {
924        if (isset($this->_packageInfo['summary'])) {
925            return $this->_packageInfo['summary'];
926        }
927        return false;
928    }
929
930    function getDescription()
931    {
932        if (isset($this->_packageInfo['description'])) {
933            return $this->_packageInfo['description'];
934        }
935        return false;
936    }
937
938    function getMaintainers($raw = false)
939    {
940        if (!isset($this->_packageInfo['lead'])) {
941            return false;
942        }
943        if ($raw) {
944            $ret = array('lead' => $this->_packageInfo['lead']);
945            (isset($this->_packageInfo['developer'])) ?
946                $ret['developer'] = $this->_packageInfo['developer'] :null;
947            (isset($this->_packageInfo['contributor'])) ?
948                $ret['contributor'] = $this->_packageInfo['contributor'] :null;
949            (isset($this->_packageInfo['helper'])) ?
950                $ret['helper'] = $this->_packageInfo['helper'] :null;
951            return $ret;
952        } else {
953            $ret = array();
954            $leads = isset($this->_packageInfo['lead'][0]) ? $this->_packageInfo['lead'] :
955                array($this->_packageInfo['lead']);
956            foreach ($leads as $lead) {
957                $s = $lead;
958                $s['handle'] = $s['user'];
959                unset($s['user']);
960                $s['role'] = 'lead';
961                $ret[] = $s;
962            }
963            if (isset($this->_packageInfo['developer'])) {
964                $leads = isset($this->_packageInfo['developer'][0]) ?
965                    $this->_packageInfo['developer'] :
966                    array($this->_packageInfo['developer']);
967                foreach ($leads as $maintainer) {
968                    $s = $maintainer;
969                    $s['handle'] = $s['user'];
970                    unset($s['user']);
971                    $s['role'] = 'developer';
972                    $ret[] = $s;
973                }
974            }
975            if (isset($this->_packageInfo['contributor'])) {
976                $leads = isset($this->_packageInfo['contributor'][0]) ?
977                    $this->_packageInfo['contributor'] :
978                    array($this->_packageInfo['contributor']);
979                foreach ($leads as $maintainer) {
980                    $s = $maintainer;
981                    $s['handle'] = $s['user'];
982                    unset($s['user']);
983                    $s['role'] = 'contributor';
984                    $ret[] = $s;
985                }
986            }
987            if (isset($this->_packageInfo['helper'])) {
988                $leads = isset($this->_packageInfo['helper'][0]) ?
989                    $this->_packageInfo['helper'] :
990                    array($this->_packageInfo['helper']);
991                foreach ($leads as $maintainer) {
992                    $s = $maintainer;
993                    $s['handle'] = $s['user'];
994                    unset($s['user']);
995                    $s['role'] = 'helper';
996                    $ret[] = $s;
997                }
998            }
999            return $ret;
1000        }
1001        return false;
1002    }
1003
1004    function getLeads()
1005    {
1006        if (isset($this->_packageInfo['lead'])) {
1007            return $this->_packageInfo['lead'];
1008        }
1009        return false;
1010    }
1011
1012    function getDevelopers()
1013    {
1014        if (isset($this->_packageInfo['developer'])) {
1015            return $this->_packageInfo['developer'];
1016        }
1017        return false;
1018    }
1019
1020    function getContributors()
1021    {
1022        if (isset($this->_packageInfo['contributor'])) {
1023            return $this->_packageInfo['contributor'];
1024        }
1025        return false;
1026    }
1027
1028    function getHelpers()
1029    {
1030        if (isset($this->_packageInfo['helper'])) {
1031            return $this->_packageInfo['helper'];
1032        }
1033        return false;
1034    }
1035
1036    function setDate($date)
1037    {
1038        if (!isset($this->_packageInfo['date'])) {
1039            // ensure that the extends tag is set up in the right location
1040            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
1041                array('time', 'version',
1042                    'stability', 'license', 'notes', 'contents', 'compatible',
1043                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
1044                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
1045                    'zendextbinrelease', 'bundle', 'changelog'), array(), 'date');
1046        }
1047        $this->_packageInfo['date'] = $date;
1048        $this->_isValid = 0;
1049    }
1050
1051    function setTime($time)
1052    {
1053        $this->_isValid = 0;
1054        if (!isset($this->_packageInfo['time'])) {
1055            // ensure that the time tag is set up in the right location
1056            $this->_packageInfo = $this->_insertBefore($this->_packageInfo,
1057                    array('version',
1058                    'stability', 'license', 'notes', 'contents', 'compatible',
1059                    'dependencies', 'providesextension', 'srcpackage', 'srcuri',
1060                    'phprelease', 'extsrcrelease', 'extbinrelease', 'zendextsrcrelease',
1061                    'zendextbinrelease', 'bundle', 'changelog'), $time, 'time');
1062        }
1063        $this->_packageInfo['time'] = $time;
1064    }
1065
1066    function getDate()
1067    {
1068        if (isset($this->_packageInfo['date'])) {
1069            return $this->_packageInfo['date'];
1070        }
1071        return false;
1072    }
1073
1074    function getTime()
1075    {
1076        if (isset($this->_packageInfo['time'])) {
1077            return $this->_packageInfo['time'];
1078        }
1079        return false;
1080    }
1081
1082    /**
1083     * @param package|api version category to return
1084     */
1085    function getVersion($key = 'release')
1086    {
1087        if (isset($this->_packageInfo['version'][$key])) {
1088            return $this->_packageInfo['version'][$key];
1089        }
1090        return false;
1091    }
1092
1093    function getStability()
1094    {
1095        if (isset($this->_packageInfo['stability'])) {
1096            return $this->_packageInfo['stability'];
1097        }
1098        return false;
1099    }
1100
1101    function getState($key = 'release')
1102    {
1103        if (isset($this->_packageInfo['stability'][$key])) {
1104            return $this->_packageInfo['stability'][$key];
1105        }
1106        return false;
1107    }
1108
1109    function getLicense($raw = false)
1110    {
1111        if (isset($this->_packageInfo['license'])) {
1112            if ($raw) {
1113                return $this->_packageInfo['license'];
1114            }
1115            if (is_array($this->_packageInfo['license'])) {
1116                return $this->_packageInfo['license']['_content'];
1117            } else {
1118                return $this->_packageInfo['license'];
1119            }
1120        }
1121        return false;
1122    }
1123
1124    function getLicenseLocation()
1125    {
1126        if (!isset($this->_packageInfo['license']) || !is_array($this->_packageInfo['license'])) {
1127            return false;
1128        }
1129        return $this->_packageInfo['license']['attribs'];
1130    }
1131
1132    function getNotes()
1133    {
1134        if (isset($this->_packageInfo['notes'])) {
1135            return $this->_packageInfo['notes'];
1136        }
1137        return false;
1138    }
1139
1140    /**
1141     * Return the <usesrole> tag contents, if any
1142     * @return array|false
1143     */
1144    function getUsesrole()
1145    {
1146        if (isset($this->_packageInfo['usesrole'])) {
1147            return $this->_packageInfo['usesrole'];
1148        }
1149        return false;
1150    }
1151
1152    /**
1153     * Return the <usestask> tag contents, if any
1154     * @return array|false
1155     */
1156    function getUsestask()
1157    {
1158        if (isset($this->_packageInfo['usestask'])) {
1159            return $this->_packageInfo['usestask'];
1160        }
1161        return false;
1162    }
1163
1164    /**
1165     * This should only be used to retrieve filenames and install attributes
1166     */
1167    function getFilelist($preserve = false)
1168    {
1169        if (isset($this->_packageInfo['filelist']) && !$preserve) {
1170            return $this->_packageInfo['filelist'];
1171        }
1172        $this->flattenFilelist();
1173        if ($contents = $this->getContents()) {
1174            $ret = array();
1175            if (!isset($contents['dir'])) {
1176                return false;
1177            }
1178            if (!isset($contents['dir']['file'][0])) {
1179                $contents['dir']['file'] = array($contents['dir']['file']);
1180            }
1181            foreach ($contents['dir']['file'] as $file) {
1182                $name = $file['attribs']['name'];
1183                if (!$preserve) {
1184                    $file = $file['attribs'];
1185                }
1186                $ret[$name] = $file;
1187            }
1188            if (!$preserve) {
1189                $this->_packageInfo['filelist'] = $ret;
1190            }
1191            return $ret;
1192        }
1193        return false;
1194    }
1195
1196    /**
1197     * Return configure options array, if any
1198     *
1199     * @return array|false
1200     */
1201    function getConfigureOptions()
1202    {
1203        if ($this->getPackageType() != 'extsrc' && $this->getPackageType() != 'zendextsrc') {
1204            return false;
1205        }
1206
1207        $releases = $this->getReleases();
1208        if (isset($releases[0])) {
1209            $releases = $releases[0];
1210        }
1211
1212        if (isset($releases['configureoption'])) {
1213            if (!isset($releases['configureoption'][0])) {
1214                $releases['configureoption'] = array($releases['configureoption']);
1215            }
1216
1217            $releases_count = count($releases['configureoption']);
1218            for ($i = 0; $i < $releases_count; ++$i) {
1219                $releases['configureoption'][$i] = $releases['configureoption'][$i]['attribs'];
1220            }
1221
1222            return $releases['configureoption'];
1223        }
1224
1225        return false;
1226    }
1227
1228    /**
1229     * This is only used at install-time, after all serialization
1230     * is over.
1231     */
1232    function resetFilelist()
1233    {
1234        $this->_packageInfo['filelist'] = array();
1235    }
1236
1237    /**
1238     * Retrieve a list of files that should be installed on this computer
1239     * @return array
1240     */
1241    function getInstallationFilelist($forfilecheck = false)
1242    {
1243        $contents = $this->getFilelist(true);
1244        if (isset($contents['dir']['attribs']['baseinstalldir'])) {
1245            $base = $contents['dir']['attribs']['baseinstalldir'];
1246        }
1247        if (isset($this->_packageInfo['bundle'])) {
1248            return PEAR::raiseError(
1249                'Exception: bundles should be handled in download code only');
1250        }
1251        $release = $this->getReleases();
1252        if ($release) {
1253            if (!isset($release[0])) {
1254                if (!isset($release['installconditions']) && !isset($release['filelist'])) {
1255                    if ($forfilecheck) {
1256                        return $this->getFilelist();
1257                    }
1258                    return $contents;
1259                }
1260                $release = array($release);
1261            }
1262            $depchecker = &$this->getPEARDependency2($this->_config, array(),
1263                array('channel' => $this->getChannel(), 'package' => $this->getPackage()),
1264                PEAR_VALIDATE_INSTALLING);
1265            foreach ($release as $instance) {
1266                if (isset($instance['installconditions'])) {
1267                    $installconditions = $instance['installconditions'];
1268                    if (is_array($installconditions)) {
1269                        PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1270                        foreach ($installconditions as $type => $conditions) {
1271                            if (!isset($conditions[0])) {
1272                                $conditions = array($conditions);
1273                            }
1274                            foreach ($conditions as $condition) {
1275                                $ret = $depchecker->{"validate{$type}Dependency"}($condition);
1276                                if (PEAR::isError($ret)) {
1277                                    PEAR::popErrorHandling();
1278                                    continue 3; // skip this release
1279                                }
1280                            }
1281                        }
1282                        PEAR::popErrorHandling();
1283                    }
1284                }
1285                // this is the release to use
1286                if (isset($instance['filelist'])) {
1287                    // ignore files
1288                    if (isset($instance['filelist']['ignore'])) {
1289                        $ignore = isset($instance['filelist']['ignore'][0]) ?
1290                            $instance['filelist']['ignore'] :
1291                            array($instance['filelist']['ignore']);
1292                        foreach ($ignore as $ig) {
1293                            unset ($contents[$ig['attribs']['name']]);
1294                        }
1295                    }
1296                    // install files as this name
1297                    if (isset($instance['filelist']['install'])) {
1298                        $installas = isset($instance['filelist']['install'][0]) ?
1299                            $instance['filelist']['install'] :
1300                            array($instance['filelist']['install']);
1301                        foreach ($installas as $as) {
1302                            $contents[$as['attribs']['name']]['attribs']['install-as'] =
1303                                $as['attribs']['as'];
1304                        }
1305                    }
1306                }
1307                if ($forfilecheck) {
1308                    foreach ($contents as $file => $attrs) {
1309                        $contents[$file] = $attrs['attribs'];
1310                    }
1311                }
1312                return $contents;
1313            }
1314        } else { // simple release - no installconditions or install-as
1315            if ($forfilecheck) {
1316                return $this->getFilelist();
1317            }
1318            return $contents;
1319        }
1320        // no releases matched
1321        return PEAR::raiseError('No releases in package.xml matched the existing operating ' .
1322            'system, extensions installed, or architecture, cannot install');
1323    }
1324
1325    /**
1326     * This is only used at install-time, after all serialization
1327     * is over.
1328     * @param string file name
1329     * @param string installed path
1330     */
1331    function setInstalledAs($file, $path)
1332    {
1333        if ($path) {
1334            return $this->_packageInfo['filelist'][$file]['installed_as'] = $path;
1335        }
1336        unset($this->_packageInfo['filelist'][$file]['installed_as']);
1337    }
1338
1339    function getInstalledLocation($file)
1340    {
1341        if (isset($this->_packageInfo['filelist'][$file]['installed_as'])) {
1342            return $this->_packageInfo['filelist'][$file]['installed_as'];
1343        }
1344        return false;
1345    }
1346
1347    /**
1348     * This is only used at install-time, after all serialization
1349     * is over.
1350     */
1351    function installedFile($file, $atts)
1352    {
1353        if (isset($this->_packageInfo['filelist'][$file])) {
1354            $this->_packageInfo['filelist'][$file] =
1355                array_merge($this->_packageInfo['filelist'][$file], $atts['attribs']);
1356        } else {
1357            $this->_packageInfo['filelist'][$file] = $atts['attribs'];
1358        }
1359    }
1360
1361    /**
1362     * Retrieve the contents tag
1363     */
1364    function getContents()
1365    {
1366        if (isset($this->_packageInfo['contents'])) {
1367            return $this->_packageInfo['contents'];
1368        }
1369        return false;
1370    }
1371
1372    /**
1373     * @param string full path to file
1374     * @param string attribute name
1375     * @param string attribute value
1376     * @param int risky but fast - use this to choose a file based on its position in the list
1377     *            of files.  Index is zero-based like PHP arrays.
1378     * @return bool success of operation
1379     */
1380    function setFileAttribute($filename, $attr, $value, $index = false)
1381    {
1382        $this->_isValid = 0;
1383        if (in_array($attr, array('role', 'name', 'baseinstalldir'))) {
1384            $this->_filesValid = false;
1385        }
1386        if ($index !== false &&
1387              isset($this->_packageInfo['contents']['dir']['file'][$index]['attribs'])) {
1388            $this->_packageInfo['contents']['dir']['file'][$index]['attribs'][$attr] = $value;
1389            return true;
1390        }
1391        if (!isset($this->_packageInfo['contents']['dir']['file'])) {
1392            return false;
1393        }
1394        $files = $this->_packageInfo['contents']['dir']['file'];
1395        if (!isset($files[0])) {
1396            $files = array($files);
1397            $ind = false;
1398        } else {
1399            $ind = true;
1400        }
1401        foreach ($files as $i => $file) {
1402            if (isset($file['attribs'])) {
1403                if ($file['attribs']['name'] == $filename) {
1404                    if ($ind) {
1405                        $this->_packageInfo['contents']['dir']['file'][$i]['attribs'][$attr] = $value;
1406                    } else {
1407                        $this->_packageInfo['contents']['dir']['file']['attribs'][$attr] = $value;
1408                    }
1409                    return true;
1410                }
1411            }
1412        }
1413        return false;
1414    }
1415
1416    function setDirtree($path)
1417    {
1418        if (!isset($this->_packageInfo['dirtree'])) {
1419            $this->_packageInfo['dirtree'] = array();
1420        }
1421        $this->_packageInfo['dirtree'][$path] = true;
1422    }
1423
1424    function getDirtree()
1425    {
1426        if (isset($this->_packageInfo['dirtree']) && count($this->_packageInfo['dirtree'])) {
1427            return $this->_packageInfo['dirtree'];
1428        }
1429        return false;
1430    }
1431
1432    function resetDirtree()
1433    {
1434        unset($this->_packageInfo['dirtree']);
1435    }
1436
1437    /**
1438     * Determines whether this package claims it is compatible with the version of
1439     * the package that has a recommended version dependency
1440     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v1|PEAR_Downloader_Package
1441     * @return boolean
1442     */
1443    function isCompatible($pf)
1444    {
1445        if (!isset($this->_packageInfo['compatible'])) {
1446            return false;
1447        }
1448        if (!isset($this->_packageInfo['channel'])) {
1449            return false;
1450        }
1451        $me = $pf->getVersion();
1452        $compatible = $this->_packageInfo['compatible'];
1453        if (!isset($compatible[0])) {
1454            $compatible = array($compatible);
1455        }
1456        $found = false;
1457        foreach ($compatible as $info) {
1458            if (strtolower($info['name']) == strtolower($pf->getPackage())) {
1459                if (strtolower($info['channel']) == strtolower($pf->getChannel())) {
1460                    $found = true;
1461                    break;
1462                }
1463            }
1464        }
1465        if (!$found) {
1466            return false;
1467        }
1468        if (isset($info['exclude'])) {
1469            if (!isset($info['exclude'][0])) {
1470                $info['exclude'] = array($info['exclude']);
1471            }
1472            foreach ($info['exclude'] as $exclude) {
1473                if (version_compare($me, $exclude, '==')) {
1474                    return false;
1475                }
1476            }
1477        }
1478        if (version_compare($me, $info['min'], '>=') && version_compare($me, $info['max'], '<=')) {
1479            return true;
1480        }
1481        return false;
1482    }
1483
1484    /**
1485     * @return array|false
1486     */
1487    function getCompatible()
1488    {
1489        if (isset($this->_packageInfo['compatible'])) {
1490            return $this->_packageInfo['compatible'];
1491        }
1492        return false;
1493    }
1494
1495    function getDependencies()
1496    {
1497        if (isset($this->_packageInfo['dependencies'])) {
1498            return $this->_packageInfo['dependencies'];
1499        }
1500        return false;
1501    }
1502
1503    function isSubpackageOf($p)
1504    {
1505        return $p->isSubpackage($this);
1506    }
1507
1508    /**
1509     * Determines whether the passed in package is a subpackage of this package.
1510     *
1511     * No version checking is done, only name verification.
1512     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
1513     * @return bool
1514     */
1515    function isSubpackage($p)
1516    {
1517        $sub = array();
1518        if (isset($this->_packageInfo['dependencies']['required']['subpackage'])) {
1519            $sub = $this->_packageInfo['dependencies']['required']['subpackage'];
1520            if (!isset($sub[0])) {
1521                $sub = array($sub);
1522            }
1523        }
1524        if (isset($this->_packageInfo['dependencies']['optional']['subpackage'])) {
1525            $sub1 = $this->_packageInfo['dependencies']['optional']['subpackage'];
1526            if (!isset($sub1[0])) {
1527                $sub1 = array($sub1);
1528            }
1529            $sub = array_merge($sub, $sub1);
1530        }
1531        if (isset($this->_packageInfo['dependencies']['group'])) {
1532            $group = $this->_packageInfo['dependencies']['group'];
1533            if (!isset($group[0])) {
1534                $group = array($group);
1535            }
1536            foreach ($group as $deps) {
1537                if (isset($deps['subpackage'])) {
1538                    $sub2 = $deps['subpackage'];
1539                    if (!isset($sub2[0])) {
1540                        $sub2 = array($sub2);
1541                    }
1542                    $sub = array_merge($sub, $sub2);
1543                }
1544            }
1545        }
1546        foreach ($sub as $dep) {
1547            if (strtolower($dep['name']) == strtolower($p->getPackage())) {
1548                if (isset($dep['channel'])) {
1549                    if (strtolower($dep['channel']) == strtolower($p->getChannel())) {
1550                        return true;
1551                    }
1552                } else {
1553                    if ($dep['uri'] == $p->getURI()) {
1554                        return true;
1555                    }
1556                }
1557            }
1558        }
1559        return false;
1560    }
1561
1562    function dependsOn($package, $channel)
1563    {
1564        if (!($deps = $this->getDependencies())) {
1565            return false;
1566        }
1567        foreach (array('package', 'subpackage') as $type) {
1568            foreach (array('required', 'optional') as $needed) {
1569                if (isset($deps[$needed][$type])) {
1570                    if (!isset($deps[$needed][$type][0])) {
1571                        $deps[$needed][$type] = array($deps[$needed][$type]);
1572                    }
1573                    foreach ($deps[$needed][$type] as $dep) {
1574                        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
1575                        if (strtolower($dep['name']) == strtolower($package) &&
1576                              $depchannel == $channel) {
1577                            return true;
1578                        }
1579                    }
1580                }
1581            }
1582            if (isset($deps['group'])) {
1583                if (!isset($deps['group'][0])) {
1584                    $dep['group'] = array($deps['group']);
1585                }
1586                foreach ($deps['group'] as $group) {
1587                    if (isset($group[$type])) {
1588                        if (!is_array($group[$type])) {
1589                            $group[$type] = array($group[$type]);
1590                        }
1591                        foreach ($group[$type] as $dep) {
1592                            $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
1593                            if (strtolower($dep['name']) == strtolower($package) &&
1594                                  $depchannel == $channel) {
1595                                return true;
1596                            }
1597                        }
1598                    }
1599                }
1600            }
1601        }
1602        return false;
1603    }
1604
1605    /**
1606     * Get the contents of a dependency group
1607     * @param string
1608     * @return array|false
1609     */
1610    function getDependencyGroup($name)
1611    {
1612        $name = strtolower($name);
1613        if (!isset($this->_packageInfo['dependencies']['group'])) {
1614            return false;
1615        }
1616        $groups = $this->_packageInfo['dependencies']['group'];
1617        if (!isset($groups[0])) {
1618            $groups = array($groups);
1619        }
1620        foreach ($groups as $group) {
1621            if (strtolower($group['attribs']['name']) == $name) {
1622                return $group;
1623            }
1624        }
1625        return false;
1626    }
1627
1628    /**
1629     * Retrieve a partial package.xml 1.0 representation of dependencies
1630     *
1631     * a very limited representation of dependencies is returned by this method.
1632     * The <exclude> tag for excluding certain versions of a dependency is
1633     * completely ignored.  In addition, dependency groups are ignored, with the
1634     * assumption that all dependencies in dependency groups are also listed in
1635     * the optional group that work with all dependency groups
1636     * @param boolean return package.xml 2.0 <dependencies> tag
1637     * @return array|false
1638     */
1639    function getDeps($raw = false, $nopearinstaller = false)
1640    {
1641        if (isset($this->_packageInfo['dependencies'])) {
1642            if ($raw) {
1643                return $this->_packageInfo['dependencies'];
1644            }
1645            $ret = array();
1646            $map = array(
1647                'php' => 'php',
1648                'package' => 'pkg',
1649                'subpackage' => 'pkg',
1650                'extension' => 'ext',
1651                'os' => 'os',
1652                'pearinstaller' => 'pkg',
1653                );
1654            foreach (array('required', 'optional') as $type) {
1655                $optional = ($type == 'optional') ? 'yes' : 'no';
1656                if (!isset($this->_packageInfo['dependencies'][$type])
1657                    || empty($this->_packageInfo['dependencies'][$type])) {
1658                    continue;
1659                }
1660                foreach ($this->_packageInfo['dependencies'][$type] as $dtype => $deps) {
1661                    if ($dtype == 'pearinstaller' && $nopearinstaller) {
1662                        continue;
1663                    }
1664                    if (!isset($deps[0])) {
1665                        $deps = array($deps);
1666                    }
1667                    foreach ($deps as $dep) {
1668                        if (!isset($map[$dtype])) {
1669                            // no support for arch type
1670                            continue;
1671                        }
1672                        if ($dtype == 'pearinstaller') {
1673                            $dep['name'] = 'PEAR';
1674                            $dep['channel'] = 'pear.php.net';
1675                        }
1676                        $s = array('type' => $map[$dtype]);
1677                        if (isset($dep['channel'])) {
1678                            $s['channel'] = $dep['channel'];
1679                        }
1680                        if (isset($dep['uri'])) {
1681                            $s['uri'] = $dep['uri'];
1682                        }
1683                        if (isset($dep['name'])) {
1684                            $s['name'] = $dep['name'];
1685                        }
1686                        if (isset($dep['conflicts'])) {
1687                            $s['rel'] = 'not';
1688                        } else {
1689                            if (!isset($dep['min']) &&
1690                                  !isset($dep['max'])) {
1691                                $s['rel'] = 'has';
1692                                $s['optional'] = $optional;
1693                            } elseif (isset($dep['min']) &&
1694                                  isset($dep['max'])) {
1695                                $s['rel'] = 'ge';
1696                                $s1 = $s;
1697                                $s1['rel'] = 'le';
1698                                $s['version'] = $dep['min'];
1699                                $s1['version'] = $dep['max'];
1700                                if (isset($dep['channel'])) {
1701                                    $s1['channel'] = $dep['channel'];
1702                                }
1703                                if ($dtype != 'php') {
1704                                    $s['name'] = $dep['name'];
1705                                    $s1['name'] = $dep['name'];
1706                                }
1707                                $s['optional'] = $optional;
1708                                $s1['optional'] = $optional;
1709                                $ret[] = $s1;
1710                            } elseif (isset($dep['min'])) {
1711                                if (isset($dep['exclude']) &&
1712                                      $dep['exclude'] == $dep['min']) {
1713                                    $s['rel'] = 'gt';
1714                                } else {
1715                                    $s['rel'] = 'ge';
1716                                }
1717                                $s['version'] = $dep['min'];
1718                                $s['optional'] = $optional;
1719                                if ($dtype != 'php') {
1720                                    $s['name'] = $dep['name'];
1721                                }
1722                            } elseif (isset($dep['max'])) {
1723                                if (isset($dep['exclude']) &&
1724                                      $dep['exclude'] == $dep['max']) {
1725                                    $s['rel'] = 'lt';
1726                                } else {
1727                                    $s['rel'] = 'le';
1728                                }
1729                                $s['version'] = $dep['max'];
1730                                $s['optional'] = $optional;
1731                                if ($dtype != 'php') {
1732                                    $s['name'] = $dep['name'];
1733                                }
1734                            }
1735                        }
1736                        $ret[] = $s;
1737                    }
1738                }
1739            }
1740            if (count($ret)) {
1741                return $ret;
1742            }
1743        }
1744        return false;
1745    }
1746
1747    /**
1748     * @return php|extsrc|extbin|zendextsrc|zendextbin|bundle|false
1749     */
1750    function getPackageType()
1751    {
1752        if (isset($this->_packageInfo['phprelease'])) {
1753            return 'php';
1754        }
1755        if (isset($this->_packageInfo['extsrcrelease'])) {
1756            return 'extsrc';
1757        }
1758        if (isset($this->_packageInfo['extbinrelease'])) {
1759            return 'extbin';
1760        }
1761        if (isset($this->_packageInfo['zendextsrcrelease'])) {
1762            return 'zendextsrc';
1763        }
1764        if (isset($this->_packageInfo['zendextbinrelease'])) {
1765            return 'zendextbin';
1766        }
1767        if (isset($this->_packageInfo['bundle'])) {
1768            return 'bundle';
1769        }
1770        return false;
1771    }
1772
1773    /**
1774     * @return array|false
1775     */
1776    function getReleases()
1777    {
1778        $type = $this->getPackageType();
1779        if ($type != 'bundle') {
1780            $type .= 'release';
1781        }
1782        if ($this->getPackageType() && isset($this->_packageInfo[$type])) {
1783            return $this->_packageInfo[$type];
1784        }
1785        return false;
1786    }
1787
1788    /**
1789     * @return array
1790     */
1791    function getChangelog()
1792    {
1793        if (isset($this->_packageInfo['changelog'])) {
1794            return $this->_packageInfo['changelog'];
1795        }
1796        return false;
1797    }
1798
1799    function hasDeps()
1800    {
1801        return isset($this->_packageInfo['dependencies']);
1802    }
1803
1804    function getPackagexmlVersion()
1805    {
1806        if (isset($this->_packageInfo['zendextsrcrelease'])) {
1807            return '2.1';
1808        }
1809        if (isset($this->_packageInfo['zendextbinrelease'])) {
1810            return '2.1';
1811        }
1812        return '2.0';
1813    }
1814
1815    /**
1816     * @return array|false
1817     */
1818    function getSourcePackage()
1819    {
1820        if (isset($this->_packageInfo['extbinrelease']) ||
1821              isset($this->_packageInfo['zendextbinrelease'])) {
1822            return array('channel' => $this->_packageInfo['srcchannel'],
1823                         'package' => $this->_packageInfo['srcpackage']);
1824        }
1825        return false;
1826    }
1827
1828    function getBundledPackages()
1829    {
1830        if (isset($this->_packageInfo['bundle'])) {
1831            return $this->_packageInfo['contents']['bundledpackage'];
1832        }
1833        return false;
1834    }
1835
1836    function getLastModified()
1837    {
1838        if (isset($this->_packageInfo['_lastmodified'])) {
1839            return $this->_packageInfo['_lastmodified'];
1840        }
1841        return false;
1842    }
1843
1844    /**
1845     * Get the contents of a file listed within the package.xml
1846     * @param string
1847     * @return string
1848     */
1849    function getFileContents($file)
1850    {
1851        if ($this->_archiveFile == $this->_packageFile) { // unpacked
1852            $dir = dirname($this->_packageFile);
1853            $file = $dir . DIRECTORY_SEPARATOR . $file;
1854            $file = str_replace(array('/', '\\'),
1855                array(DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR), $file);
1856            if (file_exists($file) && is_readable($file)) {
1857                return implode('', file($file));
1858            }
1859        } else { // tgz
1860            $tar = &new Archive_Tar($this->_archiveFile);
1861            $tar->pushErrorHandling(PEAR_ERROR_RETURN);
1862            if ($file != 'package.xml' && $file != 'package2.xml') {
1863                $file = $this->getPackage() . '-' . $this->getVersion() . '/' . $file;
1864            }
1865            $file = $tar->extractInString($file);
1866            $tar->popErrorHandling();
1867            if (PEAR::isError($file)) {
1868                return PEAR::raiseError("Cannot locate file '$file' in archive");
1869            }
1870            return $file;
1871        }
1872    }
1873
1874    function &getRW()
1875    {
1876        if (!class_exists('PEAR_PackageFile_v2_rw')) {
1877            require_once 'PEAR/PackageFile/v2/rw.php';
1878        }
1879        $a = new PEAR_PackageFile_v2_rw;
1880        foreach (get_object_vars($this) as $name => $unused) {
1881            if (!isset($this->$name)) {
1882                continue;
1883            }
1884            if ($name == '_config' || $name == '_logger'|| $name == '_registry' ||
1885                  $name == '_stack') {
1886                $a->$name = &$this->$name;
1887            } else {
1888                $a->$name = $this->$name;
1889            }
1890        }
1891        return $a;
1892    }
1893
1894    function &getDefaultGenerator()
1895    {
1896        if (!class_exists('PEAR_PackageFile_Generator_v2')) {
1897            require_once 'PEAR/PackageFile/Generator/v2.php';
1898        }
1899        $a = &new PEAR_PackageFile_Generator_v2($this);
1900        return $a;
1901    }
1902
1903    function analyzeSourceCode($file, $string = false)
1904    {
1905        if (!isset($this->_v2Validator) ||
1906              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
1907            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
1908                require_once 'PEAR/PackageFile/v2/Validator.php';
1909            }
1910            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1911        }
1912        return $this->_v2Validator->analyzeSourceCode($file, $string);
1913    }
1914
1915    function validate($state = PEAR_VALIDATE_NORMAL)
1916    {
1917        if (!isset($this->_packageInfo) || !is_array($this->_packageInfo)) {
1918            return false;
1919        }
1920        if (!isset($this->_v2Validator) ||
1921              !is_a($this->_v2Validator, 'PEAR_PackageFile_v2_Validator')) {
1922            if (!class_exists('PEAR_PackageFile_v2_Validator')) {
1923                require_once 'PEAR/PackageFile/v2/Validator.php';
1924            }
1925            $this->_v2Validator = new PEAR_PackageFile_v2_Validator;
1926        }
1927        if (isset($this->_packageInfo['xsdversion'])) {
1928            unset($this->_packageInfo['xsdversion']);
1929        }
1930        return $this->_v2Validator->validate($this, $state);
1931    }
1932
1933    function getTasksNs()
1934    {
1935        if (!isset($this->_tasksNs)) {
1936            if (isset($this->_packageInfo['attribs'])) {
1937                foreach ($this->_packageInfo['attribs'] as $name => $value) {
1938                    if ($value == 'http://pear.php.net/dtd/tasks-1.0') {
1939                        $this->_tasksNs = str_replace('xmlns:', '', $name);
1940                        break;
1941                    }
1942                }
1943            }
1944        }
1945        return $this->_tasksNs;
1946    }
1947
1948    /**
1949     * Determine whether a task name is a valid task.  Custom tasks may be defined
1950     * using subdirectories by putting a "-" in the name, as in <tasks:mycustom-task>
1951     *
1952     * Note that this method will auto-load the task class file and test for the existence
1953     * of the name with "-" replaced by "_" as in PEAR/Task/mycustom/task.php makes class
1954     * PEAR_Task_mycustom_task
1955     * @param string
1956     * @return boolean
1957     */
1958    function getTask($task)
1959    {
1960        $this->getTasksNs();
1961        // transform all '-' to '/' and 'tasks:' to '' so tasks:replace becomes replace
1962        $task = str_replace(array($this->_tasksNs . ':', '-'), array('', ' '), $task);
1963        $taskfile = str_replace(' ', '/', ucwords($task));
1964        $task = str_replace(array(' ', '/'), '_', ucwords($task));
1965        if (class_exists("PEAR_Task_$task")) {
1966            return "PEAR_Task_$task";
1967        }
1968        $fp = @fopen("PEAR/Task/$taskfile.php", 'r', true);
1969        if ($fp) {
1970            fclose($fp);
1971            require_once "PEAR/Task/$taskfile.php";
1972            return "PEAR_Task_$task";
1973        }
1974        return false;
1975    }
1976
1977    /**
1978     * Key-friendly array_splice
1979     * @param tagname to splice a value in before
1980     * @param mixed the value to splice in
1981     * @param string the new tag name
1982     */
1983    function _ksplice($array, $key, $value, $newkey)
1984    {
1985        $offset = array_search($key, array_keys($array));
1986        $after = array_slice($array, $offset);
1987        $before = array_slice($array, 0, $offset);
1988        $before[$newkey] = $value;
1989        return array_merge($before, $after);
1990    }
1991
1992    /**
1993     * @param array a list of possible keys, in the order they may occur
1994     * @param mixed contents of the new package.xml tag
1995     * @param string tag name
1996     * @access private
1997     */
1998    function _insertBefore($array, $keys, $contents, $newkey)
1999    {
2000        foreach ($keys as $key) {
2001            if (isset($array[$key])) {
2002                return $array = $this->_ksplice($array, $key, $contents, $newkey);
2003            }
2004        }
2005        $array[$newkey] = $contents;
2006        return $array;
2007    }
2008
2009    /**
2010     * @param subsection of {@link $_packageInfo}
2011     * @param array|string tag contents
2012     * @param array format:
2013     * <pre>
2014     * array(
2015     *   tagname => array(list of tag names that follow this one),
2016     *   childtagname => array(list of child tag names that follow this one),
2017     * )
2018     * </pre>
2019     *
2020     * This allows construction of nested tags
2021     * @access private
2022     */
2023    function _mergeTag($manip, $contents, $order)
2024    {
2025        if (count($order)) {
2026            foreach ($order as $tag => $curorder) {
2027                if (!isset($manip[$tag])) {
2028                    // ensure that the tag is set up
2029                    $manip = $this->_insertBefore($manip, $curorder, array(), $tag);
2030                }
2031                if (count($order) > 1) {
2032                    $manip[$tag] = $this->_mergeTag($manip[$tag], $contents, array_slice($order, 1));
2033                    return $manip;
2034                }
2035            }
2036        } else {
2037            return $manip;
2038        }
2039        if (is_array($manip[$tag]) && !empty($manip[$tag]) && isset($manip[$tag][0])) {
2040            $manip[$tag][] = $contents;
2041        } else {
2042            if (!count($manip[$tag])) {
2043                $manip[$tag] = $contents;
2044            } else {
2045                $manip[$tag] = array($manip[$tag]);
2046                $manip[$tag][] = $contents;
2047            }
2048        }
2049        return $manip;
2050    }
2051}
2052?>
Note: See TracBrowser for help on using the repository browser.