ErrorStack.php 34 KB

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