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