Scriptorama.nl

Header image showing a keyboard, mouse, laptop and books on design patterns

PHP-next: wat zit er aan te komen?

Het is alweer een tijdje geleden dat PHP 5.3 uit kwam. De verwachting was dat hierna PHP 6.0 met de langverwachte unicode ondersteuning afgemaakt zou worden. Echter lijkt het er op dat we daar nog even op zullen moeten wachten. De ontwikkelaars hebben zich in eerste instantie gefocussed op het ontwikkelen van andere interessante features, waarbij een PHP 5.4 op de korte termijn waarschijnlijker is dan PHP 6.0. In deze post vind je een paar features die inmiddels zijn toegevoegd aan PHP.

Traits

Traits is een systeem waarmee je code 'horizontaal kunt hergebruiken'. Dat is niet heel erg duidelijk. Concreet komt het er op neer dat je een losse class met enkel methods (oftewel: een Trait) definieërt en dat je deze dan gebruikt in de definitie van andere classes.

Vanaf dat moment kun je alle methodes die je in de trait hebt gedefinieërd gebruiken in die classes - zonder dat je ervoor hoeft te zorgen dat deze class op een of andere manier voorkomt in de inheritance tree. Dat houdt de code wel zo simpel en de inheritance tree schoon.

Een klein voorbeeld:

PHP:
  1. trait Logger
  2. {
  3.         public function logMessage($message)
  4.         {
  5.                 echo '[', date(DATE_RSS), '] ', $message, PHP_EOL;
  6.         }
  7. }
  8.  
  9. class ImplementatieA { use Logger; }
  10. class ImplementatieB { use Logger; }
  11.  
  12. $a = new ImplementatieA;
  13. $a->logMessage("Ik werk op class ImplementatieA");
  14.  
  15. $b = new ImplementatieB;
  16. $b->logMessage("... maar ook op ImplementatieB");
  17.  
  18. /* Output:
  19. [Sun, 26 Sep 2010 10:51:01 +0200] Ik werk op class ImplementatieA
  20. [Sun, 26 Sep 2010 10:51:01 +0200] ... maar ook op ImplementatieB
  21. */

In deze code zie je dus de trait Logger gedefinieërd worden. Vervolgens definieër ik ook 2 losse, niet gerelateerde, classes (ze komen niet voor in elkaars inheritance trees) waarin de Trait gebruikt wordt. Vervolgens kan ik op instanties van beide classes de methode logMessage aanroepen die in de trait gedefinieërd is.

Een paar dingen om op te letten:

  • In een Trait kun je alleen methoden definieëren, geen properties.
  • Een Trait is niet instantieerbaar; je kunt geen object maken van een Trait definitie.
  • Als je een Trait gebruikt aan het einde van een inheritance tree en een Trait gebruikt welke een method definieërt die al voorkomt, dan zal deze overschreven worden door de methode uit de Trait.
  • Definieërt een class die een Trait gebruikt -zelf- een methode die ook voorkomt in de Trait, dan zal de methode uit de -class- voorrang hebben en is de methode uit de Trait niet benaderbaar.

Een compleet overzicht van de Trait functionaliteit vind je in het PHP RFC: Horizontal Reuse Wiki document.

Closures kunnen met $this werken

Zoals je weet kunnen we sinds PHP 5.3 closures gebruiken in onze code (meer weten over closures in PHP 5.3?). Maar daar ontbrak nog 1 ding aan en dat is het gebruik van closures in een OO context. Er was namelijk een logica probleem bij de afhandeling van $this waardoor er voor PHP 5.3 besloten is om het gebruik van $this simpelweg uit te sluiten. Inmiddels is het alsnog -deels- geimplementeerd.

In de volgende versie van PHP wordt het mogelijk om $this te gebruiken binnen een closure. Op deze manier kan je op een encapsulated manier bijvoorbeeld private gegevens gebruiken in een methode die later aangeroepen moet worden:

PHP:
  1. <?php
  2.  
  3. class Event
  4. {
  5.         protected $_event_name = "WRONG EVENT NAME";
  6.  
  7.         public function getHandler()
  8.         {
  9.                 return function($message)
  10.                 {
  11.                         echo '[', $this->_event_name, '] ', $message, PHP_EOL;
  12.                 };
  13.         }
  14.  
  15.         public function changeName($name) { $this->_event_name = $name; }
  16.  
  17. }
  18.  
  19. $event = new Event;
  20.  
  21. $handler = $event->getHandler();
  22. $handler("I've discovered something AMAZING.");
  23.  
  24. $event->changeName("PROPER EVENT NAME");
  25. $handler("I've discovered something amazing! AGAIN!");
  26.  
  27. // Output:
  28. // [WRONG EVENT NAME] I've discovered something AMAZING.
  29. // [PROPER EVENT NAME] I've discovered something amazing! AGAIN!

Zoals je ziet gebruiken we in de methode Event::getHandler() de protected variabele Event::$_event_name. Wanneer de handler wordt aangeroepen wordt deze netjes uitgelezen zonder dat er een foutmelding wordt gegeven. Misschien denk je dat dit de encapsulatie breekt, maar dat is niet het geval. Om dit te laten werken zul je zo'n closure moeten definieëren binnen de class definitie - wat feitelijk weer betekent dat encapsulatie gewoon intact is.

Wanneer we vervolgens de protected variabele veranderen op de originele instantie en de handler closure nogmaals aanroepen blijkt tevens dat de closure daadwerkelijk die protected property uitleest want de nieuwe waarde wordt netjes getoond.

De manier waarop dit werkt is dat er intern bij een closure een scope wordt opgeslagen. Bij de closure die Event::getHandler() maakt wordt dus een referentie naar de instantie van Event ( $event ) opgeslagen zodat $this daar heen gemapped kan worden. Een interessante extra feature is dat we deze bound scope ook zelf kunnen veranderen middels de Closure::bind() en Closure::bindTo methodes:

PHP:
  1. class Mens
  2. {
  3.         protected $_naam;
  4.  
  5.         public function getNamePrinter()
  6.         {
  7.                 return function() { echo $this->_naam, PHP_EOL; };
  8.         }
  9. }
  10.  
  11. class Man extends Mens { protected $_naam = 'Mathieu'; }
  12. class Vrouw extends Mens { protected $_naam = 'Lotje'; }
  13.  
  14. $m = new Man;
  15. $v = new Vrouw;
  16.  
  17. $printer = $m->getNamePrinter();
  18. $printer(); // Mathieu
  19.  
  20. $printer = $printer->bindTo($v);
  21. $printer(); // Lotje
  22.  
  23. $printer = Closure::bind($m, $printer);
  24. $printer(); // Mathieu

Een ding dat specifiek niet geimplementeerd is, is de mogelijkheid om (public) properties waar een closure aan staat toegewezen direct aan te roepen alsof het een methode is - Javascript-style OO - zeg maar. Het volgende levert dus gewoon een foutmelding op:

PHP:
  1. // Leidt tot een foutmelding.
  2. $instance->method = function() { echo "Hi"; };
  3. $instance->method();

Wil je echt het fijne weten over wat er zoal mogelijk is met deze toevoeging? Lees dan dit artikel van Gustavo Lopes, een van de ontwikkelaars die dit mogelijk heeft gemaakt.

Verbeterde command-line interface

Ik heb al eens eerder geschreven over hoe handig een interactive commandline kan zijn en over dat de interactive shells voor PHP gebruikers nog niet geweldig zijn.

In PHP 5.4 zit een verbeterde versie van de interactive shell, met een paar erg handige features:

PHP:
  1. // Start de interactive shell met de -a optie
  2. mathieuk@MatbookPro bin  $ ./php54 -a
  3.  
  4. // Feature 1: Een Fatal error stopt het programma niet!
  5. php> niet_bestaande_functie();
  6. Fatal error: Call to undefined function niet_bestaande_functie() in php shell code on line 1
  7. php>
  8.  
  9. // Feature 2: Wijzig snel configuratie opties:
  10. php> echo date('Y-m-d H:i:s');
  11. 2010-09-26 11:04:42
  12.  
  13. php> #date.timezone=GMT
  14. php> echo date('Y-m-d H:i:s');
  15. 2010-09-26 09:05:14
  16.  
  17. // Feature 3: Custom prompt, zelfs met PHP code (zet in PHP.ini om permanent te maken):
  18. php> #cli.prompt=`echo php_uname('n'), ' ', date('[H:i:s]'), '> ';`
  19. MatbookPro.local [11:06:56]> echo 'Hello world!';
  20. Hello world!
  21. MatbookPro.local [11:07:02]>

Verder wordt er door het gebruik van de readline library nog een paar handige dingen toegevoegd: zoals history (zelfs tussen verschillende sessies) en het gebruik van verschillende toetscombinaties om door je regel te springen (Ctrl-A springt naar het begin, Ctrl-E springt naar het eind, etc.).

Scalar type-hinting

Een veel gevraagde feature. Met type-hinting kun je op belangrijke plaatsen forceren dat een variabele van een bepaald type is. Je kon al type-hinting gebruiken voor objecten, maar niet voor scalar variabelen. Dat is nu ook geimplementeerd:

PHP:
  1. function test_array(array $a) { }
  2. function test_string(string $a) { }
  3.  
  4. test_array("a");
  5. /*
  6. Catchable fatal error: Argument 1 passed to test_array() must be of the type array, string given, called in
  7. /Users/mathieuk/programs/php/bin/t4.php on line 7 and defined in /Users/mathieuk/programs/php/bin/t4.php on line 3
  8. */
  9.  
  10. test_string(array());
  11.  
  12. /*
  13. Catchable fatal error: Argument 1 passed to test_string() must be of the type string, array given, called in /Users/mathieuk/programs/php/bin/t4.php on line 8 and defined in /Users/mathieuk/programs/php/bin/t4.php on line 4
  14. */

Array dereferencing

Een veel gevraagde feature is de mogelijkheid om een return value van een functie of methode, direct te kunnen benaderen. Zonder dat je daar eerst nog een tussen variabele voor hoeft te maken. Deze feature zit momenteel ook in php-trunk:

PHP:
  1. echo explode(' ', 'mathieu kooiman')[0];
  2.  
  3. // Output: mathieu

Zelf proberen?

Je kunt alle bovenstaande features direct zelf proberen, maar je zult PHP wel zelf moeten compileren. Een recente versie van PHP-trunk die je direct kunt compileren vind je op de PHP snapshots website.

Conclusie

Als je zelf op de hoogte wil blijven van de interessante wijzigingen in de PHP taal, maar geen zin in de volledige mailinglist? Lees dan regelmatig het NEWS bestand uit de PHP broncode. Dit kan je doen door via de ViewVC website van PHP het NEWS bestand uit de trunk te bekijken:

http://svn.php.net/viewvc/php/php-src/trunk/NEWS?view=markup

Uiteraard zijn dit niet alle nieuwe toevoegingen en het is ook nog niet 100% zeker dat al deze features het ook daadwerkelijk gaan halen in de eerst volgende versie van PHP of wanneer dat dan ongeveer gaat gebeuren, maar duidelijk is wél dat er weer behoorlijk gesleuteld wordt!

Reageer ook!

Hier zitten een paar leuke dingen tussen...

Traits! Ha ik miste al jaren iets aan de classes opzet, om uit werkende elementen een class op te bouwen. Die ga ik zeker gebruiken.

Skalar type-hinting dat is zeker goed en ik absoluut proberen om me dat aan te wennen, komt ook zeker in de requirements lijst voor nieuwe code bij ons. Kleine moeite.

Array dereferencing, nou, ja, doet me wel erg denken aan de ternary operator, niet zo heel netjes maar het zal wel eens verdomd goed uitkomen. Al was het maar in php golf, en preg_replace('//e').

Namespaces, cli, backslash, dat sla ik over.

Namespaces zijn al beschikbaar in PHP5.3
Ik gebruik ze al een tijdje naar tevredenheid, alleen die backslash as operator is echt te belachelijk voor worden.

Ik zie dat safe-mod en register globals nu ook eindelijk gaan verdwijnen!

Leave a comment
Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>