PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/system/classes/kohana/core.php

https://bitbucket.org/emolina/asesores
PHP | 1637 lines | 980 code | 182 blank | 475 comment | 84 complexity | 9d376cd8981028e80f81010501611952 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-2010 Kohana Team
  14. * @license http://kohanaframework.org/license
  15. */
  16. class Kohana_Core {
  17. // Release version and codename
  18. const VERSION = '3.0.10';
  19. const CODENAME = 'Hierofalco';
  20. // Log message types
  21. const ERROR = 'ERROR';
  22. const DEBUG = 'DEBUG';
  23. const INFO = 'INFO';
  24. const CRITICAL = 'CRITICAL';
  25. const STRACE = 'STRACE';
  26. const ALERT = 'ALERT';
  27. // Common environment type constants for consistency and convenience
  28. const PRODUCTION = 'production';
  29. const STAGING = 'staging';
  30. const TESTING = 'testing';
  31. const DEVELOPMENT = 'development';
  32. // Security check that is added to all generated PHP files
  33. const FILE_SECURITY = '<?php defined(\'SYSPATH\') or die(\'No direct script access.\');';
  34. // Format of cache files: header, cache name, and data
  35. const FILE_CACHE = ":header \n\n// :name\n\n:data\n";
  36. /**
  37. * @var array Codes to turn PHP error codes into readable names.
  38. */
  39. public static $php_errors = array(
  40. E_ERROR => 'Fatal Error',
  41. E_USER_ERROR => 'User Error',
  42. E_PARSE => 'Parse Error',
  43. E_WARNING => 'Warning',
  44. E_USER_WARNING => 'User Warning',
  45. E_STRICT => 'Strict',
  46. E_NOTICE => 'Notice',
  47. E_RECOVERABLE_ERROR => 'Recoverable Error',
  48. );
  49. /**
  50. * @var string Current environment name
  51. */
  52. public static $environment = Kohana::DEVELOPMENT;
  53. /**
  54. * @var boolean True if Kohana is running from the command line
  55. */
  56. public static $is_cli = FALSE;
  57. /**
  58. * @var boolean True if Kohana is running on windows
  59. */
  60. public static $is_windows = FALSE;
  61. /**
  62. * @var boolean True if [magic quotes](http://php.net/manual/en/security.magicquotes.php) is enabled.
  63. */
  64. public static $magic_quotes = FALSE;
  65. /**
  66. * @var boolean Should errors and exceptions be logged
  67. */
  68. public static $log_errors = FALSE;
  69. /**
  70. * @var boolean TRUE if PHP safe mode is on
  71. */
  72. public static $safe_mode = FALSE;
  73. /**
  74. * @var string Character set of input and output. Set by [Kohana::init]
  75. */
  76. public static $charset = 'utf-8';
  77. /**
  78. * @var string Base URL to the application. Set by [Kohana::init]
  79. */
  80. public static $base_url = '/';
  81. /**
  82. * @var string Application index file, added to links generated by Kohana. Set by [Kohana::init]
  83. */
  84. public static $index_file = 'index.php';
  85. /**
  86. * @var string Cache directory, used by [Kohana::cache]. Set by [Kohana::init]
  87. */
  88. public static $cache_dir;
  89. /**
  90. * @var integer Default lifetime for caching, in seconds, used by [Kohana::cache]. Set by [Kohana::init]
  91. */
  92. public static $cache_life = 60;
  93. /**
  94. * @var boolean Whether to use internal caching for [Kohana::find_file], does not apply to [Kohana::cache]. Set by [Kohana::init]
  95. */
  96. public static $caching = FALSE;
  97. /**
  98. * @var boolean Whether to enable [profiling](kohana/profiling). Set by [Kohana::init]
  99. */
  100. public static $profiling = TRUE;
  101. /**
  102. * @var boolean Enable Kohana catching and displaying PHP errors and exceptions. Set by [Kohana::init]
  103. */
  104. public static $errors = TRUE;
  105. /**
  106. * @var string Error rendering view when Kohana catches PHP errors and exceptions. Set by [Kohana::init]
  107. */
  108. public static $error_view = 'kohana/error';
  109. /**
  110. * @var array Types of errors to display at shutdown
  111. */
  112. public static $shutdown_errors = array(E_PARSE, E_ERROR, E_USER_ERROR);
  113. /**
  114. * @var boolean escape quotes in Kohana::debug?
  115. */
  116. public static $debug_escape_quotes = FALSE;
  117. /**
  118. * @var Kohana_Log logging object
  119. */
  120. public static $log;
  121. /**
  122. * @var Kohana_Config config object
  123. */
  124. public static $config;
  125. /**
  126. * @var boolean Has [Kohana::init] been called?
  127. */
  128. protected static $_init = FALSE;
  129. /**
  130. * @var array Currently active modules
  131. */
  132. protected static $_modules = array();
  133. /**
  134. * @var array Include paths that are used to find files
  135. */
  136. protected static $_paths = array(APPPATH, SYSPATH);
  137. /**
  138. * @var array File path cache, used when caching is true in [Kohana::init]
  139. */
  140. protected static $_files = array();
  141. /**
  142. * @var boolean Has the file path cache changed during this execution? Used internally when when caching is true in [Kohana::init]
  143. */
  144. protected static $_files_changed = FALSE;
  145. /**
  146. * Initializes the environment:
  147. *
  148. * - Disables register_globals and magic_quotes_gpc
  149. * - Determines the current environment
  150. * - Set global settings
  151. * - Sanitizes GET, POST, and COOKIE variables
  152. * - Converts GET, POST, and COOKIE variables to the global character set
  153. *
  154. * The following settings can be set:
  155. *
  156. * Type | Setting | Description | Default Value
  157. * ----------|------------|------------------------------------------------|---------------
  158. * `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. | `"/"`
  159. * `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"`
  160. * `string` | charset | Character set used for all input and output | `"utf-8"`
  161. * `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"`
  162. * `integer` | cache_life | Lifetime, in seconds, of items cached by [Kohana::cache] | `60`
  163. * `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`
  164. * `string` | error_view | The view to use to display errors. Only used when `errors` is `TRUE`. | `"kohana/error"`
  165. * `boolean` | profile | Whether to enable the [Profiler](kohana/profiling). <br /> <br />Recommended setting: `TRUE` while developing, `FALSE` on production servers. | `TRUE`
  166. * `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`
  167. *
  168. * @throws Kohana_Exception
  169. * @param array Array of settings. See above.
  170. * @return void
  171. * @uses Kohana::globals
  172. * @uses Kohana::sanitize
  173. * @uses Kohana::cache
  174. * @uses Profiler
  175. */
  176. public static function init(array $settings = NULL)
  177. {
  178. if (Kohana::$_init)
  179. {
  180. // Do not allow execution twice
  181. return;
  182. }
  183. // Kohana is now initialized
  184. Kohana::$_init = TRUE;
  185. if (isset($settings['profile']))
  186. {
  187. // Enable profiling
  188. Kohana::$profiling = (bool) $settings['profile'];
  189. }
  190. // Start an output buffer
  191. ob_start();
  192. if (defined('E_DEPRECATED'))
  193. {
  194. // E_DEPRECATED only exists in PHP >= 5.3.0
  195. Kohana::$php_errors[E_DEPRECATED] = 'Deprecated';
  196. }
  197. if (isset($settings['errors']))
  198. {
  199. // Enable error handling
  200. Kohana::$errors = (bool) $settings['errors'];
  201. }
  202. if (Kohana::$errors === TRUE)
  203. {
  204. // Enable Kohana exception handling, adds stack traces and error source.
  205. set_exception_handler(array('Kohana', 'exception_handler'));
  206. // Enable Kohana error handling, converts all PHP errors to exceptions.
  207. set_error_handler(array('Kohana', 'error_handler'));
  208. }
  209. // Enable the Kohana shutdown handler, which catches E_FATAL errors.
  210. register_shutdown_function(array('Kohana', 'shutdown_handler'));
  211. if (isset($settings['error_view']))
  212. {
  213. if ( ! Kohana::find_file('views', $settings['error_view']))
  214. {
  215. throw new Kohana_Exception('Error view file does not exist: views/:file', array(
  216. ':file' => $settings['error_view'],
  217. ));
  218. }
  219. // Change the default error rendering
  220. Kohana::$error_view = (string) $settings['error_view'];
  221. }
  222. if (ini_get('register_globals'))
  223. {
  224. // Reverse the effects of register_globals
  225. Kohana::globals();
  226. }
  227. // Determine if we are running in a command line environment
  228. Kohana::$is_cli = (PHP_SAPI === 'cli');
  229. // Determine if we are running in a Windows environment
  230. Kohana::$is_windows = (DIRECTORY_SEPARATOR === '\\');
  231. // Determine if we are running in safe mode
  232. Kohana::$safe_mode = (bool) ini_get('safe_mode');
  233. if (isset($settings['cache_dir']))
  234. {
  235. if ( ! is_dir($settings['cache_dir']))
  236. {
  237. try
  238. {
  239. // Create the cache directory
  240. mkdir($settings['cache_dir'], 0755, TRUE);
  241. // Set permissions (must be manually set to fix umask issues)
  242. chmod($settings['cache_dir'], 0755);
  243. }
  244. catch (Exception $e)
  245. {
  246. throw new Kohana_Exception('Could not create cache directory :dir',
  247. array(':dir' => Kohana::debug_path($settings['cache_dir'])));
  248. }
  249. }
  250. // Set the cache directory path
  251. Kohana::$cache_dir = realpath($settings['cache_dir']);
  252. }
  253. else
  254. {
  255. // Use the default cache directory
  256. Kohana::$cache_dir = APPPATH.'cache';
  257. }
  258. if ( ! is_writable(Kohana::$cache_dir))
  259. {
  260. throw new Kohana_Exception('Directory :dir must be writable',
  261. array(':dir' => Kohana::debug_path(Kohana::$cache_dir)));
  262. }
  263. if (isset($settings['cache_life']))
  264. {
  265. // Set the default cache lifetime
  266. Kohana::$cache_life = (int) $settings['cache_life'];
  267. }
  268. if (isset($settings['caching']))
  269. {
  270. // Enable or disable internal caching
  271. Kohana::$caching = (bool) $settings['caching'];
  272. }
  273. if (Kohana::$caching === TRUE)
  274. {
  275. // Load the file path cache
  276. Kohana::$_files = Kohana::cache('Kohana::find_file()');
  277. }
  278. if (isset($settings['charset']))
  279. {
  280. // Set the system character set
  281. Kohana::$charset = strtolower($settings['charset']);
  282. }
  283. if (function_exists('mb_internal_encoding'))
  284. {
  285. // Set the MB extension encoding to the same character set
  286. mb_internal_encoding(Kohana::$charset);
  287. }
  288. if (isset($settings['base_url']))
  289. {
  290. // Set the base URL
  291. Kohana::$base_url = rtrim($settings['base_url'], '/').'/';
  292. }
  293. if (isset($settings['index_file']))
  294. {
  295. // Set the index file
  296. Kohana::$index_file = trim($settings['index_file'], '/');
  297. }
  298. // Determine if the extremely evil magic quotes are enabled
  299. Kohana::$magic_quotes = (bool) get_magic_quotes_gpc();
  300. // Sanitize all request variables
  301. $_GET = Kohana::sanitize($_GET);
  302. $_POST = Kohana::sanitize($_POST);
  303. $_COOKIE = Kohana::sanitize($_COOKIE);
  304. // Load the logger
  305. Kohana::$log = Kohana_Log::instance();
  306. // Load the config
  307. Kohana::$config = Kohana_Config::instance();
  308. }
  309. /**
  310. * Cleans up the environment:
  311. *
  312. * - Restore the previous error and exception handlers
  313. * - Destroy the Kohana::$log and Kohana::$config objects
  314. *
  315. * @return void
  316. */
  317. public static function deinit()
  318. {
  319. if (Kohana::$_init)
  320. {
  321. // Removed the autoloader
  322. spl_autoload_unregister(array('Kohana', 'auto_load'));
  323. if (Kohana::$errors)
  324. {
  325. // Go back to the previous error handler
  326. restore_error_handler();
  327. // Go back to the previous exception handler
  328. restore_exception_handler();
  329. }
  330. // Destroy objects created by init
  331. Kohana::$log = Kohana::$config = NULL;
  332. // Reset internal storage
  333. Kohana::$_modules = Kohana::$_files = array();
  334. Kohana::$_paths = array(APPPATH, SYSPATH);
  335. // Reset file cache status
  336. Kohana::$_files_changed = FALSE;
  337. // Kohana is no longer initialized
  338. Kohana::$_init = FALSE;
  339. }
  340. }
  341. /**
  342. * Reverts the effects of the `register_globals` PHP setting by unsetting
  343. * all global varibles except for the default super globals (GPCS, etc),
  344. * which is a [potential security hole.][ref-wikibooks]
  345. *
  346. * This is called automatically by [Kohana::init] if `register_globals` is
  347. * on.
  348. *
  349. *
  350. * [ref-wikibooks]: http://en.wikibooks.org/wiki/PHP_Programming/Register_Globals
  351. *
  352. * @return void
  353. */
  354. public static function globals()
  355. {
  356. if (isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS']))
  357. {
  358. // Prevent malicious GLOBALS overload attack
  359. echo "Global variable overload attack detected! Request aborted.\n";
  360. // Exit with an error status
  361. exit(1);
  362. }
  363. // Get the variable names of all globals
  364. $global_variables = array_keys($GLOBALS);
  365. // Remove the standard global variables from the list
  366. $global_variables = array_diff($global_variables, array(
  367. '_COOKIE',
  368. '_ENV',
  369. '_GET',
  370. '_FILES',
  371. '_POST',
  372. '_REQUEST',
  373. '_SERVER',
  374. '_SESSION',
  375. 'GLOBALS',
  376. ));
  377. foreach ($global_variables as $name)
  378. {
  379. // Unset the global variable, effectively disabling register_globals
  380. unset($GLOBALS[$name]);
  381. }
  382. }
  383. /**
  384. * Recursively sanitizes an input variable:
  385. *
  386. * - Strips slashes if magic quotes are enabled
  387. * - Normalizes all newlines to LF
  388. *
  389. * @param mixed any variable
  390. * @return mixed sanitized variable
  391. */
  392. public static function sanitize($value)
  393. {
  394. if (is_array($value) OR is_object($value))
  395. {
  396. foreach ($value as $key => $val)
  397. {
  398. // Recursively clean each value
  399. $value[$key] = Kohana::sanitize($val);
  400. }
  401. }
  402. elseif (is_string($value))
  403. {
  404. if (Kohana::$magic_quotes === TRUE)
  405. {
  406. // Remove slashes added by magic quotes
  407. $value = stripslashes($value);
  408. }
  409. if (strpos($value, "\r") !== FALSE)
  410. {
  411. // Standardize newlines
  412. $value = str_replace(array("\r\n", "\r"), "\n", $value);
  413. }
  414. }
  415. return $value;
  416. }
  417. /**
  418. * Provides auto-loading support of classes that follow Kohana's [class
  419. * naming conventions](kohana/conventions#class-names-and-file-location).
  420. * See [Loading Classes](kohana/autoloading) for more information.
  421. *
  422. * Class names are converted to file names by making the class name
  423. * lowercase and converting underscores to slashes:
  424. *
  425. * // Loads classes/my/class/name.php
  426. * Kohana::auto_load('My_Class_Name');
  427. *
  428. * You should never have to call this function, as simply calling a class
  429. * will cause it to be called.
  430. *
  431. * This function must be enabled as an autoloader in the bootstrap:
  432. *
  433. * spl_autoload_register(array('Kohana', 'auto_load'));
  434. *
  435. * @param string class name
  436. * @return boolean
  437. */
  438. public static function auto_load($class)
  439. {
  440. try
  441. {
  442. // Transform the class name into a path
  443. $file = str_replace('_', '/', strtolower($class));
  444. if ($path = Kohana::find_file('classes', $file))
  445. {
  446. // Load the class file
  447. require $path;
  448. // Class has been found
  449. return TRUE;
  450. }
  451. // Class is not in the filesystem
  452. return FALSE;
  453. }
  454. catch (Exception $e)
  455. {
  456. Kohana::exception_handler($e);
  457. die;
  458. }
  459. }
  460. /**
  461. * Changes the currently enabled modules. Module paths may be relative
  462. * or absolute, but must point to a directory:
  463. *
  464. * Kohana::modules(array('modules/foo', MODPATH.'bar'));
  465. *
  466. * @param array list of module paths
  467. * @return array enabled modules
  468. */
  469. public static function modules(array $modules = NULL)
  470. {
  471. if ($modules === NULL)
  472. {
  473. // Not changing modules, just return the current set
  474. return Kohana::$_modules;
  475. }
  476. // Start a new list of include paths, APPPATH first
  477. $paths = array(APPPATH);
  478. foreach ($modules as $name => $path)
  479. {
  480. if (is_dir($path))
  481. {
  482. // Add the module to include paths
  483. $paths[] = $modules[$name] = realpath($path).DIRECTORY_SEPARATOR;
  484. }
  485. else
  486. {
  487. // This module is invalid, remove it
  488. unset($modules[$name]);
  489. }
  490. }
  491. // Finish the include paths by adding SYSPATH
  492. $paths[] = SYSPATH;
  493. // Set the new include paths
  494. Kohana::$_paths = $paths;
  495. // Set the current module list
  496. Kohana::$_modules = $modules;
  497. foreach (Kohana::$_modules as $path)
  498. {
  499. $init = $path.'init'.EXT;
  500. if (is_file($init))
  501. {
  502. // Include the module initialization file once
  503. require_once $init;
  504. }
  505. }
  506. return Kohana::$_modules;
  507. }
  508. /**
  509. * Returns the the currently active include paths, including the
  510. * application, system, and each module's path.
  511. *
  512. * @return array
  513. */
  514. public static function include_paths()
  515. {
  516. return Kohana::$_paths;
  517. }
  518. /**
  519. * Searches for a file in the [Cascading Filesystem](kohana/files), and
  520. * returns the path to the file that has the highest precedence, so that it
  521. * can be included.
  522. *
  523. * When searching the "config", "messages", or "i18n" directories, or when
  524. * the `$array` flag is set to true, an array of all the files that match
  525. * that path in the [Cascading Filesystem](kohana/files) will be returned.
  526. * These files will return arrays which must be merged together.
  527. *
  528. * If no extension is given, the default extension (`EXT` set in
  529. * `index.php`) will be used.
  530. *
  531. * // Returns an absolute path to views/template.php
  532. * Kohana::find_file('views', 'template');
  533. *
  534. * // Returns an absolute path to media/css/style.css
  535. * Kohana::find_file('media', 'css/style', 'css');
  536. *
  537. * // Returns an array of all the "mimes" configuration files
  538. * Kohana::find_file('config', 'mimes');
  539. *
  540. * @param string directory name (views, i18n, classes, extensions, etc.)
  541. * @param string filename with subdirectory
  542. * @param string extension to search for
  543. * @param boolean return an array of files?
  544. * @return array a list of files when $array is TRUE
  545. * @return string single file path
  546. */
  547. public static function find_file($dir, $file, $ext = NULL, $array = FALSE)
  548. {
  549. if ($ext === NULL)
  550. {
  551. // Use the default extension
  552. $ext = EXT;
  553. }
  554. elseif ($ext)
  555. {
  556. // Prefix the extension with a period
  557. $ext = ".{$ext}";
  558. }
  559. else
  560. {
  561. // Use no extension
  562. $ext = '';
  563. }
  564. // Create a partial path of the filename
  565. $path = $dir.DIRECTORY_SEPARATOR.$file.$ext;
  566. if (Kohana::$caching === TRUE AND isset(Kohana::$_files[$path.($array ? '_array' : '_path')]))
  567. {
  568. // This path has been cached
  569. return Kohana::$_files[$path.($array ? '_array' : '_path')];
  570. }
  571. if (Kohana::$profiling === TRUE AND class_exists('Profiler', FALSE))
  572. {
  573. // Start a new benchmark
  574. $benchmark = Profiler::start('Kohana', __FUNCTION__);
  575. }
  576. if ($array OR $dir === 'config' OR $dir === 'i18n' OR $dir === 'messages')
  577. {
  578. // Include paths must be searched in reverse
  579. $paths = array_reverse(Kohana::$_paths);
  580. // Array of files that have been found
  581. $found = array();
  582. foreach ($paths as $dir)
  583. {
  584. if (is_file($dir.$path))
  585. {
  586. // This path has a file, add it to the list
  587. $found[] = $dir.$path;
  588. }
  589. }
  590. }
  591. else
  592. {
  593. // The file has not been found yet
  594. $found = FALSE;
  595. foreach (Kohana::$_paths as $dir)
  596. {
  597. if (is_file($dir.$path))
  598. {
  599. // A path has been found
  600. $found = $dir.$path;
  601. // Stop searching
  602. break;
  603. }
  604. }
  605. }
  606. if (Kohana::$caching === TRUE)
  607. {
  608. // Add the path to the cache
  609. Kohana::$_files[$path.($array ? '_array' : '_path')] = $found;
  610. // Files have been changed
  611. Kohana::$_files_changed = TRUE;
  612. }
  613. if (isset($benchmark))
  614. {
  615. // Stop the benchmark
  616. Profiler::stop($benchmark);
  617. }
  618. return $found;
  619. }
  620. /**
  621. * Recursively finds all of the files in the specified directory at any
  622. * location in the [Cascading Filesystem](kohana/files), and returns an
  623. * array of all the files found, sorted alphabetically.
  624. *
  625. * // Find all view files.
  626. * $views = Kohana::list_files('views');
  627. *
  628. * @param string directory name
  629. * @param array list of paths to search
  630. * @return array
  631. */
  632. public static function list_files($directory = NULL, array $paths = NULL)
  633. {
  634. if ($directory !== NULL)
  635. {
  636. // Add the directory separator
  637. $directory .= DIRECTORY_SEPARATOR;
  638. }
  639. if ($paths === NULL)
  640. {
  641. // Use the default paths
  642. $paths = Kohana::$_paths;
  643. }
  644. // Create an array for the files
  645. $found = array();
  646. foreach ($paths as $path)
  647. {
  648. if (is_dir($path.$directory))
  649. {
  650. // Create a new directory iterator
  651. $dir = new DirectoryIterator($path.$directory);
  652. foreach ($dir as $file)
  653. {
  654. // Get the file name
  655. $filename = $file->getFilename();
  656. if ($filename[0] === '.' OR $filename[strlen($filename)-1] === '~')
  657. {
  658. // Skip all hidden files and UNIX backup files
  659. continue;
  660. }
  661. // Relative filename is the array key
  662. $key = $directory.$filename;
  663. if ($file->isDir())
  664. {
  665. if ($sub_dir = Kohana::list_files($key, $paths))
  666. {
  667. if (isset($found[$key]))
  668. {
  669. // Append the sub-directory list
  670. $found[$key] += $sub_dir;
  671. }
  672. else
  673. {
  674. // Create a new sub-directory list
  675. $found[$key] = $sub_dir;
  676. }
  677. }
  678. }
  679. else
  680. {
  681. if ( ! isset($found[$key]))
  682. {
  683. // Add new files to the list
  684. $found[$key] = realpath($file->getPathName());
  685. }
  686. }
  687. }
  688. }
  689. }
  690. // Sort the results alphabetically
  691. ksort($found);
  692. return $found;
  693. }
  694. /**
  695. * Loads a file within a totally empty scope and returns the output:
  696. *
  697. * $foo = Kohana::load('foo.php');
  698. *
  699. * @param string
  700. * @return mixed
  701. */
  702. public static function load($file)
  703. {
  704. return include $file;
  705. }
  706. /**
  707. * Returns the configuration array for the requested group. See
  708. * [configuration files](kohana/files/config) for more information.
  709. *
  710. * // Get all the configuration in config/database.php
  711. * $config = Kohana::config('database');
  712. *
  713. * // Get only the default connection configuration
  714. * $default = Kohana::config('database.default')
  715. *
  716. * // Get only the hostname of the default connection
  717. * $host = Kohana::config('database.default.connection.hostname')
  718. *
  719. * @param string group name
  720. * @return Kohana_Config
  721. */
  722. public static function config($group)
  723. {
  724. static $config;
  725. if (strpos($group, '.') !== FALSE)
  726. {
  727. // Split the config group and path
  728. list ($group, $path) = explode('.', $group, 2);
  729. }
  730. if ( ! isset($config[$group]))
  731. {
  732. // Load the config group into the cache
  733. $config[$group] = Kohana::$config->load($group);
  734. }
  735. if (isset($path))
  736. {
  737. return Arr::path($config[$group], $path, NULL, '.');
  738. }
  739. else
  740. {
  741. return $config[$group];
  742. }
  743. }
  744. /**
  745. * Provides simple file-based caching for strings and arrays:
  746. *
  747. * // Set the "foo" cache
  748. * Kohana::cache('foo', 'hello, world');
  749. *
  750. * // Get the "foo" cache
  751. * $foo = Kohana::cache('foo');
  752. *
  753. * All caches are stored as PHP code, generated with [var_export][ref-var].
  754. * Caching objects may not work as expected. Storing references or an
  755. * object or array that has recursion will cause an E_FATAL.
  756. *
  757. * The cache directory and default cache lifetime is set by [Kohana::init]
  758. *
  759. * [ref-var]: http://php.net/var_export
  760. *
  761. * @throws Kohana_Exception
  762. * @param string name of the cache
  763. * @param mixed data to cache
  764. * @param integer number of seconds the cache is valid for
  765. * @return mixed for getting
  766. * @return boolean for setting
  767. */
  768. public static function cache($name, $data = NULL, $lifetime = NULL)
  769. {
  770. // Cache file is a hash of the name
  771. $file = sha1($name).'.txt';
  772. // Cache directories are split by keys to prevent filesystem overload
  773. $dir = Kohana::$cache_dir.DIRECTORY_SEPARATOR.$file[0].$file[1].DIRECTORY_SEPARATOR;
  774. if ($lifetime === NULL)
  775. {
  776. // Use the default lifetime
  777. $lifetime = Kohana::$cache_life;
  778. }
  779. if ($data === NULL)
  780. {
  781. if (is_file($dir.$file))
  782. {
  783. if ((time() - filemtime($dir.$file)) < $lifetime)
  784. {
  785. // Return the cache
  786. try
  787. {
  788. return unserialize(file_get_contents($dir.$file));
  789. }
  790. catch (Exception $e)
  791. {
  792. // Cache is corrupt, let return happen normally.
  793. }
  794. }
  795. else
  796. {
  797. try
  798. {
  799. // Cache has expired
  800. unlink($dir.$file);
  801. }
  802. catch (Exception $e)
  803. {
  804. // Cache has mostly likely already been deleted,
  805. // let return happen normally.
  806. }
  807. }
  808. }
  809. // Cache not found
  810. return NULL;
  811. }
  812. if ( ! is_dir($dir))
  813. {
  814. // Create the cache directory
  815. mkdir($dir, 0777, TRUE);
  816. // Set permissions (must be manually set to fix umask issues)
  817. chmod($dir, 0777);
  818. }
  819. // Force the data to be a string
  820. $data = serialize($data);
  821. try
  822. {
  823. // Write the cache
  824. return (bool) file_put_contents($dir.$file, $data, LOCK_EX);
  825. }
  826. catch (Exception $e)
  827. {
  828. // Failed to write cache
  829. return FALSE;
  830. }
  831. }
  832. /**
  833. * Get a message from a file. Messages are arbitary strings that are stored
  834. * in the `messages/` directory and reference by a key. Translation is not
  835. * performed on the returned values. See [message files](kohana/files/messages)
  836. * for more information.
  837. *
  838. * // Get "username" from messages/text.php
  839. * $username = Kohana::message('text', 'username');
  840. *
  841. * @param string file name
  842. * @param string key path to get
  843. * @param mixed default value if the path does not exist
  844. * @return string message string for the given path
  845. * @return array complete message list, when no path is specified
  846. * @uses Arr::merge
  847. * @uses Arr::path
  848. */
  849. public static function message($file, $path = NULL, $default = NULL)
  850. {
  851. static $messages;
  852. if ( ! isset($messages[$file]))
  853. {
  854. // Create a new message list
  855. $messages[$file] = array();
  856. if ($files = Kohana::find_file('messages', $file))
  857. {
  858. foreach ($files as $f)
  859. {
  860. // Combine all the messages recursively
  861. $messages[$file] = Arr::merge($messages[$file], Kohana::load($f));
  862. }
  863. }
  864. }
  865. if ($path === NULL)
  866. {
  867. // Return all of the messages
  868. return $messages[$file];
  869. }
  870. else
  871. {
  872. // Get a message using the path
  873. return Arr::path($messages[$file], $path, $default);
  874. }
  875. }
  876. /**
  877. * PHP error handler, converts all errors into ErrorExceptions. This handler
  878. * respects error_reporting settings.
  879. *
  880. * @throws ErrorException
  881. * @return TRUE
  882. */
  883. public static function error_handler($code, $error, $file = NULL, $line = NULL)
  884. {
  885. if (error_reporting() & $code)
  886. {
  887. // This error is not suppressed by current error reporting settings
  888. // Convert the error into an ErrorException
  889. throw new ErrorException($error, $code, 0, $file, $line);
  890. }
  891. // Do not execute the PHP error handler
  892. return TRUE;
  893. }
  894. /**
  895. * Inline exception handler, displays the error message, source of the
  896. * exception, and the stack trace of the error.
  897. *
  898. * @uses Kohana::exception_text
  899. * @param object exception object
  900. * @return boolean
  901. */
  902. public static function exception_handler(Exception $e)
  903. {
  904. try
  905. {
  906. // Get the exception information
  907. $type = get_class($e);
  908. $code = $e->getCode();
  909. $message = $e->getMessage();
  910. $file = $e->getFile();
  911. $line = $e->getLine();
  912. // Create a text version of the exception
  913. $error = Kohana::exception_text($e);
  914. if (is_object(Kohana::$log))
  915. {
  916. // Add this exception to the log
  917. Kohana::$log->add(Kohana::ERROR, $error);
  918. // Make sure the logs are written
  919. Kohana::$log->write();
  920. }
  921. if (Kohana::$is_cli)
  922. {
  923. // Just display the text of the exception
  924. echo "\n{$error}\n";
  925. return TRUE;
  926. }
  927. // Get the exception backtrace
  928. $trace = $e->getTrace();
  929. if ($e instanceof ErrorException)
  930. {
  931. if (isset(Kohana::$php_errors[$code]))
  932. {
  933. // Use the human-readable error name
  934. $code = Kohana::$php_errors[$code];
  935. }
  936. if (version_compare(PHP_VERSION, '5.3', '<'))
  937. {
  938. // Workaround for a bug in ErrorException::getTrace() that exists in
  939. // all PHP 5.2 versions. @see http://bugs.php.net/bug.php?id=45895
  940. for ($i = count($trace) - 1; $i > 0; --$i)
  941. {
  942. if (isset($trace[$i - 1]['args']))
  943. {
  944. // Re-position the args
  945. $trace[$i]['args'] = $trace[$i - 1]['args'];
  946. // Remove the args
  947. unset($trace[$i - 1]['args']);
  948. }
  949. }
  950. }
  951. }
  952. if ( ! headers_sent())
  953. {
  954. // Make sure the proper content type is sent with a 500 status
  955. header('Content-Type: text/html; charset='.Kohana::$charset, TRUE, 500);
  956. }
  957. // Start an output buffer
  958. ob_start();
  959. // Include the exception HTML
  960. include Kohana::find_file('views', Kohana::$error_view);
  961. // Display the contents of the output buffer
  962. echo ob_get_clean();
  963. return TRUE;
  964. }
  965. catch (Exception $e)
  966. {
  967. // Clean the output buffer if one exists
  968. ob_get_level() and ob_clean();
  969. // Display the exception text
  970. echo Kohana::exception_text($e), "\n";
  971. // Exit with an error status
  972. exit(1);
  973. }
  974. }
  975. /**
  976. * Catches errors that are not caught by the error handler, such as E_PARSE.
  977. *
  978. * @uses Kohana::exception_handler
  979. * @return void
  980. */
  981. public static function shutdown_handler()
  982. {
  983. if ( ! Kohana::$_init)
  984. {
  985. // Do not execute when not active
  986. return;
  987. }
  988. try
  989. {
  990. if (Kohana::$caching === TRUE AND Kohana::$_files_changed === TRUE)
  991. {
  992. // Write the file path cache
  993. Kohana::cache('Kohana::find_file()', Kohana::$_files);
  994. }
  995. }
  996. catch (Exception $e)
  997. {
  998. // Pass the exception to the handler
  999. Kohana::exception_handler($e);
  1000. }
  1001. if (Kohana::$errors AND $error = error_get_last() AND in_array($error['type'], Kohana::$shutdown_errors))
  1002. {
  1003. // Clean the output buffer
  1004. ob_get_level() and ob_clean();
  1005. // Fake an exception for nice debugging
  1006. Kohana::exception_handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line']));
  1007. // Shutdown now to avoid a "death loop"
  1008. exit(1);
  1009. }
  1010. }
  1011. /**
  1012. * Get a single line of text representing the exception:
  1013. *
  1014. * Error [ Code ]: Message ~ File [ Line ]
  1015. *
  1016. * @param object Exception
  1017. * @return string
  1018. */
  1019. public static function exception_text(Exception $e)
  1020. {
  1021. return sprintf('%s [ %s ]: %s ~ %s [ %d ]',
  1022. get_class($e), $e->getCode(), strip_tags($e->getMessage()), Kohana::debug_path($e->getFile()), $e->getLine());
  1023. }
  1024. /**
  1025. * Returns an HTML string of debugging information about any number of
  1026. * variables, each wrapped in a "pre" tag:
  1027. *
  1028. * // Displays the type and value of each variable
  1029. * echo Kohana::debug($foo, $bar, $baz);
  1030. *
  1031. * @param mixed variable to debug
  1032. * @param ...
  1033. * @return string
  1034. */
  1035. public static function debug()
  1036. {
  1037. if (func_num_args() === 0)
  1038. return;
  1039. // Get all passed variables
  1040. $variables = func_get_args();
  1041. $output = array();
  1042. foreach ($variables as $var)
  1043. {
  1044. $output[] = Kohana::_dump($var, 1024);
  1045. }
  1046. return '<pre class="debug">'.implode("\n", $output).'</pre>';
  1047. }
  1048. /**
  1049. * Returns an HTML string of information about a single variable.
  1050. *
  1051. * Borrows heavily on concepts from the Debug class of [Nette](http://nettephp.com/).
  1052. *
  1053. * @param mixed variable to dump
  1054. * @param integer maximum length of strings
  1055. * @return string
  1056. */
  1057. public static function dump($value, $length = 128)
  1058. {
  1059. return Kohana::_dump($value, $length);
  1060. }
  1061. /**
  1062. * Helper for Kohana::dump(), handles recursion in arrays and objects.
  1063. *
  1064. * @param mixed variable to dump
  1065. * @param integer maximum length of strings
  1066. * @param integer recursion level (internal)
  1067. * @return string
  1068. */
  1069. protected static function _dump( & $var, $length = 128, $level = 0)
  1070. {
  1071. if ($var === NULL)
  1072. {
  1073. return '<small>NULL</small>';
  1074. }
  1075. elseif (is_bool($var))
  1076. {
  1077. return '<small>bool</small> '.($var ? 'TRUE' : 'FALSE');
  1078. }
  1079. elseif (is_float($var))
  1080. {
  1081. return '<small>float</small> '.$var;
  1082. }
  1083. elseif (is_resource($var))
  1084. {
  1085. if (($type = get_resource_type($var)) === 'stream' AND $meta = stream_get_meta_data($var))
  1086. {
  1087. $meta = stream_get_meta_data($var);
  1088. if (isset($meta['uri']))
  1089. {
  1090. $file = $meta['uri'];
  1091. if (function_exists('stream_is_local'))
  1092. {
  1093. // Only exists on PHP >= 5.2.4
  1094. if (stream_is_local($file))
  1095. {
  1096. $file = Kohana::debug_path($file);
  1097. }
  1098. }
  1099. return '<small>resource</small><span>('.$type.')</span> '.htmlspecialchars($file, ENT_NOQUOTES, Kohana::$charset);
  1100. }
  1101. }
  1102. else
  1103. {
  1104. return '<small>resource</small><span>('.$type.')</span>';
  1105. }
  1106. }
  1107. elseif (is_string($var))
  1108. {
  1109. // Clean invalid multibyte characters. iconv is only invoked
  1110. // if there are non ASCII characters in the string, so this
  1111. // isn't too much of a hit.
  1112. $var = UTF8::clean($var, Kohana::$charset);
  1113. if (UTF8::strlen($var) > $length)
  1114. {
  1115. // Encode the truncated string
  1116. $str = htmlspecialchars(UTF8::substr($var, 0, $length), ENT_NOQUOTES, Kohana::$charset).'&nbsp;&hellip;';
  1117. }
  1118. else
  1119. {
  1120. // Encode the string
  1121. $str = htmlspecialchars($var, ENT_NOQUOTES, Kohana::$charset);
  1122. }
  1123. if (Kohana::$debug_escape_quotes)
  1124. {
  1125. // Escape strings (for syntax highlighters, mostly)
  1126. $str = str_replace('"', '\\"', $str);
  1127. }
  1128. return '<small>string</small><span>('.strlen($var).')</span> "'.$str.'"';
  1129. }
  1130. elseif (is_array($var))
  1131. {
  1132. $output = array();
  1133. // Indentation for this variable
  1134. $space = str_repeat($s = ' ', $level);
  1135. static $marker;
  1136. if ($marker === NULL)
  1137. {
  1138. // Make a unique marker
  1139. $marker = uniqid("\x00");
  1140. }
  1141. if (empty($var))
  1142. {
  1143. // Do nothing
  1144. }
  1145. elseif (isset($var[$marker]))
  1146. {
  1147. $output[] = "(\n$space$s*RECURSION*\n$space)";
  1148. }
  1149. elseif ($level < 5)
  1150. {
  1151. $output[] = "<span>(";
  1152. $var[$marker] = TRUE;
  1153. foreach ($var as $key => & $val)
  1154. {
  1155. if ($key === $marker) continue;
  1156. if ( ! is_int($key))
  1157. {
  1158. $key = '"'.htmlspecialchars($key, ENT_NOQUOTES, self::$charset).'"';
  1159. }
  1160. $output[] = "$space$s$key => ".Kohana::_dump($val, $length, $level + 1);
  1161. }
  1162. unset($var[$marker]);
  1163. $output[] = "$space)</span>";
  1164. }
  1165. else
  1166. {
  1167. // Depth too great
  1168. $output[] = "(\n$space$s...\n$space)";
  1169. }
  1170. return '<small>array</small><span>('.count($var).')</span> '.implode("\n", $output);
  1171. }
  1172. elseif (is_object($var))
  1173. {
  1174. // Copy the object as an array
  1175. $array = (array) $var;
  1176. $output = array();
  1177. // Indentation for this variable
  1178. $space = str_repeat($s = ' ', $level);
  1179. $hash = spl_object_hash($var);
  1180. // Objects that are being dumped
  1181. static $objects = array();
  1182. if (empty($var))
  1183. {
  1184. // Do nothing
  1185. }
  1186. elseif (isset($objects[$hash]))
  1187. {
  1188. $output[] = "{\n$space$s*RECURSION*\n$space}";
  1189. }
  1190. elseif ($level < 10)
  1191. {
  1192. $output[] = "<code>{";
  1193. $objects[$hash] = TRUE;
  1194. foreach ($array as $key => & $val)
  1195. {
  1196. if ($key[0] === "\x00")
  1197. {
  1198. // Determine if the access is protected or protected
  1199. $access = '<small>'.(($key[1] === '*') ? 'protected' : 'private').'</small>';
  1200. // Remove the access level from the variable name
  1201. $key = substr($key, strrpos($key, "\x00") + 1);
  1202. }
  1203. else
  1204. {
  1205. $access = '<small>public</small>';
  1206. }
  1207. $output[] = "$space$s$access $key => ".Kohana::_dump($val, $length, $level + 1);
  1208. }
  1209. unset($objects[$hash]);
  1210. $output[] = "$space}</code>";
  1211. }
  1212. else
  1213. {
  1214. // Depth too great
  1215. $output[] = "{\n$space$s...\n$space}";
  1216. }
  1217. return '<small>object</small> <span>'.get_class($var).'('.count($array).')</span> '.implode("\n", $output);
  1218. }
  1219. else
  1220. {
  1221. return '<small>'.gettype($var).'</small> '.htmlspecialchars(print_r($var, TRUE), ENT_NOQUOTES, Kohana::$charset);
  1222. }
  1223. }
  1224. /**
  1225. * Removes application, system, modpath, or docroot from a filename,
  1226. * replacing them with the plain text equivalents. Useful for debugging
  1227. * when you want to display a shorter path.
  1228. *
  1229. * // Displays SYSPATH/classes/kohana.php
  1230. * echo Kohana::debug_path(Kohana::find_file('classes', 'kohana'));
  1231. *
  1232. * @param string path to debug
  1233. * @return string
  1234. */
  1235. public static function debug_path($file)
  1236. {
  1237. if (strpos($file, APPPATH) === 0)
  1238. {
  1239. $file = 'APPPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(APPPATH));
  1240. }
  1241. elseif (strpos($file, SYSPATH) === 0)
  1242. {
  1243. $file = 'SYSPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(SYSPATH));
  1244. }
  1245. elseif (strpos($file, MODPATH) === 0)
  1246. {
  1247. $file = 'MODPATH'.DIRECTORY_SEPARATOR.substr($file, strlen(MODPATH));
  1248. }
  1249. elseif (strpos($file, DOCROOT) === 0)
  1250. {
  1251. $file = 'DOCROOT'.DIRECTORY_SEPARATOR.substr($file, strlen(DOCROOT));
  1252. }
  1253. return $file;
  1254. }
  1255. /**
  1256. * Returns an HTML string, highlighting a specific line of a file, with some
  1257. * number of lines padded above and below.
  1258. *
  1259. * // Highlights the current line of the current file
  1260. * echo Kohana::debug_source(__FILE__, __LINE__);
  1261. *
  1262. * @param string file to open
  1263. * @param integer line number to highlight
  1264. * @param integer number of padding lines
  1265. * @return string source of file
  1266. * @return FALSE file is unreadable
  1267. */
  1268. public static function debug_source($file, $line_number, $padding = 5)
  1269. {
  1270. if ( ! $file OR ! is_file($file) OR ! is_readable($file))
  1271. {
  1272. // Continuing will cause errors
  1273. return FALSE;
  1274. }
  1275. // Open the file and set the line position
  1276. $file = fopen($file, 'r');
  1277. $line = 0;
  1278. // Set the reading range
  1279. $range = array('start' => $line_number - $padding, 'end' => $line_number + $padding);
  1280. // Set the zero-padding amount for line numbers
  1281. $format = '% '.strlen($range['end']).'d';
  1282. $source = '';
  1283. while (($row = fgets($file)) !== FALSE)
  1284. {
  1285. // Increment the line number
  1286. if (++$line > $range['end'])
  1287. break;
  1288. if ($line >= $range['start'])
  1289. {
  1290. // Make the row safe for output
  1291. $row = htmlspecialchars($row, ENT_NOQUOTES, Kohana::$charset);
  1292. // Trim whitespace and sanitize the row
  1293. $row = '<span class="number">'.sprintf($format, $line).'</span> '.$row;
  1294. if ($line === $line_number)
  1295. {
  1296. // Apply highlighting to this row
  1297. $row = '<span class="line highlight">'.$row.'</span>';
  1298. }
  1299. else
  1300. {
  1301. $row = '<span class="line">'.$row.'</span>';
  1302. }
  1303. // Add to the captured source
  1304. $source .= $row;
  1305. }
  1306. }
  1307. // Close the file
  1308. fclose($file);
  1309. return '<pre class="source"><code>'.$source.'</code></pre>';
  1310. }
  1311. /**
  1312. * Returns an array of HTML strings that represent each step in the backtrace.
  1313. *
  1314. * // Displays the entire current backtrace
  1315. * echo implode('<br/>', Kohana::trace());
  1316. *
  1317. * @param string path to debug
  1318. * @return string
  1319. */
  1320. public static function trace(array $trace = NULL)
  1321. {
  1322. if ($trace === NULL)
  1323. {
  1324. // Start a new trace
  1325. $trace = debug_backtrace();
  1326. }
  1327. // Non-standard function calls
  1328. $statements = array('include', 'include_once', 'require', 'require_once');
  1329. $output = array();
  1330. foreach ($trace as $step)
  1331. {
  1332. if ( ! isset($step['function']))
  1333. {
  1334. // Invalid trace step
  1335. continue;
  1336. }
  1337. if (isset($step['file']) AND isset($step['line']))
  1338. {
  1339. // Include the source of this step
  1340. $source = Kohana::debug_source($step['file'], $step['line']);
  1341. }
  1342. if (isset($step['file']))
  1343. {
  1344. $file = $step['file'];
  1345. if (isset($step['line']))
  1346. {
  1347. $line = $step['line'];
  1348. }
  1349. }
  1350. // function()
  1351. $function = $step['function'];
  1352. if (in_array($step['function'], $statements))
  1353. {
  1354. if (empty($step['args']))
  1355. {
  1356. // No arguments
  1357. $args = array();
  1358. }
  1359. else
  1360. {
  1361. // Sanitize the file path
  1362. $args = array(Kohana::debug_path($step['args'][0]));
  1363. }
  1364. }
  1365. elseif (isset($step['args']))
  1366. {
  1367. if ( ! function_exists($step['function']) OR strpos($step['function'], '{closure}') !== FALSE)
  1368. {
  1369. // Introspection on closures or language constructs in a stack trace is impossible
  1370. $params = NULL;
  1371. }
  1372. else
  1373. {
  1374. if (isset($step['class']))
  1375. {
  1376. if (method_exists($step['class'], $step['function']))
  1377. {
  1378. $reflection = new ReflectionMethod($step['class'], $step['function']);
  1379. }
  1380. else
  1381. {
  1382. $reflection = new ReflectionMethod($step['class'], '__call');
  1383. }
  1384. }
  1385. else
  1386. {
  1387. $reflection = new ReflectionFunction($step['function']);
  1388. }
  1389. // Get the function parameters
  1390. $params = $reflection->getParameters();
  1391. }
  1392. $args = array();
  1393. foreach ($step['args'] as $i => $arg)
  1394. {
  1395. if (isset($params[$i]))
  1396. {
  1397. // Assign the argument by the parameter name
  1398. $args[$params[$i]->name] = $arg;
  1399. }
  1400. else
  1401. {
  1402. // Assign the argument by number
  1403. $args[$i] = $arg;
  1404. }
  1405. }
  1406. }
  1407. if (isset($step['class']))
  1408. {
  1409. // Class->method() or Class::method()
  1410. $function = $step['class'].$step['type'].$step['function'];
  1411. }
  1412. $output[] = array(
  1413. 'function' => $function,
  1414. 'args' => isset($args) ? $args : NULL,
  1415. 'file' => isset($file) ? $file : NULL,
  1416. 'line' => isset($line) ? $line : NULL,
  1417. 'source' => isset($source) ? $source : NULL,
  1418. );
  1419. unset($function, $args, $file, $line, $source);
  1420. }
  1421. return $output;
  1422. }
  1423. } // End Kohana