(注:学习交流用)
学习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函数)*******/