wordpress子主题路径问题

WordPress的子主题是一个非常灵活的用法,它可以帮助博主对原有主题进行扩展,但是由于子主题对父主题有继承,因此,在路径问题上就比较复杂。本文通过几个例子,来解析WordPress子主题的路径问题。

启用子主题后的路径选择

假设你的父主题是theme,子主题是theme-child,而当前的主题是theme-child,也就是子主题,那么在子主题的模板开发的时候,应该怎样正确调用路径呢?

假设父主题你是完全不能修改的,因为它会升级,你修改了,一升级所有的修改就没有了。你只能修改子主题来达到自己想要的目的。

在子主题中:

get_option('tempate') = theme
get_option('stylesheet') = theme-child

这是怎么做到的呢?这是由于我们在子主题的style.css中增加了Template: 项,而在启用子主题的时候,如果存在该项,update_option的时候,则会将该项的值作为template配置项的值。

正是由于这个原因,所以在子主题中还会有:

get_stylesheet() = get_option('stylesheet')
STYLESHEETPATH = get_stylesheet_directory() = get_theme_root(get_stylesheet())
bloginfo('stylesheet_derictory') = get_stylesheet_directory_uri() = get_theme_root_uri(get_stylesheet())
bloginfo('stylesheet_url') = get_stylesheet_uri() = get_stylesheet_directory_uri().'/style.css'

看到这里你应该了解了吧,所有的和stylesheet相关的路径都是基于get_option('stylesheet')的值的,因此都是定位到theme-child目录中去。

而同样的道理

get_template() = get_option('template')
TEMPLATEPATH = get_template_directory() = get_theme_root(get_template())
bloginfo('template_url') = get_template_directory_uri() = get_theme_root_uri(get_template())

因此,在子主题中,template都是和get_option('template')相关的,所以都是基于theme目录,这是在子主题中最显著的区别。

在父主题中为子主题继承做准备

但是,有的时候,父主题也是你开发的,你提供了父主题,并对它长期升级,而希望其他开发者基于该父主题,制作子主题来运行自己的网站。这个时候你就必须考虑,如何保证子主题是可行的,并且父主题中所使用的大部分方法到子主题里面也是有效的。

比如,你定义了一个常量 define('__IMG__',get_template_directory_uri().'/css') 你确定这样的写法是OK的吗?到了子主题中,仍然能够正确获取到父主题里面的某张图片吗?

或者,你定义了另外一个常量 define('__CSS__',get_stylesheet_directory_uri().'/css') 你确定这样可以让子主题使用自己的css目录下的文件吗?

再者,如果你希望子主题在调用某个样式文件的时候,先找子主题目录中是否存在该样式文件,如果不存在,使用父主题目录中对应的样式文件,这个功能你知道怎么去实现吗?(注意,你现在是父主题的开发者)

实际上,从程序上,template和stylesheet对应的值和前面一节是一样的,在父主题的模板代码(甚至插件的代码)中,

get_template() = theme
get_stylesheet() = theme-child

这和子主题中的结果是一样的。

其实,核心问题在于思维。也就是说,你在开发父主题的时候,必须时刻思考一下,此处是否会在子主题中做扩展。

如果必须使用父主题的,使用template;而如果允许使用子主题的,则使用stylesheet。

基于此,你在 <link rel="stylesheet"> 的href标签中,应该使用 get_stylesheet_uri() 来调用子主题的style.css(如果博主启用的是父主题,则调用的是父主题的style.css)。还有,如果你希望包含某一个php文件来实现某些功能,如果希望子主题自己可以来做的话,应该使用 include(STYLESHEETPATH.'/yourfile.php') ,但如果你不允许子主题对此进行变动,则应该直接inlcude('yourfile.php')

刚才提到,如果子主题存在,则用,如果不存在,则用父主题的,其实也很简单,通过file_exists去判断。

function get_style($file) {
    // for example: $file = 'css/mystyle.css';
    $dir_path = is_child_theme() ? STYLESHEETPATH : TEMPLATEPATH;
    $dir_url = is_child_theme() ? get_stylesheet_directory_uri() : get_template_directory_uri();
    if(file_exists($dir_path.'/'.$file))
        return $dir_url.'/'.$file;
}

这样可以确保你可以获得正确的css文件的最终URL。

插件中filter钩子切换主题的正确姿势

上面你仅仅是一个主题开发者,现在切换到插件开发者的角色。在插件中,你试图通过对设备的判断,对主题进行切换,当访客是手机访问时,你希望你的博客切换到theme-mobile主题,而该主题,其实是theme主题的子主题。

如果你去研究上面提到的那些和template相关的函数源码的话,就会发现每一个函数末尾几乎都申请了和函数名一样的钩子,只需要add_filter()即可修改其返回结果,例如在插件中增加如下代码:

add_filter('get_template','my_filter_template');
function my_filter_tmeplate($theme) {
    return $theme.'-mobile';
}

那么就会将get_template的返回结果加上'-mobile'后缀。当然,你可以把这个函数丰富一些,增加很多判断来实现你的目的。我们甚至都可以改变get_option的返回值,比如:

add_filter('option_template','my_filter_template_option');

这样,从根源上就把主题切换到了theme-mobile。而选择使用哪个主题模板文件,无非是include(TEMPLATEPATH.'/index.php') 这样的机制,而你通过改get_template()的返回值,实际上也就改变了TEMPLATEPATH。

基于这种原理,你甚至可以改get_template_directory()的返回值,让主题的路径定位到其他位置,比如:

add_filter('template_directory','my_filter_template_directory');
function my_filter_template_directory() {
    return ABSPATH.'/template';
}

这样,你直接在网站根目录建立一个template目录来存放主题就可以了。但是,即使这样,URL部分还是会定位到原来的目录去。

回到原始话题,在插件中,临时切换主题,从主题theme切换到theme-mobile,需要承担风险。我们来梳理这个过程:

目标:你希望在手机访问时,使用theme-mobile主题,因为该主题是theme主题的子主题,所以可以复用theme主题中的一些内容。而在PC端访问的时候,正常使用theme主题。

问题:如何利用上面的知识,正确filter路径?

实现:使template为theme,使stylesheet为theme-mobile。

function DeviceThemeExtends($theme){
   if(wp_is_mobile())
        $theme .= '-mobile';

    return $theme;
}
add_filter('option_stylesheet','DeviceThemeExtends' );

直接从option的根源上,实现我们的目的。

2016-01-17