上周研究了下Wordpress的源码,虽然源码写的不够精细,我们姑且不讨论其代码是否真的如其官网所说“代码如诗”,我们比较感兴趣的是,他的框架是怎样的,他是如何实现的MVC分层,又是如何很方便的扩展插件、主题,让其他人可以简单的参与到开发中来。
我们从前台的Index.php入手,找到入口文件wp-blog-header.php文件,wp-load.php主要是一些判断和初始化工作,template-loader.php文件则主要是显示整个网站的内容。实际的初始化内容在wp-settings.php中,这个也是整个系统的核心所在。他进行了一系列的check,定义相应的类,最后加载模板文件的首页文件,交予模板文件处理。
整个系统的实现自由扩展,很大程度“依赖于执行挂钩、动作和过滤器 ”,关于这个话题可以参考官方的说明,http://codex.wordpress.org/Plugin_API,这个理解了,才能更好的把握Wordpress的东西,因为整个系统都与他有关!
WordPress 中有一种叫执行挂勾的机制,允许插件把一些功能“挂载”到 WordPress 当中。也就是说,在系统运行至某一个环节时,去调用插件内的一些函数。执行挂勾分为两种:
- 动作 (Action): 动作是 WordPress 运行到某些环节,或者在某些事件发生时,就会被执行的一种挂钩。任何的插件都可以通过动作接口来指示系统在遇到这些环节或者事件的时候,就执行指定的 PHP 函数。
- 过滤器 (Filter):过滤器的是 WordPress 用于修改即将要保存或者发送出去的数据的一种挂钩。任何的插件都可以通过过滤器接口来指示系统在遇到某些环节或者事件的时候,就执行指定的 PHP 函数去修改特定的数据。
某些时候动作或过滤器可以达到相同的效果。比如要修改文章的内容,可以把插件挂载到动作 publish_post 上,在文章的内容保存到数据库前就修改它。也可以把插件挂载到过滤器 the_content 上,在文章的内容发送到浏览器前修改它。
这个很有意思,我们可以把一个网页(博客)想象成一张图纸,图纸被切分成了一块一块的区域,那么我们假设,我们默认的输出是怎么一个样子,然后,我们去改变他,例如,红色变成绿色,内容加上自己的标签,我们只需要使用过滤器,在内容输出的时候,会调用我们的方法,而我们的方法,就是hack显示的内容。而动作,则可以改变当前的行为,例如,插件是扩展系统原来没有的功能。那么我们添加钩子到事件中,这个事件(比如,输出通知信息?)的时候,就会触发我们自定义的函数,这个函数可以做你想要做的事情。
系统提供了增、删、清理和执行和合并的基本功能,而系统事件则有Wordpress自身定义好的,就是说,整个网页怎么布局,我们给你定一个大体的框框,剩下的就完全靠你自己了。
那么系统怎么实现的MVC层结构呢?我个人阅读了源码以后的理解是:
一、V层(视图层)
我认为Wordpress的视图层就是主题文件夹里面的一个一个文件,在老早以前,Wordpress的旧版本,使用的是自定义的模板语言(类似于风靡的smarty模板引擎),然后自己进行语法分析,后来干脆直接使用php语言,因此开发主体需要一定的php基础,当然不会太复杂,系统有定义的函数,供给用户取原始的数据,如此,他的所谓的模板层其实还是交给了PHP。在模板中,经常见到if:endif whiel :endwhile的语法,这个很少见,具体可以参考http://www.cnblogs.com/janoyu/archive/2010/05/04/sourcejoy_com_php_other_syntax.html,他只不过是一种不常见的php语法,跟常见的功能一样。
二、M层(数据层)
WordPress的数据层封装的不是很彻底,可能不能按照数据层来进行这样的划分,因为他的取数据的操作并不是封装的很完全,其实是按照功能定义在函数中,例如取option的时候,就调用的get_option的方法,在方法里进行了数据库操作,而取博客文章等数据的时候,主要是在query.php文件中进行,可以关注下&get_posts这个函数,我认为这就是他的数据层。
三、C层(控制层)
WordPress的入口,我觉得可以作为他的控制层,我认为一个系统应该只能有一个入口比较合理,入口的流程也比较固定,先初始化,再取数据,最后输出V层,否则会带来代码重复,配置文件要多处修改的问题,因为入口多了以后,都有自己的配置文件,就不太好办了。
我们可以看一下视图层的代码:
<?php if (have_posts()) : while (have_posts()) : the_post(); update_post_caches($posts); ?> <div class="post" id="post-<?php the_ID(); ?>"> <h2><a class="title" href="<?php the_permalink() ?>" rel="bookmark"><?php the_title(); ?></a></h2> <div class="info"><span class="date"><?php the_time(__('F jS, Y', 'inove')) ?></span> <?php if ($options['author']) : ?><span class="author"><?php the_author_posts_link(); ?></span><?php endif; ?> <?php edit_post_link(__('Edit', 'inove'), '<span class="editpost">', '</span>'); ?> <span class="comments"><?php comments_popup_link(__('No comments', 'inove'), __('1 comment', 'inove'), __('% comments', 'inove'), '', __('Comments off', 'inove')); ?></span> <div class="fixed"></div> </div> <div class="content"><?php the_content(__('Read more...', 'inove')); ?> <div class="fixed"></div> </div> <div class="under"><?php if ($options['categories']) : ?><span class="categories"><?php _e('Categories: ', 'inove'); ?></span><span><?php the_category(', '); ?></span><?php endif; ?> <?php if ($options['tags']) : ?><span class="tags"><?php _e('Tags: ', 'inove'); ?></span><span><?php the_tags('', ', ', ''); ?></span><?php endif; ?> </div> </div> <?php endwhile; else : ?>
他是通过内置的函数,与系统的数据层进行交流,而插件的机制,可以看hello插件的实现方法。
do_action和apply_filter的方法,执行系统事件下面挂载的自定义函数,当然,如果当前事件下面无挂载,那么就直接返回了。
虽然,系统的代码并不完美,还有很多可以改进的地方,例如以下这段代码:
if ( file_exists( ABSPATH . 'wp-config.php') ) { /** The config file resides in ABSPATH */ require_once( ABSPATH . 'wp-config.php' ); } elseif ( file_exists( dirname(ABSPATH) . '/wp-config.php' ) && ! file_exists( dirname(ABSPATH) . '/wp-settings.php' ) ) { /** The config file resides one level above ABSPATH but is not part of another install*/ require_once( dirname(ABSPATH) . '/wp-config.php' ); } else { dosomething(); }
我不知道第二个elseif是否会执行,以我的理解,他是永远都不会执行到的。但是对于系统的思想,就是假设一个网页的输出内容怎样的,如果有新的需求改变,你不需要动我本来的内容,只需要注入你自己的内容。这样做的好处是,无论你外部的需求怎么变,始终不会影响我主干的东西,而新人入手也比较容易,他只需要理解主干,其他人更不干涉,个人的需求的开发,只能当作是外部代码,当作插件,可以随时删除,减少系统的“侵入性”(学来的),比较适合系统较为复杂,更新迭代非常快的产品,比如淘宝主搜索。。。。
这几天一直在忙于主搜的refine筛选区的代码重构,在以前是依靠数组来维持晒选项的checkbox的输出。先将需要的选项的html代码全部扔到一个数组里,然后根据条件,例如类目信息,让某个checkbox消失掉,就unset数组的值,最后,按照数组的顺序,输出,超出的部分,则截断。这样做的坏处是,如果我现在要调换某两个的位置,或则是加一个?那么我必须要了解以前的逻辑,知道以前这个位置是个什么东西,一共有几个,我加到数组的哪个地方才能保证一定会被输出,而不会因为位置有限(只输出12个checkbox),而数组有15个出现截断的情况。
如果,我们把筛选区设置为固定的12个位置,上面6个下面6个,我只知道你要改变第几个的内容,那么我把第几个的内容改成你想要输出的html,我不用再去关心,你原来是什么内容。这样代码可以一直往后写,随便你怎么改,而新人去改的时候,也不用关心以前的逻辑,只需要关注你要的那个位置,是输出的啥玩意儿就行。而下一步则可以将多个refine进行合并,因为无非是输出的内容不同而已。
按照这个思想走下去,无论逻辑多复杂,主分支的代码则始终不会怎么去更改。
懒得画图,就这么记一下吧,避免以后忘记,返工!
有些人喜欢把wp-config.php放在一个他们认为更安全的地方,so,some body wantta change the location of this file.so
… elseif ( file_exists( dirname(ABSPATH) . ‘/wp-config.php’ ) && ! file_exists( dirname(ABSPATH) . ‘/wp-settings.php’ ) ) ……will be available.