Traits and Horizonal Design
 

Traits and Horizonal Design

on

  • 2,317 views

One of the newest and most powerful features in PHP 5.4 is Traits, or the ability to implement horizontal design into your application. In these slides we will take a look at what traits are and how ...

One of the newest and most powerful features in PHP 5.4 is Traits, or the ability to implement horizontal design into your application. In these slides we will take a look at what traits are and how to use them (including examples of when to use horizontal design) as well as review the ReflectionClass and how it can be used to describe the traits in your code base.

Statistics

Views

Total Views
2,317
Views on SlideShare
2,280
Embed Views
37

Actions

Likes
7
Downloads
41
Comments
0

3 Embeds 37

http://www.mikestowe.com 23
https://twitter.com 7
http://librosweb.es 7

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Traits and Horizonal Design Traits and Horizonal Design Presentation Transcript

  • Traits & Horizontal Design michael stowe January 17, 2014
  • MIKESTOWE .com @mikegstowe •  Open Source Contributor •  Author, Speaker, and Consultant •  10+ years experience hacking PHP •  Zend Certified PHP 5.3 Software Engineer •  Developer Advocate with Constant Contact
  • WHAT WE’RE GONNA TALK ABOUT •  Traits – What are they? •  What is Horizontal Design? •  Theory •  Sample Vertical Application •  Horizontal Application •  Benefits of Horizontal Design •  Challenges with Horizontal Design •  Using Traits in PHP •  Trait vs. Classes, Abstracts, and Interfaces •  Trait Hierarchy and Class Overrides •  Trait Method Aliasing •  Adjusting Visibility •  Using Properties in Traits •  Trait Functions •  Traits and the Reflection Class
  • WHAT ARE TRAITS “Traits are 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.”
  • WHAT ARE TRAITS “[They are] similar to a class, but intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on it’s own. It is an addition to traditional inheritance and enables horizontal composition of behavior.”
  • HUH? To think of traits in another way, think of traits as characteristics. People have unique traits that makeup who they are- hair color, eye color, height, weight, likes, dislikes, etc. But it is the combination of these traits working together that make them who they are.
  • HUH? In the same way Traits in PHP are collections of methods that can be imported into classes. Each stands alone by itself (ie brown hair), but becomes part of something more when inherited into the class (he has brown hair and brown eyes).
  • HUH? Traits are designed to allow for code reusability and extendibility. These groups of methods are designed to be pulled in and used by any other class. Like Midas’ touch, they seamlessly become part of the class utilizing them.
  • QUICK LOOK The Trait <?php trait MyTrait { public function test() { /* ... */ } }! The Class Utilizing the Trait <?php class MyClass { use MyTrait; /* now contains test method */ }! <?php
  • THE IDEA BEHIND HORIZONTAL DESIGN The concept between horizontal design/ horizontal reuse is simple, reduce duplicate code while creating more extendable applications.
  • THE IDEA BEHIND HORIZONTAL DESIGN Traditional Vertical Design Interface Parent Class Child Class
  • THE IDEA BEHIND HORIZONTAL DESIGN Traditional vertical design is very limiting. To help address these limitations there has been an increased focus on Dependency Injection, or injecting classes into another class to allow the class to access additional methods and properties. However, even this approach is very limited in its capabilities. It’s important to note that Horizontal Design does not replace Dependency Injection, but rather ensures correct usage of it.
  • THE IDEA BEHIND HORIZONTAL DESIGN Vertical Design + Horizontal Design Interface Trait Parent Class Trait Trait Child Class Trait
  • THE IDEA BEHIND HORIZONTAL DESIGN Traits can reach other traits via the Class <?php trait traitOne { public function a() { echo 'Trait One Method'; } } trait traitTwo { public function b() { return $this->a(); } } class myClass { use traitOne; use traitTwo; } $a = new myClass(); $a->b(); // echos Trait One Method !
  • THE IDEA BEHIND HORIZONTAL DESIGN Incredible Code Access and Reusability Interface Trait Parent Class Trait Trait Child Class Trait Traits can access other trait methods/ properties of child and parent classes
  • THE IDEA BEHIND HORIZONTAL DESIGN Traits can be used in a vertical manner <?php trait traitOne { public function a() { echo 'Trait One Method'; } } trait traitTwo { public function a() { return parent::a(); } } class parentClass { use TraitOne; } class myClass extends parentClass { use traitTwo; } $a = new myClass(); $a->a(); // echos Trait One Method !
  • THE IDEA BEHIND HORIZONTAL DESIGN Incredible Code Access and Reusability Interface Trait Parent Class Trait Trait Child Class Trait Traits can natively utilize other trait methods/ properties via the classes
  • THE IDEA BEHIND HORIZONTAL DESIGN Traits seamlessly work together with encapsulation <?php trait traitOne { public function a() { echo 'Trait One Method'; } } <?php trait traitOne { public function a() { echo 'Trait One Method'; } } trait traitTwo { public function b() { return $this->a(); } } trait traitTwo { public function b() { return $this->a(); } } class parentClass { use TraitOne; } class parentClass { use TraitTwo; } class myClass extends parentClass { use traitTwo; } class myClass extends parentClass { use traitOne; } $a = new myClass(); $a->b(); // echos Trait One Method $a = new myClass(); $a->b(); // echos Trait One Method ! !
  • BUILDING THE APP In the next few slides we will take a look at the difference between building a simple app vertically, verses horizontally. The app will utilize the same 5 files in both cases.
  • BUILDING A HORIZONTAL APP <?php Pull in the Traits, just like you would namespaces only within the class /** * @package ressf * @category ressf */ class ressf { use ressfbasevalidators; use ressfbaseextenders; use ressfpluginsvalidators; use ressfpluginsextenders;! Full source on GitHub: http://github.com/mikestowe/ressf Branch: master
  • BUILDING A VERTICAL APP <?php Setup namespaces for class inclusion use ressfpluginsextenders; use ressfpluginsvalidators; /** * @package ressf * @category ressf */ class ressf {! Full source on GitHub: http://github.com/mikestowe/ressf Branch: php53
  • BUILDING A HORIZONTAL APP <?php Setup the __construct() method to do some basic work /** * Construct * @return ressf */ public function __construct() { $this->tags = array_merge($this->baseTags, $this->tags); $this->extenders = array_merge($this->baseExtenders, $this->extenders); }! Full source on GitHub: http://github.com/mikestowe/ressf Branch: master
  • BUILDING A VERTICAL APP <?php In the __construct() method setup properties to store the Extenders and Validators objects. Since these objects will manipulate and make use of properties and methods within the instantiated ressf object we need to pass the ressf object to them as well. Now we can do the basic operations, utilizing the properties we just setup. /** * Construct * @return ressf */ public function __construct() { $this->extendersClass = new extenders($this); $this->validatorsClass = new validators($this); $this->tags = array_merge($this->validatorsClass->baseTags, $this->validatorsClass->tags); $this->extenders = array_merge($this->extendersClass->baseExtenders, $this->extendersClass->extenders); }! Full source on GitHub: http://github.com/mikestowe/ressf Branch: php53
  • BUILDING A VERTICAL APP <?php Do the same for the for user defined extenders and validators classes, both which will need to extend the system defined base class: namespace ressfplugins; use ressfbaseextenders as baseExtenders; /** * User Defined Extenders Trait * @package ressf * @category ressf/plugins */ class extenders extends baseExtenders { protected $ressf; public function __construct($ressf) { $this->ressf = $ressf; }! Full source on GitHub: http://github.com/mikestowe/ressf Branch: php53
  • BUILDING A VERTICAL APP <?php Also setup getters and setters for these properties: /** * Return the View * @return string */ public function getView() { return $this->view; } /** * Set the View * @param string * @return ressf */ public function setView($view) { $this->view = $view; return $this; } /** * Set Kill Process Switch * @param bool * @return ressf */ ! Full source on GitHub: http://github.com/mikestowe/ressf Branch: php53
  • BUILDING A HORIZONTAL APP <?php Because traits become part of the class, we can call the methods natively /** * Handle Extenders * @param string * @return string */ private function handleExtenders($action) { if ($action == 'retrieve') { preg_match_all('/[ressf:([A-Za-z]+)=([^]]+)]/', $this->view, $matches); }! for ($i = 0; $i < count($matches[0]); $i++) { $this->{'set' . ucfirst($matches[1][$i])}($matches[2][$i]); $this->view = str_replace($matches[0][$i], '', $this->view); } Full source on GitHub: http://github.com/mikestowe/ressf Branch: master
  • BUILDING A HORIZONTAL APP <?php Which can directly access the main class properties. /** * Set Cache Extender * @param string * @return void */ public function setApcCache($cache = 'false') { $base = $this; $this->extenders['cache'] = array( 'doCache' => ($cache != 'false' && $cache != '0'), 'md5' => $base->detect() . '_' . md5($base->view), ); self::addAction('beforeRender', function() use ($base) { if ($base->extenders['cache']['doCache']) { $cachedView = apc_fetch($base->extenders['cache']['md5'], $isCached); if ($isCached) { $base->view = $cachedView; $base->killProcess = true; } } });! Full source on GitHub: http://github.com/mikestowe/ressf Branch: master
  • BUILDING A VERTICAL APP <?php Now to call each method we can utilize the object referenced in the other objects property, like so: /** * Handle Extenders * @param string * @return string */ private function handleExtenders($action) { if ($action == 'retrieve') { preg_match_all('/[ressf:([A-Za-z]+)=([^]]+)]/', $this->view, $matches); }! for ($i = 0; $i < count($matches[0]); $i++) { $this->getExtendersClass()->{'set' . ucfirst($matches[1][$i])}($matches[2][$i]); $this->view = str_replace($matches[0][$i], '', $this->view); } Full source on GitHub: http://github.com/mikestowe/ressf Branch: php53
  • BUILDING A VERTICAL APP <?php Which will in turn talk back to the ressf class like so: /** * Set Cache Extender * @param string * @return void */ public function setApcCache($cache = 'false') { $base = $this->ressf; $base->setExtenders('cache', array( 'doCache' => ($cache != 'false' && $cache != '0'), 'md5' => $base->detect() . '_' . md5($base->getView()), )); ressf::addAction('beforeRender', function() use ($base) { $config = $base->getExtenders('cache'); if ($config['doCache']) { $cachedView = apc_fetch($config['md5'], $isCached); if ($isCached) { $base->setView($cachedView); $base->setKillProcess(true); } } });! Full source on GitHub: http://github.com/mikestowe/ressf Branch: php53
  • THE DIFFERENCE A quick breakdown of rewriting the ressf app to be PHP 5.3 compatible using Dependency Injection (DI) instead of utilizing Traits: 195 Additions | 39 Deletions https://github.com/mikestowe/ressf/compare/php53
  • THE DIFFERENCE By using horizontal design we were not only able to make the application more efficient, we were also able to reduce the amount of code by 34%! That’s just for a small, fairly simple application containing just 5 files and 454 lines of code! 195 Additions | 39 Deletions https://github.com/mikestowe/ressf/compare/php53
  • THE BENEFITS •  Reduces the amount of code needed •  Reduces Dependency Injection requirements •  Provides seamless integrations into classes •  Code can be reused by multiple classes •  Allows for properties of dependencies to be included
  • THE BENEFITS •  Allows for multiple interactions and eliminates vertical asphyxiation/ dead ends •  Plug and play friendly (traits play with other traits as long as there are no conflicts) •  No class name conflicts (as with namespaces)
  • THE CHALLENGES •  Property Conflicts – properties in traits and calling classes must be exactly the same otherwise an error is thrown •  Method Conflicts/ Overrides – traits containing the same methods may result in the wrong method being called •  Harder to navigate code (ie locating methods/ properties) •  Limited to PHP 5.4+
  • THE DIFFERENCE BETWEEN CLASSES, ABSTRACTS, INTERFACES Traits are similar to abstract classes in that they cannot be instantiated by themselves. However, unlike an abstract class traits are not extendable. You can use traits within other traits however, just as you would in a class through the use keyword. Traits can contain methods and properties, something that makes them unique in PHP.
  • TRAIT HIERARCHY AND CLASS OVERRIDES Because traits are applied at a horizontal level there is no hierarchy applied to the methods. Rather, if two traits on the same vertical plane contain identically named methods a fatal error will be thrown explaining which method could not be applied to the class. If a class has an identically named method it will override the trait method.
  • CLASS OVERRIDES Since the class contains the same method as the trait… <?php trait myTrait { public function test() { echo 'trait'; } } class myClass { use myTrait; public function test() { echo 'class'; } } (new myClass())->test(); ! The class method will be used, resulting in the script echoing out “class” <?php
  • CLASS OVERRIDES <?php However, if a trait is included in a child class, and shares the same method name with a method in the parent class, the trait will override the parent method, just as if the child class had the same method as the parent class. In this case parent methods will need to be referred to using the parent::method()  syntax. trait traitTwo { public function a() { return parent::a(); } }!
  • TRAIT ALIASING To use traits containing identically named methods or to make a trait method available within a class we can alias the trait using the as and insteadof keywords. The as keyword will give the method an alias to reference it by The insteadof keyword tells the compiler to use that traits method instead of a different identically named method from another trait.
  • TRAIT ALIASING <?php trait traitOne { public function test() { echo 'one'; } } trait traitTwo { public function test() { echo 'two'; } } class myClass { use traitOne, traitTwo { traitTwo::test insteadof traitOne; traitOne::test as one; } } (new myClass())->one(); (new myClass())->test(); ! <?php
  • TRAIT ALIASING <?php When using multiple use declarations within your class it is important to place the insteadof delcaration before the as declarations to avoid conflicts. <?php /* … */ class myClass { use traitOne, traitTwo { traitTwo::test insteadof traitOne; traitOne::test as one; } } !
  • TRAIT ALIASING Using multiple use statements: class myClass { use traitOne, traitTwo { traitOne::test as one; traitTwo::test insteadof traitOne; } use traitThree, traitFour { traitFour::testme insteadof traitThree; traitThree::testme as three; } } (new (new (new (new ! myClass())->one(); myClass())->test(); myClass())->three(); myClass())->testme(); <?php
  • TRAIT METHOD VISIBILITY <?php You can also modify the visibility state of a trait’s methods through the use  declaration: <?php trait traitOne { public function test() { echo 'one'; } } trait traitTwo { public function test() { echo 'two'; } } class myClass { use traitOne, traitTwo { traitOne::test as protected one; traitTwo::test insteadof traitOne; } }!
  • TRAIT METHOD DEPENDENCIES <?php Often times a trait method may require interaction with a class method. To ensure the class method is defined you can create an abstract method in the trait, which if called will throw a fatal error: <?php trait myTrait { public function test() { $this->doSomething(); } public abstract function doSomething(); } class myClass { use myTrait; } (new myClass())->test();!
  • TRAIT PROPERTIES One of the advantages of traits in PHP over other languages is that PHP allows traits to contain properties. This allows you to pull in a full set of code and it’s dependencies without having to manually add properties to your calling class. However, it is important to be careful when using properties as any properties declared in both the trait and the calling class must be identical in visibility, type, and value. The exception to this is when using a data-type of string for one, and a data-type int for another, as long as the state and value are identical.
  • TRAIT PROPERTIES <?php Because the visibility state of $test is different, the properties are incompatible and PHP throws a fatal error. <?php trait myTrait { public $test = 'hi'; } class myClass { use myTrait; protected $test = 'hi'; } new myClass(); !
  • TRAIT PROPERTIES <?php Because the value of $test is different, the properties are incompatible and PHP throws a fatal error. <?php trait myTrait { public $test = 'hi'; } class myClass { use myTrait; public $test = 'bye'; } new myClass(); !
  • TRAIT PROPERTIES <?php This example however will work because the property state, type, and value is the same. <?php trait myTrait { public $test = 'hi'; } class myClass { use myTrait; public $test = 'hi'; } new myClass(); !
  • TRAIT FUNCTIONS trait_exists()  – similar to class_exists(), this function checks to see if the trait has been defined and returns an boolean. get_declared_traits()  – similar to get_declared_classes(), this function returns an array of traits that have been declared.
  • REFLECTION CLASS getTraits()  returns an array of all traits used in a class, while getTraitNames()  returns an array of the trait names in a class. getTraitAliases()  returns an array of aliases linked to its original trait and method. isTrait()  returns whether or not a tested object is a trait http://php.net/manual/en/class.reflectionclass.php
  • MORE USE CASES
  • MORE USE CASES BASE CONTROLLER MOD. CONTROLLER API CONTROLLER ACTUAL What happens if we need to modify the modified controller? What if the API Controller needs new dependencies because our application was updated?
  • MORE USE CASES BASE CONTROLLER MOD. CONTROLLER ACTUAL Traits allow us to pull in classes, properties, and methods without having to create a dependency chain, leaving the modified controller easily accessible and updatable. API CONTROLLER
  • MORE USE CASES BASE CONTROLLER MOD. CONTROLLER MODIFIERS ACTUAL API CONTROLLER Traits also allow us to modify existing classes without overriding them in the vertical hierarchy. And keeps source code isolated and reusable.
  • More Information: http://php.net/manual/en/language.oop5.traits.php
  • GET THE ARTICLE http://webandphp.com/issue-3
  • THANK YOU. A big thank you to Constant Contact for making this presentation possible @mikegstowe @ctct_api visit mikestowe.com/slides for more on PHP and Web Development