Dekryptere magiske metoder i PHP

PHP gir en rekke "magiske" metoder som gjør at du kan gjøre noen ganske fine triks i objektorientert programmering. Disse metodene, identifisert av et to understrekeprefix (__), fungerer som interceptors som automatisk kalles når visse betingelser er oppfylt. Magiske metoder gir noen ekstremt nyttig funksjonalitet, og denne opplæringen vil demonstrere bruk av hver metode.


Før vi begynner

For å fullt ut forstå magiske metoder, er det nyttig å se dem i aksjon. Så la oss starte med et sett med svært enkle klasser. Her definerer vi to klasser: Enhet og batteri.

forbindelse = 'ressurs'; ekko $ dette-> navn. ' tilkoblet' . PHP_EOL;  beskyttet funksjon koble fra () // koble fra nettverket $ this-> connection = null; ekko $ dette-> navn. 'frakoblet'. PHP_EOL;  klasse batteri private $ charge = 0; offentlig funksjon setCharge ($ charge) $ charge = (int) $ charge; if ($ kostnad < 0)  $charge = 0;  elseif($charge > 100) $ charge = 100;  $ this-> charge = $ charge; ?>

Hvis ord som "metode" og "eiendom" høres fremmed til deg, vil du kanskje lese om dette først.

Enhetsobjekter vil inneholde et navn, et batteriobjekt, en rekke data og et håndtak til en ekstern ressurs. De har også metoder for å koble til og koble fra ekstern ressurs. Batteriobjekter lagrer bare en kostnad i en privat eiendom, og har en metode for å angi ladningen.

Denne opplæringen antar at du har en grunnleggende forståelse av objektorientert programmering. Hvis ord som "metode" og "eiendom" høres fremmed til deg, vil du kanskje lese om det først.

Disse klassene er ganske ubrukelige, men de er et godt eksempel på hver av de magiske metodene. Så nå som vi har laget våre enkle klasser, kan vi prøve de magiske metodene.


Byggere og destruktorer

Konstruktører og destruktorer kalles når en gjenstand er opprettet og ødelagt, henholdsvis. Et objekt er "ødelagt" når det ikke er flere referanser til det, enten fordi variabelen som holder den ble avbrudd / tilordnet eller skriptet avsluttet.

__construct ()

De __construct () Metoden er uten tvil den mest brukte magiske metoden. Det er her du gjør noen initialisering du trenger når et objekt opprettes. Du kan definere et hvilket som helst antall argumenter her, som vil bli bestått når du lager objekter. Eventuell returverdi vil bli sendt gjennom ny søkeord. Eventuelle unntak kastet i konstruktøren vil stoppe objektet opprettelse.

klassen enhet // ... offentlig funksjon __construct (batteri $ batteri, $ navn) // $ batteri kan bare være et gyldig batteri objekt $ dette-> batteri = $ batteri; $ dette-> navn = $ navn; // Koble til nettverket $ this-> connect ();  // //

Deklarering av konstruktormetoden "privat" forhindrer ekstern kode fra å direkte opprette en gjenstand.

Her har vi erklært en konstruktør som aksepterer to argumenter, et batteri og et navn. Konstruktøren tilordner hver av egenskapene som objektene krever for å fungere og kjører koble() metode. Konstruktøren lar deg sikre at en gjenstand har alle nødvendige deler før den kan eksistere.

Tips: Deklarerer konstruktormetoden "privat" forhindrer ekstern kode fra å direkte opprette en gjenstand. Dette er praktisk for å lage singleton klasser som begrenser antall objekter som kan eksistere.

Med den ovennevnte konstruktøren på plass, her er hvordan du oppretter en enhet som heter 'iMagic':

$ device = ny enhet (nytt batteri (), 'iMagic'); // iMagic tilkoblet echo $ device-> navn; // iMagic

Som du kan se, blir argumenter som sendes til klassen, faktisk overført til konstruktormetoden. Du kan også fortelle at tilkoblingsmetoden ble kalt og $ name eiendommen ble befolket.

La oss si at vi glemmer å passere et navn. Her er hva som skjer:

$ device = ny enhet (nytt batteri ()); // Resultat: PHP Advarsel: Manglende argument 2 for Device :: __ construct ()

__destruct ()

Som navnet tilsier, er det __destruct () Metoden kalles når objektet er ødelagt. Det aksepterer ingen argumenter og brukes ofte til å utføre opprydding, for eksempel å lukke en databaseforbindelse. I vårt tilfelle bruker vi den til å koble fra nettverket.

klassenhet // ... offentlig funksjon __destruct () // koble fra nettverket $ this-> disconnect (); ekko $ dette-> navn. ' var ødelagt' . PHP_EOL;  // //

Med ovennevnte destructor på plass, her er det som skjer når en Enhetsobjekt er ødelagt:

$ device = ny enhet (nytt batteri (), 'iMagic'); // iMagic-tilkoblet unset ($ device); // iMagic frakoblet // iMagic ble ødelagt

Her har vi ødelagt objektet ved hjelp av ikke-satt (). Før det blir ødelagt, kalles destruktoren koble fra() metode og skriver ut en melding, som du kan se i kommentarene.


Eiendomsoverbelastning

Merk: PHPs versjon av "overbelastning" er ikke helt den samme som de fleste andre språk, men de samme resultatene kan nås.

Dette neste settet med magiske metoder handler om å håndtere tilgang til eiendom, og definerer hva som skjer når du prøver å få tilgang til en egenskap som ikke eksisterer (eller ikke er tilgjengelig). De kan brukes til å skape pseudoegenskaper. Dette kalles overloading i PHP.

__få()

De __få() Metoden kalles når kode forsøker å få tilgang til en eiendom som ikke er tilgjengelig. Den aksepterer ett argument, som er navnet på eiendommen. Det bør returnere en verdi, som vil bli behandlet som verdien av eiendommen. Husk $ data eiendom i vår enhetsklasse? Vi lagrer disse "pseudoegenskapene" som elementer i dataregmentet, og vi kan la brukere av klassen få tilgang til dem via __få(). Slik ser det ut:

klassenhet // ... offentlig funksjon __get ($ navn) // sjekk om den navngitte nøkkelen finnes i vårt array hvis (array_key_exists ($ name, $ this-> data)) // returnerer verdien fra array returneringen $ dette-> data [$ name];  returnere null;  // //

En populær bruk av __få() Metoden er å utvide tilgangskontrollen ved å skape "skrivebeskyttet" egenskaper. Ta vår Batteriklasse, for eksempel, som har en privat eiendom. Vi kan tillate privat $ kostnad eiendom som skal leses fra utvendig kode, men ikke endret. Koden vil se slik ut:

klasse batteri private $ charge = 0; offentlig funksjon __get ($ navn) hvis (isset ($ dette -> $ navn)) return $ this -> $ name;  returnere null;  // //

I dette eksemplet merker du på bruk av variable variabler for å få tilgang til en egenskap dynamisk. Forutsetter verdien "bruker" for $ name, $ Dette -> $ name oversetter til $ Dette-> user.

__sett()

De __sett() metode kalles når kode forsøker å endre verdien en egenskap som ikke er tilgjengelig. Den aksepterer to argumenter, som er navnet på eiendommen og verdien. Her ser du ut som "pseudovariabler" -arrangementet i vår enhetsklasse:

klasse enhet // ... offentlig funksjon __set ($ navn, $ verdi) // bruk egenskapen navn som array nøkkelen $ this-> data [$ navn] = $ verdi;  // //

__isset ()

De __isset () Metoden kalles når koden ringer isset () på en eiendom som ikke er tilgjengelig. Den aksepterer ett argument, som er navnet på eiendommen. Det bør returnere en boolsk verdi som representerer eksistensen av en verdi. Igjen ved hjelp av vårt variabel array, her ser det ut som:

klasse enhet // ... offentlig funksjon __isset ($ navn) // du kan også bruke isset () her returnere array_key_exists ($ navn, $ this-> data);  // //

__unset ()

De __unset () Metoden kalles når kode forsøker å ikke-satt () en eiendom som ikke er tilgjengelig. Den aksepterer ett argument, som er navnet på eiendommen. Her ser vi hvordan vi ser ut:

klasse enhet // ... offentlig funksjon __unset ($ navn) // videresend unset () til vårt array element unset ($ this-> data [$ navn]);  // //

Eiendomsoverbelastning i aksjon

Her er alle eiendomsrelaterte magiske metoder vi har erklært:

klasse Enhet // ... offentlig $ data = array (); // butikker misc. data i en matrise // ... offentlig funksjon __get ($ navn) // sjekk om den navngitte nøkkelen finnes i vårt array hvis (array_key_exists ($ name, $ this-> data)) // returnerer verdien fra arrayen returner $ this-> data [$ navn];  returnere null;  offentlig funksjon __set ($ navn, $ verdi) // bruk eiendomsnavnet som matrise nøkkelen $ this-> data [$ name] = $ value;  offentlig funksjon __isset ($ navn) // du kan også bruke isset () her returnere array_key_exists ($ navn, $ this-> data);  offentlig funksjon __unset ($ name) // videresend unset () til vårt array element unset ($ this-> data [$ navn]);  // //

Med de ovennevnte magiske metodene, her er det som skjer når vi prøver å få tilgang til et eiendom som heter navnet. Husk at det egentlig ikke er en $ name Eiendom erklært, selv om du aldri ville vite det uten å se den interne klassekoden.

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

Vi har satt og hentet verdien av en ikke-eksisterende egenskap. Vel hvor er det lagret da?

print_r ($ enhets-> data); / * Array ([user] => Steve) * /

Som du kan se, er $ data Eiendommen inneholder nå et "navn" element med vår verdi.

var_dump (isset ($ enhets-> user)); // bool (sant)

Over er resultatet av å ringe isset () på den falske eiendommen.

ikke-satt ($ enhets-> user); var_dump (isset ($ enhets-> user)); // bool (false)

Ovenstående er resultatet av å oppheve den falske eiendommen. Bare for å være sikker på, her er vår tomme dataramming:

print_r ($ enhets-> data); / * Array () * /

Representerer objekter som tekst

Noen ganger kan det være lurt å konvertere et objekt til en strengrepresentasjon. Hvis du bare prøver å skrive ut et objekt, får vi en feil, som den nedenfor:

$ device = ny enhet (nytt batteri (), 'iMagic'); ekko $ enhet; // Resultat: PHP Fangbar dødelig feil: Objekt av klasse Enhet kunne ikke konverteres til streng

__toString ()

De __toString () Metoden kalles når kode forsøker å behandle et objekt som en streng. Det aksepterer ingen argumenter og skal returnere en streng. Dette gjør at vi kan definere hvordan objektet vil bli representert. I vårt eksempel lager vi et enkelt sammendrag:

klasse Enhet ... offentlig funksjon __toString () // er vi tilkoblet? $ connected = (isset ($ this-> tilkobling))? 'koblet': 'frakoblet'; // hvor mye data har vi? $ count = count ($ this-> data); // sett alt sammen returnere $ this-> navn. ' er ' . $ koblet til. 'med'. $ teller. 'elementer i minnet'. PHP_EOL;  ...

Med den ovenfor definerte metoden er det hva som skjer når vi prøver å skrive ut en Enhetsobjekt:

$ device = ny enhet (nytt batteri (), 'iMagic'); ekko $ enhet; // iMagic er koblet til 0 elementer i minnet

Enhetsobjektet er nå representert ved en kort oppsummering som inneholder navn, status og antall lagrede elementer.

__set_state () (PHP 5.1)

Den statiske __set_state () metode (tilgjengelig fra PHP versjon 5.1) kalles når var_export () funksjonen er påkalt vårt objekt. De var_export () funksjonen brukes til å konvertere en variabel til PHP-kode. Denne metoden aksepterer en assosiativ array som inneholder egenskapsverdiene til objektet. For enkelhets skyld, bruk det godt i vår Batteriklasse.

klasse batteri // ... offentlig statisk funksjon __set_state (array $ array) $ obj = nytt selvtillit (); $ Obj-> setCharge ($ matrise [ 'charge']); returner $ obj;  // //

Vår metode skaper bare en forekomst av sin overordnede klasse og setter å belaste til verdien i det bestått matrisen. Med den ovenfor definerte metoden er det hva som skjer når vi bruker var_export () på en Enhetsobjekt:

$ device = ny enhet (nytt batteri (), 'iMagic'); var_export ($ enhets-> batteri); / * Batteri :: __ set_state (array ('charge' => 0,)) * / eval ('$ batteri ='. Var_export ($ device-> batteri, sant). ';'); var_dump ($ batteri); / * objekt (batteri) # 3 (1) ["lade: privat"] => int (0) * /

Den første kommentaren viser hva som faktisk skjer, hvilket er det var_export () bare samtaler Batteri :: __ set_state (). Den andre kommentaren viser at vi har gjort det mulig å gjenopprette batteriet.


Kloning Objekter

Objekter, som standard, blir sendt ved referanse. Så tilordne andre variabler til et objekt vil faktisk ikke kopiere objektet, det vil ganske enkelt opprette en ny referanse til det samme objektet. For å virkelig kopiere et objekt må vi bruke klone søkeord.

Denne "pass by reference" -politikken gjelder også for objekter innenfor objekter. Selv om vi kloner en gjenstand, vil eventuelle barnobjekter det skjer for å inneholde ikke bli klonet. Så ville vi ende opp med to objekter som deler det samme barnobjektet. Her er et eksempel som illustrerer at:

$ device = ny enhet (nytt batteri (), 'iMagic'); $ device2 = klone $ enhet; $ Enhets-> batteri-> setCharge (65); echo $ device2-> batteri-> lade; // 65

Her har vi klonet en Enhetsobjekt. Husk at alle Enhetsobjekter inneholder et batteriobjekt. For å demonstrere at begge klonene i enheten deler det samme batteriet, er endringen vi laget til $ enhetens batteri reflektert i $ device2 batteri.

__clone ()

De __clone () Metode kan brukes til å løse dette problemet. Det kalles på kopien av en klonet gjenstand etter at kloning har funnet sted. Her kan du klone alle barnobjekter.

klasse Enhet ... offentlig funksjon __clone () // kopier vårt batteri objekt $ dette-> batteri = klone $ this-> batteri;  ...

Med denne metoden erklært, kan vi nå være sikker på at de klonede enhetene har sitt eget batteri.

$ device = ny enhet (nytt batteri (), 'iMagic'); $ device2 = klone $ enhet; $ Enhets-> batteri-> setCharge (65); echo $ device2-> batteri-> lade; // 0

Endringer i en enhets batteri påvirker ikke den andre.


Objekt Serialisering

Serialisering er prosessen som konverterer data til et strengformat. Dette kan brukes til å lagre hele objekter i en fil eller database. Når du unserialiserer lagrede data, har du det opprinnelige objektet akkurat som det var før. Et problem med serialisering er imidlertid at ikke alt kan bli serialisert, for eksempel databaseforbindelser. Heldigvis finnes det noen magiske metoder som tillater oss å håndtere dette problemet.

__sove()

De __sove() Metoden kalles når serialiserer () funksjonen kalles på objektet. Den aksepterer ingen argumenter og bør returnere en rekke av alle egenskapene som skal serialiseres. Du kan også fullføre eventuelle ventende oppgaver eller opprydding som kan være nødvendig i denne metoden.

Tips: Unngå å gjøre noe ødeleggende i __sove() siden dette vil påvirke det levende objektet, og du kan ikke alltid være ferdig med det.

I vårt Device-eksempel representerer forbindelsesegenskapen en ekstern ressurs som ikke kan serialiseres. Så vår __sove() Metode returnerer bare en rekke av alle egenskapene bortsett fra $ tilkobling.

klassenhet offentlig $ navn; // navnet på enheten offentlig $ batteri; // holder et batteriobjekt offentlig $ data = array (); // butikker misc. data i en array offentlig $ forbindelse; // holder noen tilkoblingsressurs // ... offentlig funksjon __sleep () // liste egenskapene for å lagre returarrangementet ('navn', 'batteri', 'data');  // //

Våre __sove() bare returnerer en liste over navnene på egenskaper som skal bevare.

__Våkn opp()

De __Våkn opp() Metoden kalles når unserialize () funksjonen kalles på det lagrede objektet. Det tar ingen argumenter og trenger ikke å returnere noe. Bruk den til å gjenopprette en databaseforbindelse eller ressurs som gikk tapt i serialisering.

I vårt Device-eksempel trenger vi ganske enkelt å gjenopprette forbindelsen vår ved å ringe vår koble() metode.

klasse Enhet // ... offentlig funksjon __wakeup () // Koble til nettverket $ this-> connect ();  // //

Metode Overbelastning

Disse to siste metodene er for å håndtere metoder. Dette er det samme konseptet som egenskapen overbelastning metoder (__få(), __sett(), etc), men anvendt på metoder.

__anrop()

De __anrop() kalles når kode forsøker å ringe utilgjengelige eller ikke-eksisterende metoder. Den aksepterer to argumenter: navnet på den kalte metoden og en rekke argumenter. Du kan bruke denne informasjonen til å ringe samme metode i et barnobjekt, for eksempel.

I eksemplene, merk bruk av call_user_func_array () funksjon. Denne funksjonen lar oss dynamisk kalle en navngitt funksjon (eller metode) med argumentene lagret i en matrise. Det første argumentet identifiserer funksjonen som skal ringes. Ved navngivningsmetoder er det første argumentet en matrise som inneholder et klassenavn eller objekteksempel og navnet på eiendommen. Det andre argumentet er alltid en indeksert rekke argumenter for å passere.

I vårt eksempel vil vi passere metallsamtalen til vår $ tilkobling eiendom (som vi antar er et objekt). Vi returnerer resultatet av det rett tilbake til telefonnummeret.

klassenhet // ... offentlig funksjon __call ($ navn, $ argumenter) // sørg for at vårt barnobjekt har denne metoden hvis (method_exists ($ this-> connection, $ name)) // videresender anropet til barnet vårt objekt returnere call_user_func_array (array ($ this-> tilkobling, $ navn), $ argumenter);  returnere null;  // //

Ovennevnte metode vil bli kalt hvis vi prøver å ringe iDontExist () metode:

$ device = ny enhet (nytt batteri (), 'iMagic'); $ Enhets-> iDontExist (); // __call () videresender dette til $ device-> connection-> iDontExist ()

__callStatic () (PHP 5.3)

De __callStatic () (tilgjengelig fra PHP versjon 5.3) er identisk med __anrop() bortsett fra at det kalles når kode forsøker å ringe utilgjengelige eller ikke-eksisterende metoder i en statisk sammenheng.

De eneste forskjellene i vårt eksempel er at vi erklærer metoden som statisk og vi refererer til et klassenavn i stedet for et objekt.

klassen enhet // ... offentlig statisk funksjon __callStatic ($ navn, $ argumenter) // sørg for at klassen vår har denne metoden hvis (method_exists ('Connection', $ name)) // videresend det statiske anropet til vår klasse retur call_user_func_array (array ('Connection', $ navn), $ argumenter);  returnere null;  // //

Ovennevnte metode vil bli kalt hvis vi prøver å ringe den statiske iDontExist () metode:

Device :: iDontExist (); // __callStatic () videresender dette til Connection :: iDontExist ()

Bruke objekter som funksjoner

Noen ganger vil du kanskje bruke et objekt som en funksjon. Å kunne bruke et objekt som en funksjon lar deg overføre funksjoner rundt som argumenter som du kan på andre språk.

__invoke () (PHP 5.3)

De __invoke () (tilgjengelig fra PHP versjon 5.3) kalles når kode prøver å bruke objektet som en funksjon. Eventuelle argumenter definert i denne metoden vil bli brukt som funksjonsargumenter. I vårt eksempel skal vi bare skrive ut argumentet det mottar.

klassenhet // ... offentlig funksjon __invoke ($ data) echo $ data;  // //

Med det ovenfor definerte, er dette hva som skjer når vi bruker en enhet som en funksjon:

$ device = ny enhet (nytt batteri (), 'iMagic'); $ Innretning ( 'test'); // equiv til $ device -> __ påkalle ('test') // Outputs: test

Bonus: __autoload ()

Dette er ikke en magisk metode, men det er fortsatt veldig nyttig. De __autoload () funksjon kalles automatisk når en klasse som ikke eksisterer er referert til. Det er ment å gi deg en siste sjanse til å laste filen som inneholder klassedeklarasjonen før skriptet mislykkes. Dette er nyttig siden du ikke alltid vil laste hver klasse bare hvis du trenger det.

Funksjonen aksepterer ett argument: navnet på den refererte klassen. Si at du har hver klasse i en fil som heter 'classname.class.php' i 'inc'-katalogen. Her er hva din autoload vil se ut:

funksjon __autoload ($ class_name) $ class_name = strtolower ($ class_name); include_once './inc/'. $ class_name. '.Class.php'; 

Konklusjon

Magiske metoder er ekstremt nyttige og gir kraftige verktøy for å utvikle fleksible applikasjonsrammer. De bringer PHP-objekter nærmere dem i andre objektorienterte språk ved å tillate deg å gjengi noen av deres mer nyttige funksjoner. Du kan lese PHP manuelle sider på magiske metoder her. Jeg håper denne opplæringen var nyttig og tydelig forklarte konseptene. Hvis du har noen spørsmål, ikke nøl med å spørre i kommentarene. Takk for at du leste.