312
Practical Object-Oriented Design Principles Brandon Savage

Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Practical Object-Oriented Design Principles

Brandon Savage

Page 2: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

What is object-oriented design?

Page 3: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Focus on objects

Page 4: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Focus on extensibility

Page 5: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Focus on reusability

Page 6: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Focus on ease of maintenance

Page 7: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Basic Principles

Page 8: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

SOLID

Page 9: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

• Single Responsibility Principle

• Open/Closed Principle

• Liskov Substitution Principle

• Interface Segregation Principle

• Dependency Inversion Principle

Page 10: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

SOLID is a great acronym. But let’s mix up

the order.

Page 11: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Inversion

Page 12: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

(This is not the dependency injection

principle)

Page 13: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The dependency inversion principle states that high-level modules should not depend on

low-level modules. Both should depend on abstractions.

Also, abstractions should not depend on details; details should depend on abstractions.

Page 14: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

What is Dependency Injection?

Page 15: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Hard-Coded Dependencies

<?php

class Controller { public function __construct() { $this->cache = new ApcCache(); } public function manageData() { return $this->cache->get('someKey'); } }

Page 16: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Hard-Coded Dependencies

<?php

class Controller { public function __construct() { $this->cache = new ApcCache(); } public function manageData() { return $this->cache->get('someKey'); } }

Page 17: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Easy to use.

Hard to test.

Page 18: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Easy to use

<?php

$controller = new Controller();

Page 19: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Easy to use.

Impossible to substitute.

Page 20: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

No substitution.<?php

class Controller { public function __construct() { $this->cache = new ApcCache(); } public function manageData() { return $this->cache->get('someKey'); } }

Page 21: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Service location

Page 22: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Service Location<?php

class Controller { public function __construct($cache = 'APCCache') { $this->cache = CacheLocator::locate($cache); } public function manageData() { return $this->cache->get('someKey'); } }

Page 23: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Service Location<?php

class Controller { public function __construct($cache = 'APCCache') { $this->cache = CacheLocator::locate($cache); } public function manageData() { return $this->cache->get('someKey'); } }

Page 24: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Service Location<?php

class Controller { public function __construct($cache = 'APCCache') { $this->cache = CacheLocator::locate($cache); } public function manageData() { return $this->cache->get('someKey'); } }

Page 25: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Now we’re tied to the locator.

Page 26: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Injection

Page 27: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Injection<?php

class Controller { public function __construct(APCCache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 28: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Injection<?php

class Controller { public function __construct(APCCache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 29: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Harder to use.

Easier to test.

Page 30: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Harder to use

<?php

$controller = new Controller(new APCCache);

Page 31: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Harder to use.

Easier to substitute.

Page 32: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

That’s dependency injection.

No more, no less.

Page 33: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Inversion

Page 34: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Injection refers to the strategy for providing objects to other objects.

It describes a methodology for how to manage dependent objects.

Page 35: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The dependency inversion principle states that high-level modules should not depend on

low-level modules. Both should depend on abstractions.

Also, abstractions should not depend on details; details should depend on abstractions.

Page 36: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

A Caching Interface<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache public function purge();

}

Page 37: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Injection<?php

class Controller { public function __construct(APCCache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 38: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Dependency Inversion<?php

class Controller { public function __construct(Cache $cache) { $this->cache = $cache; } public function manageData() { return $this->cache->get('someKey'); } }

Page 39: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

We rely on the abstraction of the Cache

interface.

Page 40: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

APCCache can’t easily be substituted (LSP Violation)

Page 41: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

By relying on the interface, we make

testing easier.

Page 42: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

A Development Cache<?php

class VoidCache implements Cache {

public function get($key) { return null; }

public function set($key, $value) { return true; }

public function delete($key) { return true; }

// Purge the whole cache public function purge() { return true; } }

Page 43: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Creating Objects

<?php

// Production $controller = new Controller(new APCCache);

// Development $controller = new Controller(new VoidCache);

Page 44: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

How can we do proper dependency injection in

PHP?

Page 45: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

How can we switch dependencies between

production and development?

Page 46: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

DI Containers

Page 47: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

A DI container is simply a programatic way of

injecting dependencies.

Page 48: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

A DI Container<?php

class Container { /** * @var array */ protected $registry = [];

/** * @param $className * @param callable $builder */ public function register($className, callable $builder) { $this->registry[$className] = $builder; }

/** * @param $className * @return mixed */ public function build($className) { if (!isset($this->registry[$className])) { throw new \InvalidArgumentException(

$className . ' is not registered'); }

return $this->registry[$className](); } }

Page 49: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Caching Strategies<?php

// production.php

return ['cacheStrategy' => 'APCCache',

];

// dev.php

return ['cacheStrategy' => 'VoidCache',

];

Page 50: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Building Our Objects

<?php

$di->register('Controller', function() use ($config, $di) { return new Controller( $di->build($config['cacheStrategy']) ); });

Page 51: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP DI Containers

• Pimple

• PHP-DI

• Aura.Di

• All Major Frameworks

Page 52: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

DI containers belong in the bootstrapping layer.

Page 53: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

DI Container Antipattern<?php

class User_Controller { public function __construct(Di $di) { $this->di = $di; }

public function getUserContacts() { $cache = $di->get('cache'); if ($contacts = $cache->get('user_contacts')) { return $contacts; } } }

Page 54: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Service location is not dependency inversion!

Page 55: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Not Dependency Inversion<?php

function delete($id) { $profile = Profile::prospect()->find($id);

if (!$profile) { return Rest::notFound(); } if ($profile->delete()) { return Rest::okay([]); } return Rest::conflict(); }

Page 56: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

If we’re using abstractions, we can easily substitute one object for another.

Page 57: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Liskov Substitution Principle

Page 58: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The Liskov substitution principle states that objects in a program should be replaceable

with instances of their subtypes without altering the correctness of the program.

E.g. if A is a type of D, then A can replace instances of D.

Page 59: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

We’re programming to an interface, not an implementation.

Page 60: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Design by contract

Page 61: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Design by contract is a philosophy which states that designers should define formal,

precise and verifiable interface specifications for software components.

Page 62: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

A Contract<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache public function purge();

}

Page 63: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

All instances of Cache can be substituted for

one another!

Page 64: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Substitution

<?php

class Controller { public function __construct(Cache $cache) { $this->cache = $cache; } }

$controller = new Controller(new File());

$controller2 = new Controller(new APC());

Page 65: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

What happens if our subclass changes the

interface?

Page 66: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Caching Interface<?php

interface Cache {

public function get($key);

public function set($key, $value);

public function delete($key);

// Purge the whole cache public function purge();

}

Page 67: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Extending the Interface

<?php

class MemcacheCache implements Cache { // all the other methods public function reconnect() { $this->memcache->connect($this->host, $this->port); } }

Page 68: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

LSP Breaks<?php

class User_Controller { public function __construct(Cache $cache) { $this->cache = $cache; }

public function getUserContacts() { $cache->reconnect(); if ($contacts = $cache->get('user_contacts')) { return $contacts; } } }

Page 69: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Open/Closed Principle

Page 70: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Software entities (classes, modules, functions, etc.) should be open for extension, but closed

for modification.

Page 71: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Two kinds of open/closed principle

Page 72: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Meyer’s Open/Closed Principle

Page 73: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Classes are open for extension through inheritance, but closed

to internal modification.

Page 74: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

- Bertrand Meyer

A class is closed, since it may be compiled, stored in a library, baselined, and used by client classes. But it is also open, since any new class

may use it as parent, adding new features. When a descendant class is defined, there is no need to change the original or to disturb its

clients.

Page 75: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Polymorphic Open/Closed Principle

Page 76: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Once defined, the interface cannot be changed (but

the internals can)

Page 77: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

“Interface” refers to the publicly visible methods.

Page 78: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

While the original interface should be honored, implementation is up

to the developer.

Page 79: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The interface should not be modified.

Page 80: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

When modifying the interface, you change an

object’s type.

Page 81: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Class vs. Type

Page 82: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Class is the name of the class that defines a particular object.

Page 83: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Type is the behavior of the object as expressed through public methods.

Page 84: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Extending the interface changes the object’s

type.

Page 85: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

New Object Type<?php

class User_Controller { public function __construct(MemcacheCache $cache) { $this->cache = $cache; }

public function getUserContacts() { $cache->reconnect(); if ($contacts = $cache->get('user_contacts')) { return $contacts; } } }

Page 86: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The return type is part of the interface, too!

Page 87: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Single Return Type

Page 88: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

(All Others Use Exceptions)

Page 89: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP 7 Return Type Declaration

Page 90: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Return Type Declaration<?php

interface Cache {

public function get($key): string;

public function set($key, $value): bool;

public function delete($key): bool;

// Purge the whole cache public function purge(): bool;

}

Page 91: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

So if extending the interface isn’t allowed, what kind of interfaces do we want?

Page 92: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Interface Segregation

Page 93: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The interface segregation principle states that no object should be forced to depend upon or

implement methods it does not use.

Smaller interfaces are better.

Page 94: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Small Interfaces

<?php

interface Countable { public function count(); }

Page 95: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Small Interfaces<?php

interface Iterator extends Traversable { public function current(); public function key(); public function next(); public function rewind(); public function valid(); }

Page 96: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Small Interfaces<?php

class ArrayClass implements Iterator, Countable { public $collection = []; public function count() { return count($this->collection); }

public function current() { return current($this->collection); }

public function key() { return key($this->collection); }

public function next() { return next($this->collection); }

public function rewind() { reset($this->collection); }

public function valid() { return (bool) $this->current(); } }

Page 97: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Objects can have more than one type!

Page 98: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Object Types<?php

class ArrayClass implements Iterator, Countable { public $collection = []; public function count() { return count($this->collection); }

public function current() { return current($this->collection); }

public function key() { return key($this->collection); }

public function next() { return next($this->collection); }

public function rewind() { reset($this->collection); }

public function valid() { return (bool) $this->current(); } }

Page 99: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Small interfaces help us keep objects discrete and single-focused.

Page 100: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Single Responsibility Principle

Page 101: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The single responsibility principle states that every class should have a single responsibility

and that responsibility should be entirely encapsulated by that class. All its services

should be narrowly aligned with that responsibility.

Page 102: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

One class, one job.

Page 103: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

One class, one job.

Page 104: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

One class, one responsibility.

Page 105: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

One domain.

Page 106: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

A class can have many jobs, but a single domain.

Page 107: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PDO’s Jobs

• Begin and end transactions

• Establish and maintain connection to the database.

• Send simple queries to the database.

• Prepare complex queries, returning a statement object.

Page 108: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PDOStatement’s Jobs

• Represent a single PDO statement.

• Execute the PDO statement.

• Return the results.

Page 109: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PDO’s domain is over the connection and queries

to the connection.

Page 110: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PDOStatement’s domain is over a specific prepared statement and its results.

Page 111: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

…entirely encapsulated…

Page 112: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

“A single reason to change.” ~ Robert Martin

Page 113: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

- Robert Martin

Gather together the things that change for the same reasons. Separate those things that

change for different reasons.

Page 114: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Law of Demeter

Page 115: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Who can my objects talk to?

Page 116: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

What should my objects know?

Page 117: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The Law of Demeter (LoD) states that objects should have limited knowledge. Specifically,

they should have limited knowledge about other units, only talk to its friends, not strangers, and

only talk to its immediate friends.

Page 118: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Limited knowledge

Page 119: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Unit Knowledge

<?php

class MyController { public function __construct( Memcache $memcacheConnection ) { $this->cache = new Memcache_Cache($memcacheConnection); } }

Page 120: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Only Close Friends

<?php

class MyController { public function someMethod() { $this->object ->someParameter ->someOtherParameter ->someMethod(); } }

Page 121: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Talk only to close friends.

Page 122: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Formal Law of Demeter

• The Object itself.

• The method’s parameters.

• Any objects created/instantiated by the method.

• The Object’s component objects.

From Wikipedia

Page 123: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Advantages

Page 124: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Lower coupling.

Page 125: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Maintainability.

Page 126: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Adaptability.

Page 127: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Disadvantages

Page 128: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Lots of wrapper calls.

Page 129: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

May not be entirely practical.

Page 130: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Antipatterns In Object-Oriented Design

Page 131: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Singleton

Page 132: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across

the system.

Page 133: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The Singleton<?php

class MySingleton {

private static $instance;

private function __construct() {}

private function __clone() {}

public static function getInstance() { if (!(self::$instance instanceof MySingleton)) { self::$instance = new MySingleton(); } return self::$instance; }

}

Page 134: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Singletons themselves aren’t bad.

Using them inappropriately is bad.

Page 135: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Most problems can be solved without a

singleton.

Page 136: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Tight Coupling

Page 137: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions
Page 138: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Tight Coupling slowly strangles a code base.

Page 139: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Law of Demeter

Page 140: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Untestable Code

Page 141: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Tightly coupled code can’t be easily tested.

Page 142: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Untestable

<?php

class Database { public function __construct() { $this->cache = new ApcCache(); } }

Page 143: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Testable

<?php

class Database { public function __construct(Cache $cache) { $this->cache = $cache; } }

Page 144: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Proper testing often requires stubs, substitutes,

mocks and doubles.

Page 145: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Premature Optimization

Page 146: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

It can be tempting to optimize your code as

you write it.

Page 147: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Avoid the temptation.

Page 148: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

When optimizing, you need real-world

conditions.

Page 149: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

When optimizing, you need to measure.

Page 150: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Avoid micro-optimizations.

Page 151: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Avoid being clever.

Page 152: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Don’t Repeat Yourself

Page 153: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Duplication is a hallmark of tight coupling

Page 154: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Duplicated code means duplicated tests.

Page 155: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

N duplication means N opportunities for

mistakes.

Page 156: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Indescriptive Naming

Page 157: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Code is written for humans.

Communicate purpose.

Page 158: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Pick names that matter.

Page 159: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Bad names

$i

$abc$def

$foo

$thing$that

Page 160: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Good names

$forIterator

$cache$masterDatabase

$deleteUserCommand

$jobQueue$createUserEvent

Page 161: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Use actual words in docblocks!

Page 162: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Bad Dockblock

<?php

/** * @param $xdd */ public function processXdd($xdd) { // do something }

Page 163: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Good Docblock

<?php

/** * Processes a single XDD for removal and archive. * * @param $xdd */ public function processXdd($xdd) { // do something }

Page 164: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions

Page 165: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are the default error handling for object-oriented programming.

Page 166: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are objects.

Page 167: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are objects<?php

$object = new Exception;

var_dump($object);

// object(Exception)[1] protected 'message' => string '' (length=0) private 'string' => string '' (length=0) protected 'code' => int 0 protected 'file' => string '/Users/brandon/Sites/test.php' (length=29) protected 'line' => int 3 private 'trace' => array (size=0) empty private 'previous' => null

Page 168: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are exceptional!

(not for control flow)

Page 169: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

You shouldn’t expect an exception.

(but you should handle it)

Page 170: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The Exception API

Page 171: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are Extensible

<?php

class MyException extends Exception {}

Page 172: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are extended to make it easier to differentiate between different kinds of errors.

We can catch specific types of exceptions through type hinting.

Page 173: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Throwing An Exception

<?php

throw new MyException('Something went wrong');

Page 174: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are thrown in PHP.

Page 175: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exceptions are special - they’re the only objects

you can throw.

Page 176: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Handling an exception means you caught it.

Page 177: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Catching Exceptions

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 178: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Catching Exceptions

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 179: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

We type-hint the exception.

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 180: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

We can catch multiple exception types.

<?php

function caughtExceptions() { try { // do something dangerous } catch (Exception1 $e) { // Do stuff } catch (Exception2 $e) { // do other stuff. } }

Page 181: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Multiple exception catching rules

• PHP will execute the first block that matches the exception type.

• You can rethrow an exception, but it will only be caught if it’s inside a try-catch block.

• Type-hint on specific exceptions first, before type-hinting on broader exception types (like \Exception)

Page 182: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exception Catching Rules

<?php

class MyException extends Exception {}

class MyExceptionTwo extends MyException {}

Page 183: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyException $e) { echo 'MyException'; } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (Exception $e) { echo 'Exception'; }

Page 184: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyException $e) { echo 'MyException'; } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (Exception $e) { echo 'Exception'; }

Page 185: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (MyException $e) { echo 'MyException'; } catch (Exception $e) { echo 'Exception'; }

Page 186: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (MyException $e) { echo 'MyException'; } catch (Exception $e) { echo 'Exception'; }

Page 187: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Finally

Page 188: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Finally<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo $e) { echo 'MyExceptionTwo'; } catch (MyException $e) { echo 'MyException'; } catch (Exception $e) { echo 'Exception'; } finally { echo "This will always be executed."; }

Page 189: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Finally is always executed.

Even if the exception is unhandled.

Page 190: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

In PHP 7, errors become exceptions.

Page 191: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Errors as ExceptionsThrowable

Error Exception

AssertionError ParseError TypeError

Page 192: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

All exceptions now implement the Throwable

interface.

Page 193: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Throwable Interface

<?php

interface Throwable { public function getMessage(); public function getCode(); public function getFile(); public function getLine(); public function getTrace(); public function getTraceAsString(); public function getPrevious(); public function __toString();

}

Page 194: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

All errors are catchable.

Page 195: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

(You should not ignore fatal errors)

Page 196: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Hint on “Throwable” to catch all errors.

Page 197: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

You can’t implement Throwable yourself.

Page 198: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

No Implementing Throwable

<?php

class ExceptionA implements Throwable { }

new ExceptionA;

//

Fatal error: Class ExceptionA cannot implement interface Throwable, extend Exception or Error instead

Page 199: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP 7.1: Multi-Catch Exception Handling

Page 200: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Exception Catching Rules

<?php

try { throw new MyExceptionTwo('test'); } catch (MyExceptionTwo | MyException $e) { echo 'MyExceptionTwo'; } catch (Exception $e) { echo 'Exception'; }

Page 201: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Rules for Exception Handling

Page 202: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

#1. Always handle exceptions at the lowest logical layer, rather than letting them bubble up.

E.g. if a database layer throws an exception, handle it in the layer that called the database

layer.

Page 203: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

#2. Each layer should be capable of throwing its own exceptions, and should not rely on the

exceptions of another layer.

E.g. if your model receives a database exception it cannot handle, it should throw its own exception. It’s okay to wrap the database

exception.

Page 204: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

#3. If something can go wrong, it should have an expected exception.

Page 205: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

#4. Do not allow for a return value of null from a method on an error condition. Throw an

exception instead.

Page 206: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Bad

<?php

function checkMath($a, $b, $answer) { if($a + $b == $answer) { return $answer; } else { return false; } }

Page 207: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Good

<?php

function checkMath($a, $b, $answer) { if($a + $b == $answer) { return $answer; } else { throw new \Exception('The math didn\'t check out!'); } }

Page 208: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

#5. Expect exceptions.

Page 209: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Page 210: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

How we do it now: Model-View-Controller

Page 211: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Classic MVCModel

View Controller

User

Page 212: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

MVC has a number of problems.

Page 213: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

We don’t actually follow this.Model

View Controller

User

Page 214: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

We follow this.Model

View Controller

User

Page 215: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Actually, we kinda do this.Model

Template Controller

User

Page 216: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Our controller ends up doing all the work.

Page 217: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

Request

Page 218: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

Request

Page 219: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

Request

Business Logic

Page 220: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

RequestAlgorithms

Database Logic

Page 221: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

RequestAlgorithms

Database Logic

Template System

Page 222: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

RequestAlgorithms

Database Logic

Template System

Page 223: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

RequestAlgorithms

Database Logic

Template System

Authentication

Page 224: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model Controller View

RequestAlgorithms

Database Logic

Template SystemAuthentication

Page 225: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

That’s a lot of responsibilities.

Page 226: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Introducing a better way

Action-Domain-Responder

Page 227: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action-Domain-Responder

Action

ResponderDomain

Page 228: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

ADR solves many problems associated with

MVC.

Page 229: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action vs. Controller• Controllers contain several related methods.

Actions are specific to a particular request.

• Controllers interact with both the model and the presentation layer. Actions interact only with the model.

• Actions are responsible for shuffling request data into the domain, and response data out.

Page 230: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Model vs. Domain

• Models are (classically) expected to interact directly with the view. Domains do not interact with the responder.

• The domain does not modify the view directly.

Page 231: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

View vs. Responder• “The template is not the view.”

• Responders have a 1:1 relationship to specific actions.

• Responders are responsible for determining how to respond to the request.

• Responders may incorporate a template system.

• The action does not know anything about the presentation logic. This is entirely the responder’s domain.

Page 232: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Page 233: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request

Page 234: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request

Page 235: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request

Business Logic

Page 236: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request Business Logic

Page 237: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request Business Logic

Template System

Page 238: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request Business Logic Template System

Page 239: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request Business Logic Template System

Authentication

Page 240: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Action Domain Responder

Request Business Logic Template SystemAuthentication ACLs

Page 241: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-7 And HTTP Messages

Page 242: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The problem we’re trying to solve

Page 243: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

We want a common way of accepting requests and dispatching responses via HTTP.

PHP was developed primarily for web development, but in many ways sucks at this.

Page 244: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-7 provides a common set of interfaces for HTTP messages (both request and response).

Page 245: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-7 RFCs

• HTTP Messages as described in RFC 7230 and RFC 7231

• URIs as described in RFC 3986

Page 246: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP uses HTTP Request messages in two ways

Page 247: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

1. Sending an HTTP request

Via cURL, stream layer, or some other means.

Page 248: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

#2 Process an incoming HTTP request.

A user makes a request to a web page that is powered and processed by PHP.

Page 249: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Sending a request

Page 250: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP does not have built-in support for HTTP

messages.

Page 251: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

You must use an add-on for HTTP requests

Page 252: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Mechanisms for requests

• PHP Streams

• The cURL extension

• ext/http

Page 253: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Streams are most common (and built into

PHP)

Page 254: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

cURL is available, powerful, but not built-in.

Page 255: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Same with ext/http. (Plus, no examples)

Page 256: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Most modern libraries making HTTP requests use abstraction so they can use any available HTTP

sending mechanism.

Page 257: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Receiving a request

Page 258: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP’s SAPI processes query strings and bodies into $_GET and $_POST.

Page 259: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

It also takes care of processing cookies, files

and server variables.

Page 260: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

A history of PHP development

Page 261: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

First, PHP was a templating language.

Page 262: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Superglobals were used everywhere, because that

was all we had.

Page 263: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP 4 came along and gave us objects.

Page 264: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP 5 came along and gave us better objects.

Page 265: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Objects begat object-oriented frameworks.

Page 266: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Frameworks began defining their own HTTP

components.

Page 267: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-0 standardized autoloading (+ PSR-4)

Page 268: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Composer made library building and distribution

easy.

Page 269: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Libraries for HTTP began to proliferate.

Page 270: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The PHP-FIG saw a need and created PSR-7.

Page 271: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Why PSR-7?

Page 272: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Multiple HTTP implementations is

confusing and wasteful.

Page 273: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Different HTTP specifications makes

interoperability impossible.

Page 274: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Direct use of superglobals is considered harmful.

Page 275: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

So the PHP-FIG set out to abstract both client- and server-side

request and response interfaces.

Page 276: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Interoperability

Page 277: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

So what did FIG actually do?

Page 278: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Interfaces

Page 279: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-7 Interfaces• Psr\Http\Message\MessageInterface;

• Psr\Http\Message\RequestInterface;

• Psr\Http\Message\ServerRequestInterface;

• Psr\Http\Message\ResponseInterface;

• Psr\Http\Message\StreamInterface;

• Psr\Http\Message\UriInterface;

• Psr\Http\Message\UploadedFileInterface;

Page 280: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The MessageInterface

Page 281: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

RequestInterface and ResponseInterface extend

from MessageInterface

Page 282: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

URI’s as objects

Page 283: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Immutability

Page 284: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Immutability is the property of being unchangeable. Whenever an immutable

request/response object is “modified”, the return value is a brand new object with the

requested modification. The original remains the same.

Page 285: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Streams

Page 286: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Streams can be hard to work with (StreamInterface

seeks to solve for this)

Page 287: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Streams are mutable!

Page 288: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Stream immutability is impossible to enforce.

Page 289: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Why is there a ServerRequestInterface?

Page 290: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Server-side requests have special needs.

Page 291: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Access to $_GET, $_POST, $_SERVER,

$_COOKIE and $_FILES.

Page 292: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Access to the URI for routing.

Page 293: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Notice: the return value of ServerRequestInterface::

getParsedBody() is indeterminate.

Page 294: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Files are objects.

Page 295: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

This fixes several problems with PHP.

Page 296: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

array( 'files' => array( 'name' => array( 0 => 'file0.txt', 1 => 'file1.html', ), 'type' => array( 0 => 'text/plain', 1 => 'text/html', ), /* etc. */ ), )

Page 297: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PHP won’t process files not in a POST request

(think PUT).

Page 298: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-15 HTTP Server Request Handlers

Page 299: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-15 defines a standard for creating reusable middleware.

Page 300: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-15 extends the PSR-7 concept by integrating it

directly into the middleware.

Page 301: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Approaches to Middleware Design

Page 302: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The “Double Pass”

<?php

public function handle( ServerRequestInterface $request, ResponseInterface $response, callable $next ) : ResponseInterface;

Page 303: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The “Single Pass” (Lambda)

<?php

public function handle( ServerRequestInterface $request, callable $next ) : ResponseInterface;

Page 304: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Middleware has no access to a response until one is generated

by the request handler.

Page 305: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

PSR-15 uses the single pass.

Page 306: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions
Page 307: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The RequestHandlerInterface

<?php

namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface;

interface RequestHandlerInterface { public function handle(

ServerRequestInterface $request): ResponseInterface; }

Page 308: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The “end of the line.”

Page 309: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

RequestHandlerInterface MUST return a response.

Page 310: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

The MiddlewareInterface

<?php

namespace Psr\Http\Server;

use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface;

interface MiddlewareInterface { public function process(

ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface;

}

Page 311: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

MUST return a response (but may delegate)

Page 312: Practical Object-Oriented Design Principles · The dependency inversion principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions

Questions?

Brandon Savage [email protected] @brandonsavage on Twitter