PageRenderTime 61ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/kohana/core.php

https://bitbucket.org/sudak/rating
PHP | 1001 lines | 582 code | 100 blank | 319 comment | 39 complexity | 875b1f0dc5dfe4fd3a1ba4e05bfa50aa MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Contains the most low-level helpers methods in Kohana:
  4. *
  5. * - Environment initialization
  6. * - Locating files within the cascading filesystem
  7. * - Auto-loading and transparent extension of classes
  8. * - Variable and path debugging
  9. *
  10. * @package Kohana
  11. * @category Base
  12. * @author Kohana Team
  13. * @copyright (c) 2008-2011 Kohana Team
  14. * @license http://kohanaframework.org/license
  15. */
  16. class Kohana_Core {
  17. // Release version and codename
  18. const VERSION = '3.2.0';
  19. const CODENAME = 'Kolibri';
  20. // Common environment type constants for consistency and convenience
  21. const PRODUCTION = 10;
  22. const STAGING = 20;
  23. const TESTING = 30;
  24. const DEVELOPMENT = 40;
  25. // Security check that is added to all generated PHP files
  26. const FILE_SECURITY = '<?php defined(\'SYSPATH\') or die(\'No direct script access.\');';
  27. // Format of cache files: header, cache name, and data
  28. const FILE_CACHE = ":header \n\n// :name\n\n:data\n";
  29. /**
  30. * @var string Current environment name
  31. */
  32. public static $environment = Kohana::DEVELOPMENT;
  33. /**
  34. * @var boolean True if Kohana is running from the command line
  35. */
  36. public static $is_cli = FALSE;
  37. /**
  38. * @var boolean True if Kohana is running on windows
  39. */
  40. public static $is_windows = FALSE;
  41. /**
  42. * @var boolean True if [magic quotes](http://php.net/manual/en/security.magicquotes.php) is enabled.
  43. */
  44. public static $magic_quotes = FALSE;
  45. /**
  46. * @var boolean Should errors and exceptions be logged
  47. */
  48. public static $log_errors = FALSE;
  49. /**
  50. * @var boolean TRUE if PHP safe mode is on
  51. */
  52. public static $safe_mode = FALSE;
  53. /**
  54. * @var string
  55. */
  56. public static $content_type = 'text/html';
  57. /**
  58. * @var string character set of input and output
  59. */
  60. public static $charset = 'utf-8';
  61. /**
  62. * @var string the name of the server Kohana is hosted upon
  63. */
  64. public static $server_name = '';
  65. /**
  66. * @var array list of valid host names for this instance
  67. */
  68. public static $hostnames = array();
  69. /**
  70. * @var string base URL to the application
  71. */
  72. public static $base_url = '/';
  73. /**
  74. * @var string Application index file, added to links generated by Kohana. Set by [Kohana::init]
  75. */
  76. public static $index_file = 'index.php';
  77. /**
  78. * @var string Cache directory, used by [Kohana::cache]. Set by [Kohana::init]
  79. */
  80. public static $cache_dir;
  81. /**
  82. * @var integer Default lifetime for caching, in seconds, used by [Kohana::cache]. Set by [Kohana::init]
  83. */
  84. public static $cache_life = 60;
  85. /**
  86. * @var boolean Whether to use internal caching for [Kohana::find_file], does not apply to [Kohana::cache]. Set by [Kohana::init]
  87. */
  88. public static $caching = FALSE;
  89. /**
  90. * @var boolean Whether to enable [profiling](kohana/profiling). Set by [Kohana::init]
  91. */
  92. public static $profiling = TRUE;
  93. /**
  94. * @var boolean Enable Kohana catching and displaying PHP errors and exceptions. Set by [Kohana::init]
  95. */
  96. public static $errors = TRUE;
  97. /**
  98. * @var array Types of errors to display at shutdown
  99. */
  100. public static $shutdown_errors = array(E_PARSE, E_ERROR, E_USER_ERROR);
  101. /**
  102. * @var boolean set the X-Powered-By header
  103. */
  104. public static $expose = FALSE;
  105. /**
  106. * @var Log logging object
  107. */
  108. public static $log;
  109. /**
  110. * @var Config config object
  111. */
  112. public static $config;
  113. /**
  114. * @var boolean Has [Kohana::init] been called?
  115. */
  116. protected static $_init = FALSE;
  117. /**
  118. * @var array Currently active modules
  119. */
  120. protected static $_modules = array();
  121. /**
  122. * @var array Include paths that are used to find files
  123. */
  124. protected static $_paths = array(APPPATH, SYSPATH);
  125. /**
  126. * @var array File path cache, used when caching is true in [Kohana::init]
  127. */
  128. protected static $_files = array();
  129. /**
  130. * @var boolean Has the file path cache changed during this execution? Used internally when when caching is true in [Kohana::init]
  131. */
  132. protected static $_files_changed = FALSE;
  133. /**
  134. * Initializes the environment:
  135. *
  136. * - Disables register_globals and magic_quotes_gpc
  137. * - Determines the current environment
  138. * - Set global settings
  139. * - Sanitizes GET, POST, and COOKIE variables
  140. * - Converts GET, POST, and COOKIE variables to the global character set
  141. *
  142. * The following settings can be set:
  143. *
  144. * Type | Setting | Description | Default Value
  145. * ----------|------------|------------------------------------------------|---------------
  146. * `string` | base_url | The base URL for your application. This should be the *relative* path from your DOCROOT to your `index.php` file, in other words, if Kohana is in a subfolder, set this to the subfolder name, otherwise leave it as the default. **The leading slash is required**, trailing slash is optional. | `"/"`
  147. * `string` | index_file | The name of the [front controller](http://en.wikipedia.org/wiki/Front_Controller_pattern). This is used by Kohana to generate relative urls like [HTML::anchor()] and [URL::base()]. This is usually `index.php`. To [remove index.php from your urls](tutorials/clean-urls), set this to `FALSE`. | `"index.php"`
  148. * `string` | charset | Character set used for all input and output | `"utf-8"`
  149. * `string` | cache_dir | Kohana's cache directory. Used by [Kohana::cache] for simple internal caching, like [Fragments](kohana/fragments) and **\[caching database queries](this should link somewhere)**. This has nothing to do with the [Cache module](cache). | `APPPATH."cache"`
  150. * `integer` | cache_life | Lifetime, in seconds, of items cached by [Kohana::cache] | `60`
  151. * `boolean` | errors | Should Kohana catch PHP errors and uncaught Exceptions and show the `error_view`. See [Error Handling](kohana/errors) for more info. <br /> <br /> Recommended setting: `TRUE` while developing, `FALSE` on production servers. | `TRUE`
  152. * `boolean` | profile | Whether to enable the [Profiler](kohana/profiling). <br /> <br />Recommended setting: `TRUE` while developing, `FALSE` on production servers. | `TRUE` * `boolean` | caching | Cache file locations to speed up [Kohana::find_file]. This has nothing to do with [Kohana::cache], [Fragments](kohana/fragments) or the [Cache module](cache). <br /> <br /> Recommended setting: `FALSE` while developing, `TRUE` on production servers. | `FALSE`
  153. *
  154. * @throws Kohana_Exception
  155. * @param array Array of settings. See above.
  156. * @return void
  157. * @uses Kohana::globals
  158. * @uses Kohana::sanitize
  159. * @uses Kohana::cache
  160. * @uses Profiler
  161. */
  162. public static function init(array $settings = NULL)
  163. {
  164. if (Kohana::$_init)
  165. {
  166. // Do not allow execution twice
  167. return;
  168. }
  169. // Kohana is now initialized
  170. Kohana::$_init = TRUE;
  171. if (isset($settings['profile']))
  172. {
  173. // Enable profiling
  174. Kohana::$profiling = (bool) $settings['profile'];
  175. }
  176. // Start an output buffer
  177. ob_start();
  178. if (isset($settings['errors']))
  179. {
  180. // Enable error handling
  181. Kohana::$errors = (bool) $settings['errors'];
  182. }
  183. if (Kohana::$errors === TRUE)
  184. {
  185. // Enable Kohana exception handling, adds stack traces and error source.
  186. set_exception_handler(array('Kohana_Exception', 'handler'));
  187. // Enable Kohana error handling, converts all PHP errors to exceptions.
  188. set_error_handler(array('Kohana', 'error_handler'));
  189. }
  190. // Enable the Kohana shutdown handler, which catches E_FATAL errors.
  191. register_shutdown_function(array('Kohana', 'shutdown_handler'));
  192. if (ini_get('register_globals'))
  193. {
  194. // Reverse the effects of register_globals
  195. Kohana::globals();
  196. }
  197. if (isset($settings['expose']))
  198. {
  199. Kohana::$expose = (bool) $settings['expose'];
  200. }
  201. // Determine if we are running in a command line environment
  202. Kohana::$is_cli = (PHP_SAPI === 'cli');
  203. // Determine if we are running in a Windows environment
  204. Kohana::$is_windows = (DIRECTORY_SEPARATOR === '\\');
  205. // Determine if we are running in safe mode
  206. Kohana::$safe_mode = (bool) ini_get('safe_mode');
  207. if (isset($settings['cache_dir']))
  208. {
  209. if ( ! is_dir($settings['cache_dir']))
  210. {
  211. try
  212. {
  213. // Create the cache directory
  214. mkdir($settings['cache_dir'], 0755, TRUE);
  215. // Set permissions (must be manually set to fix umask issues)
  216. chmod($settings['cache_dir'], 0755);
  217. }
  218. catch (Exception $e)
  219. {
  220. throw new Kohana_Exception('Could not create cache directory :dir',
  221. array(':dir' => Debug::path($settings['cache_dir'])));
  222. }
  223. }
  224. // Set the cache directory path
  225. Kohana::$cache_dir = realpath($settings['cache_dir']);
  226. }
  227. else
  228. {
  229. // Use the default cache directory
  230. Kohana::$cache_dir = APPPATH.'cache';
  231. }
  232. if ( ! is_writable(Kohana::$cache_dir))
  233. {
  234. throw new Kohana_Exception('Directory :dir must be writable',
  235. array(':dir' => Debug::path(Kohana::$cache_dir)));
  236. }
  237. if (isset($settings['cache_life']))
  238. {
  239. // Set the default cache lifetime
  240. Kohana::$cache_life = (int) $settings['cache_life'];
  241. }
  242. if (isset($settings['caching']))
  243. {
  244. // Enable or disable internal caching
  245. Kohana::$caching = (bool) $settings['caching'];
  246. }
  247. if (Kohana::$caching === TRUE)
  248. {
  249. // Load the file path cache
  250. Kohana::$_files = Kohana::cache('Kohana::find_file()');
  251. }
  252. if (isset($settings['charset']))
  253. {
  254. // Set the system character set
  255. Kohana::$charset = strtolower($settings['charset']);
  256. }
  257. if (function_exists('mb_internal_encoding'))
  258. {
  259. // Set the MB extension encoding to the same character set
  260. mb_internal_encoding(Kohana::$charset);
  261. }
  262. if (isset($settings['base_url']))
  263. {
  264. // Set the base URL
  265. Kohana::$base_url = rtrim($settings['base_url'], '/').'/';
  266. }
  267. if (isset($settings['index_file']))
  268. {
  269. // Set the index file
  270. Kohana::$index_file = trim($settings['index_file'], '/');
  271. }
  272. // Determine if the extremely evil magic quotes are enabled
  273. Kohana::$magic_quotes = (bool) get_magic_quotes_gpc();
  274. // Sanitize all request variables
  275. $_GET = Kohana::sanitize($_GET);
  276. $_POST = Kohana::sanitize($_POST);
  277. $_COOKIE = Kohana::sanitize($_COOKIE);
  278. // Load the logger
  279. Kohana::$log = Log::instance();
  280. // Load the config
  281. Kohana::$config = new Kohana_Config;
  282. }
  283. /**
  284. * Cleans up the environment:
  285. *
  286. * - Restore the previous error and exception handlers
  287. * - Destroy the Kohana::$log and Kohana::$config objects
  288. *
  289. * @return void
  290. */
  291. public static function deinit()
  292. {
  293. if (Kohana::$_init)
  294. {
  295. // Removed the autoloader
  296. spl_autoload_unregister(array('Kohana', 'auto_load'));
  297. if (Kohana::$errors)
  298. {
  299. // Go back to the previous error handler
  300. restore_error_handler();
  301. // Go back to the previous exception handler
  302. restore_exception_handler();
  303. }
  304. // Destroy objects created by init
  305. Kohana::$log = Kohana::$config = NULL;
  306. // Reset internal storage
  307. Kohana::$_modules = Kohana::$_files = array();
  308. Kohana::$_paths = array(APPPATH, SYSPATH);
  309. // Reset file cache status
  310. Kohana::$_files_changed = FALSE;
  311. // Kohana is no longer initialized
  312. Kohana::$_init = FALSE;
  313. }
  314. }
  315. /**
  316. * Reverts the effects of the `register_globals` PHP setting by unsetting
  317. * all global varibles except for the default super globals (GPCS, etc),
  318. * which is a [potential security hole.][ref-wikibooks]
  319. *
  320. * This is called automatically by [Kohana::init] if `register_globals` is
  321. * on.
  322. *
  323. *
  324. * [ref-wikibooks]: http://en.wikibooks.org/wiki/PHP_Programming/Register_Globals
  325. *
  326. * @return void
  327. */
  328. public static function globals()
  329. {
  330. if (isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS']))
  331. {
  332. // Prevent malicious GLOBALS overload attack
  333. echo "Global variable overload attack detected! Request aborted.\n";
  334. // Exit with an error status
  335. exit(1);
  336. }
  337. // Get the variable names of all globals
  338. $global_variables = array_keys($GLOBALS);
  339. // Remove the standard global variables from the list
  340. $global_variables = array_diff($global_variables, array(
  341. '_COOKIE',
  342. '_ENV',
  343. '_GET',
  344. '_FILES',
  345. '_POST',
  346. '_REQUEST',
  347. '_SERVER',
  348. '_SESSION',
  349. 'GLOBALS',
  350. ));
  351. foreach ($global_variables as $name)
  352. {
  353. // Unset the global variable, effectively disabling register_globals
  354. unset($GLOBALS[$name]);
  355. }
  356. }
  357. /**
  358. * Recursively sanitizes an input variable:
  359. *
  360. * - Strips slashes if magic quotes are enabled
  361. * - Normalizes all newlines to LF
  362. *
  363. * @param mixed any variable
  364. * @return mixed sanitized variable
  365. */
  366. public static function sanitize($value)
  367. {
  368. if (is_array($value) OR is_object($value))
  369. {
  370. foreach ($value as $key => $val)
  371. {
  372. // Recursively clean each value
  373. $value[$key] = Kohana::sanitize($val);
  374. }
  375. }
  376. elseif (is_string($value))
  377. {
  378. if (Kohana::$magic_quotes === TRUE)
  379. {
  380. // Remove slashes added by magic quotes
  381. $value = stripslashes($value);
  382. }
  383. if (strpos($value, "\r") !== FALSE)
  384. {
  385. // Standardize newlines
  386. $value = str_replace(array("\r\n", "\r"), "\n", $value);
  387. }
  388. }
  389. return $value;
  390. }
  391. /**
  392. * Provides auto-loading support of classes that follow Kohana's [class
  393. * naming conventions](kohana/conventions#class-names-and-file-location).
  394. * See [Loading Classes](kohana/autoloading) for more information.
  395. *
  396. * Class names are converted to file names by making the class name
  397. * lowercase and converting underscores to slashes:
  398. *
  399. * // Loads classes/my/class/name.php
  400. * Kohana::auto_load('My_Class_Name');
  401. *
  402. * You should never have to call this function, as simply calling a class
  403. * will cause it to be called.
  404. *
  405. * This function must be enabled as an autoloader in the bootstrap:
  406. *
  407. * spl_autoload_register(array('Kohana', 'auto_load'));
  408. *
  409. * @param string class name
  410. * @return boolean
  411. */
  412. public static function auto_load($class)
  413. {
  414. try
  415. {
  416. // Transform the class name into a path
  417. $file = str_replace('_', '/', strtolower($class));
  418. if ($path = Kohana::find_file('classes', $file))
  419. {
  420. // Load the class file
  421. require $path;
  422. // Class has been found
  423. return TRUE;
  424. }
  425. // Class is not in the filesystem
  426. return FALSE;
  427. }
  428. catch (Exception $e)
  429. {
  430. Kohana_Exception::handler($e);
  431. die;
  432. }
  433. }
  434. /**
  435. * Changes the currently enabled modules. Module paths may be relative
  436. * or absolute, but must point to a directory:
  437. *
  438. * Kohana::modules(array('modules/foo', MODPATH.'bar'));
  439. *
  440. * @param array list of module paths
  441. * @return array enabled modules
  442. */
  443. public static function modules(array $modules = NULL)
  444. {
  445. if ($modules === NULL)
  446. {
  447. // Not changing modules, just return the current set
  448. return Kohana::$_modules;
  449. }
  450. // Start a new list of include paths, APPPATH first
  451. $paths = array(APPPATH);
  452. foreach ($modules as $name => $path)
  453. {
  454. if (is_dir($path))
  455. {
  456. // Add the module to include paths
  457. $paths[] = $modules[$name] = realpath($path).DIRECTORY_SEPARATOR;
  458. }
  459. else
  460. {
  461. // This module is invalid, remove it
  462. throw new Kohana_Exception('Attempted to load an invalid or missing module \':module\' at \':path\'', array(
  463. ':module' => $name,
  464. ':path' => Debug::path($path),
  465. ));
  466. }
  467. }
  468. // Finish the include paths by adding SYSPATH
  469. $paths[] = SYSPATH;
  470. // Set the new include paths
  471. Kohana::$_paths = $paths;
  472. // Set the current module list
  473. Kohana::$_modules = $modules;
  474. foreach (Kohana::$_modules as $path)
  475. {
  476. $init = $path.'init'.EXT;
  477. if (is_file($init))
  478. {
  479. // Include the module initialization file once
  480. require_once $init;
  481. }
  482. }
  483. return Kohana::$_modules;
  484. }
  485. /**
  486. * Returns the the currently active include paths, including the
  487. * application, system, and each module's path.
  488. *
  489. * @return array
  490. */
  491. public static function include_paths()
  492. {
  493. return Kohana::$_paths;
  494. }
  495. /**
  496. * Searches for a file in the [Cascading Filesystem](kohana/files), and
  497. * returns the path to the file that has the highest precedence, so that it
  498. * can be included.
  499. *
  500. * When searching the "config", "messages", or "i18n" directories, or when
  501. * the `$array` flag is set to true, an array of all the files that match
  502. * that path in the [Cascading Filesystem](kohana/files) will be returned.
  503. * These files will return arrays which must be merged together.
  504. *
  505. * If no extension is given, the default extension (`EXT` set in
  506. * `index.php`) will be used.
  507. *
  508. * // Returns an absolute path to views/template.php
  509. * Kohana::find_file('views', 'template');
  510. *
  511. * // Returns an absolute path to media/css/style.css
  512. * Kohana::find_file('media', 'css/style', 'css');
  513. *
  514. * // Returns an array of all the "mimes" configuration files
  515. * Kohana::find_file('config', 'mimes');
  516. *
  517. * @param string directory name (views, i18n, classes, extensions, etc.)
  518. * @param string filename with subdirectory
  519. * @param string extension to search for
  520. * @param boolean return an array of files?
  521. * @return array a list of files when $array is TRUE
  522. * @return string single file path
  523. */
  524. public static function find_file($dir, $file, $ext = NULL, $array = FALSE)
  525. {
  526. if ($ext === NULL)
  527. {
  528. // Use the default extension
  529. $ext = EXT;
  530. }
  531. elseif ($ext)
  532. {
  533. // Prefix the extension with a period
  534. $ext = ".{$ext}";
  535. }
  536. else
  537. {
  538. // Use no extension
  539. $ext = '';
  540. }
  541. // Create a partial path of the filename
  542. $path = $dir.DIRECTORY_SEPARATOR.$file.$ext;
  543. if (Kohana::$caching === TRUE AND isset(Kohana::$_files[$path.($array ? '_array' : '_path')]))
  544. {
  545. // This path has been cached
  546. return Kohana::$_files[$path.($array ? '_array' : '_path')];
  547. }
  548. if (Kohana::$profiling === TRUE AND class_exists('Profiler', FALSE))
  549. {
  550. // Start a new benchmark
  551. $benchmark = Profiler::start('Kohana', __FUNCTION__);
  552. }
  553. if ($array OR $dir === 'config' OR $dir === 'i18n' OR $dir === 'messages')
  554. {
  555. // Include paths must be searched in reverse
  556. $paths = array_reverse(Kohana::$_paths);
  557. // Array of files that have been found
  558. $found = array();
  559. foreach ($paths as $dir)
  560. {
  561. if (is_file($dir.$path))
  562. {
  563. // This path has a file, add it to the list
  564. $found[] = $dir.$path;
  565. }
  566. }
  567. }
  568. else
  569. {
  570. // The file has not been found yet
  571. $found = FALSE;
  572. foreach (Kohana::$_paths as $dir)
  573. {
  574. if (is_file($dir.$path))
  575. {
  576. // A path has been found
  577. $found = $dir.$path;
  578. // Stop searching
  579. break;
  580. }
  581. }
  582. }
  583. if (Kohana::$caching === TRUE)
  584. {
  585. // Add the path to the cache
  586. Kohana::$_files[$path.($array ? '_array' : '_path')] = $found;
  587. // Files have been changed
  588. Kohana::$_files_changed = TRUE;
  589. }
  590. if (isset($benchmark))
  591. {
  592. // Stop the benchmark
  593. Profiler::stop($benchmark);
  594. }
  595. return $found;
  596. }
  597. /**
  598. * Recursively finds all of the files in the specified directory at any
  599. * location in the [Cascading Filesystem](kohana/files), and returns an
  600. * array of all the files found, sorted alphabetically.
  601. *
  602. * // Find all view files.
  603. * $views = Kohana::list_files('views');
  604. *
  605. * @param string directory name
  606. * @param array list of paths to search
  607. * @return array
  608. */
  609. public static function list_files($directory = NULL, array $paths = NULL)
  610. {
  611. if ($directory !== NULL)
  612. {
  613. // Add the directory separator
  614. $directory .= DIRECTORY_SEPARATOR;
  615. }
  616. if ($paths === NULL)
  617. {
  618. // Use the default paths
  619. $paths = Kohana::$_paths;
  620. }
  621. // Create an array for the files
  622. $found = array();
  623. foreach ($paths as $path)
  624. {
  625. if (is_dir($path.$directory))
  626. {
  627. // Create a new directory iterator
  628. $dir = new DirectoryIterator($path.$directory);
  629. foreach ($dir as $file)
  630. {
  631. // Get the file name
  632. $filename = $file->getFilename();
  633. if ($filename[0] === '.' OR $filename[strlen($filename)-1] === '~')
  634. {
  635. // Skip all hidden files and UNIX backup files
  636. continue;
  637. }
  638. // Relative filename is the array key
  639. $key = $directory.$filename;
  640. if ($file->isDir())
  641. {
  642. if ($sub_dir = Kohana::list_files($key, $paths))
  643. {
  644. if (isset($found[$key]))
  645. {
  646. // Append the sub-directory list
  647. $found[$key] += $sub_dir;
  648. }
  649. else
  650. {
  651. // Create a new sub-directory list
  652. $found[$key] = $sub_dir;
  653. }
  654. }
  655. }
  656. else
  657. {
  658. if ( ! isset($found[$key]))
  659. {
  660. // Add new files to the list
  661. $found[$key] = realpath($file->getPathName());
  662. }
  663. }
  664. }
  665. }
  666. }
  667. // Sort the results alphabetically
  668. ksort($found);
  669. return $found;
  670. }
  671. /**
  672. * Loads a file within a totally empty scope and returns the output:
  673. *
  674. * $foo = Kohana::load('foo.php');
  675. *
  676. * @param string
  677. * @return mixed
  678. */
  679. public static function load($file)
  680. {
  681. return include $file;
  682. }
  683. /**
  684. * Provides simple file-based caching for strings and arrays:
  685. *
  686. * // Set the "foo" cache
  687. * Kohana::cache('foo', 'hello, world');
  688. *
  689. * // Get the "foo" cache
  690. * $foo = Kohana::cache('foo');
  691. *
  692. * All caches are stored as PHP code, generated with [var_export][ref-var].
  693. * Caching objects may not work as expected. Storing references or an
  694. * object or array that has recursion will cause an E_FATAL.
  695. *
  696. * The cache directory and default cache lifetime is set by [Kohana::init]
  697. *
  698. * [ref-var]: http://php.net/var_export
  699. *
  700. * @throws Kohana_Exception
  701. * @param string name of the cache
  702. * @param mixed data to cache
  703. * @param integer number of seconds the cache is valid for
  704. * @return mixed for getting
  705. * @return boolean for setting
  706. */
  707. public static function cache($name, $data = NULL, $lifetime = NULL)
  708. {
  709. // Cache file is a hash of the name
  710. $file = sha1($name).'.txt';
  711. // Cache directories are split by keys to prevent filesystem overload
  712. $dir = Kohana::$cache_dir.DIRECTORY_SEPARATOR.$file[0].$file[1].DIRECTORY_SEPARATOR;
  713. if ($lifetime === NULL)
  714. {
  715. // Use the default lifetime
  716. $lifetime = Kohana::$cache_life;
  717. }
  718. if ($data === NULL)
  719. {
  720. if (is_file($dir.$file))
  721. {
  722. if ((time() - filemtime($dir.$file)) < $lifetime)
  723. {
  724. // Return the cache
  725. try
  726. {
  727. return unserialize(file_get_contents($dir.$file));
  728. }
  729. catch (Exception $e)
  730. {
  731. // Cache is corrupt, let return happen normally.
  732. }
  733. }
  734. else
  735. {
  736. try
  737. {
  738. // Cache has expired
  739. unlink($dir.$file);
  740. }
  741. catch (Exception $e)
  742. {
  743. // Cache has mostly likely already been deleted,
  744. // let return happen normally.
  745. }
  746. }
  747. }
  748. // Cache not found
  749. return NULL;
  750. }
  751. if ( ! is_dir($dir))
  752. {
  753. // Create the cache directory
  754. mkdir($dir, 0777, TRUE);
  755. // Set permissions (must be manually set to fix umask issues)
  756. chmod($dir, 0777);
  757. }
  758. // Force the data to be a string
  759. $data = serialize($data);
  760. try
  761. {
  762. // Write the cache
  763. return (bool) file_put_contents($dir.$file, $data, LOCK_EX);
  764. }
  765. catch (Exception $e)
  766. {
  767. // Failed to write cache
  768. return FALSE;
  769. }
  770. }
  771. /**
  772. * Get a message from a file. Messages are arbitary strings that are stored
  773. * in the `messages/` directory and reference by a key. Translation is not
  774. * performed on the returned values. See [message files](kohana/files/messages)
  775. * for more information.
  776. *
  777. * // Get "username" from messages/text.php
  778. * $username = Kohana::message('text', 'username');
  779. *
  780. * @param string file name
  781. * @param string key path to get
  782. * @param mixed default value if the path does not exist
  783. * @return string message string for the given path
  784. * @return array complete message list, when no path is specified
  785. * @uses Arr::merge
  786. * @uses Arr::path
  787. */
  788. public static function message($file, $path = NULL, $default = NULL)
  789. {
  790. static $messages;
  791. if ( ! isset($messages[$file]))
  792. {
  793. // Create a new message list
  794. $messages[$file] = array();
  795. if ($files = Kohana::find_file('messages', $file))
  796. {
  797. foreach ($files as $f)
  798. {
  799. // Combine all the messages recursively
  800. $messages[$file] = Arr::merge($messages[$file], Kohana::load($f));
  801. }
  802. }
  803. }
  804. if ($path === NULL)
  805. {
  806. // Return all of the messages
  807. return $messages[$file];
  808. }
  809. else
  810. {
  811. // Get a message using the path
  812. return Arr::path($messages[$file], $path, $default);
  813. }
  814. }
  815. /**
  816. * PHP error handler, converts all errors into ErrorExceptions. This handler
  817. * respects error_reporting settings.
  818. *
  819. * @throws ErrorException
  820. * @return TRUE
  821. */
  822. public static function error_handler($code, $error, $file = NULL, $line = NULL)
  823. {
  824. if (error_reporting() & $code)
  825. {
  826. // This error is not suppressed by current error reporting settings
  827. // Convert the error into an ErrorException
  828. throw new ErrorException($error, $code, 0, $file, $line);
  829. }
  830. // Do not execute the PHP error handler
  831. return TRUE;
  832. }
  833. /**
  834. * Catches errors that are not caught by the error handler, such as E_PARSE.
  835. *
  836. * @uses Kohana_Exception::handler
  837. * @return void
  838. */
  839. public static function shutdown_handler()
  840. {
  841. if ( ! Kohana::$_init)
  842. {
  843. // Do not execute when not active
  844. return;
  845. }
  846. try
  847. {
  848. if (Kohana::$caching === TRUE AND Kohana::$_files_changed === TRUE)
  849. {
  850. // Write the file path cache
  851. Kohana::cache('Kohana::find_file()', Kohana::$_files);
  852. }
  853. }
  854. catch (Exception $e)
  855. {
  856. // Pass the exception to the handler
  857. Kohana_Exception::handler($e);
  858. }
  859. if (Kohana::$errors AND $error = error_get_last() AND in_array($error['type'], Kohana::$shutdown_errors))
  860. {
  861. // Clean the output buffer
  862. ob_get_level() and ob_clean();
  863. // Fake an exception for nice debugging
  864. Kohana_Exception::handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
  865. // Shutdown now to avoid a "death loop"
  866. exit(1);
  867. }
  868. }
  869. } // End Kohana