%s:%s: %s: Assertion failed
", $file, $line, $code), $file, $line); } /** * A class which allows custom handling of PHP errors. * * This is a singleton class. There should only be one instance * of it --- you can access the one instance via $GLOBALS['ErrorManager']. * * FIXME: more docs. */ class ErrorManager { /** * Constructor. * * As this is a singleton class, you should never call this. * @access private */ function ErrorManager() { $this->_handlers = array(); $this->_fatal_handler = false; $this->_postpone_mask = 0; $this->_postponed_errors = array(); set_error_handler('ErrorManager_errorHandler'); } /** * Get mask indicating which errors are currently being postponed. * @access public * @return int The current postponed error mask. */ function getPostponedErrorMask() { return $this->_postpone_mask; } /** * Set mask indicating which errors to postpone. * * The default value of the postpone mask is zero (no errors postponed.) * * When you set this mask, any queue errors which do not match the new * mask are reported. * * @access public * @param $newmask int The new value for the mask. */ function setPostponedErrorMask($newmask) { $this->_postpone_mask = $newmask; if (function_exists('PrintXML')) PrintXML($this->_flush_errors($newmask)); else echo($this->_flush_errors($newmask)); } /** * Report any queued error messages. * @access public */ function flushPostponedErrors() { if (function_exists('PrintXML')) PrintXML($this->_flush_errors()); else echo $this->_flush_errors(); } /** * Get postponed errors, formatted as HTML. * * This also flushes the postponed error queue. * * @return object HTML describing any queued errors (or false, if none). */ function getPostponedErrorsAsHTML() { $flushed = $this->_flush_errors(); if (!$flushed) return false; if ($flushed->isEmpty()) return false; // format it with the worst class (error, warning, notice) //$class = 'notice'; $cur_err = new PhpError(0,"","",""); foreach ($flushed->_content as $err) { if ($err and isa($err, 'PhpError') and $err->errno > $cur_err->errno) { $cur_err = $err; } } if ($cur_err->isNotice()) return $flushed; $class = $cur_err->getHtmlClass(); $html = HTML::div(array('class' => $class), HTML::h4(array('class' => 'errors'), "PHP " . $cur_err->getDescription())); $html->pushContent($flushed); return $html; } /** * Push a custom error handler on the handler stack. * * Sometimes one is performing an operation where one expects * certain errors or warnings. In this case, one might not want * these errors reported in the normal manner. Installing a custom * error handler via this method allows one to intercept such * errors. * * An error handler installed via this method should be either a * function or an object method taking one argument: a PhpError * object. * * The error handler should return either: *
*
False
If it has not handled the error. In this case, * error processing will proceed as if the handler * had never been called: the error will be passed * to the next handler in the stack, or the * default handler, if there are no more handlers * in the stack. * *
True
If the handler has handled the error. If the * error was a non-fatal one, no further processing * will be done. If it was a fatal error, the * ErrorManager will still terminate the PHP * process (see setFatalHandler.) * *
A PhpError object
The error is not considered * handled, and will be passed on to * the next handler(s) in the stack * (or the default handler). The * returned PhpError need not be the * same as the one passed to the * handler. This allows the handler to * "adjust" the error message. *
* @access public * @param $handler WikiCallback Handler to call. */ function pushErrorHandler($handler) { array_unshift($this->_handlers, $handler); } /** * Pop an error handler off the handler stack. * @access public */ function popErrorHandler() { return array_shift($this->_handlers); } /** * Set a termination handler. * * This handler will be called upon fatal errors. The handler * gets passed one argument: a PhpError object describing the * fatal error. * * @access public * @param $handler WikiCallback Callback to call on fatal errors. */ function setFatalHandler($handler) { $this->_fatal_handler = $handler; } /** * Handle an error. * * The error is passed through any registered error handlers, and * then either reported or postponed. * * @access public * @param $error object A PhpError object. */ function handleError($error) { static $in_handler; if (!empty($in_handler)) { $msg = $error->_getDetail(); $msg->unshiftContent(HTML::h2(fmt("%s: error while handling error:", "ErrorManager"))); $msg->printXML(); return; } $in_handler = true; foreach ($this->_handlers as $handler) { if (!$handler) continue; $result = $handler->call($error); if (!$result) { continue; // Handler did not handle error. } elseif (is_object($result)) { // handler filtered the result. Still should pass to // the rest of the chain. if ($error->isFatal()) { // Don't let handlers make fatal errors non-fatal. $result->errno = $error->errno; } $error = $result; } else { // Handler handled error. if (!$error->isFatal()) { $in_handler = false; return; } break; } } $this->_noCacheHeaders(); // Error was either fatal, or was not handled by a handler. // Handle it ourself. if ($error->isFatal()) { echo "
Fatal
\n"; if (defined('DEBUG') and (DEBUG & _DEBUG_TRACE)) { echo "error_reporting=",error_reporting(),"\n
"; $error->printSimpleTrace(debug_backtrace()); } $this->_die($error); } else if (($error->errno & error_reporting()) != 0) { if (($error->errno & $this->_postpone_mask) != 0) { if ((function_exists('is_a') and is_a($error,'PhpErrorOnce')) or (!function_exists('is_a') and ( // stdlib independent isa() (strtolower(get_class($error)) == 'phperroronce') or (is_subclass_of($error, 'PhpErrorOnce'))))) { $error->removeDoublettes($this->_postponed_errors); if ( $error->_count < 2 ) $this->_postponed_errors[] = $error; } else { $this->_postponed_errors[] = $error; } } else { //echo "postponed errors: "; if (defined('DEBUG') and (DEBUG & _DEBUG_TRACE)) { echo "error_reporting=",error_reporting(),"\n"; $error->printSimpleTrace(debug_backtrace()); } $error->printXML(); } } $in_handler = false; } function warning($msg, $errno=E_USER_NOTICE) { $this->handleError(new PhpWikiError($errno, $msg)); } /** * @access private */ function _die($error) { //echo "\n\n"; $error->printXML(); PrintXML($this->_flush_errors()); if ($this->_fatal_handler) $this->_fatal_handler->call($error); exit -1; } /** * @access private */ function _flush_errors($keep_mask = 0) { $errors = &$this->_postponed_errors; if (empty($errors)) return ''; $flushed = HTML(); for ($i=0; $ierrno & $keep_mask) != 0) continue; unset($errors[$i]); $flushed->pushContent($error); } return $flushed; } function _noCacheHeaders() { global $request; static $already = false; if (isset($request) and isset($request->_validators)) { $request->_validators->_tag = false; $request->_validators->_mtime = false; } if ($already) return; // FIXME: Howto announce that to Request->cacheControl()? if (!headers_sent()) { header( "Cache-control: no-cache" ); header( "Pragma: nocache" ); } $already = true; } } /** * Global error handler for class ErrorManager. * * This is necessary since PHP's set_error_handler() does not allow * one to set an object method as a handler. * * @access private */ function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline) { if (!isset($GLOBALS['ErrorManager'])) { $GLOBALS['ErrorManager'] = new ErrorManager; } $error = new PhpErrorOnce($errno, $errstr, $errfile, $errline); $GLOBALS['ErrorManager']->handleError($error); } /** * A class representing a PHP error report. * * @see The PHP documentation for set_error_handler at * http://php.net/manual/en/function.set-error-handler.php . */ class PhpError { /** * The PHP errno */ var $errno; /** * The PHP error message. */ var $errstr; /** * The source file where the error occurred. */ var $errfile; /** * The line number (in $this->errfile) where the error occured. */ var $errline; /** * Construct a new PhpError. * @param $errno int * @param $errstr string * @param $errfile string * @param $errline int */ function PhpError($errno, $errstr, $errfile, $errline) { $this->errno = $errno; $this->errstr = $errstr; $this->errfile = $errfile; $this->errline = $errline; } /** * Determine whether this is a fatal error. * @return boolean True if this is a fatal error. */ function isFatal() { return ($this->errno & (2048|EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0; } /** * Determine whether this is a warning level error. * @return boolean */ function isWarning() { return ($this->errno & EM_WARNING_ERRORS) != 0; } /** * Determine whether this is a notice level error. * @return boolean */ function isNotice() { return ($this->errno & EM_NOTICE_ERRORS) != 0; } function getHtmlClass() { if ($this->isNotice()) { return 'hint'; } elseif ($this->isWarning()) { return 'errors'; } else { return 'errors'; } } function getDescription() { if ($this->isNotice()) { return 'Notice'; } elseif ($this->isWarning()) { return 'Warning'; } else { return 'Error'; } } /** * Get a printable, HTML, message detailing this error. * @return object The detailed error message. */ function _getDetail() { $dir = defined('PHPWIKI_DIR') ? PHPWIKI_DIR : substr(dirname(__FILE__),0,-4); if (substr(PHP_OS,0,3) == 'WIN') { $dir = str_replace('/','\\',$dir); $this->errfile = str_replace('/','\\',$this->errfile); $dir .= "\\"; } else $dir .= '/'; $errfile = preg_replace('|^' . preg_quote($dir) . '|', '', $this->errfile); $lines = explode("\n", $this->errstr); $msg = sprintf("%s:%d: %s[%d]: %s", $errfile, $this->errline, $this->getDescription(), $this->errno, array_shift($lines)); //$html = HTML::div(array('class' => $this->getHtmlClass()), HTML::p($msg)); // The class is now used for the div container. $html = HTML::div(HTML::p($msg)); if ($lines) { $list = HTML::ul(); foreach ($lines as $line) $list->pushContent(HTML::li($line)); $html->pushContent($list); } return $html; } /** * Print an HTMLified version of this error. * @see asXML() */ function printXML() { PrintXML($this->_getDetail()); } /** * Return an HTMLified version of this error. */ function asXML() { return AsXML($this->_getDetail()); } /** * Return a plain-text version of this error. */ function asString() { return AsString($this->_getDetail()); } function printSimpleTrace($bt) { global $HTTP_SERVER_VARS; $nl = isset($HTTP_SERVER_VARS['REQUEST_METHOD']) ? "
" : "\n"; echo $nl."Traceback:".$nl; foreach ($bt as $i => $elem) { if (!array_key_exists('file', $elem)) { continue; } print " " . $elem['file'] . ':' . $elem['line'] . $nl; } flush(); } } /** * A class representing a PhpWiki warning. * * This is essentially the same as a PhpError, except that the * error message is quieter: no source line, etc... */ class PhpWikiError extends PhpError { /** * Construct a new PhpError. * @param $errno int * @param $errstr string */ function PhpWikiError($errno, $errstr) { $this->PhpError($errno, $errstr, '?', '?'); } function _getDetail() { return HTML::div(//array('class' => $this->getHtmlClass()), HTML::p($this->getDescription() . ": $this->errstr")); } } /** * A class representing a Php warning, printed only the first time. * * Similar to PhpError, except only the first same error message is printed, * with number of occurences. */ class PhpErrorOnce extends PhpError { function PhpErrorOnce($errno, $errstr, $errfile, $errline) { $this->_count = 1; $this->PhpError($errno, $errstr, $errfile, $errline); } function _sameError($error) { if (!$error) return false; return ($this->errno == $error->errno and $this->errfile == $error->errfile and $this->errline == $error->errline); } // count similar handlers, increase _count and remove the rest function removeDoublettes(&$errors) { for ($i=0; $i < count($errors); $i++) { if (!isset($errors[$i])) continue; if ($this->_sameError($errors[$i])) { $errors[$i]->_count++; $this->_count++; if ($i) unset($errors[$i]); } } return $this->_count; } function _getDetail($count=0) { if (!$count) $count = $this->_count; $dir = defined('PHPWIKI_DIR') ? PHPWIKI_DIR : substr(dirname(__FILE__),0,-4); if (substr(PHP_OS,0,3) == 'WIN') { $dir = str_replace('/','\\',$dir); $this->errfile = str_replace('/','\\',$this->errfile); $dir .= "\\"; } else $dir .= '/'; $errfile = preg_replace('|^' . preg_quote($dir) . '|', '', $this->errfile); $lines = explode("\n", $this->errstr); $msg = sprintf("%s:%d: %s[%d]: %s %s", $errfile, $this->errline, $this->getDescription(), $this->errno, array_shift($lines), $count > 1 ? sprintf(" (...repeated %d times)",$count) : "" ); $html = HTML::div(//array('class' => $this->getHtmlClass()), HTML::p($msg)); if ($lines) { $list = HTML::ul(); foreach ($lines as $line) $list->pushContent(HTML::li($line)); $html->pushContent($list); } return $html; } } require_once(dirname(__FILE__).'/HtmlElement.php'); if (!isset($GLOBALS['ErrorManager'])) { $GLOBALS['ErrorManager'] = new ErrorManager; } // $Log: not supported by cvs2svn $ // Revision 1.35 2004/10/12 13:13:19 rurban // php5 compatibility (5.0.1 ok) // // Revision 1.34 2004/09/24 18:52:19 rurban // in deferred html error messages use the worst header and class // (notice => warning => errors) // // Revision 1.33 2004/09/14 10:28:21 rurban // use assert, maybe we should only turn it off for releases // // Revision 1.32 2004/07/08 13:50:32 rurban // various unit test fixes: print error backtrace on _DEBUG_TRACE; allusers fix; new PHPWIKI_NOMAIN constant for omitting the mainloop // // Revision 1.31 2004/07/02 09:55:58 rurban // more stability fixes: new DISABLE_GETIMAGESIZE if your php crashes when loading LinkIcons: failing getimagesize in old phps; blockparser stabilized // // Revision 1.30 2004/06/25 14:29:12 rurban // WikiGroup refactoring: // global group attached to user, code for not_current user. // improved helpers for special groups (avoid double invocations) // new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write()) // fixed a XHTML validation error on userprefs.tmpl // // Revision 1.29 2004/06/20 15:30:04 rurban // get_class case-sensitivity issues // // Revision 1.28 2004/06/16 11:51:04 rurban // fixed typo: undefined object #235 // // Revision 1.27 2004/06/13 09:38:20 rurban // isa() workaround, if stdlib.php is not loaded // // Revision 1.26 2004/06/02 18:01:45 rurban // init global FileFinder to add proper include paths at startup // adds PHPWIKI_DIR if started from another dir, lib/pear also // fix slashify for Windows // fix USER_AUTH_POLICY=old, use only USER_AUTH_ORDER methods (besides HttpAuth) // // Revision 1.25 2004/06/02 10:18:36 rurban // assert only if DEBUG is non-false // // Revision 1.24 2004/05/27 17:49:05 rurban // renamed DB_Session to DbSession (in CVS also) // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals // remove leading slash in error message // added force_unlock parameter to File_Passwd (no return on stale locks) // fixed adodb session AffectedRows // added FileFinder helpers to unify local filenames and DATA_PATH names // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR // // // (c-file-style: "gnu") // Local Variables: // mode: php // tab-width: 8 // c-basic-offset: 4 // c-hanging-comment-ender-p: nil // indent-tabs-mode: nil // End: ?>