PHP traits,   treat or threat?Nick BelhommeJanuary 28th, PHPBenelux Conference 2012, Belgium
Software Architect / Project LeadAuthor of theZend Framework 2.0 CookbookInternational Conference SpeakerContributor to va...
Market positions                   http://w3techs.com/
Market positions●   Python 0.3%
Market positions●   Ruby 0.6%●   Python 0.3%
Market positions●   Perl 1.0%●   Ruby 0.6%●   Python 0.3%
Market positions●   ColdFusion 1.2%●   Perl 1.0%●   Ruby 0.6%●   Python 0.3%
Market positions●   Java 1.2%●   ColdFusion 1.2%●   Perl 1.0%●   Ruby 0.6%●   Python 0.3%
Market positions●   ASP.NET 21.6%●   Java 1.2%●   ColdFusion 1.2%●   Perl 1.0%●   Ruby 0.6%●   Python 0.3%
Market positions●   PHP 77.4%●   ASP.NET 21.6%●   Java 1.2%●   ColdFusion 1.2%●   Perl 1.0%●   Ruby 0.6%●   Python 0.3%
PHP Market Position, 11 Jan 2012Used by high traffic sites                                              Java              ...
PHP Version Adoption    ●   PHP 5 93.9%    ●   PHP 4 6.1%    ●   PHP 3 < 0.1%    ●   PHP 6 < 0.1%
PHP 5 usage     ●   Version 5.2 73.8%     ●   Version 5.3 20.2%     ●   Version 5.1 5.8%     ●   Version 5.0 0.2%     ●   ...
Slow 5.3 Adoption?●   Shared hosting●   Companies refrain from updating stable    systems / distributions●   You
Adopt now●   More stability●   Security●   Better engine (ie garbage collection)●   New language features●   Cleaner code ...
PHP 5.4●   Array short syntax (javascript notation)●   Array dereferencing●   Class access on instantiation●   Indirect me...
Traits
ZF Use CaseZF Code base used is for illustration purposes only and do     –not-- express future possible implementations.Z...
namespace ZendView;class TemplatePathStack implements TemplateResolver {    public function setOptions($options = array())...
namespace ZendMvcRouter;class RouteBroker implements Broker{    public function setOptions($options) {       if (!is_array...
Problem: Code Duplicationif (!is_array($options) && !$options instanceof Traversable) {    throw new ExceptionInvalidArgum...
Solution in PHP5.4?             Multiple Inheritance●   Would provide the setOptions in all child    classes needed●   Int...
NOT an Option!     A better solution called Traits●   Would provide the setOptions in all classes that    use the trait●  ...
trait Options{      public function setOptions($options)      {           if (!is_array($options) && !$options instanceof ...
namespace ZendView;class TemplatePathStack implements TemplateResolver{   use Options;  public function setOption($key, $v...
namespace ZendMvcRouter;class RouteBroker implements Broker{    use Options;    public function setOption($key, $value) { ...
You have just seen traits
Lets dive deeperget your chest wet
namespace ZendLoader {use RuntimeException;   trait SplRegister {          public function register()          { spl_autol...
Precedence Order The precedence order is thatmembers from the current class   override Trait methods.
namespace ZendLoader {use RuntimeException;     class StandardAutoloader {        public function unregister() {          ...
Precedence OrderAn inherited member from abase class is overridden by a member inserted by a Trait
Distil Methods!
namespace ZendLoader {use RuntimeException;  class StandardAutoloader {     public function unregister() {       throw new...
insteadofCan be used to say which methodto use instead of the trait method.
Multiple Traits
namespace ZendFilter {  trait Locale {          protected $locale;            public function getLocale() { return $this->...
Multiple Conflicting Traits     The Diamond Problem(Not yours or a girls best friend!)
namespace ZendFilter {    trait Locale {          protected $locale;          public function getLocale() { return $this->...
Fatal error: Trait method getLocalehas not been applied, because there    are collisions with other trait               me...
Insteadofto the rescue
namespace ZendFilter {  trait Locale {          protected $locale;        public function getLocale()        public functi...
WIN!
BUTStrict Standards: ZendFilterLocale   and ZendFilterSecondLocaledefine the same property ($locale)         in the compos...
namespace ZendFilter {  trait Locale {           protected $locale = nl_BE;           public function getLocale()         ...
Fatal error: ZendFilterLocale andZendFilterSecondLocale define the   same property ($locale) in the composition of ZendFil...
namespace ZendFilter {  trait Locale {          static public $locale = en_US;          public function getLocale() { retu...
namespace ZendFilter {  trait Locale {          static public $locale = en_US;          public function getLocale() { retu...
Aliassing, useful to replace the       parent:: construct
namespace ZendMvcController {Use ...    trait StdlibDispatch {             public function dispatch(Request $request, Resp...
Aliasing could potentially be a    refactoring nightmare
namespace ZendView;class TemplatePathStack implementsTemplateResolver{    use Options {        Options::setOptions as setC...
Visibility
namespace ZendView;class TemplatePathStack implementsTemplateResolver{    use Options {Options::setOptions as protected}  ...
Interface compliance
namespace ZendView;trait Options {    public function setOptions($options) {         //set the options    }}interface Temp...
namespace ZendView;trait Options {    public function setOptions($options) { }}class TemplatePathStack {    use Options { ...
namespace ZendView;trait Options {    abstract public function setOptions($options);}class TemplatePathStack {    use Opti...
namespace ZendView;trait Options {    abstract public function setOptions($options);}class TemplatePathStack {    use Opti...
Magic Constants<?phptrait Id {     public function getClass() {         echo __CLASS__;     }     public function getTrait...
Autoloading<?phpnamespace NbeZfLoader{    class foo {        use Id;    }}namespace {    function __autoload($class) {    ...
Good things about                 traits●   Removes code duplication●   Helps keeping your code cleaner●   Maintainability...
Bad things    about traits●   Adds code complexity /    understandability with the    insteadof operator.●   Duck typing● ...
Benchmarks 5.3 vs 5.4●   ZF project: http://nickbelhomme.com●   Benchmark / Profiling tool: xhprof●   How: 2 VirtualBox Ma...
5.3.3●   Total Incl. Wall Time (microsec): 674,842 ms●   Total Incl. CPU (microsecs): 672,042 ms●   Total Incl. MemUse (by...
5.4.0RC3    Total Incl. Wall Time (microsec): 166,813 ms●   Total Incl. CPU (microsecs): 168,011 ms●   Total Incl. MemUse ...
Who runs PHP5.4 today for testing?
YOU WIN  6
Please rate my talkhttps://joind.in/4774
The End!                   THANK YOU          Please rate my talk          https://joind.in/4774contact@nickbelhomme.comSl...
Flickr Photo Credits●   Helico●   donkeyhotey●   jannem●   Thomas.constantin●   SJ photography●   davidagalvan●   stev.ie●...
Upcoming SlideShare
Loading in...5
×

PHP traits, treat or threat?

16,449

Published on

Published in: Technology
2 Comments
16 Likes
Statistics
Notes
  • Aliasing fails for abstract classes? (slide 63)
    Well, first, it is not about classes but methods in your example, and second, it does not fail.
    The important thing to realize here is that you introduce a new *additional* alias. That additional alias does not do anything to the other existing aliases. Thus, the class that gets finally composed still misses an implementation for the abstract method, and the resulting fatal error tells you exactly that.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Nice!
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
16,449
On Slideshare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
111
Comments
2
Likes
16
Embeds 0
No embeds

No notes for slide

PHP traits, treat or threat?

  1. 1. PHP traits, treat or threat?Nick BelhommeJanuary 28th, PHPBenelux Conference 2012, Belgium
  2. 2. Software Architect / Project LeadAuthor of theZend Framework 2.0 CookbookInternational Conference SpeakerContributor to various Open Source ProjectsFreelance PHP Consultant
  3. 3. Market positions http://w3techs.com/
  4. 4. Market positions● Python 0.3%
  5. 5. Market positions● Ruby 0.6%● Python 0.3%
  6. 6. Market positions● Perl 1.0%● Ruby 0.6%● Python 0.3%
  7. 7. Market positions● ColdFusion 1.2%● Perl 1.0%● Ruby 0.6%● Python 0.3%
  8. 8. Market positions● Java 1.2%● ColdFusion 1.2%● Perl 1.0%● Ruby 0.6%● Python 0.3%
  9. 9. Market positions● ASP.NET 21.6%● Java 1.2%● ColdFusion 1.2%● Perl 1.0%● Ruby 0.6%● Python 0.3%
  10. 10. Market positions● PHP 77.4%● ASP.NET 21.6%● Java 1.2%● ColdFusion 1.2%● Perl 1.0%● Ruby 0.6%● Python 0.3%
  11. 11. PHP Market Position, 11 Jan 2012Used by high traffic sites Java ColdFusion Perl ASP.NET PHPUsed by low traffic sites Used by fewer sites Used by many sites
  12. 12. PHP Version Adoption ● PHP 5 93.9% ● PHP 4 6.1% ● PHP 3 < 0.1% ● PHP 6 < 0.1%
  13. 13. PHP 5 usage ● Version 5.2 73.8% ● Version 5.3 20.2% ● Version 5.1 5.8% ● Version 5.0 0.2% ● Version 5.4 < 0.1%
  14. 14. Slow 5.3 Adoption?● Shared hosting● Companies refrain from updating stable systems / distributions● You
  15. 15. Adopt now● More stability● Security● Better engine (ie garbage collection)● New language features● Cleaner code because of more elegant ways of solving problems
  16. 16. PHP 5.4● Array short syntax (javascript notation)● Array dereferencing● Class access on instantiation● Indirect method call by array variable● Engine processes OO code faster● <?=● JSONSerializable interface● ...
  17. 17. Traits
  18. 18. ZF Use CaseZF Code base used is for illustration purposes only and do –not-- express future possible implementations.ZF2.0 is not yet released so giving ZF3.0 implementations illustrates only my own assumptions. PHP5.4 stable is not yet released so no real world projects are and should be running this version. Because of this no real best practices have been defined.
  19. 19. namespace ZendView;class TemplatePathStack implements TemplateResolver { public function setOptions($options = array()) { if (!is_array($options) && !$options instanceof Traversable) { throw new ExceptionInvalidArgumentException( __METHOD__ . expects an array or Traversable ); } foreach ($options as $key => $value) { $this->setOption($key, $value); } return $this; } public function setOption($key, $value) { switch (strtolower($key)) { case lfi_protection: $this->setLfiProtection($value); break; case script_paths: $this->addPaths($value); break; default: break; } }}
  20. 20. namespace ZendMvcRouter;class RouteBroker implements Broker{ public function setOptions($options) { if (!is_array($options) && !$options instanceof Traversable) { throw new ExceptionInvalidArgumentException(sprintf( Expected an array or Traversable; received "%s", (is_object($options) ? get_class($options) : gettype($options)) )); } foreach ($options as $key => $value) { switch (strtolower($key)) { case class_loader: // handle this case Default: break;// ignore unknown options } } return $this; }}
  21. 21. Problem: Code Duplicationif (!is_array($options) && !$options instanceof Traversable) { throw new ExceptionInvalidArgumentException(sprintf( Expected an array or Traversable; received "%s", (is_object($options) ? get_class($options) : gettype($options)) ));}foreach ($options as $key => $value) { //handle each case}return $this;
  22. 22. Solution in PHP5.4? Multiple Inheritance● Would provide the setOptions in all child classes needed● Introduces the diamond problem
  23. 23. NOT an Option! A better solution called Traits● Would provide the setOptions in all classes that use the trait● Eliminates the diamond problem
  24. 24. trait Options{ public function setOptions($options) { if (!is_array($options) && !$options instanceof Traversable) { throw new ExceptionInvalidArgumentException(sprintf( Expected an array or Traversable; received "%s", (is_object($options) ? get_class($options) : gettype($options)) )); } foreach ($options as $key => $value) { $this->setOption($key, $value); } return $this; }}
  25. 25. namespace ZendView;class TemplatePathStack implements TemplateResolver{ use Options; public function setOption($key, $value) { switch (strtolower($key)) { case lfi_protection: $this->setLfiProtection($value); break; case script_paths: $this->addPaths($value); break; default: break; } }}$templateStack = new TemplatePathStack();$templateStack->setOptions([lfi_protection => true]);
  26. 26. namespace ZendMvcRouter;class RouteBroker implements Broker{ use Options; public function setOption($key, $value) { switch (strtolower($key)) { case class_loader: // handle this case default: // ignore unknown options break; } }}$routeBroker = (new RouteBroker)->setOptions([class_loader=>SomeLoader]);
  27. 27. You have just seen traits
  28. 28. Lets dive deeperget your chest wet
  29. 29. namespace ZendLoader {use RuntimeException; trait SplRegister { public function register() { spl_autoload_register(array($this, autoload)); } public function unregister() { spl_autoload_unregister(array($this, autoload)); } } class StandardAutoloader { use SplRegister; public function unregister() { throw new RuntimeException( you should not unregister the standard autoloader once registered ); } }}namespace { $loader = new ZendLoaderStandardAutoloader(); $loader->register(); $loader->unregister(); // will throw the exception}
  30. 30. Precedence Order The precedence order is thatmembers from the current class override Trait methods.
  31. 31. namespace ZendLoader {use RuntimeException; class StandardAutoloader { public function unregister() { throw new RuntimeException( you should not unregister the standard autoloader once registered ); } }}namespace NbeZfLoader {use ZendLoader as ZendLoader; class StandardAutoloader extends ZendLoaderStandardAutoloader { use ZendLoaderSplRegister; }}namespace { $loader = new NbeZfLoaderStandardAutoloader(); $loader->register(); //will register (trait) $loader->unregister(); // will unregister (trait)}
  32. 32. Precedence OrderAn inherited member from abase class is overridden by a member inserted by a Trait
  33. 33. Distil Methods!
  34. 34. namespace ZendLoader {use RuntimeException; class StandardAutoloader { public function unregister() { throw new RuntimeException( you should not unregister the standard autoloader once registered ); } }}namespace NbeZfLoader {use ZendLoader as ZendLoader; class StandardAutoloader extends ZendLoaderStandardAutoloader { use ZendLoaderSplRegister { ZendLoaderStandardAutoloader::unregister insteadof ZendLoaderSplRegister; } }}namespace { $loader = new NbeZfLoaderStandardAutoloader(); $loader->register(); //will register (trait) $loader->unregister(); // will throw RuntimeException (base class)}
  35. 35. insteadofCan be used to say which methodto use instead of the trait method.
  36. 36. Multiple Traits
  37. 37. namespace ZendFilter { trait Locale { protected $locale; public function getLocale() { return $this->locale; } public function setLocale($locale = null) { $this->locale = ZendLocale::findLocale($locale); return $this; } } trait WhiteSpace { protected $allowWhiteSpace; public function getAllowWhiteSpace() { return $this->allowWhiteSpace; } public function setAllowWhiteSpace($allowWhiteSpace) { $this->allowWhiteSpace = (boolean) $allowWhiteSpace; return $this; } } class Alpha extends AbstractFilter { use Locale, WhiteSpace; }}
  38. 38. Multiple Conflicting Traits The Diamond Problem(Not yours or a girls best friend!)
  39. 39. namespace ZendFilter { trait Locale { protected $locale; public function getLocale() { return $this->locale; } public function setLocale($locale = null) { $this->locale = ZendLocale::findLocale($locale); return $this; } } trait SecondLocale { protected $locale; public function setLocale($locale = null) { if (is_string($locale)) { $locale = array($locale); } elseif ($locale instanceof Locale) { $locale = array($locale->toString()); } elseif (!is_array($locale)) { throw new ExceptionInvalidArgumentException( Locale has to be string, array or an instance of Zend_Locale ); } foreach ($locale as $single) { if (!Locale::isLocale($single)) { throw new ExceptionInvalidArgumentException("Unknown locale $single"); } } $this->_locale = $locale; return $this; } } class Alpha { use Locale, SecondLocale; }}namespace { (new ZendFilterAlpha)->setLocale(nl_be);}
  40. 40. Fatal error: Trait method getLocalehas not been applied, because there are collisions with other trait methods
  41. 41. Insteadofto the rescue
  42. 42. namespace ZendFilter { trait Locale { protected $locale; public function getLocale() public function setLocale($locale = null) } trait SecondLocale { protected $locale; public function setLocale($locale = null) } class Alpha { use Locale, SecondLocale { SecondLocale::setLocale insteadof Locale; } }}namespace { $alpha = new ZendFilterAlpha(); $alpha->setLocale(nl_be);}
  43. 43. WIN!
  44. 44. BUTStrict Standards: ZendFilterLocale and ZendFilterSecondLocaledefine the same property ($locale) in the composition of ZendFilterAlpha. This might be incompatible, to improve maintainability consider usingaccessor methods in traits instead.
  45. 45. namespace ZendFilter { trait Locale { protected $locale = nl_BE; public function getLocale() public function setLocale($locale = null) } trait SecondLocale { protected $locale = en_US; public function setLocale($locale = null) } class Alpha { use Locale, SecondLocale { SecondLocale::setLocale insteadof Locale; } }}namespace { $alpha = new ZendFilterAlpha(); $alpha->setLocale(nl_BE);}
  46. 46. Fatal error: ZendFilterLocale andZendFilterSecondLocale define the same property ($locale) in the composition of ZendFilterAlpha.However, the definition differs and is considered incompatible.
  47. 47. namespace ZendFilter { trait Locale { static public $locale = en_US; public function getLocale() { return self::$locale; } public function setLocale($locale = null) { self::$locale = $locale; } } class Alpha { use Locale; public function get() { return self::$locale; } }}namespace { $alpha = new ZendFilterAlpha(); echo $alpha->getLocale(); // en_US echo $alpha->get(); // en_US $alpha->setLocale(nl_be); echo $alpha->getLocale(); // nl_be echo $alpha->get(); // nl_be}
  48. 48. namespace ZendFilter { trait Locale { static public $locale = en_US; public function getLocale() { return self::$locale; } public function setLocale($locale = null) { self::$locale = $locale; } } class Alpha { use Locale; public function get() { return self::$locale; } } class Beta { use Locale; }}namespace { $alpha = new ZendFilterAlpha(); echo $alpha->getLocale(); // en_US $alpha->setLocale(nl_be); echo $alpha->getLocale(); // nl_be $beta = new ZendFilterBeta(); echo $beta->getLocale(); //en_US}
  49. 49. Aliassing, useful to replace the parent:: construct
  50. 50. namespace ZendMvcController {Use ... trait StdlibDispatch { public function dispatch(Request $request, Response $response = null) { $this->request = $request; if (!$response) { $response = new HttpResponse(); } $this->response = $response; ... if ($result->stopped()) { return $result->last(); } return $e->getResult(); } } abstract class ActionController implements Dispatchable, InjectApplicationEvent, LocatorAware { use StdlibDispatch; } abstract class RestfulController implementsDispatchable, InjectApplicationEvent, LocatorAware { use StdlibDispatch {StdlibDispatch::dispatch as dispatchTrait}; public function dispatch(Request $request, Response $response = null) { if (!$request instanceof HttpRequest) { throw new InvalidArgumentException(Expected an HTTP request); } return $this->dispatchTrait($request, $response); } }}
  51. 51. Aliasing could potentially be a refactoring nightmare
  52. 52. namespace ZendView;class TemplatePathStack implementsTemplateResolver{ use Options { Options::setOptions as setConfig }}$templateStack = (new TemplatePathStack)->setConfig([lfi_protection => true]);//$templateStack = (new TemplatePathStack)->setOptions([lfi_protection => true]);
  53. 53. Visibility
  54. 54. namespace ZendView;class TemplatePathStack implementsTemplateResolver{ use Options {Options::setOptions as protected} public function __construct($options = array()) { $this->setOptions($options); }}new TemplatePathStack([lfi_protection => true]);
  55. 55. Interface compliance
  56. 56. namespace ZendView;trait Options { public function setOptions($options) { //set the options }}interface TemplateResolver { public function setConfig($options);}class TemplatePathStack implements TemplateResolver { use Options {Options::setOptions as setConfig;}}(new TemplatePathStack)->setConfig([lfi_protection => true]);
  57. 57. namespace ZendView;trait Options { public function setOptions($options) { }}class TemplatePathStack { use Options { Options::setOptions as setConfig; }}$templateStack = (new TemplatePathStack)->setConfig([lfi_protection => true]);var_dump($templateStack instanceof Options);// false
  58. 58. namespace ZendView;trait Options { abstract public function setOptions($options);}class TemplatePathStack { use Options; public function setOptions($options) { echo implementation enforced by trait; }}$templateStack = (new TemplatePathStack)->setOptions([lfi_protection => true]);var_dump($templateStack instanceof Options); // false
  59. 59. namespace ZendView;trait Options { abstract public function setOptions($options);}class TemplatePathStack { use Options {Options::setOptions as setConfig;} public function setConfig($options) { echo implementation enforced by trait; }}// Fatal error: Class ZendViewTemplatePathStackcontains 1 abstract method and must therefore bedeclared abstract or implement the remaining methods(ZendViewTemplatePathStack::setOptions)
  60. 60. Magic Constants<?phptrait Id { public function getClass() { echo __CLASS__; } public function getTrait() { echo __TRAIT__; } }class Foo { use Id; }(new Foo)->getClass(); //Foo(new Foo)->getTrait(); //Id
  61. 61. Autoloading<?phpnamespace NbeZfLoader{ class foo { use Id; }}namespace { function __autoload($class) { var_dump($class); die(); }}// string(15) "NbeZfLoaderId"
  62. 62. Good things about traits● Removes code duplication● Helps keeping your code cleaner● Maintainability● Aliassing works for interfaces● Property handling● Easy Autoloading
  63. 63. Bad things about traits● Adds code complexity / understandability with the insteadof operator.● Duck typing● Aliassing Fails for Abstract Classes● Property handling
  64. 64. Benchmarks 5.3 vs 5.4● ZF project: http://nickbelhomme.com● Benchmark / Profiling tool: xhprof● How: 2 VirtualBox Machines - debian clones, with only PHP upgrade (5.4.0RC3)
  65. 65. 5.3.3● Total Incl. Wall Time (microsec): 674,842 ms● Total Incl. CPU (microsecs): 672,042 ms● Total Incl. MemUse (bytes): 13,827,864 bytes● Total Incl. PeakMemUse (bytes): 13,865,976 bytes● Number of Function Calls: 14,524
  66. 66. 5.4.0RC3 Total Incl. Wall Time (microsec): 166,813 ms● Total Incl. CPU (microsecs): 168,011 ms● Total Incl. MemUse (bytes):7,969,928 bytes● Total Incl. PeakMemUse (bytes):8,015,944 bytes● Number of Function Calls: 14,520
  67. 67. Who runs PHP5.4 today for testing?
  68. 68. YOU WIN 6
  69. 69. Please rate my talkhttps://joind.in/4774
  70. 70. The End! THANK YOU Please rate my talk https://joind.in/4774contact@nickbelhomme.comSlideshare, Twitter, IRC: NickBelhommehttp://blog.nickbelhomme.com
  71. 71. Flickr Photo Credits● Helico● donkeyhotey● jannem● Thomas.constantin● SJ photography● davidagalvan● stev.ie● Mosman Council● DeaPeaJay
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×