71
PHP traits, treat or threat? Nick Belhomme January 28th, PHPBenelux Conference 2012, Belgium

PHP traits, treat or threat?

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: PHP traits, treat or threat?

PHP traits, treat or threat?

Nick BelhommeJanuary 28th, PHPBenelux Conference 2012, Belgium

Page 2: PHP traits, treat or threat?

Software Architect / Project Lead

Author of the Zend Framework 2.0 Cookbook

International Conference Speaker

Contributor to various Open Source Projects

Freelance PHP Consultant

Page 3: PHP traits, treat or threat?

Market positions

http://w3techs.com/

Page 4: PHP traits, treat or threat?

Market positions

● Python 0.3%

Page 5: PHP traits, treat or threat?

Market positions

● Python 0.3%● Ruby 0.6%

Page 6: PHP traits, treat or threat?

Market positions

● Python 0.3%● Ruby 0.6%● Perl 1.0%

Page 7: PHP traits, treat or threat?

Market positions

● Python 0.3%● Ruby 0.6%

● ColdFusion 1.2%● Perl 1.0%

Page 8: PHP traits, treat or threat?

Market positions

● Python 0.3%● Ruby 0.6%

● ColdFusion 1.2%● Perl 1.0%

● Java 1.2%

Page 9: PHP traits, treat or threat?

Market positions

● Python 0.3%● Ruby 0.6%

● ColdFusion 1.2%● Perl 1.0%

● Java 1.2%● ASP.NET 21.6%

Page 10: PHP traits, treat or threat?

Market positionsMarket positions

● Python 0.3%● Ruby 0.6%

● ColdFusion 1.2%● Perl 1.0%

● Java 1.2%● ASP.NET 21.6%● PHP 77.4%

Page 11: PHP traits, treat or threat?

Java

PHP

ASP.NET

ColdFusion

Perl

Used by many sitesUsed by fewer sites

Use

d by

hig

h tra

ffic

site

sU

sed

by lo

w tr

affic

site

s

PHP Market Position, 11 Jan 2012

Page 12: PHP traits, treat or threat?

PHP Version Adoption

● PHP 5 93.9%● PHP 4 6.1%● PHP 3 < 0.1%● PHP 6 < 0.1%

Page 13: PHP traits, treat or threat?

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%

Page 14: PHP traits, treat or threat?

Slow 5.3 Adoption? ● Shared hosting● Companies refrain from updating stable

systems / distributions● You

Page 15: PHP traits, treat or threat?

Adopt now● More stability● Security● Better engine (ie garbage collection)● New language features● Cleaner code because of more elegant ways of

solving problems

Page 16: PHP traits, treat or threat?

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● ...

Page 17: PHP traits, treat or threat?

Traits

Page 18: PHP traits, treat or threat?

ZF Use Case

ZF 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 areand should be running this version.

Because of this no real best practices have been defined.

Page 19: PHP traits, treat or threat?

namespace Zend\View;class TemplatePathStack implements TemplateResolver { public function setOptions($options = array()) { if (!is_array($options) && !$options instanceof Traversable) { throw new Exception\InvalidArgumentException( __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; } }}

Page 20: PHP traits, treat or threat?

namespace Zend\Mvc\Router;class RouteBroker implements Broker{ public function setOptions($options) { if (!is_array($options) && !$options instanceof \Traversable) { throw new Exception\InvalidArgumentException(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; }}

Page 21: PHP traits, treat or threat?

Problem: Code Duplication

if (!is_array($options) && !$options instanceof \Traversable) { throw new Exception\InvalidArgumentException(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;

Page 22: PHP traits, treat or threat?

Solution in PHP5.4? Multiple Inheritance

● Would provide the setOptions in all child classes needed

● Introduces the diamond problem

Page 23: PHP traits, treat or threat?

NOT an Option!A better solution called Traits

● Would provide the setOptions in all classes that use the trait

● Eliminates the diamond problem

Page 24: PHP traits, treat or threat?

trait Options{ public function setOptions($options) { if (!is_array($options) && !$options instanceof \Traversable) { throw new Exception\InvalidArgumentException(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; }}

Page 25: PHP traits, treat or threat?

namespace Zend\View;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]);

Page 26: PHP traits, treat or threat?

namespace Zend\Mvc\Router;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']);

Page 27: PHP traits, treat or threat?

You have just seen traits

Page 28: PHP traits, treat or threat?

Lets dive deeper

get your chest wet

Page 29: PHP traits, treat or threat?

namespace Zend\Loader {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 Zend\Loader\StandardAutoloader(); $loader->register(); $loader->unregister(); // will throw the exception}

Page 30: PHP traits, treat or threat?

Precedence Order

The precedence order is that members from the current class

override Trait methods.

Page 31: PHP traits, treat or threat?

namespace Zend\Loader {use RuntimeException; class StandardAutoloader { public function unregister() { throw new RuntimeException( 'you should not unregister the standard autoloader once registered' ); } }}namespace NbeZf\Loader {use Zend\Loader as ZendLoader; class StandardAutoloader extends ZendLoader\StandardAutoloader { use ZendLoader\SplRegister; } }namespace { $loader = new NbeZf\Loader\StandardAutoloader(); $loader->register(); //will register (trait) $loader->unregister(); // will unregister (trait)}

Page 32: PHP traits, treat or threat?

Precedence Order

An inherited member from a base class is overridden by a member inserted by a Trait

Page 33: PHP traits, treat or threat?

Distil Methods!

Page 34: PHP traits, treat or threat?

namespace Zend\Loader {use RuntimeException; class StandardAutoloader { public function unregister() { throw new RuntimeException( 'you should not unregister the standard autoloader once registered' ); } }}namespace NbeZf\Loader {use Zend\Loader as ZendLoader; class StandardAutoloader extends ZendLoader\StandardAutoloader { use ZendLoader\SplRegister { ZendLoader\StandardAutoloader::unregister insteadof ZendLoader\SplRegister; } }}namespace { $loader = new NbeZf\Loader\StandardAutoloader(); $loader->register(); //will register (trait) $loader->unregister(); // will throw RuntimeException (base class)}

Page 35: PHP traits, treat or threat?

insteadof

Can be used to say which method to use instead of the trait method.

Page 36: PHP traits, treat or threat?

Multiple Traits

Page 37: PHP traits, treat or threat?

namespace Zend\Filter { 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; }}

Page 38: PHP traits, treat or threat?

Multiple Conflicting Traits

The Diamond Problem

(Not yours or a girls best friend!)

Page 39: PHP traits, treat or threat?

namespace Zend\Filter { 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 Exception\InvalidArgumentException( 'Locale has to be string, array or an instance of Zend_Locale' ); } foreach ($locale as $single) { if (!Locale::isLocale($single)) { throw new Exception\InvalidArgumentException("Unknown locale '$single'"); } } $this->_locale = $locale; return $this; } } class Alpha { use Locale, SecondLocale; }}namespace { (new Zend\Filter\Alpha)->setLocale('nl_be');}

Page 40: PHP traits, treat or threat?

Fatal error: Trait method getLocale has not been applied, because there

are collisions with other trait methods

Page 41: PHP traits, treat or threat?

Insteadof

to the rescue

Page 42: PHP traits, treat or threat?

namespace Zend\Filter { 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 Zend\Filter\Alpha(); $alpha->setLocale('nl_be');}

Page 43: PHP traits, treat or threat?

WIN!

Page 44: PHP traits, treat or threat?

BUTStrict Standards: Zend\Filter\Locale

and Zend\Filter\SecondLocale define the same property ($locale)

in the composition of Zend\Filter\Alpha. This might be

incompatible, to improve maintainability consider using

accessor methods in traits instead.

Page 45: PHP traits, treat or threat?

namespace Zend\Filter { 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 Zend\Filter\Alpha(); $alpha->setLocale('nl_BE');}

Page 46: PHP traits, treat or threat?

Fatal error: Zend\Filter\Locale and Zend\Filter\SecondLocale define the

same property ($locale) in the composition of Zend\Filter\Alpha.

However, the definition differs and is considered incompatible.

Page 47: PHP traits, treat or threat?

namespace Zend\Filter { 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 Zend\Filter\Alpha(); echo $alpha->getLocale(); // en_US echo $alpha->get(); // en_US $alpha->setLocale('nl_be'); echo $alpha->getLocale(); // nl_be echo $alpha->get(); // nl_be}

Page 48: PHP traits, treat or threat?

namespace Zend\Filter { 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 Zend\Filter\Alpha(); echo $alpha->getLocale(); // en_US $alpha->setLocale('nl_be'); echo $alpha->getLocale(); // nl_be $beta = new Zend\Filter\Beta(); echo $beta->getLocale(); //en_US}

Page 49: PHP traits, treat or threat?

Aliassing, useful to replace the parent:: construct

Page 50: PHP traits, treat or threat?

namespace Zend\Mvc\Controller {Use ... trait Stdlib\Dispatch { 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 Stdlib\Dispatch; }

abstract class RestfulController implementsDispatchable, InjectApplicationEvent, LocatorAware { use Stdlib\Dispatch {Stdlib\Dispatch::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); } }}

Page 51: PHP traits, treat or threat?

Aliasing could potentially be a refactoring nightmare

Page 52: PHP traits, treat or threat?

namespace Zend\View;class TemplatePathStack implements TemplateResolver{ use Options { Options::setOptions as setConfig }}$templateStack = (new TemplatePathStack)->setConfig(['lfi_protection' => true]);

//$templateStack = (new TemplatePathStack)->setOptions(['lfi_protection' => true]);

Page 53: PHP traits, treat or threat?

Visibility

Page 54: PHP traits, treat or threat?

namespace Zend\View;class TemplatePathStack implements TemplateResolver{ use Options {Options::setOptions as protected}

public function __construct($options = array()) { $this->setOptions($options); }

}new TemplatePathStack(['lfi_protection' => true]);

Page 55: PHP traits, treat or threat?

Interface compliance

Page 56: PHP traits, treat or threat?

namespace Zend\View;

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]);

Page 57: PHP traits, treat or threat?

namespace Zend\View;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

Page 58: PHP traits, treat or threat?

namespace Zend\View;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

Page 59: PHP traits, treat or threat?

namespace Zend\View;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 Zend\View\TemplatePathStack contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Zend\View\TemplatePathStack::setOptions)

Page 60: PHP traits, treat or threat?

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

Page 61: PHP traits, treat or threat?

Autoloading<?phpnamespace NbeZf\Loader{ class foo { use Id; }}namespace { function __autoload($class) { var_dump($class); die(); }}// string(15) "NbeZf\Loader\Id"

Page 62: PHP traits, treat or threat?

Good things about traits

● Removes code duplication● Helps keeping your code cleaner● Maintainability● Aliassing works for interfaces● Property handling● Easy Autoloading

Page 63: PHP traits, treat or threat?

Bad things about traits

● Adds code complexity / understandability with the insteadof operator.

● Duck typing● Aliassing Fails for Abstract

Classes● Property handling

Page 64: PHP traits, treat or threat?

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)

Page 65: PHP traits, treat or threat?

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

Page 66: PHP traits, treat or threat?

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

Page 67: PHP traits, treat or threat?

Who runs PHP5.4 today for testing?

Page 68: PHP traits, treat or threat?

YOU WIN

6

Page 69: PHP traits, treat or threat?

Please rate my talkhttps://joind.in/4774

Page 70: PHP traits, treat or threat?

The End!

THANK YOU

[email protected]

Slideshare, Twitter, IRC: NickBelhomme

http://blog.nickbelhomme.com

Please rate my talkhttps://joind.in/4774

Page 71: PHP traits, treat or threat?

Flickr Photo Credits● Helico● donkeyhotey● jannem● Thomas.constantin● SJ photography● davidagalvan● stev.ie● Mosman Council● DeaPeaJay