要实现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' ), ) ); }
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_success
和wp_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 }