source: branches/version-2_13_0/data/module/PEAR/Dependency2.php @ 23125

Revision 23125, 49.3 KB checked in by kimoto, 11 years ago (diff)

#2275 PEAR更新
不要なrequire_onceの削除
レガシーなPEARモジュールは使わない
SearchReplace?.phpのパスが間違っているので修正

Line 
1<?php
2/**
3 * PEAR_Dependency2, advanced dependency validation
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: Dependency2.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/**
18 * Required for the PEAR_VALIDATE_* constants
19 */
20require_once 'PEAR/Validate.php';
21
22/**
23 * Dependency check for PEAR packages
24 *
25 * This class handles both version 1.0 and 2.0 dependencies
26 * WARNING: *any* changes to this class must be duplicated in the
27 * test_PEAR_Dependency2 class found in tests/PEAR_Dependency2/setup.php.inc,
28 * or unit tests will not actually validate the changes
29 * @category   pear
30 * @package    PEAR
31 * @author     Greg Beaver <cellog@php.net>
32 * @copyright  1997-2009 The Authors
33 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
34 * @version    Release: 1.9.4
35 * @link       http://pear.php.net/package/PEAR
36 * @since      Class available since Release 1.4.0a1
37 */
38class PEAR_Dependency2
39{
40    /**
41     * One of the PEAR_VALIDATE_* states
42     * @see PEAR_VALIDATE_NORMAL
43     * @var integer
44     */
45    var $_state;
46
47    /**
48     * Command-line options to install/upgrade/uninstall commands
49     * @param array
50     */
51    var $_options;
52
53    /**
54     * @var OS_Guess
55     */
56    var $_os;
57
58    /**
59     * @var PEAR_Registry
60     */
61    var $_registry;
62
63    /**
64     * @var PEAR_Config
65     */
66    var $_config;
67
68    /**
69     * @var PEAR_DependencyDB
70     */
71    var $_dependencydb;
72
73    /**
74     * Output of PEAR_Registry::parsedPackageName()
75     * @var array
76     */
77    var $_currentPackage;
78
79    /**
80     * @param PEAR_Config
81     * @param array installation options
82     * @param array format of PEAR_Registry::parsedPackageName()
83     * @param int installation state (one of PEAR_VALIDATE_*)
84     */
85    function PEAR_Dependency2(&$config, $installoptions, $package,
86                              $state = PEAR_VALIDATE_INSTALLING)
87    {
88        $this->_config = &$config;
89        if (!class_exists('PEAR_DependencyDB')) {
90            require_once 'PEAR/DependencyDB.php';
91        }
92
93        if (isset($installoptions['packagingroot'])) {
94            // make sure depdb is in the right location
95            $config->setInstallRoot($installoptions['packagingroot']);
96        }
97
98        $this->_registry = &$config->getRegistry();
99        $this->_dependencydb = &PEAR_DependencyDB::singleton($config);
100        if (isset($installoptions['packagingroot'])) {
101            $config->setInstallRoot(false);
102        }
103
104        $this->_options = $installoptions;
105        $this->_state = $state;
106        if (!class_exists('OS_Guess')) {
107            require_once 'OS/Guess.php';
108        }
109
110        $this->_os = new OS_Guess;
111        $this->_currentPackage = $package;
112    }
113
114    function _getExtraString($dep)
115    {
116        $extra = ' (';
117        if (isset($dep['uri'])) {
118            return '';
119        }
120
121        if (isset($dep['recommended'])) {
122            $extra .= 'recommended version ' . $dep['recommended'];
123        } else {
124            if (isset($dep['min'])) {
125                $extra .= 'version >= ' . $dep['min'];
126            }
127
128            if (isset($dep['max'])) {
129                if ($extra != ' (') {
130                    $extra .= ', ';
131                }
132                $extra .= 'version <= ' . $dep['max'];
133            }
134
135            if (isset($dep['exclude'])) {
136                if (!is_array($dep['exclude'])) {
137                    $dep['exclude'] = array($dep['exclude']);
138                }
139
140                if ($extra != ' (') {
141                    $extra .= ', ';
142                }
143
144                $extra .= 'excluded versions: ';
145                foreach ($dep['exclude'] as $i => $exclude) {
146                    if ($i) {
147                        $extra .= ', ';
148                    }
149                    $extra .= $exclude;
150                }
151            }
152        }
153
154        $extra .= ')';
155        if ($extra == ' ()') {
156            $extra = '';
157        }
158
159        return $extra;
160    }
161
162    /**
163     * This makes unit-testing a heck of a lot easier
164     */
165    function getPHP_OS()
166    {
167        return PHP_OS;
168    }
169
170    /**
171     * This makes unit-testing a heck of a lot easier
172     */
173    function getsysname()
174    {
175        return $this->_os->getSysname();
176    }
177
178    /**
179     * Specify a dependency on an OS.  Use arch for detailed os/processor information
180     *
181     * There are two generic OS dependencies that will be the most common, unix and windows.
182     * Other options are linux, freebsd, darwin (OS X), sunos, irix, hpux, aix
183     */
184    function validateOsDependency($dep)
185    {
186        if ($this->_state != PEAR_VALIDATE_INSTALLING && $this->_state != PEAR_VALIDATE_DOWNLOADING) {
187            return true;
188        }
189
190        if ($dep['name'] == '*') {
191            return true;
192        }
193
194        $not = isset($dep['conflicts']) ? true : false;
195        switch (strtolower($dep['name'])) {
196            case 'windows' :
197                if ($not) {
198                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) == 'win') {
199                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
200                            return $this->raiseError("Cannot install %s on Windows");
201                        }
202
203                        return $this->warning("warning: Cannot install %s on Windows");
204                    }
205                } else {
206                    if (strtolower(substr($this->getPHP_OS(), 0, 3)) != 'win') {
207                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
208                            return $this->raiseError("Can only install %s on Windows");
209                        }
210
211                        return $this->warning("warning: Can only install %s on Windows");
212                    }
213                }
214            break;
215            case 'unix' :
216                $unices = array('linux', 'freebsd', 'darwin', 'sunos', 'irix', 'hpux', 'aix');
217                if ($not) {
218                    if (in_array($this->getSysname(), $unices)) {
219                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
220                            return $this->raiseError("Cannot install %s on any Unix system");
221                        }
222
223                        return $this->warning( "warning: Cannot install %s on any Unix system");
224                    }
225                } else {
226                    if (!in_array($this->getSysname(), $unices)) {
227                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
228                            return $this->raiseError("Can only install %s on a Unix system");
229                        }
230
231                        return $this->warning("warning: Can only install %s on a Unix system");
232                    }
233                }
234            break;
235            default :
236                if ($not) {
237                    if (strtolower($dep['name']) == strtolower($this->getSysname())) {
238                        if (!isset($this->_options['nodeps']) &&
239                              !isset($this->_options['force'])) {
240                            return $this->raiseError('Cannot install %s on ' . $dep['name'] .
241                                ' operating system');
242                        }
243
244                        return $this->warning('warning: Cannot install %s on ' .
245                            $dep['name'] . ' operating system');
246                    }
247                } else {
248                    if (strtolower($dep['name']) != strtolower($this->getSysname())) {
249                        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
250                            return $this->raiseError('Cannot install %s on ' .
251                                $this->getSysname() .
252                                ' operating system, can only install on ' . $dep['name']);
253                        }
254
255                        return $this->warning('warning: Cannot install %s on ' .
256                            $this->getSysname() .
257                            ' operating system, can only install on ' . $dep['name']);
258                    }
259                }
260        }
261        return true;
262    }
263
264    /**
265     * This makes unit-testing a heck of a lot easier
266     */
267    function matchSignature($pattern)
268    {
269        return $this->_os->matchSignature($pattern);
270    }
271
272    /**
273     * Specify a complex dependency on an OS/processor/kernel version,
274     * Use OS for simple operating system dependency.
275     *
276     * This is the only dependency that accepts an eregable pattern.  The pattern
277     * will be matched against the php_uname() output parsed by OS_Guess
278     */
279    function validateArchDependency($dep)
280    {
281        if ($this->_state != PEAR_VALIDATE_INSTALLING) {
282            return true;
283        }
284
285        $not = isset($dep['conflicts']) ? true : false;
286        if (!$this->matchSignature($dep['pattern'])) {
287            if (!$not) {
288                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
289                    return $this->raiseError('%s Architecture dependency failed, does not ' .
290                        'match "' . $dep['pattern'] . '"');
291                }
292
293                return $this->warning('warning: %s Architecture dependency failed, does ' .
294                    'not match "' . $dep['pattern'] . '"');
295            }
296
297            return true;
298        }
299
300        if ($not) {
301            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
302                return $this->raiseError('%s Architecture dependency failed, required "' .
303                    $dep['pattern'] . '"');
304            }
305
306            return $this->warning('warning: %s Architecture dependency failed, ' .
307                'required "' . $dep['pattern'] . '"');
308        }
309
310        return true;
311    }
312
313    /**
314     * This makes unit-testing a heck of a lot easier
315     */
316    function extension_loaded($name)
317    {
318        return extension_loaded($name);
319    }
320
321    /**
322     * This makes unit-testing a heck of a lot easier
323     */
324    function phpversion($name = null)
325    {
326        if ($name !== null) {
327            return phpversion($name);
328        }
329
330        return phpversion();
331    }
332
333    function validateExtensionDependency($dep, $required = true)
334    {
335        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
336              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
337            return true;
338        }
339
340        $loaded = $this->extension_loaded($dep['name']);
341        $extra  = $this->_getExtraString($dep);
342        if (isset($dep['exclude'])) {
343            if (!is_array($dep['exclude'])) {
344                $dep['exclude'] = array($dep['exclude']);
345            }
346        }
347
348        if (!isset($dep['min']) && !isset($dep['max']) &&
349            !isset($dep['recommended']) && !isset($dep['exclude'])
350        ) {
351            if ($loaded) {
352                if (isset($dep['conflicts'])) {
353                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
354                        return $this->raiseError('%s conflicts with PHP extension "' .
355                            $dep['name'] . '"' . $extra);
356                    }
357
358                    return $this->warning('warning: %s conflicts with PHP extension "' .
359                        $dep['name'] . '"' . $extra);
360                }
361
362                return true;
363            }
364
365            if (isset($dep['conflicts'])) {
366                return true;
367            }
368
369            if ($required) {
370                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
371                    return $this->raiseError('%s requires PHP extension "' .
372                        $dep['name'] . '"' . $extra);
373                }
374
375                return $this->warning('warning: %s requires PHP extension "' .
376                    $dep['name'] . '"' . $extra);
377            }
378
379            return $this->warning('%s can optionally use PHP extension "' .
380                $dep['name'] . '"' . $extra);
381        }
382
383        if (!$loaded) {
384            if (isset($dep['conflicts'])) {
385                return true;
386            }
387
388            if (!$required) {
389                return $this->warning('%s can optionally use PHP extension "' .
390                    $dep['name'] . '"' . $extra);
391            }
392
393            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
394                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
395                    '"' . $extra);
396            }
397
398            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
399                    '"' . $extra);
400        }
401
402        $version = (string) $this->phpversion($dep['name']);
403        if (empty($version)) {
404            $version = '0';
405        }
406
407        $fail = false;
408        if (isset($dep['min']) && !version_compare($version, $dep['min'], '>=')) {
409            $fail = true;
410        }
411
412        if (isset($dep['max']) && !version_compare($version, $dep['max'], '<=')) {
413            $fail = true;
414        }
415
416        if ($fail && !isset($dep['conflicts'])) {
417            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
418                return $this->raiseError('%s requires PHP extension "' . $dep['name'] .
419                    '"' . $extra . ', installed version is ' . $version);
420            }
421
422            return $this->warning('warning: %s requires PHP extension "' . $dep['name'] .
423                '"' . $extra . ', installed version is ' . $version);
424        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail && isset($dep['conflicts'])) {
425            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
426                return $this->raiseError('%s conflicts with PHP extension "' .
427                    $dep['name'] . '"' . $extra . ', installed version is ' . $version);
428            }
429
430            return $this->warning('warning: %s conflicts with PHP extension "' .
431                $dep['name'] . '"' . $extra . ', installed version is ' . $version);
432        }
433
434        if (isset($dep['exclude'])) {
435            foreach ($dep['exclude'] as $exclude) {
436                if (version_compare($version, $exclude, '==')) {
437                    if (isset($dep['conflicts'])) {
438                        continue;
439                    }
440
441                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
442                        return $this->raiseError('%s is not compatible with PHP extension "' .
443                            $dep['name'] . '" version ' .
444                            $exclude);
445                    }
446
447                    return $this->warning('warning: %s is not compatible with PHP extension "' .
448                        $dep['name'] . '" version ' .
449                        $exclude);
450                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
451                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
452                        return $this->raiseError('%s conflicts with PHP extension "' .
453                            $dep['name'] . '"' . $extra . ', installed version is ' . $version);
454                    }
455
456                    return $this->warning('warning: %s conflicts with PHP extension "' .
457                        $dep['name'] . '"' . $extra . ', installed version is ' . $version);
458                }
459            }
460        }
461
462        if (isset($dep['recommended'])) {
463            if (version_compare($version, $dep['recommended'], '==')) {
464                return true;
465            }
466
467            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
468                return $this->raiseError('%s dependency: PHP extension ' . $dep['name'] .
469                    ' version "' . $version . '"' .
470                    ' is not the recommended version "' . $dep['recommended'] .
471                    '", but may be compatible, use --force to install');
472            }
473
474            return $this->warning('warning: %s dependency: PHP extension ' .
475                $dep['name'] . ' version "' . $version . '"' .
476                ' is not the recommended version "' . $dep['recommended'].'"');
477        }
478
479        return true;
480    }
481
482    function validatePhpDependency($dep)
483    {
484        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
485              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
486            return true;
487        }
488
489        $version = $this->phpversion();
490        $extra   = $this->_getExtraString($dep);
491        if (isset($dep['exclude'])) {
492            if (!is_array($dep['exclude'])) {
493                $dep['exclude'] = array($dep['exclude']);
494            }
495        }
496
497        if (isset($dep['min'])) {
498            if (!version_compare($version, $dep['min'], '>=')) {
499                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
500                    return $this->raiseError('%s requires PHP' .
501                        $extra . ', installed version is ' . $version);
502                }
503
504                return $this->warning('warning: %s requires PHP' .
505                    $extra . ', installed version is ' . $version);
506            }
507        }
508
509        if (isset($dep['max'])) {
510            if (!version_compare($version, $dep['max'], '<=')) {
511                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
512                    return $this->raiseError('%s requires PHP' .
513                        $extra . ', installed version is ' . $version);
514                }
515
516                return $this->warning('warning: %s requires PHP' .
517                    $extra . ', installed version is ' . $version);
518            }
519        }
520
521        if (isset($dep['exclude'])) {
522            foreach ($dep['exclude'] as $exclude) {
523                if (version_compare($version, $exclude, '==')) {
524                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
525                        return $this->raiseError('%s is not compatible with PHP version ' .
526                            $exclude);
527                    }
528
529                    return $this->warning(
530                        'warning: %s is not compatible with PHP version ' .
531                        $exclude);
532                }
533            }
534        }
535
536        return true;
537    }
538
539    /**
540     * This makes unit-testing a heck of a lot easier
541     */
542    function getPEARVersion()
543    {
544        return '1.9.4';
545    }
546
547    function validatePearinstallerDependency($dep)
548    {
549        $pearversion = $this->getPEARVersion();
550        $extra = $this->_getExtraString($dep);
551        if (isset($dep['exclude'])) {
552            if (!is_array($dep['exclude'])) {
553                $dep['exclude'] = array($dep['exclude']);
554            }
555        }
556
557        if (version_compare($pearversion, $dep['min'], '<')) {
558            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
559                return $this->raiseError('%s requires PEAR Installer' . $extra .
560                    ', installed version is ' . $pearversion);
561            }
562
563            return $this->warning('warning: %s requires PEAR Installer' . $extra .
564                ', installed version is ' . $pearversion);
565        }
566
567        if (isset($dep['max'])) {
568            if (version_compare($pearversion, $dep['max'], '>')) {
569                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
570                    return $this->raiseError('%s requires PEAR Installer' . $extra .
571                        ', installed version is ' . $pearversion);
572                }
573
574                return $this->warning('warning: %s requires PEAR Installer' . $extra .
575                    ', installed version is ' . $pearversion);
576            }
577        }
578
579        if (isset($dep['exclude'])) {
580            if (!isset($dep['exclude'][0])) {
581                $dep['exclude'] = array($dep['exclude']);
582            }
583
584            foreach ($dep['exclude'] as $exclude) {
585                if (version_compare($exclude, $pearversion, '==')) {
586                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
587                        return $this->raiseError('%s is not compatible with PEAR Installer ' .
588                            'version ' . $exclude);
589                    }
590
591                    return $this->warning('warning: %s is not compatible with PEAR ' .
592                        'Installer version ' . $exclude);
593                }
594            }
595        }
596
597        return true;
598    }
599
600    function validateSubpackageDependency($dep, $required, $params)
601    {
602        return $this->validatePackageDependency($dep, $required, $params);
603    }
604
605    /**
606     * @param array dependency information (2.0 format)
607     * @param boolean whether this is a required dependency
608     * @param array a list of downloaded packages to be installed, if any
609     * @param boolean if true, then deps on pear.php.net that fail will also check
610     *                against pecl.php.net packages to accomodate extensions that have
611     *                moved to pecl.php.net from pear.php.net
612     */
613    function validatePackageDependency($dep, $required, $params, $depv1 = false)
614    {
615        if ($this->_state != PEAR_VALIDATE_INSTALLING &&
616              $this->_state != PEAR_VALIDATE_DOWNLOADING) {
617            return true;
618        }
619
620        if (isset($dep['providesextension'])) {
621            if ($this->extension_loaded($dep['providesextension'])) {
622                $save = $dep;
623                $subdep = $dep;
624                $subdep['name'] = $subdep['providesextension'];
625                PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
626                $ret = $this->validateExtensionDependency($subdep, $required);
627                PEAR::popErrorHandling();
628                if (!PEAR::isError($ret)) {
629                    return true;
630                }
631            }
632        }
633
634        if ($this->_state == PEAR_VALIDATE_INSTALLING) {
635            return $this->_validatePackageInstall($dep, $required, $depv1);
636        }
637
638        if ($this->_state == PEAR_VALIDATE_DOWNLOADING) {
639            return $this->_validatePackageDownload($dep, $required, $params, $depv1);
640        }
641    }
642
643    function _validatePackageDownload($dep, $required, $params, $depv1 = false)
644    {
645        $dep['package'] = $dep['name'];
646        if (isset($dep['uri'])) {
647            $dep['channel'] = '__uri';
648        }
649
650        $depname = $this->_registry->parsedPackageNameToString($dep, true);
651        $found = false;
652        foreach ($params as $param) {
653            if ($param->isEqual(
654                  array('package' => $dep['name'],
655                        'channel' => $dep['channel']))) {
656                $found = true;
657                break;
658            }
659
660            if ($depv1 && $dep['channel'] == 'pear.php.net') {
661                if ($param->isEqual(
662                  array('package' => $dep['name'],
663                        'channel' => 'pecl.php.net'))) {
664                    $found = true;
665                    break;
666                }
667            }
668        }
669
670        if (!$found && isset($dep['providesextension'])) {
671            foreach ($params as $param) {
672                if ($param->isExtension($dep['providesextension'])) {
673                    $found = true;
674                    break;
675                }
676            }
677        }
678
679        if ($found) {
680            $version = $param->getVersion();
681            $installed = false;
682            $downloaded = true;
683        } else {
684            if ($this->_registry->packageExists($dep['name'], $dep['channel'])) {
685                $installed = true;
686                $downloaded = false;
687                $version = $this->_registry->packageinfo($dep['name'], 'version',
688                    $dep['channel']);
689            } else {
690                if ($dep['channel'] == 'pecl.php.net' && $this->_registry->packageExists($dep['name'],
691                      'pear.php.net')) {
692                    $installed = true;
693                    $downloaded = false;
694                    $version = $this->_registry->packageinfo($dep['name'], 'version',
695                        'pear.php.net');
696                } else {
697                    $version = 'not installed or downloaded';
698                    $installed = false;
699                    $downloaded = false;
700                }
701            }
702        }
703
704        $extra = $this->_getExtraString($dep);
705        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
706            $dep['exclude'] = array($dep['exclude']);
707        }
708
709        if (!isset($dep['min']) && !isset($dep['max']) &&
710              !isset($dep['recommended']) && !isset($dep['exclude'])
711        ) {
712            if ($installed || $downloaded) {
713                $installed = $installed ? 'installed' : 'downloaded';
714                if (isset($dep['conflicts'])) {
715                    $rest = '';
716                    if ($version) {
717                        $rest = ", $installed version is " . $version;
718                    }
719
720                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
721                        return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra . $rest);
722                    }
723
724                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' . $extra . $rest);
725                }
726
727                return true;
728            }
729
730            if (isset($dep['conflicts'])) {
731                return true;
732            }
733
734            if ($required) {
735                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
736                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
737                }
738
739                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
740            }
741
742            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
743        }
744
745        if (!$installed && !$downloaded) {
746            if (isset($dep['conflicts'])) {
747                return true;
748            }
749
750            if ($required) {
751                if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
752                    return $this->raiseError('%s requires package "' . $depname . '"' . $extra);
753                }
754
755                return $this->warning('warning: %s requires package "' . $depname . '"' . $extra);
756            }
757
758            return $this->warning('%s can optionally use package "' . $depname . '"' . $extra);
759        }
760
761        $fail = false;
762        if (isset($dep['min']) && version_compare($version, $dep['min'], '<')) {
763            $fail = true;
764        }
765
766        if (isset($dep['max']) && version_compare($version, $dep['max'], '>')) {
767            $fail = true;
768        }
769
770        if ($fail && !isset($dep['conflicts'])) {
771            $installed = $installed ? 'installed' : 'downloaded';
772            $dep['package'] = $dep['name'];
773            $dep = $this->_registry->parsedPackageNameToString($dep, true);
774            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
775                return $this->raiseError('%s requires package "' . $depname . '"' .
776                    $extra . ", $installed version is " . $version);
777            }
778
779            return $this->warning('warning: %s requires package "' . $depname . '"' .
780                $extra . ", $installed version is " . $version);
781        } elseif ((isset($dep['min']) || isset($dep['max'])) && !$fail &&
782              isset($dep['conflicts']) && !isset($dep['exclude'])) {
783            $installed = $installed ? 'installed' : 'downloaded';
784            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
785                return $this->raiseError('%s conflicts with package "' . $depname . '"' . $extra .
786                    ", $installed version is " . $version);
787            }
788
789            return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
790                $extra . ", $installed version is " . $version);
791        }
792
793        if (isset($dep['exclude'])) {
794            $installed = $installed ? 'installed' : 'downloaded';
795            foreach ($dep['exclude'] as $exclude) {
796                if (version_compare($version, $exclude, '==') && !isset($dep['conflicts'])) {
797                    if (!isset($this->_options['nodeps']) &&
798                          !isset($this->_options['force'])
799                    ) {
800                        return $this->raiseError('%s is not compatible with ' .
801                            $installed . ' package "' .
802                            $depname . '" version ' .
803                            $exclude);
804                    }
805
806                    return $this->warning('warning: %s is not compatible with ' .
807                        $installed . ' package "' .
808                        $depname . '" version ' .
809                        $exclude);
810                } elseif (version_compare($version, $exclude, '!=') && isset($dep['conflicts'])) {
811                    $installed = $installed ? 'installed' : 'downloaded';
812                    if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
813                        return $this->raiseError('%s conflicts with package "' . $depname . '"' .
814                            $extra . ", $installed version is " . $version);
815                    }
816
817                    return $this->warning('warning: %s conflicts with package "' . $depname . '"' .
818                        $extra . ", $installed version is " . $version);
819                }
820            }
821        }
822
823        if (isset($dep['recommended'])) {
824            $installed = $installed ? 'installed' : 'downloaded';
825            if (version_compare($version, $dep['recommended'], '==')) {
826                return true;
827            }
828
829            if (!$found && $installed) {
830                $param = $this->_registry->getPackage($dep['name'], $dep['channel']);
831            }
832
833            if ($param) {
834                $found = false;
835                foreach ($params as $parent) {
836                    if ($parent->isEqual($this->_currentPackage)) {
837                        $found = true;
838                        break;
839                    }
840                }
841
842                if ($found) {
843                    if ($param->isCompatible($parent)) {
844                        return true;
845                    }
846                } else { // this is for validPackage() calls
847                    $parent = $this->_registry->getPackage($this->_currentPackage['package'],
848                        $this->_currentPackage['channel']);
849                    if ($parent !== null && $param->isCompatible($parent)) {
850                        return true;
851                    }
852                }
853            }
854
855            if (!isset($this->_options['nodeps']) && !isset($this->_options['force']) &&
856                  !isset($this->_options['loose'])
857            ) {
858                return $this->raiseError('%s dependency package "' . $depname .
859                    '" ' . $installed . ' version ' . $version .
860                    ' is not the recommended version ' . $dep['recommended'] .
861                    ', but may be compatible, use --force to install');
862            }
863
864            return $this->warning('warning: %s dependency package "' . $depname .
865                '" ' . $installed . ' version ' . $version .
866                ' is not the recommended version ' . $dep['recommended']);
867        }
868
869        return true;
870    }
871
872    function _validatePackageInstall($dep, $required, $depv1 = false)
873    {
874        return $this->_validatePackageDownload($dep, $required, array(), $depv1);
875    }
876
877    /**
878     * Verify that uninstalling packages passed in to command line is OK.
879     *
880     * @param PEAR_Installer $dl
881     * @return PEAR_Error|true
882     */
883    function validatePackageUninstall(&$dl)
884    {
885        if (PEAR::isError($this->_dependencydb)) {
886            return $this->_dependencydb;
887        }
888
889        $params = array();
890        // construct an array of "downloaded" packages to fool the package dependency checker
891        // into using these to validate uninstalls of circular dependencies
892        $downloaded = &$dl->getUninstallPackages();
893        foreach ($downloaded as $i => $pf) {
894            if (!class_exists('PEAR_Downloader_Package')) {
895                require_once 'PEAR/Downloader/Package.php';
896            }
897            $dp = &new PEAR_Downloader_Package($dl);
898            $dp->setPackageFile($downloaded[$i]);
899            $params[$i] = &$dp;
900        }
901
902        // check cache
903        $memyselfandI = strtolower($this->_currentPackage['channel']) . '/' .
904            strtolower($this->_currentPackage['package']);
905        if (isset($dl->___uninstall_package_cache)) {
906            $badpackages = $dl->___uninstall_package_cache;
907            if (isset($badpackages[$memyselfandI]['warnings'])) {
908                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
909                    $dl->log(0, $warning[0]);
910                }
911            }
912
913            if (isset($badpackages[$memyselfandI]['errors'])) {
914                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
915                    if (is_array($error)) {
916                        $dl->log(0, $error[0]);
917                    } else {
918                        $dl->log(0, $error->getMessage());
919                    }
920                }
921
922                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
923                    return $this->warning(
924                        'warning: %s should not be uninstalled, other installed packages depend ' .
925                        'on this package');
926                }
927
928                return $this->raiseError(
929                    '%s cannot be uninstalled, other installed packages depend on this package');
930            }
931
932            return true;
933        }
934
935        // first, list the immediate parents of each package to be uninstalled
936        $perpackagelist = array();
937        $allparents = array();
938        foreach ($params as $i => $param) {
939            $a = array(
940                'channel' => strtolower($param->getChannel()),
941                'package' => strtolower($param->getPackage())
942            );
943
944            $deps = $this->_dependencydb->getDependentPackages($a);
945            if ($deps) {
946                foreach ($deps as $d) {
947                    $pardeps = $this->_dependencydb->getDependencies($d);
948                    foreach ($pardeps as $dep) {
949                        if (strtolower($dep['dep']['channel']) == $a['channel'] &&
950                              strtolower($dep['dep']['name']) == $a['package']) {
951                            if (!isset($perpackagelist[$a['channel'] . '/' . $a['package']])) {
952                                $perpackagelist[$a['channel'] . '/' . $a['package']] = array();
953                            }
954                            $perpackagelist[$a['channel'] . '/' . $a['package']][]
955                                = array($d['channel'] . '/' . $d['package'], $dep);
956                            if (!isset($allparents[$d['channel'] . '/' . $d['package']])) {
957                                $allparents[$d['channel'] . '/' . $d['package']] = array();
958                            }
959                            if (!isset($allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']])) {
960                                $allparents[$d['channel'] . '/' . $d['package']][$a['channel'] . '/' . $a['package']] = array();
961                            }
962                            $allparents[$d['channel'] . '/' . $d['package']]
963                                       [$a['channel'] . '/' . $a['package']][]
964                                = array($d, $dep);
965                        }
966                    }
967                }
968            }
969        }
970
971        // next, remove any packages from the parents list that are not installed
972        $remove = array();
973        foreach ($allparents as $parent => $d1) {
974            foreach ($d1 as $d) {
975                if ($this->_registry->packageExists($d[0][0]['package'], $d[0][0]['channel'])) {
976                    continue;
977                }
978                $remove[$parent] = true;
979            }
980        }
981
982        // next remove any packages from the parents list that are not passed in for
983        // uninstallation
984        foreach ($allparents as $parent => $d1) {
985            foreach ($d1 as $d) {
986                foreach ($params as $param) {
987                    if (strtolower($param->getChannel()) == $d[0][0]['channel'] &&
988                          strtolower($param->getPackage()) == $d[0][0]['package']) {
989                        // found it
990                        continue 3;
991                    }
992                }
993                $remove[$parent] = true;
994            }
995        }
996
997        // remove all packages whose dependencies fail
998        // save which ones failed for error reporting
999        $badchildren = array();
1000        do {
1001            $fail = false;
1002            foreach ($remove as $package => $unused) {
1003                if (!isset($allparents[$package])) {
1004                    continue;
1005                }
1006
1007                foreach ($allparents[$package] as $kid => $d1) {
1008                    foreach ($d1 as $depinfo) {
1009                        if ($depinfo[1]['type'] != 'optional') {
1010                            if (isset($badchildren[$kid])) {
1011                                continue;
1012                            }
1013                            $badchildren[$kid] = true;
1014                            $remove[$kid] = true;
1015                            $fail = true;
1016                            continue 2;
1017                        }
1018                    }
1019                }
1020                if ($fail) {
1021                    // start over, we removed some children
1022                    continue 2;
1023                }
1024            }
1025        } while ($fail);
1026
1027        // next, construct the list of packages that can't be uninstalled
1028        $badpackages = array();
1029        $save = $this->_currentPackage;
1030        foreach ($perpackagelist as $package => $packagedeps) {
1031            foreach ($packagedeps as $parent) {
1032                if (!isset($remove[$parent[0]])) {
1033                    continue;
1034                }
1035
1036                $packagename = $this->_registry->parsePackageName($parent[0]);
1037                $packagename['channel'] = $this->_registry->channelAlias($packagename['channel']);
1038                $pa = $this->_registry->getPackage($packagename['package'], $packagename['channel']);
1039                $packagename['package'] = $pa->getPackage();
1040                $this->_currentPackage = $packagename;
1041                // parent is not present in uninstall list, make sure we can actually
1042                // uninstall it (parent dep is optional)
1043                $parentname['channel'] = $this->_registry->channelAlias($parent[1]['dep']['channel']);
1044                $pa = $this->_registry->getPackage($parent[1]['dep']['name'], $parent[1]['dep']['channel']);
1045                $parentname['package'] = $pa->getPackage();
1046                $parent[1]['dep']['package'] = $parentname['package'];
1047                $parent[1]['dep']['channel'] = $parentname['channel'];
1048                if ($parent[1]['type'] == 'optional') {
1049                    $test = $this->_validatePackageUninstall($parent[1]['dep'], false, $dl);
1050                    if ($test !== true) {
1051                        $badpackages[$package]['warnings'][] = $test;
1052                    }
1053                } else {
1054                    $test = $this->_validatePackageUninstall($parent[1]['dep'], true, $dl);
1055                    if ($test !== true) {
1056                        $badpackages[$package]['errors'][] = $test;
1057                    }
1058                }
1059            }
1060        }
1061
1062        $this->_currentPackage          = $save;
1063        $dl->___uninstall_package_cache = $badpackages;
1064        if (isset($badpackages[$memyselfandI])) {
1065            if (isset($badpackages[$memyselfandI]['warnings'])) {
1066                foreach ($badpackages[$memyselfandI]['warnings'] as $warning) {
1067                    $dl->log(0, $warning[0]);
1068                }
1069            }
1070
1071            if (isset($badpackages[$memyselfandI]['errors'])) {
1072                foreach ($badpackages[$memyselfandI]['errors'] as $error) {
1073                    if (is_array($error)) {
1074                        $dl->log(0, $error[0]);
1075                    } else {
1076                        $dl->log(0, $error->getMessage());
1077                    }
1078                }
1079
1080                if (isset($this->_options['nodeps']) || isset($this->_options['force'])) {
1081                    return $this->warning(
1082                        'warning: %s should not be uninstalled, other installed packages depend ' .
1083                        'on this package');
1084                }
1085
1086                return $this->raiseError(
1087                    '%s cannot be uninstalled, other installed packages depend on this package');
1088            }
1089        }
1090
1091        return true;
1092    }
1093
1094    function _validatePackageUninstall($dep, $required, $dl)
1095    {
1096        $depname = $this->_registry->parsedPackageNameToString($dep, true);
1097        $version = $this->_registry->packageinfo($dep['package'], 'version', $dep['channel']);
1098        if (!$version) {
1099            return true;
1100        }
1101
1102        $extra = $this->_getExtraString($dep);
1103        if (isset($dep['exclude']) && !is_array($dep['exclude'])) {
1104            $dep['exclude'] = array($dep['exclude']);
1105        }
1106
1107        if (isset($dep['conflicts'])) {
1108            return true; // uninstall OK - these packages conflict (probably installed with --force)
1109        }
1110
1111        if (!isset($dep['min']) && !isset($dep['max'])) {
1112            if (!$required) {
1113                return $this->warning('"' . $depname . '" can be optionally used by ' .
1114                        'installed package %s' . $extra);
1115            }
1116
1117            if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
1118                return $this->raiseError('"' . $depname . '" is required by ' .
1119                    'installed package %s' . $extra);
1120            }
1121
1122            return $this->warning('warning: "' . $depname . '" is required by ' .
1123                'installed package %s' . $extra);
1124        }
1125
1126        $fail = false;
1127        if (isset($dep['min']) && version_compare($version, $dep['min'], '>=')) {
1128            $fail = true;
1129        }
1130
1131        if (isset($dep['max']) && version_compare($version, $dep['max'], '<=')) {
1132            $fail = true;
1133        }
1134
1135        // we re-use this variable, preserve the original value
1136        $saverequired = $required;
1137        if (!$required) {
1138            return $this->warning($depname . $extra . ' can be optionally used by installed package' .
1139                    ' "%s"');
1140        }
1141
1142        if (!isset($this->_options['nodeps']) && !isset($this->_options['force'])) {
1143            return $this->raiseError($depname . $extra . ' is required by installed package' .
1144                ' "%s"');
1145        }
1146
1147        return $this->raiseError('warning: ' . $depname . $extra .
1148            ' is required by installed package "%s"');
1149    }
1150
1151    /**
1152     * validate a downloaded package against installed packages
1153     *
1154     * As of PEAR 1.4.3, this will only validate
1155     *
1156     * @param array|PEAR_Downloader_Package|PEAR_PackageFile_v1|PEAR_PackageFile_v2
1157     *              $pkg package identifier (either
1158     *                   array('package' => blah, 'channel' => blah) or an array with
1159     *                   index 'info' referencing an object)
1160     * @param PEAR_Downloader $dl
1161     * @param array $params full list of packages to install
1162     * @return true|PEAR_Error
1163     */
1164    function validatePackage($pkg, &$dl, $params = array())
1165    {
1166        if (is_array($pkg) && isset($pkg['info'])) {
1167            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg['info']);
1168        } else {
1169            $deps = $this->_dependencydb->getDependentPackageDependencies($pkg);
1170        }
1171
1172        $fail = false;
1173        if ($deps) {
1174            if (!class_exists('PEAR_Downloader_Package')) {
1175                require_once 'PEAR/Downloader/Package.php';
1176            }
1177
1178            $dp = &new PEAR_Downloader_Package($dl);
1179            if (is_object($pkg)) {
1180                $dp->setPackageFile($pkg);
1181            } else {
1182                $dp->setDownloadURL($pkg);
1183            }
1184
1185            PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
1186            foreach ($deps as $channel => $info) {
1187                foreach ($info as $package => $ds) {
1188                    foreach ($params as $packd) {
1189                        if (strtolower($packd->getPackage()) == strtolower($package) &&
1190                              $packd->getChannel() == $channel) {
1191                            $dl->log(3, 'skipping installed package check of "' .
1192                                        $this->_registry->parsedPackageNameToString(
1193                                            array('channel' => $channel, 'package' => $package),
1194                                            true) .
1195                                        '", version "' . $packd->getVersion() . '" will be ' .
1196                                        'downloaded and installed');
1197                            continue 2; // jump to next package
1198                        }
1199                    }
1200
1201                    foreach ($ds as $d) {
1202                        $checker = &new PEAR_Dependency2($this->_config, $this->_options,
1203                            array('channel' => $channel, 'package' => $package), $this->_state);
1204                        $dep = $d['dep'];
1205                        $required = $d['type'] == 'required';
1206                        $ret = $checker->_validatePackageDownload($dep, $required, array(&$dp));
1207                        if (is_array($ret)) {
1208                            $dl->log(0, $ret[0]);
1209                        } elseif (PEAR::isError($ret)) {
1210                            $dl->log(0, $ret->getMessage());
1211                            $fail = true;
1212                        }
1213                    }
1214                }
1215            }
1216            PEAR::popErrorHandling();
1217        }
1218
1219        if ($fail) {
1220            return $this->raiseError(
1221                '%s cannot be installed, conflicts with installed packages');
1222        }
1223
1224        return true;
1225    }
1226
1227    /**
1228     * validate a package.xml 1.0 dependency
1229     */
1230    function validateDependency1($dep, $params = array())
1231    {
1232        if (!isset($dep['optional'])) {
1233            $dep['optional'] = 'no';
1234        }
1235
1236        list($newdep, $type) = $this->normalizeDep($dep);
1237        if (!$newdep) {
1238            return $this->raiseError("Invalid Dependency");
1239        }
1240
1241        if (method_exists($this, "validate{$type}Dependency")) {
1242            return $this->{"validate{$type}Dependency"}($newdep, $dep['optional'] == 'no',
1243                $params, true);
1244        }
1245    }
1246
1247    /**
1248     * Convert a 1.0 dep into a 2.0 dep
1249     */
1250    function normalizeDep($dep)
1251    {
1252        $types = array(
1253            'pkg' => 'Package',
1254            'ext' => 'Extension',
1255            'os' => 'Os',
1256            'php' => 'Php'
1257        );
1258
1259        if (!isset($types[$dep['type']])) {
1260            return array(false, false);
1261        }
1262
1263        $type = $types[$dep['type']];
1264
1265        $newdep = array();
1266        switch ($type) {
1267            case 'Package' :
1268                $newdep['channel'] = 'pear.php.net';
1269            case 'Extension' :
1270            case 'Os' :
1271                $newdep['name'] = $dep['name'];
1272            break;
1273        }
1274
1275        $dep['rel'] = PEAR_Dependency2::signOperator($dep['rel']);
1276        switch ($dep['rel']) {
1277            case 'has' :
1278                return array($newdep, $type);
1279            break;
1280            case 'not' :
1281                $newdep['conflicts'] = true;
1282            break;
1283            case '>=' :
1284            case '>' :
1285                $newdep['min'] = $dep['version'];
1286                if ($dep['rel'] == '>') {
1287                    $newdep['exclude'] = $dep['version'];
1288                }
1289            break;
1290            case '<=' :
1291            case '<' :
1292                $newdep['max'] = $dep['version'];
1293                if ($dep['rel'] == '<') {
1294                    $newdep['exclude'] = $dep['version'];
1295                }
1296            break;
1297            case 'ne' :
1298            case '!=' :
1299                $newdep['min'] = '0';
1300                $newdep['max'] = '100000';
1301                $newdep['exclude'] = $dep['version'];
1302            break;
1303            case '==' :
1304                $newdep['min'] = $dep['version'];
1305                $newdep['max'] = $dep['version'];
1306            break;
1307        }
1308        if ($type == 'Php') {
1309            if (!isset($newdep['min'])) {
1310                $newdep['min'] = '4.4.0';
1311            }
1312
1313            if (!isset($newdep['max'])) {
1314                $newdep['max'] = '6.0.0';
1315            }
1316        }
1317        return array($newdep, $type);
1318    }
1319
1320    /**
1321     * Converts text comparing operators to them sign equivalents
1322     *
1323     * Example: 'ge' to '>='
1324     *
1325     * @access public
1326     * @param  string Operator
1327     * @return string Sign equivalent
1328     */
1329    function signOperator($operator)
1330    {
1331        switch($operator) {
1332            case 'lt': return '<';
1333            case 'le': return '<=';
1334            case 'gt': return '>';
1335            case 'ge': return '>=';
1336            case 'eq': return '==';
1337            case 'ne': return '!=';
1338            default:
1339                return $operator;
1340        }
1341    }
1342
1343    function raiseError($msg)
1344    {
1345        if (isset($this->_options['ignore-errors'])) {
1346            return $this->warning($msg);
1347        }
1348
1349        return PEAR::raiseError(sprintf($msg, $this->_registry->parsedPackageNameToString(
1350            $this->_currentPackage, true)));
1351    }
1352
1353    function warning($msg)
1354    {
1355        return array(sprintf($msg, $this->_registry->parsedPackageNameToString(
1356            $this->_currentPackage, true)));
1357    }
1358}
Note: See TracBrowser for help on using the repository browser.