万源之源之drupal7

(注:学习交流用)

学习joomla的时候,看过一位前辈写的万源之源的帖子,让我记忆深刻,最新工作需要用到了drupal,所以把学习笔记整理出来,借用了一下“万源之源”的标题。

对于程序的认识每个人都有可能不同,特别是一套相对成熟的框架,但是不管怎么理解或者怎么解释一段程序,唯一不变的就是研究对象(代码),所以需一切从代码开始。

drupal的强大毋庸置疑,本章内容对于整个drupal仅仅是最小的一部分,由于本人也是初学,有错误的地方请大家理解,并且在您方便的时候帮忙指正一下,万分感激

<?php
//定义drupal的根目录
define('DRUPAL_ROOT', getcwd());
//引入引导文件
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';

//加载drupal,在这里定义drupal全部加载。其他的方式可以具体查看这个函数的执行过程。
//这个过程就像是计算机的操作系统,如果要使用一台计算机,那么这台计算机一定要先运行
//操作系统,然后才能正常使用,我们可以选择这台计算机的启动方式
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

//drupal系统已经准备好了,现在开始执行网站程序。
menu_execute_active_handler();

//按照页面的执行逻辑,解释下面的参数
function menu_execute_active_handler($path = NULL, $deliver = TRUE){
	// Check if site is offline.
	//判断网站是否是offline
	$page_callback_result = _menu_site_is_offline() ? MENU_SITE_OFFLINE : MENU_SITE_ONLINE;

	// Allow other modules to change the site status but not the path because that
	// would not change the global variable. hook_url_inbound_alter() can be used
	// to change the path. Code later will not use the $read_only_path variable.
	$read_only_path = !empty($path) ? $path : $_GET['q'];
	//触发 menu_site_status_alter hook, 可以让module根据当前的路径修改网站的状态
	drupal_alter('menu_site_status', $page_callback_result, $read_only_path);
	
	/** Only continue if the site status is not set.
	只有在网站正常运行的时候,才执这段代码,这段代码的左右是根据 $path(现在这里是$_GET)在数据库menu_router
	表里找到路由参数,在参数中,page_callback代表可以得到本页面的主要信息内容的函数,page_arguments代表所
	需要的参数。
	menu_router 表的更新工作是在 menu_rebuild() 的函数执行的,这个函数只有在需要的时候执行,比如第一次网站
	被访问的时候或是clearn cache的时候
	在得到menu_get_item函数中, 查到相应的路由参数后,执行了
	    drupal_alter('menu_get_item', $router_item, $path, $original_map);
	可以使module修改这个路由参数。*/
	if ($page_callback_result == MENU_SITE_ONLINE) {
		if ($router_item = menu_get_item($path)) {
		if ($router_item['access']) {
			if ($router_item['include_file']) {
			require_once DRUPAL_ROOT . '/' . $router_item['include_file'];
			}

			$page_callback_result = call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
		}
		else {
			$page_callback_result = MENU_ACCESS_DENIED;
		}
		}
		else {
		$page_callback_result = MENU_NOT_FOUND;
		}
	}

	// Deliver the result of the page callback to the browser, or if requested,
	// return it raw, so calling code can do more processing.
	//是否返回浏览器所需要的整体HTML结构,还是仅仅返回页面的主要内容,这里要是返回给浏览器输出的
	if ($deliver) {
		$default_delivery_callback = (isset($router_item) && $router_item) ? $router_item['delivery_callback'] : NULL;
		//这句可以理解成,drupal 交付页面
		drupal_deliver_page($page_callback_result, $default_delivery_callback);
	}
	else {
		return $page_callback_result;
	}
}
/**
 * Delivers a page callback result to the browser in the appropriate format.
 *
 * This function is most commonly called by menu_execute_active_handler(), but
 * can also be called by error conditions such as drupal_not_found(),
 * drupal_access_denied(), and drupal_site_offline().
 *
 * 这个函数的功能就是生成交付给浏览器的最终的HTML。
 * 
 * 在这个函数中用运行了一次$router_item = menu_get_item();别担心,因为在上个函数中已经运行过
 * 这个函数了,已经把所需要的路由参数放到cache中了,所以这次得到的是cache中的路由参数
 * 
 * 在得到交付函数后,执行了
 * 	drupal_alter('page_delivery_callback', $delivery_callback);
 * 代表可以在module里改变交付函数,作用是我们可以在不改变源代码的情况下修改它。
 */
function drupal_deliver_page($page_callback_result, $default_delivery_callback = NULL){
	if (!isset($default_delivery_callback) && ($router_item = menu_get_item())) {
		$default_delivery_callback = $router_item['delivery_callback'];
	}
	$delivery_callback = !empty($default_delivery_callback) ? $default_delivery_callback : 'drupal_deliver_html_page';
	// Give modules a chance to alter the delivery callback used, based on
	// request-time context (e.g., HTTP request headers).
	drupal_alter('page_delivery_callback', $delivery_callback);
	if (function_exists($delivery_callback)) {
		//这里是默认的 drupal_deliver_html_page() 函数。
		$delivery_callback($page_callback_result);
	}
	else {
		// If a delivery callback is specified, but doesn't exist as a function,
		// something is wrong, but don't print anything, since it's not known
		// what format the response needs to be in.
		watchdog('delivery callback not found', 'callback %callback not found: %q.', array('%callback' => $delivery_callback, '%q' => $_GET['q']), WATCHDOG_ERROR);
	}
}

/**
 * Packages and sends the result of a page callback to the browser as HTML.
 *
 * 默认的交付函数,根据页面回调函数结果得到的最终的HTML。
 */
function drupal_deliver_html_page($page_callback_result){
	// Emit the correct charset HTTP header, but not if the page callback
	// result is NULL, since that likely indicates that it printed something
	// in which case, no further headers may be sent, and not if code running
	// for this page request has already set the content type header.
	
	/**
	 *如果有返回结果,并且还没有 Content-Type 则添加默认的 Content-Type
	 */
	if (isset($page_callback_result) && is_null(drupal_get_http_header('Content-Type'))) {
		drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
	}

	// Send appropriate HTTP-Header for browsers and search engines.
	/**
	 *添加页面的当前语言
	 */
	global $language;
	drupal_add_http_header('Content-Language', $language->language);

	// Menu status constants are integers; page content is a string or array.
	/**
	 *如果没有页面的主要内容,则执行错误处理,以为每个页面都有一个主要内容,如果没有
	 *找到这个需要的内容,则代表页面无效。
	 *
	 *@$page_callback_result 正常情况下是一个字符串或者数组,如果是数字,则代表
	 *是一个错误代号,根据这个错误代码执行相应的处理方法
	 */
	if (is_int($page_callback_result)) {
		/*。。。。。。。*/
	}elseif (isset($page_callback_result)) {
		// Print anything besides a menu constant, assuming it's not NULL or
		// undefined.
		/**
		 *渲染渲染整个页面。
		 */
		print drupal_render_page($page_callback_result);
	}
	
	// Perform end-of-request tasks.
	drupal_page_footer();
}

/**
 * Renders the page, including all theming.
 * 
 * 渲染整个页面,包括所有的主题内容
 */
function drupal_render_page($page) {
	//如果看了代码的引导阶段的话,你会发现很多地方用到了 drupal_static 函数,这个函数的功能可以
	//理解成扩展的static 变量,他可以让其他的函数访问并修改本函数定义的静态变量,从而实现了对象
	//属性的功能。
	$main_content_display = &drupal_static('system_main_content_added', FALSE);

	// Allow menu callbacks to return strings or arbitrary arrays to render.
	// If the array returned is not of #type page directly, we need to fill
	// in the page with defaults.
	if (is_string($page) || (is_array($page) && (!isset($page['#type']) || ($page['#type'] != 'page')))) {
		/**
		 * 这个函数的作用是设置页面的主要内容,经过这个函数,我们可以在以后通过 
		 *    drupal_set_page_content()//注意是不带参数的
		 * 得到这里设置的主要内容,但是得到以后,system_main_content_added就会自动设置等TRUE,
		 * 代表我们已经提取过页面的主要内容了
		 */
		drupal_set_page_content($page);
		/**
		 * Retrieves the default properties for the defined element type.
		 * function element_info($type){...}
		 * 这个函数触发了 module_invoke_all('element_info') hook,并把他缓存到cache里.
		 * 
		 * 这里得到page的element_info
		 */
		$page = element_info('page');
		
	}

	// Modules can add elements to $page as needed in hook_page_build().
	/**
	 * 等于句代码 module_invoke_all($hook),允许module 参与page的生成,也可以根据$page的内容定义自己的逻辑
	 */
	foreach (module_implements('page_build') as $module) {
		$function = $module . '_page_build';
		$function($page);
	}
	// Modules alter the $page as needed. Blocks are populated into regions like
	// 'sidebar_first', 'footer', etc.
	/**
	 *在生成$page之后,允许module修改 $page的参数(看到hook在drupal中的地位了吧,几乎所有的逻辑都是通过它实现的,
	 *这也是drupal的自由之处,往往自用和代码逻辑已经分布联系非常紧密,你写的代码可以让别人二次开发么?这就需要经验
	 *规划了)
	 */
	drupal_alter('page', $page);

	// If no module has taken care of the main content, add it to the page now.
	// This allows the site to still be usable even if no modules that
	// control page regions (for example, the Block module) are enabled.
	/**
	 *前面介绍过,如果还没有提取过页面的主要内容,我们在这里提取页面的主要内容,
	 *前面可以在hook里用 drupal_set_page_content() 提取页面的主要内容,如果提取了,下面将不会再一次提取。
	 */
	if (!$main_content_display) {
		$page['content']['system_main'] = drupal_set_page_content();
	}
	
	/**
	 *根据$page内容渲染页面, 也就是返回$page 的 HTML。
	 */
	return drupal_render($page);
}

/**
 *这个函数非常重要,对于刚接触drupal也比较复杂,理解这个函数要小心+细心。
 *这个函数的功能是根据传过来的参数,渲染这个数组成需要的HTML。
 *@$elements 是一个关联(键/值对)数组, 它记录着所有的渲染的标签(如何渲染,渲染的过程等等),我们称它为已经结构
 *    化的数组树。键的名字以#开始的,代表当前的记录是$elements的一个属性,而其他的表示$elements 的子树,需要重新
 *    被执行drupal_render()进行递归。
 *    #access 记录当前元素是否有权利能被渲染。
 *    #printed 表示当前元素是不是被已经渲染过。
 *    #cache 表示当前元素是否需要被缓存,缓存的结构是
 *    		1)
 *    		'#cache'=>array(
 *    			'bid'=>NULL || 'your bid',
 *    			'cid'=>'your cid'
 *    		);
 *    		2)
 *    		'#cache'=>array(
 *    			'bid'=>NULL || 'your bid',
 *    			'key'=>array('your','key'),
 *    			'granularity'=> NULL || DRUPAL_CACHE_PER_ROLE || DRUPAL_CACHE_PER_USER || DRUPAL_CACHE_PER_PAGE
 *    		);
 *    #markup 标记, 如果这个值存在而 #type的值不存在的话,就使 #type = 'markup'
 *    #defaults_loaded 表示当前元素是不是被完全的(包括所有默认的属性),如果不是执行
 *    		$elements += element_info($elements['#type']);
 *    		重新加载。
 *    #pre_render 表示当前元素是否需要提前渲染,这里的值是用来渲染的函数,参数就是当前元素
 *    #children 存放当前元素被渲染后的结果
 *    #theme_wrappers 如果有的话,用theme(theme($theme_wrapper, $elements))重新渲染,可以在首次渲染的结果上进行二次加工
 *    #post_render 过滤输出内容,用来做输出前的最后工作,用来返回浏览器执行的安全内容。
 *    #states javascript 控制dom对象的状态,常在form控制表单是使用
 *    #attached 这里存储着所需要加载的javascript,css
 *    #prefix 和 #suffix 存放着当前元素渲染后的前缀和后缀。用来做最后的输出。
 *    		$output = $prefix . $elements['#children'] . $suffix;
 *    
 */
function drupal_render(&$elements){
	//......
}
/******未完待续(接下来就是theme函数)*******/
原文地址:https://www.cnblogs.com/xinyuyuanm/p/2998619.html