PageRenderTime 59ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/inc/PEAR/ErrorStack.php

https://github.com/chregu/fluxcms
PHP | 970 lines | 436 code | 48 blank | 486 comment | 91 complexity | c02a0cca4906c3e08af8228d92faa44e MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, Apache-2.0, LGPL-2.1
  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-2006 Greg Beaver
  25. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  26. * @version CVS: $Id$
  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.4.6
  129. * @package PEAR_ErrorStack
  130. * @category Debugging
  131. * @copyright 2004-2006 Greg Beaver
  132. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  133. * @version CVS: $Id$
  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|Exception
  450. * if compatibility mode is on, a PEAR_Error is also
  451. * thrown. If the class Exception exists, then one
  452. * is returned to allow code like:
  453. * <code>
  454. * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
  455. * </code>
  456. *
  457. * The errorData property of the exception class will be set to the array
  458. * that would normally be returned. If a PEAR_Error is returned, the userinfo
  459. * property is set to the array
  460. *
  461. * Otherwise, an array is returned in this format:
  462. * <code>
  463. * array(
  464. * 'code' => $code,
  465. * 'params' => $params,
  466. * 'package' => $this->_package,
  467. * 'level' => $level,
  468. * 'time' => time(),
  469. * 'context' => $context,
  470. * 'message' => $msg,
  471. * //['repackage' => $err] repackaged error array/Exception class
  472. * );
  473. * </code>
  474. */
  475. function push($code, $level = 'error', $params = array(), $msg = false,
  476. $repackage = false, $backtrace = false)
  477. {
  478. $context = false;
  479. // grab error context
  480. if ($this->_contextCallback) {
  481. if (!$backtrace) {
  482. $backtrace = debug_backtrace();
  483. }
  484. $context = call_user_func($this->_contextCallback, $code, $params, $backtrace);
  485. }
  486. // save error
  487. $time = explode(' ', microtime());
  488. $time = $time[1] + $time[0];
  489. $err = array(
  490. 'code' => $code,
  491. 'params' => $params,
  492. 'package' => $this->_package,
  493. 'level' => $level,
  494. 'time' => $time,
  495. 'context' => $context,
  496. 'message' => $msg,
  497. );
  498. if ($repackage) {
  499. $err['repackage'] = $repackage;
  500. }
  501. // set up the error message, if necessary
  502. if ($this->_msgCallback) {
  503. $msg = call_user_func_array($this->_msgCallback,
  504. array(&$this, $err));
  505. $err['message'] = $msg;
  506. }
  507. $push = $log = true;
  508. $die = false;
  509. // try the overriding callback first
  510. $callback = $this->staticPopCallback();
  511. if ($callback) {
  512. $this->staticPushCallback($callback);
  513. }
  514. if (!is_callable($callback)) {
  515. // try the local callback next
  516. $callback = $this->popCallback();
  517. if (is_callable($callback)) {
  518. $this->pushCallback($callback);
  519. } else {
  520. // try the default callback
  521. $callback = isset($GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package]) ?
  522. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK'][$this->_package] :
  523. $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_CALLBACK']['*'];
  524. }
  525. }
  526. if (is_callable($callback)) {
  527. switch(call_user_func($callback, $err)){
  528. case PEAR_ERRORSTACK_IGNORE:
  529. return $err;
  530. break;
  531. case PEAR_ERRORSTACK_PUSH:
  532. $log = false;
  533. break;
  534. case PEAR_ERRORSTACK_LOG:
  535. $push = false;
  536. break;
  537. case PEAR_ERRORSTACK_DIE:
  538. $die = true;
  539. break;
  540. // anything else returned has the same effect as pushandlog
  541. }
  542. }
  543. if ($push) {
  544. array_unshift($this->_errors, $err);
  545. $this->_errorsByLevel[$err['level']][] = &$this->_errors[0];
  546. }
  547. if ($log) {
  548. if ($this->_logger || $GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER']) {
  549. $this->_log($err);
  550. }
  551. }
  552. if ($die) {
  553. die();
  554. }
  555. if ($this->_compat && $push) {
  556. return $this->raiseError($msg, $code, null, null, $err);
  557. }
  558. return $err;
  559. }
  560. /**
  561. * Static version of {@link push()}
  562. *
  563. * @param string $package Package name this error belongs to
  564. * @param int $code Package-specific error code
  565. * @param string $level Error level. This is NOT spell-checked
  566. * @param array $params associative array of error parameters
  567. * @param string $msg Error message, or a portion of it if the message
  568. * is to be generated
  569. * @param array $repackage If this error re-packages an error pushed by
  570. * another package, place the array returned from
  571. * {@link pop()} in this parameter
  572. * @param array $backtrace Protected parameter: use this to pass in the
  573. * {@link debug_backtrace()} that should be used
  574. * to find error context
  575. * @return PEAR_Error|null|Exception
  576. * if compatibility mode is on, a PEAR_Error is also
  577. * thrown. If the class Exception exists, then one
  578. * is returned to allow code like:
  579. * <code>
  580. * throw ($stack->push(MY_ERROR_CODE, 'error', array('username' => 'grob')));
  581. * </code>
  582. * @static
  583. */
  584. function staticPush($package, $code, $level = 'error', $params = array(),
  585. $msg = false, $repackage = false, $backtrace = false)
  586. {
  587. $s = &PEAR_ErrorStack::singleton($package);
  588. if ($s->_contextCallback) {
  589. if (!$backtrace) {
  590. if (function_exists('debug_backtrace')) {
  591. $backtrace = debug_backtrace();
  592. }
  593. }
  594. }
  595. return $s->push($code, $level, $params, $msg, $repackage, $backtrace);
  596. }
  597. /**
  598. * Log an error using PEAR::Log
  599. * @param array $err Error array
  600. * @param array $levels Error level => Log constant map
  601. * @access protected
  602. */
  603. function _log($err)
  604. {
  605. if ($this->_logger) {
  606. $logger = &$this->_logger;
  607. } else {
  608. $logger = &$GLOBALS['_PEAR_ERRORSTACK_DEFAULT_LOGGER'];
  609. }
  610. if (is_a($logger, 'Log')) {
  611. $levels = array(
  612. 'exception' => PEAR_LOG_CRIT,
  613. 'alert' => PEAR_LOG_ALERT,
  614. 'critical' => PEAR_LOG_CRIT,
  615. 'error' => PEAR_LOG_ERR,
  616. 'warning' => PEAR_LOG_WARNING,
  617. 'notice' => PEAR_LOG_NOTICE,
  618. 'info' => PEAR_LOG_INFO,
  619. 'debug' => PEAR_LOG_DEBUG);
  620. if (isset($levels[$err['level']])) {
  621. $level = $levels[$err['level']];
  622. } else {
  623. $level = PEAR_LOG_INFO;
  624. }
  625. $logger->log($err['message'], $level, $err);
  626. } else { // support non-standard logs
  627. call_user_func($logger, $err);
  628. }
  629. }
  630. /**
  631. * Pop an error off of the error stack
  632. *
  633. * @return false|array
  634. * @since 0.4alpha it is no longer possible to specify a specific error
  635. * level to return - the last error pushed will be returned, instead
  636. */
  637. function pop()
  638. {
  639. return @array_shift($this->_errors);
  640. }
  641. /**
  642. * Determine whether there are any errors on the stack
  643. * @param string|array Level name. Use to determine if any errors
  644. * of level (string), or levels (array) have been pushed
  645. * @return boolean
  646. */
  647. function hasErrors($level = false)
  648. {
  649. if ($level) {
  650. return isset($this->_errorsByLevel[$level]);
  651. }
  652. return count($this->_errors);
  653. }
  654. /**
  655. * Retrieve all errors since last purge
  656. *
  657. * @param boolean set in order to empty the error stack
  658. * @param string level name, to return only errors of a particular severity
  659. * @return array
  660. */
  661. function getErrors($purge = false, $level = false)
  662. {
  663. if (!$purge) {
  664. if ($level) {
  665. if (!isset($this->_errorsByLevel[$level])) {
  666. return array();
  667. } else {
  668. return $this->_errorsByLevel[$level];
  669. }
  670. } else {
  671. return $this->_errors;
  672. }
  673. }
  674. if ($level) {
  675. $ret = $this->_errorsByLevel[$level];
  676. foreach ($this->_errorsByLevel[$level] as $i => $unused) {
  677. // entries are references to the $_errors array
  678. $this->_errorsByLevel[$level][$i] = false;
  679. }
  680. // array_filter removes all entries === false
  681. $this->_errors = array_filter($this->_errors);
  682. unset($this->_errorsByLevel[$level]);
  683. return $ret;
  684. }
  685. $ret = $this->_errors;
  686. $this->_errors = array();
  687. $this->_errorsByLevel = array();
  688. return $ret;
  689. }
  690. /**
  691. * Determine whether there are any errors on a single error stack, or on any error stack
  692. *
  693. * The optional parameter can be used to test the existence of any errors without the need of
  694. * singleton instantiation
  695. * @param string|false Package name to check for errors
  696. * @param string Level name to check for a particular severity
  697. * @return boolean
  698. * @static
  699. */
  700. function staticHasErrors($package = false, $level = false)
  701. {
  702. if ($package) {
  703. if (!isset($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package])) {
  704. return false;
  705. }
  706. return $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->hasErrors($level);
  707. }
  708. foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
  709. if ($obj->hasErrors($level)) {
  710. return true;
  711. }
  712. }
  713. return false;
  714. }
  715. /**
  716. * Get a list of all errors since last purge, organized by package
  717. * @since PEAR 1.4.0dev BC break! $level is now in the place $merge used to be
  718. * @param boolean $purge Set to purge the error stack of existing errors
  719. * @param string $level Set to a level name in order to retrieve only errors of a particular level
  720. * @param boolean $merge Set to return a flat array, not organized by package
  721. * @param array $sortfunc Function used to sort a merged array - default
  722. * sorts by time, and should be good for most cases
  723. * @static
  724. * @return array
  725. */
  726. function staticGetErrors($purge = false, $level = false, $merge = false,
  727. $sortfunc = array('PEAR_ErrorStack', '_sortErrors'))
  728. {
  729. $ret = array();
  730. if (!is_callable($sortfunc)) {
  731. $sortfunc = array('PEAR_ErrorStack', '_sortErrors');
  732. }
  733. foreach ($GLOBALS['_PEAR_ERRORSTACK_SINGLETON'] as $package => $obj) {
  734. $test = $GLOBALS['_PEAR_ERRORSTACK_SINGLETON'][$package]->getErrors($purge, $level);
  735. if ($test) {
  736. if ($merge) {
  737. $ret = array_merge($ret, $test);
  738. } else {
  739. $ret[$package] = $test;
  740. }
  741. }
  742. }
  743. if ($merge) {
  744. usort($ret, $sortfunc);
  745. }
  746. return $ret;
  747. }
  748. /**
  749. * Error sorting function, sorts by time
  750. * @access private
  751. */
  752. function _sortErrors($a, $b)
  753. {
  754. if ($a['time'] == $b['time']) {
  755. return 0;
  756. }
  757. if ($a['time'] < $b['time']) {
  758. return 1;
  759. }
  760. return -1;
  761. }
  762. /**
  763. * Standard file/line number/function/class context callback
  764. *
  765. * This function uses a backtrace generated from {@link debug_backtrace()}
  766. * and so will not work at all in PHP < 4.3.0. The frame should
  767. * reference the frame that contains the source of the error.
  768. * @return array|false either array('file' => file, 'line' => line,
  769. * 'function' => function name, 'class' => class name) or
  770. * if this doesn't work, then false
  771. * @param unused
  772. * @param integer backtrace frame.
  773. * @param array Results of debug_backtrace()
  774. * @static
  775. */
  776. function getFileLine($code, $params, $backtrace = null)
  777. {
  778. if ($backtrace === null) {
  779. return false;
  780. }
  781. $frame = 0;
  782. $functionframe = 1;
  783. if (!isset($backtrace[1])) {
  784. $functionframe = 0;
  785. } else {
  786. while (isset($backtrace[$functionframe]['function']) &&
  787. $backtrace[$functionframe]['function'] == 'eval' &&
  788. isset($backtrace[$functionframe + 1])) {
  789. $functionframe++;
  790. }
  791. }
  792. if (isset($backtrace[$frame])) {
  793. if (!isset($backtrace[$frame]['file'])) {
  794. $frame++;
  795. }
  796. $funcbacktrace = $backtrace[$functionframe];
  797. $filebacktrace = $backtrace[$frame];
  798. $ret = array('file' => $filebacktrace['file'],
  799. 'line' => $filebacktrace['line']);
  800. // rearrange for eval'd code or create function errors
  801. if (strpos($filebacktrace['file'], '(') &&
  802. preg_match(';^(.*?)\((\d+)\) : (.*?)$;', $filebacktrace['file'],
  803. $matches)) {
  804. $ret['file'] = $matches[1];
  805. $ret['line'] = $matches[2] + 0;
  806. }
  807. if (isset($funcbacktrace['function']) && isset($backtrace[1])) {
  808. if ($funcbacktrace['function'] != 'eval') {
  809. if ($funcbacktrace['function'] == '__lambda_func') {
  810. $ret['function'] = 'create_function() code';
  811. } else {
  812. $ret['function'] = $funcbacktrace['function'];
  813. }
  814. }
  815. }
  816. if (isset($funcbacktrace['class']) && isset($backtrace[1])) {
  817. $ret['class'] = $funcbacktrace['class'];
  818. }
  819. return $ret;
  820. }
  821. return false;
  822. }
  823. /**
  824. * Standard error message generation callback
  825. *
  826. * This method may also be called by a custom error message generator
  827. * to fill in template values from the params array, simply
  828. * set the third parameter to the error message template string to use
  829. *
  830. * The special variable %__msg% is reserved: use it only to specify
  831. * where a message passed in by the user should be placed in the template,
  832. * like so:
  833. *
  834. * Error message: %msg% - internal error
  835. *
  836. * If the message passed like so:
  837. *
  838. * <code>
  839. * $stack->push(ERROR_CODE, 'error', array(), 'server error 500');
  840. * </code>
  841. *
  842. * The returned error message will be "Error message: server error 500 -
  843. * internal error"
  844. * @param PEAR_ErrorStack
  845. * @param array
  846. * @param string|false Pre-generated error message template
  847. * @static
  848. * @return string
  849. */
  850. function getErrorMessage(&$stack, $err, $template = false)
  851. {
  852. if ($template) {
  853. $mainmsg = $template;
  854. } else {
  855. $mainmsg = $stack->getErrorMessageTemplate($err['code']);
  856. }
  857. $mainmsg = str_replace('%__msg%', $err['message'], $mainmsg);
  858. if (is_array($err['params']) && count($err['params'])) {
  859. foreach ($err['params'] as $name => $val) {
  860. if (is_array($val)) {
  861. // @ is needed in case $val is a multi-dimensional array
  862. $val = @implode(', ', $val);
  863. }
  864. if (is_object($val)) {
  865. if (method_exists($val, '__toString')) {
  866. $val = $val->__toString();
  867. } else {
  868. PEAR_ErrorStack::staticPush('PEAR_ErrorStack', PEAR_ERRORSTACK_ERR_OBJTOSTRING,
  869. 'warning', array('obj' => get_class($val)),
  870. 'object %obj% passed into getErrorMessage, but has no __toString() method');
  871. $val = 'Object';
  872. }
  873. }
  874. $mainmsg = str_replace('%' . $name . '%', $val, $mainmsg);
  875. }
  876. }
  877. return $mainmsg;
  878. }
  879. /**
  880. * Standard Error Message Template generator from code
  881. * @return string
  882. */
  883. function getErrorMessageTemplate($code)
  884. {
  885. if (!isset($this->_errorMsgs[$code])) {
  886. return '%__msg%';
  887. }
  888. return $this->_errorMsgs[$code];
  889. }
  890. /**
  891. * Set the Error Message Template array
  892. *
  893. * The array format must be:
  894. * <pre>
  895. * array(error code => 'message template',...)
  896. * </pre>
  897. *
  898. * Error message parameters passed into {@link push()} will be used as input
  899. * for the error message. If the template is 'message %foo% was %bar%', and the
  900. * parameters are array('foo' => 'one', 'bar' => 'six'), the error message returned will
  901. * be 'message one was six'
  902. * @return string
  903. */
  904. function setErrorMessageTemplate($template)
  905. {
  906. $this->_errorMsgs = $template;
  907. }
  908. /**
  909. * emulate PEAR::raiseError()
  910. *
  911. * @return PEAR_Error
  912. */
  913. function raiseError()
  914. {
  915. require_once 'PEAR.php';
  916. $args = func_get_args();
  917. return call_user_func_array(array('PEAR', 'raiseError'), $args);
  918. }
  919. }
  920. $stack = &PEAR_ErrorStack::singleton('PEAR_ErrorStack');
  921. $stack->pushCallback(array('PEAR_ErrorStack', '_handleError'));
  922. ?>