Yaf源碼閱讀之請求的處理(二)

上一篇文章 我們分析了Yaf框架的啓動,包括配置文件的初始化以及框架類的加載。本文將分析Yaf

處理一次Web請求的詳細過程,這是一個Web框架最核心的部分,理解了這點,就理解了Yaf的實現原理。

 

(一)框架類的實例化

在上一篇文章提到了Yaf框架類的加載,類加載完成後,還要實例化,

我們逐一分析Yaf各個框架類的構造函數。

 

不急於分析各種調用關係,我們先看下Application啓動後,Yaf的對象關係如圖1-1所示。


   

                                                                圖1-1 Yaf對象關係圖

 

一切構造從 new Yaf_Application() 開始。

 

所有類的實例化都從Yaf_Applicaion的構造函數開始,它主要做了這幾件事:

  • 實例化Config
  • 實例化Request
  • 實例化Dispatcher
  • 實例化Loader

yaf_application.c

PHP_METHOD(yaf_application, __construct) {
	//實例化Config,用於讀取配置信息
	zconfig = yaf_config_instance(NULL, config, section TSRMLS_CC);

	//解析application的配置信息,將配置參數保存到全局變量中
	yaf_application_parse_option(
				zend_read_property(yaf_config_ce,
								   zconfig, 
								   ZEND_STRL(YAF_CONFIG_PROPERT_NAME),
				1 TSRMLS_CC) TSRMLS_CC)

	//實例化Request,保存請求的信息
	request = yaf_request_instance(NULL, YAF_G(base_uri) TSRMLS_CC);

	//實例化分發器Dispatcher,這是Yaf的核心控制類
	zdispatcher = yaf_dispatcher_instance(NULL TSRMLS_CC);
	yaf_dispatcher_set_request(zdispatcher, request TSRMLS_CC);

	//把config實例設置成Application的屬性
	zend_update_property(yaf_application_ce, self, 
						 ZEND_STRL(YAF_APPLICATION_PROPERTY_NAME_CONFIG), 
						 zconfig TSRMLS_CC);

	//把request實例設置成Application的屬性
	zend_update_property(yaf_application_ce, self, 
						 ZEND_STRL(YAF_APPLICATION_PROPERTY_NAME_DISPATCHER), 
						 zdispatcher TSRMLS_CC);

	//創建loader,用於自動加載類
	loader = yaf_loader_instance(NULL, local_library,
					strlen(YAF_G(global_library))? YAF_G(global_library) : NULL TSRMLS_CC);

	//把dispatcher設置成Application的屬性
	zend_update_property(yaf_application_ce, self, 
						 ZEND_STRL(YAF_APPLICATION_PROPERTY_NAME_DISPATCHER), 
						 zdispatcher TSRMLS_CC);
}

 

在Yaf_Application的構造函數中,調用其他類的構造函數時,又會繼續創建它依賴的其它對象,

最終所有框架類都完成實例化,形成圖1-1所示的關係圖,然後就可以開始處理用戶的請求了。

 

(二)處理請求

幾乎所有MVC的Web框架處理一個請求都會走過3個流程,如圖2-1所示。


                                                    圖2-1 MVC框架處理請求的流程圖

 

Yaf也是按照這個流程來處理請求的,更細化一點,Yaf提供了完善的API,

並支持Bootstrap和插件機制,我們引用Yaf官方文檔的流程圖來分析,如圖2-2所示。



                                 

                                  圖2-2 Yaf流程圖

 

從Yaf的流程圖可以看到,一切請求的處理從 run() 方法開始。

yaf_application.c

PHP_METHOD(yaf_application, run) {
	zval *running;
	yaf_dispatcher_t  *dispatcher;
	yaf_response_t	  *response;
	yaf_application_t *self = getThis();

	//確保application的狀態是非running的
	running = zend_read_property(yaf_application_ce, 
								 self, 
								 ZEND_STRL(YAF_APPLICATION_PROPERTY_NAME_RUN), 
								 1 TSRMLS_CC);

	if (IS_BOOL == Z_TYPE_P(running)
			&& Z_BVAL_P(running)) {
		yaf_trigger_error(YAF_ERR_STARTUP_FAILED TSRMLS_CC, 
						  "An application instance already run");
		RETURN_TRUE;
	}

	//把application的狀態置爲running
	ZVAL_BOOL(running, 1);
	zend_update_property(yaf_application_ce, 
						 self, 
						 ZEND_STRL(YAF_APPLICATION_PROPERTY_NAME_RUN),
						 running TSRMLS_CC);

	//調用dispatcher來處理這次請求
	dispatcher = zend_read_property(yaf_application_ce, 
									self, 
									ZEND_STRL(YAF_APPLICATION_PROPERTY_NAME_DISPATCHER), 
									1 TSRMLS_CC);
	if ((response = yaf_dispatcher_dispatch(dispatcher TSRMLS_CC))) {
		RETURN_ZVAL(response, 1, 1);
	}

	RETURN_FALSE;
}

 

可以看出,run() 函數調用了 yaf_dispatcher_dispatch 來處理請求。繼續探索,等等,先劇透下,

我們來看看接下來的函數調用關係圖,如圖2-3所示。


 
                                                     圖2-3 處理請求的函數調用關係圖

 

繼續分析yaf_dispatcher_dispatch,它是處理請求的中心函數,它調用路由器完成路由,

然後根據路由結果,找到對應的action執行,最後把結果發送給客戶端。

yaf_dispatcher.c

yaf_response_t * yaf_dispatcher_dispatch(yaf_dispatcher_t *dispatcher TSRMLS_DC) {
	zval *return_response, *plugins, *view;
	yaf_response_t *response;
	yaf_request_t *request;
	uint nesting = YAF_G(forward_limit);

	//實例化一個Response,用來返回的數據
	response = yaf_response_instance(NULL, sapi_module.name TSRMLS_CC);
	
	//取得request對象
	request	 = zend_read_property(yaf_dispatcher_ce, 
								  dispatcher, 
								  ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_REQUEST), 
								  1 TSRMLS_CC);

	//取得所有插件
	plugins	 = zend_read_property(yaf_dispatcher_ce, dispatcher, ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_PLUGINS), 1 TSRMLS_CC);

	if (IS_OBJECT != Z_TYPE_P(request)) {
		yaf_trigger_error(YAF_ERR_TYPE_ERROR TSRMLS_CC, "Expect a %s instance", yaf_request_ce->name);
		zval_ptr_dtor(&response);
		return NULL;
	}

	//路由此次請求
	if (!yaf_request_is_routed(request TSRMLS_CC)) {
		//在路由開始前,調用各個插件的route_startup方法
		YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_ROUTESTARTUP, request, response);
		YAF_EXCEPTION_HANDLE(dispatcher, request, response);
		
		//開始路由,路由的結果是找到處理此次請求的module、controller和action
		//這幾個結果分別保存在Request對象的$module、$controller和$action變量中
		if (!yaf_dispatcher_route(dispatcher, request TSRMLS_CC)) {
			yaf_trigger_error(YAF_ERR_ROUTE_FAILED TSRMLS_CC, "Routing request failed");
			YAF_EXCEPTION_HANDLE_NORET(dispatcher, request, response);
			zval_ptr_dtor(&response);
			return NULL;
		}
		
		yaf_dispatcher_fix_default(dispatcher, request TSRMLS_CC);
		
		//在路由完成後,調用各個插件的route_shutdown方法
		YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_ROUTESHUTDOWN, request, response);
		YAF_EXCEPTION_HANDLE(dispatcher, request, response);
		(void)yaf_request_set_routed(request, 1 TSRMLS_CC);
	} else {
		yaf_dispatcher_fix_default(dispatcher, request TSRMLS_CC);
	}

	//在路由結束後,分發循環之前調用各個插件的loop_startup函數
	YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_LOOPSTARTUP, request, response);
	YAF_EXCEPTION_HANDLE(dispatcher, request, response);

	//初始化view
	view = yaf_dispatcher_init_view(dispatcher, NULL, NULL TSRMLS_CC);
	if (!view) {
		return NULL;
	}

	//進入分發循環,最多循環 application.forward_limit 次
	do {
		//在請求分發之前執行各個插件的pre_dispatch方法
		YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_PREDISPATCH, request, response);
		
		//調用yaf_dispatcher_handle處理請求
		if (!yaf_dispatcher_handle(dispatcher, request, response, view TSRMLS_CC)) {
			YAF_EXCEPTION_HANDLE(dispatcher, request, response);
			zval_ptr_dtor(&response);
			return NULL;
		}
		yaf_dispatcher_fix_default(dispatcher, request TSRMLS_CC);
		
		//在請求分發結束後,執行各個插件的post_dispatch方法
		YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_POSTDISPATCH, request, response);
		YAF_EXCEPTION_HANDLE(dispatcher, request, response);
	} while (--nesting > 0 && !yaf_request_is_dispatched(request TSRMLS_CC));

	//在分發循環結束後,執行各個插件的loop_shutdown方法
	YAF_PLUGIN_HANDLE(plugins, YAF_PLUGIN_HOOK_LOOPSHUTDOWN, request, response);
	YAF_EXCEPTION_HANDLE(dispatcher, request, response);

	if (0 == nesting && !yaf_request_is_dispatched(request TSRMLS_CC)) {
		yaf_trigger_error(YAF_ERR_DISPATCH_FAILED TSRMLS_CC, "The max dispatch nesting %ld was reached", YAF_G(forward_limit));
		YAF_EXCEPTION_HANDLE_NORET(dispatcher, request, response);
		zval_ptr_dtor(&response);
		return NULL;
	}

	//取出返回給客戶端的數據
	return_response = zend_read_property(yaf_dispatcher_ce, dispatcher, ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_RETURN), 1 TSRMLS_CC);

	if (!Z_BVAL_P(return_response)) {
		//將數據發送給客戶端
		(void)yaf_response_send(response TSRMLS_CC);
		yaf_response_clear_body(response, NULL, 0 TSRMLS_CC);
	}

	return response;
}

 

 路由的工作交給 yaf_dispatcher_route,這裏不分析路由的詳細過程,只需瞭解,路由的目的是找出

處理此次請求的module、controller和action,並把這些信息保存在Request對象中。接下來,由

yaf_dispatcher_handle函數去調用action的方法來處理請求。我們繼續分析 yaf_dispatcher_handle。

yaf_dispatcher.c

int yaf_dispatcher_handle(yaf_dispatcher_t *dispatcher, yaf_request_t *request,  yaf_response_t *response, yaf_view_t *view TSRMLS_DC) {
	zend_class_entry *request_ce;
	char *app_dir = YAF_G(directory);

	request_ce = Z_OBJCE_P(request);

	yaf_request_set_dispatched(request, 1 TSRMLS_CC);
	if (!app_dir) {
		yaf_trigger_error(YAF_ERR_STARTUP_FAILED TSRMLS_CC, "%s requires %s(which set the application.directory) to be initialized first",
				yaf_dispatcher_ce->name, yaf_application_ce->name);
		return 0;
	} else {
		int	is_def_module = 0;
		/* int is_def_ctr = 0; */
		zval *module, *controller, *dmodule, *dcontroller, *instantly_flush;
		zend_class_entry *ce;
		yaf_controller_t *executor;
		zend_function    *fptr;

		//從Request對象中讀取路由結果(module、controller)
		module		= zend_read_property(request_ce, request, ZEND_STRL(YAF_REQUEST_PROPERTY_NAME_MODULE), 1 TSRMLS_CC);
		controller	= zend_read_property(request_ce, request, ZEND_STRL(YAF_REQUEST_PROPERTY_NAME_CONTROLLER), 1 TSRMLS_CC);

		dmodule		= zend_read_property(yaf_dispatcher_ce, dispatcher, ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_MODULE), 1 TSRMLS_CC);
		dcontroller = zend_read_property(yaf_dispatcher_ce, dispatcher, ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_CONTROLLER), 1 TSRMLS_CC);

		if (Z_TYPE_P(module) != IS_STRING
				|| !Z_STRLEN_P(module)) {
			yaf_trigger_error(YAF_ERR_DISPATCH_FAILED TSRMLS_CC, "Unexcepted a empty module name");
			return 0;
		} else if (!yaf_application_is_module_name(Z_STRVAL_P(module), Z_STRLEN_P(module) TSRMLS_CC)) {
			yaf_trigger_error(YAF_ERR_NOTFOUND_MODULE TSRMLS_CC, "There is no module %s", Z_STRVAL_P(module));
			return 0;
		}

		if (Z_TYPE_P(controller) != IS_STRING
				|| !Z_STRLEN_P(controller)) {
			yaf_trigger_error(YAF_ERR_DISPATCH_FAILED TSRMLS_CC, "Unexcepted a empty controller name");
			return 0;
		}

		if(strncasecmp(Z_STRVAL_P(dmodule), Z_STRVAL_P(module), Z_STRLEN_P(module)) == 0) {
			is_def_module = 1;
		}

		/* if (strncasecmp(Z_STRVAL_P(dcontroller), Z_STRVAL_P(controller), Z_STRLEN_P(controller)) == 0) {
			is_def_ctr = 1;
		} */

		//實例化Controller對象
		ce = yaf_dispatcher_get_controller(app_dir, Z_STRVAL_P(module), Z_STRVAL_P(controller), Z_STRLEN_P(controller), is_def_module TSRMLS_CC);
		if (!ce) {
			return 0;
		} else {
			zval  *action, *render, *ret = NULL;
			char  *action_lower, *func_name, *view_dir;
			uint  func_name_len;

			yaf_controller_t *icontroller;

			MAKE_STD_ZVAL(icontroller);
			object_init_ex(icontroller, ce);

			/* cause controller's constructor is a final method, so it must be a internal function
			   do {
			   zend_function *constructor = NULL;
			   constructor = Z_OBJ_HT_P(exec_ctr)->get_constructor(exec_ctr TSRMLS_CC);
			   if (constructor != NULL) {
			   if (zend_call_method_with_2_params(&exec_ctr, *ce
			   , &constructor, NULL, &ret, request, response) == NULL) {
			   yaf_trigger_error(YAF_ERR_CALL_FAILED, "function call for %s::__construct failed", (*ce)->name);
			   return 0;
			   }
			   }
			   } while(0);
			   */
			yaf_controller_construct(ce, icontroller, request, response, view, NULL TSRMLS_CC);
			if (EG(exception)) {
				zval_ptr_dtor(&icontroller);
				return 0;
			}
		
			/* view template directory for application, please notice that view engine's directory has high priority */
			if (is_def_module) {
				spprintf(&view_dir, 0, "%s%c%s", app_dir, DEFAULT_SLASH, "views");
			} else {
				spprintf(&view_dir, 0, "%s%c%s%c%s%c%s", app_dir, DEFAULT_SLASH, "modules", DEFAULT_SLASH, Z_STRVAL_P(module), DEFAULT_SLASH, "views");
			}

			if (YAF_G(view_directory)) {
				efree(YAF_G(view_directory));
			}
			YAF_G(view_directory) = view_dir;

			zend_update_property(ce, icontroller, ZEND_STRL(YAF_CONTROLLER_PROPERTY_NAME_NAME),	controller TSRMLS_CC);

			//從Request對象中讀取路由結果(action)
			action		 = zend_read_property(request_ce, request, ZEND_STRL(YAF_REQUEST_PROPERTY_NAME_ACTION), 1 TSRMLS_CC);
			action_lower = zend_str_tolower_dup(Z_STRVAL_P(action), Z_STRLEN_P(action));

			php_printf("dispatcher action_lower = %s\n", action_lower);

			/* because the action might call the forward to override the old action */
			Z_ADDREF_P(action);

			func_name_len = spprintf(&func_name,  0, "%s%s", action_lower, "action");
			efree(action_lower);

			php_printf("dispatcher func_name = %s\n", func_name);

			if (zend_hash_find(&((ce)->function_table), func_name, func_name_len + 1, (void **)&fptr) == SUCCESS) {
				//Action是一個定義在Controller類中的成員方法,
				//就直接調用Controller定義的action方法
				uint count = 0;
				zval ***call_args = NULL;

				ret = NULL;

				executor = icontroller;
				if (fptr->common.num_args) {
					zval *method_name;

					yaf_dispatcher_get_call_parameters(request_ce, request, fptr, &call_args, &count TSRMLS_CC);
					MAKE_STD_ZVAL(method_name);
					ZVAL_STRINGL(method_name, func_name, func_name_len, 0);

					call_user_function_ex(&(ce)->function_table, &icontroller, method_name, &ret, count, call_args, 1, NULL TSRMLS_CC);

					efree(method_name);
					efree(call_args);
				} else {
					zend_call_method(&icontroller, ce, NULL, func_name, func_name_len, &ret, 0, NULL, NULL TSRMLS_CC);
				}

				efree(func_name);

				if (!ret) {
					zval_ptr_dtor(&action);
					zval_ptr_dtor(&icontroller);
					return 0;
				}

				if ((Z_TYPE_P(ret) == IS_BOOL
							&& !Z_BVAL_P(ret))) {
					/* no auto-render */
					zval_ptr_dtor(&ret);
					zval_ptr_dtor(&action);
					zval_ptr_dtor(&icontroller);
					return 1;
				}
				zval_ptr_dtor(&ret);
			} else if ((ce = yaf_dispatcher_get_action(app_dir, icontroller,
							Z_STRVAL_P(module), is_def_module, Z_STRVAL_P(action), Z_STRLEN_P(action) TSRMLS_CC))
					&& (zend_hash_find(&(ce)->function_table, YAF_ACTION_EXECUTOR_NAME,
							sizeof(YAF_ACTION_EXECUTOR_NAME), (void **)&fptr) == SUCCESS)) {
				//Action是一個獨立的類,就實例化這個類,調用其execute方法
				zval ***call_args;
				yaf_action_t *iaction;
				uint count = 0;

				efree(func_name);

				MAKE_STD_ZVAL(iaction);
				object_init_ex(iaction, ce);

				yaf_controller_construct(ce, iaction, request, response, view, NULL TSRMLS_CC);
				executor = iaction;

				zend_update_property(ce, iaction, ZEND_STRL(YAF_CONTROLLER_PROPERTY_NAME_NAME), controller TSRMLS_CC);
				zend_update_property(ce, iaction, ZEND_STRL(YAF_ACTION_PROPERTY_NAME_CTRL), icontroller TSRMLS_CC);
				zval_ptr_dtor(&icontroller);

				if (fptr->common.num_args) {
					zval *method_name = NULL;

					yaf_dispatcher_get_call_parameters(request_ce, request, fptr, &call_args, &count TSRMLS_CC);
					MAKE_STD_ZVAL(method_name);
					ZVAL_STRINGL(method_name, YAF_ACTION_EXECUTOR_NAME, sizeof(YAF_ACTION_EXECUTOR_NAME) - 1, 0);

					call_user_function_ex(&(ce)->function_table, &iaction, method_name, &ret, count, call_args, 1, NULL TSRMLS_CC);

					efree(method_name);
					efree(call_args);
				} else {
					zend_call_method_with_0_params(&iaction, ce, NULL, "execute", &ret);
				}

				if (!ret) {
					zval_ptr_dtor(&action);
					zval_ptr_dtor(&iaction);
					return 0;
				}

				if (( Z_TYPE_P(ret) == IS_BOOL
							&& !Z_BVAL_P(ret))) {
					/* no auto-render */
					zval_ptr_dtor(&ret);
					zval_ptr_dtor(&action);
					zval_ptr_dtor(&iaction);
					return 1;
				}
				zval_ptr_dtor(&ret);
			} else {
				efree(func_name);
				zval_ptr_dtor(&icontroller);
				return 0;
			}

			if (executor) {
				/* controller's property can override the Dispatcher's */
				int auto_render = 1;
				render = zend_read_property(ce, executor, ZEND_STRL(YAF_CONTROLLER_PROPERTY_NAME_RENDER), 1 TSRMLS_CC);
				instantly_flush	= zend_read_property(yaf_dispatcher_ce, dispatcher, ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_FLUSH), 1 TSRMLS_CC);
				if (render == EG(uninitialized_zval_ptr)) {
					render = zend_read_property(yaf_dispatcher_ce, dispatcher, ZEND_STRL(YAF_DISPATCHER_PROPERTY_NAME_RENDER), 1 TSRMLS_CC);
					auto_render = Z_BVAL_P(render);
				} else if (Z_TYPE_P(render) <= IS_BOOL && !Z_BVAL_P(render)) {
					auto_render = 0;
				}

				if (auto_render) {
					ret = NULL;
					if (!Z_BVAL_P(instantly_flush)) {
						zend_call_method_with_1_params(&executor, ce, NULL, "render", &ret, action);
						zval_ptr_dtor(&executor);

						if (!ret) {
							zval_ptr_dtor(&action);
							return 0;
						} else if (IS_BOOL == Z_TYPE_P(ret) && !Z_BVAL_P(ret)) {
							zval_ptr_dtor(&ret);
							zval_ptr_dtor(&action);
							return 0;
						}

						if (Z_TYPE_P(ret) == IS_STRING && Z_STRLEN_P(ret)) {
							yaf_response_alter_body(response, NULL, 0, Z_STRVAL_P(ret), Z_STRLEN_P(ret), YAF_RESPONSE_APPEND  TSRMLS_CC);
						} 

						zval_ptr_dtor(&ret);
					} else {
						zend_call_method_with_1_params(&executor, ce, NULL, "display", &ret, action);
						zval_ptr_dtor(&executor);

						if (!ret) {
							zval_ptr_dtor(&action);
							return 0;
						}

						if ((Z_TYPE_P(ret) == IS_BOOL && !Z_BVAL_P(ret))) {
							zval_ptr_dtor(&ret);
							zval_ptr_dtor(&action);
							return 0;
						}
						zval_ptr_dtor(&ret);
					}
				} else {
					zval_ptr_dtor(&executor);
				}
			}
			zval_ptr_dtor(&action);
		}
		return 1;
	}
	return 0;
}

 

處理請求之前,yaf_dispatcher_handle函數首先從Request對象讀取路由結果

(module、controller和action)

然後實創建controller對象,執行action定義的邏輯處理請求。需要注意的是,

Yaf編寫Action有兩種方式:

(1)把action方法寫在Controller類中

 

class Controller_Index extends Yaf_Controller_Abstract {
    public function indexAction () {
        echo "Hello, World!";
    }
}

 

(2)在Controller對象的$actions保存所有action序對,序對的鍵必須與路由結果的action名字一樣,

         而且是小寫,序對的值是action類文件的相對路徑,然後在action類的execute方法中寫處理邏輯。

 

    controllers/Index.php

class Controller_Index extends Yaf_Controller_Abstract {
    public $actions = array (
        'index' => 'actions/Index.php',
    );
}

   actions/Index.php

class Action_Index extends Yaf_Action_Abstract {
    public function execute () {
        echo "Hello, World!";
    }
}

 

從源碼中可以看出,Yaf優先從Controller中尋找action方法,如果找到,就執行之,

沒有找到再從Controller中定義的$actions數組中尋找Action類,實例化這個Action類,

並執行其execute方法。

 

至此,Yaf的請求處理分析差不多了,下一篇文章將會分析Yaf的路由機制。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章