跳转到主要内容

服务器端 PHP 和排队

实现 AJAX 通信需要服务器端 PHP 脚本的两个部分。首先,我们需要将 jQuery 脚本排入网页并本地化 jQuery 脚本所需的任何 PHP 值。其次是 AJAX 请求的实际处理。

入队脚本

本节介绍了 WordPress 中 AJAX 的两个主要怪癖,这些怪癖可能会让刚接触 WordPress 的经验丰富的程序员感到困惑。一是需要将脚本排入队列,以使元链接正确显示在页面的头部部分。另外就是所有的AJAX请求都需要通过wp-admin/admin-ajax.php. 切勿直接向您的插件页面发送请求。

入队

使用该函数wp_enqueue_script()让 WordPress 在页面部分插入指向脚本的元链接。切勿在标题模板中对此类链接进行硬编码。作为插件开发人员,您无法随时访问标头模板,但无论如何这条规则都值得一提。

enqueue 函数接受五个参数,如下所示:

  • $handle是脚本的名称。
  • $src定义脚本所在的位置。为了可移植性,请使用plugins_url()构建正确的 URL。如果您要在脚本中加入除插件之外的其他内容,请使用一些相关函数来创建正确的 URL – 切勿对其进行硬编码
  • $deps是一个数组,可以处理您的新脚本所依赖的任何脚本,例如 jQuery。由于我们使用 jQuery 发送 AJAX 请求,因此您至少需要'jquery'在数组中列出。
  • $ver可让您列出版本号。
  • $argsin_footer定义页脚打印(通过键)和脚本加载策略(通过键)的参数数组,strategy例如deferasync$in_footer从 WordPress 版本 6.3 开始,这将替换/重载该参数。

wp_enqueue_script(
	'ajax-script',
	plugins_url( '/js/myjquery.js', __FILE__ ),
	array( 'jquery' ),
	'1.0.,0',
	array(
	   'in_footer' => true,
	)
);

加载插件代码页时,您无法直接将脚本排入队列。脚本必须从几个动作钩子之一排队——哪一个取决于脚本需要链接到哪种类型的页面。对于管理页面,请使用admin_enqueue_scripts. 对于前端页面,请使用wp_enqueue_scripts,但登录页面除外,在这种情况下,请使用login_enqueue_scripts

admin_enqueue_scripts钩子将当前页面文件名传递给您的回调。使用此信息仅将脚本排队到需要的页面上。前端版本没有传递任何东西。is_home()在这种情况下,请使用、等模板标签is_single()来确保您仅将脚本排入需要的位置。这是我们示例的完整排队代码:


add_action( 'admin_enqueue_scripts', 'my_enqueue' );
function my_enqueue( $hook ) {
	if ( 'myplugin_settings.php' !== $hook ) {
		return;
	}
	wp_enqueue_script(
		'ajax-script',
		plugins_url( '/js/myjquery.js', __FILE__ ),
		array( 'jquery' ),
		'1.0.0',
		array(
		   'in_footer' => true,
		)
	);
}

为什么我们在这里使用命名函数,而在 jQuery 中使用匿名函数?因为 PHP 最近才支持闭包。jQuery 对它们的支持已经有一段时间了。由于有些人可能仍在运行旧版本的 PHP,因此我们始终使用命名函数以获得最大兼容性。如果您有最新的 PHP 版本并且仅为您自己的安装进行开发,如果您愿意,可以继续使用闭包。

注册与入队

您将在其他教程中看到大量使用wp_register_script(). 这很好,但它的使用是可选的。不可选的是wp_enqueue_script()。必须调用此函数才能使您的脚本文件在网页上正确链接。那么为什么要注册脚本呢?它创建一个有用的标记或句柄,您可以根据需要轻松地在代码的各个部分引用脚本。如果您只需要加载脚本并且不在代码中的其他位置引用它,则无需注册它。

延迟脚本加载

WordPress 支持通过wp_register_script()和函数通过 WordPress 6.3 中引入的新数组参数中的键wp_enqueue_script()来指定脚本加载策略。strategy$args

支持的策略如下:

  • 推迟
    • 'strategy' => 'defer'通过向 $args 参数指定数组键值对来添加。
    • 通过 defer script 属性标记为延迟执行的脚本仅在 DOM 树完全加载后(但在 和DOMContentLoadedwindow load 事件之前)执行。与异步脚本不同,延迟脚本的执行顺序与在 DOM 中打印/添加的顺序相同。
  • 异步
    • 'strategy' => 'async'通过向参数指定数组键值对来添加$args
    • 通过 scriptasync属性标记为异步执行的脚本在浏览器加载后立即执行。异步脚本没有保证的执行顺序,因为脚本 B(尽管在脚本 A 之后添加到 DOM)可能会首先执行,因为它可能在脚本 A 之前完成加载。此类脚本可能会在 DOM 完全构建之前执行,或者活动结束后DOMContentLoaded

以下是为我们的插件中的附加脚本队列指定加载策略的示例:


wp_register_script(
    'ajax-script-two',
    plugins_url( '/js/myscript.js', __FILE__ ),
    array( ajax-script ),
    '1.0.,0',
    array(
          'strategy' => 'defer',
     )
);

使用时也适用相同的方法wp_enqueue_script()。在上面的示例中,我们表明我们打算'ajax-script-two'以延迟的方式加载脚本。

在指定延迟脚本加载策略时,在决定“合格策略”时会考虑脚本的依赖关系树(其依赖关系和/或依赖关系),以免导致应用对一个脚本有效的策略但会导致意外的执行顺序混乱,从而对树中的其他人有害。由于这种逻辑,您通过$args参数传递的预期加载策略可能不是最终(选择的)策略,但它永远不会对预期策略有害(或更严格)。

随机数

您需要创建一个随机数,以便可以将 jQuery AJAX 请求验证为合法请求,而不是来自某些未知不良行为者的潜在恶意请求。只有您的 PHP 脚本和 jQuery 脚本才会知道该值。收到请求后,您可以验证它是否与此处创建的值相同。这是为我们的示例创建随机数的方法:


$title_nonce = wp_create_nonce( 'title_example' );

该参数title_example可以是任意字符串。建议该字符串与随机数的用途相关,但它实际上可以是适合您的任何内容。

本地化

如果您还记得jQuery 部分,由 PHP 创建供 jQuery 使用的数据是在名为 的全局对象中传递的my_ajax_obj。在我们的示例中,此数据是一个随机数和 的完整 URL admin-ajax.php。分配对象属性和创建全局 jQuery 对象的过程称为本地化。这是我们示例中使用的本地化代码,它使用wp_localize_script().


wp_localize_script(
	'ajax-script',
	'my_ajax_obj',
	array(
		'ajax_url' => admin_url( 'admin-ajax.php' ),
		'nonce'    => $title_nonce,
	)
);

ajax-script请注意如何使用我们的脚本句柄,以便将全局对象分配给正确的脚本。该对象对于我们的脚本来说是全局的,而不是对于所有脚本来说。还可以从用于将脚本排队的同一个挂钩调用本地化。创建随机数也是如此,尽管该特定函数几乎可以在任何地方调用。所有这些组合在一个钩子回调中,如下所示:


add_action( 'admin_enqueue_scripts', 'my_enqueue' );

/**
 * Enqueue my scripts and assets.
 *
 * @param $hook
 */
function my_enqueue( $hook ) {
	if ( 'myplugin_settings.php' !== $hook ) {
		return;
	}
	wp_enqueue_script(
		'ajax-script',
		plugins_url( '/js/myjquery.js', __FILE__ ),
		array( 'jquery' ),
		'1.0.0',
		true
	);

	wp_localize_script(
		'ajax-script',
		'my_ajax_obj',
		array(
			'ajax_url' => admin_url( 'admin-ajax.php' ),
			'nonce'    => wp_create_nonce( 'title_example' ),
		)
	);
}
笔记:请记住仅将此随机数本地化添加到所需的页面,不要向不应该使用它的人显示随机数。current_user_can()并记住与能力或角色一起使用来完成安全性。

AJAX 操作

服务器端 PHP 代码的另一个主要部分是实际的 AJAX 处理程序,它接收 POST 数据,对其执行某些操作,然后将适当的响应发送回浏览器。它采用 WordPress操作挂钩的形式。您使用哪个挂钩标记取决于用户是否登录以及您的 jQuery 脚本作为action: value 传递的值。

笔记:$_GET、$_POST 和 $_COOKIE 与 $_REQUEST

您可能使用过一个或多个 PHP 超级全局变量,例如$_GET$_POST从表单或 cookie 中检索值(使用$_COOKIE)。也许您$_REQUEST更喜欢,或者至少已经看到过它的使用。这有点酷——无论请求方法是什么,POST或者GET,它都会有表单值。对于使用这两种方法的页面非常有效。最重要的是,它还有 cookie 值。一站式购物!这就是它的悲剧性缺陷。在名称冲突的情况下,cookie 值将覆盖任何表单值。因此,不良行为者很容易在浏览器上制作伪造的 cookie,这将覆盖您可能期望从请求中获得的任何表单值。$_REQUEST是黑客向您的表单值注入任意数据的简单途径。为了更加安全,请遵循特定变量并避免一刀切。

由于我们的 AJAX 交换是针对插件的设置页面的,因此用户必须登录。如果您还记得jQuery 部分,该action:值是"my_tag_count"。这意味着我们的操作挂钩标签将是wp_ajax_my_tag_count. 如果我们的 AJAX 交换由当前未登录的用户使用,则操作挂钩标记将是wp_ajax_nopriv_my_tag_count用于挂钩操作的基本代码如下所示:


add_action( 'wp_ajax_my_tag_count', 'my_ajax_handler' );

/**
 * Handles my AJAX request.
 */
function my_ajax_handler() {
	// Handle the ajax request here

	wp_die(); // All ajax handlers die when finished
}

AJAX 处理程序应该做的第一件事是验证 jQuery 发送的随机数check_ajax_referer(),该随机数应该与脚本排队时本地化的值相同。


check_ajax_referer( 'title_example' );

提供的参数必须与之前提供的参数相同wp_create_nonce()。如果随机数没有签出,该函数就会终止。如果这是一个真正的随机数,那么既然它被使用了,那么它的价值就不再有任何好处了。然后,您将生成一个新的并将其发送到回调脚本,以便它可以用于下一个请求。但由于 WordPress 随机数的有效期为 24 小时,因此您只需检查它即可。

数据

随着随机数的出现,我们的处理程序可以处理$_POST['title']. 首先,我们将值分配给一个新变量,运行完后wp_unslash() 删除任何意外的引号。


$title = wp_unslash( $_POST['title'] );

我们可以使用以下命令将用户的选择保存在用户元中update_user_meta()


update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $title ) );

然后我们构建一个查询来获取所选标题标签的帖子计数。


$args      = array(
	'tag' => $title,
);
$the_query = new WP_Query( $args );

最后我们可以将响应发送回 jQuery 脚本。传输数据的方式有多种。在讨论示例的具体细节之前,让我们先看看一些选项。

XML

PHP 对 XML 的支持还有一些不足之处。幸运的是,WordPress 提供了该类WP_Ajax_Response来使任务变得更容易。WP_Ajax_Response类将生成 XML 格式的响应,为标头设置正确的内容类型,输出响应 xml,然后结束 — 确保正确的 XML 响应

JSON

这种格式是轻量级且易于使用的,WordPress 提供了wp_send_json对您的响应进行 json 编码、打印和消亡的功能 - 有效地替换了WP_Ajax_Response。WordPress 还提供了wp_send_json_successwp_send_json_error函数,允许在 JS 中触发适当的 done() 或 failed() 回调。

其他

只要发送者和接收者协调一致,您就可以以任何您喜欢的方式传输数据。逗号分隔或制表符分隔等文本格式是多种可能性之一。对于少量数据,发送原始流可能就足够了。这就是我们将在示例中执行的操作 – 我们将发送实际的替换 HTML,仅此而已。


echo esc_html( $title ) . ' (' . $the_query->post_count . ') ';

在现实世界的应用程序中,您必须考虑操作可能因某种原因失败的可能性,例如数据库服务器可能已关闭。响应应该考虑到这种意外情况,并且接收响应的 jQuery 脚本应该采取相应的行动,也许会告诉用户稍后再试。

当处理程序完成所有任务后,它需要死亡。如果您使用WP_Ajax_Response或 wp_send_json* 函数,则会自动为您处理。如果没有,只需使用 WordPresswp_die() 功能即可。

AJAX 处理程序摘要

我们示例的完整 AJAX 处理程序如下所示:


/**
 * AJAX handler using JSON
 */
function my_ajax_handler__json() {
	check_ajax_referer( 'title_example' );
	$title = wp_unslash( $_POST['title'] );

	update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $title ) );

	$args      = array(
		'tag' => $title,
	);
	$the_query = new WP_Query( $args );
	wp_send_json( esc_html( $title ) . ' (' . $the_query->post_count . ') ' );
}

/**
 * AJAX handler not using JSON.
 */
function my_ajax_handler() {
	check_ajax_referer( 'title_example' );
	$title = wp_unslash( $_POST['title'] );

	update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $title ) );

	$args      = array(
		'tag' => $title,
	);
	$the_query = new WP_Query( $args );
	echo esc_html( $title ) . ' (' . $the_query->post_count . ') ';
	wp_die(); // All ajax handlers should die when finished
}