Cinci tehnici testate de luptă pe care dezvoltatorul dvs. API WordPress nu le folosește
Publicat: 2022-03-11Una dintre cele mai bune modalități de a-ți ridica statutul de dezvoltator WordPress, cel puțin în ochii clienților tăi, este să devii priceput să consumi API-uri. Iată un scenariu obișnuit pentru implementarea API-ului WordPress: clientul dvs. vă cere să adăugați un widget pe site-ul său - să zicem, de exemplu, un widget de abonament prin e-mail. Luați un cod de la serviciul lor de e-mail terță parte – poate este o etichetă de script sau un iframe
– îl lipiți în pagină și răspundeți clientului dvs. „Am înțeles!”
Din păcate, aveți de-a face cu un client ceva mai pretențios și acesta observă următoarele imperfecțiuni:
- Deși widget-ul, ca și restul site-ului, are un font sans-serif, nu este chiar cel potrivit. Widgetul folosește Helvetica în loc de fontul personalizat pe care l-ați instalat.
- Formularul de abonare al widget-ului declanșează o nouă încărcare a paginii, care poate fi perturbatoare dacă este plasată la jumătatea unui articol.
- Widgetul pare să ia un moment în plus pentru a se încărca după restul paginii, ceea ce pare șocant și ieftin.
- Clientul ar dori ca abonații să fie etichetați cu metadate în funcție de postarea de la care s-au abonat, iar widgetul nu oferă nimic care să se aseamănă la distanță cu această funcționalitate.
- Clientului i se pare enervant faptul că va trebui acum să gestioneze două tablouri de bord (wp-admin și zona de administrare pentru serviciul de e-mail).
În acest moment, unul dintre cele două lucruri s-ar putea întâmpla în mod rezonabil. Ați putea declara aceste articole „drăguțe de a avea” și să vă asigurați clientul cu privire la meritele unei soluții 80/20 sau ați putea îndeplini acele solicitări. Din experiența mea personală, am descoperit că îndeplinirea unor astfel de solicitări – adică, demonstrarea stăpânirii serviciilor terță parte – este o modalitate fiabilă de a convinge clientul că ești un fel de vrăjitor WordPress. În plus, este adesea distractiv de făcut.
În ultimul deceniu, am folosit WordPress ca platformă pentru consumul de API-uri împotriva a probabil 50 de API-uri diferite. Unele dintre cele mai comune API-uri au fost MailChimp, Google Analytics, Google Maps, CloudFlare și Bitbucket. Dar dacă trebuie să faceți mai mult, ce se întâmplă dacă aveți nevoie de o soluție personalizată?
Cum să dezvoltați un client API WordPress
În acest articol, voi dezvolta un API generic „serviciu de e-mail”, încercând tot posibilul să păstrez lucrurile cât mai agnostice posibil. Cu toate acestea, consider că este rezonabil să presupunem că avem de-a face cu un API REST JSON. Iată câteva subiecte de fundal care vă pot ajuta să vă bucurați de punctele tehnice din acest articol:
- Familia de funcții WordPress HTTP
- JSON
- ODIHNĂ
Dacă sunteți puțin familiarizat cu aceste subiecte și sunteți interesat să cercetați mai profund, faceți o pauză chiar acum și descărcați excelenta aplicație Postman. Vă permite să comunicați cu API-uri fără a scrie cod.
Cu toate acestea, dacă nu ești deloc familiarizat cu acestea, continuă să citești oricum. O audiență tehnică cu un anumit grad de experiență în WordPress va profita la maximum de acest articol, dar voi avea grijă să explic valoarea fiecărei tehnici într-un mod mai puțin tehnic. Un cititor non-tehnic va lăsa acest articol capabil să evalueze rentabilitatea investiției pentru fiecare punct înainte de a-l sponsoriza și să judece calitatea implementării odată livrat.
Notă: În cazul în care aveți nevoie de un curs rapid de reîmprospătare, puteți consulta ghidul nostru WordPress REST API.
Fără alt preambul, permiteți-mi să vă împărtășesc o mână de tehnici diferite pe care le apreciez în majoritatea API-urilor, proiectelor și echipelor cu care lucrez.
Tranzitorii: când să le țineți, când să le pliați
În paragraful meu de deschidere, am observat că clientului i-a fost deranjant să se încadreze pe două zone de administrare: wp-admin și tabloul de bord pentru serviciul lor de e-mail. O modalitate bună de a rezolva acest lucru ar fi să le oferiți un widget de tablou de bord în wp-admin, pentru a afișa un rezumat al activității recente de abonat.
Dar, din nou, acest lucru ar putea necesita solicitări HTTP multiple către API-ul de la distanță (API-ul furnizat de serviciul de e-mail), ceea ce duce la încărcări lungi de pagină. Soluția la această problemă de performanță este stocarea apelurilor API ca temporare. Acest articol Codex oferă o explicație excelentă pe care ar trebui să o citiți cu siguranță, dar o voi rezuma astfel:
- Obțineți datele de la API-ul de la distanță.
- Stocați-l utilizând
set_transient()
cu un timp de expirare la alegerea dvs. bazat pe propria judecată despre performanță, limitele ratei și marja de eroare în afișarea datelor învechite în această aplicație specială. - Continuați în logica dvs. de afaceri - procesarea datelor, returnarea unei valori, indiferent de caz.
- Când aveți nevoie din nou de date, cum ar fi la următoarea încărcare a paginii, verificați-le în memoria cache tranzitorie folosind
get_transient()
înainte de a concluziona că trebuie să le obțineți din API.
Consider că aceasta este o bază utilă și viabilă, dar puteți face un pas mai departe dacă vă gândiți o clipă la verbele REST. Dintre cele mai comune cinci metode (GET, POST, PATCH, PUT, DELETE), doar una dintre acestea aparține cache-ului tău tranzitoriu. Poți ghici care? Este GET. În plugin-urile mele, am aproape întotdeauna o clasă PHP dedicată abstractizării apelurilor către API-ul de la distanță în cauză, iar un argument la instanțierea acelei clase este metoda HTTP. Dacă nu este un apel GET, atunci nu voi invoca deloc niciun strat de cache.
În plus, dacă nu este un apel GET, atunci este înțeles că iau unele măsuri pentru a modifica datele de la distanță într-un fel, poate prin adăugarea, editarea sau eliminarea unui abonat de e-mail. Acesta ar putea fi un moment bun pentru a invalida memoria cache existentă pentru resursa respectivă, prin delete_transient()
.
Pentru a reveni la exemplul nostru de API de abonament pentru e-mail WordPress, iată cum ar funcționa acest lucru în practică:
- Un widget de tablou de bord pentru afișarea abonaților recent va apela punctul final API pentru
/subscribers
printr-o solicitare GET. Deoarece este o solicitare GET, este stocată în memoria cache-ului meu tranzitoriu. - Un widget din bara laterală pentru abonarea la lista de e-mail va apela punctul final API pentru
/subscribers
printr-o solicitare POST. Deoarece este o solicitare POST, nu numai că va evita cache-ul meu tranzitoriu, dar mă va provoca să șterg partea relevantă din memoria cache-ului meu tranzitoriu, astfel încât widgetul tabloului de bord să reflecte acest nou abonat. - Când numesc elementele tranzitorii, adesea le organizez denumindu-le literalmente după adresa URL API de la distanță pe care o apelez. Acesta este un mod util de a identifica tranzitorii corecti de șters. Dacă este un punct final care preia argumente, le voi concatena într-un șir și le voi adăuga și la numele tranzitoriu.
În calitate de client sau altă parte interesată mai puțin tehnic, ar trebui să solicitați în mod special memorarea în cache tranzitorie – sau cel puțin o discuție despre aceasta – oricând aplicația extrage date dintr-un serviciu de la distanță. Ar trebui să vă familiarizați cu excelentul plugin Query Monitor pentru a obține o vedere asupra modului în care funcționează tranzitorii. Vă va oferi o interfață pentru a naviga ce date sunt ascunse ca un tranzitoriu, cât de frecvent și pentru cât timp.
Uneori, tranzitorii nu sunt suficient de buni
Unele servicii de găzduire WordPress premium de fapt nu vă permit să utilizați tranzitorii în producție. Au cod care rulează, poate sub forma unui plugin MU sau a unui alt script, care vă va intercepta încercarea de a utiliza API-ul tranzitori și va stoca acele informații prin cache-ul obiectelor. WP-Engine, în configurația sa cea mai comună, este un prim exemplu în acest sens.
Dacă pur și simplu stocați și preluați date, de fapt nu trebuie să vă pese de acest lucru și este posibil să nu observați niciodată că se întâmplă. Întreaga familie de *_transient()
vă va oferi același rezultat final, doar filtrat pentru a utiliza cache-ul obiectului în loc de cache-ul tranzitoriu. Unde s-ar putea să întâmpinați probleme, totuși, este atunci când încercați să ștergeți tranzitorii. Iata de ce.
Dacă integrarea API-ului dvs. este suficient de complexă pentru a merita propria pagină de setări, vă recomandăm să includeți o interfață de utilizare pentru a permite utilizatorului administrator să golească întregul cache tranzitoriu pentru pluginul dvs. . Cea mai obișnuită utilizare a acestui buton ar fi atunci când clientul modifică unele date direct pe serviciul de la distanță și dorește să invalideze memoria cache pe care o stocăm în WordPress. Acest buton poate fi, de asemenea, util dacă clientul modifică acreditările contului, cheile API sau, în general, ca buton de „resetare din fabrică” pentru depanare.
Chiar dacă ați fost suficient de deștept pentru a vă spați de nume toate cheile tranzitorii, astfel încât să aveți o speranță de a identifica fiecare dintre ele pentru delete_transient()
, cel mai bun scenariu probabil implică în continuare SQL brut, pe care încerc întotdeauna să-l evit în 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 ); } } ?>
Nu convenabil, nu eficient. În schimb, această situație necesită stocarea în cache a obiectelor, deoarece stocarea în cache a obiectelor ne oferă o modalitate convenabilă de a grupa valorile stocate în cache . În acest fel, atunci când trebuie să goliți toate valorile din cache legate de pluginul dvs., este un simplu apel la wp_cache_delete( $key, $group )
.
Aș rezuma toate acestea spunând: Nu puteți fi un expert în consumarea API-urilor dacă nu sunteți încă expert în gestionarea memoriei cache pentru acele date.

În calitate de client, lucrul cheie la care trebuie să aveți grijă este comportamentul aberant al memoriei cache între mediile de realizare și de producție. Cu alte cuvinte, deși testarea unui nou lot de lucru în staging este întotdeauna o practică bună, stocarea în cache este ceva care trebuie testat și în producție cu aceeași grijă.
API-ul de la distanță vă poate ajuta să vă informați ierarhia claselor PHP
Când stabilesc diferitele clase PHP pentru pluginul meu, adesea mi se pare util să imit modul în care sunt definite punctele finale API-de exemplu, ce par să aibă în comun următoarele puncte finale?
- 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
Toate returnează colecții , prin care mă refer la rezultatul unei solicitări GET, returnând rezultate de la zero la mai multe, unde fiecare rezultat este membru al unui tablou. Ar putea suna destul de evident, dar consider că este un prompt util pentru următoarea structură de clasă din codul meu PHP:
-
class.collection.php
, o clasă abstractă -
class.subscribers.php
extinde clasa abstractă,Collection
. -
class.lists.php
extinde clasa abstractă,Collection
. -
class.campaigns.php
extinde clasa abstractă,Collection
.
Clasa abstractă ar lua ca singur argument o serie de parametri de interogare: lucruri precum paginarea, coloana de sortare, ordinea de sortare și filtrele de căutare. Ar avea metode pentru sarcini obișnuite, cum ar fi apelarea API-ului la distanță, gestionarea erorilor și, probabil, transformarea rezultatelor într-un meniu HTML <select>
sau un jQueryUI AutoSuggest. Clasele care instanțează clasa abstractă ar putea fi destul de scurte, poate făcând puțin mai mult decât specificarea șirului de utilizat în URL-ul punctului final al API-ului *.json
.
În mod similar, ce au în comun următoarele puncte finale?
- 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
Toate returnează un articol , prin care mă refer la un singur membru specific, unic al unei colecții: lucruri precum un anumit abonat de e-mail, o listă de e-mail sau o campanie de e-mail. Prin urmare, îmi place să folosesc următoarea structură în codul meu PHP:
-
class.item.php
, o clasă abstractă -
class.subscriber.php
extinde clasa abstractă,Item
. -
class.list.php
extinde clasa abstractă,Item
. -
class.campaign.php
extinde clasa abstractă,Item
.
Clasa abstractă ar lua ca singur argument un șir pentru a identifica elementul specific solicitat. Încă o dată, clasele instanțiate ar putea fi destul de scurte, poate făcând puțin mai mult decât specificarea șirului de utilizat în */duy736td.json
.
Există multe abordări pentru structurarea moștenirii clasei, dar chiar dacă adoptați o abordare diferită față de ceea ce am subliniat mai sus, pun pariu că există șanse mari ca structura API-ului de la distanță să vă ajute la informarea structurii aplicației dvs.
Ca client, un simptom obișnuit al arhitecturii slabe este atunci când trebuie să solicitați aceeași schimbare în mod repetat într-o aplicație. De exemplu, dacă solicitați ca rapoartele să returneze 100 de rezultate pe pagină în loc de 10 și trebuie să continuați să repetați acea solicitare pentru rapoarte de abonați, rapoarte de campanie, rapoarte de dezabonare etc., este posibil să detectați o arhitectură de clasă slabă. În această situație, merită să întrebați echipa dvs. dacă ar beneficia de un ciclu de refactorizare: un corp de lucru în care scopul nu este de a schimba comportamentul produsului, ci mai degrabă de a îmbunătăți codul de bază, astfel încât să devină mai ușor să schimbați comportamentul. a produsului în viitor.
Cazul de utilizare perfect pentru WP_Error
Mi-e rușine să recunosc că mi-a luat ani mai mult decât ar trebui să încep să folosesc familia de funcții WP_Error
în codul meu. Am avut tendința să-mi codific drumul, fie presupunând că nu vor fi niciodată erori de care să ne preocupe programatic, fie gestionându-le de la caz la caz. Lucrul cu API-uri de la distanță reduce această mentalitate ca un fascicul laser, deoarece prezintă un caz de utilizare extrem de convenabil și puternic pentru utilizarea WP_Error
.
Amintiți-vă mai devreme că am menționat că am adesea o clasă PHP al cărei scop este de a face solicitări HTTP către API-ul de la distanță. Când dezlipiți toată placa, toată manipularea datelor, toate preocupările secundare, acea clasă se reduce într-adevăr la apelarea wp_remote_request()
pentru a obține un obiect de răspuns HTTP din API. În mod convenabil, wp_remote_request()
va returna în schimb un WP_Error
dacă apelul nu se execută dintr-un motiv oarecare, dar ce se întâmplă dacă apelul reușește să returneze un răspuns HTTP de tip nefavorabil?
De exemplu, poate că am făcut un apel către punctul final /lists.json
, dar acest cont nu are încă liste configurate. Acest lucru ar returna un răspuns HTTP valid, dar cu un cod de stare de 400. Deși aceasta nu este chiar o eroare fatală în sine, din perspectiva unui cod front-end care vrea să transforme acest apel API într-un meniu drop-down, un 400 ar putea la fel de bine să fie un WSOD! Prin urmare, mi se pare util să fac o analiză suplimentară a rezultatului wp_remote_request()
, eventual returnând o WP_Error
la urmă:
<?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; } ?>
Acest model poate ajuta la simplificarea codului care invocă clasa noastră de apelant, deoarece știm că ne putem baza în siguranță pe is_wp_error()
înainte de a continua cu ieșirea noastră.
Ca client, ar trebui să jucați ocazional rolul unui utilizator rău intenționat, al unui utilizator confuz și al unui utilizator nerăbdător. Utilizați aplicația în moduri în care nu a fost menită să fie utilizată. Fă lucrurile pe care dezvoltatorii tăi par să nu vrea să le faci. Ia notă de ceea ce se întâmplă. Primești mesaje de eroare utile? Primiți vreun mesaj de eroare? Dacă nu, ar putea merita să sponsorizați un corp de lucru în jurul unei mai bune gestionări a erorilor.
Puterea frumoasă de depanare a ob_get_clean()
Web-ul modern programabil, unde aproape fiecare site consumă API-urile altor site-uri și este el însuși consumat prin intermediul propriului API, a devenit o arenă incredibil de puternică pentru cod. Dar tocmai această calitate îl poate face destul de lent.
Este obișnuit ca solicitările HTTP de la distanță să fie părțile cele mai consumatoare de timp ale încărcării unei anumite pagini. Din acest motiv, multe componente bazate pe API se execută fie prin Ajax, fie prin cron. De exemplu, o sugestie automată pentru căutarea într-o listă de abonați de e-mail ar trebui probabil să pună ping la cerere la sursa de date de la distanță, la fiecare apăsare a tastei, în loc să încarce toți cei 100.000 de abonați din DOM pe măsură ce pagina se încarcă. Dacă aceasta nu este o opțiune, poate că o interogare mare s-ar putea sincroniza pe o sarcină cron nocturnă, astfel încât rezultatele să poată fi extrase dintr-o oglindă locală, mai degrabă decât din API-ul de la distanță.
Problema cu această abordare este că poate fi dificil de depanat. În loc să porniți pur și simplu WP_DEBUG
și să lăsați mesajele de eroare să treacă în fereastra browserului dvs., sunteți blocat să căutați în consola de rețea a browserului sau să urmăriți un fișier jurnal pe măsură ce se execută o sarcină cron (sperăm că?). Mi se pare incomod.
O modalitate de a îmbunătăți această situație este să efectuați apeluri atente și strategice către error_log()
. Dar, din nou, o problemă comună cu înregistrarea în jurnal este aceea că, în cazul aplicațiilor mari sau ocupate, jurnalele de erori pot crește prea mari sau pot crește prea repede pentru a fi utile pentru monitorizare sau analiză. Prin urmare, trebuie să fim selectivi cu ceea ce înregistrăm, gândindu-ne la asta la fel de mult ca și cu logica aplicației noastre actuale . Este păcat că ți-ai făcut timp să înregistrezi o eroare exotică de tip margine, care pare să apară doar intermitent la o sarcină cron nefrecventă, doar pentru a realiza că adevărata natură a erorii te-a ocolit încă o dată, deoarece nu ai reușit să înregistrezi un anumit membru al matricei. , să zicem, a valorii ofensive.
Prin urmare, filosofia mea a devenit, nu înregistrez întotdeauna, dar când o fac, înregistrez totul . Cu alte cuvinte, după identificarea unei funcții deosebit de îngrijorătoare, o voi înregistra cu o plasă cât mai largă posibil:
<?php function debug( $bug ) { ob_start(); var_dump( $bug ); $out = ob_get_clean(); error_log( $out ); } ?>
Aceasta înseamnă var_dump()
să găsească întreaga valoare buggy într-o singură intrare în fișierul jurnal de erori.
În calitate de client, merită să verificați periodic utilizarea totală a memoriei fișierelor pentru aplicația dvs. Dacă observați că vă confruntați brusc cu limitele de stocare din contul dvs. de găzduire, există șanse mari ca un jurnal de erori sălbatic să fie de vină. Dezvoltatorii tăi vor beneficia de un ciclu de lucru axat pe o logare mai bună – și clienții tăi vor beneficia, de asemenea!
Nu tocmai Clickbait, dar va merge
Vă rugăm să iertați structura de listă a acestui articol. Nu am putut forța aceste puncte într-o temă de articol mai unificatoare, deoarece aceste modele sunt foarte generice: se aplică oricărui punct final JSON REST și oricărei ieșiri WordPress .
Acestea sunt tiparele pe care le văd apar din nou și din nou, indiferent de ce este API-ul la distanță sau pentru ce îl folosim în WordPress. Am mers atât de departe încât să adun toate aceste tipuri de principii într-un plugin care îmi accelerează foarte mult munca. Aveți puncte similare pe care le păstrați pentru fiecare proiect? Vă rog să le împărtășesc, ca să le pot fura și să le adaug în placa mea!