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