# 服务器端 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**可让您列出版本号。 - **$args**`in_footer`定义页脚打印(通过键)和脚本加载策略(通过键)的参数数组,`strategy`例如`defer`或`async`。`$in_footer`从 WordPress 版本 6.3 开始,这将替换/重载该参数。
```php 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()`来确保您仅将脚本排入需要的位置。这是我们示例的完整排队代码:
```php 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 树完全加载后(但在 和`DOMContentLoaded`window load 事件之前)执行。与异步脚本不同,延迟脚本的执行顺序与在 DOM 中打印/添加的顺序相同。 - **异步** - `'strategy' => 'async'`通过向参数指定数组键值对来添加`$args`。 - 通过 script`async`属性标记为异步执行的脚本在浏览器加载后立即执行。异步脚本没有保证的执行顺序,因为脚本 B(尽管在脚本 A 之后添加到 DOM)可能会首先执行,因为它可能在脚本 A 之前完成加载。此类脚本可能会在 DOM 完全构建之前执行,或者活动结束后`DOMContentLoaded`。 以下是为我们的插件中的附加脚本队列指定加载策略的示例:
```php 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 脚本才会知道该值。收到请求后,您可以验证它是否与此处创建的值相同。这是为我们的示例创建随机数的方法:
```php $title_nonce = wp_create_nonce( 'title_example' ); ``` 该参数`title_example`可以是任意字符串。建议该字符串与随机数的用途相关,但它实际上可以是适合您的任何内容。 #### 本地化 如果您还记得[jQuery 部分](https://developer.wordpress.org/plugins/javascript/jquery/),由 PHP 创建供 jQuery 使用的数据是在名为 的全局对象中传递的`my_ajax_obj`。在我们的示例中,此数据是一个随机数和 的完整 URL `admin-ajax.php`。分配对象属性和创建全局 jQuery 对象的过程称为**本地化**。这是我们示例中使用的本地化代码,它使用`wp_localize_script()`.
```php wp_localize_script( 'ajax-script', 'my_ajax_obj', array( 'ajax_url' => admin_url( 'admin-ajax.php' ), 'nonce' => $title_nonce, ) ); ``` `ajax-script`请注意如何使用我们的脚本句柄,以便将全局对象分配给正确的脚本。该对象对于我们的脚本来说是全局的,而不是对于所有脚本来说。还可以从用于将脚本排队的同一个挂钩调用本地化。创建随机数也是如此,尽管该特定函数几乎可以在任何地方调用。所有这些组合在一个钩子回调中,如下所示:
```php 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[操作挂钩](https://developer.wordpress.org/plugins/hooks/actions/)的形式。您使用哪个挂钩标记取决于用户是否登录以及您的 jQuery 脚本作为*action:* value 传递的值。 笔记:**$\_GET、$\_POST 和 $\_COOKIE 与 $\_REQUEST** 您可能使用过一个或多个 PHP 超级全局变量,例如`$_GET`或`$_POST`从表单或 cookie 中检索值(使用`$_COOKIE`)。也许您`$_REQUEST`更喜欢,或者至少已经看到过它的使用。这有点酷——无论请求方法是什么,`POST`或者`GET`,它都会有表单值。对于使用这两种方法的页面非常有效。最重要的是,它还有 cookie 值。一站式购物!这就是它的悲剧性缺陷。在名称冲突的情况下,cookie 值将覆盖任何表单值。因此,不良行为者很容易在浏览器上制作伪造的 cookie,这将覆盖您可能期望从请求中获得的任何表单值。`$_REQUEST`是黑客向您的表单值注入任意数据的简单途径。为了更加安全,请遵循特定变量并避免一刀切。 由于我们的 AJAX 交换是针对插件的设置页面的,因此用户必须登录。如果您还记得[jQuery 部分](https://developer.wordpress.org/plugins/javascript/jquery/),该`action:`值是`"my_tag_count"`。这意味着我们的操作挂钩标签将是`wp_ajax_my_tag_count`. 如果我们的 AJAX 交换由当前未登录的用户使用,则操作挂钩标记将是`wp_ajax_nopriv_my_tag_count`用于挂钩操作的基本代码如下所示:
```php 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()`,该随机数应该与脚本排队时本地化的值相同。
```php check_ajax_referer( 'title_example' ); ``` 提供的参数必须与[之前](https://developer.wordpress.org/plugins/javascript/enqueuing/#php-nonce)提供的参数相同`wp_create_nonce()`。如果随机数没有签出,该函数就会终止。如果这是一个真正的随机数,那么既然它被使用了,那么它的价值就不再有任何好处了。然后,您将生成一个新的并将其发送到回调脚本,以便它可以用于下一个请求。但由于 WordPress 随机数的有效期为 24 小时,因此您只需检查它即可。 #### 数据 随着随机数的出现,我们的处理程序可以处理`$_POST['title']`. 首先,我们将值分配给一个新变量,运行完后[wp\_unslash()](https://developer.wordpress.org/reference/functions/wp_unslash/) 删除任何意外的引号。
```php $title = wp_unslash( $_POST['title'] ); ``` 我们可以使用以下命令将用户的选择保存在用户元中[update\_user\_meta()](https://developer.wordpress.org/reference/functions/update_user_meta/)。
```php update_user_meta( get_current_user_id(), 'title_preference', sanitize_post_title( $title ) ); ``` 然后我们构建一个查询来获取所选标题标签的帖子计数。
```php $args = array( 'tag' => $title, ); $the_query = new WP_Query( $args ); ``` 最后我们可以将响应发送回 jQuery 脚本。传输数据的方式有多种。在讨论示例的具体细节之前,让我们先看看一些选项。 #### XML PHP 对 XML 的支持还有一些不足之处。幸运的是,WordPress 提供了该类`WP_Ajax_Response`来使任务变得更容易。WP\_Ajax\_Response类将生成 XML 格式的响应,为标头设置正确的内容类型,输出响应 xml,然后结束 — 确保正确的 XML 响应[。](https://developer.wordpress.org/reference/classes/wp_ajax_response/) #### JSON 这种格式是轻量级且易于使用的,WordPress 提供了`wp_send_json`对您的响应进行 json 编码、打印和消亡的功能 - 有效地替换了[WP\_Ajax\_Response](https://developer.wordpress.org/reference/classes/wp_ajax_response/)。WordPress 还提供了`wp_send_json_success`和`wp_send_json_error`函数,允许在 JS 中触发适当的 done() 或 failed() 回调。 #### 其他 只要发送者和接收者协调一致,您就可以以任何您喜欢的方式传输数据。逗号分隔或制表符分隔等文本格式是多种可能性之一。对于少量数据,发送原始流可能就足够了。这就是我们将在示例中执行的操作 – 我们将发送实际的替换 HTML,仅此而已。
```php echo esc_html( $title ) . ' (' . $the_query->post_count . ') '; ``` 在现实世界的应用程序中,您必须考虑操作可能因某种原因失败的可能性,例如数据库服务器可能已关闭。响应应该考虑到这种意外情况,并且接收响应的 jQuery 脚本应该采取相应的行动,也许会告诉用户稍后再试。 #### 死 当处理程序完成所有任务后,它需要死亡。如果您使用[WP\_Ajax\_Response](https://developer.wordpress.org/reference/classes/wp_ajax_response/)或 wp\_send\_json\* 函数,则会自动为您处理。如果没有,只需使用 WordPress`wp_die() `功能即可。 #### AJAX 处理程序摘要 我们示例的完整 AJAX 处理程序如下所示:
```php /** * 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 . ') ' ); } ```
```php /** * 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 } ```