Source for file limonade.php

Documentation is available at limonade.php

  1. <?php
  2.                                                                   
  3. # ============================================================================ #
  4.  
  5. /**
  6.  *  L I M O N A D E
  7.  * 
  8.  *  a PHP micro framework.
  9.  * 
  10.  *  For more informations: {@link http://github/sofadesign/limonade}
  11.  *  
  12.  *  @author Fabrice Luraine
  13.  *  @copyright Copyright (c) 2009 Fabrice Luraine
  14.  *  @license http://opensource.org/licenses/mit-license.php The MIT License
  15.  *  @package limonade
  16.  */
  17.  
  18. #   -----------------------------------------------------------------------    #
  19. #    Copyright (c) 2009 Fabrice Luraine                                        #
  20. #                                                                              #
  21. #    Permission is hereby granted, free of charge, to any person               #
  22. #    obtaining a copy of this software and associated documentation            #
  23. #    files (the "Software"), to deal in the Software without                   #
  24. #    restriction, including without limitation the rights to use,              #
  25. #    copy, modify, merge, publish, distribute, sublicense, and/or sell         #
  26. #    copies of the Software, and to permit persons to whom the                 #
  27. #    Software is furnished to do so, subject to the following                  #
  28. #    conditions:                                                               #
  29. #                                                                              #
  30. #    The above copyright notice and this permission notice shall be            #
  31. #    included in all copies or substantial portions of the Software.           #
  32. #                                                                              #
  33. #    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,           #
  34. #    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES           #
  35. #    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND                  #
  36. #    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT               #
  37. #    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,              #
  38. #    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING              #
  39. #    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR             #
  40. #    OTHER DEALINGS IN THE SOFTWARE.                                           #
  41. # ============================================================================ # 
  42.  
  43.  
  44.  
  45.  
  46.  
  47.  
  48.  
  49.  
  50. # ============================================================================ #
  51. #    0. PREPARE                                                                #
  52. # ============================================================================ #
  53.  
  54. ## CONSTANTS __________________________________________________________________
  55. /**
  56.  * Limonade version
  57.  */
  58. define('LIMONADE',              '0.5.0');
  59. define('LIM_NAME',              'Un grand cru qui sait se faire attendre');
  60. define('LIM_START_MICROTIME',   (float)substr(microtime()010));
  61. define('LIM_SESSION_NAME',      'LIMONADE'.str_replace('.','x',LIMONADE));
  62. define('LIM_SESSION_FLASH_KEY''_lim_flash_messages');
  63. define('LIM_START_MEMORY',      memory_get_usage());
  64. define('E_LIM_HTTP',            32768);
  65. define('E_LIM_PHP',             65536);
  66. define('E_LIM_DEPRECATED',      35000);
  67. define('NOT_FOUND',             404);
  68. define('SERVER_ERROR',          500);
  69. define('ENV_PRODUCTION',        10);
  70. define('ENV_DEVELOPMENT',       100);
  71. define('X-SENDFILE',            10);
  72. define('X-LIGHTTPD-SEND-FILE',  20);
  73.  
  74. # for PHP 5.3.0 <
  75. if(!defined('E_DEPRECATED'))      define('E_DEPRECATED'8192);
  76. if(!defined('E_USER_DEPRECATED')) define('E_USER_DEPRECATED'16384);
  77.  
  78.  
  79. ## SETTING BASIC SECURITY _____________________________________________________
  80.  
  81. # A. Unsets all global variables set from a superglobal array
  82.  
  83. /**
  84.  * @access private
  85.  * @return void 
  86.  */
  87. function unregister_globals()
  88. {
  89.   $args func_get_args();
  90.   foreach($args as $k => $v)
  91.     if(array_key_exists($k$GLOBALS)) unset($GLOBALS[$k]);
  92. }
  93.  
  94. if(ini_get('register_globals'))
  95. {
  96.   unregister_globals'_POST''_GET''_COOKIE''_REQUEST''_SERVER'
  97.                       '_ENV''_FILES');
  98.   ini_set('register_globals'0);
  99. }
  100.  
  101. # B. removing magic quotes
  102.  
  103. /**
  104.  * @access private
  105.  * @param string $array 
  106.  * @return array 
  107.  */
  108. function remove_magic_quotes($array)
  109. {
  110.   foreach ($array as $k => $v)
  111.     $array[$kis_array($vremove_magic_quotes($vstripslashes($v);
  112.   return $array;
  113. }
  114.  
  115. {
  116.   $_GET    remove_magic_quotes($_GET);
  117.   $_POST   remove_magic_quotes($_POST);
  118.   $_COOKIE remove_magic_quotes($_COOKIE);
  119.   ini_set('magic_quotes_gpc'0);
  120. }
  121.  
  122. if(function_exists('set_magic_quotes_runtime'&& get_magic_quotes_runtime()) set_magic_quotes_runtime(false);
  123.  
  124. # C. Disable error display
  125. #    by default, no error reporting; it will be switched on later in run().
  126. #    ini_set('display_errors', 1); must be called explicitly in app file
  127. #    if you want to show errors before running app
  128. ini_set('display_errors'0);
  129.  
  130. ## SETTING INTERNAL ROUTES _____________________________________________________
  131.  
  132. dispatch(array("/_lim_css/*.css"array('_lim_css_filename'))'render_limonade_css');
  133.   /**
  134.    * Internal controller that responds to route /_lim_css/*.css
  135.    *
  136.    * @access private
  137.    * @return string 
  138.    */
  139.   function render_limonade_css()
  140.   {
  141.     option('views_dir'file_path(option('limonade_public_dir')'css'));
  142.     $fpath file_path(params('_lim_css_filename').".css");
  143.     return css($fpathnull)// with no layout
  144.   }
  145.  
  146. dispatch(array("/_lim_public/**"array('_lim_public_file'))'render_limonade_file');
  147.   /**
  148.    * Internal controller that responds to route /_lim_public/**
  149.    *
  150.    * @access private
  151.    * @return void 
  152.    */
  153.   function render_limonade_file()
  154.   {
  155.     $fpath file_path(option('limonade_public_dir')params('_lim_public_file'));
  156.     return render_file($fpathtrue);
  157.   }
  158.  
  159.  
  160.  
  161.  
  162.                                      # # #
  163.  
  164.  
  165.  
  166.  
  167. # ============================================================================ #
  168. #    1. BASE                                                                   #
  169. # ============================================================================ #
  170.  
  171. ## ABSTRACTS ___________________________________________________________________
  172.  
  173. # Abstract methods that might be redefined by user:
  174. #
  175. # - function configure(){}
  176. # - function initialize(){}
  177. # - function autoload_controller($callback){}
  178. # - function before($route){}
  179. # - function after($output, $route){}
  180. # - function not_found($errno, $errstr, $errfile=null, $errline=null){}
  181. # - function server_error($errno, $errstr, $errfile=null, $errline=null){}
  182. # - function route_missing($request_method, $request_uri){}
  183. # - function before_exit(){}
  184. # - function before_render($content_or_func, $layout, $locals, $view_path){}
  185. # - function autorender($route){}
  186. #
  187. # See abstract.php for more details.
  188.  
  189.  
  190. ## MAIN PUBLIC FUNCTIONS _______________________________________________________
  191.  
  192. /**
  193.  * Set and returns options values
  194.  * 
  195.  * If multiple values are provided, set $name option with an array of those values.
  196.  * If there is only one value, set $name option with the provided $values
  197.  *
  198.  * @param string $name 
  199.  * @param mixed  $values,... 
  200.  * @return mixed option value for $name if $name argument is provided, else return all options
  201.  */
  202. function option($name null$values null)
  203. {
  204.   static $options array();
  205.   $args func_get_args();
  206.   $name array_shift($args);
  207.   if(is_null($name)) return $options;
  208.   if(!empty($args))
  209.   {
  210.     $options[$namecount($args$args $args[0];
  211.   }
  212.   if(array_key_exists($name$options)) return $options[$name];
  213.   return;
  214. }
  215.  
  216. /**
  217.  * Set and returns params
  218.  * 
  219.  * Depending on provided arguments:
  220.  * 
  221.  *  * Reset params if first argument is null
  222.  * 
  223.  *  * If first argument is an array, merge it with current params
  224.  * 
  225.  *  * If there is a second argument $value, set param $name (first argument) with $value
  226.  * <code>
  227.  *  params('name', 'Doe') // set 'name' => 'Doe'
  228.  * </code>
  229.  *  * If there is more than 2 arguments, set param $name (first argument) value with
  230.  *    an array of next arguments
  231.  * <code>
  232.  *  params('months', 'jan', 'feb', 'mar') // set 'month' => array('months', 'jan', 'feb', 'mar')
  233.  * </code>
  234.  * 
  235.  * @param mixed $name_or_array_or_null could be null || array of params || name of a param (optional)
  236.  * @param mixed $value,... for the $name param (optional)
  237.  * @return mixed all params, or one if a first argument $name is provided
  238.  */
  239. function params($name_or_array_or_null null$value null)
  240. {
  241.   static $params array();
  242.   $args func_get_args();
  243.  
  244.   if(func_num_args(0)
  245.   {
  246.     $name array_shift($args);
  247.     if(is_null($name))
  248.     {
  249.       # Reset params
  250.       $params array();
  251.       return $params;
  252.     }
  253.     if(is_array($name))
  254.     {
  255.       $params array_merge($params$name);
  256.       return $params;
  257.     }
  258.     $nargs count($args);
  259.     if($nargs 0)
  260.     {
  261.       $value $nargs $args $args[0];
  262.       $params[$name$value;
  263.     }
  264.     return array_key_exists($name,$params$params[$namenull;
  265.   }
  266.  
  267.   return $params;
  268. }
  269.  
  270. /**
  271.  * Set and returns template variables
  272.  * 
  273.  * If multiple values are provided, set $name variable with an array of those values.
  274.  * If there is only one value, set $name variable with the provided $values
  275.  *
  276.  * @param string $name 
  277.  * @param mixed  $values,... 
  278.  * @return mixed variable value for $name if $name argument is provided, else return all variables
  279.  */
  280. function set($name null$values null)
  281. {
  282.   static $vars array();
  283.   $args func_get_args();
  284.   $name array_shift($args);
  285.   if(is_null($name)) return $vars;
  286.   if(!empty($args))
  287.   {
  288.     $vars[$namecount($args$args $args[0];
  289.   }
  290.   if(array_key_exists($name$vars)) return $vars[$name];
  291.   return $vars;
  292. }
  293.  
  294. /**
  295.  * Sets a template variable with a value or a default value if value is empty
  296.  *
  297.  * @param string $name 
  298.  * @param string $value 
  299.  * @param string $default 
  300.  * @return mixed setted value
  301.  */
  302. function set_or_default($name$value$default)
  303. {
  304.   return set($namevalue_or_default($value$default));
  305. }
  306.  
  307. /**
  308.  * Running application
  309.  *
  310.  * @param string $env 
  311.  * @return void 
  312.  */
  313. function run($env null)
  314. {
  315.   if(is_null($env)) $env env();
  316.    
  317.   # 0. Set default configuration
  318.   $root_dir  dirname(app_file());
  319.   $base_path dirname(file_path($env['SERVER']['SCRIPT_NAME']));
  320.   $base_file basename($env['SERVER']['SCRIPT_NAME']);
  321.   $base_uri  file_path($base_path(($base_file == 'index.php''?' $base_file.'?'));
  322.   $lim_dir   dirname(__FILE__);
  323.   option('root_dir',           $root_dir);
  324.   option('base_path',          $base_path);
  325.   option('base_uri',           $base_uri)// set it manually if you use url_rewriting
  326.   option('limonade_dir',       file_path($lim_dir));
  327.   option('limonade_views_dir'file_path($lim_dir'limonade''views'));
  328.   option('limonade_public_dir',file_path($lim_dir'limonade''public'));
  329.   option('public_dir',         file_path($root_dir'public'));
  330.   option('views_dir',          file_path($root_dir'views'));
  331.   option('controllers_dir',    file_path($root_dir'controllers'));
  332.   option('lib_dir',            file_path($root_dir'lib'));
  333.   option('error_views_dir',    option('limonade_views_dir'));
  334.   option('env',                ENV_PRODUCTION);
  335.   option('debug',              true);
  336.   option('session',            LIM_SESSION_NAME)// true, false or the name of your session
  337.   option('encoding',           'utf-8');
  338.   option('signature',          LIM_NAME)// X-Limonade header value or false to hide it
  339.   option('gzip',               false);
  340.   option('x-sendfile',         0)// 0: disabled, 
  341.                                    // X-SENDFILE: for Apache and Lighttpd v. >= 1.5,
  342.                                    // X-LIGHTTPD-SEND-FILE: for Apache and Lighttpd v. < 1.5
  343.  
  344.   # 1. Set handlers
  345.   # 1.1 Set error handling
  346.   ini_set('display_errors'1);
  347.   set_error_handler('error_handler_dispatcher'E_ALL E_NOTICE);
  348.   
  349.   # 1.2 Register shutdown function
  350.   register_shutdown_function('stop_and_exit');
  351.  
  352.   # 2. Set user configuration
  353.   call_if_exists('configure');
  354.   
  355.   # 2.1 Set gzip compression if defined
  356.   if(is_bool(option('gzip')) && option('gzip'))
  357.   {
  358.     ini_set('zlib.output_compression''1');
  359.   }
  360.   
  361.   # 2.2 Set X-Limonade header
  362.   if($signature option('signature')) header("X-Limonade: $signature");
  363.  
  364.   # 3. Loading libs
  365.   require_once_dir(option('lib_dir'));
  366.  
  367.   # 4. Starting session
  368.   if(!defined('SID'&& option('session'))
  369.   {
  370.     if(!is_bool(option('session'))) session_name(option('session'));
  371.     if(!session_start()) trigger_error("An error occured while trying to start the session"E_USER_WARNING);
  372.   }
  373.  
  374.   # 5. Set some default methods if needed
  375.   if(!function_exists('after'))
  376.   {
  377.     function after($output)
  378.     {
  379.       return $output;
  380.     }
  381.   }
  382.   if(!function_exists('route_missing'))
  383.   {
  384.     function route_missing($request_method$request_uri)
  385.     {
  386.       halt(NOT_FOUND"($request_method$request_uri");
  387.     }
  388.   }
  389.  
  390.   call_if_exists('initialize');
  391.  
  392.   # 6. Check request
  393.   if($rm request_method($env))
  394.   {
  395.     if(request_is_head($env)) ob_start()// then no output
  396.  
  397.     if(!request_method_is_allowed($rm))
  398.       halt(HTTP_NOT_IMPLEMENTED"The requested method <code>'$rm'</code> is not implemented");
  399.  
  400.     # 6.1 Check matching route
  401.     if($route route_find($rmrequest_uri($env)))
  402.     {
  403.       params($route['params']);
  404.  
  405.       # 6.2 Load controllers dir
  406.       if(!function_exists('autoload_controller'))
  407.       {
  408.         function autoload_controller($callback)
  409.         {
  410.           require_once_dir(option('controllers_dir'));
  411.         }
  412.       }
  413.       autoload_controller($route['callback']);
  414.  
  415.       if(is_callable($route['callback']))
  416.       {
  417.         # 6.3 Call before function
  418.         call_if_exists('before'$route);
  419.  
  420.         # 6.4 Call matching controller function and output result
  421.         $output call_user_func_array($route['callback']array_values($route['params']));
  422.         if(is_null($output)) $output call_if_exists('autorender'$route);
  423.         echo after(error_notices_render($output$route);
  424.       }
  425.       else halt(SERVER_ERROR"Routing error: undefined function '{$route['callback']}'"$route);      
  426.     }
  427.     else route_missing($rmrequest_uri($env));
  428.  
  429.   }
  430.   else halt(HTTP_NOT_IMPLEMENTED"The requested method <code>'$rm'</code> is not implemented");
  431. }
  432.  
  433. /**
  434.  * Stop and exit limonade application
  435.  *
  436.  * @access private
  437.  * @param boolean exit or not
  438.  * @return void 
  439.  */
  440. function stop_and_exit($exit true)
  441. {
  442.   call_if_exists('before_exit'$exit);
  443.   $flash_sweep true;
  444.   $headers headers_list();
  445.   foreach($headers as $header)
  446.   {
  447.     // If a Content-Type header exists, flash_sweep only if is text/html
  448.     // Else if there's no Content-Type header, flash_sweep by default
  449.     if(stripos($header'Content-Type:'=== 0)
  450.     {
  451.       $flash_sweep stripos($header'Content-Type: text/html'=== 0;
  452.       break;
  453.     }
  454.   }
  455.   if($flash_sweepflash_sweep();
  456.   if(defined('SID')) session_write_close();
  457.   if($exitexit;
  458. }
  459.  
  460. /**
  461.  * Returns limonade environment variables:
  462.  *
  463.  * 'SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE',
  464.  * 'GET', 'POST', 'PUT', 'DELETE'
  465.  * 
  466.  * If a null argument is passed, reset and rebuild environment
  467.  *
  468.  * @param null @reset reset and rebuild environment
  469.  * @return array 
  470.  */
  471. function env($reset null)
  472. {
  473.   static $env array();
  474.   if(func_num_args(0)
  475.   {
  476.     $args func_get_args();
  477.     if(is_null($args[0])) $env array();
  478.   }
  479.  
  480.   if(empty($env))
  481.   {
  482.     if(empty($GLOBALS['_SERVER']))
  483.     {
  484.       // Fixing empty $GLOBALS['_SERVER'] bug 
  485.       // http://sofadesign.lighthouseapp.com/projects/29612-limonade/tickets/29-env-is-empty
  486.       $GLOBALS['_SERVER']  =$_SERVER;
  487.       $GLOBALS['_FILES']   =$_FILES;
  488.       $GLOBALS['_REQUEST'=$_REQUEST;
  489.       $GLOBALS['_SESSION'=$_SESSION;
  490.       $GLOBALS['_ENV']     =$_ENV;
  491.       $GLOBALS['_COOKIE']  =$_COOKIE;
  492.     }
  493.  
  494.     $glo_names array('SERVER''FILES''REQUEST''SESSION''ENV''COOKIE');
  495.  
  496.     $vars array_merge($glo_namesrequest_methods());
  497.     foreach($vars as $var)
  498.     {
  499.       $varname "_$var";
  500.       if(!array_key_exists($varname$GLOBALS)) $GLOBALS[$varnamearray();
  501.       $env[$var=$GLOBALS[$varname];
  502.     }
  503.  
  504.     $method request_method($env);
  505.     if($method == 'PUT' || $method == 'DELETE')
  506.     {
  507.       $varname "_$method";
  508.       if(array_key_exists('_method'$_POST&& $_POST['_method'== $method)
  509.       {
  510.         foreach($_POST as $k => $v)
  511.         {
  512.           if($k == "_method"continue;
  513.           $GLOBALS[$varname][$k$v;
  514.         }
  515.       }
  516.       else
  517.       {
  518.         parse_str(file_get_contents('php://input')$GLOBALS[$varname]);
  519.       }
  520.     }
  521.   }
  522.   return $env;
  523. }
  524.  
  525. /**
  526.  * Returns application root file path
  527.  *
  528.  * @return string 
  529.  */
  530. function app_file()
  531. {
  532.   static $file;
  533.   if(empty($file))
  534.   {
  535.     $debug_backtrace debug_backtrace();
  536.     $stacktrace array_pop($debug_backtrace);
  537.     $file $stacktrace['file'];
  538.   }
  539.   return file_path($file);
  540. }
  541.  
  542.  
  543.  
  544.  
  545.                                      # # #
  546.  
  547.  
  548.  
  549.  
  550. # ============================================================================ #
  551. #    2. ERROR                                                                  #
  552. # ============================================================================ #
  553.  
  554. /**
  555.  * Associate a function with error code(s) and return all associations
  556.  *
  557.  * @param string $errno 
  558.  * @param string $function 
  559.  * @return array 
  560.  */
  561. function error($errno null$function null)
  562. {
  563.   static $errors array();
  564.   if(func_num_args(0)
  565.   {
  566.     $errors[array('errno'=>$errno'function'=> $function);
  567.   }
  568.   return $errors;
  569. }
  570.  
  571. /**
  572.  * Raise an error, passing a given error number and an optional message,
  573.  * then exit.
  574.  * Error number should be a HTTP status code or a php user error (E_USER...)
  575.  * $errno and $msg arguments can be passsed in any order
  576.  * If no arguments are passed, default $errno is SERVER_ERROR (500)
  577.  *
  578.  * @param int,string $errno Error number or message string
  579.  * @param string,string $msg Message string or error number
  580.  * @param mixed $debug_args extra data provided for debugging
  581.  * @return void 
  582.  */
  583. function halt($errno SERVER_ERROR$msg ''$debug_args null)
  584. {
  585.   $args func_get_args();
  586.   $error array_shift($args);
  587.  
  588.   # switch $errno and $msg args
  589.   # TODO cleanup / refactoring
  590.   if(is_string($errno))
  591.   {
  592.    $msg $errno;
  593.    $oldmsg array_shift($args);
  594.    $errno empty($oldmsgSERVER_ERROR $oldmsg;
  595.   }
  596.   else if(!empty($args)) $msg array_shift($args);
  597.  
  598.   if(empty($msg&& $errno == NOT_FOUND$msg request_uri();
  599.   if(empty($msg)) $msg "";
  600.   if(!empty($args)) $debug_args $args;
  601.   set('_lim_err_debug_args'$debug_args);
  602.  
  603.   error_handler_dispatcher($errno$msgnullnull);
  604.  
  605. }
  606.  
  607. /**
  608.  * Internal error handler dispatcher
  609.  * Find and call matching error handler and exit
  610.  * If no match found, call default error handler
  611.  *
  612.  * @access private
  613.  * @param int $errno 
  614.  * @param string $errstr 
  615.  * @param string $errfile 
  616.  * @param string $errline 
  617.  * @return void 
  618.  */
  619. function error_handler_dispatcher($errno$errstr$errfile$errline)
  620. {
  621.   $back_trace debug_backtrace();
  622.   while($trace array_shift($back_trace))
  623.   {
  624.     if($trace['function'== 'halt')
  625.     {
  626.       $errfile $trace['file'];
  627.       $errline $trace['line'];
  628.       break;
  629.     }
  630.   }  
  631.  
  632.   # Notices and warning won't halt execution
  633.   if(error_wont_halt_app($errno))
  634.   {
  635.     error_notice($errno$errstr$errfile$errline);
  636.       return;
  637.   }
  638.   else
  639.   {
  640.     # Other errors will stop application
  641.     static $handlers array();
  642.     if(empty($handlers))
  643.     {
  644.       error(E_LIM_PHP'error_default_handler');
  645.       $handlers error();
  646.     }
  647.     
  648.     $is_http_err http_response_status_is_valid($errno);
  649.     while($handler array_shift($handlers))
  650.     {
  651.       $e is_array($handler['errno']$handler['errno'array($handler['errno']);
  652.       while($ee array_shift($e))
  653.       {
  654.         if($ee == $errno || $ee == E_LIM_PHP || ($ee == E_LIM_HTTP && $is_http_err))
  655.         {
  656.           echo call_if_exists($handler['function']$errno$errstr$errfile$errline);
  657.           exit;
  658.         }
  659.       }
  660.     }
  661.   }
  662. }
  663.  
  664.  
  665. /**
  666.  * Default error handler
  667.  *
  668.  * @param string $errno 
  669.  * @param string $errstr 
  670.  * @param string $errfile 
  671.  * @param string $errline 
  672.  * @return string error output
  673.  */
  674. function error_default_handler($errno$errstr$errfile$errline)
  675. {
  676.   $is_http_err http_response_status_is_valid($errno);
  677.   $http_error_code $is_http_err $errno SERVER_ERROR;
  678.  
  679.   status($http_error_code);
  680.  
  681.   return $http_error_code == NOT_FOUND ?
  682.             error_not_found_output($errno$errstr$errfile$errline:
  683.             error_server_error_output($errno$errstr$errfile$errline);                    
  684. }
  685.  
  686. /**
  687.  * Returns not found error output
  688.  *
  689.  * @access private
  690.  * @param string $msg 
  691.  * @return string 
  692.  */
  693. function error_not_found_output($errno$errstr$errfile$errline)
  694. {
  695.   if(!function_exists('not_found'))
  696.   {
  697.     /**
  698.      * Default not found error output
  699.      *
  700.      * @param string $errno 
  701.      * @param string $errstr 
  702.      * @param string $errfile 
  703.      * @param string $errline 
  704.      * @return string
  705.      */
  706.     function not_found($errno$errstr$errfile=null$errline=null)
  707.     {
  708.       option('views_dir'option('error_views_dir'));
  709.       $msg h(rawurldecode($errstr));
  710.       return html("<h1>Page not found:</h1><p><code>{$msg}</code></p>"error_layout());
  711.     }
  712.   }
  713.   return not_found($errno$errstr$errfile$errline);
  714. }
  715.  
  716. /**
  717.  * Returns server error output
  718.  *
  719.  * @access private
  720.  * @param int $errno 
  721.  * @param string $errstr 
  722.  * @param string $errfile 
  723.  * @param string $errline 
  724.  * @return string 
  725.  */
  726. function error_server_error_output($errno$errstr$errfile$errline)
  727. {
  728.   if(!function_exists('server_error'))
  729.   {
  730.     /**
  731.      * Default server error output
  732.      *
  733.      * @param string $errno 
  734.      * @param string $errstr 
  735.      * @param string $errfile 
  736.      * @param string $errline 
  737.      * @return string
  738.      */
  739.     function server_error($errno$errstr$errfile=null$errline=null)
  740.     {
  741.       $is_http_error http_response_status_is_valid($errno);
  742.       $args compact('errno''errstr''errfile''errline''is_http_error');
  743.       option('views_dir'option('limonade_views_dir'));
  744.       $html render('error.html.php'null$args);    
  745.       option('views_dir'option('error_views_dir'));
  746.       return html($htmlerror_layout()$args);
  747.     }
  748.   }
  749.   return server_error($errno$errstr$errfile$errline);
  750. }
  751.  
  752. /**
  753.  * Set and returns error output layout
  754.  *
  755.  * @param string $layout 
  756.  * @return string 
  757.  */
  758. function error_layout($layout false)
  759. {
  760.   static $o_layout 'default_layout.php';
  761.   if($layout !== false)
  762.   {
  763.     option('error_views_dir'option('views_dir'));
  764.     $o_layout $layout;
  765.   }
  766.   return $o_layout;
  767. }
  768.  
  769.  
  770. /**
  771.  * Set a notice if arguments are provided
  772.  * Returns all stored notices.
  773.  * If $errno argument is null, reset the notices array
  774.  *
  775.  * @access private
  776.  * @param string, null $str
  777.  * @return array 
  778.  */
  779. function error_notice($errno false$errstr null$errfile null$errline null)
  780. {
  781.   static $notices array();
  782.   if($errno$notices[compact('errno''errstr''errfile''errline');
  783.   else if(is_null($errno)) $notices array();
  784.   return $notices;
  785. }
  786.  
  787. /**
  788.  * Returns notices output rendering and reset notices
  789.  *
  790.  * @return string 
  791.  */
  792. {
  793.   if(option('debug'&& option('env'ENV_PRODUCTION)
  794.   {
  795.     $notices error_notice();
  796.     error_notice(null)// reset notices
  797.     $c_view_dir option('views_dir')// keep for restore after render
  798.     option('views_dir'option('limonade_views_dir'));
  799.     $o render('_notices.html.php'nullarray('notices' => $notices));
  800.     option('views_dir'$c_view_dir)// restore current views dir
  801.  
  802.     return $o;
  803.   }
  804. }
  805.  
  806. /**
  807.  * Checks if an error is will halt application execution.
  808.  * Notices and warnings will not.
  809.  *
  810.  * @access private
  811.  * @param string $num error code number
  812.  * @return boolean 
  813.  */
  814. function error_wont_halt_app($num)
  815. {
  816.   return $num == E_NOTICE ||
  817.          $num == E_WARNING ||
  818.          $num == E_CORE_WARNING ||
  819.          $num == E_COMPILE_WARNING ||
  820.          $num == E_USER_WARNING ||
  821.          $num == E_USER_NOTICE ||
  822.          $num == E_DEPRECATED ||
  823.          $num == E_USER_DEPRECATED ||
  824.          $num == E_LIM_DEPRECATED;
  825. }
  826.  
  827.  
  828.  
  829. /**
  830.  * return error code name for a given code num, or return all errors names
  831.  *
  832.  * @param string $num 
  833.  * @return mixed 
  834.  */
  835. function error_type($num null)
  836. {
  837.   $types array (
  838.               E_ERROR              => 'ERROR',
  839.               E_WARNING            => 'WARNING',
  840.               E_PARSE              => 'PARSING ERROR',
  841.               E_NOTICE             => 'NOTICE',
  842.               E_CORE_ERROR         => 'CORE ERROR',
  843.               E_CORE_WARNING       => 'CORE WARNING',
  844.               E_COMPILE_ERROR      => 'COMPILE ERROR',
  845.               E_COMPILE_WARNING    => 'COMPILE WARNING',
  846.               E_USER_ERROR         => 'USER ERROR',
  847.               E_USER_WARNING       => 'USER WARNING',
  848.               E_USER_NOTICE        => 'USER NOTICE',
  849.               E_STRICT             => 'STRICT NOTICE',
  850.               E_RECOVERABLE_ERROR  => 'RECOVERABLE ERROR',
  851.               E_DEPRECATED         => 'DEPRECATED WARNING',
  852.               E_USER_DEPRECATED    => 'USER DEPRECATED WARNING',
  853.               E_LIM_DEPRECATED     => 'LIMONADE DEPRECATED WARNING'
  854.               );
  855.   return is_null($num$types $types[$num];
  856. }
  857.  
  858. /**
  859.  * Returns http response status for a given error number
  860.  *
  861.  * @param string $errno 
  862.  * @return int 
  863.  */
  864. function error_http_status($errno)
  865. {
  866.   $code http_response_status_is_valid($errno$errno SERVER_ERROR;
  867.   return http_response_status($code);
  868. }
  869.  
  870.                                      # # #
  871.  
  872.  
  873.  
  874.  
  875. # ============================================================================ #
  876. #    3. REQUEST                                                                #
  877. # ============================================================================ #
  878.  
  879. /**
  880.  * Returns current request method for a given environment or current one
  881.  *
  882.  * @param string $env 
  883.  * @return string 
  884.  */
  885. function request_method($env null)
  886. {
  887.   if(is_null($env)) $env env();
  888.   $m array_key_exists('REQUEST_METHOD'$env['SERVER']$env['SERVER']['REQUEST_METHOD'null;
  889.   if($m == "POST" && array_key_exists('_method'$env['POST'])) 
  890.     $m strtoupper($env['POST']['_method']);
  891.   {
  892.     trigger_error("'$m' request method is unknown or unavailable."E_USER_WARNING);
  893.     $m false;
  894.   }
  895.   return $m;
  896. }
  897.  
  898. /**
  899.  * Checks if a request method or current one is allowed
  900.  *
  901.  * @param string $m 
  902.  * @return bool 
  903.  */
  904. function request_method_is_allowed($m null)
  905. {
  906.   if(is_null($m)) $m request_method();
  907.   return in_array(strtoupper($m)request_methods());
  908. }
  909.  
  910. /**
  911.  * Checks if request method is GET
  912.  *
  913.  * @param string $env 
  914.  * @return bool 
  915.  */
  916. function request_is_get($env null)
  917. {
  918.   return request_method($env== "GET";
  919. }
  920.  
  921. /**
  922.  * Checks if request method is POST
  923.  *
  924.  * @param string $env 
  925.  * @return bool 
  926.  */
  927. function request_is_post($env null)
  928. {
  929.   return request_method($env== "POST";
  930. }
  931.  
  932. /**
  933.  * Checks if request method is PUT
  934.  *
  935.  * @param string $env 
  936.  * @return bool 
  937.  */
  938. function request_is_put($env null)
  939. {
  940.   return request_method($env== "PUT";
  941. }
  942.  
  943. /**
  944.  * Checks if request method is DELETE
  945.  *
  946.  * @param string $env 
  947.  * @return bool 
  948.  */
  949. function request_is_delete($env null)
  950. {
  951.   return request_method($env== "DELETE";
  952. }
  953.  
  954. /**
  955.  * Checks if request method is HEAD
  956.  *
  957.  * @param string $env 
  958.  * @return bool 
  959.  */
  960. function request_is_head($env null)
  961. {
  962.   return request_method($env== "HEAD";
  963. }
  964.  
  965. /**
  966.  * Returns allowed request methods
  967.  *
  968.  * @return array 
  969.  */
  970. function request_methods()
  971. {
  972.   return array("GET","POST","PUT","DELETE""HEAD");
  973. }
  974.  
  975. /**
  976.  * Returns current request uri (the path that will be compared with routes)
  977.  * 
  978.  * (Inspired from codeigniter URI::_fetch_uri_string method)
  979.  *
  980.  * @return string 
  981.  */
  982. function request_uri($env null)
  983. {
  984.   static $uri null;
  985.   if(is_null($env))
  986.   {
  987.     if(!is_null($uri)) return $uri;
  988.     $env env();
  989.   }
  990.  
  991.   if(array_key_exists('uri'$env['GET']))
  992.   {
  993.     $uri $env['GET']['uri'];
  994.   }
  995.   else if(array_key_exists('u'$env['GET']))
  996.   {
  997.     $uri $env['GET']['u'];
  998.   }
  999.   // bug: dot are converted to _... so we can't use it...
  1000.   // else if (count($env['GET']) == 1 && trim(key($env['GET']), '/') != '')
  1001.   // {
  1002.   //  $uri = key($env['GET']);
  1003.   // }
  1004.   else
  1005.   {
  1006.     $app_file app_file();
  1007.     $path_info = isset($env['SERVER']['PATH_INFO']$env['SERVER']['PATH_INFO'@getenv('PATH_INFO');
  1008.     $query_string =  isset($env['SERVER']['QUERY_STRING']$env['SERVER']['QUERY_STRING'@getenv('QUERY_STRING');
  1009.  
  1010.     // Is there a PATH_INFO variable?
  1011.     // Note: some servers seem to have trouble with getenv() so we'll test it two ways
  1012.     if (trim($path_info'/'!= '' && $path_info != "/".$app_file)
  1013.     {
  1014.       if(strpos($path_info'&'!== 0)
  1015.       {
  1016.         # exclude GET params
  1017.         $params explode('&'$path_info);
  1018.         $path_info array_shift($params);
  1019.         # populate $_GET
  1020.         foreach($params as $param)
  1021.         {
  1022.           if(strpos($param'='0)
  1023.           {
  1024.             list($k$vexplode('='$param);
  1025.             $env['GET'][$k$v;
  1026.           }
  1027.         }
  1028.       }
  1029.       $uri $path_info;
  1030.     }
  1031.     // No PATH_INFO?... What about QUERY_STRING?
  1032.     elseif (trim($query_string'/'!= '')
  1033.     {
  1034.       $uri $query_string;
  1035.       $get $env['GET'];
  1036.       if(count($get0)
  1037.       {
  1038.         # exclude GET params
  1039.         $keys  array_keys($get);
  1040.         $first array_shift($keys);
  1041.         if(strpos($query_string$first=== 0$uri $first;
  1042.       }
  1043.     }
  1044.     elseif(array_key_exists('REQUEST_URI'$env['SERVER']&& !empty($env['SERVER']['REQUEST_URI']))
  1045.     {
  1046.       $request_uri rtrim(rawurldecode($env['SERVER']['REQUEST_URI'])'?/').'/';
  1047.       $base_path $env['SERVER']['SCRIPT_NAME'];
  1048.  
  1049.       if($request_uri."index.php" == $base_path$request_uri .= "index.php";
  1050.       $uri str_replace($base_path''$request_uri);
  1051.     }
  1052.     elseif($env['SERVER']['argc'&& trim($env['SERVER']['argv'][1]'/'!= '')
  1053.     {
  1054.       $uri $env['SERVER']['argv'][1];
  1055.     }
  1056.   }
  1057.  
  1058.   $uri rtrim($uri"/")# removes ending /
  1059.   if(empty($uri))
  1060.   {
  1061.     $uri '/';
  1062.   }
  1063.   else if($uri[0!= '/')
  1064.   {
  1065.     $uri '/' $uri# add a leading slash
  1066.   }
  1067.   return rawurldecode($uri);
  1068. }
  1069.  
  1070.  
  1071.  
  1072.  
  1073.                                      # # #
  1074.  
  1075.  
  1076.  
  1077.  
  1078. # ============================================================================ #
  1079. #    4. ROUTER                                                                 #
  1080. # ============================================================================ #
  1081.  
  1082. /**
  1083.  * An alias of {@link dispatch_get()}
  1084.  *
  1085.  * @return void 
  1086.  */
  1087. function dispatch($path_or_array$callback$options array())
  1088. {
  1089.   dispatch_get($path_or_array$callback$options);
  1090. }
  1091.  
  1092. /**
  1093.  * Add a GET route. Also automatically defines a HEAD route.
  1094.  *
  1095.  * @param string $path_or_array 
  1096.  * @param string $callback 
  1097.  * @param array $options (optional). See {@link route()} for available options.
  1098.  * @return void 
  1099.  */
  1100. function dispatch_get($path_or_array$callback$options array())
  1101. {
  1102.   route("GET"$path_or_array$callback$options);
  1103.   route("HEAD"$path_or_array$callback$options);
  1104. }
  1105.  
  1106. /**
  1107.  * Add a POST route
  1108.  *
  1109.  * @param string $path_or_array 
  1110.  * @param string $callback 
  1111.  * @param array $options (optional). See {@link route()} for available options.
  1112.  * @return void 
  1113.  */
  1114. function dispatch_post($path_or_array$callback$options array())
  1115. {
  1116.   route("POST"$path_or_array$callback$options);
  1117. }
  1118.  
  1119. /**
  1120.  * Add a PUT route
  1121.  *
  1122.  * @param string $path_or_array 
  1123.  * @param string $callback 
  1124.  * @param array $options (optional). See {@link route()} for available options.
  1125.  * @return void 
  1126.  */
  1127. function dispatch_put($path_or_array$callback$options array())
  1128. {
  1129.   route("PUT"$path_or_array$callback$options);
  1130. }
  1131.  
  1132. /**
  1133.  * Add a DELETE route
  1134.  *
  1135.  * @param string $path_or_array 
  1136.  * @param string $callback 
  1137.  * @param array $options (optional). See {@link route()} for available options.
  1138.  * @return void 
  1139.  */
  1140. function dispatch_delete($path_or_array$callback$options array())
  1141. {
  1142.   route("DELETE"$path_or_array$callback$options);
  1143. }
  1144.  
  1145.  
  1146. /**
  1147.  * Add route if required params are provided.
  1148.  * Delete all routes if null is passed as a unique argument
  1149.  * Return all routes
  1150.  * 
  1151.  * @see route_build()
  1152.  * @access private
  1153.  * @param string $method 
  1154.  * @param string|array$path_or_array 
  1155.  * @param callback $func 
  1156.  * @param array $options (optional). Available options:
  1157.  *    - 'params' key with an array of parameters: for parametrized routes.
  1158.  *      those parameters will be merged with routes parameters.
  1159.  * @return array 
  1160.  */
  1161. function route()
  1162. {
  1163.   static $routes array();
  1164.   $nargs func_num_args();
  1165.   if$nargs 0)
  1166.   {
  1167.     $args func_get_args();
  1168.     if($nargs === && is_null($args[0])) $routes array();
  1169.     else if($nargs 3trigger_error("Missing arguments for route()"E_USER_ERROR);
  1170.     else
  1171.     {
  1172.       $method        $args[0];
  1173.       $path_or_array $args[1];
  1174.       $func          $args[2];
  1175.       $options       $nargs $args[3array();
  1176.  
  1177.       $routes[route_build($method$path_or_array$func$options);
  1178.     }
  1179.   }
  1180.   return $routes;
  1181. }
  1182.  
  1183. /**
  1184.  * An alias of route(null): reset all routes
  1185.  * 
  1186.  * @access private
  1187.  * @return void 
  1188.  */
  1189. function route_reset()
  1190. {
  1191.   route(null);
  1192. }
  1193.  
  1194. /**
  1195.  * Build a route and return it
  1196.  *
  1197.  * @access private
  1198.  * @param string $method allowed http method (one of those returned by {@link request_methods()})
  1199.  * @param string|array$path_or_array 
  1200.  * @param callback $callback callback called when route is found. It can be
  1201.  *    a function, an object method, a static method or a closure.
  1202.  *    See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
  1203.  *    to learn more about callbacks.
  1204.  * @param array $options (optional). Available options:
  1205.  *    - 'params' key with an array of parameters: for parametrized routes.
  1206.  *      those parameters will be merged with routes parameters.
  1207.  * @return array array with keys "method", "pattern", "names", "callback", "options"
  1208.  */
  1209. function route_build($method$path_or_array$callback$options array())
  1210. {
  1211.   $method strtoupper($method);
  1212.   if(!in_array($methodrequest_methods())) 
  1213.     trigger_error("'$method' request method is unkown or unavailable."E_USER_WARNING);
  1214.  
  1215.   if(is_array($path_or_array))
  1216.   {
  1217.     $path  array_shift($path_or_array);
  1218.     $names $path_or_array[0];
  1219.   }
  1220.   else
  1221.   {
  1222.     $path  $path_or_array;
  1223.     $names array();
  1224.   }
  1225.  
  1226.   $single_asterisk_subpattern   "(?:/([^\/]*))?";
  1227.   $double_asterisk_subpattern   "(?:/(.*))?";
  1228.   $optionnal_slash_subpattern   "(?:/*?)";
  1229.   $no_slash_asterisk_subpattern "(?:([^\/]*))?";
  1230.  
  1231.   if($path[0== "^")
  1232.   {
  1233.     if($path{strlen($path1!= "$"$path .= "$";
  1234.      $pattern "#".$path."#i";
  1235.   }
  1236.   else if(empty($path|| $path == "/")
  1237.   {
  1238.     $pattern "#^".$optionnal_slash_subpattern."$#";
  1239.   }
  1240.   else
  1241.   {
  1242.     $parsed array();
  1243.     $elts explode('/'$path);
  1244.  
  1245.     $parameters_count 0;
  1246.  
  1247.     foreach($elts as $elt)
  1248.     {
  1249.       if(empty($elt)) continue;
  1250.  
  1251.       $name null
  1252.  
  1253.       # extracting double asterisk **
  1254.       if($elt == "**"):
  1255.         $parsed[$double_asterisk_subpattern;
  1256.         $name $parameters_count;
  1257.  
  1258.       # extracting single asterisk *
  1259.       elseif($elt == "*"):
  1260.         $parsed[$single_asterisk_subpattern;
  1261.         $name $parameters_count;
  1262.  
  1263.       # extracting named parameters :my_param 
  1264.       elseif($elt[0== ":"):
  1265.         if(preg_match('/^:([^\:]+)$/'$elt$matches))
  1266.         {
  1267.           $parsed[$single_asterisk_subpattern;
  1268.           $name $matches[1];
  1269.         };
  1270.  
  1271.       elseif(strpos($elt'*'!== false):
  1272.         $sub_elts explode('*'$elt);
  1273.         $parsed_sub array();
  1274.         foreach($sub_elts as $sub_elt)
  1275.         {
  1276.           $parsed_sub[preg_quote($sub_elt"#");
  1277.           $name $parameters_count;
  1278.         }
  1279.         // 
  1280.         $parsed["/".implode($no_slash_asterisk_subpattern$parsed_sub);
  1281.  
  1282.       else:
  1283.         $parsed["/".preg_quote($elt"#");
  1284.  
  1285.       endif;
  1286.  
  1287.       /* set parameters names */ 
  1288.       if(is_null($name)) continue;
  1289.       if(!array_key_exists($parameters_count$names|| is_null($names[$parameters_count]))
  1290.         $names[$parameters_count$name;
  1291.       $parameters_count++;
  1292.     }
  1293.  
  1294.     $pattern "#^".implode(''$parsed).$optionnal_slash_subpattern."?$#i";
  1295.   }
  1296.  
  1297.   return array"method"       => $method,
  1298.                 "pattern"      => $pattern,
  1299.                 "names"        => $names,
  1300.                 "callback"     => $callback,
  1301.                 "options"      => $options  );
  1302. }
  1303.  
  1304. /**
  1305.  * Find a route and returns it.
  1306.  * Parameters values extracted from the path are added and merged
  1307.  * with the default 'params' option of the route
  1308.  * If not found, returns false.
  1309.  * Routes are checked from first added to last added.
  1310.  *
  1311.  * @access private
  1312.  * @param string $method 
  1313.  * @param string $path 
  1314.  * @return array,false route array has same keys as route returned by
  1315.  *   {@link route_build()} ("method", "pattern", "names", "callback", "options")
  1316.  *   + the processed "params" key
  1317.  */
  1318. function route_find($method$path)
  1319. {
  1320.   $routes route();
  1321.   $method strtoupper($method);
  1322.   foreach($routes as $route)
  1323.   {
  1324.     if($method == $route["method"&& preg_match($route["pattern"]$path$matches))
  1325.     {
  1326.       $options $route["options"];
  1327.       $params array_key_exists('params'$options$options["params"array();
  1328.       if(count($matches1)
  1329.       {
  1330.         array_shift($matches);
  1331.         $n_matches count($matches);
  1332.         $names     array_values($route["names"]);
  1333.         $n_names   count($names);
  1334.         if$n_matches $n_names )
  1335.         {
  1336.           $a array_fill(0$n_names $n_matchesnull);
  1337.           $matches array_merge($matches$a);
  1338.         }
  1339.         else if$n_matches $n_names )
  1340.         {
  1341.           $names range($n_names$n_matches 1);
  1342.         }
  1343.         $params array_replace($paramsarray_combine($names$matches));
  1344.       }
  1345.       $route["params"$params;
  1346.       return $route;
  1347.     }
  1348.   }
  1349.   return false;
  1350. }
  1351.  
  1352.  
  1353.  
  1354.  
  1355.  
  1356. # ============================================================================ #
  1357. #    5. OUTPUT AND RENDERING                                                   #
  1358. # ============================================================================ #
  1359.  
  1360. /**
  1361.  * Returns a string to output
  1362.  * 
  1363.  * It might use a template file, a function, or a formatted string (like {@link sprintf()}).
  1364.  * It could be embraced by a layout or not.
  1365.  * Local vars can be passed in addition to variables made available with the {@link set()}
  1366.  * function.
  1367.  *
  1368.  * @param string $content_or_func 
  1369.  * @param string $layout 
  1370.  * @param string $locals 
  1371.  * @return string 
  1372.  */
  1373. function render($content_or_func$layout ''$locals array())
  1374. {
  1375.   $args func_get_args();
  1376.   $content_or_func array_shift($args);
  1377.   $layout count($argsarray_shift($argslayout();
  1378.   $view_path file_path(option('views_dir'),$content_or_func);
  1379.   
  1380.   if(function_exists('before_render'))
  1381.     list($content_or_func$layout$locals$view_pathbefore_render($content_or_func$layout$locals$view_path);    
  1382.   
  1383.   $vars array_merge(set()$locals);
  1384.  
  1385.   $flash flash_now();
  1386.   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);  
  1387.   else if(!empty($flash)) $vars['flash'$flash;
  1388.  
  1389.   $infinite_loop false;
  1390.  
  1391.   # Avoid infinite loop: this function is in the backtrace ?
  1392.   if(function_exists($content_or_func))
  1393.   {
  1394.     $back_trace debug_backtrace();
  1395.     while($trace array_shift($back_trace))
  1396.     {
  1397.       if($trace['function'== strtolower($content_or_func))
  1398.       {
  1399.         $infinite_loop true;
  1400.         break;
  1401.       }
  1402.     }
  1403.   }
  1404.  
  1405.   if(function_exists($content_or_func&& !$infinite_loop)
  1406.   {
  1407.     ob_start();
  1408.     call_user_func($content_or_func$vars);
  1409.     $content ob_get_clean();
  1410.   }
  1411.   elseif(file_exists($view_path))
  1412.   {
  1413.     ob_start();
  1414.     extract($vars);
  1415.     include $view_path;
  1416.     $content ob_get_clean();
  1417.   }
  1418.   else
  1419.   {
  1420.     if(substr_count($content_or_func'%'!== count($vars)) $content $content_or_func;
  1421.     else $content vsprintf($content_or_func$vars);
  1422.   }
  1423.  
  1424.   if(empty($layout)) return $content;
  1425.  
  1426.   return render($layoutnullarray('content' => $content));
  1427. }
  1428.  
  1429. /**
  1430.  * Returns a string to output
  1431.  * 
  1432.  * Shortcut to render with no layout.
  1433.  *
  1434.  * @param string $content_or_func 
  1435.  * @param string $locals 
  1436.  * @return string 
  1437.  */
  1438. function partial($content_or_func$locals array())
  1439. {
  1440.   return render($content_or_funcnull$locals);
  1441. }
  1442.  
  1443. /**
  1444.  * Returns html output with proper http headers
  1445.  *
  1446.  * @param string $content_or_func 
  1447.  * @param string $layout 
  1448.  * @param string $locals 
  1449.  * @return string 
  1450.  */ 
  1451. function html($content_or_func$layout ''$locals array())
  1452. {
  1453.   if(!headers_sent()) header('Content-Type: text/html; charset='.strtolower(option('encoding')));
  1454.   $args func_get_args();
  1455.   return call_user_func_array('render'$args);
  1456. }
  1457.  
  1458. /**
  1459.  * Set and return current layout
  1460.  *
  1461.  * @param string $function_or_file 
  1462.  * @return string 
  1463.  */
  1464. function layout($function_or_file null)
  1465. {
  1466.   static $layout null;
  1467.   if(func_num_args(0$layout $function_or_file;
  1468.   return $layout;
  1469. }
  1470.  
  1471. /**
  1472.  * Returns xml output with proper http headers
  1473.  *
  1474.  * @param string $content_or_func 
  1475.  * @param string $layout 
  1476.  * @param string $locals 
  1477.  * @return string 
  1478.  */
  1479. function xml($data)
  1480. {
  1481.   if(!headers_sent()) header('Content-Type: text/xml; charset='.strtolower(option('encoding')));
  1482.   $args func_get_args();
  1483.   return call_user_func_array('render'$args);
  1484. }
  1485.  
  1486. /**
  1487.  * Returns css output with proper http headers
  1488.  *
  1489.  * @param string $content_or_func 
  1490.  * @param string $layout 
  1491.  * @param string $locals 
  1492.  * @return string 
  1493.  */
  1494. function css($content_or_func$layout ''$locals array())
  1495. {
  1496.   if(!headers_sent()) header('Content-Type: text/css; charset='.strtolower(option('encoding')));
  1497.   $args func_get_args();
  1498.   return call_user_func_array('render'$args);
  1499. }
  1500.  
  1501. /**
  1502.  * Returns javacript output with proper http headers
  1503.  *
  1504.  * @param string $content_or_func 
  1505.  * @param string $layout 
  1506.  * @param string $locals 
  1507.  * @return string 
  1508.  */
  1509. function js($content_or_func$layout ''$locals array())
  1510. {
  1511.   if(!headers_sent()) header('Content-Type: application/javascript; charset='.strtolower(option('encoding')));
  1512.   $args func_get_args();
  1513.   return call_user_func_array('render'$args);
  1514. }
  1515.  
  1516. /**
  1517.  * Returns txt output with proper http headers
  1518.  *
  1519.  * @param string $content_or_func 
  1520.  * @param string $layout 
  1521.  * @param string $locals 
  1522.  * @return string 
  1523.  */
  1524. function txt($content_or_func$layout ''$locals array())
  1525. {
  1526.   if(!headers_sent()) header('Content-Type: text/plain; charset='.strtolower(option('encoding')));
  1527.   $args func_get_args();
  1528.   return call_user_func_array('render'$args);
  1529. }
  1530.  
  1531. /**
  1532.  * Returns json representation of data with proper http headers
  1533.  *
  1534.  * @param string $data 
  1535.  * @param int $json_option 
  1536.  * @return string 
  1537.  */
  1538. function json($data$json_option 0)
  1539. {
  1540.   if(!headers_sent()) header('Content-Type: application/json; charset='.strtolower(option('encoding')));
  1541.   return version_compare(PHP_VERSION'5.3.0''>='json_encode($data$json_optionjson_encode($data);
  1542. }
  1543.  
  1544. /**
  1545.  * undocumented function
  1546.  *
  1547.  * @param string $filename 
  1548.  * @param string $return 
  1549.  * @return mixed number of bytes delivered or file output if $return = true
  1550.  */
  1551. function render_file($filename$return false)
  1552. {
  1553.   # TODO implements X-SENDFILE headers
  1554.   // if($x-sendfile = option('x-sendfile'))
  1555.   // {
  1556.   //    // add a X-Sendfile header for apache and Lighttpd >= 1.5
  1557.   //    if($x-sendfile > X-SENDFILE) // add a X-LIGHTTPD-send-file header 
  1558.   //   
  1559.   // }
  1560.   // else
  1561.   // {
  1562.   //   
  1563.   // }
  1564.   $filename str_replace('../'''$filename);
  1565.   if(file_exists($filename))
  1566.   {
  1567.     $content_type mime_type(file_extension($filename));
  1568.     $header 'Content-type: '.$content_type;
  1569.     if(file_is_text($filename)) $header .= '; charset='.strtolower(option('encoding'));
  1570.     if(!headers_sent()) header($header);
  1571.     return file_read($filename$return);
  1572.   }
  1573.   else halt(NOT_FOUND"unknown filename $filename");
  1574. }
  1575.  
  1576.  
  1577.  
  1578.  
  1579.  
  1580.  
  1581.                                      # # #
  1582.  
  1583.  
  1584.  
  1585.  
  1586. # ============================================================================ #
  1587. #    6. HELPERS                                                                #
  1588. # ============================================================================ #
  1589.  
  1590. /**
  1591.  * Returns an url composed of params joined with /
  1592.  * A param can be a string or an array.
  1593.  * If param is an array, its members will be added at the end of the return url
  1594.  * as GET parameters "&key=value".
  1595.  *
  1596.  * @param string or array $param1, $param2 ...
  1597.  * @return string 
  1598.  */ 
  1599. function url_for($params null)
  1600. {
  1601.   $paths  array();
  1602.   $params func_get_args();
  1603.   $GET_params array();
  1604.   foreach($params as $param)
  1605.   {
  1606.     if(is_array($param))
  1607.     {
  1608.       $GET_params array_merge($GET_params$param);
  1609.       continue;
  1610.     }
  1611.     if(filter_var_url($param))
  1612.     {
  1613.       $paths[$param;
  1614.       continue;
  1615.     }
  1616.     $p explode('/',$param);
  1617.     foreach($p as $v)
  1618.     {
  1619.       if($v != ""$paths[str_replace('%23''#'rawurlencode($v));
  1620.     }
  1621.   }
  1622.  
  1623.   $path rtrim(implode('/'$paths)'/');
  1624.   
  1625.   if(!filter_var_url($path)) 
  1626.   {
  1627.     # it's a relative URL or an URL without a schema
  1628.     $base_uri option('base_uri');
  1629.     $path file_path($base_uri$path);
  1630.   }
  1631.   
  1632.   if(!empty($GET_params))
  1633.   {
  1634.     $is_first_qs_param true;
  1635.     $path_as_no_question_mark strpos($path'?'=== false;
  1636.       
  1637.     foreach($GET_params as $k => $v)
  1638.     {
  1639.       $qs_separator $is_first_qs_param && $path_as_no_question_mark 
  1640.                         '?' '&amp;'
  1641.       $path .= $qs_separator rawurlencode($k'=' rawurlencode($v);
  1642.       $is_first_qs_param false;
  1643.     }
  1644.   }
  1645.   
  1646.   if(DIRECTORY_SEPARATOR != '/'$path str_replace(DIRECTORY_SEPARATOR'/'$path);
  1647.  
  1648.   return $path;
  1649. }
  1650.  
  1651. /**
  1652.  * An alias of {@link htmlspecialchars()}.
  1653.  * If no $charset is provided, uses option('encoding') value
  1654.  *
  1655.  * @param string $str 
  1656.  * @param string $quote_style 
  1657.  * @param string $charset 
  1658.  * @return void 
  1659.  */
  1660. function h($str$quote_style ENT_NOQUOTES$charset null)
  1661. {
  1662.   if(is_null($charset)) $charset strtoupper(option('encoding'));
  1663.   return htmlspecialchars($str$quote_style$charset)
  1664. }
  1665.  
  1666. /**
  1667.  * Set and returns flash messages that will be available in the next action
  1668.  * via the {@link flash_now()} function or the view variable <code>$flash</code>.
  1669.  * 
  1670.  * If multiple values are provided, set <code>$name</code> variable with an array of those values.
  1671.  * If there is only one value, set <code>$name</code> variable with the provided $values
  1672.  * or if it's <code>$name</code> is an array, merge it with current messages.
  1673.  *
  1674.  * @param string, array $name
  1675.  * @param mixed  $values,... 
  1676.  * @return mixed variable value for $name if $name argument is provided, else return all variables
  1677.  */
  1678. function flash($name null$value null)
  1679. {
  1680.   if(!defined('SID')) trigger_error("Flash messages can't be used because session isn't enabled"E_USER_WARNING);
  1681.   static $messages array();
  1682.   $args func_get_args();
  1683.   $name array_shift($args);
  1684.   if(is_null($name)) return $messages;
  1685.   if(is_array($name)) return $messages array_merge($messages$name);
  1686.   if(!empty($args))
  1687.   {
  1688.     $messages[$namecount($args$args $args[0];
  1689.   }
  1690.   if(!array_key_exists($name$messages)) return null;
  1691.   else return $messages[$name];
  1692.   return $messages;
  1693. }
  1694.  
  1695. /**
  1696.  * Set and returns flash messages available for the current action, included those
  1697.  * defined in the previous action with {@link flash()}
  1698.  * Those messages will also be passed to the views and made available in the
  1699.  * <code>$flash</code> variable.
  1700.  * 
  1701.  * If multiple values are provided, set <code>$name</code> variable with an array of those values.
  1702.  * If there is only one value, set <code>$name</code> variable with the provided $values
  1703.  * or if it's <code>$name</code> is an array, merge it with current messages.
  1704.  *
  1705.  * @param string, array $name
  1706.  * @param mixed  $values,... 
  1707.  * @return mixed variable value for $name if $name argument is provided, else return all variables
  1708.  */
  1709. function flash_now($name null$value null)
  1710. {
  1711.   static $messages null;
  1712.   if(is_null($messages))
  1713.   {
  1714.     $fkey LIM_SESSION_FLASH_KEY;
  1715.     $messages array();
  1716.     if(defined('SID'&& array_key_exists($fkey$_SESSION)) $messages $_SESSION[$fkey];
  1717.   }
  1718.   $args func_get_args();
  1719.   $name array_shift($args);
  1720.   if(is_null($name)) return $messages;
  1721.   if(is_array($name)) return $messages array_merge($messages$name);
  1722.   if(!empty($args))
  1723.   {
  1724.     $messages[$namecount($args$args $args[0];
  1725.   }
  1726.   if(!array_key_exists($name$messages)) return null;
  1727.   else return $messages[$name];
  1728.   return $messages;
  1729. }
  1730.  
  1731. /**
  1732.  * Delete current flash messages in session, and set new ones stored with
  1733.  * flash function.
  1734.  * Called before application exit.
  1735.  *
  1736.  * @access private
  1737.  * @return void 
  1738.  */
  1739. function flash_sweep()
  1740. {
  1741.   if(defined('SID'))
  1742.   {
  1743.     $fkey LIM_SESSION_FLASH_KEY;
  1744.     $_SESSION[$fkeyflash();
  1745.   }
  1746. }
  1747.  
  1748. /**
  1749.  * Starts capturing block of text
  1750.  *
  1751.  * Calling without params stops capturing (same as end_content_for()).
  1752.  * After capturing the captured block is put into a variable
  1753.  * named $name for later use in layouts. If second parameter
  1754.  * is supplied, its content will be used instead of capturing
  1755.  * a block of text.
  1756.  *
  1757.  * @param string $name 
  1758.  * @param string $content 
  1759.  * @return void 
  1760.  */
  1761. function content_for($name null$content null)
  1762. {
  1763.   static $_name null;
  1764.   if(is_null($name&& !is_null($_name))
  1765.   {
  1766.     set($_nameob_get_clean());
  1767.     $_name null;    
  1768.   }
  1769.   elseif(!is_null($name&& !isset($content))
  1770.   {
  1771.     $_name $name;    
  1772.     ob_start();
  1773.   }
  1774.   elseif(isset($name$content))
  1775.   {
  1776.     set($name$content);
  1777.   }
  1778. }
  1779.  
  1780. /**
  1781.  * Stops capturing block of text
  1782.  *
  1783.  * @return void 
  1784.  */
  1785. function end_content_for()
  1786. {
  1787. }
  1788.  
  1789. /**
  1790.  * Shows current memory and execution time of the application.
  1791.  * 
  1792.  * @access public
  1793.  * @return array
  1794.  */
  1795. function benchmark()
  1796. {
  1797.   $current_mem_usage memory_get_usage();
  1798.   $execution_time microtime(LIM_START_MICROTIME;
  1799.   
  1800.   return array(
  1801.     'current_memory' => $current_mem_usage,
  1802.     'start_memory' => LIM_START_MEMORY,
  1803.     'average_memory' => (LIM_START_MEMORY $current_mem_usage2,
  1804.     'execution_time' => $execution_time
  1805.   );
  1806. }
  1807.  
  1808.  
  1809.  
  1810.  
  1811.                                      # # #
  1812.  
  1813.  
  1814.  
  1815.  
  1816. # ============================================================================ #
  1817. #    7. UTILS                                                                  #
  1818. # ============================================================================ #
  1819.  
  1820. /**
  1821.  * Calls a function if exists
  1822.  *
  1823.  * @param callback $callback a function stored in a string variable,
  1824.  *    or an object and the name of a method within the object
  1825.  *    See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
  1826.  *    to learn more about callbacks.
  1827.  * @param mixed $arg,.. (optional)
  1828.  * @return mixed 
  1829.  */
  1830. function call_if_exists($callback)
  1831. {
  1832.   $args func_get_args();
  1833.   $callback array_shift($args);
  1834.   if(is_callable($callback)) return call_user_func_array($callback$args);
  1835.   return;
  1836. }
  1837.  
  1838. /**
  1839.  * Define a constant unless it already exists
  1840.  *
  1841.  * @param string $name 
  1842.  * @param string $value 
  1843.  * @return void 
  1844.  */
  1845. function define_unless_exists($name$value)
  1846. {
  1847.   if(!defined($name)) define($name$value);
  1848. }
  1849.  
  1850. /**
  1851.  * Return a default value if provided value is empty
  1852.  *
  1853.  * @param mixed $value 
  1854.  * @param mixed $default default value returned if $value is empty
  1855.  * @return mixed 
  1856.  */
  1857. function value_or_default($value$default)
  1858. {
  1859.   return empty($value$default $value;
  1860. }
  1861.  
  1862. /**
  1863.  * An alias of {@link value_or_default()}
  1864.  *
  1865.  * 
  1866.  * @param mixed $value 
  1867.  * @param mixed $default 
  1868.  * @return mixed 
  1869.  */
  1870. function v($value$default)
  1871. {
  1872.   return value_or_default($value$default);
  1873. }
  1874.  
  1875. /**
  1876.  * Load php files with require_once in a given dir
  1877.  *
  1878.  * @param string $path Path in which are the file to load
  1879.  * @param string $pattern a regexp pattern that filter files to load
  1880.  * @param bool $prevents_output security option that prevents output
  1881.  * @return array paths of loaded files
  1882.  */
  1883. function require_once_dir($path$pattern "*.php"$prevents_output true)
  1884. {
  1885.   if($path[strlen($path1!= "/"$path .= "/";
  1886.   $filenames glob($path.$pattern);
  1887.   if(!is_array($filenames)) $filenames array();
  1888.   if($prevents_outputob_start();
  1889.   foreach($filenames as $filenamerequire_once $filename;
  1890.   if($prevents_outputob_end_clean();
  1891.   return $filenames;
  1892. }
  1893.  
  1894. /**
  1895.  * Dumps a variable into inspectable format
  1896.  *
  1897.  * @param anything $var the variable to debug
  1898.  * @param bool $output_as_html sets whether to wrap output in <pre> tags. default: true
  1899.  * @return string the variable with output
  1900.  */
  1901. function debug($var$output_as_html true)
  1902.   if is_null($var) ) return '<span class="null-value">[NULL]</span>'};
  1903.   $out '';
  1904.   switch ($var
  1905.   
  1906.     case empty($var):
  1907.       $out '[empty value]';
  1908.       break;
  1909.     
  1910.     case is_array($var):
  1911.       $out var_export($vartrue);
  1912.       break;
  1913.     
  1914.     case is_object($var):
  1915.       $out var_export($vartrue);
  1916.       break;
  1917.       
  1918.     case is_string($var):
  1919.       $out $var;
  1920.       break;
  1921.     
  1922.     default:
  1923.       $out var_export($vartrue);
  1924.       break;
  1925.   }
  1926.   if ($output_as_html$out h($out);  }
  1927.   return "<pre>\n" $out ."</pre>";
  1928. }
  1929.  
  1930.  
  1931. ## HTTP utils  _________________________________________________________________
  1932.  
  1933.  
  1934. ### Constants: HTTP status codes
  1935.  
  1936. define'HTTP_CONTINUE',                      100 );
  1937. define'HTTP_SWITCHING_PROTOCOLS',           101 );
  1938. define'HTTP_PROCESSING',                    102 );
  1939. define'HTTP_OK',                            200 );
  1940. define'HTTP_CREATED',                       201 );
  1941. define'HTTP_ACCEPTED',                      202 );
  1942. define'HTTP_NON_AUTHORITATIVE',             203 );
  1943. define'HTTP_NO_CONTENT',                    204 );
  1944. define'HTTP_RESET_CONTENT',                 205 );
  1945. define'HTTP_PARTIAL_CONTENT',               206 );
  1946. define'HTTP_MULTI_STATUS',                  207 );
  1947.                                               
  1948. define'HTTP_MULTIPLE_CHOICES',              300 );
  1949. define'HTTP_MOVED_PERMANENTLY',             301 );
  1950. define'HTTP_MOVED_TEMPORARILY',             302 );
  1951. define'HTTP_SEE_OTHER',                     303 );
  1952. define'HTTP_NOT_MODIFIED',                  304 );
  1953. define'HTTP_USE_PROXY',                     305 );
  1954. define'HTTP_TEMPORARY_REDIRECT',            307 );
  1955.  
  1956. define'HTTP_BAD_REQUEST',                   400 );
  1957. define'HTTP_UNAUTHORIZED',                  401 );
  1958. define'HTTP_PAYMENT_REQUIRED',              402 );
  1959. define'HTTP_FORBIDDEN',                     403 );
  1960. define'HTTP_NOT_FOUND',                     404 );
  1961. define'HTTP_METHOD_NOT_ALLOWED',            405 );
  1962. define'HTTP_NOT_ACCEPTABLE',                406 );
  1963. define'HTTP_PROXY_AUTHENTICATION_REQUIRED'407 );
  1964. define'HTTP_REQUEST_TIME_OUT',              408 );
  1965. define'HTTP_CONFLICT',                      409 );
  1966. define'HTTP_GONE',                          410 );
  1967. define'HTTP_LENGTH_REQUIRED',               411 );
  1968. define'HTTP_PRECONDITION_FAILED',           412 );
  1969. define'HTTP_REQUEST_ENTITY_TOO_LARGE',      413 );
  1970. define'HTTP_REQUEST_URI_TOO_LARGE',         414 );
  1971. define'HTTP_UNSUPPORTED_MEDIA_TYPE',        415 );
  1972. define'HTTP_RANGE_NOT_SATISFIABLE',         416 );
  1973. define'HTTP_EXPECTATION_FAILED',            417 );
  1974. define'HTTP_UNPROCESSABLE_ENTITY',          422 );
  1975. define'HTTP_LOCKED',                        423 );
  1976. define'HTTP_FAILED_DEPENDENCY',             424 );
  1977. define'HTTP_UPGRADE_REQUIRED',              426 );
  1978.  
  1979. define'HTTP_INTERNAL_SERVER_ERROR',         500 );
  1980. define'HTTP_NOT_IMPLEMENTED',               501 );
  1981. define'HTTP_BAD_GATEWAY',                   502 );
  1982. define'HTTP_SERVICE_UNAVAILABLE',           503 );
  1983. define'HTTP_GATEWAY_TIME_OUT',              504 );
  1984. define'HTTP_VERSION_NOT_SUPPORTED',         505 );
  1985. define'HTTP_VARIANT_ALSO_VARIES',           506 );
  1986. define'HTTP_INSUFFICIENT_STORAGE',          507 );
  1987. define'HTTP_NOT_EXTENDED',                  510 );
  1988.  
  1989. /**
  1990.  * Output proper HTTP header for a given HTTP code
  1991.  *
  1992.  * @param string $code 
  1993.  * @return void 
  1994.  */
  1995. function status($code 500)
  1996. {
  1997.   if(!headers_sent())
  1998.   {
  1999.     $str http_response_status_code($code);
  2000.     header($str);
  2001.   }
  2002. }
  2003.  
  2004. /**
  2005.  * Http redirection
  2006.  * 
  2007.  * Same use as {@link url_for()}
  2008.  * By default HTTP status code is 302, but a different code can be specified
  2009.  * with a status key in array parameter.
  2010.  * 
  2011.  * <code>
  2012.  * redirecto('new','url'); # 302 HTTP_MOVED_TEMPORARILY by default
  2013.  * redirecto('new','url', array('status' => HTTP_MOVED_PERMANENTLY));
  2014.  * </code>
  2015.  * 
  2016.  * @param string or array $param1, $param2...
  2017.  * @return void 
  2018.  */
  2019. function redirect_to($params)
  2020. {
  2021.   # [NOTE]: (from php.net) HTTP/1.1 requires an absolute URI as argument to Â» Location:
  2022.   # including the scheme, hostname and absolute path, but some clients accept
  2023.   # relative URIs. You can usually use $_SERVER['HTTP_HOST'],
  2024.   # $_SERVER['PHP_SELF'] and dirname() to make an absolute URI from a relative
  2025.   # one yourself.
  2026.  
  2027.   # TODO make absolute uri
  2028.   if(!headers_sent())
  2029.   {
  2030.     $status HTTP_MOVED_TEMPORARILY# default for a redirection in PHP
  2031.     $params func_get_args();
  2032.     $n_params array();
  2033.     # extract status param if exists
  2034.     foreach($params as $param)
  2035.     {
  2036.       if(is_array($param))
  2037.       {
  2038.         if(array_key_exists('status'$param))
  2039.         {
  2040.           $status $param['status'];
  2041.           unset($param['status']);
  2042.         }
  2043.       }
  2044.       $n_params[$param;
  2045.     }
  2046.     $uri call_user_func_array('url_for'$n_params);
  2047.     stop_and_exit(false);
  2048.     header('Location: '.$uritrue$status);
  2049.     exit;
  2050.   }
  2051. }
  2052.  
  2053. /**
  2054.  * Http redirection
  2055.  *
  2056.  * @deprecated deprecated since version 0.4. Please use {@link redirect_to()} instead.
  2057.  * @param string $url 
  2058.  * @return void 
  2059.  */
  2060. function redirect($uri)
  2061. {
  2062.   # halt('redirect() is deprecated. Please use redirect_to() instead.', E_LIM_DEPRECATED);
  2063.   # halt not necesary... it won't be visible because of http redirection...
  2064.   redirect_to($uri);
  2065. }
  2066.  
  2067. /**
  2068.  * Returns HTTP response status for a given code.
  2069.  * If no code provided, return an array of all status
  2070.  *
  2071.  * @param string $num 
  2072.  * @return string,array 
  2073.  */
  2074. function http_response_status($num null)
  2075. {
  2076.   $status =  array(
  2077.       100 => 'Continue',
  2078.       101 => 'Switching Protocols',
  2079.       102 => 'Processing',
  2080.  
  2081.       200 => 'OK',
  2082.       201 => 'Created',
  2083.       202 => 'Accepted',
  2084.       203 => 'Non-Authoritative Information',
  2085.       204 => 'No Content',
  2086.       205 => 'Reset Content',
  2087.       206 => 'Partial Content',
  2088.       207 => 'Multi-Status',
  2089.       226 => 'IM Used',
  2090.  
  2091.       300 => 'Multiple Choices',
  2092.       301 => 'Moved Permanently',
  2093.       302 => 'Found',
  2094.       303 => 'See Other',
  2095.       304 => 'Not Modified',
  2096.       305 => 'Use Proxy',
  2097.       306 => 'Reserved',
  2098.       307 => 'Temporary Redirect',
  2099.  
  2100.       400 => 'Bad Request',
  2101.       401 => 'Unauthorized',
  2102.       402 => 'Payment Required',
  2103.       403 => 'Forbidden',
  2104.       404 => 'Not Found',
  2105.       405 => 'Method Not Allowed',
  2106.       406 => 'Not Acceptable',
  2107.       407 => 'Proxy Authentication Required',
  2108.       408 => 'Request Timeout',
  2109.       409 => 'Conflict',
  2110.       410 => 'Gone',
  2111.       411 => 'Length Required',
  2112.       412 => 'Precondition Failed',
  2113.       413 => 'Request Entity Too Large',
  2114.       414 => 'Request-URI Too Long',
  2115.       415 => 'Unsupported Media Type',
  2116.       416 => 'Requested Range Not Satisfiable',
  2117.       417 => 'Expectation Failed',
  2118.       422 => 'Unprocessable Entity',
  2119.       423 => 'Locked',
  2120.       424 => 'Failed Dependency',
  2121.       426 => 'Upgrade Required',
  2122.  
  2123.       500 => 'Internal Server Error',
  2124.       501 => 'Not Implemented',
  2125.       502 => 'Bad Gateway',
  2126.       503 => 'Service Unavailable',
  2127.       504 => 'Gateway Timeout',
  2128.       505 => 'HTTP Version Not Supported',
  2129.       506 => 'Variant Also Negotiates',
  2130.       507 => 'Insufficient Storage',
  2131.       510 => 'Not Extended'
  2132.   );
  2133.   if(is_null($num)) return $status;
  2134.   return array_key_exists($num$status$status[$num'';
  2135. }
  2136.  
  2137. /**
  2138.  * Checks if an HTTP response code is valid
  2139.  *
  2140.  * @param string $num 
  2141.  * @return bool 
  2142.  */
  2143. {
  2144.   $r http_response_status($num);
  2145.   return !empty($r);
  2146. }
  2147.  
  2148. /**
  2149.  * Returns an HTTP response status string for a given code
  2150.  *
  2151.  * @param string $num 
  2152.  * @return string 
  2153.  */
  2154. {
  2155.   $protocole empty($_SERVER["SERVER_PROTOCOL"]"HTTP/1.1" $_SERVER["SERVER_PROTOCOL"];
  2156.   if($str http_response_status($num)) return "$protocole $num $str";
  2157. }
  2158.  
  2159. /**
  2160.  * Check if the _Accept_ header is present, and includes the given `type`.
  2161.  *
  2162.  * When the _Accept_ header is not present `true` is returned. Otherwise
  2163.  * the given `type` is matched by an exact match, and then subtypes. You
  2164.  * may pass the subtype such as "html" which is then converted internally
  2165.  * to "text/html" using the mime lookup table.
  2166.  *
  2167.  * @param string $type 
  2168.  * @param string $env 
  2169.  * @return bool 
  2170.  */
  2171. function http_ua_accepts($type$env null)
  2172. {
  2173.   if(is_null($env)) $env env();
  2174.   $accept array_key_exists('HTTP_ACCEPT'$env['SERVER']$env['SERVER']['HTTP_ACCEPT'null;
  2175.   
  2176.   if(!$accept || $accept === '*/*'return true;
  2177.   
  2178.   if($type)
  2179.   {
  2180.     // Allow "html" vs "text/html" etc
  2181.     if(!strpos($type'/')) $type mime_type($type);
  2182.     
  2183.     // Check if we have a direct match
  2184.     if(strpos($accept$type> -1return true;
  2185.     
  2186.     // Check if we have type/*  
  2187.     $type_parts explode('/'$type)
  2188.     $type $type_parts[0].'/*';
  2189.     return (strpos($accept$type> -1);
  2190.   }
  2191.   
  2192.   return false
  2193. }
  2194.  
  2195. ## FILE utils  _________________________________________________________________
  2196.  
  2197. /**
  2198.  * Returns mime type for a given extension or if no extension is provided,
  2199.  * all mime types in an associative array, with extensions as keys.
  2200.  * (extracted from Orbit source http://orbit.luaforge.net/)
  2201.  *
  2202.  * @param string $ext 
  2203.  * @return string, array
  2204.  */
  2205. function mime_type($ext null)
  2206. {
  2207.   $types array(
  2208.     'ai'      => 'application/postscript',
  2209.     'aif'     => 'audio/x-aiff',
  2210.     'aifc'    => 'audio/x-aiff',
  2211.     'aiff'    => 'audio/x-aiff',
  2212.     'asc'     => 'text/plain',
  2213.     'atom'    => 'application/atom+xml',
  2214.     'atom'    => 'application/atom+xml',
  2215.     'au'      => 'audio/basic',
  2216.     'avi'     => 'video/x-msvideo',
  2217.     'bcpio'   => 'application/x-bcpio',
  2218.     'bin'     => 'application/octet-stream',
  2219.     'bmp'     => 'image/bmp',
  2220.     'cdf'     => 'application/x-netcdf',
  2221.     'cgm'     => 'image/cgm',
  2222.     'class'   => 'application/octet-stream',
  2223.     'cpio'    => 'application/x-cpio',
  2224.     'cpt'     => 'application/mac-compactpro',
  2225.     'csh'     => 'application/x-csh',
  2226.     'css'     => 'text/css',
  2227.     'csv'     => 'text/csv',
  2228.     'dcr'     => 'application/x-director',
  2229.     'dir'     => 'application/x-director',
  2230.     'djv'     => 'image/vnd.djvu',
  2231.     'djvu'    => 'image/vnd.djvu',
  2232.     'dll'     => 'application/octet-stream',
  2233.     'dmg'     => 'application/octet-stream',
  2234.     'dms'     => 'application/octet-stream',
  2235.     'doc'     => 'application/msword',
  2236.     'dtd'     => 'application/xml-dtd',
  2237.     'dvi'     => 'application/x-dvi',
  2238.     'dxr'     => 'application/x-director',
  2239.     'eps'     => 'application/postscript',
  2240.     'etx'     => 'text/x-setext',
  2241.     'exe'     => 'application/octet-stream',
  2242.     'ez'      => 'application/andrew-inset',
  2243.     'gif'     => 'image/gif',
  2244.     'gram'    => 'application/srgs',
  2245.     'grxml'   => 'application/srgs+xml',
  2246.     'gtar'    => 'application/x-gtar',
  2247.     'hdf'     => 'application/x-hdf',
  2248.     'hqx'     => 'application/mac-binhex40',
  2249.     'htm'     => 'text/html',
  2250.     'html'    => 'text/html',
  2251.     'ice'     => 'x-conference/x-cooltalk',
  2252.     'ico'     => 'image/x-icon',
  2253.     'ics'     => 'text/calendar',
  2254.     'ief'     => 'image/ief',
  2255.     'ifb'     => 'text/calendar',
  2256.     'iges'    => 'model/iges',
  2257.     'igs'     => 'model/iges',
  2258.     'jpe'     => 'image/jpeg',
  2259.     'jpeg'    => 'image/jpeg',
  2260.     'jpg'     => 'image/jpeg',
  2261.     'js'      => 'application/x-javascript',
  2262.     'json'    => 'application/json',
  2263.     'kar'     => 'audio/midi',
  2264.     'latex'   => 'application/x-latex',
  2265.     'lha'     => 'application/octet-stream',
  2266.     'lzh'     => 'application/octet-stream',
  2267.     'm3u'     => 'audio/x-mpegurl',
  2268.     'man'     => 'application/x-troff-man',
  2269.     'mathml'  => 'application/mathml+xml',
  2270.     'me'      => 'application/x-troff-me',
  2271.     'mesh'    => 'model/mesh',
  2272.     'mid'     => 'audio/midi',
  2273.     'midi'    => 'audio/midi',
  2274.     'mif'     => 'application/vnd.mif',
  2275.     'mov'     => 'video/quicktime',
  2276.     'movie'   => 'video/x-sgi-movie',
  2277.     'mp2'     => 'audio/mpeg',
  2278.     'mp3'     => 'audio/mpeg',
  2279.     'mpe'     => 'video/mpeg',
  2280.     'mpeg'    => 'video/mpeg',
  2281.     'mpg'     => 'video/mpeg',
  2282.     'mpga'    => 'audio/mpeg',
  2283.     'ms'      => 'application/x-troff-ms',
  2284.     'msh'     => 'model/mesh',
  2285.     'mxu'     => 'video/vnd.mpegurl',
  2286.     'nc'      => 'application/x-netcdf',
  2287.     'oda'     => 'application/oda',
  2288.     'ogg'     => 'application/ogg',
  2289.     'pbm'     => 'image/x-portable-bitmap',
  2290.     'pdb'     => 'chemical/x-pdb',
  2291.     'pdf'     => 'application/pdf',
  2292.     'pgm'     => 'image/x-portable-graymap',
  2293.     'pgn'     => 'application/x-chess-pgn',
  2294.     'png'     => 'image/png',
  2295.     'pnm'     => 'image/x-portable-anymap',
  2296.     'ppm'     => 'image/x-portable-pixmap',
  2297.     'ppt'     => 'application/vnd.ms-powerpoint',
  2298.     'ps'      => 'application/postscript',
  2299.     'qt'      => 'video/quicktime',
  2300.     'ra'      => 'audio/x-pn-realaudio',
  2301.     'ram'     => 'audio/x-pn-realaudio',
  2302.     'ras'     => 'image/x-cmu-raster',
  2303.     'rdf'     => 'application/rdf+xml',
  2304.     'rgb'     => 'image/x-rgb',
  2305.     'rm'      => 'application/vnd.rn-realmedia',
  2306.     'roff'    => 'application/x-troff',
  2307.     'rss'     => 'application/rss+xml',
  2308.     'rtf'     => 'text/rtf',
  2309.     'rtx'     => 'text/richtext',
  2310.     'sgm'     => 'text/sgml',
  2311.     'sgml'    => 'text/sgml',
  2312.     'sh'      => 'application/x-sh',
  2313.     'shar'    => 'application/x-shar',
  2314.     'silo'    => 'model/mesh',
  2315.     'sit'     => 'application/x-stuffit',
  2316.     'skd'     => 'application/x-koan',
  2317.     'skm'     => 'application/x-koan',
  2318.     'skp'     => 'application/x-koan',
  2319.     'skt'     => 'application/x-koan',
  2320.     'smi'     => 'application/smil',
  2321.     'smil'    => 'application/smil',
  2322.     'snd'     => 'audio/basic',
  2323.     'so'      => 'application/octet-stream',
  2324.     'spl'     => 'application/x-futuresplash',
  2325.     'src'     => 'application/x-wais-source',
  2326.     'sv4cpio' => 'application/x-sv4cpio',
  2327.     'sv4crc'  => 'application/x-sv4crc',
  2328.     'svg'     => 'image/svg+xml',
  2329.     'svgz'    => 'image/svg+xml',
  2330.     'swf'     => 'application/x-shockwave-flash',
  2331.     't'       => 'application/x-troff',
  2332.     'tar'     => 'application/x-tar',
  2333.     'tcl'     => 'application/x-tcl',
  2334.     'tex'     => 'application/x-tex',
  2335.     'texi'    => 'application/x-texinfo',
  2336.     'texinfo' => 'application/x-texinfo',
  2337.     'tif'     => 'image/tiff',
  2338.     'tiff'    => 'image/tiff',
  2339.     'tr'      => 'application/x-troff',
  2340.     'tsv'     => 'text/tab-separated-values',
  2341.     'txt'     => 'text/plain',
  2342.     'ustar'   => 'application/x-ustar',
  2343.     'vcd'     => 'application/x-cdlink',
  2344.     'vrml'    => 'model/vrml',
  2345.     'vxml'    => 'application/voicexml+xml',
  2346.     'wav'     => 'audio/x-wav',
  2347.     'wbmp'    => 'image/vnd.wap.wbmp',
  2348.     'wbxml'   => 'application/vnd.wap.wbxml',
  2349.     'wml'     => 'text/vnd.wap.wml',
  2350.     'wmlc'    => 'application/vnd.wap.wmlc',
  2351.     'wmls'    => 'text/vnd.wap.wmlscript',
  2352.     'wmlsc'   => 'application/vnd.wap.wmlscriptc',
  2353.     'wrl'     => 'model/vrml',
  2354.     'xbm'     => 'image/x-xbitmap',
  2355.     'xht'     => 'application/xhtml+xml',
  2356.     'xhtml'   => 'application/xhtml+xml',
  2357.     'xls'     => 'application/vnd.ms-excel',
  2358.     'xml'     => 'application/xml',
  2359.     'xpm'     => 'image/x-xpixmap',
  2360.     'xsl'     => 'application/xml',
  2361.     'xslt'    => 'application/xslt+xml',
  2362.     'xul'     => 'application/vnd.mozilla.xul+xml',
  2363.     'xwd'     => 'image/x-xwindowdump',
  2364.     'xyz'     => 'chemical/x-xyz',
  2365.     'zip'     => 'application/zip'
  2366.   );
  2367.   return is_null($ext$types $types[strtolower($ext)];
  2368. }
  2369.  
  2370. /**
  2371.  * Detect MIME Content-type for a file
  2372.  *
  2373.  * @param string $filename Path to the tested file.
  2374.  * @return string 
  2375.  */
  2376. function file_mime_content_type($filename)
  2377. {
  2378.   $ext file_extension($filename)/* strtolower isn't necessary */
  2379.   if($mime mime_type($ext)) return $mime;
  2380.   elseif (function_exists('finfo_open'))
  2381.   {
  2382.     if($finfo finfo_open(FILEINFO_MIME))
  2383.     {
  2384.       if($mime finfo_file($finfo$filename))
  2385.       {
  2386.         finfo_close($finfo);
  2387.         return $mime;        
  2388.       }
  2389.     }
  2390.   }
  2391.   return 'application/octet-stream';
  2392. }
  2393.  
  2394.  
  2395. /**
  2396.  * Read and output file content and return filesize in bytes or status after
  2397.  * closing file.
  2398.  * This function is very efficient for outputing large files without timeout
  2399.  * nor too expensive memory use
  2400.  *
  2401.  * @param string $filename 
  2402.  * @param string $retbytes 
  2403.  * @return bool, int
  2404.  */
  2405. function file_read_chunked($filename$retbytes true)
  2406. {
  2407.   $chunksize 1*(1024*1024)// how many bytes per chunk
  2408.   $buffer    '';
  2409.   $cnt       0;
  2410.   $handle    fopen($filename'rb');
  2411.   if ($handle === falsereturn false;
  2412.  
  2413.   ob_start();
  2414.   while (!feof($handle)) {
  2415.     $buffer fread($handle$chunksize);
  2416.     echo $buffer;
  2417.     ob_flush();
  2418.     flush();
  2419.     if ($retbytes$cnt += strlen($buffer);
  2420.     set_time_limit(0);
  2421.   }
  2422.  
  2423.   $status fclose($handle);
  2424.   if ($retbytes && $statusreturn $cnt// return num. bytes delivered like readfile() does.
  2425.   return $status;
  2426. }
  2427.  
  2428. /**
  2429.  * Create a file path by concatenation of given arguments.
  2430.  * Windows paths with backslash directory separators are normalized in *nix paths.
  2431.  *
  2432.  * @param string $path, ...
  2433.  * @return string normalized path
  2434.  */
  2435. function file_path($path)
  2436. {
  2437.   $args func_get_args();
  2438.   $ds '/'
  2439.   $win_ds '\\';
  2440.   $n_path count($argsimplode($ds$args$path;
  2441.   if(strpos($n_path$win_ds!== false$n_path str_replace$win_ds$ds$n_path );
  2442.   $n_path preg_replace'/'.preg_quote($ds$ds).'{2,}'.'/'
  2443.                           $ds
  2444.                           $n_path);
  2445.   return $n_path;
  2446. }
  2447.  
  2448. /**
  2449.  * Returns file extension or false if none
  2450.  *
  2451.  * @param string $filename 
  2452.  * @return string, false
  2453.  */
  2454. function file_extension($filename)
  2455. {
  2456.   $pos strrpos($filename'.');
  2457.   if($pos !== falsereturn substr($filename$pos 1);
  2458.   return false;
  2459. }
  2460.  
  2461. /**
  2462.  * Checks if $filename is a text file
  2463.  *
  2464.  * @param string $filename 
  2465.  * @return bool 
  2466.  */
  2467. function file_is_text($filename)
  2468. {
  2469.   if($mime file_mime_content_type($filename)) return substr($mime,0,5== "text/";
  2470.   return null;
  2471. }
  2472.  
  2473. /**
  2474.  * Checks if $filename is a binary file
  2475.  *
  2476.  * @param string $filename 
  2477.  * @return void 
  2478.  */
  2479. function file_is_binary($filename)
  2480. {
  2481.   $is_text file_is_text($filename);
  2482.   return is_null($is_textnull !$is_text;
  2483. }
  2484.  
  2485. /**
  2486.  * Return or output file content
  2487.  *
  2488.  * @return     string, int
  2489.  *                
  2490.  ***/
  2491.  
  2492. function file_read($filename$return false)
  2493. {
  2494.   if(!file_exists($filename)) trigger_error("$filename doesn't exists"E_USER_ERROR);
  2495.   if($returnreturn file_get_contents($filename);
  2496.   return file_read_chunked($filename);
  2497. }
  2498.  
  2499. /**
  2500.  * Returns an array of files contained in a directory
  2501.  *
  2502.  * @param string $dir 
  2503.  * @return array 
  2504.  */
  2505. function file_list_dir($dir)
  2506. {
  2507.   $files array();
  2508.   if ($handle opendir($dir))
  2509.   {
  2510.     while (false !== ($file readdir($handle)))
  2511.     {
  2512.       if ($file[0!= "." && $file != ".."$files[$file;
  2513.     }
  2514.     closedir($handle);
  2515.   }
  2516.   return $files;
  2517. }
  2518.  
  2519. ## Extra utils  ________________________________________________________________
  2520.  
  2521. if(!function_exists('array_replace'))
  2522. {
  2523.   /**
  2524.    * For PHP 5 < 5.3.0 (backward compatibility)
  2525.    * (from {@link http://www.php.net/manual/fr/function.array-replace.php#92549 this php doc. note})
  2526.    * 
  2527.    * @see array_replace()
  2528.    * @param string $array 
  2529.    * @param string $array1 
  2530.    * @return $array 
  2531.    */
  2532.   function array_replacearray &$arrayarray &$array1 )
  2533.   {
  2534.     $args  func_get_args();
  2535.     $count func_num_args();
  2536.  
  2537.     for ($i 0$i $count++$i)
  2538.     {
  2539.       if(is_array($args[$i]))
  2540.       {
  2541.         foreach ($args[$ias $key => $val$array[$key$val;
  2542.       }
  2543.       else
  2544.       {
  2545.         trigger_error(
  2546.           __FUNCTION__ . '(): Argument #' ($i+1' is not an array',
  2547.           E_USER_WARNING
  2548.         );
  2549.         return null;
  2550.       }
  2551.     }
  2552.     return $array;
  2553.   }
  2554. }
  2555.  
  2556. /**
  2557.  * Check if a string is an url
  2558.  *
  2559.  * This implementation no longer requires
  2560.  * {@link http://www.php.net/manual/en/book.filter.php the filter extenstion},
  2561.  * so it will improve compatibility with older PHP versions.
  2562.  *
  2563.  * @param string $str 
  2564.  * @return false, str   the string if true, false instead
  2565.  */
  2566. function filter_var_url($str)
  2567. {
  2568.   $regexp '@^https?://([-[:alnum:]]+\.)+[a-zA-Z]{2,6}(:[0-9]+)?(.*)?$@';
  2569.   $options array"options" => array("regexp" => $regexp ));
  2570.   return preg_match($regexp$str$str false;
  2571. }
  2572.  
  2573.  
  2574.  
  2575.  
  2576.  
  2577. #   ================================= END ==================================   #

Documentation generated on Sat, 16 Oct 2010 19:09:45 +0200 by phpDocumentor 1.4.3