核心kohana
1 <?php defined('SYSPATH') OR die('No direct access allowed.'); 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 Gleez\Core 11 * @author Sandeep Sangamreddi - Gleez 12 * @author Kohana Team 13 * @copyright (c) 2011-2013 Gleez Technologies 14 * @copyright (c) 2008-2012 Kohana Team 15 * @license http://gleezcms.org/license Gleez CMS License 16 * @license http://kohanaframework.org/license 17 */ 18 class Kohana { 19 20 // Release version and codename 21 const VERSION = '3.2.2'; 22 const CODENAME = 'hypoleucos'; 23 24 // Common environment type constants for consistency and convenience 25 const PRODUCTION = 10; 26 const STAGING = 20; 27 const TESTING = 30; 28 const DEVELOPMENT = 40; 29 30 // Security check that is added to all generated PHP files 31 const FILE_SECURITY = '<?php defined(\'SYSPATH\') OR die(\'No direct script access.\');'; 32 33 // Format of cache files: header, cache name, and data 34 const FILE_CACHE = ":header \n\n// :name\n\n:data\n"; 35 36 /** 37 * @var string Current environment name 38 */ 39 public static $environment = Kohana::DEVELOPMENT; 40 41 /** 42 * @var boolean True if Kohana is running from the command line 43 */ 44 public static $is_cli = FALSE; 45 46 /** 47 * @var boolean True if Kohana is running on windows 48 */ 49 public static $is_windows = FALSE; 50 51 /** 52 * @var boolean True if [magic quotes](http://php.net/manual/en/security.magicquotes.php) is enabled. 53 */ 54 public static $magic_quotes = FALSE; 55 56 /** 57 * @var boolean Should errors and exceptions be logged 58 */ 59 public static $log_errors = FALSE; 60 61 /** 62 * @var boolean TRUE if PHP safe mode is on 63 */ 64 public static $safe_mode = FALSE; 65 66 /** 67 * @var string 68 */ 69 public static $content_type = 'text/html'; 70 71 /** 72 * @var string character set of input and output 73 */ 74 public static $charset = 'utf-8'; 75 76 /** 77 * @var string the name of the server Kohana is hosted upon 78 */ 79 public static $server_name = ''; 80 81 /** 82 * @var array list of valid host names for this instance 83 */ 84 public static $hostnames = array(); 85 86 /** 87 * @var string base URL to the application 88 */ 89 public static $base_url = '/'; 90 91 /** 92 * @var string Application index file, added to links generated by Kohana. Set by [Kohana::init] 93 */ 94 public static $index_file = 'index.php'; 95 96 /** 97 * @var string Cache directory, used by [Kohana::cache]. Set by [Kohana::init] 98 */ 99 public static $cache_dir; 100 101 /** 102 * @var integer Default lifetime for caching, in seconds, used by [Kohana::cache]. Set by [Kohana::init] 103 */ 104 public static $cache_life = 60; 105 106 /** 107 * @var boolean Whether to use internal caching for [Kohana::find_file], does not apply to [Kohana::cache]. Set by [Kohana::init] 108 */ 109 public static $caching = FALSE; 110 111 /** 112 * @var boolean Whether to enable [profiling](kohana/profiling). Set by [Kohana::init] 113 */ 114 public static $profiling = TRUE; 115 116 /** 117 * @var boolean Enable Kohana catching and displaying PHP errors and exceptions. Set by [Kohana::init] 118 */ 119 public static $errors = TRUE; 120 121 /** 122 * @var array Types of errors to display at shutdown 123 */ 124 public static $shutdown_errors = array(E_PARSE, E_ERROR, E_USER_ERROR); 125 126 /** 127 * @var boolean set the X-Powered-By header 128 */ 129 public static $expose = FALSE; 130 131 /** 132 * @var Log logging object 133 */ 134 public static $log; 135 136 /** 137 * @var Config config object 138 */ 139 public static $config; 140 141 /** 142 * @var boolean Has [Kohana::init] been called? 143 */ 144 protected static $_init = FALSE; 145 146 /** 147 * @var array Currently active modules 148 */ 149 protected static $_modules = array(); 150 151 /** 152 * @var array Include paths that are used to find files 153 */ 154 protected static $_paths = array(APPPATH, GLZPATH, SYSPATH); 155 156 /** 157 * @var array File path cache, used when caching is true in [Kohana::init] 158 */ 159 protected static $_files = array(); 160 161 /** 162 * @var boolean Has the file path cache changed during this execution? Used internally when when caching is true in [Kohana::init] 163 */ 164 protected static $_files_changed = FALSE; 165 166 /** 167 * Initializes the environment: 168 * 169 * - Disables register_globals and magic_quotes_gpc 170 * - Determines the current environment 171 * - Set global settings 172 * - Sanitizes GET, POST, and COOKIE variables 173 * - Converts GET, POST, and COOKIE variables to the global character set 174 * 175 * The following settings can be set: 176 * 177 * Type | Setting | Description | Default Value 178 * ----------|------------|------------------------------------------------|--------------- 179 * `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. | `"/"` 180 * `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"` 181 * `string` | charset | Character set used for all input and output | `"utf-8"` 182 * `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"` 183 * `integer` | cache_life | Lifetime, in seconds, of items cached by [Kohana::cache] | `60` 184 * `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` 185 * `boolean` | profile | Whether to enable the [Profiler](kohana/profiling). <br /> <br />Recommended setting: `TRUE` while developing, `FALSE` on production servers. | `TRUE` 186 * `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` 187 * `boolean` | expose | Set the X-Powered-By header 188 * 189 * @throws Kohana_Exception 190 * @param array $settings Array of settings. See above. 191 * @return void 192 * @uses Kohana::globals 193 * @uses Kohana::sanitize 194 * @uses Kohana::cache 195 * @uses Profiler 196 */ 197 public static function init(array $settings = NULL) 198 { 199 if (Kohana::$_init) 200 { 201 // Do not allow execution twice 202 // 不允许执行两次,默认 Kohana::$_init = FALSE; 203 return; 204 } 205 206 // Kohana is now initialized 207 Kohana::$_init = TRUE; 208 209 210 if (isset($settings['profile'])) 211 { 212 // Enable profiling 213 Kohana::$profiling = (bool) $settings['profile']; 214 } 215 216 217 // Start an output buffer 218 // 打开输出控制缓冲 219 ob_start(); 220 221 222 if (isset($settings['errors'])) 223 { 224 // Enable error handling 225 Kohana::$errors = (bool) $settings['errors']; 226 } 227 228 if (Kohana::$errors === TRUE) 229 { 230 // Enable Kohana exception handling, adds stack traces and error source. 231 // 设置一个用户定义的异常处理函数 232 set_exception_handler(array('Kohana_Exception', 'handler')); 233 234 // Enable Kohana error handling, converts all PHP errors to exceptions. 235 // 设置一个用户定义的错误处理函数 236 set_error_handler(array('Kohana', 'error_handler')); 237 } 238 239 240 241 242 243 // Enable the Kohana shutdown handler, which catches E_FATAL errors. 244 // Register a function for execution on shutdown 245 register_shutdown_function(array('Kohana', 'shutdown_handler')); 246 247 248 // ini_get — 获取一个配置选项的值 249 if (ini_get('register_globals')) 250 { 251 // Reverse the effects of register_globals 252 Kohana::globals(); 253 } 254 255 256 if (isset($settings['expose'])) 257 { 258 Kohana::$expose = (bool) $settings['expose']; 259 } 260 261 262 263 // Determine if we are running in a command line environment 264 Kohana::$is_cli = (PHP_SAPI === 'cli'); 265 266 267 268 // Determine if we are running in a Windows environment 269 Kohana::$is_windows = (DIRECTORY_SEPARATOR === '\\'); 270 271 272 273 // Determine if we are running in safe mode 274 Kohana::$safe_mode = (bool) ini_get('safe_mode'); 275 276 277 278 if (isset($settings['cache_dir'])) 279 { 280 if ( ! is_dir($settings['cache_dir'])) 281 { 282 try 283 { 284 // Create the cache directory 285 mkdir($settings['cache_dir'], 0755, TRUE); 286 287 // Set permissions (must be manually set to fix umask issues) 288 chmod($settings['cache_dir'], 0755); 289 } 290 catch (Exception $e) 291 { 292 throw new Kohana_Exception('Could not create cache directory :dir', 293 array(':dir' => Debug::path($settings['cache_dir']))); 294 } 295 } 296 297 // Set the cache directory path 298 Kohana::$cache_dir = realpath($settings['cache_dir']); 299 } 300 else 301 { 302 // Use the default cache directory 303 Kohana::$cache_dir = APPPATH.'cache'; 304 } 305 306 307 308 if ( ! is_writable(Kohana::$cache_dir)) 309 { 310 throw new Kohana_Exception('Directory :dir must be writable', 311 array(':dir' => Debug::path(Kohana::$cache_dir))); 312 } 313 314 315 316 if (isset($settings['cache_life'])) 317 { 318 // Set the default cache lifetime 319 Kohana::$cache_life = (int) $settings['cache_life']; 320 } 321 322 323 324 if (isset($settings['caching'])) 325 { 326 // Enable or disable internal caching 327 Kohana::$caching = (bool) $settings['caching']; 328 } 329 330 331 332 if (Kohana::$caching === TRUE) 333 { 334 // Load the file path cache 335 Kohana::$_files = Kohana::cache('Kohana::find_file()'); 336 } 337 338 339 340 if (isset($settings['charset'])) 341 { 342 // Set the system character set 343 Kohana::$charset = strtolower($settings['charset']); 344 } 345 346 347 348 349 if (function_exists('mb_internal_encoding')) 350 { 351 // Set the MB extension encoding to the same character set 352 // 设置/获取内部字符编码 353 mb_internal_encoding(Kohana::$charset); 354 } 355 356 357 358 if (isset($settings['base_url'])) 359 { 360 // Set the base URL 361 Kohana::$base_url = rtrim($settings['base_url'], '/').'/'; 362 } 363 364 365 366 if (isset($settings['index_file'])) 367 { 368 // Set the index file 369 Kohana::$index_file = trim($settings['index_file'], '/'); 370 } 371 372 373 374 // Determine if the extremely evil magic quotes are enabled 375 Kohana::$magic_quotes = version_compare(PHP_VERSION, '5.4') < 0 AND get_magic_quotes_gpc(); 376 377 378 379 // Sanitize all request variables 380 $_GET = Kohana::sanitize($_GET); 381 $_POST = Kohana::sanitize($_POST); 382 $_COOKIE = Kohana::sanitize($_COOKIE); 383 384 385 // instanceof 用于确定一个 PHP 变量是否属于某一类 class 的实例: 386 // instanceof 也可用来确定一个变量是不是继承自某一父类的子类的实例: 387 // 检查一个对象是否不是某个类的实例,可以使用逻辑运算符 not。--- 388 389 // Load the logger if one doesn't already exist 390 if ( ! Kohana::$log instanceof Log) 391 { 392 Kohana::$log = Log::instance(); 393 } 394 395 // Load the config if one doesn't already exist 396 if ( ! Kohana::$config instanceof Config) 397 { 398 Kohana::$config = new Config; 399 } 400 } 401 402 /** 403 * Cleans up the environment: 404 * 405 * - Restore the previous error and exception handlers 406 * - Destroy the Kohana::$log and Kohana::$config objects 407 * 408 * @return void 409 */ 410 public static function deinit() 411 { 412 if (Kohana::$_init) //已经加载配置选项 413 { 414 // Removed the autoloader 415 spl_autoload_unregister(array('Kohana', 'auto_load')); 416 417 if (Kohana::$errors) 418 { 419 // Go back to the previous error handler 420 // 还原之前的错误处理函数 421 restore_error_handler(); 422 423 // Go back to the previous exception handler 424 // 恢复之前定义过的异常处理函数 425 restore_exception_handler(); 426 } 427 428 // Destroy objects created by init 429 Kohana::$log = Kohana::$config = NULL; 430 431 // Reset internal storage 432 Kohana::$_modules = Kohana::$_files = array(); 433 Kohana::$_paths = array(APPPATH, GLZPATH, SYSPATH); 434 435 // Reset file cache status 436 Kohana::$_files_changed = FALSE; 437 438 // Kohana is no longer initialized 439 Kohana::$_init = FALSE; 440 } 441 } 442 443 /** 444 * Reverts the effects of the `register_globals` PHP setting by unsetting 445 * all global varibles except for the default super globals (GPCS, etc), 446 * which is a [potential security hole.][ref-wikibooks] 447 * 448 * This is called automatically by [Kohana::init] if `register_globals` is 449 * on. 450 * 451 * 452 * [ref-wikibooks]: http://en.wikibooks.org/wiki/PHP_Programming/Register_Globals 453 * 454 * @return void 455 */ 456 public static function globals() 457 { 458 if (isset($_REQUEST['GLOBALS']) OR isset($_FILES['GLOBALS'])) 459 { 460 // Prevent malicious GLOBALS overload attack 461 echo "Global variable overload attack detected! Request aborted.\n"; 462 463 // Exit with an error status 464 exit(1); 465 } 466 467 // Get the variable names of all globals 468 $global_variables = array_keys($GLOBALS); 469 470 // Remove the standard global variables from the list 471 $global_variables = array_diff($global_variables, array( 472 '_COOKIE', 473 '_ENV', 474 '_GET', 475 '_FILES', 476 '_POST', 477 '_REQUEST', 478 '_SERVER', 479 '_SESSION', 480 'GLOBALS', 481 )); 482 483 foreach ($global_variables as $name) 484 { 485 // Unset the global variable, effectively disabling register_globals 486 unset($GLOBALS[$name]); 487 } 488 } 489 490 /** 491 * Recursively sanitizes an input variable: 492 * 493 * - Strips slashes if magic quotes are enabled 494 * - Normalizes all newlines to LF 495 * 496 * @param mixed $value any variable 497 * @return mixed sanitized variable 498 */ 499 public static function sanitize($value) 500 { 501 if (is_array($value) OR is_object($value)) 502 { 503 foreach ($value as $key => $val) 504 { 505 // Recursively clean each value 506 $value[$key] = Kohana::sanitize($val); 507 } 508 } 509 elseif (is_string($value)) 510 { 511 if (Kohana::$magic_quotes === TRUE) 512 { 513 // Remove slashes added by magic quotes 514 $value = stripslashes($value); 515 } 516 517 if (strpos($value, "\r") !== FALSE) 518 { 519 // Standardize newlines 520 $value = str_replace(array("\r\n", "\r"), "\n", $value); 521 } 522 } 523 524 return $value; 525 } 526 527 /** 528 * Provides auto-loading support of classes that follow Kohana's [class 529 * naming conventions](kohana/conventions#class-names-and-file-location). 530 * See [Loading Classes](kohana/autoloading) for more information. 531 * 532 * Class names are converted to file names by making the class name 533 * lowercase and converting underscores to slashes: 534 * 535 * // Loads classes/my/class/name.php 536 * Kohana::auto_load('My_Class_Name'); 537 * 538 * You should never have to call this function, as simply calling a class 539 * will cause it to be called. 540 * 541 * This function must be enabled as an autoloader in the bootstrap: 542 * 543 * spl_autoload_register(array('Kohana', 'auto_load')); 544 * 545 * @param string $class class name 546 * @return boolean 547 */ 548 public static function auto_load($class) 549 { 550 try 551 { 552 // Transform the class name into a path 553 $file = str_replace('_', '/', strtolower($class)); 554 555 if ($path = Kohana::find_file('classes', $file)) 556 { 557 // Load the class file 558 require $path; 559 560 // Class has been found 561 return TRUE; 562 } 563 564 // Class is not in the filesystem 565 return FALSE; 566 } 567 catch (Exception $e) 568 { 569 Kohana_Exception::handler($e); 570 die; 571 } 572 } 573 574 /** 575 * Changes the currently enabled modules. Module paths may be relative 576 * or absolute, but must point to a directory: 577 * 578 * Kohana::modules(array('modules/foo', MODPATH.'bar')); 579 * 580 * @param array $modules list of module paths 581 * @return array enabled modules 582 */ 583 public static function modules(array $modules = NULL) 584 { 585 if ($modules === NULL) 586 { 587 // Not changing modules, just return the current set 588 return Kohana::$_modules; 589 } 590 591 592 593 594 595 // Start a new list of include paths, APPPATH first 596 $paths = array(APPPATH); 597 598 foreach ($modules as $name => $path) 599 { 600 if (is_dir($path)) 601 { 602 // Add the module to include paths 603 $paths[] = $modules[$name] = realpath($path).DIRECTORY_SEPARATOR; 604 } 605 else 606 { 607 // This module is invalid, remove it 608 throw new Kohana_Exception('Attempted to load an invalid or missing module \':module\' at \':path\'', array( 609 ':module' => $name, 610 ':path' => Debug::path($path), 611 )); 612 } 613 } 614 615 // Include GLZPATH before system for CFS 616 $paths[] = GLZPATH; 617 618 // Finish the include paths by adding SYSPATH 619 $paths[] = SYSPATH; 620 621 622 623 624 625 626 627 // Set the new include paths 628 Kohana::$_paths = $paths; 629 630 // Set the current module list 631 Kohana::$_modules = $modules; 632 633 634 635 636 /** Run Gleez Components */ 637 Gleez::ready(); 638 639 640 641 // 加载每个模块下init.php配置路由文件 642 foreach (Kohana::$_modules as $path) 643 { 644 $init = $path.'init'.EXT; 645 646 if (is_file($init)) 647 { 648 // Include the module initialization file once 649 require_once $init; 650 } 651 } 652 653 654 655 //@todo better handling instead of init 656 require_once GLZPATH.'init'.EXT; 657 658 659 660 661 return Kohana::$_modules; 662 } 663 664 /** 665 * Returns the the currently active include paths, including the 666 * application, system, and each module's path. 667 * 668 * @return array 669 */ 670 public static function include_paths() 671 { 672 return Kohana::$_paths; 673 } 674 675 /** 676 * Searches for a file in the [Cascading Filesystem](kohana/files), and 677 * returns the path to the file that has the highest precedence, so that it 678 * can be included. 679 * 680 * When searching the "config", "messages", or "i18n" directories, or when 681 * the `$array` flag is set to true, an array of all the files that match 682 * that path in the [Cascading Filesystem](kohana/files) will be returned. 683 * These files will return arrays which must be merged together. 684 * 685 * If no extension is given, the default extension (`EXT` set in 686 * `index.php`) will be used. 687 * 688 * // Returns an absolute path to views/template.php 689 * Kohana::find_file('views', 'template'); 690 * 691 * // Returns an absolute path to media/css/style.css 692 * Kohana::find_file('media', 'css/style', 'css'); 693 * 694 * // Returns an array of all the "mimes" configuration files 695 * Kohana::find_file('config', 'mimes'); 696 * 697 * @param string $dir directory name (views, i18n, classes, extensions, etc.) 698 * @param string $file filename with subdirectory 699 * @param string $ext extension to search for 700 * @param boolean $array return an array of files? 701 * @return array a list of files when $array is TRUE 702 * @return string single file path 703 */ 704 public static function find_file($dir, $file, $ext = NULL, $array = FALSE) 705 { 706 if ($ext === NULL) 707 { 708 // Use the default extension 709 $ext = EXT; 710 } 711 elseif ($ext) 712 { 713 // Prefix the extension with a period 714 $ext = ".{$ext}"; 715 } 716 else 717 { 718 // Use no extension 719 $ext = ''; 720 } 721 722 723 724 // Create a partial path of the filename 725 $path = $dir.DIRECTORY_SEPARATOR.$file.$ext; 726 727 728 729 730 if (Kohana::$caching === TRUE AND isset(Kohana::$_files[$path.($array ? '_array' : '_path')])) 731 { 732 // This path has been cached 733 return Kohana::$_files[$path.($array ? '_array' : '_path')]; 734 } 735 736 737 738 739 if (Kohana::$profiling === TRUE AND class_exists('Profiler', FALSE)) 740 { 741 // Start a new benchmark 742 $benchmark = Profiler::start('Kohana', __FUNCTION__); 743 } 744 745 746 747 748 if ($array OR $dir === 'config' OR $dir === 'i18n' OR $dir === 'messages') 749 { 750 // Include paths must be searched in reverse 751 $paths = array_reverse(Kohana::$_paths); 752 753 // Array of files that have been found 754 $found = array(); 755 756 foreach ($paths as $dir) 757 { 758 if (is_file($dir.$path)) 759 { 760 // This path has a file, add it to the list 761 $found[] = $dir.$path; 762 } 763 } 764 } 765 else 766 { 767 // The file has not been found yet 768 $found = FALSE; 769 770 foreach (Kohana::$_paths as $dir) 771 { 772 if (is_file($dir.$path)) 773 { 774 // A path has been found 775 $found = $dir.$path; 776 777 // Stop searching 778 break; 779 } 780 } 781 } 782 783 784 785 786 787 788 if (Kohana::$caching === TRUE) 789 { 790 // Add the path to the cache 791 Kohana::$_files[$path.($array ? '_array' : '_path')] = $found; 792 793 // Files have been changed 794 Kohana::$_files_changed = TRUE; 795 } 796 797 798 799 800 if (isset($benchmark)) 801 { 802 // Stop the benchmark 803 Profiler::stop($benchmark); 804 } 805 806 return $found; 807 } 808 809 /** 810 * Recursively finds all of the files in the specified directory at any 811 * location in the [Cascading Filesystem](kohana/files), and returns an 812 * array of all the files found, sorted alphabetically. 813 * 814 * // Find all view files. 815 * $views = Kohana::list_files('views'); 816 * 817 * @param string $directory directory name 818 * @param array $paths list of paths to search 819 * @return array 820 */ 821 public static function list_files($directory = NULL, array $paths = NULL) 822 { 823 if ($directory !== NULL) 824 { 825 // Add the directory separator 826 $directory .= DIRECTORY_SEPARATOR; 827 } 828 829 830 831 if ($paths === NULL) 832 { 833 // Use the default paths 834 $paths = Kohana::$_paths; 835 } 836 837 838 839 // Create an array for the files 840 $found = array(); 841 842 foreach ($paths as $path) 843 { 844 if (is_dir($path.$directory)) 845 { 846 // Create a new directory iterator 847 $dir = new DirectoryIterator($path.$directory); 848 849 foreach ($dir as $file) 850 { 851 // Get the file name 852 $filename = $file->getFilename(); 853 854 if ($filename[0] === '.' OR $filename[strlen($filename)-1] === '~') 855 { 856 // Skip all hidden files and UNIX backup files 857 continue; 858 } 859 860 // Relative filename is the array key 861 $key = $directory.$filename; 862 863 if ($file->isDir()) 864 { 865 if ($sub_dir = Kohana::list_files($key, $paths)) 866 { 867 if (isset($found[$key])) 868 { 869 // Append the sub-directory list 870 $found[$key] += $sub_dir; 871 } 872 else 873 { 874 // Create a new sub-directory list 875 $found[$key] = $sub_dir; 876 } 877 } 878 } 879 else 880 { 881 if ( ! isset($found[$key])) 882 { 883 // Add new files to the list 884 $found[$key] = realpath($file->getPathName()); 885 } 886 } 887 } 888 } 889 } 890 891 // Sort the results alphabetically 892 ksort($found); 893 894 return $found; 895 } 896 897 /** 898 * Loads a file within a totally empty scope and returns the output: 899 * 900 * $foo = Kohana::load('foo.php'); 901 * 902 * @param string $file 903 * @return mixed 904 */ 905 public static function load($file) 906 { 907 return include $file; 908 } 909 910 /** 911 * Provides simple file-based caching for strings and arrays: 912 * 913 * // Set the "foo" cache 914 * Kohana::cache('foo', 'hello, world'); 915 * 916 * // Get the "foo" cache 917 * $foo = Kohana::cache('foo'); 918 * 919 * All caches are stored as PHP code, generated with [var_export][ref-var]. 920 * Caching objects may not work as expected. Storing references or an 921 * object or array that has recursion will cause an E_FATAL. 922 * 923 * The cache directory and default cache lifetime is set by [Kohana::init] 924 * 925 * [ref-var]: http://php.net/var_export 926 * 927 * @throws Kohana_Exception 928 * @param string $name name of the cache 929 * @param mixed $data data to cache 930 * @param integer $lifetime number of seconds the cache is valid for 931 * @return mixed for getting 932 * @return boolean for setting 933 */ 934 public static function cache($name, $data = NULL, $lifetime = NULL) 935 { 936 // Cache file is a hash of the name 937 $file = sha1($name).'.txt'; 938 939 940 // Cache directories are split by keys to prevent filesystem overload 941 $dir = Kohana::$cache_dir.DIRECTORY_SEPARATOR. $file[0].$file[1].DIRECTORY_SEPARATOR; 942 943 944 if ($lifetime === NULL) 945 { 946 // Use the default lifetime 947 $lifetime = Kohana::$cache_life; 948 } 949 950 951 // 返回 952 if ($data === NULL) 953 { 954 if (is_file($dir.$file)) 955 { 956 if ((time() - filemtime($dir.$file)) < $lifetime) 957 { 958 // Return the cache 959 try 960 { 961 return unserialize(file_get_contents($dir.$file)); 962 } 963 catch (Exception $e) 964 { 965 // Cache is corrupt, let return happen normally. 966 } 967 } 968 else 969 { 970 try 971 { 972 // Cache has expired 973 unlink($dir.$file); 974 } 975 catch (Exception $e) 976 { 977 // Cache has mostly likely already been deleted, 978 // let return happen normally. 979 } 980 } 981 } 982 983 // Cache not found 984 return NULL; 985 } 986 987 988 989 990 if ( ! is_dir($dir)) 991 { 992 // Create the cache directory 993 mkdir($dir, 0777, TRUE); 994 995 // Set permissions (must be manually set to fix umask issues) 996 chmod($dir, 0777); 997 } 998 999 // Force the data to be a string 1000 $data = serialize($data); 1001 1002 try 1003 { 1004 // Write the cache 1005 return (bool) file_put_contents($dir.$file, $data, LOCK_EX); 1006 } 1007 catch (Exception $e) 1008 { 1009 // Failed to write cache 1010 return FALSE; 1011 } 1012 } 1013 1014 /** 1015 * Get a message from a file. Messages are arbitary strings that are stored 1016 * in the `messages/` directory and reference by a key. Translation is not 1017 * performed on the returned values. See [message files](kohana/files/messages) 1018 * for more information. 1019 * 1020 * // Get "username" from messages/text.php 1021 * $username = Kohana::message('text', 'username'); 1022 * 1023 * @param string $file file name 1024 * @param string $path key path to get 1025 * @param mixed $default default value if the path does not exist 1026 * @return string message string for the given path 1027 * @return array complete message list, when no path is specified 1028 * @uses Arr::merge 1029 * @uses Arr::path 1030 */ 1031 public static function message($file, $path = NULL, $default = NULL) 1032 { 1033 static $messages; 1034 1035 if ( ! isset($messages[$file])) 1036 { 1037 // Create a new message list 1038 $messages[$file] = array(); 1039 1040 if ($files = Kohana::find_file('messages', $file)) 1041 { 1042 foreach ($files as $f) 1043 { 1044 // Combine all the messages recursively 1045 $messages[$file] = Arr::merge($messages[$file], Kohana::load($f)); 1046 } 1047 } 1048 } 1049 1050 if ($path === NULL) 1051 { 1052 // Return all of the messages 1053 return $messages[$file]; 1054 } 1055 else 1056 { 1057 // Get a message using the path 1058 return Arr::path($messages[$file], $path, $default); 1059 } 1060 } 1061 1062 /** 1063 * PHP error handler, converts all errors into ErrorExceptions. This handler 1064 * respects error_reporting settings. 1065 * 1066 * @throws ErrorException 1067 * @return TRUE 1068 */ 1069 public static function error_handler($code, $error, $file = NULL, $line = NULL) 1070 { 1071 if (error_reporting() & $code) 1072 { 1073 // This error is not suppressed by current error reporting settings 1074 // Convert the error into an ErrorException 1075 throw new ErrorException($error, $code, 0, $file, $line); 1076 } 1077 1078 // Do not execute the PHP error handler 1079 return TRUE; 1080 } 1081 1082 /** 1083 * Catches errors that are not caught by the error handler, such as E_PARSE. 1084 * 1085 * @uses Kohana_Exception::handler 1086 * @return void 1087 */ 1088 public static function shutdown_handler() 1089 { 1090 if ( ! Kohana::$_init) 1091 { 1092 // Do not execute when not active 1093 return; 1094 } 1095 1096 try 1097 { 1098 if (Kohana::$caching === TRUE AND Kohana::$_files_changed === TRUE) 1099 { 1100 // Write the file path cache 1101 Kohana::cache('Kohana::find_file()', Kohana::$_files); 1102 } 1103 } 1104 catch (Exception $e) 1105 { 1106 // Pass the exception to the handler 1107 Kohana_Exception::handler($e); 1108 } 1109 1110 if (Kohana::$errors AND $error = error_get_last() AND in_array($error['type'], Kohana::$shutdown_errors)) 1111 { 1112 // Clean the output buffer 1113 ob_get_level() and ob_clean(); 1114 1115 // Fake an exception for nice debugging 1116 Kohana_Exception::handler(new ErrorException($error['message'], $error['type'], 0, $error['file'], $error['line'])); 1117 1118 // Shutdown now to avoid a "death loop" 1119 exit(1); 1120 } 1121 } 1122 1123 } // End Kohana 1124 1125 if ( ! function_exists('__')) 1126 { 1127 /** 1128 * Translate strings to the page language or a given language 1129 * 1130 * The PHP function [strtr](http://php.net/strtr) is used for replacing parameters. 1131 * <code> 1132 * __('Welcome back, :user', array(':user' => $username)); 1133 * </code> 1134 * 1135 * [!!] The target language is defined by [I18n::$lang]. 1136 * 1137 * @param string $string Text to translate 1138 * @param array $values Values to replace in the translated text. [Optional] 1139 * An associative array of replacements to make after translation. 1140 * Incidences of any key in this array are replaced with the corresponding value. 1141 * Based on the first character of the key, the value is escaped and/or themed: 1142 * - !variable: inserted as is 1143 * - :variable: inserted as is 1144 * - @variable: escape plain text to HTML (HTML::chars) 1145 * - %variable: escape text and theme as a placeholder for user-submitted 1146 * - ^variable: escape text and uppercase the first character of each word in a string 1147 * - ~variable: escape text and make a string's first character uppercase 1148 * content (HTML::chars + theme_placeholder) 1149 * @param string $lang Source language [Optional] 1150 * @return string 1151 * 1152 * @uses I18n::get 1153 * @uses HTML::chars 1154 */ 1155 function __($string, array $values = NULL, $lang = 'en-us') 1156 { 1157 if ($lang !== I18n::$lang) 1158 { 1159 // The message and target languages are different 1160 // Get the translation for this message 1161 $string = I18n::get($string); 1162 } 1163 1164 if (empty($values)) 1165 { 1166 return $string; 1167 } 1168 else 1169 { 1170 // Transform arguments before inserting them. 1171 foreach ($values as $key => $value) 1172 { 1173 switch ($key[0]) 1174 { 1175 case '@': 1176 // Escaped only 1177 $values[$key] = HTML::chars($value); 1178 break; 1179 case '%': 1180 // Escaped and placeholder 1181 $values[$key] = '<em class="placeholder">' . HTML::chars($value) . '</em>'; 1182 break; 1183 case '^': 1184 // Escaped and uppercase the first character of each word in a string 1185 $values[$key] = ucwords(HTML::chars($value)); 1186 break; 1187 case '~': 1188 // Escaped and make a string's first character uppercase 1189 $values[$key] = ucfirst(HTML::chars($value)); 1190 break; 1191 case '!': 1192 case ':': 1193 default: 1194 // Pass-through 1195 } 1196 } 1197 } 1198 1199 return strtr($string, $values); 1200 } 1201 } 1202 1203 function __n($count, $singular, $plural, array $values = array(), $lang = 'en-us') 1204 { 1205 if ($lang !== I18n::$lang) 1206 { 1207 $string = $count === 1 ? I18n::get($singular) : Gleez_I18n::get_plural($plural, $count); 1208 } 1209 else 1210 $string = $count === 1 ? $singular : $plural; 1211 1212 return strtr($string, array_merge($values, array('%count' => $count))); 1213 }