OOP for Drupal Developers

D33d8bdd9096c80b8d1acca8d28410b5?s=47 Lorna Mitchell
September 29, 2014

OOP for Drupal Developers

Introduction to Object-Oriented PHP for DrupalCon Amsterdam

D33d8bdd9096c80b8d1acca8d28410b5?s=128

Lorna Mitchell

September 29, 2014
Tweet

Transcript

  1. None
  2. What?

  3. What is OOP? Object orientation is the computer science of

    long words lornajane
  4. OOP Glossary • methods these are functions. Seriously • properties

    these are variables • class recipe for making an object • object cool thing with its own properties and methods
  5. Why?

  6. Encapsulation In OOP: • array indexes become “properties” • functions

    lose their prefix and become “methods” An object contains both data and functionality
  7. Encapsulation Imagine an array of data $date = array("hours" =>

    "14", "minutes" => "36", "timezone" => "Europe/Amsterdam"); And some functions date_get_timestamp($date); date_get_time($date); date_get_datetime_in_timezone($date, $tz);
  8. Autoloading Code in classes can be autoloaded. This means: •

    you will never write another require() statement • only the code actually needed will ever be parsed
  9. How Autoloading Works

  10. Prerequisites for Autoloading • one class per file • very

    predictable mapping from namespaced classname to file location • e.g. \MyLib\User lives in lib/MyLib/User.php For more details, see PSR4: http://www.php-fig.org/psr/psr-4/
  11. Write an Autoloader Write a function which accepts the classname

    and includes the relevant file. To tell PHP about your autoloader: spl_autoload_register("my_autoload_func");
  12. Extendable We can take an object, and just change one

    bit or add something, without copy pasting. The keyword is “inheritance”
  13. Inheritance Take this ClickableButton class and make it go ping

    when clicked. class PingingButton extends ClickableButton Our new class gets everything that was in ClickableButton and we can add/override as we wish
  14. Pluggable Code expecting one type of object can easily accept

    a similar one. We can extend or replace objects without changing the code that handles it.
  15. How?

  16. Start With a Class We’ll create a class with properties

    and methods.
  17. PHP User Class Start by declaring the class and its

    properties … 1 class User { 2 protected $username; 3 protected $email;
  18. PHP User Class … next the constructor … 1 public

    function __construct($username) { 2 // lookup table for people's details 3 $people = [ 4 "lornajane" => "lorna@lornajane.net", 5 "crell" => "larry@...", 6 "emmajanehw" => "emma@..." 7 ]; 8 9 $this->username = $username; 10 $this->email = $people[$username]; 11 12 }
  19. PHP User Class … and a couple of other methods

    1 public function getUsername() { 2 return $this->username; 3 } 4 5 public function getGravatar() { 6 $url = "http://www.gravatar.com/avatar/" 7 . md5($this->email); 8 return "<img src=\"$url\">"; 9 } 10 }
  20. How to Use a Class First we need to require

    or autoload the code. Then we instantiate the object.
  21. Autoloader spl_autoload_register(function ($classname) { require __DIR__ . '/../inc/' . $classname

    . '.php'; });
  22. Instantiate a User Object Create an object by using the

    new keyword $user = new User("lornajane");
  23. User Object in a Template This is what I have

    in my template file <?=$user->getGravatar()?> Hi, <?=$user->getUsername()?>
  24. Project Overview . ├── inc │ └── User.php ├── public

    │ └── index.php ├── README.md └── templates ├── footer.php ├── greet.php ├── header.php └── login.php
  25. Look What We Made

  26. Look What We Made

  27. And Breathe

  28. Other Cool OOP Things • inheritance • namespaces • interfaces

    • exceptions
  29. Inheritance We have a User class. How about something a

    little more specific? 1 class AdminUser extends User { 2 public function getUsername() { 3 $name = parent::getUsername(); 4 return $name . " (admin)"; 5 } 6 }
  30. Use the Admin Class If the user ticked the box,

    we stored that too 1 if($_SESSION['admin']) { 2 $user = new AdminUser($_SESSION['username']); 3 } else { 4 $user = new User($_SESSION['username']); 5 }
  31. Now Look

  32. Now Look

  33. What About StdClass? The StdClass class is just the parent

    of all other objects in PHP It’s better to declare a class for a specific use
  34. Namespaces Namespaces allow us to separate our code, and use

    multiple libraries
  35. Namespaces We change class declarations and rename files to match

    inc/User.php becomes inc/User/User.php namespace User; class User { inc/AdminUser.php becomes inc/User/Admin.php namespace User; class Admin extends User {
  36. Project Overview . ├── inc │ ├── Animal │ │

    └── Cat.php │ └── User │ ├── Admin.php │ └── User.php ├── public │ └── index.php ├── README.md └── templates └── ...
  37. Namespaces The autoloader needs to change as well 1 spl_autoload_register(function

    ($classname) { 2 require __DIR__ . '/../inc/' 3 . str_replace('\\', DIRECTORY_SEPARATOR, $classname) 4 . '.php'; 5 });
  38. Namespaces We also need to give the qualified name of

    the object when instantiating it: $user = new \User\User("lornajane");
  39. Namespaces New classes can go into new namespaces 1 namespace

    Animal; 2 3 class Cat { 4 protected $username; 5 protected $email; 6 7 public function __construct() { 8 // I only know one cat 9 $this->username = "orbit"; 10 $this->email = "orbit@lornajane.net"; 11 }
  40. Using Another Object Let’s instantiate and use some objects //

    featured users $featured = array( new \User\User("emmajanehw"), new \Animal\Cat()); <h2>Other Excellent Users Of This Site:</h2> <?php foreach($featured as $user): ?> <?=$user->getGravatar()?>&nbsp; <?php endforeach; ?>
  41. Showing Off Featured Users

  42. Object Identity How can we check if an object has

    a getGravatar() method?
  43. Object Identity How can we check if an object has

    a getGravatar() method? Check the object type with instanceof • but one is a Cat and one is a User, and what if we add more types?
  44. Interfaces An interface is a contract. It defines what an

    object can do. We could make a Picturesque interface interface Picturesque { public function getGravatar(); } Good news: autoloading works for interfaces!
  45. Interfaces A Class an implement an interface. Class Cat implements

    Picturesque If it does, it MUST have all the methods defined exactly as in the interface. (ours already do)
  46. Object Identity How can we check if an object has

    a getGravatar() method? Check the object type with instanceof • but one is a Cat and one is a User, and what if we add more types?
  47. Object Identity How can we check if an object has

    a getGravatar() method? Check the object type with instanceof • but one is a Cat and one is a User, and what if we add more types? Only accept objects which implement the Picturesque interface
  48. TypeHinting How to require that a particular interface is used

    function addFeaturedUser(Picturesque $user) { return $user; } Adapt our code to use this function // featured users $featured[] = addFeaturedUser(new \User\User("emmajanehw")); $featured[] = addFeaturedUser(new \Animal\Cat());
  49. Exceptions Exceptions are: • beautiful errors • objects • opportunities

  50. Exceptions in PHP Exceptions are Objects; they can be extended

    PHP itself throws exceptions: $db = new PDO("foo", "bar"); PHP Fatal error: Uncaught exception ‘PDOExceptio with message ‘invalid data source name’
  51. Exceptions in PHP We should catch exceptions: 1 try {

    2 // success case code goes here 3 $db = new PDO("foo", "bar"); 4 } catch(PDOException $e) { 5 echo "some DB fail occurred"; 6 }
  52. Throwing Exceptions function addTwoNumbers($a, $b) { if(($a == 0) ||

    ($b == 0)) { throw new Exception("Zero is Boring!"); } return $a + $b; } echo addTwoNumbers(3,2); // 5 echo addTwoNumbers(5,0); // error!! Fatal error: Uncaught exception 'Exception' with message 'Zero is Boring!'
  53. Extending Exceptions We can extend the Exception class for our

    own use 1 class DontBeDaftException extends Exception { 2 } 3 4 function inspectCurtains($colour) { 5 if($colour == "orange" || $colour == "spotty") { 6 throw new DontBeDaftException($colour . 'is daft'); 7 } 8 echo "The curtains are $colour\n"; 9 }
  54. And Breathe

  55. And Breathe (just one more thing)

  56. Duplicate Functionality Did you notice that the getGravatar() method is

    identical in those unrelated classes?
  57. Traits Traits are simply compiler-assisted copy/paste. Rasmus Lerdorf

  58. Traits A trait looks a lot like a class, or

    a bit of one 1 trait Gravatar { 2 public function getGravatar() { 3 $url = "http://www.gravatar.com/avatar/" 4 . md5($this->email); 5 return "<img src=\"$url\">"; 6 } 7 } Good news: autoloading works for traits!
  59. Traits Put a trait into your class with the use

    keyword: 1 namespace User; 2 3 class User implements \Picturesque { 4 protected $username; 5 protected $email; 6 7 use \Gravatar; 8 9 public function __construct($username) { 10 ....
  60. Project Overview . ├── inc │ ├── Animal │ │

    └── Cat.php │ ├── Gravatar.php │ ├── Picturesque.php │ └── User │ ├── Admin.php │ └── User.php ├── public │ └── index.php ├── README.md └── templates └── ...
  61. That’s all, folks

  62. TL;DR • class recipe for making an object • object

    an item with properties and methods • inheritance use one class as basis for another • interface contract agreeement for classes • typehinting how to identify a class • trait how to use the same functionality in two unrelated classes
  63. Questions? (and resources) Intermediate PHP http://lrnja.net/php-video Contact me: @lornajane or

    http://lornajane.net
  64. None