Magic Methods

Door PHP erik, 21 jaar geleden, 7.032x bekeken

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.

Gesponsorde koppelingen

Inhoudsopgave

  1. Inleiding
  2. __construct
  3. __destruct
  4. __sleep & __wakeup
  5. __toString
  6. __clone
  7. __autoload
  8. __set_state
  9. __get, __set, __call, __isset, __unset
  10. Links

 

Er zijn 26 reacties op 'Magic methods'

PHP hulp
PHP hulp
0 seconden vanaf nu
 

Gesponsorde koppelingen
Gebruiker PHP
Gebruiker PHP
21 jaar geleden
 
0 +1 -0 -1
Erg mooie tutorial, alles goed uitgelegd en goede voorbeelden gebruikt!
Ultimatum
ultimatum
21 jaar geleden
 
0 +1 -0 -1
Hele mooie tutorial inderdaad. Vooral de __construct() is mooi uitgelegd, nu ga ik ook mooi singleton gebruiken op mijn database class, want is toch verdomt handig :p.

Ik snap alleen nog niet echt wat nou het voordeel van __sleep en __wakeup is. Vooral niet je laatste voorbeeld in een database class.
Arend a
Arend a
21 jaar geleden
 
0 +1 -0 -1
Zeer nette tutorial! Het niveau gaat nog eens omhoog hiero ;)
PHP erik
PHP erik
21 jaar geleden
 
0 +1 -0 -1
Quote:
Ik snap alleen nog niet echt wat nou het voordeel van __sleep en __wakeup is. Vooral niet je laatste voorbeeld in een database class.
Het kan wenselijk zijn een object op een later tijdstip opnieuw te gebruiken, met alle instellingen die al door een script zijn gemanipuleerd. Zo kun je je object extern opslaan en later weer aanroepen. Met een databaseclass is dit misschien vaak niet handig, maar het is niet nuttig om hier een heel complex script uit te leggen waar het wel heel nuttig is. Je komt ooit misschien wel in een situatie waarin je het nodig hebt, als je maar onthoudt hoe het werkt :)

edit: woordje "database" veranderd naar "databaseclass"
Jan Koehoorn
Jan Koehoorn
21 jaar geleden
 
0 +1 -0 -1
Goeie tutorial inderdaad.

Paar foutjes:

pagina 4:
bij het voorbeeld __sleep ontbreekt het woord 'function' in de constructor

pagina 5:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
<?php

class Foo {
    public function __toString() {
        echo 'Foo';
    }
}


$foo = new Foo;
echo $foo;
?>

moet zijn:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
10
11
12
<?php

class Foo {
    public function __toString() {
        return 'Foo';
    }
}


$foo = new Foo;
echo $foo;

?>

Pagina 8 bij het voorbeeld van __set_state
De eval functie levert een error op.
PHP erik
PHP erik
21 jaar geleden
 
0 +1 -0 -1
Thanks Jan, ik heb het aangepast. Die pagina 8 bekijk ik nog even nu.
Jelmer -
Jelmer -
21 jaar geleden
 
0 +1 -0 -1
Klein schoonheidsfoutje op de 2e pagina: In het laatste voorbeeld heb je 'echo' voor je aanroep naar de constructor van de parent staan, maar die constructor geeft niets terug :)

En nog een kleine waarschuwing voor __autoload: als je via class_exists() gaat vissen, zal __autoload() bij iedere klasse die op dat moment niet bestaat aangeroepen worden. Daarna wordt op de achtergrond class_exists() nog een keer aangeroepen om te kijken of hij na de aanroep op __autoload() wel bestaat. Hier heeft class_exists() overigens een 2e parameter voor, die kan je op false zetten waardoor __autoload() niet zal worden aangeroepen.

Mooie tutorial verder :D
PHP erik
PHP erik
21 jaar geleden
 
0 +1 -0 -1
Ok? alle genoemde foutjes zijn er uit. Thanks Jan en Jelmer.
Johan
Johan
21 jaar geleden
 
0 +1 -0 -1
Hele mooie tut! Lekker duidelijk, kan ik goed gebruiken bij de overstap naar OOP..

Netjes!!! :D
Kalle P
Kalle P
21 jaar geleden
 
0 +1 -0 -1
Wat een topgozer ben je erik. Doe de groeten aan kek.
Henk
Henk
21 jaar geleden
 
0 +1 -0 -1
Hee PHPerik! Supergoede tutorial! Helder, heerlijk, en duidelijk. Ik probeer het Singleton idee te snappen, maar het lukt niet. Stel ik heb een class Database { }, en ik heb een class Forum { }.

Hoe kan ik in de klasse Forum dan de database verbinding oproepen?
PHP erik
PHP erik
21 jaar geleden
 
0 +1 -0 -1
dan kun je ook gewoon DB::getInstance() gebruiken. Verder kun je in de DB-klasse gewoon al je databasefuncties defini?ren. Want getInstance() returnt gewoon een instantie van de hele klasse, zodat je dus een DB connectie hebt en er alles mee kunt doen.

Dus je kunt bijvoorbeeld in je Forum-class een variabele maken genaamd $dbinstance en dan zoiets:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?
class Forum {
   public $dbinstance;  

   public function __construct() {
      $this->dbinstance = DB::getInstance();
   }
}

?>


Zoals je ziet kun je dan gewoon $this->dbinstance gebruiken om de database-class te gebruiken.
Henk
Henk
21 jaar geleden
 
0 +1 -0 -1
Ah, oke. Bedankt, maar ik heb toch nog wat vraagjes. Want het volgende staat in je code:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?php
    public static function getInstance() {
        if (is_null(self::$connectie_resource)) {
            self::$connectie_resource = new DB();
        }

        
        return self::$connectie_resource;
    }

?>
Maar hier return je toch eigenlijk geen instantie? Of komt dat door de naam? Want bij een connectie_resource denk ik aan dat wat de funcie mysql_connect ( ) returnt. Moet dit niet database_instantie zijn of iets dergelijks?

En wat ik ook niet snap: je zegt dat je niet meerdere connecties open hebt, maar dat lijkt er wel op, omdat je in de functie getInstance een nieuwe instantie van DB aanmaakt. ( self::$connectie_resource = new DB();)

Ook geef je geen parameters mee in de constructor van db klasse maar dit is denk ik weggelaten omdat het irrelevant is. Want zou je niet eigenlijk newDB('localhost','user','pass''database'); doen?

Ook vind ik het eigenlijk raar dat je Database:: gebruikt. Want ??n van de voordelen van met klasses werken verdwijnt dan: meerdere connecties open hebben. Omdat je geen instantie gebruikt van de klasse Database, maar de klasse Database zonder instantie aan te maken gebruikt.

Ik zelf denk meer aan zoiets:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
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
29
30
31
32
<?php
    class Database
    {
        /*
         * Constructor
         */

        function __construct ( $sHost , $sUser , $sPass , $sDatabase )
        {

            $this->rConnectie = mysql_connect ( );
            mysql_select_db ( $sDatabase , $this->rConnectie );
        }
        
        function
getInstance ( )
        {

            return new self ( $this->sHost , $this->sUser , $this->sPass , $this->sDatabase );
        }
    }

    
    class Forum
    {
        var
$oDb;
        
        public function __construct ( )
        {

            $this->oDb = Database::getInstance ( );
            /*
             * Of
             */

            $this->oDb = $_GLOBALS [ 'oDb' ];
        }
    }

?>
Maar dan maak je alsnog een nieuwe instantie aan.

Ik ben heel erg in de war!
PHP erik
PHP erik
21 jaar geleden
 
0 +1 -0 -1
@Henk
Jouw script haalt juist het voordeel uit het script. De naam database_instantie zou inderdaad beter zijn dan connectie_resource, ik zal het zo aanpassen.

Kijk naar deze regel in mijn script:
self::$connectie_resource = new DB();
Deze propt een instantie in de variabele $connectie_resource.

Kijk nu naar:
is_null(self::$connectie_resource)
Hij controleert dus of de instantie al in deze variabele zit.

Daarna returnt hij sowieso die instantie, die dus opgeslagen zit in self::$connectie_resource.

Lees anders op internet een andere tutorial over Singleton, of bekijk het script nog een paar keer goed. Want het is eigenlijk een heel simpel principe en ik zie ook niet helemaal in wat je er niet aan begrijpt.

Jouw voorbeeld maakt juist meerdere database-instanties (en connecties) aan en dat is juist niet wenselijk. Je doet toch ook nooit twee keer mysql_connect() in je script?

Bedenk wel dat mijn voorbeeld (DB) slechts een deel van de hele class is. Je moet in werkelijkheid nog al je functies toevoegen aan de class DB. Hij is dus niet alleen bedoeld om te connecten.

Bekijk even opnieuw op de pagina __construct naar mijn wijzigingen in het script en de extra alinea onderin.
Henk
Henk
21 jaar geleden
 
0 +1 -0 -1
Ok? ik heb je aanpassing gelezen.

Nog een vraag: moet je in de constructor van class DB { } niet al
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
    self::$db_instantie = new DB();
?>
doen? Of misschien:
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
<?php
    self::$db_instantie = $this;
?>
Want nu maak je toch een nieuwe instantie van class DB { } aan als je voor het eerst DB::getInstance ( ) doet?

En ik snap ook niet hoe het werkt met de parameters die je meegeeft aan de constructor van de DB klasse. Want in mijn klasse moet je new DB('localhost','user','pass','database'); doen. Hoe gaat dat?
PHP erik
PHP erik
21 jaar geleden
 
0 +1 -0 -1
Als je new gebruikt, dan maak je een instantie aan van een klasse en wordt de constructor uitgevoerd. Als je DB::getInstance() doet, dan roep je een statische functie aan en wordt de constructor NIET uitgevoerd. De constructor wordt pas uitgevoerd bij new DB.

Je kunt in dit geval bijvoorbeeld arguments meegeven op deze manier: DB::getInstance($a, $b, $c); en deze dan weer doorgeven door:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
9
<?php
    public static function getInstance($a, $b, $c) {
        if (is_null(self::$db_instantie)) {
            self::$connectie_resource = new DB($a, $b, $c);
        }

        
        return self::$db_instantie;
    }

?>


Maar let er dus goed op: bij het aanroepen van de statische functie getInstance() wordt nog NIET de constructor uitgevoerd. Die wordt pas uitgevoerd bij de regel "self::$connectie_resource = new DB($a, $b, $c);"
Henk
Henk
21 jaar geleden
 
0 +1 -0 -1
Precies! Maar als je voor het eerst de functie DB::getInstance ( ) aanroept, dan is er dus nog geen (referentie naar een) instantie opgeslagen in "self::$connectie_resource", en dan wordt er dus een nieuwe instantie gemaakt met new. Dan heb je dus 2 connecties open.

Oftewel: je moet in de constructor van class DB { } ook al "self::$connectie_resource = new DB($a, $b, $c);" doen. Maar als je in de constructor al new doet, dan is het een oneindige loop?
Jelmer -
Jelmer -
21 jaar geleden
 
0 +1 -0 -1
Quote:
Dan heb je dus 2 connecties open.

Hoezo? Waneer heb je dan eerder de constructor aangeroepen? Bij mijn weten is er maar een aanroep naar deze constructor, er is maar 1 'new' statement, en dat is in getInstance.
Henk
Henk
21 jaar geleden
 
0 +1 -0 -1
Aha, je moet dus in je procedurele code niet meer new DB () doen, maar DB::getInstance?
Jelmer -
Jelmer -
21 jaar geleden
 
0 +1 -0 -1
Jep. Dat is de grap van die static dingen. Die zijn niet gebonden aan een instantie (en je kan dan ook geen $this binnen ze gebruiken)
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
$db = DB::getInstance();
$a = DB::getInstrance();

is vergelijkbaar met
Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
$instantie = new DB();
$db = $instantie;// of voor PHP4 oudjes $db = &$instantie;
$a = $instantie;
Henk
Henk
21 jaar geleden
 
0 +1 -0 -1
Maar je kan dan dus niet 1 keer $oDb = Database::getInstance ( 'localhost' , 'user' , 'pass' , 'database' ); doen, zonder dat je die gegevens later weer moet ingeven. Dat is waarom ze statisch zijn. En je kan dus ook niet een connectie hebben naar een database op de ene server, en een andere connectie naar een database op een andere server. Dat heet dus statisch.

Klopt dat?
Jelmer -
Jelmer -
21 jaar geleden
 
0 +1 -0 -1
Als je de constructor niet private maakt zou dat geen probleem moeten zijn. Je standaardverbinding maak je dan via getInstance, waarbij je bij de eerste keer aanroepen de gegevens meegeeft (daarna immers niet meer nodig, de instantie is dan gemaakt, en getInstance doet dan niets meer met die gegevens)

Voor de 2e verbinding gebruik je DB gewoon als een normaal object, via new DB, en laat je heel het getInstance verhaal achterwege.
Henk
Henk
21 jaar geleden
 
0 +1 -0 -1
Aha :-D Ik snap het! Dat is inderdaad best handig! Bedankt Jelmer en PHPerik!
Kees Schepers
kees Schepers
21 jaar geleden
 
0 +1 -0 -1
Nette tutorial Erik, voel me eigenlijk geroepen om ook zelf een keer iets te maken :p
Lode
Lode
20 jaar geleden
 
0 +1 -0 -1
Zeker goede Tut!!!

NB.
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.

voor debuggen kan je een echo in je __destruct(); gooien:

Code (php)
PHP script in nieuw venster Selecteer het PHP script
1
2
3
4
5
6
7
8
<?php
class schaap{

    public function __destruct(){
         __CLASS__.' -> '.__FUNCTION__.'<br/>'.PHP_EOL;
    }
}

?>


er is inderdaad geen logische volgorde met __destruct meestal...
Bij een Exception wordt __destruct(); al helemaal niet aan geroepen!

dit kan je eventueel omzeilen met register_shutdown_function();

Bij een database verbinding heb je geen __destruct(){}
nodig om de verbinding te sluiten! php parsed per view, is geen continu draaiend script. Dus verbinding zal zoiezo verbroken worden!
mits je mysql_pconnect(); gebruikt natuurlijk... (dan moet je echt ff het e.e.a. aanpassen, want pconnect is overkill 99,9%)

Verder ben je natuurlijk afhankelijk van je mysql/my en/of SET_SESSION_SQL_MODE instellingen hoelang threads bewaard worden...
PHP hulp
PHP hulp
0 seconden vanaf nu
 

Gesponsorde koppelingen
Cees Rijken
Cees Rijken
20 jaar geleden
 
0 +1 -0 -1
Dikke boeken doorgebladerd over magic methods, maar geen enkele was zo verhelderend als PHPEriks tutorial! Thumbs up!!!

Kwam er in de praktijk achter dat als ik self gebruik, dat in die class variabelen worden geschreven als self::$variabele, maar functies als self::functie (dus zonder $).....

Om te reageren heb je een account nodig en je moet ingelogd zijn.

Inhoudsopgave

  1. Inleiding
  2. __construct
  3. __destruct
  4. __sleep & __wakeup
  5. __toString
  6. __clone
  7. __autoload
  8. __set_state
  9. __get, __set, __call, __isset, __unset
  10. Links

Labels

  • Geen tags toegevoegd.

PHP tutorial opties

 
 

Om de gebruiksvriendelijkheid van onze website en diensten te optimaliseren maken wij gebruik van cookies. Deze cookies gebruiken wij voor functionaliteiten, analytische gegevens en marketing doeleinden. U vindt meer informatie in onze privacy statement.