15 changed files with 0 additions and 9678 deletions
-
208pear/PEAR/Autoloader.php
-
426pear/PEAR/Builder.php
-
398pear/PEAR/Command.php
-
2094pear/PEAR/Common.php
-
1169pear/PEAR/Config.php
-
487pear/PEAR/Dependency.php
-
348pear/PEAR/DependencyDB.php
-
680pear/PEAR/Downloader.php
-
981pear/PEAR/ErrorStack.php
-
359pear/PEAR/Exception.php
-
1068pear/PEAR/Installer.php
-
165pear/PEAR/Packager.php
-
538pear/PEAR/Registry.php
-
394pear/PEAR/Remote.php
-
363pear/PEAR/RunTest.php
@ -1,208 +0,0 @@ |
|||
<?php |
|||
// /* vim: set expandtab tabstop=4 shiftwidth=4: */
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Author: Stig Bakken <ssb@php.net> |
|
|||
// | |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
if (!extension_loaded("overload")) { |
|||
// die hard without ext/overload
|
|||
die("Rebuild PHP with the `overload' extension to use PEAR_Autoloader"); |
|||
} |
|||
|
|||
require_once "PEAR.php"; |
|||
|
|||
/** |
|||
* This class is for objects where you want to separate the code for |
|||
* some methods into separate classes. This is useful if you have a |
|||
* class with not-frequently-used methods that contain lots of code |
|||
* that you would like to avoid always parsing. |
|||
* |
|||
* The PEAR_Autoloader class provides autoloading and aggregation. |
|||
* The autoloading lets you set up in which classes the separated |
|||
* methods are found. Aggregation is the technique used to import new |
|||
* methods, an instance of each class providing separated methods is |
|||
* stored and called every time the aggregated method is called. |
|||
* |
|||
* @author Stig Sæther Bakken <ssb@php.net> |
|||
*/ |
|||
class PEAR_Autoloader extends PEAR |
|||
{ |
|||
// {{{ properties
|
|||
|
|||
/** |
|||
* Map of methods and classes where they are defined |
|||
* |
|||
* @var array |
|||
* |
|||
* @access private |
|||
*/ |
|||
var $_autoload_map = array(); |
|||
|
|||
/** |
|||
* Map of methods and aggregate objects |
|||
* |
|||
* @var array |
|||
* |
|||
* @access private |
|||
*/ |
|||
var $_method_map = array(); |
|||
|
|||
// }}}
|
|||
// {{{ addAutoload()
|
|||
|
|||
/** |
|||
* Add one or more autoload entries. |
|||
* |
|||
* @param string $method which method to autoload |
|||
* |
|||
* @param string $classname (optional) which class to find the method in. |
|||
* If the $method parameter is an array, this |
|||
* parameter may be omitted (and will be ignored |
|||
* if not), and the $method parameter will be |
|||
* treated as an associative array with method |
|||
* names as keys and class names as values. |
|||
* |
|||
* @return void |
|||
* |
|||
* @access public |
|||
*/ |
|||
function addAutoload($method, $classname = null) |
|||
{ |
|||
if (is_array($method)) { |
|||
array_walk($method, create_function('$a,&$b', '$b = strtolower($b);')); |
|||
$this->_autoload_map = array_merge($this->_autoload_map, $method); |
|||
} else { |
|||
$this->_autoload_map[strtolower($method)] = $classname; |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ removeAutoload()
|
|||
|
|||
/** |
|||
* Remove an autoload entry. |
|||
* |
|||
* @param string $method which method to remove the autoload entry for |
|||
* |
|||
* @return bool TRUE if an entry was removed, FALSE if not |
|||
* |
|||
* @access public |
|||
*/ |
|||
function removeAutoload($method) |
|||
{ |
|||
$method = strtolower($method); |
|||
$ok = isset($this->_autoload_map[$method]); |
|||
unset($this->_autoload_map[$method]); |
|||
return $ok; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ addAggregateObject()
|
|||
|
|||
/** |
|||
* Add an aggregate object to this object. If the specified class |
|||
* is not defined, loading it will be attempted following PEAR's |
|||
* file naming scheme. All the methods in the class will be |
|||
* aggregated, except private ones (name starting with an |
|||
* underscore) and constructors. |
|||
* |
|||
* @param string $classname what class to instantiate for the object. |
|||
* |
|||
* @return void |
|||
* |
|||
* @access public |
|||
*/ |
|||
function addAggregateObject($classname) |
|||
{ |
|||
$classname = strtolower($classname); |
|||
if (!class_exists($classname)) { |
|||
$include_file = preg_replace('/[^a-z0-9]/i', '_', $classname); |
|||
include_once $include_file; |
|||
} |
|||
$obj =& new $classname; |
|||
$methods = get_class_methods($classname); |
|||
foreach ($methods as $method) { |
|||
// don't import priviate methods and constructors
|
|||
if ($method{0} != '_' && $method != $classname) { |
|||
$this->_method_map[$method] = $obj; |
|||
} |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ removeAggregateObject()
|
|||
|
|||
/** |
|||
* Remove an aggregate object. |
|||
* |
|||
* @param string $classname the class of the object to remove |
|||
* |
|||
* @return bool TRUE if an object was removed, FALSE if not |
|||
* |
|||
* @access public |
|||
*/ |
|||
function removeAggregateObject($classname) |
|||
{ |
|||
$ok = false; |
|||
$classname = strtolower($classname); |
|||
reset($this->_method_map); |
|||
while (list($method, $obj) = each($this->_method_map)) { |
|||
if (is_a($obj, $classname)) { |
|||
unset($this->_method_map[$method]); |
|||
$ok = true; |
|||
} |
|||
} |
|||
return $ok; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ __call()
|
|||
|
|||
/** |
|||
* Overloaded object call handler, called each time an |
|||
* undefined/aggregated method is invoked. This method repeats |
|||
* the call in the right aggregate object and passes on the return |
|||
* value. |
|||
* |
|||
* @param string $method which method that was called |
|||
* |
|||
* @param string $args An array of the parameters passed in the |
|||
* original call |
|||
* |
|||
* @return mixed The return value from the aggregated method, or a PEAR |
|||
* error if the called method was unknown. |
|||
*/ |
|||
function __call($method, $args, &$retval) |
|||
{ |
|||
$method = strtolower($method); |
|||
if (empty($this->_method_map[$method]) && isset($this->_autoload_map[$method])) { |
|||
$this->addAggregateObject($this->_autoload_map[$method]); |
|||
} |
|||
if (isset($this->_method_map[$method])) { |
|||
$retval = call_user_func_array(array($this->_method_map[$method], $method), $args); |
|||
return true; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// }}}
|
|||
} |
|||
|
|||
overload("PEAR_Autoloader"); |
|||
|
|||
?>
|
|||
@ -1,426 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Authors: Stig Sæther Bakken <ssb@php.net> |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
require_once 'PEAR/Common.php'; |
|||
|
|||
/** |
|||
* Class to handle building (compiling) extensions. |
|||
* |
|||
* @author Stig Sæther Bakken <ssb@php.net> |
|||
*/ |
|||
class PEAR_Builder extends PEAR_Common |
|||
{ |
|||
// {{{ properties
|
|||
|
|||
var $php_api_version = 0; |
|||
var $zend_module_api_no = 0; |
|||
var $zend_extension_api_no = 0; |
|||
|
|||
var $extensions_built = array(); |
|||
|
|||
var $current_callback = null; |
|||
|
|||
// used for msdev builds
|
|||
var $_lastline = null; |
|||
var $_firstline = null; |
|||
// }}}
|
|||
// {{{ constructor
|
|||
|
|||
/** |
|||
* PEAR_Builder constructor. |
|||
* |
|||
* @param object $ui user interface object (instance of PEAR_Frontend_*) |
|||
* |
|||
* @access public |
|||
*/ |
|||
function PEAR_Builder(&$ui) |
|||
{ |
|||
parent::PEAR_Common(); |
|||
$this->setFrontendObject($ui); |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ _build_win32()
|
|||
|
|||
/** |
|||
* Build an extension from source on windows. |
|||
* requires msdev |
|||
*/ |
|||
function _build_win32($descfile, $callback = null) |
|||
{ |
|||
if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) { |
|||
return $info; |
|||
} |
|||
$dir = dirname($descfile); |
|||
$old_cwd = getcwd(); |
|||
|
|||
if (!@chdir($dir)) { |
|||
return $this->raiseError("could not chdir to $dir"); |
|||
} |
|||
$this->log(2, "building in $dir"); |
|||
|
|||
$dsp = $info['package'].'.dsp'; |
|||
if (!@is_file("$dir/$dsp")) { |
|||
return $this->raiseError("The DSP $dsp does not exist."); |
|||
} |
|||
// XXX TODO: make release build type configurable
|
|||
$command = 'msdev '.$dsp.' /MAKE "'.$info['package']. ' - Release"'; |
|||
|
|||
$this->current_callback = $callback; |
|||
$err = $this->_runCommand($command, array(&$this, 'msdevCallback')); |
|||
if (PEAR::isError($err)) { |
|||
return $err; |
|||
} |
|||
|
|||
// figure out the build platform and type
|
|||
$platform = 'Win32'; |
|||
$buildtype = 'Release'; |
|||
if (preg_match('/.*?'.$info['package'].'\s-\s(\w+)\s(.*?)-+/i',$this->_firstline,$matches)) { |
|||
$platform = $matches[1]; |
|||
$buildtype = $matches[2]; |
|||
} |
|||
|
|||
if (preg_match('/(.*)?\s-\s(\d+).*?(\d+)/',$this->_lastline,$matches)) { |
|||
if ($matches[2]) { |
|||
// there were errors in the build
|
|||
return $this->raiseError("There were errors during compilation."); |
|||
} |
|||
$out = $matches[1]; |
|||
} else { |
|||
return $this->raiseError("Did not understand the completion status returned from msdev.exe."); |
|||
} |
|||
|
|||
// msdev doesn't tell us the output directory :/
|
|||
// open the dsp, find /out and use that directory
|
|||
$dsptext = join(file($dsp),''); |
|||
|
|||
// this regex depends on the build platform and type having been
|
|||
// correctly identified above.
|
|||
$regex ='/.*?!IF\s+"\$\(CFG\)"\s+==\s+("'. |
|||
$info['package'].'\s-\s'. |
|||
$platform.'\s'. |
|||
$buildtype.'").*?'. |
|||
'\/out:"(.*?)"/is'; |
|||
|
|||
if ($dsptext && preg_match($regex,$dsptext,$matches)) { |
|||
// what we get back is a relative path to the output file itself.
|
|||
$outfile = realpath($matches[2]); |
|||
} else { |
|||
return $this->raiseError("Could not retrieve output information from $dsp."); |
|||
} |
|||
if (@copy($outfile, "$dir/$out")) { |
|||
$outfile = "$dir/$out"; |
|||
} |
|||
|
|||
$built_files[] = array( |
|||
'file' => "$outfile", |
|||
'php_api' => $this->php_api_version, |
|||
'zend_mod_api' => $this->zend_module_api_no, |
|||
'zend_ext_api' => $this->zend_extension_api_no, |
|||
); |
|||
|
|||
return $built_files; |
|||
} |
|||
// }}}
|
|||
|
|||
// {{{ msdevCallback()
|
|||
function msdevCallback($what, $data) |
|||
{ |
|||
if (!$this->_firstline) |
|||
$this->_firstline = $data; |
|||
$this->_lastline = $data; |
|||
} |
|||
// }}}
|
|||
|
|||
// {{{ _harventInstDir
|
|||
/** |
|||
* @param string |
|||
* @param string |
|||
* @param array |
|||
* @access private |
|||
*/ |
|||
function _harvestInstDir($dest_prefix, $dirname, &$built_files) |
|||
{ |
|||
$d = opendir($dirname); |
|||
if (!$d) |
|||
return false; |
|||
|
|||
$ret = true; |
|||
while (($ent = readdir($d)) !== false) { |
|||
if ($ent{0} == '.') |
|||
continue; |
|||
|
|||
$full = $dirname . DIRECTORY_SEPARATOR . $ent; |
|||
if (is_dir($full)) { |
|||
if (!$this->_harvestInstDir( |
|||
$dest_prefix . DIRECTORY_SEPARATOR . $ent, |
|||
$full, $built_files)) { |
|||
$ret = false; |
|||
break; |
|||
} |
|||
} else { |
|||
$dest = $dest_prefix . DIRECTORY_SEPARATOR . $ent; |
|||
$built_files[] = array( |
|||
'file' => $full, |
|||
'dest' => $dest, |
|||
'php_api' => $this->php_api_version, |
|||
'zend_mod_api' => $this->zend_module_api_no, |
|||
'zend_ext_api' => $this->zend_extension_api_no, |
|||
); |
|||
} |
|||
} |
|||
closedir($d); |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ build()
|
|||
|
|||
/** |
|||
* Build an extension from source. Runs "phpize" in the source |
|||
* directory, but compiles in a temporary directory |
|||
* (/var/tmp/pear-build-USER/PACKAGE-VERSION). |
|||
* |
|||
* @param string $descfile path to XML package description file |
|||
* |
|||
* @param mixed $callback callback function used to report output, |
|||
* see PEAR_Builder::_runCommand for details |
|||
* |
|||
* @return array an array of associative arrays with built files, |
|||
* format: |
|||
* array( array( 'file' => '/path/to/ext.so', |
|||
* 'php_api' => YYYYMMDD, |
|||
* 'zend_mod_api' => YYYYMMDD, |
|||
* 'zend_ext_api' => YYYYMMDD ), |
|||
* ... ) |
|||
* |
|||
* @access public |
|||
* |
|||
* @see PEAR_Builder::_runCommand |
|||
* @see PEAR_Common::infoFromDescriptionFile |
|||
*/ |
|||
function build($descfile, $callback = null) |
|||
{ |
|||
if (PEAR_OS == "Windows") { |
|||
return $this->_build_win32($descfile,$callback); |
|||
} |
|||
if (PEAR_OS != 'Unix') { |
|||
return $this->raiseError("building extensions not supported on this platform"); |
|||
} |
|||
if (PEAR::isError($info = $this->infoFromDescriptionFile($descfile))) { |
|||
return $info; |
|||
} |
|||
$dir = dirname($descfile); |
|||
$old_cwd = getcwd(); |
|||
if (!@chdir($dir)) { |
|||
return $this->raiseError("could not chdir to $dir"); |
|||
} |
|||
$vdir = "$info[package]-$info[version]"; |
|||
if (is_dir($vdir)) { |
|||
chdir($vdir); |
|||
} |
|||
$dir = getcwd(); |
|||
$this->log(2, "building in $dir"); |
|||
$this->current_callback = $callback; |
|||
putenv('PATH=' . $this->config->get('bin_dir') . ':' . getenv('PATH')); |
|||
$err = $this->_runCommand("phpize", array(&$this, 'phpizeCallback')); |
|||
if (PEAR::isError($err)) { |
|||
return $err; |
|||
} |
|||
if (!$err) { |
|||
return $this->raiseError("`phpize' failed"); |
|||
} |
|||
|
|||
// {{{ start of interactive part
|
|||
$configure_command = "$dir/configure"; |
|||
if (isset($info['configure_options'])) { |
|||
foreach ($info['configure_options'] as $o) { |
|||
list($r) = $this->ui->userDialog('build', |
|||
array($o['prompt']), |
|||
array('text'), |
|||
array(@$o['default'])); |
|||
if (substr($o['name'], 0, 5) == 'with-' && |
|||
($r == 'yes' || $r == 'autodetect')) { |
|||
$configure_command .= " --$o[name]"; |
|||
} else { |
|||
$configure_command .= " --$o[name]=".trim($r); |
|||
} |
|||
} |
|||
} |
|||
// }}} end of interactive part
|
|||
|
|||
// FIXME make configurable
|
|||
if(!$user=getenv('USER')){ |
|||
$user='defaultuser'; |
|||
} |
|||
$build_basedir = "/var/tmp/pear-build-$user"; |
|||
$build_dir = "$build_basedir/$info[package]-$info[version]"; |
|||
$inst_dir = "$build_basedir/install-$info[package]-$info[version]"; |
|||
$this->log(1, "building in $build_dir"); |
|||
if (is_dir($build_dir)) { |
|||
System::rm(array('-rf', $build_dir)); |
|||
} |
|||
if (!System::mkDir(array('-p', $build_dir))) { |
|||
return $this->raiseError("could not create build dir: $build_dir"); |
|||
} |
|||
$this->addTempFile($build_dir); |
|||
if (!System::mkDir(array('-p', $inst_dir))) { |
|||
return $this->raiseError("could not create temporary install dir: $inst_dir"); |
|||
} |
|||
$this->addTempFile($inst_dir); |
|||
|
|||
if (getenv('MAKE')) { |
|||
$make_command = getenv('MAKE'); |
|||
} else { |
|||
$make_command = 'make'; |
|||
} |
|||
$to_run = array( |
|||
$configure_command, |
|||
$make_command, |
|||
"$make_command INSTALL_ROOT=\"$inst_dir\" install",
|
|||
"find \"$inst_dir\" -ls"
|
|||
); |
|||
if (!@chdir($build_dir)) { |
|||
return $this->raiseError("could not chdir to $build_dir"); |
|||
} |
|||
putenv('PHP_PEAR_VERSION=@PEAR-VER@'); |
|||
foreach ($to_run as $cmd) { |
|||
$err = $this->_runCommand($cmd, $callback); |
|||
if (PEAR::isError($err)) { |
|||
chdir($old_cwd); |
|||
return $err; |
|||
} |
|||
if (!$err) { |
|||
chdir($old_cwd); |
|||
return $this->raiseError("`$cmd' failed"); |
|||
} |
|||
} |
|||
if (!($dp = opendir("modules"))) { |
|||
chdir($old_cwd); |
|||
return $this->raiseError("no `modules' directory found"); |
|||
} |
|||
$built_files = array(); |
|||
$prefix = exec("php-config --prefix"); |
|||
$this->_harvestInstDir($prefix, $inst_dir . DIRECTORY_SEPARATOR . $prefix, $built_files); |
|||
chdir($old_cwd); |
|||
return $built_files; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ phpizeCallback()
|
|||
|
|||
/** |
|||
* Message callback function used when running the "phpize" |
|||
* program. Extracts the API numbers used. Ignores other message |
|||
* types than "cmdoutput". |
|||
* |
|||
* @param string $what the type of message |
|||
* @param mixed $data the message |
|||
* |
|||
* @return void |
|||
* |
|||
* @access public |
|||
*/ |
|||
function phpizeCallback($what, $data) |
|||
{ |
|||
if ($what != 'cmdoutput') { |
|||
return; |
|||
} |
|||
$this->log(1, rtrim($data)); |
|||
if (preg_match('/You should update your .aclocal.m4/', $data)) { |
|||
return; |
|||
} |
|||
$matches = array(); |
|||
if (preg_match('/^\s+(\S[^:]+):\s+(\d{8})/', $data, $matches)) { |
|||
$member = preg_replace('/[^a-z]/', '_', strtolower($matches[1])); |
|||
$apino = (int)$matches[2]; |
|||
if (isset($this->$member)) { |
|||
$this->$member = $apino; |
|||
//$msg = sprintf("%-22s : %d", $matches[1], $apino);
|
|||
//$this->log(1, $msg);
|
|||
} |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _runCommand()
|
|||
|
|||
/** |
|||
* Run an external command, using a message callback to report |
|||
* output. The command will be run through popen and output is |
|||
* reported for every line with a "cmdoutput" message with the |
|||
* line string, including newlines, as payload. |
|||
* |
|||
* @param string $command the command to run |
|||
* |
|||
* @param mixed $callback (optional) function to use as message |
|||
* callback |
|||
* |
|||
* @return bool whether the command was successful (exit code 0 |
|||
* means success, any other means failure) |
|||
* |
|||
* @access private |
|||
*/ |
|||
function _runCommand($command, $callback = null) |
|||
{ |
|||
$this->log(1, "running: $command"); |
|||
$pp = @popen("$command 2>&1", "r"); |
|||
if (!$pp) { |
|||
return $this->raiseError("failed to run `$command'"); |
|||
} |
|||
if ($callback && $callback[0]->debug == 1) { |
|||
$olddbg = $callback[0]->debug; |
|||
$callback[0]->debug = 2; |
|||
} |
|||
|
|||
while ($line = fgets($pp, 1024)) { |
|||
if ($callback) { |
|||
call_user_func($callback, 'cmdoutput', $line); |
|||
} else { |
|||
$this->log(2, rtrim($line)); |
|||
} |
|||
} |
|||
if ($callback && isset($olddbg)) { |
|||
$callback[0]->debug = $olddbg; |
|||
} |
|||
$exitcode = @pclose($pp); |
|||
return ($exitcode == 0); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ log()
|
|||
|
|||
function log($level, $msg) |
|||
{ |
|||
if ($this->current_callback) { |
|||
if ($this->debug >= $level) { |
|||
call_user_func($this->current_callback, 'output', $msg); |
|||
} |
|||
return; |
|||
} |
|||
return PEAR_Common::log($level, $msg); |
|||
} |
|||
|
|||
// }}}
|
|||
} |
|||
|
|||
?>
|
|||
@ -1,398 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Author: Stig Bakken <ssb@php.net> |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
|
|||
require_once "PEAR.php"; |
|||
|
|||
/** |
|||
* List of commands and what classes they are implemented in. |
|||
* @var array command => implementing class |
|||
*/ |
|||
$GLOBALS['_PEAR_Command_commandlist'] = array(); |
|||
|
|||
/** |
|||
* List of shortcuts to common commands. |
|||
* @var array shortcut => command |
|||
*/ |
|||
$GLOBALS['_PEAR_Command_shortcuts'] = array(); |
|||
|
|||
/** |
|||
* Array of command objects |
|||
* @var array class => object |
|||
*/ |
|||
$GLOBALS['_PEAR_Command_objects'] = array(); |
|||
|
|||
/** |
|||
* Which user interface class is being used. |
|||
* @var string class name |
|||
*/ |
|||
$GLOBALS['_PEAR_Command_uiclass'] = 'PEAR_Frontend_CLI'; |
|||
|
|||
/** |
|||
* Instance of $_PEAR_Command_uiclass. |
|||
* @var object |
|||
*/ |
|||
$GLOBALS['_PEAR_Command_uiobject'] = null; |
|||
|
|||
/** |
|||
* PEAR command class, a simple factory class for administrative |
|||
* commands. |
|||
* |
|||
* How to implement command classes: |
|||
* |
|||
* - The class must be called PEAR_Command_Nnn, installed in the |
|||
* "PEAR/Common" subdir, with a method called getCommands() that |
|||
* returns an array of the commands implemented by the class (see |
|||
* PEAR/Command/Install.php for an example). |
|||
* |
|||
* - The class must implement a run() function that is called with three |
|||
* params: |
|||
* |
|||
* (string) command name |
|||
* (array) assoc array with options, freely defined by each |
|||
* command, for example: |
|||
* array('force' => true) |
|||
* (array) list of the other parameters |
|||
* |
|||
* The run() function returns a PEAR_CommandResponse object. Use |
|||
* these methods to get information: |
|||
* |
|||
* int getStatus() Returns PEAR_COMMAND_(SUCCESS|FAILURE|PARTIAL) |
|||
* *_PARTIAL means that you need to issue at least |
|||
* one more command to complete the operation |
|||
* (used for example for validation steps). |
|||
* |
|||
* string getMessage() Returns a message for the user. Remember, |
|||
* no HTML or other interface-specific markup. |
|||
* |
|||
* If something unexpected happens, run() returns a PEAR error. |
|||
* |
|||
* - DON'T OUTPUT ANYTHING! Return text for output instead. |
|||
* |
|||
* - DON'T USE HTML! The text you return will be used from both Gtk, |
|||
* web and command-line interfaces, so for now, keep everything to |
|||
* plain text. |
|||
* |
|||
* - DON'T USE EXIT OR DIE! Always use pear errors. From static |
|||
* classes do PEAR::raiseError(), from other classes do |
|||
* $this->raiseError(). |
|||
*/ |
|||
class PEAR_Command |
|||
{ |
|||
// {{{ factory()
|
|||
|
|||
/** |
|||
* Get the right object for executing a command. |
|||
* |
|||
* @param string $command The name of the command |
|||
* @param object $config Instance of PEAR_Config object |
|||
* |
|||
* @return object the command object or a PEAR error |
|||
* |
|||
* @access public |
|||
* @static |
|||
*/ |
|||
function factory($command, &$config) |
|||
{ |
|||
if (empty($GLOBALS['_PEAR_Command_commandlist'])) { |
|||
PEAR_Command::registerCommands(); |
|||
} |
|||
if (isset($GLOBALS['_PEAR_Command_shortcuts'][$command])) { |
|||
$command = $GLOBALS['_PEAR_Command_shortcuts'][$command]; |
|||
} |
|||
if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { |
|||
return PEAR::raiseError("unknown command `$command'"); |
|||
} |
|||
$class = $GLOBALS['_PEAR_Command_commandlist'][$command]; |
|||
if (!class_exists($class)) { |
|||
return PEAR::raiseError("unknown command `$command'"); |
|||
} |
|||
$ui =& PEAR_Command::getFrontendObject(); |
|||
$obj = &new $class($ui, $config); |
|||
return $obj; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ & getFrontendObject()
|
|||
|
|||
/** |
|||
* Get instance of frontend object. |
|||
* |
|||
* @return object |
|||
* @static |
|||
*/ |
|||
function &getFrontendObject() |
|||
{ |
|||
if (empty($GLOBALS['_PEAR_Command_uiobject'])) { |
|||
$GLOBALS['_PEAR_Command_uiobject'] = &new $GLOBALS['_PEAR_Command_uiclass']; |
|||
} |
|||
return $GLOBALS['_PEAR_Command_uiobject']; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ & setFrontendClass()
|
|||
|
|||
/** |
|||
* Load current frontend class. |
|||
* |
|||
* @param string $uiclass Name of class implementing the frontend |
|||
* |
|||
* @return object the frontend object, or a PEAR error |
|||
* @static |
|||
*/ |
|||
function &setFrontendClass($uiclass) |
|||
{ |
|||
if (is_object($GLOBALS['_PEAR_Command_uiobject']) && |
|||
is_a($GLOBALS['_PEAR_Command_uiobject'], $uiclass)) { |
|||
return $GLOBALS['_PEAR_Command_uiobject']; |
|||
} |
|||
if (!class_exists($uiclass)) { |
|||
$file = str_replace('_', '/', $uiclass) . '.php'; |
|||
if (PEAR_Command::isIncludeable($file)) { |
|||
include_once $file; |
|||
} |
|||
} |
|||
if (class_exists($uiclass)) { |
|||
$obj = &new $uiclass; |
|||
// quick test to see if this class implements a few of the most
|
|||
// important frontend methods
|
|||
if (method_exists($obj, 'userConfirm')) { |
|||
$GLOBALS['_PEAR_Command_uiobject'] = &$obj; |
|||
$GLOBALS['_PEAR_Command_uiclass'] = $uiclass; |
|||
return $obj; |
|||
} else { |
|||
$err = PEAR::raiseError("not a frontend class: $uiclass"); |
|||
return $err; |
|||
} |
|||
} |
|||
$err = PEAR::raiseError("no such class: $uiclass"); |
|||
return $err; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ setFrontendType()
|
|||
|
|||
// }}}
|
|||
// {{{ isIncludeable()
|
|||
|
|||
/** |
|||
* @param string $path relative or absolute include path |
|||
* @return boolean |
|||
* @static |
|||
*/ |
|||
function isIncludeable($path) |
|||
{ |
|||
if (file_exists($path) && is_readable($path)) { |
|||
return true; |
|||
} |
|||
$ipath = explode(PATH_SEPARATOR, ini_get('include_path')); |
|||
foreach ($ipath as $include) { |
|||
$test = realpath($include . DIRECTORY_SEPARATOR . $path); |
|||
if (file_exists($test) && is_readable($test)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* Set current frontend. |
|||
* |
|||
* @param string $uitype Name of the frontend type (for example "CLI") |
|||
* |
|||
* @return object the frontend object, or a PEAR error |
|||
* @static |
|||
*/ |
|||
function setFrontendType($uitype) |
|||
{ |
|||
$uiclass = 'PEAR_Frontend_' . $uitype; |
|||
return PEAR_Command::setFrontendClass($uiclass); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ registerCommands()
|
|||
|
|||
/** |
|||
* Scan through the Command directory looking for classes |
|||
* and see what commands they implement. |
|||
* |
|||
* @param bool (optional) if FALSE (default), the new list of |
|||
* commands should replace the current one. If TRUE, |
|||
* new entries will be merged with old. |
|||
* |
|||
* @param string (optional) where (what directory) to look for |
|||
* classes, defaults to the Command subdirectory of |
|||
* the directory from where this file (__FILE__) is |
|||
* included. |
|||
* |
|||
* @return bool TRUE on success, a PEAR error on failure |
|||
* |
|||
* @access public |
|||
* @static |
|||
*/ |
|||
function registerCommands($merge = false, $dir = null) |
|||
{ |
|||
if ($dir === null) { |
|||
$dir = dirname(__FILE__) . '/Command'; |
|||
} |
|||
$dp = @opendir($dir); |
|||
if (empty($dp)) { |
|||
return PEAR::raiseError("registerCommands: opendir($dir) failed"); |
|||
} |
|||
if (!$merge) { |
|||
$GLOBALS['_PEAR_Command_commandlist'] = array(); |
|||
} |
|||
while ($entry = readdir($dp)) { |
|||
if ($entry{0} == '.' || substr($entry, -4) != '.php' || $entry == 'Common.php') { |
|||
continue; |
|||
} |
|||
$class = "PEAR_Command_".substr($entry, 0, -4); |
|||
$file = "$dir/$entry"; |
|||
include_once $file; |
|||
// List of commands
|
|||
if (empty($GLOBALS['_PEAR_Command_objects'][$class])) { |
|||
$GLOBALS['_PEAR_Command_objects'][$class] = &new $class($ui, $config); |
|||
} |
|||
$implements = $GLOBALS['_PEAR_Command_objects'][$class]->getCommands(); |
|||
foreach ($implements as $command => $desc) { |
|||
$GLOBALS['_PEAR_Command_commandlist'][$command] = $class; |
|||
$GLOBALS['_PEAR_Command_commanddesc'][$command] = $desc; |
|||
} |
|||
$shortcuts = $GLOBALS['_PEAR_Command_objects'][$class]->getShortcuts(); |
|||
foreach ($shortcuts as $shortcut => $command) { |
|||
$GLOBALS['_PEAR_Command_shortcuts'][$shortcut] = $command; |
|||
} |
|||
} |
|||
@closedir($dp); |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ getCommands()
|
|||
|
|||
/** |
|||
* Get the list of currently supported commands, and what |
|||
* classes implement them. |
|||
* |
|||
* @return array command => implementing class |
|||
* |
|||
* @access public |
|||
* @static |
|||
*/ |
|||
function getCommands() |
|||
{ |
|||
if (empty($GLOBALS['_PEAR_Command_commandlist'])) { |
|||
PEAR_Command::registerCommands(); |
|||
} |
|||
return $GLOBALS['_PEAR_Command_commandlist']; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ getShortcuts()
|
|||
|
|||
/** |
|||
* Get the list of command shortcuts. |
|||
* |
|||
* @return array shortcut => command |
|||
* |
|||
* @access public |
|||
* @static |
|||
*/ |
|||
function getShortcuts() |
|||
{ |
|||
if (empty($GLOBALS['_PEAR_Command_shortcuts'])) { |
|||
PEAR_Command::registerCommands(); |
|||
} |
|||
return $GLOBALS['_PEAR_Command_shortcuts']; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ getGetoptArgs()
|
|||
|
|||
/** |
|||
* Compiles arguments for getopt. |
|||
* |
|||
* @param string $command command to get optstring for |
|||
* @param string $short_args (reference) short getopt format |
|||
* @param array $long_args (reference) long getopt format |
|||
* |
|||
* @return void |
|||
* |
|||
* @access public |
|||
* @static |
|||
*/ |
|||
function getGetoptArgs($command, &$short_args, &$long_args) |
|||
{ |
|||
if (empty($GLOBALS['_PEAR_Command_commandlist'])) { |
|||
PEAR_Command::registerCommands(); |
|||
} |
|||
if (!isset($GLOBALS['_PEAR_Command_commandlist'][$command])) { |
|||
return null; |
|||
} |
|||
$class = $GLOBALS['_PEAR_Command_commandlist'][$command]; |
|||
$obj = &$GLOBALS['_PEAR_Command_objects'][$class]; |
|||
return $obj->getGetoptArgs($command, $short_args, $long_args); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ getDescription()
|
|||
|
|||
/** |
|||
* Get description for a command. |
|||
* |
|||
* @param string $command Name of the command |
|||
* |
|||
* @return string command description |
|||
* |
|||
* @access public |
|||
* @static |
|||
*/ |
|||
function getDescription($command) |
|||
{ |
|||
if (!isset($GLOBALS['_PEAR_Command_commanddesc'][$command])) { |
|||
return null; |
|||
} |
|||
return $GLOBALS['_PEAR_Command_commanddesc'][$command]; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ getHelp()
|
|||
|
|||
/** |
|||
* Get help for command. |
|||
* |
|||
* @param string $command Name of the command to return help for |
|||
* |
|||
* @access public |
|||
* @static |
|||
*/ |
|||
function getHelp($command) |
|||
{ |
|||
$cmds = PEAR_Command::getCommands(); |
|||
if (isset($cmds[$command])) { |
|||
$class = $cmds[$command]; |
|||
return $GLOBALS['_PEAR_Command_objects'][$class]->getHelp($command); |
|||
} |
|||
return false; |
|||
} |
|||
// }}}
|
|||
} |
|||
|
|||
?>
|
|||
2094
pear/PEAR/Common.php
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
1169
pear/PEAR/Config.php
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,487 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Authors: Tomas V.V.Cox <cox@idecnet.com> |
|
|||
// | Stig Bakken <ssb@php.net> |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
require_once "PEAR.php"; |
|||
|
|||
define('PEAR_DEPENDENCY_MISSING', -1); |
|||
define('PEAR_DEPENDENCY_CONFLICT', -2); |
|||
define('PEAR_DEPENDENCY_UPGRADE_MINOR', -3); |
|||
define('PEAR_DEPENDENCY_UPGRADE_MAJOR', -4); |
|||
define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5); |
|||
define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6); |
|||
define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL', -7); |
|||
define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL', -8); |
|||
define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL', -9); |
|||
|
|||
/** |
|||
* Dependency check for PEAR packages |
|||
* |
|||
* The class is based on the dependency RFC that can be found at |
|||
* http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1 |
|||
* |
|||
* @author Tomas V.V.Vox <cox@idecnet.com> |
|||
* @author Stig Bakken <ssb@php.net> |
|||
*/ |
|||
class PEAR_Dependency |
|||
{ |
|||
// {{{ constructor
|
|||
/** |
|||
* Constructor |
|||
* |
|||
* @access public |
|||
* @param object Registry object |
|||
* @return void |
|||
*/ |
|||
function PEAR_Dependency(&$registry) |
|||
{ |
|||
$this->registry = &$registry; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ callCheckMethod()
|
|||
|
|||
/** |
|||
* This method maps the XML dependency definition to the |
|||
* corresponding one from PEAR_Dependency |
|||
* |
|||
* <pre> |
|||
* $opts => Array |
|||
* ( |
|||
* [type] => pkg |
|||
* [rel] => ge |
|||
* [version] => 3.4 |
|||
* [name] => HTML_Common |
|||
* [optional] => false |
|||
* ) |
|||
* </pre> |
|||
* |
|||
* @param string Error message |
|||
* @param array Options |
|||
* @return boolean |
|||
*/ |
|||
function callCheckMethod(&$errmsg, $opts) |
|||
{ |
|||
$rel = isset($opts['rel']) ? $opts['rel'] : 'has'; |
|||
$req = isset($opts['version']) ? $opts['version'] : null; |
|||
$name = isset($opts['name']) ? $opts['name'] : null; |
|||
$opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ? |
|||
$opts['optional'] : null; |
|||
$errmsg = ''; |
|||
switch ($opts['type']) { |
|||
case 'pkg': |
|||
return $this->checkPackage($errmsg, $name, $req, $rel, $opt); |
|||
break; |
|||
case 'ext': |
|||
return $this->checkExtension($errmsg, $name, $req, $rel, $opt); |
|||
break; |
|||
case 'php': |
|||
return $this->checkPHP($errmsg, $req, $rel); |
|||
break; |
|||
case 'prog': |
|||
return $this->checkProgram($errmsg, $name); |
|||
break; |
|||
case 'os': |
|||
return $this->checkOS($errmsg, $name); |
|||
break; |
|||
case 'sapi': |
|||
return $this->checkSAPI($errmsg, $name); |
|||
break; |
|||
case 'zend': |
|||
return $this->checkZend($errmsg, $name); |
|||
break; |
|||
default: |
|||
return "'{$opts['type']}' dependency type not supported"; |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkPackage()
|
|||
|
|||
/** |
|||
* Package dependencies check method |
|||
* |
|||
* @param string $errmsg Empty string, it will be populated with an error message, if any |
|||
* @param string $name Name of the package to test |
|||
* @param string $req The package version required |
|||
* @param string $relation How to compare versions with each other |
|||
* @param bool $opt Whether the relationship is optional |
|||
* |
|||
* @return mixed bool false if no error or the error string |
|||
*/ |
|||
function checkPackage(&$errmsg, $name, $req = null, $relation = 'has', |
|||
$opt = false) |
|||
{ |
|||
if (is_string($req) && substr($req, 0, 2) == 'v.') { |
|||
$req = substr($req, 2); |
|||
} |
|||
switch ($relation) { |
|||
case 'has': |
|||
if (!$this->registry->packageExists($name)) { |
|||
if ($opt) { |
|||
$errmsg = "package `$name' is recommended to utilize some features."; |
|||
return PEAR_DEPENDENCY_MISSING_OPTIONAL; |
|||
} |
|||
$errmsg = "requires package `$name'"; |
|||
return PEAR_DEPENDENCY_MISSING; |
|||
} |
|||
return false; |
|||
case 'not': |
|||
if ($this->registry->packageExists($name)) { |
|||
$errmsg = "conflicts with package `$name'"; |
|||
return PEAR_DEPENDENCY_CONFLICT; |
|||
} |
|||
return false; |
|||
case 'lt': |
|||
case 'le': |
|||
case 'eq': |
|||
case 'ne': |
|||
case 'ge': |
|||
case 'gt': |
|||
$version = $this->registry->packageInfo($name, 'version'); |
|||
if (!$this->registry->packageExists($name) |
|||
|| !version_compare("$version", "$req", $relation)) |
|||
{ |
|||
$code = $this->codeFromRelation($relation, $version, $req, $opt); |
|||
if ($opt) { |
|||
$errmsg = "package `$name' version " . $this->signOperator($relation) . |
|||
" $req is recommended to utilize some features."; |
|||
if ($version) { |
|||
$errmsg .= " Installed version is $version"; |
|||
} |
|||
return $code; |
|||
} |
|||
$errmsg = "requires package `$name' " . |
|||
$this->signOperator($relation) . " $req"; |
|||
return $code; |
|||
} |
|||
return false; |
|||
} |
|||
$errmsg = "relation '$relation' with requirement '$req' is not supported (name=$name)"; |
|||
return PEAR_DEPENDENCY_BAD_DEPENDENCY; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkPackageUninstall()
|
|||
|
|||
/** |
|||
* Check package dependencies on uninstall |
|||
* |
|||
* @param string $error The resultant error string |
|||
* @param string $warning The resultant warning string |
|||
* @param string $name Name of the package to test |
|||
* |
|||
* @return bool true if there were errors |
|||
*/ |
|||
function checkPackageUninstall(&$error, &$warning, $package) |
|||
{ |
|||
$error = null; |
|||
$packages = $this->registry->listPackages(); |
|||
foreach ($packages as $pkg) { |
|||
if ($pkg == $package) { |
|||
continue; |
|||
} |
|||
$deps = $this->registry->packageInfo($pkg, 'release_deps'); |
|||
if (empty($deps)) { |
|||
continue; |
|||
} |
|||
foreach ($deps as $dep) { |
|||
if ($dep['type'] == 'pkg' && strcasecmp($dep['name'], $package) == 0) { |
|||
if ($dep['rel'] == 'ne' || $dep['rel'] == 'not') { |
|||
continue; |
|||
} |
|||
if (isset($dep['optional']) && $dep['optional'] == 'yes') { |
|||
$warning .= "\nWarning: Package '$pkg' optionally depends on '$package'"; |
|||
} else { |
|||
$error .= "Package '$pkg' depends on '$package'\n"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return ($error) ? true : false; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkExtension()
|
|||
|
|||
/** |
|||
* Extension dependencies check method |
|||
* |
|||
* @param string $name Name of the extension to test |
|||
* @param string $req_ext_ver Required extension version to compare with |
|||
* @param string $relation How to compare versions with eachother |
|||
* @param bool $opt Whether the relationship is optional |
|||
* |
|||
* @return mixed bool false if no error or the error string |
|||
*/ |
|||
function checkExtension(&$errmsg, $name, $req = null, $relation = 'has', |
|||
$opt = false) |
|||
{ |
|||
if ($relation == 'not') { |
|||
if (extension_loaded($name)) { |
|||
$errmsg = "conflicts with PHP extension '$name'"; |
|||
return PEAR_DEPENDENCY_CONFLICT; |
|||
} else { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
if (!extension_loaded($name)) { |
|||
if ($relation == 'not') { |
|||
return false; |
|||
} |
|||
if ($opt) { |
|||
$errmsg = "'$name' PHP extension is recommended to utilize some features"; |
|||
return PEAR_DEPENDENCY_MISSING_OPTIONAL; |
|||
} |
|||
$errmsg = "'$name' PHP extension is not installed"; |
|||
return PEAR_DEPENDENCY_MISSING; |
|||
} |
|||
if ($relation == 'has') { |
|||
return false; |
|||
} |
|||
$code = false; |
|||
if (is_string($req) && substr($req, 0, 2) == 'v.') { |
|||
$req = substr($req, 2); |
|||
} |
|||
$ext_ver = phpversion($name); |
|||
$operator = $relation; |
|||
// Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90)
|
|||
if (!version_compare("$ext_ver", "$req", $operator)) { |
|||
$errmsg = "'$name' PHP extension version " . |
|||
$this->signOperator($operator) . " $req is required"; |
|||
$code = $this->codeFromRelation($relation, $ext_ver, $req, $opt); |
|||
if ($opt) { |
|||
$errmsg = "'$name' PHP extension version " . $this->signOperator($operator) . |
|||
" $req is recommended to utilize some features"; |
|||
return $code; |
|||
} |
|||
} |
|||
return $code; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkOS()
|
|||
|
|||
/** |
|||
* Operating system dependencies check method |
|||
* |
|||
* @param string $os Name of the operating system |
|||
* |
|||
* @return mixed bool false if no error or the error string |
|||
*/ |
|||
function checkOS(&$errmsg, $os) |
|||
{ |
|||
// XXX Fixme: Implement a more flexible way, like
|
|||
// comma separated values or something similar to PEAR_OS
|
|||
static $myos; |
|||
if (empty($myos)) { |
|||
include_once "OS/Guess.php"; |
|||
$myos = new OS_Guess(); |
|||
} |
|||
// only 'has' relation is currently supported
|
|||
if ($myos->matchSignature($os)) { |
|||
return false; |
|||
} |
|||
$errmsg = "'$os' operating system not supported"; |
|||
return PEAR_DEPENDENCY_CONFLICT; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkPHP()
|
|||
|
|||
/** |
|||
* PHP version check method |
|||
* |
|||
* @param string $req which version to compare |
|||
* @param string $relation how to compare the version |
|||
* |
|||
* @return mixed bool false if no error or the error string |
|||
*/ |
|||
function checkPHP(&$errmsg, $req, $relation = 'ge') |
|||
{ |
|||
// this would be a bit stupid, but oh well :)
|
|||
if ($relation == 'has') { |
|||
return false; |
|||
} |
|||
if ($relation == 'not') { |
|||
$errmsg = 'Invalid dependency - "not" is not allowed for php dependencies, ' . |
|||
'php cannot conflict with itself'; |
|||
return PEAR_DEPENDENCY_BAD_DEPENDENCY; |
|||
} |
|||
if (substr($req, 0, 2) == 'v.') { |
|||
$req = substr($req,2, strlen($req) - 2); |
|||
} |
|||
$php_ver = phpversion(); |
|||
$operator = $relation; |
|||
if (!version_compare("$php_ver", "$req", $operator)) { |
|||
$errmsg = "PHP version " . $this->signOperator($operator) . |
|||
" $req is required"; |
|||
return PEAR_DEPENDENCY_CONFLICT; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkProgram()
|
|||
|
|||
/** |
|||
* External program check method. Looks for executable files in |
|||
* directories listed in the PATH environment variable. |
|||
* |
|||
* @param string $program which program to look for |
|||
* |
|||
* @return mixed bool false if no error or the error string |
|||
*/ |
|||
function checkProgram(&$errmsg, $program) |
|||
{ |
|||
// XXX FIXME honor safe mode
|
|||
$exe_suffix = OS_WINDOWS ? '.exe' : ''; |
|||
$path_elements = explode(PATH_SEPARATOR, getenv('PATH')); |
|||
foreach ($path_elements as $dir) { |
|||
$file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix; |
|||
if (@file_exists($file) && @is_executable($file)) { |
|||
return false; |
|||
} |
|||
} |
|||
$errmsg = "'$program' program is not present in the PATH"; |
|||
return PEAR_DEPENDENCY_MISSING; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkSAPI()
|
|||
|
|||
/** |
|||
* SAPI backend check method. Version comparison is not yet |
|||
* available here. |
|||
* |
|||
* @param string $name name of SAPI backend |
|||
* @param string $req which version to compare |
|||
* @param string $relation how to compare versions (currently |
|||
* hardcoded to 'has') |
|||
* @return mixed bool false if no error or the error string |
|||
*/ |
|||
function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has') |
|||
{ |
|||
// XXX Fixme: There is no way to know if the user has or
|
|||
// not other SAPI backends installed than the installer one
|
|||
|
|||
$sapi_backend = php_sapi_name(); |
|||
// Version comparisons not supported, sapi backends don't have
|
|||
// version information yet.
|
|||
if ($sapi_backend == $name) { |
|||
return false; |
|||
} |
|||
$errmsg = "'$sapi_backend' SAPI backend not supported"; |
|||
return PEAR_DEPENDENCY_CONFLICT; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkZend()
|
|||
|
|||
/** |
|||
* Zend version check method |
|||
* |
|||
* @param string $req which version to compare |
|||
* @param string $relation how to compare the version |
|||
* |
|||
* @return mixed bool false if no error or the error string |
|||
*/ |
|||
function checkZend(&$errmsg, $req, $relation = 'ge') |
|||
{ |
|||
if (substr($req, 0, 2) == 'v.') { |
|||
$req = substr($req,2, strlen($req) - 2); |
|||
} |
|||
$zend_ver = zend_version(); |
|||
$operator = substr($relation,0,2); |
|||
if (!version_compare("$zend_ver", "$req", $operator)) { |
|||
$errmsg = "Zend version " . $this->signOperator($operator) . |
|||
" $req is required"; |
|||
return PEAR_DEPENDENCY_CONFLICT; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ signOperator()
|
|||
|
|||
/** |
|||
* Converts text comparing operators to them sign equivalents |
|||
* |
|||
* Example: 'ge' to '>=' |
|||
* |
|||
* @access public |
|||
* @param string Operator |
|||
* @return string Sign equivalent |
|||
*/ |
|||
function signOperator($operator) |
|||
{ |
|||
switch($operator) { |
|||
case 'lt': return '<'; |
|||
case 'le': return '<='; |
|||
case 'gt': return '>'; |
|||
case 'ge': return '>='; |
|||
case 'eq': return '=='; |
|||
case 'ne': return '!='; |
|||
default: |
|||
return $operator; |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ codeFromRelation()
|
|||
|
|||
/** |
|||
* Convert relation into corresponding code |
|||
* |
|||
* @access public |
|||
* @param string Relation |
|||
* @param string Version |
|||
* @param string Requirement |
|||
* @param bool Optional dependency indicator |
|||
* @return integer |
|||
*/ |
|||
function codeFromRelation($relation, $version, $req, $opt = false) |
|||
{ |
|||
$code = PEAR_DEPENDENCY_BAD_DEPENDENCY; |
|||
switch ($relation) { |
|||
case 'gt': case 'ge': case 'eq': |
|||
// upgrade
|
|||
$have_major = preg_replace('/\D.*/', '', $version); |
|||
$need_major = preg_replace('/\D.*/', '', $req); |
|||
if ($need_major > $have_major) { |
|||
$code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL : |
|||
PEAR_DEPENDENCY_UPGRADE_MAJOR; |
|||
} else { |
|||
$code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL : |
|||
PEAR_DEPENDENCY_UPGRADE_MINOR; |
|||
} |
|||
break; |
|||
case 'lt': case 'le': case 'ne': |
|||
$code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL : |
|||
PEAR_DEPENDENCY_CONFLICT; |
|||
break; |
|||
} |
|||
return $code; |
|||
} |
|||
|
|||
// }}}
|
|||
} |
|||
?>
|
|||
@ -1,348 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Authors: Tomas V.V.Cox <cox@idecnet.com> |
|
|||
// | |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
/** |
|||
Experimental dependencies database handling functions (not yet in production) |
|||
|
|||
<?php |
|||
include './DependencyDB.php'; |
|||
PEAR::setErrorHandling(PEAR_ERROR_DIE); |
|||
$dep = PEAR_DependencyDB::singleton('./test.deps', '/home/cox/www/include/pear'); |
|||
//$dep->rebuildDB();
|
|||
$a = $dep->checkAction('uninstall', 'net_socket'); |
|||
print_r($a); |
|||
?>
|
|||
|
|||
**/ |
|||
|
|||
require_once 'PEAR.php'; |
|||
require_once 'PEAR/Registry.php'; |
|||
require_once 'PEAR/Dependency.php'; |
|||
|
|||
class PEAR_DependencyDB extends PEAR |
|||
{ |
|||
// {{{ properties
|
|||
|
|||
var $pear_reg = false; |
|||
var $pear_dep = false; |
|||
var $depdb_file = false; |
|||
var $lockfile = false; |
|||
var $lock_fp = false; |
|||
var $depdb_version = '1.0'; |
|||
|
|||
// }}}
|
|||
// {{{ & singleton()
|
|||
|
|||
function &singleton($depdb_file, $reg_file) |
|||
{ |
|||
$obj = new PEAR_DependencyDB; |
|||
$reg = &new PEAR_Registry($reg_file); |
|||
$obj->pear_reg = $reg; |
|||
$obj->lockfile = $reg->lockfile; |
|||
$obj->pear_dep = new PEAR_Dependency($reg); |
|||
$obj->depdb_file = $depdb_file; |
|||
$obj->assertDepsDB(); |
|||
return $obj; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ assertDepsDB()
|
|||
|
|||
function assertDepsDB() |
|||
{ |
|||
if (!is_file($this->depdb_file)) { |
|||
$this->rebuildDB(); |
|||
} else { |
|||
$depdb = $this->_getDepDB(); |
|||
// Datatype format has been changed, rebuild the Deps DB
|
|||
if ($depdb['depdb_version'] != $this->depdb_version) { |
|||
$this->rebuildDB(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _lock()
|
|||
|
|||
function _lock($mode = LOCK_EX) |
|||
{ |
|||
if (!eregi('Windows 9', php_uname())) { |
|||
if ($mode != LOCK_UN && is_resource($this->lock_fp)) { |
|||
// XXX does not check type of lock (LOCK_SH/LOCK_EX)
|
|||
return true; |
|||
} |
|||
$open_mode = 'w'; |
|||
// XXX People reported problems with LOCK_SH and 'w'
|
|||
if ($mode === LOCK_SH) { |
|||
if (@!is_file($this->lockfile)) { |
|||
touch($this->lockfile); |
|||
} |
|||
$open_mode = 'r'; |
|||
} |
|||
|
|||
$this->lock_fp = @fopen($this->lockfile, $open_mode); |
|||
|
|||
if (!is_resource($this->lock_fp)) { |
|||
return $this->raiseError("could not create lock file" . |
|||
(isset($php_errormsg) ? ": " . $php_errormsg : "")); |
|||
} |
|||
if (!(int)flock($this->lock_fp, $mode)) { |
|||
switch ($mode) { |
|||
case LOCK_SH: $str = 'shared'; break; |
|||
case LOCK_EX: $str = 'exclusive'; break; |
|||
case LOCK_UN: $str = 'unlock'; break; |
|||
default: $str = 'unknown'; break; |
|||
} |
|||
return $this->raiseError("could not acquire $str lock ($this->lockfile)"); |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _unlock()
|
|||
|
|||
function _unlock() |
|||
{ |
|||
$ret = $this->_lock(LOCK_UN); |
|||
$this->lock_fp = null; |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ rebuildDepsFile()
|
|||
|
|||
function rebuildDB() |
|||
{ |
|||
$depdb = array('depdb_version' => $this->depdb_version); |
|||
$packages = $this->pear_reg->listPackages(); |
|||
foreach ($packages as $package) { |
|||
$deps = $this->pear_reg->packageInfo($package, 'release_deps'); |
|||
$this->setPackageDep($depdb, $package, $deps); |
|||
} |
|||
print_r($depdb); |
|||
$error = $this->_writeDepDB($depdb); |
|||
if (PEAR::isError($error)) { |
|||
return $error; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ & _getDepDB()
|
|||
|
|||
function &_getDepDB() |
|||
{ |
|||
if (!$fp = fopen($this->depdb_file, 'r')) { |
|||
$err = $this->raiseError("Could not open dependencies file `".$this->depdb_file."'"); |
|||
return $err; |
|||
} |
|||
$data = unserialize(fread($fp, filesize($this->depdb_file))); |
|||
fclose($fp); |
|||
return $data; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _writeDepDB()
|
|||
|
|||
function _writeDepDB(&$deps) |
|||
{ |
|||
if (PEAR::isError($e = $this->_lock(LOCK_EX))) { |
|||
return $e; |
|||
} |
|||
if (!$fp = fopen($this->depdb_file, 'wb')) { |
|||
$this->_unlock(); |
|||
return $this->raiseError("Could not open dependencies file `".$this->depfile."' for writting"); |
|||
} |
|||
fwrite($fp, serialize($deps)); |
|||
fclose($fp); |
|||
$this->_unlock(); |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
/* |
|||
// {{{ removePackageDep()
|
|||
function removePackageDep($package) |
|||
{ |
|||
$data = &$this->_depGetDepDB(); |
|||
if (PEAR::isError($data)) { |
|||
return $data; |
|||
} |
|||
// Other packages depends on this package, can't be removed
|
|||
if (isset($data['deps'][$package])) { |
|||
return $data['deps'][$package]; |
|||
} |
|||
// The package depends on others, remove those dependencies
|
|||
if (isset($data['pkgs'][$package])) { |
|||
foreach ($data['pkgs'][$package] as $pkg => $key) { |
|||
// remove the dependency
|
|||
unset($data['deps'][$pkg][$key]); |
|||
// if no more dependencies, remove the subject too
|
|||
if (!count($data['deps'][$pkg])) { |
|||
unset($data['deps'][$pkg]); |
|||
} |
|||
} |
|||
// remove the package from the index list
|
|||
unset($data['pkgs'][$package]); |
|||
} |
|||
return $this->_depWriteDepDB(); |
|||
} |
|||
// }}}
|
|||
*/ |
|||
|
|||
// {{{ checkAction()
|
|||
|
|||
function checkAction($action, $package, $new_version = null) |
|||
{ |
|||
$depdb = &$this->_getDepDB(); |
|||
if (PEAR::isError($depdb)) { |
|||
return $depdb; |
|||
} |
|||
$fails = ''; |
|||
switch($action) { |
|||
case 'uninstall': |
|||
// Other packages depends on this package, can't be removed
|
|||
if (isset($depdb['deps'][$package])) { |
|||
foreach ($depdb['deps'][$package] as $dep) { |
|||
if (!$dep['optional']) { |
|||
$fails .= "Package '" . $dep['depend'] . "' depends on '$package'\n"; |
|||
} |
|||
} |
|||
return $fails; |
|||
} |
|||
return true; |
|||
case 'install': |
|||
case 'upgrade': |
|||
// Other packages depend on this package, check deps. Ex:
|
|||
// <dep type='pkg' rel='lt' version='1.0'>Foo</dep> and we are trying to
|
|||
// update Foo to version 2.0
|
|||
if (isset($depdb['deps'][$package])) { |
|||
foreach ($depdb['deps'][$package] as $dep) { |
|||
$relation = $dep['rel']; |
|||
if ($relation == 'not') { |
|||
$fails .= "Package '" . $dep['depend'] . "' conflicts with '$package'\n"; |
|||
} elseif ($relation != 'has' && $new_version !== null) { |
|||
if (!version_compare("$new_version", "{$dep['version']}", $relation) && |
|||
!$dep['optional']) { |
|||
$fails .= "Package '" . $dep['depend'] . "' requires ". |
|||
"$package " . $this->pear_dep->signOperator($relation) . |
|||
" " . $dep['version']; |
|||
} |
|||
} |
|||
} |
|||
if (isset($fails)) { |
|||
return $fails; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ commitAction()
|
|||
|
|||
function commitAction($action, $package) |
|||
{ |
|||
|
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ setPackageDep()
|
|||
|
|||
/** |
|||
* Update or insert a the dependencies of a package, prechecking |
|||
* that the package won't break any dependency in the process |
|||
|
|||
The data structure is as follows: |
|||
$dep_db = array( |
|||
// Other packages depends on this packages
|
|||
'deps' => array( |
|||
'Package Name' => array( |
|||
0 => array( |
|||
// This package depends on 'Package Name'
|
|||
'depend' => 'Package', |
|||
// Which version 'Package' needs of 'Package Name'
|
|||
'version' => '1.0', |
|||
// The requirement (version_compare() operator)
|
|||
'rel' => 'ge', |
|||
// whether the dependency is optional
|
|||
'optional' => true/false |
|||
), |
|||
), |
|||
) |
|||
// This packages are dependant on other packages
|
|||
'pkgs' => array( |
|||
'Package Dependant' => array( |
|||
// This is a index list with paths over the 'deps' array for quick
|
|||
// searching things like "what dependecies has this package?"
|
|||
// $dep_db['deps']['Package Name'][3]
|
|||
'Package Name' => 3 // key in array ['deps']['Package Name']
|
|||
), |
|||
) |
|||
) |
|||
|
|||
Note: It only supports package dependencies no other type |
|||
*/ |
|||
|
|||
function setPackageDep(&$data, $package, $rel_deps = array()) |
|||
{ |
|||
// This package has no dependencies
|
|||
if (!is_array($rel_deps) || !count($rel_deps)) { |
|||
return true; |
|||
} |
|||
|
|||
// The package depends on others, register that
|
|||
foreach ($rel_deps as $dep) { |
|||
// We only support deps of type 'pkg's
|
|||
if ($dep && $dep['type'] == 'pkg' && isset($dep['name'])) { |
|||
$dep_name = strtolower($dep['name']); |
|||
$write = array('depend' => $package, |
|||
'rel' => $dep['rel']); |
|||
if ($dep['rel'] != 'has') { |
|||
$write['version'] = $dep['version']; |
|||
} |
|||
if (isset($dep['optional']) && $dep['optional'] == 'yes') { |
|||
$write['optional'] = true; |
|||
} else { |
|||
$write['optional'] = false; |
|||
} |
|||
settype($data['deps'][$dep_name], 'array'); |
|||
|
|||
// The dependency already exists, update it
|
|||
if (isset($data['pkgs'][$package][$dep_name])) { |
|||
$key = $data['pkgs'][$package][$dep_name]; |
|||
$data['deps'][$dep_name][$key] = $write; |
|||
|
|||
// New dependency, insert it
|
|||
} else { |
|||
$key = count($data['deps'][$dep_name]); |
|||
$data['deps'][$dep_name][$key] = $write; |
|||
settype($data['pkgs'][$package], 'array'); |
|||
$data['pkgs'][$package][$dep_name] = $key; |
|||
} |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
// }}}
|
|||
} |
|||
?>
|
|||
@ -1,680 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Authors: Stig Bakken <ssb@php.net> |
|
|||
// | Tomas V.V.Cox <cox@idecnet.com> |
|
|||
// | Martin Jansen <mj@php.net> |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
require_once 'PEAR/Common.php'; |
|||
require_once 'PEAR/Registry.php'; |
|||
require_once 'PEAR/Dependency.php'; |
|||
require_once 'PEAR/Remote.php'; |
|||
require_once 'System.php'; |
|||
|
|||
|
|||
define('PEAR_INSTALLER_OK', 1); |
|||
define('PEAR_INSTALLER_FAILED', 0); |
|||
define('PEAR_INSTALLER_SKIPPED', -1); |
|||
define('PEAR_INSTALLER_ERROR_NO_PREF_STATE', 2); |
|||
|
|||
/** |
|||
* Administration class used to download PEAR packages and maintain the |
|||
* installed package database. |
|||
* |
|||
* @since PEAR 1.4 |
|||
* @author Greg Beaver <cellog@php.net> |
|||
*/ |
|||
class PEAR_Downloader extends PEAR_Common |
|||
{ |
|||
/** |
|||
* @var PEAR_Config |
|||
* @access private |
|||
*/ |
|||
var $_config; |
|||
|
|||
/** |
|||
* @var PEAR_Registry |
|||
* @access private |
|||
*/ |
|||
var $_registry; |
|||
|
|||
/** |
|||
* @var PEAR_Remote |
|||
* @access private |
|||
*/ |
|||
var $_remote; |
|||
|
|||
/** |
|||
* Preferred Installation State (snapshot, devel, alpha, beta, stable) |
|||
* @var string|null |
|||
* @access private |
|||
*/ |
|||
var $_preferredState; |
|||
|
|||
/** |
|||
* Options from command-line passed to Install. |
|||
* |
|||
* Recognized options:<br /> |
|||
* - onlyreqdeps : install all required dependencies as well |
|||
* - alldeps : install all dependencies, including optional |
|||
* - installroot : base relative path to install files in |
|||
* - force : force a download even if warnings would prevent it |
|||
* @see PEAR_Command_Install |
|||
* @access private |
|||
* @var array |
|||
*/ |
|||
var $_options; |
|||
|
|||
/** |
|||
* Downloaded Packages after a call to download(). |
|||
* |
|||
* Format of each entry: |
|||
* |
|||
* <code> |
|||
* array('pkg' => 'package_name', 'file' => '/path/to/local/file', |
|||
* 'info' => array() // parsed package.xml
|
|||
* ); |
|||
* </code> |
|||
* @access private |
|||
* @var array |
|||
*/ |
|||
var $_downloadedPackages = array(); |
|||
|
|||
/** |
|||
* Packages slated for download. |
|||
* |
|||
* This is used to prevent downloading a package more than once should it be a dependency |
|||
* for two packages to be installed. |
|||
* Format of each entry: |
|||
* |
|||
* <pre> |
|||
* array('package_name1' => parsed package.xml, 'package_name2' => parsed package.xml, |
|||
* ); |
|||
* </pre> |
|||
* @access private |
|||
* @var array |
|||
*/ |
|||
var $_toDownload = array(); |
|||
|
|||
/** |
|||
* Array of every package installed, with names lower-cased. |
|||
* |
|||
* Format: |
|||
* <code> |
|||
* array('package1' => 0, 'package2' => 1, ); |
|||
* </code> |
|||
* @var array |
|||
*/ |
|||
var $_installed = array(); |
|||
|
|||
/** |
|||
* @var array |
|||
* @access private |
|||
*/ |
|||
var $_errorStack = array(); |
|||
|
|||
// {{{ PEAR_Downloader()
|
|||
|
|||
function PEAR_Downloader(&$ui, $options, &$config) |
|||
{ |
|||
$this->_options = $options; |
|||
$this->_config = &$config; |
|||
$this->_preferredState = $this->_config->get('preferred_state'); |
|||
$this->ui = &$ui; |
|||
if (!$this->_preferredState) { |
|||
// don't inadvertantly use a non-set preferred_state
|
|||
$this->_preferredState = null; |
|||
} |
|||
|
|||
$php_dir = $this->_config->get('php_dir'); |
|||
if (isset($this->_options['installroot'])) { |
|||
if (substr($this->_options['installroot'], -1) == DIRECTORY_SEPARATOR) { |
|||
$this->_options['installroot'] = substr($this->_options['installroot'], 0, -1); |
|||
} |
|||
$php_dir = $this->_prependPath($php_dir, $this->_options['installroot']); |
|||
} |
|||
$this->_registry = &new PEAR_Registry($php_dir); |
|||
$this->_remote = &new PEAR_Remote($config); |
|||
|
|||
if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { |
|||
$this->_installed = $this->_registry->listPackages(); |
|||
array_walk($this->_installed, create_function('&$v,$k','$v = strtolower($v);')); |
|||
$this->_installed = array_flip($this->_installed); |
|||
} |
|||
parent::PEAR_Common(); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ configSet()
|
|||
function configSet($key, $value, $layer = 'user') |
|||
{ |
|||
$this->_config->set($key, $value, $layer); |
|||
$this->_preferredState = $this->_config->get('preferred_state'); |
|||
if (!$this->_preferredState) { |
|||
// don't inadvertantly use a non-set preferred_state
|
|||
$this->_preferredState = null; |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ setOptions()
|
|||
function setOptions($options) |
|||
{ |
|||
$this->_options = $options; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _downloadFile()
|
|||
/** |
|||
* @param string filename to download |
|||
* @param string version/state |
|||
* @param string original value passed to command-line |
|||
* @param string|null preferred state (snapshot/devel/alpha/beta/stable) |
|||
* Defaults to configuration preferred state |
|||
* @return null|PEAR_Error|string |
|||
* @access private |
|||
*/ |
|||
function _downloadFile($pkgfile, $version, $origpkgfile, $state = null) |
|||
{ |
|||
if (is_null($state)) { |
|||
$state = $this->_preferredState; |
|||
} |
|||
// {{{ check the package filename, and whether it's already installed
|
|||
$need_download = false; |
|||
if (preg_match('#^(http|ftp)://#', $pkgfile)) { |
|||
$need_download = true; |
|||
} elseif (!@is_file($pkgfile)) { |
|||
if ($this->validPackageName($pkgfile)) { |
|||
if ($this->_registry->packageExists($pkgfile)) { |
|||
if (empty($this->_options['upgrade']) && empty($this->_options['force'])) { |
|||
$errors[] = "$pkgfile already installed"; |
|||
return; |
|||
} |
|||
} |
|||
$pkgfile = $this->getPackageDownloadUrl($pkgfile, $version); |
|||
$need_download = true; |
|||
} else { |
|||
if (strlen($pkgfile)) { |
|||
$errors[] = "Could not open the package file: $pkgfile"; |
|||
} else { |
|||
$errors[] = "No package file given"; |
|||
} |
|||
return; |
|||
} |
|||
} |
|||
// }}}
|
|||
|
|||
// {{{ Download package -----------------------------------------------
|
|||
if ($need_download) { |
|||
$downloaddir = $this->_config->get('download_dir'); |
|||
if (empty($downloaddir)) { |
|||
if (PEAR::isError($downloaddir = System::mktemp('-d'))) { |
|||
return $downloaddir; |
|||
} |
|||
$this->log(3, '+ tmp dir created at ' . $downloaddir); |
|||
} |
|||
$callback = $this->ui ? array(&$this, '_downloadCallback') : null; |
|||
$this->pushErrorHandling(PEAR_ERROR_RETURN); |
|||
$file = $this->downloadHttp($pkgfile, $this->ui, $downloaddir, $callback); |
|||
$this->popErrorHandling(); |
|||
if (PEAR::isError($file)) { |
|||
if ($this->validPackageName($origpkgfile)) { |
|||
if (!PEAR::isError($info = $this->_remote->call('package.info', |
|||
$origpkgfile))) { |
|||
if (!count($info['releases'])) { |
|||
return $this->raiseError('Package ' . $origpkgfile . |
|||
' has no releases'); |
|||
} else { |
|||
return $this->raiseError('No releases of preferred state "' |
|||
. $state . '" exist for package ' . $origpkgfile . |
|||
'. Use ' . $origpkgfile . '-state to install another' . |
|||
' state (like ' . $origpkgfile .'-beta)', |
|||
PEAR_INSTALLER_ERROR_NO_PREF_STATE); |
|||
} |
|||
} else { |
|||
return $pkgfile; |
|||
} |
|||
} else { |
|||
return $this->raiseError($file); |
|||
} |
|||
} |
|||
$pkgfile = $file; |
|||
} |
|||
// }}}
|
|||
return $pkgfile; |
|||
} |
|||
// }}}
|
|||
// {{{ getPackageDownloadUrl()
|
|||
|
|||
function getPackageDownloadUrl($package, $version = null) |
|||
{ |
|||
if ($version) { |
|||
$package .= "-$version"; |
|||
} |
|||
if ($this === null || $this->_config === null) { |
|||
$package = "http://pear.php.net/get/$package"; |
|||
} else { |
|||
$package = "http://" . $this->_config->get('master_server') . |
|||
"/get/$package"; |
|||
} |
|||
if (!extension_loaded("zlib")) { |
|||
$package .= '?uncompress=yes'; |
|||
} |
|||
return $package; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ extractDownloadFileName($pkgfile, &$version)
|
|||
|
|||
function extractDownloadFileName($pkgfile, &$version) |
|||
{ |
|||
if (@is_file($pkgfile)) { |
|||
return $pkgfile; |
|||
} |
|||
// regex defined in Common.php
|
|||
if (preg_match(PEAR_COMMON_PACKAGE_DOWNLOAD_PREG, $pkgfile, $m)) { |
|||
$version = (isset($m[3])) ? $m[3] : null; |
|||
return $m[1]; |
|||
} |
|||
$version = null; |
|||
return $pkgfile; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// }}}
|
|||
// {{{ getDownloadedPackages()
|
|||
|
|||
/** |
|||
* Retrieve a list of downloaded packages after a call to {@link download()}. |
|||
* |
|||
* Also resets the list of downloaded packages. |
|||
* @return array |
|||
*/ |
|||
function getDownloadedPackages() |
|||
{ |
|||
$ret = $this->_downloadedPackages; |
|||
$this->_downloadedPackages = array(); |
|||
$this->_toDownload = array(); |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ download()
|
|||
|
|||
/** |
|||
* Download any files and their dependencies, if necessary |
|||
* |
|||
* BC-compatible method name |
|||
* @param array a mixed list of package names, local files, or package.xml |
|||
*/ |
|||
function download($packages) |
|||
{ |
|||
return $this->doDownload($packages); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ doDownload()
|
|||
|
|||
/** |
|||
* Download any files and their dependencies, if necessary |
|||
* |
|||
* @param array a mixed list of package names, local files, or package.xml |
|||
*/ |
|||
function doDownload($packages) |
|||
{ |
|||
$mywillinstall = array(); |
|||
$state = $this->_preferredState; |
|||
|
|||
// {{{ download files in this list if necessary
|
|||
foreach($packages as $pkgfile) { |
|||
$need_download = false; |
|||
if (!is_file($pkgfile)) { |
|||
if (preg_match('#^(http|ftp)://#', $pkgfile)) { |
|||
$need_download = true; |
|||
} |
|||
$pkgfile = $this->_downloadNonFile($pkgfile); |
|||
if (PEAR::isError($pkgfile)) { |
|||
return $pkgfile; |
|||
} |
|||
if ($pkgfile === false) { |
|||
continue; |
|||
} |
|||
} // end is_file()
|
|||
|
|||
$tempinfo = $this->infoFromAny($pkgfile); |
|||
if ($need_download) { |
|||
$this->_toDownload[] = $tempinfo['package']; |
|||
} |
|||
if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { |
|||
// ignore dependencies if there are any errors
|
|||
if (!PEAR::isError($tempinfo)) { |
|||
$mywillinstall[strtolower($tempinfo['package'])] = @$tempinfo['release_deps']; |
|||
} |
|||
} |
|||
$this->_downloadedPackages[] = array('pkg' => $tempinfo['package'], |
|||
'file' => $pkgfile, 'info' => $tempinfo); |
|||
} // end foreach($packages)
|
|||
// }}}
|
|||
|
|||
// {{{ extract dependencies from downloaded files and then download
|
|||
// them if necessary
|
|||
if (isset($this->_options['alldeps']) || isset($this->_options['onlyreqdeps'])) { |
|||
$deppackages = array(); |
|||
foreach ($mywillinstall as $package => $alldeps) { |
|||
if (!is_array($alldeps)) { |
|||
// there are no dependencies
|
|||
continue; |
|||
} |
|||
$fail = false; |
|||
foreach ($alldeps as $info) { |
|||
if ($info['type'] != 'pkg') { |
|||
continue; |
|||
} |
|||
$ret = $this->_processDependency($package, $info, $mywillinstall); |
|||
if ($ret === false) { |
|||
continue; |
|||
} |
|||
if ($ret === 0) { |
|||
$fail = true; |
|||
continue; |
|||
} |
|||
if (PEAR::isError($ret)) { |
|||
return $ret; |
|||
} |
|||
$deppackages[] = $ret; |
|||
} // foreach($alldeps
|
|||
if ($fail) { |
|||
$deppackages = array(); |
|||
} |
|||
} |
|||
|
|||
if (count($deppackages)) { |
|||
$this->doDownload($deppackages); |
|||
} |
|||
} // }}} if --alldeps or --onlyreqdeps
|
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _downloadNonFile($pkgfile)
|
|||
|
|||
/** |
|||
* @return false|PEAR_Error|string false if loop should be broken out of, |
|||
* string if the file was downloaded, |
|||
* PEAR_Error on exception |
|||
* @access private |
|||
*/ |
|||
function _downloadNonFile($pkgfile) |
|||
{ |
|||
$origpkgfile = $pkgfile; |
|||
$state = null; |
|||
$pkgfile = $this->extractDownloadFileName($pkgfile, $version); |
|||
if (preg_match('#^(http|ftp)://#', $pkgfile)) { |
|||
return $this->_downloadFile($pkgfile, $version, $origpkgfile); |
|||
} |
|||
if (!$this->validPackageName($pkgfile)) { |
|||
return $this->raiseError("Package name '$pkgfile' not valid"); |
|||
} |
|||
// ignore packages that are installed unless we are upgrading
|
|||
$curinfo = $this->_registry->packageInfo($pkgfile); |
|||
if ($this->_registry->packageExists($pkgfile) |
|||
&& empty($this->_options['upgrade']) && empty($this->_options['force'])) { |
|||
$this->log(0, "Package '{$curinfo['package']}' already installed, skipping"); |
|||
return false; |
|||
} |
|||
if (in_array($pkgfile, $this->_toDownload)) { |
|||
return false; |
|||
} |
|||
$releases = $this->_remote->call('package.info', $pkgfile, 'releases', true); |
|||
if (!count($releases)) { |
|||
return $this->raiseError("No releases found for package '$pkgfile'"); |
|||
} |
|||
// Want a specific version/state
|
|||
if ($version !== null) { |
|||
// Passed Foo-1.2
|
|||
if ($this->validPackageVersion($version)) { |
|||
if (!isset($releases[$version])) { |
|||
return $this->raiseError("No release with version '$version' found for '$pkgfile'"); |
|||
} |
|||
// Passed Foo-alpha
|
|||
} elseif (in_array($version, $this->getReleaseStates())) { |
|||
$state = $version; |
|||
$version = 0; |
|||
foreach ($releases as $ver => $inf) { |
|||
if ($inf['state'] == $state && version_compare("$version", "$ver") < 0) { |
|||
$version = $ver; |
|||
break; |
|||
} |
|||
} |
|||
if ($version == 0) { |
|||
return $this->raiseError("No release with state '$state' found for '$pkgfile'"); |
|||
} |
|||
// invalid suffix passed
|
|||
} else { |
|||
return $this->raiseError("Invalid suffix '-$version', be sure to pass a valid PEAR ". |
|||
"version number or release state"); |
|||
} |
|||
// Guess what to download
|
|||
} else { |
|||
$states = $this->betterStates($this->_preferredState, true); |
|||
$possible = false; |
|||
$version = 0; |
|||
$higher_version = 0; |
|||
$prev_hi_ver = 0; |
|||
foreach ($releases as $ver => $inf) { |
|||
if (in_array($inf['state'], $states) && version_compare("$version", "$ver") < 0) { |
|||
$version = $ver; |
|||
break; |
|||
} else { |
|||
$ver = (string)$ver; |
|||
if (version_compare($prev_hi_ver, $ver) < 0) { |
|||
$prev_hi_ver = $higher_version = $ver; |
|||
} |
|||
} |
|||
} |
|||
if ($version === 0 && !isset($this->_options['force'])) { |
|||
return $this->raiseError('No release with state equal to: \'' . implode(', ', $states) . |
|||
"' found for '$pkgfile'"); |
|||
} elseif ($version === 0) { |
|||
$this->log(0, "Warning: $pkgfile is state '" . $releases[$higher_version]['state'] . "' which is less stable " . |
|||
"than state '$this->_preferredState'"); |
|||
} |
|||
} |
|||
// Check if we haven't already the version
|
|||
if (empty($this->_options['force']) && !is_null($curinfo)) { |
|||
if ($curinfo['version'] == $version) { |
|||
$this->log(0, "Package '{$curinfo['package']}-{$curinfo['version']}' already installed, skipping"); |
|||
return false; |
|||
} elseif (version_compare("$version", "{$curinfo['version']}") < 0) { |
|||
$this->log(0, "Package '{$curinfo['package']}' version '{$curinfo['version']}' " . |
|||
" is installed and {$curinfo['version']} is > requested '$version', skipping"); |
|||
return false; |
|||
} |
|||
} |
|||
$this->_toDownload[] = $pkgfile; |
|||
return $this->_downloadFile($pkgfile, $version, $origpkgfile, $state); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _processDependency($package, $info, $mywillinstall)
|
|||
|
|||
/** |
|||
* Process a dependency, download if necessary |
|||
* @param array dependency information from PEAR_Remote call |
|||
* @param array packages that will be installed in this iteration |
|||
* @return false|string|PEAR_Error |
|||
* @access private |
|||
* @todo Add test for relation 'lt'/'le' -> make sure that the dependency requested is |
|||
* in fact lower than the required value. This will be very important for BC dependencies |
|||
*/ |
|||
function _processDependency($package, $info, $mywillinstall) |
|||
{ |
|||
$state = $this->_preferredState; |
|||
if (!isset($this->_options['alldeps']) && isset($info['optional']) && |
|||
$info['optional'] == 'yes') { |
|||
// skip optional deps
|
|||
$this->log(0, "skipping Package '$package' optional dependency '$info[name]'"); |
|||
return false; |
|||
} |
|||
// {{{ get releases
|
|||
$releases = $this->_remote->call('package.info', $info['name'], 'releases', true); |
|||
if (PEAR::isError($releases)) { |
|||
return $releases; |
|||
} |
|||
if (!count($releases)) { |
|||
if (!isset($this->_installed[strtolower($info['name'])])) { |
|||
$this->pushError("Package '$package' dependency '$info[name]' ". |
|||
"has no releases"); |
|||
} |
|||
return false; |
|||
} |
|||
$found = false; |
|||
$save = $releases; |
|||
while(count($releases) && !$found) { |
|||
if (!empty($state) && $state != 'any') { |
|||
list($release_version, $release) = each($releases); |
|||
if ($state != $release['state'] && |
|||
!in_array($release['state'], $this->betterStates($state))) |
|||
{ |
|||
// drop this release - it ain't stable enough
|
|||
array_shift($releases); |
|||
} else { |
|||
$found = true; |
|||
} |
|||
} else { |
|||
$found = true; |
|||
} |
|||
} |
|||
if (!count($releases) && !$found) { |
|||
$get = array(); |
|||
foreach($save as $release) { |
|||
$get = array_merge($get, |
|||
$this->betterStates($release['state'], true)); |
|||
} |
|||
$savestate = array_shift($get); |
|||
$this->pushError( "Release for '$package' dependency '$info[name]' " . |
|||
"has state '$savestate', requires '$state'"); |
|||
return 0; |
|||
} |
|||
if (in_array(strtolower($info['name']), $this->_toDownload) || |
|||
isset($mywillinstall[strtolower($info['name'])])) { |
|||
// skip upgrade check for packages we will install
|
|||
return false; |
|||
} |
|||
if (!isset($this->_installed[strtolower($info['name'])])) { |
|||
// check to see if we can install the specific version required
|
|||
if ($info['rel'] == 'eq') { |
|||
return $info['name'] . '-' . $info['version']; |
|||
} |
|||
// skip upgrade check for packages we don't have installed
|
|||
return $info['name']; |
|||
} |
|||
// }}}
|
|||
|
|||
// {{{ see if a dependency must be upgraded
|
|||
$inst_version = $this->_registry->packageInfo($info['name'], 'version'); |
|||
if (!isset($info['version'])) { |
|||
// this is a rel='has' dependency, check against latest
|
|||
if (version_compare($release_version, $inst_version, 'le')) { |
|||
return false; |
|||
} else { |
|||
return $info['name']; |
|||
} |
|||
} |
|||
if (version_compare($info['version'], $inst_version, 'le')) { |
|||
// installed version is up-to-date
|
|||
return false; |
|||
} |
|||
return $info['name']; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _downloadCallback()
|
|||
|
|||
function _downloadCallback($msg, $params = null) |
|||
{ |
|||
switch ($msg) { |
|||
case 'saveas': |
|||
$this->log(1, "downloading $params ..."); |
|||
break; |
|||
case 'done': |
|||
$this->log(1, '...done: ' . number_format($params, 0, '', ',') . ' bytes'); |
|||
break; |
|||
case 'bytesread': |
|||
static $bytes; |
|||
if (empty($bytes)) { |
|||
$bytes = 0; |
|||
} |
|||
if (!($bytes % 10240)) { |
|||
$this->log(1, '.', false); |
|||
} |
|||
$bytes += $params; |
|||
break; |
|||
case 'start': |
|||
$this->log(1, "Starting to download {$params[0]} (".number_format($params[1], 0, '', ',')." bytes)"); |
|||
break; |
|||
} |
|||
if (method_exists($this->ui, '_downloadCallback')) |
|||
$this->ui->_downloadCallback($msg, $params); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _prependPath($path, $prepend)
|
|||
|
|||
function _prependPath($path, $prepend) |
|||
{ |
|||
if (strlen($prepend) > 0) { |
|||
if (OS_WINDOWS && preg_match('/^[a-z]:/i', $path)) { |
|||
$path = $prepend . substr($path, 2); |
|||
} else { |
|||
$path = $prepend . $path; |
|||
} |
|||
} |
|||
return $path; |
|||
} |
|||
// }}}
|
|||
// {{{ pushError($errmsg, $code)
|
|||
|
|||
/** |
|||
* @param string |
|||
* @param integer |
|||
*/ |
|||
function pushError($errmsg, $code = -1) |
|||
{ |
|||
array_push($this->_errorStack, array($errmsg, $code)); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ getErrorMsgs()
|
|||
|
|||
function getErrorMsgs() |
|||
{ |
|||
$msgs = array(); |
|||
$errs = $this->_errorStack; |
|||
foreach ($errs as $err) { |
|||
$msgs[] = $err[0]; |
|||
} |
|||
$this->_errorStack = array(); |
|||
return $msgs; |
|||
} |
|||
|
|||
// }}}
|
|||
} |
|||
// }}}
|
|||
|
|||
?>
|
|||
@ -1,981 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Author: Gregory Beaver <cellog@php.net> |
|
|||
// | |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
/** |
|||
* Error Stack Implementation |
|||
* |
|||
* This is an incredibly simple implementation of a very complex error handling |
|||
* facility. It contains the ability |
|||
* to track multiple errors from multiple packages simultaneously. In addition, |
|||
* it can track errors of many levels, save data along with the error, context |
|||
* information such as the exact file, line number, class and function that |
|||
* generated the error, and if necessary, it can raise a traditional PEAR_Error. |
|||
* It has built-in support for PEAR::Log, to log errors as they occur |
|||
* |
|||
* Since version 0.2alpha, it is also possible to selectively ignore errors, |
|||
* through the use of an error callback, see {@link pushCallback()} |
|||
* |
|||
* Since version 0.3alpha, it is possible to specify the exception class |
|||
* returned from {@link push()} |
|||
* |
|||
* Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can |
|||
* still be done quite handily in an error callback or by manipulating the returned array |
|||
* @author Greg Beaver <cellog@php.net> |
|||
* @version PEAR1.3.2 (beta) |
|||
* @package PEAR_ErrorStack |
|||
* @category Debugging |
|||
* @license http://www.php.net/license/3_0.txt PHP License v3.0 |
|||
*/ |
|||
|
|||
/** |
|||
* Singleton storage |
|||
* |
|||
* Format: |
|||
* <pre> |
|||
* array( |
|||
* 'package1' => PEAR_ErrorStack object, |
|||
* 'package2' => PEAR_ErrorStack object, |
|||
* ... |
|||
* ) |
|||
* </pre> |
|||
* @access private |
|||
* @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] |
|||
*/ |
|||
$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); |
|||
|
|||
/** |
|||
* Global error callback (default) |
|||
* |
|||
* This is only used if set to non-false. * is the default callback for |
|||
* all packages, whereas specific packages may set a default callback |
|||
* for all instances, regardless of whether they are a singleton or not. |
|||
* |
|||
* To exclude non-singletons, only set the local callback for the singleton |
|||
* @see PEAR_ErrorStack::setDefaultCallback() |
|||
* @access private |
|||
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] |
|||
*/ |
|||
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( |
|||
'*' => false, |
|||
); |
|||
|
|||
/** |
|||
* Global Log object (default) |
|||
* |
|||
* This is only used if set to non-false. Use to set a default log object for |
|||
* all stacks, regardless of instantiation order or location |
|||
* @see PEAR_ErrorStack::setDefaultLogger() |
|||
* @access private |
|||
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] |
|||
*/ |
|||
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; |
|||
|
|||
/** |
|||
* Global Overriding Callback |
|||
* |
|||
* This callback will override any error callbacks that specific loggers have set. |
|||
* Use with EXTREME caution |
|||
* @see PEAR_ErrorStack::staticPushCallback() |
|||
* @access private |
|||
* @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] |
|||
*/ |
|||
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); |
|||
|
|||
/**#@+
|
|||
* One of four possible return values from the error Callback |
|||
* @see PEAR_ErrorStack::_errorCallback() |
|||
*/ |
|||
/** |
|||
* If this is returned, then the error will be both pushed onto the stack |
|||
* and logged. |
|||
*/ |
|||
define('PEAR_ERRORSTACK_PUSHANDLOG', 1); |
|||
/** |
|||
* If this is returned, then the error will only be pushed onto the stack, |
|||
* and not logged. |
|||
*/ |
|||
define('PEAR_ERRORSTACK_PUSH', 2); |
|||
/** |
|||
* If this is returned, then the error will only be logged, but not pushed |
|||
* onto the error stack. |
|||
*/ |
|||
define('PEAR_ERRORSTACK_LOG', 3); |
|||
/** |
|||
* If this is returned, then the error is completely ignored. |
|||
*/ |
|||
define('PEAR_ERRORSTACK_IGNORE', 4); |
|||
/** |
|||
* If this is returned, then the error is logged and die() is called. |
|||
*/ |
|||
define('PEAR_ERRORSTACK_DIE', 5); |
|||
/**#@-*/ |
|||
|
|||
/** |
|||
* Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in |
|||
* the singleton method. |
|||
*/ |
|||
define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); |
|||
|
|||
/** |
|||
* Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} |
|||
* that has no __toString() method |
|||
*/ |
|||
define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); |
|||
/** |
|||
* Error Stack Implementation |
|||
* |
|||
* Usage: |
|||
* <code> |
|||
* // global error stack
|
|||
* $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); |
|||
* // local error stack
|
|||
* $local_stack = new PEAR_ErrorStack('MyPackage'); |
|||
* </code> |
|||
* @copyright 2004 Gregory Beaver |
|||
* @package PEAR_ErrorStack |
|||
* @license http://www.php.net/license/3_0.txt PHP License |
|||
*/ |
|||
class PEAR_ErrorStack { |
|||
/** |
|||
* Errors are stored in the order that they are pushed on the stack. |
|||
* @since 0.4alpha Errors are no longer organized by error level. |
|||
* This renders pop() nearly unusable, and levels could be more easily |
|||
* handled in a callback anyway |
|||
* @var array |
|||
* @access private |
|||
*/ |
|||
var $_errors = array(); |
|||
|
|||
/** |
|||
* Storage of errors by level. |
|||
* |
|||
* Allows easy retrieval and deletion of only errors from a particular level |
|||
* @since PEAR 1.4.0dev |
|||
* @var array |
|||
* @access private |
|||
*/ |
|||
var $_errorsByLevel = array(); |
|||
|
|||
/** |
|||
* Package name this error stack represents |
|||
* @var string |
|||
* @access protected |
|||
*/ |
|||
var $_package; |
|||
|
|||
/** |
|||
* Determines whether a PEAR_Error is thrown upon every error addition |
|||
* @var boolean |
|||
* @access private |
|||
*/ |
|||
var $_compat = false; |
|||
|
|||
/** |
|||
* If set to a valid callback, this will be used to generate the error |
|||
* message from the error code, otherwise the message passed in will be |
|||
* used |
|||
* @var false|string|array |
|||
* @access private |
|||
*/ |
|||
var $_msgCallback = false; |
|||
|
|||
/** |
|||
* If set to a valid callback, this will be used to generate the error |
|||
* context for an error. For PHP-related errors, this will be a file |
|||
* and line number as retrieved from debug_backtrace(), but can be |
|||
* customized for other purposes. The error might actually be in a separate |
|||
* configuration file, or in a database query. |
|||
* @var false|string|array |
|||
* @access protected |
|||
*/ |
|||
var $_contextCallback = false; |
|||
|
|||
/** |
|||
* If set to a valid callback, this will be called every time an error |
|||
* is pushed onto the stack. The return value will be used to determine |
|||
* whether to allow an error to be pushed or logged. |
|||
* |
|||
* The return value must be one an PEAR_ERRORSTACK_* constant |
|||
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG |
|||
* @var false|string|array |
|||
* @access protected |
|||
*/ |
|||
var $_errorCallback = array(); |
|||
|
|||
/** |
|||
* PEAR::Log object for logging errors |
|||
* @var false|Log |
|||
* @access protected |
|||
*/ |
|||
var $_logger = false; |
|||
|
|||
/** |
|||
* Error messages - designed to be overridden |
|||
* @var array |
|||
* @abstract |
|||
*/ |
|||
var $_errorMsgs = array(); |
|||
|
|||
/** |
|||
* Set up a new error stack |
|||
* |
|||
* @param string $package name of the package this error stack represents |
|||
* @param callback $msgCallback callback used for error message generation |
|||
* @param callback $contextCallback callback used for context generation, |
|||
* defaults to {@link getFileLine()} |
|||
* @param boolean $throwPEAR_Error |
|||
*/ |
|||
function PEAR_ErrorStack($package, $msgCallback = false, $contextCallback = false, |
|||
$throwPEAR_Error = false) |
|||
{ |
|||
$this->_package = $package; |
|||
$this->setMessageCallback($msgCallback); |
|||
$this->setContextCallback($contextCallback); |
|||
$this->_compat = $throwPEAR_Error; |
|||
} |
|||
|
|||
/** |
|||
* Return a single error stack for this package. |
|||
* |
|||
* Note that all parameters are ignored if the stack for package $package |
|||
* has already been instantiated |
|||
* @param string $package name of the package this error stack represents |
|||
* @param callback $msgCallback callback used for error message generation |
|||
* @param callback $contextCallback callback used for context generation, |
|||
* defaults to {@link getFileLine()} |
|||
* @param boolean $throwPEAR_Error |
|||
* @param string $stackClass class to instantiate |
|||
* @static |
|||
* @return PEAR_ErrorStack |
|||
*/ |
|||
function &singleton($package, $msgCallback = false, $contextCallback = false, |
|||
$throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack') |
|||
{ |
|||
if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { |
|||
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; |
|||
} |
|||
if (!class_exists($stackClass)) { |
|||
if (function_exists('debug_backtrace')) { |
|||
$trace = debug_backtrace(); |
|||
} |
|||
PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, |
|||
'exception', array('stackclass' => $stackClass), |
|||
'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', |
|||
false, $trace); |
|||
} |
|||
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = |
|||
&new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); |
|||
} |
|||
|
|||
/** |
|||
* Internal error handler for PEAR_ErrorStack class |
|||
* |
|||
* Dies if the error is an exception (and would have died anyway) |
|||
* @access private |
|||
*/ |
|||
function _handleError($err) |
|||
{ |
|||
if ($err['level'] == 'exception') { |
|||
$message = $err['message']; |
|||
if (isset($_SERVER['REQUEST_URI'])) { |
|||
echo '<br />'; |
|||
} else { |
|||
echo "\n"; |
|||
} |
|||
var_dump($err['context']); |
|||
die($message); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Set up a PEAR::Log object for all error stacks that don't have one |
|||
* @param Log $log |
|||
* @static |
|||
*/ |
|||
function setDefaultLogger(&$log) |
|||
{ |
|||
if (is_object($log) && method_exists($log, 'log') ) { |
|||
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; |
|||
} elseif (is_callable($log)) { |
|||
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Set up a PEAR::Log object for this error stack |
|||
* @param Log $log |
|||
*/ |
|||
function setLogger(&$log) |
|||
{ |
|||
if (is_object($log) && method_exists($log, 'log') ) { |
|||
$this->_logger = &$log; |
|||
} elseif (is_callable($log)) { |
|||
$this->_logger = &$log; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Set an error code => error message mapping callback |
|||
* |
|||
* This method sets the callback that can be used to generate error |
|||
* messages for any instance |
|||
* @param array|string Callback function/method |
|||
*/ |
|||
function setMessageCallback($msgCallback) |
|||
{ |
|||
if (!$msgCallback) { |
|||
$this->_msgCallback = array(&$this, 'getErrorMessage'); |
|||
} else { |
|||
if (is_callable($msgCallback)) { |
|||
$this->_msgCallback = $msgCallback; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Get an error code => error message mapping callback |
|||
* |
|||
* This method returns the current callback that can be used to generate error |
|||
* messages |
|||
* @return array|string|false Callback function/method or false if none |
|||
*/ |
|||
function getMessageCallback() |
|||
{ |
|||
return $this->_msgCallback; |
|||
} |
|||
|
|||
/** |
|||
* Sets a default callback to be used by all error stacks |
|||
* |
|||
* This method sets the callback that can be used to generate error |
|||
* messages for a singleton |
|||
* @param array|string Callback function/method |
|||
* @param string Package name, or false for all packages |
|||
* @static |
|||
*/ |
|||
function setDefaultCallback($callback = false, $package = false) |
|||
{ |
|||
if (!is_callable($callback)) { |
|||
$callback = false; |
|||
} |
|||
$package = $package ? $package : '*'; |
|||
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; |
|||
} |
|||
|
|||
/** |
|||
* Set a callback that generates context information (location of error) for an error stack |
|||
* |
|||
* This method sets the callback that can be used to generate context |
|||
* information for an error. Passing in NULL will disable context generation |
|||
* and remove the expensive call to debug_backtrace() |
|||
* @param array|string|null Callback function/method |
|||
*/ |
|||
function setContextCallback($contextCallback) |
|||
{ |
|||
if ($contextCallback === null) { |
|||
return $this->_contextCallback = false; |
|||
} |
|||
if (!$contextCallback) { |
|||
$this->_contextCallback = array(&$this, 'getFileLine'); |
|||
} else { |
|||
if (is_callable($contextCallback)) { |
|||
$this->_contextCallback = $contextCallback; |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Set an error Callback |
|||
* If set to a valid callback, this will be called every time an error |
|||
* is pushed onto the stack. The return value will be used to determine |
|||
* whether to allow an error to be pushed or logged. |
|||
* |
|||
* The return value must be one of the ERRORSTACK_* constants. |
|||
* |
|||
* This functionality can be used to emulate PEAR's pushErrorHandling, and |
|||
* the PEAR_ERROR_CALLBACK mode, without affecting the integrity of |
|||
* the error stack or logging |
|||
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG |
|||
* @see popCallback() |
|||
* @param string|array $cb |
|||
*/ |
|||
function pushCallback($cb) |
|||
{ |
|||
array_push($this->_errorCallback, $cb); |
|||
} |
|||
|
|||
/** |
|||
* Remove a callback from the error callback stack |
|||
* @see pushCallback() |
|||
* @return array|string|false |
|||
*/ |
|||
function popCallback() |
|||
{ |
|||
if (!count($this->_errorCallback)) { |
|||
return false; |
|||
} |
|||
return array_pop($this->_errorCallback); |
|||
} |
|||
|
|||
/** |
|||
* Set a temporary overriding error callback for every package error stack |
|||
* |
|||
* Use this to temporarily disable all existing callbacks (can be used |
|||
* to emulate the @ operator, for instance) |
|||
* @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG |
|||
* @see staticPopCallback(), pushCallback() |
|||
* @param string|array $cb |
|||
* @static |
|||
*/ |
|||
function staticPushCallback($cb) |
|||
{ |
|||
array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); |
|||
} |
|||
|
|||
/** |
|||
* Remove a temporary overriding error callback |
|||
* @see staticPushCallback() |
|||
* @return array|string|false |
|||
* @static |
|||
*/ |
|||
function staticPopCallback() |
|||
{ |
|||
$ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); |
|||
if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { |
|||
$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); |
|||
} |
|||
return $ret; |
|||
} |
|||
|
|||
/** |
|||
* Add an error to the stack |
|||
* |
|||
* If the message generator exists, it is called with 2 parameters. |
|||
* - the current Error Stack object |
|||
* - an array that is in the same format as an error. Available indices |
|||
* are 'code', 'package', 'time', 'params', 'level', and 'context' |
|||
* |
|||
* Next, if the error should contain context information, this is |
|||
* handled by the context grabbing method. |
|||
* Finally, the error is pushed onto the proper error stack |
|||
* @param int $code Package-specific error code |
|||
* @param string $level Error level. This is NOT spell-checked |
|||
* @param array $params associative array of error parameters |
|||
* @param string $msg Error message, or a portion of it if the message |
|||
* is to be generated |
|||
* @param array $repackage If this error re-packages an error pushed by |
|||
* another package, place the array returned from |
|||
* {@link pop()} in this parameter |
|||
* @param array $backtrace Protected parameter: use this to pass in the |
|||
* {@link debug_backtrace()} that should be used |
|||
* to find error context |
|||
* @return PEAR_Error|array|Exception |
|||
* if compatibility mode is on, a PEAR_Error is also |
|||
* thrown. If the class Exception exists, then one |
|||
* is returned to allow code like: |
|||
* <code> |
|||
* throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); |
|||
* </code> |
|||
* |
|||
* The errorData property of the exception class will be set to the array |
|||
* that would normally be returned. If a PEAR_Error is returned, the userinfo |
|||
* property is set to the array |
|||
* |
|||
* Otherwise, an array is returned in this format: |
|||
* <code> |
|||
* array( |
|||
* 'code' => $code, |
|||
* 'params' => $params, |
|||
* 'package' => $this->_package, |
|||
* 'level' => $level, |
|||
* 'time' => time(), |
|||
* 'context' => $context, |
|||
* 'message' => $msg, |
|||
* //['repackage' => $err] repackaged error array/Exception class
|
|||
* ); |
|||
* </code> |
|||
*/ |
|||
function push($code, $level = 'error', $params = array(), $msg = false, |
|||
$repackage = false, $backtrace = false) |
|||
{ |
|||
$context = false; |
|||
// grab error context
|
|||
if ($this->_contextCallback) { |
|||
if (!$backtrace) { |
|||
$backtrace = debug_backtrace(); |
|||
} |
|||
$context = call_user_func($this->_contextCallback, $code, $params, $backtrace); |
|||
} |
|||
|
|||
// save error
|
|||
$time = explode(' ', microtime()); |
|||
$time = $time[1] + $time[0]; |
|||
$err = array( |
|||
'code' => $code, |
|||
'params' => $params, |
|||
'package' => $this->_package, |
|||
'level' => $level, |
|||
'time' => $time, |
|||
'context' => $context, |
|||
'message' => $msg, |
|||
); |
|||
|
|||
// set up the error message, if necessary
|
|||
if ($this->_msgCallback) { |
|||
$msg = call_user_func_array($this->_msgCallback, |
|||
array(&$this, $err)); |
|||
$err['message'] = $msg; |
|||
} |
|||
|
|||
if ($repackage) { |
|||
$err['repackage'] = $repackage; |
|||
} |
|||
$push = $log = true; |
|||
$die = false; |
|||
// try the overriding callback first
|
|||
$callback = $this->staticPopCallback(); |
|||
if ($callback) { |
|||
$this->staticPushCallback($callback); |
|||
} |
|||
if (!is_callable($callback)) { |
|||
// try the local callback next
|
|||
$callback = $this->popCallback(); |
|||
if (is_callable($callback)) { |
|||
$this->pushCallback($callback); |
|||
} else { |
|||
// try the default callback
|
|||
$callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? |
|||
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : |
|||
$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; |
|||
} |
|||
} |
|||
if (is_callable($callback)) { |
|||
switch(call_user_func($callback, $err)){ |
|||
case PEAR_ERRORSTACK_IGNORE: |
|||
return $err; |
|||
break; |
|||
case PEAR_ERRORSTACK_PUSH: |
|||
$log = false; |
|||
break; |
|||
case PEAR_ERRORSTACK_LOG: |
|||
$push = false; |
|||
break; |
|||
case PEAR_ERRORSTACK_DIE: |
|||
$die = true; |
|||
break; |
|||
// anything else returned has the same effect as pushandlog
|
|||
} |
|||
} |
|||
if ($push) { |
|||
array_unshift($this->_errors, $err); |
|||
$this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; |
|||
} |
|||
if ($log) { |
|||
if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { |
|||
$this->_log($err); |
|||
} |
|||
} |
|||
if ($die) { |
|||
die(); |
|||
} |
|||
if ($this->_compat && $push) { |
|||
return $this->raiseError($msg, $code, null, null, $err); |
|||
} |
|||
return $err; |
|||
} |
|||
|
|||
/** |
|||
* Static version of {@link push()} |
|||
* |
|||
* @param string $package Package name this error belongs to |
|||
* @param int $code Package-specific error code |
|||
* @param string $level Error level. This is NOT spell-checked |
|||
* @param array $params associative array of error parameters |
|||
* @param string $msg Error message, or a portion of it if the message |
|||
* is to be generated |
|||
* @param array $repackage If this error re-packages an error pushed by |
|||
* another package, place the array returned from |
|||
* {@link pop()} in this parameter |
|||
* @param array $backtrace Protected parameter: use this to pass in the |
|||
* {@link debug_backtrace()} that should be used |
|||
* to find error context |
|||
* @return PEAR_Error|null|Exception |
|||
* if compatibility mode is on, a PEAR_Error is also |
|||
* thrown. If the class Exception exists, then one |
|||
* is returned to allow code like: |
|||
* <code> |
|||
* throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob'))); |
|||
* </code> |
|||
* @static |
|||
*/ |
|||
function staticPush($package, $code, $level = 'error', $params = array(), |
|||
$msg = false, $repackage = false, $backtrace = false) |
|||
{ |
|||
$s = &PEAR_ErrorStack::singleton($package); |
|||
if ($s->_contextCallback) { |
|||
if (!$backtrace) { |
|||
if (function_exists('debug_backtrace')) { |
|||
$backtrace = debug_backtrace(); |
|||
} |
|||
} |
|||
} |
|||
return $s->push($code, $level, $params, $msg, $repackage, $backtrace); |
|||
} |
|||
|
|||
/** |
|||
* Log an error using PEAR::Log |
|||
* @param array $err Error array |
|||
* @param array $levels Error level => Log constant map |
|||
* @access protected |
|||
*/ |
|||
function _log($err) |
|||
{ |
|||
if ($this->_logger) { |
|||
$logger = &$this->_logger; |
|||
} else { |
|||
$logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; |
|||
} |
|||
if (is_a($logger, 'Log')) { |
|||
$levels = array( |
|||
'exception' => PEAR_LOG_CRIT, |
|||
'alert' => PEAR_LOG_ALERT, |
|||
'critical' => PEAR_LOG_CRIT, |
|||
'error' => PEAR_LOG_ERR, |
|||
'warning' => PEAR_LOG_WARNING, |
|||
'notice' => PEAR_LOG_NOTICE, |
|||
'info' => PEAR_LOG_INFO, |
|||
'debug' => PEAR_LOG_DEBUG); |
|||
if (isset($levels[$err['level']])) { |
|||
$level = $levels[$err['level']]; |
|||
} else { |
|||
$level = PEAR_LOG_INFO; |
|||
} |
|||
$logger->log($err['message'], $level, $err); |
|||
} else { // support non-standard logs
|
|||
call_user_func($logger, $err); |
|||
} |
|||
} |
|||
|
|||
|
|||
/** |
|||
* Pop an error off of the error stack |
|||
* |
|||
* @return false|array |
|||
* @since 0.4alpha it is no longer possible to specify a specific error |
|||
* level to return - the last error pushed will be returned, instead |
|||
*/ |
|||
function pop() |
|||
{ |
|||
return @array_shift($this->_errors); |
|||
} |
|||
|
|||
/** |
|||
* Determine whether there are any errors on the stack |
|||
* @param string|array Level name. Use to determine if any errors |
|||
* of level (string), or levels (array) have been pushed |
|||
* @return boolean |
|||
*/ |
|||
function hasErrors($level = false) |
|||
{ |
|||
if ($level) { |
|||
return isset($this->_errorsByLevel[$level]); |
|||
} |
|||
return count($this->_errors); |
|||
} |
|||
|
|||
/** |
|||
* Retrieve all errors since last purge |
|||
* |
|||
* @param boolean set in order to empty the error stack |
|||
* @param string level name, to return only errors of a particular severity |
|||
* @return array |
|||
*/ |
|||
function getErrors($purge = false, $level = false) |
|||
{ |
|||
if (!$purge) { |
|||
if ($level) { |
|||
if (!isset($this->_errorsByLevel[$level])) { |
|||
return array(); |
|||
} else { |
|||
return $this->_errorsByLevel[$level]; |
|||
} |
|||
} else { |
|||
return $this->_errors; |
|||
} |
|||
} |
|||
if ($level) { |
|||
$ret = $this->_errorsByLevel[$level]; |
|||
foreach ($this->_errorsByLevel[$level] as $i => $unused) { |
|||
// entries are references to the $_errors array
|
|||
$this->_errorsByLevel[$level][$i] = false; |
|||
} |
|||
// array_filter removes all entries === false
|
|||
$this->_errors = array_filter($this->_errors); |
|||
unset($this->_errorsByLevel[$level]); |
|||
return $ret; |
|||
} |
|||
$ret = $this->_errors; |
|||
$this->_errors = array(); |
|||
$this->_errorsByLevel = array(); |
|||
return $ret; |
|||
} |
|||
|
|||
/** |
|||
* Determine whether there are any errors on a single error stack, or on any error stack |
|||
* |
|||
* The optional parameter can be used to test the existence of any errors without the need of |
|||
* singleton instantiation |
|||
* @param string|false Package name to check for errors |
|||
* @param string Level name to check for a particular severity |
|||
* @return boolean |
|||
* @static |
|||
*/ |
|||
function staticHasErrors($package = false, $level = false) |
|||
{ |
|||
if ($package) { |
|||
if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { |
|||
return false; |
|||
} |
|||
return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); |
|||
} |
|||
foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { |
|||
if ($obj->hasErrors($level)) { |
|||
return true; |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* Get a list of all errors since last purge, organized by package |
|||
* @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be |
|||
* @param boolean $purge Set to purge the error stack of existing errors |
|||
* @param string $level Set to a level name in order to retrieve only errors of a particular level |
|||
* @param boolean $merge Set to return a flat array, not organized by package |
|||
* @param array $sortfunc Function used to sort a merged array - default |
|||
* sorts by time, and should be good for most cases |
|||
* @static |
|||
* @return array |
|||
*/ |
|||
function staticGetErrors($purge = false, $level = false, $merge = false, |
|||
$sortfunc = array('PEAR_ErrorStack', '_sortErrors')) |
|||
{ |
|||
$ret = array(); |
|||
if (!is_callable($sortfunc)) { |
|||
$sortfunc = array('PEAR_ErrorStack', '_sortErrors'); |
|||
} |
|||
foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { |
|||
$test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); |
|||
if ($test) { |
|||
if ($merge) { |
|||
$ret = array_merge($ret, $test); |
|||
} else { |
|||
$ret[$package] = $test; |
|||
} |
|||
} |
|||
} |
|||
if ($merge) { |
|||
usort($ret, $sortfunc); |
|||
} |
|||
return $ret; |
|||
} |
|||
|
|||
/** |
|||
* Error sorting function, sorts by time |
|||
* @access private |
|||
*/ |
|||
function _sortErrors($a, $b) |
|||
{ |
|||
if ($a['time'] == $b['time']) { |
|||
return 0; |
|||
} |
|||
if ($a['time'] < $b['time']) { |
|||
return 1; |
|||
} |
|||
return -1; |
|||
} |
|||
|
|||
/** |
|||
* Standard file/line number/function/class context callback |
|||
* |
|||
* This function uses a backtrace generated from {@link debug_backtrace()} |
|||
* and so will not work at all in PHP < 4.3.0. The frame should |
|||
* reference the frame that contains the source of the error. |
|||
* @return array|false either array('file' => file, 'line' => line, |
|||
* 'function' => function name, 'class' => class name) or |
|||
* if this doesn't work, then false |
|||
* @param unused |
|||
* @param integer backtrace frame. |
|||
* @param array Results of debug_backtrace() |
|||
* @static |
|||
*/ |
|||
function getFileLine($code, $params, $backtrace = null) |
|||
{ |
|||
if ($backtrace === null) { |
|||
return false; |
|||
} |
|||
$frame = 0; |
|||
$functionframe = 1; |
|||
if (!isset($backtrace[1])) { |
|||
$functionframe = 0; |
|||
} else { |
|||
while (isset($backtrace[$functionframe]['function']) && |
|||
$backtrace[$functionframe]['function'] == 'eval' && |
|||
isset($backtrace[$functionframe + 1])) { |
|||
$functionframe++; |
|||
} |
|||
} |
|||
if (isset($backtrace[$frame])) { |
|||
if (!isset($backtrace[$frame]['file'])) { |
|||
$frame++; |
|||
} |
|||
$funcbacktrace = $backtrace[$functionframe]; |
|||
$filebacktrace = $backtrace[$frame]; |
|||
$ret = array('file' => $filebacktrace['file'], |
|||
'line' => $filebacktrace['line']); |
|||
// rearrange for eval'd code or create function errors
|
|||
if (strpos($filebacktrace['file'], '(') && |
|||
preg_match(';^(.*?)\((\d+)\) : (.*?)$;', $filebacktrace['file'], |
|||
$matches)) { |
|||
$ret['file'] = $matches[1]; |
|||
$ret['line'] = $matches[2] + 0; |
|||
} |
|||
if (isset($funcbacktrace['function']) && isset($backtrace[1])) { |
|||
if ($funcbacktrace['function'] != 'eval') { |
|||
if ($funcbacktrace['function'] == '__lambda_func') { |
|||
$ret['function'] = 'create_function() code'; |
|||
} else { |
|||
$ret['function'] = $funcbacktrace['function']; |
|||
} |
|||
} |
|||
} |
|||
if (isset($funcbacktrace['class']) && isset($backtrace[1])) { |
|||
$ret['class'] = $funcbacktrace['class']; |
|||
} |
|||
return $ret; |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* Standard error message generation callback |
|||
* |
|||
* This method may also be called by a custom error message generator |
|||
* to fill in template values from the params array, simply |
|||
* set the third parameter to the error message template string to use |
|||
* |
|||
* The special variable %__msg% is reserved: use it only to specify |
|||
* where a message passed in by the user should be placed in the template, |
|||
* like so: |
|||
* |
|||
* Error message: %msg% - internal error |
|||
* |
|||
* If the message passed like so: |
|||
* |
|||
* <code> |
|||
* $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); |
|||
* </code> |
|||
* |
|||
* The returned error message will be "Error message: server error 500 -
|
|||
* internal error"
|
|||
* @param PEAR_ErrorStack |
|||
* @param array |
|||
* @param string|false Pre-generated error message template |
|||
* @static |
|||
* @return string |
|||
*/ |
|||
function getErrorMessage(&$stack, $err, $template = false) |
|||
{ |
|||
if ($template) { |
|||
$mainmsg = $template; |
|||
} else { |
|||
$mainmsg = $stack->getErrorMessageTemplate($err['code']); |
|||
} |
|||
$mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); |
|||
if (count($err['params'])) { |
|||
foreach ($err['params'] as $name => $val) { |
|||
if (is_array($val)) { |
|||
// @ is needed in case $val is a multi-dimensional array
|
|||
$val = @implode(', ', $val); |
|||
} |
|||
if (is_object($val)) { |
|||
if (method_exists($val, '__toString')) { |
|||
$val = $val->__toString(); |
|||
} else { |
|||
PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, |
|||
'warning', array('obj' => get_class($val)), |
|||
'object %obj% passed into getErrorMessage, but has no __toString() method'); |
|||
$val = 'Object'; |
|||
} |
|||
} |
|||
$mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); |
|||
} |
|||
} |
|||
return $mainmsg; |
|||
} |
|||
|
|||
/** |
|||
* Standard Error Message Template generator from code |
|||
* @return string |
|||
*/ |
|||
function getErrorMessageTemplate($code) |
|||
{ |
|||
if (!isset($this->_errorMsgs[$code])) { |
|||
return '%__msg%'; |
|||
} |
|||
return $this->_errorMsgs[$code]; |
|||
} |
|||
|
|||
/** |
|||
* Set the Error Message Template array |
|||
* |
|||
* The array format must be: |
|||
* <pre> |
|||
* array(error code => 'message template',...) |
|||
* </pre> |
|||
* |
|||
* Error message parameters passed into {@link push()} will be used as input |
|||
* for the error message. If the template is 'message %foo% was %bar%', and the |
|||
* parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will |
|||
* be 'message one was six' |
|||
* @return string |
|||
*/ |
|||
function setErrorMessageTemplate($template) |
|||
{ |
|||
$this->_errorMsgs = $template; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* emulate PEAR::raiseError() |
|||
* |
|||
* @return PEAR_Error |
|||
*/ |
|||
function raiseError() |
|||
{ |
|||
require_once 'PEAR.php'; |
|||
$args = func_get_args(); |
|||
return call_user_func_array(array('PEAR', 'raiseError'), $args); |
|||
} |
|||
} |
|||
$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); |
|||
$stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); |
|||
?>
|
|||
@ -1,359 +0,0 @@ |
|||
<?php |
|||
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */ |
|||
// +----------------------------------------------------------------------+
|
|||
// | PEAR_Exception |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 2004 The PEAR Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available at through the world-wide-web at |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Authors: Tomas V.V.Cox <cox@idecnet.com> |
|
|||
// | Hans Lellelid <hans@velum.net> |
|
|||
// | Bertrand Mansion <bmansion@mamasam.com> |
|
|||
// | Greg Beaver <cellog@php.net> |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
|
|||
/** |
|||
* Base PEAR_Exception Class |
|||
* |
|||
* WARNING: This code should be considered stable, but the API is |
|||
* subject to immediate and drastic change, so API stability is |
|||
* at best alpha |
|||
* |
|||
* 1) Features: |
|||
* |
|||
* - Nestable exceptions (throw new PEAR_Exception($msg, $prev_exception)) |
|||
* - Definable triggers, shot when exceptions occur |
|||
* - Pretty and informative error messages |
|||
* - Added more context info available (like class, method or cause) |
|||
* - cause can be a PEAR_Exception or an array of mixed |
|||
* PEAR_Exceptions/PEAR_ErrorStack warnings |
|||
* - callbacks for specific exception classes and their children |
|||
* |
|||
* 2) Ideas: |
|||
* |
|||
* - Maybe a way to define a 'template' for the output |
|||
* |
|||
* 3) Inherited properties from PHP Exception Class: |
|||
* |
|||
* protected $message |
|||
* protected $code |
|||
* protected $line |
|||
* protected $file |
|||
* private $trace |
|||
* |
|||
* 4) Inherited methods from PHP Exception Class: |
|||
* |
|||
* __clone |
|||
* __construct |
|||
* getMessage |
|||
* getCode |
|||
* getFile |
|||
* getLine |
|||
* getTraceSafe |
|||
* getTraceSafeAsString |
|||
* __toString |
|||
* |
|||
* 5) Usage example |
|||
* |
|||
* <code> |
|||
* require_once 'PEAR/Exception.php'; |
|||
* |
|||
* class Test { |
|||
* function foo() { |
|||
* throw new PEAR_Exception('Error Message', ERROR_CODE); |
|||
* } |
|||
* } |
|||
* |
|||
* function myLogger($pear_exception) { |
|||
* echo $pear_exception->getMessage(); |
|||
* } |
|||
* // each time a exception is thrown the 'myLogger' will be called
|
|||
* // (its use is completely optional)
|
|||
* PEAR_Exception::addObserver('myLogger'); |
|||
* $test = new Test; |
|||
* try { |
|||
* $test->foo(); |
|||
* } catch (PEAR_Exception $e) { |
|||
* print $e; |
|||
* } |
|||
* </code> |
|||
* |
|||
* @since PHP 5 |
|||
* @package PEAR |
|||
* @version $Revision$ |
|||
* @author Tomas V.V.Cox <cox@idecnet.com> |
|||
* @author Hans Lellelid <hans@velum.net> |
|||
* @author Bertrand Mansion <bmansion@mamasam.com> |
|||
* |
|||
*/ |
|||
class PEAR_Exception extends Exception |
|||
{ |
|||
const OBSERVER_PRINT = -2; |
|||
const OBSERVER_TRIGGER = -4; |
|||
const OBSERVER_DIE = -8; |
|||
protected $cause; |
|||
private static $_observers = array(); |
|||
private static $_uniqueid = 0; |
|||
private $_trace; |
|||
|
|||
/** |
|||
* Supported signatures: |
|||
* PEAR_Exception(string $message); |
|||
* PEAR_Exception(string $message, int $code); |
|||
* PEAR_Exception(string $message, Exception $cause); |
|||
* PEAR_Exception(string $message, Exception $cause, int $code); |
|||
* PEAR_Exception(string $message, array $causes); |
|||
* PEAR_Exception(string $message, array $causes, int $code); |
|||
*/ |
|||
public function __construct($message, $p2 = null, $p3 = null) |
|||
{ |
|||
if (is_int($p2)) { |
|||
$code = $p2; |
|||
$this->cause = null; |
|||
} elseif ($p2 instanceof Exception || is_array($p2)) { |
|||
$code = $p3; |
|||
if (is_array($p2) && isset($p2['message'])) { |
|||
// fix potential problem of passing in a single warning
|
|||
$p2 = array($p2); |
|||
} |
|||
$this->cause = $p2; |
|||
} else { |
|||
$code = null; |
|||
$this->cause = null; |
|||
} |
|||
parent::__construct($message, $code); |
|||
$this->signal(); |
|||
} |
|||
|
|||
/** |
|||
* @param mixed $callback - A valid php callback, see php func is_callable() |
|||
* - A PEAR_Exception::OBSERVER_* constant |
|||
* - An array(const PEAR_Exception::OBSERVER_*, |
|||
* mixed $options) |
|||
* @param string $label The name of the observer. Use this if you want |
|||
* to remove it later with removeObserver() |
|||
*/ |
|||
public static function addObserver($callback, $label = 'default') |
|||
{ |
|||
self::$_observers[$label] = $callback; |
|||
} |
|||
|
|||
public static function removeObserver($label = 'default') |
|||
{ |
|||
unset(self::$_observers[$label]); |
|||
} |
|||
|
|||
/** |
|||
* @return int unique identifier for an observer |
|||
*/ |
|||
public static function getUniqueId() |
|||
{ |
|||
return self::$_uniqueid++; |
|||
} |
|||
|
|||
private function signal() |
|||
{ |
|||
foreach (self::$_observers as $func) { |
|||
if (is_callable($func)) { |
|||
call_user_func($func, $this); |
|||
continue; |
|||
} |
|||
settype($func, 'array'); |
|||
switch ($func[0]) { |
|||
case self::OBSERVER_PRINT : |
|||
$f = (isset($func[1])) ? $func[1] : '%s'; |
|||
printf($f, $this->getMessage()); |
|||
break; |
|||
case self::OBSERVER_TRIGGER : |
|||
$f = (isset($func[1])) ? $func[1] : E_USER_NOTICE; |
|||
trigger_error($this->getMessage(), $f); |
|||
break; |
|||
case self::OBSERVER_DIE : |
|||
$f = (isset($func[1])) ? $func[1] : '%s'; |
|||
die(printf($f, $this->getMessage())); |
|||
break; |
|||
default: |
|||
trigger_error('invalid observer type', E_USER_WARNING); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Return specific error information that can be used for more detailed |
|||
* error messages or translation. |
|||
* |
|||
* This method may be overridden in child exception classes in order |
|||
* to add functionality not present in PEAR_Exception and is a placeholder |
|||
* to define API |
|||
* |
|||
* The returned array must be an associative array of parameter => value like so: |
|||
* <pre> |
|||
* array('name' => $name, 'context' => array(...)) |
|||
* </pre> |
|||
* @return array |
|||
*/ |
|||
public function getErrorData() |
|||
{ |
|||
return array(); |
|||
} |
|||
|
|||
/** |
|||
* Returns the exception that caused this exception to be thrown |
|||
* @access public |
|||
* @return Exception|array The context of the exception |
|||
*/ |
|||
public function getCause() |
|||
{ |
|||
return $this->cause; |
|||
} |
|||
|
|||
/** |
|||
* Function must be public to call on caused exceptions |
|||
* @param array |
|||
*/ |
|||
public function getCauseMessage(&$causes) |
|||
{ |
|||
$trace = $this->getTraceSafe(); |
|||
$cause = array('class' => get_class($this), |
|||
'message' => $this->message, |
|||
'file' => 'unknown', |
|||
'line' => 'unknown'); |
|||
if (isset($trace[0])) { |
|||
if (isset($trace[0]['file'])) { |
|||
$cause['file'] = $trace[0]['file']; |
|||
$cause['line'] = $trace[0]['line']; |
|||
} |
|||
} |
|||
if ($this->cause instanceof PEAR_Exception) { |
|||
$this->cause->getCauseMessage($causes); |
|||
} |
|||
if (is_array($this->cause)) { |
|||
foreach ($this->cause as $cause) { |
|||
if ($cause instanceof PEAR_Exception) { |
|||
$cause->getCauseMessage($causes); |
|||
} elseif (is_array($cause) && isset($cause['message'])) { |
|||
// PEAR_ErrorStack warning
|
|||
$causes[] = array( |
|||
'class' => $cause['package'], |
|||
'message' => $cause['message'], |
|||
'file' => isset($cause['context']['file']) ? |
|||
$cause['context']['file'] : |
|||
'unknown', |
|||
'line' => isset($cause['context']['line']) ? |
|||
$cause['context']['line'] : |
|||
'unknown', |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public function getTraceSafe() |
|||
{ |
|||
if (!isset($this->_trace)) { |
|||
$this->_trace = $this->getTrace(); |
|||
if (empty($this->_trace)) { |
|||
$backtrace = debug_backtrace(); |
|||
$this->_trace = array($backtrace[count($backtrace)-1]); |
|||
} |
|||
} |
|||
return $this->_trace; |
|||
} |
|||
|
|||
public function getErrorClass() |
|||
{ |
|||
$trace = $this->getTraceSafe(); |
|||
return $trace[0]['class']; |
|||
} |
|||
|
|||
public function getErrorMethod() |
|||
{ |
|||
$trace = $this->getTraceSafe(); |
|||
return $trace[0]['function']; |
|||
} |
|||
|
|||
public function __toString() |
|||
{ |
|||
if (isset($_SERVER['REQUEST_URI'])) { |
|||
return $this->toHtml(); |
|||
} |
|||
return $this->toText(); |
|||
} |
|||
|
|||
public function toHtml() |
|||
{ |
|||
$trace = $this->getTraceSafe(); |
|||
$causes = array(); |
|||
$this->getCauseMessage($causes); |
|||
$html = '<table border="1" cellspacing="0">' . "\n"; |
|||
foreach ($causes as $i => $cause) { |
|||
$html .= '<tr><td colspan="3" bgcolor="#ff9999">' |
|||
. str_repeat('-', $i) . ' <b>' . $cause['class'] . '</b>: ' |
|||
. htmlspecialchars($cause['message']) . ' in <b>' . $cause['file'] . '</b> ' |
|||
. 'on line <b>' . $cause['line'] . '</b>' |
|||
. "</td></tr>\n"; |
|||
} |
|||
$html .= '<tr><td colspan="3" bgcolor="#aaaaaa" align="center"><b>Exception trace</b></td></tr>' . "\n" |
|||
. '<tr><td align="center" bgcolor="#cccccc" width="20"><b>#</b></td>' |
|||
. '<td align="center" bgcolor="#cccccc"><b>Function</b></td>' |
|||
. '<td align="center" bgcolor="#cccccc"><b>Location</b></td></tr>' . "\n"; |
|||
|
|||
foreach ($trace as $k => $v) { |
|||
$html .= '<tr><td align="center">' . $k . '</td>' |
|||
. '<td>'; |
|||
if (!empty($v['class'])) { |
|||
$html .= $v['class'] . $v['type']; |
|||
} |
|||
$html .= $v['function']; |
|||
$args = array(); |
|||
if (!empty($v['args'])) { |
|||
foreach ($v['args'] as $arg) { |
|||
if (is_null($arg)) $args[] = 'null'; |
|||
elseif (is_array($arg)) $args[] = 'Array'; |
|||
elseif (is_object($arg)) $args[] = 'Object('.get_class($arg).')'; |
|||
elseif (is_bool($arg)) $args[] = $arg ? 'true' : 'false'; |
|||
elseif (is_int($arg) || is_double($arg)) $args[] = $arg; |
|||
else { |
|||
$arg = (string)$arg; |
|||
$str = htmlspecialchars(substr($arg, 0, 16)); |
|||
if (strlen($arg) > 16) $str .= '…'; |
|||
$args[] = "'" . $str . "'"; |
|||
} |
|||
} |
|||
} |
|||
$html .= '(' . implode(', ',$args) . ')' |
|||
. '</td>' |
|||
. '<td>' . $v['file'] . ':' . $v['line'] . '</td></tr>' . "\n"; |
|||
} |
|||
$html .= '<tr><td align="center">' . ($k+1) . '</td>' |
|||
. '<td>{main}</td>' |
|||
. '<td> </td></tr>' . "\n" |
|||
. '</table>'; |
|||
return $html; |
|||
} |
|||
|
|||
public function toText() |
|||
{ |
|||
$causes = array(); |
|||
$this->getCauseMessage($causes); |
|||
$causeMsg = ''; |
|||
foreach ($causes as $i => $cause) { |
|||
$causeMsg .= str_repeat(' ', $i) . $cause['class'] . ': ' |
|||
. $cause['message'] . ' in ' . $cause['file'] |
|||
. ' on line ' . $cause['line'] . "\n"; |
|||
} |
|||
return $causeMsg . $this->getTraceAsString(); |
|||
} |
|||
} |
|||
|
|||
?>
|
|||
1068
pear/PEAR/Installer.php
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,165 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Authors: Stig Bakken <ssb@php.net> |
|
|||
// | Tomas V.V.Cox <cox@idecnet.com> |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
require_once 'PEAR/Common.php'; |
|||
require_once 'System.php'; |
|||
|
|||
/** |
|||
* Administration class used to make a PEAR release tarball. |
|||
* |
|||
* TODO: |
|||
* - add an extra param the dir where to place the created package |
|||
* |
|||
* @since PHP 4.0.2 |
|||
* @author Stig Bakken <ssb@php.net> |
|||
*/ |
|||
class PEAR_Packager extends PEAR_Common |
|||
{ |
|||
// {{{ constructor
|
|||
|
|||
function PEAR_Packager() |
|||
{ |
|||
parent::PEAR_Common(); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ destructor
|
|||
|
|||
function _PEAR_Packager() |
|||
{ |
|||
parent::_PEAR_Common(); |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ package()
|
|||
|
|||
function package($pkgfile = null, $compress = true) |
|||
{ |
|||
// {{{ validate supplied package.xml file
|
|||
if (empty($pkgfile)) { |
|||
$pkgfile = 'package.xml'; |
|||
} |
|||
// $this->pkginfo gets populated inside
|
|||
$pkginfo = $this->infoFromDescriptionFile($pkgfile); |
|||
if (PEAR::isError($pkginfo)) { |
|||
return $this->raiseError($pkginfo); |
|||
} |
|||
|
|||
$pkgdir = dirname(realpath($pkgfile)); |
|||
$pkgfile = basename($pkgfile); |
|||
|
|||
$errors = $warnings = array(); |
|||
$this->validatePackageInfo($pkginfo, $errors, $warnings, $pkgdir); |
|||
foreach ($warnings as $w) { |
|||
$this->log(1, "Warning: $w"); |
|||
} |
|||
foreach ($errors as $e) { |
|||
$this->log(0, "Error: $e"); |
|||
} |
|||
if (sizeof($errors) > 0) { |
|||
return $this->raiseError('Errors in package'); |
|||
} |
|||
// }}}
|
|||
|
|||
$pkgver = $pkginfo['package'] . '-' . $pkginfo['version']; |
|||
|
|||
// {{{ Create the package file list
|
|||
$filelist = array(); |
|||
$i = 0; |
|||
|
|||
foreach ($pkginfo['filelist'] as $fname => $atts) { |
|||
$file = $pkgdir . DIRECTORY_SEPARATOR . $fname; |
|||
if (!file_exists($file)) { |
|||
return $this->raiseError("File does not exist: $fname"); |
|||
} else { |
|||
$filelist[$i++] = $file; |
|||
if (empty($pkginfo['filelist'][$fname]['md5sum'])) { |
|||
$md5sum = md5_file($file); |
|||
$pkginfo['filelist'][$fname]['md5sum'] = $md5sum; |
|||
} |
|||
$this->log(2, "Adding file $fname"); |
|||
} |
|||
} |
|||
// }}}
|
|||
|
|||
// {{{ regenerate package.xml
|
|||
$new_xml = $this->xmlFromInfo($pkginfo); |
|||
if (PEAR::isError($new_xml)) { |
|||
return $this->raiseError($new_xml); |
|||
} |
|||
if (!($tmpdir = System::mktemp(array('-d')))) { |
|||
return $this->raiseError("PEAR_Packager: mktemp failed"); |
|||
} |
|||
$newpkgfile = $tmpdir . DIRECTORY_SEPARATOR . 'package.xml'; |
|||
$np = @fopen($newpkgfile, 'wb'); |
|||
if (!$np) { |
|||
return $this->raiseError("PEAR_Packager: unable to rewrite $pkgfile as $newpkgfile"); |
|||
} |
|||
fwrite($np, $new_xml); |
|||
fclose($np); |
|||
// }}}
|
|||
|
|||
// {{{ TAR the Package -------------------------------------------
|
|||
$ext = $compress ? '.tgz' : '.tar'; |
|||
$dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext; |
|||
$tar =& new Archive_Tar($dest_package, $compress); |
|||
$tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
|
|||
// ----- Creates with the package.xml file
|
|||
$ok = $tar->createModify(array($newpkgfile), '', $tmpdir); |
|||
if (PEAR::isError($ok)) { |
|||
return $this->raiseError($ok); |
|||
} elseif (!$ok) { |
|||
return $this->raiseError('PEAR_Packager: tarball creation failed'); |
|||
} |
|||
// ----- Add the content of the package
|
|||
if (!$tar->addModify($filelist, $pkgver, $pkgdir)) { |
|||
return $this->raiseError('PEAR_Packager: tarball creation failed'); |
|||
} |
|||
$this->log(1, "Package $dest_package done"); |
|||
if (file_exists("$pkgdir/CVS/Root")) { |
|||
$cvsversion = preg_replace('/[^a-z0-9]/i', '_', $pkginfo['version']); |
|||
$cvstag = "RELEASE_$cvsversion"; |
|||
$this->log(1, "Tag the released code with `pear cvstag $pkgfile'"); |
|||
$this->log(1, "(or set the CVS tag $cvstag by hand)"); |
|||
} |
|||
// }}}
|
|||
|
|||
return $dest_package; |
|||
} |
|||
|
|||
// }}}
|
|||
} |
|||
|
|||
// {{{ md5_file() utility function
|
|||
if (!function_exists('md5_file')) { |
|||
function md5_file($file) { |
|||
if (!$fd = @fopen($file, 'r')) { |
|||
return false; |
|||
} |
|||
$md5 = md5(fread($fd, filesize($file))); |
|||
fclose($fd); |
|||
return $md5; |
|||
} |
|||
} |
|||
// }}}
|
|||
|
|||
?>
|
|||
@ -1,538 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Author: Stig Bakken <ssb@php.net> |
|
|||
// | Tomas V.V.Cox <cox@idecnet.com> |
|
|||
// | |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
/* |
|||
TODO: |
|||
- Transform into singleton() |
|||
- Add application level lock (avoid change the registry from the cmdline |
|||
while using the GTK interface, for ex.) |
|||
*/ |
|||
require_once "System.php"; |
|||
require_once "PEAR.php"; |
|||
|
|||
define('PEAR_REGISTRY_ERROR_LOCK', -2); |
|||
define('PEAR_REGISTRY_ERROR_FORMAT', -3); |
|||
define('PEAR_REGISTRY_ERROR_FILE', -4); |
|||
|
|||
/** |
|||
* Administration class used to maintain the installed package database. |
|||
*/ |
|||
class PEAR_Registry extends PEAR |
|||
{ |
|||
// {{{ properties
|
|||
|
|||
/** Directory where registry files are stored. |
|||
* @var string |
|||
*/ |
|||
var $statedir = ''; |
|||
|
|||
/** File where the file map is stored |
|||
* @var string |
|||
*/ |
|||
var $filemap = ''; |
|||
|
|||
/** Name of file used for locking the registry |
|||
* @var string |
|||
*/ |
|||
var $lockfile = ''; |
|||
|
|||
/** File descriptor used during locking |
|||
* @var resource |
|||
*/ |
|||
var $lock_fp = null; |
|||
|
|||
/** Mode used during locking |
|||
* @var int |
|||
*/ |
|||
var $lock_mode = 0; // XXX UNUSED
|
|||
|
|||
/** Cache of package information. Structure: |
|||
* array( |
|||
* 'package' => array('id' => ... ), |
|||
* ... ) |
|||
* @var array |
|||
*/ |
|||
var $pkginfo_cache = array(); |
|||
|
|||
/** Cache of file map. Structure: |
|||
* array( '/path/to/file' => 'package', ... ) |
|||
* @var array |
|||
*/ |
|||
var $filemap_cache = array(); |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ constructor
|
|||
|
|||
/** |
|||
* PEAR_Registry constructor. |
|||
* |
|||
* @param string (optional) PEAR install directory (for .php files) |
|||
* |
|||
* @access public |
|||
*/ |
|||
function PEAR_Registry($pear_install_dir = PEAR_INSTALL_DIR) |
|||
{ |
|||
parent::PEAR(); |
|||
$ds = DIRECTORY_SEPARATOR; |
|||
$this->install_dir = $pear_install_dir; |
|||
$this->statedir = $pear_install_dir.$ds.'.registry'; |
|||
$this->filemap = $pear_install_dir.$ds.'.filemap'; |
|||
$this->lockfile = $pear_install_dir.$ds.'.lock'; |
|||
|
|||
// XXX Compatibility code should be removed in the future
|
|||
// rename all registry files if any to lowercase
|
|||
if (!OS_WINDOWS && $handle = @opendir($this->statedir)) { |
|||
$dest = $this->statedir . DIRECTORY_SEPARATOR; |
|||
while (false !== ($file = readdir($handle))) { |
|||
if (preg_match('/^.*[A-Z].*\.reg$/', $file)) { |
|||
rename($dest . $file, $dest . strtolower($file)); |
|||
} |
|||
} |
|||
closedir($handle); |
|||
} |
|||
if (!file_exists($this->filemap)) { |
|||
$this->rebuildFileMap(); |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ destructor
|
|||
|
|||
/** |
|||
* PEAR_Registry destructor. Makes sure no locks are forgotten. |
|||
* |
|||
* @access private |
|||
*/ |
|||
function _PEAR_Registry() |
|||
{ |
|||
parent::_PEAR(); |
|||
if (is_resource($this->lock_fp)) { |
|||
$this->_unlock(); |
|||
} |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ _assertStateDir()
|
|||
|
|||
/** |
|||
* Make sure the directory where we keep registry files exists. |
|||
* |
|||
* @return bool TRUE if directory exists, FALSE if it could not be |
|||
* created |
|||
* |
|||
* @access private |
|||
*/ |
|||
function _assertStateDir() |
|||
{ |
|||
if (!@is_dir($this->statedir)) { |
|||
if (!System::mkdir(array('-p', $this->statedir))) { |
|||
return $this->raiseError("could not create directory '{$this->statedir}'"); |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _packageFileName()
|
|||
|
|||
/** |
|||
* Get the name of the file where data for a given package is stored. |
|||
* |
|||
* @param string package name |
|||
* |
|||
* @return string registry file name |
|||
* |
|||
* @access public |
|||
*/ |
|||
function _packageFileName($package) |
|||
{ |
|||
return $this->statedir . DIRECTORY_SEPARATOR . strtolower($package) . '.reg'; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _openPackageFile()
|
|||
|
|||
function _openPackageFile($package, $mode) |
|||
{ |
|||
$this->_assertStateDir(); |
|||
$file = $this->_packageFileName($package); |
|||
$fp = @fopen($file, $mode); |
|||
if (!$fp) { |
|||
return null; |
|||
} |
|||
return $fp; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _closePackageFile()
|
|||
|
|||
function _closePackageFile($fp) |
|||
{ |
|||
fclose($fp); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ rebuildFileMap()
|
|||
|
|||
function rebuildFileMap() |
|||
{ |
|||
$packages = $this->listPackages(); |
|||
$files = array(); |
|||
foreach ($packages as $package) { |
|||
$version = $this->packageInfo($package, 'version'); |
|||
$filelist = $this->packageInfo($package, 'filelist'); |
|||
if (!is_array($filelist)) { |
|||
continue; |
|||
} |
|||
foreach ($filelist as $name => $attrs) { |
|||
if (isset($attrs['role']) && $attrs['role'] != 'php') { |
|||
continue; |
|||
} |
|||
if (isset($attrs['baseinstalldir'])) { |
|||
$file = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; |
|||
} else { |
|||
$file = $name; |
|||
} |
|||
$file = preg_replace(',^/+,', '', $file); |
|||
$files[$file] = $package; |
|||
} |
|||
} |
|||
$this->_assertStateDir(); |
|||
$fp = @fopen($this->filemap, 'wb'); |
|||
if (!$fp) { |
|||
return false; |
|||
} |
|||
$this->filemap_cache = $files; |
|||
fwrite($fp, serialize($files)); |
|||
fclose($fp); |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ readFileMap()
|
|||
|
|||
function readFileMap() |
|||
{ |
|||
$fp = @fopen($this->filemap, 'r'); |
|||
if (!$fp) { |
|||
return $this->raiseError('PEAR_Registry: could not open filemap', PEAR_REGISTRY_ERROR_FILE, null, null, $php_errormsg); |
|||
} |
|||
$fsize = filesize($this->filemap); |
|||
$rt = get_magic_quotes_runtime(); |
|||
set_magic_quotes_runtime(0); |
|||
$data = fread($fp, $fsize); |
|||
set_magic_quotes_runtime($rt); |
|||
fclose($fp); |
|||
$tmp = unserialize($data); |
|||
if (!$tmp && $fsize > 7) { |
|||
return $this->raiseError('PEAR_Registry: invalid filemap data', PEAR_REGISTRY_ERROR_FORMAT, null, null, $data); |
|||
} |
|||
$this->filemap_cache = $tmp; |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _lock()
|
|||
|
|||
/** |
|||
* Lock the registry. |
|||
* |
|||
* @param integer lock mode, one of LOCK_EX, LOCK_SH or LOCK_UN. |
|||
* See flock manual for more information. |
|||
* |
|||
* @return bool TRUE on success, FALSE if locking failed, or a |
|||
* PEAR error if some other error occurs (such as the |
|||
* lock file not being writable). |
|||
* |
|||
* @access private |
|||
*/ |
|||
function _lock($mode = LOCK_EX) |
|||
{ |
|||
if (!eregi('Windows 9', php_uname())) { |
|||
if ($mode != LOCK_UN && is_resource($this->lock_fp)) { |
|||
// XXX does not check type of lock (LOCK_SH/LOCK_EX)
|
|||
return true; |
|||
} |
|||
if (PEAR::isError($err = $this->_assertStateDir())) { |
|||
return $err; |
|||
} |
|||
$open_mode = 'w'; |
|||
// XXX People reported problems with LOCK_SH and 'w'
|
|||
if ($mode === LOCK_SH || $mode === LOCK_UN) { |
|||
if (@!is_file($this->lockfile)) { |
|||
touch($this->lockfile); |
|||
} |
|||
$open_mode = 'r'; |
|||
} |
|||
|
|||
if (!is_resource($this->lock_fp)) { |
|||
$this->lock_fp = @fopen($this->lockfile, $open_mode); |
|||
} |
|||
|
|||
if (!is_resource($this->lock_fp)) { |
|||
return $this->raiseError("could not create lock file" . |
|||
(isset($php_errormsg) ? ": " . $php_errormsg : "")); |
|||
} |
|||
if (!(int)flock($this->lock_fp, $mode)) { |
|||
switch ($mode) { |
|||
case LOCK_SH: $str = 'shared'; break; |
|||
case LOCK_EX: $str = 'exclusive'; break; |
|||
case LOCK_UN: $str = 'unlock'; break; |
|||
default: $str = 'unknown'; break; |
|||
} |
|||
return $this->raiseError("could not acquire $str lock ($this->lockfile)", |
|||
PEAR_REGISTRY_ERROR_LOCK); |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _unlock()
|
|||
|
|||
function _unlock() |
|||
{ |
|||
$ret = $this->_lock(LOCK_UN); |
|||
if (is_resource($this->lock_fp)) { |
|||
fclose($this->lock_fp); |
|||
} |
|||
$this->lock_fp = null; |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _packageExists()
|
|||
|
|||
function _packageExists($package) |
|||
{ |
|||
return file_exists($this->_packageFileName($package)); |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _packageInfo()
|
|||
|
|||
function _packageInfo($package = null, $key = null) |
|||
{ |
|||
if ($package === null) { |
|||
return array_map(array($this, '_packageInfo'), |
|||
$this->_listPackages()); |
|||
} |
|||
$fp = $this->_openPackageFile($package, 'r'); |
|||
if ($fp === null) { |
|||
return null; |
|||
} |
|||
$rt = get_magic_quotes_runtime(); |
|||
set_magic_quotes_runtime(0); |
|||
$data = fread($fp, filesize($this->_packageFileName($package))); |
|||
set_magic_quotes_runtime($rt); |
|||
$this->_closePackageFile($fp); |
|||
$data = unserialize($data); |
|||
if ($key === null) { |
|||
return $data; |
|||
} |
|||
if (isset($data[$key])) { |
|||
return $data[$key]; |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ _listPackages()
|
|||
|
|||
function _listPackages() |
|||
{ |
|||
$pkglist = array(); |
|||
$dp = @opendir($this->statedir); |
|||
if (!$dp) { |
|||
return $pkglist; |
|||
} |
|||
while ($ent = readdir($dp)) { |
|||
if ($ent{0} == '.' || substr($ent, -4) != '.reg') { |
|||
continue; |
|||
} |
|||
$pkglist[] = substr($ent, 0, -4); |
|||
} |
|||
return $pkglist; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ packageExists()
|
|||
|
|||
function packageExists($package) |
|||
{ |
|||
if (PEAR::isError($e = $this->_lock(LOCK_SH))) { |
|||
return $e; |
|||
} |
|||
$ret = $this->_packageExists($package); |
|||
$this->_unlock(); |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ packageInfo()
|
|||
|
|||
function packageInfo($package = null, $key = null) |
|||
{ |
|||
if (PEAR::isError($e = $this->_lock(LOCK_SH))) { |
|||
return $e; |
|||
} |
|||
$ret = $this->_packageInfo($package, $key); |
|||
$this->_unlock(); |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ listPackages()
|
|||
|
|||
function listPackages() |
|||
{ |
|||
if (PEAR::isError($e = $this->_lock(LOCK_SH))) { |
|||
return $e; |
|||
} |
|||
$ret = $this->_listPackages(); |
|||
$this->_unlock(); |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ addPackage()
|
|||
|
|||
function addPackage($package, $info) |
|||
{ |
|||
if ($this->packageExists($package)) { |
|||
return false; |
|||
} |
|||
if (PEAR::isError($e = $this->_lock(LOCK_EX))) { |
|||
return $e; |
|||
} |
|||
$fp = $this->_openPackageFile($package, 'wb'); |
|||
if ($fp === null) { |
|||
$this->_unlock(); |
|||
return false; |
|||
} |
|||
$info['_lastmodified'] = time(); |
|||
fwrite($fp, serialize($info)); |
|||
$this->_closePackageFile($fp); |
|||
$this->_unlock(); |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ deletePackage()
|
|||
|
|||
function deletePackage($package) |
|||
{ |
|||
if (PEAR::isError($e = $this->_lock(LOCK_EX))) { |
|||
return $e; |
|||
} |
|||
$file = $this->_packageFileName($package); |
|||
$ret = @unlink($file); |
|||
$this->rebuildFileMap(); |
|||
$this->_unlock(); |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ updatePackage()
|
|||
|
|||
function updatePackage($package, $info, $merge = true) |
|||
{ |
|||
$oldinfo = $this->packageInfo($package); |
|||
if (empty($oldinfo)) { |
|||
return false; |
|||
} |
|||
if (PEAR::isError($e = $this->_lock(LOCK_EX))) { |
|||
return $e; |
|||
} |
|||
$fp = $this->_openPackageFile($package, 'w'); |
|||
if ($fp === null) { |
|||
$this->_unlock(); |
|||
return false; |
|||
} |
|||
$info['_lastmodified'] = time(); |
|||
if ($merge) { |
|||
fwrite($fp, serialize(array_merge($oldinfo, $info))); |
|||
} else { |
|||
fwrite($fp, serialize($info)); |
|||
} |
|||
$this->_closePackageFile($fp); |
|||
if (isset($info['filelist'])) { |
|||
$this->rebuildFileMap(); |
|||
} |
|||
$this->_unlock(); |
|||
return true; |
|||
} |
|||
|
|||
// }}}
|
|||
// {{{ checkFileMap()
|
|||
|
|||
/** |
|||
* Test whether a file belongs to a package. |
|||
* |
|||
* @param string $path file path, absolute or relative to the pear |
|||
* install dir |
|||
* |
|||
* @return string which package the file belongs to, or an empty |
|||
* string if the file does not belong to an installed package |
|||
* |
|||
* @access public |
|||
*/ |
|||
function checkFileMap($path) |
|||
{ |
|||
if (is_array($path)) { |
|||
static $notempty; |
|||
if (empty($notempty)) { |
|||
$notempty = create_function('$a','return !empty($a);'); |
|||
} |
|||
$pkgs = array(); |
|||
foreach ($path as $name => $attrs) { |
|||
if (is_array($attrs) && isset($attrs['baseinstalldir'])) { |
|||
$name = $attrs['baseinstalldir'].DIRECTORY_SEPARATOR.$name; |
|||
} |
|||
$pkgs[$name] = $this->checkFileMap($name); |
|||
} |
|||
return array_filter($pkgs, $notempty); |
|||
} |
|||
if (empty($this->filemap_cache) && PEAR::isError($this->readFileMap())) { |
|||
return $err; |
|||
} |
|||
if (isset($this->filemap_cache[$path])) { |
|||
return $this->filemap_cache[$path]; |
|||
} |
|||
$l = strlen($this->install_dir); |
|||
if (substr($path, 0, $l) == $this->install_dir) { |
|||
$path = preg_replace('!^'.DIRECTORY_SEPARATOR.'+!', '', substr($path, $l)); |
|||
} |
|||
if (isset($this->filemap_cache[$path])) { |
|||
return $this->filemap_cache[$path]; |
|||
} |
|||
return ''; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
} |
|||
|
|||
?>
|
|||
@ -1,394 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Author: Stig Bakken <ssb@php.net> |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
|
|||
require_once 'PEAR.php'; |
|||
require_once 'PEAR/Config.php'; |
|||
|
|||
/** |
|||
* This is a class for doing remote operations against the central |
|||
* PEAR database. |
|||
* |
|||
* @nodep XML_RPC_Value |
|||
* @nodep XML_RPC_Message |
|||
* @nodep XML_RPC_Client |
|||
*/ |
|||
class PEAR_Remote extends PEAR |
|||
{ |
|||
// {{{ properties
|
|||
|
|||
var $config = null; |
|||
var $cache = null; |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ PEAR_Remote(config_object)
|
|||
|
|||
function PEAR_Remote(&$config) |
|||
{ |
|||
$this->PEAR(); |
|||
$this->config = &$config; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ getCache()
|
|||
|
|||
|
|||
function getCache($args) |
|||
{ |
|||
$id = md5(serialize($args)); |
|||
$cachedir = $this->config->get('cache_dir'); |
|||
$filename = $cachedir . DIRECTORY_SEPARATOR . 'xmlrpc_cache_' . $id; |
|||
if (!file_exists($filename)) { |
|||
return null; |
|||
}; |
|||
|
|||
$fp = fopen($filename, 'rb'); |
|||
if (!$fp) { |
|||
return null; |
|||
} |
|||
$content = fread($fp, filesize($filename)); |
|||
fclose($fp); |
|||
$result = array( |
|||
'age' => time() - filemtime($filename), |
|||
'lastChange' => filemtime($filename), |
|||
'content' => unserialize($content), |
|||
); |
|||
return $result; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ saveCache()
|
|||
|
|||
function saveCache($args, $data) |
|||
{ |
|||
$id = md5(serialize($args)); |
|||
$cachedir = $this->config->get('cache_dir'); |
|||
if (!file_exists($cachedir)) { |
|||
System::mkdir(array('-p', $cachedir)); |
|||
} |
|||
$filename = $cachedir.'/xmlrpc_cache_'.$id; |
|||
|
|||
$fp = @fopen($filename, "wb"); |
|||
if ($fp) { |
|||
fwrite($fp, serialize($data)); |
|||
fclose($fp); |
|||
}; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ call(method, [args...])
|
|||
|
|||
function call($method) |
|||
{ |
|||
$_args = $args = func_get_args(); |
|||
|
|||
$this->cache = $this->getCache($args); |
|||
$cachettl = $this->config->get('cache_ttl'); |
|||
// If cache is newer than $cachettl seconds, we use the cache!
|
|||
if ($this->cache !== null && $this->cache['age'] < $cachettl) { |
|||
return $this->cache['content']; |
|||
}; |
|||
|
|||
if (extension_loaded("xmlrpc")) { |
|||
$result = call_user_func_array(array(&$this, 'call_epi'), $args); |
|||
if (!PEAR::isError($result)) { |
|||
$this->saveCache($_args, $result); |
|||
}; |
|||
return $result; |
|||
} |
|||
if (!@include_once("XML/RPC.php")) { |
|||
return $this->raiseError("For this remote PEAR operation you need to install the XML_RPC package"); |
|||
} |
|||
array_shift($args); |
|||
$server_host = $this->config->get('master_server'); |
|||
$username = $this->config->get('username'); |
|||
$password = $this->config->get('password'); |
|||
$eargs = array(); |
|||
foreach($args as $arg) $eargs[] = $this->_encode($arg); |
|||
$f = new XML_RPC_Message($method, $eargs); |
|||
if ($this->cache !== null) { |
|||
$maxAge = '?maxAge='.$this->cache['lastChange']; |
|||
} else { |
|||
$maxAge = ''; |
|||
}; |
|||
$proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; |
|||
if ($proxy = parse_url($this->config->get('http_proxy'))) { |
|||
$proxy_host = @$proxy['host']; |
|||
$proxy_port = @$proxy['port']; |
|||
$proxy_user = @urldecode(@$proxy['user']); |
|||
$proxy_pass = @urldecode(@$proxy['pass']); |
|||
} |
|||
$c = new XML_RPC_Client('/xmlrpc.php'.$maxAge, $server_host, 80, $proxy_host, $proxy_port, $proxy_user, $proxy_pass); |
|||
if ($username && $password) { |
|||
$c->setCredentials($username, $password); |
|||
} |
|||
if ($this->config->get('verbose') >= 3) { |
|||
$c->setDebug(1); |
|||
} |
|||
$r = $c->send($f); |
|||
if (!$r) { |
|||
return $this->raiseError("XML_RPC send failed"); |
|||
} |
|||
$v = $r->value(); |
|||
if ($e = $r->faultCode()) { |
|||
if ($e == $GLOBALS['XML_RPC_err']['http_error'] && strstr($r->faultString(), '304 Not Modified') !== false) { |
|||
return $this->cache['content']; |
|||
} |
|||
return $this->raiseError($r->faultString(), $e); |
|||
} |
|||
|
|||
$result = XML_RPC_decode($v); |
|||
$this->saveCache($_args, $result); |
|||
return $result; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ call_epi(method, [args...])
|
|||
|
|||
function call_epi($method) |
|||
{ |
|||
do { |
|||
if (extension_loaded("xmlrpc")) { |
|||
break; |
|||
} |
|||
if (OS_WINDOWS) { |
|||
$ext = 'dll'; |
|||
} elseif (PHP_OS == 'HP-UX') { |
|||
$ext = 'sl'; |
|||
} elseif (PHP_OS == 'AIX') { |
|||
$ext = 'a'; |
|||
} else { |
|||
$ext = 'so'; |
|||
} |
|||
$ext = OS_WINDOWS ? 'dll' : 'so'; |
|||
@dl("xmlrpc-epi.$ext"); |
|||
if (extension_loaded("xmlrpc")) { |
|||
break; |
|||
} |
|||
@dl("xmlrpc.$ext"); |
|||
if (extension_loaded("xmlrpc")) { |
|||
break; |
|||
} |
|||
return $this->raiseError("unable to load xmlrpc extension"); |
|||
} while (false); |
|||
$params = func_get_args(); |
|||
array_shift($params); |
|||
$method = str_replace("_", ".", $method); |
|||
$request = xmlrpc_encode_request($method, $params); |
|||
$server_host = $this->config->get("master_server"); |
|||
if (empty($server_host)) { |
|||
return $this->raiseError("PEAR_Remote::call: no master_server configured"); |
|||
} |
|||
$server_port = 80; |
|||
if ($http_proxy = $this->config->get('http_proxy')) { |
|||
$proxy = parse_url($http_proxy); |
|||
$proxy_host = $proxy_port = $proxy_user = $proxy_pass = ''; |
|||
$proxy_host = @$proxy['host']; |
|||
$proxy_port = @$proxy['port']; |
|||
$proxy_user = @urldecode(@$proxy['user']); |
|||
$proxy_pass = @urldecode(@$proxy['pass']); |
|||
$fp = @fsockopen($proxy_host, $proxy_port); |
|||
$use_proxy = true; |
|||
} else { |
|||
$use_proxy = false; |
|||
$fp = @fsockopen($server_host, $server_port); |
|||
} |
|||
if (!$fp && $http_proxy) { |
|||
return $this->raiseError("PEAR_Remote::call: fsockopen(`$proxy_host', $proxy_port) failed"); |
|||
} elseif (!$fp) { |
|||
return $this->raiseError("PEAR_Remote::call: fsockopen(`$server_host', $server_port) failed"); |
|||
} |
|||
$len = strlen($request); |
|||
$req_headers = "Host: $server_host:$server_port\r\n" . |
|||
"Content-type: text/xml\r\n" . |
|||
"Content-length: $len\r\n"; |
|||
$username = $this->config->get('username'); |
|||
$password = $this->config->get('password'); |
|||
if ($username && $password) { |
|||
$req_headers .= "Cookie: PEAR_USER=$username; PEAR_PW=$password\r\n"; |
|||
$tmp = base64_encode("$username:$password"); |
|||
$req_headers .= "Authorization: Basic $tmp\r\n"; |
|||
} |
|||
if ($this->cache !== null) { |
|||
$maxAge = '?maxAge='.$this->cache['lastChange']; |
|||
} else { |
|||
$maxAge = ''; |
|||
}; |
|||
|
|||
if ($use_proxy && $proxy_host != '' && $proxy_user != '') { |
|||
$req_headers .= 'Proxy-Authorization: Basic ' |
|||
.base64_encode($proxy_user.':'.$proxy_pass) |
|||
."\r\n"; |
|||
} |
|||
|
|||
if ($this->config->get('verbose') > 3) { |
|||
print "XMLRPC REQUEST HEADERS:\n"; |
|||
var_dump($req_headers); |
|||
print "XMLRPC REQUEST BODY:\n"; |
|||
var_dump($request); |
|||
} |
|||
|
|||
if ($use_proxy && $proxy_host != '') { |
|||
$post_string = "POST http://".$server_host; |
|||
if ($proxy_port > '') { |
|||
$post_string .= ':'.$server_port; |
|||
} |
|||
} else { |
|||
$post_string = "POST "; |
|||
} |
|||
|
|||
fwrite($fp, ($post_string."/xmlrpc.php$maxAge HTTP/1.0\r\n$req_headers\r\n$request")); |
|||
$response = ''; |
|||
$line1 = fgets($fp, 2048); |
|||
if (!preg_match('!^HTTP/[0-9\.]+ (\d+) (.*)!', $line1, $matches)) { |
|||
return $this->raiseError("PEAR_Remote: invalid HTTP response from XML-RPC server"); |
|||
} |
|||
switch ($matches[1]) { |
|||
case "200": // OK
|
|||
break; |
|||
case "304": // Not Modified
|
|||
return $this->cache['content']; |
|||
case "401": // Unauthorized
|
|||
if ($username && $password) { |
|||
return $this->raiseError("PEAR_Remote: authorization failed", 401); |
|||
} else { |
|||
return $this->raiseError("PEAR_Remote: authorization required, please log in first", 401); |
|||
} |
|||
default: |
|||
return $this->raiseError("PEAR_Remote: unexpected HTTP response", (int)$matches[1], null, null, "$matches[1] $matches[2]"); |
|||
} |
|||
while (trim(fgets($fp, 2048)) != ''); // skip rest of headers
|
|||
while ($chunk = fread($fp, 10240)) { |
|||
$response .= $chunk; |
|||
} |
|||
fclose($fp); |
|||
if ($this->config->get('verbose') > 3) { |
|||
print "XMLRPC RESPONSE:\n"; |
|||
var_dump($response); |
|||
} |
|||
$ret = xmlrpc_decode($response); |
|||
if (is_array($ret) && isset($ret['__PEAR_TYPE__'])) { |
|||
if ($ret['__PEAR_TYPE__'] == 'error') { |
|||
if (isset($ret['__PEAR_CLASS__'])) { |
|||
$class = $ret['__PEAR_CLASS__']; |
|||
} else { |
|||
$class = "PEAR_Error"; |
|||
} |
|||
if ($ret['code'] === '') $ret['code'] = null; |
|||
if ($ret['message'] === '') $ret['message'] = null; |
|||
if ($ret['userinfo'] === '') $ret['userinfo'] = null; |
|||
if (strtolower($class) == 'db_error') { |
|||
$ret = $this->raiseError(PEAR::errorMessage($ret['code']), |
|||
$ret['code'], null, null, |
|||
$ret['userinfo']); |
|||
} else { |
|||
$ret = $this->raiseError($ret['message'], $ret['code'], |
|||
null, null, $ret['userinfo']); |
|||
} |
|||
} |
|||
} elseif (is_array($ret) && sizeof($ret) == 1 && isset($ret[0]) |
|||
&& is_array($ret[0]) && |
|||
!empty($ret[0]['faultString']) && |
|||
!empty($ret[0]['faultCode'])) { |
|||
extract($ret[0]); |
|||
$faultString = "XML-RPC Server Fault: " . |
|||
str_replace("\n", " ", $faultString); |
|||
return $this->raiseError($faultString, $faultCode); |
|||
} |
|||
return $ret; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
// {{{ _encode
|
|||
|
|||
// a slightly extended version of XML_RPC_encode
|
|||
function _encode($php_val) |
|||
{ |
|||
global $XML_RPC_Boolean, $XML_RPC_Int, $XML_RPC_Double; |
|||
global $XML_RPC_String, $XML_RPC_Array, $XML_RPC_Struct; |
|||
|
|||
$type = gettype($php_val); |
|||
$xmlrpcval = new XML_RPC_Value; |
|||
|
|||
switch($type) { |
|||
case "array": |
|||
reset($php_val); |
|||
$firstkey = key($php_val); |
|||
end($php_val); |
|||
$lastkey = key($php_val); |
|||
if ($firstkey === 0 && is_int($lastkey) && |
|||
($lastkey + 1) == count($php_val)) { |
|||
$is_continuous = true; |
|||
reset($php_val); |
|||
$size = count($php_val); |
|||
for ($expect = 0; $expect < $size; $expect++, next($php_val)) { |
|||
if (key($php_val) !== $expect) { |
|||
$is_continuous = false; |
|||
break; |
|||
} |
|||
} |
|||
if ($is_continuous) { |
|||
reset($php_val); |
|||
$arr = array(); |
|||
while (list($k, $v) = each($php_val)) { |
|||
$arr[$k] = $this->_encode($v); |
|||
} |
|||
$xmlrpcval->addArray($arr); |
|||
break; |
|||
} |
|||
} |
|||
// fall though if not numerical and continuous
|
|||
case "object": |
|||
$arr = array(); |
|||
while (list($k, $v) = each($php_val)) { |
|||
$arr[$k] = $this->_encode($v); |
|||
} |
|||
$xmlrpcval->addStruct($arr); |
|||
break; |
|||
case "integer": |
|||
$xmlrpcval->addScalar($php_val, $XML_RPC_Int); |
|||
break; |
|||
case "double": |
|||
$xmlrpcval->addScalar($php_val, $XML_RPC_Double); |
|||
break; |
|||
case "string": |
|||
case "NULL": |
|||
$xmlrpcval->addScalar($php_val, $XML_RPC_String); |
|||
break; |
|||
case "boolean": |
|||
$xmlrpcval->addScalar($php_val, $XML_RPC_Boolean); |
|||
break; |
|||
case "unknown type": |
|||
default: |
|||
return null; |
|||
} |
|||
return $xmlrpcval; |
|||
} |
|||
|
|||
// }}}
|
|||
|
|||
} |
|||
|
|||
?>
|
|||
@ -1,363 +0,0 @@ |
|||
<?php |
|||
//
|
|||
// +----------------------------------------------------------------------+
|
|||
// | PHP Version 5 |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Copyright (c) 1997-2004 The PHP Group |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | This source file is subject to version 3.0 of the PHP license, |
|
|||
// | that is bundled with this package in the file LICENSE, and is |
|
|||
// | available through the world-wide-web at the following url: |
|
|||
// | http://www.php.net/license/3_0.txt. |
|
|||
// | If you did not receive a copy of the PHP license and are unable to |
|
|||
// | obtain it through the world-wide-web, please send a note to |
|
|||
// | license@php.net so we can mail you a copy immediately. |
|
|||
// +----------------------------------------------------------------------+
|
|||
// | Authors: Tomas V.V.Cox <cox@idecnet.com> |
|
|||
// | Greg Beaver <cellog@php.net> |
|
|||
// | |
|
|||
// +----------------------------------------------------------------------+
|
|||
//
|
|||
// $Id$
|
|||
//
|
|||
|
|||
/** |
|||
* Simplified version of PHP's test suite |
|||
* -- EXPERIMENTAL -- |
|||
|
|||
Try it with: |
|||
|
|||
$ php -r 'include "../PEAR/RunTest.php"; $t=new PEAR_RunTest; $o=$t->run("./pear_system.phpt");print_r($o);' |
|||
|
|||
|
|||
TODO: |
|||
|
|||
Actually finish the development and testing |
|||
|
|||
*/ |
|||
|
|||
require_once 'PEAR.php'; |
|||
require_once 'PEAR/Config.php'; |
|||
|
|||
define('DETAILED', 1); |
|||
putenv("PHP_PEAR_RUNTESTS=1"); |
|||
|
|||
class PEAR_RunTest |
|||
{ |
|||
var $_logger; |
|||
|
|||
/** |
|||
* An object that supports the PEAR_Common->log() signature, or null |
|||
* @param PEAR_Common|null |
|||
*/ |
|||
function PEAR_RunTest($logger = null) |
|||
{ |
|||
$this->_logger = $logger; |
|||
} |
|||
|
|||
//
|
|||
// Run an individual test case.
|
|||
//
|
|||
|
|||
function run($file, $ini_settings = '') |
|||
{ |
|||
$cwd = getcwd(); |
|||
$conf = &PEAR_Config::singleton(); |
|||
$php = $conf->get('php_bin'); |
|||
//var_dump($php);exit;
|
|||
global $log_format, $info_params, $ini_overwrites; |
|||
|
|||
$info_params = ''; |
|||
$log_format = 'LEOD'; |
|||
|
|||
// Load the sections of the test file.
|
|||
$section_text = array( |
|||
'TEST' => '(unnamed test)', |
|||
'SKIPIF' => '', |
|||
'GET' => '', |
|||
'ARGS' => '', |
|||
); |
|||
|
|||
if (!is_file($file) || !$fp = fopen($file, "r")) { |
|||
return PEAR::raiseError("Cannot open test file: $file"); |
|||
} |
|||
|
|||
$section = ''; |
|||
while (!feof($fp)) { |
|||
$line = fgets($fp); |
|||
|
|||
// Match the beginning of a section.
|
|||
if (ereg('^--([A-Z]+)--',$line,$r)) { |
|||
$section = $r[1]; |
|||
$section_text[$section] = ''; |
|||
continue; |
|||
} |
|||
|
|||
// Add to the section text.
|
|||
$section_text[$section] .= $line; |
|||
} |
|||
fclose($fp); |
|||
|
|||
$shortname = str_replace($cwd.'/', '', $file); |
|||
$tested = trim($section_text['TEST'])." [$shortname]"; |
|||
|
|||
$tmp = realpath(dirname($file)); |
|||
$tmp_skipif = $tmp . uniqid('/phpt.'); |
|||
$tmp_file = ereg_replace('\.phpt$','.php',$file); |
|||
$tmp_post = $tmp . uniqid('/phpt.'); |
|||
|
|||
@unlink($tmp_skipif); |
|||
@unlink($tmp_file); |
|||
@unlink($tmp_post); |
|||
|
|||
// unlink old test results
|
|||
@unlink(ereg_replace('\.phpt$','.diff',$file)); |
|||
@unlink(ereg_replace('\.phpt$','.log',$file)); |
|||
@unlink(ereg_replace('\.phpt$','.exp',$file)); |
|||
@unlink(ereg_replace('\.phpt$','.out',$file)); |
|||
|
|||
// Check if test should be skipped.
|
|||
$info = ''; |
|||
$warn = false; |
|||
if (array_key_exists('SKIPIF', $section_text)) { |
|||
if (trim($section_text['SKIPIF'])) { |
|||
$this->save_text($tmp_skipif, $section_text['SKIPIF']); |
|||
//$extra = substr(PHP_OS, 0, 3) !== "WIN" ?
|
|||
// "unset REQUEST_METHOD;": "";
|
|||
|
|||
//$output = `$extra $php $info_params -f $tmp_skipif`;
|
|||
$output = `$php $info_params -f $tmp_skipif`; |
|||
unlink($tmp_skipif); |
|||
if (eregi("^skip", trim($output))) { |
|||
$skipreason = "SKIP $tested"; |
|||
$reason = (eregi("^skip[[:space:]]*(.+)\$", trim($output))) ? eregi_replace("^skip[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE; |
|||
if ($reason) { |
|||
$skipreason .= " (reason: $reason)"; |
|||
} |
|||
$this->_logger->log(0, $skipreason); |
|||
if (isset($old_php)) { |
|||
$php = $old_php; |
|||
} |
|||
return 'SKIPPED'; |
|||
} |
|||
if (eregi("^info", trim($output))) { |
|||
$reason = (ereg("^info[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^info[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE; |
|||
if ($reason) { |
|||
$info = " (info: $reason)"; |
|||
} |
|||
} |
|||
if (eregi("^warn", trim($output))) { |
|||
$reason = (ereg("^warn[[:space:]]*(.+)\$", trim($output))) ? ereg_replace("^warn[[:space:]]*(.+)\$", "\\1", trim($output)) : FALSE; |
|||
if ($reason) { |
|||
$warn = true; /* only if there is a reason */ |
|||
$info = " (warn: $reason)"; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// We've satisfied the preconditions - run the test!
|
|||
$this->save_text($tmp_file,$section_text['FILE']); |
|||
|
|||
$args = $section_text['ARGS'] ? ' -- '.$section_text['ARGS'] : ''; |
|||
|
|||
$cmd = "$php$ini_settings -f $tmp_file$args 2>&1"; |
|||
if (isset($this->_logger)) { |
|||
$this->_logger->log(2, 'Running command "' . $cmd . '"'); |
|||
} |
|||
|
|||
$savedir = getcwd(); // in case the test moves us around
|
|||
if (isset($section_text['RETURNS'])) { |
|||
ob_start(); |
|||
system($cmd, $return_value); |
|||
$out = ob_get_contents(); |
|||
ob_end_clean(); |
|||
@unlink($tmp_post); |
|||
$section_text['RETURNS'] = (int) trim($section_text['RETURNS']); |
|||
$returnfail = ($return_value != $section_text['RETURNS']); |
|||
} else { |
|||
$out = `$cmd`; |
|||
$returnfail = false; |
|||
} |
|||
chdir($savedir); |
|||
// Does the output match what is expected?
|
|||
$output = trim($out); |
|||
$output = preg_replace('/\r\n/', "\n", $output); |
|||
|
|||
if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { |
|||
if (isset($section_text['EXPECTF'])) { |
|||
$wanted = trim($section_text['EXPECTF']); |
|||
} else { |
|||
$wanted = trim($section_text['EXPECTREGEX']); |
|||
} |
|||
$wanted_re = preg_replace('/\r\n/',"\n",$wanted); |
|||
if (isset($section_text['EXPECTF'])) { |
|||
$wanted_re = preg_quote($wanted_re, '/'); |
|||
// Stick to basics
|
|||
$wanted_re = str_replace("%s", ".+?", $wanted_re); //not greedy
|
|||
$wanted_re = str_replace("%i", "[+\-]?[0-9]+", $wanted_re); |
|||
$wanted_re = str_replace("%d", "[0-9]+", $wanted_re); |
|||
$wanted_re = str_replace("%x", "[0-9a-fA-F]+", $wanted_re); |
|||
$wanted_re = str_replace("%f", "[+\-]?\.?[0-9]+\.?[0-9]*(E-?[0-9]+)?", $wanted_re); |
|||
$wanted_re = str_replace("%c", ".", $wanted_re); |
|||
// %f allows two points "-.0.0" but that is the best *simple* expression
|
|||
} |
|||
/* DEBUG YOUR REGEX HERE |
|||
var_dump($wanted_re); |
|||
print(str_repeat('=', 80) . "\n"); |
|||
var_dump($output); |
|||
*/ |
|||
if (!$returnfail && preg_match("/^$wanted_re\$/s", $output)) { |
|||
@unlink($tmp_file); |
|||
$this->_logger->log(0, "PASS $tested$info"); |
|||
if (isset($old_php)) { |
|||
$php = $old_php; |
|||
} |
|||
return 'PASSED'; |
|||
} |
|||
|
|||
} else { |
|||
$wanted = trim($section_text['EXPECT']); |
|||
$wanted = preg_replace('/\r\n/',"\n",$wanted); |
|||
// compare and leave on success
|
|||
$ok = (0 == strcmp($output,$wanted)); |
|||
if (!$returnfail && $ok) { |
|||
@unlink($tmp_file); |
|||
$this->_logger->log(0, "PASS $tested$info"); |
|||
if (isset($old_php)) { |
|||
$php = $old_php; |
|||
} |
|||
return 'PASSED'; |
|||
} |
|||
} |
|||
|
|||
// Test failed so we need to report details.
|
|||
if ($warn) { |
|||
$this->_logger->log(0, "WARN $tested$info"); |
|||
} else { |
|||
$this->_logger->log(0, "FAIL $tested$info"); |
|||
} |
|||
|
|||
if (isset($section_text['RETURNS'])) { |
|||
$GLOBALS['__PHP_FAILED_TESTS__'][] = array( |
|||
'name' => $file, |
|||
'test_name' => $tested, |
|||
'output' => ereg_replace('\.phpt$','.log', $file), |
|||
'diff' => ereg_replace('\.phpt$','.diff', $file), |
|||
'info' => $info, |
|||
'return' => $return_value |
|||
); |
|||
} else { |
|||
$GLOBALS['__PHP_FAILED_TESTS__'][] = array( |
|||
'name' => $file, |
|||
'test_name' => $tested, |
|||
'output' => ereg_replace('\.phpt$','.log', $file), |
|||
'diff' => ereg_replace('\.phpt$','.diff', $file), |
|||
'info' => $info, |
|||
); |
|||
} |
|||
|
|||
// write .exp
|
|||
if (strpos($log_format,'E') !== FALSE) { |
|||
$logname = ereg_replace('\.phpt$','.exp',$file); |
|||
if (!$log = fopen($logname,'w')) { |
|||
return PEAR::raiseError("Cannot create test log - $logname"); |
|||
} |
|||
fwrite($log,$wanted); |
|||
fclose($log); |
|||
} |
|||
|
|||
// write .out
|
|||
if (strpos($log_format,'O') !== FALSE) { |
|||
$logname = ereg_replace('\.phpt$','.out',$file); |
|||
if (!$log = fopen($logname,'w')) { |
|||
return PEAR::raiseError("Cannot create test log - $logname"); |
|||
} |
|||
fwrite($log,$output); |
|||
fclose($log); |
|||
} |
|||
|
|||
// write .diff
|
|||
if (strpos($log_format,'D') !== FALSE) { |
|||
$logname = ereg_replace('\.phpt$','.diff',$file); |
|||
if (!$log = fopen($logname,'w')) { |
|||
return PEAR::raiseError("Cannot create test log - $logname"); |
|||
} |
|||
fwrite($log, $this->generate_diff($wanted, $output, |
|||
isset($section_text['RETURNS']) ? array(trim($section_text['RETURNS']), |
|||
$return_value) : null)); |
|||
fclose($log); |
|||
} |
|||
|
|||
// write .log
|
|||
if (strpos($log_format,'L') !== FALSE) { |
|||
$logname = ereg_replace('\.phpt$','.log',$file); |
|||
if (!$log = fopen($logname,'w')) { |
|||
return PEAR::raiseError("Cannot create test log - $logname"); |
|||
} |
|||
fwrite($log,"
|
|||
---- EXPECTED OUTPUT |
|||
$wanted |
|||
---- ACTUAL OUTPUT |
|||
$output |
|||
---- FAILED |
|||
");
|
|||
if ($returnfail) { |
|||
fwrite($log,"
|
|||
---- EXPECTED RETURN |
|||
$section_text[RETURNS] |
|||
---- ACTUAL RETURN |
|||
$return_value |
|||
");
|
|||
} |
|||
fclose($log); |
|||
//error_report($file,$logname,$tested);
|
|||
} |
|||
|
|||
if (isset($old_php)) { |
|||
$php = $old_php; |
|||
} |
|||
|
|||
return $warn ? 'WARNED' : 'FAILED'; |
|||
} |
|||
|
|||
function generate_diff($wanted, $output, $return_value) |
|||
{ |
|||
$w = explode("\n", $wanted); |
|||
$o = explode("\n", $output); |
|||
$w1 = array_diff_assoc($w,$o); |
|||
$o1 = array_diff_assoc($o,$w); |
|||
$w2 = array(); |
|||
$o2 = array(); |
|||
foreach($w1 as $idx => $val) $w2[sprintf("%03d<",$idx)] = sprintf("%03d- ", $idx+1).$val; |
|||
foreach($o1 as $idx => $val) $o2[sprintf("%03d>",$idx)] = sprintf("%03d+ ", $idx+1).$val; |
|||
$diff = array_merge($w2, $o2); |
|||
ksort($diff); |
|||
if ($return_value) { |
|||
$extra = "##EXPECTED: $return_value[0]\r\n##RETURNED: $return_value[1]"; |
|||
} else { |
|||
$extra = ''; |
|||
} |
|||
return implode("\r\n", $diff) . $extra; |
|||
} |
|||
|
|||
//
|
|||
// Write the given text to a temporary file, and return the filename.
|
|||
//
|
|||
|
|||
function save_text($filename, $text) |
|||
{ |
|||
if (!$fp = fopen($filename, 'w')) { |
|||
return PEAR::raiseError("Cannot open file '" . $filename . "' (save_text)"); |
|||
} |
|||
fwrite($fp,$text); |
|||
fclose($fp); |
|||
if (1 < DETAILED) echo "
|
|||
FILE $filename {{{ |
|||
$text |
|||
}}} |
|||
";
|
|||
} |
|||
|
|||
} |
|||
?>
|
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue