system/core/Common.php 文件中可以定义 公共函数,我们可以在这里定义自己的公共函数。在任何情况下你都能够使用这些函数。使用他们不需要载入任何类库或辅助函数。
接下来分析下该文件中自带的一些方法:
1 <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); 2 3 // ------------------------------------------------------------------------ 4 5 /** 6 * Common Functions 7 */ 8 9 /** 10 * 为什么还要定义这些全局函数呢?比如说,下面有很多函数, 11 * 如get_config()、config_item()这两个方法不是应该由 12 * core/Config.php这个组件去做么?那个load_class()不应该由core/Loader.php去做么? 13 * 把这些函数定义出来貌似 感觉架构变得不那么优雅,有点多余。 14 * 其实是出于这样一种情况: 15 * 比如说,如果一切和配置有关的动作都由Config组件来完成, 16 * 一切加载的动作都由Loader来完成, 17 * 试想一下,如果我要加载Config组件,那么,必须得通过Loader来加载, 18 * 所以Loader必须比Config要更早实例化, 19 * 但是如果Loader实例化的时候需要一些和Loader有关的配置信息才能实例化呢? 20 * 那就必须通过Config来为它取得配置信息。 21 * 这里就出现了鸡和鸡蛋的问题。。 22 * 我之前写自己的框架也纠结过这样的问题,后来参考了YII框架, 23 * 发现它里面其实都有同样的问题,它里面有个Exception的组件, 24 * 但是在加载这个Exception组件之前,在加载其它组件的时候, 25 * 如果出错了,那谁来处理异常和错误信息呢?答案就是先定义一些公共的函数。 26 * 所以这些公共函数就很好地解决了这个问题,这也是为什么Common.php要很早被引入。 27 * 28 */ 29 30 // ------------------------------------------------------------------------ 31 32 /** 33 * Determines if the current version of PHP is greater then the supplied value 34 */ 35 if ( ! function_exists('is_php')) 36 { 37 //判断当前php版本是不是$version以上的。调用version_compare()这个函数。 38 function is_php($version = '5.0.0') 39 { 40 static $_is_php; 41 $version = (string)$version; 42 43 if ( ! isset($_is_php[$version])) 44 { 45 //PHP_VERION能够获得当前php版本。 46 $_is_php[$version] = (version_compare(PHP_VERSION, $version) < 0) 47 ? FALSE : TRUE; 48 } 49 50 return $_is_php[$version]; 51 } 52 } 53 54 // ------------------------------------------------------------------------ 55 56 /** 57 * Tests for file writability 58 */ 59 if ( ! function_exists('is_really_writable')) 60 { 61 //该函数和php官方手册上面写的差不多,兼容linux/Unix和windows系统: 62 //http://www.php.net/manual/en/function.is-writable.php 63 function is_really_writable($file) 64 { 65 66 //DIRECTORY_SEPARATOR是系统的目录分割符。利用它的值可以知道当前是不是linux系统。 67 if (DIRECTORY_SEPARATOR == '/' AND @ini_get("safe_mode") == FALSE) 68 { 69 //如果是linux系统的话,那么可以直接调用此方法来判断文件是否可写。 70 return is_writable($file); 71 } 72 73 //如果是windows系统,则尝试写入一个文件来判断。 74 75 if (is_dir($file)) 76 { 77 //如果是目录,则创建一个随机命名的文件。 78 $file = rtrim($file, '/').'/'.md5(mt_rand(1,100).mt_rand(1,100)); 79 80 //如果文件不能创建,则返回不可写。 81 if (($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) 82 { 83 return FALSE; 84 } 85 86 fclose($fp); 87 //删除刚才的文件。 88 @chmod($file, DIR_WRITE_MODE); 89 @unlink($file); 90 return TRUE; 91 } 92 elseif ( ! is_file($file) 93 OR ($fp = @fopen($file, FOPEN_WRITE_CREATE)) === FALSE) 94 { 95 //如果是一个文件,而通过写入方式打不开,则返回不可写。 96 return FALSE; 97 } 98 99 fclose($fp); 100 return TRUE; 101 } 102 } 103 104 // ------------------------------------------------------------------------ 105 106 /** 107 * Class registry 108 */ 109 if ( ! function_exists('load_class')) 110 { 111 //加载类。默认是加载libraries里面的,如果要加载核心组件,$directory就为'core' 112 function &load_class($class, $directory = 'libraries', $prefix = 'CI_') 113 { 114 115 //用一个静态数组,保存已经加载过的类的实例,防止多次实例消耗资源,实现单例化。 116 static $_classes = array(); 117 // Does the class exist? If so, we're done... 118 if (isset($_classes[$class])) 119 { 120 return $_classes[$class];//如果已经保存在这里,就返回它。 121 } 122 $name = FALSE; 123 //这里,如果应用目录下有和系统目录下相同的类的话,优先引入应用目录,也就是你自己定义的。 124 foreach (array(APPPATH, BASEPATH) as $path) 125 { 126 if (file_exists($path.$directory.'/'.$class.'.php')) 127 { 128 $name = $prefix.$class; 129 if (class_exists($name) === FALSE) 130 { 131 require($path.$directory.'/'.$class.'.php'); 132 } 133 break; 134 } 135 } 136 //这里就用到的前缀扩展,如果在应用目录相应的目录下,有自己写的一些对CI库的扩展, 137 //那么我们加载的是它,而不是原来的。因为我们写的扩展是继承了CI原来的。 138 //所以可以看出,即使是CI的核心组件(core/下面的)我们都可以为之进行扩展。 139 if (file_exists(APPPATH.$directory.'/' 140 .config_item('subclass_prefix').$class.'.php')) 141 { 142 $name = config_item('subclass_prefix').$class; 143 if (class_exists($name) === FALSE) 144 { 145 require(APPPATH.$directory.'/'.config_item('subclass_prefix').$class.'.php'); 146 } 147 } 148 // Did we find the class? 149 if ($name === FALSE) 150 { 151 //这里用的是exit();来提示错误,而不是用show_error(); 152 //这是因为这个load_class的错误有可能 153 //在加载Exception组件之前发。 154 exit('Unable to locate the specified class: '.$class.'.php'); 155 } 156 // Keep track of what we just loaded 157 //这个函数只是用来记录已经被加载过的类的类名而已。 158 is_loaded($class); 159 $_classes[$class] = new $name(); 160 return $_classes[$class]; 161 } 162 } 163 // -------------------------------------------------------------------- 164 /** 165 * Keeps track of which libraries have been loaded. This function is 166 * called by the load_class() function above 167 */ 168 if ( ! function_exists('is_loaded')) 169 { 170 //记录有哪些类是已经被加载的。 171 function is_loaded($class = '') 172 { 173 static $_is_loaded = array(); 174 if ($class != '') 175 { 176 $_is_loaded[strtolower($class)] = $class; 177 } 178 return $_is_loaded; 179 } 180 } 181 // ------------------------------------------------------------------------ 182 /** 183 * Loads the main config.php file 184 */ 185 if ( ! function_exists('get_config')) 186 { 187 //这个是读取配置信息的函数,在Config类被实例化之前,由它暂负责。 188 //而在Config类被实例化之前,我们需要读取的配置信息, 189 //其实仅仅是config.php这个主配置文件的。所以这个方法是不能读出 190 //config/下其它配置文件的信息的。 191 //这个$replace参数,是提供一个临时替换配置信息的机会,仅一次,因为执行一次后, 192 //配置信息都会保存在静态变量$_config中,不能改变。 193 function &get_config($replace = array()) 194 { 195 static $_config; 196 if (isset($_config)) 197 { 198 return $_config[0]; 199 } 200 // Is the config file in the environment folder? 201 if ( ! defined('ENVIRONMENT') OR 202 ! file_exists($file_path = APPPATH.'config/'.ENVIRONMENT.'/config.php')) 203 { 204 $file_path = APPPATH.'config/config.php'; 205 } 206 // Fetch the config file 207 if ( ! file_exists($file_path)) 208 { 209 exit('The configuration file does not exist.'); 210 } 211 require($file_path); 212 // Does the $config array exist in the file? 213 if ( ! isset($config) OR ! is_array($config)) 214 { 215 exit('Your config file does not appear to be formatted correctly.'); 216 } 217 // Are any values being dynamically replaced? 218 if (count($replace) > 0) 219 { 220 foreach ($replace as $key => $val) 221 { 222 if (isset($config[$key])) 223 { 224 $config[$key] = $val; 225 } 226 } 227 } 228 return $_config[0] =& $config; 229 } 230 } 231 // ------------------------------------------------------------------------ 232 /** 233 * Returns the specified config item 234 */ 235 if ( ! function_exists('config_item')) 236 { 237 //取得配置数组中某个元素。 238 function config_item($item) 239 { 240 static $_config_item = array(); 241 if ( ! isset($_config_item[$item])) 242 { 243 $config =& get_config(); 244 if ( ! isset($config[$item])) 245 { 246 return FALSE; 247 } 248 $_config_item[$item] = $config[$item]; 249 } 250 return $_config_item[$item]; 251 } 252 } 253 // ------------------------------------------------------------------------ 254 /** 255 * Error Handler 256 */ 257 //这里的show_error和下面的show_404以及 _exception_handler这三个错误的处理, 258 //实质都是由Exception组件完成的。 259 //详见core/Exception.php. 260 if ( ! function_exists('show_error')) 261 { 262 function show_error($message, $status_code = 500, 263 $heading = 'An Error Was Encountered') 264 { 265 $_error =& load_class('Exceptions', 'core'); 266 echo $_error->show_error($heading, $message, 'error_general', $status_code); 267 exit; 268 } 269 } 270 // ------------------------------------------------------------------------ 271 /** 272 * 404 Page Handler 273 */ 274 if ( ! function_exists('show_404')) 275 { 276 function show_404($page = '', $log_error = TRUE) 277 { 278 $_error =& load_class('Exceptions', 'core'); 279 $_error->show_404($page, $log_error); 280 exit; 281 } 282 } 283 // ------------------------------------------------------------------------ 284 /** 285 * Error Logging Interface 286 */ 287 if ( ! function_exists('log_message')) 288 { 289 function log_message($level = 'error', $message, $php_error = FALSE) 290 { 291 static $_log; 292 if (config_item('log_threshold') == 0) 293 { 294 return; 295 } 296 $_log =& load_class('Log'); 297 $_log->write_log($level, $message, $php_error); 298 } 299 } 300 // ------------------------------------------------------------------------ 301 /** 302 * Set HTTP Status Header 303 */ 304 if ( ! function_exists('set_status_header')) 305 { 306 function set_status_header($code = 200, $text = '') 307 { 308 //此函数构造一个响应头。$stati为响应码与其响应说明。 309 $stati = array( 310 200 => 'OK', 311 201 => 'Created', 312 202 => 'Accepted', 313 203 => 'Non-Authoritative Information', 314 204 => 'No Content', 315 205 => 'Reset Content', 316 206 => 'Partial Content', 317 300 => 'Multiple Choices', 318 301 => 'Moved Permanently', 319 302 => 'Found', 320 304 => 'Not Modified', 321 305 => 'Use Proxy', 322 307 => 'Temporary Redirect', 323 400 => 'Bad Request', 324 401 => 'Unauthorized', 325 403 => 'Forbidden', 326 404 => 'Not Found', 327 405 => 'Method Not Allowed', 328 406 => 'Not Acceptable', 329 407 => 'Proxy Authentication Required', 330 408 => 'Request Timeout', 331 409 => 'Conflict', 332 410 => 'Gone', 333 411 => 'Length Required', 334 412 => 'Precondition Failed', 335 413 => 'Request Entity Too Large', 336 414 => 'Request-URI Too Long', 337 415 => 'Unsupported Media Type', 338 416 => 'Requested Range Not Satisfiable', 339 417 => 'Expectation Failed', 340 500 => 'Internal Server Error', 341 501 => 'Not Implemented', 342 502 => 'Bad Gateway', 343 503 => 'Service Unavailable', 344 504 => 'Gateway Timeout', 345 505 => 'HTTP Version Not Supported' 346 ); 347 //如果调用此函数本身出错,则发出一个错误。 348 if ($code == '' OR ! is_numeric($code)) 349 { 350 show_error('Status codes must be numeric', 500); 351 } 352 if (isset($stati[$code]) AND $text == '') 353 { 354 $text = $stati[$code]; 355 } 356 //如果$text为空,一般是因为调用此函数时,给的响应码不正确同时又没有写出响应报文信息。 357 if ($text == '') 358 { 359 show_error('No status text available. 360 Please check your status code number or supply your own message text.', 500); 361 } 362 //取得当前协议。 363 $server_protocol = (isset($_SERVER['SERVER_PROTOCOL'])) 364 ? $_SERVER['SERVER_PROTOCOL'] : FALSE; 365 //php_sapi_name()方法可以获得PHP与服务器之间的接口类型, 366 //下面是以cgi类型和以服务器模块形式类型的不同发出响应的方式。 367 if (substr(php_sapi_name(), 0, 3) == 'cgi') 368 { 369 header("Status: {$code} {$text}", TRUE); 370 } 371 elseif ($server_protocol == 'HTTP/1.1' OR $server_protocol == 'HTTP/1.0') 372 { 373 header($server_protocol." {$code} {$text}", TRUE, $code); 374 } 375 else 376 { 377 header("HTTP/1.1 {$code} {$text}", TRUE, $code); 378 } 379 } 380 } 381 // -------------------------------------------------------------------- 382 /** 383 * Exception Handler 384 */ 385 if ( ! function_exists('_exception_handler')) 386 { 387 //在CodeIgniter.php中执行set_error_handler('_exception_handler');后, 388 //以后一切非致命(非fatal)错误信息都由它处理。 389 //触发错误的时候,会产生几个参数,错误级别(号),错误信息,错误文件,错误行。 390 function _exception_handler($severity, $message, $filepath, $line) 391 { 392 /** 393 * 有关错误等级的内容可看: 394 * http://blog.163.com/wu_guoqing/blog/static/19653701820127269312682/ 395 * E_STRICT对于大多数情况来说都是没多大作用的错误提示, 396 * 这里CI把它屏蔽掉,如果实在要查看,可以查看日志文件。 397 */ 398 if ($severity == E_STRICT) 399 { 400 return; 401 } 402 //真正起到错误处理的是Exception组件。 403 $_error =& load_class('Exceptions', 'core'); 404 /* 405 * 注意下面的符号是&而不是&&,php的错误等级的值都是有规律的, 406 * 例如1,2,4,8...(1,10,100,1000)等等,实际上,php是通过位运算来实现的, 407 * 使得错误控制更精准。(类似linux的权限控制,rwx) 408 * 在设置error_reporting()的时候,可通过E_XX|E_YY|E_ZZ的形式来设置, 409 * 而判断的时候则通过E_XX&error_repoorting()来判断 410 * E_XX有没有设置。例如1,10,100,1000相或|,则值为1111, 411 * 则以后1,10,100,1000中任意一个与1111相&,值都为它本身。 412 * 而E_ALL可以看到是除E_STRICT之外其它等级的“或(|)运算”。 413 * 个人理解,之所以E_ALL的值是不同版本有所不同的,是 414 * 因为有时候会加入新的错误级别,从而导致这个E_ALL的值也不一样。 415 */ 416 if (($severity & error_reporting()) == $severity) 417 { 418 //如果符合则交给Exception组件的show_php_error();进行处理。 419 $_error->show_php_error($severity, $message, $filepath, $line); 420 } 421 //下面两行只是根据配置文件判断要不要log错误信息而已。 422 if (config_item('log_threshold') == 0) 423 { 424 return; 425 } 426 $_error->log_exception($severity, $message, $filepath, $line); 427 } 428 } 429 // -------------------------------------------------------------------- 430 /** 431 * Remove Invisible Characters 432 */ 433 if ( ! function_exists('remove_invisible_characters')) 434 { 435 function remove_invisible_characters($str, $url_encoded = TRUE) 436 { 437 $non_displayables = array(); 438 // every control character except newline (dec 10) 439 // carriage return (dec 13), and horizontal tab (dec 09) 440 if ($url_encoded) 441 { 442 $non_displayables[] = '/%0[0-8bcef]/'; // url encoded 00-08, 11, 12, 14, 15 443 $non_displayables[] = '/%1[0-9a-f]/'; // url encoded 16-31 444 } 445 $non_displayables[] = '/[x00-x08x0Bx0Cx0E-x1Fx7F]+/S'; 446 // 00-08, 11, 12, 14-31, 127 447 do 448 { 449 $str = preg_replace($non_displayables, '', $str, -1, $count); 450 } 451 while ($count); 452 return $str; 453 } 454 } 455 // ------------------------------------------------------------------------ 456 /** 457 * Returns HTML escaped variable 458 */ 459 if ( ! function_exists('html_escape')) 460 { 461 function html_escape($var) 462 { 463 if (is_array($var)) 464 { 465 return array_map('html_escape', $var); 466 } 467 else 468 { 469 return htmlspecialchars($var, ENT_QUOTES, config_item('charset')); 470 } 471 } 472 }