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

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

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

Line 
1<?php
2/**
3 * PEAR_DependencyDB, advanced installed packages dependency database
4 *
5 * PHP versions 4 and 5
6 *
7 * @category   pear
8 * @package    PEAR
9 * @author     Tomas V. V. Cox <cox@idecnet.com>
10 * @author     Greg Beaver <cellog@php.net>
11 * @copyright  1997-2009 The Authors
12 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
13 * @version    CVS: $Id: DependencyDB.php 313023 2011-07-06 19:17:11Z dufuz $
14 * @link       http://pear.php.net/package/PEAR
15 * @since      File available since Release 1.4.0a1
16 */
17
18/**
19 * Needed for error handling
20 */
21require_once 'PEAR.php';
22require_once 'PEAR/Config.php';
23
24$GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'] = array();
25/**
26 * Track dependency relationships between installed packages
27 * @category   pear
28 * @package    PEAR
29 * @author     Greg Beaver <cellog@php.net>
30 * @author     Tomas V.V.Cox <cox@idec.net.com>
31 * @copyright  1997-2009 The Authors
32 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
33 * @version    Release: 1.9.4
34 * @link       http://pear.php.net/package/PEAR
35 * @since      Class available since Release 1.4.0a1
36 */
37class PEAR_DependencyDB
38{
39    // {{{ properties
40
41    /**
42     * This is initialized by {@link setConfig()}
43     * @var PEAR_Config
44     * @access private
45     */
46    var $_config;
47    /**
48     * This is initialized by {@link setConfig()}
49     * @var PEAR_Registry
50     * @access private
51     */
52    var $_registry;
53    /**
54     * Filename of the dependency DB (usually .depdb)
55     * @var string
56     * @access private
57     */
58    var $_depdb = false;
59    /**
60     * File name of the lockfile (usually .depdblock)
61     * @var string
62     * @access private
63     */
64    var $_lockfile = false;
65    /**
66     * Open file resource for locking the lockfile
67     * @var resource|false
68     * @access private
69     */
70    var $_lockFp = false;
71    /**
72     * API version of this class, used to validate a file on-disk
73     * @var string
74     * @access private
75     */
76    var $_version = '1.0';
77    /**
78     * Cached dependency database file
79     * @var array|null
80     * @access private
81     */
82    var $_cache;
83
84    // }}}
85    // {{{ & singleton()
86
87    /**
88     * Get a raw dependency database.  Calls setConfig() and assertDepsDB()
89     * @param PEAR_Config
90     * @param string|false full path to the dependency database, or false to use default
91     * @return PEAR_DependencyDB|PEAR_Error
92     * @static
93     */
94    function &singleton(&$config, $depdb = false)
95    {
96        $phpdir = $config->get('php_dir', null, 'pear.php.net');
97        if (!isset($GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir])) {
98            $a = new PEAR_DependencyDB;
99            $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir] = &$a;
100            $a->setConfig($config, $depdb);
101            $e = $a->assertDepsDB();
102            if (PEAR::isError($e)) {
103                return $e;
104            }
105        }
106
107        return $GLOBALS['_PEAR_DEPENDENCYDB_INSTANCE'][$phpdir];
108    }
109
110    /**
111     * Set up the registry/location of dependency DB
112     * @param PEAR_Config|false
113     * @param string|false full path to the dependency database, or false to use default
114     */
115    function setConfig(&$config, $depdb = false)
116    {
117        if (!$config) {
118            $this->_config = &PEAR_Config::singleton();
119        } else {
120            $this->_config = &$config;
121        }
122
123        $this->_registry = &$this->_config->getRegistry();
124        if (!$depdb) {
125            $this->_depdb = $this->_config->get('php_dir', null, 'pear.php.net') .
126                DIRECTORY_SEPARATOR . '.depdb';
127        } else {
128            $this->_depdb = $depdb;
129        }
130
131        $this->_lockfile = dirname($this->_depdb) . DIRECTORY_SEPARATOR . '.depdblock';
132    }
133    // }}}
134
135    function hasWriteAccess()
136    {
137        if (!file_exists($this->_depdb)) {
138            $dir = $this->_depdb;
139            while ($dir && $dir != '.') {
140                $dir = dirname($dir); // cd ..
141                if ($dir != '.' && file_exists($dir)) {
142                    if (is_writeable($dir)) {
143                        return true;
144                    }
145
146                    return false;
147                }
148            }
149
150            return false;
151        }
152
153        return is_writeable($this->_depdb);
154    }
155
156    // {{{ assertDepsDB()
157
158    /**
159     * Create the dependency database, if it doesn't exist.  Error if the database is
160     * newer than the code reading it.
161     * @return void|PEAR_Error
162     */
163    function assertDepsDB()
164    {
165        if (!is_file($this->_depdb)) {
166            $this->rebuildDB();
167            return;
168        }
169
170        $depdb = $this->_getDepDB();
171        // Datatype format has been changed, rebuild the Deps DB
172        if ($depdb['_version'] < $this->_version) {
173            $this->rebuildDB();
174        }
175
176        if ($depdb['_version']{0} > $this->_version{0}) {
177            return PEAR::raiseError('Dependency database is version ' .
178                $depdb['_version'] . ', and we are version ' .
179                $this->_version . ', cannot continue');
180        }
181    }
182
183    /**
184     * Get a list of installed packages that depend on this package
185     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
186     * @return array|false
187     */
188    function getDependentPackages(&$pkg)
189    {
190        $data = $this->_getDepDB();
191        if (is_object($pkg)) {
192            $channel = strtolower($pkg->getChannel());
193            $package = strtolower($pkg->getPackage());
194        } else {
195            $channel = strtolower($pkg['channel']);
196            $package = strtolower($pkg['package']);
197        }
198
199        if (isset($data['packages'][$channel][$package])) {
200            return $data['packages'][$channel][$package];
201        }
202
203        return false;
204    }
205
206    /**
207     * Get a list of the actual dependencies of installed packages that depend on
208     * a package.
209     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
210     * @return array|false
211     */
212    function getDependentPackageDependencies(&$pkg)
213    {
214        $data = $this->_getDepDB();
215        if (is_object($pkg)) {
216            $channel = strtolower($pkg->getChannel());
217            $package = strtolower($pkg->getPackage());
218        } else {
219            $channel = strtolower($pkg['channel']);
220            $package = strtolower($pkg['package']);
221        }
222
223        $depend = $this->getDependentPackages($pkg);
224        if (!$depend) {
225            return false;
226        }
227
228        $dependencies = array();
229        foreach ($depend as $info) {
230            $temp = $this->getDependencies($info);
231            foreach ($temp as $dep) {
232                if (
233                    isset($dep['dep'], $dep['dep']['channel'], $dep['dep']['name']) &&
234                    strtolower($dep['dep']['channel']) == $channel &&
235                    strtolower($dep['dep']['name']) == $package
236                ) {
237                    if (!isset($dependencies[$info['channel']])) {
238                        $dependencies[$info['channel']] = array();
239                    }
240
241                    if (!isset($dependencies[$info['channel']][$info['package']])) {
242                        $dependencies[$info['channel']][$info['package']] = array();
243                    }
244                    $dependencies[$info['channel']][$info['package']][] = $dep;
245                }
246            }
247        }
248
249        return $dependencies;
250    }
251
252    /**
253     * Get a list of dependencies of this installed package
254     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array
255     * @return array|false
256     */
257    function getDependencies(&$pkg)
258    {
259        if (is_object($pkg)) {
260            $channel = strtolower($pkg->getChannel());
261            $package = strtolower($pkg->getPackage());
262        } else {
263            $channel = strtolower($pkg['channel']);
264            $package = strtolower($pkg['package']);
265        }
266
267        $data = $this->_getDepDB();
268        if (isset($data['dependencies'][$channel][$package])) {
269            return $data['dependencies'][$channel][$package];
270        }
271
272        return false;
273    }
274
275    /**
276     * Determine whether $parent depends on $child, near or deep
277     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
278     * @param array|PEAR_PackageFile_v2|PEAR_PackageFile_v2
279     */
280    function dependsOn($parent, $child)
281    {
282        $c = array();
283        $this->_getDepDB();
284        return $this->_dependsOn($parent, $child, $c);
285    }
286
287    function _dependsOn($parent, $child, &$checked)
288    {
289        if (is_object($parent)) {
290            $channel = strtolower($parent->getChannel());
291            $package = strtolower($parent->getPackage());
292        } else {
293            $channel = strtolower($parent['channel']);
294            $package = strtolower($parent['package']);
295        }
296
297        if (is_object($child)) {
298            $depchannel = strtolower($child->getChannel());
299            $deppackage = strtolower($child->getPackage());
300        } else {
301            $depchannel = strtolower($child['channel']);
302            $deppackage = strtolower($child['package']);
303        }
304
305        if (isset($checked[$channel][$package][$depchannel][$deppackage])) {
306            return false; // avoid endless recursion
307        }
308
309        $checked[$channel][$package][$depchannel][$deppackage] = true;
310        if (!isset($this->_cache['dependencies'][$channel][$package])) {
311            return false;
312        }
313
314        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
315            if (isset($info['dep']['uri'])) {
316                if (is_object($child)) {
317                    if ($info['dep']['uri'] == $child->getURI()) {
318                        return true;
319                    }
320                } elseif (isset($child['uri'])) {
321                    if ($info['dep']['uri'] == $child['uri']) {
322                        return true;
323                    }
324                }
325                return false;
326            }
327
328            if (strtolower($info['dep']['channel']) == $depchannel &&
329                  strtolower($info['dep']['name']) == $deppackage) {
330                return true;
331            }
332        }
333
334        foreach ($this->_cache['dependencies'][$channel][$package] as $info) {
335            if (isset($info['dep']['uri'])) {
336                if ($this->_dependsOn(array(
337                        'uri' => $info['dep']['uri'],
338                        'package' => $info['dep']['name']), $child, $checked)) {
339                    return true;
340                }
341            } else {
342                if ($this->_dependsOn(array(
343                        'channel' => $info['dep']['channel'],
344                        'package' => $info['dep']['name']), $child, $checked)) {
345                    return true;
346                }
347            }
348        }
349
350        return false;
351    }
352
353    /**
354     * Register dependencies of a package that is being installed or upgraded
355     * @param PEAR_PackageFile_v2|PEAR_PackageFile_v2
356     */
357    function installPackage(&$package)
358    {
359        $data = $this->_getDepDB();
360        unset($this->_cache);
361        $this->_setPackageDeps($data, $package);
362        $this->_writeDepDB($data);
363    }
364
365    /**
366     * Remove dependencies of a package that is being uninstalled, or upgraded.
367     *
368     * Upgraded packages first uninstall, then install
369     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2|array If an array, then it must have
370     *        indices 'channel' and 'package'
371     */
372    function uninstallPackage(&$pkg)
373    {
374        $data = $this->_getDepDB();
375        unset($this->_cache);
376        if (is_object($pkg)) {
377            $channel = strtolower($pkg->getChannel());
378            $package = strtolower($pkg->getPackage());
379        } else {
380            $channel = strtolower($pkg['channel']);
381            $package = strtolower($pkg['package']);
382        }
383
384        if (!isset($data['dependencies'][$channel][$package])) {
385            return true;
386        }
387
388        foreach ($data['dependencies'][$channel][$package] as $dep) {
389            $found      = false;
390            $depchannel = isset($dep['dep']['uri']) ? '__uri' : strtolower($dep['dep']['channel']);
391            $depname    = strtolower($dep['dep']['name']);
392            if (isset($data['packages'][$depchannel][$depname])) {
393                foreach ($data['packages'][$depchannel][$depname] as $i => $info) {
394                    if ($info['channel'] == $channel && $info['package'] == $package) {
395                        $found = true;
396                        break;
397                    }
398                }
399            }
400
401            if ($found) {
402                unset($data['packages'][$depchannel][$depname][$i]);
403                if (!count($data['packages'][$depchannel][$depname])) {
404                    unset($data['packages'][$depchannel][$depname]);
405                    if (!count($data['packages'][$depchannel])) {
406                        unset($data['packages'][$depchannel]);
407                    }
408                } else {
409                    $data['packages'][$depchannel][$depname] =
410                        array_values($data['packages'][$depchannel][$depname]);
411                }
412            }
413        }
414
415        unset($data['dependencies'][$channel][$package]);
416        if (!count($data['dependencies'][$channel])) {
417            unset($data['dependencies'][$channel]);
418        }
419
420        if (!count($data['dependencies'])) {
421            unset($data['dependencies']);
422        }
423
424        if (!count($data['packages'])) {
425            unset($data['packages']);
426        }
427
428        $this->_writeDepDB($data);
429    }
430
431    /**
432     * Rebuild the dependency DB by reading registry entries.
433     * @return true|PEAR_Error
434     */
435    function rebuildDB()
436    {
437        $depdb = array('_version' => $this->_version);
438        if (!$this->hasWriteAccess()) {
439            // allow startup for read-only with older Registry
440            return $depdb;
441        }
442
443        $packages = $this->_registry->listAllPackages();
444        if (PEAR::isError($packages)) {
445            return $packages;
446        }
447
448        foreach ($packages as $channel => $ps) {
449            foreach ($ps as $package) {
450                $package = $this->_registry->getPackage($package, $channel);
451                if (PEAR::isError($package)) {
452                    return $package;
453                }
454                $this->_setPackageDeps($depdb, $package);
455            }
456        }
457
458        $error = $this->_writeDepDB($depdb);
459        if (PEAR::isError($error)) {
460            return $error;
461        }
462
463        $this->_cache = $depdb;
464        return true;
465    }
466
467    /**
468     * Register usage of the dependency DB to prevent race conditions
469     * @param int one of the LOCK_* constants
470     * @return true|PEAR_Error
471     * @access private
472     */
473    function _lock($mode = LOCK_EX)
474    {
475        if (stristr(php_uname(), 'Windows 9')) {
476            return true;
477        }
478
479        if ($mode != LOCK_UN && is_resource($this->_lockFp)) {
480            // XXX does not check type of lock (LOCK_SH/LOCK_EX)
481            return true;
482        }
483
484        $open_mode = 'w';
485        // XXX People reported problems with LOCK_SH and 'w'
486        if ($mode === LOCK_SH) {
487            if (!file_exists($this->_lockfile)) {
488                touch($this->_lockfile);
489            } elseif (!is_file($this->_lockfile)) {
490                return PEAR::raiseError('could not create Dependency lock file, ' .
491                    'it exists and is not a regular file');
492            }
493            $open_mode = 'r';
494        }
495
496        if (!is_resource($this->_lockFp)) {
497            $this->_lockFp = @fopen($this->_lockfile, $open_mode);
498        }
499
500        if (!is_resource($this->_lockFp)) {
501            return PEAR::raiseError("could not create Dependency lock file" .
502                                     (isset($php_errormsg) ? ": " . $php_errormsg : ""));
503        }
504
505        if (!(int)flock($this->_lockFp, $mode)) {
506            switch ($mode) {
507                case LOCK_SH: $str = 'shared';    break;
508                case LOCK_EX: $str = 'exclusive'; break;
509                case LOCK_UN: $str = 'unlock';    break;
510                default:      $str = 'unknown';   break;
511            }
512
513            return PEAR::raiseError("could not acquire $str lock ($this->_lockfile)");
514        }
515
516        return true;
517    }
518
519    /**
520     * Release usage of dependency DB
521     * @return true|PEAR_Error
522     * @access private
523     */
524    function _unlock()
525    {
526        $ret = $this->_lock(LOCK_UN);
527        if (is_resource($this->_lockFp)) {
528            fclose($this->_lockFp);
529        }
530        $this->_lockFp = null;
531        return $ret;
532    }
533
534    /**
535     * Load the dependency database from disk, or return the cache
536     * @return array|PEAR_Error
537     */
538    function _getDepDB()
539    {
540        if (!$this->hasWriteAccess()) {
541            return array('_version' => $this->_version);
542        }
543
544        if (isset($this->_cache)) {
545            return $this->_cache;
546        }
547
548        if (!$fp = fopen($this->_depdb, 'r')) {
549            $err = PEAR::raiseError("Could not open dependencies file `".$this->_depdb."'");
550            return $err;
551        }
552
553        $rt = get_magic_quotes_runtime();
554        set_magic_quotes_runtime(0);
555        clearstatcache();
556        fclose($fp);
557        $data = unserialize(file_get_contents($this->_depdb));
558        set_magic_quotes_runtime($rt);
559        $this->_cache = $data;
560        return $data;
561    }
562
563    /**
564     * Write out the dependency database to disk
565     * @param array the database
566     * @return true|PEAR_Error
567     * @access private
568     */
569    function _writeDepDB(&$deps)
570    {
571        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
572            return $e;
573        }
574
575        if (!$fp = fopen($this->_depdb, 'wb')) {
576            $this->_unlock();
577            return PEAR::raiseError("Could not open dependencies file `".$this->_depdb."' for writing");
578        }
579
580        $rt = get_magic_quotes_runtime();
581        set_magic_quotes_runtime(0);
582        fwrite($fp, serialize($deps));
583        set_magic_quotes_runtime($rt);
584        fclose($fp);
585        $this->_unlock();
586        $this->_cache = $deps;
587        return true;
588    }
589
590    /**
591     * Register all dependencies from a package in the dependencies database, in essence
592     * "installing" the package's dependency information
593     * @param array the database
594     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
595     * @access private
596     */
597    function _setPackageDeps(&$data, &$pkg)
598    {
599        $pkg->setConfig($this->_config);
600        if ($pkg->getPackagexmlVersion() == '1.0') {
601            $gen = &$pkg->getDefaultGenerator();
602            $deps = $gen->dependenciesToV2();
603        } else {
604            $deps = $pkg->getDeps(true);
605        }
606
607        if (!$deps) {
608            return;
609        }
610
611        if (!is_array($data)) {
612            $data = array();
613        }
614
615        if (!isset($data['dependencies'])) {
616            $data['dependencies'] = array();
617        }
618
619        $channel = strtolower($pkg->getChannel());
620        $package = strtolower($pkg->getPackage());
621
622        if (!isset($data['dependencies'][$channel])) {
623            $data['dependencies'][$channel] = array();
624        }
625
626        $data['dependencies'][$channel][$package] = array();
627        if (isset($deps['required']['package'])) {
628            if (!isset($deps['required']['package'][0])) {
629                $deps['required']['package'] = array($deps['required']['package']);
630            }
631
632            foreach ($deps['required']['package'] as $dep) {
633                $this->_registerDep($data, $pkg, $dep, 'required');
634            }
635        }
636
637        if (isset($deps['optional']['package'])) {
638            if (!isset($deps['optional']['package'][0])) {
639                $deps['optional']['package'] = array($deps['optional']['package']);
640            }
641
642            foreach ($deps['optional']['package'] as $dep) {
643                $this->_registerDep($data, $pkg, $dep, 'optional');
644            }
645        }
646
647        if (isset($deps['required']['subpackage'])) {
648            if (!isset($deps['required']['subpackage'][0])) {
649                $deps['required']['subpackage'] = array($deps['required']['subpackage']);
650            }
651
652            foreach ($deps['required']['subpackage'] as $dep) {
653                $this->_registerDep($data, $pkg, $dep, 'required');
654            }
655        }
656
657        if (isset($deps['optional']['subpackage'])) {
658            if (!isset($deps['optional']['subpackage'][0])) {
659                $deps['optional']['subpackage'] = array($deps['optional']['subpackage']);
660            }
661
662            foreach ($deps['optional']['subpackage'] as $dep) {
663                $this->_registerDep($data, $pkg, $dep, 'optional');
664            }
665        }
666
667        if (isset($deps['group'])) {
668            if (!isset($deps['group'][0])) {
669                $deps['group'] = array($deps['group']);
670            }
671
672            foreach ($deps['group'] as $group) {
673                if (isset($group['package'])) {
674                    if (!isset($group['package'][0])) {
675                        $group['package'] = array($group['package']);
676                    }
677
678                    foreach ($group['package'] as $dep) {
679                        $this->_registerDep($data, $pkg, $dep, 'optional',
680                            $group['attribs']['name']);
681                    }
682                }
683
684                if (isset($group['subpackage'])) {
685                    if (!isset($group['subpackage'][0])) {
686                        $group['subpackage'] = array($group['subpackage']);
687                    }
688
689                    foreach ($group['subpackage'] as $dep) {
690                        $this->_registerDep($data, $pkg, $dep, 'optional',
691                            $group['attribs']['name']);
692                    }
693                }
694            }
695        }
696
697        if ($data['dependencies'][$channel][$package] == array()) {
698            unset($data['dependencies'][$channel][$package]);
699            if (!count($data['dependencies'][$channel])) {
700                unset($data['dependencies'][$channel]);
701            }
702        }
703    }
704
705    /**
706     * @param array the database
707     * @param PEAR_PackageFile_v1|PEAR_PackageFile_v2
708     * @param array the specific dependency
709     * @param required|optional whether this is a required or an optional dep
710     * @param string|false dependency group this dependency is from, or false for ordinary dep
711     */
712    function _registerDep(&$data, &$pkg, $dep, $type, $group = false)
713    {
714        $info = array(
715            'dep'   => $dep,
716            'type'  => $type,
717            'group' => $group
718        );
719
720        $dep  = array_map('strtolower', $dep);
721        $depchannel = isset($dep['channel']) ? $dep['channel'] : '__uri';
722        if (!isset($data['dependencies'])) {
723            $data['dependencies'] = array();
724        }
725
726        $channel = strtolower($pkg->getChannel());
727        $package = strtolower($pkg->getPackage());
728
729        if (!isset($data['dependencies'][$channel])) {
730            $data['dependencies'][$channel] = array();
731        }
732
733        if (!isset($data['dependencies'][$channel][$package])) {
734            $data['dependencies'][$channel][$package] = array();
735        }
736
737        $data['dependencies'][$channel][$package][] = $info;
738        if (isset($data['packages'][$depchannel][$dep['name']])) {
739            $found = false;
740            foreach ($data['packages'][$depchannel][$dep['name']] as $i => $p) {
741                if ($p['channel'] == $channel && $p['package'] == $package) {
742                    $found = true;
743                    break;
744                }
745            }
746        } else {
747            if (!isset($data['packages'])) {
748                $data['packages'] = array();
749            }
750
751            if (!isset($data['packages'][$depchannel])) {
752                $data['packages'][$depchannel] = array();
753            }
754
755            if (!isset($data['packages'][$depchannel][$dep['name']])) {
756                $data['packages'][$depchannel][$dep['name']] = array();
757            }
758
759            $found = false;
760        }
761
762        if (!$found) {
763            $data['packages'][$depchannel][$dep['name']][] = array(
764                'channel' => $channel,
765                'package' => $package
766            );
767        }
768    }
769}
Note: See TracBrowser for help on using the repository browser.