1. Deze website gebruikt cookies en vergelijkbare technologie├źn. Dat houdt in dat wij het gedrag van bezoekers verzamelen, gebruiken en met derden delen om u een optimale gebruikerservaring te bieden. Cookies van derden kunnen worden gebruikt om advertenties te tonen die aansluiten op uw interesses. Door deze website verder te gebruiken, gaat u akkoord met ons gebruik van cookies. Leer Meer.

[PHP]Callback functions maken

Discussie in 'Webdesign' gestart door pderaaij, 23 aug 2006.

Topicstatus:
Niet open voor verdere reacties.
  1. pderaaij

    pderaaij Guest

    *
    [hand]
    *
    Inleiding
    In deze tutorial ga ik je laten zien hoe je gebruikt maakt van callback functions en wat hier het nut van kan zijn.

    Ik ga er wel van uit dat je vrij sterk bent in OOP PHP programmeren. Deze techniek is trouwens ook alleen praktisch in gebruik op het moment dat je in OOP programmeert.

    Wat zijn callbacks?
    Als we even een uitstapje naar WikiPedia maken komen we het volgende tegen:

    Had ik al gezegd dat het goed kunnen lezen en begrijpen van Engels een vereiste is om goed te leren programmeren. Nee? hierbij dan...

    Nu vind ik deze omschrijving maar een beetje zo-zo. Ik zou het meer als volgt willen omschrijven.

    Javascripters hebben al gebruik gemaakt van een callback function. Wel eens gebruik gemaakt van de OnClick functie? Ja, ja dat is nou een callback. Denk maar even aan bovenstaande definitie. Weet jij wanneer een gebruiker op de knop drukt?

    Uitvoeren in PHP

    Voordat we het echt over callback functions gaan hebben eerst nog een trucje van php.

    Wist je namelijk dat je een variabele de naam van een functie kan geven en dan de variable als functie kan gebruiken.

    PHP:
    <?php
        $fRound 
    "round";
        
        echo 
    "Door middel van een variabele maken we van het getal 3.85 het afgeronde getal "$fRound(3.85) .". Zie je?";
    ?>
    Dit werkt ook met objecten.

    PHP:
    <?php
    class CPerson
    {
        function 
    sayHello($name)
        {
            echo 
    "Hello " $name "!";
        }
    }
        
        
    $mHallo "sayHello";
        
    $oJan = new CPerson();
        
    $oJan->$mHallo("Jan");
    ?>
    Het probleem met deze methode is alleen dat het script hierdoor erg onduidelijk wordt. Je ziet niet dan het object sayHello aanroept. Gelukkig heeft PHP zelf een transparantere methode om dit doel te bereiken. De functie call_user_func namelijk.


    call_user_func

    Laten we gelijk met een voorbeeld werken om te laten zien hoe dit werk.

    PHP:
    <?php
    class CPerson
    {
        public function 
    doYouSleep()
        {
            echo 
    "ZzzZzzzZzzzZzzzzzz<br>";
        }
        public static function 
    sayHello($name)
        {    
            echo 
    "Hello " $name "!<br>";
        }
    }

    $oJan = new CPerson();

    call_user_func(array($oJan"doYouSleep"));
    call_user_func(array(CPerson"doYouSleep"));
    call_user_func(array($oJan"sayHello"), "Jan");
    ?>
    We hebben de functies even in een class gestopt maar hierdoor verandert de aanroep van de call_user_func. De eerste parameter wordt een array waarbij de eerste key het object moet zijn en de tweede key de functie.

    Overigens zie je dat de tweede aanroep direct naar de class is. In deze situatie is dat geen probleem aangezien we geen gebruik maken van variabelen. In het volgende voorbeeld is dat wel een probleem zoals je kan zien.

    PHP:
    <?php
    class CPerson
    {
        private 
    $_sName;
        
        public function 
    __construct()
        {
            
    $this->_sName "Jan";
        }
        
        public function 
    doYouSleep()
        {
            print 
    $this->_name " does sleep<br>";
            echo 
    "ZzzZzzzZzzzZzzzzzz<br><br>";
        }
        public static function 
    sayHello($name)
        {    
            echo 
    "Hello " $name "!<br>";
        }
    }

    $oJan = new CPerson();

    call_user_func(array(CPerson"doYouSleep"));
    call_user_func(array($oJan"sayHello"), "Jan");
    ?>
    Probeer deze code maar eens uit te voeren en voila... je krijgt een mooie fatal error zeggend: "Fatal error: Using $this when not in object context in C:\wamp\www\softcity\call_object_var.php on line 13". Wat is nu het probleem?

    Als je kijkt naar de functie doYouSleep dan zie je dat we de naam willen weergeven die opgeslagen is in de _name variabele van het huidige object. Alleen roepen we de class direct aan en niet een object die de _name variabele heeft ingevuld. De $this pointer wijst nu naar niets!

    Dit is ook niet op te lossen. Je kunt deze aanroepen alleen maken naar functies die static zijn.

    Goed de basistheorie zit er in. Op naar een werkend en praktisch voorbeeld dus.


    De praktijk

    Okay, we gaan een heel simpel voorbeeldje maken die laat zien hoe je de callback functie kan gebruiken in een real life applicatie.

    Eerst de code:

    PHP:
    <?php
    class CClub
    {
        
    // club members
        
    private $_sClubName;
        private 
    $_sClubDivision;
        public 
    $_aPlayers;
        
        
    // callback variables
        
    private $_onLoad;
        private 
    $_isLoaded false;
        
        function 
    __construct($sClubName$sClubDivision)
        {
            
    $this->_sClubName $sClubName;
            
    $this->_sClubDivision $sClubDivision;
            
    $this->_aPlayers    = array();
            
            
    $this->setLoadCallBack('_showPlayers'$this);
        }
        
        function 
    addPlayer(CPlayer $oPlayer)
        {
            
    $this->_aPlayers[] = $oPlayer;
        }
        
        function 
    printClubInfo()
        {
            echo 
    $this->_sClubName " speelt in de " $this->_sClubDivision ."<br>";
        }
        
        function 
    printClubPlayers()
        {
            
    $this->_checkCallback();
            echo 
    $this->_sClubName " heeft " $this->length() . " spelers in huis.<br>";
        }
        
        public function 
    setLoadCallBack($functionName$objOrClass null)
        {
            if(
    $objOrClass)
            {
                
    $callback = array($objOrClass$functionName);
            }
            else
            {
                
    $callback $functionName;
            }
            
            if(!
    is_callable($callbackfalse$callableName))
            {
                throw new 
    Exception("$callableName is not callable as a parameter to onLoad");
                return 
    false;
            }
            
            
    $this->_onLoad $callback;
        }
        
        private function 
    _checkCallback()
        {
            if(isset(
    $this->_onLoad) && !$this->_isLoaded)
            {
                
    $this->_isLoaded true;
                
    call_user_func($this->_onLoad$this);
            }
        }
        
        private function 
    _showPlayers()
        {
            echo 
    "Spelers aan het laden van " $this->_sClubName "<br>";
            
            
    $this->addPlayer(new CPlayer("Klaas-Jan Huntelaar"2));
            
    $this->addPlayer(new CPlayer("Danny Koevermans"4));
        }
        
        public function 
    length()
        {
            
    $this->_checkCallback();
            return 
    sizeof($this->_aPlayers);
        }
    }

    class 
    CPlayer
    {
        private 
    $_sName;
        private 
    $_iGoals
        
        function 
    __construct($sName$iGoals)
        {
            
    $this->_sName $sName;
            
    $this->_iGoals $iGoals;
        }
        
        public function 
    printPlayer()
        {
            echo 
    $this->_sName " heeft dit seizoen " $this->_iGoals ." gemaakt.<br><br>";
        }
    }

    $oAjax     = new CClub("Ajax""Eredivisie");
    $oAZ     = new CClub("AZ""Eredivisie");

    $oAjax->printClubInfo();
    $oAZ->printClubInfo();
    echo 
    "<br>";
    echo 
    "Bekijk de spelers van AJAX";
    $oAjax->printClubPlayers();

    echo 
    "<br><br>We hebben niets met de _aPlayers array van AZ gedaan dus is de callback function niet aangeroepen. Even controleren, het zou 0 moeten zijn<br>";
    echo 
    "Aantal AZ spelers: " count($oAZ->_aPlayers);

    echo 
    "<br>Nog eens proberen: ( oAZ->length() )<br>";
    echo 
    "Aantal AZ spelers: " $oAZ->length();
    ?>
    Ik heb twee classes gemaakt. De eerste is de CClub class. In deze class zitten nogal wat foefjes om de callback methode te laten werken. Ik neem alleen de stukken code door die met de callback methode te maken hebben.

    Als eerste maken we twee variabelen aan: $_onLoad en $_isLoaded. Deze worden gebruikt in twee functies. setLoadCallBack en _checkCallBack.

    De eerste functie setLoadCallBack voert een aantal controles uit om te kijken of de functie wel bestaat. Zoals je eerder hebt gelezen is er een verschil tussen een oproep voor een methode of voor een normale functie. Dat controleren we eerst dus. Als het een class is komt het in een array.

    Vervolgens controleren we of de callback function wel aan te roepen is. Zo niet dan volgt er een mooie exception en wordt de functie met false geretouneerd.

    Is alles in orde dan slaan we de functienaam op in de private var $_onLoad;

    De functie setLoadCallBack roepen we in de constructor van CClub aan met de volgende aanroep

    PHP:
    $this->setLoadCallBack('_showPlayers'$this);
    De eerste parameter is de callback functie, de tweede een verwijzing naar het huidige object.

    In de volgende functie, _checkCallBack(). controleren we eerst of de twee variabelen geldig zijn, zo niet dan betekent het dat de functie setLoadCallBack niet is aangeroepen. Als alles in order is dan laden we de functie en zetten we de flag op true zodat de data niet nogmaals geladen hoeft te worden.

    Deze functie is de sleutel tot het functioneren van deze callback methode. Deze bepaalt dat de callback function aangeroepen moet worden.

    In de body maken we twee objecten aan $oAjax en $oAZ. Vervolgens laten we wat basisinformatie op het scherm printen. Tot op dit moment zijn de spelers nog niet geladen en staan ze niet in het object.

    Op het moment dat we de spelers van $oAjax willen printen gebeurt er het volgende:

    -> Aanroep naar printClubPlayers()
    -> Aanroep naar _checkCallBack()
    -> Aanroep naar _showPlayers();
    -> Printen van laadbericht
    -> Toevoegen spelers
    -> Terugval naar _checkCallBack()
    -> Terugval naar printClubPlayers()
    -> Printen van aantal spelers
    -> Terugval naar body.

    Pas op het moment dat je wat met de spelers informatie gaat doen worden ze pas geladen.

    Dat laten we zien door het aantal spelers van $oAZ te tellen. Dat is 0. Tellen we met onze eigen functie is het aantal 2, want dit is weer een handeling met de spelers dus moeten die geladen worden.


    Tot slot

    Dit was mijn verhaal over callback functions. Ik hoop dat je er iets aan gehad heb en dat je ziet hoe je het in je eigen applicatie kan gebruiken. Als je nog vragen stel ze dan op het forum.

    [/hand]
    *
     
    Laatst bewerkt door een moderator: 14 sep 2006
  2. coda

    coda Vaak hier

    Netjes (Y), Duidelijk uitgelegd.

    Ik wist helemaal niet dat deze methode van scripten bestond in PHP. Begrijp het script (na het even uitgepluist te hebben) wel, maar begrijp niet helemaal wat de meerwaarde is door het op deze manier te doen. Moet er zelf nog even er mee gaan spelen dus ;)

    Tnx voor de tut, dat word weer een nachtje koffie, testen en debuggen :cool:

    PS: Mischien even erbij zetten dat dat de code in de tut geschreven is voor > PHP 5 :unsure:
     
    Laatst bewerkt: 8 sep 2006
  3. pderaaij

    pderaaij Guest

    Ik denk dat als je van __construct, function CClub() maakt en private/public var dit ook in de laatste PHP4 versies werkt.

    De meerwaarde van dit systeem is om zonder al te veel extra code heel veel overhead te besparen.

    Je kunt dus een forum schrijven met een class voor de berichten. Als je de forum index oproept hoef je alleen maar de titel en auteur te hebben. Pas als je het bericht in gaat de rest. Normaal gesproken zou je dan heel veel methodes extra moeten te schrijven nu hoeft dat niet.

    Daarnaast is dit systeem het beste te gebruiken in combinatie met een Collection object, misschien dat ik daar ook nog wel een tutorial over schrijf.






    ~~~~~~~~~~​

    *mod-edit* : Omdat we na overzetting van tijdelijke --> vaste handleidingen, tutorialtopics sluiten, kunnen evtle reacties op en/of vragen over deze tut, verder gesteld worden in het vragengedeelte van het forum, i.c. bij Web-designing (verwijs er dan even bij naar deze tutorial). :)
    Ben je de TS en wil je evtl wat aanpassen, toevoegen, .. contacteer dan even iemand van het NCF-team

    *
     
    Laatst bewerkt door een moderator: 14 sep 2006
Topicstatus:
Niet open voor verdere reacties.

Deel Deze Pagina