PageRenderTime 57ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/limonade.php

https://github.com/cbrumelle/limonade
PHP | 2375 lines | 1310 code | 236 blank | 829 comment | 214 complexity | 16b702c734bf95849dccea996fac1d16 MD5 | raw 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 before(){}
  139. # function after(){}
  140. # function not_found(){}
  141. # function server_error(){}
  142. # function route_missing(){}
  143. # function before_exit(){}
  144. ## MAIN PUBLIC FUNCTIONS _______________________________________________________
  145. /**
  146. * Set and returns options values
  147. *
  148. * If multiple values are provided, set $name option with an array of those values.
  149. * If there is only one value, set $name option with the provided $values
  150. *
  151. * @param string $name
  152. * @param mixed $values,...
  153. * @return mixed option value for $name if $name argument is provided, else return all options
  154. */
  155. function option($name = null, $values = null)
  156. {
  157. static $options = array();
  158. $args = func_get_args();
  159. $name = array_shift($args);
  160. if(is_null($name)) return $options;
  161. if(!empty($args))
  162. {
  163. $options[$name] = count($args) > 1 ? $args : $args[0];
  164. }
  165. if(array_key_exists($name, $options)) return $options[$name];
  166. return;
  167. }
  168. /**
  169. * Set and returns params
  170. *
  171. * Depending on provided arguments:
  172. *
  173. * * Reset params if first argument is null
  174. *
  175. * * If first argument is an array, merge it with current params
  176. *
  177. * * If there is a second argument $value, set param $name (first argument) with $value
  178. * <code>
  179. * params('name', 'Doe') // set 'name' => 'Doe'
  180. * </code>
  181. * * If there is more than 2 arguments, set param $name (first argument) value with
  182. * an array of next arguments
  183. * <code>
  184. * params('months', 'jan', 'feb', 'mar') // set 'month' => array('months', 'jan', 'feb', 'mar')
  185. * </code>
  186. *
  187. * @param mixed $name_or_array_or_null could be null || array of params || name of a param (optional)
  188. * @param mixed $value,... for the $name param (optional)
  189. * @return mixed all params, or one if a first argument $name is provided
  190. */
  191. function params($name_or_array_or_null = null, $value = null)
  192. {
  193. static $params = array();
  194. $args = func_get_args();
  195. if(func_num_args() > 0)
  196. {
  197. $name = array_shift($args);
  198. if(is_null($name))
  199. {
  200. # Reset params
  201. $params = array();
  202. return $params;
  203. }
  204. if(is_array($name))
  205. {
  206. $params = array_merge($params, $name);
  207. return $params;
  208. }
  209. $nargs = count($args);
  210. if($nargs > 0)
  211. {
  212. $value = $nargs > 1 ? $args : $args[0];
  213. $params[$name] = $value;
  214. }
  215. return array_key_exists($name,$params) ? $params[$name] : null;
  216. }
  217. return $params;
  218. }
  219. /**
  220. * Set and returns template variables
  221. *
  222. * If multiple values are provided, set $name variable with an array of those values.
  223. * If there is only one value, set $name variable with the provided $values
  224. *
  225. * @param string $name
  226. * @param mixed $values,...
  227. * @return mixed variable value for $name if $name argument is provided, else return all variables
  228. */
  229. function set($name = null, $values = null)
  230. {
  231. static $vars = array();
  232. $args = func_get_args();
  233. $name = array_shift($args);
  234. if(is_null($name)) return $vars;
  235. if(!empty($args))
  236. {
  237. $vars[$name] = count($args) > 1 ? $args : $args[0];
  238. }
  239. if(array_key_exists($name, $vars)) return $vars[$name];
  240. return $vars;
  241. }
  242. /**
  243. * Sets a template variable with a value or a default value if value is empty
  244. *
  245. * @param string $name
  246. * @param string $value
  247. * @param string $default
  248. * @return mixed setted value
  249. */
  250. function set_or_default($name, $value, $default)
  251. {
  252. return set($name, value_or_default($value, $default));
  253. }
  254. /**
  255. * Running application
  256. *
  257. * @param string $env
  258. * @return void
  259. */
  260. function run($env = null)
  261. {
  262. if(is_null($env)) $env = env();
  263. # 0. Set default configuration
  264. $root_dir = dirname(app_file());
  265. $base_path = dirname(file_path($env['SERVER']['SCRIPT_NAME']));
  266. $base_file = basename($env['SERVER']['SCRIPT_NAME']);
  267. $base_uri = file_path($base_path, (($base_file == 'index.php') ? '?' : $base_file.'?'));
  268. $lim_dir = dirname(__FILE__);
  269. option('root_dir', $root_dir);
  270. option('base_path', $base_path);
  271. option('base_uri', $base_uri); // set it manually if you use url_rewriting
  272. option('limonade_dir', file_path($lim_dir));
  273. option('limonade_views_dir', file_path($lim_dir, 'limonade', 'views'));
  274. option('limonade_public_dir',file_path($lim_dir, 'limonade', 'public'));
  275. option('public_dir', file_path($root_dir, 'public'));
  276. option('views_dir', file_path($root_dir, 'views'));
  277. option('controllers_dir', file_path($root_dir, 'controllers'));
  278. option('lib_dir', file_path($root_dir, 'lib'));
  279. option('error_views_dir', option('limonade_views_dir'));
  280. option('env', ENV_PRODUCTION);
  281. option('debug', true);
  282. option('session', LIM_SESSION_NAME); // true, false or the name of your session
  283. option('encoding', 'utf-8');
  284. option('gzip', false);
  285. option('x-sendfile', 0); // 0: disabled,
  286. // X-SENDFILE: for Apache and Lighttpd v. >= 1.5,
  287. // X-LIGHTTPD-SEND-FILE: for Apache and Lighttpd v. < 1.5
  288. # 1. Set handlers
  289. # 1.1 Set error handling
  290. ini_set('display_errors', 1);
  291. set_error_handler('error_handler_dispatcher', E_ALL ^ E_NOTICE);
  292. # 1.2 Register shutdown function
  293. register_shutdown_function('stop_and_exit');
  294. # 2. Set user configuration
  295. call_if_exists('configure');
  296. # 2.1 Set gzip compression if defined
  297. if(is_bool(option('gzip')) && option('gzip'))
  298. {
  299. ini_set('zlib.output_compression', '1');
  300. }
  301. # 3. Loading libs
  302. require_once_dir(option('lib_dir'));
  303. # 4. Starting session
  304. if(!defined('SID') && option('session'))
  305. {
  306. if(!is_bool(option('session'))) session_name(option('session'));
  307. if(!session_start()) trigger_error("An error occured while trying to start the session", E_USER_WARNING);
  308. }
  309. # 5. Set some default methods if needed
  310. if(!function_exists('after'))
  311. {
  312. function after($output)
  313. {
  314. return $output;
  315. }
  316. }
  317. if(!function_exists('route_missing'))
  318. {
  319. function route_missing($request_method, $request_uri)
  320. {
  321. halt(NOT_FOUND, "($request_method) $request_uri");
  322. }
  323. }
  324. # 6. Check request
  325. if($rm = request_method())
  326. {
  327. if(request_is_head()) ob_start(); // then no output
  328. if(!request_method_is_allowed($rm))
  329. halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
  330. # 6.1 Check matching route
  331. if($route = route_find($rm, request_uri()))
  332. {
  333. params($route['params']);
  334. # 6.2 Load controllers dir
  335. require_once_dir(option('controllers_dir'));
  336. if(is_callable($route['function']))
  337. {
  338. # 6.3 Call before function
  339. call_if_exists('before', $route);
  340. # 6.4 Call matching controller function and output result
  341. if($output = call_user_func_array($route['function'], array_values($route['params'])))
  342. {
  343. echo after(error_notices_render() . $output);
  344. }
  345. }
  346. else halt(SERVER_ERROR, "Routing error: undefined function '{\
  347. ['function']}'", $route);
  348. }
  349. else route_missing($rm, request_uri());
  350. }
  351. else halt(HTTP_NOT_IMPLEMENTED, "The requested method <code>'$rm'</code> is not implemented");
  352. }
  353. /**
  354. * Stop and exit limonade application
  355. *
  356. * @access private
  357. * @param boolean exit or not
  358. * @return void
  359. */
  360. function stop_and_exit($exit = true)
  361. {
  362. call_if_exists('before_exit');
  363. $flash_sweep = true;
  364. $headers = headers_list();
  365. foreach($headers as $header)
  366. {
  367. // If a Content-Type header exists, flash_sweep only if is text/html
  368. // Else if there's no Content-Type header, flash_sweep by default
  369. if(stripos($header, 'Content-Type:') === 0)
  370. {
  371. $flash_sweep = stripos($header, 'Content-Type: text/html') === 0;
  372. break;
  373. }
  374. }
  375. if($flash_sweep) flash_sweep();
  376. if(defined('SID')) session_write_close();
  377. if(request_is_head()) ob_end_clean();
  378. if($exit) exit;
  379. }
  380. /**
  381. * Returns limonade environment variables:
  382. *
  383. * 'SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE',
  384. * 'GET', 'POST', 'PUT', 'DELETE'
  385. *
  386. * If a null argument is passed, reset and rebuild environment
  387. *
  388. * @param null @reset reset and rebuild environment
  389. * @return array
  390. */
  391. function env($reset = null)
  392. {
  393. static $env = array();
  394. if(func_num_args() > 0)
  395. {
  396. $args = func_get_args();
  397. if(is_null($args[0])) $env = array();
  398. }
  399. if(empty($env))
  400. {
  401. if(empty($GLOBALS['_SERVER']))
  402. {
  403. // Fixing empty $GLOBALS['_SERVER'] bug
  404. // http://sofadesign.lighthouseapp.com/projects/29612-limonade/tickets/29-env-is-empty
  405. $GLOBALS['_SERVER'] =& $_SERVER;
  406. $GLOBALS['_FILES'] =& $_FILES;
  407. $GLOBALS['_REQUEST'] =& $_REQUEST;
  408. $GLOBALS['_SESSION'] =& $_SESSION;
  409. $GLOBALS['_ENV'] =& $_ENV;
  410. $GLOBALS['_COOKIE'] =& $_COOKIE;
  411. }
  412. $glo_names = array('SERVER', 'FILES', 'REQUEST', 'SESSION', 'ENV', 'COOKIE');
  413. $vars = array_merge($glo_names, request_methods());
  414. foreach($vars as $var)
  415. {
  416. $varname = "_$var";
  417. if(!array_key_exists($varname, $GLOBALS)) $GLOBALS[$varname] = array();
  418. $env[$var] =& $GLOBALS[$varname];
  419. }
  420. $method = request_method($env);
  421. if($method == 'PUT' || $method == 'DELETE')
  422. {
  423. $varname = "_$method";
  424. if(array_key_exists('_method', $_POST) && $_POST['_method'] == $method)
  425. {
  426. foreach($_POST as $k => $v)
  427. {
  428. if($k == "_method") continue;
  429. $GLOBALS[$varname][$k] = $v;
  430. }
  431. }
  432. else
  433. {
  434. parse_str(file_get_contents('php://input'), $GLOBALS[$varname]);
  435. }
  436. }
  437. }
  438. return $env;
  439. }
  440. /**
  441. * Returns application root file path
  442. *
  443. * @return string
  444. */
  445. function app_file()
  446. {
  447. static $file;
  448. if(empty($file))
  449. {
  450. $stacktrace = array_pop(debug_backtrace());
  451. $file = $stacktrace['file'];
  452. }
  453. return file_path($file);
  454. }
  455. # # #
  456. # ============================================================================ #
  457. # 2. ERROR #
  458. # ============================================================================ #
  459. /**
  460. * Associate a function with error code(s) and return all associations
  461. *
  462. * @param string $errno
  463. * @param string $function
  464. * @return array
  465. */
  466. function error($errno = null, $function = null)
  467. {
  468. static $errors = array();
  469. if(func_num_args() > 0)
  470. {
  471. $errors[] = array('errno'=>$errno, 'function'=> $function);
  472. }
  473. return $errors;
  474. }
  475. /**
  476. * Raise an error, passing a given error number and an optional message,
  477. * then exit.
  478. * Error number should be a HTTP status code or a php user error (E_USER...)
  479. * $errno and $msg arguments can be passsed in any order
  480. * If no arguments are passed, default $errno is SERVER_ERROR (500)
  481. *
  482. * @param int,string $errno Error number or message string
  483. * @param string,string $msg Message string or error number
  484. * @param mixed $debug_args extra data provided for debugging
  485. * @return void
  486. */
  487. function halt($errno = SERVER_ERROR, $msg = '', $debug_args = null)
  488. {
  489. $args = func_get_args();
  490. $error = array_shift($args);
  491. # switch $errno and $msg args
  492. # TODO cleanup / refactoring
  493. if(is_string($errno))
  494. {
  495. $msg = $errno;
  496. $oldmsg = array_shift($args);
  497. $errno = empty($oldmsg) ? SERVER_ERROR : $oldmsg;
  498. }
  499. else if(!empty($args)) $msg = array_shift($args);
  500. if(empty($msg) && $errno == NOT_FOUND) $msg = request_uri();
  501. if(empty($msg)) $msg = "";
  502. if(!empty($args)) $debug_args = $args;
  503. set('_lim_err_debug_args', $debug_args);
  504. error_handler_dispatcher($errno, $msg, null, null);
  505. }
  506. /**
  507. * Internal error handler dispatcher
  508. * Find and call matching error handler and exit
  509. * If no match found, call default error handler
  510. *
  511. * @access private
  512. * @param int $errno
  513. * @param string $errstr
  514. * @param string $errfile
  515. * @param string $errline
  516. * @return void
  517. */
  518. function error_handler_dispatcher($errno, $errstr, $errfile, $errline)
  519. {
  520. $back_trace = debug_backtrace();
  521. while($trace = array_shift($back_trace))
  522. {
  523. if($trace['function'] == 'halt')
  524. {
  525. $errfile = $trace['file'];
  526. $errline = $trace['line'];
  527. break;
  528. }
  529. }
  530. # Notices and warning won't halt execution
  531. if(error_wont_halt_app($errno))
  532. {
  533. error_notice($errno, $errstr, $errfile, $errline);
  534. return;
  535. }
  536. else
  537. {
  538. # Other errors will stop application
  539. $handlers = error();
  540. $is_http_err = http_response_status_is_valid($errno);
  541. foreach($handlers as $handler)
  542. {
  543. $e = is_array($handler['errno']) ? $handler['errno'] : array($handler['errno']);
  544. while($ee = array_shift($e))
  545. {
  546. if($ee == $errno || $ee == E_LIM_PHP || ($ee == E_LIM_HTTP && $is_http_err))
  547. {
  548. echo call_if_exists($handler['function'], $errno, $errstr, $errfile, $errline);
  549. exit;
  550. }
  551. }
  552. }
  553. echo error_default_handler($errno, $errstr, $errfile, $errline);
  554. }
  555. }
  556. /**
  557. * Default error handler
  558. *
  559. * @param string $errno
  560. * @param string $errstr
  561. * @param string $errfile
  562. * @param string $errline
  563. * @return string error output
  564. */
  565. function error_default_handler($errno, $errstr, $errfile, $errline)
  566. {
  567. $is_http_err = http_response_status_is_valid($errno);
  568. $http_error_code = $is_http_err ? $errno : SERVER_ERROR;
  569. status($http_error_code);
  570. return $http_error_code == NOT_FOUND ?
  571. error_not_found_output($errno, $errstr, $errfile, $errline) :
  572. error_server_error_output($errno, $errstr, $errfile, $errline);
  573. }
  574. /**
  575. * Returns not found error output
  576. *
  577. * @access private
  578. * @param string $msg
  579. * @return string
  580. */
  581. function error_not_found_output($errno, $errstr, $errfile, $errline)
  582. {
  583. if(!function_exists('not_found'))
  584. {
  585. /**
  586. * Default not found error output
  587. *
  588. * @param string $errno
  589. * @param string $errstr
  590. * @param string $errfile
  591. * @param string $errline
  592. * @return string
  593. */
  594. function not_found($errno, $errstr, $errfile=null, $errline=null)
  595. {
  596. option('views_dir', option('error_views_dir'));
  597. $msg = h(rawurldecode($errstr));
  598. return html("<h1>Page not found:</h1><p><code>{$msg}</code></p>", error_layout());
  599. }
  600. }
  601. return not_found($errno, $errstr, $errfile, $errline);
  602. }
  603. /**
  604. * Returns server error output
  605. *
  606. * @access private
  607. * @param int $errno
  608. * @param string $errstr
  609. * @param string $errfile
  610. * @param string $errline
  611. * @return string
  612. */
  613. function error_server_error_output($errno, $errstr, $errfile, $errline)
  614. {
  615. if(!function_exists('server_error'))
  616. {
  617. /**
  618. * Default server error output
  619. *
  620. * @param string $errno
  621. * @param string $errstr
  622. * @param string $errfile
  623. * @param string $errline
  624. * @return string
  625. */
  626. function server_error($errno, $errstr, $errfile=null, $errline=null)
  627. {
  628. $is_http_error = http_response_status_is_valid($errno);
  629. $args = compact('errno', 'errstr', 'errfile', 'errline', 'is_http_error');
  630. option('views_dir', option('limonade_views_dir'));
  631. $html = render('error.html.php', null, $args);
  632. option('views_dir', option('error_views_dir'));
  633. return html($html, error_layout(), $args);
  634. }
  635. }
  636. return server_error($errno, $errstr, $errfile, $errline);
  637. }
  638. /**
  639. * Set and returns error output layout
  640. *
  641. * @param string $layout
  642. * @return string
  643. */
  644. function error_layout($layout = false)
  645. {
  646. static $o_layout = 'default_layout.php';
  647. if($layout !== false)
  648. {
  649. option('error_views_dir', option('views_dir'));
  650. $o_layout = $layout;
  651. }
  652. return $o_layout;
  653. }
  654. /**
  655. * Set a notice if arguments are provided
  656. * Returns all stored notices.
  657. * If $errno argument is null, reset the notices array
  658. *
  659. * @access private
  660. * @param string, null $str
  661. * @return array
  662. */
  663. function error_notice($errno = false, $errstr = null, $errfile = null, $errline = null)
  664. {
  665. static $notices = array();
  666. if($errno) $notices[] = compact('errno', 'errstr', 'errfile', 'errline');
  667. else if(is_null($errno)) $notices = array();
  668. return $notices;
  669. }
  670. /**
  671. * Returns notices output rendering and reset notices
  672. *
  673. * @return string
  674. */
  675. function error_notices_render()
  676. {
  677. if(option('debug') && option('env') > ENV_PRODUCTION)
  678. {
  679. $notices = error_notice();
  680. error_notice(null); // reset notices
  681. $c_view_dir = option('views_dir'); // keep for restore after render
  682. option('views_dir', option('limonade_views_dir'));
  683. $o = render('_notices.html.php', null, array('notices' => $notices));
  684. option('views_dir', $c_view_dir); // restore current views dir
  685. return $o;
  686. }
  687. }
  688. /**
  689. * Checks if an error is will halt application execution.
  690. * Notices and warnings will not.
  691. *
  692. * @access private
  693. * @param string $num error code number
  694. * @return boolean
  695. */
  696. function error_wont_halt_app($num)
  697. {
  698. return $num == E_NOTICE ||
  699. $num == E_WARNING ||
  700. $num == E_CORE_WARNING ||
  701. $num == E_COMPILE_WARNING ||
  702. $num == E_USER_WARNING ||
  703. $num == E_USER_NOTICE ||
  704. $num == E_DEPRECATED ||
  705. $num == E_USER_DEPRECATED ||
  706. $num == E_LIM_DEPRECATED;
  707. }
  708. /**
  709. * return error code name for a given code num, or return all errors names
  710. *
  711. * @param string $num
  712. * @return mixed
  713. */
  714. function error_type($num = null)
  715. {
  716. $types = array (
  717. E_ERROR => 'ERROR',
  718. E_WARNING => 'WARNING',
  719. E_PARSE => 'PARSING ERROR',
  720. E_NOTICE => 'NOTICE',
  721. E_CORE_ERROR => 'CORE ERROR',
  722. E_CORE_WARNING => 'CORE WARNING',
  723. E_COMPILE_ERROR => 'COMPILE ERROR',
  724. E_COMPILE_WARNING => 'COMPILE WARNING',
  725. E_USER_ERROR => 'USER ERROR',
  726. E_USER_WARNING => 'USER WARNING',
  727. E_USER_NOTICE => 'USER NOTICE',
  728. E_STRICT => 'STRICT NOTICE',
  729. E_RECOVERABLE_ERROR => 'RECOVERABLE ERROR',
  730. E_DEPRECATED => 'DEPRECATED WARNING',
  731. E_USER_DEPRECATED => 'USER DEPRECATED WARNING',
  732. E_LIM_DEPRECATED => 'LIMONADE DEPRECATED WARNING'
  733. );
  734. return is_null($num) ? $types : $types[$num];
  735. }
  736. /**
  737. * Returns http response status for a given error number
  738. *
  739. * @param string $errno
  740. * @return int
  741. */
  742. function error_http_status($errno)
  743. {
  744. $code = http_response_status_is_valid($errno) ? $errno : SERVER_ERROR;
  745. return http_response_status($code);
  746. }
  747. # # #
  748. # ============================================================================ #
  749. # 3. REQUEST #
  750. # ============================================================================ #
  751. /**
  752. * Returns current request method for a given environment or current one
  753. *
  754. * @param string $env
  755. * @return string
  756. */
  757. function request_method($env = null)
  758. {
  759. if(is_null($env)) $env = env();
  760. $m = array_key_exists('REQUEST_METHOD', $env['SERVER']) ? $env['SERVER']['REQUEST_METHOD'] : null;
  761. if($m == "POST" && array_key_exists('_method', $env['POST']))
  762. $m = strtoupper($env['POST']['_method']);
  763. if(!in_array(strtoupper($m), request_methods()))
  764. {
  765. trigger_error("'$m' request method is unkown or unavailable.", E_USER_WARNING);
  766. $m = false;
  767. }
  768. return $m;
  769. }
  770. /**
  771. * Checks if a request method or current one is allowed
  772. *
  773. * @param string $m
  774. * @return bool
  775. */
  776. function request_method_is_allowed($m = null)
  777. {
  778. if(is_null($m)) $m = request_method();
  779. return in_array(strtoupper($m), request_methods());
  780. }
  781. /**
  782. * Checks if request method is GET
  783. *
  784. * @param string $env
  785. * @return bool
  786. */
  787. function request_is_get($env = null)
  788. {
  789. return request_method($env) == "GET";
  790. }
  791. /**
  792. * Checks if request method is POST
  793. *
  794. * @param string $env
  795. * @return bool
  796. */
  797. function request_is_post($env = null)
  798. {
  799. return request_method($env) == "POST";
  800. }
  801. /**
  802. * Checks if request method is PUT
  803. *
  804. * @param string $env
  805. * @return bool
  806. */
  807. function request_is_put($env = null)
  808. {
  809. return request_method($env) == "PUT";
  810. }
  811. /**
  812. * Checks if request method is DELETE
  813. *
  814. * @param string $env
  815. * @return bool
  816. */
  817. function request_is_delete($env = null)
  818. {
  819. return request_method($env) == "DELETE";
  820. }
  821. /**
  822. * Checks if request method is HEAD
  823. *
  824. * @param string $env
  825. * @return bool
  826. */
  827. function request_is_head($env = null)
  828. {
  829. return request_method($env) == "HEAD";
  830. }
  831. /**
  832. * Returns allowed request methods
  833. *
  834. * @return array
  835. */
  836. function request_methods()
  837. {
  838. return array("GET","POST","PUT","DELETE", "HEAD");
  839. }
  840. /**
  841. * Returns current request uri (the path that will be compared with routes)
  842. *
  843. * (Inspired from codeigniter URI::_fetch_uri_string method)
  844. *
  845. * @return string
  846. */
  847. function request_uri($env = null)
  848. {
  849. static $uri = null;
  850. if(is_null($env))
  851. {
  852. if(!is_null($uri)) return $uri;
  853. $env = env();
  854. }
  855. if(array_key_exists('uri', $env['GET']))
  856. {
  857. $uri = $env['GET']['uri'];
  858. }
  859. else if(array_key_exists('u', $env['GET']))
  860. {
  861. $uri = $env['GET']['u'];
  862. }
  863. // bug: dot are converted to _... so we can't use it...
  864. // else if (count($env['GET']) == 1 && trim(key($env['GET']), '/') != '')
  865. // {
  866. // $uri = key($env['GET']);
  867. // }
  868. else
  869. {
  870. $app_file = app_file();
  871. $path_info = isset($env['SERVER']['PATH_INFO']) ? $env['SERVER']['PATH_INFO'] : @getenv('PATH_INFO');
  872. $query_string = isset($env['SERVER']['QUERY_STRING']) ? $env['SERVER']['QUERY_STRING'] : @getenv('QUERY_STRING');
  873. // Is there a PATH_INFO variable?
  874. // Note: some servers seem to have trouble with getenv() so we'll test it two ways
  875. if (trim($path_info, '/') != '' && $path_info != "/".$app_file)
  876. {
  877. $uri = $path_info;
  878. }
  879. // No PATH_INFO?... What about QUERY_STRING?
  880. elseif (trim($query_string, '/') != '')
  881. {
  882. $uri = $query_string;
  883. }
  884. elseif(array_key_exists('REQUEST_URI', $env['SERVER']) && !empty($env['SERVER']['REQUEST_URI']))
  885. {
  886. $request_uri = rtrim(rawurldecode($env['SERVER']['REQUEST_URI']), '?/').'/';
  887. $base_path = $env['SERVER']['SCRIPT_NAME'];
  888. if($request_uri."index.php" == $base_path) $request_uri .= "index.php";
  889. $uri = str_replace($base_path, '', $request_uri);
  890. }
  891. elseif($env['SERVER']['argc'] > 1 && trim($env['SERVER']['argv'][1], '/') != '')
  892. {
  893. $uri = $env['SERVER']['argv'][1];
  894. }
  895. }
  896. $uri = rtrim($uri, "/"); # removes ending /
  897. if(empty($uri))
  898. {
  899. $uri = '/';
  900. }
  901. else if($uri[0] != '/')
  902. {
  903. $uri = '/' . $uri; # add a leading slash
  904. }
  905. return rawurldecode($uri);
  906. }
  907. # # #
  908. # ============================================================================ #
  909. # 4. ROUTER #
  910. # ============================================================================ #
  911. /**
  912. * An alias of {@link dispatch_get()}
  913. *
  914. * @return void
  915. */
  916. function dispatch($path_or_array, $function, $options = array())
  917. {
  918. dispatch_get($path_or_array, $function, $options);
  919. }
  920. /**
  921. * Add a GET route. Also automatically defines a HEAD route.
  922. *
  923. * @param string $path_or_array
  924. * @param string $function
  925. * @param array $options (optional). See {@link route()} for available options.
  926. * @return void
  927. */
  928. function dispatch_get($path_or_array, $function, $options = array())
  929. {
  930. route("GET", $path_or_array, $function, $options);
  931. route("HEAD", $path_or_array, $function, $options);
  932. }
  933. /**
  934. * Add a POST route
  935. *
  936. * @param string $path_or_array
  937. * @param string $function
  938. * @param array $options (optional). See {@link route()} for available options.
  939. * @return void
  940. */
  941. function dispatch_post($path_or_array, $function, $options = array())
  942. {
  943. route("POST", $path_or_array, $function, $options);
  944. }
  945. /**
  946. * Add a PUT route
  947. *
  948. * @param string $path_or_array
  949. * @param string $function
  950. * @param array $options (optional). See {@link route()} for available options.
  951. * @return void
  952. */
  953. function dispatch_put($path_or_array, $function, $options = array())
  954. {
  955. route("PUT", $path_or_array, $function, $options);
  956. }
  957. /**
  958. * Add a DELETE 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_delete($path_or_array, $function, $options = array())
  966. {
  967. route("DELETE", $path_or_array, $function, $options);
  968. }
  969. /**
  970. * Add route if required params are provided.
  971. * Delete all routes if null is passed as a unique argument
  972. * Return all routes
  973. *
  974. * @see route_build()
  975. * @access private
  976. * @param string $method
  977. * @param string|array $path_or_array
  978. * @param callback $func
  979. * @param array $options (optional)
  980. * @return array
  981. */
  982. function route()
  983. {
  984. static $routes = array();
  985. $nargs = func_num_args();
  986. if( $nargs > 0)
  987. {
  988. $args = func_get_args();
  989. if($nargs === 1 && is_null($args[0])) $routes = array();
  990. else if($nargs < 3) trigger_error("Missing arguments for route()", E_USER_ERROR);
  991. else
  992. {
  993. $method = $args[0];
  994. $path_or_array = $args[1];
  995. $func = $args[2];
  996. $options = $nargs > 3 ? $args[3] : array();
  997. $routes[] = route_build($method, $path_or_array, $func, $options);
  998. }
  999. }
  1000. return $routes;
  1001. }
  1002. /**
  1003. * An alias of route(null): reset all routes
  1004. *
  1005. * @access private
  1006. * @return void
  1007. */
  1008. function route_reset()
  1009. {
  1010. route(null);
  1011. }
  1012. /**
  1013. * Build a route and return it
  1014. *
  1015. * @access private
  1016. * @param string $method allowed http method (one of those returned by {@link request_methods()})
  1017. * @param string|array $path_or_array
  1018. * @param callback $func callback function called when route is found. It can be
  1019. * a function, an object method, a static method or a closure.
  1020. * See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
  1021. * to learn more about callbacks.
  1022. * @param array $options (optional). Available options:
  1023. * - 'params' key with an array of parameters: for parametrized routes.
  1024. * those parameters will be merged with routes parameters.
  1025. * @return array array with keys "method", "pattern", "names", "function", "options"
  1026. */
  1027. function route_build($method, $path_or_array, $func, $options = array())
  1028. {
  1029. $method = strtoupper($method);
  1030. if(!in_array($method, request_methods()))
  1031. trigger_error("'$method' request method is unkown or unavailable.", E_USER_WARNING);
  1032. if(is_array($path_or_array))
  1033. {
  1034. $path = array_shift($path_or_array);
  1035. $names = $path_or_array[0];
  1036. }
  1037. else
  1038. {
  1039. $path = $path_or_array;
  1040. $names = array();
  1041. }
  1042. $single_asterisk_subpattern = "(?:/([^\/]*))?";
  1043. $double_asterisk_subpattern = "(?:/(.*))?";
  1044. $optionnal_slash_subpattern = "(?:/*?)";
  1045. $no_slash_asterisk_subpattern = "(?:([^\/]*))?";
  1046. if($path[0] == "^")
  1047. {
  1048. if($path{strlen($path) - 1} != "$") $path .= "$";
  1049. $pattern = "#".$path."#i";
  1050. }
  1051. else if(empty($path) || $path == "/")
  1052. {
  1053. $pattern = "#^".$optionnal_slash_subpattern."$#";
  1054. }
  1055. else
  1056. {
  1057. $parsed = array();
  1058. $elts = explode('/', $path);
  1059. $parameters_count = 0;
  1060. foreach($elts as $elt)
  1061. {
  1062. if(empty($elt)) continue;
  1063. $name = null;
  1064. # extracting double asterisk **
  1065. if($elt == "**"):
  1066. $parsed[] = $double_asterisk_subpattern;
  1067. $name = $parameters_count;
  1068. # extracting single asterisk *
  1069. elseif($elt == "*"):
  1070. $parsed[] = $single_asterisk_subpattern;
  1071. $name = $parameters_count;
  1072. # extracting named parameters :my_param
  1073. elseif($elt[0] == ":"):
  1074. if(preg_match('/^:([^\:]+)$/', $elt, $matches))
  1075. {
  1076. $parsed[] = $single_asterisk_subpattern;
  1077. $name = $matches[1];
  1078. };
  1079. elseif(strpos($elt, '*') !== false):
  1080. $sub_elts = explode('*', $elt);
  1081. $parsed_sub = array();
  1082. foreach($sub_elts as $sub_elt)
  1083. {
  1084. $parsed_sub[] = preg_quote($sub_elt, "#");
  1085. $name = $parameters_count;
  1086. }
  1087. //
  1088. $parsed[] = "/".implode($no_slash_asterisk_subpattern, $parsed_sub);
  1089. else:
  1090. $parsed[] = "/".preg_quote($elt, "#");
  1091. endif;
  1092. /* set parameters names */
  1093. if(is_null($name)) continue;
  1094. if(!array_key_exists($parameters_count, $names) || is_null($names[$parameters_count]))
  1095. $names[$parameters_count] = $name;
  1096. $parameters_count++;
  1097. }
  1098. $pattern = "#^".implode('', $parsed).$optionnal_slash_subpattern."?$#i";
  1099. }
  1100. return array( "method" => $method,
  1101. "pattern" => $pattern,
  1102. "names" => $names,
  1103. "function" => $func,
  1104. "options" => $options );
  1105. }
  1106. /**
  1107. * Find a route and returns it.
  1108. * If not found, returns false.
  1109. * Routes are checked from first added to last added.
  1110. *
  1111. * @access private
  1112. * @param string $method
  1113. * @param string $path
  1114. * @return array,false
  1115. */
  1116. function route_find($method, $path)
  1117. {
  1118. $routes = route();
  1119. $method = strtoupper($method);
  1120. foreach($routes as $route)
  1121. {
  1122. if($method == $route["method"] && preg_match($route["pattern"], $path, $matches))
  1123. {
  1124. $options = $route["options"];
  1125. $params = array_key_exists('params', $options) ? $options["params"] : array();
  1126. if(count($matches) > 1)
  1127. {
  1128. array_shift($matches);
  1129. $n_matches = count($matches);
  1130. $names = array_values($route["names"]);
  1131. $n_names = count($names);
  1132. if( $n_matches < $n_names )
  1133. {
  1134. $a = array_fill(0, $n_names - $n_matches, null);
  1135. $matches = array_merge($matches, $a);
  1136. }
  1137. else if( $n_matches > $n_names )
  1138. {
  1139. $names = range($n_names, $n_matches - 1);
  1140. }
  1141. $params = array_replace($params, array_combine($names, $matches));
  1142. }
  1143. $route["params"] = $params;
  1144. return $route;
  1145. }
  1146. }
  1147. return false;
  1148. }
  1149. # ============================================================================ #
  1150. # OUTPUT AND RENDERING #
  1151. # ============================================================================ #
  1152. /**
  1153. * Returns a string to output
  1154. *
  1155. * It might use a a template file or function, a formatted string (like {@link sprintf()}).
  1156. * It could be embraced by a layout or not.
  1157. * Local vars can be passed in addition to variables made available with the {@link set()}
  1158. * function.
  1159. *
  1160. * @param string $content_or_func
  1161. * @param string $layout
  1162. * @param string $locals
  1163. * @return string
  1164. */
  1165. function render($content_or_func, $layout = '', $locals = array())
  1166. {
  1167. $args = func_get_args();
  1168. $content_or_func = array_shift($args);
  1169. $layout = count($args) > 0 ? array_shift($args) : layout();
  1170. $view_path = file_path(option('views_dir'),$content_or_func);
  1171. $vars = array_merge(set(), $locals);
  1172. $flash = flash_now();
  1173. 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);
  1174. else if(!empty($flash)) $vars['flash'] = $flash;
  1175. $infinite_loop = false;
  1176. # Avoid infinite loop: this function is in the backtrace ?
  1177. if(function_exists($content_or_func))
  1178. {
  1179. $back_trace = debug_backtrace();
  1180. while($trace = array_shift($back_trace))
  1181. {
  1182. if($trace['function'] == strtolower($content_or_func))
  1183. {
  1184. $infinite_loop = true;
  1185. break;
  1186. }
  1187. }
  1188. }
  1189. if(function_exists($content_or_func) && !$infinite_loop)
  1190. {
  1191. ob_start();
  1192. call_user_func($content_or_func, $vars);
  1193. $content = ob_get_clean();
  1194. }
  1195. elseif(file_exists($view_path))
  1196. {
  1197. ob_start();
  1198. extract($vars);
  1199. include $view_path;
  1200. $content = ob_get_clean();
  1201. }
  1202. else
  1203. {
  1204. if(substr_count($content_or_func, '%') !== count($vars)) $content = $content_or_func;
  1205. else $content = vsprintf($content_or_func, $vars);
  1206. }
  1207. if(empty($layout)) return $content;
  1208. return render($layout, null, array('content' => $content));
  1209. }
  1210. /**
  1211. * Returns a string to output
  1212. *
  1213. * Shortcut to render with no layout.
  1214. *
  1215. * @param string $content_or_func
  1216. * @param string $locals
  1217. * @return string
  1218. */
  1219. function partial($content_or_func, $locals = array())
  1220. {
  1221. return render($content_or_func, null, $locals);
  1222. }
  1223. /**
  1224. * Returns html output with proper http headers
  1225. *
  1226. * @param string $content_or_func
  1227. * @param string $layout
  1228. * @param string $locals
  1229. * @return string
  1230. */
  1231. function html($content_or_func, $layout = '', $locals = array())
  1232. {
  1233. if(!headers_sent()) header('Content-Type: text/html; charset='.strtolower(option('encoding')));
  1234. $args = func_get_args();
  1235. return call_user_func_array('render', $args);
  1236. }
  1237. /**
  1238. * Set and return current layout
  1239. *
  1240. * @param string $function_or_file
  1241. * @return string
  1242. */
  1243. function layout($function_or_file = null)
  1244. {
  1245. static $layout = null;
  1246. if(func_num_args() > 0) $layout = $function_or_file;
  1247. return $layout;
  1248. }
  1249. /**
  1250. * Returns xml output with proper http headers
  1251. *
  1252. * @param string $content_or_func
  1253. * @param string $layout
  1254. * @param string $locals
  1255. * @return string
  1256. */
  1257. function xml($data)
  1258. {
  1259. if(!headers_sent()) header('Content-Type: text/xml; charset='.strtolower(option('encoding')));
  1260. $args = func_get_args();
  1261. return call_user_func_array('render', $args);
  1262. }
  1263. /**
  1264. * Returns css output with proper http headers
  1265. *
  1266. * @param string $content_or_func
  1267. * @param string $layout
  1268. * @param string $locals
  1269. * @return string
  1270. */
  1271. function css($content_or_func, $layout = '', $locals = array())
  1272. {
  1273. if(!headers_sent()) header('Content-Type: text/css; charset='.strtolower(option('encoding')));
  1274. $args = func_get_args();
  1275. return call_user_func_array('render', $args);
  1276. }
  1277. /**
  1278. * Returns txt output with proper http headers
  1279. *
  1280. * @param string $content_or_func
  1281. * @param string $layout
  1282. * @param string $locals
  1283. * @return string
  1284. */
  1285. function txt($content_or_func, $layout = '', $locals = array())
  1286. {
  1287. if(!headers_sent()) header('Content-Type: text/plain; charset='.strtolower(option('encoding')));
  1288. $args = func_get_args();
  1289. return call_user_func_array('render', $args);
  1290. }
  1291. /**
  1292. * Returns json representation of data with proper http headers
  1293. *
  1294. * @param string $data
  1295. * @param int $json_option
  1296. * @return string
  1297. */
  1298. function json($data, $json_option = 0)
  1299. {
  1300. if(!headers_sent()) header('Content-Type: application/x-javascript; charset='.strtolower(option('encoding')));
  1301. return version_compare(PHP_VERSION, '5.3.0', '>=') ? json_encode($data, $json_option) : json_encode($data);
  1302. }
  1303. /**
  1304. * undocumented function
  1305. *
  1306. * @param string $filename
  1307. * @param string $return
  1308. * @return mixed number of bytes delivered or file output if $return = true
  1309. */
  1310. function render_file($filename, $return = false)
  1311. {
  1312. # TODO implements X-SENDFILE headers
  1313. // if($x-sendfile = option('x-sendfile'))
  1314. // {
  1315. // // add a X-Sendfile header for apache and Lighttpd >= 1.5
  1316. // if($x-sendfile > X-SENDFILE) // add a X-LIGHTTPD-send-file header
  1317. //
  1318. // }
  1319. // else
  1320. // {
  1321. //
  1322. // }
  1323. $filename = str_replace('../', '', $filename);
  1324. if(file_exists($filename))
  1325. {
  1326. $content_type = mime_type(file_extension($filename));
  1327. $header = 'Content-type: '.$content_type;
  1328. if(file_is_text($filename)) $header .= '; charset='.strtolower(option('encoding'));
  1329. if(!headers_sent()) header($header);
  1330. return file_read($filename, $return);
  1331. }
  1332. else halt(NOT_FOUND, "unknown filename $filename");
  1333. }
  1334. # # #
  1335. # ============================================================================ #
  1336. # 5. HELPERS #
  1337. # ============================================================================ #
  1338. /**
  1339. * Returns an url composed of params joined with /
  1340. *
  1341. * @param string $params,...
  1342. * @return string
  1343. */
  1344. function url_for($params = null)
  1345. {
  1346. $paths = array();
  1347. $params = func_get_args();
  1348. $first = true;
  1349. foreach($params as $param)
  1350. {
  1351. if($first)
  1352. {
  1353. if(filter_var($param , FILTER_VALIDATE_URL))
  1354. {
  1355. $paths[] = $param;
  1356. continue;
  1357. }
  1358. }
  1359. $p = explode('/',$param);
  1360. foreach($p as $v)
  1361. {
  1362. if(!empty($v)) $paths[] = str_replace('%23', '#', rawurlencode($v));
  1363. }
  1364. }
  1365. $path = rtrim(implode('/', $paths), '/');
  1366. if(!filter_var($path , FILTER_VALIDATE_URL))
  1367. {
  1368. # it's a relative URL or an URL without a schema
  1369. $base_uri = option('base_uri');
  1370. $path = file_path($base_uri, $path);
  1371. }
  1372. if(DIRECTORY_SEPARATOR != '/') $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
  1373. return $path;
  1374. }
  1375. /**
  1376. * An alias of {@link htmlspecialchars()}.
  1377. * If no $charset is provided, uses option('encoding') value
  1378. *
  1379. * @param string $str
  1380. * @param string $quote_style
  1381. * @param string $charset
  1382. * @return void
  1383. */
  1384. function h($str, $quote_style = ENT_NOQUOTES, $charset = null)
  1385. {
  1386. if(is_null($charset)) $charset = strtoupper(option('encoding'));
  1387. return htmlspecialchars($str, $quote_style, $charset);
  1388. }
  1389. /**
  1390. * Set and returns flash messages that will be available in the next action
  1391. * via the {@link flash_now()} function or the view variable <code>$flash</code>.
  1392. *
  1393. * If multiple values are provided, set <code>$name</code> variable with an array of those values.
  1394. * If there is only one value, set <code>$name</code> variable with the provided $values
  1395. * or if it's <code>$name</code> is an array, merge it with current messages.
  1396. *
  1397. * @param string, array $name
  1398. * @param mixed $values,...
  1399. * @return mixed variable value for $name if $name argument is provided, else return all variables
  1400. */
  1401. function flash($name = null, $value = null)
  1402. {
  1403. if(!defined('SID')) trigger_error("Flash messages can't be used because session isn't enabled", E_USER_WARNING);
  1404. static $messages = array();
  1405. $args = func_get_args();
  1406. $name = array_shift($args);
  1407. if(is_null($name)) return $messages;
  1408. if(is_array($name)) return $messages = array_merge($messages, $name);
  1409. if(!empty($args))
  1410. {
  1411. $messages[$name] = count($args) > 1 ? $args : $args[0];
  1412. }
  1413. if(array_key_exists($name, $messages)) return $messages[$name];
  1414. return $messages;
  1415. }
  1416. /**
  1417. * Set and returns flash messages available for the current action, included those
  1418. * defined in the previous action with {@link flash()}
  1419. * Those messages will also be passed to the views and made available in the
  1420. * <code>$flash</code> variable.
  1421. *
  1422. * If multiple values are provided, set <code>$name</code> variable with an array of those values.
  1423. * If there is only one value, set <code>$name</code> variable with the provided $values
  1424. * or if it's <code>$name</code> is an array, merge it with current messages.
  1425. *
  1426. * @param string, array $name
  1427. * @param mixed $values,...
  1428. * @return mixed variable value for $name if $name argument is provided, else return all variables
  1429. */
  1430. function flash_now($name = null, $value = null)
  1431. {
  1432. static $messages = null;
  1433. if(is_null($messages))
  1434. {
  1435. $fkey = LIM_SESSION_FLASH_KEY;
  1436. $messages = array();
  1437. if(defined('SID') && array_key_exists($fkey, $_SESSION)) $messages = $_SESSION[$fkey];
  1438. }
  1439. $args = func_get_args();
  1440. $name = array_shift($args);
  1441. if(is_null($name)) return $messages;
  1442. if(is_array($name)) return $messages = array_merge($messages, $name);
  1443. if(!empty($args))
  1444. {
  1445. $messages[$name] = count($args) > 1 ? $args : $args[0];
  1446. }
  1447. if(array_key_exists($name, $messages)) return $messages[$name];
  1448. return $messages;
  1449. }
  1450. /**
  1451. * Delete current flash messages in session, and set new ones stored with
  1452. * flash function.
  1453. * Called before application exit.
  1454. *
  1455. * @access private
  1456. * @return void
  1457. */
  1458. function flash_sweep()
  1459. {
  1460. if(defined('SID'))
  1461. {
  1462. $fkey = LIM_SESSION_FLASH_KEY;
  1463. $_SESSION[$fkey] = flash();
  1464. }
  1465. }
  1466. /**
  1467. * Starts capturing block of text
  1468. *
  1469. * Calling without params stops capturing (same as end_content_for()).
  1470. * After capturing the captured block is put into a variable
  1471. * named $name for later use in layouts. If second parameter
  1472. * is supplied, its content will be used instead of capturing
  1473. * a block of text.
  1474. *
  1475. * @param string $name
  1476. * @param string $content
  1477. * @return void
  1478. */
  1479. function content_for($name = null, $content = null)
  1480. {
  1481. static $_name = null;
  1482. if(is_null($name) && !is_null($_name))
  1483. {
  1484. set($_name, ob_get_clean());
  1485. $_name = null;
  1486. }
  1487. elseif(!is_null($name) && !isset($content))
  1488. {
  1489. $_name = $name;
  1490. ob_start();
  1491. }
  1492. elseif(isset($name, $content))
  1493. {
  1494. set($name, $content);
  1495. }
  1496. }
  1497. /**
  1498. * Stops capturing block of text
  1499. *
  1500. * @return void
  1501. */
  1502. function end_content_for()
  1503. {
  1504. content_for();
  1505. }
  1506. /**
  1507. * Shows current memory and execution time of the application.
  1508. *
  1509. * @access public
  1510. * @return array
  1511. */
  1512. function benchmark()
  1513. {
  1514. $current_mem_usage = memory_get_usage();
  1515. $execution_time = microtime() - LIM_START_MICROTIME;
  1516. return array(
  1517. 'current_memory' => $current_mem_usage,
  1518. 'start_memory' => LIM_START_MEMORY,
  1519. 'average_memory' => (LIM_START_MEMORY + $current_mem_usage) / 2,
  1520. 'execution_time' => $execution_time
  1521. );
  1522. }
  1523. # # #
  1524. # ============================================================================ #
  1525. # 6. UTILS #
  1526. # ============================================================================ #
  1527. /**
  1528. * Calls a function if exists
  1529. *
  1530. * @param callback $func a function stored in a string variable,
  1531. * or an object and the name of a method within the object
  1532. * See {@link http://php.net/manual/en/language.pseudo-types.php#language.types.callback php documentation}
  1533. * to learn more about callbacks.
  1534. * @param mixed $arg,.. (optional)
  1535. * @return mixed
  1536. */
  1537. function call_if_exists($func)
  1538. {
  1539. $args = func_get_args();
  1540. $func = array_shift($args);
  1541. if(is_callable($func)) return call_user_func_array($func, $args);
  1542. return;
  1543. }
  1544. /**
  1545. * Define a constant unless it already exists
  1546. *
  1547. * @param string $name
  1548. * @param string $value
  1549. * @return void
  1550. */
  1551. function define_unless_exists($name, $value)
  1552. {
  1553. if(!defined($name)) define($name, $value);
  1554. }
  1555. /**
  1556. * Return a default value if provided value is empty
  1557. *
  1558. * @param mixed $value
  1559. * @param mixed $default default value returned if $value is empty
  1560. * @return mixed
  1561. */
  1562. function value_or_default($value, $default)
  1563. {
  1564. return empty($value) ? $default : $value;
  1565. }
  1566. /**
  1567. * An alias of {@link value_or_default()}
  1568. *
  1569. *
  1570. * @param mixed $value
  1571. * @param mixed $default
  1572. * @return mixed
  1573. */
  1574. function v($value, $default)
  1575. {
  1576. return value_or_default($value, $default);
  1577. }
  1578. /**
  1579. * Load php files with require_once in a given dir
  1580. *
  1581. * @param string $path Path in which are the file to load
  1582. * @param string $pattern a regexp pattern that filter files to load
  1583. * @param bool $prevents_output security option that prevents output
  1584. * @return array paths of loaded files
  1585. */
  1586. function require_once_dir($path, $pattern = "*.php", $prevents_output = true)
  1587. {
  1588. if($path[strlen($path) - 1] != "/") $path .= "/";
  1589. $filenames = glob($path.$pattern);
  1590. if(!is_array($filenames)) $filenames = array();
  1591. if($prevents_output) ob_start();
  1592. foreach($filenames as $filename) require_once $filename;
  1593. if($prevents_output) ob_end_clean();
  1594. return $filenames;
  1595. }
  1596. ## HTTP utils _________________________________________________________________
  1597. ### Constants: HTTP status codes
  1598. define( 'HTTP_CONTINUE', 100 );
  1599. define( 'HTTP_SWITCHING_PROTOCOLS', 101 );
  1600. define( 'HTTP_PROCESSING', 102 );
  1601. define( 'HTTP_OK', 200 );
  1602. define( 'HTTP_CREATED', 201 );
  1603. define( 'HTTP_ACCEPTED', 202 );
  1604. define( 'HTTP_NON_AUTHORITATIVE', 203 );
  1605. define( 'HTTP_NO_CONTENT', 204 );
  1606. define( 'HTTP_RESET_CONTENT', 205 );
  1607. define( 'HTTP_PARTIAL_CONTENT', 206 );
  1608. define( 'HTTP_MULTI_STATUS', 207 );
  1609. define( 'HTTP_MULTIPLE_CHOICES', 300 );
  1610. define( 'HTTP_MOVED_PERMANENTLY', 301 );
  1611. define( 'HTTP_MOVED_TEMPORARILY', 302 );
  1612. define( 'HTTP_SEE_OTHER', 303 );
  1613. define( 'HTTP_NOT_MODIFIED', 304 );
  1614. define( 'HTTP_USE_PROXY', 305 );
  1615. define( 'HTTP_TEMPORARY_REDIRECT', 307 );
  1616. define( 'HTTP_BAD_REQUEST', 400 );
  1617. define( 'HTTP_UNAUTHORIZED', 401 );
  1618. define( 'HTTP_PAYMENT_REQUIRED', 402 );
  1619. define( 'HTTP_FORBIDDEN', 403 );
  1620. define( 'HTTP_NOT_FOUND', 404 );
  1621. define( 'HTTP_METHOD_NOT_ALLOWED', 405 );
  1622. define( 'HTTP_NOT_ACCEPTABLE', 406 );
  1623. define( 'HTTP_PROXY_AUTHENTICATION_REQUIRED', 407 );
  1624. define( 'HTTP_REQUEST_TIME_OUT', 408 );
  1625. define( 'HTTP_CONFLICT', 409 );
  1626. define( 'HTTP_GONE', 410 );
  1627. define( 'HTTP_LENGTH_REQUIRED', 411 );
  1628. define( 'HTTP_PRECONDITION_FAILED', 412 );
  1629. define( 'HTTP_REQUEST_ENTITY_TOO_LARGE', 413 );
  1630. define( 'HTTP_REQUEST_URI_TOO_LARGE', 414 );
  1631. define( 'HTTP_UNSUPPORTED_MEDIA_TYPE', 415 );
  1632. define( 'HTTP_RANGE_NOT_SATISFIABLE', 416 );
  1633. define( 'HTTP_EXPECTATION_FAILED', 417 );
  1634. define( 'HTTP_UNPROCESSABLE_ENTITY', 422 );
  1635. define( 'HTTP_LOCKED', 423 );
  1636. define( 'HTTP_FAILED_DEPENDENCY', 424 );
  1637. define( 'HTTP_UPGRADE_REQUIRED', 426 );
  1638. define( 'HTTP_INTERNAL_SERVER_ERROR', 500 );
  1639. define( 'HTTP_NOT_IMPLEMENTED', 501 );
  1640. define( 'HTTP_BAD_GATEWAY', 502 );
  1641. define( 'HTTP_SERVICE_UNAVAILABLE', 503 );
  1642. define( 'HTTP_GATEWAY_TIME_OUT', 504 );
  1643. define( 'HTTP_VERSION_NOT_SUPPORTED', 505 );
  1644. define( 'HTTP_VARIANT_ALSO_VARIES', 506 );
  1645. define( 'HTTP_INSUFFICIENT_STORAGE', 507 );
  1646. define( 'HTTP_NOT_EXTENDED', 510 );
  1647. /**
  1648. * Output proper HTTP header for a given HTTP code
  1649. *
  1650. * @param string $code
  1651. * @return void
  1652. */
  1653. function status($code = 500)
  1654. {
  1655. if(!headers_sent())
  1656. {
  1657. $str = http_response_status_code($code);
  1658. header($str);
  1659. }
  1660. }
  1661. /**
  1662. * Http redirection
  1663. *
  1664. * @param string $params,...
  1665. * @return void
  1666. */
  1667. function redirect_to($params)
  1668. {
  1669. # [NOTE]: (from php.net) HTTP/1.1 requires an absolute URI as argument to » Location:
  1670. # including the scheme, hostname and absolute path, but some clients accept
  1671. # relative URIs. You can usually use $_SERVER['HTTP_HOST'],
  1672. # $_SERVER['PHP_SELF'] and dirname() to make an absolute URI from a relative
  1673. # one yourself.
  1674. # TODO make absolute uri
  1675. if(!headers_sent())
  1676. {
  1677. $params = func_get_args();
  1678. $uri = call_user_func_array('url_for', $params);
  1679. stop_and_exit(false);
  1680. header('Location: '.$uri);
  1681. exit;
  1682. }
  1683. }
  1684. /**
  1685. * Http redirection
  1686. *
  1687. * @deprecated deprecated since version 0.4. Please use {@link redirect_to()} instead.
  1688. * @param string $url
  1689. * @return void
  1690. */
  1691. function redirect($uri)
  1692. {
  1693. # halt('redirect() is deprecated. Please use redirect_to() instead.', E_LIM_DEPRECATED);
  1694. # halt not necesary... it won't be visible because of http redirection...
  1695. redirect_to($uri);
  1696. }
  1697. /**
  1698. * Returns HTTP response status for a given code.
  1699. * If no code provided, return an array of all status
  1700. *
  1701. * @param string $num
  1702. * @return string,array
  1703. */
  1704. function http_response_status($num = null)
  1705. {
  1706. $status = array(
  1707. 100 => 'Continue',
  1708. 101 => 'Switching Protocols',
  1709. 102 => 'Processing',
  1710. 200 => 'OK',
  1711. 201 => 'Created',
  1712. 202 => 'Accepted',
  1713. 203 => 'Non-Authoritative Information',
  1714. 204 => 'No Content',
  1715. 205 => 'Reset Content',
  1716. 206 => 'Partial Content',
  1717. 207 => 'Multi-Status',
  1718. 226 => 'IM Used',
  1719. 300 => 'Multiple Choices',
  1720. 301 => 'Moved Permanently',
  1721. 302 => 'Found',
  1722. 303 => 'See Other',
  1723. 304 => 'Not Modified',
  1724. 305 => 'Use Proxy',
  1725. 306 => 'Reserved',
  1726. 307 => 'Temporary Redirect',
  1727. 400 => 'Bad Request',
  1728. 401 => 'Unauthorized',
  1729. 402 => 'Payment Required',
  1730. 403 => 'Forbidden',
  1731. 404 => 'Not Found',
  1732. 405 => 'Method Not Allowed',
  1733. 406 => 'Not Acceptable',
  1734. 407 => 'Proxy Authentication Required',
  1735. 408 => 'Request Timeout',
  1736. 409 => 'Conflict',
  1737. 410 => 'Gone',
  1738. 411 => 'Length Required',
  1739. 412 => 'Precondition Failed',
  1740. 413 => 'Request Entity Too Large',
  1741. 414 => 'Request-URI Too Long',
  1742. 415 => 'Unsupported Media Type',
  1743. 416 => 'Requested Range Not Satisfiable',
  1744. 417 => 'Expectation Failed',
  1745. 422 => 'Unprocessable Entity',
  1746. 423 => 'Locked',
  1747. 424 => 'Failed Dependency',
  1748. 426 => 'Upgrade Required',
  1749. 500 => 'Internal Server Error',
  1750. 501 => 'Not Implemented',
  1751. 502 => 'Bad Gateway',
  1752. 503 => 'Service Unavailable',
  1753. 504 => 'Gateway Timeout',
  1754. 505 => 'HTTP Version Not Supported',
  1755. 506 => 'Variant Also Negotiates',
  1756. 507 => 'Insufficient Storage',
  1757. 510 => 'Not Extended'
  1758. );
  1759. if(is_null($num)) return $status;
  1760. return array_key_exists($num, $status) ? $status[$num] : '';
  1761. }
  1762. /**
  1763. * Checks if an HTTP response code is valid
  1764. *
  1765. * @param string $num
  1766. * @return bool
  1767. */
  1768. function http_response_status_is_valid($num)
  1769. {
  1770. $r = http_response_status($num);
  1771. return !empty($r);
  1772. }
  1773. /**
  1774. * Returns an HTTP response status string for a given code
  1775. *
  1776. * @param string $num
  1777. * @return string
  1778. */
  1779. function http_response_status_code($num)
  1780. {
  1781. if($str = http_response_status($num)) return "HTTP/1.1 $num $str";
  1782. }
  1783. ## FILE utils _________________________________________________________________
  1784. /**
  1785. * Returns mime type for a given extension or if no extension is provided,
  1786. * all mime types in an associative array, with extensions as keys.
  1787. * (extracted from Orbit source http://orbit.luaforge.net/)
  1788. *
  1789. * @param string $ext
  1790. * @return string, array
  1791. */
  1792. function mime_type($ext = null)
  1793. {
  1794. $types = array(
  1795. 'ai' => 'application/postscript',
  1796. 'aif' => 'audio/x-aiff',
  1797. 'aifc' => 'audio/x-aiff',
  1798. 'aiff' => 'audio/x-aiff',
  1799. 'asc' => 'text/plain',
  1800. 'atom' => 'application/atom+xml',
  1801. 'atom' => 'application/atom+xml',
  1802. 'au' => 'audio/basic',
  1803. 'avi' => 'video/x-msvideo',
  1804. 'bcpio' => 'application/x-bcpio',
  1805. 'bin' => 'application/octet-stream',
  1806. 'bmp' => 'image/bmp',
  1807. 'cdf' => 'application/x-netcdf',
  1808. 'cgm' => 'image/cgm',
  1809. 'class' => 'application/octet-stream',
  1810. 'cpio' => 'application/x-cpio',
  1811. 'cpt' => 'application/mac-compactpro',
  1812. 'csh' => 'application/x-csh',
  1813. 'css' => 'text/css',
  1814. 'csv' => 'text/csv',
  1815. 'dcr' => 'application/x-director',
  1816. 'dir' => 'application/x-director',
  1817. 'djv' => 'image/vnd.djvu',
  1818. 'djvu' => 'image/vnd.djvu',
  1819. 'dll' => 'application/octet-stream',
  1820. 'dmg' => 'application/octet-stream',
  1821. 'dms' => 'application/octet-stream',
  1822. 'doc' => 'application/msword',
  1823. 'dtd' => 'application/xml-dtd',
  1824. 'dvi' => 'application/x-dvi',
  1825. 'dxr' => 'application/x-director',
  1826. 'eps' => 'application/postscript',
  1827. 'etx' => 'text/x-setext',
  1828. 'exe' => 'application/octet-stream',
  1829. 'ez' => 'application/andrew-inset',
  1830. 'gif' => 'image/gif',
  1831. 'gram' => 'application/srgs',
  1832. 'grxml' => 'application/srgs+xml',
  1833. 'gtar' => 'application/x-gtar',
  1834. 'hdf' => 'application/x-hdf',
  1835. 'hqx' => 'application/mac-binhex40',
  1836. 'htm' => 'text/html',
  1837. 'html' => 'text/html',
  1838. 'ice' => 'x-conference/x-cooltalk',
  1839. 'ico' => 'image/x-icon',
  1840. 'ics' => 'text/calendar',
  1841. 'ief' => 'image/ief',
  1842. 'ifb' => 'text/calendar',
  1843. 'iges' => 'model/iges',
  1844. 'igs' => 'model/iges',
  1845. 'jpe' => 'image/jpeg',
  1846. 'jpeg' => 'image/jpeg',
  1847. 'jpg' => 'image/jpeg',
  1848. 'js' => 'application/x-javascript',
  1849. 'kar' => 'audio/midi',
  1850. 'latex' => 'application/x-latex',
  1851. 'lha' => 'application/octet-stream',
  1852. 'lzh' => 'application/octet-stream',
  1853. 'm3u' => 'audio/x-mpegurl',
  1854. 'man' => 'application/x-troff-man',
  1855. 'mathml' => 'application/mathml+xml',
  1856. 'me' => 'application/x-troff-me',
  1857. 'mesh' => 'model/mesh',
  1858. 'mid' => 'audio/midi',
  1859. 'midi' => 'audio/midi',
  1860. 'mif' => 'application/vnd.mif',
  1861. 'mov' => 'video/quicktime',
  1862. 'movie' => 'video/x-sgi-movie',
  1863. 'mp2' => 'audio/mpeg',
  1864. 'mp3' => 'audio/mpeg',
  1865. 'mpe' => 'video/mpeg',
  1866. 'mpeg' => 'video/mpeg',
  1867. 'mpg' => 'video/mpeg',
  1868. 'mpga' => 'audio/mpeg',
  1869. 'ms' => 'application/x-troff-ms',
  1870. 'msh' => 'model/mesh',
  1871. 'mxu' => 'video/vnd.mpegurl',
  1872. 'nc' => 'application/x-netcdf',
  1873. 'oda' => 'application/oda',
  1874. 'ogg' => 'application/ogg',
  1875. 'pbm' => 'image/x-portable-bitmap',
  1876. 'pdb' => 'chemical/x-pdb',
  1877. 'pdf' => 'application/pdf',
  1878. 'pgm' => 'image/x-portable-graymap',
  1879. 'pgn' => 'application/x-chess-pgn',
  1880. 'png' => 'image/png',
  1881. 'pnm' => 'image/x-portable-anymap',
  1882. 'ppm' => 'image/x-portable-pixmap',
  1883. 'ppt' => 'application/vnd.ms-powerpoint',
  1884. 'ps' => 'application/postscript',
  1885. 'qt' => 'video/quicktime',
  1886. 'ra' => 'audio/x-pn-realaudio',
  1887. 'ram' => 'audio/x-pn-realaudio',
  1888. 'ras' => 'image/x-cmu-raster',
  1889. 'rdf' => 'application/rdf+xml',
  1890. 'rgb' => 'image/x-rgb',
  1891. 'rm' => 'application/vnd.rn-realmedia',
  1892. 'roff' => 'application/x-troff',
  1893. 'rss' => 'application/rss+xml',
  1894. 'rtf' => 'text/rtf',
  1895. 'rtx' => 'text/richtext',
  1896. 'sgm' => 'text/sgml',
  1897. 'sgml' => 'text/sgml',
  1898. 'sh' => 'application/x-sh',
  1899. 'shar' => 'application/x-shar',
  1900. 'silo' => 'model/mesh',
  1901. 'sit' => 'application/x-stuffit',
  1902. 'skd' => 'application/x-koan',
  1903. 'skm' => 'application/x-koan',
  1904. 'skp' => 'application/x-koan',
  1905. 'skt' => 'application/x-koan',
  1906. 'smi' => 'application/smil',
  1907. 'smil' => 'application/smil',
  1908. 'snd' => 'audio/basic',
  1909. 'so' => 'application/octet-stream',
  1910. 'spl' => 'application/x-futuresplash',
  1911. 'src' => 'application/x-wais-source',
  1912. 'sv4cpio' => 'application/x-sv4cpio',
  1913. 'sv4crc' => 'application/x-sv4crc',
  1914. 'svg' => 'image/svg+xml',
  1915. 'svgz' => 'image/svg+xml',
  1916. 'swf' => 'application/x-shockwave-flash',
  1917. 't' => 'application/x-troff',
  1918. 'tar' => 'application/x-tar',
  1919. 'tcl' => 'application/x-tcl',
  1920. 'tex' => 'application/x-tex',
  1921. 'texi' => 'application/x-texinfo',
  1922. 'texinfo' => 'application/x-texinfo',
  1923. 'tif' => 'image/tiff',
  1924. 'tiff' => 'image/tiff',
  1925. 'tr' => 'application/x-troff',
  1926. 'tsv' => 'text/tab-separated-values',
  1927. 'txt' => 'text/plain',
  1928. 'ustar' => 'application/x-ustar',
  1929. 'vcd' => 'application/x-cdlink',
  1930. 'vrml' => 'model/vrml',
  1931. 'vxml' => 'application/voicexml+xml',
  1932. 'wav' => 'audio/x-wav',
  1933. 'wbmp' => 'image/vnd.wap.wbmp',
  1934. 'wbxml' => 'application/vnd.wap.wbxml',
  1935. 'wml' => 'text/vnd.wap.wml',
  1936. 'wmlc' => 'application/vnd.wap.wmlc',
  1937. 'wmls' => 'text/vnd.wap.wmlscript',
  1938. 'wmlsc' => 'application/vnd.wap.wmlscriptc',
  1939. 'wrl' => 'model/vrml',
  1940. 'xbm' => 'image/x-xbitmap',
  1941. 'xht' => 'application/xhtml+xml',
  1942. 'xhtml' => 'application/xhtml+xml',
  1943. 'xls' => 'application/vnd.ms-excel',
  1944. 'xml' => 'application/xml',
  1945. 'xpm' => 'image/x-xpixmap',
  1946. 'xsl' => 'application/xml',
  1947. 'xslt' => 'application/xslt+xml',
  1948. 'xul' => 'application/vnd.mozilla.xul+xml',
  1949. 'xwd' => 'image/x-xwindowdump',
  1950. 'xyz' => 'chemical/x-xyz',
  1951. 'zip' => 'application/zip'
  1952. );
  1953. return is_null($ext) ? $types : $types[strtolower($ext)];
  1954. }
  1955. /**
  1956. * Detect MIME Content-type for a file
  1957. *
  1958. * @param string $filename Path to the tested file.
  1959. * @return string
  1960. */
  1961. function file_mime_content_type($filename)
  1962. {
  1963. $ext = file_extension($filename); /* strtolower isn't necessary */
  1964. if($mime = mime_type($ext)) return $mime;
  1965. elseif (function_exists('finfo_open'))
  1966. {
  1967. $finfo = finfo_open(FILEINFO_MIME);
  1968. $mime = finfo_file($finfo, $filename);
  1969. finfo_close($finfo);
  1970. return $mime;
  1971. }
  1972. else return 'application/octet-stream';
  1973. }
  1974. /**
  1975. * Read and output file content and return filesize in bytes or status after
  1976. * closing file.
  1977. * This function is very efficient for outputing large files without timeout
  1978. * nor too expensive memory use
  1979. *
  1980. * @param string $filename
  1981. * @param string $retbytes
  1982. * @return bool, int
  1983. */
  1984. function file_read_chunked($filename, $retbytes = true)
  1985. {
  1986. $chunksize = 1*(1024*1024); // how many bytes per chunk
  1987. $buffer = '';
  1988. $cnt = 0;
  1989. $handle = fopen($filename, 'rb');
  1990. if ($handle === false) return false;
  1991. ob_start();
  1992. while (!feof($handle)) {
  1993. $buffer = fread($handle, $chunksize);
  1994. echo $buffer;
  1995. ob_flush();
  1996. flush();
  1997. if ($retbytes) $cnt += strlen($buffer);
  1998. set_time_limit(0);
  1999. }
  2000. ob_end_flush();
  2001. $status = fclose($handle);
  2002. if ($retbytes && $status) return $cnt; // return num. bytes delivered like readfile() does.
  2003. return $status;
  2004. }
  2005. /**
  2006. * Create a file path by concatenation of given arguments.
  2007. * Windows paths with backslash directory separators are normalized in *nix paths.
  2008. *
  2009. * @param string $path, ...
  2010. * @return string normalized path
  2011. */
  2012. function file_path($path)
  2013. {
  2014. $args = func_get_args();
  2015. $ds = '/';
  2016. $win_ds = '\\';
  2017. $n_path = count($args) > 1 ? implode($ds, $args) : $path;
  2018. if(strpos($n_path, $win_ds) !== false) $n_path = str_replace( $win_ds, $ds, $n_path );
  2019. $n_path = preg_replace( '/'.preg_quote($ds, $ds).'{2,}'.'/',
  2020. $ds,
  2021. $n_path);
  2022. return $n_path;
  2023. }
  2024. /**
  2025. * Returns file extension or false if none
  2026. *
  2027. * @param string $filename
  2028. * @return string, false
  2029. */
  2030. function file_extension($filename)
  2031. {
  2032. $pos = strrpos($filename, '.');
  2033. if($pos !== false) return substr($filename, $pos + 1);
  2034. return false;
  2035. }
  2036. /**
  2037. * Checks if $filename is a text file
  2038. *
  2039. * @param string $filename
  2040. * @return bool
  2041. */
  2042. function file_is_text($filename)
  2043. {
  2044. if($mime = file_mime_content_type($filename)) return substr($mime,0,5) == "text/";
  2045. return null;
  2046. }
  2047. /**
  2048. * Checks if $filename is a binary file
  2049. *
  2050. * @param string $filename
  2051. * @return void
  2052. */
  2053. function file_is_binary($filename)
  2054. {
  2055. $is_text = file_is_text($filename);
  2056. return is_null($is_text) ? null : !$is_text;
  2057. }
  2058. /**
  2059. * Return or output file content
  2060. *
  2061. * @return string, int
  2062. *
  2063. **/
  2064. function file_read($filename, $return = false)
  2065. {
  2066. if(!file_exists($filename)) trigger_error("$filename doesn't exists", E_USER_ERROR);
  2067. if($return) return file_get_contents($filename);
  2068. return file_read_chunked($filename);
  2069. }
  2070. /**
  2071. * Returns an array of files contained in a directory
  2072. *
  2073. * @param string $dir
  2074. * @return array
  2075. */
  2076. function file_list_dir($dir)
  2077. {
  2078. $files = array();
  2079. if ($handle = opendir($dir))
  2080. {
  2081. while (false !== ($file = readdir($handle)))
  2082. {
  2083. if ($file[0] != "." && $file != "..") $files[] = $file;
  2084. }
  2085. closedir($handle);
  2086. }
  2087. return $files;
  2088. }
  2089. ## Extra utils ________________________________________________________________
  2090. if(!function_exists('array_replace'))
  2091. {
  2092. /**
  2093. * For PHP 5 < 5.3.0 (backward compatibility)
  2094. * (from {@link http://www.php.net/manual/fr/function.array-replace.php#92549 this php doc. note})
  2095. *
  2096. * @see array_replace()
  2097. * @param string $array
  2098. * @param string $array1
  2099. * @return $array
  2100. */
  2101. function array_replace( array &$array, array &$array1 )
  2102. {
  2103. $args = func_get_args();
  2104. $count = func_num_args();
  2105. for ($i = 0; $i < $count; ++$i)
  2106. {
  2107. if(is_array($args[$i]))
  2108. {
  2109. foreach ($args[$i] as $key => $val) $array[$key] = $val;
  2110. }
  2111. else
  2112. {
  2113. trigger_error(
  2114. __FUNCTION__ . '(): Argument #' . ($i+1) . ' is not an array',
  2115. E_USER_WARNING
  2116. );
  2117. return null;
  2118. }
  2119. }
  2120. return $array;
  2121. }
  2122. }
  2123. # ================================= END ================================== #