服务端PHP和引入

要实现AJAX通信,服务器端PHP脚本需要完成两个部分。首先,我们需要将jQuery脚本引入到web页面上,并本地化jQuery脚本需要的PHP值,其次是AJAX请求的实际处理。

 

引入脚本

本节介绍了WordPress中AJAX的两个主要特性,这两个特性让刚接触WordPress的有经验的程序员感到困惑。一个是需要将脚本引入,以使meta link正确显示在页面的head部分。另一个是所有AJAX请求都需要通过wp-admin/admin-ajax.php发送。永远不要直接向插件页面发送请求。

 

排队引入

使用函数wp_enqueue_script()让WordPress在页面插入指向脚本的meta link。不要在header模板中硬编码此类链接。作为插件开发人员,您没有现成的header模板访问权限,但无论如何,这条规则值得一提。

排队函数采用三个参数。第一个是任意标记或句柄,用于在其他函数中引用此脚本。第二个是脚本文件的完整URL。为了便于移植,请使用plugins_url()构建适当的URL。如果您将脚本引入以获取插件以外的内容,请使用一些相关函数来创建适当的URL,不要硬编码。第三个参数是脚本依赖的其他脚本标记、句柄的数组。由于我们使用jQuery发送AJAX请求,您至少需要在数组中列出“jQuery”。始终使用数组,即使它只有单个依赖项。我们示例中的排队调用如下所示:

wp_enqueue_script(
	'ajax-script',
	plugins_url( '/js/myjquery.js', __FILE__ ),
	array( 'jquery' ),
	'1.0.0',
	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',
		true
	);
}

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

 

注册与排队

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

 

Nonce

您需要创建一个nonce,以便jQuery AJAX请求可以被验证为合法请求,而不是来自某个未知坏角色的潜在恶意请求。只有PHP脚本和jQuery脚本才知道这个值。当收到请求时,您可以验证它是否与此处创建的值相同。以下是如何为我们的示例创建nonce:

$title_nonce = wp_create_nonce( 'title_example' );

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

 

本地化

如果您回想一下jQuery部分,由PHP创建供jQuery使用的数据被传递到名为my_ajax_obj的全局对象中。在我们的示例中,该数据是一个nonce和admin-ajax.php的完整URL。分配对象属性和创建全局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是如何使用的,以便将全局对象分配给正确的脚本。该对象对于我们的脚本是全局的,而不是所有脚本。本地化也可以从用于将脚本排队的同一个钩子中调用。创建nonce也是如此,尽管该特定函数几乎可以在任何地方调用。所有这些结合在一个钩子回调中,如下所示:

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' ),
		)
	);
}
Note:记住,只将此nonce本地化添加到所需的页面,不要向不使用它的人显示nonce。记住使用current_user_can()和一个能力或角色来完成安全性验证。

 

AJAX 动作

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

Note:$_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()发送的nonce,该值应该与脚本排队时本地化的值相同。

check_ajax_referer( 'title_example' );

提供的参数必须与之前在wp_create_nonce()提供的参数相同。如果nonce未检出,则函数将简单地终止。如果这是一个真正的nonce,现在它被使用了,那么这个值就不再有效。然后您将生成一个新的,并将其发送到回调脚本,这样它就可以用于下一个请求。但是由于WordPress的nonce在24小时内有效,你只需要检查一下就可以了。

 

数据

有了nonce,我们的处理程序可以处理$_POST['title']中包含的jQuery脚本发送的数据。我们可以使用update_user_meta()将用户的选择保存在用户meta中。

update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $_POST['title'] ) );

然后,我们构建一个查询,以获取所选标题标签的文章数量。

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

最后,我们可以将响应发送回jQuery脚本。有几种传输数据的方法。在处理示例的细节之前,让我们先看看一些选项。

 

XML

PHP对XML的支持还有待改进。幸运的是,WordPress提供了WP_Ajax_Response类来简化任务。WP_Ajax_Response类将生成一个XML格式的响应,为header设置正确的内容类型,输出响应XML,然后die - 确保正确的XML响应。

 

JSON

这种格式轻量级且易于使用,WordPress提供了wp_send_json函数来对您的响应进行json编码、打印并最终替换WP_Ajax_Response。WordPress还提供了wp_send_json_successwp_send_json_error函数,允许在JS中触发适当的done()或fail()回调。

 

其他

只要发送方和接收方协调一致,就可以以任何方式传输数据。逗号分隔或制表符分隔等文本格式是多种可能性之一。对于少量数据,发送原始流可能就足够了。这就是我们在示例中要做的——我们将发送实际的替换HTML,而不是其他内容。

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

在真实的应用程序中,您必须考虑操作可能由于某种原因而失败的可能性,例如,数据库服务器可能已关闭。响应应该考虑到这种偶然性,并且接收响应的jQuery脚本应该相应地进行操作,可能会告诉用户稍后再试。

 

Die

当处理程序完成所有任务时,它需要die。如果您使用的是WP_Ajax_Response或wp_send_json*函数,这将自动为您处理。如果没有,只需使用WordPresswp_die()函数。

 

AJAX处理程序总结

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

/**
 * AJAX 处理程序使用 JSON
 */
function my_ajax_handler__json() {
	check_ajax_referer( 'title_example' );
	update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $_POST['title'] ) );
	$args      = array(
		'tag' => $_POST['title'],
	);
	$the_query = new WP_Query( $args );
	wp_send_json( esc_html( $_POST['title'] ) . ' (' . $the_query->post_count . ') ' );
}
/**
 * AJAX 处理程序不使用 JSON.
 */
function my_ajax_handler() {
	check_ajax_referer( 'title_example' );
	update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $_POST['title'] ) );
	$args      = array(
		'tag' => $_POST['title'],
	);
	$the_query = new WP_Query( $args );
	echo esc_html( $_POST['title'] ) . ' (' . $the_query->post_count . ') ';
	wp_die(); // 当完成所有ajax处理程序后需要die
}