1<?php 2/** 3 * Error Stack Implementation 4 * 5 * This is an incredibly simple implementation of a very complex error handling 6 * facility. It contains the ability 7 * to track multiple errors from multiple packages simultaneously. In addition, 8 * it can track errors of many levels, save data along with the error, context 9 * information such as the exact file, line number, class and function that 10 * generated the error, and if necessary, it can raise a traditional PEAR_Error. 11 * It has built-in support for PEAR::Log, to log errors as they occur 12 * 13 * Since version 0.2alpha, it is also possible to selectively ignore errors, 14 * through the use of an error callback, see {@link pushCallback()} 15 * 16 * Since version 0.3alpha, it is possible to specify the exception class 17 * returned from {@link push()} 18 * 19 * Since version PEAR1.3.2, ErrorStack no longer instantiates an exception class. This can 20 * still be done quite handily in an error callback or by manipulating the returned array 21 * @category Debugging 22 * @package PEAR_ErrorStack 23 * @author Greg Beaver <cellog@php.net> 24 * @copyright 2004-2008 Greg Beaver 25 * @license http://opensource.org/licenses/bsd-license.php New BSD License 26 * @link http://pear.php.net/package/PEAR_ErrorStack 27 */ 28 29/** 30 * Singleton storage 31 * 32 * Format: 33 * <pre> 34 * array( 35 * 'package1' => PEAR_ErrorStack object, 36 * 'package2' => PEAR_ErrorStack object, 37 * ... 38 * ) 39 * </pre> 40 * @access private 41 * @global array $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] 42 */ 43$GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] = array(); 44 45/** 46 * Global error callback (default) 47 * 48 * This is only used if set to non-false. * is the default callback for 49 * all packages, whereas specific packages may set a default callback 50 * for all instances, regardless of whether they are a singleton or not. 51 * 52 * To exclude non-singletons, only set the local callback for the singleton 53 * @see PEAR_ErrorStack::setDefaultCallback() 54 * @access private 55 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] 56 */ 57$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'] = array( 58 '*' => false, 59); 60 61/** 62 * Global Log object (default) 63 * 64 * This is only used if set to non-false. Use to set a default log object for 65 * all stacks, regardless of instantiation order or location 66 * @see PEAR_ErrorStack::setDefaultLogger() 67 * @access private 68 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] 69 */ 70$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = false; 71 72/** 73 * Global Overriding Callback 74 * 75 * This callback will override any error callbacks that specific loggers have set. 76 * Use with EXTREME caution 77 * @see PEAR_ErrorStack::staticPushCallback() 78 * @access private 79 * @global array $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] 80 */ 81$GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); 82 83/**#@+ 84 * One of four possible return values from the error Callback 85 * @see PEAR_ErrorStack::_errorCallback() 86 */ 87/** 88 * If this is returned, then the error will be both pushed onto the stack 89 * and logged. 90 */ 91define('PEAR_ERRORSTACK_PUSHANDLOG', 1); 92/** 93 * If this is returned, then the error will only be pushed onto the stack, 94 * and not logged. 95 */ 96define('PEAR_ERRORSTACK_PUSH', 2); 97/** 98 * If this is returned, then the error will only be logged, but not pushed 99 * onto the error stack. 100 */ 101define('PEAR_ERRORSTACK_LOG', 3); 102/** 103 * If this is returned, then the error is completely ignored. 104 */ 105define('PEAR_ERRORSTACK_IGNORE', 4); 106/** 107 * If this is returned, then the error is logged and die() is called. 108 */ 109define('PEAR_ERRORSTACK_DIE', 5); 110/**#@-*/ 111 112/** 113 * Error code for an attempt to instantiate a non-class as a PEAR_ErrorStack in 114 * the singleton method. 115 */ 116define('PEAR_ERRORSTACK_ERR_NONCLASS', 1); 117 118/** 119 * Error code for an attempt to pass an object into {@link PEAR_ErrorStack::getMessage()} 120 * that has no __toString() method 121 */ 122define('PEAR_ERRORSTACK_ERR_OBJTOSTRING', 2); 123/** 124 * Error Stack Implementation 125 * 126 * Usage: 127 * <code> 128 * // global error stack 129 * $global_stack = &PEAR_ErrorStack::singleton('MyPackage'); 130 * // local error stack 131 * $local_stack = new PEAR_ErrorStack('MyPackage'); 132 * </code> 133 * @author Greg Beaver <cellog@php.net> 134 * @version 1.10.12 135 * @package PEAR_ErrorStack 136 * @category Debugging 137 * @copyright 2004-2008 Greg Beaver 138 * @license http://opensource.org/licenses/bsd-license.php New BSD License 139 * @link http://pear.php.net/package/PEAR_ErrorStack 140 */ 141class PEAR_ErrorStack { 142 /** 143 * Errors are stored in the order that they are pushed on the stack. 144 * @since 0.4alpha Errors are no longer organized by error level. 145 * This renders pop() nearly unusable, and levels could be more easily 146 * handled in a callback anyway 147 * @var array 148 * @access private 149 */ 150 var $_errors = array(); 151 152 /** 153 * Storage of errors by level. 154 * 155 * Allows easy retrieval and deletion of only errors from a particular level 156 * @since PEAR 1.4.0dev 157 * @var array 158 * @access private 159 */ 160 var $_errorsByLevel = array(); 161 162 /** 163 * Package name this error stack represents 164 * @var string 165 * @access protected 166 */ 167 var $_package; 168 169 /** 170 * Determines whether a PEAR_Error is thrown upon every error addition 171 * @var boolean 172 * @access private 173 */ 174 var $_compat = false; 175 176 /** 177 * If set to a valid callback, this will be used to generate the error 178 * message from the error code, otherwise the message passed in will be 179 * used 180 * @var false|string|array 181 * @access private 182 */ 183 var $_msgCallback = false; 184 185 /** 186 * If set to a valid callback, this will be used to generate the error 187 * context for an error. For PHP-related errors, this will be a file 188 * and line number as retrieved from debug_backtrace(), but can be 189 * customized for other purposes. The error might actually be in a separate 190 * configuration file, or in a database query. 191 * @var false|string|array 192 * @access protected 193 */ 194 var $_contextCallback = false; 195 196 /** 197 * If set to a valid callback, this will be called every time an error 198 * is pushed onto the stack. The return value will be used to determine 199 * whether to allow an error to be pushed or logged. 200 * 201 * The return value must be one an PEAR_ERRORSTACK_* constant 202 * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG 203 * @var false|string|array 204 * @access protected 205 */ 206 var $_errorCallback = array(); 207 208 /** 209 * PEAR::Log object for logging errors 210 * @var false|Log 211 * @access protected 212 */ 213 var $_logger = false; 214 215 /** 216 * Error messages - designed to be overridden 217 * @var array 218 * @abstract 219 */ 220 var $_errorMsgs = array(); 221 222 /** 223 * Set up a new error stack 224 * 225 * @param string $package name of the package this error stack represents 226 * @param callback $msgCallback callback used for error message generation 227 * @param callback $contextCallback callback used for context generation, 228 * defaults to {@link getFileLine()} 229 * @param boolean $throwPEAR_Error 230 */ 231 function __construct($package, $msgCallback = false, $contextCallback = false, 232 $throwPEAR_Error = false) 233 { 234 $this->_package = $package; 235 $this->setMessageCallback($msgCallback); 236 $this->setContextCallback($contextCallback); 237 $this->_compat = $throwPEAR_Error; 238 } 239 240 /** 241 * Return a single error stack for this package. 242 * 243 * Note that all parameters are ignored if the stack for package $package 244 * has already been instantiated 245 * @param string $package name of the package this error stack represents 246 * @param callback $msgCallback callback used for error message generation 247 * @param callback $contextCallback callback used for context generation, 248 * defaults to {@link getFileLine()} 249 * @param boolean $throwPEAR_Error 250 * @param string $stackClass class to instantiate 251 * 252 * @return PEAR_ErrorStack 253 */ 254 public static function &singleton( 255 $package, $msgCallback = false, $contextCallback = false, 256 $throwPEAR_Error = false, $stackClass = 'PEAR_ErrorStack' 257 ) { 258 if (isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { 259 return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; 260 } 261 if (!class_exists($stackClass)) { 262 if (function_exists('debug_backtrace')) { 263 $trace = debug_backtrace(); 264 } 265 PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_NONCLASS, 266 'exception', array('stackclass' => $stackClass), 267 'stack class "%stackclass%" is not a valid class name (should be like PEAR_ErrorStack)', 268 false, $trace); 269 } 270 $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package] = 271 new $stackClass($package, $msgCallback, $contextCallback, $throwPEAR_Error); 272 273 return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]; 274 } 275 276 /** 277 * Internal error handler for PEAR_ErrorStack class 278 * 279 * Dies if the error is an exception (and would have died anyway) 280 * @access private 281 */ 282 function _handleError($err) 283 { 284 if ($err['level'] == 'exception') { 285 $message = $err['message']; 286 if (isset($_SERVER['REQUEST_URI'])) { 287 echo '<br />'; 288 } else { 289 echo "\n"; 290 } 291 var_dump($err['context']); 292 die($message); 293 } 294 } 295 296 /** 297 * Set up a PEAR::Log object for all error stacks that don't have one 298 * @param Log $log 299 */ 300 public static function setDefaultLogger(&$log) 301 { 302 if (is_object($log) && method_exists($log, 'log') ) { 303 $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; 304 } elseif (is_callable($log)) { 305 $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'] = &$log; 306 } 307 } 308 309 /** 310 * Set up a PEAR::Log object for this error stack 311 * @param Log $log 312 */ 313 function setLogger(&$log) 314 { 315 if (is_object($log) && method_exists($log, 'log') ) { 316 $this->_logger = &$log; 317 } elseif (is_callable($log)) { 318 $this->_logger = &$log; 319 } 320 } 321 322 /** 323 * Set an error code => error message mapping callback 324 * 325 * This method sets the callback that can be used to generate error 326 * messages for any instance 327 * @param array|string Callback function/method 328 */ 329 function setMessageCallback($msgCallback) 330 { 331 if (!$msgCallback) { 332 $this->_msgCallback = array(&$this, 'getErrorMessage'); 333 } else { 334 if (is_callable($msgCallback)) { 335 $this->_msgCallback = $msgCallback; 336 } 337 } 338 } 339 340 /** 341 * Get an error code => error message mapping callback 342 * 343 * This method returns the current callback that can be used to generate error 344 * messages 345 * @return array|string|false Callback function/method or false if none 346 */ 347 function getMessageCallback() 348 { 349 return $this->_msgCallback; 350 } 351 352 /** 353 * Sets a default callback to be used by all error stacks 354 * 355 * This method sets the callback that can be used to generate error 356 * messages for a singleton 357 * @param array|string Callback function/method 358 * @param string Package name, or false for all packages 359 */ 360 public static function setDefaultCallback($callback = false, $package = false) 361 { 362 if (!is_callable($callback)) { 363 $callback = false; 364 } 365 $package = $package ? $package : '*'; 366 $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$package] = $callback; 367 } 368 369 /** 370 * Set a callback that generates context information (location of error) for an error stack 371 * 372 * This method sets the callback that can be used to generate context 373 * information for an error. Passing in NULL will disable context generation 374 * and remove the expensive call to debug_backtrace() 375 * @param array|string|null Callback function/method 376 */ 377 function setContextCallback($contextCallback) 378 { 379 if ($contextCallback === null) { 380 return $this->_contextCallback = false; 381 } 382 if (!$contextCallback) { 383 $this->_contextCallback = array(&$this, 'getFileLine'); 384 } else { 385 if (is_callable($contextCallback)) { 386 $this->_contextCallback = $contextCallback; 387 } 388 } 389 } 390 391 /** 392 * Set an error Callback 393 * If set to a valid callback, this will be called every time an error 394 * is pushed onto the stack. The return value will be used to determine 395 * whether to allow an error to be pushed or logged. 396 * 397 * The return value must be one of the ERRORSTACK_* constants. 398 * 399 * This functionality can be used to emulate PEAR's pushErrorHandling, and 400 * the PEAR_ERROR_CALLBACK mode, without affecting the integrity of 401 * the error stack or logging 402 * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG 403 * @see popCallback() 404 * @param string|array $cb 405 */ 406 function pushCallback($cb) 407 { 408 array_push($this->_errorCallback, $cb); 409 } 410 411 /** 412 * Remove a callback from the error callback stack 413 * @see pushCallback() 414 * @return array|string|false 415 */ 416 function popCallback() 417 { 418 if (!count($this->_errorCallback)) { 419 return false; 420 } 421 return array_pop($this->_errorCallback); 422 } 423 424 /** 425 * Set a temporary overriding error callback for every package error stack 426 * 427 * Use this to temporarily disable all existing callbacks (can be used 428 * to emulate the @ operator, for instance) 429 * @see PEAR_ERRORSTACK_PUSHANDLOG, PEAR_ERRORSTACK_PUSH, PEAR_ERRORSTACK_LOG 430 * @see staticPopCallback(), pushCallback() 431 * @param string|array $cb 432 */ 433 public static function staticPushCallback($cb) 434 { 435 array_push($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'], $cb); 436 } 437 438 /** 439 * Remove a temporary overriding error callback 440 * @see staticPushCallback() 441 * @return array|string|false 442 */ 443 public static function staticPopCallback() 444 { 445 $ret = array_pop($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK']); 446 if (!is_array($GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'])) { 447 $GLOBALS['_PEAR_ERRORSTACK_OVERRIDE_CALLBACK'] = array(); 448 } 449 return $ret; 450 } 451 452 /** 453 * Add an error to the stack 454 * 455 * If the message generator exists, it is called with 2 parameters. 456 * - the current Error Stack object 457 * - an array that is in the same format as an error. Available indices 458 * are 'code', 'package', 'time', 'params', 'level', and 'context' 459 * 460 * Next, if the error should contain context information, this is 461 * handled by the context grabbing method. 462 * Finally, the error is pushed onto the proper error stack 463 * @param int $code Package-specific error code 464 * @param string $level Error level. This is NOT spell-checked 465 * @param array $params associative array of error parameters 466 * @param string $msg Error message, or a portion of it if the message 467 * is to be generated 468 * @param array $repackage If this error re-packages an error pushed by 469 * another package, place the array returned from 470 * {@link pop()} in this parameter 471 * @param array $backtrace Protected parameter: use this to pass in the 472 * {@link debug_backtrace()} that should be used 473 * to find error context 474 * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also 475 * thrown. If a PEAR_Error is returned, the userinfo 476 * property is set to the following array: 477 * 478 * <code> 479 * array( 480 * 'code' => $code, 481 * 'params' => $params, 482 * 'package' => $this->_package, 483 * 'level' => $level, 484 * 'time' => time(), 485 * 'context' => $context, 486 * 'message' => $msg, 487 * //['repackage' => $err] repackaged error array/Exception class 488 * ); 489 * </code> 490 * 491 * Normally, the previous array is returned. 492 */ 493 function push($code, $level = 'error', $params = array(), $msg = false, 494 $repackage = false, $backtrace = false) 495 { 496 $context = false; 497 // grab error context 498 if ($this->_contextCallback) { 499 if (!$backtrace) { 500 $backtrace = debug_backtrace(); 501 } 502 $context = call_user_func($this->_contextCallback, $code, $params, $backtrace); 503 } 504 505 // save error 506 $time = explode(' ', microtime()); 507 $time = $time[1] + $time[0]; 508 $err = array( 509 'code' => $code, 510 'params' => $params, 511 'package' => $this->_package, 512 'level' => $level, 513 'time' => $time, 514 'context' => $context, 515 'message' => $msg, 516 ); 517 518 if ($repackage) { 519 $err['repackage'] = $repackage; 520 } 521 522 // set up the error message, if necessary 523 if ($this->_msgCallback) { 524 $msg = call_user_func_array($this->_msgCallback, 525 array(&$this, $err)); 526 $err['message'] = $msg; 527 } 528 $push = $log = true; 529 $die = false; 530 // try the overriding callback first 531 $callback = $this->staticPopCallback(); 532 if ($callback) { 533 $this->staticPushCallback($callback); 534 } 535 if (!is_callable($callback)) { 536 // try the local callback next 537 $callback = $this->popCallback(); 538 if (is_callable($callback)) { 539 $this->pushCallback($callback); 540 } else { 541 // try the default callback 542 $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ? 543 $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] : 544 $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*']; 545 } 546 } 547 if (is_callable($callback)) { 548 switch(call_user_func($callback, $err)){ 549 case PEAR_ERRORSTACK_IGNORE: 550 return $err; 551 break; 552 case PEAR_ERRORSTACK_PUSH: 553 $log = false; 554 break; 555 case PEAR_ERRORSTACK_LOG: 556 $push = false; 557 break; 558 case PEAR_ERRORSTACK_DIE: 559 $die = true; 560 break; 561 // anything else returned has the same effect as pushandlog 562 } 563 } 564 if ($push) { 565 array_unshift($this->_errors, $err); 566 if (!isset($this->_errorsByLevel[$err['level']])) { 567 $this->_errorsByLevel[$err['level']] = array(); 568 } 569 $this->_errorsByLevel[$err['level']][] = &$this->_errors[0]; 570 } 571 if ($log) { 572 if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) { 573 $this->_log($err); 574 } 575 } 576 if ($die) { 577 die(); 578 } 579 if ($this->_compat && $push) { 580 return $this->raiseError($msg, $code, null, null, $err); 581 } 582 return $err; 583 } 584 585 /** 586 * Static version of {@link push()} 587 * 588 * @param string $package Package name this error belongs to 589 * @param int $code Package-specific error code 590 * @param string $level Error level. This is NOT spell-checked 591 * @param array $params associative array of error parameters 592 * @param string $msg Error message, or a portion of it if the message 593 * is to be generated 594 * @param array $repackage If this error re-packages an error pushed by 595 * another package, place the array returned from 596 * {@link pop()} in this parameter 597 * @param array $backtrace Protected parameter: use this to pass in the 598 * {@link debug_backtrace()} that should be used 599 * to find error context 600 * @return PEAR_Error|array if compatibility mode is on, a PEAR_Error is also 601 * thrown. see docs for {@link push()} 602 */ 603 public static function staticPush( 604 $package, $code, $level = 'error', $params = array(), 605 $msg = false, $repackage = false, $backtrace = false 606 ) { 607 $s = &PEAR_ErrorStack::singleton($package); 608 if ($s->_contextCallback) { 609 if (!$backtrace) { 610 if (function_exists('debug_backtrace')) { 611 $backtrace = debug_backtrace(); 612 } 613 } 614 } 615 return $s->push($code, $level, $params, $msg, $repackage, $backtrace); 616 } 617 618 /** 619 * Log an error using PEAR::Log 620 * @param array $err Error array 621 * @param array $levels Error level => Log constant map 622 * @access protected 623 */ 624 function _log($err) 625 { 626 if ($this->_logger) { 627 $logger = &$this->_logger; 628 } else { 629 $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']; 630 } 631 if (is_a($logger, 'Log')) { 632 $levels = array( 633 'exception' => PEAR_LOG_CRIT, 634 'alert' => PEAR_LOG_ALERT, 635 'critical' => PEAR_LOG_CRIT, 636 'error' => PEAR_LOG_ERR, 637 'warning' => PEAR_LOG_WARNING, 638 'notice' => PEAR_LOG_NOTICE, 639 'info' => PEAR_LOG_INFO, 640 'debug' => PEAR_LOG_DEBUG); 641 if (isset($levels[$err['level']])) { 642 $level = $levels[$err['level']]; 643 } else { 644 $level = PEAR_LOG_INFO; 645 } 646 $logger->log($err['message'], $level, $err); 647 } else { // support non-standard logs 648 call_user_func($logger, $err); 649 } 650 } 651 652 653 /** 654 * Pop an error off of the error stack 655 * 656 * @return false|array 657 * @since 0.4alpha it is no longer possible to specify a specific error 658 * level to return - the last error pushed will be returned, instead 659 */ 660 function pop() 661 { 662 $err = @array_shift($this->_errors); 663 if (!is_null($err)) { 664 @array_pop($this->_errorsByLevel[$err['level']]); 665 if (!count($this->_errorsByLevel[$err['level']])) { 666 unset($this->_errorsByLevel[$err['level']]); 667 } 668 } 669 return $err; 670 } 671 672 /** 673 * Pop an error off of the error stack, static method 674 * 675 * @param string package name 676 * @return boolean 677 * @since PEAR1.5.0a1 678 */ 679 static function staticPop($package) 680 { 681 if ($package) { 682 if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { 683 return false; 684 } 685 return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->pop(); 686 } 687 } 688 689 /** 690 * Determine whether there are any errors on the stack 691 * @param string|array Level name. Use to determine if any errors 692 * of level (string), or levels (array) have been pushed 693 * @return boolean 694 */ 695 function hasErrors($level = false) 696 { 697 if ($level) { 698 return isset($this->_errorsByLevel[$level]); 699 } 700 return count($this->_errors); 701 } 702 703 /** 704 * Retrieve all errors since last purge 705 * 706 * @param boolean set in order to empty the error stack 707 * @param string level name, to return only errors of a particular severity 708 * @return array 709 */ 710 function getErrors($purge = false, $level = false) 711 { 712 if (!$purge) { 713 if ($level) { 714 if (!isset($this->_errorsByLevel[$level])) { 715 return array(); 716 } else { 717 return $this->_errorsByLevel[$level]; 718 } 719 } else { 720 return $this->_errors; 721 } 722 } 723 if ($level) { 724 $ret = $this->_errorsByLevel[$level]; 725 foreach ($this->_errorsByLevel[$level] as $i => $unused) { 726 // entries are references to the $_errors array 727 $this->_errorsByLevel[$level][$i] = false; 728 } 729 // array_filter removes all entries === false 730 $this->_errors = array_filter($this->_errors); 731 unset($this->_errorsByLevel[$level]); 732 return $ret; 733 } 734 $ret = $this->_errors; 735 $this->_errors = array(); 736 $this->_errorsByLevel = array(); 737 return $ret; 738 } 739 740 /** 741 * Determine whether there are any errors on a single error stack, or on any error stack 742 * 743 * The optional parameter can be used to test the existence of any errors without the need of 744 * singleton instantiation 745 * @param string|false Package name to check for errors 746 * @param string Level name to check for a particular severity 747 * @return boolean 748 */ 749 public static function staticHasErrors($package = false, $level = false) 750 { 751 if ($package) { 752 if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) { 753 return false; 754 } 755 return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level); 756 } 757 foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { 758 if ($obj->hasErrors($level)) { 759 return true; 760 } 761 } 762 return false; 763 } 764 765 /** 766 * Get a list of all errors since last purge, organized by package 767 * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be 768 * @param boolean $purge Set to purge the error stack of existing errors 769 * @param string $level Set to a level name in order to retrieve only errors of a particular level 770 * @param boolean $merge Set to return a flat array, not organized by package 771 * @param array $sortfunc Function used to sort a merged array - default 772 * sorts by time, and should be good for most cases 773 * 774 * @return array 775 */ 776 public static function staticGetErrors( 777 $purge = false, $level = false, $merge = false, 778 $sortfunc = array('PEAR_ErrorStack', '_sortErrors') 779 ) { 780 $ret = array(); 781 if (!is_callable($sortfunc)) { 782 $sortfunc = array('PEAR_ErrorStack', '_sortErrors'); 783 } 784 foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) { 785 $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level); 786 if ($test) { 787 if ($merge) { 788 $ret = array_merge($ret, $test); 789 } else { 790 $ret[$package] = $test; 791 } 792 } 793 } 794 if ($merge) { 795 usort($ret, $sortfunc); 796 } 797 return $ret; 798 } 799 800 /** 801 * Error sorting function, sorts by time 802 * @access private 803 */ 804 public static function _sortErrors($a, $b) 805 { 806 if ($a['time'] == $b['time']) { 807 return 0; 808 } 809 if ($a['time'] < $b['time']) { 810 return 1; 811 } 812 return -1; 813 } 814 815 /** 816 * Standard file/line number/function/class context callback 817 * 818 * This function uses a backtrace generated from {@link debug_backtrace()} 819 * and so will not work at all in PHP < 4.3.0. The frame should 820 * reference the frame that contains the source of the error. 821 * @return array|false either array('file' => file, 'line' => line, 822 * 'function' => function name, 'class' => class name) or 823 * if this doesn't work, then false 824 * @param unused 825 * @param integer backtrace frame. 826 * @param array Results of debug_backtrace() 827 */ 828 public static function getFileLine($code, $params, $backtrace = null) 829 { 830 if ($backtrace === null) { 831 return false; 832 } 833 $frame = 0; 834 $functionframe = 1; 835 if (!isset($backtrace[1])) { 836 $functionframe = 0; 837 } else { 838 while (isset($backtrace[$functionframe]['function']) && 839 $backtrace[$functionframe]['function'] == 'eval' && 840 isset($backtrace[$functionframe + 1])) { 841 $functionframe++; 842 } 843 } 844 if (isset($backtrace[$frame])) { 845 if (!isset($backtrace[$frame]['file'])) { 846 $frame++; 847 } 848 $funcbacktrace = $backtrace[$functionframe]; 849 $filebacktrace = $backtrace[$frame]; 850 $ret = array('file' => $filebacktrace['file'], 851 'line' => $filebacktrace['line']); 852 // rearrange for eval'd code or create function errors 853 if (strpos($filebacktrace['file'], '(') && 854 preg_match(';^(.*?)\((\d+)\) : (.*?)\\z;', $filebacktrace['file'], 855 $matches)) { 856 $ret['file'] = $matches[1]; 857 $ret['line'] = $matches[2] + 0; 858 } 859 if (isset($funcbacktrace['function']) && isset($backtrace[1])) { 860 if ($funcbacktrace['function'] != 'eval') { 861 if ($funcbacktrace['function'] == '__lambda_func') { 862 $ret['function'] = 'create_function() code'; 863 } else { 864 $ret['function'] = $funcbacktrace['function']; 865 } 866 } 867 } 868 if (isset($funcbacktrace['class']) && isset($backtrace[1])) { 869 $ret['class'] = $funcbacktrace['class']; 870 } 871 return $ret; 872 } 873 return false; 874 } 875 876 /** 877 * Standard error message generation callback 878 * 879 * This method may also be called by a custom error message generator 880 * to fill in template values from the params array, simply 881 * set the third parameter to the error message template string to use 882 * 883 * The special variable %__msg% is reserved: use it only to specify 884 * where a message passed in by the user should be placed in the template, 885 * like so: 886 * 887 * Error message: %msg% - internal error 888 * 889 * If the message passed like so: 890 * 891 * <code> 892 * $stack->push(ERROR_CODE, 'error', array(), 'server error 500'); 893 * </code> 894 * 895 * The returned error message will be "Error message: server error 500 - 896 * internal error" 897 * @param PEAR_ErrorStack 898 * @param array 899 * @param string|false Pre-generated error message template 900 * 901 * @return string 902 */ 903 public static function getErrorMessage(&$stack, $err, $template = false) 904 { 905 if ($template) { 906 $mainmsg = $template; 907 } else { 908 $mainmsg = $stack->getErrorMessageTemplate($err['code']); 909 } 910 $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg); 911 if (is_array($err['params']) && count($err['params'])) { 912 foreach ($err['params'] as $name => $val) { 913 if (is_array($val)) { 914 // @ is needed in case $val is a multi-dimensional array 915 $val = @implode(', ', $val); 916 } 917 if (is_object($val)) { 918 if (method_exists($val, '__toString')) { 919 $val = $val->__toString(); 920 } else { 921 PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING, 922 'warning', array('obj' => get_class($val)), 923 'object %obj% passed into getErrorMessage, but has no __toString() method'); 924 $val = 'Object'; 925 } 926 } 927 $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg); 928 } 929 } 930 return $mainmsg; 931 } 932 933 /** 934 * Standard Error Message Template generator from code 935 * @return string 936 */ 937 function getErrorMessageTemplate($code) 938 { 939 if (!isset($this->_errorMsgs[$code])) { 940 return '%__msg%'; 941 } 942 return $this->_errorMsgs[$code]; 943 } 944 945 /** 946 * Set the Error Message Template array 947 * 948 * The array format must be: 949 * <pre> 950 * array(error code => 'message template',...) 951 * </pre> 952 * 953 * Error message parameters passed into {@link push()} will be used as input 954 * for the error message. If the template is 'message %foo% was %bar%', and the 955 * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will 956 * be 'message one was six' 957 * @return string 958 */ 959 function setErrorMessageTemplate($template) 960 { 961 $this->_errorMsgs = $template; 962 } 963 964 965 /** 966 * emulate PEAR::raiseError() 967 * 968 * @return PEAR_Error 969 */ 970 function raiseError() 971 { 972 require_once 'PEAR.php'; 973 $args = func_get_args(); 974 return call_user_func_array(array('PEAR', 'raiseError'), $args); 975 } 976} 977$stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack'); 978$stack->pushCallback(array('PEAR_ErrorStack', '_handleError')); 979?> 980