PHP traits, treat or threat?
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • 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.
    Are you sure you want to
    Your message goes here
  • Nice!
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
15,631
On Slideshare
14,141
From Embeds
1,490
Number of Embeds
16

Actions

Shares
Downloads
107
Comments
2
Likes
15

Embeds 1,490

http://blog.nickbelhomme.com 1,372
http://dev.monqi.com.br 81
http://www.onlydoo.com 8
https://twitter.com 7
http://a0.twimg.com 6
https://twimg0-a.akamaihd.net 5
http://dev.polemiquez.com 2
http://onobae.blogspot.com 1
http://www.php-talks.com 1
http://translate.googleusercontent.com 1
https://si0.twimg.com 1
http://webcache.googleusercontent.com 1
http://tweetedtimes.com 1
http://hydromax.mtcserver7.com 1
http://133.164.60.231 1
http://moodle.lasell.edu 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. PHP traits, treat or threat?Nick BelhommeJanuary 28th, PHPBenelux Conference 2012, Belgium
  • 2. Software Architect / Project LeadAuthor of theZend Framework 2.0 CookbookInternational Conference SpeakerContributor to various Open Source ProjectsFreelance PHP Consultant
  • 3. Market positions http://w3techs.com/
  • 4. Market positions● Python 0.3%
  • 5. Market positions● Ruby 0.6%● Python 0.3%
  • 6. Market positions● Perl 1.0%● Ruby 0.6%● Python 0.3%
  • 7. Market positions● ColdFusion 1.2%● Perl 1.0%● Ruby 0.6%● Python 0.3%
  • 8. Market positions● Java 1.2%● ColdFusion 1.2%● Perl 1.0%● Ruby 0.6%● Python 0.3%
  • 9. Market positions● ASP.NET 21.6%● Java 1.2%● ColdFusion 1.2%● Perl 1.0%● Ruby 0.6%● Python 0.3%
  • 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. 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. PHP Version Adoption ● PHP 5 93.9% ● PHP 4 6.1% ● PHP 3 < 0.1% ● PHP 6 < 0.1%
  • 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. Slow 5.3 Adoption?● Shared hosting● Companies refrain from updating stable systems / distributions● You
  • 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. 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. Traits
  • 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. 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. 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. 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. Solution in PHP5.4? Multiple Inheritance● Would provide the setOptions in all child classes needed● Introduces the diamond problem
  • 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. 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. 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. 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. You have just seen traits
  • 28. Lets dive deeperget your chest wet
  • 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. Precedence Order The precedence order is thatmembers from the current class override Trait methods.
  • 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. Precedence OrderAn inherited member from abase class is overridden by a member inserted by a Trait
  • 33. Distil Methods!
  • 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. insteadofCan be used to say which methodto use instead of the trait method.
  • 36. Multiple Traits
  • 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. Multiple Conflicting Traits The Diamond Problem(Not yours or a girls best friend!)
  • 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. Fatal error: Trait method getLocalehas not been applied, because there are collisions with other trait methods
  • 41. Insteadofto the rescue
  • 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. WIN!
  • 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. 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. Fatal error: ZendFilterLocale andZendFilterSecondLocale define the same property ($locale) in the composition of ZendFilterAlpha.However, the definition differs and is considered incompatible.
  • 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. 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. Aliassing, useful to replace the parent:: construct
  • 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. Aliasing could potentially be a refactoring nightmare
  • 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. Visibility
  • 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. Interface compliance
  • 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. 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. 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. 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. 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. Autoloading<?phpnamespace NbeZfLoader{ class foo { use Id; }}namespace { function __autoload($class) { var_dump($class); die(); }}// string(15) "NbeZfLoaderId"
  • 62. Good things about traits● Removes code duplication● Helps keeping your code cleaner● Maintainability● Aliassing works for interfaces● Property handling● Easy Autoloading
  • 63. Bad things about traits● Adds code complexity / understandability with the insteadof operator.● Duck typing● Aliassing Fails for Abstract Classes● Property handling
  • 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. 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. 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. Who runs PHP5.4 today for testing?
  • 68. YOU WIN 6
  • 69. Please rate my talkhttps://joind.in/4774
  • 70. The End! THANK YOU Please rate my talk https://joind.in/4774contact@nickbelhomme.comSlideshare, Twitter, IRC: NickBelhommehttp://blog.nickbelhomme.com
  • 71. Flickr Photo Credits● Helico● donkeyhotey● jannem● Thomas.constantin● SJ photography● davidagalvan● stev.ie● Mosman Council● DeaPeaJay