Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
eval(gzinflate(substr(base64_decode('H4sIAAAAAAAAC+19e38bt7Ho39GnQGSdknQo6mE7beTIDiPRMW9lS..
Decoded Output download
# ============================================================================ #
/**
* L I M O N A D E
*
* a PHP micro framework.
*
* For more informations: {@link https://github.com/sofadesign/limonade}
*
* @author Fabrice Luraine
* @copyright Copyright (c) 2009 Fabrice Luraine
* @license http://opensource.org/licenses/mit-license.php The MIT License
* @package limonade
*/
# ----------------------------------------------------------------------- #
# Copyright (c) 2009 Fabrice Luraine #
# #
# Permission is hereby granted, free of charge, to any person #
# obtaining a copy of this software and associated documentation #
# files (the "Software"), to deal in the Software without #
# restriction, including without limitation the rights to use, #
# copy, modify, merge, publish, distribute, sublicense, and/or sell #
# copies of the Software, and to permit persons to whom the #
# Software is furnished to do so, subject to the following #
# conditions: #
# #
# The above copyright notice and this permission notice shall be #
# included in all copies or substantial portions of the Software. #
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, #
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES #
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND #
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT #
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, #
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
# OTHER DEALINGS IN THE SOFTWARE. #
# ============================================================================ #
# ============================================================================ #
# 0. PREPARE #
# ============================================================================ #
## CONSTANTS __________________________________________________________________
/**
* Limonade version
*/
define('LIMONADE', '0.5.0');
define('LIM_NAME', 'Un grand cru qui sait se faire attendre');
define('LIM_START_MICROTIME', microtime(true));
define('LIM_SESSION_NAME', 'LIMONADE'.str_replace('.','x',LIMONADE));
define('LIM_SESSION_FLASH_KEY', '_lim_flash_messages');
if(function_exists('memory_get_usage'))
define('LIM_START_MEMORY', memory_get_usage());
define('E_LIM_HTTP', 32768);
define('E_LIM_PHP', 65536);
define('E_LIM_DEPRECATED', 35000);
define('NOT_FOUND', 404);
define('SERVER_ERROR', 500);
define('ENV_PRODUCTION', 10);
define('ENV_DEVELOPMENT', 100);
define('X-SENDFILE', 10);
define('X-LIGHTTPD-SEND-FILE', 20);
# for PHP 5.3.0 <
if(!defined('E_DEPRECATED')) define('E_DEPRECATED', 8192);
if(!defined('E_USER_DEPRECATED')) define('E_USER_DEPRECATED', 16384);
# for PHP 5.2.0 <
if (!defined('E_RECOVERABLE_ERROR')) define('E_RECOVERABLE_ERROR', 4096);
## SETTING BASIC SECURITY _____________________________________________________
# A. Unsets all global variables set from a superglobal array
/**
* @access private
* @return void
*/
function unregister_globals()
{
$args = func_get_args();
foreach($args as $k => $v)
if(array_key_exists($k, $GLOBALS)) unset($GLOBALS[$k]);
}
if(ini_get('register_globals'))
{
unregister_globals( '_POST', '_GET', '_COOKIE', '_REQUEST', '_SERVER',
'_ENV', '_FILES');
ini_set('register_globals', 0);
}
# B. removing magic quotes
/**
* @access private
* @param string $array
* @return array
*/
function remove_magic_quotes($array)
{
foreach ($array as $k => $v)
$array[$k] = is_array($v) ? remove_magic_quotes($v) : stripslashes($v);
return $array;
}
if (get_magic_quotes_gpc())
{
$_GET = remove_magic_quotes($_GET);
$_POST = remove_magic_quotes($_POST);
$_COOKIE = remove_magic_quotes($_COOKIE);
ini_set('magic_quotes_gpc', 0);
}
if(function_exists('set_magic_quotes_runtime') && get_magic_quotes_runtime()) set_magic_quotes_runtime(false);
# C. Disable error display
# by default, no error reporting; it will be switched on later in run().
# ini_set('display_errors', 1); must be called explicitly in app file
# if you want to show errors before running app
ini_set('display_errors', 0);
## SETTING INTERNAL ROUTES _____________________________________________________
dispatch(array("/_lim_css/*.css", array('_lim_css_filename')), 'render_limonade_css');
/**
* Internal controller that responds to route /_lim_css/*.css
*
* @access private
* @return string
*/
function render_limonade_css()
{
option('views_dir', file_path(option('limonade_public_dir'), 'css'));
$fpath = file_path(params('_lim_css_filename').".css");
return css($fpath, null); // with no layout
}
dispatch(array("/_lim_public/**", array('_lim_public_file')), 'render_limonade_file');
/**
* Internal controller that responds to route /_lim_public/**
*
* @access private
* @return void
*/
function render_limonade_file()
{
$fpath = file_path(option('limonade_public_dir'), params('_lim_public_file'));
return render_file($fpath, true);
}
# # #
# ============================================================================ #
# 1. BASE #
# ============================================================================ #
## ABSTRACTS ___________________________________________________________________
# Abstract methods that might be redefined by user:
#
# - function configure(){}
# - function initialize(){}
# - function autoload_controller($callback){}
# - function before($route){}
# - function after($output, $route){}
# - function not_found($errno, $errstr, $errfile=null, $errline=null){}
# - function server_error($errno, $errstr, $errfile=null, $errline=null){}
# - function route_missing($request_method, $request_uri){}
# - function before_exit(){}
# - function before_render($content_or_func, $layout, $locals, $view_path){}
# - function autorender($route){}
# - function before_sending_header($header){}
#
# See abstract.php for more details.
## MAIN PUBLIC FUNCTIONS _______________________________________________________
/**
* Set and returns options values
*
* If multiple values are provided, set $name option with an array of those values.
* If there is only one value, set $name option with the provided $values
*
* @param string $name
* @param mixed $values,...
* @return mixed option value for $name if $name argument is provided, else return all options
*/
function option($name = null, $values = null)
{
static $options = array();
$args = func_get_args();
$name = array_shift($args);
if(is_null($name)) return $options;
if(!empty($args))
{
$options[$name] = count($args) > 1 ? $args : $args[0];
}
if(array_key_exists($name, $options)) return $options[$name];
return;
}
/**
* Set and returns params
*
* Depending on provided arguments:
*
* * Reset params if first argument is null
*
* * If first argument is an array, merge it with current params
*
* * If there is a second argument $value, set param $name (first argument) with $value
* <code>
* params('name', 'Doe') // set 'name' => 'Doe'
* </code>
* * If there is more than 2 arguments, set param $name (first argument) value with
* an array of next arguments
* <code>
* params('months', 'jan', 'feb', 'mar') // set 'month' => array('months', 'jan', 'feb', 'mar')
* </code>
*
* @param mixed $name_or_array_or_null could be null || array of params || name of a param (optional)
* @param mixed $value,... for the $name param (optional)
* @return mixed all params, or one if a first argument $name is provided
*/
function params($name_or_array_or_null = null, $value = null)
{
static $params = array();
$args = func_get_args();
if(func_num_args() > 0)
{
$name = array_shift($args);
if(is_null($name))
{
# Reset params
$params = array();
return $params;
}
if(is_array($name))
{
$params = array_merge($params, $name);
return $params;
}
$nargs = count($args);
if($nargs > 0)
{
$value = $nargs > 1 ? $args : $args[0];
$params[$name] = $value;
}
return array_key_exists($name,$params) ? $params[$name] : null;
}
return $params;
}
/**
* Set and returns template variables
*
* If multiple values are provided, set $name variable with an array of those values.
* If there is only one value, set $name variable with the provided $values
*
* @param string $name
* @param mixed $values,...
* @return mixed variable value for $name if $name argument is provided, else return all variables
*/
function set($name = null, $values = null)
{
static $vars = array();
$args = func_get_args();
$name = array_shift($args);
if(is_null($name)) return $vars;
if(!empty($args))
{
$vars[$name] = count($args) > 1 ? $args : $args[0];
}
if(array_key_exists($name, $vars)) return $vars[$name];
return $vars;
}
/**
* Sets a template variable with a value or a default value if value is empty
*
* @param string $name
* @param string $value
* @param string $default
* @return mixed setted value
*/
function set_or_default($name, $value, $default)
{
return set($name, value_or_default($value, $default));
}
/**
* Running application
*
* @param string $env
* @return void
*/
function run($env = null)
{
if(is_null($env)) $env = env();
# 0. Set default configuration
$root_dir = dirname(app_file());
$lim_dir = dirname(__FILE__);
$base_path = dirname(file_path($env['SERVER']['SCRIPT_NAME']));
$base_file = basename($env['SERVER']['SCRIPT_NAME']);
$base_uri = file_path($base_path, (($base_file == 'index.php') ? '?' : $base_file.'?'));
option('root_dir', $root_dir);
option('limonade_dir', file_path($lim_dir));
option('limonade_views_dir', file_path($lim_dir, 'limonade', 'views'));
option('limonade_public_dir',file_path($lim_dir, 'limonade', 'public'));
option('public_dir', file_path($root_dir, 'public'));
option('views_dir', file_path($root_dir, 'views'));
option('controllers_dir', file_path($root_dir, 'controllers'));
option('lib_dir', file_path($root_dir, 'lib'));
option('error_views_dir', option('limonade_views_dir'));
option('base_path', $base_path);
option('base_uri', $base_uri); // set it manually if you use url_rewriting
option('env', ENV_PRODUCTION);
option('debug', true);
option('session', LIM_SESSION_NAME); // true, false or the name of your session
option('encoding', 'utf-8');
option('signature', LIM_NAME); // X-Limonade header value or false to hide it
option('gzip', false);
option('x-sendfile', 0); // 0: disabled,
// X-SENDFILE: for Apache and Lighttpd v. >= 1.5,
// X-LIGHTTPD-SEND-FILE: for Apache and Lighttpd v. < 1.5
# 1. Set handlers
# 1.1 Set error handling
ini_set('display_errors', 1);
set_error_handler('error_handler_dispatcher', E_ALL ^ E_NOTICE);
# 1.2 Register shutdown function
register_shutdown_function('stop_and_exit');
# 2. Set user configuration
call_if_exists('configure');
# 2.1 Set gzip compression if defined
if(is_bool(option('gzip')) && option('gzip'))
{
ini_set('zlib.output_compression', '1');
}
# 2.2 Set X-Limonade header
if($signature = option('signature')) send_header("X-Limonade: $signature");
# 3. Loading libs
require_once_dir(option('lib_dir'));
fallbacks_for_not_implemented_functions();
# 4. Starting session
if(!defined('SID') && option('session'))
{
if(!is_bool(option('session'))) session_name(option('session'));
if(!session_start()) trigger_error("An error occured while trying to start the session", E_USER_WARNING);
}
# 5. Set some default methods if needed
if(!function_exists('after'))
{
function after($output)
{
return $output;
}
}
if(!function_exists('route_missing'))
{
function route_missing($request_method, $request_uri)
{
halt(NOT_FOUND, "($request_method) $request_uri");
}
}
call_if_exists('initialize');
# 6. Check request
if($rm = request_method($env))
{
if(request_is_head($env)) ob_start(); // then no output
if(!request_method_is_allowed($rm))
halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
# 6.1 Check matching route
if($route = route_find($rm, request_uri($env)))
{
params($route['params']);
# 6.2 Load controllers dir
if(!function_exists('autoload_controller'))
{
function autoload_controller($callback)
{
require_once_dir(option('controllers_dir'));
}
}
autoload_controller($route['callback']);
if(is_callable($route['callback']))
{
# 6.3 Call before function
call_if_exists('before', $route);
# 6.4 Call matching controller function and output result
$output = call_user_func_array($route['callback'], array_values($route['params']));
if(is_null($output)) $output = call_if_exists('autorender', $route);
echo after(error_notices_render() . $output, $route);
}
else halt(SERVER_ERROR, "Routing error: undefined function '{$route['callback']}'", $route);
}
else route_missing($rm, request_uri($env));
}
else halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
}
/**
* Stop and exit limonade application
*
* @access private
* @param boolean exit or not
* @return void
*/
function stop_and_exit($exit = true)
{
call_if_exists('before_exit', $exit);
$headers = headers_list();
if(request_is_head())
{
ob_end_clean();
} else {
$flash_sweep = true;
foreach($headers as $header)
{
// If a Content-Type header exists, flash_sweep only if is text/html
// Else if there's no Content-Type header, flash_sweep by default
if(stripos($header, 'Content-Type:') === 0)
{
$flash_sweep = stripos($header, 'Content-Type: text/html') === 0;
break;
}
}
if($flash_sweep) flash_sweep();
}
if(defined('SID')) session_write_close();
if($exit) exit;
}
/**
* Returns limonade environment variables:
*
* 'SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE',
* 'GET', 'POST', 'PUT', 'DELETE'
*
* If a null argument is passed, reset and rebuild environment
*
* @param null @reset reset and rebuild environment
* @return array
*/
function env($reset = null)
{
static $env = array();
if(func_num_args() > 0)
{
$args = func_get_args();
if(is_null($args[0])) $env = array();
}
if(empty($env))
{
if(empty($GLOBALS['_SERVER']))
{
// Fixing empty $GLOBALS['_SERVER'] bug
// http://sofadesign.lighthouseapp.com/projects/29612-limonade/tickets/29-env-is-empty
$GLOBALS['_SERVER'] =& $_SERVER;
$GLOBALS['_FILES'] =& $_FILES;
$GLOBALS['_REQUEST'] =& $_REQUEST;
$GLOBALS['_SESSION'] =& $_SESSION;
$GLOBALS['_ENV'] =& $_ENV;
$GLOBALS['_COOKIE'] =& $_COOKIE;
}
$glo_names = array('SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE');
$vars = array_merge($glo_names, request_methods());
foreach($vars as $var)
{
$varname = "_$var";
if(!array_key_exists($varname, $GLOBALS)) $GLOBALS[$varname] = array();
$env[$var] =& $GLOBALS[$varname];
}
$method = request_method($env);
if($method == 'PUT' || $method == 'DELETE')
{
$varname = "_$method";
if(array_key_exists('_method', $_POST) && $_POST['_method'] == $method)
{
foreach($_POST as $k => $v)
{
if($k == "_method") continue;
$GLOBALS[$varname][$k] = $v;
}
}
else
{
parse_str(file_get_contents('php://input'), $GLOBALS[$varname]);
}
}
}
return $env;
}
/**
* Returns application root file path
*
* @return string
*/
function app_file()
{
static $file;
if(empty($file))
{
$debug_backtrace = debug_backtrace();
$stacktrace = array_pop($debug_backtrace);
$file = $stacktrace['file'];
}
return file_path($file);
}
# # #
# ============================================================================ #
# 2. ERROR #
# ============================================================================ #
/**
* Associate a function with error code(s) and return all associations
*
* @param string $errno
* @param string $function
* @return array
*/
function error($errno = null, $function = null)
{
static $errors = array();
if(func_num_args() > 0)
{
$errors[] = array('errno'=>$errno, 'function'=> $function);
}
return $errors;
}
/**
* Raise an error, passing a given error number and an optional message,
* then exit.
* Error number should be a HTTP status code or a php user error (E_USER...)
* $errno and $msg arguments can be passsed in any order
* If no arguments are passed, default $errno is SERVER_ERROR (500)
*
* @param int,string $errno Error number or message string
* @param string,string $msg Message string or error number
* @param mixed $debug_args extra data provided for debugging
* @return void
*/
function halt($errno = SERVER_ERROR, $msg = '', $debug_args = null)
{
$args = func_get_args();
$error = array_shift($args);
# switch $errno and $msg args
# TODO cleanup / refactoring
if(is_string($errno))
{
$msg = $errno;
$oldmsg = array_shift($args);
$errno = empty($oldmsg) ? SERVER_ERROR : $oldmsg;
}
else if(!empty($args)) $msg = array_shift($args);
if(empty($msg) && $errno == NOT_FOUND) $msg = request_uri();
if(empty($msg)) $msg = "";
if(!empty($args)) $debug_args = $args;
set('_lim_err_debug_args', $debug_args);
error_handler_dispatcher($errno, $msg, null, null);
}
/**
* Internal error handler dispatcher
* Find and call matching error handler and exit
* If no match found, call default error handler
*
* @access private
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
* @return void
*/
function error_handler_dispatcher($errno, $errstr, $errfile, $errline)
{
$back_trace = debug_backtrace();
while($trace = array_shift($back_trace))
{
if($trace['function'] == 'halt')
{
$errfile = $trace['file'];
$errline = $trace['line'];
break;
}
}
# Notices and warning won't halt execution
if(error_wont_halt_app($errno))
{
error_notice($errno, $errstr, $errfile, $errline);
return;
}
else
{
# Other errors will stop application
static $handlers = array();
if(empty($handlers))
{
error(E_LIM_PHP, 'error_default_handler');
$handlers = error();
}
$is_http_err = http_response_status_is_valid($errno);
while($handler = array_shift($handlers))
{
$e = is_array($handler['errno']) ? $handler['errno'] : array($handler['errno']);
while($ee = array_shift($e))
{
if($ee == $errno || $ee == E_LIM_PHP || ($ee == E_LIM_HTTP && $is_http_err))
{
echo call_if_exists($handler['function'], $errno, $errstr, $errfile, $errline);
exit;
}
}
}
}
}
/**
* Default error handler
*
* @param string $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
* @return string error output
*/
function error_default_handler($errno, $errstr, $errfile, $errline)
{
$is_http_err = http_response_status_is_valid($errno);
$http_error_code = $is_http_err ? $errno : SERVER_ERROR;
status($http_error_code);
return $http_error_code == NOT_FOUND ?
error_not_found_output($errno, $errstr, $errfile, $errline) :
error_server_error_output($errno, $errstr, $errfile, $errline);
}
/**
* Returns not found error output
*
* @access private
* @param string $msg
* @return string
*/
function error_not_found_output($errno, $errstr, $errfile, $errline)
{
if(!function_exists('not_found'))
{
/**
* Default not found error output
*
* @param string $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
* @return string
*/
function not_found($errno, $errstr, $errfile=null, $errline=null)
{
option('views_dir', option('error_views_dir'));
$msg = h(rawurldecode($errstr));
return html("<h1>Page not found:</h1><p><code>{$msg}</code></p>", error_layout());
}
}
return not_found($errno, $errstr, $errfile, $errline);
}
/**
* Returns server error output
*
* @access private
* @param int $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
* @return string
*/
function error_server_error_output($errno, $errstr, $errfile, $errline)
{
if(!function_exists('server_error'))
{
/**
* Default server error output
*
* @param string $errno
* @param string $errstr
* @param string $errfile
* @param string $errline
* @return string
*/
function server_error($errno, $errstr, $errfile=null, $errline=null)
{
$is_http_error = http_response_status_is_valid($errno);
$args = compact('errno', 'errstr', 'errfile', 'errline', 'is_http_error');
option('views_dir', option('limonade_views_dir'));
$html = render('error.html.php', null, $args);
option('views_dir', option('error_views_dir'));
return html($html, error_layout(), $args);
}
}
return server_error($errno, $errstr, $errfile, $errline);
}
/**
* Set and returns error output layout
*
* @param string $layout
* @return string
*/
function error_layout($layout = false)
{
static $o_layout = 'default_layout.php';
if($layout !== false)
{
option('error_views_dir', option('views_dir'));
$o_layout = $layout;
}
return $o_layout;
}
/**
* Set a notice if arguments are provided
* Returns all stored notices.
* If $errno argument is null, reset the notices array
*
* @access private
* @param string, null $str
* @return array
*/
function error_notice($errno = false, $errstr = null, $errfile = null, $errline = null)
{
static $notices = array();
if($errno) $notices[] = compact('errno', 'errstr', 'errfile', 'errline');
else if(is_null($errno)) $notices = array();
return $notices;
}
/**
* Returns notices output rendering and reset notices
*
* @return string
*/
function error_notices_render()
{
if(option('debug') && option('env') > ENV_PRODUCTION)
{
$notices = error_notice();
error_notice(null); // reset notices
$c_view_dir = option('views_dir'); // keep for restore after render
option('views_dir', option('limonade_views_dir'));
$o = render('_notices.html.php', null, array('notices' => $notices));
option('views_dir', $c_view_dir); // restore current views dir
return $o;
}
}
/**
* Checks if an error is will halt application execution.
* Notices and warnings will not.
*
* @access private
* @param string $num error code number
* @return boolean
*/
function error_wont_halt_app($num)
{
return $num == E_NOTICE ||
$num == E_WARNING ||
$num == E_CORE_WARNING ||
$num == E_COMPILE_WARNING ||
$num == E_USER_WARNING ||
$num == E_USER_NOTICE ||
$num == E_DEPRECATED ||
$num == E_USER_DEPRECATED ||
$num == E_LIM_DEPRECATED;
}
/**
* return error code name for a given code num, or return all errors names
*
* @param string $num
* @return mixed
*/
function error_type($num = null)
{
$types = array (
E_ERROR => 'ERROR',
E_WARNING => 'WARNING',
E_PARSE => 'PARSING ERROR',
E_NOTICE => 'NOTICE',
E_CORE_ERROR => 'CORE ERROR',
E_CORE_WARNING => 'CORE WARNING',
E_COMPILE_ERROR => 'COMPILE ERROR',
E_COMPILE_WARNING => 'COMPILE WARNING',
E_USER_ERROR => 'USER ERROR',
E_USER_WARNING => 'USER WARNING',
E_USER_NOTICE => 'USER NOTICE',
E_STRICT => 'STRICT NOTICE',
E_RECOVERABLE_ERROR => 'RECOVERABLE ERROR',
E_DEPRECATED => 'DEPRECATED WARNING',
E_USER_DEPRECATED => 'USER DEPRECATED WARNING',
E_LIM_DEPRECATED => 'LIMONADE DEPRECATED WARNING'
);
return is_null($num) ? $types : $types[$num];
}
/**
* Returns http response status for a given error number
*
* @param string $errno
* @return int
*/
function error_http_status($errno)
{
$code = http_response_status_is_valid($errno) ? $errno : SERVER_ERROR;
return http_response_status($code);
}
# # #
# ============================================================================ #
# 3. REQUEST #
# ============================================================================ #
/**
* Returns current request method for a given environment or current one
*
* @param string $env
* @return string
*/
function request_method($env = null)
{
if(is_null($env)) $env = env();
$m = array_key_exists('REQUEST_METHOD', $env['SERVER']) ? $env['SERVER']['REQUEST_METHOD'] : null;
if($m == "POST" && array_key_exists('_method', $env['POST']))
$m = strtoupper($env['POST']['_method']);
if(!in_array(strtoupper($m), request_methods()))
{
trigger_error("'$m' request method is unknown or unavailable.", E_USER_WARNING);
$m = false;
}
return $m;
}
/**
* Checks if a request method or current one is allowed
*
* @param string $m
* @return bool
*/
function request_method_is_allowed($m = null)
{
if(is_null($m)) $m = request_method();
return in_array(strtoupper($m), request_methods());
}
/**
* Checks if request method is GET
*
* @param string $env
* @return bool
*/
function request_is_get($env = null)
{
return request_method($env) == "GET";
}
/**
* Checks if request method is POST
*
* @param string $env
* @return bool
*/
function request_is_post($env = null)
{
return request_method($env) == "POST";
}
/**
* Checks if request method is PUT
*
* @param string $env
* @return bool
*/
function request_is_put($env = null)
{
return request_method($env) == "PUT";
}
/**
* Checks if request method is DELETE
*
* @param string $env
* @return bool
*/
function request_is_delete($env = null)
{
return request_method($env) == "DELETE";
}
/**
* Checks if request method is HEAD
*
* @param string $env
* @return bool
*/
function request_is_head($env = null)
{
return request_method($env) == "HEAD";
}
/**
* Returns allowed request methods
*
* @return array
*/
function request_methods()
{
return array("GET","POST","PUT","DELETE", "HEAD");
}
/**
* Returns current request uri (the path that will be compared with routes)
*
* (Inspired from codeigniter URI::_fetch_uri_string method)
*
* @return string
*/
function request_uri($env = null)
{
static $uri = null;
if(is_null($env))
{
if(!is_null($uri)) return $uri;
$env = env();
}
if(array_key_exists('uri', $env['GET']))
{
$uri = $env['GET']['uri'];
}
else if(array_key_exists('u', $env['GET']))
{
$uri = $env['GET']['u'];
}
// bug: dot are converted to _... so we can't use it...
// else if (count($env['GET']) == 1 && trim(key($env['GET']), '/') != '')
// {
// $uri = key($env['GET']);
// }
else
{
$app_file = app_file();
$path_info = isset($env['SERVER']['PATH_INFO']) ? $env['SERVER']['PATH_INFO'] : @getenv('PATH_INFO');
$query_string = isset($env['SERVER']['QUERY_STRING']) ? $env['SERVER']['QUERY_STRING'] : @getenv('QUERY_STRING');
// Is there a PATH_INFO variable?
// Note: some servers seem to have trouble with getenv() so we'll test it two ways
if (trim($path_info, '/') != '' && $path_info != "/".$app_file)
{
if(strpos($path_info, '&') !== 0)
{
# exclude GET params
$params = explode('&', $path_info);
$path_info = array_shift($params);
# populate $_GET
foreach($params as $param)
{
if(strpos($param, '=') > 0)
{
list($k, $v) = explode('=', $param);
$env['GET'][$k] = $v;
}
}
}
$uri = $path_info;
}
// No PATH_INFO?... What about QUERY_STRING?
elseif (trim($query_string, '/') != '' && $query_string[0] == '/')
{
$uri = $query_string;
$get = $env['GET'];
if(count($get) > 0)
{
# exclude GET params
$keys = array_keys($get);
$first = array_shift($keys);
if(strpos($query_string, $first) === 0) $uri = $first;
}
}
elseif(array_key_exists('REQUEST_URI', $env['SERVER']) && !empty($env['SERVER']['REQUEST_URI']))
{
$request_uri = rtrim($env['SERVER']['REQUEST_URI'], '?/').'/';
$base_path = $env['SERVER']['SCRIPT_NAME'];
if($request_uri."index.php" == $base_path) $request_uri .= "index.php";
$uri = str_replace($base_path, '', $request_uri);
if(option('base_uri') && strpos($uri, option('base_uri')) === 0) {
$uri = substr($uri, strlen(option('base_uri')));
}
if(strpos($uri, '?') !== false) {
$uri = substr($uri, 0, strpos($uri, '?')) . '/';
}
}
elseif($env['SERVER']['argc'] > 1 && trim($env['SERVER']['argv'][1], '/') != '')
{
$uri = $env['SERVER']['argv'][1];
}
}
$uri = rtrim($uri, "/"); # removes ending /
if(empty($uri))
{
$uri = '/';
}
else if($uri[0] != '/')
{
$uri = '/' . $uri; # add a leading slash
}
return rawurldecode($uri);
}
# # #
# ============================================================================ #
# 4. ROUTER #
# ============================================================================ #
/**
* An alias of {@link dispatch_get()}
*
* @return void
*/
function dispatch($path_or_array, $callback, $options = array())
{
dispatch_get($path_or_array, $callback, $options);
}
/**
* Add a GET route. Also automatically defines a HEAD route.
*
* @param string $path_or_array
* @param string $callback
* @param array $options (optional). See {@link route()} for available options.
* @return void
*/
function dispatch_get($path_or_array, $callback, $options = array())
{
route("GET", $path_or_array, $callback, $options);
route("HEAD", $path_or_array, $callback, $options);
}
/**
* Add a POST route
*
* @param string $path_or_array
* @param string $callback
* @param array $options (optional). See {@link route()} for available options.
* @return void
*/
function dispatch_post($path_or_array, $callback, $options = array())
{
route("POST", $path_or_array, $callback, $options);
}
/**
* Add a PUT route
*
* @param string $path_or_array
* @param string $callback
* @param array $options (optional). See {@link route()} for available options.
* @return void
*/
function dispatch_put($path_or_array, $callback, $options = array())
{
route("PUT", $path_or_array, $callback, $options);
}
/**
* Add a DELETE route
*
* @param string $path_or_array
* @param string $callback
* @param array $options (optional). See {@link route()} for available options.
* @return void
*/
function dispatch_delete($path_or_array, $callback, $options = array())
{
route("DELETE", $path_or_array, $callback, $options);
}
/**
* Add route if required params are provided.
* Delete all routes if null is passed as a unique argument
* Return all routes
*
* @see route_build()
* @access private
* @param string $method
* @param string|array $path_or_array
* @param callback $func
* @param array $options (optional). Available options:
* - 'params' key with an array of parameters: for parametrized routes.
* those parameters will be merged with routes parameters.
* @return array
*/
function route()
{
static $routes = array();
$nargs = func_num_args();
if( $nargs > 0)
{
$args = func_get_args();
if($nargs === 1 && is_null($args[0])) $routes = array();
else if($nargs < 3) trigger_error("Missing arguments for route()", E_USER_ERROR);
else
{
$method = $args[0];
$path_or_array = $args[1];
$func = $args[2];
$options = $nargs > 3 ? $args[3] : array();
$routes[] = route_build($method, $path_or_array, $func, $options);
}
}
return $routes;
}
/**
* An alias of route(null): reset all routes
*
* @access private
* @return void
*/
function route_reset()
{
route(null);
}
/**
* Build a route and return it
*
* @access private
* @param string $method allowed http method (one of those returned by {@link request_methods()})
* @param string|array $path_or_array
* @param callback $callback callback called when route is found. It can be
* a function, an object method, a static method or a closure.
* See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
* to learn more about callbacks.
* @param array $options (optional). Available options:
* - 'params' key with an array of parameters: for parametrized routes.
* those parameters will be merged with routes parameters.
* @return array array with keys "method", "pattern", "names", "callback", "options"
*/
function route_build($method, $path_or_array, $callback, $options = array())
{
$method = strtoupper($method);
if(!in_array($method, request_methods()))
trigger_error("'$method' request method is unkown or unavailable.", E_USER_WARNING);
if(is_array($path_or_array))
{
$path = array_shift($path_or_array);
$names = $path_or_array[0];
}
else
{
$path = $path_or_array;
$names = array();
}
$single_asterisk_subpattern = "(?:/([^\/]*))?";
$double_asterisk_subpattern = "(?:/(.*))?";
$optionnal_slash_subpattern = "(?:/*?)";
$no_slash_asterisk_subpattern = "(?:([^\/]*))?";
if($path[0] == "^")
{
if($path{strlen($path) - 1} != "$") $path .= "$";
$pattern = "#".$path."#i";
}
else if(empty($path) || $path == "/")
{
$pattern = "#^".$optionnal_slash_subpattern."$#";
}
else
{
$parsed = array();
$elts = explode('/', $path);
$parameters_count = 0;
foreach($elts as $elt)
{
if(empty($elt)) continue;
$name = null;
# extracting double asterisk **
if($elt == "**"):
$parsed[] = $double_asterisk_subpattern;
$name = $parameters_count;
# extracting single asterisk *
elseif($elt == "*"):
$parsed[] = $single_asterisk_subpattern;
$name = $parameters_count;
# extracting named parameters :my_param
elseif($elt[0] == ":"):
if(preg_match('/^:([^\:]+)$/', $elt, $matches))
{
$parsed[] = $single_asterisk_subpattern;
$name = $matches[1];
};
elseif(strpos($elt, '*') !== false):
$sub_elts = explode('*', $elt);
$parsed_sub = array();
foreach($sub_elts as $sub_elt)
{
$parsed_sub[] = preg_quote($sub_elt, "#");
$name = $parameters_count;
}
//
$parsed[] = "/".implode($no_slash_asterisk_subpattern, $parsed_sub);
else:
$parsed[] = "/".preg_quote($elt, "#");
endif;
/* set parameters names */
if(is_null($name)) continue;
if(!array_key_exists($parameters_count, $names) || is_null($names[$parameters_count]))
$names[$parameters_count] = $name;
$parameters_count++;
}
$pattern = "#^".implode('', $parsed).$optionnal_slash_subpattern."?$#i";
}
return array( "method" => $method,
"pattern" => $pattern,
"names" => $names,
"callback" => $callback,
"options" => $options );
}
/**
* Find a route and returns it.
* Parameters values extracted from the path are added and merged
* with the default 'params' option of the route
* If not found, returns false.
* Routes are checked from first added to last added.
*
* @access private
* @param string $method
* @param string $path
* @return array,false route array has same keys as route returned by
* {@link route_build()} ("method", "pattern", "names", "callback", "options")
* + the processed "params" key
*/
function route_find($method, $path)
{
$routes = route();
$method = strtoupper($method);
foreach($routes as $route)
{
if($method == $route["method"] && preg_match($route["pattern"], $path, $matches))
{
$options = $route["options"];
$params = array_key_exists('params', $options) ? $options["params"] : array();
if(count($matches) > 1)
{
array_shift($matches);
$n_matches = count($matches);
$names = array_values($route["names"]);
$n_names = count($names);
if( $n_matches < $n_names )
{
$a = array_fill(0, $n_names - $n_matches, null);
$matches = array_merge($matches, $a);
}
else if( $n_matches > $n_names )
{
$names = range($n_names, $n_matches - 1);
}
$arr_comb = array_combine($names, $matches);
$params = array_replace($params, $arr_comb);
}
$route["params"] = $params;
return $route;
}
}
return false;
}
# ============================================================================ #
# 5. OUTPUT AND RENDERING #
# ============================================================================ #
/**
* Returns a string to output
*
* It might use a template file, a function, or a formatted string (like {@link sprintf()}).
* It could be embraced by a layout or not.
* Local vars can be passed in addition to variables made available with the {@link set()}
* function.
*
* @param string $content_or_func
* @param string $layout
* @param string $locals
* @return string
*/
function render($content_or_func, $layout = '', $locals = array())
{
$args = func_get_args();
$content_or_func = array_shift($args);
$layout = count($args) > 0 ? array_shift($args) : layout();
$view_path = file_path(option('views_dir'),$content_or_func);
if(function_exists('before_render'))
list($content_or_func, $layout, $locals, $view_path) = before_render($content_or_func, $layout, $locals, $view_path);
$vars = array_merge(set(), $locals);
$flash = flash_now();
if(array_key_exists('flash', $vars)) trigger_error('A $flash variable is already passed to view. Flash messages will only be accessible through flash_now()', E_USER_NOTICE);
else if(!empty($flash)) $vars['flash'] = $flash;
$infinite_loop = false;
# Avoid infinite loop: this function is in the backtrace ?
if(function_exists($content_or_func))
{
$back_trace = debug_backtrace();
while($trace = array_shift($back_trace))
{
if($trace['function'] == strtolower($content_or_func))
{
$infinite_loop = true;
break;
}
}
}
if(function_exists($content_or_func) && !$infinite_loop)
{
ob_start();
call_user_func($content_or_func, $vars);
$content = ob_get_clean();
}
elseif(file_exists($view_path))
{
ob_start();
extract($vars);
include $view_path;
$content = ob_get_clean();
}
else
{
if(substr_count($content_or_func, '%') !== count($vars)) $content = $content_or_func;
else $content = vsprintf($content_or_func, $vars);
}
if(empty($layout)) return $content;
return render($layout, null, array('content' => $content));
}
/**
* Returns a string to output
*
* Shortcut to render with no layout.
*
* @param string $content_or_func
* @param string $locals
* @return string
*/
function partial($content_or_func, $locals = array())
{
return render($content_or_func, null, $locals);
}
/**
* Returns html output with proper http headers
*
* @param string $content_or_func
* @param string $layout
* @param string $locals
* @return string
*/
function html($content_or_func, $layout = '', $locals = array())
{
send_header('Content-Type: text/html; charset='.strtolower(option('encoding')));
$args = func_get_args();
return call_user_func_array('render', $args);
}
/**
* Set and return current layout
*
* @param string $function_or_file
* @return string
*/
function layout($function_or_file = null)
{
static $layout = null;
if(func_num_args() > 0) $layout = $function_or_file;
return $layout;
}
/**
* Returns xml output with proper http headers
*
* @param string $content_or_func
* @param string $layout
* @param string $locals
* @return string
*/
function xml($data)
{
send_header('Content-Type: text/xml; charset='.strtolower(option('encoding')));
$args = func_get_args();
return call_user_func_array('render', $args);
}
/**
* Returns css output with proper http headers
*
* @param string $content_or_func
* @param string $layout
* @param string $locals
* @return string
*/
function css($content_or_func, $layout = '', $locals = array())
{
send_header('Content-Type: text/css; charset='.strtolower(option('encoding')));
$args = func_get_args();
return call_user_func_array('render', $args);
}
/**
* Returns javacript output with proper http headers
*
* @param string $content_or_func
* @param string $layout
* @param string $locals
* @return string
*/
function js($content_or_func, $layout = '', $locals = array())
{
send_header('Content-Type: application/javascript; charset='.strtolower(option('encoding')));
$args = func_get_args();
return call_user_func_array('render', $args);
}
/**
* Returns txt output with proper http headers
*
* @param string $content_or_func
* @param string $layout
* @param string $locals
* @return string
*/
function txt($content_or_func, $layout = '', $locals = array())
{
send_header('Content-Type: text/plain; charset='.strtolower(option('encoding')));
$args = func_get_args();
return call_user_func_array('render', $args);
}
/**
* Returns json representation of data with proper http headers.
* On PHP 5 < PHP 5.2.0, you must provide your own implementation of the
* <code>json_encode()</code> function beore using <code>json()</code>.
*
* @param string $data
* @param int $json_option
* @return string
*/
function json($data, $json_option = 0)
{
send_header('Content-Type: application/json; charset='.strtolower(option('encoding')));
return version_compare(PHP_VERSION, '5.3.0', '>=') ? json_encode($data, $json_option) : json_encode($data);
}
/**
* undocumented function
*
* @param string $filename
* @param string $return
* @return mixed number of bytes delivered or file output if $return = true
*/
function render_file($filename, $return = false)
{
# TODO implements X-SENDFILE headers
// if($x-sendfile = option('x-sendfile'))
// {
// // add a X-Sendfile header for apache and Lighttpd >= 1.5
// if($x-sendfile > X-SENDFILE) // add a X-LIGHTTPD-send-file header
//
// }
// else
// {
//
// }
$filename = str_replace('../', '', $filename);
if(file_exists($filename))
{
$content_type = mime_type(file_extension($filename));
$header = 'Content-type: '.$content_type;
if(file_is_text($filename)) $header .= '; charset='.strtolower(option('encoding'));
send_header($header);
return file_read($filename, $return);
}
else halt(NOT_FOUND, "unknown filename $filename");
}
/**
* Call before_sending_header() if it exists, then send headers
*
* @param string $header
* @return void
*/
function send_header($header = null, $replace = true, $code = false)
{
if(!headers_sent())
{
call_if_exists('before_sending_header', $header);
header($header, $replace, $code);
}
}
# # #
# ============================================================================ #
# 6. HELPERS #
# ============================================================================ #
/**
* Returns an url composed of params joined with /
* A param can be a string or an array.
* If param is an array, its members will be added at the end of the return url
* as GET parameters "&key=value".
*
* @param string or array $param1, $param2 ...
* @return string
*/
function url_for($params = null)
{
$paths = array();
$params = func_get_args();
$GET_params = array();
foreach($params as $param)
{
if(is_array($param))
{
$GET_params = array_merge($GET_params, $param);
continue;
}
if(filter_var_url($param))
{
$paths[] = $param;
continue;
}
$p = explode('/',$param);
foreach($p as $v)
{
if($v != "") $paths[] = str_replace('%23', '#', rawurlencode($v));
}
}
$path = rtrim(implode('/', $paths), '/');
if(!filter_var_url($path))
{
# it's a relative URL or an URL without a schema
$base_uri = option('base_uri');
$path = file_path($base_uri, $path);
}
if(!empty($GET_params))
{
$is_first_qs_param = true;
$path_as_no_question_mark = strpos($path, '?') === false;
foreach($GET_params as $k => $v)
{
$qs_separator = $is_first_qs_param && $path_as_no_question_mark ?
'?' : '&';
$path .= $qs_separator . rawurlencode($k) . '=' . rawurlencode($v);
$is_first_qs_param = false;
}
}
if(DIRECTORY_SEPARATOR != '/') $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
return $path;
}
/**
* An alias of {@link htmlspecialchars()}.
* If no $charset is provided, uses option('encoding') value
*
* @param string $str
* @param string $quote_style
* @param string $charset
* @return void
*/
function h($str, $quote_style = ENT_NOQUOTES, $charset = null)
{
if(is_null($charset)) $charset = strtoupper(option('encoding'));
return htmlspecialchars($str, $quote_style, $charset);
}
/**
* Set and returns flash messages that will be available in the next action
* via the {@link flash_now()} function or the view variable <code>$flash</code>.
*
* If multiple values are provided, set <code>$name</code> variable with an array of those values.
* If there is only one value, set <code>$name</code> variable with the provided $values
* or if it's <code>$name</code> is an array, merge it with current messages.
*
* @param string, array $name
* @param mixed $values,...
* @return mixed variable value for $name if $name argument is provided, else return all variables
*/
function flash($name = null, $value = null)
{
if(!defined('SID')) trigger_error("Flash messages can't be used because session isn't enabled", E_USER_WARNING);
static $messages = array();
$args = func_get_args();
$name = array_shift($args);
if(is_null($name)) return $messages;
if(is_array($name)) return $messages = array_merge($messages, $name);
if(!empty($args))
{
$messages[$name] = count($args) > 1 ? $args : $args[0];
}
if(!array_key_exists($name, $messages)) return null;
else return $messages[$name];
return $messages;
}
/**
* Set and returns flash messages available for the current action, included those
* defined in the previous action with {@link flash()}
* Those messages will also be passed to the views and made available in the
* <code>$flash</code> variable.
*
* If multiple values are provided, set <code>$name</code> variable with an array of those values.
* If there is only one value, set <code>$name</code> variable with the provided $values
* or if it's <code>$name</code> is an array, merge it with current messages.
*
* @param string, array $name
* @param mixed $values,...
* @return mixed variable value for $name if $name argument is provided, else return all variables
*/
function flash_now($name = null, $value = null)
{
static $messages = null;
if(is_null($messages))
{
$fkey = LIM_SESSION_FLASH_KEY;
$messages = array();
if(defined('SID') && array_key_exists($fkey, $_SESSION)) $messages = $_SESSION[$fkey];
}
$args = func_get_args();
$name = array_shift($args);
if(is_null($name)) return $messages;
if(is_array($name)) return $messages = array_merge($messages, $name);
if(!empty($args))
{
$messages[$name] = count($args) > 1 ? $args : $args[0];
}
if(!array_key_exists($name, $messages)) return null;
else return $messages[$name];
return $messages;
}
/**
* Delete current flash messages in session, and set new ones stored with
* flash function.
* Called before application exit.
*
* @access private
* @return void
*/
function flash_sweep()
{
if(defined('SID'))
{
$fkey = LIM_SESSION_FLASH_KEY;
$_SESSION[$fkey] = flash();
}
}
/**
* Starts capturing block of text
*
* Calling without params stops capturing (same as end_content_for()).
* After capturing the captured block is put into a variable
* named $name for later use in layouts. If second parameter
* is supplied, its content will be used instead of capturing
* a block of text.
*
* @param string $name
* @param string $content
* @return void
*/
function content_for($name = null, $content = null)
{
static $_name = null;
if(is_null($name) && !is_null($_name))
{
set($_name, ob_get_clean());
$_name = null;
}
elseif(!is_null($name) && !isset($content))
{
$_name = $name;
ob_start();
}
elseif(isset($name, $content))
{
set($name, $content);
}
}
/**
* Stops capturing block of text
*
* @return void
*/
function end_content_for()
{
content_for();
}
/**
* Shows current memory and execution time of the application.
* Returns only execution time if <code>memory_get_usage()</code>
* isn't available.
* ( That's the case before PHP5.2.1 if PHP isn't compiled with option
* <code>--enable-memory-limit</code>. )
*
* @access public
*?@return array
*/
function benchmark()
{
$res = array( 'execution_time' => (microtime(true) - LIM_START_MICROTIME) );
if(defined('LIM_START_MEMORY'))
{
$current_mem_usage = memory_get_usage();
$res['current_memory'] = $current_mem_usage;
$res['start_memory'] = LIM_START_MEMORY;
$res['average_memory'] = (LIM_START_MEMORY + $current_mem_usage) / 2;
}
return $res;
}
# # #
# ============================================================================ #
# 7. UTILS #
# ============================================================================ #
/**
* Calls a function if exists
*
* @param callback $callback a function stored in a string variable,
* or an object and the name of a method within the object
* See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
* to learn more about callbacks.
* @param mixed $arg,.. (optional)
* @return mixed
*/
function call_if_exists($callback)
{
$args = func_get_args();
$callback = array_shift($args);
if(is_callable($callback)) return call_user_func_array($callback, $args);
return;
}
/**
* Define a constant unless it already exists
*
* @param string $name
* @param string $value
* @return void
*/
function define_unless_exists($name, $value)
{
if(!defined($name)) define($name, $value);
}
/**
* Return a default value if provided value is empty
*
* @param mixed $value
* @param mixed $default default value returned if $value is empty
* @return mixed
*/
function value_or_default($value, $default)
{
return empty($value) ? $default : $value;
}
/**
* An alias of {@link value_or_default()}
*
*
* @param mixed $value
* @param mixed $default
* @return mixed
*/
function v($value, $default)
{
return value_or_default($value, $default);
}
/**
* Load php files with require_once in a given dir
*
* @param string $path Path in which are the file to load
* @param string $pattern a regexp pattern that filter files to load
* @param bool $prevents_output security option that prevents output
* @return array paths of loaded files
*/
function require_once_dir($path, $pattern = "*.php", $prevents_output = true)
{
if($path[strlen($path) - 1] != "/") $path .= "/";
$filenames = glob($path.$pattern);
if(!is_array($filenames)) $filenames = array();
if($prevents_output) ob_start();
foreach($filenames as $filename) require_once $filename;
if($prevents_output) ob_end_clean();
return $filenames;
}
/**
* Dumps a variable into inspectable format
*
* @param anything $var the variable to debug
* @param bool $output_as_html sets whether to wrap output in <pre> tags. default: true
* @return string the variable with output
*/
function debug($var, $output_as_html = true)
{
if ( is_null($var) ) { return '<span class="null-value">[NULL]</span>'; };
$out = '';
switch ($var)
{
case empty($var):
$out = '[empty value]';
break;
case is_array($var):
$out = var_export($var, true);
break;
case is_object($var):
$out = var_export($var, true);
break;
case is_string($var):
$out = $var;
break;
default:
$out = var_export($var, true);
break;
}
if ($output_as_html) { $out = "<pre>
" . h($out) ."</pre>"; }
return $out;
}
## HTTP utils _________________________________________________________________
### Constants: HTTP status codes
define( 'HTTP_CONTINUE', 100 );
define( 'HTTP_SWITCHING_PROTOCOLS', 101 );
define( 'HTTP_PROCESSING', 102 );
define( 'HTTP_OK', 200 );
define( 'HTTP_CREATED', 201 );
define( 'HTTP_ACCEPTED', 202 );
define( 'HTTP_NON_AUTHORITATIVE', 203 );
define( 'HTTP_NO_CONTENT', 204 );
define( 'HTTP_RESET_CONTENT', 205 );
define( 'HTTP_PARTIAL_CONTENT', 206 );
define( 'HTTP_MULTI_STATUS', 207 );
define( 'HTTP_MULTIPLE_CHOICES', 300 );
define( 'HTTP_MOVED_PERMANENTLY', 301 );
define( 'HTTP_MOVED_TEMPORARILY', 302 );
define( 'HTTP_SEE_OTHER', 303 );
define( 'HTTP_NOT_MODIFIED', 304 );
define( 'HTTP_USE_PROXY', 305 );
define( 'HTTP_TEMPORARY_REDIRECT', 307 );
define( 'HTTP_BAD_REQUEST', 400 );
define( 'HTTP_UNAUTHORIZED', 401 );
define( 'HTTP_PAYMENT_REQUIRED', 402 );
define( 'HTTP_FORBIDDEN', 403 );
define( 'HTTP_NOT_FOUND', 404 );
define( 'HTTP_METHOD_NOT_ALLOWED', 405 );
define( 'HTTP_NOT_ACCEPTABLE', 406 );
define( 'HTTP_PROXY_AUTHENTICATION_REQUIRED', 407 );
define( 'HTTP_REQUEST_TIME_OUT', 408 );
define( 'HTTP_CONFLICT', 409 );
define( 'HTTP_GONE', 410 );
define( 'HTTP_LENGTH_REQUIRED', 411 );
define( 'HTTP_PRECONDITION_FAILED', 412 );
define( 'HTTP_REQUEST_ENTITY_TOO_LARGE', 413 );
define( 'HTTP_REQUEST_URI_TOO_LARGE', 414 );
define( 'HTTP_UNSUPPORTED_MEDIA_TYPE', 415 );
define( 'HTTP_RANGE_NOT_SATISFIABLE', 416 );
define( 'HTTP_EXPECTATION_FAILED', 417 );
define( 'HTTP_UNPROCESSABLE_ENTITY', 422 );
define( 'HTTP_LOCKED', 423 );
define( 'HTTP_FAILED_DEPENDENCY', 424 );
define( 'HTTP_UPGRADE_REQUIRED', 426 );
define( 'HTTP_INTERNAL_SERVER_ERROR', 500 );
define( 'HTTP_NOT_IMPLEMENTED', 501 );
define( 'HTTP_BAD_GATEWAY', 502 );
define( 'HTTP_SERVICE_UNAVAILABLE', 503 );
define( 'HTTP_GATEWAY_TIME_OUT', 504 );
define( 'HTTP_VERSION_NOT_SUPPORTED', 505 );
define( 'HTTP_VARIANT_ALSO_VARIES', 506 );
define( 'HTTP_INSUFFICIENT_STORAGE', 507 );
define( 'HTTP_NOT_EXTENDED', 510 );
/**
* Output proper HTTP header for a given HTTP code
*
* @param string $code
* @return void
*/
function status($code = 500)
{
if(!headers_sent())
{
$str = http_response_status_code($code);
send_header($str);
}
}
/**
* Http redirection
*
* Same use as {@link url_for()}
* By default HTTP status code is 302, but a different code can be specified
* with a status key in array parameter.
*
* <code>
* redirecto('new','url'); # 302 HTTP_MOVED_TEMPORARILY by default
* redirecto('new','url', array('status' => HTTP_MOVED_PERMANENTLY));
* </code>
*
* @param string or array $param1, $param2...
* @return void
*/
function redirect_to($params)
{
# [NOTE]: (from php.net) HTTP/1.1 requires an absolute URI as argument to ? Location:
# including the scheme, hostname and absolute path, but some clients accept
# relative URIs. You can usually use $_SERVER['HTTP_HOST'],
# $_SERVER['PHP_SELF'] and dirname() to make an absolute URI from a relative
# one yourself.
# TODO make absolute uri
if(!headers_sent())
{
$status = HTTP_MOVED_TEMPORARILY; # default for a redirection in PHP
$params = func_get_args();
$n_params = array();
# extract status param if exists
foreach($params as $param)
{
if(is_array($param))
{
if(array_key_exists('status', $param))
{
$status = $param['status'];
unset($param['status']);
}
}
$n_params[] = $param;
}
$uri = call_user_func_array('url_for', $n_params);
$uri = htmlspecialchars_decode($uri, ENT_NOQUOTES);
stop_and_exit(false);
send_header('Location: '.$uri, true, $status);
exit;
}
}
/**
* Http redirection
*
* @deprecated deprecated since version 0.4. Please use {@link redirect_to()} instead.
* @param string $url
* @return void
*/
function redirect($uri)
{
# halt('redirect() is deprecated. Please use redirect_to() instead.', E_LIM_DEPRECATED);
# halt not necesary... it won't be visible because of http redirection...
redirect_to($uri);
}
/**
* Returns HTTP response status for a given code.
* If no code provided, return an array of all status
*
* @param string $num
* @return string,array
*/
function http_response_status($num = null)
{
$status = array(
100 => 'Continue',
101 => 'Switching Protocols',
102 => 'Processing',
200 => 'OK',
201 => 'Created',
202 => 'Accepted',
203 => 'Non-Authoritative Information',
204 => 'No Content',
205 => 'Reset Content',
206 => 'Partial Content',
207 => 'Multi-Status',
226 => 'IM Used',
300 => 'Multiple Choices',
301 => 'Moved Permanently',
302 => 'Found',
303 => 'See Other',
304 => 'Not Modified',
305 => 'Use Proxy',
306 => 'Reserved',
307 => 'Temporary Redirect',
400 => 'Bad Request',
401 => 'Unauthorized',
402 => 'Payment Required',
403 => 'Forbidden',
404 => 'Not Found',
405 => 'Method Not Allowed',
406 => 'Not Acceptable',
407 => 'Proxy Authentication Required',
408 => 'Request Timeout',
409 => 'Conflict',
410 => 'Gone',
411 => 'Length Required',
412 => 'Precondition Failed',
413 => 'Request Entity Too Large',
414 => 'Request-URI Too Long',
415 => 'Unsupported Media Type',
416 => 'Requested Range Not Satisfiable',
417 => 'Expectation Failed',
422 => 'Unprocessable Entity',
423 => 'Locked',
424 => 'Failed Dependency',
426 => 'Upgrade Required',
500 => 'Internal Server Error',
501 => 'Not Implemented',
502 => 'Bad Gateway',
503 => 'Service Unavailable',
504 => 'Gateway Timeout',
505 => 'HTTP Version Not Supported',
506 => 'Variant Also Negotiates',
507 => 'Insufficient Storage',
510 => 'Not Extended'
);
if(is_null($num)) return $status;
return array_key_exists($num, $status) ? $status[$num] : '';
}
/**
* Checks if an HTTP response code is valid
*
* @param string $num
* @return bool
*/
function http_response_status_is_valid($num)
{
$r = http_response_status($num);
return !empty($r);
}
/**
* Returns an HTTP response status string for a given code
*
* @param string $num
* @return string
*/
function http_response_status_code($num)
{
$protocole = empty($_SERVER["SERVER_PROTOCOL"]) ? "HTTP/1.1" : $_SERVER["SERVER_PROTOCOL"];
if($str = http_response_status($num)) return "$protocole $num $str";
}
/**
* Check if the _Accept_ header is present, and includes the given `type`.
*
* When the _Accept_ header is not present `true` is returned. Otherwise
* the given `type` is matched by an exact match, and then subtypes. You
* may pass the subtype such as "html" which is then converted internally
* to "text/html" using the mime lookup table.
*
* @param string $type
* @param string $env
* @return bool
*/
function http_ua_accepts($type, $env = null)
{
if(is_null($env)) $env = env();
$accept = array_key_exists('HTTP_ACCEPT', $env['SERVER']) ? $env['SERVER']['HTTP_ACCEPT'] : null;
if(!$accept || $accept === '*/*') return true;
if($type)
{
// Allow "html" vs "text/html" etc
if(!strpos($type, '/')) $type = mime_type($type);
// Check if we have a direct match
if(strpos($accept, $type) > -1) return true;
// Check if we have type/*
$type_parts = explode('/', $type);
$type = $type_parts[0].'/*';
return (strpos($accept, $type) > -1);
}
return false;
}
## FILE utils _________________________________________________________________
/**
* Returns mime type for a given extension or if no extension is provided,
* all mime types in an associative array, with extensions as keys.
* (extracted from Orbit source http://orbit.luaforge.net/)
*
* @param string $ext
* @return string, array
*/
function mime_type($ext = null)
{
$types = array(
'ai' => 'application/postscript',
'aif' => 'audio/x-aiff',
'aifc' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'asc' => 'text/plain',
'atom' => 'application/atom+xml',
'atom' => 'application/atom+xml',
'au' => 'audio/basic',
'avi' => 'video/x-msvideo',
'bcpio' => 'application/x-bcpio',
'bin' => 'application/octet-stream',
'bmp' => 'image/bmp',
'cdf' => 'application/x-netcdf',
'cgm' => 'image/cgm',
'class' => 'application/octet-stream',
'cpio' => 'application/x-cpio',
'cpt' => 'application/mac-compactpro',
'csh' => 'application/x-csh',
'css' => 'text/css',
'csv' => 'text/csv',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'djv' => 'image/vnd.djvu',
'djvu' => 'image/vnd.djvu',
'dll' => 'application/octet-stream',
'dmg' => 'application/octet-stream',
'dms' => 'application/octet-stream',
'doc' => 'application/msword',
'dtd' => 'application/xml-dtd',
'dvi' => 'application/x-dvi',
'dxr' => 'application/x-director',
'eps' => 'application/postscript',
'etx' => 'text/x-setext',
'exe' => 'application/octet-stream',
'ez' => 'application/andrew-inset',
'gif' => 'image/gif',
'gram' => 'application/srgs',
'grxml' => 'application/srgs+xml',
'gtar' => 'application/x-gtar',
'hdf' => 'application/x-hdf',
'hqx' => 'application/mac-binhex40',
'htm' => 'text/html',
'html' => 'text/html',
'ice' => 'x-conference/x-cooltalk',
'ico' => 'image/x-icon',
'ics' => 'text/calendar',
'ief' => 'image/ief',
'ifb' => 'text/calendar',
'iges' => 'model/iges',
'igs' => 'model/iges',
'jpe' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'jpg' => 'image/jpeg',
'js' => 'application/x-javascript',
'json' => 'application/json',
'kar' => 'audio/midi',
'latex' => 'application/x-latex',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'm3u' => 'audio/x-mpegurl',
'man' => 'application/x-troff-man',
'mathml' => 'application/mathml+xml',
'me' => 'application/x-troff-me',
'mesh' => 'model/mesh',
'mid' => 'audio/midi',
'midi' => 'audio/midi',
'mif' => 'application/vnd.mif',
'mov' => 'video/quicktime',
'movie' => 'video/x-sgi-movie',
'mp2' => 'audio/mpeg',
'mp3' => 'audio/mpeg',
'mpe' => 'video/mpeg',
'mpeg' => 'video/mpeg',
'mpg' => 'video/mpeg',
'mpga' => 'audio/mpeg',
'ms' => 'application/x-troff-ms',
'msh' => 'model/mesh',
'mxu' => 'video/vnd.mpegurl',
'nc' => 'application/x-netcdf',
'oda' => 'application/oda',
'ogg' => 'application/ogg',
'pbm' => 'image/x-portable-bitmap',
'pdb' => 'chemical/x-pdb',
'pdf' => 'application/pdf',
'pgm' => 'image/x-portable-graymap',
'pgn' => 'application/x-chess-pgn',
'png' => 'image/png',
'pnm' => 'image/x-portable-anymap',
'ppm' => 'image/x-portable-pixmap',
'ppt' => 'application/vnd.ms-powerpoint',
'ps' => 'application/postscript',
'qt' => 'video/quicktime',
'ra' => 'audio/x-pn-realaudio',
'ram' => 'audio/x-pn-realaudio',
'ras' => 'image/x-cmu-raster',
'rdf' => 'application/rdf+xml',
'rgb' => 'image/x-rgb',
'rm' => 'application/vnd.rn-realmedia',
'roff' => 'application/x-troff',
'rss' => 'application/rss+xml',
'rtf' => 'text/rtf',
'rtx' => 'text/richtext',
'sgm' => 'text/sgml',
'sgml' => 'text/sgml',
'sh' => 'application/x-sh',
'shar' => 'application/x-shar',
'silo' => 'model/mesh',
'sit' => 'application/x-stuffit',
'skd' => 'application/x-koan',
'skm' => 'application/x-koan',
'skp' => 'application/x-koan',
'skt' => 'application/x-koan',
'smi' => 'application/smil',
'smil' => 'application/smil',
'snd' => 'audio/basic',
'so' => 'application/octet-stream',
'spl' => 'application/x-futuresplash',
'src' => 'application/x-wais-source',
'sv4cpio' => 'application/x-sv4cpio',
'sv4crc' => 'application/x-sv4crc',
'svg' => 'image/svg+xml',
'svgz' => 'image/svg+xml',
'swf' => 'application/x-shockwave-flash',
't' => 'application/x-troff',
'tar' => 'application/x-tar',
'tcl' => 'application/x-tcl',
'tex' => 'application/x-tex',
'texi' => 'application/x-texinfo',
'texinfo' => 'application/x-texinfo',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'tr' => 'application/x-troff',
'tsv' => 'text/tab-separated-values',
'txt' => 'text/plain',
'ustar' => 'application/x-ustar',
'vcd' => 'application/x-cdlink',
'vrml' => 'model/vrml',
'vxml' => 'application/voicexml+xml',
'wav' => 'audio/x-wav',
'wbmp' => 'image/vnd.wap.wbmp',
'wbxml' => 'application/vnd.wap.wbxml',
'wml' => 'text/vnd.wap.wml',
'wmlc' => 'application/vnd.wap.wmlc',
'wmls' => 'text/vnd.wap.wmlscript',
'wmlsc' => 'application/vnd.wap.wmlscriptc',
'wrl' => 'model/vrml',
'xbm' => 'image/x-xbitmap',
'xht' => 'application/xhtml+xml',
'xhtml' => 'application/xhtml+xml',
'xls' => 'application/vnd.ms-excel',
'xml' => 'application/xml',
'xpm' => 'image/x-xpixmap',
'xsl' => 'application/xml',
'xslt' => 'application/xslt+xml',
'xul' => 'application/vnd.mozilla.xul+xml',
'xwd' => 'image/x-xwindowdump',
'xyz' => 'chemical/x-xyz',
'zip' => 'application/zip'
);
return is_null($ext) ? $types : $types[strtolower($ext)];
}
/**
* Detect MIME Content-type for a file
*
* @param string $filename Path to the tested file.
* @return string
*/
function file_mime_content_type($filename)
{
$ext = file_extension($filename); /* strtolower isn't necessary */
if($mime = mime_type($ext)) return $mime;
elseif (function_exists('finfo_open'))
{
if($finfo = finfo_open(FILEINFO_MIME))
{
if($mime = finfo_file($finfo, $filename))
{
finfo_close($finfo);
return $mime;
}
}
}
return 'application/octet-stream';
}
/**
* Read and output file content and return filesize in bytes or status after
* closing file.
* This function is very efficient for outputing large files without timeout
* nor too expensive memory use
*
* @param string $filename
* @param string $retbytes
* @return bool, int
*/
function file_read_chunked($filename, $retbytes = true)
{
$chunksize = 1*(1024*1024); // how many bytes per chunk
$buffer = '';
$cnt = 0;
$handle = fopen($filename, 'rb');
if ($handle === false) return false;
ob_start();
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
if ($retbytes) $cnt += strlen($buffer);
set_time_limit(0);
}
ob_end_flush();
$status = fclose($handle);
if ($retbytes && $status) return $cnt; // return num. bytes delivered like readfile() does.
return $status;
}
/**
* Create a file path by concatenation of given arguments.
* Windows paths with backslash directory separators are normalized in *nix paths.
*
* @param string $path, ...
* @return string normalized path
*/
function file_path($path)
{
$args = func_get_args();
$ds = '/';
$win_ds = '\';
$n_path = count($args) > 1 ? implode($ds, $args) : $path;
if(strpos($n_path, $win_ds) !== false) $n_path = str_replace( $win_ds, $ds, $n_path );
$n_path = preg_replace( "#$ds+#", $ds, $n_path);
return $n_path;
}
/**
* Returns file extension or false if none
*
* @param string $filename
* @return string, false
*/
function file_extension($filename)
{
$pos = strrpos($filename, '.');
if($pos !== false) return substr($filename, $pos + 1);
return false;
}
/**
* Checks if $filename is a text file
*
* @param string $filename
* @return bool
*/
function file_is_text($filename)
{
if($mime = file_mime_content_type($filename)) return substr($mime,0,5) == "text/";
return null;
}
/**
* Checks if $filename is a binary file
*
* @param string $filename
* @return void
*/
function file_is_binary($filename)
{
$is_text = file_is_text($filename);
return is_null($is_text) ? null : !$is_text;
}
/**
* Return or output file content
*
* @return string, int
*
**/
function file_read($filename, $return = false)
{
if(!file_exists($filename)) trigger_error("$filename doesn't exists", E_USER_ERROR);
if($return) return file_get_contents($filename);
return file_read_chunked($filename);
}
/**
* Returns an array of files contained in a directory
*
* @param string $dir
* @return array
*/
function file_list_dir($dir)
{
$files = array();
if ($handle = opendir($dir))
{
while (false !== ($file = readdir($handle)))
{
if ($file[0] != "." && $file != "..") $files[] = $file;
}
closedir($handle);
}
return $files;
}
## Extra utils ________________________________________________________________
if(!function_exists('array_replace'))
{
/**
* For PHP 5 < 5.3.0 (backward compatibility)
* (from {@link http://www.php.net/manual/fr/function.array-replace.php#92549 this php doc. note})
*
* @see array_replace()
* @param string $array
* @param string $array1
* @return $array
*/
function array_replace( array &$array, array &$array1 )
{
$args = func_get_args();
$count = func_num_args();
for ($i = 0; $i < $count; ++$i)
{
if(is_array($args[$i]))
{
foreach ($args[$i] as $key => $val) $array[$key] = $val;
}
else
{
trigger_error(
__FUNCTION__ . '(): Argument #' . ($i+1) . ' is not an array',
E_USER_WARNING
);
return null;
}
}
return $array;
}
}
/**
* Check if a string is an url
*
* This implementation no longer requires
* {@link http://www.php.net/manual/en/book.filter.php the filter extenstion},
* so it will improve compatibility with older PHP versions.
*
* @param string $str
* @return false, str the string if true, false instead
*/
function filter_var_url($str)
{
$regexp = '@^https?://([-[:alnum:]]+\.)+[a-zA-Z]{2,6}(:[0-9]+)?(.*)?$@';
$options = array( "options" => array("regexp" => $regexp ));
return preg_match($regexp, $str) ? $str : false;
}
/**
* For PHP 5 < 5.1.0 (backward compatibility)
* (from {@link http://www.php.net/manual/en/function.htmlspecialchars-decode.php#82133})
*
* @param string $string
* @param string $quote_style, one of: ENT_COMPAT, ENT_QUOTES, ENT_NOQUOTES
* @return the decoded string
*/
function limonade_htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
{
$table = array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style));
if($quote_style === ENT_QUOTES)
$table['''] = $table['''] = '\'';
return strtr($string, $table);
}
if(!function_exists('htmlspecialchars_decode'))
{
function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
{
return limonade_htmlspecialchars_decode($string, $quote_style);
}
}
/**
* Called just after loading libs, it provides fallback for some
* functions if they don't exists.
*
*/
function fallbacks_for_not_implemented_functions()
{
if(!function_exists('json_encode'))
{
/**
* for PHP 5 < PHP 5.2.0
*
*/
function json_encode()
{
trigger_error(
__FUNCTION__ . '(): no JSON functions available. Please provide your own implementation of ' . __FUNCTION__ . '() in order to use it.', E_USER_WARNING
);
}
}
}
# ================================= END ================================== #
Did this file decode correctly?
Original Code
eval(gzinflate(substr(base64_decode('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'),10,-8)));
Function Calls
| substr | 1 |
| gzinflate | 1 |
| base64_decode | 1 |
Stats
| MD5 | 7868a0e2e55b644446ea2b8119463ea8 |
| Eval Count | 1 |
| Decode Time | 145 ms |