ErrorStack.php 33 KB

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