Tutorials

Object Overloading

Over het manipuleren van de meest standaard acties van een klasse, namelijk het toewijzen en uitlezen van een variabele en het uitvoeren van een method. (PHP5 versie)

Pagina 1

Het verschil

Normaal gesproken definieer je een klasse met vaste methodes en interne variabelen. Een variabele vraag je dan op via $object->variabele. Stel dat je een klasse hebt die zijn gegevens uit de database haalt, en zodra er iets verandert aan deze waarden deze wijzigingen weer door wil voeren in de database. Dit zou alleen kunnen door een method te maken die kijkt of er iets verandert is en deze method zou weer handmatig moeten worden aangeroepen. Met object overloading kan je controle krijgen over deze handel. Je kan namelijk je eigen methods definiëren die het toewijzen en het ophalen van de variabelen gaan regelen. Nu heeft de klasse het direct in de gaten waneer er een van zijn variabelen wordt opgeroepen en kan hij zo nodig actie ondernemen.

Of stop je je [php]mysql_fetch_row[/php]-uitvoer in een array in het object, maar heb je geen zin om alsmaar $object->results->userId op te roepen?
Pagina 2

__get()

Precies bijhouden welke variabelen er worden opgevraagd? Definieer een method genaamd __get($key). Als argument ($key) krijgt deze de naam van de variabele die je uit wilt lezen.
echo $object->variabele1001;
kan dan worden gezien als
echo $object->__get(‘variabele1001’);
.

Waar je vervolgens de waarde vandaan halt mag je helemaal zelf weten! Dit kan dus een array zijn, $this->waarden[$key], of een echt bestaande variabele van het object, $this->$key, of zelfs databaseuitvoer!
Uiteraard geef je de waarde vervolgens terug via [php]return[/php].

[voorbeeld 1]
<?php
class Voorbeeld {

/*
* deze variabele mogen we niet van buitenaf,
* berijken maar wel via klassen die
* voortborduren op dit voorbeeld
*/
protected $waarden = array();


public function __construct()
{

$this->waarden['iets'] = 'aap';

}

public function __get($key)
{
echo "debug: variabele '$key' werd opgevraagd\n";
return isset($this->waarden[$key]) ? $this->waarden[$key] : 'bestaat niet!';
}
}

$object = new Voorbeeld();
echo $object->iets;
//geeft 'aap'

echo $object->fictieveVariabele;
//geeft 'bestaat niet!'

?>

Wat je ook meteen kunt zien in [voorbeeld 1] is dat je ook zelf kan bepalen wat je terug geeft waneer de variabele niet bestaat. Soms is het nuttig om in plaats van een foutmelding een lege array terug te geven, of juist meteen een fatal error. Dat is puur afhankelijk van de functie van de klasse.
Pagina 3

__set()

__set() is bijna hetzelfde verhaal, maar dan anders. __set($key, $value) heeft twee argumenten, een naam van de variabele en de waarde die deze moet krijgen. Ik denk dat een voorbeeld de rest wel verteld.
[voorbeeld 2]
<?php
//we breiden ons vorige voorbeeld even uit
class Voorbeeld2 extends Voorbeeld {
public function __construct()
{
//roep Voorbeeld::__construct() aan.
parent::__construct();
}

public function __set($key, $value)
{
echo "debug: variabele '$key' wordt overschreven met waarde '$value'\n";

// is de variabele al eens eerder geset?
if(isset($this->waarden[$key])) {
$this->waarden[$key] .= $value; //voeg dan de waarde toe
} else {
$this->waarden[$key] = $value; //maak een entry in de array
}
}
}

$object = new Voorbeeld2();
echo $object->nieuweWaarde; //geeft 'bestaat niet!';

$object->nieuweWaarde = 'Hello';
echo $object->nieuweWaarde; //geeft 'Hello';

$object->nieuweWaarde = ' World';
echo $object->nieuweWaarde; //geeft 'Hello World';
?>
Pagina 4

__isset() & __unset()

Ik neem aan dat dit gedeelte wel redelijk voor zichzelf spreekt. __isset() is als het ware een 'wrapper' voor [php]isset[/php]() en __unset() voor [php]unset[/php]().
[voorbeeld 3]
<?php
class Voorbeeld3 extends Voorbeeld2 {
public function __construct()
{
//roep Voorbeeld2::__construct() aan.
parent::__construct();
}

public function __isset($key) {
echo "debug: kijken of '$key' wel geset is\n";
return isset($this->waarden[$key]);
}

public function __unset($key) {
echo "debug: vernietigen van '$key'\n";
unset($this->waarden[$key]);
}
}

$object = new Voorbeeld3();
echo isset($object->iets) ? 'is geset' : 'is niet geset';
//geeft 'is geset', immers in Voorbeeld::__construct()

$object->iets = ' anders';
echo $object->iets;
//geeft 'iets anders', immers door Voorbeeld2::__set() is ' anders' toegevoegd.

unset($object->iets);
echo $object->iets;
//geeft 'bestaat niet!'
?>
Pagina 5

__call()

__call() is in feite hetzelfde als __get(), maar dan voor het aanroepen van functies. __call($methodName, $arguments) krijgt 2 argumenten mee. De naam van de method die je aanroept, en een array met argumenten die je bij je aanroep hebt meegegeven. De uitvoer van de functie geef je terug via [php]return[/php].
[voorbeeld 6]
<?php
class Voorbeeld6 {
public function __call($methodName, $arguments)
{
echo "Ik vraag $method aan met deze argumenten:";
print_r($arguments);
return 'Ik ben nep!';
}

public function ikBestaWel()
{
return 'Zie je wel!';
}
}

$object = new Voorbeeld6();
echo $object->blablaNepMethod('schaap', 'boer', 'pinuing');
/*
* geeft 'Ik vraag blablaNepMethod aan met deze argumenten:
* Array (
* [0] => 'schaap',
* [1] => 'boer',
* [2] => 'pinuing'
* )
* Ik ben nep (return-waarde immers)
*/

echo $object->ikBestaWel();
/*
* zal 'zie je wel!' geven, en geen 'Ik vraag ...', het is een
* public functie dus zal niet langs __call() gaan.
*/

Voorbeeld6::blablaNepMethod('iets');
/*
* zal een error opleveren, want static wordt overloading niet gebruikt.
* Meer hierover op de volgende pagina.
*/
?>
Pagina 6

Dingen om rekening mee te houden

Let op dat als een publieke waarde met dezelfde naam in het object gedefinieerd is, dat deze niet meer langs de overload-functies komt!
[voorbeeld 4]
<?php
class Voorbeeld4 extends Voorbeeld {
public $waardeX = 'All your base are belong to us!';

public function __construct()
{
//roep Voorbeeld::__construct() aan.
parent::__construct();
}
}

$object = new Voorbeeld4();
echo $object->waardeX;
/*
* waneer hij de overload-method zou pakken,
* zou hij 'bestaat niet!' geven. Hij geeft
* echter 'All your base are belong to us!',
* en er zal ook geen debug-regeltje verschijnen.
*/
?>

Ook werkt overloading niet waneer je statisch de variabelen opvraagt. Later bij __call() zullen we zien dat dat ook hierbij het geval is.
[voorbeeld 5]
<?php
error_reporting(E_ALL);
echo Voorbeeld::$iets;
//dit zal een melding opleveren.
?>

Uiteraard werkt overloading ook niet intern in de klasse, anders zou bij __set() deze functie zichzelf altijd aanroepen, en zo in een lus terecht komen.

overload-methods moeten net als __construct & __destruct de vlaggen 'public' en niet 'static' meekrijgen.
Pagina 7

Mogelijke toepassing

Ik zelf gebruik nu overloading in mijn Viewport-klassen. Even in het klein:
<?php
class ViewportObject {
public function __render($template)
{
ob_start();
include($template);
return ob_get_clean();
}
}


class Viewport {
protected $viewportObject;
protected $templateFile;

public function __construct()
{
$this->viewportObject = new ViewportObject;
}

public function __set($key, $value)
{
$this->viewportObject->$key = $value;
}

public function setTemplateFile($file)
{
if(is_readable($file)) {
$this->templateFile = $file;
}
}

public function render()
{
echo $this->viewportObject->__render($this->templateFile);
}
}

$viewport = new Viewport();

$viewport->variabele = 'Hello World';
//nu wijs ik dus eigenlijk deze variabele toe aan de instance van ViewportObject.

echo method_exists($viewport, 'setTemplateFile') ? 'bestaat' : 'bestaat niet';
//geeft als het goed is 'bestaat'

$viewport->setTemplateFile('template');
$viewport->render();
?>

Met als template

<p><?php echo $this->variabele; ?></p>
<p>
   <span>Als het goed is levert deze op 'bestaat niet'</span>
   <span><?php echo method_exists($this, 'setTemplateFile') ? 'bestaat' : 'bestaat niet'; ?></span>
</p>


Hiermee heb ik dus simpele templates in PHP, een beetje orde in mijn variabelen, en geen toegang tot methods van mijn viewport die ik niet hoor te hebben vanuit mijn template.

Reacties

0
Nog geen reacties.