Successfully reported this slideshow.

Coming to Terms with OOP In Drupal - php[world] 2016

0

Share

Loading in …3
×
1 of 57
1 of 57

Coming to Terms with OOP In Drupal - php[world] 2016

0

Share

Download to read offline

Drupal 8 has not only brought to the table a much improved admin experience, but has now moved on from its procedural roots into the realm of Object Oriented Programming. While this is a great thing for developers, many Drupal developers have never been introduced to OOP. This talk will explore a very high-level overview of objects, inheritance, composition, and how to architect your code as it relates to Drupal 8.

Drupal 8 has not only brought to the table a much improved admin experience, but has now moved on from its procedural roots into the realm of Object Oriented Programming. While this is a great thing for developers, many Drupal developers have never been introduced to OOP. This talk will explore a very high-level overview of objects, inheritance, composition, and how to architect your code as it relates to Drupal 8.

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Coming to Terms with OOP In Drupal - php[world] 2016

  1. 1. Coming to Terms with OOP in Drupal Chris Tankersley php[world] 2016 php[world] 2016 1
  2. 2. In the beginning… function mymodule_modules_enabled($modules) { // Stuff happens } function mymodule_menu() { return array( // … ); } php[world] 2016 2
  3. 3. Procedural Programming • Code is kept in Procedures (functions) • Procedures contain steps to be carried out php[world] 2016 3
  4. 4. Why don’t people like it? • Can be hard to test • Can be hard to isolate • Can be hard to name functions succinctly • Can be hard to organize • Can be hard to share • Nearly impossible to modify original, 3rd party code php[world] 2016 4
  5. 5. Why do we want OOP? • Better ability to test • Better architecture through code encapsulation • With Namespaces, better naming • The ability to share code that can be extended php[world] 2016 5
  6. 6. Vocabulary • Class – Textual representation of how an object is made up • Object – Variable built from a class that holds data and performs actions php[world] 2016 6
  7. 7. php[world] 2016 7 Class class Employee { protected $name; protected $number; public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; } public function viewData() { echo <<<ENDTEXT Name: {$this->name} Number: {$this->number} ENDTEXT; } }
  8. 8. Object <?php $manager= new Employee(); // ^ // | // `--- This is the Object php[world] 2016 8
  9. 9. php[world] 2016 9 Methods and Properties class Employee { protected $name; // This is a property protected $number; // This is a Method public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; } public function viewData() { echo <<<ENDTEXT Name: {$this->name} Number: {$this->number} ENDTEXT; } }
  10. 10. New Vocabulary • Property – Data inside of an object • Method – Function inside of an object • Both are accessed using the -> notation php[world] 2016 10
  11. 11. Visibility • Public – Anyone can access the property/method • Protected – Only the class, or child classes, can access • Private – Only the class itself can access php[world] 2016 11
  12. 12. php[world] 2016 12 Class class Employee { public $name; // Anyone can access this protected $number; // Only itself, and children can access private $ssn; // Only itself can access this // Anyone can call this method public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; } public function viewData() { echo <<<ENDTEXT Name: {$this->name} Number: {$this->number} ENDTEXT; } }
  13. 13. Object <?php $manager= new Employee(); $manager->name = ‘Bob’; $manager->number = 1234; // PHP Fatal error: Cannot access protected property Employee::$number php[world] 2016 13
  14. 14. Extending Classes • Inheritance • Composition php[world] 2016 14
  15. 15. Namespaces • Sets up “packages” of code for organization • Is a “path” separated by • Allows us to have classes with simple, clear names and avoid naming collisions php[world] 2016 15
  16. 16. namespace DrupalblockEntity; class Block { } php[world] 2016 16
  17. 17. $block = new DrupalblockEntityBlock(); php[world] 2016 17
  18. 18. use DrupalblockEntityBlock; use MyNamespaceblockEntityBlock as MyBlock; $block = new Block(); $my_block = new MyBlock(); php[world] 2016 18
  19. 19. Inheritance php[world] 2016 19
  20. 20. The first thing most people learn • Classes are “things” in the real world • We should construct class properties based on Attributes • Number of wheels • Sound it makes • We should construct class methods based on “Actions” • Running • Speaking • Jumping php[world] 2016 20
  21. 21. New Vocabulary • Parent Class – Class that is extended • Child Class – Class that is extending another class In PHP, a class can be both a Child and a Parent at the same time php[world] 2016 21
  22. 22. Our Structure php[world] 2016 22 Employee Manager Scientist Laborer
  23. 23. The Employee Class php[world] 2016 23 abstract class Employee { protected $name; // Employee Name protected $number; // Employee Number public function setData($data) { $this->name = $data['name']; $this->number = $data['number']; } public function viewData() { echo <<<ENDTEXT Name: {$this->name} Number: {$this->number} ENDTEXT; } }
  24. 24. The Manager Class php[world] 2016 24 class Manager extends Employee { protected $title; // Employee Title protected $dues; // Golf Dues public function setData($data) { parent::setData($data); $this->title = $data['title']; $this->dues = $data['dues']; } public function viewData() { parent::viewData(); echo <<<ENDTEXT Title: {$this->title} Golf Dues: {$this->dues} ENDTEXT; } }
  25. 25. The Scientist Class php[world] 2016 25 class Scientist extends Employee { protected $pubs; // Number of Publications public function setData($data) { parent::setData($data); $this->pubs = $data['pubs']; } public function viewData() { parent::viewData(); echo <<<ENDTEXT Publications: {$this->pubs} ENDTEXT; } }
  26. 26. The Laborer Class php[world] 2016 26 class Laborer extends Employee { }
  27. 27. What does this teach us? • Inheritance • Makes it easier to group code together and share it amongst classes • Allows us to extend code as needed • PHP allows Single inheritance php[world] 2016 27
  28. 28. We use it all the time namespace DrupalblockEntity; use DrupalCoreConfigEntityConfigEntityBase; use DrupalblockBlockInterface; use DrupalCoreEntityEntityWithPluginCollectionInterface; class Block extends ConfigEntityBase, implements BlockInterface, EntityWithPluginCollectionInterface { protected $id; protected $settings; // … public function getRegion() { return $this->region; } // … } php[world] 2016 28
  29. 29. Why it Works (Most of the time, Kinda) • Allows us to extend things we didn’t necessarily create • Encourages code re-use • Allows developers to abstract away things php[world] 2016 29
  30. 30. Why can Inheritance Be Bad • PHP only allows Single Inheritance on an Class • You can have a series of Inheritance though, for example CEO extends Manager, Manager extends Employee • Long inheritance chains can be a code smell • Private members and methods cannot be used by Child classes • Single Inheritance can make it hard to ‘bolt on’ new functionality between disparate classes php[world] 2016 30
  31. 31. Composition php[world] 2016 31
  32. 32. The General Idea • Classes contain other classes to do work and extend that way, instead of through Inheritance • Interfaces define “contracts” that objects will adhere to • Your classes implement interfaces to add needed functionality php[world] 2016 32
  33. 33. Interfaces interface EmployeeInterface { protected $name; protected $number; public function getName(); public function setName($name); public function getNumber(); public function setNumber($number); } interface ManagerInterface { protected $golfHandicap; public function getHandicap(); public function setHandicap($handicap); } php[world] 2016 33
  34. 34. Interface Implementation class Employee implements EmployeeInterface { public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } } class Manager implements EmployeeInterface, ManagerInterface { // defines the employee getters/setters as well public function getHandicap() { return $this->handicap; } public function setHandicap($handicap) { $this->handicap = $handicap; } } php[world] 2016 34
  35. 35. This is Good and Bad • “HAS-A” is tends to be more flexible than “IS-A” • Somewhat easier to understand, since there isn’t a hierarchy you have to backtrack • Each class must provide their own Implementation, so can lead to code duplication php[world] 2016 35
  36. 36. Traits • Allows small blocks of code to be defined that can be used by many classes • Useful when abstract classes/inheritance would be cumbersome • My Posts and Pages classes shouldn’t need to extend a Slugger class just to generate slugs. php[world] 2016 36
  37. 37. Avoid Code-Duplication with Traits trait EmployeeTrait { public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } } class Employee implements EmployeeInterface { use EmployeeTrait; } class Manager implements EmployeeInterface, ManagerInterface { use EmployeeTrait; use ManagerTrait; } php[world] 2016 37
  38. 38. Taking Advantage of OOP php[world] 2016 38
  39. 39. Coupling php[world] 2016 39
  40. 40. What is Coupling? • Coupling is how dependent your code is on another class • The more classes you are coupled to, the more changes affect your class php[world] 2016 40
  41. 41. class Block extends ConfigEntityBase, implements BlockInterface, EntityWithPluginCollectionInterface { public function getPlugin() { return $this->getPluginCollection()->get($this->plugin); } protected function getPluginCollection() { if (!$this->pluginCollection) { $this->pluginCollection = new BlockPluginCollection( Drupal::service('plugin.manager.block'), $this->plugin, $this->get('settings'), $this->id()); } return $this->pluginCollection; } } php[world] 2016 41
  42. 42. Law of Demeter php[world] 2016 42
  43. 43. Dependency Injection php[world] 2016 43
  44. 44. What is Dependency Injection? • Injecting dependencies into classes, instead of having the class create it • Allows for much easier testing • Allows for a much easier time swapping out code • Reduces the coupling that happens between classes php[world] 2016 44
  45. 45. Method Injection class MapService { public function getLatLong(GoogleMaps $map, $street, $city, $state) { return $map->getLatLong($street . ' ' . $city . ' ' . $state); } public function getAddress(GoogleMaps $map, $lat, $long) { return $map->getAddress($lat, $long); } } php[world] 2016 45
  46. 46. Constructor Injection class MapService { protected $map; public function __construct(GoogleMaps $map) { $this->map = $map; } public function getLatLong($street, $city, $state) { return $this ->map ->getLatLong($street . ' ' . $city . ' ' . $state); } } php[world] 2016 46
  47. 47. Setter Injection class MapService { protected $map; public function setMap(GoogleMaps $map) { $this->map = $map; } public function getMap() { return $this->map; } public function getLatLong($street, $city, $state) { return $this->getMap()->getLatLong($street . ' ' . $city . ' ' . $state); } } php[world] 2016 47
  48. 48. Single Responsibility Principle php[world] 2016 48
  49. 49. Single Responsibility Principle • Every class should have a single responsibility, and that responsibility should be encapsulated in that class php[world] 2016 49
  50. 50. What is a Responsibility? • Responsibility is a “Reason To Change” – Robert C. Martin • By having more than one “Reason to Change”, code is harder to maintain and becomes coupled • Since the class is coupled to multiple responsibilities, it becomes harder for the class to adapt to any one responsibility php[world] 2016 50
  51. 51. An Example /** * Create a new invoice instance. * * @param LaravelCashierContractsBillable $billable * @param object * @return void */ public function __construct(BillableContract $billable, $invoice) { $this->billable = $billable; $this->files = new Filesystem; $this->stripeInvoice = $invoice; } /** * Create an invoice download response. * * @param array $data * @param string $storagePath * @return SymfonyComponentHttpFoundationResponse */ public function download(array $data, $storagePath = null) { $filename = $this->getDownloadFilename($data['product']); $document = $this->writeInvoice($data, $storagePath); $response = new Response($this->files->get($document), 200, [ 'Content-Description' => 'File Transfer', 'Content-Disposition' => 'attachment; filename="'.$filename.'"', 'Content-Transfer-Encoding' => 'binary', 'Content-Type' => 'application/pdf', ]); $this->files->delete($document); return $response; } php[world] 2016 51 https://github.com/laravel/cashier/blob/master/src/Laravel/Cashier/Invoice.php
  52. 52. Why is this Bad? • This single class has the following responsibilities: • Generating totals for the invoice (including discounts/coupons) • Generating an HTML View of the invoice (Invoice::view()) • Generating a PDF download of the invoice(Invoice::download()) • This is coupled to a shell script as well • Two different displays handled by the class. Adding more means more responsibility • Coupled to a specific HTML template, the filesystem, the Laravel Views system, and PhantomJS via the shell script php[world] 2016 52
  53. 53. How to Improve • Change responsibility to just building the invoice data • Move the ‘output’ stuff to other classes php[world] 2016 53
  54. 54. Unit Testing php[world] 2016 54
  55. 55. This is not a testing talk • Using Interfaces makes it easier to mock objects • Reducing coupling and following Demeter’s Law makes you have to mock less objects • Dependency Injection means you only mock what you need for that test • Single Responsibility means your test should be short and sweet • Easier testing leads to more testing php[world] 2016 55
  56. 56. Additional Resources • Clean Code – Robert C. Martin • PHP Objects, Patterns, and Practice – Matt Zandstra php[world] 2016 56
  57. 57. Thank You! • Software Engineer, InQuest • Co-Host of “Jerks Talk Games” • http://jerkstalkgames.com • Author of “Docker for Developers” • https://leanpub.com/dockerfordevs • http://ctankersley.com • chris@ctankersley.com • @dragonmantank php[world] 2016 57

×