1<?php 2// 3// +----------------------------------------------------------------------+ 4// | PHP Version 5 | 5// +----------------------------------------------------------------------+ 6// | Copyright (c) 1997-2004 The PHP Group | 7// +----------------------------------------------------------------------+ 8// | This source file is subject to version 3.0 of the PHP license, | 9// | that is bundled with this package in the file LICENSE, and is | 10// | available through the world-wide-web at the following url: | 11// | http://www.php.net/license/3_0.txt. | 12// | If you did not receive a copy of the PHP license and are unable to | 13// | obtain it through the world-wide-web, please send a note to | 14// | license@php.net so we can mail you a copy immediately. | 15// +----------------------------------------------------------------------+ 16// | Authors: Tomas V.V.Cox <cox@idecnet.com> | 17// | Stig Bakken <ssb@php.net> | 18// +----------------------------------------------------------------------+ 19// 20// THIS FILE IS DEPRECATED IN FAVOR OF DEPENDENCY2.PHP, AND IS NOT USED IN THE INSTALLER 21// $Id: Dependency.php,v 1.41 2006/01/06 04:47:36 cellog Exp $ 22 23require_once "PEAR.php"; 24require_once "OS/Guess.php"; 25 26define('PEAR_DEPENDENCY_MISSING', -1); 27define('PEAR_DEPENDENCY_CONFLICT', -2); 28define('PEAR_DEPENDENCY_UPGRADE_MINOR', -3); 29define('PEAR_DEPENDENCY_UPGRADE_MAJOR', -4); 30define('PEAR_DEPENDENCY_BAD_DEPENDENCY', -5); 31define('PEAR_DEPENDENCY_MISSING_OPTIONAL', -6); 32define('PEAR_DEPENDENCY_CONFLICT_OPTIONAL', -7); 33define('PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL', -8); 34define('PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL', -9); 35 36/** 37 * Dependency check for PEAR packages 38 * 39 * The class is based on the dependency RFC that can be found at 40 * http://cvs.php.net/cvs.php/pearweb/rfc. It requires PHP >= 4.1 41 * 42 * @author Tomas V.V.Vox <cox@idecnet.com> 43 * @author Stig Bakken <ssb@php.net> 44 */ 45class PEAR_Dependency 46{ 47 // {{{ constructor 48 /** 49 * Constructor 50 * 51 * @access public 52 * @param object Registry object 53 * @return void 54 */ 55 function PEAR_Dependency(&$registry) 56 { 57 $this->registry = &$registry; 58 } 59 60 // }}} 61 // {{{ callCheckMethod() 62 63 /** 64 * This method maps the XML dependency definition to the 65 * corresponding one from PEAR_Dependency 66 * 67 * <pre> 68 * $opts => Array 69 * ( 70 * [type] => pkg 71 * [rel] => ge 72 * [version] => 3.4 73 * [name] => HTML_Common 74 * [optional] => false 75 * ) 76 * </pre> 77 * 78 * @param string Error message 79 * @param array Options 80 * @return boolean 81 */ 82 function callCheckMethod(&$errmsg, $opts) 83 { 84 $rel = isset($opts['rel']) ? $opts['rel'] : 'has'; 85 $req = isset($opts['version']) ? $opts['version'] : null; 86 $name = isset($opts['name']) ? $opts['name'] : null; 87 $channel = isset($opts['channel']) ? $opts['channel'] : 'pear.php.net'; 88 $opt = (isset($opts['optional']) && $opts['optional'] == 'yes') ? 89 $opts['optional'] : null; 90 $errmsg = ''; 91 switch ($opts['type']) { 92 case 'pkg': 93 return $this->checkPackage($errmsg, $name, $req, $rel, $opt, $channel); 94 break; 95 case 'ext': 96 return $this->checkExtension($errmsg, $name, $req, $rel, $opt); 97 break; 98 case 'php': 99 return $this->checkPHP($errmsg, $req, $rel); 100 break; 101 case 'prog': 102 return $this->checkProgram($errmsg, $name); 103 break; 104 case 'os': 105 return $this->checkOS($errmsg, $name); 106 break; 107 case 'sapi': 108 return $this->checkSAPI($errmsg, $name); 109 break; 110 case 'zend': 111 return $this->checkZend($errmsg, $name); 112 break; 113 default: 114 return "'{$opts['type']}' dependency type not supported"; 115 } 116 } 117 118 // }}} 119 // {{{ checkPackage() 120 121 /** 122 * Package dependencies check method 123 * 124 * @param string $errmsg Empty string, it will be populated with an error message, if any 125 * @param string $name Name of the package to test 126 * @param string $req The package version required 127 * @param string $relation How to compare versions with each other 128 * @param bool $opt Whether the relationship is optional 129 * @param string $channel Channel name 130 * 131 * @return mixed bool false if no error or the error string 132 */ 133 function checkPackage(&$errmsg, $name, $req = null, $relation = 'has', 134 $opt = false, $channel = 'pear.php.net') 135 { 136 if (is_string($req) && substr($req, 0, 2) == 'v.') { 137 $req = substr($req, 2); 138 } 139 switch ($relation) { 140 case 'has': 141 if (!$this->registry->packageExists($name, $channel)) { 142 if ($opt) { 143 $errmsg = "package `$channel/$name' is recommended to utilize some features."; 144 return PEAR_DEPENDENCY_MISSING_OPTIONAL; 145 } 146 $errmsg = "requires package `$channel/$name'"; 147 return PEAR_DEPENDENCY_MISSING; 148 } 149 return false; 150 case 'not': 151 if ($this->registry->packageExists($name, $channel)) { 152 $errmsg = "conflicts with package `$channel/$name'"; 153 return PEAR_DEPENDENCY_CONFLICT; 154 } 155 return false; 156 case 'lt': 157 case 'le': 158 case 'eq': 159 case 'ne': 160 case 'ge': 161 case 'gt': 162 $version = $this->registry->packageInfo($name, 'version', $channel); 163 if (!$this->registry->packageExists($name, $channel) 164 || !version_compare("$version", "$req", $relation)) 165 { 166 $code = $this->codeFromRelation($relation, $version, $req, $opt); 167 if ($opt) { 168 $errmsg = "package `$channel/$name' version " . $this->signOperator($relation) . 169 " $req is recommended to utilize some features."; 170 if ($version) { 171 $errmsg .= " Installed version is $version"; 172 } 173 return $code; 174 } 175 $errmsg = "requires package `$channel/$name' " . 176 $this->signOperator($relation) . " $req"; 177 return $code; 178 } 179 return false; 180 } 181 $errmsg = "relation '$relation' with requirement '$req' is not supported (name=$channel/$name)"; 182 return PEAR_DEPENDENCY_BAD_DEPENDENCY; 183 } 184 185 // }}} 186 // {{{ checkPackageUninstall() 187 188 /** 189 * Check package dependencies on uninstall 190 * 191 * @param string $error The resultant error string 192 * @param string $warning The resultant warning string 193 * @param string $name Name of the package to test 194 * @param string $channel Channel name of the package 195 * 196 * @return bool true if there were errors 197 */ 198 function checkPackageUninstall(&$error, &$warning, $package, $channel = 'pear.php.net') 199 { 200 $channel = strtolower($channel); 201 $error = null; 202 $channels = $this->registry->listAllPackages(); 203 foreach ($channels as $channelname => $packages) { 204 foreach ($packages as $pkg) { 205 if ($pkg == $package && $channel == $channelname) { 206 continue; 207 } 208 $deps = $this->registry->packageInfo($pkg, 'release_deps', $channel); 209 if (empty($deps)) { 210 continue; 211 } 212 foreach ($deps as $dep) { 213 $depchannel = isset($dep['channel']) ? $dep['channel'] : 'pear.php.net'; 214 if ($dep['type'] == 'pkg' && (strcasecmp($dep['name'], $package) == 0) && 215 ($depchannel == $channel)) { 216 if ($dep['rel'] == 'ne') { 217 continue; 218 } 219 if (isset($dep['optional']) && $dep['optional'] == 'yes') { 220 $warning .= "\nWarning: Package '$depchannel/$pkg' optionally depends on '$channel:/package'"; 221 } else { 222 $error .= "Package '$depchannel/$pkg' depends on '$channel/$package'\n"; 223 } 224 } 225 } 226 } 227 } 228 return ($error) ? true : false; 229 } 230 231 // }}} 232 // {{{ checkExtension() 233 234 /** 235 * Extension dependencies check method 236 * 237 * @param string $name Name of the extension to test 238 * @param string $req_ext_ver Required extension version to compare with 239 * @param string $relation How to compare versions with eachother 240 * @param bool $opt Whether the relationship is optional 241 * 242 * @return mixed bool false if no error or the error string 243 */ 244 function checkExtension(&$errmsg, $name, $req = null, $relation = 'has', 245 $opt = false) 246 { 247 if ($relation == 'not') { 248 if (extension_loaded($name)) { 249 $errmsg = "conflicts with PHP extension '$name'"; 250 return PEAR_DEPENDENCY_CONFLICT; 251 } else { 252 return false; 253 } 254 } 255 256 if (!extension_loaded($name)) { 257 if ($relation == 'ne') { 258 return false; 259 } 260 if ($opt) { 261 $errmsg = "'$name' PHP extension is recommended to utilize some features"; 262 return PEAR_DEPENDENCY_MISSING_OPTIONAL; 263 } 264 $errmsg = "'$name' PHP extension is not installed"; 265 return PEAR_DEPENDENCY_MISSING; 266 } 267 if ($relation == 'has') { 268 return false; 269 } 270 $code = false; 271 if (is_string($req) && substr($req, 0, 2) == 'v.') { 272 $req = substr($req, 2); 273 } 274 $ext_ver = phpversion($name); 275 $operator = $relation; 276 // Force params to be strings, otherwise the comparation will fail (ex. 0.9==0.90) 277 if (!version_compare("$ext_ver", "$req", $operator)) { 278 $errmsg = "'$name' PHP extension version " . 279 $this->signOperator($operator) . " $req is required"; 280 $code = $this->codeFromRelation($relation, $ext_ver, $req, $opt); 281 if ($opt) { 282 $errmsg = "'$name' PHP extension version " . $this->signOperator($operator) . 283 " $req is recommended to utilize some features"; 284 return $code; 285 } 286 } 287 return $code; 288 } 289 290 // }}} 291 // {{{ checkOS() 292 293 /** 294 * Operating system dependencies check method 295 * 296 * @param string $os Name of the operating system 297 * 298 * @return mixed bool false if no error or the error string 299 */ 300 function checkOS(&$errmsg, $os) 301 { 302 // XXX Fixme: Implement a more flexible way, like 303 // comma separated values or something similar to PEAR_OS 304 static $myos; 305 if (empty($myos)) { 306 $myos = new OS_Guess(); 307 } 308 // only 'has' relation is currently supported 309 if ($myos->matchSignature($os)) { 310 return false; 311 } 312 $errmsg = "'$os' operating system not supported"; 313 return PEAR_DEPENDENCY_CONFLICT; 314 } 315 316 // }}} 317 // {{{ checkPHP() 318 319 /** 320 * PHP version check method 321 * 322 * @param string $req which version to compare 323 * @param string $relation how to compare the version 324 * 325 * @return mixed bool false if no error or the error string 326 */ 327 function checkPHP(&$errmsg, $req, $relation = 'ge') 328 { 329 // this would be a bit stupid, but oh well :) 330 if ($relation == 'has') { 331 return false; 332 } 333 if ($relation == 'not') { 334 $errmsg = "Invalid dependency - 'not' is allowed when specifying PHP, you must run PHP in PHP"; 335 return PEAR_DEPENDENCY_BAD_DEPENDENCY; 336 } 337 if (substr($req, 0, 2) == 'v.') { 338 $req = substr($req,2, strlen($req) - 2); 339 } 340 $php_ver = phpversion(); 341 $operator = $relation; 342 if (!version_compare("$php_ver", "$req", $operator)) { 343 $errmsg = "PHP version " . $this->signOperator($operator) . 344 " $req is required"; 345 return PEAR_DEPENDENCY_CONFLICT; 346 } 347 return false; 348 } 349 350 // }}} 351 // {{{ checkProgram() 352 353 /** 354 * External program check method. Looks for executable files in 355 * directories listed in the PATH environment variable. 356 * 357 * @param string $program which program to look for 358 * 359 * @return mixed bool false if no error or the error string 360 */ 361 function checkProgram(&$errmsg, $program) 362 { 363 // XXX FIXME honor safe mode 364 $exe_suffix = OS_WINDOWS ? '.exe' : ''; 365 $path_elements = explode(PATH_SEPARATOR, getenv('PATH')); 366 foreach ($path_elements as $dir) { 367 $file = $dir . DIRECTORY_SEPARATOR . $program . $exe_suffix; 368 if (@file_exists($file) && @is_executable($file)) { 369 return false; 370 } 371 } 372 $errmsg = "'$program' program is not present in the PATH"; 373 return PEAR_DEPENDENCY_MISSING; 374 } 375 376 // }}} 377 // {{{ checkSAPI() 378 379 /** 380 * SAPI backend check method. Version comparison is not yet 381 * available here. 382 * 383 * @param string $name name of SAPI backend 384 * @param string $req which version to compare 385 * @param string $relation how to compare versions (currently 386 * hardcoded to 'has') 387 * @return mixed bool false if no error or the error string 388 */ 389 function checkSAPI(&$errmsg, $name, $req = null, $relation = 'has') 390 { 391 // XXX Fixme: There is no way to know if the user has or 392 // not other SAPI backends installed than the installer one 393 394 $sapi_backend = php_sapi_name(); 395 // Version comparisons not supported, sapi backends don't have 396 // version information yet. 397 if ($sapi_backend == $name) { 398 return false; 399 } 400 $errmsg = "'$sapi_backend' SAPI backend not supported"; 401 return PEAR_DEPENDENCY_CONFLICT; 402 } 403 404 // }}} 405 // {{{ checkZend() 406 407 /** 408 * Zend version check method 409 * 410 * @param string $req which version to compare 411 * @param string $relation how to compare the version 412 * 413 * @return mixed bool false if no error or the error string 414 */ 415 function checkZend(&$errmsg, $req, $relation = 'ge') 416 { 417 if (substr($req, 0, 2) == 'v.') { 418 $req = substr($req,2, strlen($req) - 2); 419 } 420 $zend_ver = zend_version(); 421 $operator = substr($relation,0,2); 422 if (!version_compare("$zend_ver", "$req", $operator)) { 423 $errmsg = "Zend version " . $this->signOperator($operator) . 424 " $req is required"; 425 return PEAR_DEPENDENCY_CONFLICT; 426 } 427 return false; 428 } 429 430 // }}} 431 // {{{ signOperator() 432 433 /** 434 * Converts text comparing operators to them sign equivalents 435 * 436 * Example: 'ge' to '>=' 437 * 438 * @access public 439 * @param string Operator 440 * @return string Sign equivalent 441 */ 442 function signOperator($operator) 443 { 444 switch($operator) { 445 case 'lt': return '<'; 446 case 'le': return '<='; 447 case 'gt': return '>'; 448 case 'ge': return '>='; 449 case 'eq': return '=='; 450 case 'ne': return '!='; 451 default: 452 return $operator; 453 } 454 } 455 456 // }}} 457 // {{{ codeFromRelation() 458 459 /** 460 * Convert relation into corresponding code 461 * 462 * @access public 463 * @param string Relation 464 * @param string Version 465 * @param string Requirement 466 * @param bool Optional dependency indicator 467 * @return integer 468 */ 469 function codeFromRelation($relation, $version, $req, $opt = false) 470 { 471 $code = PEAR_DEPENDENCY_BAD_DEPENDENCY; 472 switch ($relation) { 473 case 'gt': case 'ge': case 'eq': 474 // upgrade 475 $have_major = preg_replace('/\D.*/', '', $version); 476 $need_major = preg_replace('/\D.*/', '', $req); 477 if ($need_major > $have_major) { 478 $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MAJOR_OPTIONAL : 479 PEAR_DEPENDENCY_UPGRADE_MAJOR; 480 } else { 481 $code = $opt ? PEAR_DEPENDENCY_UPGRADE_MINOR_OPTIONAL : 482 PEAR_DEPENDENCY_UPGRADE_MINOR; 483 } 484 break; 485 case 'lt': case 'le': case 'ne': 486 $code = $opt ? PEAR_DEPENDENCY_CONFLICT_OPTIONAL : 487 PEAR_DEPENDENCY_CONFLICT; 488 break; 489 } 490 return $code; 491 } 492 493 // }}} 494} 495?> 496