PageRenderTime 66ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/limonade.php

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

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