關於yaf的控制器命名,一個糾結的問題(續)

loader相關的步驟,已經補上!


前面寫過一篇《關於yaf的控制器命名,一個糾結的問題》。沒想到yaf羣裏面也有跟我遇到一樣問題的人,分享下解決辦法。


寫完那篇博文後,我嘗試了多種思路,又是SPL又是配置的,歷經了不少坎坷,但還是順利的達成了我想要的目標。還是那句老話,辦法總比困難多。下面,我介紹一下我的這個方案,不過這個方案的基本原理是修改yaf的源碼重新編譯,不喜歡這個方案的同學可以忽略本文了。


What

我們要達成什麼樣的目標?

1、修改yaf控制器的命名規範,如ZF一樣:“控制器類名 = 模塊名_控制器名Controller”,文件命名不變。

2、爲了兼容以前缺省模塊中的代碼,在缺省模塊中,控制器命名規則使用yaf默認的規則,即:“控制器類名 = 控制器名Controller”


How

how之前,我們有必要先弄清Why。

前面的文章裏說過了,yaf之所以不支持根據模塊名來區分控制器,是因爲在yaf 的自動加載過程中對這些特定命名的類(或插件或模型)進行了特殊處理,並且沒有對這些類所屬的模塊加以區分。比如:Module1_IndexController 類繼承了Module1_BaseController類,yaf在分發的時候,會實例化這個控制器,自然也就會想辦法加載它和Base,你可以在yaf_dispatcher.c中找到實例化控制器的函數,它叫做“yaf_dispatcher_get_controller”,並且被聲明爲:

zend_class_entry * yaf_dispatcher_get_controller(char *app_dir, char *module, char *controller, int len, int def_module TSRMLS_DC)


直觀理解,傳遞app的目錄,模塊名、控制名、長度、默認控制器這些個參數,就可以了實例化控制器了。在這個函數中,實現了默認的控制器類命名規則:“控制器類名 = 控制器名Controller”(name_suffix默認值的情況下)。

而Module1_BaseController則是通過yaf_loader中的autoload自定加載的,yaf對控制器、插件和模型的差別化處理正是在這個autoload方法中實現的。所以,我們還得更改這個方法中對於模塊處理的方式,來讓php可以自動的找到Module1_BaseController類。


好了,知道命名規則的實現原理了,我們就有的放矢地修改這個命名規則。寫到這裏我不禁感嘆鳥哥V5啊,所有我們用到的參數都幫我們準備妥當了:除了控制器名、模塊名居然還有缺省模塊名!

萬事具備,只欠東風:

在yaf_dispatcher.c中的yaf_dispatcher_get_controller函數中找到如下代碼:

		if (YAF_G(name_suffix)) {
			class_len = spprintf(&class, 0, "%s%s%s", controller, YAF_G(name_separator), "Controller");
		} else {
			class_len = spprintf(&class, 0, "%s%s%s", "Controller", YAF_G(name_separator), controller);
		}

改爲如下代碼:

		if (YAF_G(name_suffix)) {
			if (def_module){
				class_len = spprintf(&class, 0, "%s%s%s", controller, YAF_G(name_separator), "Controller");
			}else{
				class_len = spprintf(&class, 0, "%s%s%s%s%s", module, "_", controller, YAF_G(name_separator), "Controller");
			}
		} else {
			if (def_module){
				class_len = spprintf(&class, 0, "%s%s%s", "Controller", YAF_G(name_separator), controller);
			}else{
				class_len = spprintf(&class, 0, "%s%s%s%s%s", module, "_", "Controller", YAF_G(name_separator), controller);
			}
		}

代碼的意思直白,不用多說。

下面是autoload方法的修改:

在yaf_loader.c中添加如下靜態函數

/** {{{ static void yaf_loader_category_withmodule(char *class, uint file_name_len, char *directory, char *category TSRMLS_DC)
 */
static void yaf_loader_category_withmodule(char ** file_name, uint file_name_len, char **directory, char *class_name, char *category TSRMLS_DC) {
	char *q, *p, *seg, *temp_classname = NULL, *app_directory;
	uint seg_len = 0 ,separator_len = 0;
	separator_len = YAF_G(name_separator_len);
	app_directory = YAF_G(directory);

	p = class_name;
	temp_classname = estrdup(class_name);
	q = p;

	while (1) {
		while(++q && *q != '_' && *q != '\0');

		if (*q != '\0') {
			seg_len	= q - p;
			seg	 	= estrndup(p, seg_len);
			temp_classname = estrdup(class_name + seg_len + 1);
		}
		break;
	}
	if(seg_len && (yaf_application_is_module_name(seg, seg_len TSRMLS_CC))){
		spprintf(directory, 0, "%s%c%s%c%s%c%s", app_directory, DEFAULT_SLASH,
			YAF_MODULE_DIRECTORY_NAME, DEFAULT_SLASH, seg, DEFAULT_SLASH, category);
		if (YAF_G(name_suffix)) {
			*file_name = estrndup(temp_classname, (file_name_len - seg_len - 1));
		} else {
			*file_name = estrdup(temp_classname + seg_len  + separator_len + 1);
		}
	}
}
/* }}} */

這個函數實現對控制、插件和模型在命名上區分出模塊並改變自定加載的目錄。

然後在,autoload方法中找到形如“/* this is a controller class */”的註釋,再在“break”語句之前的一行,添加如下代碼:

yaf_loader_category_withmodule(&file_name, file_name_len, &directory, class_name, YAF_CONTROLLER_DIRECTORY_NAME TSRMLS_CC);

這行代碼是對上面定義的函數的調用,以影響下文中加載的類名和加載目錄。

好了,保存、重新make、目的達成!

親測可用!O(∩_∩)O哈哈~


Think

整個過程中,總結了一下幾個問題:

1、鳥哥的代碼質量確實好

2、我的C語言功底確實爛

3、修改或編寫C擴展,對於PHPer來說並不是想象的那麼痛苦和困難,試着鑽進去之後,你會發現:你愛PHP,你更愛C語言。


End

到目前爲止,我還不瞭解鳥哥如此設計模塊控制器命名規則的初衷,他有時間的話,一定找他問問明白。順便向他推銷一下文中所述的命名規則,畢竟像ZF靠攏一點的話,更容易拉ZF的粉絲過來用yaf。


另外在yaf的使用過程中,發現一個forward的問題,這個問題是:如果在控制器的init方法中forward,會導致apache(win)或php-fpm(linux)的崩潰,抽空也研究一下。


祝yaf越來越好。

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