PEAR
[ class tree: PEAR ] [ index: PEAR ] [ all elements ]

Source for file Installer.php

Documentation is available at Installer.php

  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 5                                                        |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2004 The PHP Group                                |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license,       |
  9. // | that is bundled with this package in the file LICENSE, and is        |
  10. // | available through the world-wide-web at the following url:           |
  11. // | http://www.php.net/license/3_0.txt.                                  |
  12. // | If you did not receive a copy of the PHP license and are unable to   |
  13. // | obtain it through the world-wide-web, please send a note to          |
  14. // | license@php.net so we can mail you a copy immediately.               |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Stig Bakken <ssb@php.net>                                   |
  17. // |          Tomas V.V.Cox <cox@idecnet.com>                             |
  18. // |          Martin Jansen <mj@php.net>                                  |
  19. // +----------------------------------------------------------------------+
  20. //
  21. // $Id: Installer.php,v 1.150.2.2 2005/02/17 17:47:55 cellog Exp $
  22.  
  23. require_once 'PEAR/Downloader.php';
  24.  
  25. /**
  26.  * Administration class used to install PEAR packages and maintain the
  27.  * installed package database.
  28.  *
  29.  * TODO:
  30.  *   - Check dependencies break on package uninstall (when no force given)
  31.  *   - add a guessInstallDest() method with the code from _installFile() and
  32.  *     use that method in Registry::_rebuildFileMap() & Command_Registry::doList(),
  33.  *     others..
  34.  *
  35.  * @since PHP 4.0.2
  36.  * @author Stig Bakken <ssb@php.net>
  37.  * @author Martin Jansen <mj@php.net>
  38.  * @author Greg Beaver <cellog@php.net>
  39.  */
  40. class PEAR_Installer extends PEAR_Downloader
  41. {
  42.     // {{{ properties
  43.  
  44.     /** name of the package directory, for example Foo-1.0
  45.      * @var string 
  46.      */
  47.     var $pkgdir;
  48.  
  49.     /** directory where PHP code files go
  50.      * @var string 
  51.      */
  52.     var $phpdir;
  53.  
  54.     /** directory where PHP extension files go
  55.      * @var string 
  56.      */
  57.     var $extdir;
  58.  
  59.     /** directory where documentation goes
  60.      * @var string 
  61.      */
  62.     var $docdir;
  63.  
  64.     /** installation root directory (ala PHP's INSTALL_ROOT or
  65.      * automake's DESTDIR
  66.      * @var string 
  67.      */
  68.     var $installroot '';
  69.  
  70.     /** debug level
  71.      * @var int 
  72.      */
  73.     var $debug = 1;
  74.  
  75.     /** temporary directory
  76.      * @var string 
  77.      */
  78.     var $tmpdir;
  79.  
  80.     /** PEAR_Registry object used by the installer
  81.      * @var object 
  82.      */
  83.     var $registry;
  84.  
  85.     /** List of file transactions queued for an install/upgrade/uninstall.
  86.      *
  87.      *  Format:
  88.      *    array(
  89.      *      0 => array("rename => array("from-file", "to-file")),
  90.      *      1 => array("delete" => array("file-to-delete")),
  91.      *      ...
  92.      *    )
  93.      *
  94.      * @var array 
  95.      */
  96.     var $file_operations = array();
  97.  
  98.     // }}}
  99.  
  100.     // {{{ constructor
  101.  
  102.     /**
  103.      * PEAR_Installer constructor.
  104.      *
  105.      * @param object $ui user interface object (instance of PEAR_Frontend_*)
  106.      *
  107.      * @access public
  108.      */
  109.     function PEAR_Installer(&$ui)
  110.     {
  111.         parent::PEAR_Common();
  112.         $this->setFrontendObject($ui);
  113.         $this->debug $this->config->get('verbose');
  114.         //$this->registry = &new PEAR_Registry($this->config->get('php_dir'));
  115.     }
  116.  
  117.     // }}}
  118.  
  119.     // {{{ _deletePackageFiles()
  120.  
  121.     /**
  122.      * Delete a package's installed files, does not remove empty directories.
  123.      *
  124.      * @param string $package package name
  125.      *
  126.      * @return bool TRUE on success, or a PEAR error on failure
  127.      *
  128.      * @access private
  129.      */
  130.     function _deletePackageFiles($package)
  131.     {
  132.         if (!strlen($package)) {
  133.             return $this->raiseError("No package to uninstall given");
  134.         }
  135.         $filelist $this->registry->packageInfo($package'filelist');
  136.         if ($filelist == null{
  137.             return $this->raiseError("$package not installed");
  138.         }
  139.         foreach ($filelist as $file => $props{
  140.             if (empty($props['installed_as'])) {
  141.                 continue;
  142.             }
  143.             $path $this->_prependPath($props['installed_as']$this->installroot);
  144.             $this->addFileOperation('delete'array($path));
  145.         }
  146.         return true;
  147.     }
  148.  
  149.     // }}}
  150.     // {{{ _installFile()
  151.  
  152.     /**
  153.      * @param string filename
  154.      * @param array attributes from <file> tag in package.xml
  155.      * @param string path to install the file in
  156.      * @param array options from command-line
  157.      * @access private
  158.      */
  159.     function _installFile($file$atts$tmp_path$options)
  160.     {
  161.         // {{{ return if this file is meant for another platform
  162.         static $os;
  163.         if (isset($atts['platform'])) {
  164.             if (empty($os)) {
  165.                 include_once "OS/Guess.php";
  166.                 $os = new OS_Guess();
  167.             }
  168.             if (strlen($atts['platform']&& $atts['platform']{0== '!'{
  169.                 $negate = true;
  170.                 $platform substr($atts['platform']1);
  171.             else {
  172.                 $negate = false;
  173.                 $platform $atts['platform'];
  174.             }
  175.             if ((bool) $os->matchSignature($platform=== $negate{
  176.                 $this->log(3"skipped $file (meant for $atts[platform], we are ".$os->getSignature().")");
  177.                 return PEAR_INSTALLER_SKIPPED;
  178.             }
  179.         }
  180.         // }}}
  181.  
  182.         // {{{ assemble the destination paths
  183.         switch ($atts['role']{
  184.             case 'doc':
  185.             case 'data':
  186.             case 'test':
  187.                 $dest_dir $this->config->get($atts['role''_dir'.
  188.                             DIRECTORY_SEPARATOR . $this->pkginfo['package'];
  189.                 unset($atts['baseinstalldir']);
  190.                 break;
  191.             case 'ext':
  192.             case 'php':
  193.                 $dest_dir $this->config->get($atts['role''_dir');
  194.                 break;
  195.             case 'script':
  196.                 $dest_dir $this->config->get('bin_dir');
  197.                 break;
  198.             case 'src':
  199.             case 'extsrc':
  200.                 $this->source_files++;
  201.                 return;
  202.             default:
  203.                 return $this->raiseError("Invalid role `$atts[role]' for file $file");
  204.         }
  205.         $save_destdir $dest_dir;
  206.         if (!empty($atts['baseinstalldir'])) {
  207.             $dest_dir .= DIRECTORY_SEPARATOR . $atts['baseinstalldir'];
  208.         }
  209.         if (dirname($file!= '.' && empty($atts['install-as'])) {
  210.             $dest_dir .= DIRECTORY_SEPARATOR . dirname($file);
  211.         }
  212.         if (empty($atts['install-as'])) {
  213.             $dest_file $dest_dir . DIRECTORY_SEPARATOR . basename($file);
  214.         else {
  215.             $dest_file $dest_dir . DIRECTORY_SEPARATOR . $atts['install-as'];
  216.         }
  217.         $orig_file $tmp_path . DIRECTORY_SEPARATOR . $file;
  218.  
  219.         // Clean up the DIRECTORY_SEPARATOR mess
  220.         $ds2 = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
  221.         list($dest_file$orig_filepreg_replace(array('!\\\\+!''!/!'"!$ds2+!"),
  222.                                                     DIRECTORY_SEPARATOR,
  223.                                                     array($dest_file$orig_file));
  224.         $installed_as $dest_file;
  225.         $final_dest_file $this->_prependPath($dest_file$this->installroot);
  226.         $dest_dir dirname($final_dest_file);
  227.         $dest_file $dest_dir . DIRECTORY_SEPARATOR . '.tmp' basename($final_dest_file);
  228.         // }}}
  229.  
  230.         if (!@is_dir($dest_dir)) {
  231.             if (!$this->mkDirHier($dest_dir)) {
  232.                 return $this->raiseError("failed to mkdir $dest_dir",
  233.                                          PEAR_INSTALLER_FAILED);
  234.             }
  235.             $this->log(3"+ mkdir $dest_dir");
  236.         }
  237.         if (empty($atts['replacements'])) {
  238.             if (!file_exists($orig_file)) {
  239.                 return $this->raiseError("file does not exist",
  240.                                          PEAR_INSTALLER_FAILED);
  241.             }
  242.             if (!@copy($orig_file$dest_file)) {
  243.                 return $this->raiseError("failed to write $dest_file",
  244.                                          PEAR_INSTALLER_FAILED);
  245.             }
  246.             $this->log(3"+ cp $orig_file $dest_file");
  247.             if (isset($atts['md5sum'])) {
  248.                 $md5sum md5_file($dest_file);
  249.             }
  250.         else {
  251.             // {{{ file with replacements
  252.             if (!file_exists($orig_file)) {
  253.                 return $this->raiseError("file does not exist",
  254.                                          PEAR_INSTALLER_FAILED);
  255.             }
  256.             $fp fopen($orig_file"r");
  257.             $contents fread($fpfilesize($orig_file));
  258.             fclose($fp);
  259.             if (isset($atts['md5sum'])) {
  260.                 $md5sum md5($contents);
  261.             }
  262.             $subst_from $subst_to = array();
  263.             foreach ($atts['replacements'as $a{
  264.                 $to '';
  265.                 if ($a['type'== 'php-const'{
  266.                     if (preg_match('/^[a-z0-9_]+$/i'$a['to'])) {
  267.                         eval("\$to = $a[to];");
  268.                     else {
  269.                         $this->log(0"invalid php-const replacement: $a[to]");
  270.                         continue;
  271.                     }
  272.                 elseif ($a['type'== 'pear-config'{
  273.                     $to $this->config->get($a['to']);
  274.                     if (is_null($to)) {
  275.                         $this->log(0"invalid pear-config replacement: $a[to]");
  276.                         continue;
  277.                     }
  278.                 elseif ($a['type'== 'package-info'{
  279.                     if (isset($this->pkginfo[$a['to']]&& is_string($this->pkginfo[$a['to']])) {
  280.                         $to $this->pkginfo[$a['to']];
  281.                     else {
  282.                         $this->log(0"invalid package-info replacement: $a[to]");
  283.                         continue;
  284.                     }
  285.                 }
  286.                 if (!is_null($to)) {
  287.                     $subst_from[$a['from'];
  288.                     $subst_to[$to;
  289.                 }
  290.             }
  291.             $this->log(3"doing ".sizeof($subst_from)." substitution(s) for $final_dest_file");
  292.             if (sizeof($subst_from)) {
  293.                 $contents str_replace($subst_from$subst_to$contents);
  294.             }
  295.             $wp @fopen($dest_file"wb");
  296.             if (!is_resource($wp)) {
  297.                 return $this->raiseError("failed to create $dest_file$php_errormsg",
  298.                                          PEAR_INSTALLER_FAILED);
  299.             }
  300.             if (!fwrite($wp$contents)) {
  301.                 return $this->raiseError("failed writing to $dest_file$php_errormsg",
  302.                                          PEAR_INSTALLER_FAILED);
  303.             }
  304.             fclose($wp);
  305.             // }}}
  306.         }
  307.         // {{{ check the md5
  308.         if (isset($md5sum)) {
  309.             if (strtolower($md5sum== strtolower($atts['md5sum'])) {
  310.                 $this->log(2"md5sum ok: $final_dest_file");
  311.             else {
  312.                 if (empty($options['force'])) {
  313.                     // delete the file
  314.                     @unlink($dest_file);
  315.                     return $this->raiseError("bad md5sum for file $final_dest_file",
  316.                                              PEAR_INSTALLER_FAILED);
  317.                 else {
  318.                     $this->log(0"warning : bad md5sum for file $final_dest_file");
  319.                 }
  320.             }
  321.         }
  322.         // }}}
  323.         // {{{ set file permissions
  324.         if (!OS_WINDOWS{
  325.             if ($atts['role'== 'script'{
  326.                 $mode = 0777 ~(int)octdec($this->config->get('umask'));
  327.                 $this->log(3"+ chmod +x $dest_file");
  328.             else {
  329.                 $mode = 0666 ~(int)octdec($this->config->get('umask'));
  330.             }
  331.             $this->addFileOperation("chmod"array($mode$dest_file));
  332.             if (!@chmod($dest_file$mode)) {
  333.                 $this->log(0"failed to change mode of $dest_file");
  334.             }
  335.         }
  336.         // }}}
  337.         $this->addFileOperation("rename"array($dest_file$final_dest_file));
  338.         // Store the full path where the file was installed for easy unistall
  339.         $this->addFileOperation("installed_as"array($file$installed_as,
  340.                                 $save_destdirdirname(substr($dest_filestrlen($save_destdir)))));
  341.  
  342.         //$this->log(2, "installed: $dest_file");
  343.         return PEAR_INSTALLER_OK;
  344.     }
  345.  
  346.     // }}}
  347.     // {{{ addFileOperation()
  348.  
  349.     /**
  350.      * Add a file operation to the current file transaction.
  351.      *
  352.      * @see startFileTransaction()
  353.      * @var string $type This can be one of:
  354.      *     - rename:  rename a file ($data has 2 values)
  355.      *     - chmod:   change permissions on a file ($data has 2 values)
  356.      *     - delete:  delete a file ($data has 1 value)
  357.      *     - rmdir:   delete a directory if empty ($data has 1 value)
  358.      *     - installed_as: mark a file as installed ($data has 4 values).
  359.      * @var array $data For all file operations, this array must contain the
  360.      *     full path to the file or directory that is being operated on.  For
  361.      *     the rename command, the first parameter must be the file to rename,
  362.      *     the second its new name.
  363.      *
  364.      *     The installed_as operation contains 4 elements in this order:
  365.      *     1. Filename as listed in the filelist element from package.xml
  366.      *     2. Full path to the installed file
  367.      *     3. Full path from the php_dir configuration variable used in this
  368.      *        installation
  369.      *     4. Relative path from the php_dir that this file is installed in
  370.      */
  371.     function addFileOperation($type$data)
  372.     {
  373.         if (!is_array($data)) {
  374.             return $this->raiseError('Internal Error: $data in addFileOperation'
  375.                 . ' must be an array, was ' gettype($data));
  376.         }
  377.         if ($type == 'chmod'{
  378.             $octmode decoct($data[0]);
  379.             $this->log(3"adding to transaction: $type $octmode $data[1]");
  380.         else {
  381.             $this->log(3"adding to transaction: $type " . implode(" "$data));
  382.         }
  383.         $this->file_operations[= array($type$data);
  384.     }
  385.  
  386.     // }}}
  387.     // {{{ startFileTransaction()
  388.  
  389.     function startFileTransaction($rollback_in_case = false)
  390.     {
  391.         if (count($this->file_operations&& $rollback_in_case{
  392.             $this->rollbackFileTransaction();
  393.         }
  394.         $this->file_operations = array();
  395.     }
  396.  
  397.     // }}}
  398.     // {{{ commitFileTransaction()
  399.  
  400.     function commitFileTransaction()
  401.     {
  402.         $n count($this->file_operations);
  403.         $this->log(2"about to commit $n file operations");
  404.         // {{{ first, check permissions and such manually
  405.         $errors = array();
  406.         foreach ($this->file_operations as $tr{
  407.             list($type$data$tr;
  408.             switch ($type{
  409.                 case 'rename':
  410.                     if (!file_exists($data[0])) {
  411.                         $errors[= "cannot rename file $data[0], doesn't exist";
  412.                     }
  413.                     // check that dest dir. is writable
  414.                     if (!is_writable(dirname($data[1]))) {
  415.                         $errors[= "permission denied ($type): $data[1]";
  416.                     }
  417.                     break;
  418.                 case 'chmod':
  419.                     // check that file is writable
  420.                     if (!is_writable($data[1])) {
  421.                         $errors[= "permission denied ($type): $data[1] " . decoct($data[0]);
  422.                     }
  423.                     break;
  424.                 case 'delete':
  425.                     if (!file_exists($data[0])) {
  426.                         $this->log(2"warning: file $data[0] doesn't exist, can't be deleted");
  427.                     }
  428.                     // check that directory is writable
  429.                     if (file_exists($data[0]&& !is_writable(dirname($data[0]))) {
  430.                         $errors[= "permission denied ($type): $data[0]";
  431.                     }
  432.                     break;
  433.             }
  434.  
  435.         }
  436.         // }}}
  437.         $m sizeof($errors);
  438.         if ($m > 0{
  439.             foreach ($errors as $error{
  440.                 $this->log(1$error);
  441.             }
  442.             return false;
  443.         }
  444.         // {{{ really commit the transaction
  445.         foreach ($this->file_operations as $tr{
  446.             list($type$data$tr;
  447.             switch ($type{
  448.                 case 'rename':
  449.                     @unlink($data[1]);
  450.                     @rename($data[0]$data[1]);
  451.                     $this->log(3"+ mv $data[0] $data[1]");
  452.                     break;
  453.                 case 'chmod':
  454.                     @chmod($data[1]$data[0]);
  455.                     $octmode decoct($data[0]);
  456.                     $this->log(3"+ chmod $octmode $data[1]");
  457.                     break;
  458.                 case 'delete':
  459.                     @unlink($data[0]);
  460.                     $this->log(3"+ rm $data[0]");
  461.                     break;
  462.                 case 'rmdir':
  463.                     @rmdir($data[0]);
  464.                     $this->log(3"+ rmdir $data[0]");
  465.                     break;
  466.                 case 'installed_as':
  467.                     $this->pkginfo['filelist'][$data[0]]['installed_as'$data[1];
  468.                     if (!isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
  469.                         $this->pkginfo['filelist']['dirtree'][dirname($data[1])= true;
  470.                         while(!empty($data[3]&& $data[3!= '/' && $data[3!= '\\'
  471.                               && $data[3!= '.'{
  472.                             $this->pkginfo['filelist']['dirtree']
  473.                                 [$this->_prependPath($data[3]$data[2])= true;
  474.                             $data[3dirname($data[3]);
  475.                         }
  476.                     }
  477.                     break;
  478.             }
  479.         }
  480.         // }}}
  481.         $this->log(2"successfully committed $n file operations");
  482.         $this->file_operations = array();
  483.         return true;
  484.     }
  485.  
  486.     // }}}
  487.     // {{{ rollbackFileTransaction()
  488.  
  489.     function rollbackFileTransaction()
  490.     {
  491.         $n count($this->file_operations);
  492.         $this->log(2"rolling back $n file operations");
  493.         foreach ($this->file_operations as $tr{
  494.             list($type$data$tr;
  495.             switch ($type{
  496.                 case 'rename':
  497.                     @unlink($data[0]);
  498.                     $this->log(3"+ rm $data[0]");
  499.                     break;
  500.                 case 'mkdir':
  501.                     @rmdir($data[0]);
  502.                     $this->log(3"+ rmdir $data[0]");
  503.                     break;
  504.                 case 'chmod':
  505.                     break;
  506.                 case 'delete':
  507.                     break;
  508.                 case 'installed_as':
  509.                     if (isset($this->pkginfo['filelist'])) {
  510.                         unset($this->pkginfo['filelist'][$data[0]]['installed_as']);
  511.                     }
  512.                     if (isset($this->pkginfo['filelist']['dirtree'][dirname($data[1])])) {
  513.                         unset($this->pkginfo['filelist']['dirtree'][dirname($data[1])]);
  514.                         while(!empty($data[3]&& $data[3!= '/' && $data[3!= '\\'
  515.                               && $data[3!= '.'{
  516.                             unset($this->pkginfo['filelist']['dirtree']
  517.                                 [$this->_prependPath($data[3]$data[2])]);
  518.                             $data[3dirname($data[3]);
  519.                         }
  520.                     }
  521.                     if (isset($this->pkginfo['filelist']['dirtree'])
  522.                           && !count($this->pkginfo['filelist']['dirtree'])) {
  523.                         unset($this->pkginfo['filelist']['dirtree']);
  524.                     }
  525.                     break;
  526.             }
  527.         }
  528.         $this->file_operations = array();
  529.     }
  530.  
  531.     // }}}
  532.     // {{{ mkDirHier($dir)
  533.  
  534.     function mkDirHier($dir)
  535.     {
  536.         $this->addFileOperation('mkdir'array($dir));
  537.         return parent::mkDirHier($dir);
  538.     }
  539.  
  540.     // }}}
  541.     // {{{ download()
  542.  
  543.     /**
  544.      * Download any files and their dependencies, if necessary
  545.      *
  546.      * @param array a mixed list of package names, local files, or package.xml
  547.      * @param PEAR_Config 
  548.      * @param array options from the command line
  549.      * @param array this is the array that will be populated with packages to
  550.      *               install.  Format of each entry:
  551.      *
  552.      *  <code>
  553.      *  array('pkg' => 'package_name', 'file' => '/path/to/local/file',
  554.      *     'info' => array() // parsed package.xml
  555.      *  );
  556.      *  </code>
  557.      * @param array this will be populated with any error messages
  558.      * @param false private recursion variable
  559.      * @param false private recursion variable
  560.      * @param false private recursion variable
  561.      * @deprecated in favor of PEAR_Downloader
  562.      */
  563.     function download($packages$options&$config&$installpackages,
  564.                       &$errors$installed = false$willinstall = false$state = false)
  565.     {
  566.         // trickiness: initialize here
  567.         parent::PEAR_Downloader($this->ui$options$config);
  568.         $ret = parent::download($packages);
  569.         $errors $this->getErrorMsgs();
  570.         $installpackages $this->getDownloadedPackages();
  571.         trigger_error("PEAR Warning: PEAR_Installer::download() is deprecated " .
  572.                       "in favor of PEAR_Downloader class"E_USER_WARNING);
  573.         return $ret;
  574.     }
  575.  
  576.     // }}}
  577.     // {{{ install()
  578.  
  579.     /**
  580.      * Installs the files within the package file specified.
  581.      *
  582.      * @param string $pkgfile path to the package file
  583.      * @param array $options 
  584.      *  recognized options:
  585.      *  - installroot   : optional prefix directory for installation
  586.      *  - force         : force installation
  587.      *  - register-only : update registry but don't install files
  588.      *  - upgrade       : upgrade existing install
  589.      *  - soft          : fail silently
  590.      *  - nodeps        : ignore dependency conflicts/missing dependencies
  591.      *  - alldeps       : install all dependencies
  592.      *  - onlyreqdeps   : install only required dependencies
  593.      *
  594.      * @return array|PEAR_Errorpackage info if successful
  595.      */
  596.  
  597.     function install($pkgfile$options = array())
  598.     {
  599.         $php_dir $this->config->get('php_dir');
  600.         if (isset($options['installroot'])) {
  601.             if (substr($options['installroot']-1== DIRECTORY_SEPARATOR{
  602.                 $options['installroot'substr($options['installroot']0-1);
  603.             }
  604.             $php_dir $this->_prependPath($php_dir$options['installroot']);
  605.             $this->installroot $options['installroot'];
  606.         else {
  607.             $this->installroot '';
  608.         }
  609.         $this->registry &new PEAR_Registry($php_dir);
  610.         //  ==> XXX should be removed later on
  611.         $flag_old_format = false;
  612.  
  613.         if (substr($pkgfile-4== '.xml'{
  614.             $descfile $pkgfile;
  615.         else {
  616.             // {{{ Decompress pack in tmp dir -------------------------------------
  617.  
  618.             // To allow relative package file names
  619.             $pkgfile realpath($pkgfile);
  620.  
  621.             if (PEAR::isError($tmpdir System::mktemp('-d'))) {
  622.                 return $tmpdir;
  623.             }
  624.             $this->log(3'+ tmp dir created at ' $tmpdir);
  625.  
  626.             $tar = new Archive_Tar($pkgfile);
  627.             if (!@$tar->extract($tmpdir)) {
  628.                 return $this->raiseError("unable to unpack $pkgfile");
  629.             }
  630.  
  631.             // {{{ Look for existing package file
  632.             $descfile $tmpdir . DIRECTORY_SEPARATOR . 'package.xml';
  633.  
  634.             if (!is_file($descfile)) {
  635.                 // ----- Look for old package archive format
  636.                 // In this format the package.xml file was inside the
  637.                 // Package-n.n directory
  638.                 $dp opendir($tmpdir);
  639.                 do {
  640.                     $pkgdir readdir($dp);
  641.                 while ($pkgdir{0== '.');
  642.  
  643.                 $descfile $tmpdir . DIRECTORY_SEPARATOR . $pkgdir . DIRECTORY_SEPARATOR . 'package.xml';
  644.                 $flag_old_format = true;
  645.                 $this->log(0"warning : you are using an archive with an old format");
  646.             }
  647.             // }}}
  648.             // <== XXX This part should be removed later on
  649.             // }}}
  650.         }
  651.  
  652.         if (!is_file($descfile)) {
  653.             return $this->raiseError("no package.xml file after extracting the archive");
  654.         }
  655.  
  656.         // Parse xml file -----------------------------------------------
  657.         $pkginfo $this->infoFromDescriptionFile($descfile);
  658.         if (PEAR::isError($pkginfo)) {
  659.             return $pkginfo;
  660.         }
  661.         $this->validatePackageInfo($pkginfo$errors$warnings);
  662.         // XXX We allow warnings, do we have to do it?
  663.         if (count($errors)) {
  664.             if (empty($options['force'])) {
  665.                 return $this->raiseError("The following errors where found (use force option to install anyway):\n".
  666.                                          implode("\n"$errors));
  667.             else {
  668.                 $this->log(0"warning : the following errors were found:\n".
  669.                            implode("\n"$errors));
  670.             }
  671.         }
  672.  
  673.         $pkgname $pkginfo['package'];
  674.  
  675.         // {{{ Check dependencies -------------------------------------------
  676.         if (isset($pkginfo['release_deps']&& empty($options['nodeps'])) {
  677.             $dep_errors '';
  678.             $error $this->checkDeps($pkginfo$dep_errors);
  679.             if ($error == true{
  680.                 if (empty($options['soft'])) {
  681.                     $this->log(0substr($dep_errors1));
  682.                 }
  683.                 return $this->raiseError("$pkgname: Dependencies failed");
  684.             else if (!empty($dep_errors)) {
  685.                 // Print optional dependencies
  686.                 if (empty($options['soft'])) {
  687.                     $this->log(0$dep_errors);
  688.                 }
  689.             }
  690.         }
  691.         // }}}
  692.  
  693.         // {{{ checks to do when not in "force" mode
  694.         if (empty($options['force'])) {
  695.             $test $this->registry->checkFileMap($pkginfo);
  696.             if (sizeof($test)) {
  697.                 $tmp $test;
  698.                 foreach ($tmp as $file => $pkg{
  699.                     if ($pkg == $pkgname{
  700.                         unset($test[$file]);
  701.                     }
  702.                 }
  703.                 if (sizeof($test)) {
  704.                     $msg = "$pkgname: conflicting files found:\n";
  705.                     $longest max(array_map("strlen"array_keys($test)));
  706.                     $fmt = "%${longest}s (%s)\n";
  707.                     foreach ($test as $file => $pkg{
  708.                         $msg .= sprintf($fmt$file$pkg);
  709.                     }
  710.                     return $this->raiseError($msg);
  711.                 }
  712.             }
  713.         }
  714.         // }}}
  715.  
  716.         $this->startFileTransaction();
  717.  
  718.         if (empty($options['upgrade'])) {
  719.             // checks to do only when installing new packages
  720.             if (empty($options['force']&& $this->registry->packageExists($pkgname)) {
  721.                 return $this->raiseError("$pkgname already installed");
  722.             }
  723.         else {
  724.             if ($this->registry->packageExists($pkgname)) {
  725.                 $v1 $this->registry->packageInfo($pkgname'version');
  726.                 $v2 $pkginfo['version'];
  727.                 $cmp version_compare("$v1""$v2"'gt');
  728.                 if (empty($options['force']&& !version_compare("$v2""$v1"'gt')) {
  729.                     return $this->raiseError("upgrade to a newer version ($v2 is not newer than $v1)");
  730.                 }
  731.                 if (empty($options['register-only'])) {
  732.                     // when upgrading, remove old release's files first:
  733.                     if (PEAR::isError($err $this->_deletePackageFiles($pkgname))) {
  734.                         return $this->raiseError($err);
  735.                     }
  736.                 }
  737.             }
  738.         }
  739.  
  740.         // {{{ Copy files to dest dir ---------------------------------------
  741.  
  742.         // info from the package it self we want to access from _installFile
  743.         $this->pkginfo &$pkginfo;
  744.         // used to determine whether we should build any C code
  745.         $this->source_files = 0;
  746.  
  747.         if (empty($options['register-only'])) {
  748.             if (!is_dir($php_dir)) {
  749.                 return $this->raiseError("no script destination directory\n",
  750.                                          nullPEAR_ERROR_DIE);
  751.             }
  752.  
  753.             $tmp_path dirname($descfile);
  754.             if (substr($pkgfile-4!= '.xml'{
  755.                 $tmp_path .= DIRECTORY_SEPARATOR . $pkgname '-' $pkginfo['version'];
  756.             }
  757.  
  758.             //  ==> XXX This part should be removed later on
  759.             if ($flag_old_format{
  760.                 $tmp_path dirname($descfile);
  761.             }
  762.             // <== XXX This part should be removed later on
  763.  
  764.             // {{{ install files
  765.             foreach ($pkginfo['filelist'as $file => $atts{
  766.                 $this->expectError(PEAR_INSTALLER_FAILED);
  767.                 $res $this->_installFile($file$atts$tmp_path$options);
  768.                 $this->popExpect();
  769.                 if (PEAR::isError($res)) {
  770.                     if (empty($options['ignore-errors'])) {
  771.                         $this->rollbackFileTransaction();
  772.                         if ($res->getMessage(== "file does not exist"{
  773.                             $this->raiseError("file $file in package.xml does not exist");
  774.                         }
  775.                         return $this->raiseError($res);
  776.                     else {
  777.                         $this->log(0"Warning: " $res->getMessage());
  778.                     }
  779.                 }
  780.                 if ($res != PEAR_INSTALLER_OK{
  781.                     // Do not register files that were not installed
  782.                     unset($pkginfo['filelist'][$file]);
  783.                 }
  784.             }
  785.             // }}}
  786.  
  787.             // {{{ compile and install source files
  788.             if ($this->source_files > 0 && empty($options['nobuild'])) {
  789.                 $this->log(1"$this->source_files source files, building");
  790.                 $bob &new PEAR_Builder($this->ui);
  791.                 $bob->debug = $this->debug;
  792.                 $built $bob->build($descfilearray(&$this'_buildCallback'));
  793.                 if (PEAR::isError($built)) {
  794.                     $this->rollbackFileTransaction();
  795.                     return $built;
  796.                 }
  797.                 $this->log(1"\nBuild process completed successfully");
  798.                 foreach ($built as $ext{
  799.                     $bn basename($ext['file']);
  800.                     list($_ext_name$_ext_suffexplode('.'$bn);
  801.                     if ($_ext_suff == '.so' || $_ext_suff == '.dll'{
  802.                         if (extension_loaded($_ext_name)) {
  803.                             $this->raiseError("Extension '$_ext_name' already loaded. " .
  804.                                               'Please unload it in your php.ini file ' .
  805.                                               'prior to install or upgrade');
  806.                         }
  807.                         $role 'ext';
  808.                     else {
  809.                         $role 'src';
  810.                     }
  811.                     $dest $ext['dest'];
  812.                     $this->log(1"Installing '$ext[file]'");
  813.                     $copyto $this->_prependPath($dest$this->installroot);
  814.                     $copydir dirname($copyto);
  815.                     if (!@is_dir($copydir)) {
  816.                         if (!$this->mkDirHier($copydir)) {
  817.                             return $this->raiseError("failed to mkdir $copydir",
  818.                                 PEAR_INSTALLER_FAILED);
  819.                         }
  820.                         $this->log(3"+ mkdir $copydir");
  821.                     }
  822.                     if (!@copy($ext['file']$copyto)) {
  823.                         return $this->raiseError("failed to write $copyto"PEAR_INSTALLER_FAILED);
  824.                     }
  825.                     $this->log(3"+ cp $ext[file] $copyto");
  826.                     if (!OS_WINDOWS{
  827.                         $mode = 0666 ~(int)octdec($this->config->get('umask'));
  828.                         $this->addFileOperation('chmod'array($mode$copyto));
  829.                         if (!@chmod($copyto$mode)) {
  830.                             $this->log(0"failed to change mode of $copyto");
  831.                         }
  832.                     }
  833.                     $this->addFileOperation('rename'array($ext['file']$copyto));
  834.  
  835.                     $pkginfo['filelist'][$bn= array(
  836.                         'role' => $role,
  837.                         'installed_as' => $dest,
  838.                         'php_api' => $ext['php_api'],
  839.                         'zend_mod_api' => $ext['zend_mod_api'],
  840.                         'zend_ext_api' => $ext['zend_ext_api'],
  841.                         );
  842.                 }
  843.             }
  844.             // }}}
  845.         }
  846.  
  847.         if (!$this->commitFileTransaction()) {
  848.             $this->rollbackFileTransaction();
  849.             return $this->raiseError("commit failed"PEAR_INSTALLER_FAILED);
  850.         }
  851.         // }}}
  852.  
  853.         $ret = false;
  854.         // {{{ Register that the package is installed -----------------------
  855.         if (empty($options['upgrade'])) {
  856.             // if 'force' is used, replace the info in registry
  857.             if (!empty($options['force']&& $this->registry->packageExists($pkgname)) {
  858.                 $this->registry->deletePackage($pkgname);
  859.             }
  860.             $ret $this->registry->addPackage($pkgname$pkginfo);
  861.         else {
  862.             // new: upgrade installs a package if it isn't installed
  863.             if (!$this->registry->packageExists($pkgname)) {
  864.                 $ret $this->registry->addPackage($pkgname$pkginfo);
  865.             else {
  866.                 $ret $this->registry->updatePackage($pkgname$pkginfofalse);
  867.             }
  868.         }
  869.         if (!$ret{
  870.             return $this->raiseError("Adding package $pkgname to registry failed");
  871.         }
  872.         // }}}
  873.         return $pkginfo;
  874.     }
  875.  
  876.     // }}}
  877.     // {{{ uninstall()
  878.  
  879.     /**
  880.      * Uninstall a package
  881.      *
  882.      * This method removes all files installed by the application, and then
  883.      * removes any empty directories.
  884.      * @param string package name
  885.      * @param array Command-line options.  Possibilities include:
  886.      *
  887.      *               - installroot: base installation dir, if not the default
  888.      *               - nodeps: do not process dependencies of other packages to ensure
  889.      *                         uninstallation does not break things
  890.      */
  891.     function uninstall($package$options = array())
  892.     {
  893.         $php_dir $this->config->get('php_dir');
  894.         if (isset($options['installroot'])) {
  895.             if (substr($options['installroot']-1== DIRECTORY_SEPARATOR{
  896.                 $options['installroot'substr($options['installroot']0-1);
  897.             }
  898.             $this->installroot $options['installroot'];
  899.             $php_dir $this->_prependPath($php_dir$this->installroot);
  900.         else {
  901.             $this->installroot '';
  902.         }
  903.         $this->registry &new PEAR_Registry($php_dir);
  904.         $filelist $this->registry->packageInfo($package'filelist');
  905.         if ($filelist == null{
  906.             return $this->raiseError("$package not installed");
  907.         }
  908.         if (empty($options['nodeps'])) {
  909.             $depchecker &new PEAR_Dependency($this->registry);
  910.             $error $depchecker->checkPackageUninstall($errors$warning$package);
  911.             if ($error{
  912.                 return $this->raiseError($errors 'uninstall failed');
  913.             }
  914.             if ($warning{
  915.                 $this->log(0$warning);
  916.             }
  917.         }
  918.         // {{{ Delete the files
  919.         $this->startFileTransaction();
  920.         if (PEAR::isError($err $this->_deletePackageFiles($package))) {
  921.             $this->rollbackFileTransaction();
  922.             return $this->raiseError($err);
  923.         }
  924.         if (!$this->commitFileTransaction()) {
  925.             $this->rollbackFileTransaction();
  926.             return $this->raiseError("uninstall failed");
  927.         else {
  928.             $this->startFileTransaction();
  929.             if (!isset($filelist['dirtree']|| !count($filelist['dirtree'])) {
  930.                 return $this->registry->deletePackage($package);
  931.             }
  932.             // attempt to delete empty directories
  933.             uksort($filelist['dirtree']array($this'_sortDirs'));
  934.             foreach($filelist['dirtree'as $dir => $notused{
  935.                 $this->addFileOperation('rmdir'array($dir));
  936.             }
  937.             if (!$this->commitFileTransaction()) {
  938.                 $this->rollbackFileTransaction();
  939.             }
  940.         }
  941.         // }}}
  942.  
  943.         // Register that the package is no longer installed
  944.         return $this->registry->deletePackage($package);
  945.     }
  946.  
  947.     // }}}
  948.     // {{{ _sortDirs()
  949.     function _sortDirs($a$b)
  950.     {
  951.         if (strnatcmp($a$b== -1return 1;
  952.         if (strnatcmp($a$b== 1return -1;
  953.         return 0;
  954.     }
  955.  
  956.     // }}}
  957.     // {{{ checkDeps()
  958.  
  959.     /**
  960.      * Check if the package meets all dependencies
  961.      *
  962.      * @param  array   Package information (passed by reference)
  963.      * @param  string  Error message (passed by reference)
  964.      * @return boolean False when no error occured, otherwise true
  965.      */
  966.     function checkDeps(&$pkginfo&$errors)
  967.     {
  968.         if (empty($this->registry)) {
  969.             $this->registry &new PEAR_Registry($this->config->get('php_dir'));
  970.         }
  971.         $depchecker &new PEAR_Dependency($this->registry);
  972.         $error $errors '';
  973.         $failed_deps $optional_deps = array();
  974.         if (is_array($pkginfo['release_deps'])) {
  975.             foreach($pkginfo['release_deps'as $dep{
  976.                 $code $depchecker->callCheckMethod($error$dep);
  977.                 if ($code{
  978.                     if (isset($dep['optional']&& $dep['optional'== 'yes'{
  979.                         $optional_deps[= array($dep$code$error);
  980.                     else {
  981.                         $failed_deps[= array($dep$code$error);
  982.                     }
  983.                 }
  984.             }
  985.             // {{{ failed dependencies
  986.             $n count($failed_deps);
  987.             if ($n > 0{
  988.                 for ($i = 0; $i $n$i++{
  989.                     if (isset($failed_deps[$i]['type'])) {
  990.                         $type $failed_deps[$i]['type'];
  991.                     else {
  992.                         $type 'pkg';
  993.                     }
  994.                     switch ($failed_deps[$i][1]{
  995.                         case PEAR_DEPENDENCY_MISSING:
  996.                             if ($type == 'pkg'{
  997.                                 // install
  998.                             }
  999.                             $errors .= "\n" $failed_deps[$i][2];
  1000.                             break;
  1001.                         case PEAR_DEPENDENCY_UPGRADE_MINOR:
  1002.                             if ($type == 'pkg'{
  1003.                                 // upgrade
  1004.                             }
  1005.                             $errors .= "\n" $failed_deps[$i][2];
  1006.                             break;
  1007.                         default:
  1008.                             $errors .= "\n" $failed_deps[$i][2];
  1009.                             break;
  1010.                     }
  1011.                 }
  1012.                 return true;
  1013.             }
  1014.             // }}}
  1015.  
  1016.             // {{{ optional dependencies
  1017.             $count_optional count($optional_deps);
  1018.             if ($count_optional > 0{
  1019.                 $errors "Optional dependencies:";
  1020.  
  1021.                 for ($i = 0; $i $count_optional$i++{
  1022.                     if (isset($optional_deps[$i]['type'])) {
  1023.                         $type $optional_deps[$i]['type'];
  1024.                     else {
  1025.                         $type 'pkg';
  1026.                     }
  1027.                     switch ($optional_deps[$i][1]{
  1028.                         case PEAR_DEPENDENCY_MISSING:
  1029.                         case PEAR_DEPENDENCY_UPGRADE_MINOR:
  1030.                         default:
  1031.                             $errors .= "\n" $optional_deps[$i][2];
  1032.                             break;
  1033.                     }
  1034.                 }
  1035.                 return false;
  1036.             }
  1037.             // }}}
  1038.         }
  1039.         return false;
  1040.     }
  1041.  
  1042.     // }}}
  1043.     // {{{ _buildCallback()
  1044.  
  1045.     function _buildCallback($what$data)
  1046.     {
  1047.         if (($what == 'cmdoutput' && $this->debug > 1||
  1048.             ($what == 'output' && $this->debug > 0)) {
  1049.             $this->ui->outputData(rtrim($data)'build');
  1050.         }
  1051.     }
  1052.  
  1053.     // }}}
  1054. }
  1055.  
  1056. // {{{ md5_file() utility function
  1057. if (!function_exists("md5_file")) {
  1058.     function md5_file($filename{
  1059.         $fp fopen($filename"r");
  1060.         if (!$fpreturn null;
  1061.         $contents fread($fpfilesize($filename));
  1062.         fclose($fp);
  1063.         return md5($contents);
  1064.     }
  1065. }
  1066. // }}}
  1067.  
  1068. ?>

Documentation generated on Mon, 11 Mar 2019 14:23:58 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.