Tutorials
Magic Methods
Magic Methods in PHP 5: hoe werken ze en wat kun je ermee? Ook zeer nuttige uitleg van o.a. de Singleton design pattern bij de pagina __construct.
Pagina 1
Inleiding
Zoals jullie weten is object georienteerd programmeren (OOP) in PHP 5 helemaal opnieuw ontworpen door de makers van PHP en is er daardoor heel veel functionaliteit bijgekomen. Ik wil het in deze turorial hebben over de magic methods die PHP biedt.
Magic methods zijn methods (functies binnen een klasse) die bijzonder zijn. Ze worden namelijk aangeroepen als er iets gebeurt. Bijvoorbeeld als er een instance van een class wordt gemaakt of als er een bepaalde functie wordt aangeroepen binnen een script.
We beginnen op de volgende pagina met de waarschijnlijk meest gebruikte en handigste magic method: __construct. Vervolgens ga ik ze stap voor stap af en leg ik uit hoe ze werken en waarom ze handig zijn in de praktijk. Want daar draait het uiteindelijk om.
Magic methods zijn methods (functies binnen een klasse) die bijzonder zijn. Ze worden namelijk aangeroepen als er iets gebeurt. Bijvoorbeeld als er een instance van een class wordt gemaakt of als er een bepaalde functie wordt aangeroepen binnen een script.
We beginnen op de volgende pagina met de waarschijnlijk meest gebruikte en handigste magic method: __construct. Vervolgens ga ik ze stap voor stap af en leg ik uit hoe ze werken en waarom ze handig zijn in de praktijk. Want daar draait het uiteindelijk om.
Pagina 2
__construct
Hoe en wat
De functie __construct (ook wel de constructor genoemd) kan worden gedefinieerd binnen een class. De functie wordt aangeroepen wanneer er een instance van deze class wordt gemaakt. Ik zal direct een voorbeeld geven om het duidelijk te maken.
<?php
class PHPhulp {
public function __construct() {
echo 'Er is een instance van PHPhulp gemaakt!';
}
}
$instance = new PHPhulp;
?>
Bovenstaand script zal als output hebben:
Je kunt ook één of meerdere arguments meegeven aan __construct, net als bij iedere normale method binnen een class. Voorbeeld:
<?php
class PHPhulp {
public function __construct($printmij) {
echo $printmij;
}
}
$instance = new PHPhulp('PHPerik');
?>
Bovenstaand script zal als output hebben:
In de praktijk
In de praktijk wordt deze functie heel veel gebruikt en is het handig om zo bij het instantiëren van de klasse al een aantal instellingen te setten. Zo kun je bijvoorbeeld een functie aanroepen die een bepaalde variabele vult. Weer een voorbeeld:
<?php
class PHPhulp {
public $status;
public function __construct($naam) {
$this->status = $this->bepaalStatus($naam);
}
private function bepaalStatus($naam) {
if ($naam == 'PHPerik' || $naam == 'Bas Kreleger' /* etc */) {
return 'phphulpteam';
}
else {
return 'phphulpuser';
}
}
}
$instance = new PHPhulp('PHPerik');
?>
Het script heeft nu aan de hand van de parameter $naam bepaald dat ik PHPerik ben en dus lid ben van het PHPhulp team. Dit is opgeslagen in $this->status. Zo kun je dus direct al een belangrijke variabele setten aan de hand van een function. Zo kun je nog veel meer toepassingen bedenken voor construct.
Private construct & Singleton Design Pattern
Als je een constructor private maakt, dan kun je geen instantie van een klasse meer maken buiten de klasse zelf. Nu denk je: wat heb je dan nog aan die klasse? Nou; veel. Het is soms wenselijk dat een instantie van een klasse maar één keer aangemaakt kan worden. Bijvoorbeeld bij een database-klasse. Om dit te doen kun je een instantie van de klasse maken door een static function te gebruiken, zodat de instantie binnen de klasse zelf wordt gemaakt. Dit design pattern heet Singleton. Hier een voorbeeld:
<?php
class DB {
private static $db_instantie;
private $connectie;
private function __construct() {
$this->connectie = mysql_connect();
}
public static function getInstance() {
if (is_null(self::$db_instantie)) {
self::$db_instantie = new DB();
}
return self::$db_instantie;
}
}
$db = DB::getInstance();
?>
Ik zal dit even nader toelichten. Zoals je ziet roep je de statische functie getInstance() aan met DB::getInstance(). Deze functie bepaalt vervolgens of er al een instantie is van de klasse. Als die instantie er als is, dan bevat $db_instantie dus een waarde. Zoals je ziet controleert de functie of er een
waarde is met is_null(). Als $db_instantie inderdaad NULL is, dan maakt de functie een nieuwe instantie van de klasse aan en wordt er een mysql_connect() uitgevoerd door de constructor van de klasse. Als de $db_instantie echter niet NULL is, dan returnt hij de $db_instantie die reeds bestaat.
In de praktijk is dit handig omdat je niet wil dat er meerdere connecties naar een database gemaakt worden. Op deze manier kun je makkelijk met includes werken die allemaal zelf de DB-klasse aanroepen. Als er namelijk al connectie is, dan returnt hij die instantie, en als er nog geen connectie is dan maakt hij een nieuwe connectie/instantie. Je hebt dus nooit last van twee resources en je hoeft ook niet te letten op waar je wel en niet de DB klasse instantieert; de klasse regelt het namelijk zelf.
Let wel op dat bovenstaande klasse slechts een deel is van een normale database-klasse. Je zal binnen deze klasse namelijk nog je functies voor select, insert, etc, moeten definiëren. Singleton wordt dus toegepast binnen een class, het is niet een aparte class op zich.
Parent constructors
Als je een klasse aanroept die een parent-class heeft, dan wordt alleen de constructor van de child-class aangeroepen.
<?php
class A {
public function __construct() {
echo 'Foo';
}
}
class B extends A {
public function __construct() {
echo 'Bar';
}
}
$instance = new B;
?>
Bovenstaand script zal als output hebben:
Je kunt echter de parent constructor wel aanroepen.
<?php
class A {
public function __construct() {
echo 'Foo';
}
}
class B extends A {
public function __construct() {
parent::__construct();
echo 'Bar';
}
}
$instance = new B;
?>
Bovenstaand script zal als output hebben:
De functie __construct (ook wel de constructor genoemd) kan worden gedefinieerd binnen een class. De functie wordt aangeroepen wanneer er een instance van deze class wordt gemaakt. Ik zal direct een voorbeeld geven om het duidelijk te maken.
<?php
class PHPhulp {
public function __construct() {
echo 'Er is een instance van PHPhulp gemaakt!';
}
}
$instance = new PHPhulp;
?>
Bovenstaand script zal als output hebben:
Er is een instance van PHPhulp gemaakt!Je kunt ook één of meerdere arguments meegeven aan __construct, net als bij iedere normale method binnen een class. Voorbeeld:
<?php
class PHPhulp {
public function __construct($printmij) {
echo $printmij;
}
}
$instance = new PHPhulp('PHPerik');
?>
Bovenstaand script zal als output hebben:
PHPerikIn de praktijk
In de praktijk wordt deze functie heel veel gebruikt en is het handig om zo bij het instantiëren van de klasse al een aantal instellingen te setten. Zo kun je bijvoorbeeld een functie aanroepen die een bepaalde variabele vult. Weer een voorbeeld:
<?php
class PHPhulp {
public $status;
public function __construct($naam) {
$this->status = $this->bepaalStatus($naam);
}
private function bepaalStatus($naam) {
if ($naam == 'PHPerik' || $naam == 'Bas Kreleger' /* etc */) {
return 'phphulpteam';
}
else {
return 'phphulpuser';
}
}
}
$instance = new PHPhulp('PHPerik');
?>
Het script heeft nu aan de hand van de parameter $naam bepaald dat ik PHPerik ben en dus lid ben van het PHPhulp team. Dit is opgeslagen in $this->status. Zo kun je dus direct al een belangrijke variabele setten aan de hand van een function. Zo kun je nog veel meer toepassingen bedenken voor construct.
Private construct & Singleton Design Pattern
Als je een constructor private maakt, dan kun je geen instantie van een klasse meer maken buiten de klasse zelf. Nu denk je: wat heb je dan nog aan die klasse? Nou; veel. Het is soms wenselijk dat een instantie van een klasse maar één keer aangemaakt kan worden. Bijvoorbeeld bij een database-klasse. Om dit te doen kun je een instantie van de klasse maken door een static function te gebruiken, zodat de instantie binnen de klasse zelf wordt gemaakt. Dit design pattern heet Singleton. Hier een voorbeeld:
<?php
class DB {
private static $db_instantie;
private $connectie;
private function __construct() {
$this->connectie = mysql_connect();
}
public static function getInstance() {
if (is_null(self::$db_instantie)) {
self::$db_instantie = new DB();
}
return self::$db_instantie;
}
}
$db = DB::getInstance();
?>
Ik zal dit even nader toelichten. Zoals je ziet roep je de statische functie getInstance() aan met DB::getInstance(). Deze functie bepaalt vervolgens of er al een instantie is van de klasse. Als die instantie er als is, dan bevat $db_instantie dus een waarde. Zoals je ziet controleert de functie of er een
waarde is met is_null(). Als $db_instantie inderdaad NULL is, dan maakt de functie een nieuwe instantie van de klasse aan en wordt er een mysql_connect() uitgevoerd door de constructor van de klasse. Als de $db_instantie echter niet NULL is, dan returnt hij de $db_instantie die reeds bestaat.
In de praktijk is dit handig omdat je niet wil dat er meerdere connecties naar een database gemaakt worden. Op deze manier kun je makkelijk met includes werken die allemaal zelf de DB-klasse aanroepen. Als er namelijk al connectie is, dan returnt hij die instantie, en als er nog geen connectie is dan maakt hij een nieuwe connectie/instantie. Je hebt dus nooit last van twee resources en je hoeft ook niet te letten op waar je wel en niet de DB klasse instantieert; de klasse regelt het namelijk zelf.
Let wel op dat bovenstaande klasse slechts een deel is van een normale database-klasse. Je zal binnen deze klasse namelijk nog je functies voor select, insert, etc, moeten definiëren. Singleton wordt dus toegepast binnen een class, het is niet een aparte class op zich.
Parent constructors
Als je een klasse aanroept die een parent-class heeft, dan wordt alleen de constructor van de child-class aangeroepen.
<?php
class A {
public function __construct() {
echo 'Foo';
}
}
class B extends A {
public function __construct() {
echo 'Bar';
}
}
$instance = new B;
?>
Bovenstaand script zal als output hebben:
BarJe kunt echter de parent constructor wel aanroepen.
<?php
class A {
public function __construct() {
echo 'Foo';
}
}
class B extends A {
public function __construct() {
parent::__construct();
echo 'Bar';
}
}
$instance = new B;
?>
Bovenstaand script zal als output hebben:
FooBarPagina 3
__destruct
Hoe en wat
Naast de constructor heb je ook de destructor. De destructor kun je ook in de klasse definiëren en wordt aangeroepen als een instantie van een klasse wordt verwijderd (met unset()) en alle references naar deze instantie ook. Als de instantie niet verwijderd is dan wordt de destructor altijd aan het einde van de script-execution aangeroepen. Onthoud goed: ook al verwijder je een instantie van een klasse, maar er bestaan nog references naar die instantie, dan wordt de destructor nog niet aangeroepen.
Voorbeeld:
<?php
$a = new foo();
$b = $a;
unset($a); // dit is NIET genoeg. er is nog een reference naar $a in $b.
unset($b); // nu wordt wel de destructor uitgevoerd
/* Note: in PHP 5 worden objecten ALTIJD by reference aan elkaar doorgegeven en nooit gekopieerd. als je een object wil kopieëren dan zul je de operator clone moeten gebruiken. meer hierover op één van de volgende pagina's */
?>
In de praktijk
In de praktijk is een destructor vaak handig om troep op te ruimen. Hiermee bedoel ik; stel je hebt een database-connectie, dan kun je de destructor de connectie automatisch laten sluiten. Want de destructor wordt na de PHP execution uitgevoerd, dus als alle code al is geparsed en uitgevoerd.
LET OP: als een destructor van een andere klasse gebruikt maakt van een database-connectie, dan moet je NIET de destructor gebruiken om de database-connectie te sluiten. Het kan dan namelijk zo zijn dat de database-destructor éérst wordt aangeroepen en daarna pas de destructor van de andere klasse. Gevolg; die andere destructor kan niet meer met de database praten. Ik vind zelf overigens dat je geen database-connecties moet gebruiken in destructors, mede vanwege bovenstaande reden.
Wat je dus wel altijd zeker weet is dat een destructor altijd wordt uitgevoerd op een bepaald moment, alleen het is niet altijd te voorspellen wanneer precies vanwege de references en aangemaakte objecten.
Naast de constructor heb je ook de destructor. De destructor kun je ook in de klasse definiëren en wordt aangeroepen als een instantie van een klasse wordt verwijderd (met unset()) en alle references naar deze instantie ook. Als de instantie niet verwijderd is dan wordt de destructor altijd aan het einde van de script-execution aangeroepen. Onthoud goed: ook al verwijder je een instantie van een klasse, maar er bestaan nog references naar die instantie, dan wordt de destructor nog niet aangeroepen.
Voorbeeld:
<?php
$a = new foo();
$b = $a;
unset($a); // dit is NIET genoeg. er is nog een reference naar $a in $b.
unset($b); // nu wordt wel de destructor uitgevoerd
/* Note: in PHP 5 worden objecten ALTIJD by reference aan elkaar doorgegeven en nooit gekopieerd. als je een object wil kopieëren dan zul je de operator clone moeten gebruiken. meer hierover op één van de volgende pagina's */
?>
In de praktijk
In de praktijk is een destructor vaak handig om troep op te ruimen. Hiermee bedoel ik; stel je hebt een database-connectie, dan kun je de destructor de connectie automatisch laten sluiten. Want de destructor wordt na de PHP execution uitgevoerd, dus als alle code al is geparsed en uitgevoerd.
LET OP: als een destructor van een andere klasse gebruikt maakt van een database-connectie, dan moet je NIET de destructor gebruiken om de database-connectie te sluiten. Het kan dan namelijk zo zijn dat de database-destructor éérst wordt aangeroepen en daarna pas de destructor van de andere klasse. Gevolg; die andere destructor kan niet meer met de database praten. Ik vind zelf overigens dat je geen database-connecties moet gebruiken in destructors, mede vanwege bovenstaande reden.
Wat je dus wel altijd zeker weet is dat een destructor altijd wordt uitgevoerd op een bepaald moment, alleen het is niet altijd te voorspellen wanneer precies vanwege de references en aangemaakte objecten.
Pagina 4
__sleep & __wakeup
__sleep wordt aangeroepen bij het gebruik van serialize() op een object. __wakeup wordt aangeroepen bij het gebruik van unserialize() op een object. Het is belangrijk goed te begrijpen wat serialize() en unserialize() doen om het nut van __sleep & __wakeup te begrijpen.
(un)serialize()
Serialize() is een functie die een variabele, waaronder een array of object, kan omzetten naar een string. Deze string kun je dan bijvoorbeeld opslaan. Als je deze string dan later weer unserialize()t, dan heb je weer de originele variabele in PHP. Voorbeeld:
<?php
$array = array('a', 'b', 'c');
echo serialize($array);
?>
Bovenstaand script zal als output hebben:
Als ik nu deze string in een ander scriptje handmatig invoer en unserialize(), dan kan ik weer gewoon m'n $array gebruiken:
<?php
$string = 'a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}';
$array = unserialize($string);
print_r($array);
?>
Bovenstaand script zal als output hebben:
Back to magic
Ik denk dat je nu wel goed begrijpt hoe deze functies werken. Je kunt dit dus ook toepassen op een object. Zo kun je later een object opnieuw gebruiken met dezelfde instellingen en variabele-waarden binnen de klasse van het object. Omdat objecten nogal ingewikkelde datatypes zijn, heeft men in PHP 5 dus ook twee magic methods toegevoegd die aangeroepen worden bij het omzetten van object naar string. Dit heeft als nut om bijvoorbeeld je klasse op te schonen zodat bijvoorbeeld bepaalde variabelen worden teruggezet op standaard-waarden.
__sleep
__sleep wordt aangeroepen bij een serialize(), mits je __sleep hebt gedefinieerd binnen de klasse. Als je deze functie gebruikt dan moet je een array returnen met de namen van de variabelen die je opgeslagen wil hebben in je geserializede string. Als je geen waarde returnt dan wordt een E_NOTICE gegooid.
<?php
class Foo {
public $a, $b, $c;
public function __construct() {
$this->a = 'foobar';
$this->b = 'raboof';
$this->c = array('a', 'b', 'c');
}
public function __sleep() {
$this->c = array('d', 'e', 'f');
return array('b', 'c'); // $b en $c moeten geserialized worden
}
}
$foo = new Foo;
echo serialize($foo);
?>
__wakeup
__wakeup wordt aangeroepen bij unserialize() en is bijvoorbeeld handig om een database-connectie te maken.
Praktijkvoorbeeld van PHP.net
<?php
class Connection {
protected $link;
private $server, $username, $password, $db;
public function __construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}
private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}
public function __sleep()
{
return array('server', 'username', 'password', 'db');
}
public function __wakeup()
{
$this->connect();
}
}
?>
(un)serialize()
Serialize() is een functie die een variabele, waaronder een array of object, kan omzetten naar een string. Deze string kun je dan bijvoorbeeld opslaan. Als je deze string dan later weer unserialize()t, dan heb je weer de originele variabele in PHP. Voorbeeld:
<?php
$array = array('a', 'b', 'c');
echo serialize($array);
?>
Bovenstaand script zal als output hebben:
a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}Als ik nu deze string in een ander scriptje handmatig invoer en unserialize(), dan kan ik weer gewoon m'n $array gebruiken:
<?php
$string = 'a:3:{i:0;s:1:"a";i:1;s:1:"b";i:2;s:1:"c";}';
$array = unserialize($string);
print_r($array);
?>
Bovenstaand script zal als output hebben:
Array ( [0] => a [1] => b [2] => c )Back to magic
Ik denk dat je nu wel goed begrijpt hoe deze functies werken. Je kunt dit dus ook toepassen op een object. Zo kun je later een object opnieuw gebruiken met dezelfde instellingen en variabele-waarden binnen de klasse van het object. Omdat objecten nogal ingewikkelde datatypes zijn, heeft men in PHP 5 dus ook twee magic methods toegevoegd die aangeroepen worden bij het omzetten van object naar string. Dit heeft als nut om bijvoorbeeld je klasse op te schonen zodat bijvoorbeeld bepaalde variabelen worden teruggezet op standaard-waarden.
__sleep
__sleep wordt aangeroepen bij een serialize(), mits je __sleep hebt gedefinieerd binnen de klasse. Als je deze functie gebruikt dan moet je een array returnen met de namen van de variabelen die je opgeslagen wil hebben in je geserializede string. Als je geen waarde returnt dan wordt een E_NOTICE gegooid.
<?php
class Foo {
public $a, $b, $c;
public function __construct() {
$this->a = 'foobar';
$this->b = 'raboof';
$this->c = array('a', 'b', 'c');
}
public function __sleep() {
$this->c = array('d', 'e', 'f');
return array('b', 'c'); // $b en $c moeten geserialized worden
}
}
$foo = new Foo;
echo serialize($foo);
?>
__wakeup
__wakeup wordt aangeroepen bij unserialize() en is bijvoorbeeld handig om een database-connectie te maken.
Praktijkvoorbeeld van PHP.net
<?php
class Connection {
protected $link;
private $server, $username, $password, $db;
public function __construct($server, $username, $password, $db)
{
$this->server = $server;
$this->username = $username;
$this->password = $password;
$this->db = $db;
$this->connect();
}
private function connect()
{
$this->link = mysql_connect($this->server, $this->username, $this->password);
mysql_select_db($this->db, $this->link);
}
public function __sleep()
{
return array('server', 'username', 'password', 'db');
}
public function __wakeup()
{
$this->connect();
}
}
?>
Pagina 5
__toString
__toString is een simpele magic method. Hij wordt aangeroepen als een object tot string omgevormd wordt. In de praktijk is dat bijvoorbeeld bij een echo.
Voorbeeld:
<?php
class Foo {
public function __toString() {
return 'Foo';
}
}
$foo = new Foo;
echo $foo;
?>
Bovenstaand script zal als output hebben:
Als je geen __toString definieert
Als je een object echo't zonder dat de klasse een __toString() functie kent, dan krijg je:
SimpleXML
SimpleXML gebruikt dit principe ook. SimpleXML maakt namelijk voor elke Node een apart object. Zo kan je bijvoorbeeld iets krijgen als $SimpleXML->bibliotheek->boek->titel. Je kunt nu ook een Node echo-en en dan krijg je de waarde van de Node, doordat __toString() is gedefinieerd. Maar je kunt de Node ook weer gebruiken om dieper te graven. DOM gebruikt dit ook.
Voorbeeld:
<?php
class Foo {
public function __toString() {
return 'Foo';
}
}
$foo = new Foo;
echo $foo;
?>
Bovenstaand script zal als output hebben:
FooAls je geen __toString definieert
Als je een object echo't zonder dat de klasse een __toString() functie kent, dan krijg je:
Catchable fatal error: Object of class A could not be converted to string in /locatie/X.php on line Y
SimpleXML
SimpleXML gebruikt dit principe ook. SimpleXML maakt namelijk voor elke Node een apart object. Zo kan je bijvoorbeeld iets krijgen als $SimpleXML->bibliotheek->boek->titel. Je kunt nu ook een Node echo-en en dan krijg je de waarde van de Node, doordat __toString() is gedefinieerd. Maar je kunt de Node ook weer gebruiken om dieper te graven. DOM gebruikt dit ook.
Pagina 6
__clone
De operator clone
In PHP 4 was het zo dat als je een bestaand object toewees aan een nieuwe variabele, dat in de nieuwe variabele een kopie van dit object zat. Dat is echter niet logisch. Bekijk het volgende voorbeeld.
<?php
class Foo {
public $Boek;
}
$a = new Foo;
$a->Boek = 'Harry Potter en de 7 dwergen';
$b = $a;
$b->Boek = 'Harry Potter en de 7 PHPhulpers';
echo $a->Boek;
?>
Output PHP 4:
Output PHP 5:
Dit verschil is te verklaren doordat PHP 4 een kopie van $a opslaat in $b, en PHP 5 een referentie van $b naar $a maakt. Dat wil zeggen dat $a en $b in PHP 4 eigenlijk helemaal los van elkaar staan, terwijl in PHP 5 $a en $b eigenlijk dezelfde variabele is. Een reference dus.
Als je echter in PHP 5 een object wil kopiëren, in plaats van er naar wil verwijzen, dan is er de operator clone beschikbaar in PHP 5. Dus:
<?php
class Foo {
public $Boek;
}
$a = new Foo;
$a->Boek = 'Harry Potter en de 7 dwergen';
$b = clone $a;
$b->Boek = 'Harry Potter en de 7 PHPhulpers';
echo $a->Boek;
?>
Bovenstaand script zal als output hebben:
Magic method __clone
Als een object gekloond wordt met clone, dan wordt zijn magische functie __clone() aangeroepen, mits deze is gedefinieerd.
<?php
class Foo {
public $Boek;
public function __clone() {
$this->Boek = 'Harry Potter en de 7 PHPhulpers';
}
}
$a = new Foo;
$a->Boek = 'Harry Potter en de 7 dwergen';
$b = clone $a;
echo $b->Boek;
?>
Bovenstaand script zal als output hebben:
Zoals je ziet is __clone() een soort constructor voor het nieuwe object. De wijzigingen die __clone() aanbrengt gelden voor het nieuwe object en niet voor het oude object. Dus $a blijft in bovenstaand geval ongewijzigd, terwijl $b->Boek een nieuwe waarde heeft gekregen.
In PHP 4 was het zo dat als je een bestaand object toewees aan een nieuwe variabele, dat in de nieuwe variabele een kopie van dit object zat. Dat is echter niet logisch. Bekijk het volgende voorbeeld.
<?php
class Foo {
public $Boek;
}
$a = new Foo;
$a->Boek = 'Harry Potter en de 7 dwergen';
$b = $a;
$b->Boek = 'Harry Potter en de 7 PHPhulpers';
echo $a->Boek;
?>
Output PHP 4:
Harry Potter en de 7 dwergenOutput PHP 5:
Harry Potter en de 7 PHPhulpersDit verschil is te verklaren doordat PHP 4 een kopie van $a opslaat in $b, en PHP 5 een referentie van $b naar $a maakt. Dat wil zeggen dat $a en $b in PHP 4 eigenlijk helemaal los van elkaar staan, terwijl in PHP 5 $a en $b eigenlijk dezelfde variabele is. Een reference dus.
Als je echter in PHP 5 een object wil kopiëren, in plaats van er naar wil verwijzen, dan is er de operator clone beschikbaar in PHP 5. Dus:
<?php
class Foo {
public $Boek;
}
$a = new Foo;
$a->Boek = 'Harry Potter en de 7 dwergen';
$b = clone $a;
$b->Boek = 'Harry Potter en de 7 PHPhulpers';
echo $a->Boek;
?>
Bovenstaand script zal als output hebben:
Harry Potter en de 7 dwergenMagic method __clone
Als een object gekloond wordt met clone, dan wordt zijn magische functie __clone() aangeroepen, mits deze is gedefinieerd.
<?php
class Foo {
public $Boek;
public function __clone() {
$this->Boek = 'Harry Potter en de 7 PHPhulpers';
}
}
$a = new Foo;
$a->Boek = 'Harry Potter en de 7 dwergen';
$b = clone $a;
echo $b->Boek;
?>
Bovenstaand script zal als output hebben:
Harry Potter en de 7 PHPhulpersZoals je ziet is __clone() een soort constructor voor het nieuwe object. De wijzigingen die __clone() aanbrengt gelden voor het nieuwe object en niet voor het oude object. Dus $a blijft in bovenstaand geval ongewijzigd, terwijl $b->Boek een nieuwe waarde heeft gekregen.
Pagina 7
__autoload
Het is vaak heel irritant en tijdrovend om steeds allemaal classes te moeten includen in je scripts. En in elk script weer een paar anderen. In PHP 5 is hier een oplossing voor: __autoload. In de functie __autoload(), die niet gedefinieerd wordt binnen een klasse maar juist in de procedural code, kun je instellen hoe alle gebruikte klasses automatisch worden geinclude. Het is heel simpel. Hier een voorbeeld van PHP.net:
<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
In dit geval wordt MyClass1 gebruikt en MyClass2. Automatisch worden hiervoor MyClass1.php en MyClass2.php ingeladen. Er zijn dus geen overbodige includes en het kost totaal geen tijd. Zoals in Java moeten in dit geval de klasse-namen gelijk zijn aan de bestandsnamen.
<?php
function __autoload($class_name) {
require_once $class_name . '.php';
}
$obj = new MyClass1();
$obj2 = new MyClass2();
?>
In dit geval wordt MyClass1 gebruikt en MyClass2. Automatisch worden hiervoor MyClass1.php en MyClass2.php ingeladen. Er zijn dus geen overbodige includes en het kost totaal geen tijd. Zoals in Java moeten in dit geval de klasse-namen gelijk zijn aan de bestandsnamen.
Pagina 8
__set_state
De magische functie __set_state is een iets ingewikkelder verhaal dan de andere magic methods. In PHP heb je de functie var_export(). Deze functie is vergelijkbaar met var_dump() (en print_r()), maar heeft als verschil dat deze PHP code uitspuugt die je dus weer kan hergebruiken. Het probleem is echter dat een object een heel complex datatype is en PHP niet precies snapt hoe hij dit moet doen met var_export(). De oplossing is __set_state.
Als je een object exporteert met var_export(), dan zal PHP dat voortaan in het volgende formaat teruggeven:
<?php
Class1::__set_state(array('var1' => 'value', 'var2' => 'value', ));
?>
Zoals je ziet is dit parse-bare code. Als je deze code uitvoert, dan zal namelijk de statische functie __set_state() van klasse Class1 worden uitgevoerd. De array binnen __set_state() bevat de namen van variabelen met hun waardes. Als je nu bovenstaande code weer uitvoert in PHP, dan werkt het gewoon en wordt de functie __set_state() aangeroepen. Als je deze gedefinieerd hebt binnen de klasse (in dit geval Class1), dan heb je dus geen errors. Je kunt dan dus ook eval() gebruiken op een var_export() zonder te hoeven vrezen dat er iets mis gaat.
Hier een voorbeeld van hoe je Class1 zou kunnen definiëren:
<?php
class Class1 {
public $var1, $var2;
public function __construct() {
// doe iets
}
// leuke functie hier en daar
public static function __set_state(array $vars) { // kan ook gewoon __set_state($vars)
$instance = new Class1;
$instance->var1 = $vars['var1'];
$instance->var2 = $vars['var2'];
return $instance;
}
}
$a = new Class1;
$a->var1 = 'foo';
$a->var2 = 'bar';
$export = var_export($a, true);
eval('$b=' . $export . ';');
echo $b->var1 . $b->var2;
?>
Bovenstaand script zal als output hebben:
Als je een object exporteert met var_export(), dan zal PHP dat voortaan in het volgende formaat teruggeven:
<?php
Class1::__set_state(array('var1' => 'value', 'var2' => 'value', ));
?>
Zoals je ziet is dit parse-bare code. Als je deze code uitvoert, dan zal namelijk de statische functie __set_state() van klasse Class1 worden uitgevoerd. De array binnen __set_state() bevat de namen van variabelen met hun waardes. Als je nu bovenstaande code weer uitvoert in PHP, dan werkt het gewoon en wordt de functie __set_state() aangeroepen. Als je deze gedefinieerd hebt binnen de klasse (in dit geval Class1), dan heb je dus geen errors. Je kunt dan dus ook eval() gebruiken op een var_export() zonder te hoeven vrezen dat er iets mis gaat.
Hier een voorbeeld van hoe je Class1 zou kunnen definiëren:
<?php
class Class1 {
public $var1, $var2;
public function __construct() {
// doe iets
}
// leuke functie hier en daar
public static function __set_state(array $vars) { // kan ook gewoon __set_state($vars)
$instance = new Class1;
$instance->var1 = $vars['var1'];
$instance->var2 = $vars['var2'];
return $instance;
}
}
$a = new Class1;
$a->var1 = 'foo';
$a->var2 = 'bar';
$export = var_export($a, true);
eval('$b=' . $export . ';');
echo $b->var1 . $b->var2;
?>
Bovenstaand script zal als output hebben:
foobarPagina 9
__get, __set, __call, __isset, __unset
Deze methods zijn bedoeld voor Object Overloading. Hier is al een uitgebreide tutorial over, geschreven door Jelmer.
Zie: http://www.phphulp.nl/php/tutorials/4/376/
Zie: http://www.phphulp.nl/php/tutorials/4/376/
Pagina 10
Links
magic methods
http://nl3.php.net/manual/en/language.oop5.magic.php
__construct / __destruct
http://nl3.php.net/manual/en/language.oop5.decon.php
__clone
http://nl3.php.net/manual/en/language.oop5.cloning.php
__autoload
http://nl3.php.net/manual/en/language.oop5.autoload.php
__set_state
http://hades.phparch.com/ceres/public/article/index.php/art::php5::magic_set_state_method
__get, __set, __call, __isset, __unset
http://www.phphulp.nl/php/tutorials/4/376/
http://nl3.php.net/manual/en/language.oop5.magic.php
__construct / __destruct
http://nl3.php.net/manual/en/language.oop5.decon.php
__clone
http://nl3.php.net/manual/en/language.oop5.cloning.php
__autoload
http://nl3.php.net/manual/en/language.oop5.autoload.php
__set_state
http://hades.phparch.com/ceres/public/article/index.php/art::php5::magic_set_state_method
__get, __set, __call, __isset, __unset
http://www.phphulp.nl/php/tutorials/4/376/
Reacties
0