\SparkLib\Fail::setup($public_or_not) somewhere near * the start of your application. At SparkFun, we do this in a configuration file * shortly after setting up \SparkLib\Autoloader. */ class Fail { protected static $failCount = 0; protected static $exceptionCount = 0; protected static $failText = ''; protected static $reference = ''; /** * Image to display with public errors. */ public static $img_url = 'https://dlnmh9ip6v2uc.cloudfront.net/images/sparkfail.png'; /** * Send all errors straight to the log? */ public static $errorLogAll = false; /** * Include user agent with fail logging? */ public static $logUserAgent = false; /** * Log to a specific file via file_put_contents() instead of using * error_log(). */ public static $logFile = null; /** * Log only strings matching this regex. */ public static $grep = null; /** * Break out backtraces for php E_ errors/warning/notices/whatever */ public static $doBacktraces = false; /** * Map numbers to human-readable error types. */ public static $errorList = array( 1 => 'E_ERROR', 2 => 'E_WARNING', 4 => 'E_PARSE', 8 => 'E_NOTICE', 16 => 'E_CORE_ERROR', 32 => 'E_CORE_WARNING', 64 => 'E_COMPILE_ERROR', 128 => 'E_COMPILE_WARNING', 256 => 'E_USER_ERROR', 512 => 'E_USER_WARNING', 1024 => 'E_USER_NOTICE', 2048 => 'E_STRICT', 4096 => 'E_RECOVERABLE_ERROR', 8192 => 'E_DEPRECATED', 16384 => 'E_USER_DEPRECATED', 30719 => 'E_ALL', ); /** * List errors which should be promoted to exceptions. * * Something like: * * * \SparkLib\Fail::$exceptionOnErrors = array(E_RECOVERABLE_ERROR => true); * */ public static $exceptionOnErrors = array(); /** * Set up error & exception handling, logging, etc. * * @param $public boolean are we operating in public? */ public static function setup ($public = true) { $class = get_called_class(); // We always log exceptions // The array here is a callback for \SparkLib\Fail::log() set_exception_handler(array($class, 'log')); if ($public) { // We're in public - this will display something safe for users: register_shutdown_function(array($class, 'PukePublic')); } else { // We're on a development box. set_error_handler(array($class, 'handleError')); // Did this come from a browser? if (isset($_SERVER['QUERY_STRING']) || isset($_SERVER['REMOTE_HOST'])) register_shutdown_function(array($class, 'PukeDevelopment')); } } /** * Log an arbitrary string or Exception. * * @param $message string|Exception message to log * @param $type string optional type of message */ public static function log ($message, $type = 'error') { // Did we get an Exception? if ($message instanceof \Exception) { // Get a unique string - we can log this, and then display it to the // user as a reference number. self::$reference .= substr(sha1(time() . __FILE__), 0, 10) . ' '; self::$exceptionCount++; $message_text = self::$reference . ':: Uncaught ' . get_class($message) . ' - ' . $message->getMessage() . "\n" . $message->getTraceAsString(); } elseif (is_array($message) || is_object($message)) { $message_text = print_r($message, 1); } else { $message_text = $message; } // if we're grepping for something specific, make sure this message matches: if (isset(static::$grep) && (! preg_match(static::$grep, $message_text))) { return; } // If blode is sitting around, send it our message. if (class_exists('Event')) { Event::err($message_text); } if (static::$logUserAgent && isset($_SERVER['HTTP_USER_AGENT'])) { $message_text .= ' [' . $_SERVER['HTTP_USER_AGENT'] . ']'; } $message_text .= "\n"; self::$failCount++; self::$failText .= $message_text; if (isset(self::$logFile)) { // Note deliberate error suppression; there's a good chance this // write will fail from the command line. @file_put_contents(self::$logFile, $message_text, \FILE_APPEND); } else { error_log($message_text); } } /** * Catch an error and do something useful with it, ignoring a * few less helpful things. */ public static function handleError ($errno, $errstr, $errfile, $errline, $errcontext) { // Filter out low-urgency stuff we don't care about right now: // XXX: Get rid of SFE-specific stuff here. if ($errno === \E_NOTICE) { if (stristr($errstr, 'undefined index')) return; } if ($errno === \E_DEPRECATED) { if (strstr($errfile, 'Barcode.php')) return; } if (($errno === \E_WARNING) || ($errno === \E_STRICT)) { if (strstr($errfile, 'Barcode.php')) return; if (strstr($errfile, 'Code39.php')) return; } $errorType = static::$errorList[(int)$errno]; self::log("$errfile +$errline - {$errorType} - $errstr\n"); if (static::$doBacktraces) { // pull this current function call off of the trace $trace = debug_backtrace(); array_shift($trace); self::log(static::compile_backtrace($trace)); } if (isset(static::$exceptionOnErrors[ $errno ])) { // these get thrown on type errors and possibly elsewhere - let's // see what happens if we make sure they're fatal throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); } } /** * Check whether we've seen any failure. */ public static function noFail () { return (self::$failCount === 0); } /** * How many failures have we logged? */ public static function failCount () { return self::$failCount; } /** * How many exceptions have we seen? * * Fundamentally this is kind of silly, since we're probably only * ever going to get _one_. */ public static function exceptionCount () { return self::$exceptionCount; } /** * Return a simple version of the failure log. */ public static function render () { if (self::noFail()) { return 'No known failures.'; } return self::$failText . "\n " . self::$failCount . ' failures'; } /** * Build a human-readable backtrace. */ public static function compile_backtrace (array $backtrace) { $str = ''; foreach ($backtrace as $stack_frame) { $str .= "{$stack_frame['file']} +{$stack_frame['line']}: {$stack_frame['function']}("; $first_argument = true; foreach ($stack_frame['args'] as $function_argument) { if (! $first_argument ) $str .= ', '; $str .= $type = gettype($function_argument); switch ($type){ case 'integer': case 'double': case 'boolean': $str .= "({$function_argument})"; break; case 'string': $str .= '("'; $str .= substr($function_argument,0, min(15, strlen($function_argument)) ); $str .= '")'; break; case 'array': $str .= '[' . count($function_argument) . ']'; break; // a bit untested... case 'object': $str .= '('. get_class($function_argument) . ')'; break; case 'resource': break; } $first_argument = false; } $str .= ")\n"; } $str .= "\n\n"; return $str; } /** * If we've seen any exceptions, spit out a friendly-ish error message, * safe for consumption by the general public. */ public static function PukePublic () { $img = static::$img_url; if (self::$exceptionCount) { ?>

Something broke.

...not to worry. We've logged this error and will be looking into it.

Reference #

skip it // Are we supposed to just use error_log()? / if (self::noFail() || self::$errorLogAll) { return; } $img_url = static::$img_url; // fuuuuuugly: print << pre#sparkfail-errors { position: absolute; top: 5px; left: 5px; z-index: 2147483647; text-align: left; background: white; color: red; border: 1px solid black; padding: 10px; background-image: url({$img_url}); background-repeat: no-repeat; padding-bottom: 150px; background-position: bottom right; } pre#sparkfail-errors b { float: left; font-weight: bold; color: black; } pre#sparkfail-errors b.close { cursor: pointer; color: blue; float: right; margin: 0; padding: 0; font-size: 1.1em; } pre#sparkfail-errors hr { margin: 0; padding: 0; }
SparkFail[X]

HTML; print self::render() . "\n
"; print ""; } }