Op 1 maart 2012 berichtte het PHP development team vol trots dat PHP 5.4 stable beschikbaar werd gesteld! Geweldig nieuws! Direct na de tweet van PHP ging ik naar de changelog pagina van PHP om te zien wat er precies vernieuwd is!

De dingen die me meteen opvielen waren:

  • Added support for Traits.
  • Added built-in web server that is intended for testing purpose.

Er zijn natuurlijk veel meer wijzigingen om heel erg enthousiast over te worden, maar deze twee sprongen er met kop en schouders boven uit. Over het gebruik van de ingebouwde webserver wil ik het niet gaan hebben, wellicht in een ander artikel als hier behoefte aan is. Wel wil ik graag gaan bespreken wat “Traits” precies zijn en wat je er mee kan doen, het scheelt veel mensen namelijk veel dubbele code!

Wat is “Trait” en waar kan ik het mee vergelijken?
Een trait kan je vergelijken met een abstracte class, waarvan je geen instantie kan maken op zich zelf (toch wordt het ook wel vergeleken met een interface). Ik vergelijk traits het liefste met een DLL file, kortom een verzameling van functies (library) De PHP documentatie bericht het volgende over een trait:

Traits is a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies.

Ok, duidelijk toch? Traits zijn dus bedoeld om het “erf” limiet van een class te vergroten. In PHP is namelijk niet mogelijk om meer dan één class te erven (inherit/extends). In het volgende voorbeeld kan ik misschien laten zien wat ik hiermee bedoel:

<?php
class MySQLReader extends ConnectToDB
{
}

class FileReader extends IOStats
{
}
?>

Hier boven staan twee classes (MySQLReader en FileReader) welke beide zijn gekoppeld aan een andere class om functies te erven van die class. Echter is het probleem als deze twee classes ook gebruik willen maken van bepaalde “standaard” functies, bijv. singleton patterns. PHP ondersteund namelijk geen zogenaamde “multiple inheritance”, dus elke class zou de standaard functies aanwezig moeten hebben (dubbele code). Je zou ook een extend op een extend kunnen doen, maar dan krijg je een inherit hiërarchie waar niemand meer wat van snapt uiteindelijk, dus dat doen we liever niet. Maar goed, als we dus de Singleton pattern willen inladen in beide classes, biedt een trait echt de uitkomst (uiteraard ook voor andere standaard functionaliteit). Hieronder staat het bovengenoemde voorbeeld nog een, maar dan met een trait en de Singleton pattern er in.

<?php
trait Singleton
{
private static $instance;

public static function getInstance()
{
if(!(self::$instance instanceof self))
{
self::$instance = new self;
}
return self::$instance;
}
}

class cMySQLReader extends cConnectToDB
{
use Singleton;
}

class cFileReader extends cIOStats
{
use Singleton;
}
?>

Zoals je hierboven kan zien kunnen dus beide classes extended zijn aan een andere class en hier dus functies van erven, maar ook standaard functionaliteit inladen door middel van een trait. De Singleton trait bevat één static functie (getInstance()) met een één op één implementatie van de Singleton Pattern. Deze functie maakt een object van de class die de trait gebruikt en return’t deze. In het volgende voorbeeld proberen we een object te maken van de classes door middel van getInstance().

<?php
$cMySQLReader = cMySQLReader::getInstance();
$cFileReader = cFileReader::getInstance();

var_dump($cMySQLReader);  //object(cMySQLReader)
var_dump($cFileReader);  //object(cFileReader)
?>

We kunnen zien dat $cMySQLReader een object is van cMySQLReader en $cFileReader van cFileReader, maar ze gedragen zich nu als singletons. De functie in de trait Singleton is dus één op één geërfd (gekopieerd) in de beide classes door middel van het de syntax “use”.

Met subclassing (extends) wordt de property static getInstance() niet laten zien in een dump, echter met een trait wel (alsof het zo hoort).

Meer dan één trait gebruiken?! Wow!
Ok, traits vergroten dus het erf limiet van een class, maar met één maar met oneindig veel! Je kan dus zoveel traits inladen als je zelf wilt. Een simpel voorbeeld wat ik kan laten zien om dit duidelijk te krijgen is het het volgende:

<?php
trait Hallo
{
function zegHallo()
{
echo "Hallo";
}
}

trait Wereld
{
function zegWereld()
{
echo "Wereld";
}
}

class cBasic
{
use Hallo, Wereld;
}

$cBasic = new cBasic();
echo "{$cBasic->zegHallo()} {$cBasic->zegWereld()}"; // Hallo Wereld
?>

Hierboven hebben we twee traits namelijk “Hallo” en “Wereld”. De trait “Hallo” kan alleen “Hallo” zeggen en de trait “Wereld” alleen “Wereld” door middel van een echo. In de cBasic class maken gebruik van beide trait en dus bevat de class cBasic ineens de methodes van de beide traits. Door beide methodes aan te spreken in cBasic kan de class dus “Hallo Wereld” zeggen.

Een trait gemaakt van traits! Ja ja, dat kan!
Als een applicatie aan het groeien is kan het voorkomen dat bepaalde “setjes” traits gebruikt worden door bepaalde classes. PHP 5.4 staat het toe om traits te maken van andere traits, zodat we maar één trait in een class hoeven te gebruiken in plaats van heel veel. Ik gebruik hiervoor het voorbeeld dat ik hierboven heb aangegeven:

<?php
trait HalloWereld
{
use Hallo, Wereld;
}

class cBasic
{
use HalloWereld;
}

$cBasic = new cBasic();
echo "{$cBasic->zegHallo()} {$cBasic->zegWereld()}"; // Hallo Wereld
?>

Ik heb een trait gemaakt genaamd “HalloWereld”, die gebruikt maakt van de trait “Hallo” en “Wereld” en deze gebruikt in de class “cBasic”. Aangezien de methodes geërfd worden in de trait “HalloWereld” is de uitkomst het zelfde, maar dan met minder traits achter de use syntaxt in de cBasic class.

Ik ben nog bezig met dit artikel, maar toch zet ik hem vast online omdat het artikel de beginselen al bevat en ik denk dat deze al aardig nuttig zijn.