When developers are introduced to object-oriented programming, one of the first things they are taught is that nouns turn into objects, verbs into methods, and Dog is a subclass of Animal. OOP is more than just turning things into classes and objects and showing that both Boats and Cars have motors, and that Dogs and Cats both speak(). Let's look at OOP in real-world settings and go beyond cars and dogs to see how to use object-oriented programming properly in PHP. Traits, composition, inheritance – none of it is off limits!
Scale your database traffic with Read & Write split using MySQL Router
OOP Is More Than Cars and Dogs
1. OOP is More Than Cars
and Dogs
Chris Tankersley
php[tek] 2015
php[tek] 2015 1
2. Who Am I
• PHP Programmer for over 10 years
• Work/know a lot of different
languages, even COBOL
• Primarily do Zend Framework 2
• https://github.com/dragonmantank
php[tek] 2015 2
3. Quick Vocabulary Lesson
• Class – Definition of code
• Object – Instantiation of a Class
• Member – Variable belonging to a class
• Method – Function belonging to a class
There will be more as we go along
php[tek] 2015 3
4. php[tek] 2015 4
Class
class Employee {
protected $name; // This is a member
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;
}
}
7. Let’s count the reasons
• Because we’re told to, procedural programming leads to spaghetti
code
• We deal with objects every day, so it shouldn’t be too hard
• We want to allow for code re-use
• We want to group like code together
• We want to easily extend our code
• We want to be able to easily test our code
php[tek] 2015 7
17. What we’re all taught
• Classes are “things” in the real world
• We should construct class members based on Attributes
• Number of wheels
• Sound it makes
• We should construct class methods based on “Actions”
• Running
• Speaking
• Jumping
php[tek] 2015 17
18. 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[tek] 2015 18
21. The Employee Class
php[tek] 2015 21
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;
}
}
22. The Manager Class
php[tek] 2015 22
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;
}
}
23. The Scientist Class
php[tek] 2015 23
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;
}
}
25. 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[tek] 2015 25
26. We use it all the time
namespace ApplicationController;
use ZendMvcControllerAbstractActionController;
use ZendViewModelViewModel;
Class IndexController extends AbstractActionController {
public function indexAction() {
/** @var VendorVendorService $vendor */
$vendor = $this->serviceLocator->get('VendorVendorService');
$view = new ViewModel();
return $view;
}
} php[tek] 2015 26
27. 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[tek] 2015 27
28. How to use it
• Understand the difference between Public, Protected, and Private
• Public – Anyone can use this, even children
• Protected – Anything internal can use this, even children
• Private – This is mine, hands off
• Abstract vs Concrete Classes
• Abstract classes cannot be instantiated directly, they must be extended
php[tek] 2015 28
29. The Employee Class
php[tek] 2015 29
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;
}
}
30. The Manager Class
php[tek] 2015 30
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;
}
}
31. An Example
// Fatal error: Cannot instantiate abstract class Employee
$employee = new Employee();
// We can do this though!
$manager = new Manager();
// Fatal error: Cannot access protected property Manager::$name
$manager->name = 'Bob McManager’;
// setData is public, so we can use that
$manager->setData(['name' => 'Bob McManager’,'number' => 1]);
// We can also view the data, since it's public
$manager->viewData();
php[tek] 2015 31
32. 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[tek] 2015 32
34. 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[tek] 2015 34
35. 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[tek] 2015 35
36. 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[tek] 2015 36
37. 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[tek] 2015 37
38. 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[tek] 2015 38
39. 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[tek] 2015 39
42. 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[tek] 2015 42
43. namespace ApplicationController;
use ZendMvcControllerAbstractActionController;
use ZendViewModelViewModel;
class MapController extends AbstractActionController
{
public function indexAction()
{
// Position is an array with a Latitude and Longitude object
$position = $this->getServiceLocator()->get('MapService’)
->getLatLong('123 Main Street', 'Defiance', 'OH');
echo $position->latitude->getPoint();
}
}
php[tek] 2015 43
46. 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[tek] 2015 46
47. 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[tek] 2015 47
48. 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[tek] 2015 48
49. 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[tek] 2015 49
51. Single Responsibility Principle
• Every class should have a single responsibility, and that responsibility
should be encapsulated in that class
php[tek] 2015 51
52. 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[tek] 2015 52
53. 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[tek] 2015 53
https://github.com/laravel/cashier/blob/master/src/Laravel/Cashier/Invoice.php
54. 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[tek] 2015 54
55. How to Improve
• Change responsibility to just building the invoice data
• Move the ‘output’ stuff to other classes
php[tek] 2015 55
57. [Could not afford licensing fee for Grumpy Testing Picture]
php[tek] 2015 57
58. 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[tek] 2015 58
60. We can make a dog with wheels!
• Abstract class for Animal
• Class for Dog that extends Animal
• Trait for Wheels
• With the write methodology, we could even unit test this
In the real world, we can now represent a crippled dog
php[tek] 2015 60