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