PageRenderTime 45ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/kohana/core.php

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