您的 WordPress API 开发人员未使用的五种久经考验的技术

已发表: 2022-03-11

至少在您的客户眼中,提升您作为 WordPress 开发人员的地位的最佳方法之一就是熟练使用 API。 这是 WordPress API 实现的一个常见场景:您的客户要求您向他们的站点添加一个小部件——例如,一个电子邮件订阅小部件。 您从他们的第三方电子邮件服务中获取一些代码(可能是脚本标签或iframe ),将其粘贴到页面中,然后回复您的客户,“知道了!”

不幸的是,您正在与要求更高的客户打交道,他们注意到以下缺陷:

  • 尽管该小部件与网站的其他部分一样具有无衬线字体,但它并不完全正确。 该小部件使用 Helvetica 而不是您安装的自定义字体。
  • 小部件的订阅表单会触发新的页面加载,如果将其放置在文章的中途,这可能会造成干扰。
  • 在页面的其余部分之后,小部件似乎需要额外的时间来加载,这感觉很刺耳而且很便宜。
  • 客户希望根据订阅者订阅的帖子使用元数据标记订阅者,并且该小部件不提供与此功能类似的任何东西。
  • 客户发现他们现在必须管理两个仪表板(wp-admin 和电子邮件服务的管理区域)很烦人。

在这一点上,可能会发生两件事中的一件。 您可以将这些项目声明为“不错”,并向您的客户保证 80/20 解决方案的优点,或者您可以满足这些要求。 根据我的个人经验,我发现交付此类请求(即展示对第三方服务的掌握程度)是让客户相信您是某种 WordPress 向导的可靠方法。 另外,这通常很有趣。

在过去的十年中,我使用 WordPress 作为 API 消费平台,可能有 50 种不同的 API。 一些最常见的 API 是 MailChimp、Google Analytics、Google Maps、CloudFlare 和 Bitbucket。 但是,如果您需要做更多事情,如果您需要定制解决方案怎么办?

如何开发 WordPress API 客户端

在本文中,我将针对通用的“电子邮件服务”API 进行开发,尽我最大的努力使事情尽可能不可知。 但是,我确实认为我们正在处理 JSON REST API 是合理的。 以下是一些背景主题,可能会帮助您享受本文中的技术要点:

  • WordPress HTTP 系列函数
  • JSON
  • 休息

如果您发现自己对这些主题稍微熟悉并且有兴趣深入挖掘,请立即暂停并下载出色的 Postman 应用程序。 它允许您在不编写代码的情况下与 API 进行通信。

Postman 的仪表板截图

邮差。 也许我最喜欢的开发工具?

但是,如果您根本不熟悉这些内容,请继续阅读。 具有一定程度 WordPress 经验的技术读者将从本文中获得最大收益,但我会注意以不太技术化的方式解释每种技术的价值。 非技术读者将使本文能够在赞助之前评估每个点的投资回报率,并在交付后判断实施质量。

注意:如果您需要快速复习课程,可以查看我们的 WordPress REST API 指南。

在没有进一步的序言的情况下,请允许我与您分享一些不同的技术,我发现自己在与我合作的大多数 API、项目和团队中都很欣赏这些技术。

瞬态:何时握住它们,何时折叠它们

在我的开头段落中,我注意到客户发现跨越两个管理区域很烦人:wp-admin 和他们的电子邮件服务的仪表板。 解决这个问题的一个好方法是在 wp-admin 中为他们提供一个仪表板小部件,用于显示他们最近订阅者活动的摘要。

wp-admin 仪表板小部件的屏幕截图

我们可能在 WordPress 中提供的仪表板 UI 类型的示例,以节省我们的客户前往第三方电子邮件服务提供商的时间。

但是话又说回来,这可能需要向远程 API(电子邮件服务提供的 API)发出多个 HTTP 请求,从而导致页面加载时间过长。 此性能问题的解决方案是将 API 调用存储为瞬态。 这篇 Codex 文章提供了一个很好的解释,你一定要阅读,但我会总结一下:

  1. 从远程 API 获取数据。
  2. 使用set_transient()存储它,并根据您自己对性能、速率限制和在此特定应用程序中显示过时数据的错误余量的判断来选择到期时间。
  3. 继续你的业务逻辑——处理数据,返回一个值,不管是什么情况。
  4. 当您再次需要数据时,例如在下一页加载时,请在确定您需要从 API 获取数据之前,使用get_transient()在瞬态缓存中检查它。

我认为这是一个有用且可行的基础,但如果您考虑一下 REST 动词,您可以更进一步。 在五种最常用的方法(GET、POST、PATCH、PUT、DELETE)中,只有一种属于您的瞬态缓存。 你能猜出是哪一个吗? 这是获取。 在我的插件中,我几乎总是有一个 PHP 类专门用于抽象对相关远程 API 的调用,并且在实例化该类时的一个参数是 HTTP 方法。 如果它不是 GET 调用,那么我根本不会调用任何缓存层。

此外,如果它不是一个 GET 调用,那么有理由认为我正在采取一些行动以某种方式更改远程数据,可能是通过添加、编辑或删除电子邮件订阅者。 这可能是通过delete_transient()使该资源的现有缓存失效的好时机。

回到我们的 WordPress 电子邮件订阅 API 示例,以下是它在实践中的工作方式:

  • 用于显示最近订阅者的仪表板小部件将通过 GET 请求调用/subscribers的 API 端点。 因为它是一个 GET 请求,所以它被存储在我的临时缓存中。
  • 用于订阅电子邮件列表的侧边栏小部件将通过 POST 请求调用/subscribers的 API 端点。 因为它是一个 POST 请求,它不仅会避免我的瞬态缓存,还会促使我删除我的瞬态缓存的相关部分,以便仪表板小部件反映这个新订阅者。
  • 在命名瞬变时,我经常通过在我调用的远程 API URL 之后字面上命名它们来组织它们。 这是识别要删除的正确瞬态的便捷方法。 如果它是一个带有参数的端点,我会将它们连接成一个字符串并将它们也添加到瞬态名称中。

作为客户或其他技术含量较低的利益相关者,您应该在应用程序从远程服务中提取数据的任何时候专门请求临时缓存(或至少对其进行讨论)。 您应该熟悉出色的 Query Monitor 插件,以了解瞬态是如何工作的。 它将为您提供一个界面,用于浏览哪些数据作为瞬态存储、存储频率和存储时间。

有时瞬态还不够好

一些高级 WordPress 托管服务实际上不允许您在生产中使用瞬态。 他们运行的代码可能以 MU 插件或其他脚本的形式运行,这将拦截您尝试使用瞬态 API 并通过对象缓存存储该信息。 WP-Engine 在其最常见的配置中就是一个典型的例子。

标题中描述的 phpMyAdmin 视图的屏幕截图

phpMyAdmin UI 中的一个令人震惊的景象:一个完全没有瞬态的生产站点? 这可能意味着对象缓存在起作用。

如果您只是简单地存储和检索数据,您实际上不必关心这一点,甚至可能永远不会注意到它正在发生。 整个*_transient()函数系列将为您提供相同的最终结果,只是过滤为使用对象缓存而不是瞬态缓存。 但是,在尝试删除瞬变时,您可能会遇到问题。 这就是为什么。

如果您的 API 集成足够复杂,值得拥有自己的设置页面,您可能希望包含一个 UI 以允许管理员用户清除插件的整个临时缓存。 此按钮最常见的用途是客户端直接在远程服务上更改某些数据,并希望使我们存储在 WordPress 中的缓存无效。 如果客户端更改帐户凭据、API 密钥或只是作为“恢复出厂设置”按钮进行调试,此按钮也可能会派上用场。

选项按钮的屏幕截图

允许客户端清空其 API 数据的本地缓存的 UI 示例。

即使您足够聪明地命名所有临时键,以便您有希望为delete_transient()识别它们中的每一个,最好的情况可能仍然涉及原始 SQL,我在 WordPress 中总是尽量避免:

 <?php // Purge all the transients associated with our plugin. function purge() { global $wpdb; $prefix = esc_sql( $this -> get_transient_prefix() ); $options = $wpdb -> options; $t = esc_sql( "_transient_timeout_$prefix%" ); $sql = $wpdb -> prepare ( " SELECT option_name FROM $options WHERE option_name LIKE '%s' ", $t ); $transients = $wpdb -> get_col( $sql ); // For each transient... foreach( $transients as $transient ) { // Strip away the WordPress prefix in order to arrive at the transient key. $key = str_replace( '_transient_timeout_', '', $transient ); // Now that we have the key, use WordPress core to the delete the transient. delete_transient( $key ); } } ?>

不方便,效率不高。 相反,这种情况需要对象缓存,因为对象缓存为我们提供了一种将缓存值组合在一起的便捷方式。 这样,当您需要清空与插件相关的所有缓存值时,只需对wp_cache_delete( $key, $group )进行简单的单行调用。

我会这样总结所有这些:如果您还不是管理数据缓存的专家,那么您就不能成为使用 API 的专家。

作为客户端,需要注意的关键是暂存环境和生产环境之间的异常缓存行为。 换句话说,尽管在暂存中测试一批新工作始终是一个好习惯,但缓存在生产中也必须同样小心地进行测试。

远程 API 可以帮助通知您的 PHP 类层次结构

在为我的插件设计各种 PHP 类时,我经常发现模仿 API 端点的定义方式很有帮助——例如,以下端点似乎有什么共同点?

  • https://api.example-email-service.com/v1/subscribers.json
  • https://api.example-email-service.com/v1/lists.json
  • https://api.example-email-service.com/v1/campaigns.json

它们都返回collections ,我的意思是 GET 请求的结果,返回零对多的结果,其中每个结果都是数组的成员。 这听起来很明显,但我发现它对我的 PHP 代码中的以下类结构很有帮助:

  • class.collection.php ,一个抽象类
  • class.subscribers.php扩展了抽象类Collection
  • class.lists.php扩展了抽象类Collection
  • class.campaigns.php扩展了抽象类Collection

抽象类将一个查询参数数组作为其唯一参数:诸如分页、排序列、排序顺序和搜索过滤器之类的东西。 它将具有用于常见任务的方法,例如调用远程 API、处理错误,以及可能将结果转换为 HTML <select>菜单或 jQueryUI AutoSuggest。 实例化抽象类的类可能很短,可能只是指定要在*.json API 端点 URL 中使用的字符串。

Mailchimp API 游乐场的屏幕截图

Mailchimp 为沙盒 API 调用等发布了一个 API “playground”。 它还可以作为浏览其 API 的整个数据层次结构的便捷方式,让我们了解如何构建自己的类层次结构。

同样,以下端点有什么共同点?

  • https://api.example-email-service.com/v1/subscribers/104abyh4.json
  • https://api.example-email-service.com/v1/lists/837dy1h2.json
  • https://api.example-email-service.com/v1/campaigns/9i8udr43.json

他们都返回一个item ,我的意思是一个特定的、唯一的集合成员:比如一个特定的电子邮件订阅者、一个电子邮件列表或一个电子邮件活动。 因此,我喜欢在我的 PHP 代码中使用以下结构:

  • class.item.php ,一个抽象类
  • class.subscriber.php扩展了抽象类Item
  • class.list.php扩展了抽象类Item
  • class.campaign.php扩展了抽象类Item

抽象类将使用一个字符串作为其唯一参数,以标识所请求的特定项目。 再一次,被实例化的类可能很短,可能只是指定要在*/duy736td.json中使用的字符串。

构建类继承的方法有很多,但即使您采用与我上面概述的方法不同的方法,我敢打赌远程 API 的结构很有可能有助于了解应用程序的结构。

作为客户,糟糕架构的一个常见症状是当您发现自己不得不在应用程序中一遍又一遍地请求相同的更改。 例如,如果您要求报告每页返回 100 个结果而不是 10 个,并且您必须不断重复该请求以获取订阅者报告、活动报告、取消订阅报告等,您可能会检测到糟糕的类架构。 在这种情况下,值得询问您的团队他们是否会从重构周期中受益:一组工作的目标不是改变产品的行为,而是改进底层代码,以便更容易改变行为未来的产品。

WP_Error的完美用例

我很尴尬地承认,在我的代码中正确开始使用WP_Error系列函数花了我数年的时间。 我倾向于只编写自己的方式,或者假设永远不会有错误值得以编程方式关注,或者根据具体情况处理它们。 使用远程 API 就像激光束一样打破了这种心态,因为它为使用WP_Error提供了一个极其方便和强大的用例。

回想一下,我之前提到过我经常有一个 PHP 类,其目的是向远程 API 发出 HTTP 请求。 当您剥离所有样板、所有数据操作、所有次要问题时,该类实际上归结为调用wp_remote_request()以便从 API 获取 HTTP 响应对象。 方便的是,如果调用由于某种原因无法执行, wp_remote_request()将改为返回WP_Error ,但如果调用成功返回了不受欢迎的 HTTP 响应呢?

订阅表格的屏幕截图

从技术上讲,API 调用有效,但并非完全没有警告。 需要在整个代码库中以一致的方式捕获和报告这些警告。

例如,也许我们调用了/lists.json端点,但这个特定帐户还没有设置任何列表。 这将返回一个有效的 HTTP 响应,但状态代码为 400。虽然这本身并不是一个致命错误,但从一些希望将此 API 调用转换为下拉菜单的前端代码的角度来看,400 可能也是一个WSOD! 因此,我发现对wp_remote_request()的结果进行一些额外的解析很有帮助,毕竟可能会返回一个WP_Error

 <?php function call() { $response = wp_remote_request( $this -> url, $this -> args ); $code = wp_remote_retrieve_response_code( $response ); $first_digit = $code[0]; $good_responses = array( 2, 3 ); if( ! in_array( $first_digit, $good_responses ) { $body = wp_remote_retrieve_body( $response ); $out = new WP_Error( $code, $body ); } else { $out = $response; } return $out; } ?>

这种模式可以帮助简化调用调用者类的代码,因为我们知道在继续输出之前我们可以安全地依赖is_wp_error()

作为客户,你应该偶尔扮演恶意用户、困惑用户和不耐烦用户的角色。 以不应该使用的方式使用该应用程序。 做你的开发人员似乎不希望你做的事情。 注意会发生什么。 您是否收到有用的错误消息? 您是否收到任何错误消息? 如果不是这样,可能值得赞助一些围绕更好的错误处理的工作。

ob_get_clean()的美丽调试能力

现代可编程网络,几乎每个站点都使用其他站点的 API,并且本身通过自己的 API 使用,已经成为一个非常强大的代码舞台。 但正是这种品质也让它变得相当缓慢。

远程 HTTP 请求通常是给定页面加载中最耗时的部分。 出于这个原因,许多 API 驱动的组件通过 Ajax 或 cron 执行。 例如,搜索电子邮件订阅者列表的自动建议可能应该在每次击键时按需 ping 远程数据源,而不是在页面加载时加载 DOM 中的所有 100,000 个订阅者。 如果这不是一个选项,也许大型查询可以在每晚的 cron 任务上同步,以便可以从本地镜像而不是远程 API 中提取结果。

这种方法的问题在于它可能难以调试。 不是简单地打开WP_DEBUG并让错误消息滚动到浏览器窗口中,而是卡在浏览器网络控制台中,或者在 cron 任务(希望如此?)正在执行时跟踪日志文件。 我觉得这很不舒服。

改善这种情况的一种方法是对error_log()进行谨慎和战略性的调用。 但话说回来,日志记录的一个常见问题是,对于大型或繁忙的应用程序,错误日志可能会变得过大或增长过快,无法用于监控或解析。 因此,我们必须对我们记录的内容进行选择性处理,就像对待我们的实际应用程序逻辑一样多考虑。 很遗憾花时间记录一些奇异的边缘情况错误,这些错误似乎只在一些不常见的 cron 任务中间歇性发生,只是为了意识到错误的真实性质再次避开了你,因为你没有记录一些特定的数组成员,比如说,有问题的价值。

因此,我的理念变成了,我并不总是记录,但当我这样做时,我会记录所有内容。 换句话说,在识别出一个特别令人担忧的函数之后,我会用尽可能宽的网络记录它:

 <?php function debug( $bug ) { ob_start(); var_dump( $bug ); $out = ob_get_clean(); error_log( $out ); } ?>

这相当于var_dump()将整个错误值放入错误日志文件中的单个条目中。

错误日志文件的屏幕截图

错误日志已变得太大而无法进行人体工程学调试。

作为客户端,值得定期检查应用程序的总文件内存使用情况。 如果您发现自己突然违反了托管帐户中的存储限制,则很有可能是错误日志失控了。 您的开发人员将从专注于更好日志记录的工作周期中受益——您的客户也将受益!

不完全是点击诱饵,但它会做

请原谅本文的列表结构。 我无法将这些观点强加到更统一的文章主题中,因为这些模式非常通用:它们适用于任何 JSON REST 端点和任何 WordPress 输出

它们是我反复看到的模式,无论远程 API 是什么,或者我们在 WordPress 中使用它做什么。 我已经将所有这些类型的原则收集到一个插件样板中,这极大地加速了我的工作。 您是否为每个项目保留了相似的点? 请分享它们,以便我可以窃取它们并将它们添加到我的样板中!

相关:如何进行现代 WordPress 开发(第 1 部分)