吵吵   2013-01-24  阅读:2,746

在众多的博客系统中,wordpress凭借其功能强大,灵活易用的特点抓住了大把的用户的心。在20011年的时候,wordpress.com的每月访问量就已经超过了三个亿。

一款优秀的程序离不开其优秀的设计,wordpress的强大功能也离不开其插件系统的设计。至少在吵吵看来,wordrpress的众多插件已经成为其必不可少的东西了,比如postview浏览数统计插件等。wordpress本身所带的东西很少,少到其就是一个非常简洁的blog而已。但是正是因为有了这众多的插件和灵活易用的开发接口,才使得worpress能够真正的为我所用,而随心所欲。


今天我们要做的事情是再读一读wordpress的代码,了解一下它的插件技术。

1、运行机制

wordpress的插件都是放在其plugin的文件夹下面的,所以它载入和启用插件的技术并不难理解,因为它就是读php文件么。那么它是如何判断那些是已经启用的插件呢?

我们先找到wp-setting.php文件中启动插件系统的函数:

// Load active plugins.
foreach ( wp_get_active_and_valid_plugins() as $plugin )
	include_once( $plugin );
unset( $plugin );

从上我们明白它是调用了wp_get_active_and_valid_plugins()函数返回了已经启用插件的路径,再用include_once将插件的php文件包含进来,因此我们的插件就用上了。

再找到wp-includs\load.php中的wp_get_active_and_valid_plugins函数:

function wp_get_active_and_valid_plugins() {
	$plugins = array();
	$active_plugins = (array) get_option( 'active_plugins', array() );

	// Check for hacks file if the option is enabled
	if ( get_option( 'hack_file' ) && file_exists( ABSPATH . 'my-hacks.php' ) ) {
		_deprecated_file( 'my-hacks.php', '1.5' );
		array_unshift( $plugins, ABSPATH . 'my-hacks.php' );
	}

	if ( empty( $active_plugins ) || defined( 'WP_INSTALLING' ) )
		return $plugins;

	$network_plugins = is_multisite() ? wp_get_active_network_plugins() : false;

	foreach ( $active_plugins as $plugin ) {
		if ( ! validate_file( $plugin ) // $plugin must validate as file
			&& '.php' == substr( $plugin, -4 ) // $plugin must end with '.php'
			&& file_exists( WP_PLUGIN_DIR . '/' . $plugin ) // $plugin must exist
			// not already included as a network plugin
			&& ( ! $network_plugins || ! in_array( WP_PLUGIN_DIR . '/' . $plugin, $network_plugins ) )
			)
		$plugins[] = WP_PLUGIN_DIR . '/' . $plugin;
	}
	return $plugins;
}

上面这个函数就是用于获取启用插件的路径的,可以看出来,该函数获取已经启用的插件是从get_option( ‘active_plugins’, array() )获取的,即从数据库中option表中的active_plugin这行数据中取出来的,那么关于插件的禁用和启用我们就非常之明白了,就是保存在数据库中的了。

在wp-includs\plugin.php中我们也可以找到关于插件用的一些函数,例如钩子函数,还有启用插件,禁用插件等函数。以下展示一个卸载插件的函数:

/**
 * Set the uninstallation hook for a plugin.
 *
 * Registers the uninstall hook that will be called when the user clicks on the
 * uninstall link that calls for the plugin to uninstall itself. The link won't
 * be active unless the plugin hooks into the action.
 *
 * The plugin should not run arbitrary code outside of functions, when
 * registering the uninstall hook. In order to run using the hook, the plugin
 * will have to be included, which means that any code laying outside of a
 * function will be run during the uninstall process. The plugin should not
 * hinder the uninstall process.
 *
 * If the plugin can not be written without running code within the plugin, then
 * the plugin should create a file named 'uninstall.php' in the base plugin
 * folder. This file will be called, if it exists, during the uninstall process
 * bypassing the uninstall hook. The plugin, when using the 'uninstall.php'
 * should always check for the 'WP_UNINSTALL_PLUGIN' constant, before
 * executing.
 *
 * @since 2.7
 *
 * @param string $file
 * @param callback $callback The callback to run when the hook is called. Must be a static method or function.
 */
function register_uninstall_hook( $file, $callback ) {
	if ( is_array( $callback ) && is_object( $callback[0] ) ) {
		_doing_it_wrong( __FUNCTION__, __( 'Only a static class method or function can be used in an uninstall hook.' ), '3.1' );
		return;
	}

	// The option should not be autoloaded, because it is not needed in most
	// cases. Emphasis should be put on using the 'uninstall.php' way of
	// uninstalling the plugin.
	$uninstallable_plugins = (array) get_option('uninstall_plugins');
	$uninstallable_plugins[plugin_basename($file)] = $callback;
	update_option('uninstall_plugins', $uninstallable_plugins);
}

2、核心技术

钩子技术的设计思想将原本顺序执行的php代码变成一个个可以控制的节点了,这样子我们就可以很方便的在某个节点设置一些钩子,方便的改变某些内容和功能了。这事实上是让我们从顺序编程转变到了事件编程了,这有点类似windows程序了,是一种事件响应的机制了。

这种机制的要求是比较严格的,需要设计者考虑到很多东西,如怎么将一个过程拆分为一个个子节点、那些内容才是要修改的并且可以供hook的。当面对一个过程不是很明显的系统的时候,或者变化性太大的系统的时候,这些子节点的hook就貌似会变得无用武之地了,因此考虑和设计这样一套钩子系统也是蛮有难度的。

关于钩子技术的详细解析我就不再说了,核心就是用call_user_func_array()这回调函数来调用我们自定义的函数,如果你感兴趣可以参考我过去写的博文:
wordpress插件编程技术之钩子技术详解

3、强大的数据库操作

当然,仅仅设计了那么多的过程让你hook一定还不过瘾了。你可能新增加一些东西和过程,比如增加一张表存放每篇日志的访问量,因此你在插件中可能会用到数据库或者其它文件操作等。

$wpdb类是一个从ezSQL继承过来的wordpress数据库类,你只需要将其引用Global $wpdb之后就可以方便的用它来进行数据库操作了,它本省已经提供了很多函数与接口,可以轻易的获取日志的ID、标题等内容。

虽然你还可以使用php的众多函数,但是有了wp提供的一些api,你的编程会变得更加容易和简洁,我想你也会乐意在插件中使用它提供的这些api函数。例如wp_die函数就是重新封装的die函数,他会给出一个提示信息,但是这个带着漂亮边框的提示信息绝对会比你直接输出一行字要好看的多。

wordpress

如何设计一个插件系统?

1、设计数据表。我所说的表是用来记录插件信息的表,至于插件自身功能需要设计的表单那该是插件去完成的任务了。对于一个插件系统,你至少需要一张表来记录该插件启用、停用、卸载等信息。如果可能的话,将版本号、网址等都囊括进去,这样子你至少完成一个插件系统的基础功能了。

2、设计功能接口。插件的很大一部分功能是修改原有的显示或者内容的,但是却不用修改原有代码。那么在做原有的系统的时候,你就该考虑那些内容会改变,而在此设置一个节点。至于你是用hook或者还是用全局变量来控制你的内容修改,那就是各有各的想法了。但是我比较推荐worpress搞的钩子机制。一方面它逻辑清楚,另外一方面回调机制还可以控制调用顺序。例如你做的首页登录时候的一个提示信息框,如果用到hook机制或者提供api的话,插件就可以轻易添加这些提示信息了,就是一个函数搞定了,你不用再去修改原来的代码了。

3、设计通用api。通用api的使用可以大大加快你的开发效率,而且不必太考虑底层的问题,一个设计的好的api需要同时满足功能全面和简洁的要求,这原本就很考验系统架构师的能力。这些年也呈现出一些趋势,一些非计算机专业的人在其专业领域内做的一些系统往往简洁、代码整洁,而交给一些计算机公司做的一个程序却往往反复修改、面目全非而又bug众多。这折射出来的一个问题是:需求与编程之间是有矛盾的,而能够处理好这些矛盾的人往往是对于需求与编程都很了解的人。由此而推论,程序员的需求量依旧很大,他们的行业会更加细化,但是编程这个时候就只是和英语一样成为一件工具了。

然而,人类文明的产生不就是从使用工具开始的么,人类社会的发展不就是工具的更新换代么?

吵吵微信朋友圈,请付款实名加入:

吵吵 吵吵

4条回应:“wordpress插件技术的全面解析”

  1. 小五说道:

    好插件越来越少了

  2. wordpress因插件而强大

  3. 红巾怪侠说道:

    文中第一行20011错了。

  4. 糗事百科说道:

    无意溜达到了你的网站 留下足迹

发表评论

电子邮件地址不会被公开。 必填项已用*标注