PageRenderTime 7ms CodeModel.GetById 93ms app.highlight 68ms RepoModel.GetById 1ms app.codeStats 1ms

/lib/limonade.php

http://github.com/sofadesign/limonade-blog-example
PHP | 2289 lines | 1264 code | 231 blank | 794 comment | 206 complexity | 456870fec8c582b9fc2e0710b534f574 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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 */
  58define('LIMONADE',              '0.4.6');
  59define('LIM_START_MICROTIME',   (float)substr(microtime(), 0, 10));
  60define('LIM_SESSION_NAME',      'Fresh_and_Minty_Limonade_App');
  61define('LIM_SESSION_FLASH_KEY', '_lim_flash_messages');
  62define('E_LIM_HTTP',            32768);
  63define('E_LIM_PHP',             65536);
  64define('E_LIM_DEPRECATED',      35000);
  65define('NOT_FOUND',             404);
  66define('SERVER_ERROR',          500);
  67define('ENV_PRODUCTION',        10);
  68define('ENV_DEVELOPMENT',       100);
  69define('X-SENDFILE',            10);
  70define('X-LIGHTTPD-SEND-FILE',  20);
  71
  72# for PHP 5.3.0 <
  73if(!defined('E_DEPRECATED'))      define('E_DEPRECATED', 8192);
  74if(!defined('E_USER_DEPRECATED')) define('E_USER_DEPRECATED', 16384);
  75
  76
  77## SETTING BASIC SECURITY _____________________________________________________
  78
  79# A. Unsets all global variables set from a superglobal array
  80
  81/**
  82 * @access private
  83 * @return void
  84 */
  85function unregister_globals()
  86{
  87  $args = func_get_args();
  88  foreach($args as $k => $v)
  89    if(array_key_exists($k, $GLOBALS)) unset($GLOBALS[$k]);
  90}
  91
  92if(ini_get('register_globals'))
  93{
  94  unregister_globals( '_POST', '_GET', '_COOKIE', '_REQUEST', '_SERVER', 
  95                      '_ENV', '_FILES');
  96  ini_set('register_globals', 0);
  97}
  98
  99# B. removing magic quotes
 100
 101/**
 102 * @access private
 103 * @param string $array 
 104 * @return array
 105 */
 106function remove_magic_quotes($array)
 107{
 108  foreach ($array as $k => $v)
 109    $array[$k] = is_array($v) ? remove_magic_quotes($v) : stripslashes($v);
 110  return $array;
 111}
 112
 113if (get_magic_quotes_gpc())
 114{
 115  $_GET    = remove_magic_quotes($_GET);
 116  $_POST   = remove_magic_quotes($_POST);
 117  $_COOKIE = remove_magic_quotes($_COOKIE);
 118  ini_set('magic_quotes_gpc', 0);
 119}
 120
 121if(get_magic_quotes_runtime()) set_magic_quotes_runtime(false);
 122
 123# C. Disable error display
 124#    by default, no error reporting; it will be switched on later in run().
 125#    ini_set('display_errors', 1); must be called explicitly in app file
 126#    if you want to show errors before running app
 127ini_set('display_errors', 0);
 128
 129## SETTING INTERNAL ROUTES _____________________________________________________
 130
 131dispatch(array("/_lim_css/*.css", array('_lim_css_filename')), 'render_limonade_css');
 132  /**
 133   * Internal controller that responds to route /_lim_css/*.css
 134   *
 135   * @access private
 136   * @return string
 137   */
 138  function render_limonade_css()
 139  {
 140    option('views_dir', file_path(option('limonade_public_dir'), 'css'));
 141    $fpath = file_path(params('_lim_css_filename').".css");
 142    return css($fpath, null); // with no layout
 143  }
 144
 145dispatch(array("/_lim_public/**", array('_lim_public_file')), 'render_limonade_file');
 146  /**
 147   * Internal controller that responds to route /_lim_public/**
 148   *
 149   * @access private
 150   * @return void
 151   */
 152  function render_limonade_file()
 153  {
 154    $fpath = file_path(option('limonade_public_dir'), params('_lim_public_file'));
 155    return render_file($fpath, true);
 156  }
 157
 158
 159
 160
 161                                     # # #
 162
 163
 164
 165
 166# ============================================================================ #
 167#    1. BASE                                                                   #
 168# ============================================================================ #
 169 
 170## ABSTRACTS ___________________________________________________________________
 171
 172# function configure(){}
 173# function before(){}
 174# function after(){}
 175# function not_found(){}
 176# function server_error(){}
 177# function route_missing(){}
 178# function before_exit(){}
 179
 180
 181## MAIN PUBLIC FUNCTIONS _______________________________________________________
 182
 183/**
 184 * Set and returns options values
 185 * 
 186 * If multiple values are provided, set $name option with an array of those values.
 187 * If there is only one value, set $name option with the provided $values
 188 *
 189 * @param string $name 
 190 * @param mixed  $values,... 
 191 * @return mixed option value for $name if $name argument is provided, else return all options
 192 */
 193function option($name = null, $values = null)
 194{
 195  static $options = array();
 196  $args = func_get_args();
 197  $name = array_shift($args);
 198  if(is_null($name)) return $options;
 199  if(!empty($args))
 200  {
 201    $options[$name] = count($args) > 1 ? $args : $args[0];
 202  }
 203  if(array_key_exists($name, $options)) return $options[$name];
 204  return;
 205}
 206
 207/**
 208 * Set and returns params
 209 * 
 210 * Depending on provided arguments:
 211 * 
 212 *  * Reset params if first argument is null
 213 * 
 214 *  * If first argument is an array, merge it with current params
 215 * 
 216 *  * If there is a second argument $value, set param $name (first argument) with $value
 217 * <code>
 218 *  params('name', 'Doe') // set 'name' => 'Doe'
 219 * </code>
 220 *  * If there is more than 2 arguments, set param $name (first argument) value with
 221 *    an array of next arguments
 222 * <code>
 223 *  params('months', 'jan', 'feb', 'mar') // set 'month' => array('months', 'jan', 'feb', 'mar')
 224 * </code>
 225 * 
 226 * @param mixed $name_or_array_or_null could be null || array of params || name of a param (optional)
 227 * @param mixed $value,... for the $name param (optional)
 228 * @return mixed all params, or one if a first argument $name is provided
 229 */
 230function params($name_or_array_or_null = null, $value = null)
 231{
 232  static $params = array();
 233  $args = func_get_args();
 234
 235  if(func_num_args() > 0)
 236  {
 237    $name = array_shift($args);
 238    if(is_null($name))
 239    {
 240      # Reset params
 241      $params = array();
 242      return $params;
 243    }
 244    if(is_array($name))
 245    {
 246      $params = array_merge($params, $name);
 247      return $params;
 248    }
 249    $nargs = count($args);
 250    if($nargs > 0)
 251    {
 252      $value = $nargs > 1 ? $args : $args[0];
 253      $params[$name] = $value;
 254    }
 255    return array_key_exists($name,$params) ? $params[$name] : null;
 256  }
 257
 258  return $params;
 259}
 260
 261/**
 262 * Set and returns template variables
 263 * 
 264 * If multiple values are provided, set $name variable with an array of those values.
 265 * If there is only one value, set $name variable with the provided $values
 266 *
 267 * @param string $name 
 268 * @param mixed  $values,... 
 269 * @return mixed variable value for $name if $name argument is provided, else return all variables
 270 */
 271function set($name = null, $values = null)
 272{
 273  static $vars = array();
 274  $args = func_get_args();
 275  $name = array_shift($args);
 276  if(is_null($name)) return $vars;
 277  if(!empty($args))
 278  {
 279    $vars[$name] = count($args) > 1 ? $args : $args[0];
 280  }
 281  if(array_key_exists($name, $vars)) return $vars[$name];
 282  return $vars;
 283}
 284
 285/**
 286 * Sets a template variable with a value or a default value if value is empty
 287 *
 288 * @param string $name 
 289 * @param string $value 
 290 * @param string $default 
 291 * @return mixed setted value
 292 */
 293function set_or_default($name, $value, $default)
 294{
 295  return set($name, value_or_default($value, $default));
 296}
 297
 298/**
 299 * Running application
 300 *
 301 * @param string $env 
 302 * @return void
 303 */
 304function run($env = null)
 305{
 306  if(is_null($env)) $env = env();
 307   
 308  # 0. Set default configuration
 309  $root_dir  = dirname(app_file());
 310  $base_path = dirname(file_path($env['SERVER']['SCRIPT_NAME']));
 311  $base_file = basename($env['SERVER']['SCRIPT_NAME']);
 312  $base_uri  = file_path($base_path, (($base_file == 'index.php') ? '?' : $base_file.'?'));
 313  $lim_dir   = dirname(__FILE__);
 314  option('root_dir',           $root_dir);
 315  option('base_path',          $base_path);
 316  option('base_uri',           $base_uri); // set it manually if you use url_rewriting
 317  option('limonade_dir',       file_path($lim_dir));
 318  option('limonade_views_dir', file_path($lim_dir, 'limonade', 'views'));
 319  option('limonade_public_dir',file_path($lim_dir, 'limonade', 'public'));
 320  option('public_dir',         file_path($root_dir, 'public'));
 321  option('views_dir',          file_path($root_dir, 'views'));
 322  option('controllers_dir',    file_path($root_dir, 'controllers'));
 323  option('lib_dir',            file_path($root_dir, 'lib'));
 324  option('error_views_dir',    option('limonade_views_dir'));
 325  option('env',                ENV_PRODUCTION);
 326  option('debug',              true);
 327  option('session',            LIM_SESSION_NAME); // true, false or the name of your session
 328  option('encoding',           'utf-8');
 329  option('x-sendfile',         0); // 0: disabled, 
 330                                   // X-SENDFILE: for Apache and Lighttpd v. >= 1.5,
 331                                   // X-LIGHTTPD-SEND-FILE: for Apache and Lighttpd v. < 1.5
 332
 333  # 1. Set error handling
 334  ini_set('display_errors', 1);
 335  set_error_handler('error_handler_dispatcher', E_ALL ^ E_NOTICE);
 336
 337  # 2. Set user configuration
 338  call_if_exists('configure');
 339
 340  # 3. Loading libs
 341  require_once_dir(option('lib_dir'));
 342
 343  # 4. Starting session
 344  if(!defined('SID') && option('session'))
 345  {
 346    if(!is_bool(option('session'))) session_name(option('session'));
 347    if(!session_start()) trigger_error("An error occured while trying to start the session", E_USER_WARNING);
 348  }
 349
 350  # 5. Set some default methods if needed
 351  if(!function_exists('after'))
 352  {
 353    function after($output)
 354    {
 355      return $output;
 356    }
 357  }
 358  if(!function_exists('route_missing'))
 359  {
 360    function route_missing($request_method, $request_uri)
 361    {
 362      halt(NOT_FOUND, "($request_method) $request_uri");
 363    }
 364  }
 365
 366  # 6. Check request
 367  if($rm = request_method())
 368  {
 369    if(request_is_head()) ob_start(); // then no output
 370
 371    if(!request_method_is_allowed($rm))
 372      halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
 373
 374    # 6.1 Check matching route
 375    if($route = route_find($rm, request_uri()))
 376    {
 377      params($route['params']);
 378
 379      # 6.2 Load controllers dir
 380      require_once_dir(option('controllers_dir'));
 381
 382      if(is_callable($route['function']))
 383      {
 384        # 6.3 Call before function
 385        call_if_exists('before');
 386
 387        # 6.4 Call matching controller function and output result
 388        if($output = call_user_func($route['function']))
 389        {
 390          echo after(error_notices_render() . $output);
 391        }
 392        stop_and_exit();
 393      }
 394      else halt(SERVER_ERROR, "Routing error: undefined function '{$route['function']}'", $route);      
 395    }
 396    else route_missing($rm, request_uri());
 397
 398  }
 399  else halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
 400}
 401
 402/**
 403 * Stop and exit limonade application
 404 *
 405 * @access private 
 406 * @param boolean exit or not
 407 * @return void
 408 */
 409function stop_and_exit($exit = true)
 410{
 411  call_if_exists('before_exit');
 412  $flash_sweep = true;
 413  $headers = headers_list();
 414  foreach($headers as $header)
 415  {
 416    // If a Content-Type header exists, flash_sweep only if is text/html
 417    // Else if there's no Content-Type header, flash_sweep by default
 418    if(stripos($header, 'Content-Type:') === 0)
 419    {
 420      $flash_sweep = stripos($header, 'Content-Type: text/html') === 0;
 421      break;
 422    }
 423  }
 424  if($flash_sweep) flash_sweep();
 425  if(defined('SID')) session_write_close();
 426  if(request_is_head()) ob_end_clean();
 427  if($exit) exit;
 428}
 429
 430/**
 431 * Returns limonade environment variables:
 432 *
 433 * 'SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE', 
 434 * 'GET', 'POST', 'PUT', 'DELETE'
 435 * 
 436 * If a null argument is passed, reset and rebuild environment
 437 *
 438 * @param null @reset reset and rebuild environment
 439 * @return array
 440 */
 441function env($reset = null)
 442{
 443  static $env = array();
 444  if(func_num_args() > 0)
 445  {
 446    $args = func_get_args();
 447    if(is_null($args[0])) $env = array();
 448  }
 449
 450  if(empty($env))
 451  {
 452    if(empty($GLOBALS['_SERVER']))
 453    {
 454      // Fixing empty $GLOBALS['_SERVER'] bug 
 455      // http://sofadesign.lighthouseapp.com/projects/29612-limonade/tickets/29-env-is-empty
 456      $GLOBALS['_SERVER']  =& $_SERVER;
 457      $GLOBALS['_FILES']   =& $_FILES;
 458      $GLOBALS['_REQUEST'] =& $_REQUEST;
 459      $GLOBALS['_SESSION'] =& $_SESSION;
 460      $GLOBALS['_ENV']     =& $_ENV;
 461      $GLOBALS['_COOKIE']  =& $_COOKIE;
 462    }
 463
 464    $glo_names = array('SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE');
 465
 466    $vars = array_merge($glo_names, request_methods());
 467    foreach($vars as $var)
 468    {
 469      $varname = "_$var";
 470      if(!array_key_exists($varname, $GLOBALS)) $GLOBALS[$varname] = array();
 471      $env[$var] =& $GLOBALS[$varname];
 472    }
 473
 474    $method = request_method($env);
 475    if($method == 'PUT' || $method == 'DELETE')
 476    {
 477      $varname = "_$method";
 478      if(array_key_exists('_method', $_POST) && $_POST['_method'] == $method)
 479      {
 480        foreach($_POST as $k => $v)
 481        {
 482          if($k == "_method") continue;
 483          $GLOBALS[$varname][$k] = $v;
 484        }
 485      }
 486      else
 487      {
 488        parse_str(file_get_contents('php://input'), $GLOBALS[$varname]);
 489      }
 490    }
 491  }
 492  return $env;
 493}
 494
 495/**
 496 * Returns application root file path
 497 *
 498 * @return string
 499 */
 500function app_file()
 501{
 502  static $file;
 503  if(empty($file))
 504  {
 505    $stacktrace = array_pop(debug_backtrace());
 506    $file = $stacktrace['file'];
 507  }
 508  return file_path($file);
 509}
 510
 511
 512
 513
 514                                     # # #
 515
 516
 517
 518
 519# ============================================================================ #
 520#    2. ERROR                                                                  #
 521# ============================================================================ #
 522 
 523/**
 524 * Associate a function with error code(s) and return all associations
 525 *
 526 * @param string $errno 
 527 * @param string $function 
 528 * @return array
 529 */
 530function error($errno = null, $function = null)
 531{
 532  static $errors = array();
 533  if(func_num_args() > 0)
 534  {
 535    $errors[] = array('errno'=>$errno, 'function'=> $function);
 536  }
 537  return $errors;
 538}
 539
 540/**
 541 * Raise an error, passing a given error number and an optional message,
 542 * then exit.
 543 * Error number should be a HTTP status code or a php user error (E_USER...)
 544 * $errno and $msg arguments can be passsed in any order
 545 * If no arguments are passed, default $errno is SERVER_ERROR (500)
 546 *
 547 * @param int,string $errno Error number or message string
 548 * @param string,string $msg Message string or error number
 549 * @param mixed $debug_args extra data provided for debugging
 550 * @return void
 551 */
 552function halt($errno = SERVER_ERROR, $msg = '', $debug_args = null)
 553{
 554  $args = func_get_args();
 555  $error = array_shift($args);
 556
 557  # switch $errno and $msg args
 558  # TODO cleanup / refactoring
 559  if(is_string($errno))
 560  {
 561   $msg = $errno;
 562   $oldmsg = array_shift($args);
 563   $errno = empty($oldmsg) ? SERVER_ERROR : $oldmsg;
 564  }
 565  else if(!empty($args)) $msg = array_shift($args);
 566
 567  if(empty($msg) && $errno == NOT_FOUND) $msg = request_uri();
 568  if(empty($msg)) $msg = "";
 569  if(!empty($args)) $debug_args = $args;
 570  set('_lim_err_debug_args', $debug_args);
 571
 572  error_handler_dispatcher($errno, $msg, null, null);
 573
 574}
 575
 576/**
 577 * Internal error handler dispatcher
 578 * Find and call matching error handler and exit
 579 * If no match found, call default error handler
 580 *
 581 * @access private
 582 * @param int $errno 
 583 * @param string $errstr 
 584 * @param string $errfile 
 585 * @param string $errline 
 586 * @return void
 587 */
 588function error_handler_dispatcher($errno, $errstr, $errfile, $errline)
 589{
 590  $back_trace = debug_backtrace();
 591  while($trace = array_shift($back_trace))
 592  {
 593    if($trace['function'] == 'halt')
 594    {
 595      $errfile = $trace['file'];
 596      $errline = $trace['line'];
 597      break;
 598    }
 599  }  
 600
 601  # Notices and warning won't halt execution
 602  if(error_wont_halt_app($errno))
 603  {
 604    error_notice($errno, $errstr, $errfile, $errline);
 605  	return;
 606  }
 607  else
 608  {
 609    # Other errors will stop application
 610    $handlers = error();
 611    $is_http_err = http_response_status_is_valid($errno);
 612    foreach($handlers as $handler)
 613    {
 614      $e = is_array($handler['errno']) ? $handler['errno'] : array($handler['errno']);
 615      while($ee = array_shift($e))
 616      {
 617        if($ee == $errno || $ee == E_LIM_PHP || ($ee == E_LIM_HTTP && $is_http_err))
 618        {
 619          echo call_if_exists($handler['function'], $errno, $errstr, $errfile, $errline);
 620          exit;
 621        }
 622      }
 623    }
 624    echo error_default_handler($errno, $errstr, $errfile, $errline);
 625    stop_and_exit();
 626  }
 627}
 628
 629
 630/**
 631 * Default error handler
 632 *
 633 * @param string $errno 
 634 * @param string $errstr 
 635 * @param string $errfile 
 636 * @param string $errline 
 637 * @return string error output
 638 */
 639function error_default_handler($errno, $errstr, $errfile, $errline)
 640{
 641  $is_http_err = http_response_status_is_valid($errno);
 642  $http_error_code = $is_http_err ? $errno : SERVER_ERROR;
 643
 644  status($http_error_code);
 645
 646  return $http_error_code == NOT_FOUND ?
 647            error_not_found_output($errno, $errstr, $errfile, $errline) :
 648            error_server_error_output($errno, $errstr, $errfile, $errline);                    
 649}
 650
 651/**
 652 * Returns not found error output
 653 *
 654 * @access private
 655 * @param string $msg 
 656 * @return string
 657 */
 658function error_not_found_output($errno, $errstr, $errfile, $errline)
 659{
 660  if(!function_exists('not_found'))
 661  {
 662    /**
 663     * Default not found error output
 664     *
 665     * @param string $errno 
 666     * @param string $errstr 
 667     * @param string $errfile 
 668     * @param string $errline 
 669     * @return string
 670     */
 671    function not_found($errno, $errstr, $errfile=null, $errline=null)
 672    {
 673      option('views_dir', option('error_views_dir'));
 674      $msg = h(rawurldecode($errstr));
 675      return html("<h1>Page not found:</h1><p><code>{$msg}</code></p>", error_layout());
 676    }
 677  }
 678  return not_found($errno, $errstr, $errfile, $errline);
 679}
 680
 681/**
 682 * Returns server error output
 683 *
 684 * @access private
 685 * @param int $errno 
 686 * @param string $errstr 
 687 * @param string $errfile 
 688 * @param string $errline 
 689 * @return string
 690 */
 691function error_server_error_output($errno, $errstr, $errfile, $errline)
 692{
 693  if(!function_exists('server_error'))
 694  {
 695    /**
 696     * Default server error output
 697     *
 698     * @param string $errno 
 699     * @param string $errstr 
 700     * @param string $errfile 
 701     * @param string $errline 
 702     * @return string
 703     */
 704    function server_error($errno, $errstr, $errfile=null, $errline=null)
 705    {
 706      $is_http_error = http_response_status_is_valid($errno);
 707      $args = compact('errno', 'errstr', 'errfile', 'errline', 'is_http_error');
 708      option('views_dir', option('limonade_views_dir'));
 709      $html = render('error.html.php', null, $args);	
 710      option('views_dir', option('error_views_dir'));
 711      return html($html, error_layout(), $args);
 712    }
 713  }
 714  return server_error($errno, $errstr, $errfile, $errline);
 715}
 716
 717/**
 718 * Set and returns error output layout
 719 *
 720 * @param string $layout 
 721 * @return string
 722 */
 723function error_layout($layout = false)
 724{
 725  static $o_layout = 'default_layout.php';
 726  if($layout !== false)
 727  {
 728    option('error_views_dir', option('views_dir'));
 729    $o_layout = $layout;
 730  }
 731  return $o_layout;
 732}
 733
 734
 735/**
 736 * Set a notice if arguments are provided
 737 * Returns all stored notices.
 738 * If $errno argument is null, reset the notices array
 739 *
 740 * @access private
 741 * @param string, null $str 
 742 * @return array
 743 */
 744function error_notice($errno = false, $errstr = null, $errfile = null, $errline = null)
 745{
 746  static $notices = array();
 747  if($errno) $notices[] = compact('errno', 'errstr', 'errfile', 'errline');
 748  else if(is_null($errno)) $notices = array();
 749  return $notices;
 750}
 751
 752/**
 753 * Returns notices output rendering and reset notices
 754 *
 755 * @return string
 756 */
 757function error_notices_render()
 758{
 759  if(option('debug') && option('env') > ENV_PRODUCTION)
 760  {
 761    $notices = error_notice();
 762    error_notice(null); // reset notices
 763    $c_view_dir = option('views_dir'); // keep for restore after render
 764    option('views_dir', option('limonade_views_dir'));
 765    $o = render('_notices.html.php', null, array('notices' => $notices));
 766    option('views_dir', $c_view_dir); // restore current views dir
 767
 768    return $o;
 769  }
 770}
 771
 772/**
 773 * Checks if an error is will halt application execution. 
 774 * Notices and warnings will not.
 775 *
 776 * @access private
 777 * @param string $num error code number
 778 * @return boolean
 779 */
 780function error_wont_halt_app($num)
 781{
 782  return $num == E_NOTICE ||
 783         $num == E_WARNING ||
 784         $num == E_CORE_WARNING ||
 785         $num == E_COMPILE_WARNING ||
 786         $num == E_USER_WARNING ||
 787         $num == E_USER_NOTICE ||
 788         $num == E_DEPRECATED ||
 789         $num == E_USER_DEPRECATED ||
 790         $num == E_LIM_DEPRECATED;
 791}
 792
 793
 794
 795/**
 796 * return error code name for a given code num, or return all errors names
 797 *
 798 * @param string $num 
 799 * @return mixed
 800 */
 801function error_type($num = null)
 802{
 803  $types = array (
 804              E_ERROR              => 'ERROR',
 805              E_WARNING            => 'WARNING',
 806              E_PARSE              => 'PARSING ERROR',
 807              E_NOTICE             => 'NOTICE',
 808              E_CORE_ERROR         => 'CORE ERROR',
 809              E_CORE_WARNING       => 'CORE WARNING',
 810              E_COMPILE_ERROR      => 'COMPILE ERROR',
 811              E_COMPILE_WARNING    => 'COMPILE WARNING',
 812              E_USER_ERROR         => 'USER ERROR',
 813              E_USER_WARNING       => 'USER WARNING',
 814              E_USER_NOTICE        => 'USER NOTICE',
 815              E_STRICT             => 'STRICT NOTICE',
 816              E_RECOVERABLE_ERROR  => 'RECOVERABLE ERROR',
 817              E_DEPRECATED         => 'DEPRECATED WARNING',
 818              E_USER_DEPRECATED    => 'USER DEPRECATED WARNING',
 819              E_LIM_DEPRECATED     => 'LIMONADE DEPRECATED WARNING'
 820              );
 821  return is_null($num) ? $types : $types[$num];
 822}
 823
 824/**
 825 * Returns http response status for a given error number
 826 *
 827 * @param string $errno 
 828 * @return int
 829 */
 830function error_http_status($errno)
 831{
 832  $code = http_response_status_is_valid($errno) ? $errno : SERVER_ERROR;
 833  return http_response_status($code);
 834}
 835
 836
 837
 838
 839                                     # # #
 840
 841
 842
 843
 844# ============================================================================ #
 845#    3. REQUEST                                                                #
 846# ============================================================================ #
 847 
 848/**
 849 * Returns current request method for a given environment or current one
 850 *
 851 * @param string $env 
 852 * @return string
 853 */
 854function request_method($env = null)
 855{
 856  if(is_null($env)) $env = env();
 857  $m = array_key_exists('REQUEST_METHOD', $env['SERVER']) ? $env['SERVER']['REQUEST_METHOD'] : null;
 858  if($m == "POST" && array_key_exists('_method', $env['POST'])) 
 859    $m = strtoupper($env['POST']['_method']);
 860  if(!in_array(strtoupper($m), request_methods()))
 861  {
 862    trigger_error("'$m' request method is unkown or unavailable.", E_USER_WARNING);
 863    $m = false;
 864  }
 865  return $m;
 866}
 867
 868/**
 869 * Checks if a request method or current one is allowed
 870 *
 871 * @param string $m 
 872 * @return bool
 873 */
 874function request_method_is_allowed($m = null)
 875{
 876  if(is_null($m)) $m = request_method();
 877  return in_array(strtoupper($m), request_methods());
 878}
 879
 880/**
 881 * Checks if request method is GET
 882 *
 883 * @param string $env 
 884 * @return bool
 885 */
 886function request_is_get($env = null)
 887{
 888  return request_method($env) == "GET";
 889}
 890
 891/**
 892 * Checks if request method is POST
 893 *
 894 * @param string $env 
 895 * @return bool
 896 */
 897function request_is_post($env = null)
 898{
 899  return request_method($env) == "POST";
 900}
 901
 902/**
 903 * Checks if request method is PUT
 904 *
 905 * @param string $env 
 906 * @return bool
 907 */
 908function request_is_put($env = null)
 909{
 910  return request_method($env) == "PUT";
 911}
 912
 913/**
 914 * Checks if request method is DELETE
 915 *
 916 * @param string $env 
 917 * @return bool
 918 */
 919function request_is_delete($env = null)
 920{
 921  return request_method($env) == "DELETE";
 922}
 923
 924/**
 925 * Checks if request method is HEAD
 926 *
 927 * @param string $env 
 928 * @return bool
 929 */
 930function request_is_head($env = null)
 931{
 932  return request_method($env) == "HEAD";
 933}
 934
 935/**
 936 * Returns allowed request methods
 937 *
 938 * @return array
 939 */
 940function request_methods()
 941{
 942  return array("GET","POST","PUT","DELETE", "HEAD");
 943}
 944
 945/**
 946 * Returns current request uri (the path that will be compared with routes)
 947 * 
 948 * (Inspired from codeigniter URI::_fetch_uri_string method)
 949 *
 950 * @return string
 951 */
 952function request_uri($env = null)
 953{
 954  static $uri = null;
 955  if(is_null($env))
 956  {
 957    if(!is_null($uri)) return $uri;
 958    $env = env();
 959  }
 960
 961  if(array_key_exists('uri', $env['GET']))
 962  {
 963    $uri = $env['GET']['uri'];
 964  }
 965  else if(array_key_exists('u', $env['GET']))
 966  {
 967    $uri = $env['GET']['u'];
 968  }
 969  // bug: dot are converted to _... so we can't use it...
 970  // else if (count($env['GET']) == 1 && trim(key($env['GET']), '/') != '')
 971  // {
 972  //  $uri = key($env['GET']);
 973  // }
 974  else
 975  {
 976    $app_file = app_file();
 977    $path_info = isset($env['SERVER']['PATH_INFO']) ? $env['SERVER']['PATH_INFO'] : @getenv('PATH_INFO');
 978    $query_string =  isset($env['SERVER']['QUERY_STRING']) ? $env['SERVER']['QUERY_STRING'] : @getenv('QUERY_STRING');
 979
 980    // Is there a PATH_INFO variable?
 981  	// Note: some servers seem to have trouble with getenv() so we'll test it two ways
 982  	if (trim($path_info, '/') != '' && $path_info != "/".$app_file)
 983  	{
 984  	  $uri = $path_info;
 985  	}
 986  	// No PATH_INFO?... What about QUERY_STRING?
 987  	elseif (trim($query_string, '/') != '')
 988  	{
 989  	  $uri = $query_string;
 990  	}
 991  	elseif(array_key_exists('REQUEST_URI', $env['SERVER']) && !empty($env['SERVER']['REQUEST_URI']))
 992  	{
 993  	  $request_uri = rtrim(rawurldecode($env['SERVER']['REQUEST_URI']), '?/').'/';
 994  	  $base_path = $env['SERVER']['SCRIPT_NAME'];
 995
 996      if($request_uri."index.php" == $base_path) $request_uri .= "index.php";
 997  	  $uri = str_replace($base_path, '', $request_uri);
 998  	}
 999  	elseif($env['SERVER']['argc'] > 1 && trim($env['SERVER']['argv'][1], '/') != '')
1000    {
1001      $uri = $env['SERVER']['argv'][1];
1002    }
1003  }
1004
1005  $uri = rtrim($uri, "/"); # removes ending /
1006  if(empty($uri))
1007  {
1008    $uri = '/';
1009  }
1010  else if($uri[0] != '/')
1011  {
1012    $uri = '/' . $uri; # add a leading slash
1013  }
1014  return $uri;
1015}
1016
1017
1018
1019
1020                                     # # #
1021
1022
1023
1024
1025# ============================================================================ #
1026#    4. ROUTER                                                                 #
1027# ============================================================================ #
1028 
1029/**
1030 * an alias of dispatch_get
1031 *
1032 * @return void
1033 */
1034function dispatch($path_or_array, $function)
1035{
1036  dispatch_get($path_or_array, $function);
1037}
1038
1039/**
1040 * Add a GET route. Also automatically defines a HEAD route.
1041 *
1042 * @param string $path_or_array 
1043 * @param string $function 
1044 * @return void
1045 */
1046function dispatch_get($path_or_array, $function)
1047{
1048  route("GET", $path_or_array, $function);
1049  route("HEAD", $path_or_array, $function);
1050}
1051
1052/**
1053 * Add a POST route
1054 *
1055 * @param string $path_or_array 
1056 * @param string $function 
1057 * @return void
1058 */
1059function dispatch_post($path_or_array, $function)
1060{
1061  route("POST", $path_or_array, $function);
1062}
1063
1064/**
1065 * Add a PUT route
1066 *
1067 * @param string $path_or_array 
1068 * @param string $function 
1069 * @return void
1070 */
1071function dispatch_put($path_or_array, $function)
1072{
1073  route("PUT", $path_or_array, $function);
1074}
1075
1076/**
1077 * Add a DELETE route
1078 *
1079 * @param string $path_or_array 
1080 * @param string $function 
1081 * @return void
1082 */
1083function dispatch_delete($path_or_array, $function)
1084{
1085  route("DELETE", $path_or_array, $function);
1086}
1087
1088
1089/**
1090 * Add route if required params are provided.
1091 * Delete all routes if null is passed as a unique argument
1092 * Return all routes
1093 * 
1094 * @access private
1095 * @param string $method 
1096 * @param string $path_or_array 
1097 * @param string $func
1098 * @return array
1099 */
1100function route()
1101{
1102  static $routes = array();
1103  $nargs = func_num_args();
1104  if( $nargs > 0)
1105  {
1106    $args = func_get_args();
1107    if($nargs === 1 && is_null($args[0])) $routes = array();
1108    else if($nargs < 3) trigger_error("Missing arguments for route()", E_USER_ERROR);
1109    else
1110    {
1111      $method        = $args[0];
1112      $path_or_array = $args[1];
1113      $func          = $args[2];
1114
1115      $routes[] = route_build($method, $path_or_array, $func);
1116    }
1117  }
1118  return $routes;
1119}
1120
1121/**
1122 * An alias of route(null): reset all routes
1123 * 
1124 * @access private
1125 * @return void
1126 */
1127function route_reset()
1128{
1129  route(null);
1130}
1131
1132/**
1133 * Build a route and return it
1134 *
1135 * @access private
1136 * @param string $method 
1137 * @param string $path_or_array 
1138 * @param string $func
1139 * @return array
1140 */
1141function route_build($method, $path_or_array, $func)
1142{
1143  $method = strtoupper($method);
1144  if(!in_array($method, request_methods())) 
1145    trigger_error("'$method' request method is unkown or unavailable.", E_USER_WARNING);
1146
1147  if(is_array($path_or_array))
1148  {
1149    $path  = array_shift($path_or_array);
1150    $names = $path_or_array[0];
1151  }
1152  else
1153  {
1154    $path  = $path_or_array;
1155    $names = array();
1156  }
1157
1158  $single_asterisk_subpattern   = "(?:/([^\/]*))?";
1159  $double_asterisk_subpattern   = "(?:/(.*))?";
1160  $optionnal_slash_subpattern   = "(?:/*?)";
1161  $no_slash_asterisk_subpattern = "(?:([^\/]*))?";
1162
1163  if($path[0] == "^")
1164  {
1165    if($path{strlen($path) - 1} != "$") $path .= "$";
1166     $pattern = "#".$path."#i";
1167  }
1168  else if(empty($path) || $path == "/")
1169  {
1170    $pattern = "#^".$optionnal_slash_subpattern."$#";
1171  }
1172  else
1173  {
1174    $parsed = array();
1175    $elts = explode('/', $path);
1176
1177    $parameters_count = 0;
1178
1179    foreach($elts as $elt)
1180    {
1181      if(empty($elt)) continue;
1182
1183      $name = null; 
1184
1185      # extracting double asterisk **
1186      if($elt == "**"):
1187        $parsed[] = $double_asterisk_subpattern;
1188        $name = $parameters_count;
1189
1190      # extracting single asterisk *
1191      elseif($elt == "*"):
1192        $parsed[] = $single_asterisk_subpattern;
1193        $name = $parameters_count;
1194               
1195      # extracting named parameters :my_param 
1196      elseif($elt[0] == ":"):
1197        if(preg_match('/^:([^\:]+)$/', $elt, $matches))
1198        {
1199          $parsed[] = $single_asterisk_subpattern;
1200          $name = $matches[1];
1201        };
1202
1203      elseif(strpos($elt, '*') !== false):
1204        $sub_elts = explode('*', $elt);
1205        $parsed_sub = array();
1206        foreach($sub_elts as $sub_elt)
1207        {
1208          $parsed_sub[] = preg_quote($sub_elt, "#");
1209          $name = $parameters_count;
1210        }
1211        // 
1212        $parsed[] = "/".implode($no_slash_asterisk_subpattern, $parsed_sub);
1213
1214      else:
1215        $parsed[] = "/".preg_quote($elt, "#");
1216
1217      endif;
1218
1219      /* set parameters names */ 
1220      if(is_null($name)) continue;
1221      if(!array_key_exists($parameters_count, $names) || is_null($names[$parameters_count]))
1222        $names[$parameters_count] = $name;
1223      $parameters_count++;
1224    }
1225
1226    $pattern = "#^".implode('', $parsed).$optionnal_slash_subpattern."?$#i";
1227  }
1228
1229  return array( "method"       => $method,
1230                "pattern"      => $pattern,
1231                "names"        => $names,
1232                "function"     => $func     );
1233}
1234
1235/**
1236 * Find a route and returns it.
1237 * If not found, returns false.
1238 * Routes are checked from first added to last added.
1239 *
1240 * @access private
1241 * @param string $method 
1242 * @param string $path 
1243 * @return array,false
1244 */
1245function route_find($method, $path)
1246{
1247  $routes = route();
1248  $method = strtoupper($method);
1249  foreach($routes as $route)
1250  {
1251    if($method == $route["method"] && preg_match($route["pattern"], $path, $matches))
1252    {
1253      $params = array();
1254      if(count($matches) > 1)
1255      {
1256        array_shift($matches);
1257        $n_matches = count($matches);
1258        $names     = array_values($route["names"]);
1259        $n_names   = count($names);
1260        if( $n_matches < $n_names )
1261        {
1262          $a = array_fill(0, $n_names - $n_matches, null);
1263          $matches = array_merge($matches, $a);
1264        }
1265        else if( $n_matches > $n_names )
1266        {
1267          $names = range($n_names, $n_matches - 1);
1268        }
1269        $params = array_combine($names, $matches);
1270      }
1271      $route["params"] = $params;
1272      return $route;
1273    }
1274  }
1275  return false;
1276}
1277
1278
1279
1280
1281
1282# ============================================================================ #
1283#    OUTPUT AND RENDERING                                                      #
1284# ============================================================================ #
1285
1286/**
1287 * Returns a string to output
1288 * 
1289 * It might use a a template file or function, a formatted string (like {@link sprintf()}).
1290 * It could be embraced by a layout or not.
1291 * Local vars can be passed in addition to variables made available with the {@link set()}
1292 * function.
1293 *
1294 * @param string $content_or_func 
1295 * @param string $layout 
1296 * @param string $locals 
1297 * @return string
1298 */
1299function render($content_or_func, $layout = '', $locals = array())
1300{
1301  $args = func_get_args();
1302  $content_or_func = array_shift($args);
1303  $layout = count($args) > 0 ? array_shift($args) : layout();
1304  $view_path = file_path(option('views_dir'),$content_or_func);
1305  $vars = array_merge(set(), $locals);
1306
1307  $flash = flash_now();
1308  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);  
1309  else if(!empty($flash)) $vars['flash'] = $flash;
1310
1311  $infinite_loop = false;
1312
1313  # Avoid infinite loop: this function is in the backtrace ?
1314  if(function_exists($content_or_func))
1315  {
1316    $back_trace = debug_backtrace();
1317    while($trace = array_shift($back_trace))
1318    {
1319      if($trace['function'] == strtolower($content_or_func))
1320      {
1321        $infinite_loop = true;
1322        break;
1323      }
1324    }
1325  }
1326
1327  if(function_exists($content_or_func) && !$infinite_loop)
1328  {
1329    ob_start();
1330    call_user_func($content_or_func, $vars);
1331    $content = ob_get_clean();
1332  }
1333  elseif(file_exists($view_path))
1334  {
1335    ob_start();
1336    extract($vars);
1337    include $view_path;
1338    $content = ob_get_clean();
1339  }
1340  else
1341  {
1342    if(substr_count($content_or_func, '%') !== count($vars)) $content = $content_or_func;
1343    else $content = vsprintf($content_or_func, $vars);
1344  }
1345
1346  if(empty($layout)) return $content;
1347
1348  return render($layout, null, array('content' => $content));
1349}
1350
1351/**
1352 * Returns a string to output
1353 * 
1354 * Shortcut to render with no layout.
1355 *
1356 * @param string $content_or_func 
1357 * @param string $locals 
1358 * @return string
1359 */
1360function render_partial($content_or_func, $locals = array())
1361{
1362  return render($content_or_func, null, $locals);
1363}
1364
1365/**
1366 * Returns html output with proper http headers
1367 *
1368 * @param string $content_or_func 
1369 * @param string $layout 
1370 * @param string $locals 
1371 * @return string
1372 */ 
1373function html($content_or_func, $layout = '', $locals = array())
1374{
1375  if(!headers_sent()) header('Content-Type: text/html; charset='.strtolower(option('encoding')));
1376  $args = func_get_args();
1377  return call_user_func_array('render', $args);
1378}
1379
1380/**
1381 * Set and return current layout
1382 *
1383 * @param string $function_or_file 
1384 * @return string
1385 */
1386function layout($function_or_file = null)
1387{
1388  static $layout = null;
1389  if(func_num_args() > 0) $layout = $function_or_file;
1390  return $layout;
1391}
1392
1393/**
1394 * Returns xml output with proper http headers
1395 *
1396 * @param string $content_or_func 
1397 * @param string $layout 
1398 * @param string $locals 
1399 * @return string
1400 */
1401function xml($data)
1402{
1403  if(!headers_sent()) header('Content-Type: text/xml; charset='.strtolower(option('encoding')));
1404  $args = func_get_args();
1405  return call_user_func_array('render', $args);
1406}
1407
1408/**
1409 * Returns css output with proper http headers
1410 *
1411 * @param string $content_or_func 
1412 * @param string $layout 
1413 * @param string $locals 
1414 * @return string
1415 */
1416function css($content_or_func, $layout = '', $locals = array())
1417{
1418  if(!headers_sent()) header('Content-Type: text/css; charset='.strtolower(option('encoding')));
1419  $args = func_get_args();
1420  return call_user_func_array('render', $args);
1421}
1422
1423/**
1424 * Returns txt output with proper http headers
1425 *
1426 * @param string $content_or_func 
1427 * @param string $layout 
1428 * @param string $locals 
1429 * @return string
1430 */
1431function txt($content_or_func, $layout = '', $locals = array())
1432{
1433  if(!headers_sent()) header('Content-Type: text/plain; charset='.strtolower(option('encoding')));
1434  $args = func_get_args();
1435  return call_user_func_array('render', $args);
1436}
1437
1438/**
1439 * Returns json representation of data with proper http headers
1440 *
1441 * @param string $data 
1442 * @param int $json_option
1443 * @return string
1444 */
1445function json($data, $json_option = 0)
1446{
1447  if(!headers_sent()) header('Content-Type: application/x-javascript; charset='.strtolower(option('encoding')));
1448  return version_compare(PHP_VERSION, '5.3.0', '>=') ? json_encode($data, $json_option) : json_encode($data);
1449}
1450
1451/**
1452 * undocumented function
1453 *
1454 * @param string $filename 
1455 * @param string $return 
1456 * @return mixed number of bytes delivered or file output if $return = true
1457 */
1458function render_file($filename, $return = false)
1459{
1460  # TODO implements X-SENDFILE headers
1461  // if($x-sendfile = option('x-sendfile'))
1462  // {
1463  //    // add a X-Sendfile header for apache and Lighttpd >= 1.5
1464  //    if($x-sendfile > X-SENDFILE) // add a X-LIGHTTPD-send-file header 
1465  //   
1466  // }
1467  // else
1468  // {
1469  //   
1470  // }
1471  $filename = str_replace('../', '', $filename);
1472  if(file_exists($filename))
1473  {
1474    $content_type = mime_type(file_extension($filename));
1475    $header = 'Content-type: '.$content_type;
1476    if(file_is_text($filename)) $header .= '; charset='.strtolower(option('encoding'));
1477    if(!headers_sent()) header($header);
1478    return file_read($filename, $return);
1479  }
1480  else halt(NOT_FOUND, "unknown filename $filename");
1481}
1482
1483
1484
1485
1486
1487
1488                                     # # #
1489
1490
1491
1492
1493# ============================================================================ #
1494#    5. HELPERS                                                                #
1495# ============================================================================ #
1496
1497/**
1498 * Returns an url composed of params joined with /
1499 *
1500 * @param string $params,... 
1501 * @return string
1502 */ 
1503function url_for($params = null)
1504{
1505  $paths  = array();
1506  $params = func_get_args();
1507  $first  = true;
1508  foreach($params as $param)
1509  {
1510    if($first)
1511    {
1512      if(filter_var($param , FILTER_VALIDATE_URL))
1513      {
1514        $paths[] = $param;
1515        continue;
1516      }
1517    }
1518    $p = explode('/',$param);
1519    foreach($p as $v)
1520    {
1521      if(!empty($v)) $paths[] = str_replace('%23', '#', rawurlencode($v));
1522    }
1523  }
1524
1525  $path = rtrim(implode('/', $paths), '/');
1526  
1527  if(!filter_var($path , FILTER_VALIDATE_URL)) 
1528  {
1529    # it's a relative URL or an URL without a schema
1530    $base_uri = option('base_uri');
1531    $path = file_path($base_uri, $path);
1532  }
1533
1534  if(DIRECTORY_SEPARATOR != '/') $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
1535
1536  return $path;
1537}
1538
1539/**
1540 * An alias of {@link htmlspecialchars()}.
1541 * If no $charset is provided, uses option('encoding') value
1542 *
1543 * @param string $str 
1544 * @param string $quote_style 
1545 * @param string $charset 
1546 * @return void
1547 */
1548function h($str, $quote_style = ENT_NOQUOTES, $charset = null)
1549{
1550  if(is_null($charset)) $charset = strtoupper(option('encoding'));
1551  return htmlspecialchars($str, $quote_style, $charset); 
1552}
1553
1554/**
1555 * Set and returns flash messages that will be available in the next action
1556 * via the {@link flash_now()} function or the view variable <code>$flash</code>.
1557 * 
1558 * If multiple values are provided, set <code>$name</code> variable with an array of those values.
1559 * If there is only one value, set <code>$name</code> variable with the provided $values
1560 * or if it's <code>$name</code> is an array, merge it with current messages.
1561 *
1562 * @param string, array $name 
1563 * @param mixed  $values,... 
1564 * @return mixed variable value for $name if $name argument is provided, else return all variables
1565 */
1566function flash($name = null, $value = null)
1567{
1568  if(!defined('SID')) trigger_error("Flash messages can't be used because session isn't enabled", E_USER_WARNING);
1569  static $messages = array();
1570  $args = func_get_args();
1571  $name = array_shift($args);
1572  if(is_null($name)) return $messages;
1573  if(is_array($name)) return $messages = array_merge($messages, $name);
1574  if(!empty($args))
1575  {
1576    $messages[$name] = count($args) > 1 ? $args : $args[0];
1577  }
1578  if(array_key_exists($name, $messages)) return $messages[$name];
1579  return $messages;
1580}
1581
1582/**
1583 * Set and returns flash messages available for the current action, included those
1584 * defined in the previous action with {@link flash()}
1585 * Those messages will also be passed to the views and made available in the 
1586 * <code>$flash</code> variable.
1587 * 
1588 * If multiple values are provided, set <code>$name</code> variable with an array of those values.
1589 * If there is only one value, set <code>$name</code> variable with the provided $values
1590 * or if it's <code>$name</code> is an array, merge it with current messages.
1591 *
1592 * @param string, array $name 
1593 * @param mixed  $values,... 
1594 * @return mixed variable value for $name if $name argument is provided, else return all variables
1595 */
1596function flash_now($name = null, $value = null)
1597{
1598  static $messages = null;
1599  if(is_null($messages))
1600  {
1601    $fkey = LIM_SESSION_FLASH_KEY;
1602    $messages = array();
1603    if(defined('SID') && array_key_exists($fkey, $_SESSION)) $messages = $_SESSION[$fkey];
1604  }
1605  $args = func_get_args();
1606  $name = array_shift($args);
1607  if(is_null($name)) return $messages;
1608  if(is_array($name)) return $messages = array_merge($messages, $name);
1609  if(!empty($args))
1610  {
1611    $messages[$name] = count($args) > 1 ? $args : $args[0];
1612  }
1613  if(array_key_exists($name, $messages)) return $messages[$name];
1614  return $messages;
1615}
1616
1617/**
1618 * Delete current flash messages in session, and set new ones stored with 
1619 * flash function.
1620 * Called before application exit.
1621 *
1622 * @access private
1623 * @return void
1624 */
1625function flash_sweep()
1626{
1627  if(defined('SID'))
1628  {
1629    $fkey = LIM_SESSION_FLASH_KEY;
1630    $_SESSION[$fkey] = flash();
1631  }
1632}
1633
1634/**
1635 * Starts capturing block of text
1636 *
1637 * Calling without params stops capturing (same as end_content_for()).
1638 * After capturing the captured block is put into a variable
1639 * named $name for later use in layouts. If second parameter
1640 * is supplied, its content will be used instead of capturing
1641 * a block of text.
1642 *
1643 * @param string $name
1644 * @param string $content
1645 * @return void
1646 */
1647function content_for($name = null, $content = null)
1648{
1649  static $_name = null;
1650  if(is_null($name) && !is_null($_name))
1651  {
1652    set($_name, ob_get_clean());
1653    $_name = null;	
1654  }
1655  elseif(!is_null($name) && !isset($content))
1656  {
1657    $_name = $name;	
1658    ob_start();
1659  }
1660  elseif(isset($name, $content))
1661  {
1662    set($name, $content);
1663  }
1664}
1665
1666/**
1667 * Stops capturing block of text
1668 *
1669 * @return void
1670 */
1671function end_content_for()
1672{
1673  content_for();
1674}
1675
1676
1677
1678
1679                                     # # #
1680
1681
1682
1683
1684# ============================================================================ #
1685#    6. UTILS                                                                  #
1686# ============================================================================ #
1687 
1688/**
1689 * Calls a function if exists
1690 *
1691 * @param string $func the function name
1692 * @param mixed $arg,.. (optional)
1693 * @return mixed
1694 */
1695function call_if_exists($func)
1696{
1697  $args = func_get_args();
1698  $func = array_shift($args);
1699  if(function_exists($func)) return call_user_func_array($func, $args);
1700  return;
1701}
1702
1703/**
1704 * Define a constant unless it already exists
1705 *
1706 * @param string $name 
1707 * @param string $value 
1708 * @return void
1709 */
1710function define_unless_exists($name, $value)
1711{
1712  if(!defined($name)) define($name, $value);
1713}
1714
1715/**
1716 * Return a default value if provided value is empty
1717 *
1718 * @param mixed $value 
1719 * @param mixed $default default value returned if $value is empty
1720 * @return mixed
1721 */
1722function value_or_default($value, $default)
1723{
1724  return empty($value) ? $default : $value;
1725}
1726
1727/**
1728 * An alias of {@link value_or_default()}
1729 *
1730 * 
1731 * @param mixed $value 
1732 * @param mixed $default 
1733 * @return mixed
1734 */
1735function v($value, $default)
1736{
1737  return value_or_default($value, $default);
1738}
1739
1740/**
1741 * Load php files with require_once in a given dir
1742 *
1743 * @param string $path Path in which are the file to load
1744 * @param string $pattern a regexp pattern that filter files to load
1745 * @return array paths of loaded files
1746 */
1747function require_once_dir($path, $pattern = "*.php")
1748{
1749  if($path[strlen($path) - 1] != "/") $path .= "/";
1750  $filenames = glob($path.$pattern);
1751  if(!is_array($filenames)) $filenames = array();
1752  foreach($filenames as $filename) require_once $filename;
1753  return $filenames;
1754}
1755
1756
1757## HTTP utils  _________________________________________________________________
1758
1759
1760### Constants: HTTP status codes
1761
1762define( 'HTTP_CONTINUE',                      100 );
1763define( 'HTTP_SWITCHING_PROTOCOLS',           101 );
1764define( 'HTTP_PROCESSING',                    102 );
1765define( 'HTTP_OK',                            200 );
1766define( 'HTTP_CREATED',                       201 );
1767define( 'HTTP_ACCEPTED',                      202 );
1768define( 'HTTP_NON_AUTHORITATIVE',             203 );
1769define( 'HTTP_NO_CONTENT',                    204 );
1770define( 'HTTP_RESET_CONTENT',                 205 );
1771define( 'HTTP_PARTIAL_CONTENT',               206 );
1772define( 'HTTP_MULTI_STATUS',                  207 );
1773                                              
1774define( 'HTTP_MULTIPLE_CHOICES',              300 );
1775define( 'HTTP_MOVED_PERMANENTLY',             301 );
1776define( 'HTTP_MOVED_TEMPORARILY',             302 );
1777define( 'HTTP_SEE_OTHER',                     303 );
1778define( 'HTTP_NOT_MODIFIED',                  304 );
1779define( 'HTTP_USE_PROXY',                     305 );
1780define( 'HTTP_TEMPORARY_REDIRECT',            307 );
1781
1782define( 'HTTP_BAD_REQUEST',                   400 );
1783define( 'HTTP_UNAUTHORIZED',                  401 );
1784define( 'HTTP_PAYMENT_REQUIRED',              402 );
1785define( 'HTTP_FORBIDDEN',                     403 );
1786define( 'HTTP_NOT_FOUND',                     404 );
1787define( 'HTTP_METHOD_NOT_ALLOWED',            405 );
1788define( 'HTTP_NOT_ACCEPTABLE',                406 );
1789define( 'HTTP_PROXY_AUTHENTICATION_REQUIRED', 407 );
1790define( 'HTTP_REQUEST_TIME_OUT',              408 );
1791define( 'HTTP_CONFLICT',                      409 );
1792define( 'HTTP_GONE',                          410 );
1793define( 'HTTP_LENGTH_REQUIRED',               411 );
1794define( 'HTTP_PRECONDITION_FAILED',           412 );
1795define( 'HTTP_REQUEST_ENTITY_TOO_LARGE',      413 );
1796define( 'HTTP_REQUEST_URI_TOO_LARGE',         414 );
1797define( 'HTTP_UNSUPPORTED_MEDIA_TYPE',        415 );
1798define( 'HTTP_RANGE_NOT_SATISFIABLE',         416 );
1799define( 'HTTP_EXPECTATION_FAILED',            417 );
1800define( 'HTTP_UNPROCESSABLE_ENTITY',          422 );
1801define( 'HTTP_LOCKED',                        423 );
1802define( 'HTTP_FAILED_DEPENDENCY',             424 );
1803define( 'HTTP_UPGRADE_REQUIRED',              426 );
1804
1805define( 'HTTP_INTERNAL_SERVER_ERROR',         500 );
1806define( 'HTTP_NOT_IMPLEMENTED',               501 );
1807define( 'HTTP_BAD_GATEWAY',                   502 );
1808define( 'HTTP_SERVICE_UNAVAILABLE',           503 );
1809define( 'HTTP_GATEWAY_TIME_OUT',              504 );
1810define( 'HTTP_VERSION_NOT_SUPPORTED',         505 );
1811define( 'HTTP_VARIANT_ALSO_VARIES',           506 );
1812define( 'HTTP_INSUFFICIENT_STORAGE',          507 );
1813define( 'HTTP_NOT_EXTENDED',                  510 );
1814
1815/**
1816 * Output proper HTTP header for a given HTTP code
1817 *
1818 * @param string $code 
1819 * @return void
1820 */
1821function status($code = 500)
1822{
1823  if(!headers_sent())
1824  {
1825    $str = http_response_status_code($code);
1826    header($str);
1827  }
1828}
1829
1830/**
1831 * Http redirection
1832 *
1833 * @param string $params,... 
1834 * @return void
1835 */
1836function redirect_to($params)
1837{
1838  # [NOTE]: (from php.net) HTTP/1.1 requires an absolute URI as argument …

Large files files are truncated, but you can click here to view the full file