Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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
 */
21
require_once 'PEAR.php';
22
require_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
 */
37
class 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
}