Cum să abordați dezvoltarea modernă WordPress (Partea 2)
Publicat: 2022-03-11WordPress este cea mai folosită tehnologie de site de pe planetă și cu un motiv întemeiat. Cu toate acestea, codul moștenit în nucleul său este o mizerie, iar această problemă ajunge în cascadă dezvoltatorilor terți. Unii dezvoltatori iau asta ca pe o scuză pentru a tăia colțuri în propriul cod WordPress PHP, dar această abordare este mai costisitoare pe termen lung pentru toate modificările, cu excepția celor mai banale.
În partea 1 a seriei noastre din două părți, ne-am concentrat pe proiecte generale și instrumente de flux de lucru, urmate de dezvoltarea front-end. Acum este timpul să luăm taurul de coarne și să lupți cu PHP: mai exact, cum să urmezi cele mai bune practici atunci când lucrezi cu codul WordPress back-end. S-ar putea să vă gândiți la asta ca la un tutorial PHP/WordPress, dar puțin mai avansat, presupunând că ați făcut deja unele personalizări back-end WordPress.
Deci, care principii moderne de design software și caracteristici PHP vă vor oferi cea mai bună valoare pentru timpul dvs.? Următoarele sunt 10 practici de dezvoltare WordPress și PHP pe care le recomand cu căldură.
Cea mai bună practică pentru dezvoltare WordPress modernă #1: Urmați „Separarea preocupărilor”
Separarea preocupărilor înseamnă că părți ale codului PHP WordPress care au funcționalități sau scopuri diferite nu ar trebui amestecate. În schimb, ar trebui să fie organizate în secțiuni sau module distincte, trecându-se datele între ele printr-o interfață definită. (O interfață este un set de parametri definiți pe care un modul îi ia ca intrare și ceea ce iese înapoi.) Un termen strâns legat este principiul responsabilității unice : Fiecare modul de cod (sau funcție) ar trebui să fie responsabil doar pentru un singur lucru.
Scopul final al respectării acestor principii este producerea unui cod modular și, prin urmare, care poate fi întreținut, extensibil și reutilizabil.
A fost o gură, așa că să ne uităm la un exemplu de WordPress PHP (din WordPress core) care încurcă totul. Acest stil de codare este adesea numit „cod spaghete”, deoarece înțelegerea funcționării sale interioare este aproape imposibilă. Fragmentul de mai jos a fost redactat pentru concizie; cu toate acestea, stilul și formatarea originale au fost păstrate.
$id = isset( $_REQUEST['id'] ) ? intval( $_REQUEST['id'] ) : 0; <table class="form-table"> <?php $blog_prefix = $wpdb->get_blog_prefix( $id ); $sql = "SELECT * FROM {$blog_prefix}options WHERE option_name NOT LIKE %s AND option_name NOT LIKE %s"; $query = $wpdb->prepare( $sql, $wpdb->esc_like( '_' ) . '%', '%' . $wpdb->esc_like( 'user_roles' ) ); $options = $wpdb->get_results( $query ); foreach ( $options as $option ) { if ( strpos( $option->option_value, "\n" ) === false ) { ?> <tr class="form-field"> <th scope="row"><label for="<?php echo esc_attr( $option->option_name ); ?>"><?php echo esc_html( ucwords( str_replace( '_', ' ', $option->option_name ) ) ); ?></label></th> <?php if ( $is_main_site && in_array( $option->option_name, array( 'siteurl', 'home' ) ) ) { ?> <td><code><?php echo esc_html( $option->option_value ); ?></code></td> <?php } else { ?> <td><input class="<?php echo $class; ?>" name="option[<?php echo esc_attr( $option->option_name ); ?>]" type="text" value="<?php echo esc_attr( $option->option_value ); ?>" size="40" <?php disabled( $disabled ); ?> /></td> <?php } ?> </tr> <?php } } // End foreach </table>
În primul rând, este total de neînțeles. Și îmi place că singurul comentariu este End foreach
, care este complet redundant. Avem interogarea bazei de date, procesarea rezultatelor interogării, procesare suplimentară încorporată în HTML (există un if
/ else
imbricat acolo dacă nu ați observat), evadarea ieșirii și șablonul HTML, toate combinate. O altă problemă este parametrul $id
care vine direct din $_REQUEST
global, spre deosebire de transmiterea unui parametru real unei funcții.
Privind acest lucru, este perfect de înțeles de ce nucleul WordPress a rămas în mare parte același timp de ani de zile. Refactorizarea acestui tip de cod – mai ales menținând comportamentul existent – este o sarcină cu adevărat epică pe care nimeni nu și-ar dori să o facă.
Deci, cum o facem corect? Ei bine, un lucru de reținut este că nu există o cale adevărată . Am menționat mai sus calități pentru care ar trebui să ne străduim: Avem nevoie de codul PHP personalizat WordPress pentru a putea fi întreținut și modular. Să vedem cum putem împărți codul de mai sus în module.
- Interogările SQL ar trebui să fie într-un modul separat, evident. WordPress are deja o clasă
WP_Query
bine abstractizată care ar trebui folosită ca exemplu. - Tot HTML intră într-un șablon. Vom acoperi mai jos șabloanele PHP.
- Codul PHP rămas ar trebui să fie împachetat într-o funcție - mai multe funcții dacă codul este prea lung sau complex pentru o funcție. Parametri precum
$id
sunt transmisi prin argumentele funcției.
Aceasta este o rescrie foarte simplificată a exemplului de mai sus:
function betterSiteSettings($args) { $data = WP_Settings_Query($args); // process $data here $context = array_merge([], $data_processed, $other_data); return Template::render('template.name', $context); }
Cea mai bună practică pentru dezvoltarea WordPress modernă #2: Evitați variabilele globale
WordPress are mult prea multe variabile globale. De ce variabilele globale sunt rele? Acestea fac codul dvs. PHP WordPress greu de urmărit și fac ca starea aplicației să fie nesigură. Orice bucată de cod PHP – și asta înseamnă orice plugin care este instalat în WordPress – poate citi și scrie variabile globale, deci nu există nicio garanție că acestea conțin date valide. Încercarea de a înțelege ce variabile globale sunt folosite în ceva de genul Loop nu este nici o sarcină trivială.
Să privim asta dintr-un unghi practic. Acest exemplu vine de la WooCommerce. Probabil că fiecare dezvoltator WordPress știe ce este – bucla:
<?php while ( have_posts() ) : the_post(); ?> <?php wc_get_template_part( 'content', 'single-product' ); ?> <?php endwhile; // end of the loop. ?>
Fragmentul de mai sus redă un șablon de produs. De unde știe ce produs să arate, având în vedere că nu există parametri trecuți către wc_get_template_part
? Privind șablonul, vedem că începe cu global $product;
, deci acolo este stocat obiectul produs curent.
Acum imaginați-vă că avem o pagină de catalog care caută și filtrează produse și dorim să afișăm o fereastră pop-up „detalii despre produs” în timp ce rămânem pe aceeași pagină. În culise, scriptul front-end face o solicitare AJAX pentru a obține acel șablon de produs. Nu putem apela pur și simplu wc_get_template_part('content', 'single-product')
deoarece nu folosește parametri, așa că trebuie să setăm câteva variabile globale pentru ca acest lucru să funcționeze.
Cazuri de utilizare mai complexe ar implica mai mult de un șablon, hook-uri declanșate în acele șabloane și pluginuri terță parte care își adaugă apelurile înapoi la acele hook-uri. Poate escalada rapid. Nu avem de unde să știm pe ce stare globală se bazează aceste apeluri. Pluginurile terță parte sunt libere să modifice orice variabilă globală în apelurile lor inverse. În loc să folosim sistemul, începem să luptăm cu sistemul, întâmpinând bug-uri ciudate care provin dintr-o stare globală nesigură.
Nu ar fi mai sensibil să trecem acel ID de produs ca parametru? Apoi am putea reutiliza acel șablon fără să ne facem griji cu privire la dezordinea variabilelor globale utilizate de WordPress.
Cea mai bună practică pentru dezvoltare WordPress modernă #3: Utilizați programarea orientată pe obiecte (OOP)
Modularitatea duce la conceptul de obiecte și programare orientată pe obiecte. La nivelul de bază, OOP este o modalitate de organizare a codului. Funcțiile și variabilele sunt grupate în clase și sunt numite metode de clasă și, respectiv, proprietăți . Manualul pluginului WordPress recomandă utilizarea OOP pentru a vă organiza codul PHP personalizat WordPress.
Un principiu important în OOP este limitarea accesului la metode și proprietăți – sau în termeni PHP, denotându-le ca private
sau protected
– astfel încât numai alte metode de clasă le pot accesa și modifica. Un termen OOP pentru aceasta este încapsulare : datele sunt încapsulate în interiorul clasei și singura modalitate de a modifica acele date este prin utilizarea metodelor de clasă furnizate.
Acest lucru face depanarea și menținerea codului mult mai ușoară decât este atunci când utilizați variabile globale care pot fi modificate oriunde în întreaga bază de cod. Luați în considerare variabila globală de post
WordPress. Îl puteți accesa oriunde în codul dvs. și multe funcționalități depind de utilizarea acestuia. Ce se întâmplă dacă ai putea restricționa modificările doar la funcțiile de bază ale WordPress, dar citirea ar fi permisă oricui? Ascunderea sau încapsularea variabilei globale post
într-o clasă și construirea unei interfețe în jurul acesteia ar face acest lucru posibil.
Aceasta este doar o descriere de bază a OOP și a modului în care poate fi utilizată în dezvoltarea WordPress modernă. Pentru studii suplimentare, recomand cu tărie cartea electronică a lui Carl Alexander, Descoperiți programarea orientată pe obiecte folosind WordPress, care are cel mai cuprinzător și util conținut disponibil pe tema OOP în WordPress.
Este important să ne amintim că OOP nu este un glonț de argint: codul rău poate fi scris cu OOP la fel de ușor ca cu orice altă paradigmă de programare.
Să ne aprofundăm în câteva sfaturi specifice despre utilizarea PHP pentru dezvoltarea WordPress.
Cea mai bună practică PHP modernă #1: vizați PHP 7.0+
Utilizarea caracteristicilor PHP moderne necesită, ei bine, o versiune modernă a PHP. Pur și simplu nu există niciun motiv pentru a accepta versiuni PHP mai mici de 7.0. Chiar și nucleul WordPress va necesita PHP 7.0 încă de la sfârșitul anului 2019.
Cu toate acestea, este o bună practică să verificați versiunea minimă pentru a evita „ecranul alb al morții” în medii incompatibile. Fragmentul de mai jos arată utilizarea unui antet de plugin pentru a declara o versiune PHP minimă cu o condiție de gardă în cod.
<?php /** * Plugin Name: My Awesome Plugin * Requires PHP: 7.0 */ // bails if PHP version is lower than required if (version_compare(PHP_VERSION, '7.0.0', '<')) { // add admin notice here return; } // the rest of the actual plugin here
Cea mai bună practică PHP modernă nr. 2: adoptați standardele industriale PHP (Ghid de stil de codare PSR-2)
PSR-urile sunt recomandări publicate de PHP Framework Interop Group. Ele sunt standardele industriale de facto în orice flux de lucru PHP modern și se poate spune cu siguranță că comunitatea PHP în ansamblu urmează aceste standarde. PSR-2 este o recomandare care descrie stilul de codare. Cadrele PHP populare, cum ar fi Symfony și Laravel, urmează PSR-2.
De ce ați folosi PSR-2 în loc de standardul de codare WordPress? În principal pentru că standardele WordPress sunt învechite și nu folosesc nicio caracteristică a limbii mai noi. Acest lucru este de înțeles deoarece nucleul WordPress trebuie să-și urmeze propriile standarde. A trebuit să suporte PHP 5.2 până de curând, iar PSR-2 nu este compatibil cu PHP 5.2.
S-ar putea să nu fie evident, dar nu există nicio cerință de a utiliza standardele de codare WordPress decât dacă vă angajați la bază. Nu ar fi nicio problemă cu trimiterea unui plugin care urmează standardul PSR-2 în directorul de pluginuri WordPress. De fapt, există câteva argumente foarte bune pentru a face acest lucru.

Cea mai bună practică PHP modernă #3: Utilizați un motor de șabloane PHP
PHP nu este un motor de șablon. A început ca unul, dar apoi a evoluat într-un limbaj de programare complet și nu există niciun motiv să îl folosiți în continuare pentru șabloane. Cele mai populare două motoare de șablon pentru PHP sunt Twig și Blade, care sunt utilizate de Symfony și, respectiv, Laravel. Acest articol va folosi Twig ca exemplu de motor de șabloane; cu toate acestea, Blade are caracteristici și funcționalități comparabile. Vă implor să vă uitați la ambele și să decideți singur care vi se potrivește cel mai bine.
Exemplul de mai jos compară un șablon PHP și șablonul Twig corespunzător. Afișarea și evadarea ieșirii sunt deosebit de detaliate în exemplul PHP:
foreach ( $options as $option ) { ?> <tr class="form-field"> <th scope="row"> <label for="<?php echo esc_attr( $option->option_name ); ?>"> <?php echo esc_html( strtolower( $option->option_name ) ); ?> </label> </th> </tr> <?php } // End foreach
În Twig, acest lucru este mai concis și mai ușor de citit:
{% for option in options %} <tr class="form-field"> <th scope="row"> <label for="{{ option.option_name }}"> {{ option.option_name }} </label> </th> </tr> {% endfor %}
Principalele avantaje ale Twig sunt:
- Sintaxă lizibilă și concisă
- Ieșire automată
- Extensie de șablon prin moștenire și blocuri
Din punct de vedere al performanței, Twig se compilează în șabloane PHP și nu are practic nicio suprasarcină. Twig are doar un subset de constructe de limbaj PHP limitate doar la șabloane. Acest lucru îi obligă pe dezvoltatori să elimine logica de afaceri din șabloane, impunând astfel separarea preocupărilor.
Există chiar și Twig pentru WordPress. Se numește Timber și este o modalitate excelentă de a începe să creați șabloane mai bune. Tema de pornire Timber este un exemplu perfect de organizare a temelor în modul OOP.
Cea mai bună practică PHP modernă #4: Folosiți Composer
Composer este un manager de dependențe pentru PHP. Este un instrument care permite declararea bibliotecilor pe care le folosește un proiect și apoi automatizarea descărcărilor, instalărilor și actualizărilor acestora. Apoi trebuie doar să includeți fișierul de încărcare automată al Composer, vendor/autoload.php
, în loc să solicitați manual fiecare bibliotecă.
Pluginurile și temele WordPress nu folosesc adesea biblioteci terțe. Acest lucru se datorează parțial deoarece WordPress are un API extins care satisface aproape orice nevoie și, parțial, din cauza posibilelor conflicte de versiuni. Luați în considerare două pluginuri care necesită aceeași bibliotecă PHP, dar versiuni diferite ale acesteia. Pluginul care rulează primul primește versiunea corectă, iar al doilea plugin primește și versiunea respectivă. Aceasta este foarte probabil o altă situație de ecran alb al morții.
Pentru a evita conflictele, managementul dependențelor ar trebui folosit la nivel de aplicație, adică site-ul WordPress ca întreg. Aceasta este ceea ce face Roots (Bedrock, mai precis). Când este utilizat la nivel de pachet (plugin sau temă), Composer poate provoca conflicte atunci când utilizează biblioteci terțe. Este o problemă cunoscută. Singura soluție care există până acum este redenumirea spațiilor de nume ale bibliotecii externe în ceva unic și aceasta nu este o sarcină banală.
Totuși, Composer are încă o utilizare: încărcarea automată a propriilor clase. Dar înainte de a merge mai departe cu încărcarea automată, trebuie să fim la curent cu spațiile de nume PHP.
Cea mai bună practică PHP modernă #5: Utilizați spații de nume
Miezul WordPress fiind un proiect moștenit, folosește un spațiu de nume global sau, altfel spus, nici un spațiu de nume. Orice clase sau funcții declarate la nivel global (adică nu în interiorul unei alte clase sau funcție) sunt vizibile oriunde în întreaga bază de cod. Numele lor trebuie să fie unice nu numai în baza de cod, ci și pentru toate pluginurile și temele care sunt folosite acum sau care pot fi folosite în viitor.
Ciocnirea denumirii (de exemplu, declararea unei funcții cu un nume care există deja) duce de obicei la ecranul alb al morții și nu vrem asta. Codexul WordPress recomandă să prefixați toate funcțiile și clasele cu ceva unic. Deci, în loc să avem nume simple de clasă, cum ar fi Order
, obținem ceva de genul Akrte_Awesome_Plugin_Order
unde „Akrte” este prefixul unic pe care tocmai l-am creat.
Spațiile de nume pot fi văzute ca grupuri – sau foldere, dacă folosim o analogie cu sistemul de fișiere – care ajută la organizarea codului și la evitarea coliziunii de nume. Puteți avea spații de nume complexe care sunt separate cu o bară oblică, la fel ca folderele imbricate. (Spațiile de nume PHP folosesc în special o bară oblică inversă.)
Acele părți ale spațiului de nume sunt numite sub-spații de nume. Exemplul nostru de clasă Akrte_Awesome_Plugin_Order
ar fi Akrte\Awesome_Plugin\Order
dacă ați terminat folosind spațiile de nume. Aici Akrte
și Awesome_Plugin
sunt părți ale spațiului de nume (sau sub-spații de nume), iar Order
este numele clasei. Apoi puteți adăuga o instrucțiune de use
și puteți utiliza numai numele clasei după aceea. Cu siguranta arata mai bine:
use Akrte\Awesome_Plugin\Order; $a = new Order;
Evident, spațiile de nume ar trebui să fie unice; astfel, ar trebui să dăm primului sub-spațiu de nume „rădăcină” un nume unic și acesta este de obicei un nume de furnizor. De exemplu, clasa WC_REST_Order_Notes_V2_Controller
ar putea fi refăcută cu spații de nume ca acesta:
namespace WooCommerce\RestApi\V2\Controllers; class OrderNotes {}
Baza de cod WooCommerce folosește în zilele noastre spații de nume; de exemplu, în versiunea 4 a API-ului WooCommerce REST.
Cea mai bună practică PHP modernă #6: Folosiți un încărcător automat
În majoritatea fluxurilor de lucru PHP, modalitatea obișnuită de a lega fișierele PHP între ele este utilizarea instrucțiunilor require
sau include
. Pe măsură ce un proiect crește, primiți zeci de declarații require
în fișierul principal de plugin. Un autoloader automatizează includerea fișierelor și face acest lucru numai după cum este necesar. Din punct de vedere tehnic, este o funcție care require
un fișier care conține o clasă sau o funcție atunci când este întâlnită pentru prima dată în cod. Nu mai este nevoie să adăugați manual instrucțiunile require
.
Adesea, există și un câștig semnificativ de performanță, deoarece un autoloader încarcă numai modulele care sunt utilizate într-o anumită solicitare. Fără un autoloader, întreaga bază de cod este inclusă chiar dacă o solicitare utilizează doar, să zicem, 10% din cod.
O funcție de încărcare automată trebuie să știe în ce fișiere se află clasele și funcțiile dvs. Și există un standard PHP-FIG, PSR-4, pentru asta.
Se spune că o parte a unui spațiu de nume, prefixul, este desemnată să corespundă unui folder de bază. Sub-spațiile de nume care îl urmează corespund folderelor din interiorul folderului de bază. În cele din urmă, numele clasei corespunde cu numele fișierului. Un exemplu de clasă Akrte\AwesomePlugin\Models\Order
ar avea nevoie de structura de foldere de mai jos:
/awesome-plugin awesome-plugin.php /includes /Models Order.php
Prefixul spațiului de nume Akrte\AwesomePlugin\
corespunde folderului includes
așa cum este specificat în configurația de încărcare automată discutată mai jos. Sub-spațiul de nume Models
are un folder Models
corespunzător, iar clasa Order
este conținută în Order.php
.
Din fericire, nu este nevoie să implementați singur o funcție de încărcare automată. Composer poate crea un autoloader pentru tine:
- Instalați Composer
- Creați un fișier
composer.json
în folderul rădăcină al proiectului. Ar trebui să conțină aceste rânduri:
{ "name": "vendor-name/plugin-name", "require": {}, "autoload": { "psr-4": { "Akrte\\AwesomePlugin\\": "includes/" } } }
- Executați
composer install
. - Includeți
vendor/autoload.php
în partea de sus a fișierului principal PHP al pluginului, astfel:
<?php /** * Plugin Name: My Awesome Plugin */ defined('ABSPATH') || exit; require __DIR__ . '/vendor/autoload.php'; // do stuff
Pe lângă spațiile de nume, cea mai recentă bază de cod WooCommerce folosește și un autoloader Composer.
Cu aceste principii de design PHP acoperite, este timpul să legăm toate lecțiile noastre PHP înapoi la personalizarea back-end WordPress cu o ultimă recomandare.
Cea mai bună practică de dezvoltare WordPress modernă #4: Luați în considerare utilizarea stivei Roots
Roots este cel mai cuprinzător flux de lucru modern de dezvoltare WordPress existent. Cu toate acestea, aș spune că nu ar trebui folosit neapărat în fiecare proiect WordPress, deoarece:
- Roots trebuie folosit de la început. Cel mai frecvent motiv, într-adevăr. Refactorizarea unui proiect existent ar fi prea costisitoare.
- Este opinie. Bine când ești de acord, rău când nu. Este posibil să preferați alte moduri de a vă organiza tema, de exemplu. Proiectele cu opinii necesită, de asemenea, timp pentru a învăța „modul lor”.
- Nu toată lumea o știe. Dezvoltatorul care va veni după tine pentru a întreține site-ul pe care l-ai creat cu stiva Roots poate să nu aibă idee despre ce este și să se întrebe ce s-a întâmplat cu folderele WordPress. Ar trebui să ne gândim la colegii noștri dezvoltatori WordPress.
În general, ați dori să înțelegeți pe deplin toate beneficiile și dezavantajele oricărui proiect cu opinie puternică înainte de a vă angaja să îl utilizați.
Principii moderne PHP și software: Dezvoltarea back-end WordPress este robustă
Este destul de evident că nu există un mod adevărat de a scrie software. Conceptele, cum ar fi separarea preocupărilor, sunt vechi de zeci de ani; cu toate acestea, ceea ce înseamnă practic a fost întotdeauna contestat. Luați, de exemplu, CSS. La început, l-am introdus ca style
în HTML, apoi am decis că separarea preocupărilor este în foile CSS separate.
Un deceniu înainte: aplicațiile JavaScript actuale folosesc componente ca implementare de separare a preocupărilor. Dezvoltatorii front-end gravitează către CSS-in-JS, iar asta înseamnă practic introducerea CSS din nou în HTML (ei bine, nu este atât de simplu, dar înțelegi ideea). Cercul este complet!
Cele mai bune practici au vizat întotdeauna îmbunătățirea experienței dezvoltatorilor:
Programele trebuie scrise pentru ca oamenii să le citească și doar întâmplător pentru ca mașini să le execute.
Abelson & Sussman, Structura și interpretarea programelor de calculator
Unele dintre practicile din acest tutorial PHP WordPress sunt rapid și ușor de implementat în proiectul tău. De exemplu, încărcătorul automat: faceți-o o dată pe proiect și bucurați-vă. Pe de altă parte, noile idei de arhitectură software au nevoie de timp, practică și numeroase iterații pentru a fi la îndemână și confortabil. Recompensele sunt însă mult mai mari. Nu numai că vei fi mai eficient în ceea ce faci, dar și să te bucuri mai mult de ceea ce faci. Iar plăcerea regulată a muncii pe care o desfășurați pentru clienți este poate singurul mod în care aceasta poate fi sustenabilă.