Refleksjon er generelt definert som et programs evne til å inspisere seg selv og endre sin logikk på kjøretid. I mindre tekniske termer spør ettertanke om et objekt for å fortelle deg om egenskapene og metodene, og endre disse medlemmene (selv private). I denne leksjonen graver vi inn i hvordan dette oppnås, og når det kan vise seg å være nyttig.
Ved begynnelsen av programmeringsalderen var det forsamlingsspråket. Et program som er skrevet i samling, ligger på fysiske registre inne i datamaskinen. Dens sammensetning, metoder og verdier kan inspiseres når som helst ved å lese registret. Enda mer, du kan endre programmet mens det kjørte ved å bare endre disse registre. Det krevde litt intim kunnskap om det løpende programmet, men det var iboende reflekterende.
Som med noen kule leketøy, bruk refleksjon, men ikke misbruk det.
Som høyere nivå programmeringsspråk (som C) kom sammen, ble denne reflektiviteten forsvunnet og forsvunnet. Det ble senere gjeninnført med objektorientert programmering.
I dag kan de fleste programmeringsspråk bruke refleksjon. Statisk typede språk, som Java, har lite eller ingen problemer med refleksjon. Det jeg finner interessant er imidlertid at et dynamisk skrevet språk (som PHP eller Ruby) er sterkt basert på refleksjon. Uten refleksjonskonseptet ville det være sannsynlig at and-typing ikke kunne gjennomføres. Når du sender ett objekt til et annet (en parameter, for eksempel), har mottaksobjektet ingen måte å vite strukturen og typen av objektet. Alt det kan gjøre, er å bruke refleksjon for å identifisere metodene som kan og ikke kan kalles på det mottatte objektet.
Refleksjon er utbredt i PHP. Faktisk er det flere situasjoner når du kan bruke den uten å vite det. For eksempel:
// Nettuts.php require_once 'Editor.php'; klasse Nettutsats funksjon publishNextArticle () $ editor = new Editor ('John Doe'); $ Editor-> setNextArticle ( '135523'); $ Editor-> publisere ();
Og:
// Editor.php Class Editor privat $ navn; offentlig $ artikkel; funksjon __construct ($ navn) $ this-> name = $ name; offentlig funksjon setNextArticle ($ articleId) $ this-> articleId = $ articleId; offentlig funksjon publisere () // publiser logikk går her return true;
I denne koden har vi en direkte samtale til en lokalt initiert variabel med en kjent type. Oppretter redigereren i publishNextArticle ()
gjør det klart at $ redaktør
variabel er av typen Redaktør
. Ingen refleksjon er nødvendig her, men la oss introdusere en ny klasse, kalt sjef
:
// Manager.php require_once './Editor.php'; require_once './Nettuts.php'; Klasse Manager funksjon doJobFor (DateTime $ date) if ((new DateTime ()) -> getTimestamp ()> $ date-> getTimestamp ()) $ editor = ny editor ('John Doe'); $ nettuts = nytt Nettuts (); $ Nettuts-> publishNextArticle ($ redaktør);
Endre, endre Nettuts
, som så:
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) $ editor-> setNextArticle ('135523'); $ Editor-> publisere ();
Nå, Nettuts
har absolutt ingen forbindelse med Redaktør
klasse. Den inkluderer ikke filen, den starter ikke klassen, og den vet ikke engang at den eksisterer. Jeg kunne passere et objekt av hvilken som helst type i publishNextArticle ()
metode og koden ville fungere.
Som du kan se fra dette klassediagrammet, Nettuts
har bare et direkte forhold til sjef
. sjef
skaper det, og derfor, sjef
kommer an på Nettuts
. Men Nettuts
har ikke lenger noe forhold til Redaktør
klasse, og Redaktør
er bare relatert til sjef
.
På kjøretid, Nettuts
bruker en Redaktør
objekt, dermed <setNextArticle ()
og publisere()
fremgangsmåter.
Vi kan gjøre PHP vise detaljene til et objekt. La oss lage en PHPUnit-test for å hjelpe oss med å utøve vår kode enkelt:
// ReflectionTest.php require_once '... /Editor.php'; require_once '... /Nettuts.php'; klasse ReflectionTest utvider PHPUnit_Framework_TestCase funksjon testItCanReflect () $ editor = new Editor ('John Doe'); $ tuts = new Nettuts (); $ Tuts-> publishNextArticle ($ redaktør);
Nå legg til en var_dump ()
til Nettuts
:
// Nettuts.php class NetTuts funksjon publishNextArticle ($ editor) $ editor-> setNextArticle ('135523'); $ Editor-> publisere (); var_dump (ny ReflectionClass ($ editor));
Kjør testen, og se på magien som skjer i utgangen:
PHPUnit 3.6.11 av Sebastian Bergmann ... objekt (ReflectionClass) # 197 (1) ["navn"] => streng (6) "Editor" Tid: 0 sekunder, Minne: 2.25Mb OK (1 test, 0 påstander)
Vår refleksjonsklasse har a Navn
eiendom satt til den opprinnelige typen av $ redaktør
variabel: Redaktør
, men det er ikke mye informasjon. Hva med Redaktør
s metoder?
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) $ editor-> setNextArticle ('135523'); $ Editor-> publisere (); $ reflektor = ny ReflectionClass ($ editor); var_dump ($ reflector-> getMethods ());
I denne koden tildeler vi refleksjonsklassen 'forekomst til $ reflektor
variabel slik at vi nå kan utløse sine metoder. ReflectionClass
utsetter et stort sett med metoder som du kan bruke til å skaffe en objekts informasjon. En av disse metodene er getMethods ()
, som returnerer en matrise som inneholder hver metodeinformasjon.
PHPUnit 3.6.11 av Sebastian Bergmann ... array (3) [0] => og objekt (ReflectionMethod) # 196 (2) ["navn"] => streng (11) "__construct" ["class"] => streng (6) "Editor" [1] => og objekt (ReflectionMethod) # 195 (2) ["navn"] => streng (14) "setNextArticle" [2] => og objekt (ReflectionMethod) # 194 (2) ["navn"] => streng (7) "publiser" ["class"] => streng (6) "Editor" Tid: 0 sekunder , Minne: 2,25Mb OK (1 test, 0 påstander)
En annen metode, getProperties ()
, henter egenskapene (selv private egenskaper!) av objektet:
PHPUnit 3.6.11 av Sebastian Bergmann ... array (2) [0] => og objekt (ReflectionProperty) # 196 (2) ["navn"] => streng (4) "navn" ["class"] => streng (6) "Editor" [1] => og objekt (ReflectionProperty) # 195 (2) ["navn"] => streng (9) "artikkelId" Tid: 0 sekunder, Minne: 2,25Mb OK (1 test, 0 påstander)
Elementene i arrays returnerte fra getMethod ()
og getProperties ()
er av typen ReflectionMethod
og ReflectionProperty
, henholdsvis; disse objektene er ganske nyttige:
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) $ editor-> setNextArticle ('135523'); $ Editor-> publisere (); // første anrop for å publisere () $ reflector = ny ReflectionClass ($ editor); $ publishMethod = $ reflector-> getMethod ('publiser'); $ PublishMethod-> påberope ($ redaktør); // andre anrop for å publisere ()
Her bruker vi getMethod ()
å hente en enkelt metode med navnet "publisere"; Resultatet av dette er a ReflectionMethod
gjenstand. Så kaller vi påkalle ()
metode, passerer den $ redaktør
objekt, for å utføre redaktørens publisere()
metode en gang til.
Denne prosessen var enkel i vårt tilfelle, fordi vi allerede hadde en Redaktør
motsette seg å sende til påkalle ()
. Vi kan ha flere Redaktør
objekter under noen omstendigheter, noe som gir oss luksusen til å velge hvilket objekt å bruke. Under andre omstendigheter kan vi ikke ha noen objekter å jobbe med, i så fall må vi skaffe en fra ReflectionClass
.
La oss endre Redaktør
's publisere()
metode for å demonstrere dobbeltanropet:
// Editor.php class editor [...] offentlig funksjon publisere () // publiser logikk går her echo ("HERE \ n"); returnere sant;
Og den nye produksjonen:
PHPUnit 3.6.11 av Sebastian Bergmann ... HER HER Tid: 0 sekunder, Minne: 2.25Mb OK (1 test, 0 påstander)
Vi kan også endre kode på kjøretid. Hva med å endre en privat variabel som ikke har noen offentlig setter? La oss legge til en metode til Redaktør
som henter redaktørens navn:
// Editor.php Class Editor privat $ navn; offentlig $ artikkel; funksjon __construct ($ navn) $ this-> name = $ name; [...] funksjon getEditorName () return $ this-> name;
Denne nye metoden kalles, getEditorName ()
, og returnerer bare verdien fra privatpersonen $ name
variabel. De $ name
variabel er satt på opprettelsestidspunktet, og vi har ingen offentlige metoder som lar oss endre det. Men vi kan få tilgang til denne variabelen ved hjelp av refleksjon. Du kan først prøve den mer åpenbare tilnærmingen:
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) var_dump ($ editor-> getEditorName ()); $ reflektor = ny ReflectionClass ($ editor); $ editorName = $ reflector-> getProperty ('name'); $ EditorName-> getValue ($ redaktør);
Selv om dette gir verdi på var_dump ()
linje, kaster det en feil når du prøver å hente verdien med refleksjon:
PHPUnit 3.6.11 av Sebastian Bergmann. Estring (8) "John Doe" Tid: 0 sekunder, Minne: 2.50Mb Det var 1 feil: 1) ReflectionTest :: testItCanReflect ReflectionException: Kan ikke få tilgang til ikke-offentlig medlem Redaktør :: navn [...] / Refleksjon i PHP / Kilde / NetTuts.php: 13 [...] / Refleksjon i PHP / Source / Tests / ReflectionTest.php: 13 / usr / bin / phpunit: 46 FEIL! Test: 1, påstander: 0, feil: 1.
For å løse dette problemet må vi spørre ReflectionProperty
motsette seg å gi oss tilgang til de private variablene og metodene:
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) var_dump ($ editor-> getEditorName ()); $ reflektor = ny ReflectionClass ($ editor); $ editorName = $ reflector-> getProperty ('name'); $ EditorName-> setAccessible (true); var_dump ($ editorName-> getValue ($ redaktør));
ringe setAccessible ()
og passerer ekte
gjør kunsten:
PHPUnit 3.6.11 av Sebastian Bergmann ... streng (8) "John Doe" streng (8) "John Doe" Tid: 0 sekunder, Minne: 2.25Mb OK (1 test, 0 påstander)
Som du kan se, har vi klart å lese privat variabel. Den første produksjonslinjen er fra objektets egen getEditorName ()
metode, og den andre kommer fra refleksjon. Men hva med å endre verdien av en privat variabel? Bruke SetValue ()
metode:
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) var_dump ($ editor-> getEditorName ()); $ reflektor = ny ReflectionClass ($ editor); $ editorName = $ reflector-> getProperty ('name'); $ EditorName-> setAccessible (true); $ editorName-> setValue ($ editor, 'Mark Twain'); var_dump ($ editorName-> getValue ($ redaktør));
Og det er det. Denne koden endrer "John Doe" til "Mark Twain".
PHPUnit 3.6.11 av Sebastian Bergmann ... streng (8) "John Doe" streng (10) "Mark Twain" Tid: 0 sekunder, Minne: 2.25Mb OK (1 test, 0 påstander)
Noen av PHPs innebygde funksjonalitet indirekte bruker refleksjon, en er den call_user_func ()
funksjon.
De call_user_func ()
funksjon aksepterer en matrise: det første elementet peker mot en gjenstand, og den andre en metode navn. Du kan levere en valgfri parameter, som deretter sendes til den oppkalte metoden. For eksempel:
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) var_dump ($ editor-> getEditorName ()); $ reflektor = ny ReflectionClass ($ editor); $ editorName = $ reflector-> getProperty ('name'); $ EditorName-> setAccessible (true); $ editorName-> setValue ($ editor, 'Mark Twain'); var_dump ($ editorName-> getValue ($ redaktør)); var_dump (call_user_func (array ($ editor, 'getEditorName')));
Følgende utgang demonstrerer at koden henter riktig verdi:
PhpUnit 3.6.11 av Sebastian Bergmann ... streng (8) "John Doe" streng (10) Mark Twain-streng (10) Mark Twain Tid: 0 sekunder, Minne: 2.25Mb OK (1 test, 0 påstander)
Et annet eksempel på indirekte refleksjon er å kalle en metode av verdien i en variabel, i motsetning til å kalle det direkte. For eksempel:
// Nettuts.php class Nettuts funksjon publishNextArticle ($ editor) var_dump ($ editor-> getEditorName ()); $ reflektor = ny ReflectionClass ($ editor); $ editorName = $ reflector-> getProperty ('name'); $ EditorName-> setAccessible (true); $ editorName-> setValue ($ editor, 'Mark Twain'); var_dump ($ editorName-> getValue ($ redaktør)); $ methodName = 'getEditorName'; var_dump ($ editor -> $ metode ());
Denne koden gir samme utgang som forrige eksempel. PHP erstatter bare variabelen med strengen den representerer og kaller metoden. Den fungerer også når du vil lage objekter ved å bruke variabler for klassenavn.
Nå som vi har satt de tekniske detaljene bak oss, når skal vi utnytte refleksjon? Her er noen scenarier:
Som med noen kule leketøy, bruk refleksjon, men ikke misbruk det. Refleksjon er kostbart når du inspiserer mange objekter, og det har potensial til å komplisere prosjektets arkitektur og design. Jeg anbefaler at du bare bruker det når det faktisk gir deg en fordel, eller når du ikke har noe annet rentabelt alternativ.
Personlig har jeg bare brukt refleksjon i noen få tilfeller, oftest når jeg bruker tredjepartsmoduler som mangler dokumentasjon. Jeg finner meg selv ofte ved å bruke kode som ligner på det siste eksemplet. Det er lett å ringe riktig metode, når MVC reagerer med en variabel som inneholder "add" eller "remove" -verdiene.
Takk for at du leste!