Создание плагина WordPress, использующего сервисные API, «От супа до орехов»

Опубликовано: 2022-03-10
Краткий обзор ↬ Все большее число общедоступных API-интерфейсов предоставляют мощные сервисы для расширения функциональности наших приложений. WordPress — невероятно динамичная и гибкая CMS, которая поддерживает все, от небольших личных блогов до крупных веб-сайтов электронной коммерции и всего, что между ними. Частью того, что делает WordPress таким универсальным, является его мощная система плагинов , которая позволяет невероятно легко добавлять функциональные возможности. Мы рассмотрим, как я создал GitHub Pipeline, плагин, который позволяет отображать данные из GitHub API на страницах WordPress с помощью шорткодов. Я приведу конкретные примеры и фрагменты кода, но считайте технику, описанную здесь, образцом того, как использовать API любого сервиса с плагином. Мы начнем с самого начала, но предполагается некоторое знакомство с WordPress и разработкой плагинов, и мы не будем тратить время на темы для начинающих, такие как установка WordPress или Composer.

Все большее количество общедоступных API предоставляет мощные сервисы для расширения функциональности наших приложений. WordPress — невероятно динамичная и гибкая CMS, которая поддерживает все, от небольших личных блогов до крупных веб-сайтов электронной коммерции и всего, что между ними. Частью того, что делает WordPress таким универсальным, является его мощная система плагинов , которая позволяет невероятно легко добавлять функциональные возможности.

Мы рассмотрим, как я создал GitHub Pipeline, плагин, который позволяет отображать данные из GitHub API на страницах WordPress с помощью шорткодов. Я приведу конкретные примеры и фрагменты кода, но считайте технику, описанную здесь, образцом того, как использовать API любого сервиса с плагином.

Дальнейшее чтение на SmashingMag:

  • Основы WordPress: как создать плагин WordPress
  • Как развернуть плагины WordPress с помощью GitHub с помощью Transients
  • Три подхода к добавлению настраиваемых полей в ваш плагин

Мы начнем с самого начала, но предполагается некоторое знакомство с WordPress и разработкой плагинов, и мы не будем тратить время на темы для начинающих, такие как установка WordPress или Composer.

Еще после прыжка! Продолжить чтение ниже ↓

Тебе понадобиться:

  • среда PHP со свежеустановленным WordPress;
  • учетная запись GitHub (или другого поставщика API, если вы хотите импровизировать);
  • Композитор (рекомендуется).

Выбор API

Первым шагом в написании такого плагина является выбор API. В этом руководстве мы будем использовать GitHub API. Если вы думаете об использовании другого API, рассмотрите несколько важных факторов, которые могут повлиять на то, насколько полезным будет ваш проект.

Первое, на что я обращаю внимание, — это тщательность и качество документации по API. Если он скудный или устаревший, будьте готовы потратить время на просмотр исходного кода, чтобы ответить на ваши собственные вопросы. Кроме того, подумайте, насколько зрелым является API и насколько ответственно поставщик разработал его версию. Нет ничего хуже, чем потратить время на создание чего-то отличного только для того, чтобы оно сломалось из-за изменений в исходном API. Найдите систему управления версиями в URL-адресах конечных точек.

 // good https://api.stable.com/v1/user/ // danger https://api.dodgy.com/user/

Рискуя констатировать очевидное, программирование для стороннего API подразумевает доверительные отношения, и не все API созданы равными.

Покупка библиотеки

Известные бренды часто распространяют библиотеки или SDK, чтобы упростить работу с их API. Если это не так, не забудьте выяснить, написал ли кто-то еще библиотеку, прежде чем идти и изобретать велосипед. Google и GitHub — два отличных места для начала исследования. Количество звездочек или форков на GitHub — хороший показатель эффективности библиотеки. Возраст самых последних коммитов и/или количество открытых проблем являются показателем того, насколько активно они поддерживаются. В моем случае мне повезло найти прекрасный PHP GitHub API от KNP Labs.

Написание собственного с помощью Guzzle

Если у вашего провайдера нет подходящей библиотеки, вам все равно не стоит начинать с нуля, потому что такие инструменты, как Guzzle, значительно упрощают работу с HTTP-запросами. Guzzle является оберткой для PHP-библиотеки cURL и избавляет от многих головных болей, обычно связанных с настройкой и выполнением запросов вручную. Даже если вы делаете только один или два запроса, я все равно рекомендую использовать его, потому что он сделает ваш код более надежным, а его установка с помощью Composer — это проще простого.

Настройка плагина

Мы начнем с базового скелета минимального плагина WordPress, каталога с двумя файлами. Важно выбрать описательное и уникальное имя папки, чтобы избежать конфликтов с другими плагинами. Если название вашего плагина несколько общее, рассмотрите возможность добавления уникального префикса.

 github-api/ readme.txt github-api.php

readme.txt содержит метаданные для вашего плагина, которые появятся на wordpress.org , если вы решите опубликовать его там. Прочтите о публикации плагинов WordPress в документации или ознакомьтесь с исчерпывающим образцом readme.txt.

Файл PHP также содержит некоторые метаданные в заголовке, которые будут использоваться для отображения информации о вашем плагине на панели инструментов. Начните с заголовка, который выглядит так:

 <?php /** Plugin Name: GitHub API description: >- Add GitHub project information using shortcode Version: 1.0 Author: Your Name License: GPLv2 or later Text Domain: github-api */

Из соображений безопасности также рекомендуется запретить прямой доступ к файлу, например:

 defined( 'ABSPATH' ) or die( 'No script kiddies please!' );

На этом этапе плагин ничего не делает, но когда вы скопируете файлы в wp-content/plugins , он должен появиться в списке плагинов, и вы сможете его активировать.

Страница плагинов панели управления WordPress
Страница плагинов панели управления WordPress. (Посмотреть большую версию)

Далее мы хотим включить библиотеку, которая будет обрабатывать запросы API. В следующем примере кода мы включаем PHP GitHub API KNP Labs, но вы можете включить любую зависимость, заменив knplabs/github-api используемым пакетом.

 $ cd wp-content/plugins/github-api $ composer require knplabs/github-api

Теперь ваша файловая структура должна выглядеть следующим образом:

 github-api/ composer.json composer.lock github-api.php readme.txt vendor/

Теперь, когда файлы на месте, нам нужно потребовать файл vendor/autoload.php , который был создан при его установке.

 require_once 'vendor/autoload.php';

Если все настроено правильно, вы сможете создать экземпляр класса из библиотеки без фатальной ошибки.

 $testing = new \Github\Client();

Но это всего лишь быстрый способ проверить, доступны ли ваши зависимости. Я рекомендую определить новый класс, расширяющий библиотеку.

 class MyGithub extends \Github\Client {};

Это не критично, но делает ваш код более гибким в нескольких отношениях. Самый очевидный способ — вы можете изменить или добавить новые функции, если вам это нужно. Но это также упрощает жизнь, если вы когда-нибудь захотите переключить библиотеки в будущем, потому что вам придется вносить изменения только в одном месте, а не во всем коде.

Шорткоды WordPress

Теперь пришло время настроить наш первый шорткод, который представляет собой специальный фрагмент, позволяющий генерировать контент, размещая его в публикации или на странице. Если вы совершенно не знакомы с шорткодами или хотите глубже понять, как они работают, ознакомьтесь с официальной документацией по шорткодам. В моем примере я буду отображать список проблем GitHub везде, где автор размещает этот шорткод: [github_issues]

Как и раньше, давайте начнем с минимального примера, чтобы убедиться, что шорткод правильно зарегистрирован, прежде чем мы приступим к вызову API.

 function github_issues_func( $atts ) { return "Hello world!"; } add_shortcode( "github_issues", "github_issues_func" );

Теперь, если вы опубликуете страницу, содержащую [github_issues] , вы должны увидеть «Hello world» при посещении страницы. Теперь, когда это работает, давайте настроим вызов API.

В предыдущем разделе мы настроили автозагрузку для нашей библиотеки GitHub и определили собственный класс для ее расширения. Теперь мы можем начать использовать его для вызовов API. Итак, мы добавим его в функцию обратного вызова шорткода. В качестве доказательства концепции давайте загрузим все задачи из репозитория GitHub Pipeline. Имеется документация по этому методу.

 function github_issues_func( $atts ) { // Instantiate our class $gh = new MyGithub(); // Make the API call to get issues, passing in the GitHub owner and repository $issues = $gh->api('issue')->all('TransitScreen', 'wp-github-pipeline'); // Handle the case when there are no issues if ( empty($issues) ) return "<strong>" . __("No issues to show", 'githup-api') . "</strong>"; // We're going to return a string. First, we open a list. $return = "<ul>"; // Loop over the returned issues foreach( $issues as $issue ) { // Add a list item for each issue to the string // (Feel free to get fancier here) // Maybe make each one a link to the issue issuing $issue['url] ) $return .= "<li>{$issue['title']}</li>"; } // Don't forget to close the list $return .= "</ul>"; return $return; } add_shortcode( 'github_issues', 'github_issues_func' );

Теперь вы сможете просмотреть ту же страницу, которую мы использовали выше для тестирования шорткода, и на этот раз вы должны увидеть неупорядоченный список проблем. Но ждать! Прежде чем двигаться дальше, давайте сделаем одну оптимизацию, чтобы наш код было легче тестировать. (Вы тестируете, верно?!) Вместо того, чтобы использовать new в нашей функции для создания экземпляра класса библиотеки GitHub, давайте передадим его в качестве параметра и создадим только в случае необходимости.

 function github_issues_func( $atts, $gh=null ) { // Conditionally instantiate our class $gh = ( $gh ) ? $gh : new MyGithub(); …

Эта модификация напоминает шаблон внедрения зависимостей, что значительно упрощает тестирование нашей функции. Модульное тестирование WordPress выходит за рамки этого руководства, но легко увидеть, как эта новая версия позволяет нам легко передавать «поддельные» данные API в функцию, чтобы утверждения нашего теста точно знали, чего ожидать.

То, что мы сделали до сих пор, хорошо подходит для внутреннего проекта, который будет использоваться только в одном репозитории, но было бы гораздо полезнее, если бы пользователи могли настраивать, из какого репозитория получать задачи. Давайте установим это.

Во-первых, мы будем использовать хук WordPress для регистрации нашей новой страницы настроек, а функция обратного вызова определит метки и заголовки меню. Мы будем использовать тот же поэтапный подход, чтобы заставить его работать.

 // Register the menu. add_action( "admin_menu", "gh_plugin_menu_func" ); function gh_plugin_menu_func() { add_submenu_page( "options-general.php", // Which menu parent "GitHub", // Page title "GitHub", // Menu title "manage_options", // Minimum capability (manage_options is an easy way to target administrators) "github", // Menu slug "gh_plugin_options" // Callback that prints the markup ); } // Print the markup for the page function gh_plugin_options() { if ( !current_user_can( "manage_options" ) ) { wp_die( __( "You do not have sufficient permissions to access this page." ) ); } echo "Hello world!"; }

Теперь вы сможете войти в панель управления и увидеть новое меню GitHub в разделе «Настройки», а затем щелкнуть его, чтобы увидеть пустую страницу настроек с надписью «Hello world!»

Пустая страница плагинов WordPress
Пустая страница плагинов WordPress. (Посмотреть большую версию)

Далее мы заменим Hello word на фактическую разметку формы. Я приведу вам простой пример, который вы можете стилизовать так мало или так, как вам нравится.

 ?> <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="update_github_settings" /> <h3><?php _e("GitHub Repository Info", "github-api"); ?></h3> <p> <label><?php _e("GitHub Organization:", "github-api"); ?></label> <input class="" type="text" name="gh_org" value="<?php echo get_option('gh_org'); ?>" /> </p> <p> <label><?php _e("GitHub repository (slug):", "github-api"); ?></label> <input class="" type="text" name="gh_repo" value="<?php echo get_option('gh_repo'); ?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Save", "github-api"); ?>" /> </form> <?php

Классы CSS, которые я включил, соответствуют классам, используемым ядром WordPress, что является хорошим способом заставить вашу пользовательскую страницу параметров выглядеть достойно без дополнительных усилий.

Пользовательская страница настроек WordPress с основными стилями
Пользовательская страница настроек WordPress с основными стилями. (Посмотреть большую версию)

Есть две важные вещи, которые нужно понять об этой форме. Во-первых, конечная точка, которой он подчиняется, должна быть /wp-admin/admin-post.php . Вы можете жестко указать этот путь, но если веб-сайт установлен в подкаталоге, он не будет работать. Итак, мы используем встроенную admin_url() для ее динамического создания.

Во-вторых, обратите внимание на скрытый ввод с именем action . Это поле позволяет WordPress узнать, что делать с запросом на публикацию, отправленным формой. value соответствует имени хука действия, который мы будем использовать для установки функции обратного вызова. Имя действия, к которому мы подключимся, будет этим значением с префиксом admin post . Итак, в нашем случае нам нужно добавить это:

 add_action( 'admin_post_update_github_settings', 'github_handle_save' );

Я считаю, что это один из самых причудливых и менее интуитивных аспектов создания пользовательских меню администрирования, но как только вы привыкнете к этому, это будет довольно безболезненно. Как вы могли догадаться, второй параметр add_action() — это имя нашей функции обратного вызова, которая фактически сохранит значения в базе данных.

 function github_handle_save() { // Get the options that were sent $org = (!empty($_POST["gh_org"])) ? $_POST["gh_org"] : NULL; $repo = (!empty($_POST["gh_repo"])) ? $_POST["gh_repo"] : NULL; // Validation would go here // Update the values update_option( "gh_repo", $repo, TRUE ); update_option("gh_org", $org, TRUE); // Redirect back to settings page // The ?page=github corresponds to the "slug" // set in the fourth parameter of add_submenu_page() above. $redirect_url = get_bloginfo("url") . "/wp-admin/options-general.php?page=github&status=success"; header("Location: ".$redirect_url); exit; }

Пример также весьма минимален. В производственных условиях вы, вероятно, захотите добавить некоторую проверку. Кроме того, в этом примере мы автоматически передаем status=success в URL-адрес. Разметка формы на самом деле еще не использует его. Чтобы условно показать сообщение об успехе, добавьте что-то вроде следующего над формой в gh_plugin_options() :

 if ( isset($_GET['status']) && $_GET['status']=='success') { ?> <div class="updated notice is-dismissible"> <p><?php _e("Settings updated!", "github-api"); ?></p> <button type="button" class="notice-dismiss"> <span class="screen-reader-text"><?php _e("Dismiss this notice.", "github-api"); ?></span> </button> </div> <?php }

Если вы добавляете проверку, используйте тот же шаблон для передачи различных статусов и сообщений, чтобы пользователь знал, если и почему его отправка формы не удалась.

Теперь вы сможете сохранить новые значения владельца и репозитория. Протестируйте его, указав владельца и репозиторий любого общедоступного проекта на GitHub. Последний шаг — вернуться к функции обратного вызова github_issues_func() и заменить жестко заданные значения владельца и репозитория.

 … $issues = $gh->api("issue")->all(get_option("gh_org"), get_option("gh_repo")); …

Как только это будет сделано, вернитесь на страницу, где вы добавили шорткод. Теперь вы должны увидеть задачи из любого проекта, который вы установили.

Бонусный раунд: аутентификация OAuth 2.0

Подход, который мы использовали выше, отлично работает для общедоступных репозиториев, но что, если мы хотим использовать этот плагин с частным репозиторием, требующим аутентификации? API-интерфейс GitHub выполняет аутентификацию с использованием протокола OAuth 2.0, с которым вы столкнетесь при работе с большинством популярных API-интерфейсов в наши дни. Основной рабочий процесс OAuth 2.0 выглядит следующим образом:

  1. Вы регистрируете приложение (иногда называемое «клиентом») у провайдера и получаете уникальный идентификатор и секретный ключ.
  2. Ваше приложение делает запрос к конечной точке проверки подлинности поставщика, передавая указанные выше учетные данные, а также URL-адрес перенаправления, который поставщик использует для перенаправления запроса обратно в конечную точку вашего приложения.
  3. Затем пользователю будет предложено принять ваш запрос на доступ. Если они это сделают, провайдер использует URL-адрес, который вы отправили с запросом, чтобы перенаправить пользователя обратно в ваше приложение вместе с временным кодом.
  4. Ваше приложение захватывает этот код, а затем делает второй запрос, передавая этот код поставщику. Провайдер отвечает токеном доступа, который ваше приложение затем использует для аутентификации у провайдера.

Мы начнем с регистрации приложения на GitHub. Как и в случае с большинством популярных API, этот раздел находится в разделе веб-сайта для разработчиков.

Страница регистрации нового приложения GitHub
Новая страница регистрации приложений GitHub. (Посмотреть большую версию)

Самое главное здесь — URL-адрес обратного вызова авторизации. URL-адрес перенаправления, переданный на третьем шаге, должен соответствовать введенному здесь или включать его. Итак, во время разработки я обычно захожу на домашнюю страницу сайта. Таким образом, мое приложение может перенаправлять на любой путь.

Затем мы добавим вторую форму на нашу страницу настроек в gh_plugin_options() для отправки идентификатора клиента и секрета приложения.

 <form method="post" action="<?php echo admin_url( 'admin-post.php'); ?>"> <input type="hidden" name="action" value="oauth_submit" /> <h3>Oauth 2.0</h3> <p> <label><?php _e("GitHub Application Client ID:", "github-api"); ?></label> <input class="" type="text" name="client_id" value="<?php echo get_option('client_id')?>" /> </p> <p> <label><?php _e("GitHub Application Client Secret:", "github-api"); ?></label> <input class="" type="password" name="client_secret" value="<?php echo get_option('client_secret')?>" /> </p> <input class="button button-primary" type="submit" value="<?php _e("Authorize", "github-api"); ?>" /> </form>

Чтобы упростить жизнь, я буду использовать GitHub Provider for OAuth 2.0 Client от The League of Extraordinary Packages. Итак, во-первых, давайте снова воспользуемся Composer, чтобы добавить зависимость:

 composer require league/oauth2-github

Обратите внимание, что библиотека GitHub от KNP Labs также имеет встроенную поддержку OAuth 2.0. Так что в моем конкретном примере это несколько избыточно. Но я хотел представить эту библиотеку, потому что она принадлежит к набору клиентских библиотек OAuth 2.0 для конкретных поставщиков, которые расширяют одну и ту же структуру, поддерживаемую могущественной Лигой экстраординарных пакетов. Вы можете просмотреть полный список поддерживаемых поставщиков или прочитать инструкции о том, как расширить платформу для поддержки нового поставщика.

Давайте создадим функцию для запроса и сохранения токена, когда пользователь отправляет форму. Поскольку GitHub является одним из уже поддерживаемых провайдеров, я могу скопировать пример в его документации с парой изменений.

 function handle_oauth() { // If the form was just submitted, save the values // (Step 1 above) if ( isset($_POST["client_id"]) && isset($_POST["client_secret"]) ) { update_option( "client_id", $_POST["client_id"], TRUE ); update_option("client_secret", $_POST["client_secret"], TRUE); } // Get the saved application info $client_id = get_option("client_id"); $client_secret = get_option("client_secret"); if ($client_id && $client_secret) { $provider = new League\OAuth2\Client\Provider\Github([ "clientId" => $client_id, "clientSecret" => $client_secret, "redirectUri" => admin_url("options-general.php?page=github"), ]); } // If this is a form submission, start the workflow // (Step 2) if (!isset($_GET["code"]) && $_SERVER["REQUEST_METHOD"] === "POST") { // If we don't have an authorization code, then get one $authUrl = $provider->getAuthorizationUrl(); $_SESSION["oauth2state"] = $provider->getState(); header("Location: ".$authUrl); exit; // Check given state against previously stored one to mitigate CSRF attack // (Step 3 just happened and the user was redirected back) } elseif (empty($_GET["state"]) || ($_GET["state"] !== $_SESSION["oauth2state"])) { unset($_SESSION["oauth2state"]); exit("Invalid state"); } else { // Try to get an access token (using the authorization code grant) // (Step 4) $token = $provider->getAccessToken("authorization_code", [ "code" => $_GET["code"] ]); // Save the token for future use update_option( "github_token", $token->getToken(), TRUE ); } }

И так же, как мы сделали с другой формой, нам нужно добавить хук действия, чтобы функция вызывалась при сохранении формы.

 add_action( "admin_post_oauth_submit", "handle_oauth" );

Сохранение формы теперь должно отправить вас на страницу авторизации поставщика API. После авторизации ваше приложение может использовать сохраненный токен для запроса данных из приватных репозиториев, к которым у вас есть доступ. В библиотеке, которую я использую в KNP Labs, есть для этого удобный метод.

 $gh = new MyGithub(); $gh->authenticate( get_option("github_token"), NULL, Github\Client::AUTH_HTTP_TOKEN);

Библиотеки будут различаться тем, как именно они обрабатывают аутентификацию, но так или иначе вы будете передавать токен, который затем будет выполнять аутентифицированные запросы от имени пользователя.

Заключение

Мы рассмотрели много вопросов, и я постарался, чтобы примеры были как можно меньше, чтобы общий рабочий процесс оставался ясным. Доступен полный исходный код из этого руководства. Я надеюсь, что теперь у вас есть четкое представление о движущихся элементах, связанных с созданием плагина WordPress, использующего API сторонних сервисов, и, надеюсь, вы вдохновились на написание собственного плагина WordPress API.

Заключительные примечания

  • Кодекс WordPress (документация)
  • GitHub API (документация)
  • OAuth 2.0 (документация)
  • Композитор (документация)
  • Лига необычных пакетов
  • Жрать документацию