PageRenderTime 68ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/limonade.php

https://github.com/liana866/FileZ
PHP | 2482 lines | 1390 code | 242 blank | 850 comment | 234 complexity | 923308602fa41f85b8550c4a6b859245 MD5 | raw file
Possible License(s): GPL-3.0, BSD-3-Clause, LGPL-2.1

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

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