CodeIgniter源码分析之CodeIgniter.php

http://blog.163.com/wu_guoqing/blog/static/196537018201281671321308/


<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * 上面:
 * 这个BASEPATH,就是在入口文件(index.php)里面定义的那个BASEPATH~
 * 如果没有定义BASEPATH,那么直接退出,下面程序都不执行。其实除了入口文件index.php开头没有这句话之外,所有文件都会有这句话
 * 也就是说,所有文件都不能单独运行,一定是index.php在运行过程中把这些文件通
 * 过某种方式引进来运行,所以只有入口文件index.php才能被访问。
 * 
 */

/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------

/**
 * System Initialization File
 *
 * Loads the base classes and executes the request.
 *
 * @package		CodeIgniter
 * @subpackage	codeigniter
 * @category	Front-controller
 * @author		ExpressionEngine Dev Team
 * @link		http://codeigniter.com/user_guide/
 */

/**
 * CodeIgniter Version
 *
 * @var string
 *
 */
	define('CI_VERSION', '2.2.0');

/**
 * CodeIgniter Branch (Core = TRUE, Reactor = FALSE)
 *
 * @var boolean
 *
 */
	define('CI_CORE', FALSE);//?wjx

/*
 * ------------------------------------------------------
 *  Load the global functions
 * ------------------------------------------------------
 */
//在这里引入了很多全局函数。这些函数在整个应用运行的过程都是非常重要的。
  //这些函数都是应该最先被引入,起码要先于下面的组件,为什么呢?详见core/Common.php,建议在这个地方先略过。
	
	require(BASEPATH.'core/Common.php');  //Common.php框架定义的一些函数  global functions

/*
 * ------------------------------------------------------
 *  Load the framework constants(常量)
 * ------------------------------------------------------
 */
 //加载配置的常量。这个配置文件里面默认已经有一些和文件有关的常量。
 
 //下面这个判断可以看出一开始我们在index.php里面定义的那个ENVIRONMENT的作用之一,如果是定义某个环境,
 //会调用相应的配置文件,这样就可以使得应用在相应的环境中运行。不仅仅是这个常量的配置文件是这样子,
 //以后你会发现,其实全部配置文件都是先判断当前环境再引入。
 //方便切换,只需在index.php里面改一下ENVIRONMENT的值。
	if (defined('ENVIRONMENT') AND file_exists(APPPATH.'config/'.ENVIRONMENT.'/constants.php'))
	{//ENVIRONMENT has in index.php
		require(APPPATH.'config/'.ENVIRONMENT.'/constants.php');
	}
	else
	{
		  //当然啦,如果压根没有这个环境下的配置文件,就会调用默认的。CI手册上也有说,各种环境下的相同的配置文件,可以直接放在
  //config/下,而不需要每个环境的目录下都有。
		require(APPPATH.'config/constants.php');
	}

/*
 * ------------------------------------------------------
 *  Define a custom error handler so we can log PHP errors
 * ------------------------------------------------------
 */
	set_error_handler('_exception_handler');
	/*
	set_error_handler() 函数设置用户自定义的错误处理函数。
	该函数用于创建运行时期间的用户自己的错误处理方法。
	该函数会返回旧的错误处理程序,若失败,则返回 null。
		set_error_handler(error_function,error_types)
			error_function	必需。规定发生错误时运行的函数。
			error_types	可选。规定在哪个错误报告级别会显示用户定义的错误。默认是 "E_ALL"。
	*/

	if ( ! is_php('5.3'))
	{
		@set_magic_quotes_runtime(0); // Kill magic quotes   过滤 特殊字符提交的时候提示数据库错误
		//http://www.baidu.com/s?ie=utf-8&f=3&rsv_bp=1&tn=baidu&wd=php%20%40set_magic_quotes_runtime&rsv_enter=0&rsv_sug3=99&rsv_sug4=4875&rsv_sug1=31&oq=chdir&rsv_sug2=0&rsp=0&inputT=570&rsv_sug=1&rsv_n=2
		//http://blog.csdn.net/nstwolf/article/details/5806616
		//http://www.php100.com/html/webkaifa/PHP/PHP/2009/1223/3695.html
		//这个magic_quotes就是会自动把那些由$_GET,$_POST等传过来的值进行处理,加\。
	  //这个东西最好不要打开,虽然某些时候会帮到忙,不过过滤、转义等处理最后还是手动做好一些。
	  //php5.3以上默认是把这个东西关掉的(linux下/etc/php.ini里面)。这个东西本来就不应该有。
	}

/*
 * ------------------------------------------------------
 *  Set the subclass_prefix
 * ------------------------------------------------------
 *
 * Normally the "subclass_prefix" is set in the config file.
 * The subclass prefix allows CI to know if a core class is
 * being extended via a library in the local application
 * "libraries" folder. Since CI allows config items to be
 * overriden via data set in the main index. php file,
 * before proceeding we need to know if a subclass_prefix
 * override exists.  If so, we will set this value now,
 * before any classes are loaded
 * Note: Since the config file data is cached it doesn't
 * hurt to load it here.
 */
	if (isset($assign_to_config['subclass_prefix']) AND $assign_to_config['subclass_prefix'] != '')
	{//|设置子文件,扩展类的前缀
		get_config(array('subclass_prefix' => $assign_to_config['subclass_prefix']));
	}
/*
isset函数是检测变量是否设置。
格式:bool isset ( mixed var [, mixed var [, ...]] )
返回值:
若变量不存在则返回 FALSE 
若变量存在且其值为NULL,也返回 FALSE 
若变量存在且值不为NULL,则返回 TURE 
同时检查多个变量时,每个单项都符合上一条要求时才返回 TRUE,否则结果为 FALSE
如果已经使用 unset() 释放了一个变量之后,它将不再是 isset()。若使用 isset() 测试一个被设置成 NULL 的变量,将返回 FALSE。
同时要注意的是一个 NULL 字节("\0")并不等同于 PHP 的 NULL 常数。
警告: isset() 只能用于变量,因为传递任何其它参数都将造成解析错误。若想检测常量是否已设置,可使用 defined() 函数。

这个get_config($replace)就是从配置文件里面读取信息,这里是读取config/config.php中的配置信息
  这个参数$replace的作用是什么呢?就是临时把修改配置文件的意思,注意并没有从改变文件的值,这个改变只是
  停留在内存的层面上。
  而$assign_to_config['xxx'];是在index.php中定义的一个配置信息数组,这个配置数组要优先权要大于配置文件当中的。
  所以这个判断的作用是,看看有没有在index.php里面定义了 $assign_to_config['subclass_prefix'],如果有的话,
  就那把配置文件中的$config['subclass_prefix']的值改成这个。
*/


/*
 * ------------------------------------------------------
 *  Set a liberal script execution time limit
 * ------------------------------------------------------
 */
	if (function_exists("set_time_limit") == TRUE AND @ini_get("safe_mode") == 0)
	{//ini_get是获取php.ini里的环境变量的值.  经查没有safe_mode
		@set_time_limit(300);
	}

/*
 * ------------------------------------------------------
 *  Start the timer... tick tock tick tock...
 * ------------------------------------------------------
 */
//load_class()是用来加载类的,准确来说,是用来取得某个类的一个单一实例。
 //像下来的调用就是返回system/core/Benchmark的一个实例。core里面的大都是CI的组件。
 
 /**
  * Benchmark是基准点的意思。
  * 其实这个类的功能很简单,只是用来计算程序运行消耗的时间和内存而已。
  * 以后经常在某个地方冒出来句$BM->mark('xxx');这个作用就是记录运行到当前位置的时候的时间点。
  * 通过两个时间点,就可以计算出时间了。
 */
	$BM =& load_class('Benchmark', 'core');
	$BM->mark('total_execution_time_start'); //  core/Benchmark.php ->function mark()
	$BM->mark('loading_time:_base_classes_start');

/*
 * ------------------------------------------------------
 *  Instantiate the hooks class
 * ------------------------------------------------------
 */
 //取得Hooks组件。
 /**
  * 这个hook也是非常非常棒的一个东西!它可以让我们很好地扩展和改造CI~
  * 可以这样理解,一个应用从运行到结束这个期间,CI为我们保留了一些位置,在这些位置上面可以让开发人员放上所谓的
  * “钩子”(其实就是一段程序啦!),在应用运行过程中,当运行到有可以放钩子的位置的时候,先检测开发人员有没有
  * 实现这里的钩子,如果有就运行它。
  * 有些地方甚至可以用自己写的钩子程序替代CI框架本来的程序。
  */
	$EXT =& load_class('Hooks', 'core');

/*
 * ------------------------------------------------------
 *  Is there a "pre_system" hook?
 * ------------------------------------------------------
 */
 //这里就有一个钩子啦。大概意思是:整个应用系统开始动了,这里要不要先让开发人员来一段程序?
 //如果你定义了pre_system这个钩子,那么,其实它就是在这个位置运行的。
	$EXT->_call_hook('pre_system');

/*
 * ------------------------------------------------------
 *  Instantiate the config class
 * ------------------------------------------------------
 */
 //取得配置组件。
	$CFG =& load_class('Config', 'core');

	// Do we have any manually set config items in the index.php file?
	//如果有在index.php定义配置数组,那么就丢给配置组件CFG,以后就由CFG来保管了配置信息了。
	if (isset($assign_to_config))
	{
		$CFG->_assign_to_config($assign_to_config);
	}

/*
 * ------------------------------------------------------
 *  Instantiate the UTF-8 class
 * ------------------------------------------------------
 *
 * Note: Order here is rather important as the UTF-8
 * class needs to be used very early on, but it cannot
 * properly determine if UTf-8 can be supported until
 * after the Config class is instantiated.
 *
 */
//取得UTF-8组件,这里暂时不用管它。
	$UNI =& load_class('Utf8', 'core');

/*
 * ------------------------------------------------------
 *  Instantiate the URI class
 * ------------------------------------------------------
 */
 //取得URI组件。
	$URI =& load_class('URI', 'core');

/*
 * ------------------------------------------------------
 *  Instantiate the routing class and set the routing
 * ------------------------------------------------------
 */
     //取得URI的好基友RTR
	$RTR =& load_class('Router', 'core');
	$RTR->_set_routing();
	//RTR的这个_set_routing();其实做了非常多的事情。。详见core/Router.php。非常重要。

	// Set any routing overrides that may exist in the main index file
	if (isset($routing))
	{
		//这个$routing是在index.php入口文件中可以配置的一个数组。这里起到路由覆盖的作用。
  //index.php里面配置的信息永远都是最优先的。
  //在这里无论你请求的路由是什么,只要有配置$routing(当然要配对),就会被它重定向。
  //所以我觉得这句话放在这个地方有点坑,上面_set_routing搞了那么久,一下子就被覆盖掉了。(index.php里面的routing已经被注释了,应该不坑吧?)
		$RTR->_set_overrides($routing);
	}

/*
 * ------------------------------------------------------
 *  Instantiate the output class
 * ------------------------------------------------------
 */
	$OUT =& load_class('Output', 'core');
	//输出组件。这个输出组件有什么用?输出不是$this->load->view()么?其实它们两个也是好基友。
 //详见:core/Output.php core/Loader.php

/*
 * ------------------------------------------------------
 *	Is there a valid cache file?  If so, we're done...
 * ------------------------------------------------------
 */
//下面是输出缓存的处理,这里允许我们自己写个hook来取替代CI原来Output类的缓存输出。
	if ($EXT->_call_hook('cache_override') === FALSE)
	{
		if ($OUT->_display_cache($CFG, $URI) == TRUE)
		{
			exit;//如果可以输出缓存,那么就没有必要再做其它事了。输出结果后直接退出。
		}
	}

/*
 * -----------------------------------------------------
 * Load the security class for xss and csrf support
 * -----------------------------------------------------
 */
//取得安全组件(安全组件暂时不详讲,因为对于CI一个运作流程来说,
//它不是必要的。CI的安全处理以后会作为一个新话题来探讨)
	$SEC =& load_class('Security', 'core');

/*
 * ------------------------------------------------------
 *  Load the Input class and sanitize globals
 * ------------------------------------------------------
 */
//取得安全组件的好基友INPUT组件。
//(主要是结合安全组件作一些输入方面的安全处理,$this->input->post()这些常用的操作都是
 //由它们两个负责的。)
	$IN	=& load_class('Input', 'core');

/*
 * ------------------------------------------------------
 *  Load the Language class
 * ------------------------------------------------------
 */
//语言组件。
	$LANG =& load_class('Lang', 'core');

/*
 * ------------------------------------------------------
 *  Load the app controller and local controller
 * ------------------------------------------------------
 *
 */
	// Load the base controller class
//引入控制器父类文件。这里和其它组件的引入方式不一样,
//用load_class();因为我们最终用到的并不是这个父类,
 //而是我们自己写在application/controllers/下的某个由uri请求的控制器。
	require BASEPATH.'core/Controller.php';

//定义get_instance();方法,通过调用CI_Controller::get_instance()可以实现单例化,
 //调用此函数可方便以后直接取得当前应用控制器。
	function &get_instance()
	{
		return CI_Controller::get_instance();
	}


//和其它组件一样,控制器父类同样可以通过前缀的方式进行扩展。
	if (file_exists(APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php'))
	{
		require APPPATH.'core/'.$CFG->config['subclass_prefix'].'Controller.php';
	}

	// Load the local application controller
	// Note: The Router class automatically validates the controller path using the router->_validate_request().
	// If this include fails it means that the default controller in the Routes.php file is not resolving to something valid.
	if ( ! file_exists(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php'))
	{
		//其实如果能够进入这里,说明了上面的$RTR->_set_routing();在_validate_request()的时候
		//一定是在请求默认控制器。
       //详见:core/Router.php
		show_error('Unable to load your default controller. Please make sure the controller specified in your Routes.php file is valid.');
	}

 //引入我们最终确认的应用控制器。
	include(APPPATH.'controllers/'.$RTR->fetch_directory().$RTR->fetch_class().'.php');

	// Set a mark point for benchmarking
	$BM->mark('loading_time:_base_classes_end');
	//这里mark一下,说明CI的所需要的基本的类都加载完了。












/*
 * ------------------------------------------------------
 *  Security check
 * ------------------------------------------------------
 *
 *  None of the functions in the app controller or the
 *  loader class can be called via the URI, nor can
 *  controller functions that begin with an underscore
 *  下面主要进行一些方法上的验证。
 *  因为毕竟我们是通过URI直接调用控制器里面的方法的,其实这是个很危险的事情。
 *  必须要保证我们原本没想过要通过URI访问的方法不能访问。
 *  
 *  CI里面规定以_下划线开头的方法,一般是作为非公开的方法,即使方法定义为public。
 *  其实不仅仅是CI这么做,把非公开的方法名以_开头,是很好的一种规范。
 *  第二个就是父类CI_Controller里面的方法也是不允许通过URI访问的。
 *  如果URI请求这样的方法,那么会作为404处理。
 */
	$class  = $RTR->fetch_class();
	$method = $RTR->fetch_method();

	if ( ! class_exists($class)
		OR strncmp($method, '_', 1) == 0
		OR in_array(strtolower($method), array_map('strtolower', get_class_methods('CI_Controller')))
		)
	{
		if ( ! empty($RTR->routes['404_override']))
		{
			$x = explode('/', $RTR->routes['404_override']);
			$class = $x[0];
			$method = (isset($x[1]) ? $x[1] : 'index');
			if ( ! class_exists($class))
			{
				if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
				{
					show_404("{$class}/{$method}");
				}

				include_once(APPPATH.'controllers/'.$class.'.php');
			}
		}
		else
		{
			show_404("{$class}/{$method}");
		}
	}

/*
 * ------------------------------------------------------
 *  Is there a "pre_controller" hook?
 * ------------------------------------------------------
 */
//这里又一个钩子,这个钩子的位置往往都是在一些特殊的位置的,像这里就是发生在实例化控制器前。
	$EXT->_call_hook('pre_controller');

/*
 * ------------------------------------------------------
 *  Instantiate the requested controller
 * ------------------------------------------------------
 */
	// Mark a start point so we can benchmark the controller
	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_start');

	$CI = new $class();//折腾了这么久,终于实例化我们想要的控制器了

/*
 * ------------------------------------------------------
 *  Is there a "post_controller_constructor" hook?
 * ------------------------------------------------------
 */
	$EXT->_call_hook('post_controller_constructor');

/*
 * ------------------------------------------------------
 *  Call the requested method
 * ------------------------------------------------------
 */
	// Is there a "remap" function? If so, we call it instead
	if (method_exists($CI, '_remap'))
	{
		//这个_remap也是一个非常棒的东西!如果有定义,它会优先被调用,
		//在这个方法里面我们可以根据$method和参数,随意
  //做任何处理和加工。即使那个$method不存在。
		$CI->_remap($method, array_slice($URI->rsegments, 2));
	}
	else
	{
		// is_callable() returns TRUE on some versions of PHP 5 for private and protected
		// methods, so we'll use this workaround for consistent behavior
		//如果请求的方法不存在,那么就按404处理。
		if ( ! in_array(strtolower($method), array_map('strtolower', get_class_methods($CI))))
		{
			// Check and see if we are using a 404 override and use it.
			if ( ! empty($RTR->routes['404_override']))
			{
				$x = explode('/', $RTR->routes['404_override']);
				$class = $x[0];
				$method = (isset($x[1]) ? $x[1] : 'index');
				if ( ! class_exists($class))
				{
					if ( ! file_exists(APPPATH.'controllers/'.$class.'.php'))
					{
						show_404("{$class}/{$method}");
					}

					include_once(APPPATH.'controllers/'.$class.'.php');
					unset($CI);//这里是把原来的控制器删掉,改用我们定义的404重定向的类
					$CI = new $class();
				}
			}
			else
			{
				show_404("{$class}/{$method}");
			}
		}

		// Call the requested method.
		// Any URI segments present (besides the class/function) will be passed to the method for convenience
		//终于调用了!!!!!!!!!!!!!!!!!!!!!!!!!!!就在这里。
   //不过,不是打击你,虽然我们请求的控制器的那个方法被调用了,但是实际上,我们想要的输出并没有完全输出来。
   //这就是因为$this->load->view();并不是马上输出结果,而是把结果放到缓冲区,然后最后Output类把它冲出来。
		call_user_func_array(array(&$CI, $method), array_slice($URI->rsegments, 2));
	}


	// Mark a benchmark end point
	$BM->mark('controller_execution_time_( '.$class.' / '.$method.' )_end');

/*
 * ------------------------------------------------------
 *  Is there a "post_controller" hook?
 * ------------------------------------------------------
 */
	$EXT->_call_hook('post_controller');

/*
 * ------------------------------------------------------
 *  Send the final rendered output to the browser
 * ------------------------------------------------------
 */
	if ($EXT->_call_hook('display_override') === FALSE)
	{
		$OUT->_display();
	//这里,把$this->load->view();里面缓冲的输出结果输出,基本上一个流程总算完成了。详见core/Output.php
	}

/*
 * ------------------------------------------------------
 *  Is there a "post_system" hook?
 * ------------------------------------------------------
 */
	$EXT->_call_hook('post_system');

/*
 * ------------------------------------------------------
 *  Close the DB connection if one exists
 * ------------------------------------------------------
 */
	if (class_exists('CI_DB') AND isset($CI->db))
	{
		$CI->db->close();//关闭连接
	}


/* End of file CodeIgniter.php */
/* Location: ./system/core/CodeIgniter.php */


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值