Object-Oriented Autoloading în WordPress, Partea 2

În tutorialul anterior, am acoperit o mulțime de concepte, toate acestea fiind necesare pentru a înțelege pe deplin ce facem în acest tutorial.

Mai exact, am abordat următoarele subiecte:

  • interfețe orientate pe obiecte
  • principiul responsabilității unice
  • cum arată acestea în PHP
  • unde ne îndreptăm cu plugin-ul nostru

În unele serii, este ușor să ignorați tutorialele care nu se pot construi unul pe altul; totuși, această serie nu are intenția de a fi așa. În schimb, este destinat să fie citit în ordine succesivă și este menit să se bazeze pe conținutul fiecărui tutorial anterior.

Cu asta a spus că presupun că sunteți prinși. 

Noțiuni de bază

Chiar dacă aș fi menționat acest lucru în primul tutorial, tot îmi place să fiu sigur că suntem pe aceeași pagină în ceea ce privește ceea ce facem în fiecare tutorial și cu ce software aveți nevoie.

Foaia de parcurs

Deci, în acest tutorial, planul este după cum urmează:

  1. Examinați codul pe care l-am scris până acum.
  2. Determinați modul în care am putea să îl refacem folosind tehnici orientate pe obiecte.
  3. Furnizați conturul la nivel înalt pentru implementarea noastră.

În cele din urmă, nu vom scrie mult cod în acest tutorial, dar vom scrie câteva. Este, totuși, un tutorial practic prin faptul că efectuăm analize și design orientate pe obiecte. Aceasta este o etapă necesară pentru multe proiecte de anvergură (și ceva ce ar trebui să se întâmple pentru proiecte de mici dimensiuni).

De ce ai nevoie

Dacă ați urmat de-a lungul, ar trebui să fiți deja pregătiți. Dar pentru a vă asigura că este vorba despre versiunea scurtă a tot ceea ce aveți nevoie:

  • un mediu de dezvoltare local adaptat sistemului dvs. de operare
  • un director din care WordPress 4.6.1 este găzduit
  • un editor de text sau IDE
  • cunoașterea API-ului WordPress Plugin

Cu toate acestea, suntem gata să lucrăm la codul partajat în tutorialul anterior. Deci sa începem.

Analizând codul

Primul lucru pe care vrem să-l facem este să analizăm starea curentă a autoloaderului nostru. Ar putea părea o mulțime de cod pentru a lipi într-un singur bloc de cod, dar asta în sine ne arată că avem ceva de făcut pentru a face.

Cu asta a spus, iată starea curentă a autoloaderului nostru:

 0; $ i--) // Citiți componenta curentă a părții de fișier. $ curent = strtolower ($ file_parts [$ i]); $ curent = str_ireplace ('_', '-', $ curent); // Dacă suntem la prima intrare, atunci suntem la numele fișierului. dacă (numără ($ file_parts) - 1 === $ i) / * Dacă "interfața" este conținută în părțile din numele fișierului, atunci * definește $ file_name diferit, astfel încât să fie încărcat corect. * În caz contrar, trebuie doar să setați $ file_name egal cu cel al structurii de nume * filename *. * / if (strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'interfață')) // Luați numele interfeței din numele său calificat. $ nume_intervenție = explode ('_', $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ numele interfeței [0]; $ file_name = "interfață- $ nume_intervenție.php";  altceva $ file_name = "class- $ current.php";  altceva $ namespace = '/'. $ curent. Spațiu de nume $;  // Acum construi o cale către fișier folosind maparea în locația fișierului. $ filepath = trailingslashit (dirname (dirname (__FILE__)) $ namespace); $ filepath. = $ nume_fișier; // Dacă fișierul există în calea specificată, atunci includeți-l. dacă (file_exists ($ filepath)) include_once ($ filepath);  altceva wp_die (esc_html ("Fișierul care încearcă să fie încărcat la $ filepath nu există")); 

În acest moment, rețineți că principiul responsabilității unice prevede următoarele:

O clasă ar trebui să aibă doar un singur motiv pentru a se schimba.

Chiar acum, nu avem nici măcar o clasă, să nu mai vorbim de mai multe metode individuale care au doar un singur motiv să se schimbe.

Și deși ar putea fi logic să începeți prin a rupe această metodă autoloader în metode mai mici, individuale, să începem de la un nivel superior și să începem să ne gândim la un autoloader în termeni de interfață. Apoi vom examina crearea unei clase (sau clase).

Analiza orientată pe obiecte: responsabilități

Rețineți din tutorialul anterior că o interfață este definită de manualul PHP după cum urmează:

Interfețele de obiecte vă permit să creați un cod care să specifice metodele pe care o clasă trebuie să le implementeze, fără a fi nevoie să definiți modul în care sunt gestionate aceste metode.

Având în vedere codul și definițiile de mai sus, să ne gândim la ceea ce trebuie să facă un autoloader dintr-o perspectivă mai modulară. Mai exact, hai să o împărțim în puncte care reprezintă ceea ce ar putea fi suficient pentru a se schimba. Nu, este posibil să nu folosim toate aceste puncte, dar de aceea se numește analiză. Vom lucra la design mai târziu.

Codul face următoarele:

  1. Validează că lucrăm în mod explicit cu spațiul nostru de nume.
  2. Împărțiți numele de clasă de intrare în părți pentru a determina dacă este o clasă sau o interfață (deci $ class_name este un nume de variabilă proastă).
  3. Verifică dacă lucrăm cu un fișier de interfață.
  4. Verifică dacă lucrăm cu un fișier de clasă.
  5. Verifică dacă lucrăm cu o interfață.
  6. Pe baza rezultatelor condițiilor de mai sus, generează un nume de fișier.
  7. Construiește o cale de fișier bazată pe numele de fișier generat.
  8. Dacă fișierul există la numele generat, îl include.
  9. În caz contrar, codul generează o eroare.

Astfel, codul de mai sus nouă lucruri-adică este macar nouă motive să se schimbe - înainte de a-și termina lucrările. 

Acest lucru ar trebui să fie de la sine înțeles, dar această funcție particulară este un exemplu perfect că putem refactor să demonstreze analiză orientată pe obiecte, design, interfețe și implementare.

Și aceasta ridică o întrebare: Unde începem chiar?

Analiza orientată pe obiecte

În acest moment, este corect să spunem că putem începe să facem o analiză orientată pe obiecte - adică să privim ce clase potențiale avem și cum interacționează - având în vedere tot ceea ce am enumerat mai sus. Amintiți-vă, de asemenea, dorim ca principiul responsabilității unice să ne ajute să ne îndrume în luarea deciziilor noastre.

În acest moment, nu suntem îngrijorați de modul în care clasele vor comunica unul cu celălalt. În schimb, ne concentrăm mai mult pe crearea de clase care au un singur motiv de schimbare.

Cu asta am spus că voi oferi un set de clase care ar putea funcționa. Înainte de a merge mai departe, uita-te la ceea ce am făcut și încercați să veniți cu propria listă. Atunci putem compara notele.

Un cuvânt despre abilități

Rețineți că ați putea avea o idee mai bună decât cele enumerate mai jos sau puteți lua ceva de la ceea ce am împărtășit. Indiferent, acesta este un exercițiu de învățare. Încercăm să îmbunătățim codul nostru, organizația noastră și, în cele din urmă, să devenim programatori mai buni.

Cursurile noastre potențiale

Având în vedere ceea ce am enumerat mai sus, am venit cu următoarele clase:

  1. Autoloader. Aceasta este clasa principală care este responsabilă pentru includerea în cele din urmă a clasei noastre, a spațiului nostru de nume sau a interfeței noastre. Vom numi această clasă. Restul sunt clase care vor avea grijă de munca necesară pe care această clasă trebuie să o includă. 
  2. NamespaceValidator. Acest fișier va examina clasa de intrare, interfața sau ceea ce aveți și veți determina dacă este valid. Acest lucru ne va da factorul decisiv dacă putem continua cu restul codului nostru nu. 
  3. FileInvestigator. Această clasă examinează tipul de fișier care este trecut în autoloader. Acesta va determina dacă este vorba despre o clasă, o interfață sau un spațiu de nume și returnează numele fișierului complet calificat în fișier astfel încât acesta să fie inclus.
  4. FileRegistry. Aceasta va folosi calea de fișier complet calificată returnată în cele din urmă de la celelalte clase și va include în plugin.

Si asta e. Acum, clasele terță parte din pluginul nostru trebuie doar să știe despre clasa de autoloader, dar autoloaderul va avea nevoie de cunoștințe despre o altă clasă, iar alte clase vor avea nevoie de cunoștințe despre alte clase.

Acolo sunteți modalități de a face față acestei situații (folosind containerele de injecție de dependență, dar acest lucru depășește scopul acestui proiect). Dar ceea ce ne vom strădui să facem prin codul nostru este să minimizăm cât de multe clase știu unul despre celălalt.

Obiect-Oriented Design

În acest moment, diferiți dezvoltatori, firme, agenții și echipe vor lua o altă abordare a modului în care proiectează sistemul pe care lucrează.

Una dintre cele mai comune metode de a face acest lucru este de a folosi ceva numit diagrama UML. Deși este util, nu este ceva care merită făcut în cadrul acestui tutorial, deoarece va necesita un întreg tutorial pentru a explica toate piesele.

Deci, pentru scopurile tutorialului nostru și din moment ce lucrăm cu un număr atât de mic de coduri, vom încerca să eliminăm modul în care fiecare dintre clasele de mai sus ar putea funcționa înainte de a le implementa. În acest fel, vom avea o idee despre cum putem organiza codul nostru.

Rețineți că nu vom fi numiți încă niciunul din acest cod și niciunul din acest cod nu ar trebui implementat sau testat încă de la WordPress. Vom intra în asta în următorul tutorial.

Să începem cu Autoloader și lucrează de acolo.

Autoloader

Rețineți că această clasă este responsabilă pentru includerea dosarului necesar. Acesta este fișierul care va fi înregistrat cu spl_autoload_register funcţie. 

namespace_validator = nou NamespaceValidator (); $ this-> file_registry = noul FileRegistry ();  încărcarea funcției publice ($ filename) if ($ this-> namespace_validator-> is_valid ($ filename)) $ this-> file_registry-> load (numele fișierului $);  

Rețineți că această clasă depinde de NamespaceValidator si FileRegistry clasă. Vom vedea mai multe detalii în câteva minute.

NamespaceValidator

Acest fișier va analiza numele de fișier primit și va determina dacă este valid. Acest lucru se realizează prin examinarea spațiilor de nume din numele fișierului.

Dacă fișierul face de fapt aparțin spațiului nostru de nume, atunci putem presupune că este sigur să încărcăm fișierul nostru.

FileInvestigator

Această clasă face destul de mult de lucru, deși o parte din ea se face prin metode foarte simple, foarte mici de ajutor. Pe parcursul executării, acesta examinează tipul fișierului pe care a trecut-o. 

Apoi, acesta va prelua numele de fișier complet calificat pentru tipul de fișier.

get_file_name ($ file_parts, $ curent, $ i); dacă (numără ($ file_parts) - 1! == $ i) $ filepath = trailingslashit ($ filepath);  retur $ filepath;  funcția privată get_file_name ($ file_parts, $ current, $ i) $ filename = "; if (count ($ file_parts) - 1 === $ i) if ($ this-> is_interface ($ file_parts) fișier nume = $ this-> get_interface_name ($ file_parts); altceva $ filename = $ this-> get_class_name ($ curent); altceva $ filename = $ this-> get_namespace_name ($ curent);  funcția privată is_interface ($ file_parts) retur strpos (strtolower ($ file_parts [count ($ file_parts) - 1]), 'interfață' $ file_parts [count ($ file_parts) - 1]); $ interface_name = $ interface_name [0]; return "interface- $ interface_name.php"; funcția privată get_class_name ($ current) return "class- $ current.php" ; funcția privată get_namespace_name ($ curent) return '/'. $ current;

Dacă există un fișier care poate fi refactat un pic mai mult, atunci acesta este. La urma urmei, încearcă să determine dacă lucrăm cu o clasă, o interfață sau o clasă. O fabrică simplă ar putea fi mai potrivită pentru acest lucru.

Când vine timpul să punem în aplicare codul nostru, probabil că vom refactoriza acest lucru mai departe. Până atunci, acesta este un proiect preliminar care poate funcționa destul de bine.

FileRegistry

Aceasta va folosi calea fișierului complet calificată și va include fișierul; în caz contrar, va folosi API-ul WordPress pentru a afișa un mesaj de eroare.

clasa FileRegistry private $ investigator; funcția publică __construct () $ this-> investigator = new FileInvestigator ();  încărcarea funcției publice ($ filepath) $ filepath = $ this-> investigator-> get_filetype ($ filepath); $ filepath = rtrim (plugin_dir_path (dirname (__FILE__)), '/'). $ Filepath; dacă (file_exists ($ filepath)) include_once ($ filepath);  altceva wp_die (esc_html ('Fișierul specificat nu există.'));  

O altă alternativă la utilizarea API-ului WordPress ar fi aruncarea unui mesaj de excepție personalizat. În acest fel, vom putea separa complet sau decupla codul nostru de la WordPress.

Încă o dată, acest cod este o transmisie de la autoloaderul inițial. În timpul implementării, putem schimba și acest lucru.

Concluzie

Bine, deci ne-am uitat la codul existent pentru autoloader-ul nostru, și apoi am stubat un cod potențial pe care îl putem folosi pe baza unor analize și design orientate pe obiecte.

Este soluția la care lucrăm mai bine decât ceea ce avem? Absolut. Aceasta va funcționa în contextul WordPress și al plugin-ului nostru existent? Nu vom ști până când nu vom începe să conectăm acest plugin la pluginul nostru.

După cum am menționat anterior, există încă unele zone în care am putea refactoriza acest cod. Dacă vom lovi aceste tipuri de probleme atunci când implementăm codul nostru în versiunea finală a plugin-ului nostru, vom analiza exact ceea ce facem.

Indiferent de situație, codul pe care îl avem acum ar trebui să fie mai ușor de citit (deși încă mai avem DocBlocks și câteva comentarii inline de introdus) și mai susținute și chiar mai teste.

Cu toate acestea, sper că acest lucru ți-a dat o idee despre modul de a lua o metodă lungă și ao sparge în mai multe cursuri conduse de scopuri. Sigur, având mai multe clase ar putea să se simtă ciudat la început, dar asta nu înseamnă că este un lucru rău. Aveți mai multe fișiere (și astfel clase) cu un cod mai mic decât un fișier cu o mulțime de cod este mai bine.

Îmbrățișează natura contraintuitivă a programării orientate pe obiecte în această privință. În tutorialul următor, ne vom întoarce la pluginul nostru și vom lucra la implementarea unei variații a codului de mai sus. Probabil că vom depana și câteva dintre ele. La urma urmei, rareori ne facem bine prima data

Până atunci, dacă sunteți interesat să citiți mai multe despre programarea orientată obiect în contextul WordPress, puteți găsi toate tutorialele mele anterioare pe pagina mea de profil. Simțiți-vă liber să urmați pe blogul meu sau urmați-mă pe Twitter unde vorbesc frecvent despre ambele.

Resurse

  • Obiect-Oriented Autoloading în WordPress, Partea 1
  • Spații de nume
  • Autoîncărcarea
  • interfeţe
  • API-ul Plugin-ului WordPress
  • Principiul unic de responsabilitate
Cod