ErrorStack.php 33 KB

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