6
Tags:
php
Skrevet af
Bruger #6653
@ 13.08.2006
Member Overloading
Denne artikel beskriver, hvordan man kan skjule funktioner til at få og ændre klassevariabler med. På den måde kan man få en pænere og mere stilren kode. Artiklen forudsætter grundlæggende kendskab til objektorientering i PHP.
Problemstilling
I kun meget simple eksempler vil man deklarere en variabel som public. Men bliver det blot en anelse mere avanceret, vil man deklarere variablen som protected eller private og lave funktioner til at få og ændre variablen. Følg eksemplet:
class Example
{
protected $name = 'Eric';
public function getName() { return $this->name; }
public function setName($value) { $this->name = $value; }
}
Fordelen ved denne metode er, at man kan validere værdierne i funktionerne. På den måde har man kontrol over både hvilken type og kvliken værdi, variablen har, og det er ikke blot en fordel, men mange steder er det også nødvendigt. Ulempen er dog, at man skal kalde to forskellige funktioner for henholdsvis at få variablen og at ændre variablen. Det kan muligvis skabe forvirring, og det ser mindre pænt ud i kildekoden. Derfor er denne metode ikke at foretrække.
Metoden, der er at foretrække, kaldes member overloading. Med denne metode kaldes en bestemt funktion, når man forsøger at kalde en variabel, som ikke findes. Det er vigtigt at huske, at member overloading kun kan lade sig gøre, hvis den kaldede variabel er usynlig eller ikke findes. Derfor skal variablen være usynlig uden for klassen. Se dette eksempel:
class Example
{
public $name = 'Eric';
protected $age = 90;
}
$ex = new ExampleTwo; // opret objektet
echo($ex->name); // virker som det skal, udskriver "Eric"
/* Nedenfor kan member overloading lade sig gøre, fordi variablen er usynlig.
Parseren vil også melde fejl. */
echo($ex->age);
/* Nedenfor kan member overloading igen lade sig gøre, fordi variablen ikke
findes. PHP melder ikke fejl, men reagerer som ved en tom variabel og
returnerer NULL. */
echo($ex->height);
Funktioner til løsning
I eksemplerne nedenfor bruger jeg bl.a. disse to funktioner. Uddybende dokumentation om disse og andre brugte funktioner findes i dokumentationen til PHP.
__get(string $name): $name er navnet på den variabel, scriptet forsøger at hente.
__set(string $name, string $value): $name er navnet på den variabel, scriptet orsøger at ændre til værdien af $value.
Eksempel 1
Der arbejdes direkte med variablen:
class ExampleOne
{
/* Variablen $name er ikke synlig uden for klassen - derfor kan member
overloading lade sig gøre her. */
protected $name = 'Eric';
/* Denne metode kan også erklæres som private eller public, men af hensyn
til eventuelle fejl har jeg valgt ikke at erklære den som public. */
protected function __get($name)
{
/* property_exists() tager både et objekt og en tekststreng som første
argument, men kun en tekststreng som andet argument. Funktionen
returnerer true, hvis variablen findes - og da vi i øjeblikket
befindes os inde i klassen, er også variabler erklæret som protected
og private synlige. */
if (property_exists($this, $name)) {
/* Læg godt mærke til dollartegnet: $this->$name. Hvis der
havde stået $this->name, kaldtes variablen 'name'. Men da der er
et dollartegn her, kaldes variablen med navn efter værdien af
variablen $name. */
return $this->$name;
}
}
/* For kommentarer til denne metode, se venligst __get(). */
protected function __set($name, $value)
{
if (property_exists($this, $name)) {
$this->$name = $value;
}
}
}
/* Først oprettes objektet. */
$ex = new ExampleOne;
/* Her kaldes en variabel, som ikke er synlig. Normalt ville parseren melde
fejl, men da metoden __get() er defineret, kaldes den i stedet for.
Funkionen returnerer den skjulte variabel. */
echo($ex->name);
/* Her ændres en variabel, som ikke er synlig. Normalt ville parseren melde
fejl, men da metoden __set() er defineret, kaldes den i stedet for.
Funktionen ændrer den skjulte variabel. */
$ex->name = 'John';
/* Her udskrives variablen med den nye værdi. */
echo($ex->name);
Eksempel 2
Der arbejdes med variablen gennem funktioner:
class ExampleTwo
{
protected $name = 'Eric';
protected function __get($name)
{
/* method_exists() fungerer på samme måde som property_exists(), og
returnerer true, hvis metoden findes. */
if (method_exists($this, "get$name")) {
/* call_user_func() tager som første argument enten en tekststreng
med navnet på den funtion, man vil kalde, eller et array med
første værdi som et klassenavn eller et objekt og anden værdi
som navnet på funktionen i klassen. */
return call_user_func(array($this, "get$name"));
}
}
protected function __set($name, $value)
{
if (method_exists($this, "set$name")) {
/* call_user_func() tager også argumenter til den ønskede funktion.
Argumenterne tilføjes som argumenter til call_user_func(), og
der er ingen begrænsning for antallet. */
call_user_func(array($this, "set$name"), $value);
}
}
/* I PHP er der ingen forskel på store og små bogstaver. Derfor spiller det
ingen rolle om funktionen hedder getName(), GETNAME() eller gETNamE().
Dog plejer man at bruge store eller små bogstaver efter sammenhængen. */
protected function getName()
{
return $this->name;
}
protected function setName($value) {
$this->name = $value;
}
}
$ex = new ExampleTwo;
/* I linjen nedenunder vil funktionen __get() kaldes med første parameter som
'name'. */
echo($ex->name);
/* I linjen nedenfor vil funktionen __set() kaldes med første og anden
parameter som henholdsvis 'name' og 'John'. */
$test->name = 'John';
echo($ex->name);
Eksempel 3
Det kan kombineres på utallige måder:
class ExampleThree
{
protected $name = 'Eric';
protected $data = array('age' => 90);
protected function __get($name)
{
/* Med funktioner */
if (method_exists($this, "get$name")) {
return call_user_func(array($this, "get$name"));
/* Med variabler */
} elseif (property_exists($this, $name)) {
return $this->$name;
/* Med værdier i et array */
} elseif (array_key_exists($name, $this->data)) {
return $this->data[$key];
/* Eller ved en simpel sammenligning */
} elseif ($name === 'height') {
return 185;
/* Husk at melde fejl, hvis den ikke findes */
} else {
trigger_error("Variablen $name blev ikke fundet");
}
}
protected function __set($name, $value)
{
/* Med funktioner */
if (method_exists($this, "get$name")) {
return call_user_func(array($this, "get$name"), $value);
/* Med variabler */
} elseif (property_exists($this, $name)) {
return ($this->$name = $value);
/* Med værdier i et array */
} elseif (array_key_exists($name, $this->data)) {
return ($this->data[$key] = $value);
/* Eller ved en simpel sammenligning */
} elseif ($name === 'height') {
trigger_error("Variablen 'height' er skrivebeskyttet");
/* Husk at melde fejl, hvis den ikke findes */
} else {
trigger_error("Variablen '$name' blev ikke fundet");
}
}
protected function getName() {
return $this->name;
}
}
$ex = new ExampleThree;
/* Nedenfor vil funktionen getName() kaldes. */
echo($ex->name);
/* Nedenfor hentes værdien i arrayet. */
echo($ex->age);
/* Nedenfor arbejdes der direkte med variablen. */
$ex->name = 'John';
/* Nedenfor meldes fejl. */
$ex->height = 205;
Efterskrift
Jeg er af den overbevisning, at objektorienteret programmering er kommet for at blive. Endvidere ved jeg, at member overloading allerede findes i en lang række eksisterende programmeringssprog. Objektorientering eksisterede på et meget lavt plan i PHP4, men med version 5 blev mange nye ting introduceret - bl.a. member overloading.
Det var mit mål, at denne artikel kan bruges som guide til member overloading, og som appetitvækker til mange af den andre spændende ting, PHP5 har at byde på gennem sin objektorienterede del. Til sidst vil jeg anbefale dig at gå på opdagelse i dokumentationen til PHP. Dokumentationen er ikke svært tilgængelig, og den indeholder forklaringer, faktabokse, eksempler og et register, så den er let at bruge. Tak for opmærksomheden og god lærelyst.
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (5)
Synes det en god artikel dog lidt underligt at den har overskrften Method Overloading og ikke Member Overloading. Og jeg finder det yderst suspect at du siger at variablen height er skrivebeskyttet når denne i sandhed ikke er en variable da den slet ikke er defineret i din class men er derimod en constant værdi kodet direkte ind i set metoden.
Endvidere ville jeg mene at bruge __set og __get på om en property exist og så sætte denne property eller returnere den fuldkommen ødelægger hele ideen med at gøre properties private da dette jo gør dem public. Og synes måske også du burde have nævnt det indlysende at brug af __set og __get uanset hvad du bruger dem til på fører et ekstra brug af computer kraft og tid i stedet for at bruge de funktionaliteter som du prøver at gøre let tilgængelige det værende metoder eller variabler.
Udemærket artikel, dog vil jeg knytte en lille advarsel til brugen af __get og __set.
Selvom __get og __set kan virke meget brugbare og relativt "nemme" og implementere, vil jeg mene at brugen af dem skal holdes på et absolut minimum. Man ender meget hurtigt med noget kode, som er tæt på umuligt at debugge.
class Example
{
public $name = 'Eric';
protected $age = 90;
}
$ex = new ExampleTwo; // opret objektet
Har du afprøvet dine koder før du har skrevet dem med i artiklen? Din klasse skulle hedde ExampleTwo og ikke Example, så vidt jeg kan se, men det er selvfølgelig en af de små daglidags fejl man begår
tjaah.. du bruger jo overhovedet ikke noget som har med overloading at gøre. Du viser jo bare brugen af 2 magiske metoder.
www.php.net/overloading
enjoy
Det handler ikke om brug af overloading, og jeg vil give JT ret i at det er svært at debugge. lav istedet private og protected variabler og lav specifikke funktioner til at sætte de enkelte variabler, det er pænere og meget lettere at debugge.
Du skal være
logget ind for at skrive en kommentar.