首页 > 编程语言 >drupal源码分析

drupal源码分析

时间:2022-11-30 21:32:28浏览次数:42  
标签:分析 function drupal item theme 源码 template path router


说实在的,drupal确实很复杂, 在国内用的人也不多,唯一看的懂的中文技术网站估计就是 drupalchina.org了,
但是对于源代码的分析是少之又少.没有办法,只能自己来从头看起了!
越复杂的东西,往往功能越强大,drupal也是这样的.
其实,看drupal 的代码想一次就弄清楚,恐怕不是很容易,我是前后看了三次,才渐渐对他略有了解, 真羞!
分析代码之前,先了解drupal的几个重要的系统必须的也是访问页面老是查来查去的几个表!
system 这个表记录系统所有的模块和主题的开启状态以及相关信息,我们开启某一模块和主题,就是使用这个表来记录!
blocks 记录系统中所有可用的区块,记录的信息包括 区块名称,所属模块,所属主题,位置.等
node 
node_revisions 记录所发内容的实际数据 包括 body title 
variables 
cache  
menu_router 

drupal 的原理是根据传递的参数获得相应的路径进而找到相应的模块,并调用之.从开发的角度上看,
系统的每一次点击连接,就是在调用我们的模块!这样,就形成了我们写模块,然后通过各个超级连接来
调用. 通过超级连接调用?听起来比较怪, 事实上,以前做web开发的时候,基本是每一种连接对应一个单独的页面,
这样,我们的程序就有了 index  show.php  , edit.php  等 .我们看下drupal,根目录下除了index.php  再看不到
一点和网站显示相关的文件了! 那drupal是怎么在一个页面中各个地址间跳来跳去的呢. 记得有一个高人说过,源码
之前无秘密,想知道这个秘密,我们就必须打开index.php来看个究竟!
然而,结果又一次让我们失望! 在index.php 中我们只看到了这样的代码
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
$return = menu_execute_active_handler();
print theme('page', $return);
也就是说,系统的运行,就是这三个函数的功劳! 先从drupal_bootstrap这个函数看起吧!
从 bootstrap.inc中我们知道了 DRUPAL_BOOTSTRAP_FULL 的值是8! 这样 看下面的函数:
function drupal_bootstrap($phase) {
 
static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION,
DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE,
DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION,
DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE, DRUPAL_BOOTSTRAP_LANGUAGE,
DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $phase_index = 0;
  while ($phase >= $phase_index && isset($phases[$phase_index])) {
    $current_phase = $phases[$phase_index];
    unset($phases[$phase_index++]);
    _drupal_bootstrap($current_phase);
  }
}
其实就是循环调用  _drupal_bootstrap  8 次,下面是对 _drupal_bootstrap的分析, 一些函数只写了功能,并没有列出实际代码!
function _drupal_bootstrap($phase) {


case DRUPAL_BOOTSTRAP_CONFIGURATION:
    
     // 从$globals 中清除 $_post  $_GET  
    
     // global $timers; 
    
   conf_init();// 给 $conf赋值 空数组
     //  包含 
    
case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
  
     // 包含  ./includes/cache.inc 
  
      
    
    

  
   
    
    

  
    if (drupal_is_denied('host', ip_address())) { // 
      
      
      
    
    

  
     require_once variable_get('session_inc', './includes/session.inc'); 
    
    
    

  
  
    
    
    
    
     $cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache();// 
    
     if ($cache_mode != CACHE_AGGRESSIVE) { 
      
    
    
    
      
      
      
        
      
      
      
    
    
    
    

  
    
    

  
    
    
    
    

  
    
    
    

}

到这里,我们知道了drupal_bootstrap的实际作用, 就是加载所有的系统必须文件和模块文件并运行模块函数 hook_init,这就是为什么我们调用
哪个模块,都可以很自由的调用呢,他一次全部include所有我们开启了的模块文件了!!!接着看menu_execute_active_handler(),
function menu_execute_active_handler($path = NULL) {
  if (_menu_site_is_offline()) {
    return MENU_SITE_OFFLINE;
  }
  if (variable_get('menu_rebuild_needed', FALSE)) {
    menu_rebuild();
  }
  if ($router_item = menu_get_item($path)) { //  menu_get_item 函数的作用是根据传递的参数返回需要调用的模块名字!
    if ($router_item['access']) {
      if ($router_item['file']) {
        require_once($router_item['file']);
      }
     
return call_user_func_array($router_item['page_callback'],
$router_item['page_arguments']);  // $router_item['page_callback']
是模块里面的 callback函数
    }
    else {
      return MENU_ACCESS_DENIED;
    }
  }
  return MENU_NOT_FOUND;
}
menu_get_item的代码如下:::
function menu_get_item($path = NULL, $router_item = NULL) {
  static $router_items;
  if (!isset($path)) {
    $path = $_GET['q'];
  }
  if (isset($router_item)) {
    $router_items[$path] = $router_item;
  }
  if (!isset($router_items[$path])) {
    $original_map = arg(NULL, $path);
    $parts = array_slice($original_map, 0, MENU_MAX_PARTS);
   
list($ancestors, $placeholders) = menu_get_ancestors($parts); 
//返回Array ( [0] => Array ( [0] => annotate ) [1] => Array (
[0] => '%s' ) )  接着查 menu_router表 .
   
if ($router_item = db_fetch_array(db_query_range('SELECT * FROM
{menu_router} WHERE path IN ('. implode (',', $placeholders) .') ORDER
BY fit DESC', $ancestors, 0, 1))) {
      $map = _menu_translate($router_item, $original_map);
      if ($map === FALSE) {
        $router_items[$path] = FALSE;
        return FALSE;
      }
      if ($router_item['access']) {
        $router_item['map'] = $map;
       
$router_item['page_arguments'] =
array_merge(menu_unserialize($router_item['page_arguments'], $map),
array_slice($map, $router_item['number_parts']));
      }
    }
    $router_items[$path] = $router_item;
  }
  return $router_items[$path];
}
其实menu_get_item是关键了, 他其实就是 接收q的值,然后解析出对应的模块,并调用函数!!!到这一步,我们基本弄清楚了菜单路由的原理,下面看看theme('page')这
个函数了,他是最关键的,我们定义的区块和区块里面的内容以及我们自己做的模版文件*.tpl.php都是通过他来显示的!够牛吧!!;下面,将theme分解开来看,我们将基本可以弄清楚
他的原理.
function theme() {
  $args = func_get_args(); //获得传递给 theme的参数
  $hook = array_shift($args); // $hook 是 接受的第一个参数  是  page ,blocks  block ,menu_item , 你可以直接输出$hook看了!
  static $hooks = NULL; //记录函数每次运行后的值
  if (!isset($hooks)) { //第一次调用则初始化
    init_theme();// 获取当前主题 并初始化 此函数里面调用了 theme_get_registry(); theme_get_registry()里面的static 将记录每次获取的值
    //下面列出init_theme代码:
    ///
$hooks = theme_get_registry(); //给 $hooks值,使其不为NULL
///
}
 if (is_array($hook)) {
    foreach ($hook as $candidate) {
      if (isset($hooks[$candidate])) {
        break;
      }
    }
    $hook = $candidate;
 

if (!isset($hooks[$hook])) { //  $hook['page']  $hook['blocks'] 不存在则返回!!!!
    return;
  }  // $hook 是传递给theme的参数!!! $hooks 是 theme_get_registry的返回的值 是存在cache中的 ,通过构建后的数组!!!
  $info = $hooks[$hook];//为什么传block就调用block, 传page 就调用 page的模版 这里就是了 可以输出$hook看下
  global $theme_path;
  $temp = $theme_path;
  // point path_to_theme() to the currently used theme path:
 

// Include a file if the theme function or preprocess function is held elsewhere.
  if (!empty($info['file'])) {  //  $info == $hook['page'] 或则 $hook['page']之类,反正是 $hooks数组中的!!   判断有文件则包含之
   

   if (isset($info['path'])) {
      $include_file = $info['path'] .'/'. $include_file;
    }
    include_once($include_file);
  }
  if (isset($info['function'])) {  //有函数则调用之!  [function] => theme_blocks
   

   $output = call_user_func_array($info['function'], $args);
  }
  else {  //else开始
    // The theme call is a template.
    $variables = array(
      'template_files' => array()
    );
    if (!empty($info['arguments'])) {
      $count = 0;
      foreach ($info['arguments'] as $name => $default) {
        $variables[$name] = isset($args[$count]) ? $args[$count] : $default;
        $count++;
      }
   

   // default render function and extension.
    $render_function = 'theme_render_template'; //一个函数名
   

   // Run through the theme engine variables, if necessary
    global $theme_engine;
    if (isset($theme_engine)) {
      // If theme or theme engine is implementing this, it may have
      // a different extension and a different renderer.
      if ($hooks[$hook]['type'] != 'module') {
        if (function_exists($theme_engine .'_render_template')) {
          $render_function = $theme_engine .'_render_template';
        }
        $extension_function = $theme_engine .'_extension';
        if (function_exists($extension_function)) {
          $extension = $extension_function();
        }
      }
   

  
if (isset($info['preprocess functions']) &&
is_array($info['preprocess functions'])) {  //有 preprocess functions
则调用之
      // This construct ensures that we can keep a reference through
      // call_user_func_array.
      $args = array(&$variables, $hook);
      foreach ($info['preprocess functions'] as $preprocess_function) {
        if (function_exists($preprocess_function)) {
          call_user_func_array($preprocess_function, $args);
        }
      }
   

   // Get suggestions for alternate templates out of the variables
    // that were set. This lets us dynamically choose a template
    // from a list. The order is FILO, so this array is ordered from
    // least appropriate first to most appropriate last.
   

   if (isset($variables['template_files'])) {
      $suggestions = $variables['template_files'];
    }
    if (isset($variables['template_file'])) {
      $suggestions[] = $variables['template_file'];
   

   if ($suggestions) {
      $template_file = drupal_discover_template($info['theme paths'], $suggestions, $extension); //根据路径返回相应的 tpl 文件
   

   if (empty($template_file)) {
      $template_file = $hooks[$hook]['template'] . $extension;
      if (isset($hooks[$hook]['path'])) {
        $template_file = $hooks[$hook]['path'] .'/'. $template_file;
      }
    }
    $output = $render_function($template_file, $variables); //调用theme_render_template ,下面是代码,其实就是根据路径包含各tpl文件,并获得输出缓冲! 
    //   

   }
    else {
      $blocks = '';
    }
    // Assign region to a region variable.
    isset($variables[$region]) ? $variables[$region] .= $blocks : $variables[$region] = $blocks;
 

通过调用 theme('blocks') ,又调用了[function] => theme_blocks, 以下是 theme_blocks, 他又调用theme了不过是 theme('block', $block)!
function theme_blocks($region) {
  $output = '';
   if ($list = block_list($region)) {
    foreach ($list as $key => $block) {
      // $key == <i>module</i>_<i>delta</i>
      $output .= theme('block', $block);
    }
  }
  // Add any content assigned to this region through drupal_set_content() calls.
 

return $output;
}
转了一圈,有点晕? 其实,就是这样: 先获得系统关于主题的一个数组.放在$hooks中,接着从 $hooks 中找出相应的数组 然后接着进行处理 . 首先调用的肯定是 $hooks['page']
这是关键,从$hooks['page'] 取出需要数据后,接着看 file  function  等键的值,有file的包含,有处理函数的调用 , 在调用函数的时候调用了template_preprocess_page,

函数是显示用来显示页面中各区块的  首先列出各区域 然后.foreach  theme('blocks',$regions);而在调用 
theme('blocks')的时候,又有数组[blocks] => Array(Array ( [arguments] =>
Array ( [region] => ) [type] => module [theme path] =>
modules/system [function] => theme_blocks
这样,在调用 theme('blocks') 也就是在显示 block的时候, 又调用了 theme_blocks ,我们看下 theme_blocks  的函数,该函数先找出区块某一区域的所有值放入数组中 然后接着调用 theme('block')
最后这些变量都得到了,也该输出了吧,接着调用  drupal_discover_template  这个函数 是根据 路径和参数来返回各个tpl文件 比如 node  则调用 node.tpl.php
最后, theme_render_template 这个函数真正的包含各个tpl文件,并 ob_start  ob_get_contents 来获取缓冲区的内容!!!到这里,页面显示完毕!!!
theme 实际上是一个递归函数, 从 page 开始 一直到 block 再到 menu_item  link  依次调用,层层递归  !!!!!!
到这里, drupal 的代码基本被我们翻了一小下,应该有收获了!!

标签:分析,function,drupal,item,theme,源码,template,path,router
From: https://blog.51cto.com/u_710020/5900572

相关文章

  • n1_python数据分析
    1.Python语言及其特征优点:代码编写效率高;程序代码可读性强;可扩展性强;属于解释型语言,支持动态数据类型。缺点:①属于解释型语言,代码执行速度较慢;②在移动终端类应用......
  • RocketMQ系列-搭建Namesrv源码调试环境
    RocketMQ系列-搭建Namesrv源码调试环境在学习任何一个技术框架的时候,我们通常都是先了解是什么,有什么作用、解决什么问题、设计亮点和设计思想是什么;当然对于技术学习上来......
  • tomcat_安装&卸载&启动以及启动问题分析
    tomcat_安装&卸载&启动以及启动问题分析Tomcat:web服务器软件1.下载:https://tomcat.apache.org/2.安装:解压压缩包即可注意:安装目录建议不要有中文和空......
  • 案例10_黑马旅游网分析和结果
    案例10_黑马旅游网分析:案例_旅游网站分析和实现:<!doctypehtml><htmllang="zh-CN"><head><metacharset="utf-8"><!--设编码--><metahttp-equiv="X-UA......
  • MySQL源码编译安装
    MySQL源码编译安装Linux环境:CentOS7.6MySQL版本:MySQL5.7.37安装路径:/usr/local/mysql1.创建相关目录#创建用户useradd-s/sbin/nologinmysql#创建安装目录并......
  • windbg 分析 32 位进程的 64 位转储文件
    场景:x86的项目在x64的windows机器上运行时出现未响应的情况,使用任务管理器创建该进程的转储文件因为项目是32位的,所以使用x86的windbg来调试dmp文件,使用kn......
  • 计算机视觉的常用测试数据集和源码
    以下是computervision:algorithmandapplication计算机视觉算法与应用这本书中附录里的关于计算机视觉的一些测试数据集和源码站点,整理了下,加了点中文注解。ComputerVision......
  • UAC实例分析-USB音响
     在AndroidUSB之复合设备(gadget)详解一篇中提到了USBGadgetUAC(USBAudioClass)的使用。Rockchip平台可以支持UAC1(兼容USBAudioClassspecification1.0)和UAC2(......
  • 文学论坛挂的马 Worm.Win32.Agent.imh 的简单分析
    文学论坛挂的马Worm.Win32.Agent.imh的简单分析endurer原创2007-08-23第1版昨天没能下载文学论坛挂的马,刚才再试,终于下载回来了。ga.exe采用UPX壳脱壳前:文件说明符:D:......
  • 搞清楚Spring事件机制后:Spring的源码看起来简单多了
    Spring框架已然是Javaeee开发领域的霸主,无论是使用SpringBoot还是SpringCloud,都离不开Spring框架。作为Java开发者,无论是面试求职还是日常开发,就必须得熟练掌握、运用Sprin......