Tworzenie wtyczki WordPress, która wykorzystuje interfejsy API usług, „od zupy do orzechów”
Opublikowany: 2022-03-10Coraz większa liczba publicznie dostępnych interfejsów API zapewnia potężne usługi rozszerzające funkcjonalność naszych aplikacji. WordPress to niezwykle dynamiczny i elastyczny CMS, który obsługuje wszystko, od małych osobistych blogów po główne witryny e-commerce i wszystko pomiędzy. Częścią tego, co sprawia, że WordPress jest tak wszechstronny, jest jego potężny system wtyczek , który sprawia, że niezwykle łatwo jest dodawać funkcje.
Omówimy, jak stworzyłem GitHub Pipeline, wtyczkę, która pozwala wyświetlać dane z GitHub API na stronach WordPress za pomocą skrótów. Podam konkretne przykłady i fragmenty kodu, ale rozważ opisaną tutaj technikę, jak wykorzystać dowolny interfejs API usługi z wtyczką.
Dalsze czytanie na SmashingMag:
- Podstawy WordPressa: jak stworzyć wtyczkę WordPress
- Jak wdrażać wtyczki WordPress za pomocą GitHub za pomocą transjentów
- Trzy podejścia do dodawania konfigurowalnych pól do wtyczki
Zaczniemy od początku, ale zakłada się pewien stopień zaznajomienia się z WordPressem i tworzeniem wtyczek i nie będziemy spędzać czasu na tematach dla początkujących, takich jak instalowanie WordPressa czy Composera.
Będziesz potrzebował:
- środowisko PHP ze świeżą instalacją WordPressa;
- konto GitHub (lub inny dostawca API, jeśli chcesz improwizować);
- Kompozytor (zalecane).
Wybór API
Pierwszym krokiem w pisaniu tego rodzaju wtyczki jest wybór API. W tym samouczku użyjemy interfejsu API GitHub. Jeśli myślisz o użyciu innego interfejsu API, rozważ kilka ważnych czynników, które mogą wpłynąć na to, jak satysfakcjonujący będzie Twój projekt.
Jedną z pierwszych rzeczy, na które patrzę, jest dokładność i jakość dokumentacji API. Jeśli jest rzadki lub przestarzały, przygotuj się na spędzenie czasu na przeglądaniu kodu źródłowego, aby odpowiedzieć na własne pytania. Zastanów się również, jak dojrzały jest interfejs API i jak odpowiedzialnie wersjonował go dostawca. Nie ma nic gorszego niż zainwestowanie czasu w stworzenie czegoś wspaniałego, tylko po to, by się zepsuło z powodu zmian w pierwotnym API. Poszukaj systemu wersjonowania w adresach URL punktów końcowych.
// good https://api.stable.com/v1/user/ // danger https://api.dodgy.com/user/
Ryzykując stwierdzenie oczywiste, programowanie przeciwko API innej firmy wiąże się z relacją zaufania, a nie wszystkie interfejsy API są sobie równe.
Zakupy do biblioteki
Znane marki często dystrybuują biblioteki lub pakiety SDK, aby ułatwić pracę z ich interfejsem API. Jeśli tak nie jest, pamiętaj, aby sprawdzić, czy ktoś inny nie napisał biblioteki już przed wyjazdem i wymyśleniem koła na nowo. Google i GitHub to dwa świetne miejsca do rozpoczęcia badań. Liczba gwiazdek lub widełek na GitHub jest dobrym wskaźnikiem efektywności biblioteki. Wiek ostatnich zatwierdzeń i/lub liczba otwartych problemów wskazuje, jak aktywnie jest utrzymywany. W moim przypadku udało mi się znaleźć przepiękne API PHP GitHub firmy KNP Labs.
Pisanie własnego z Guzzle
Jeśli nie ma zadowalającej biblioteki dla twojego dostawcy, nadal nie powinieneś zaczynać od zera, ponieważ narzędzia takie jak Guzzle znacznie ułatwiają pracę z żądaniami HTTP. Guzzle otacza bibliotekę cURL PHP i usuwa wiele problemów związanych z ręcznym konfigurowaniem i tworzeniem żądań. Nawet jeśli wysyłasz tylko jedno lub dwa żądania, nadal polecam go używać, ponieważ sprawi, że Twój kod będzie bardziej niezawodny, a zainstalowanie go za pomocą Composera to bułka z masłem.
Konfigurowanie wtyczki
Zaczniemy od podstawowego szkieletu minimalnej wtyczki WordPress, katalogu z dwoma plikami. Wybór opisowej i niepowtarzalnej nazwy folderu jest ważny, aby uniknąć konfliktów z innymi wtyczkami. Jeśli nazwa wtyczki jest dość ogólna, rozważ dodanie unikalnego przedrostka.
github-api/ readme.txt github-api.php
Plik readme.txt
zawiera metadane Twojej wtyczki, które pojawią się na wordpress.org
, jeśli zdecydujesz się je tam opublikować. Przeczytaj o publikowaniu wtyczek WordPress w dokumentacji lub sprawdź obszerny przykładowy plik readme.txt.
Plik PHP zawiera również pewne metadane w nagłówku, które będą używane do wyświetlania informacji o Twojej wtyczce na pulpicie nawigacyjnym. Zacznij od nagłówka, który wygląda tak:
<?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 */
Ze względów bezpieczeństwa dobrym pomysłem jest również odmowa bezpośredniego dostępu do pliku, na przykład:
defined( 'ABSPATH' ) or die( 'No script kiddies please!' );
W tym momencie wtyczka nic nie zrobi, ale kiedy skopiujesz pliki do wp-content/plugins
, powinna pojawić się na liście wtyczek i powinieneś być w stanie ją aktywować.

Następnie będziemy chcieli dołączyć bibliotekę, która będzie obsługiwać żądania API. W poniższym przykładzie kodu uwzględniamy API GitHub PHP firmy KNP Labs, ale możesz uwzględnić dowolną zależność, zastępując knplabs/github-api
pakietem, którego używasz.
$ cd wp-content/plugins/github-api $ composer require knplabs/github-api
Teraz twoja struktura plików powinna wyglądać następująco:
github-api/ composer.json composer.lock github-api.php readme.txt vendor/
Teraz, gdy pliki są na swoim miejscu, musimy wymagać vendor/autoload.php
, który został utworzony podczas jego instalacji.
require_once 'vendor/autoload.php';
Jeśli wszystko zostało poprawnie skonfigurowane, powinieneś być w stanie utworzyć instancję klasy z biblioteki bez wyrzucenia błędu krytycznego.
$testing = new \Github\Client();
Ale to tylko szybki sposób na sprawdzenie, czy Twoje zależności są dostępne. Polecam zdefiniowanie nowej klasy, która rozszerza bibliotekę.
class MyGithub extends \Github\Client {};
Nie jest to krytyczne, ale na kilka sposobów czyni Twój kod bardziej elastycznym. Najbardziej oczywistym sposobem jest to, że możesz zmienić lub dodać nową funkcjonalność, jeśli zajdzie taka potrzeba. Ale ułatwia to również życie, jeśli kiedykolwiek będziesz chciał zmienić biblioteki w przyszłości, ponieważ będziesz musiał wprowadzać zmiany tylko w jednym miejscu, a nie w całym kodzie.
Skróty WordPress
Teraz nadszedł czas na skonfigurowanie naszego pierwszego shortcode, czyli specjalnego fragmentu, który umożliwia generowanie treści poprzez umieszczenie jej w poście lub na stronie. Jeśli jesteś zupełnie nowy w shortcodes lub chcesz lepiej zrozumieć, jak one działają, sprawdź oficjalną dokumentację shortcode. W moim przykładzie zamierzam wyświetlić listę problemów z GitHub wszędzie tam, gdzie autor umieści ten krótki kod: [github_issues]
Tak jak poprzednio, zacznijmy od minimalnego przykładu, aby upewnić się, że krótki kod jest poprawnie zarejestrowany, zanim zaczniemy pracować nad wywołaniem API.
function github_issues_func( $atts ) { return "Hello world!"; } add_shortcode( "github_issues", "github_issues_func" );
Teraz, jeśli opublikujesz stronę zawierającą [github_issues]
, powinieneś zobaczyć „Hello world” podczas odwiedzania strony. Teraz, gdy to działa, skonfigurujmy wywołanie API.
W poprzedniej sekcji ustawiliśmy automatyczne ładowanie dla naszej biblioteki GitHub i zdefiniowaliśmy własną klasę, aby ją rozszerzyć. Teraz możemy zacząć używać go do wykonywania wywołań API. Tak więc dodamy go do funkcji zwrotnej shortcode. Jako dowód koncepcji pobierzmy wszystkie problemy z repozytorium GitHub Pipeline. Dostępna jest dokumentacja dla tej metody.
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' );
Teraz powinieneś być w stanie wyświetlić tę samą stronę, której użyliśmy powyżej do testowania shortcode, i tym razem powinieneś zobaczyć nieuporządkowaną listę problemów. Ale poczekaj! Zanim przejdziemy dalej, zróbmy jedną optymalizację, aby nasz kod był łatwiejszy do przetestowania. (Testujesz, prawda?!) Zamiast używać new
w naszej funkcji do tworzenia instancji klasy biblioteki GitHub, przekażmy ją jako parametr i stwórzmy instancję tylko wtedy, gdy musimy.
function github_issues_func( $atts, $gh=null ) { // Conditionally instantiate our class $gh = ( $gh ) ? $gh : new MyGithub(); …
Modyfikacja ta przypomina wzorzec wstrzykiwania zależności, co znacznie ułatwia testowanie naszej funkcji. Testowanie jednostkowe WordPressa wykracza poza zakres tego samouczka, ale łatwo jest zobaczyć, jak ta nowa wersja ułatwia nam przekazywanie „fałszywych” danych API do funkcji, dzięki czemu asercje naszego testu dokładnie wiedzą, czego się spodziewać.
To, co do tej pory zrobiliśmy, jest dobre dla wewnętrznego projektu, który będzie używany tylko w jednym repozytorium, ale byłoby o wiele bardziej przydatne, gdyby użytkownicy mogli skonfigurować, z którego repozytorium mają być pobierane problemy. Ustawmy to.
Najpierw użyjemy haka WordPress, aby zarejestrować naszą nową stronę ustawień, a funkcja wywołania zwrotnego zdefiniuje etykiety i tytuły menu. Użyjemy tego samego podejścia przyrostowego, aby to działało.
// 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!"; }
Teraz powinieneś być w stanie zalogować się do pulpitu nawigacyjnego i zobaczyć nowe menu GitHub w „Ustawieniach”, a następnie kliknąć je, aby wyświetlić pustą stronę ustawień z „Witaj świecie!”


Następnie Hello word
faktycznym znacznikiem formularza. Podam ci przykład szkieletu, na który możesz stylizować tak mało lub tak dużo, jak chcesz.
?> <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
Zawarte przeze mnie klasy CSS pasują do klas używanych przez rdzeń WordPressa, co jest dobrym sposobem na sprawienie, aby Twoja strona z opcjami niestandardowymi wyglądała przyzwoicie bez dodatkowego wysiłku.

W tym formularzu należy zrozumieć dwie ważne rzeczy. Po pierwsze, punkt końcowy, do którego się zgłasza, musi być /wp-admin/admin-post.php
. Możesz na stałe zakodować tę ścieżkę, ale jeśli witryna jest zainstalowana w podkatalogu, nie będzie działać. Dlatego używamy wbudowanej admin_url()
, aby tworzyć ją dynamicznie.
Po drugie, zwróć uwagę na ukryte dane wejściowe z nazwą action
. To pole pozwala WordPressowi wiedzieć, co zrobić z żądaniem posta przesłanym przez formularz. value
odpowiada nazwie haka akcji, którego użyjemy do ustawienia funkcji zwrotnej. Nazwą akcji, do której się podłączymy, będzie ta wartość, poprzedzona przedrostkiem admin post
. Tak więc w naszym przypadku musimy dodać to:
add_action( 'admin_post_update_github_settings', 'github_handle_save' );
Uważam, że jest to jeden z bardziej dziwacznych, mniej intuicyjnych aspektów tworzenia niestandardowych menu administracyjnych, ale kiedy już się do tego przyzwyczaisz, jest to całkiem bezbolesne. Jak można się domyślić, drugim parametrem add_action()
jest nazwa naszej funkcji zwrotnej, która faktycznie zapisze wartości w bazie danych.
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; }
Przykład jest również dość minimalny. W środowisku produkcyjnym prawdopodobnie chciałbyś dodać walidację. Ponadto w tym przykładzie automatycznie przekazujemy z powrotem status=success
do adresu URL. Znaczniki formularza jeszcze go nie używają. Aby warunkowo wyświetlić komunikat o powodzeniu, dodaj coś podobnego do poniższego powyżej formularza w 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 }
Jeśli dodasz walidację, użyj tego samego wzorca, aby przekazać różne stany i komunikaty, aby użytkownik wiedział, czy i dlaczego jego przesłanie formularza nie powiodło się.
Teraz powinieneś być w stanie zapisać nowe wartości właściciela i repozytorium. Przetestuj go, wprowadzając właściciela i repozytorium dowolnego projektu publicznego na GitHub. Ostatnim krokiem jest powrót do funkcji zwrotnej github_issues_func()
i zastąpienie zakodowanych na sztywno wartości właściciela i repozytorium.
… $issues = $gh->api("issue")->all(get_option("gh_org"), get_option("gh_repo")); …
Gdy to nastąpi, ponownie odwiedź stronę, na której dodałeś krótki kod. Powinieneś teraz zobaczyć problemy z dowolnego ustawionego projektu.
Runda bonusowa: Uwierzytelnianie OAuth 2.0
Podejście, którego użyliśmy powyżej, działa świetnie w przypadku repozytoriów publicznych, ale co, jeśli chcemy używać tej wtyczki z repozytorium prywatnym, które wymaga uwierzytelnienia? Interfejs API GitHub uwierzytelnia się przy użyciu protokołu OAuth 2.0, z którym obecnie spotkasz się podczas pracy z najpopularniejszymi interfejsami API. Podstawowy przepływ pracy OAuth 2.0 wygląda następująco:
- Rejestrujesz aplikację (czasami nazywaną „klientem”) u dostawcy i otrzymujesz unikalny identyfikator i tajny klucz.
- Twoja aplikacja wysyła żądanie do punktu końcowego uwierzytelniania dostawcy, przekazując powyższe poświadczenia oraz adres URL przekierowania, którego dostawca używa do przekierowywania żądania z powrotem do punktu końcowego aplikacji.
- Użytkownik jest następnie proszony o zaakceptowanie prośby o dostęp. Jeśli tak, dostawca używa adresu URL, który wysłałeś z żądaniem przekierowania użytkownika z powrotem do Twojej aplikacji, wraz z kodem tymczasowym.
- Twoja aplikacja przechwytuje ten kod, a następnie wykonuje drugie żądanie, przekazując ten kod z powrotem do dostawcy. Dostawca odpowiada tokenem dostępu, którego aplikacja następnie używa do uwierzytelniania u dostawcy.
Zaczniemy od zarejestrowania aplikacji w serwisie GitHub. Podobnie jak w przypadku większości popularnych interfejsów API, ta sekcja znajduje się w obszarze programisty witryny.

Najważniejszą rzeczą jest tutaj URL wywołania zwrotnego autoryzacji. Adres URL przekierowania przekazany w kroku trzecim musi być zgodny z wprowadzonym tutaj lub zawierać go. Dlatego podczas tworzenia zazwyczaj wchodzę na stronę główną serwisu. W ten sposób moja aplikacja może przekierować na dowolną ścieżkę.
Następnie dodamy drugi formularz do naszej strony ustawień, w gh_plugin_options()
, w celu podania identyfikatora klienta i sekretu aplikacji.
<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>
Aby ułatwić sobie życie, użyję dostawcy GitHub dla klienta OAuth 2.0 od The League of Extraordinary Packages. Więc najpierw użyjmy Composera ponownie, aby dodać zależność:
composer require league/oauth2-github
Zauważ, że biblioteka GitHub z KNP Labs ma również wbudowaną obsługę OAuth 2.0. Tak więc w moim konkretnym przykładzie jest to nieco zbędne. Chciałem jednak wprowadzić tę bibliotekę, ponieważ należy ona do zestawu bibliotek klienckich OAuth 2.0 specyficznych dla dostawcy, które rozszerzają tę samą strukturę utrzymywaną przez potężną Ligę Nadzwyczajnych Pakietów. Możesz wyświetlić pełną listę obsługiwanych dostawców lub przeczytać instrukcje dotyczące rozszerzania struktury w celu obsługi nowego dostawcy.
Stwórzmy funkcję do żądania i zapisywania tokena, gdy użytkownik przesyła formularz. Ponieważ GitHub jest jednym z obsługiwanych już dostawców, mogę skopiować przykład do jego dokumentacji z kilkoma modyfikacjami.
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 ); } }
I tak jak w przypadku innego formularza, musimy dodać hak akcji, aby funkcja była wywoływana podczas zapisywania formularza.
add_action( "admin_post_oauth_submit", "handle_oauth" );
Zapisanie formularza powinno teraz skierować Cię na stronę autoryzacyjną dostawcy API. Po autoryzacji Twoja aplikacja może wykorzystać zapisany token do żądania danych z prywatnych repozytoriów, do których masz dostęp. Biblioteka, której używam przez KNP Labs, ma do tego przydatną metodę.
$gh = new MyGithub(); $gh->authenticate( get_option("github_token"), NULL, Github\Client::AUTH_HTTP_TOKEN);
Biblioteki będą się różnić pod względem sposobu obsługi uwierzytelniania, ale w taki czy inny sposób przekażesz token, który następnie wyśle uwierzytelnione żądania w imieniu użytkownika.
Wniosek
Omówiliśmy wiele zagadnień i starałem się, aby przykłady były jak najmniejsze, aby ogólny przepływ pracy pozostał przejrzysty. Dostępny jest pełny kod źródłowy z tego samouczka. Mam nadzieję, że teraz dobrze rozumiesz poruszające się elementy związane z tworzeniem wtyczki WordPress, która wykorzystuje interfejsy API usług innych firm, i mam nadzieję, że zainspirujesz się do napisania własnej wtyczki API WordPress.
Uwagi końcowe
- Kodeks WordPressa (dokumentacja)
- GitHub API (dokumentacja)
- OAuth 2.0 (dokumentacja)
- Kompozytor (dokumentacja)
- Liga Nadzwyczajnych Pakietów
- Dokumentacja Guzzle