Slide 1

Slide 1 text

PHP FOR GROWN-UPS

Slide 2

Slide 2 text

WHY TALK ABOUT THIS?

Slide 3

Slide 3 text

PHP HAS A BAD RAP... (WHICH IT PARTLY DESERVES)

Slide 4

Slide 4 text

BUT YOU CAN USE IT TO BUILD PRETTY SOLID STUFF (IF YOU KNOW HOW)

Slide 5

Slide 5 text

SO LET’S TALK ABOUT THE HOW

Slide 6

Slide 6 text

(BUT FIRST, A COUPLE SMALL DISCLAIMERS...)

Slide 7

Slide 7 text

NONE OF THESE IDEAS ARE NEW (IN FACT, MOST ARE PRETTY OLD)

Slide 8

Slide 8 text

ALL OF THIS CAN BE FOUND IN BOOKS (NONE OF WHICH SPECIFIC TO PHP)

Slide 9

Slide 9 text

GOOD CODE

Slide 10

Slide 10 text

MAINTAINABLE RELIABLE (RE)USABLE

Slide 11

Slide 11 text

MAINTAINABLE CODE (ALMOST) ALL CODE CAN AND WILL CHANGE

Slide 12

Slide 12 text

UNDERSTANDABLE DECOUPLED CONSISTENTLY ABSTRACTED

Slide 13

Slide 13 text

“The ratio of time spent reading code versus writing is well over 10 to 1” -- Uncle Bob

Slide 14

Slide 14 text

NAMING (CLASSES AND VARIABLES)

Slide 15

Slide 15 text

DESCRIPTIVE AND EXPRESSIVE

Slide 16

Slide 16 text

$light->setStatus(true);

Slide 17

Slide 17 text

$light->turnOn();

Slide 18

Slide 18 text

NAMESPACES

Slide 19

Slide 19 text

EXTRACT METHOD // before: if (is_numeric($amount) && $amount > 0) // after: if ($this->isValidAmount($amount))

Slide 20

Slide 20 text

private function isValidAmount($amount) { return is_numeric($amount) && $amount > 0; }

Slide 21

Slide 21 text

NAMES WILL SOMETIMES LIE TO YOU

Slide 22

Slide 22 text

// neither of these is actually a locale $this->locale = $locale->locale;

Slide 23

Slide 23 text

COMMENT ALL THE THINGS (WELL, NOT ALL THE THINGS...)

Slide 24

Slide 24 text

CODE SAYS WHAT COMMENTS SAY WHY

Slide 25

Slide 25 text

// set the patient’s clinic $patient->setClinic($clinic); // ORLY?

Slide 26

Slide 26 text

// to register a patient, set the patient’s clinic $patient->setClinic($clinic);

Slide 27

Slide 27 text

GOOD NAMING AND GOOD ABSTRACTIONS BEAT GOOD COMMENTS

Slide 28

Slide 28 text

$clinic->registerPatient($patient);

Slide 29

Slide 29 text

PRINCIPLE OF LEAST ASTONISHMENT (MINIMIZE THE NUMBER OF WTFS PER MINUTE)

Slide 30

Slide 30 text

PHPDOC ALL THE THINGS CLASSES, VARIABLES, METHODS, ARGUMENTS

Slide 31

Slide 31 text

/** * If what this method does can not be made obvious from * its name, explain it here. * * @param string $firstname * @return void */ public function changeFirstName($firstName) { // code }

Slide 32

Slide 32 text

WHY BOTHER IF PHP DOES NOT ENFORCE THIS? COMMUNICATES INTENT WORKS GREAT WITH IDES

Slide 33

Slide 33 text

“Shy code - modules that don’t reveal anything unnecessary to other modules and that don’t rely on other modules’ implementations” - The Pragmatic Programmer

Slide 34

Slide 34 text

WHY DECOUPLING? ISOLATES CHANGES MAKES YOUR CODE MORE FLEXIBLE ENCOURAGES ABSTRACTION

Slide 35

Slide 35 text

”Modules that don’t reveal anything unnecessary to other modules” ENCAPSULATION

Slide 36

Slide 36 text

$totalPrice = 0; foreach ($order->getItems() as $item) { /** @var OrderItem $item */ $amount = $item->getAmount(); $price = $item->getPrice(); $totalPrice += $amount * $price; } $totalPrice += $order->getShippingCosts();

Slide 37

Slide 37 text

$totalPrice = $order->getTotalPrice();

Slide 38

Slide 38 text

”Tell, don’t ask”

Slide 39

Slide 39 text

$discountedItems = array(); foreach ($order->getItems() as $item) { /** @var OrderItem $item */ $oldPrice = $item->getPrice(); $newPrice = $oldPrice - ($oldPrice * ($discountPercentage / 100)) $item->setPrice($discountPercentage); $discountedItems[] = $item; } $order->setItems($items);

Slide 40

Slide 40 text

$order->applyDiscount($discountPercentage);

Slide 41

Slide 41 text

“[...] modules that don’t rely on other modules’ implementations”

Slide 42

Slide 42 text

FLAVORS OF COUPLING INTERNAL DEPENDENCIES EXTERNAL PACKAGES DATABASES/ORMS FRAMEWORKS

Slide 43

Slide 43 text

EVEN PROGRAMMING LANGUAGES

Slide 44

Slide 44 text

DEPENDENCY INJECTION

Slide 45

Slide 45 text

Before: class EmployeeService { /** * @param integer $employeeId * @return void */ public function delete($id) { $employeeStore = new EmployeeStore(); $employeeStore->deleteById($id); } }

Slide 46

Slide 46 text

After: class EmployeeService { /* * @var EmployeeStore */ private $employeeStore /** * @param mixed $employeeStore * @return void */ public function __construct($employeeStore) { $this->employeeStore = $employeeStore; } // rest of class here... }

Slide 47

Slide 47 text

/** * @param integer $id * @return void */ public function deleteById($id) { $this->employeeStore->deleteById($id); }

Slide 48

Slide 48 text

public function __construct($employeeStore)

Slide 49

Slide 49 text

public function __construct(EmployeeStore $employeeStore)

Slide 50

Slide 50 text

interface EmployeeStoreInterface { /** * @param integer $id */ public function deleteById($id); }

Slide 51

Slide 51 text

class PdoEmployeeStore implements EmployeeStoreInterface { /** * @param integer $id */ public function deleteById($id) { // PDO implementation here } }

Slide 52

Slide 52 text

public function __construct(EmployeeStoreInterface $employeeStore)

Slide 53

Slide 53 text

TREAT INTERFACES AS THE REAL THING

Slide 54

Slide 54 text

“That seems like an hell of a lot of work...”

Slide 55

Slide 55 text

DI CONTAINER ”IF I AM ASKING FOR AN EMPLOYEESTORE, GIVE ME AN INSTANCE OF THIS CLASS”

Slide 56

Slide 56 text

SYMFONY DEPENDENCYINJECTION LARAVEL IOC CONTAINER ORNO DI PIMPLE

Slide 57

Slide 57 text

“Mathematics is the art of giving the same name to different things.” ― Henri Poincaré

Slide 58

Slide 58 text

/** * @param string $searchTerm * @param string $productCategory * @param string $make * @param string $color */ public function search($searchTerm, $productCategory, $make, $color) { switch ($productType) { // search products } }

Slide 59

Slide 59 text

/** * @param SearchTerm $searchTerm * @param ProductCategory $productCategory * @param Make $make * @param Color $color */ public function search( SearchTerm $searchTerm, ProductCategory $productCategory, Make $make, Color $color ) { switch($productType->getTypeId()) // search products ) }

Slide 60

Slide 60 text

VALUE OBJECTS class SearchTerm { const MAX_TERM_LENGTH = 30; /** * @var string */ private $searchTerm; /** * @param string $term * @throws InvalidTermException */ public function __construct($term) { if (strlen($term) >= self::MAX_TERM_LENGTH) { throw new InvalidTermException($term); } } }

Slide 61

Slide 61 text

”But isn’t this coupling?”

Slide 62

Slide 62 text

“Figuring out if two people are married is easy. Figuring out if they should be is hard.” - Michael Feathers

Slide 63

Slide 63 text

/** * @param SearchQuery $query * @return SearchResults */ public function search(SearchQuery $query) { /** @var SearchWorker $worker */ $worker = $this->getWorkerForQuery($query); /** @var SearchResults $results */ $results = $worker->process($query); return $results; }

Slide 64

Slide 64 text

ABSTRACTION TOOL NAMESPACES

Slide 65

Slide 65 text

ABSTRACTION LEVELS

Slide 66

Slide 66 text

LOW LEVEL OF ABSTRACTION $statement = $database->prepare( “INSERT INTO `fish`(‘name’, ‘species’, ‘gender’, ‘tankId’) VALUES(?, ?, ?, ?)” ); $statement->execute(array(‘Nemo’, $speciesId, ‘M’, $tankId));

Slide 67

Slide 67 text

HIGH LEVEL OF ABSTRACTION $command = new IntroduceFishCommand($fish, $tank); $this->commandBus->execute($command);

Slide 68

Slide 68 text

LAYERED ARCHITECTURE

Slide 69

Slide 69 text

HTTP LAYER FRAMEWORK LAYER SERVICE LAYER DOMAIN LAYER PRESENTATION LAYER

Slide 70

Slide 70 text

”You gotta keep ‘em separated”

Slide 71

Slide 71 text

RELIABLE CODE ”YOU ARE NOT PHPREPARED”

Slide 72

Slide 72 text

KEEP IT UNDERSTANDABLE CODE DEFENSIVELY KEEP IT TESTABLE USE YOUR LANGUAGE FEATURES

Slide 73

Slide 73 text

In order to prevent defects in code, you first need to understand it

Slide 74

Slide 74 text

”Rely only on reliable things” - The Pragmatic Programmer

Slide 75

Slide 75 text

Not a reliable thing THE PHP TYPE SYSTEM

Slide 76

Slide 76 text

if (! $amount) { // do something } // $amount can actually be 0, “0”, “”, null or false for this to be true

Slide 77

Slide 77 text

if ($amount == 0) { // do something } // you would think that explicitly checking for 0 would improve things...

Slide 78

Slide 78 text

Computer says no...

Slide 79

Slide 79 text

if ($amount === 0) { // do something } // this will only evaluate as true when $amount is actually the number 0

Slide 80

Slide 80 text

CRANK YOUR ERROR REPORTING UP KNOW WHY YOUR CODE WORKS BE AWARE OF FAILURE SCENARIOS CATCH AND THROW EXCEPTIONS

Slide 81

Slide 81 text

”Test your software, or your users will” - The Pragmatic Programmer

Slide 82

Slide 82 text

TEST-DRIVEN DEVELOPMENT

Slide 83

Slide 83 text

PHPUNIT PHPSPEC CODECEPTION BEHAT

Slide 84

Slide 84 text

TDD IS NOT JUST UNIT TESTING

Slide 85

Slide 85 text

ACCEPTANCE FUNCTIONAL INTEGRATION UNIT

Slide 86

Slide 86 text

MORE CONFIDENCE BETTER DESIGN FEWER DEFECTS REGRESSION CATCHING TESTS AS DOCUMENTATION

Slide 87

Slide 87 text

TYPEHINTING VISIBILITY (PRIVATE BY DEFAULT) INTERFACES EXCEPTIONS ISSET, IS_NUMERIC, ETC.

Slide 88

Slide 88 text

USE PHPDOC WITH A GOOD IDE

Slide 89

Slide 89 text

DECOUPLED CODE GOOD ABSTRACTIONS NAMESPACES COMPOSER AUTOLOADING/IDES

Slide 90

Slide 90 text

COMPOSER OPEN SOURCE PRIVATE REPOSITORIES IN COMPOSER.JSON TORAN PROXY

Slide 91

Slide 91 text

// FILE: composer.json { "name": "behat/behat", "description": "Scenario-oriented BDD framework for PHP 5.3", “require”: {} }

Slide 92

Slide 92 text

// FILE: composer.json { "repositories": [ { "type": "vcs", "url": "http://github.com/user/repo" }, ], “require”: { “user/package”: “*” } }

Slide 93

Slide 93 text

AUTOLOADING NO MORE REQUIRE/INCLUDE JUST USE THE CLASS (OPTIONAL) USE STATEMENT

Slide 94

Slide 94 text

SOLID PRINCIPLES

Slide 95

Slide 95 text

SINGLE RESPONSIBILITY PRINCIPLE OPEN-CLOSED PRINCIPLE LISKOV SUBSTITUTION PRINCIPLE INTERFACE SEGREGATION PRINCIPLE DEPENDENCY INVERSION PRINCIPLE

Slide 96

Slide 96 text

SINGLE RESPONSIBILITY PRINCIPLE ”A class should have one, and only one, reason to change.”

Slide 97

Slide 97 text

”Every class, or similar structure, should have only one job to do.” - Richard Carr

Slide 98

Slide 98 text

ASK YOURSELF “What responsibilities does this piece of code have?”

Slide 99

Slide 99 text

$user = new User($userName, $password, $firstName, $lastName, $email); $user->save(); // later on... $user->login($userName, $password);

Slide 100

Slide 100 text

CONTAIN LOGIN CREDENTIALS CONTAIN PERSONAL INFORMATION STORE TO DATABASE ACCESS MANAGEMENT

Slide 101

Slide 101 text

USER CLASS CUSTOMER CLASS USERSTORE / CUSTOMERSTORE CLASSES AUTHENTICATIONMANAGER CLASS

Slide 102

Slide 102 text

$userAccount = new UserAccount($userName, $password); $authenticationManager->register($userAccount); $customer = new Customer($firstName, $lastName, $email); $customer->linkUserAccount($userAccount); $customerService->register($customer); $authenticationService->login($userName, $password)

Slide 103

Slide 103 text

OPEN-CLOSED PRINCIPLE “A class should be open for extension, but closed for modification”

Slide 104

Slide 104 text

public function ship(Product $product, $shippingMethod) { switch ($shippingmethod) { case shipping::shipping_default: $this->defaultshipping($product); break; case shipping::shipping_priority: $this->priorityshipping($product); break; } }

Slide 105

Slide 105 text

public function addShippingMethod(ShippingMethodInterface $method, $identifier) { $this->shippingMethods[$identifier] = $method; } public function ship(Product $product, $shippingMethodIdentifier) { $shippingMethod = $this->shippingMethods[$shippingMethodIdentifier]; $shippingMethod->ship($product); }

Slide 106

Slide 106 text

LISKOV SUBSTITUTION PRINCIPE ”Subclasses must be substitutable for their base classes.”

Slide 107

Slide 107 text

“Any implementation of an abstraction (interface) should be substitutable in any place that the abstraction is accepted” - Jeffrey Way

Slide 108

Slide 108 text

interface VehicleInterface { public function driveTo(Destination $destination); }

Slide 109

Slide 109 text

class Boat implements VehicleInterface { public function driveTo(Destination $destination) { // start driving } }

Slide 110

Slide 110 text

class Car implements VehicleInterface { public function driveTo(Destination $destination) { // start driving } public function swapTires(TyreTypeInterface $tyreType) { // swap tyres } }

Slide 111

Slide 111 text

/** @var VehicleInterface $vehicle */ $vehicle->swapTyres($tyreType); // Car: works // Boat: Method ‘swapTires’ not found in class Boat

Slide 112

Slide 112 text

INTERFACE SEGREGATION PRINCIPLE ”Never force classes to implement methods that they do not use”

Slide 113

Slide 113 text

interface VehicleInterface { public function driveTo(Destination $destination); public function swapTires(TyreTypeInterface $tyreType); }

Slide 114

Slide 114 text

class Boat implements VehicleInterface { public function driveTo(Destination $destination) { // start driving } public function swapTires(TyreTypeInterface $tyreType) { throw new BoatHasNoWheelsException(); } }

Slide 115

Slide 115 text

interface VehicleInterface { public function driveTo(Destination $destination); } interface WheeledVehicleInterface { public function swapTires(TyreTypeInterface $tyreType); }

Slide 116

Slide 116 text

class Car implements VehicleInterface, WheeledVehicleInterface

Slide 117

Slide 117 text

class Boat implements VehicleInterface, BoatInterface

Slide 118

Slide 118 text

DEPENDENCY INVERSION PRINCIPE ”Depend on abstractions, not on concretions.”

Slide 119

Slide 119 text

class StoreHouse { private $supplier; public function __construct(FooBarInc $supplier) { $this->supplier = $supplier; } }

Slide 120

Slide 120 text

class StoreHouse { private $supplier; public function __construct(SupplierInterface $supplier) { $this->supplier = $supplier; } }

Slide 121

Slide 121 text

class FooBarIncSupplier implements SupplierInterface { public function order(Product $product) { $orderId = $this->fooBarInc->createProductOrder(); $this->fooBarInc->addProductToOrder($orderId); } }

Slide 122

Slide 122 text

QUESTIONS?

Slide 123

Slide 123 text

READING LIST Patterns of Enterprise Application Architecture - Martin Fowler Clean Code - Robert C. Martin Growing Object-Oriented Software, Guided By Tests (the GOOS book) - Steve Freeman & Nat Pryce The Pragmatic Programmer - Dave Thomas & Andy Hunt

Slide 124

Slide 124 text

VIDEOS Models and Service Layers; Hemoglobin and Hobgoblins - Ross Tuck (https://www.youtube.com/watch?v=3uV3ngl1Z8g) Unbreakable Domain Models - Mathias Verraes (https:// www.youtube.com/watch?v=ZJ63ltuwMaE) The Framework As An Implementation Detail (https:// www.youtube.com/watch?v=0L_9NutiJlc)

Slide 125

Slide 125 text

PODCASTS Elephant in the Room (http://elephantintheroom.io)

Slide 126

Slide 126 text

No content