. ======================================================================*/ /** @mainpage KjwLib-php45 * * KjwLib-45 is a PHP4.3+ and PHP5 compatible library for PHP. * It tries to simplify a couple of common operations, most notably the * use of database storage, early error handling (with backtraces by * mail or as inline-html) and plain mail support that handles UTF-8 * properly. * * @section database_example Database example *
 * //define('KJW_STDLOG', fopen('/tmp/debug_log', 'w')); // uncomment to get debug log
 * require_once 'KjwSql.php';
 * $db = kjwsql_from_url('mysql://user:pass\@host/database');
 * $db->insertArray('mytable', array('string_column', 'somestring', 'int_column', 1234));
 * echo $db->insertId();
 * 
* * This does the following behind the scenes: * - Connect to the MySQL database host 'host' with credentials 'user' * and 'pass'. * - Selects the database 'database'. * - Creates an insert statement with columns `string_column` and `int_column`, * properly escaped for MySQL (using backticks) and 'somestring' escaped by * using php's mysql_real_escape_string. * - Executes the statement and checks for errors. * - Finally it echoes the mysql_insert_id (the new id created by the * AUTO_INCREMENT column, if available. * * @section errorhandling_example Error handling example *
 * ini_set('date.timezone', 'Europe/Amsterdam'); // only set this if not set in config already
 * error_reporting(E_ALL | E_STRICT); // optional, depends on your config
 * define('KJW_EMAIL_ERRORS_TO', 'your-email\@example.com'); // don't define this if you want your errors on display
 * require_once 'KjwLib.php';
 * $x = $undefined_variable; // <-- THIS TRIGGERS AN ERROR
 * 
* * This makes sure that you get a pretty backtrace about where you're * referencing $undefined_variable before it was set, either by mail or on * display. */ require_once(dirname(__FILE__) . '/KjwCompat.php'); require_once(dirname(__FILE__) . '/KjwObject.php'); /** @defgroup globals Library globals * * KjwLib global functions. */ /** @ingroup globals * * Stores a suppressed error. When you use the at-sign (@) to suppress * an error, the error message is stored in this global. * Normally, you do not need to suppress many errors. * Think twice before using it. */ $kjw_suppressed_error = null; /** @ingroup globals * * We don't want to have to use @-error-suppression in our own shutdown * or error handlers. Use this to get dictionary values without having * to trap errors. * * @param $dict The associative array. * @param $key The key of the element to get. * @param $default The value to return when the key does not exist. * Defaults to the empty string. * @return The value or the supplied default. */ function kjw_array_get($dict, $key, $default = '') { if (!isset($dict[$key])) return $default; return $dict[$key]; } /** @ingroup globals * * The KjwLib error handler. This handles all PHP errors which are trapped * by the `error_reporting` ini variable. All non-suppressed errors get sent * to kjw_croak(). If you suppress the error, the error message is stored * in the $kjw_suppressed_error global. * * @param $errno An error number. * @param $errstr An error message. * @param $errfile The full path of the file in which the error occurred. * @param $errline The line in the file where the error occurred. * @param $errcontext The variables local to where the error occurred. * @note You do not call this. PHP calls this. */ function kjw_error_handler($errno, $errstr, $errfile, $errline, $errcontext) { // If error_reporting returns 0, the user suppressed the error (e.g. with '@'). if (($error_reporting = error_reporting()) != 0) { // Only do something if user error_reporting matches if ($error_reporting & $errno) { //// Check exceptions.. this is here for PHP4/5 compatibility (4 doesn't have the static keyword). */ //if (preg_match('/^Non-static method .* should not be called statically$/', $errstr)) // return; //if (preg_match('/^is_a\(\): Deprecated\./', $errstr)) // DAMMIT! // return; // Die die die, with a nice message. kjw_croak('(global)', 'Internal Server Error', "$errstr.", 1); } } else { global $kjw_suppressed_error; $kjw_suppressed_error = $errstr; } } /** @ingroup globals * * Shutdown handler to catch fatal errors. See discussion here: * http://stackoverflow.com/questions/277224/how-do-i-catch-a-php-fatal-error * * @note You do not call this. PHP calls this. */ function kjw_on_shutdown() { $error = error_get_last(); /* * > The following error types cannot be handled with a user defined * > function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, * > E_COMPILE_ERROR, E_COMPILE_WARNING [...]. * I.e. we have to catch them here. We explicitly *ignore* all other errors * here because they *were* handled by our custom error handler already * (or suppressed, in which case they would show up here causing trouble). */ if ($error !== null && in_array($error['type'], array( E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING ))) { kjw_error_handler($error['type'], $error['message'], $error['file'], $error['line'], array()); } } /** @ingroup globals * * Print a message to the resource pointed to by KJW_STDLOG. * If you want to debug SQL queries for example, fopen() a file in write mode * and assign it to the KJW_STDLOG define. Some KjwLib functions and methods * call this. It might be useful for profiling. * * @param $msg The message to print. * @param $level The importance (DEBUG, INFO, NOTICE, WARNING, ERROR). * @param $namespace The tracing object name or context. */ function kjw_trace($msg, $level = 'DEBUG', $namespace = '(global)') { if (defined('KJW_STDLOG')) fwrite(KJW_STDLOG, strftime('%y-%m-%d %H:%M:%S %z: ') . "$level: $namespace: $msg\n"); } /** @ingroup globals * * Report an error and die. This function checks php_sapi_name() to see * if we're running CLI or non-CLI (CGI) mode. In non-CLI mode we add HTML * to the error message for extra readability. * * The KJW_EMAIL_ERRORS_TO decides whether we should print the detailed message as well * or print only the short one and send the detailed message by email. * A call to trace()/kjw_trace() is also made. * * @param $namespace The class/namespace where the croaking was initiated. * @param $shortMsg An accompanying message which contains no information an * attacker might use. * @param $detailedMsg An accompanying message with all the details. * @param $backtraceShift How many functions we should pop from the stack so we don't show * the error handling itself. * @return This function does *not* return. */ function kjw_croak($namespace, $shortMsg, $detailedMsg, $backtraceShift = 0) { // User might expect the error and ignore (@-prefix). // This is not recommended though. if (error_reporting() == 0) { global $kjw_suppressed_error; $kjw_suppressed_error = $detailedMsg; return; } // What will it be? $print_html = php_sapi_name() != 'cli'; // Set message content $shortTextMsg = "** $shortMsg **\n\n" . "An error has occurred. We apologise for the inconvenience and ask you to try again later.\n" . "Most likely, the webmaster is working on fixing this error at this very moment.\n"; $shortHtmlMsg = "
" . htmlentities($shortMsg) . "
\n" . "An error has occurred. We apologise for the inconvenience and ask you to try again later.
\n" . "Most likely, the webmaster is working on fixing this error at this very moment.

\n" . "Go back to home if refreshing this page does not help.\n"; // Run htmlentities over all, regardless $details = array(); $details[] = "Date: " . strftime('%Y-%m-%d %H:%M:%S.') . sprintf('%06d', (int)substr(microtime(), 2, 6)); $details[] = "Detailed message:\n " . htmlentities($detailedMsg); $details[] = "Croak namespace/object: [" . htmlentities($namespace) . "]"; if (isset($_SERVER['REMOTE_ADDR'])) $details[] = "Caller IP: [" . htmlentities($_SERVER['REMOTE_ADDR']) . "]"; if (isset($_SERVER['HTTP_HOST']) && isset($_SERVER['REQUEST_URI'])) { $proto = (kjw_array_get($_SERVER, 'HTTPS') == 'on' ? 'https' : 'http'); $details[] = "Viewed URL: " . htmlentities("$proto://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]") . " ($_SERVER[REQUEST_METHOD])"; } if (isset($_SERVER['HTTP_REFERER'])) $details[] = "Referrer URL: " . htmlentities($_SERVER['HTTP_REFERER']); if (isset($_SERVER['HTTP_USER_AGENT'])) $details[] = "User-Agent: " . htmlentities($_SERVER['HTTP_USER_AGENT']); // The backtrace $backtrace = ''; $back = debug_backtrace(); while ($backtraceShift--) array_shift($back); foreach($back as $k => $v) { $backtrace .= sprintf(" %-20s %s()\n", htmlentities(basename(kjw_array_get($v, 'file'))) . ':' . htmlentities(kjw_array_get($v, 'line')), htmlentities(kjw_array_get($v, 'function'))); } // Client data (not secret) $cookie = serialize($_COOKIE); $post = serialize($_POST); // Session data (secret!) if (isset($_SESSION)) $session = serialize($_SESSION); else $session = ''; $server = serialize($_SERVER); // Send the message by mail if appropriate if (KJW_EMAIL_ERRORS_TO != '') { require_once(dirname(__FILE__) . '/KjwMail.php'); kjwmail_sendplain( KJW_EMAIL_ERRORS_TO, 'KjwLib::kjw_croak: ' . substr($shortMsg, 0, 40), $shortTextMsg . "\n[DETAILS]\n * " . implode("\n * ", $details) . "\n\n[BACKTRACE]\n$backtrace\n" . "[POST]\n $post\n\n[COOKIE]\n $cookie\n\n[SESSION]\n $session\n\n[SERVER]\n $server\n\n" ); } // Add the message to a trace log kjw_trace($shortMsg, 'ERROR', $namespace); kjw_trace("DETAILS\n * " . implode("\n * ", $details), 'DEBUG', $namespace); kjw_trace("BACKTRACE\n$backtrace", 'DEBUG', $namespace); kjw_trace("POST = $post", 'DEBUG', $namespace); kjw_trace("COOKIE = $cookie", 'DEBUG', $namespace); kjw_trace("SESSION = $session", 'DEBUG', $namespace); kjw_trace("SERVER = $server", 'DEBUG', $namespace); // Display the message if (!$print_html) { echo $shortTextMsg; if (KJW_EMAIL_ERRORS_TO == '') echo "\n[DETAILS]\n * ", implode("\n * ", $details), "\n\n[BACKTRACE]\n", $backtrace; } else { // Break out of all sorts of html crapzola. echo "
\n"; // Assemble total message $totalMsg = "
$shortHtmlMsg
\n"; if (KJW_EMAIL_ERRORS_TO == '') $totalMsg .= "
[DETAILS]\n * " . implode("\n * ", $details)
					. "\n\n[BACKTRACE]\n$backtrace
\n"; $totalMsg .= "
"; // Echo the message echo $totalMsg; // Echo it again, in a script so it replaces the entire body. echo ""; echo ""; } // And finally.. die(-1); } /* * Initialize constants/defines. */ if (!defined('KJW_DEBUG')) define('KJW_DEBUG', false); if (!defined('KJW_STDLOG')) // Where to write log output. if (defined('STDERR')) // If we have stderr, write there, else don't write. define('KJW_STDLOG', STDERR); if (!defined('KJW_DIE_ON_TRANSIENT_ERROR')) // KjwSql uses this when connection fails. define('KJW_DIE_ON_TRANSIENT_ERROR', false); if (!defined('KJW_EMAIL_ERRORS_TO')) // Mail the errors instead of displaying them.. define('KJW_EMAIL_ERRORS_TO', ''); /* * Initialize error handler and shutdown handler (to catch fatal errors * as well). */ set_error_handler('kjw_error_handler'); register_shutdown_function('kjw_on_shutdown'); ?>