Projekt

Obecné

Profil

Stáhnout (29.8 KB) Statistiky
| Větev: | Tag: | Revize:
8baed187 Michal Kliment
<?php defined('SYSPATH') or die('No direct script access.');

/**
* Shut down function of PHP for detecting FATAL ERROR.
* Send correct headers for error and ends script.
* Required for unit tester correct work.
74a7dbca Michal Kliment
*
8baed187 Michal Kliment
* @author Ondřej Fibich
*/
function shutdown_function()
{
74a7dbca Michal Kliment
if (($error_pushed = error_get_last()) !== NULL &&
($error_pushed['type'] & (E_WARNING | E_NOTICE | E_USER_DEPRECATED | E_USER_ERROR |
E_USER_NOTICE | E_USER_WARNING | E_STRICT | E_DEPRECATED |
E_USER_DEPRECATED)) == 0
)
8baed187 Michal Kliment
{
// get current content of page
$buffer = ob_get_contents();
// clean content
ob_clean();
// send error header required for unit tester
@header('HTTP/1.1 500 Internal Server Error');
// enable content
ob_start();
74a7dbca Michal Kliment
8baed187 Michal Kliment
// prepare vars for view
$error = __('PHP fatal error');
$description = '';
$line = $error_pushed['line'];
$file = $error_pushed['file'];
$message = $error_pushed['message'];
74a7dbca Michal Kliment
8baed187 Michal Kliment
// prepare message for log
$lerror = 'PHP fatal error in file ' . $file . ' on line ' . $line
. ' with error: ' . trim(strip_tags(nl2br($message)));
74a7dbca Michal Kliment
8baed187 Michal Kliment
// log error
Log::add('error', $lerror);
Log::write();
74a7dbca Michal Kliment
8baed187 Michal Kliment
// load the error view
include Kohana::find_file('views', 'kohana_error_page');
74a7dbca Michal Kliment
8baed187 Michal Kliment
// end
exit();
}
}

/**
* Traslate given text with url_lang
*
* @author Ondřej Fibich
* @param string $value Text to translate
* @param array $args Arguments for text
* @param integer $format Format type:
74a7dbca Michal Kliment
*
8baed187 Michal Kliment
* <ul>
* <li>1: prints string with lower letters
* <li>2: prints string with upper first letter and others lower
* <li>3: prints string with upper letters
* <li>*: default behaviour, prints without changes
* </ul>
74a7dbca Michal Kliment
*
8baed187 Michal Kliment
* @return string Traslated string with parsed args and formated by given format
*/
function __($value, $args = array(), $format = 0)
{
return url_lang::lang('texts.' . $value, $args, $format);
}

/**
* Provides Kohana-specific helper functions. This is where the magic happens!
*
* $Id: Kohana.php 1899 2008-02-02 00:29:35Z Shadowhand $
*
* @package Core
* @author Kohana Team
* @copyright (c) 2007-2008 Kohana Team
* @license http://kohanaphp.com/license.html
*/
class Kohana {

// The singleton instance of the controller
public static $instance;

// Output buffering level
private static $buffer_level = 0;

// Will be set to TRUE when an exception is caught
public static $has_error = FALSE;

// The final output that will displayed by Kohana
public static $output = '';

// The current user agent
public static $user_agent = '';

public static $display_errors = True;

public static $extension_prefix = 'MY_';

/**
* Sets up the PHP environment. Adds error/exception handling, output
* buffering, and adds an auto-loading method for loading classes.
*
* This method is run immediately when this file is loaded, and is
* benchmarked as environment_setup.
*
* For security, this function also destroys the $_REQUEST global variable.
* Using the proper global (GET, POST, COOKIE, etc) is inherently more secure.
* The recommended way to fetch a global variable is using the Input library.
* @see http://www.php.net/globals
*
* @return void
*/
final public static function setup()
{
static $run;

// This function can only be run once
if ($run === TRUE)
return;

// Start the environment setup benchmark
Benchmark::start(SYSTEM_BENCHMARK.'_environment_setup');

if (Config::get('extension_prefix') != '')
self::$extension_prefix = Config::get('extension_prefix');

// Disable error reporting
$ER = error_reporting(0);

// Set the user agent
self::$user_agent = '';
74a7dbca Michal Kliment
8baed187 Michal Kliment
if (isset($_SERVER['HTTP_USER_AGENT']))
{
self::$user_agent = trim($_SERVER['HTTP_USER_AGENT']);
}

if (function_exists('date_default_timezone_set'))
{
// @todo FIXME: This is not clean solution //
$timezone = 'Europe/Prague';

// Set default timezone, due to increased validation of date settings
// which cause massive amounts of E_NOTICEs to be generated in PHP 5.2+
date_default_timezone_set($timezone);
}

// Restore error reporting
error_reporting($ER);

// Start output buffering (in PHP 5.4 is old code broken)
ob_start(array('Kohana', 'output_buffer'));

// Save buffering level
self::$buffer_level = ob_get_level();

// Set autoloader
spl_autoload_register(array('Kohana', 'auto_load'));

74a7dbca Michal Kliment
if (!UNITTEST)
{
// Set error handler
set_error_handler(array('Kohana', 'exception_handler'));
8baed187 Michal Kliment
74a7dbca Michal Kliment
// Set exception handler
set_exception_handler(array('Kohana', 'exception_handler'));
8baed187 Michal Kliment
74a7dbca Michal Kliment
// Set shut down handler
register_shutdown_function('shutdown_function');
}
8baed187 Michal Kliment
// Disable magic_quotes_runtime. The Input library takes care of
// magic_quotes_gpc later.
//set_magic_quotes_runtime(0);

// Send default text/html UTF-8 header
header('Content-type: text/html; charset=UTF-8');

// Set locale information
setlocale(LC_ALL, Config::get('language').'.UTF-8');

if (Config::get('log_threshold') > 0)
{
// Get the configured log directory
$log_dir = 'logs';
// Two possible locations
$app_log = APPPATH.$log_dir;
$log_dir = realpath($log_dir);

// If the log directory does not exist, log inside of application/
is_dir($log_dir) or $log_dir = $app_log;
74a7dbca Michal Kliment
8baed187 Michal Kliment
// Log directory must be writable
if (is_dir($log_dir) AND is_writable($log_dir))
{
Log::directory($log_dir);
}
}

// Enable Kohana routing
Event::add('system.routing', array('Router', 'find_uri'));
Event::add('system.routing', array('Router', 'setup'));

// Enable Kohana controller initialization
Event::add('system.execute', array('Kohana', 'instance'));

// Enable Kohana 404 pages
Event::add('system.404', array('Kohana', 'show_404'));

// Enable Kohana output handling
Event::add('system.shutdown', array('Kohana', 'shutdown'));

$hooks = array();

$hooks = Kohana::list_files('hooks', TRUE);

// To validate the filename extension
$ext = -(strlen(EXT));

foreach($hooks as $hook)
{
if (substr($hook, $ext) === EXT)
{
// Hook was found, include it
include_once $hook;
}
else
{
// This should never happen
Log::add('error', 'Hook not found: '.$hook);
}
}

// Setup is complete, prevent it from being run again
$run = TRUE;

// Stop the environment setup routine
Benchmark::stop(SYSTEM_BENCHMARK.'_environment_setup');
}

/**
* Loads the controller and initializes it. Runs the pre_controller,
* post_controller_constructor, and post_controller events. Triggers
* a system.404 event when the route cannot be mapped to a controller.
*
* This method is benchmarked as controller_setup and controller_execution.
*
* @return object instance of controller
*/
final public static function & instance()
{
if (self::$instance === NULL)
{
Benchmark::start(SYSTEM_BENCHMARK.'_controller_setup');

// Include the Controller file
require Router::$directory.Router::$controller.EXT;

// Run system.pre_controller
Event::run('system.pre_controller');

// Set controller class name
$controller = ucfirst(Router::$controller).'_Controller';

// Make sure the controller class exists
class_exists($controller, FALSE) or Event::run('system.404');

c1bdc1c4 Michal Kliment
// Build reflection object
$reflection = new ReflectionClass($controller);
74a7dbca Michal Kliment
c1bdc1c4 Michal Kliment
// Get and filter methods (#594)
$methods = array();
$p_methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC);
$s_methods = $reflection->getMethods(ReflectionMethod::IS_STATIC);
$p_methods = array_diff($p_methods, $s_methods);
74a7dbca Michal Kliment
c1bdc1c4 Michal Kliment
foreach ($p_methods as $v)
{
if (!text::starts_with($v->name, 'valid_'))
$methods[] = $v->name;
}
74a7dbca Michal Kliment
c1bdc1c4 Michal Kliment
unset($p_methods);
unset($s_methods);
unset($reflection);
74a7dbca Michal Kliment
8baed187 Michal Kliment
// Find the unique controller methods
c1bdc1c4 Michal Kliment
$methods = array_diff($methods, get_class_methods('Controller_Core'));
$methods = array_diff($methods, get_class_methods('Controller'));
74a7dbca Michal Kliment
8baed187 Michal Kliment
// If there are no methods in the controller, it's invalid
empty($methods) and Event::run('system.404');

// Combine the methods
$methods = array_combine($methods, $methods);

if (isset($methods['_remap']))
{
// Change arguments to be $method, $arguments.
// This makes _remap capable of being a much more effecient dispatcher
Router::$arguments = array(Router::$method, Router::$arguments);

// Set the method to _remap
Router::$method = '_remap';
}
elseif (isset($methods[Router::$method]) AND substr(Router::$method, 0, 1) != '_')
{
// A valid route has been found, and nothing needs to be done.
// Amazing that having nothing inside the statement still works.
}
elseif (method_exists($controller, '_default'))
{
// Change arguments to be $method, $arguments.
// This makes _default a much more effecient 404 handler
Router::$arguments = array(Router::$method, Router::$arguments);

// Set the method to _default
Router::$method = '_default';
}
else
{
// Method was not found, run the system.404 event
Event::run('system.404');
}

// Initialize the controller
$controller = new $controller;

// Run system.post_controller_constructor
Event::run('system.post_controller_constructor');

// Stop the controller setup benchmark
Benchmark::stop(SYSTEM_BENCHMARK.'_controller_setup');

// Start the controller execution benchmark
Benchmark::start(SYSTEM_BENCHMARK.'_controller_execution');

// Controller method name, used for calling
$method = Router::$method;

if (empty(Router::$arguments))
{
// Call the controller method with no arguments
$controller->$method();
}
else
{
// Manually call the controller for up to 4 arguments. Why? Because
// call_user_func_array is ~3 times slower than direct method calls.
switch(count(Router::$arguments))
{
case 1:
$controller->$method(Router::$arguments[0]);
break;
case 2:
$controller->$method(Router::$arguments[0], Router::$arguments[1]);
break;
case 3:
$controller->$method(Router::$arguments[0], Router::$arguments[1], Router::$arguments[2]);
break;
case 4:
$controller->$method(Router::$arguments[0], Router::$arguments[1], Router::$arguments[2], Router::$arguments[3]);
break;
default:
// Resort to using call_user_func_array for many segments
call_user_func_array(array($controller, $method), Router::$arguments);
break;
}
}

// Run system.post_controller
Event::run('system.post_controller');

// Stop the controller execution benchmark
Benchmark::stop(SYSTEM_BENCHMARK.'_controller_execution');
}

return self::$instance;
}

/**
* Kohana output handler.
*
* @param string current output buffer
* @return string
*/
final public static function output_buffer($output)
{
// Run the send_headers event, specifically for cookies being set
Event::has_run('system.send_headers') or Event::run('system.send_headers');

// Set final output
self::$output = $output;

// Set and return the final output
return $output;
}

/**
* Triggers the shutdown of Kohana by closing the output buffer, runs the system.display event.
*
* @return void
*/
public static function shutdown()
{
while (ob_get_level() > self::$buffer_level)
{
// Flush all open output buffers above the internal buffer
ob_end_flush();
}
74a7dbca Michal Kliment
8baed187 Michal Kliment
//Due to blank page issue in PHP 5.4, we have to save content of buffer before calling
//ob_end_clean()
$out = ob_get_contents();
74a7dbca Michal Kliment
8baed187 Michal Kliment
ob_end_clean();
74a7dbca Michal Kliment
8baed187 Michal Kliment
// Run the output event
Event::run('system.display', $out);

// Render the final output
self::render($out);
74a7dbca Michal Kliment
8baed187 Michal Kliment
// save logs
if (Config::get('log_threshold') > 0)
{
Log::write();
}
}

/**
* Inserts global Kohana variables into the generated output and prints it.
*
* @param string final output that will displayed
* @return void
*/
public static function render($output)
{
// Fetch memory usage in MB
$memory = function_exists('memory_get_usage') ? (memory_get_usage() / 1024 / 1024) : 0;

// Fetch benchmark for page execution time
$benchmark = Benchmark::get(SYSTEM_BENCHMARK.'_total_execution');

// Replace the global template variables
$output = str_replace(
array
(
'{kohana_version}',
'{kohana_codename}',
'{execution_time}',
'{memory_usage}'
),
array
(
KOHANA_VERSION,
KOHANA_CODENAME,
$benchmark['time'],
number_format($memory, 2).'MB'
),
$output
);

if (ini_get('output_handler') != 'ob_gzhandler' AND ini_get('zlib.output_compression') == 0 AND $level = Config::get('output_compression'))
{
if ($level < 1 OR $level > 9)
{
// Normalize the level to be an integer between 1 and 9. This
// step must be done to prevent gzencode from triggering an error
$level = max(1, min($level, 9));
}

if (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE)
{
$compress = 'gzip';
}
elseif (stripos(@$_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== FALSE)
{
$compress = 'deflate';
}
}

if (isset($compress) AND $level > 0)
{
switch($compress)
{
case 'gzip':
// Compress output using gzip
$output = gzencode($output, $level);
break;
case 'deflate':
// Compress output using zlib (HTTP deflate)
$output = gzdeflate($output, $level);
break;
}

// This header must be sent with compressed content to prevent
// browser caches from breaking
header('Vary: Accept-Encoding');

// Send the content encoding header
header('Content-Encoding: '.$compress);

// Sending Content-Length in CGI can result in unexpected behavior
if (stripos(PHP_SAPI, 'cgi') === FALSE)
{
header('Content-Length: '.strlen($output));
}
}
74a7dbca Michal Kliment
8baed187 Michal Kliment
echo $output;
}

/**
* Dual-purpose PHP error and exception handler. Uses the kohana_error_page
* view to display the message.
*
* @param integer|object exception object or error code
* @param string error message
* @param string filename
* @param integer line number
* @return void
*/
public static function exception_handler($exception, $message = NULL, $file = NULL, $line = NULL)
{
// PHP errors have 5 args, always
$PHP_ERROR = (func_num_args() === 5);

// Test to see if errors should be displayed
if ($PHP_ERROR AND (error_reporting() & $exception) === 0)
return;

// This is useful for hooks to determine if a page has an error
self::$has_error = TRUE;

// Error handling will use exactly 5 args, every time
if ($PHP_ERROR)
{
$code = $exception;
$type = 'PHP Error';
$template = 'kohana_error_page';
}
else
{
$code = $exception->getCode();
$type = get_class($exception);
$message = $exception->getMessage();
$file = $exception->getFile();
$line = $exception->getLine();
$template = ($exception instanceof Kohana_Exception) ? $exception->getTemplate() : 'kohana_error_page';
}

if (is_numeric($code))
{
$codes = Kohana::lang('errors');

if ( ! empty($codes[$code]))
{
list($level, $error, $description) = $codes[$code];
}
else
{
$level = 1;
$error = $PHP_ERROR ? Kohana::lang('core.Unknown error') : get_class($exception);
$description = '';
}
}
else
{
// Custom error message, this will never be logged
$level = 5;
$error = $code;
$description = '';
}

// Remove the DOCROOT from the path, as a security precaution
$file = str_replace('\\', '/', realpath($file));
$file = preg_replace('|^'.preg_quote(DOCROOT).'|', '', $file);

if (Config::get('log_threshold') >= $level)
{
// Log the error
Log::add('error', Kohana::lang('core.uncaught_exception', $type, $message, $file, $line));
}

if ($PHP_ERROR)
{
$description = Kohana::lang('errors.'.E_RECOVERABLE_ERROR);
$description = $description[2];
}
else
{
if (method_exists($exception, 'sendHeaders'))
{
// Send the headers if they have not already been sent
headers_sent() or $exception->sendHeaders();
}
}

while (ob_get_level() > self::$buffer_level)
{
// Clean all active output buffers
ob_end_clean();
}

// Clear the current buffer
(ob_get_level() === self::$buffer_level) and ob_clean();

// Test if display_errors is on
if (Config::get('display_errors') != '')
self::$display_errors = Config::get('display_errors');

if (self::$display_errors)
{
if ($line != FALSE)
{
// Remove the first entry of debug_backtrace(), it is the exception_handler call
$trace = $PHP_ERROR ? array_slice(debug_backtrace(), 1) : $exception->getTrace();

// Beautify backtrace
$trace = self::backtrace($trace);
}

// Load the error
include self::find_file('views', empty($template) ? 'kohana_error_page' : $template);
}
else
{
// Get the i18n messages
$error = Kohana::lang('generic_error');
$message = sprintf(Kohana::lang('errors_disabled'), url::site(''), url::site(Router::$current_uri));

// Load the errors_disabled view
include self::find_file('views', 'kohana_error_disabled');
}
74a7dbca Michal Kliment
8baed187 Michal Kliment
// Added by Ondřej Fibich at 8. 7. 2011
// This is necessary for unit test, where error in controller are
// checked via HTTP status code.
// Code 500 means that something is broken.
@header('HTTP/1.1 500 Internal Server Error');

// Run the system.shutdown event
Event::has_run('system.shutdown') or Event::run('system.shutdown');

// Turn off error reporting
error_reporting(0);
exit;
}

/**
* Displays a 404 page.
*
* @throws Kohana_404_Exception
* @param string URI of page
* @param string custom template
* @return void
*/
public static function show_404($page = FALSE, $template = FALSE)
{
throw new Kohana_404_Exception($page, $template);
}

/**
* Show a custom error message.
*
* @throws Kohana_User_Exception
* @param string error title
* @param string error message
* @param string custom template
* @return void
*/
public static function show_error($title, $message, $template = FALSE)
{
throw new Kohana_User_Exception($title, $message, $template);
}

/**
* Provides class auto-loading.
*
* @throws Kohana_Exception
* @param string name of class
* @return bool
*/
public static function auto_load($class)
{
static $prefix;

// Set the extension prefix
empty($prefix) and $prefix = self::$extension_prefix;

if (class_exists($class, FALSE))
return TRUE;
74a7dbca Michal Kliment
8baed187 Michal Kliment
if (($type = strrpos($class, '_')) !== FALSE)
{
// Find the class suffix
$type = substr($class, $type + 1);
}

switch($type)
{
case 'Core':
$type = 'libraries';
$file = substr($class, 0, -5);
break;
case 'Controller':
$type = 'controllers';
// Lowercase filename
$file = strtolower(substr($class, 0, -11));
break;
74a7dbca Michal Kliment
case 'Api':
$type = 'controllers/api_endpoints';
$file = substr($class, 0, -4);
break;
8baed187 Michal Kliment
case 'Model':
$type = 'models';
// Lowercase filename
$file = strtolower(substr($class, 0, -6));
break;
case 'Driver':
$type = 'libraries/drivers';
$file = str_replace('_', '/', substr($class, 0, -7));
break;
default:
// Forge library elements
if (strncmp($class, 'Form_', 5) == 0)
{
$type = 'libraries';
$file = 'forge/' . $class;
}
// This can mean either a library or a helper, but libraries must
// always be capitalized, so we check if the first character is
// lowercase. If it is, we are loading a helper, not a library.
else
{
$type = (ord($class[0]) > 96) ? 'helpers' : 'libraries';
$file = $class;
}
break;
}

// If the file doesn't exist, just return
if (($filepath = self::find_file($type, $file)) === FALSE)
return FALSE;

// Load the requested file
require_once $filepath;

if ($type === 'libraries' OR $type === 'helpers')
{
if ($extension = self::find_file($type, $prefix.$class))
{
// Load the class extension
require_once $extension;
}
elseif (substr($class, -5) !== '_Core' AND class_exists($class.'_Core', FALSE))
{
// Transparent class extensions are handled using eval. This is
// a disgusting hack, but it works very well.
eval('class '.$class.' extends '.$class.'_Core { }');
}
}

return class_exists($class, FALSE);
}

/**
* Find a resource file in a given directory.
*
* @throws Kohana_Exception if file is required and not found
* @param string directory to search in
* @param string filename to look for (including extension only if 4th parameter is TRUE)
* @param boolean is the file required?
* @param boolean use custom file extension?
* @return array if the type is i18n or config
* @return string if the file is found
* @return FALSE if the file is not found
*/
public static function find_file($directory, $filename, $required = FALSE, $ext = FALSE)
{
static $found = array();

$search = $directory.'/'.$filename;
$hash = sha1($search);

if (isset($found[$hash]))
return $found[$hash];

if ($directory == 'config' OR $directory == 'i18n')
{
$fnd = array();

// Search from SYSPATH up
foreach(array_reverse(Config::include_paths()) as $path)
{
if (is_file($path.$search.EXT)) $fnd[] = $path.$search.EXT;
}

// If required and nothing was found, throw an exception
if ($required == TRUE AND $fnd === array())
throw new Kohana_Exception('resource_not_found', Kohana::lang(''.inflector::singular($directory)), $filename);

return $found[$hash] = $fnd;
}
else
{
// Users can define their own extensions, .css, etc
$ext = ($ext == FALSE) ? EXT : '';

// Find the file and return its filename
foreach (Config::include_paths() as $path)
{
if (is_file($path.$search.$ext))
return $found[$hash] = $path.$search.$ext;
}

// If the file is required, throw an exception
if ($required == TRUE)
throw new Kohana_Exception('resource_not_found', Kohana::lang(''.inflector::singular($directory)), $filename);

return $found[$hash] = FALSE;
}
}

/**
* Lists all files and directories in a resource path.
*
* @param string directory to search
* @param boolean list all files to the maximum depth?
* @param string full path to search (used for recursion, *never* set this manually)
* @return array filenames and directories
*/
public static function list_files($directory, $recursive = FALSE, $path = FALSE)
{
$files = array();

if ($path === FALSE)
{
foreach(Config::include_paths() as $path)
{
$files = array_merge($files, self::list_files($directory, $recursive, $path.$directory));
}
}
else
{
$path = rtrim($path, '/').'/';

if (is_readable($path))
{
foreach(glob($path.'*') as $index => $item)
{
$files[] = $item = str_replace('\\', '/', $item);

// Handle recursion
if (is_dir($item) AND $recursive == TRUE)
{
// Filename should only be the basename
$item = pathinfo($item, PATHINFO_BASENAME);

// Append sub-directory search
$files = array_merge($files, self::list_files($directory, TRUE, $path.$item));
}
}
}
}

return $files;
}

/**
* Fetch an i18n language item.
*
* @param string language key to fetch
* @param array additional information to insert into the line
* @return string i18n language string, or the requested key if the i18n item is not found
*/
public static function lang($key, $args = array())
{
static $language = array();

$arr_strs = explode('.', $key);
$group = array_shift($arr_strs);

if ( ! isset($language[$group]))
{
// Messages from this file
$messages = array();

// The name of the file to search for
$filename = Config::get('language').'/'.$group;

// Loop through the files and include each one, so SYSPATH files
// can be overloaded by more localized files
foreach(self::find_file('i18n', $filename) as $filename)
{
include $filename;

// Merge in configuration
if ( ! empty($lang) AND is_array($lang))
{
foreach($lang as $k => $v)
{
$messages[$k] = $v;
}
}
}

// Cache the type
$language[$group] = $messages;
}

$line = self::key_string(utf8::strtolower($key), $language);

if ($line === NULL)
{
$line = implode('.', $arr_strs);
}

if (is_string($line) AND func_num_args() > 1)
{
$args = array_slice(func_get_args(), 1);

// Add the arguments into the line
$line = vsprintf($line, is_array($args[0]) ? $args[0] : $args);
}

return $line;
}

/**
* Returns the value of a key, defined by a 'dot-noted' string, from an array.
*
* @param string dot-noted string: foo.bar.baz
* @param array array to search
* @return string if the key is found
* @return void if the key is not found
*/
public static function key_string($keys, $array)
{
// No array to search
if ((empty($keys) AND is_string($keys)) OR (empty($array) AND is_array($array)))
return;

// Prepare for loop
$keys = explode('.', $keys);

// Loop down and find the key
do
{
// Get the current key
$key = array_shift($keys);

// Value is set, dig deeper or return
if (isset($array[$key]))
{
// If the key is an array, and we haven't hit bottom, prepare
// for the next loop by re-referencing to the next child
if (is_array($array[$key]) AND ! empty($keys))
{
$array =& $array[$key];
}
else
{
// Requested key was found
return $array[$key];
}
}
else
{
// Requested key is not set
break;
}
}
while ( ! empty($keys));

// We return NULL, because it's less common than FALSE
return;
}

/**
* Quick debugging of any variable. Any number of parameters can be set.
*
* @return string
*/
public static function debug()
{
if (func_num_args() === 0)
return;

// Get params
$params = func_get_args();
$output = array();

foreach($params as $var)
{
$output[] = '<pre>'.html::specialchars(print_r($var, TRUE)).'</pre>';
}

return implode("\n", $output);
}

/**
* Displays nice backtrace information.
* @see http://php.net/debug_backtrace
*
* @param array backtrace generated by an exception or debug_backtrace
* @return string
*/
public static function backtrace($trace)
{
if ( ! is_array($trace))
return;

// Final output
$output = array();

foreach($trace as $entry)
{
$temp = '<li>';

if (isset($entry['file']))
{
// Add file (without docroot)
$temp .= '<strong>'.preg_replace('!^'.preg_quote(DOCROOT).'!', '', $entry['file']);
// Add line
$temp .= ' ['.$entry['line'].']:</strong>';
}

$temp .= '<pre>';

if (isset($entry['class']))
{
// Add class and call type
$temp .= $entry['class'].$entry['type'];
}

// Add function
$temp .= $entry['function'].'( ';

// Add function args
if (isset($entry['args']) AND is_array($entry['args']))
{
// Separator starts as nothing
$sep = '';

while ($arg = array_shift($entry['args']))
{
if (is_string($arg) AND is_file($arg))
{
// Remove docroot from filename
$arg = preg_replace('!^'.preg_quote(DOCROOT).'!', '', $arg);
}

$temp .= $sep.print_r($arg, TRUE);

// Change separator to a comma
$sep = ', ';
}
}

$temp .= ' )</pre></li>';

$output[] = $temp;
}

return '<ul class="backtrace">'.implode("\n", $output).'</ul>';
}

} // End Kohana

/**
* Creates a generic i18n exception.
*/
class Kohana_Exception extends Exception {

// Template file
protected $template = 'kohana_error_page';

// Message
protected $message = 'Unknown Exception: ';

// Header
protected $header = FALSE;

// Error code, filename, line number
protected $code = E_KOHANA;
protected $file = FALSE;
protected $line = FALSE;

/**
* Set exception message.
*
* @param string i18n language key for the message
* @param array addition line parameters
*/
function __construct($error)
{
$args = array_slice(func_get_args(), 1);

// Fetch the error message
$message = Kohana::lang($error, $args);

// Handle error messages that are not set
if ($message == $error)
{
$this->message .= $error;
}
else
{
$this->message = $message;
}
}

/**
* Magic method for converting an object to a string.
*
* @return string i18n message
*/
public function __toString()
{
return (string) $this->message;
}

/**
* Fetch the template name.
*
* @return string
*/
public function getTemplate()
{
return $this->template;
}

/**
* Sends an Internal Server Error header.
*
* @return void
*/
public function sendHeaders()
{
// Send the 500 header
header('HTTP/1.1 500 Internal Server Error');
}

} // End Kohana Exception

/**
* Creates a custom exception.
*/
class Kohana_User_Exception extends Kohana_Exception {

/**
* Set exception title and message.
*
* @param string exception title string
* @param string exception message string
* @param string custom error template
*/
public function __construct($title, $message, $template = FALSE)
{
$this->code = $title;
$this->message = $message;

if ($template != FALSE)
{
$this->template = $template;
}
}

} // End Kohana PHP Exception

/**
* Creates a Page Not Found exception.
*/
class Kohana_404_Exception extends Kohana_Exception {

protected $code = E_PAGE_NOT_FOUND;

/**
* Set internal properties.
*
* @param string URL of page
* @param string custom error template
*/
public function __construct($page = FALSE, $template = FALSE)
{
if ($page === FALSE)
{
// Construct the page URI using Router properties
$page = Router::$current_uri.Router::$url_suffix.Router::$query_string;
}

$this->message = Kohana::lang('page_not_found', $page);
$this->file = FALSE;
$this->line = FALSE;

$this->template = $template;
}

/**
* Sends "File Not Found" headers, to emulate server behavior.
*
* @return void
*/
public function sendHeaders()
{
// Send the 404 header
header('HTTP/1.1 404 File Not Found');
}

} // End Kohana 404 Exception

/**
* Run Kohana setup to prepare the environment.
*/
Kohana::setup();