• De afgelopen dagen zijn er meerdere fora waarop bestaande accounts worden overgenomen door spammers. De gebruikersnamen en wachtwoorden zijn via een hack of een lek via andere sites buitgemaakt. Via have i been pwned? kan je controleren of jouw gegeven ook zijn buitgemaakt. Wijzig bij twijfel jouw wachtwoord of schakel de twee-staps-verificatie in.

[PHP]Callback functions maken

Status
Niet open voor verdere reacties.
P

pderaaij

Gast
*
[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:

In computer programming, a callback is executable code that is passed as an argument to other code. It allows a lower-level software layer to call a function defined in a higher-level layer.

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.

Een callback function is een function die wordt aangeroepen op het moment dat er een event plaatsvindt. Je weet niet wanneer dit event wordt aangeroepen en hebt daarmee ook geen controle over de aanroep. Dus vertel je de computer dat de callback function moet worden uitgevoerd wanneer een bijzonder event plaatsvindt.

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($callback, false, $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:
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 door een moderator:
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:
Status
Niet open voor verdere reacties.
Steun Ons

Nieuwste berichten

Terug
Bovenaan