PageRenderTime 37ms CodeModel.GetById 33ms RepoModel.GetById 1ms app.codeStats 0ms

/library/limonade.php

https://bitbucket.org/cpoppema/stockpiler
PHP | 2585 lines | 1433 code | 252 blank | 900 comment | 240 complexity | dd4b975da456266c94a9c1fd55dbd255 MD5 | raw file

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

  1. <?php
  2. # ============================================================================ #
  3. /**
  4. * L I M O N A D E
  5. *
  6. * a PHP micro framework.
  7. *
  8. * For more informations: {@link http://github/sofadesign/limonade}
  9. *
  10. * @author Fabrice Luraine
  11. * @copyright Copyright (c) 2009 Fabrice Luraine
  12. * @license http://opensource.org/licenses/mit-license.php The MIT License
  13. * @package limonade
  14. */
  15. # ----------------------------------------------------------------------- #
  16. # Copyright (c) 2009 Fabrice Luraine #
  17. # #
  18. # Permission is hereby granted, free of charge, to any person #
  19. # obtaining a copy of this software and associated documentation #
  20. # files (the "Software"), to deal in the Software without #
  21. # restriction, including without limitation the rights to use, #
  22. # copy, modify, merge, publish, distribute, sublicense, and/or sell #
  23. # copies of the Software, and to permit persons to whom the #
  24. # Software is furnished to do so, subject to the following #
  25. # conditions: #
  26. # #
  27. # The above copyright notice and this permission notice shall be #
  28. # included in all copies or substantial portions of the Software. #
  29. # #
  30. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, #
  31. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES #
  32. # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND #
  33. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT #
  34. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, #
  35. # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING #
  36. # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR #
  37. # OTHER DEALINGS IN THE SOFTWARE. #
  38. # ============================================================================ #
  39. # ============================================================================ #
  40. # 0. PREPARE #
  41. # ============================================================================ #
  42. ## CONSTANTS __________________________________________________________________
  43. /**
  44. * Limonade version
  45. */
  46. define('LIMONADE', '0.5.0');
  47. define('LIM_NAME', 'Un grand cru qui sait se faire attendre');
  48. define('LIM_START_MICROTIME', (float)substr(microtime(), 0, 10));
  49. define('LIM_SESSION_NAME', 'LIMONADE'.str_replace('.','x',LIMONADE));
  50. define('LIM_SESSION_FLASH_KEY', '_lim_flash_messages');
  51. define('LIM_START_MEMORY', memory_get_usage());
  52. define('E_LIM_HTTP', 32768);
  53. define('E_LIM_PHP', 65536);
  54. define('E_LIM_DEPRECATED', 35000);
  55. define('NOT_FOUND', 404);
  56. define('SERVER_ERROR', 500);
  57. define('ENV_PRODUCTION', 10);
  58. define('ENV_DEVELOPMENT', 100);
  59. define('X-SENDFILE', 10);
  60. define('X-LIGHTTPD-SEND-FILE', 20);
  61. # for PHP 5.3.0 <
  62. if(!defined('E_DEPRECATED')) define('E_DEPRECATED', 8192);
  63. if(!defined('E_USER_DEPRECATED')) define('E_USER_DEPRECATED', 16384);
  64. ## SETTING BASIC SECURITY _____________________________________________________
  65. # A. Unsets all global variables set from a superglobal array
  66. /**
  67. * @access private
  68. * @return void
  69. */
  70. function unregister_globals()
  71. {
  72. $args = func_get_args();
  73. foreach($args as $k => $v)
  74. if(array_key_exists($k, $GLOBALS)) unset($GLOBALS[$k]);
  75. }
  76. if(ini_get('register_globals'))
  77. {
  78. unregister_globals( '_POST', '_GET', '_COOKIE', '_REQUEST', '_SERVER',
  79. '_ENV', '_FILES');
  80. ini_set('register_globals', 0);
  81. }
  82. # B. removing magic quotes
  83. /**
  84. * @access private
  85. * @param string $array
  86. * @return array
  87. */
  88. function remove_magic_quotes($array)
  89. {
  90. foreach ($array as $k => $v)
  91. $array[$k] = is_array($v) ? remove_magic_quotes($v) : stripslashes($v);
  92. return $array;
  93. }
  94. if (get_magic_quotes_gpc())
  95. {
  96. $_GET = remove_magic_quotes($_GET);
  97. $_POST = remove_magic_quotes($_POST);
  98. $_COOKIE = remove_magic_quotes($_COOKIE);
  99. ini_set('magic_quotes_gpc', 0);
  100. }
  101. if(function_exists('set_magic_quotes_runtime') && get_magic_quotes_runtime()) set_magic_quotes_runtime(false);
  102. # C. Disable error display
  103. # by default, no error reporting; it will be switched on later in run().
  104. # ini_set('display_errors', 1); must be called explicitly in app file
  105. # if you want to show errors before running app
  106. ini_set('display_errors', 0);
  107. ## SETTING INTERNAL ROUTES _____________________________________________________
  108. dispatch(array("/_lim_css/*.css", array('_lim_css_filename')), 'render_limonade_css');
  109. /**
  110. * Internal controller that responds to route /_lim_css/*.css
  111. *
  112. * @access private
  113. * @return string
  114. */
  115. function render_limonade_css()
  116. {
  117. option('views_dir', file_path(option('limonade_public_dir'), 'css'));
  118. $fpath = file_path(params('_lim_css_filename').".css");
  119. return css($fpath, null); // with no layout
  120. }
  121. dispatch(array("/_lim_public/**", array('_lim_public_file')), 'render_limonade_file');
  122. /**
  123. * Internal controller that responds to route /_lim_public/**
  124. *
  125. * @access private
  126. * @return void
  127. */
  128. function render_limonade_file()
  129. {
  130. $fpath = file_path(option('limonade_public_dir'), params('_lim_public_file'));
  131. return render_file($fpath, true);
  132. }
  133. # # #
  134. # ============================================================================ #
  135. # 1. BASE #
  136. # ============================================================================ #
  137. ## ABSTRACTS ___________________________________________________________________
  138. # Abstract methods that might be redefined by user:
  139. #
  140. # - function configure(){}
  141. # - function initialize(){}
  142. # - function autoload_controller($callback){}
  143. # - function before($route){}
  144. # - function after($output, $route){}
  145. # - function not_found($errno, $errstr, $errfile=null, $errline=null){}
  146. # - function server_error($errno, $errstr, $errfile=null, $errline=null){}
  147. # - function route_missing($request_method, $request_uri){}
  148. # - function before_exit(){}
  149. # - function before_render($content_or_func, $layout, $locals, $view_path){}
  150. # - function autorender($route){}
  151. #
  152. # See abstract.php for more details.
  153. ## MAIN PUBLIC FUNCTIONS _______________________________________________________
  154. /**
  155. * Set and returns options values
  156. *
  157. * If multiple values are provided, set $name option with an array of those values.
  158. * If there is only one value, set $name option with the provided $values
  159. *
  160. * @param string $name
  161. * @param mixed $values,...
  162. * @return mixed option value for $name if $name argument is provided, else return all options
  163. */
  164. function option($name = null, $values = null)
  165. {
  166. static $options = array();
  167. $args = func_get_args();
  168. $name = array_shift($args);
  169. if(is_null($name)) return $options;
  170. if(!empty($args))
  171. {
  172. $options[$name] = count($args) > 1 ? $args : $args[0];
  173. }
  174. if(array_key_exists($name, $options)) return $options[$name];
  175. return;
  176. }
  177. /**
  178. * Set and returns params
  179. *
  180. * Depending on provided arguments:
  181. *
  182. * * Reset params if first argument is null
  183. *
  184. * * If first argument is an array, merge it with current params
  185. *
  186. * * If there is a second argument $value, set param $name (first argument) with $value
  187. * <code>
  188. * params('name', 'Doe') // set 'name' => 'Doe'
  189. * </code>
  190. * * If there is more than 2 arguments, set param $name (first argument) value with
  191. * an array of next arguments
  192. * <code>
  193. * params('months', 'jan', 'feb', 'mar') // set 'month' => array('months', 'jan', 'feb', 'mar')
  194. * </code>
  195. *
  196. * @param mixed $name_or_array_or_null could be null || array of params || name of a param (optional)
  197. * @param mixed $value,... for the $name param (optional)
  198. * @return mixed all params, or one if a first argument $name is provided
  199. */
  200. function params($name_or_array_or_null = null, $value = null)
  201. {
  202. static $params = array();
  203. $args = func_get_args();
  204. if(func_num_args() > 0)
  205. {
  206. $name = array_shift($args);
  207. if(is_null($name))
  208. {
  209. # Reset params
  210. $params = array();
  211. return $params;
  212. }
  213. if(is_array($name))
  214. {
  215. $params = array_merge($params, $name);
  216. return $params;
  217. }
  218. $nargs = count($args);
  219. if($nargs > 0)
  220. {
  221. $value = $nargs > 1 ? $args : $args[0];
  222. $params[$name] = $value;
  223. }
  224. return array_key_exists($name,$params) ? $params[$name] : null;
  225. }
  226. return $params;
  227. }
  228. /**
  229. * Set and returns template variables
  230. *
  231. * If multiple values are provided, set $name variable with an array of those values.
  232. * If there is only one value, set $name variable with the provided $values
  233. *
  234. * @param string $name
  235. * @param mixed $values,...
  236. * @return mixed variable value for $name if $name argument is provided, else return all variables
  237. */
  238. function set($name = null, $values = null)
  239. {
  240. static $vars = array();
  241. $args = func_get_args();
  242. $name = array_shift($args);
  243. if(is_null($name)) return $vars;
  244. if(!empty($args))
  245. {
  246. $vars[$name] = count($args) > 1 ? $args : $args[0];
  247. }
  248. if(array_key_exists($name, $vars)) return $vars[$name];
  249. return $vars;
  250. }
  251. /**
  252. * Sets a template variable with a value or a default value if value is empty
  253. *
  254. * @param string $name
  255. * @param string $value
  256. * @param string $default
  257. * @return mixed setted value
  258. */
  259. function set_or_default($name, $value, $default)
  260. {
  261. return set($name, value_or_default($value, $default));
  262. }
  263. /**
  264. * Running application
  265. *
  266. * @param string $env
  267. * @return void
  268. */
  269. function run($env = null)
  270. {
  271. if(is_null($env)) $env = env();
  272. # 0. Set default configuration
  273. $root_dir = dirname(app_file());
  274. $base_path = dirname(file_path($env['SERVER']['SCRIPT_NAME']));
  275. $base_file = basename($env['SERVER']['SCRIPT_NAME']);
  276. $base_uri = file_path($base_path, (($base_file == 'index.php') ? '?' : $base_file.'?'));
  277. $lim_dir = dirname(__FILE__);
  278. option('root_dir', $root_dir);
  279. option('base_path', $base_path);
  280. option('base_uri', $base_uri); // set it manually if you use url_rewriting
  281. option('limonade_dir', file_path($lim_dir));
  282. option('limonade_views_dir', file_path($lim_dir, 'limonade', 'views'));
  283. option('limonade_public_dir',file_path($lim_dir, 'limonade', 'public'));
  284. option('public_dir', file_path($root_dir, 'public'));
  285. option('views_dir', file_path($root_dir, 'views'));
  286. option('controllers_dir', file_path($root_dir, 'controllers'));
  287. option('lib_dir', file_path($root_dir, 'lib'));
  288. option('error_views_dir', option('limonade_views_dir'));
  289. option('env', ENV_PRODUCTION);
  290. option('debug', true);
  291. option('session', LIM_SESSION_NAME); // true, false or the name of your session
  292. option('encoding', 'utf-8');
  293. option('signature', LIM_NAME); // X-Limonade header value or false to hide it
  294. option('gzip', false);
  295. option('x-sendfile', 0); // 0: disabled,
  296. // X-SENDFILE: for Apache and Lighttpd v. >= 1.5,
  297. // X-LIGHTTPD-SEND-FILE: for Apache and Lighttpd v. < 1.5
  298. # 1. Set handlers
  299. # 1.1 Set error handling
  300. ini_set('display_errors', 1);
  301. set_error_handler('error_handler_dispatcher', E_ALL ^ E_NOTICE);
  302. # 1.2 Register shutdown function
  303. register_shutdown_function('stop_and_exit');
  304. # 2. Set user configuration
  305. call_if_exists('configure');
  306. # 2.1 Set gzip compression if defined
  307. if(is_bool(option('gzip')) && option('gzip'))
  308. {
  309. ini_set('zlib.output_compression', '1');
  310. }
  311. # 2.2 Set X-Limonade header
  312. if($signature = option('signature')) header("X-Limonade: $signature");
  313. # 3. Loading libs
  314. require_once_dir(option('lib_dir'));
  315. # 4. Starting session
  316. if(!defined('SID') && option('session'))
  317. {
  318. if(!is_bool(option('session'))) session_name(option('session'));
  319. if(!session_start()) trigger_error("An error occured while trying to start the session", E_USER_WARNING);
  320. }
  321. # 5. Set some default methods if needed
  322. if(!function_exists('after'))
  323. {
  324. function after($output)
  325. {
  326. return $output;
  327. }
  328. }
  329. if(!function_exists('route_missing'))
  330. {
  331. function route_missing($request_method, $request_uri)
  332. {
  333. halt(NOT_FOUND, "($request_method) $request_uri");
  334. }
  335. }
  336. call_if_exists('initialize');
  337. # 6. Check request
  338. if($rm = request_method($env))
  339. {
  340. if(request_is_head($env)) ob_start(); // then no output
  341. if(!request_method_is_allowed($rm))
  342. halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
  343. # 6.1 Check matching route
  344. if($route = route_find($rm, request_uri($env)))
  345. {
  346. params($route['params']);
  347. # 6.2 Load controllers dir
  348. if(!function_exists('autoload_controller'))
  349. {
  350. function autoload_controller($callback)
  351. {
  352. require_once_dir(option('controllers_dir'));
  353. }
  354. }
  355. autoload_controller($route['callback']);
  356. if(is_callable($route['callback']))
  357. {
  358. # 6.3 Call before function
  359. call_if_exists('before', $route);
  360. # 6.4 Call matching controller function and output result
  361. $output = call_user_func_array($route['callback'], array_values($route['params']));
  362. if(is_null($output)) $output = call_if_exists('autorender', $route);
  363. echo after(error_notices_render() . $output, $route);
  364. }
  365. else halt(SERVER_ERROR, "Routing error: undefined function '{$route['callback']}'", $route);
  366. }
  367. else route_missing($rm, request_uri($env));
  368. }
  369. else halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
  370. }
  371. /**
  372. * Stop and exit limonade application
  373. *
  374. * @access private
  375. * @param boolean exit or not
  376. * @return void
  377. */
  378. function stop_and_exit($exit = true)
  379. {
  380. call_if_exists('before_exit', $exit);
  381. $flash_sweep = true;
  382. $headers = headers_list();
  383. foreach($headers as $header)
  384. {
  385. // If a Content-Type header exists, flash_sweep only if is text/html
  386. // Else if there's no Content-Type header, flash_sweep by default
  387. if(stripos($header, 'Content-Type:') === 0)
  388. {
  389. $flash_sweep = stripos($header, 'Content-Type: text/html') === 0;
  390. break;
  391. }
  392. }
  393. if($flash_sweep) flash_sweep();
  394. if(defined('SID')) session_write_close();
  395. if(request_is_head()) ob_end_clean();
  396. if($exit) exit;
  397. }
  398. /**
  399. * Returns limonade environment variables:
  400. *
  401. * 'SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE',
  402. * 'GET', 'POST', 'PUT', 'DELETE'
  403. *
  404. * If a null argument is passed, reset and rebuild environment
  405. *
  406. * @param null @reset reset and rebuild environment
  407. * @return array
  408. */
  409. function env($reset = null)
  410. {
  411. static $env = array();
  412. if(func_num_args() > 0)
  413. {
  414. $args = func_get_args();
  415. if(is_null($args[0])) $env = array();
  416. }
  417. if(empty($env))
  418. {
  419. if(empty($GLOBALS['_SERVER']))
  420. {
  421. // Fixing empty $GLOBALS['_SERVER'] bug
  422. // http://sofadesign.lighthouseapp.com/projects/29612-limonade/tickets/29-env-is-empty
  423. $GLOBALS['_SERVER'] =& $_SERVER;
  424. $GLOBALS['_FILES'] =& $_FILES;
  425. $GLOBALS['_REQUEST'] =& $_REQUEST;
  426. $GLOBALS['_SESSION'] =& $_SESSION;
  427. $GLOBALS['_ENV'] =& $_ENV;
  428. $GLOBALS['_COOKIE'] =& $_COOKIE;
  429. }
  430. $glo_names = array('SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE');
  431. $vars = array_merge($glo_names, request_methods());
  432. foreach($vars as $var)
  433. {
  434. $varname = "_$var";
  435. if(!array_key_exists($varname, $GLOBALS)) $GLOBALS[$varname] = array();
  436. $env[$var] =& $GLOBALS[$varname];
  437. }
  438. $method = request_method($env);
  439. if($method == 'PUT' || $method == 'DELETE')
  440. {
  441. $varname = "_$method";
  442. if(array_key_exists('_method', $_POST) && $_POST['_method'] == $method)
  443. {
  444. foreach($_POST as $k => $v)
  445. {
  446. if($k == "_method") continue;
  447. $GLOBALS[$varname][$k] = $v;
  448. }
  449. }
  450. else
  451. {
  452. parse_str(file_get_contents('php://input'), $GLOBALS[$varname]);
  453. }
  454. }
  455. }
  456. return $env;
  457. }
  458. /**
  459. * Returns application root file path
  460. *
  461. * @return string
  462. */
  463. function app_file()
  464. {
  465. static $file;
  466. if(empty($file))
  467. {
  468. $debug_backtrace = debug_backtrace();
  469. $stacktrace = array_pop($debug_backtrace);
  470. $file = $stacktrace['file'];
  471. }
  472. return file_path($file);
  473. }
  474. # # #
  475. # ============================================================================ #
  476. # 2. ERROR #
  477. # ============================================================================ #
  478. /**
  479. * Associate a function with error code(s) and return all associations
  480. *
  481. * @param string $errno
  482. * @param string $function
  483. * @return array
  484. */
  485. function error($errno = null, $function = null)
  486. {
  487. static $errors = array();
  488. if(func_num_args() > 0)
  489. {
  490. $errors[] = array('errno'=>$errno, 'function'=> $function);
  491. }
  492. return $errors;
  493. }
  494. /**
  495. * Raise an error, passing a given error number and an optional message,
  496. * then exit.
  497. * Error number should be a HTTP status code or a php user error (E_USER...)
  498. * $errno and $msg arguments can be passsed in any order
  499. * If no arguments are passed, default $errno is SERVER_ERROR (500)
  500. *
  501. * @param int,string $errno Error number or message string
  502. * @param string,string $msg Message string or error number
  503. * @param mixed $debug_args extra data provided for debugging
  504. * @return void
  505. */
  506. function halt($errno = SERVER_ERROR, $msg = '', $debug_args = null)
  507. {
  508. $args = func_get_args();
  509. $error = array_shift($args);
  510. # switch $errno and $msg args
  511. # TODO cleanup / refactoring
  512. if(is_string($errno))
  513. {
  514. $msg = $errno;
  515. $oldmsg = array_shift($args);
  516. $errno = empty($oldmsg) ? SERVER_ERROR : $oldmsg;
  517. }
  518. else if(!empty($args)) $msg = array_shift($args);
  519. if(empty($msg) && $errno == NOT_FOUND) $msg = request_uri();
  520. if(empty($msg)) $msg = "";
  521. if(!empty($args)) $debug_args = $args;
  522. set('_lim_err_debug_args', $debug_args);
  523. error_handler_dispatcher($errno, $msg, null, null);
  524. }
  525. /**
  526. * Internal error handler dispatcher
  527. * Find and call matching error handler and exit
  528. * If no match found, call default error handler
  529. *
  530. * @access private
  531. * @param int $errno
  532. * @param string $errstr
  533. * @param string $errfile
  534. * @param string $errline
  535. * @return void
  536. */
  537. function error_handler_dispatcher($errno, $errstr, $errfile, $errline)
  538. {
  539. $back_trace = debug_backtrace();
  540. while($trace = array_shift($back_trace))
  541. {
  542. if($trace['function'] == 'halt')
  543. {
  544. $errfile = $trace['file'];
  545. $errline = $trace['line'];
  546. break;
  547. }
  548. }
  549. # Notices and warning won't halt execution
  550. if(error_wont_halt_app($errno))
  551. {
  552. error_notice($errno, $errstr, $errfile, $errline);
  553. return;
  554. }
  555. else
  556. {
  557. # Other errors will stop application
  558. static $handlers = array();
  559. if(empty($handlers))
  560. {
  561. error(E_LIM_PHP, 'error_default_handler');
  562. $handlers = error();
  563. }
  564. $is_http_err = http_response_status_is_valid($errno);
  565. while($handler = array_shift($handlers))
  566. {
  567. $e = is_array($handler['errno']) ? $handler['errno'] : array($handler['errno']);
  568. while($ee = array_shift($e))
  569. {
  570. if($ee == $errno || $ee == E_LIM_PHP || ($ee == E_LIM_HTTP && $is_http_err))
  571. {
  572. echo call_if_exists($handler['function'], $errno, $errstr, $errfile, $errline);
  573. exit;
  574. }
  575. }
  576. }
  577. }
  578. }
  579. /**
  580. * Default error handler
  581. *
  582. * @param string $errno
  583. * @param string $errstr
  584. * @param string $errfile
  585. * @param string $errline
  586. * @return string error output
  587. */
  588. function error_default_handler($errno, $errstr, $errfile, $errline)
  589. {
  590. $is_http_err = http_response_status_is_valid($errno);
  591. $http_error_code = $is_http_err ? $errno : SERVER_ERROR;
  592. status($http_error_code);
  593. return $http_error_code == NOT_FOUND ?
  594. error_not_found_output($errno, $errstr, $errfile, $errline) :
  595. error_server_error_output($errno, $errstr, $errfile, $errline);
  596. }
  597. /**
  598. * Returns not found error output
  599. *
  600. * @access private
  601. * @param string $msg
  602. * @return string
  603. */
  604. function error_not_found_output($errno, $errstr, $errfile, $errline)
  605. {
  606. if(!function_exists('not_found'))
  607. {
  608. /**
  609. * Default not found error output
  610. *
  611. * @param string $errno
  612. * @param string $errstr
  613. * @param string $errfile
  614. * @param string $errline
  615. * @return string
  616. */
  617. function not_found($errno, $errstr, $errfile=null, $errline=null)
  618. {
  619. option('views_dir', option('error_views_dir'));
  620. $msg = h(rawurldecode($errstr));
  621. return html("<h1>Page not found:</h1><p><code>{$msg}</code></p>", error_layout());
  622. }
  623. }
  624. return not_found($errno, $errstr, $errfile, $errline);
  625. }
  626. /**
  627. * Returns server error output
  628. *
  629. * @access private
  630. * @param int $errno
  631. * @param string $errstr
  632. * @param string $errfile
  633. * @param string $errline
  634. * @return string
  635. */
  636. function error_server_error_output($errno, $errstr, $errfile, $errline)
  637. {
  638. if(!function_exists('server_error'))
  639. {
  640. /**
  641. * Default server error output
  642. *
  643. * @param string $errno
  644. * @param string $errstr
  645. * @param string $errfile
  646. * @param string $errline
  647. * @return string
  648. */
  649. function server_error($errno, $errstr, $errfile=null, $errline=null)
  650. {
  651. $is_http_error = http_response_status_is_valid($errno);
  652. $args = compact('errno', 'errstr', 'errfile', 'errline', 'is_http_error');
  653. option('views_dir', option('limonade_views_dir'));
  654. $html = render('error.html.php', null, $args);
  655. option('views_dir', option('error_views_dir'));
  656. return html($html, error_layout(), $args);
  657. }
  658. }
  659. return server_error($errno, $errstr, $errfile, $errline);
  660. }
  661. /**
  662. * Set and returns error output layout
  663. *
  664. * @param string $layout
  665. * @return string
  666. */
  667. function error_layout($layout = false)
  668. {
  669. static $o_layout = 'default_layout.php';
  670. if($layout !== false)
  671. {
  672. option('error_views_dir', option('views_dir'));
  673. $o_layout = $layout;
  674. }
  675. return $o_layout;
  676. }
  677. /**
  678. * Set a notice if arguments are provided
  679. * Returns all stored notices.
  680. * If $errno argument is null, reset the notices array
  681. *
  682. * @access private
  683. * @param string, null $str
  684. * @return array
  685. */
  686. function error_notice($errno = false, $errstr = null, $errfile = null, $errline = null)
  687. {
  688. static $notices = array();
  689. if($errno) $notices[] = compact('errno', 'errstr', 'errfile', 'errline');
  690. else if(is_null($errno)) $notices = array();
  691. return $notices;
  692. }
  693. /**
  694. * Returns notices output rendering and reset notices
  695. *
  696. * @return string
  697. */
  698. function error_notices_render()
  699. {
  700. if(option('debug') && option('env') > ENV_PRODUCTION)
  701. {
  702. $notices = error_notice();
  703. error_notice(null); // reset notices
  704. $c_view_dir = option('views_dir'); // keep for restore after render
  705. option('views_dir', option('limonade_views_dir'));
  706. $o = render('_notices.html.php', null, array('notices' => $notices));
  707. option('views_dir', $c_view_dir); // restore current views dir
  708. return $o;
  709. }
  710. }
  711. /**
  712. * Checks if an error is will halt application execution.
  713. * Notices and warnings will not.
  714. *
  715. * @access private
  716. * @param string $num error code number
  717. * @return boolean
  718. */
  719. function error_wont_halt_app($num)
  720. {
  721. return $num == E_NOTICE ||
  722. $num == E_WARNING ||
  723. $num == E_CORE_WARNING ||
  724. $num == E_COMPILE_WARNING ||
  725. $num == E_USER_WARNING ||
  726. $num == E_USER_NOTICE ||
  727. $num == E_DEPRECATED ||
  728. $num == E_USER_DEPRECATED ||
  729. $num == E_LIM_DEPRECATED;
  730. }
  731. /**
  732. * return error code name for a given code num, or return all errors names
  733. *
  734. * @param string $num
  735. * @return mixed
  736. */
  737. function error_type($num = null)
  738. {
  739. $types = array (
  740. E_ERROR => 'ERROR',
  741. E_WARNING => 'WARNING',
  742. E_PARSE => 'PARSING ERROR',
  743. E_NOTICE => 'NOTICE',
  744. E_CORE_ERROR => 'CORE ERROR',
  745. E_CORE_WARNING => 'CORE WARNING',
  746. E_COMPILE_ERROR => 'COMPILE ERROR',
  747. E_COMPILE_WARNING => 'COMPILE WARNING',
  748. E_USER_ERROR => 'USER ERROR',
  749. E_USER_WARNING => 'USER WARNING',
  750. E_USER_NOTICE => 'USER NOTICE',
  751. E_STRICT => 'STRICT NOTICE',
  752. E_RECOVERABLE_ERROR => 'RECOVERABLE ERROR',
  753. E_DEPRECATED => 'DEPRECATED WARNING',
  754. E_USER_DEPRECATED => 'USER DEPRECATED WARNING',
  755. E_LIM_DEPRECATED => 'LIMONADE DEPRECATED WARNING'
  756. );
  757. return is_null($num) ? $types : $types[$num];
  758. }
  759. /**
  760. * Returns http response status for a given error number
  761. *
  762. * @param string $errno
  763. * @return int
  764. */
  765. function error_http_status($errno)
  766. {
  767. $code = http_response_status_is_valid($errno) ? $errno : SERVER_ERROR;
  768. return http_response_status($code);
  769. }
  770. # # #
  771. # ============================================================================ #
  772. # 3. REQUEST #
  773. # ============================================================================ #
  774. /**
  775. * Returns current request method for a given environment or current one
  776. *
  777. * @param string $env
  778. * @return string
  779. */
  780. function request_method($env = null)
  781. {
  782. if(is_null($env)) $env = env();
  783. $m = array_key_exists('REQUEST_METHOD', $env['SERVER']) ? $env['SERVER']['REQUEST_METHOD'] : null;
  784. if($m == "POST" && array_key_exists('_method', $env['POST']))
  785. $m = strtoupper($env['POST']['_method']);
  786. if(!in_array(strtoupper($m), request_methods()))
  787. {
  788. trigger_error("'$m' request method is unknown or unavailable.", E_USER_WARNING);
  789. $m = false;
  790. }
  791. return $m;
  792. }
  793. /**
  794. * Checks if a request method or current one is allowed
  795. *
  796. * @param string $m
  797. * @return bool
  798. */
  799. function request_method_is_allowed($m = null)
  800. {
  801. if(is_null($m)) $m = request_method();
  802. return in_array(strtoupper($m), request_methods());
  803. }
  804. /**
  805. * Checks if request method is GET
  806. *
  807. * @param string $env
  808. * @return bool
  809. */
  810. function request_is_get($env = null)
  811. {
  812. return request_method($env) == "GET";
  813. }
  814. /**
  815. * Checks if request method is POST
  816. *
  817. * @param string $env
  818. * @return bool
  819. */
  820. function request_is_post($env = null)
  821. {
  822. return request_method($env) == "POST";
  823. }
  824. /**
  825. * Checks if request method is PUT
  826. *
  827. * @param string $env
  828. * @return bool
  829. */
  830. function request_is_put($env = null)
  831. {
  832. return request_method($env) == "PUT";
  833. }
  834. /**
  835. * Checks if request method is DELETE
  836. *
  837. * @param string $env
  838. * @return bool
  839. */
  840. function request_is_delete($env = null)
  841. {
  842. return request_method($env) == "DELETE";
  843. }
  844. /**
  845. * Checks if request method is HEAD
  846. *
  847. * @param string $env
  848. * @return bool
  849. */
  850. function request_is_head($env = null)
  851. {
  852. return request_method($env) == "HEAD";
  853. }
  854. /**
  855. * Returns allowed request methods
  856. *
  857. * @return array
  858. */
  859. function request_methods()
  860. {
  861. return array("GET","POST","PUT","DELETE", "HEAD");
  862. }
  863. /**
  864. * Returns current request uri (the path that will be compared with routes)
  865. *
  866. * (Inspired from codeigniter URI::_fetch_uri_string method)
  867. *
  868. * @return string
  869. */
  870. function request_uri($env = null)
  871. {
  872. static $uri = null;
  873. if(is_null($env))
  874. {
  875. if(!is_null($uri)) return $uri;
  876. $env = env();
  877. }
  878. if(array_key_exists('uri', $env['GET']))
  879. {
  880. $uri = $env['GET']['uri'];
  881. }
  882. else if(array_key_exists('u', $env['GET']))
  883. {
  884. $uri = $env['GET']['u'];
  885. }
  886. // bug: dot are converted to _... so we can't use it...
  887. // else if (count($env['GET']) == 1 && trim(key($env['GET']), '/') != '')
  888. // {
  889. // $uri = key($env['GET']);
  890. // }
  891. else
  892. {
  893. $app_file = app_file();
  894. $path_info = isset($env['SERVER']['PATH_INFO']) ? $env['SERVER']['PATH_INFO'] : @getenv('PATH_INFO');
  895. $query_string = isset($env['SERVER']['QUERY_STRING']) ? $env['SERVER']['QUERY_STRING'] : @getenv('QUERY_STRING');
  896. // Is there a PATH_INFO variable?
  897. // Note: some servers seem to have trouble with getenv() so we'll test it two ways
  898. if (trim($path_info, '/') != '' && $path_info != "/".$app_file)
  899. {
  900. if(strpos($path_info, '&') !== 0)
  901. {
  902. # exclude GET params
  903. $params = explode('&', $path_info);
  904. $path_info = array_shift($params);
  905. # populate $_GET
  906. foreach($params as $param)
  907. {
  908. if(strpos($param, '=') > 0)
  909. {
  910. list($k, $v) = explode('=', $param);
  911. $env['GET'][$k] = $v;
  912. }
  913. }
  914. }
  915. $uri = $path_info;
  916. }
  917. // No PATH_INFO?... What about QUERY_STRING?
  918. elseif (trim($query_string, '/') != '')
  919. {
  920. $uri = $query_string;
  921. $get = $env['GET'];
  922. if(count($get) > 0)
  923. {
  924. # exclude GET params
  925. $keys = array_keys($get);
  926. $first = array_shift($keys);
  927. if(strpos($query_string, $first) === 0) $uri = $first;
  928. }
  929. }
  930. elseif(array_key_exists('REQUEST_URI', $env['SERVER']) && !empty($env['SERVER']['REQUEST_URI']))
  931. {
  932. $request_uri = rtrim(rawurldecode($env['SERVER']['REQUEST_URI']), '?/').'/';
  933. $base_path = $env['SERVER']['SCRIPT_NAME'];
  934. if($request_uri."index.php" == $base_path) $request_uri .= "index.php";
  935. $uri = str_replace($base_path, '', $request_uri);
  936. }
  937. elseif($env['SERVER']['argc'] > 1 && trim($env['SERVER']['argv'][1], '/') != '')
  938. {
  939. $uri = $env['SERVER']['argv'][1];
  940. }
  941. }
  942. $uri = rtrim($uri, "/"); # removes ending /
  943. if(empty($uri))
  944. {
  945. $uri = '/';
  946. }
  947. else if($uri[0] != '/')
  948. {
  949. $uri = '/' . $uri; # add a leading slash
  950. }
  951. return rawurldecode($uri);
  952. }
  953. # # #
  954. # ============================================================================ #
  955. # 4. ROUTER #
  956. # ============================================================================ #
  957. /**
  958. * An alias of {@link dispatch_get()}
  959. *
  960. * @return void
  961. */
  962. function dispatch($path_or_array, $callback, $options = array())
  963. {
  964. dispatch_get($path_or_array, $callback, $options);
  965. }
  966. /**
  967. * Add a GET route. Also automatically defines a HEAD route.
  968. *
  969. * @param string $path_or_array
  970. * @param string $callback
  971. * @param array $options (optional). See {@link route()} for available options.
  972. * @return void
  973. */
  974. function dispatch_get($path_or_array, $callback, $options = array())
  975. {
  976. route("GET", $path_or_array, $callback, $options);
  977. route("HEAD", $path_or_array, $callback, $options);
  978. }
  979. /**
  980. * Add a POST route
  981. *
  982. * @param string $path_or_array
  983. * @param string $callback
  984. * @param array $options (optional). See {@link route()} for available options.
  985. * @return void
  986. */
  987. function dispatch_post($path_or_array, $callback, $options = array())
  988. {
  989. route("POST", $path_or_array, $callback, $options);
  990. }
  991. /**
  992. * Add a PUT route
  993. *
  994. * @param string $path_or_array
  995. * @param string $callback
  996. * @param array $options (optional). See {@link route()} for available options.
  997. * @return void
  998. */
  999. function dispatch_put($path_or_array, $callback, $options = array())
  1000. {
  1001. route("PUT", $path_or_array, $callback, $options);
  1002. }
  1003. /**
  1004. * Add a DELETE route
  1005. *
  1006. * @param string $path_or_array
  1007. * @param string $callback
  1008. * @param array $options (optional). See {@link route()} for available options.
  1009. * @return void
  1010. */
  1011. function dispatch_delete($path_or_array, $callback, $options = array())
  1012. {
  1013. route("DELETE", $path_or_array, $callback, $options);
  1014. }
  1015. /**
  1016. * Add route if required params are provided.
  1017. * Delete all routes if null is passed as a unique argument
  1018. * Return all routes
  1019. *
  1020. * @see route_build()
  1021. * @access private
  1022. * @param string $method
  1023. * @param string|array $path_or_array
  1024. * @param callback $func
  1025. * @param array $options (optional). Available options:
  1026. * - 'params' key with an array of parameters: for parametrized routes.
  1027. * those parameters will be merged with routes parameters.
  1028. * @return array
  1029. */
  1030. function route()
  1031. {
  1032. static $routes = array();
  1033. $nargs = func_num_args();
  1034. if( $nargs > 0)
  1035. {
  1036. $args = func_get_args();
  1037. if($nargs === 1 && is_null($args[0])) $routes = array();
  1038. else if($nargs < 3) trigger_error("Missing arguments for route()", E_USER_ERROR);
  1039. else
  1040. {
  1041. $method = $args[0];
  1042. $path_or_array = $args[1];
  1043. $func = $args[2];
  1044. $options = $nargs > 3 ? $args[3] : array();
  1045. $routes[] = route_build($method, $path_or_array, $func, $options);
  1046. }
  1047. }
  1048. return $routes;
  1049. }
  1050. /**
  1051. * An alias of route(null): reset all routes
  1052. *
  1053. * @access private
  1054. * @return void
  1055. */
  1056. function route_reset()
  1057. {
  1058. route(null);
  1059. }
  1060. /**
  1061. * Build a route and return it
  1062. *
  1063. * @access private
  1064. * @param string $method allowed http method (one of those returned by {@link request_methods()})
  1065. * @param string|array $path_or_array
  1066. * @param callback $callback callback called when route is found. It can be
  1067. * a function, an object method, a static method or a closure.
  1068. * See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
  1069. * to learn more about callbacks.
  1070. * @param array $options (optional). Available options:
  1071. * - 'params' key with an array of parameters: for parametrized routes.
  1072. * those parameters will be merged with routes parameters.
  1073. * @return array array with keys "method", "pattern", "names", "callback", "options"
  1074. */
  1075. function route_build($method, $path_or_array, $callback, $options = array())
  1076. {
  1077. $method = strtoupper($method);
  1078. if(!in_array($method, request_methods()))
  1079. trigger_error("'$method' request method is unkown or unavailable.", E_USER_WARNING);
  1080. if(is_array($path_or_array))
  1081. {
  1082. $path = array_shift($path_or_array);
  1083. $names = $path_or_array[0];
  1084. }
  1085. else
  1086. {
  1087. $path = $path_or_array;
  1088. $names = array();
  1089. }
  1090. $single_asterisk_subpattern = "(?:/([^\/]*))?";
  1091. $double_asterisk_subpattern = "(?:/(.*))?";
  1092. $optionnal_slash_subpattern = "(?:/*?)";
  1093. $no_slash_asterisk_subpattern = "(?:([^\/]*))?";
  1094. if($path[0] == "^")
  1095. {
  1096. if($path{strlen($path) - 1} != "$") $path .= "$";
  1097. $pattern = "#".$path."#i";
  1098. }
  1099. else if(empty($path) || $path == "/")
  1100. {
  1101. $pattern = "#^".$optionnal_slash_subpattern."$#";
  1102. }
  1103. else
  1104. {
  1105. $parsed = array();
  1106. $elts = explode('/', $path);
  1107. $parameters_count = 0;
  1108. foreach($elts as $elt)
  1109. {
  1110. if(empty($elt)) continue;
  1111. $name = null;
  1112. # extracting double asterisk **
  1113. if($elt == "**"):
  1114. $parsed[] = $double_asterisk_subpattern;
  1115. $name = $parameters_count;
  1116. # extracting single asterisk *
  1117. elseif($elt == "*"):
  1118. $parsed[] = $single_asterisk_subpattern;
  1119. $name = $parameters_count;
  1120. # extracting named parameters :my_param
  1121. elseif($elt[0] == ":"):
  1122. if(preg_match('/^:([^\:]+)$/', $elt, $matches))
  1123. {
  1124. $parsed[] = $single_asterisk_subpattern;
  1125. $name = $matches[1];
  1126. };
  1127. elseif(strpos($elt, '*') !== false):
  1128. $sub_elts = explode('*', $elt);
  1129. $parsed_sub = array();
  1130. foreach($sub_elts as $sub_elt)
  1131. {
  1132. $parsed_sub[] = preg_quote($sub_elt, "#");
  1133. $name = $parameters_count;
  1134. }
  1135. //
  1136. $parsed[] = "/".implode($no_slash_asterisk_subpattern, $parsed_sub);
  1137. else:
  1138. $parsed[] = "/".preg_quote($elt, "#");
  1139. endif;
  1140. /* set parameters names */
  1141. if(is_null($name)) continue;
  1142. if(!array_key_exists($parameters_count, $names) || is_null($names[$parameters_count]))
  1143. $names[$parameters_count] = $name;
  1144. $parameters_count++;
  1145. }
  1146. $pattern = "#^".implode('', $parsed).$optionnal_slash_subpattern."?$#i";
  1147. }
  1148. return array( "method" => $method,
  1149. "pattern" => $pattern,
  1150. "names" => $names,
  1151. "callback" => $callback,
  1152. "options" => $options );
  1153. }
  1154. /**
  1155. * Find a route and returns it.
  1156. * Parameters values extracted from the path are added and merged
  1157. * with the default 'params' option of the route
  1158. * If not found, returns false.
  1159. * Routes are checked from first added to last added.
  1160. *
  1161. * @access private
  1162. * @param string $method
  1163. * @param string $path
  1164. * @return array,false route array has same keys as route returned by
  1165. * {@link route_build()} ("method", "pattern", "names", "callback", "options")
  1166. * + the processed "params" key
  1167. */
  1168. function route_find($method, $path)
  1169. {
  1170. $routes = route();
  1171. $method = strtoupper($method);
  1172. foreach($routes as $route)
  1173. {
  1174. if($method == $route["method"] && preg_match($route["pattern"], $path, $matches))
  1175. {
  1176. $options = $route["options"];
  1177. $params = array_key_exists('params', $options) ? $options["params"] : array();
  1178. if(count($matches) > 1)
  1179. {
  1180. array_shift($matches);
  1181. $n_matches = count($matches);
  1182. $names = array_values($route["names"]);
  1183. $n_names = count($names);
  1184. if( $n_matches < $n_names )
  1185. {
  1186. $a = array_fill(0, $n_names - $n_matches, null);
  1187. $matches = array_merge($matches, $a);
  1188. }
  1189. else if( $n_matches > $n_names )
  1190. {
  1191. $names = range($n_names, $n_matches - 1);
  1192. }
  1193. $params = array_replace($params, array_combine($names, $matches));
  1194. }
  1195. $route["params"] = $params;
  1196. return $route;
  1197. }
  1198. }
  1199. return false;
  1200. }
  1201. # ============================================================================ #
  1202. # 5. OUTPUT AND RENDERING #
  1203. # ============================================================================ #
  1204. /**
  1205. * Returns a string to output
  1206. *
  1207. * It might use a template file, a function, or a formatted string (like {@link sprintf()}).
  1208. * It could be embraced by a layout or not.
  1209. * Local vars can be passed in addition to variables made available with the {@link set()}
  1210. * function.
  1211. *
  1212. * @param string $content_or_func
  1213. * @param string $layout
  1214. * @param string $locals
  1215. * @return string
  1216. */
  1217. function render($content_or_func, $layout = '', $locals = array())
  1218. {
  1219. $args = func_get_args();
  1220. $content_or_func = array_shift($args);
  1221. $layout = count($args) > 0 ? array_shift($args) : layout();
  1222. $view_path = file_path(option('views_dir'),$content_or_func);
  1223. if(function_exists('before_render'))
  1224. list($content_or_func, $layout, $locals, $view_path) = before_render($content_or_func, $layout, $locals, $view_path);
  1225. $vars = array_merge(set(), $locals);
  1226. $flash = flash_now();
  1227. 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);
  1228. else if(!empty($flash)) $vars['flash'] = $flash;
  1229. $infinite_loop = false;
  1230. # Avoid infinite loop: this function is in the backtrace ?
  1231. if(function_exists($content_or_func))
  1232. {
  1233. $back_trace = debug_backtrace();
  1234. while($trace = array_shift($back_trace))
  1235. {
  1236. if($trace['function'] == strtolower($content_or_func))
  1237. {
  1238. $infinite_loop = true;
  1239. break;
  1240. }
  1241. }
  1242. }
  1243. if(function_exists($content_or_func) && !$infinite_loop)
  1244. {
  1245. ob_start();
  1246. call_user_func($content_or_func, $vars);
  1247. $content = ob_get_clean();
  1248. }
  1249. elseif(file_exists($view_path))
  1250. {
  1251. ob_start();
  1252. extract($vars);
  1253. include $view_path;
  1254. $content = ob_get_clean();
  1255. }
  1256. else
  1257. {
  1258. if(substr_count($content_or_func, '%') !== count($vars)) $content = $content_or_func;
  1259. else $content = vsprintf($content_or_func, $vars);
  1260. }
  1261. if(empty($layout)) return $content;
  1262. return render($layout, null, array('content' => $content));
  1263. }
  1264. /**
  1265. * Returns a string to output
  1266. *
  1267. * Shortcut to render with no layout.
  1268. *
  1269. * @param string $content_or_func
  1270. * @param string $locals
  1271. * @return string
  1272. */
  1273. function partial($content_or_func, $locals = array())
  1274. {
  1275. return render($content_or_func, null, $locals);
  1276. }
  1277. /**
  1278. * Returns html output with proper http headers
  1279. *
  1280. * @param string $content_or_func
  1281. * @param string $layout
  1282. * @param string $locals
  1283. * @return string
  1284. */
  1285. function html($content_or_func, $layout = '', $locals = array())
  1286. {
  1287. if(!headers_sent()) header('Content-Type: text/html; charset='.strtolower(option('encoding')));
  1288. $args = func_get_args();
  1289. return call_user_func_array('render', $args);
  1290. }
  1291. /**
  1292. * Set and return current layout
  1293. *
  1294. * @param string $function_or_file
  1295. * @return string
  1296. */
  1297. function layout($function_or_file = null)
  1298. {
  1299. static $layout = null;
  1300. if(func_num_args() > 0) $layout = $function_or_file;
  1301. return $layout;
  1302. }
  1303. /**
  1304. * Returns xml output with proper http headers
  1305. *
  1306. * @param string $content_or_func
  1307. * @param string $layout
  1308. * @param string $locals
  1309. * @return string
  1310. */
  1311. function xml($data)
  1312. {
  1313. if(!headers_sent()) header('Content-Type: text/xml; charset='.strtolower(option('encoding')));
  1314. $args = func_get_args();
  1315. return call_user_func_array('render', $args);
  1316. }
  1317. /**
  1318. * Returns css output with proper http headers
  1319. *
  1320. * @param string $content_or_func
  1321. * @param string $layout
  1322. * @param string $locals
  1323. * @return string
  1324. */
  1325. function css($content_or_func, $layout = '', $locals = array())
  1326. {
  1327. if(!headers_sent()) header('Content-Type: text/css; charset='.strtolower(option('encoding')));
  1328. $args = func_get_args();
  1329. return call_user_func_array('render', $args);
  1330. }
  1331. /**
  1332. * Returns javacript output with proper http headers
  1333. *
  1334. * @param string $content_or_func
  1335. * @param string $layout
  1336. * @param string $locals
  1337. * @return string
  1338. */
  1339. function js($content_or_func, $layout = '', $locals = array())
  1340. {
  1341. if(!headers_sent()) header('Content-Type: application/javascript; charset='.strtolower(option('encoding')));
  1342. $args = func_get_args();
  1343. return call_user_func_array('render', $args);
  1344. }
  1345. /**
  1346. * Returns txt output with proper http headers
  1347. *
  1348. * @param string $content_or_func
  1349. * @param string $layout
  1350. * @param string $locals
  1351. * @return string
  1352. */
  1353. function txt($content_or_func, $layout = '', $locals = array())
  1354. {
  1355. if(!headers_sent()) header('Content-Type: text/plain; charset='.strtolower(option('encoding')));
  1356. $args = func_get_args();
  1357. return call_user_func_array('render', $args);
  1358. }
  1359. /**
  1360. * Returns json representation of data with proper http headers
  1361. *
  1362. * @param string $data
  1363. * @param int $json_option
  1364. * @return string
  1365. */
  1366. function json($data, $json_option = 0)
  1367. {
  1368. if(!headers_sent()) header('Content-Type: application/json; charset='.strtolower(option('encoding')));
  1369. return version_compare(PHP_VERSION, '5.3.0', '>=') ? json_encode($data, $json_option) : json_encode($data);
  1370. }
  1371. /**
  1372. * undocumented function
  1373. *
  1374. * @param string $filename
  1375. * @param string $return
  1376. * @return mixed number of bytes delivered or file output if $return = true
  1377. */
  1378. function render_file($filename, $return = false)
  1379. {
  1380. # TODO implements X-SENDFILE headers
  1381. // if($x-sendfile = option('x-sendfile'))
  1382. // {
  1383. // // add a X-Sendfile header for apache and Lighttpd >= 1.5
  1384. // if($x-sendfile > X-SENDFILE) // add a X-LIGHTTPD-send-file header
  1385. //
  1386. // }
  1387. // else
  1388. // {
  1389. //
  1390. // }
  1391. $filename = str_replace('../', '', $filename);
  1392. if(file_exists($filename))
  1393. {
  1394. $content_type = mime_type(file_extension($filename));
  1395. $header = 'Content-type: '.$content_type;
  1396. if(file_is_text($filename)) $header .= '; charset='.strtolower(option('encoding'));
  1397. if(!headers_sent()) header($header);
  1398. return file_read($filename, $return);
  1399. }
  1400. else halt(NOT_FOUND, "unknown filename $filename");
  1401. }
  1402. # # #
  1403. # ============================================================================ #
  1404. # 6. HELPERS #
  1405. # ============================================================================ #
  1406. /**
  1407. * Returns an url composed of params joined with /
  1408. * A param can be a string or an array.
  1409. * If param is an array, its members will be added at the end of the return url
  1410. * as GET parameters "&key=value".
  1411. *
  1412. * @param string or array $param1, $param2 ...
  1413. * @return string
  1414. */
  1415. function url_for($params = null)
  1416. {
  1417. $paths = array();
  1418. $params = func_get_args();
  1419. $GET_params = array();
  1420. foreach($params as $param)
  1421. {
  1422. if(is_array($param))
  1423. {
  1424. $GET_params = array_merge($GET_params, $param);
  1425. continue;
  1426. }
  1427. if(filter_var_url($param))
  1428. {
  1429. $paths[] = $param;
  1430. continue;
  1431. }
  1432. $p = explode('/',$param);
  1433. foreach($p as $v)
  1434. {
  1435. if($v != "") $paths[] = str_replace('%23', '#', rawurlencode($v));
  1436. }
  1437. }
  1438. $path = rtrim(implode('/', $paths), '/');
  1439. if(!filter_var_url($path))
  1440. {
  1441. # it's a relative URL or an URL without a schema
  1442. $base_uri = option('base_uri');
  1443. $path = file_path($base_uri, $path);
  1444. }
  1445. if(!empty($GET_params))
  1446. {
  1447. $is_first_qs_param = true;
  1448. $path_as_no_question_mark = strpos($path, '?') === false;
  1449. foreach($GET_params as $k => $v)
  1450. {
  1451. $qs_separator = $is_first_qs_param && $path_as_no_question_mark ?
  1452. '?' : '&amp;';
  1453. $path .= $qs_separator . rawurlencode($k) . '=' . rawurlencode($v);
  1454. $is_first_qs_param = false;
  1455. }
  1456. }
  1457. if(DIRECTORY_SEPARATOR != '/') $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
  1458. return $path;
  1459. }
  1460. /**
  1461. * An alias of {@link htmlspecialchars()}.
  1462. * If no $charset is provided, uses option('encoding') value
  1463. *
  1464. * @param string $str
  1465. * @param string $quote_style
  1466. * @param string $charset
  1467. * @return void
  1468. */
  1469. function h($str, $quote_style = ENT_NOQUOTES, $charset = null)
  1470. {
  1471. if(is_null($charset)) $charset = strtoupper(option('encoding'));
  1472. return htmlspecialchars($str, $quote_style, $charset);
  1473. }
  1474. /**
  1475. * Set and returns flash messages that will be available in the next action
  1476. * via the {@link flash_now()} function or the view variable <code>$flash</code>.
  1477. *
  1478. * If multiple values are provided, set <code>$name</code> variable with an array of those values.
  1479. * If there is only one value, set <code>$name</code> variable with the provided $values
  1480. * or if it's <code>$name</code> is an array, merge it with current messages.
  1481. *
  1482. * @param string, array $name
  1483. * @param mixed $values,...
  1484. * @return mixed variable value for $name if $name argument is provided, else return all variables
  1485. */
  1486. function flash($name = null, $value = null)
  1487. {
  1488. if(!defined('SID')) trigger_error("Flash messages can't be used because session isn't enabled", E_USER_WARNING);
  1489. static $messages = array();
  1490. $args = func_get_args();
  1491. $name = array_shift($args);
  1492. if(is_null($name)) return $messages;
  1493. if(is_array($name)) return $messages = array_merge($messages, $name);
  1494. if(!empty($args))
  1495. {
  1496. $messages[$name] = count($args) > 1 ? $args : $args[0];
  1497. }
  1498. if(!array_key_exists($name, $messages)) return null;
  1499. else return $messages[$name];
  1500. return $messages;
  1501. }
  1502. /**
  1503. * Set and returns flash messages available for the current action, included those
  1504. * defined in the previous action with {@link flash()}
  1505. * Those messages will also be passed to the views and made available in the
  1506. * <code>$flash</code> variable.
  1507. *
  1508. * If multiple values are provided, set <code>$name</code> variable with an array of those values.
  1509. * If there is only one value, set <code>$name</code> variable with the provided $values
  1510. * or if it's <code>$name</code> is an array, merge it with current messages.
  1511. *
  1512. * @param string, array $name
  1513. * @param mixed $values,...
  1514. * @return mixed variable value for $name if $name argument is provided, else return all variables
  1515. */
  1516. function flash_now($name = null, $value = null)
  1517. {
  1518. static $messages = null;
  1519. if(is_null($messages))
  1520. {
  1521. $fkey = LIM_SESSION_FLASH_KEY;
  1522. $messages = array();
  1523. if(defined('SID') && array_key_exists($fkey, $_SESSION)) $messages = $_SESSION[$fkey];
  1524. }
  1525. $args = func_get_args();
  1526. $name = array_shift($args);
  1527. if(is_null($name)) return $messages;
  1528. if(is_array($name)) return $messages = array_merge($messages, $name);
  1529. if(!empty($args))
  1530. {
  1531. $messages[$name] = count($args) > 1 ? $args : $args[0];
  1532. }
  1533. if(!array_key_exists($name, $messages)) return null;
  1534. else return $messages[$name];
  1535. return $messages;
  1536. }
  1537. /**
  1538. * Delete current flash messages in session, and set new ones stored with
  1539. * flash function.
  1540. * Called before application exit.
  1541. *
  1542. * @access private
  1543. * @return void
  1544. */
  1545. function flash_sweep()
  1546. {
  1547. if(defined('SID'))
  1548. {
  1549. $fkey = LIM_SESSION_FLASH_KEY;
  1550. $_SESSION[$fkey] = flash();
  1551. }
  1552. }
  1553. /**
  1554. * Starts capturing block of text
  1555. *
  1556. * Calling without params stops capturing (same as end_content_for()).
  1557. * After capturing the captured block is put into a variable
  1558. * named $name for later use in layouts. If second parameter
  1559. * is supplied, its content will be used instead of capturing
  1560. * a block of text.
  1561. *
  1562. * @param string $name
  1563. * @param string $content
  1564. * @return void
  1565. */
  1566. function content_for($name = null, $content = null)
  1567. {
  1568. static $_name = null;
  1569. if(is_null($name) && !is_null($_name))
  1570. {
  1571. set($_name, ob_get_clean());
  1572. $_name = null;
  1573. }

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