Decipherarea metodelor magice în PHP

PHP oferă o serie de metode "magice" care vă permit să faceți niște trucuri destul de îngrijite în programarea orientată obiect. Aceste metode, identificate printr-un prefix de două subliniere (__), funcționează ca interceptoare care sunt numite automat atunci când sunt îndeplinite anumite condiții. Metodele Magic oferă câteva funcții extrem de utile, iar acest tutorial va demonstra utilizarea fiecărei metode.


Înainte de a începe

Pentru a înțelege pe deplin metodele magice, este util să le vezi în acțiune. Deci, să începem cu un set de bază de clase foarte simple. Aici definim două clase: Dispozitiv și baterie.

conexiune = 'resursă'; echo $ this-> name. ' conectat' . PHP_EOL;  funcția protejată deconectați () // deconectați în siguranță de la rețea $ this-> connection = null; echo $ this-> name. "deconectat". PHP_EOL;  clasa Baterie încărcare privată $ = 0; funcția publică setCharge (taxa $) $ charge = (int) $ charge; în cazul în care (taxa de $ < 0)  $charge = 0;  elseif($charge > 100) încărcare $ = 100;  $ this-> charge = $ charge; ?>

Dacă cuvinte precum "metoda" și "proprietatea" vă sun străine, puteți să citiți mai întâi acest lucru.

Obiectele dispozitivului vor deține un nume, un obiect Baterie, o serie de date și un mâner pentru o anumită resursă externă. De asemenea, au metode pentru conectarea și deconectarea resursei externe. Obiectele de acumulare stochează pur și simplu o taxă într-o proprietate privată și au o metodă de a stabili încărcarea.

Acest tutorial presupune că aveți o înțelegere de bază a programării orientate obiect. Dacă cuvinte precum "metoda" și "proprietatea" vă sun străine, este posibil să doriți să citiți mai întâi acest lucru.

Aceste clase sunt destul de inutile, dar fac un bun exemplu pentru fiecare dintre metodele magice. Deci, acum, când am creat clasele noastre simple, putem încerca metodele magice.


Constructori și distrugătoare

Constructorii și distrugatorii sunt numiți atunci când un obiect este creat și distrus, respectiv. Un obiect este "distrus" atunci când nu mai există referințe la el, fie pentru că variabila care a ținut-o era dezactivată / realocată, fie scenariul sa încheiat.

__construi()

__construi() metoda este de departe cea mai frecvent utilizată metodă magică. Aici faceți orice inițializare de care aveți nevoie atunci când este creat un obiect. Puteți defini aici orice număr de argumente care vor fi transmise atunci când creați obiecte. Orice valoare de returnare va fi trecută prin nou cuvinte cheie. Orice excepții aruncate în constructor vor opri crearea de obiecte.

dispozitivul de clasă // ... funcția publică __construct (bateria $ baterie, $ nume) // $ bateria poate fi doar un obiect de baterie valid $ this-> battery = $ battery; $ this-> nume = $ nume; // conectați la rețea $ this-> connect ();  // ...

Declarația metodei constructorului "privat" împiedică codul extern să creeze direct un obiect.

Aici am declarat un constructor care acceptă două argumente, o baterie și un nume. Constructorul atribuie fiecare dintre proprietățile pe care obiectele le cere să funcționeze și să le execute conectați() metodă. Constructorul vă permite să vă asigurați că un obiect are toate piesele necesare înainte de a putea exista.

Bacsis: Declararea metodei constructorului "privat" împiedică codul extern să creeze direct un obiect. Acest lucru este util pentru crearea de clase singleton care restricționează numărul de obiecte care pot exista.

Cu constructorul de mai sus, iată cum creați un dispozitiv numit "iMagic":

$ device = dispozitiv nou (nou baterie (), "iMagic"); // iMagic conectat echo $ device-> name; // iMagic

După cum puteți vedea, argumentele transmise clasei sunt de fapt transmise metodei constructorului. De asemenea, puteți spune că a fost apelată metoda de conectare și numele $ proprietatea a fost populată.

Să spunem că uităm să-i dăm un nume. Iată ce se întâmplă:

$ device = dispozitiv nou (baterie nouă ()); // Result: PHP Avertisment: Argument lipsă 2 pentru Device :: __ construct ()

__destruct ()

După cum sugerează și numele, __destruct () metoda este numită atunci când obiectul este distrus. Nu acceptă argumente și este folosit în mod obișnuit pentru a efectua orice operațiuni de curățare, cum ar fi închiderea unei conexiuni de bază de date. În cazul nostru, îl vom folosi pentru a vă deconecta de la rețea.

clasa Device // ... funcția publică __destruct () // deconectare de la rețea $ this-> disconnect (); echo $ this-> name. ' a fost distrus' . PHP_EOL;  // ...

Cu distrugătorul de mai sus, iată ce se întâmplă atunci când un obiect Dispozitiv este distrus:

$ device = dispozitiv nou (nou baterie (), "iMagic"); // iMagic conectat dezactivat ($ device); // iMagic deconectat // iMagic a fost distrus

Aici, am distrus obiectul utilizând unset (). Înainte de a fi distrus, distrugătorul îl cheamă deconecta() și imprimă un mesaj, pe care îl puteți vedea în comentarii.


Suprasolicitarea proprietății

Notă: Versiunea PHP a "supraîncărcării" nu este la fel ca majoritatea celorlalte limbi, deși pot fi obținute aceleași rezultate.

Următorul set de metode magice este vorba despre abordarea accesului la proprietăți, definind ce se întâmplă atunci când încercați să accesați o proprietate care nu există (sau nu este accesibilă). Ele pot fi folosite pentru a crea proprietăți pseudo. Aceasta se numește supraîncărcare în PHP.

__obține()

__obține() metoda se numește atunci când codul încearcă să acceseze o proprietate care nu este accesibilă. Acceptă un argument, care este numele proprietății. Ar trebui să returneze o valoare, care va fi tratată ca fiind valoarea proprietății. Amintiți-vă $ date proprietate în clasa dispozitivului nostru? Vom păstra aceste "proprietăți pseudo" ca elemente în matricea de date și le vom permite utilizatorilor din clasa noastră să le acceseze prin __obține(). Iată cum arată:

($ name = $ date)) // returneaza valoarea din randul matricei // ... functia publica __get ($ name) // verifica daca cheia numita exista in matricea noastra daca (array_key_exists ($ name, $ this-> data) $ this-> date [numele $];  return null;  // ...

O utilizare populară a __obține() metoda este de a extinde controlul accesului prin crearea de proprietăți "numai pentru citire". Utilizați clasa bateriei, de exemplu, care are o proprietate privată. Putem permite privat taxa de $ proprietățile care trebuie citite din codul exterior, dar care nu s-au schimbat. Codul ar arăta astfel:

bateria clasa încărcare privată $ = 0; funcția publică __get ($ name) if (isset ($ this -> $ name)) retur $ this -> $ name;  return null;  // ...

În acest exemplu, rețineți utilizarea variabilelor variabile pentru a accesa dinamic o proprietate. Presupunând valoarea "utilizator" pentru numele $, $ This -> $ name se traduce la $ This-> utilizator.

__a stabilit()

__a stabilit() metoda se numește atunci când codul încearcă să modifice valoarea unei proprietăți care nu este accesibilă. Acceptă două argumente, care sunt numele proprietății și valoarea. Iată ce arată pentru matricea "pseudo variables" din clasa dispozitivului nostru:

clasa Device // ... funcția publică __set ($ name, $ value) // folosiți numele proprietății ca cheia de matrice $ this-> data [$ name] = $ value;  // ...

__isset ()

__isset () metoda este apelată atunci când apelurile de cod isset () pe o proprietate care nu este accesibilă. Acceptă un argument, care este numele proprietății. Ar trebui să returneze o valoare booleană reprezentând existența unei valori. Folosind din nou matricea noastră variabilă, iată cum arată:

clasa Device // ... funcția publică __isset ($ name) // ar putea folosi isset () aici return array_key_exists ($ name, $ this-> data);  // ...

__unset ()

__unset () metoda se numește atunci când codul încearcă să unset () o proprietate care nu este accesibilă. Acceptă un argument, care este numele proprietății. Iată cum arată a noastră:

clasa Device // ... funcția publică __unset ($ name) // transmite unset () elementului nostru de matrice unset ($ this-> data [$ name]);  // ...

Suprasolicitarea proprietății în acțiune

Iată toate metodele magice legate de proprietate pe care le-am declarat:

dispozitivul de clasă // ... public $ data = array (); // stochează misc. datele într-o matrice // ... funcția publică __get ($ name) // verificați dacă cheia numită există în matricea noastră dacă (array_key_exists ($ name, $ this-> data)) // returnează valoarea din array returnați $ this-> data [$ name];  return null;  funcția publică __set ($ name, $ value) // folosiți numele proprietății ca cheia de matrice $ this-> data [$ name] = $ value;  funcția publică __isset ($ name) // ar putea folosi isset () aici return array_key_exists ($ name, $ this-> data);  funcția publică __unset ($ name) // transmite unset () la elementul de matrice neuniform ($ this-> data [$ name]);  // ...

Cu metodele magice de mai sus, iată ce se întâmplă atunci când încercăm să accesăm o proprietate numită nume. Amintiți-vă că nu există într-adevăr a numele $ proprietatea declarată, deși nu ați fi știut niciodată acest lucru fără a vedea codul de clasă internă.

$ device-> user = 'Steve'; echo $ device-> user; // Steve

Am setat și am recuperat cu succes valoarea unei proprietăți inexistente. Unde este depozitată atunci?

print_r ($ device-> date); / * Array ([utilizator] => Steve) * /

După cum puteți vedea, $ date proprietatea conține acum un element "nume" cu valoarea noastră.

var_dump (isset ($ device-> utilizator)); // bool (true)

Mai sus este rezultatul chemării isset () pe proprietatea falsă.

unset (utilizator $ device->); var_dump (isset ($ device-> utilizator)); // bool (false)

Mai sus este rezultatul dezactivării proprietății false. Doar pentru a ne asigura, aici este matricea noastră de date goale:

print_r ($ device-> date); / * Array () * /

Reprezentarea obiectelor ca text

Uneori este posibil să doriți să transformați un obiect într-o reprezentare de șir. Dacă încercați să imprimați un obiect, vom primi o eroare, cum ar fi cea de mai jos:

$ device = dispozitiv nou (nou baterie (), "iMagic"); echo $ device; // Rezultat: PHP eroare fatală fatală: Obiectul dispozitivului de clasă nu a putut fi convertit în șir

__toString ()

__toString () metoda se numește atunci când codul încearcă să trateze un obiect ca un șir. Nu acceptă argumente și ar trebui să returneze un șir. Acest lucru ne permite să definim modul în care va fi reprezentat obiectul. În exemplul nostru, vom crea un rezumat simplu:

clasa Device ... funcția publică __toString () // suntem conectați? $ connected = (isset ($ this-> connection))? "conectat": "deconectat"; // cât de mult avem date? $ count = count ($ this-> data); // puneți totul împreună returnați $ this-> name. ' este ' . $ conectat. ' cu ' . $ count. "elemente în memorie". PHP_EOL;  ...

Cu metoda definită mai sus, iată ce se întâmplă atunci când încercăm să tipăm un obiect Device:

$ device = dispozitiv nou (nou baterie (), "iMagic"); echo $ device; // iMagic este conectat cu 0 elemente din memorie

Obiectul Device este reprezentat acum de un scurt sumar care conține numele, starea și numărul de articole stocate.

__set_state () (PHP 5.1)

Staticul __set_state () (disponibilă începând cu versiunea 5.1 din PHP) se numește atunci când var_export () funcția este chemată pe obiectul nostru. var_export () funcția este folosită pentru a converti o variabilă în cod PHP. Această metodă acceptă o matrice asociativă care conține valorile proprietății obiectului. Pentru simplitate, folosește-o bine în clasa bateriei noastre.

clasa Baterie // ... funcția statică publică __set_state (array $ array) $ obj = new self (); $ Obj-> setCharge ($ array [ 'taxa']); returnați $ obj;  // ...

Metoda noastră creează pur și simplu o instanță a clasei părinte și setează încărcarea la valoarea din matricea trecută. Prin metoda definită mai sus, iată ce se întâmplă atunci când o folosim var_export () pe un obiect Dispozitiv:

$ device = dispozitiv nou (nou baterie (), "iMagic"); var_export ($ device-> baterie); / * Baterie :: __ set_state (array ('charge' => 0,)) * / eval ('$ battery ='. Var_export ($ device-> battery, true). var_dump ($ baterie); / * obiect (baterie) # 3 (1) ["încărcare: privată"] => int (0)

Primul comentariu arată ce se întâmplă de fapt, și anume var_export () pur și simplu sună Baterie :: __ set_state (). Al doilea comentariu ne arată că am reușit să recreăm bateria.


Clonarea obiectelor

Obiectele, în mod prestabilit, sunt transmise prin referință. Deci, atribuirea altor variabile unui obiect nu va copia obiectul, ci va crea pur și simplu o referință nouă la același obiect. Pentru a copia cu adevărat un obiect, trebuie să folosim clona cuvinte cheie.

Această politică "trece prin referință" se aplică și obiectelor din obiecte. Chiar dacă clonăm un obiect, orice obiecte de copil pe care se întâmplă să le conțin nu vor fi clonate. Așadar, am ajunge la două obiecte care au același obiect copil. Iată un exemplu care ilustrează că:

$ device = dispozitiv nou (nou baterie (), "iMagic"); $ device2 = dispozitiv clone $; $ Device-> baterii> setCharge (65); echo $ device2-> battery-> charge; // 65

Aici, am clonat un obiect Device. Amintiți-vă că toate obiectele dispozitivului conțin un obiect Baterie. Pentru a demonstra că ambele clone ale Dispozitivului au aceeași baterie, schimbarea pe care am făcut-o cu bateria dispozitivului $ este reflectată în bateria $ device2's.

__clone ()

__clone () metoda poate fi utilizată pentru a rezolva această problemă. Se cheamă pe copia unui obiect clonat după ce are loc clonarea. Aici puteți clona orice obiecte copil.

class Device ... funcția publică __clone () // copiați obiectul Baterie $ this-> battery = clone $ this-> battery;  ...

Cu această metodă declarată, putem fi siguri că dispozitivele clonate au fiecare propria lor baterie.

$ device = dispozitiv nou (nou baterie (), "iMagic"); $ device2 = dispozitiv clone $; $ Device-> baterii> setCharge (65); echo $ device2-> battery-> charge; // 0

Modificările bateriei unui dispozitiv nu afectează cealaltă.


Serializarea obiectelor

Serializarea este procesul care convertește toate datele într-un format de șir. Aceasta poate fi utilizată pentru a stoca obiecte întregi într-un fișier sau într-o bază de date. Când dezarhivați datele stocate, veți avea obiectul original exact așa cum a fost înainte. O problemă cu serializarea, totuși, este că nu totul poate fi serializat, cum ar fi conexiunile bazei de date. Din fericire există câteva metode magice care ne permit să rezolvăm această problemă.

__dormi()

__dormi() metoda este numită atunci când serialize () este apelată pe obiect. Nu acceptă argumente și ar trebui să returneze o serie de proprietăți care ar trebui să fie serializate. De asemenea, puteți finaliza toate sarcinile sau curățarea în așteptare care pot fi necesare în această metodă.

Bacsis: Evitați să faceți ceva distructiv __dormi() deoarece acest lucru va afecta obiectul viu, și s-ar putea să nu fi întotdeauna încheiat cu el.

În exemplul nostru de dispozitive, proprietatea de conectare reprezintă o resursă externă care nu poate fi serializată. Deci, noi __dormi() metoda returnează pur și simplu o matrice a tuturor proprietăților cu excepția conexiune $.

dispozitiv de clasă public $ name; // numele dispozitivului public $ battery; // deține un obiect Baterie publică $ data = array (); // stochează misc. datele într-o conexiune publică $ matrice; // păstrează o anumită resursă de conexiune // ... funcția publică __sleep () // afișează proprietățile pentru a salva matricea de returnare ("nume", "baterie", "date");  // ...

Al nostru __dormi() returnează pur și simplu o listă cu numele proprietăților care ar trebui păstrate.

__trezește-te()

__trezește-te() metoda este numită atunci când unserialize () funcția este apelată pe obiectul stocat. Nu acceptă argumente și nu trebuie să returneze nimic. Utilizați-l pentru a restabili conexiunea bazei de date sau resursele care au fost pierdute în serializare.

În exemplul dispozitivului nostru, pur și simplu trebuie să restabiliți conexiunea prin apelul nostru conectați() metodă.

clasa Device // ... funcția publică __wakeup () // reconectați la rețea $ this-> connect ();  // ...

Metoda de supraîncărcare

Aceste două metode sunt pentru tratarea metodelor. Acesta este același concept ca și metodele de supraîncărcare a proprietăților (__obține(), __a stabilit(), etc), dar aplicate metodelor.

__apel()

__apel() este apelat atunci când codul încearcă să apeleze metode inaccesibile sau inexistente. Acceptă două argumente: numele metodei chemate și o serie de argumente. Puteți utiliza aceste informații pentru a apela aceeași metodă într-un obiect copil, de exemplu.

În exemple, rețineți utilizarea call_user_func_array () funcţie. Această funcție ne permite să chemăm dinamic o funcție denumită (sau o metodă) cu argumentele stocate într-o matrice. Primul argument identifică funcția de apel. În cazul metodelor de numire, primul argument este o matrice care conține un nume de clasă sau o instanță de obiect și numele proprietății. Al doilea argument este întotdeauna o serie de argumente indexate pentru a trece.

În exemplul nostru, vom trece metoda de apel la adresa noastră conexiune $ proprietate (care presupunem că este un obiect). Vom întoarce rezultatul căutării direct în codul de apelare.

($ name, $ arguments) // asigurați-vă că obiectul copil are această metodă dacă (method_exists ($ this-> connection, $ name)) // transmite apelul către copilul nostru retur de obiecte call_user_func_array (array ($ this-> connection, $ name), $ arguments);  return null;  // ...

Metoda de mai sus se va numi dacă încercăm să sunăm iDontExist () metodă:

$ device = dispozitiv nou (nou baterie (), "iMagic"); $ Device-> iDontExist (); // __call () trimite acest lucru la $ device-> connection-> iDontExist ()

__callStatic () (PHP 5.3)

__callStatic () (disponibil din versiunea 5.3 din PHP) este identic cu __apel() cu excepția faptului că se numește atunci când codul încearcă să apeleze metode inaccesibile sau inexistente într-un context static.

Singurele diferențe în exemplul nostru sunt faptul că declarăm metoda static și trimitem un nume de clasă în locul unui obiect.

($ name, $ arguments) // asigurați-vă că clasa noastră are această metodă dacă (method_exists ('Connection', $ name)) // transmite apelul static la returnul nostru de clasă call_user_func_array (array ('Connection', $ name), argumente $);  return null;  // ...

Metoda de mai sus se va numi dacă încercăm să sunăm static iDontExist () metodă:

Dispozitiv :: iDontExist (); // __callStatic () trimite acest lucru la Connection :: iDontExist ()

Utilizarea obiectelor ca funcții

Uneori este posibil să doriți să utilizați un obiect ca o funcție. Fiind capabil să utilizați un obiect ca o funcție vă permite să transmiteți funcții în jurul valorii de ca argumente ca dvs. puteți în alte limbi.

__invoke () (PHP 5.3)

__invoca() (disponibil din versiunea 5.3 din PHP) se numește atunci când codul încearcă să utilizeze obiectul ca o funcție. Toate argumentele definite în această metodă vor fi folosite ca argumente pentru funcții. În exemplul nostru, vom imprima pur și simplu argumentul pe care îl primește.

dispozitiv de clasă // ... funcția publică __invoke ($ data) echo $ data;  // ...

Cu cele definite mai sus, acest lucru se întâmplă atunci când folosim un dispozitiv ca o funcție:

$ device = dispozitiv nou (nou baterie (), "iMagic"); Dispozitiv de $ ( 'test'); // equiv la $ device -> __ invoke ('test') // Outputs: test

Bonus: __autoload ()

Aceasta nu este o metodă magică, dar este încă foarte utilă. __autoload () se numește automat atunci când se face referire la o clasă care nu există. Acesta este menit să vă dea o ultimă șansă de a încărca fișierul care conține declarația de clasă înainte ca scriptul dvs. să nu reușească. Acest lucru este util, deoarece nu doriți întotdeauna să încărcați fiecare clasă doar în cazul în care aveți nevoie de ea.

Funcția acceptă un argument: numele clasei de referință. Spuneți că aveți fiecare clasă într-un fișier numit 'classname.class.php' în directorul 'inc'. Iată ce ar arăta autoload-ul dvs.:

funcția __autoload ($ class_name) $ class_name = strtolower ($ class_name); include_once './inc/'. $ class_name. '.Class.php'; 

Concluzie

Metodele magice sunt extrem de utile și oferă instrumente puternice pentru dezvoltarea cadrelor flexibile de aplicare. Acestea aduc obiecte PHP mai aproape de cele din alte limbi orientate pe obiecte, permițându-vă să reproduceți unele dintre caracteristicile lor mai utile. Puteți citi paginile manual PHP despre metode magice aici. Sper că acest tutorial a fost de ajutor și a explicat clar conceptele. Dacă aveți întrebări, nu ezitați să întrebați în comentarii. Vă mulțumim pentru lectură.

Cod