Slide 1

Slide 1 text

Tommy Mühle | tommy-muehle.io Defensive programming (with PHP) 1

Slide 2

Slide 2 text

Tommy Mühle | tommy-muehle.io Tommy Mühle 
 Software Engineer and Author 2

Slide 3

Slide 3 text

Tommy Mühle | tommy-muehle.io Why defensive programming? 3

Slide 4

Slide 4 text

Tommy Mühle | tommy-muehle.io 4

Slide 5

Slide 5 text

Tommy Mühle | tommy-muehle.io Defend your code against misuse by others 5

Slide 6

Slide 6 text

Tommy Mühle | tommy-muehle.io Let’s go defense 6

Slide 7

Slide 7 text

Tommy Mühle | tommy-muehle.io Use PHP7 7

Slide 8

Slide 8 text

Tommy Mühle | tommy-muehle.io Scalar type declarations 8

Slide 9

Slide 9 text

Tommy Mühle | tommy-muehle.io 9 class Counter { private $count = 0; public function add(int $count) { $this->count += $count; } }

Slide 10

Slide 10 text

Tommy Mühle | tommy-muehle.io Return type declarations 10

Slide 11

Slide 11 text

Tommy Mühle | tommy-muehle.io 11 class Customer { private $isVerified = false; public function verify() : void { $this->isVerified = true; } public function isVerified() : bool { return $this->isVerified; } }

Slide 12

Slide 12 text

Tommy Mühle | tommy-muehle.io declare(strict_types=1); 12

Slide 13

Slide 13 text

Tommy Mühle | tommy-muehle.io 13 https:/ /github.com/krakjoe/autostrict

Slide 14

Slide 14 text

Tommy Mühle | tommy-muehle.io If you haven’t PHP7 14

Slide 15

Slide 15 text

Tommy Mühle | tommy-muehle.io Check parameters by yourself 15

Slide 16

Slide 16 text

Tommy Mühle | tommy-muehle.io 16 class Counter { public function add($count) { $this->ensureIsInteger($count); // ... } private function ensureIsInteger($value) { // ... throw new \RuntimeException('...'); } }

Slide 17

Slide 17 text

Tommy Mühle | tommy-muehle.io 17 https:/ /github.com/beberlei/assert

Slide 18

Slide 18 text

Tommy Mühle | tommy-muehle.io 18 use Assert\Assertion; class Counter { private $count; /** * @param int $count */ public function add($count) { Assertion::integer($count); $this->count += $count; } }

Slide 19

Slide 19 text

Tommy Mühle | tommy-muehle.io Options for all versions 19

Slide 20

Slide 20 text

Tommy Mühle | tommy-muehle.io Injections only via constructor 20

Slide 21

Slide 21 text

Tommy Mühle | tommy-muehle.io 21 class Person { private $address; /** * @param Address $address */ public function __construct(Address $address) { $this->address = $address; } }

Slide 22

Slide 22 text

Tommy Mühle | tommy-muehle.io You can also use named constructors 22

Slide 23

Slide 23 text

Tommy Mühle | tommy-muehle.io 23 class Person { // ... /** * @param Address $address */ public function __construct(Address $address){ ... } public static function createDetailedPerson( Address $address, Details $details) { $person = new self($address); $person->details = $details; return $person; } }

Slide 24

Slide 24 text

Tommy Mühle | tommy-muehle.io Setter for everything 24

Slide 25

Slide 25 text

Tommy Mühle | tommy-muehle.io https:/ /speakerdeck.com/ tommymuehle/why-setters-are-bad 25

Slide 26

Slide 26 text

Tommy Mühle | tommy-muehle.io Few public methods 26

Slide 27

Slide 27 text

Tommy Mühle | tommy-muehle.io Use objects instead of primitives 27

Slide 28

Slide 28 text

Tommy Mühle | tommy-muehle.io What? 28

Slide 29

Slide 29 text

Tommy Mühle | tommy-muehle.io 29 Avoid primitive types class Person { // ... public function addEmail(string $email) { // ... } public function addAddress(array $address) { // ... } }

Slide 30

Slide 30 text

Tommy Mühle | tommy-muehle.io 30 class Person { // ... public function addEmail(EmailAddress $email) { // ... } public function addAddress(Address $address) { // ... } } Use value objects

Slide 31

Slide 31 text

Tommy Mühle | tommy-muehle.io 31 final class Email { private $mailbox; private $host; public function __construct(string $email) { if (false === strpos($email, '@')) { throw new \InvalidArgumentException( 'This does not look like an email!'); } list($this->mailbox, $this->host) = explode('@', $email); } public function __toString() { return sprintf('%s@%s', $this->mailbox, $this->host); } }

Slide 32

Slide 32 text

Tommy Mühle | tommy-muehle.io Mark classes as final 32

Slide 33

Slide 33 text

Tommy Mühle | tommy-muehle.io 33 abstract class User { // ... } final class GuestUser extends User { // ... }

Slide 34

Slide 34 text

Tommy Mühle | tommy-muehle.io 33 abstract class User { // ... } final class GuestUser extends User { // ... }

Slide 35

Slide 35 text

Tommy Mühle | tommy-muehle.io Private properties by default 34

Slide 36

Slide 36 text

Tommy Mühle | tommy-muehle.io No uninitialized properties 35

Slide 37

Slide 37 text

Tommy Mühle | tommy-muehle.io No mixed properties 36

Slide 38

Slide 38 text

Tommy Mühle | tommy-muehle.io Avoid traits if you’re unsure 37

Slide 39

Slide 39 text

Tommy Mühle | tommy-muehle.io 38 trait EventGenerator { private $events; public function releaseEvents() { $pendingEvents = $this->events; $this->events = []; return $pendingEvents; } protected function raiseEvent(DomainEvent $event) { $this->events[] = $event; } }

Slide 40

Slide 40 text

Tommy Mühle | tommy-muehle.io Extremely defensive 39

Slide 41

Slide 41 text

Tommy Mühle | tommy-muehle.io No mixed return types 40

Slide 42

Slide 42 text

class Person { private $birthday; /** * @return null|int */ public function getAge() { if (!isset($this->birthday)) { return null; } // Calculate $age ... return $age; } } Tommy Mühle | tommy-muehle.io 41

Slide 43

Slide 43 text

Tommy Mühle | tommy-muehle.io 42 class Person { private $birthday; /** * @return int * @throws InvalidArgumentException */ public function getAge() { if (!isset($this->birthday)) { throw new InvalidArgumentException('...'); } // Calculate and return $age ... } public function hasBirthday() { return isset($this->birthday); } }

Slide 44

Slide 44 text

Tommy Mühle | tommy-muehle.io You can also use null objects instead 43

Slide 45

Slide 45 text

Tommy Mühle | tommy-muehle.io Without null object 44

Slide 46

Slide 46 text

Tommy Mühle | tommy-muehle.io 45 final class Order { /** @var Money */ private $price; /** @var Discount */ private $discount; public function getPrice() { $priceWithDiscount = $this->price; if (!$this->discount instanceof Discount) { return $priceWithDiscount; } // ... return new Money($priceWithDiscount); } }

Slide 47

Slide 47 text

Tommy Mühle | tommy-muehle.io With null object 46

Slide 48

Slide 48 text

Tommy Mühle | tommy-muehle.io 47 interface Amountable { public function getAmount(); } final class HeavyUserDiscount implements Amountable { public function getAmount() { $amount = 0.00; // ... return new Money($amount); } }

Slide 49

Slide 49 text

Tommy Mühle | tommy-muehle.io 48 interface Amountable { public function getAmount(); } final class NoDiscount implements Amountable { public function getAmount() { return new Money(0.00); } }

Slide 50

Slide 50 text

Tommy Mühle | tommy-muehle.io 49 final class Order { /** @var Money */ private $price; /** @var Discount */ private $discount; public function getPrice() { $amount = $this->price->getAmount() - $this->discount->getAmount(); return new Money($amount); } }

Slide 51

Slide 51 text

Tommy Mühle | tommy-muehle.io No optional parameters 50

Slide 52

Slide 52 text

class Person { private $address; private $details; public function __construct( Address $address, Details $details = null) { $this->address = $address; $this->details = $details; } } Tommy Mühle | tommy-muehle.io 51

Slide 53

Slide 53 text

Tommy Mühle | tommy-muehle.io 52 class Person { private $address; private $details; // ... public function __construct(Address $address) { $this->address = $address; } public function addDetails(Details $details) { $this->details = $details; } }

Slide 54

Slide 54 text

Tommy Mühle | tommy-muehle.io Block magic method usage 53

Slide 55

Slide 55 text

Tommy Mühle | tommy-muehle.io 54 class Person { // ... public function __clone() { throw new LogicException( 'Why would you even clone me?'); } public function __sleep() { throw new BadMethodCallException( 'Serialize, really?'); } }

Slide 56

Slide 56 text

Tommy Mühle | tommy-muehle.io Summary 55

Slide 57

Slide 57 text

Tommy Mühle | tommy-muehle.io You can always think different 56

Slide 58

Slide 58 text

Tommy Mühle | tommy-muehle.io 57 https:/ /laracasts.com/series/php-bits/episodes/1 Visual Debt

Slide 59

Slide 59 text

Questions?

Slide 60

Slide 60 text

Thank you! Slides http:/ /bit.ly/2qFHyQa Images https:/ /pixabay.com/ @tommy_muehle