summaryrefslogtreecommitdiff
path: root/pear/PEAR/DependencyDB.php
blob: ce76350a71e3ef4331cadf426d7543c47304b264 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
<?php
//
// +----------------------------------------------------------------------+
// | PHP Version 5                                                        |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group                                |
// +----------------------------------------------------------------------+
// | This source file is subject to version 3.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available through the world-wide-web at the following url:           |
// | http://www.php.net/license/3_0.txt.                                  |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors:   Tomas V.V.Cox <cox@idecnet.com>                           |
// |                                                                      |
// +----------------------------------------------------------------------+
//
// $Id$

/**
Experimental dependencies database handling functions (not yet in production)

<?php
include './DependencyDB.php';
PEAR::setErrorHandling(PEAR_ERROR_DIE);
$dep = PEAR_DependencyDB::singleton('./test.deps', '/home/cox/www/include/pear');
//$dep->rebuildDB();
$a = $dep->checkAction('uninstall', 'net_socket');
print_r($a);
?>

**/

require_once 'PEAR.php';
require_once 'PEAR/Registry.php';
require_once 'PEAR/Dependency.php';

class PEAR_DependencyDB extends PEAR
{
    // {{{ properties

    var $pear_reg = false;
    var $pear_dep = false;
    var $depdb_file = false;
    var $lockfile = false;
    var $lock_fp = false;
    var $depdb_version = '1.0';

    // }}}
    // {{{ & singleton()

    function &singleton($depdb_file, $reg_file)
    {
        $obj = new PEAR_DependencyDB;
        $reg = &new PEAR_Registry($reg_file);
        $obj->pear_reg = $reg;
        $obj->lockfile = $reg->lockfile;
        $obj->pear_dep = new PEAR_Dependency($reg);
        $obj->depdb_file = $depdb_file;
        $obj->assertDepsDB();
        return $obj;
    }

    // }}}
    // {{{ assertDepsDB()

    function assertDepsDB()
    {
        if (!is_file($this->depdb_file)) {
            $this->rebuildDB();
        } else {
            $depdb = $this->_getDepDB();
            // Datatype format has been changed, rebuild the Deps DB
            if ($depdb['depdb_version'] != $this->depdb_version) {
                $this->rebuildDB();
            }
        }
    }

    // }}}
    // {{{ _lock()

    function _lock($mode = LOCK_EX)
    {
        if (!eregi('Windows 9', php_uname())) {
            if ($mode != LOCK_UN && is_resource($this->lock_fp)) {
                // XXX does not check type of lock (LOCK_SH/LOCK_EX)
                return true;
            }
            $open_mode = 'w';
            // XXX People reported problems with LOCK_SH and 'w'
            if ($mode === LOCK_SH) {
                if (@!is_file($this->lockfile)) {
                    touch($this->lockfile);
                }
                $open_mode = 'r';
            }

            $this->lock_fp = @fopen($this->lockfile, $open_mode);

            if (!is_resource($this->lock_fp)) {
                return $this->raiseError("could not create lock file" .
                                         (isset($php_errormsg) ? ": " . $php_errormsg : ""));
            }
            if (!(int)flock($this->lock_fp, $mode)) {
                switch ($mode) {
                    case LOCK_SH: $str = 'shared';    break;
                    case LOCK_EX: $str = 'exclusive'; break;
                    case LOCK_UN: $str = 'unlock';    break;
                    default:      $str = 'unknown';   break;
                }
                return $this->raiseError("could not acquire $str lock ($this->lockfile)");
            }
        }
        return true;
    }

    // }}}
    // {{{ _unlock()

    function _unlock()
    {
        $ret = $this->_lock(LOCK_UN);
        $this->lock_fp = null;
        return $ret;
    }

    // }}}
    // {{{ rebuildDepsFile()

    function rebuildDB()
    {
        $depdb = array('depdb_version' => $this->depdb_version);
        $packages = $this->pear_reg->listPackages();
        foreach ($packages as $package) {
            $deps = $this->pear_reg->packageInfo($package, 'release_deps');
            $this->setPackageDep($depdb, $package, $deps);
        }
        print_r($depdb);
        $error = $this->_writeDepDB($depdb);
        if (PEAR::isError($error)) {
            return $error;
        }
        return true;
    }

    // }}}
    // {{{ & _getDepDB()

    function &_getDepDB()
    {
        if (!$fp = fopen($this->depdb_file, 'r')) {
            $err = $this->raiseError("Could not open dependencies file `".$this->depdb_file."'");
            return $err;
        }
        $data = unserialize(fread($fp, filesize($this->depdb_file)));
        fclose($fp);
        return $data;
    }

    // }}}
    // {{{ _writeDepDB()

    function _writeDepDB(&$deps)
    {
        if (PEAR::isError($e = $this->_lock(LOCK_EX))) {
            return $e;
        }
        if (!$fp = fopen($this->depdb_file, 'wb')) {
            $this->_unlock();
            return $this->raiseError("Could not open dependencies file `".$this->depfile."' for writting");
        }
        fwrite($fp, serialize($deps));
        fclose($fp);
        $this->_unlock();
        return true;
    }

    // }}}
    /*
    // {{{ removePackageDep()
    function removePackageDep($package)
    {
        $data = &$this->_depGetDepDB();
        if (PEAR::isError($data)) {
            return $data;
        }
        // Other packages depends on this package, can't be removed
        if (isset($data['deps'][$package])) {
            return $data['deps'][$package];
        }
        // The package depends on others, remove those dependencies
        if (isset($data['pkgs'][$package])) {
            foreach ($data['pkgs'][$package] as $pkg => $key) {
                // remove the dependency
                unset($data['deps'][$pkg][$key]);
                // if no more dependencies, remove the subject too
                if (!count($data['deps'][$pkg])) {
                    unset($data['deps'][$pkg]);
                }
            }
            // remove the package from the index list
            unset($data['pkgs'][$package]);
        }
        return $this->_depWriteDepDB();
    }
    // }}}
    */

    // {{{ checkAction()

    function checkAction($action, $package, $new_version = null)
    {
        $depdb = &$this->_getDepDB();
        if (PEAR::isError($depdb)) {
            return $depdb;
        }
        $fails = '';
        switch($action) {
            case 'uninstall':
                // Other packages depends on this package, can't be removed
                if (isset($depdb['deps'][$package])) {
                    foreach ($depdb['deps'][$package] as $dep) {
                        if (!$dep['optional']) {
                            $fails .= "Package '" . $dep['depend'] . "' depends on '$package'\n";
                        }
                    }
                    return $fails;
                }
                return true;
            case 'install':
            case 'upgrade':
                // Other packages depend on this package, check deps. Ex:
                // <dep type='pkg' rel='lt' version='1.0'>Foo</dep> and we are trying to
                // update Foo to version 2.0
                if (isset($depdb['deps'][$package])) {
                    foreach ($depdb['deps'][$package] as $dep) {
                        $relation = $dep['rel'];
                        if ($relation == 'not') {
                            $fails .= "Package '" . $dep['depend'] . "' conflicts with '$package'\n";
                        } elseif ($relation != 'has' && $new_version !== null) {
                            if (!version_compare("$new_version", "{$dep['version']}", $relation) &&
                                !$dep['optional']) {
                                $fails .= "Package '" . $dep['depend'] . "' requires ".
                                          "$package " . $this->pear_dep->signOperator($relation) .
                                          " " . $dep['version'];
                            }
                        }
                    }
                    if (isset($fails)) {
                        return $fails;
                    }
                }
                return true;
        }
    }

    // }}}
    // {{{ commitAction()

    function commitAction($action, $package)
    {

    }

    // }}}
    // {{{ setPackageDep()

    /**
    * Update or insert a the dependencies of a package, prechecking
    * that the package won't break any dependency in the process

    The data structure is as follows:
    $dep_db = array(
        // Other packages depends on this packages
        'deps' => array(
            'Package Name' => array(
                0 => array(
                    // This package depends on 'Package Name'
                    'depend' => 'Package',
                    // Which version 'Package' needs of 'Package Name'
                    'version' => '1.0',
                    // The requirement (version_compare() operator)
                    'rel' => 'ge',
                    // whether the dependency is optional
                    'optional' => true/false
                ),
            ),
        )
        // This packages are dependant on other packages
        'pkgs' => array(
            'Package Dependant' => array(
                // This is a index list with paths over the 'deps' array for quick
                // searching things like "what dependecies has this package?"
                // $dep_db['deps']['Package Name'][3]
                'Package Name' => 3 // key in array ['deps']['Package Name']
            ),
        )
    )

    Note: It only supports package dependencies no other type
    */

    function setPackageDep(&$data, $package, $rel_deps = array())
    {
        // This package has no dependencies
        if (!is_array($rel_deps) || !count($rel_deps)) {
            return true;
        }

        // The package depends on others, register that
        foreach ($rel_deps as $dep) {
            // We only support deps of type 'pkg's
            if ($dep && $dep['type'] == 'pkg' && isset($dep['name'])) {
                $dep_name = strtolower($dep['name']);
                $write = array('depend'  => $package,
                               'rel'     => $dep['rel']);
                if ($dep['rel'] != 'has') {
                    $write['version'] = $dep['version'];
                }
                if (isset($dep['optional']) && $dep['optional'] == 'yes') {
                    $write['optional'] = true;
                } else {
                    $write['optional'] = false;
                }
                settype($data['deps'][$dep_name], 'array');

                // The dependency already exists, update it
                if (isset($data['pkgs'][$package][$dep_name])) {
                    $key = $data['pkgs'][$package][$dep_name];
                    $data['deps'][$dep_name][$key] = $write;

                // New dependency, insert it
                } else {
                    $key = count($data['deps'][$dep_name]);
                    $data['deps'][$dep_name][$key] = $write;
                    settype($data['pkgs'][$package], 'array');
                    $data['pkgs'][$package][$dep_name] = $key;
                }
            }
        }
        return true;
    }
    // }}}
}
?>