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_Command_Install (install, upgrade, upgrade-all, uninstall, bundle, run-scripts commands)
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * @category   pear
8
 * @package    PEAR
9
 * @author     Stig Bakken <ssb@php.net>
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: Install.php 313023 2011-07-06 19:17:11Z dufuz $
14
 * @link       http://pear.php.net/package/PEAR
15
 * @since      File available since Release 0.1
16
 */
17
 
18
/**
19
 * base class
20
 */
21
require_once 'PEAR/Command/Common.php';
22
 
23
/**
24
 * PEAR commands for installation or deinstallation/upgrading of
25
 * packages.
26
 *
27
 * @category   pear
28
 * @package    PEAR
29
 * @author     Stig Bakken <ssb@php.net>
30
 * @author     Greg Beaver <cellog@php.net>
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 0.1
36
 */
37
class PEAR_Command_Install extends PEAR_Command_Common
38
{
39
    // {{{ properties
40
 
41
    var $commands = array(
42
        'install' => array(
43
            'summary' => 'Install Package',
44
            'function' => 'doInstall',
45
            'shortcut' => 'i',
46
            'options' => array(
47
                'force' => array(
48
                    'shortopt' => 'f',
49
                    'doc' => 'will overwrite newer installed packages',
50
                    ),
51
                'loose' => array(
52
                    'shortopt' => 'l',
53
                    'doc' => 'do not check for recommended dependency version',
54
                    ),
55
                'nodeps' => array(
56
                    'shortopt' => 'n',
57
                    'doc' => 'ignore dependencies, install anyway',
58
                    ),
59
                'register-only' => array(
60
                    'shortopt' => 'r',
61
                    'doc' => 'do not install files, only register the package as installed',
62
                    ),
63
                'soft' => array(
64
                    'shortopt' => 's',
65
                    'doc' => 'soft install, fail silently, or upgrade if already installed',
66
                    ),
67
                'nobuild' => array(
68
                    'shortopt' => 'B',
69
                    'doc' => 'don\'t build C extensions',
70
                    ),
71
                'nocompress' => array(
72
                    'shortopt' => 'Z',
73
                    'doc' => 'request uncompressed files when downloading',
74
                    ),
75
                'installroot' => array(
76
                    'shortopt' => 'R',
77
                    'arg' => 'DIR',
78
                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
79
                    ),
80
                'packagingroot' => array(
81
                    'shortopt' => 'P',
82
                    'arg' => 'DIR',
83
                    'doc' => 'root directory used when packaging files, like RPM packaging',
84
                    ),
85
                'ignore-errors' => array(
86
                    'doc' => 'force install even if there were errors',
87
                    ),
88
                'alldeps' => array(
89
                    'shortopt' => 'a',
90
                    'doc' => 'install all required and optional dependencies',
91
                    ),
92
                'onlyreqdeps' => array(
93
                    'shortopt' => 'o',
94
                    'doc' => 'install all required dependencies',
95
                    ),
96
                'offline' => array(
97
                    'shortopt' => 'O',
98
                    'doc' => 'do not attempt to download any urls or contact channels',
99
                    ),
100
                'pretend' => array(
101
                    'shortopt' => 'p',
102
                    'doc' => 'Only list the packages that would be downloaded',
103
                    ),
104
                ),
105
            'doc' => '[channel/]<package> ...
106
Installs one or more PEAR packages.  You can specify a package to
107
install in four ways:
108
 
109
"Package-1.0.tgz" : installs from a local file
110
 
111
"http://example.com/Package-1.0.tgz" : installs from
112
anywhere on the net.
113
 
114
"package.xml" : installs the package described in
115
package.xml.  Useful for testing, or for wrapping a PEAR package in
116
another package manager such as RPM.
117
 
118
"Package[-version/state][.tar]" : queries your default channel\'s server
119
({config master_server}) and downloads the newest package with
120
the preferred quality/state ({config preferred_state}).
121
 
122
To retrieve Package version 1.1, use "Package-1.1," to retrieve
123
Package state beta, use "Package-beta."  To retrieve an uncompressed
124
file, append .tar (make sure there is no file by the same name first)
125
 
126
To download a package from another channel, prefix with the channel name like
127
"channel/Package"
128
 
129
More than one package may be specified at once.  It is ok to mix these
130
four ways of specifying packages.
131
'),
132
        'upgrade' => array(
133
            'summary' => 'Upgrade Package',
134
            'function' => 'doInstall',
135
            'shortcut' => 'up',
136
            'options' => array(
137
                'channel' => array(
138
                    'shortopt' => 'c',
139
                    'doc' => 'upgrade packages from a specific channel',
140
                    'arg' => 'CHAN',
141
                    ),
142
                'force' => array(
143
                    'shortopt' => 'f',
144
                    'doc' => 'overwrite newer installed packages',
145
                    ),
146
                'loose' => array(
147
                    'shortopt' => 'l',
148
                    'doc' => 'do not check for recommended dependency version',
149
                    ),
150
                'nodeps' => array(
151
                    'shortopt' => 'n',
152
                    'doc' => 'ignore dependencies, upgrade anyway',
153
                    ),
154
                'register-only' => array(
155
                    'shortopt' => 'r',
156
                    'doc' => 'do not install files, only register the package as upgraded',
157
                    ),
158
                'nobuild' => array(
159
                    'shortopt' => 'B',
160
                    'doc' => 'don\'t build C extensions',
161
                    ),
162
                'nocompress' => array(
163
                    'shortopt' => 'Z',
164
                    'doc' => 'request uncompressed files when downloading',
165
                    ),
166
                'installroot' => array(
167
                    'shortopt' => 'R',
168
                    'arg' => 'DIR',
169
                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
170
                    ),
171
                'ignore-errors' => array(
172
                    'doc' => 'force install even if there were errors',
173
                    ),
174
                'alldeps' => array(
175
                    'shortopt' => 'a',
176
                    'doc' => 'install all required and optional dependencies',
177
                    ),
178
                'onlyreqdeps' => array(
179
                    'shortopt' => 'o',
180
                    'doc' => 'install all required dependencies',
181
                    ),
182
                'offline' => array(
183
                    'shortopt' => 'O',
184
                    'doc' => 'do not attempt to download any urls or contact channels',
185
                    ),
186
                'pretend' => array(
187
                    'shortopt' => 'p',
188
                    'doc' => 'Only list the packages that would be downloaded',
189
                    ),
190
                ),
191
            'doc' => '<package> ...
192
Upgrades one or more PEAR packages.  See documentation for the
193
"install" command for ways to specify a package.
194
 
195
When upgrading, your package will be updated if the provided new
196
package has a higher version number (use the -f option if you need to
197
upgrade anyway).
198
 
199
More than one package may be specified at once.
200
'),
201
        'upgrade-all' => array(
202
            'summary' => 'Upgrade All Packages [Deprecated in favor of calling upgrade with no parameters]',
203
            'function' => 'doUpgradeAll',
204
            'shortcut' => 'ua',
205
            'options' => array(
206
                'channel' => array(
207
                    'shortopt' => 'c',
208
                    'doc' => 'upgrade packages from a specific channel',
209
                    'arg' => 'CHAN',
210
                    ),
211
                'nodeps' => array(
212
                    'shortopt' => 'n',
213
                    'doc' => 'ignore dependencies, upgrade anyway',
214
                    ),
215
                'register-only' => array(
216
                    'shortopt' => 'r',
217
                    'doc' => 'do not install files, only register the package as upgraded',
218
                    ),
219
                'nobuild' => array(
220
                    'shortopt' => 'B',
221
                    'doc' => 'don\'t build C extensions',
222
                    ),
223
                'nocompress' => array(
224
                    'shortopt' => 'Z',
225
                    'doc' => 'request uncompressed files when downloading',
226
                    ),
227
                'installroot' => array(
228
                    'shortopt' => 'R',
229
                    'arg' => 'DIR',
230
                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT), use packagingroot for RPM',
231
                    ),
232
                'ignore-errors' => array(
233
                    'doc' => 'force install even if there were errors',
234
                    ),
235
                'loose' => array(
236
                    'doc' => 'do not check for recommended dependency version',
237
                    ),
238
                ),
239
            'doc' => '
240
WARNING: This function is deprecated in favor of using the upgrade command with no params
241
 
242
Upgrades all packages that have a newer release available.  Upgrades are
243
done only if there is a release available of the state specified in
244
"preferred_state" (currently {config preferred_state}), or a state considered
245
more stable.
246
'),
247
        'uninstall' => array(
248
            'summary' => 'Un-install Package',
249
            'function' => 'doUninstall',
250
            'shortcut' => 'un',
251
            'options' => array(
252
                'nodeps' => array(
253
                    'shortopt' => 'n',
254
                    'doc' => 'ignore dependencies, uninstall anyway',
255
                    ),
256
                'register-only' => array(
257
                    'shortopt' => 'r',
258
                    'doc' => 'do not remove files, only register the packages as not installed',
259
                    ),
260
                'installroot' => array(
261
                    'shortopt' => 'R',
262
                    'arg' => 'DIR',
263
                    'doc' => 'root directory used when installing files (ala PHP\'s INSTALL_ROOT)',
264
                    ),
265
                'ignore-errors' => array(
266
                    'doc' => 'force install even if there were errors',
267
                    ),
268
                'offline' => array(
269
                    'shortopt' => 'O',
270
                    'doc' => 'do not attempt to uninstall remotely',
271
                    ),
272
                ),
273
            'doc' => '[channel/]<package> ...
274
Uninstalls one or more PEAR packages.  More than one package may be
275
specified at once.  Prefix with channel name to uninstall from a
276
channel not in your default channel ({config default_channel})
277
'),
278
        'bundle' => array(
279
            'summary' => 'Unpacks a Pecl Package',
280
            'function' => 'doBundle',
281
            'shortcut' => 'bun',
282
            'options' => array(
283
                'destination' => array(
284
                   'shortopt' => 'd',
285
                    'arg' => 'DIR',
286
                    'doc' => 'Optional destination directory for unpacking (defaults to current path or "ext" if exists)',
287
                    ),
288
                'force' => array(
289
                    'shortopt' => 'f',
290
                    'doc' => 'Force the unpacking even if there were errors in the package',
291
                ),
292
            ),
293
            'doc' => '<package>
294
Unpacks a Pecl Package into the selected location. It will download the
295
package if needed.
296
'),
297
        'run-scripts' => array(
298
            'summary' => 'Run Post-Install Scripts bundled with a package',
299
            'function' => 'doRunScripts',
300
            'shortcut' => 'rs',
301
            'options' => array(
302
            ),
303
            'doc' => '<package>
304
Run post-installation scripts in package <package>, if any exist.
305
'),
306
    );
307
 
308
    // }}}
309
    // {{{ constructor
310
 
311
    /**
312
     * PEAR_Command_Install constructor.
313
     *
314
     * @access public
315
     */
316
    function PEAR_Command_Install(&$ui, &$config)
317
    {
318
        parent::PEAR_Command_Common($ui, $config);
319
    }
320
 
321
    // }}}
322
 
323
    /**
324
     * For unit testing purposes
325
     */
326
    function &getDownloader(&$ui, $options, &$config)
327
    {
328
        if (!class_exists('PEAR_Downloader')) {
329
            require_once 'PEAR/Downloader.php';
330
        }
331
        $a = &new PEAR_Downloader($ui, $options, $config);
332
        return $a;
333
    }
334
 
335
    /**
336
     * For unit testing purposes
337
     */
338
    function &getInstaller(&$ui)
339
    {
340
        if (!class_exists('PEAR_Installer')) {
341
            require_once 'PEAR/Installer.php';
342
        }
343
        $a = &new PEAR_Installer($ui);
344
        return $a;
345
    }
346
 
347
    function enableExtension($binaries, $type)
348
    {
349
        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
350
            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
351
        }
352
        $ini = $this->_parseIni($phpini);
353
        if (PEAR::isError($ini)) {
354
            return $ini;
355
        }
356
        $line = 0;
357
        if ($type == 'extsrc' || $type == 'extbin') {
358
            $search = 'extensions';
359
            $enable = 'extension';
360
        } else {
361
            $search = 'zend_extensions';
362
            ob_start();
363
            phpinfo(INFO_GENERAL);
364
            $info = ob_get_contents();
365
            ob_end_clean();
366
            $debug = function_exists('leak') ? '_debug' : '';
367
            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
368
            $enable = 'zend_extension' . $debug . $ts;
369
        }
370
        foreach ($ini[$search] as $line => $extension) {
371
            if (in_array($extension, $binaries, true) || in_array(
372
                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
373
                // already enabled - assume if one is, all are
374
                return true;
375
            }
376
        }
377
        if ($line) {
378
            $newini = array_slice($ini['all'], 0, $line);
379
        } else {
380
            $newini = array();
381
        }
382
        foreach ($binaries as $binary) {
383
            if ($ini['extension_dir']) {
384
                $binary = basename($binary);
385
            }
386
            $newini[] = $enable . '="' . $binary . '"' . (OS_UNIX ? "\n" : "\r\n");
387
        }
388
        $newini = array_merge($newini, array_slice($ini['all'], $line));
389
        $fp = @fopen($phpini, 'wb');
390
        if (!$fp) {
391
            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
392
        }
393
        foreach ($newini as $line) {
394
            fwrite($fp, $line);
395
        }
396
        fclose($fp);
397
        return true;
398
    }
399
 
400
    function disableExtension($binaries, $type)
401
    {
402
        if (!($phpini = $this->config->get('php_ini', null, 'pear.php.net'))) {
403
            return PEAR::raiseError('configuration option "php_ini" is not set to php.ini location');
404
        }
405
        $ini = $this->_parseIni($phpini);
406
        if (PEAR::isError($ini)) {
407
            return $ini;
408
        }
409
        $line = 0;
410
        if ($type == 'extsrc' || $type == 'extbin') {
411
            $search = 'extensions';
412
            $enable = 'extension';
413
        } else {
414
            $search = 'zend_extensions';
415
            ob_start();
416
            phpinfo(INFO_GENERAL);
417
            $info = ob_get_contents();
418
            ob_end_clean();
419
            $debug = function_exists('leak') ? '_debug' : '';
420
            $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
421
            $enable = 'zend_extension' . $debug . $ts;
422
        }
423
        $found = false;
424
        foreach ($ini[$search] as $line => $extension) {
425
            if (in_array($extension, $binaries, true) || in_array(
426
                  $ini['extension_dir'] . DIRECTORY_SEPARATOR . $extension, $binaries, true)) {
427
                $found = true;
428
                break;
429
            }
430
        }
431
        if (!$found) {
432
            // not enabled
433
            return true;
434
        }
435
        $fp = @fopen($phpini, 'wb');
436
        if (!$fp) {
437
            return PEAR::raiseError('cannot open php.ini "' . $phpini . '" for writing');
438
        }
439
        if ($line) {
440
            $newini = array_slice($ini['all'], 0, $line);
441
            // delete the enable line
442
            $newini = array_merge($newini, array_slice($ini['all'], $line + 1));
443
        } else {
444
            $newini = array_slice($ini['all'], 1);
445
        }
446
        foreach ($newini as $line) {
447
            fwrite($fp, $line);
448
        }
449
        fclose($fp);
450
        return true;
451
    }
452
 
453
    function _parseIni($filename)
454
    {
455
        if (!file_exists($filename)) {
456
            return PEAR::raiseError('php.ini "' . $filename . '" does not exist');
457
        }
458
 
459
        if (filesize($filename) > 300000) {
460
            return PEAR::raiseError('php.ini "' . $filename . '" is too large, aborting');
461
        }
462
 
463
        ob_start();
464
        phpinfo(INFO_GENERAL);
465
        $info = ob_get_contents();
466
        ob_end_clean();
467
        $debug = function_exists('leak') ? '_debug' : '';
468
        $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
469
        $zend_extension_line = 'zend_extension' . $debug . $ts;
470
        $all = @file($filename);
471
        if (!$all) {
472
            return PEAR::raiseError('php.ini "' . $filename .'" could not be read');
473
        }
474
        $zend_extensions = $extensions = array();
475
        // assume this is right, but pull from the php.ini if it is found
476
        $extension_dir = ini_get('extension_dir');
477
        foreach ($all as $linenum => $line) {
478
            $line = trim($line);
479
            if (!$line) {
480
                continue;
481
            }
482
            if ($line[0] == ';') {
483
                continue;
484
            }
485
            if (strtolower(substr($line, 0, 13)) == 'extension_dir') {
486
                $line = trim(substr($line, 13));
487
                if ($line[0] == '=') {
488
                    $x = trim(substr($line, 1));
489
                    $x = explode(';', $x);
490
                    $extension_dir = str_replace('"', '', array_shift($x));
491
                    continue;
492
                }
493
            }
494
            if (strtolower(substr($line, 0, 9)) == 'extension') {
495
                $line = trim(substr($line, 9));
496
                if ($line[0] == '=') {
497
                    $x = trim(substr($line, 1));
498
                    $x = explode(';', $x);
499
                    $extensions[$linenum] = str_replace('"', '', array_shift($x));
500
                    continue;
501
                }
502
            }
503
            if (strtolower(substr($line, 0, strlen($zend_extension_line))) ==
504
                  $zend_extension_line) {
505
                $line = trim(substr($line, strlen($zend_extension_line)));
506
                if ($line[0] == '=') {
507
                    $x = trim(substr($line, 1));
508
                    $x = explode(';', $x);
509
                    $zend_extensions[$linenum] = str_replace('"', '', array_shift($x));
510
                    continue;
511
                }
512
            }
513
        }
514
        return array(
515
            'extensions' => $extensions,
516
            'zend_extensions' => $zend_extensions,
517
            'extension_dir' => $extension_dir,
518
            'all' => $all,
519
        );
520
    }
521
 
522
    // {{{ doInstall()
523
 
524
    function doInstall($command, $options, $params)
525
    {
526
        if (!class_exists('PEAR_PackageFile')) {
527
            require_once 'PEAR/PackageFile.php';
528
        }
529
 
530
        if (isset($options['installroot']) && isset($options['packagingroot'])) {
531
            return $this->raiseError('ERROR: cannot use both --installroot and --packagingroot');
532
        }
533
 
534
        $reg = &$this->config->getRegistry();
535
        $channel = isset($options['channel']) ? $options['channel'] : $this->config->get('default_channel');
536
        if (!$reg->channelExists($channel)) {
537
            return $this->raiseError('Channel "' . $channel . '" does not exist');
538
        }
539
 
540
        if (empty($this->installer)) {
541
            $this->installer = &$this->getInstaller($this->ui);
542
        }
543
 
544
        if ($command == 'upgrade' || $command == 'upgrade-all') {
545
            // If people run the upgrade command but pass nothing, emulate a upgrade-all
546
            if ($command == 'upgrade' && empty($params)) {
547
                return $this->doUpgradeAll($command, $options, $params);
548
            }
549
            $options['upgrade'] = true;
550
        } else {
551
            $packages = $params;
552
        }
553
 
554
        $instreg = &$reg; // instreg used to check if package is installed
555
        if (isset($options['packagingroot']) && !isset($options['upgrade'])) {
556
            $packrootphp_dir = $this->installer->_prependPath(
557
                $this->config->get('php_dir', null, 'pear.php.net'),
558
                $options['packagingroot']);
559
            $instreg = new PEAR_Registry($packrootphp_dir); // other instreg!
560
 
561
            if ($this->config->get('verbose') > 2) {
562
                $this->ui->outputData('using package root: ' . $options['packagingroot']);
563
            }
564
        }
565
 
566
        $abstractpackages = $otherpackages = array();
567
        // parse params
568
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
569
 
570
        foreach ($params as $param) {
571
            if (strpos($param, 'http://') === 0) {
572
                $otherpackages[] = $param;
573
                continue;
574
            }
575
 
576
            if (strpos($param, 'channel://') === false && @file_exists($param)) {
577
                if (isset($options['force'])) {
578
                    $otherpackages[] = $param;
579
                    continue;
580
                }
581
 
582
                $pkg = new PEAR_PackageFile($this->config);
583
                $pf  = $pkg->fromAnyFile($param, PEAR_VALIDATE_DOWNLOADING);
584
                if (PEAR::isError($pf)) {
585
                    $otherpackages[] = $param;
586
                    continue;
587
                }
588
 
589
                $exists   = $reg->packageExists($pf->getPackage(), $pf->getChannel());
590
                $pversion = $reg->packageInfo($pf->getPackage(), 'version', $pf->getChannel());
591
                $version_compare = version_compare($pf->getVersion(), $pversion, '<=');
592
                if ($exists && $version_compare) {
593
                    if ($this->config->get('verbose')) {
594
                        $this->ui->outputData('Ignoring installed package ' .
595
                            $reg->parsedPackageNameToString(
596
                            array('package' => $pf->getPackage(),
597
                                  'channel' => $pf->getChannel()), true));
598
                    }
599
                    continue;
600
                }
601
                $otherpackages[] = $param;
602
                continue;
603
            }
604
 
605
            $e = $reg->parsePackageName($param, $channel);
606
            if (PEAR::isError($e)) {
607
                $otherpackages[] = $param;
608
            } else {
609
                $abstractpackages[] = $e;
610
            }
611
        }
612
        PEAR::staticPopErrorHandling();
613
 
614
        // if there are any local package .tgz or remote static url, we can't
615
        // filter.  The filter only works for abstract packages
616
        if (count($abstractpackages) && !isset($options['force'])) {
617
            // when not being forced, only do necessary upgrades/installs
618
            if (isset($options['upgrade'])) {
619
                $abstractpackages = $this->_filterUptodatePackages($abstractpackages, $command);
620
            } else {
621
                $count = count($abstractpackages);
622
                foreach ($abstractpackages as $i => $package) {
623
                    if (isset($package['group'])) {
624
                        // do not filter out install groups
625
                        continue;
626
                    }
627
 
628
                    if ($instreg->packageExists($package['package'], $package['channel'])) {
629
                        if ($count > 1) {
630
                            if ($this->config->get('verbose')) {
631
                                $this->ui->outputData('Ignoring installed package ' .
632
                                    $reg->parsedPackageNameToString($package, true));
633
                            }
634
                            unset($abstractpackages[$i]);
635
                        } elseif ($count === 1) {
636
                            // Lets try to upgrade it since it's already installed
637
                            $options['upgrade'] = true;
638
                        }
639
                    }
640
                }
641
            }
642
            $abstractpackages =
643
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
644
        } elseif (count($abstractpackages)) {
645
            $abstractpackages =
646
                array_map(array($reg, 'parsedPackageNameToString'), $abstractpackages);
647
        }
648
 
649
        $packages = array_merge($abstractpackages, $otherpackages);
650
        if (!count($packages)) {
651
            $c = '';
652
            if (isset($options['channel'])){
653
                $c .= ' in channel "' . $options['channel'] . '"';
654
            }
655
            $this->ui->outputData('Nothing to ' . $command . $c);
656
            return true;
657
        }
658
 
659
        $this->downloader = &$this->getDownloader($this->ui, $options, $this->config);
660
        $errors = $downloaded = $binaries = array();
661
        $downloaded = &$this->downloader->download($packages);
662
        if (PEAR::isError($downloaded)) {
663
            return $this->raiseError($downloaded);
664
        }
665
 
666
        $errors = $this->downloader->getErrorMsgs();
667
        if (count($errors)) {
668
            $err = array();
669
            $err['data'] = array();
670
            foreach ($errors as $error) {
671
                if ($error !== null) {
672
                    $err['data'][] = array($error);
673
                }
674
            }
675
 
676
            if (!empty($err['data'])) {
677
                $err['headline'] = 'Install Errors';
678
                $this->ui->outputData($err);
679
            }
680
 
681
            if (!count($downloaded)) {
682
                return $this->raiseError("$command failed");
683
            }
684
        }
685
 
686
        $data = array(
687
            'headline' => 'Packages that would be Installed'
688
        );
689
 
690
        if (isset($options['pretend'])) {
691
            foreach ($downloaded as $package) {
692
                $data['data'][] = array($reg->parsedPackageNameToString($package->getParsedPackage()));
693
            }
694
            $this->ui->outputData($data, 'pretend');
695
            return true;
696
        }
697
 
698
        $this->installer->setOptions($options);
699
        $this->installer->sortPackagesForInstall($downloaded);
700
        if (PEAR::isError($err = $this->installer->setDownloadedPackages($downloaded))) {
701
            $this->raiseError($err->getMessage());
702
            return true;
703
        }
704
 
705
        $binaries = $extrainfo = array();
706
        foreach ($downloaded as $param) {
707
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
708
            $info = $this->installer->install($param, $options);
709
            PEAR::staticPopErrorHandling();
710
            if (PEAR::isError($info)) {
711
                $oldinfo = $info;
712
                $pkg = &$param->getPackageFile();
713
                if ($info->getCode() != PEAR_INSTALLER_NOBINARY) {
714
                    if (!($info = $pkg->installBinary($this->installer))) {
715
                        $this->ui->outputData('ERROR: ' .$oldinfo->getMessage());
716
                        continue;
717
                    }
718
 
719
                    // we just installed a different package than requested,
720
                    // let's change the param and info so that the rest of this works
721
                    $param = $info[0];
722
                    $info  = $info[1];
723
                }
724
            }
725
 
726
            if (!is_array($info)) {
727
                return $this->raiseError("$command failed");
728
            }
729
 
730
            if ($param->getPackageType() == 'extsrc' ||
731
                  $param->getPackageType() == 'extbin' ||
732
                  $param->getPackageType() == 'zendextsrc' ||
733
                  $param->getPackageType() == 'zendextbin'
734
            ) {
735
                $pkg = &$param->getPackageFile();
736
                if ($instbin = $pkg->getInstalledBinary()) {
737
                    $instpkg = &$instreg->getPackage($instbin, $pkg->getChannel());
738
                } else {
739
                    $instpkg = &$instreg->getPackage($pkg->getPackage(), $pkg->getChannel());
740
                }
741
 
742
                foreach ($instpkg->getFilelist() as $name => $atts) {
743
                    $pinfo = pathinfo($atts['installed_as']);
744
                    if (!isset($pinfo['extension']) ||
745
                          in_array($pinfo['extension'], array('c', 'h'))
746
                    ) {
747
                        continue; // make sure we don't match php_blah.h
748
                    }
749
 
750
                    if ((strpos($pinfo['basename'], 'php_') === 0 &&
751
                          $pinfo['extension'] == 'dll') ||
752
                          // most unices
753
                          $pinfo['extension'] == 'so' ||
754
                          // hp-ux
755
                          $pinfo['extension'] == 'sl') {
756
                        $binaries[] = array($atts['installed_as'], $pinfo);
757
                        break;
758
                    }
759
                }
760
 
761
                if (count($binaries)) {
762
                    foreach ($binaries as $pinfo) {
763
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
764
                        $ret = $this->enableExtension(array($pinfo[0]), $param->getPackageType());
765
                        PEAR::staticPopErrorHandling();
766
                        if (PEAR::isError($ret)) {
767
                            $extrainfo[] = $ret->getMessage();
768
                            if ($param->getPackageType() == 'extsrc' ||
769
                                  $param->getPackageType() == 'extbin') {
770
                                $exttype = 'extension';
771
                            } else {
772
                                ob_start();
773
                                phpinfo(INFO_GENERAL);
774
                                $info = ob_get_contents();
775
                                ob_end_clean();
776
                                $debug = function_exists('leak') ? '_debug' : '';
777
                                $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
778
                                $exttype = 'zend_extension' . $debug . $ts;
779
                            }
780
                            $extrainfo[] = 'You should add "' . $exttype . '=' .
781
                                $pinfo[1]['basename'] . '" to php.ini';
782
                        } else {
783
                            $extrainfo[] = 'Extension ' . $instpkg->getProvidesExtension() .
784
                                ' enabled in php.ini';
785
                        }
786
                    }
787
                }
788
            }
789
 
790
            if ($this->config->get('verbose') > 0) {
791
                $chan = $param->getChannel();
792
                $label = $reg->parsedPackageNameToString(
793
                    array(
794
                        'channel' => $chan,
795
                        'package' => $param->getPackage(),
796
                        'version' => $param->getVersion(),
797
                    ));
798
                $out = array('data' => "$command ok: $label");
799
                if (isset($info['release_warnings'])) {
800
                    $out['release_warnings'] = $info['release_warnings'];
801
                }
802
                $this->ui->outputData($out, $command);
803
 
804
                if (!isset($options['register-only']) && !isset($options['offline'])) {
805
                    if ($this->config->isDefinedLayer('ftp')) {
806
                        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
807
                        $info = $this->installer->ftpInstall($param);
808
                        PEAR::staticPopErrorHandling();
809
                        if (PEAR::isError($info)) {
810
                            $this->ui->outputData($info->getMessage());
811
                            $this->ui->outputData("remote install failed: $label");
812
                        } else {
813
                            $this->ui->outputData("remote install ok: $label");
814
                        }
815
                    }
816
                }
817
            }
818
 
819
            $deps = $param->getDeps();
820
            if ($deps) {
821
                if (isset($deps['group'])) {
822
                    $groups = $deps['group'];
823
                    if (!isset($groups[0])) {
824
                        $groups = array($groups);
825
                    }
826
 
827
                    foreach ($groups as $group) {
828
                        if ($group['attribs']['name'] == 'default') {
829
                            // default group is always installed, unless the user
830
                            // explicitly chooses to install another group
831
                            continue;
832
                        }
833
                        $extrainfo[] = $param->getPackage() . ': Optional feature ' .
834
                            $group['attribs']['name'] . ' available (' .
835
                            $group['attribs']['hint'] . ')';
836
                    }
837
 
838
                    $extrainfo[] = $param->getPackage() .
839
                        ': To install optional features use "pear install ' .
840
                        $reg->parsedPackageNameToString(
841
                            array('package' => $param->getPackage(),
842
                                  'channel' => $param->getChannel()), true) .
843
                              '#featurename"';
844
                }
845
            }
846
 
847
            $pkg = &$instreg->getPackage($param->getPackage(), $param->getChannel());
848
            // $pkg may be NULL if install is a 'fake' install via --packagingroot
849
            if (is_object($pkg)) {
850
                $pkg->setConfig($this->config);
851
                if ($list = $pkg->listPostinstallScripts()) {
852
                    $pn = $reg->parsedPackageNameToString(array('channel' =>
853
                       $param->getChannel(), 'package' => $param->getPackage()), true);
854
                    $extrainfo[] = $pn . ' has post-install scripts:';
855
                    foreach ($list as $file) {
856
                        $extrainfo[] = $file;
857
                    }
858
                    $extrainfo[] = $param->getPackage() .
859
                        ': Use "pear run-scripts ' . $pn . '" to finish setup.';
860
                    $extrainfo[] = 'DO NOT RUN SCRIPTS FROM UNTRUSTED SOURCES';
861
                }
862
            }
863
        }
864
 
865
        if (count($extrainfo)) {
866
            foreach ($extrainfo as $info) {
867
                $this->ui->outputData($info);
868
            }
869
        }
870
 
871
        return true;
872
    }
873
 
874
    // }}}
875
    // {{{ doUpgradeAll()
876
 
877
    function doUpgradeAll($command, $options, $params)
878
    {
879
        $reg = &$this->config->getRegistry();
880
        $upgrade = array();
881
 
882
        if (isset($options['channel'])) {
883
            $channels = array($options['channel']);
884
        } else {
885
            $channels = $reg->listChannels();
886
        }
887
 
888
        foreach ($channels as $channel) {
889
            if ($channel == '__uri') {
890
                continue;
891
            }
892
 
893
            // parse name with channel
894
            foreach ($reg->listPackages($channel) as $name) {
895
                $upgrade[] = $reg->parsedPackageNameToString(array(
896
                        'channel' => $channel,
897
                        'package' => $name
898
                    ));
899
            }
900
        }
901
 
902
        $err = $this->doInstall($command, $options, $upgrade);
903
        if (PEAR::isError($err)) {
904
            $this->ui->outputData($err->getMessage(), $command);
905
        }
906
   }
907
 
908
    // }}}
909
    // {{{ doUninstall()
910
 
911
    function doUninstall($command, $options, $params)
912
    {
913
        if (count($params) < 1) {
914
            return $this->raiseError("Please supply the package(s) you want to uninstall");
915
        }
916
 
917
        if (empty($this->installer)) {
918
            $this->installer = &$this->getInstaller($this->ui);
919
        }
920
 
921
        if (isset($options['remoteconfig'])) {
922
            $e = $this->config->readFTPConfigFile($options['remoteconfig']);
923
            if (!PEAR::isError($e)) {
924
                $this->installer->setConfig($this->config);
925
            }
926
        }
927
 
928
        $reg = &$this->config->getRegistry();
929
        $newparams = array();
930
        $binaries = array();
931
        $badparams = array();
932
        foreach ($params as $pkg) {
933
            $channel = $this->config->get('default_channel');
934
            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
935
            $parsed = $reg->parsePackageName($pkg, $channel);
936
            PEAR::staticPopErrorHandling();
937
            if (!$parsed || PEAR::isError($parsed)) {
938
                $badparams[] = $pkg;
939
                continue;
940
            }
941
            $package = $parsed['package'];
942
            $channel = $parsed['channel'];
943
            $info = &$reg->getPackage($package, $channel);
944
            if ($info === null &&
945
                 ($channel == 'pear.php.net' || $channel == 'pecl.php.net')) {
946
                // make sure this isn't a package that has flipped from pear to pecl but
947
                // used a package.xml 1.0
948
                $testc = ($channel == 'pear.php.net') ? 'pecl.php.net' : 'pear.php.net';
949
                $info = &$reg->getPackage($package, $testc);
950
                if ($info !== null) {
951
                    $channel = $testc;
952
                }
953
            }
954
            if ($info === null) {
955
                $badparams[] = $pkg;
956
            } else {
957
                $newparams[] = &$info;
958
                // check for binary packages (this is an alias for those packages if so)
959
                if ($installedbinary = $info->getInstalledBinary()) {
960
                    $this->ui->log('adding binary package ' .
961
                        $reg->parsedPackageNameToString(array('channel' => $channel,
962
                            'package' => $installedbinary), true));
963
                    $newparams[] = &$reg->getPackage($installedbinary, $channel);
964
                }
965
                // add the contents of a dependency group to the list of installed packages
966
                if (isset($parsed['group'])) {
967
                    $group = $info->getDependencyGroup($parsed['group']);
968
                    if ($group) {
969
                        $installed = $reg->getInstalledGroup($group);
970
                        if ($installed) {
971
                            foreach ($installed as $i => $p) {
972
                                $newparams[] = &$installed[$i];
973
                            }
974
                        }
975
                    }
976
                }
977
            }
978
        }
979
        $err = $this->installer->sortPackagesForUninstall($newparams);
980
        if (PEAR::isError($err)) {
981
            $this->ui->outputData($err->getMessage(), $command);
982
            return true;
983
        }
984
        $params = $newparams;
985
        // twist this to use it to check on whether dependent packages are also being uninstalled
986
        // for circular dependencies like subpackages
987
        $this->installer->setUninstallPackages($newparams);
988
        $params = array_merge($params, $badparams);
989
        $binaries = array();
990
        foreach ($params as $pkg) {
991
            $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
992
            if ($err = $this->installer->uninstall($pkg, $options)) {
993
                $this->installer->popErrorHandling();
994
                if (PEAR::isError($err)) {
995
                    $this->ui->outputData($err->getMessage(), $command);
996
                    continue;
997
                }
998
                if ($pkg->getPackageType() == 'extsrc' ||
999
                      $pkg->getPackageType() == 'extbin' ||
1000
                      $pkg->getPackageType() == 'zendextsrc' ||
1001
                      $pkg->getPackageType() == 'zendextbin') {
1002
                    if ($instbin = $pkg->getInstalledBinary()) {
1003
                        continue; // this will be uninstalled later
1004
                    }
1005
 
1006
                    foreach ($pkg->getFilelist() as $name => $atts) {
1007
                        $pinfo = pathinfo($atts['installed_as']);
1008
                        if (!isset($pinfo['extension']) ||
1009
                              in_array($pinfo['extension'], array('c', 'h'))) {
1010
                            continue; // make sure we don't match php_blah.h
1011
                        }
1012
                        if ((strpos($pinfo['basename'], 'php_') === 0 &&
1013
                              $pinfo['extension'] == 'dll') ||
1014
                              // most unices
1015
                              $pinfo['extension'] == 'so' ||
1016
                              // hp-ux
1017
                              $pinfo['extension'] == 'sl') {
1018
                            $binaries[] = array($atts['installed_as'], $pinfo);
1019
                            break;
1020
                        }
1021
                    }
1022
                    if (count($binaries)) {
1023
                        foreach ($binaries as $pinfo) {
1024
                            PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
1025
                            $ret = $this->disableExtension(array($pinfo[0]), $pkg->getPackageType());
1026
                            PEAR::staticPopErrorHandling();
1027
                            if (PEAR::isError($ret)) {
1028
                                $extrainfo[] = $ret->getMessage();
1029
                                if ($pkg->getPackageType() == 'extsrc' ||
1030
                                      $pkg->getPackageType() == 'extbin') {
1031
                                    $exttype = 'extension';
1032
                                } else {
1033
                                    ob_start();
1034
                                    phpinfo(INFO_GENERAL);
1035
                                    $info = ob_get_contents();
1036
                                    ob_end_clean();
1037
                                    $debug = function_exists('leak') ? '_debug' : '';
1038
                                    $ts = preg_match('/Thread Safety.+enabled/', $info) ? '_ts' : '';
1039
                                    $exttype = 'zend_extension' . $debug . $ts;
1040
                                }
1041
                                $this->ui->outputData('Unable to remove "' . $exttype . '=' .
1042
                                    $pinfo[1]['basename'] . '" from php.ini', $command);
1043
                            } else {
1044
                                $this->ui->outputData('Extension ' . $pkg->getProvidesExtension() .
1045
                                    ' disabled in php.ini', $command);
1046
                            }
1047
                        }
1048
                    }
1049
                }
1050
                $savepkg = $pkg;
1051
                if ($this->config->get('verbose') > 0) {
1052
                    if (is_object($pkg)) {
1053
                        $pkg = $reg->parsedPackageNameToString($pkg);
1054
                    }
1055
                    $this->ui->outputData("uninstall ok: $pkg", $command);
1056
                }
1057
                if (!isset($options['offline']) && is_object($savepkg) &&
1058
                      defined('PEAR_REMOTEINSTALL_OK')) {
1059
                    if ($this->config->isDefinedLayer('ftp')) {
1060
                        $this->installer->pushErrorHandling(PEAR_ERROR_RETURN);
1061
                        $info = $this->installer->ftpUninstall($savepkg);
1062
                        $this->installer->popErrorHandling();
1063
                        if (PEAR::isError($info)) {
1064
                            $this->ui->outputData($info->getMessage());
1065
                            $this->ui->outputData("remote uninstall failed: $pkg");
1066
                        } else {
1067
                            $this->ui->outputData("remote uninstall ok: $pkg");
1068
                        }
1069
                    }
1070
                }
1071
            } else {
1072
                $this->installer->popErrorHandling();
1073
                if (!is_object($pkg)) {
1074
                    return $this->raiseError("uninstall failed: $pkg");
1075
                }
1076
                $pkg = $reg->parsedPackageNameToString($pkg);
1077
            }
1078
        }
1079
 
1080
        return true;
1081
    }
1082
 
1083
    // }}}
1084
 
1085
 
1086
    // }}}
1087
    // {{{ doBundle()
1088
    /*
1089
    (cox) It just downloads and untars the package, does not do
1090
            any check that the PEAR_Installer::_installFile() does.
1091
    */
1092
 
1093
    function doBundle($command, $options, $params)
1094
    {
1095
        $opts = array(
1096
            'force'        => true,
1097
            'nodeps'       => true,
1098
            'soft'         => true,
1099
            'downloadonly' => true
1100
        );
1101
        $downloader = &$this->getDownloader($this->ui, $opts, $this->config);
1102
        $reg = &$this->config->getRegistry();
1103
        if (count($params) < 1) {
1104
            return $this->raiseError("Please supply the package you want to bundle");
1105
        }
1106
 
1107
        if (isset($options['destination'])) {
1108
            if (!is_dir($options['destination'])) {
1109
                System::mkdir('-p ' . $options['destination']);
1110
            }
1111
            $dest = realpath($options['destination']);
1112
        } else {
1113
            $pwd  = getcwd();
1114
            $dir  = $pwd . DIRECTORY_SEPARATOR . 'ext';
1115
            $dest = is_dir($dir) ? $dir : $pwd;
1116
        }
1117
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
1118
        $err = $downloader->setDownloadDir($dest);
1119
        PEAR::staticPopErrorHandling();
1120
        if (PEAR::isError($err)) {
1121
            return PEAR::raiseError('download directory "' . $dest .
1122
                '" is not writeable.');
1123
        }
1124
        $result = &$downloader->download(array($params[0]));
1125
        if (PEAR::isError($result)) {
1126
            return $result;
1127
        }
1128
        if (!isset($result[0])) {
1129
            return $this->raiseError('unable to unpack ' . $params[0]);
1130
        }
1131
        $pkgfile = &$result[0]->getPackageFile();
1132
        $pkgname = $pkgfile->getName();
1133
        $pkgversion = $pkgfile->getVersion();
1134
 
1135
        // Unpacking -------------------------------------------------
1136
        $dest .= DIRECTORY_SEPARATOR . $pkgname;
1137
        $orig = $pkgname . '-' . $pkgversion;
1138
 
1139
        $tar = &new Archive_Tar($pkgfile->getArchiveFile());
1140
        if (!$tar->extractModify($dest, $orig)) {
1141
            return $this->raiseError('unable to unpack ' . $pkgfile->getArchiveFile());
1142
        }
1143
        $this->ui->outputData("Package ready at '$dest'");
1144
    // }}}
1145
    }
1146
 
1147
    // }}}
1148
 
1149
    function doRunScripts($command, $options, $params)
1150
    {
1151
        if (!isset($params[0])) {
1152
            return $this->raiseError('run-scripts expects 1 parameter: a package name');
1153
        }
1154
 
1155
        $reg = &$this->config->getRegistry();
1156
        PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
1157
        $parsed = $reg->parsePackageName($params[0], $this->config->get('default_channel'));
1158
        PEAR::staticPopErrorHandling();
1159
        if (PEAR::isError($parsed)) {
1160
            return $this->raiseError($parsed);
1161
        }
1162
 
1163
        $package = &$reg->getPackage($parsed['package'], $parsed['channel']);
1164
        if (!is_object($package)) {
1165
            return $this->raiseError('Could not retrieve package "' . $params[0] . '" from registry');
1166
        }
1167
 
1168
        $package->setConfig($this->config);
1169
        $package->runPostinstallScripts();
1170
        $this->ui->outputData('Install scripts complete', $command);
1171
        return true;
1172
    }
1173
 
1174
    /**
1175
     * Given a list of packages, filter out those ones that are already up to date
1176
     *
1177
     * @param $packages: packages, in parsed array format !
1178
     * @return list of packages that can be upgraded
1179
     */
1180
    function _filterUptodatePackages($packages, $command)
1181
    {
1182
        $reg = &$this->config->getRegistry();
1183
        $latestReleases = array();
1184
 
1185
        $ret = array();
1186
        foreach ($packages as $package) {
1187
            if (isset($package['group'])) {
1188
                $ret[] = $package;
1189
                continue;
1190
            }
1191
 
1192
            $channel = $package['channel'];
1193
            $name    = $package['package'];
1194
            if (!$reg->packageExists($name, $channel)) {
1195
                $ret[] = $package;
1196
                continue;
1197
            }
1198
 
1199
            if (!isset($latestReleases[$channel])) {
1200
                // fill in cache for this channel
1201
                $chan = &$reg->getChannel($channel);
1202
                if (PEAR::isError($chan)) {
1203
                    return $this->raiseError($chan);
1204
                }
1205
 
1206
                $base2 = false;
1207
                $preferred_mirror = $this->config->get('preferred_mirror', null, $channel);
1208
                if ($chan->supportsREST($preferred_mirror) &&
1209
                    (
1210
                       //($base2 = $chan->getBaseURL('REST1.4', $preferred_mirror)) ||
1211
                       ($base  = $chan->getBaseURL('REST1.0', $preferred_mirror))
1212
                    )
1213
                ) {
1214
                    $dorest = true;
1215
                }
1216
 
1217
                PEAR::staticPushErrorHandling(PEAR_ERROR_RETURN);
1218
                if (!isset($package['state'])) {
1219
                    $state = $this->config->get('preferred_state', null, $channel);
1220
                } else {
1221
                    $state = $package['state'];
1222
                }
1223
 
1224
                if ($dorest) {
1225
                    if ($base2) {
1226
                        $rest = &$this->config->getREST('1.4', array());
1227
                        $base = $base2;
1228
                    } else {
1229
                        $rest = &$this->config->getREST('1.0', array());
1230
                    }
1231
 
1232
                    $installed = array_flip($reg->listPackages($channel));
1233
                    $latest    = $rest->listLatestUpgrades($base, $state, $installed, $channel, $reg);
1234
                }
1235
 
1236
                PEAR::staticPopErrorHandling();
1237
                if (PEAR::isError($latest)) {
1238
                    $this->ui->outputData('Error getting channel info from ' . $channel .
1239
                        ': ' . $latest->getMessage());
1240
                    continue;
1241
                }
1242
 
1243
                $latestReleases[$channel] = array_change_key_case($latest);
1244
            }
1245
 
1246
            // check package for latest release
1247
            $name_lower = strtolower($name);
1248
            if (isset($latestReleases[$channel][$name_lower])) {
1249
                // if not set, up to date
1250
                $inst_version    = $reg->packageInfo($name, 'version', $channel);
1251
                $channel_version = $latestReleases[$channel][$name_lower]['version'];
1252
                if (version_compare($channel_version, $inst_version, 'le')) {
1253
                    // installed version is up-to-date
1254
                    continue;
1255
                }
1256
 
1257
                // maintain BC
1258
                if ($command == 'upgrade-all') {
1259
                    $this->ui->outputData(array('data' => 'Will upgrade ' .
1260
                        $reg->parsedPackageNameToString($package)), $command);
1261
                }
1262
                $ret[] = $package;
1263
            }
1264
        }
1265
 
1266
        return $ret;
1267
    }
1268
}