PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/system/classes/Kohana/Core.php

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