1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/hotspot_ynh.git synced 2024-09-03 19:25:53 +02:00
hotspot_ynh/sources/lib/limonade.php

2707 lines
75 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
# ============================================================================ #
/**
* 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', 'PATCH'
*
* 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);
$varname = "_$method";
if ((isset($_SERVER['CONTENT_TYPE'])) && (strpos($_SERVER['CONTENT_TYPE'], 'application/json') === 0))
{
// handle PUT/POST requests which have JSON in request body
$GLOBALS[$varname] = json_decode(file_get_contents('php://input'), true);
}
elseif($method == 'PUT' || $method == 'DELETE')
{
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";
}
/**
* Checks if request method is PATCH
*
* @param string $env
* @return bool
*/
function request_is_patch($env = null)
{
return request_method($env) == "PATCH";
}
/**
* Returns allowed request methods
*
* @return array
*/
function request_methods()
{
return array("GET","POST","PUT","DELETE","HEAD","PATCH");
}
/**
* 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 a PATCH route
*
* @param string $path_or_array
* @param string $callback
* @param array $options (optional). See {@link route()} for available options.
* @return void
*/
function dispatch_patch($path_or_array, $callback, $options = array())
{
route("PATCH", $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 ?
'?' : '&amp;';
$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>\n" . 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.
* If extension is unknown, returns null.
* (extracted from Orbit source http://orbit.luaforge.net/).
*
*
* @param string $ext
* @return string, array, null
*/
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'
);
if (is_null($ext)) return $types;
$lower_ext = strtolower($ext);
return isset($types[$lower_ext]) ? $types[$lower_ext] : null;
}
/**
* 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['&#039;'] = $table['&#39;'] = '\'';
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 ================================== #