get
Soms heb je een class met allerlei get functies:
getId()
getUsername()
getFirstName()
getLastName()
getAge()
getPhoneNumber()
getMailAddress()
getCity()
getCountry()
... en ga zo maar door.
We gebruiken al die get functies omdat we onze properties private willen houden, en we willen voorkomen dat iemand van buitenaf de properties kan wijzigen. Maar nu zat ik me dus af te vragen... Waarom niet 1 get functie en dan als volgt gebruiken...
get('id')
get('username')
get('firstname')
get('lastname')
get('age')
enzovoorts...
Dan heb je nog maar 1 get functie nodig! Voor- / nadelen?
De C# oplossing (die in php 5.5 of 6 waarschijnlijk gaat komen) is natuurlijk de ultieme oplossing.
Wat voor ons het kortste in de buurt komt is het volgende:
Domain properties waar verder niets mee gebeurt maken we public.
Waar wel iets mee moet gebeuren lossen we op via de magic __get/__set in een basisklasse:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
class BaseClass
{
public function __get( $name )
{
if( method_exists( $this, 'Get' . $name ))
return $this->{'Get' . $name}();
else
trigger_error ( "Getter for $name does not exists" );
}
public function __set( $name, $value )
{
if( method_exists( $this, 'Set' . $name ))
$this->{'Set' . $name}( $value );
else
trigger_error ( "Setter for $name does not exists" );
}
}
?>
class BaseClass
{
public function __get( $name )
{
if( method_exists( $this, 'Get' . $name ))
return $this->{'Get' . $name}();
else
trigger_error ( "Getter for $name does not exists" );
}
public function __set( $name, $value )
{
if( method_exists( $this, 'Set' . $name ))
$this->{'Set' . $name}( $value );
else
trigger_error ( "Setter for $name does not exists" );
}
}
?>
In alle gevallen kun je dus $model->Waarde gebruiken, terwijl er toch een getter/setter aangeroepen wordt. Zo kun je b.v. ook heel gemakkelijk lazy loading implementeren. Of readonly properties.
voorbeeld
Code (php)
my 2 cents ...
Gewijzigd op 27/08/2012 17:17:55 door John Berg
Ozzie PHP op 27/08/2012 17:03:12:
Dan heb je nog maar 1 get functie nodig! Voor- / nadelen?
Het grote nadeel: je hebt geen apart voorgedefinieerde methodes meer. Daarmee verlies je de hulp van code completion en (op langere termijn) als het niet goed gedocumenteerd is het overzicht van welke properties allemaal wel en niet ophaalbaar zijn.
Voordeel: het scheelt enorm veel code en je bent veel flexibeler.
Het voorbeeld van John vind ik er een beetje tussenin. Of het veel voordelen heeft vraag ik me een beetje af, want je moet nog steeds alle getters/setters schrijven.
Mijn antwoord op wat je moet doen: geen :-)
De ene keer maak ik wel getters/setters, de andere keer via een magic methode of een algemene functie zoals je die zelf ook voorstelt. Mijns inziens is er niet een algemeen 'beste' methode.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
class User {
// ...
public function get($property)
{
return $this->get{ucfirst($property)}(); // weet niet of dit helemaal mogelijk is...
}
private function getName()
{
return ucwords($this->name);
}
private function getCountry()
{
return $this->country;
}
// ...
}
$user = new User();
$user->set('name', 'Wouter'); // sets name
echo 'Hallo '.$user->get('name'); // gets name
?>
class User {
// ...
public function get($property)
{
return $this->get{ucfirst($property)}(); // weet niet of dit helemaal mogelijk is...
}
private function getName()
{
return ucwords($this->name);
}
private function getCountry()
{
return $this->country;
}
// ...
}
$user = new User();
$user->set('name', 'Wouter'); // sets name
echo 'Hallo '.$user->get('name'); // gets name
?>
Of je doet iets als jQuery doet, dat lijkt het meest op de C# oplossing die in PHP gaat komen:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?php
class User
{
// ...
public function name()
{
if (1 == func_num_args()) {
// handles setter
$arguments = func_get_args();
$this->name = $arguments[0];
/* of vanaf PHP5.4
$this->name = func_get_args()[0]; */
} else {
// handles getter
return $this->name;
}
}
// ...
}
$user = new User();
$user->name('Wouter'); // sets name
echo 'Hallo '.$user->name(); // gets name
?>
class User
{
// ...
public function name()
{
if (1 == func_num_args()) {
// handles setter
$arguments = func_get_args();
$this->name = $arguments[0];
/* of vanaf PHP5.4
$this->name = func_get_args()[0]; */
} else {
// handles getter
return $this->name;
}
}
// ...
}
$user = new User();
$user->name('Wouter'); // sets name
echo 'Hallo '.$user->name(); // gets name
?>
En mocht je niet weten wat die C# oplossing is:
Code (php)
Gewijzigd op 27/08/2012 17:46:19 door Wouter J
Houd de gewone getters en setters.
Dan is er niemand die je tegenhoudt om een extra functie 'get' te hebben die het getten en setten groepeert.
Nu, vraag jezelf dit toch eens af:
voor wie doe je het? Voor de persoon die de class schrijft of voor de persoon die de class gebruikt?
Gewijzigd op 27/08/2012 17:51:58 door Kris Peeters
n.b. als het property bestaat worden de magic __get/__set niet aangeroepen.
Gewijzigd op 27/08/2012 17:53:18 door John Berg
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
public function __get( $name )
{
if( method_exists( $this, 'Get' . $name ))
return $this->{'Get' . $name}();
else
trigger_error ( "Getter for $name does not exists" );
}
?>
public function __get( $name )
{
if( method_exists( $this, 'Get' . $name ))
return $this->{'Get' . $name}();
else
trigger_error ( "Getter for $name does not exists" );
}
?>
Hieruit maak ik op dat alleen de properties die daadwerkelijk een getter hebben (gecontroleerd via de method_exists call) een return waarde kunnen krijgen. Alle andere leveren een error op. Dus, zo concludeer ik, alleen als er toch nog een aparte getter is geschreven voor de property kan je ophalen.
Maar graag uitleg als ik dit dus verkeerd zie.
Kris Peeters op 27/08/2012 17:48:36:
Nu, vraag jezelf dit toch eens af:
voor wie doe je het? Voor de persoon die de class schrijft of voor de persoon die de class gebruikt?
voor wie doe je het? Voor de persoon die de class schrijft of voor de persoon die de class gebruikt?
In eerste instantie voor mezelf. Op een gegeven moment krijg je zoveel getters dat ik me afvraag of het niet handiger kan... of beter gezegd, handiger MOET!
Het is inderdaad wel lastig dat je dan geen aparte functie meer hebt die automatisch door je editor wordt aangevuld. Dat is inderdaad een goede, maar het bespaart wel heel veel code. De opmerking over het niet kunnen bewerken van data had ik inderdaad even niet bij stil gestaan. Wellicht zou je dit dan al moeten regelen bij het setten?
Auw, dat doet echt pijn aan mijn ogen....
Gewijzigd op 27/08/2012 18:08:30 door Erwin H
Als je dus schrijft $myclass->Foo en Foo is public dan wordt de __get niet aangeroepen. Zo is PHP ontworpen
Als je schrijft $myclass->Bar en Bar is geen property van $myclass dan wordt de __get aangeroepen die dan probeert $myclass->GetBar() te callen.
We vinden dat het onderstaande stukje code geen enkele meerwaarde heeft:
Code (php)
Dus, waarom zou je deze hele berg code niet vervangen door dit, wat functioneel precies hetzelfde doet:
Gewijzigd op 27/08/2012 19:13:14 door John Berg
John Berg op 27/08/2012 19:12:10:
Als je dus schrijft $myclass->Foo en Foo is public dan wordt de __get niet aangeroepen. Zo is PHP ontworpen
Ik begrijp je nu, in eerste instantie begreep ik niet dat je bedoelde dat je public properties had. Deels misschien ook wel omdat ik daar enigszins allergisch voor ben :-)
Op zich heb je natuurlijk wel gelijk dat het simpel doorgeven van een waarde via een getter en setter weinig meer waarde heeft en dat je er een public property van zou kunnen maken. Alleen vind ik het persoonlijk niet netjes om het zo te doen. Een property is dat, het 'property' (eigendom) van een object en het object bepaalt wat ermee gebeurt. Bij mij zijn properties dus nooit public.
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class Test extends BaseClass
{
private $_foo;
public $bar;
protected function GetFoo()
{
return $_foo;
}
protected function SetFoo( $value )
{
$this->_foo = $value;
}
}
// het gebruik
$test = new Test;
$test->bar = 'proef';
echo $test->bar; // zal 'proef' laten zien
$test->foo = 'ook een proef';
echo $test->foo; // zal 'ook een proef' laten zien
$test->GetFoo(); // geeft foutmelding
?>
class Test extends BaseClass
{
private $_foo;
public $bar;
protected function GetFoo()
{
return $_foo;
}
protected function SetFoo( $value )
{
$this->_foo = $value;
}
}
// het gebruik
$test = new Test;
$test->bar = 'proef';
echo $test->bar; // zal 'proef' laten zien
$test->foo = 'ook een proef';
echo $test->foo; // zal 'ook een proef' laten zien
$test->GetFoo(); // geeft foutmelding
?>
Dus, net als in C# kun je op de "normale" wijze met properties werken. Waar niets bijzonders gebeurt, en dat is bij de meeste zo, maak je publics. Waar je iets bijzonders wil doen gebruik je de Get/Set, die echter voor de buitenwereld verborgen blijft.
Uiteraard is dit maar een tijdelijke oplossing, de echte oplossing komt met PHP 5.5 of PHP 6 (zie het voorbeeld wat Wouter gaf van C#)
We willen buiten de class zo compatible mogelijk blijven met de nieuwe aanroep vandaar.
Gewijzigd op 27/08/2012 19:52:16 door John Berg
Omdat je dan de flexibiliteit hebt om het later wel anders te doen zonder eerst de hele code aan te passen. Of wat denk je van een subklasse die wat bijzonders willen doen?
Voorbeeld:
Code (php)
Hier is Foo readonly, en de subclass wordt alleen aangemaakt als Foo voor de eerste keer aangeroepen wordt.
Dus:
Code (php)
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
<?php
$test = new Test; // maakt alleen Test, maar geen Foo;
$bar = $test->Foo; // zal object Foo aanmaken en teruggeven
$foobar = $test->Foo; // zal object Foo teruggeven
$test->Foo = 1; // foutmelding
?>
$test = new Test; // maakt alleen Test, maar geen Foo;
$bar = $test->Foo; // zal object Foo aanmaken en teruggeven
$foobar = $test->Foo; // zal object Foo teruggeven
$test->Foo = 1; // foutmelding
?>
Gewijzigd op 27/08/2012 20:05:32 door John Berg