. ======================================================================*/ /** @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 = "[DETAILS]\n * " . implode("\n * ", $details)
. "\n\n[BACKTRACE]\n$backtrace\n";
$totalMsg .= "