Drupal中,主题是可以继承的,或者说是扩展。例如,要创建一个新的名为custom的主题,该主题与名为default的主题只有某些细小的差别。这个时候,不需要复制一份default到custom,可以在custom声明该主题继承自default就可以了。
主题的继承关系在info文件中说明。首先,default主题的info文件不需要修改:
name = Default Theme
custom主题的info文件需要特别地声明base theme属性:
name = Custom Theme base theme = default
Drupal内部是如何解析这种继承关系的呢?解析的过程发生在system_list()函数中:
$result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC"); foreach ($result as $record) { $record->info = unserialize($record->info); // Build a list of themes. if ($record->type == 'theme') { $lists['theme'][$record->name] = $record; } } foreach ($lists['theme'] as $key => $theme) { if (!empty($theme->info['base theme'])) { // Make a list of the theme's base themes. require_once DRUPAL_ROOT . '/includes/theme.inc'; $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key); // Don't proceed if there was a problem with the root base theme. if (!current($lists['theme'][$key]->base_themes)) { continue; } // Determine the root base theme. $base_key = key($lists['theme'][$key]->base_themes); // Add to the list of sub-themes for each of the theme's base themes. foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; } // Add the base theme's theme engine info. $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme'; } else { // A plain theme is its own engine. $base_key = $key; if (!isset($lists['theme'][$key]->info['engine'])) { $lists['theme'][$key]->info['engine'] = 'theme'; } } // Set the theme engine prefix. $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine']; }
首先,从系统表system中读取出主题信息,记录到$lists['theme']数组:
$result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC"); foreach ($result as $record) { $record->info = unserialize($record->info); // Build a list of themes. if ($record->type == 'theme') { $lists['theme'][$record->name] = $record; } }
然后,遍历$lists['theme']数组。如果声明了base theme属性,则通过drupal_find_base_themes()函数获取父主题:
require_once DRUPAL_ROOT . '/includes/theme.inc'; $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key); // Don't proceed if there was a problem with the root base theme. if (!current($lists['theme'][$key]->base_themes)) { continue; }
drupal_find_base_theme()返回的是一个数组。在当前例子中,返回的数组是array('default' => 'Default Theme')。主题也是可以多重继承的,假设default主题再继承自top主题,drupal_find_base_theme()返回的数组则是array('top' => 'Top Theme', 'default' => 'Default Theme')。base_themes数组是有序的,最顶层的主题在最前面,然后依次下来。
function drupal_find_base_themes($themes, $key, $used_keys = array()) { $base_key = $themes[$key]->info['base theme']; // Does the base theme exist? if (!isset($themes[$base_key])) { return array($base_key => NULL); } $current_base_theme = array($base_key => $themes[$base_key]->info['name']); // Is the base theme itself a child of another theme? if (isset($themes[$base_key]->info['base theme'])) { // Do we already know the base themes of this theme? if (isset($themes[$base_key]->base_themes)) { return $themes[$base_key]->base_themes + $current_base_theme; } // Prevent loops. if (!empty($used_keys[$base_key])) { return array($base_key => NULL); } $used_keys[$base_key] = TRUE; return drupal_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme; } // If we get here, then this is our parent theme. return $current_base_theme; }
接下来得到root base theme,base_themes数组中的第一个key,上例中是top。
// Determine the root base theme. $base_key = key($lists['theme'][$key]->base_themes);
再后,更新每个base theme的sub_themes属性。sub_themes是无序的,取决于于主题加载顺序。
// Add to the list of sub-themes for each of the theme's base themes. foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) { $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name']; }
最后,设置引擎前缀engine prefix,就是主题解析函数都是以什么开头的。注意变量$base_key,当有继承存在,$base_key代表root base theme,否则就是current theme。当前主题有声明engine属性时,prefix就是engine属性,否则就是$base_key。
if (!empty($theme->info['base theme'])) { // Determine the root base theme. $base_key = key($lists['theme'][$key]->base_themes); // Add the base theme's theme engine info. $lists['theme'][$key]->info['engine'] = isset($lists['theme'][$base_key]->info['engine']) ? $lists['theme'][$base_key]->info['engine'] : 'theme'; } else { // A plain theme is its own engine. $base_key = $key; if (!isset($lists['theme'][$key]->info['engine'])) { $lists['theme'][$key]->info['engine'] = 'theme'; } } // Set the theme engine prefix. $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];