WordPress的动作和过滤器钩子有什么区别?

255 阅读9分钟

动作和过滤器钩子是各种WordPress API的一个基本部分。没有它们,你在你的主题和(特别是)你的插件中能做的事情就会受到限制。

但有时很容易混淆这两者,特别是在WordPress有一个动作钩子和一个过滤器钩子同名的情况下。

在这篇文章中,我将定义动作钩子和过滤器钩子,并描述它们之间的区别,我还将演示如何在你的主题和插件中使用它们。我还会给出一些例子,说明什么时候可以使用它们。

当你把动作和过滤器钩子添加到你的代码中时(或者你把函数挂到钩子上),了解动作和过滤器是如何被WordPress调用的,以及什么是以什么顺序发生的,会有帮助。我不会在这里详细介绍,因为我们有另一个教程来做这项工作。

定义和区别

让我们从一些定义开始。我也会定义动作和过滤器的钩子和函数,所以你可以看到它们之间的区别。

函数

函数是大多数人在学习WordPress开发时的第一件事;如果你在你的主题的functions.php文件中添加了代码,那么你就已经写了一个函数。

函数指定了某些事情将如何发生。你编写一个函数来查询数据,输出内容,或执行许多其他任务。你可以在你的主题模板文件中直接调用(执行)函数,或者你可以把它们挂到动作或过滤器钩子上。函数也可以包括模板标签,如条件标签,以指定函数应该何时应用。

我将在本文后面向你展示执行函数的不同方法。

动作钩子

动作钩子(或行动)在某些事情发生时被触发,比如加载一个页面,用户登录,或你在主题或插件中定义的自定义动作。

你可以使用函数来添加你自己的动作钩子。 do_action()函数添加你自己的动作钩子,我很快就会演示。你钩住该动作的任何函数就会在代码中的该点运行。

过滤器钩子

过滤器钩子,或称过滤器,控制某些事情的发生或改变已经在输出的东西。你可以用一个过滤器来输出特定格式的元数据,覆盖你的插件的文本输出,或者阻止某些东西被显示出来。

你可以在你的代码中添加过滤器,使用 [apply_filters()](https://codex.wordpress.org/Function_Reference/apply_filters)函数在你的代码中添加过滤器,我很快就会演示这一点。正如 "应用 "一词所表明的,你将过滤器应用到现有的代码中,而你用do_action() 创建的动作在你将函数挂到它之前是空的。

使用函数、动作和过滤器

让我们来看看一些例子,示范如何使用函数、动作和过滤器。首先,我们来看看如何在你的代码中直接使用函数,而不把它们附加到钩子上。

直接调用函数

下面是一个在模板文件中直接调用函数的例子。在我的客户网站中,我在页脚添加了一个colophon,其中包括版权信息。下面是这个函数:

if ( ! function_exists( 'compass_colophon' ) ) {
function compass_colophon() { ?>
    <section class="colophon" role="contentinfo">
		<small class="copyright left">
			<?php echo compass_copyright(); ?>
			<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
			<?php bloginfo( 'name' ); ?>
			</a>
		</small><!-- #copyright -->

		<small class="credits right">
				Powered by <a href="https://wordpress.org/">WordPress</a> and designed by <a href="https://compass-design.co.uk">Compass Design</a>.
			</a>
		</small><!-- #credits -->
	</section><!--#colophon-->
	<?php }
}

这个函数是可插拔的,因为我在父主题中使用了它;如果我在子主题中创建一个同名的新函数,它将覆盖这个函数。注意,这个函数包括另一个函数,compass_colophon() ,在代码中直接调用它。

这个函数在我的父主题的functions.php 文件中。我可以在我的主题的footer.php 文件中直接调用它,像这样:

compass_colophon();

这就在我的主题中调用它的地方输出了函数中的代码。你也可以向你的函数传递参数,然后在函数中使用这些参数。

正如我即将演示的那样,这个函数也可以与一个动作或一个过滤器挂钩。

将函数与动作挂钩

与其直接调用colophon函数,不如把它附加到一个钩子上,这样会更灵活。

创建动作钩子

我可以在footer**.php**文件中添加一个动作钩子,而不是在我的页脚文件中调用compass_colophon() 函数,通过添加这个:

do_action( 'compass_in_footer' );

do_action() 函数有一个强制参数,就是动作的名称。你也可以选择向它添加参数。

将函数与动作挂钩

所以现在我需要把它挂到我的新动作钩上,而不是调用我的colophon函数。在我的functions.php文件中,我在我的函数中添加了这个:

add_action( 'compass_in_footer', 'compass_colophon' );

这将我的函数与compass_in_footer 动作挂钩,这意味着我函数中的代码将在代码中放置动作的地方运行。第一个参数是动作钩子的名称,第二个参数是我的函数的名称。

这样做的好处是,你可以把多个函数挂在同一个动作上,而且你可以设置优先级,使它们按照你希望的顺序启动。

因此,假设我有另一个函数想挂到我的compass_in_footer 钩子上,叫做compass_smallprint() ,它包含一些更小的打印:

if ( ! function_exists( compass_smallprint() ) ) {
    function compass_smallprint() {
		// contents of function here
	}
}
add_action( 'compass_in_footer', 'compass_smallprint', 20 );

你可以看到,在这里我给我的add_action() 函数添加了第三个参数,也就是优先级。默认的优先级是10 ,如果你把这个留空,它将被应用。因此,由于我没有为我的compass_colophon() 函数设置优先级,为compass_smallprint() 函数设置20将使该函数 compass_colophon() 函数之后运行。

解除函数与动作的连接

有时你想阻止一个函数的运行,一种方法是创建一个假的函数版本,如果它是可插拔的,则什么也不做。简单地说,可插拔函数允许你根据代码的执行顺序来覆盖一些行为。如果你没有听说过可插拔函数,你可能也想学习一下可插拔函数的基础知识和它们的用法,以便迎头赶上:

另一种方法是通过使用动作钩子来实现。如果函数已经被挂到一个动作钩子上,那么你可以用 [remove_action()](https://codex.wordpress.org/Function_Reference/remove_action)函数。因此,如果我想阻止我的compass_smallprint() 函数的运行,我就像这样把它从compass_in_footer 动作中解除钩子。

remove_action( 'compass_in_footer', 'compass_smallprint', 20 );

remove_action() 函数有三个参数:动作钩子的名字,函数的名字,以及该函数最初与动作钩子的优先级。你必须包括优先级,这样才能发挥作用。

如果你想阻止所有函数的执行,你也可以从一个动作中取消所有函数的钩子。这样做的时候要小心,因为可能有一些你不知道的函数被挂在你的动作上。

要做到这一点,使用 [remove_all_actions()](https://codex.wordpress.org/Function_Reference/remove_all_actions)函数:

remove_all_actions( 'compass_in_footer' );

添加一个优先级数字作为第二个参数,只删除与该动作挂钩的、具有你指定的优先级的函数,这给你更多的控制。

将函数挂在过滤器上

你还可以选择将你的函数与过滤器挂钩。当你想改变或覆盖一些现有的代码时,你可以这样做。当你创建过滤器钩子(使用 apply_filters()函数),你把它包在你的主题或插件的代码中,然后由附加在钩子上的任何过滤器来改变。

如果你有想要覆盖默认设置的主题或插件选项,或者你正在创建一个可能被子主题覆盖的元素的父主题,这可能很有用。

创建过滤器钩子

apply_filters() 函数有三个参数:过滤器钩子的名字,你想过滤的值(即默认值),和可选的变量,然后你把这些变量传递给与过滤器挂钩的函数。

你可以在你的主题模板文件中添加一个过滤器,或者在一个通过动作钩子钩住的函数中添加。让我们来看看这两种选择。

回到我的compass_colophon() ,我把它转换为一个过滤器,把它的内容添加到我的footer.php文件中的apply_filters() 函数中,像这样:

echo apply_filters( 'compass_colophon', '
    <section class="colophon" role="contentinfo">
		<small class="copyright left">
			<?php echo compass_copyright(); ?>
			<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
			<?php bloginfo( 'name' ); ?>
			</a>
		</small><!-- #copyright -->

		<small class="credits right">
				Powered by <a href="http://wordpress.org/">WordPress</a> and designed by <a href="http://compass-design.co.uk">Compass Design</a>.
			</a>
		</small><!-- #credits -->
	</section><!--#colophon-->'
);

这将输出我设置为apply_filters() 函数第二个参数的代码。

然而,我不喜欢直接把它添加到我的主题模板文件中,所以我将把过滤器添加到我已经通过动作钩子附加的函数中。

因此,我使用上面演示的do_action() 函数将compass_in_footer 动作添加到我的footer.php文件中,然后我在我的function.php文件中创建一个函数,该函数与该动作挂钩并包含一个过滤器:

if ( ! function_exists( 'compass_colophon' ) ) {
function compass_colophon() {
    echo apply_filters( 'compass_colophon_filter', '
		<section class="colophon" role="contentinfo">
			<small class="copyright left">
				<?php echo compass_copyright(); ?>
				<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
				<?php bloginfo( 'name' ); ?>
				</a>
			</small><!-- #copyright -->
	
			<small class="credits right">
					Powered by <a href="http://wordpress.org/">WordPress</a> and designed by <a href="http://compass-design.co.uk">Compass Design</a>.
				</a>
			</small><!-- #credits -->
		</section><!--#colophon-->'
	);
}
add_action( 'compass_in_footer', 'compass_colophon' );

这意味着我现在可以通过以下三种方式之一覆盖默认内容:

  • 在我的子主题中创建一个名为compass_colophon() 的新函数,它覆盖了我的父主题中的函数,因为它是可插入的
  • compass_in_footer 动作钩子上解开compass_colophon() 函数,然后写一个新的函数,我把它附在它的位置上
  • 创建一个新的函数,然后把它挂到compass_colophon_filter 过滤器钩子上,这样就可以覆盖我的apply_filters() 函数中的值了。

在现实生活中,你不需要有这么多的选项,所以你更可能在你的函数中对部分内容而不是全部内容应用过滤器。

所以我可以创建两个过滤器,一个用于版权部分,另一个用于学分:

if ( ! function_exists( 'compass_colophon' ) ) {
function compass_colophon() {
    
	echo '<section class="colophon" role="contentinfo">';
		
		echo apply_filters( 'compass_copyright_filter', '
			<small class="copyright left">
				<?php echo compass_copyright(); ?>
				<a href="<?php echo home_url( '/' ) ?>" title="<?php echo esc_attr( get_bloginfo( 'name', 'display' ) ); ?>" rel="home">
				<?php bloginfo( 'name' ); ?>
				</a>
			</small><!-- #copyright -->'
		);
		
		echo apply_filters( 'compass_credit_filter', '
			<small class="credits right">
					Powered by <a href="http://wordpress.org/">WordPress</a> and designed by <a href="http://compass-design.co.uk">Compass Design</a>.
				</a>
			</small><!-- #credits -->'
		);
	echo '</section><!--#colophon-->';
}
add_action( 'compass_in_footer', 'compass_colophon' );

然后,我就可以通过解开compass_colophon函数的钩子或者在我的子主题中写一个新的函数来覆盖整个函数,或者我可以创建一个与compass_copyright_filtercompass_credits_filter 过滤器钩子挂钩的函数,来单独覆盖每个元素。

将函数与过滤器挂钩

要把一个函数挂到一个过滤器钩子上,你可以使用add_filter() 函数,它有两个参数:钩子的名字和函数的名字。

因此,要改变学分,我会写这个函数:

function new_credits() { ?>
    <small class="credits right">
		Powered by <a href="http://wordpress.org/">WordPress</a> and designed by <a href="http://rachelmccollin.co.uk">Rachel McCollin</a>.
			</a>
	</small><!-- #credits -->
<?php }
add_filter( 'compass_credits_filter', 'new_credits' );

这就用我的new_credits() 函数的内容覆盖了我原来的compass_credits_filter 过滤器钩子中设置的值,但保持compass_colophon() 函数中的其他内容不变。

在将函数与过滤器挂钩时,你也可以指定优先级,这与动作钩子的方式完全相同。优先级较低的函数将被首先运行。

解除过滤器中的函数钩子

与动作钩子一样,你也可以从过滤器钩子中删除函数。你可以使用 [remove_filter()](https://codex.wordpress.org/Function_Reference/remove_filter)函数,它有三个参数:过滤器钩子的名字,函数的名字,以及优先级,如果函数最初被挂在过滤器上时设置了优先级,那么优先级是必须的。

因此,要删除我的new_credits() 函数,我使用这个:

remove_filter( 'compass_credits_filter', 'new_credits' );

然后,代码输出将恢复到我在原来的apply_filters() 函数中指定的值。因此,如果我想删除new_credits() 函数,并且不在其位置上出现任何东西,我必须添加一个新的函数。然后,我解除第一个函数的钩子,并像这样钩住我的新函数:

function no_credits() {
    return;
}
remove_filter( 'compass_credits_filter', 'new_credits' );
add_filter( 'compass_credits_filter', 'no_credits' );

快速回顾

让我们鸟瞰一下整个事情,以更好地理解它是如何结合在一起的。我们在本教程中的主要目的是将内容输出到页脚,并以一种使其他人很容易修改页脚内容的方式来完成。

最简单的方法是直接调用footer.php文件中的函数。然而,这在函数的执行和输出方面失去了一些灵活性。克服这一限制的方法之一是使用钩子,这就是我们接下来所做的。

我们用调用do_action() 替换了对compass_colophon() 函数的调用。请记住,do_action() 函数并没有调用compass_colophon() 。它只是在调用do_action() 的位置上创建了一个动作钩。在我们的例子中,动作钩子的名称是compass_in_footer

当动作compass_in_footer 被触发时,我们要调用的实际函数是通过调用add_action() 函数来指定的。它把我们的钩子名称作为第一个参数,把我们的回调函数作为第二个参数。我们附加到compass_in_footer 动作的第一个回调函数是compass_colophon() 函数。

我们还可以选择将多个回调函数附加到同一个钩子上。这正是我们在添加compass_smallprint() 函数作为动作钩子的另一个回调函数时所做的。函数被调用的顺序由传递给add_action() 的第三个参数值决定。这使我们能够确保函数compass_smallprint()compass_colophon() 之后执行。

使用动作钩子的一个好处是,当动作被触发时,你也可以通过将回调函数的名称传递给remove_action() ,来停止任何附加的回调函数的执行。

在这一点上,我们有一个在页脚被触发的动作钩子,我们调用了附加在动作钩子上的不同回调函数。基本上,我们采取了一些行动,并输出我们希望放在网站页脚的内容。其他人也能够解除我们的函数,并将他们自己的回调函数附加到动作钩上。

如果你只想部分地改变页脚的输出,而不完全覆盖或解除原始函数的钩子呢?这就是过滤器被证明是有用的时候。

我们通过使用apply_filters() 函数来创建新的过滤器钩子。它至少接受两个参数。第一个参数是过滤器钩子的名称,我们在调用compass_copyright_filtercompass_credit_filter 时将其设置为apply_filters() 。第二个参数是我们要过滤或修改的值。我们把它设置为显示版权和信用信息的HTML代码。

然后我们可以将我们自己的函数与这些过滤器挂钩。我们在add_filter() 函数的帮助下做到这一点。这个函数接受两个参数。第一个是过滤器钩子的名称。第二个参数是你想用来过滤值的回调函数的名称。

另一件需要记住的重要事情是,你传递给apply_filters() 的值只有在有一个使用add_filter() 函数的回调附件时才会被过滤。否则,作为第二个参数传递给apply_filters() 的值将保持不变。

我们使用这个功能来改变字幕,但保持页脚的版权信息不改变。

最后的思考

了解动作钩子和过滤器钩子之间的区别,并能够有效地使用这两种钩子,将使你的主题和插件开发得到提升。事实上,如果不使用至少一种类型的钩子,你根本无法编写插件,因为插件中的代码被激活的唯一途径是通过它所连接的动作和过滤器钩子。

本指南向你展示了如何使用一个函数、一个动作钩子和一个或多个过滤器钩子来添加同样的功能,以及从钩子中删除函数的技术和关于每种技术何时更有用的建议。

除了把函数挂到你自己创建的动作和过滤器钩子上之外,你还可以把它们挂到WordPress提供的动作过滤器上,比如wp_head 动作或body_class 过滤器。