92
PHP 4 ADULTS OBJECT CALISTHENICS AND CLEAN CODE

PHP for Adults: Clean Code and Object Calisthenics

Embed Size (px)

Citation preview

Page 1: PHP for Adults: Clean Code and Object Calisthenics

PHP 4 ADULTSOBJECT CALISTHENICS AND CLEAN CODE

Page 2: PHP for Adults: Clean Code and Object Calisthenics

GUILHERMEBLANCO

GUILHERMEBLANCO

Guilherme Blanco

Page 3: PHP for Adults: Clean Code and Object Calisthenics

MOTIVATION

▸ Readability

▸ Maintainability

▸ Reusability

▸ Testability

Page 4: PHP for Adults: Clean Code and Object Calisthenics

SUMMING UP…

Page 5: PHP for Adults: Clean Code and Object Calisthenics
Page 6: PHP for Adults: Clean Code and Object Calisthenics

CLEAN CODE

Page 7: PHP for Adults: Clean Code and Object Calisthenics

S T U P I D

Page 8: PHP for Adults: Clean Code and Object Calisthenics

SINGLETON TIGHT COUPLING UNTESTABILITY PREMATURE OPTIMIZATION INDESCRIPTIVE NAMING DUPLICATION

Page 9: PHP for Adults: Clean Code and Object Calisthenics

S O L I D

Page 10: PHP for Adults: Clean Code and Object Calisthenics

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

Page 11: PHP for Adults: Clean Code and Object Calisthenics

LISKOV SUBSTITUTION PRINCIPLE

Page 12: PHP for Adults: Clean Code and Object Calisthenics

interface Bird { public function setLocation($longitude, $latitude);

public function setHeight($height);

public function draw(); }

Page 13: PHP for Adults: Clean Code and Object Calisthenics

class Penguin implements Bird { public function setHeight($height) { // Do nothing }

}

Page 14: PHP for Adults: Clean Code and Object Calisthenics

interface Bird { public function setLocation($longitude, $latitude);

public function draw();}

interface FlightfulBird extends Bird { public function setHeight($height);

}

Page 15: PHP for Adults: Clean Code and Object Calisthenics

DEPENDENCY INVERSION PRINCIPLE

Page 16: PHP for Adults: Clean Code and Object Calisthenics

namespace Dating\UserBundle\Entity { class User { /** @var \Dating\UserBundle\Entity\Image */ protected $avatar; } }

namespace Dating\MediaBundle\Entity { class Image { /** @var \Dating\UserBundle\Entity\User */ protected $owner; } }

Page 17: PHP for Adults: Clean Code and Object Calisthenics

namespace Dating\UserBundle\Entity { class User { /** @var AvatarInterface */ protected $avatar; }

interface AvatarInterface { // ... }}

namespace Dating\MediaBundle\Entity { use Dating\UserBundle\Entity\AvatarInterface;

class Image implements AvatarInterface { /** @var \Dating\UserBundle\Entity\User */ protected $owner; } }

Page 18: PHP for Adults: Clean Code and Object Calisthenics

OBJECT CALISTHENICS

Page 19: PHP for Adults: Clean Code and Object Calisthenics

RULE #1ONLY ONE INDENTATION LEVEL PER METHOD

Page 20: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters='', $validators='', $options='') { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if ($input->hasInvalid() || $input->hasMissing()) { foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { if (strpos($message, "empty")) { throw new Tss_FormException( "The field {$field} cannot be empty!", 3, "javascript:history.back();" ); } else { throw new Tss_FormException( "{$message}", 3, "javascript:history.back();" ); } } } }

return $input;}

Page 21: PHP for Adults: Clean Code and Object Calisthenics

12

34

public function validateForm($filters='', $validators='', $options='') { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if ($input->hasInvalid() || $input->hasMissing()) { foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { if (strpos($message, "empty")) { throw new Tss_FormException( "The field {$field} cannot be empty!", 3, "javascript:history.back();" ); } else { throw new Tss_FormException( "{$message}", 3, "javascript:history.back();" ); } } } }

return $input;}

Class prototype

Page 22: PHP for Adults: Clean Code and Object Calisthenics

EARLY RETURNS

Page 23: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { if (strpos($message, "empty")) { throw new Tss_FormException( "The field {$field} cannot be empty!", 3, "javascript:history.back();" ); } else { throw new Tss_FormException( "{$message}", 3, "javascript:history.back();" ); } } }

return $input;}

Page 24: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { if (strpos($message, "empty")) { throw new Tss_FormException( "The field {$field} cannot be empty!", 3, "javascript:history.back();" ); } else { throw new Tss_FormException( "{$message}", 3, "javascript:history.back();" ); } } }

return $input;}

Page 25: PHP for Adults: Clean Code and Object Calisthenics

12

3

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { if (strpos($message, "empty")) { throw new Tss_FormException( "The field {$field} cannot be empty!", 3, "javascript:history.back();" ); } else { throw new Tss_FormException( "{$message}", 3, "javascript:history.back();" ); } } }

return $input;}

Page 26: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : $message;

throw new Tss_FormException( $errorMessage, 3, "javascript:history.back();" ); } }

return $input;}

Page 27: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : $message;

throw new Tss_FormException( $errorMessage, 3, "javascript:history.back();" ); } }

return $input;}

Page 28: PHP for Adults: Clean Code and Object Calisthenics

12

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { foreach ($messageList as $message) { $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : $message;

throw new Tss_FormException( $errorMessage, 3, "javascript:history.back();" ); } }

return $input;}

Page 29: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { $message = array_shift($messageList); $jsAction = "javascript:history.back();"; $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : $message;

throw new Tss_FormException($errorMessage, 3, $jsAction); }

return $input;}

Page 30: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST;

$input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { $message = array_shift($messageList); $jsAction = "javascript:history.back();"; $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : $message;

throw new Tss_FormException($errorMessage, 3, $jsAction); }

return $input;}

Logical groups

Variable interpolation

Page 31: PHP for Adults: Clean Code and Object Calisthenics

public function validateForm($filters=array(), $validators=array(), $options=array()) { $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options);

$input->setDefaultEscapeFilter(new Zend_Filter_StringTrim());

if (! ($input->hasInvalid() || $input->hasMissing())) { return $input; }

foreach ($input->getMessages() as $field => $messageList) { $message = array_shift($messageList); $jsAction = "javascript:history.back();"; $errorMessage = (strpos($message, "empty") === false) ? sprintf("The field %s cannot be empty!", $field) : $message;

throw new Tss_FormException($errorMessage, 3, $jsAction); }

return $input;}

Page 32: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Single Responsibility Principle ("S" in SOLID)

▸ Reusability

Page 33: PHP for Adults: Clean Code and Object Calisthenics

RULE #2NO "ELSE" KEYWORD

Page 34: PHP for Adults: Clean Code and Object Calisthenics

public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity)) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }

Page 35: PHP for Adults: Clean Code and Object Calisthenics

public function createPost($request) { $entity = new Post(); $form = new MyForm($entity); $form->bind($request); if ($form->isValid()){ $repository = $this->getRepository('MyBundle:Post'); if (!$repository->exists($entity)) { $repository->save($entity); return $this->redirect('create_ok'); } else { $error = "Post Title already exists"; return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields"; return array('form' => $form, 'error' => $error); } }

Type-casting

Coding standards

Separate into logical groups.

Consider as paragraphs!

Page 36: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if ($form->isValid()) { if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); } }

Page 37: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if ($form->isValid()) { if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); } }

Page 38: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if ($form->isValid()) { if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); } }

Page 39: PHP for Adults: Clean Code and Object Calisthenics

UMLNORMAL VS. ALTERNATIVE FLOWS

Page 40: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if ($form->isValid()) { if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); } }

Page 41: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if ($form->isValid()) { if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); } }

Page 42: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if ($form->isValid()) { if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } } else { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); } }

Page 43: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if (! $form->isValid()) { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); }

if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } }

Page 44: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if (! $form->isValid()) { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); }

if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } }

Page 45: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if (! $form->isValid()) { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); }

if (! $repository->exists($entity)) { $repository->save($entity);

return $this->redirect('create_ok'); } else { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); } }

Page 46: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if (! $form->isValid()) { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); }

if ($repository->exists($entity)) { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); }

$repository->save($entity);

return $this->redirect('create_ok');}

Page 47: PHP for Adults: Clean Code and Object Calisthenics

public function createPost(Request $request) { $repository = $this->getRepository(‘MyBundle:Post'); $entity = new Post(); $form = new MyForm($entity);

$form->bind($request);

if (! $form->isValid()) { $error = "Invalid fields";

return array('form' => $form, 'error' => $error); }

if ($repository->exists($entity)) { $error = "Post Title already exists";

return array('form' => $form, 'error' => $error); }

$repository->save($entity);

return $this->redirect('create_ok');}

Page 48: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Prevents code duplication

▸ Increases legibility

▸ Reduce cyclomatic complexity

Page 49: PHP for Adults: Clean Code and Object Calisthenics

RULE #3ENCAPSULATE ALL PRIMITIVE TYPES AND STRINGS

Page 50: PHP for Adults: Clean Code and Object Calisthenics

RULE #3ENCAPSULATE ALL PRIMITIVE TYPES AND STRINGS

IF THEY HAVE BEHAVIOR

Page 51: PHP for Adults: Clean Code and Object Calisthenics

BUT… WHY?

Page 52: PHP for Adults: Clean Code and Object Calisthenics

EXCESSIVE USAGE OF OBJECTS IN PHP (IF PHP <7!) DRASTICALLY INCREASES MEMORY FOOTPRINT!

Guilherme Blanco

Page 53: PHP for Adults: Clean Code and Object Calisthenics

class Item { final public static function find($id) { if (is_string($id) && trim($id) != '') { // do find ... }

throw new \InvalidArgumentException('$id must be a non-empty string'); }

final public static function create($id, array $data) { if ( ! is_string($id)) { throw new \InvalidArgumentException('$id must be a string'); }

if (empty(trim($id))) { throw new \InvalidArgumentException('$id must be a non-empty string'); }

// do create ... }}

Page 54: PHP for Adults: Clean Code and Object Calisthenics

class Item { final public static function find($id) { if (! is_string($id) || trim($id) === '') { throw new \InvalidArgumentException('$id must be a non-empty string'); }

// do find ... }

final public static function create($id, array $data) { if (! is_string($id) || trim($id) === '') { throw new \InvalidArgumentException('$id must be a non-empty string'); }

// do create ... }}

Page 55: PHP for Adults: Clean Code and Object Calisthenics

class Item { final public static function find($id) { if (! is_string($id) || trim($id) === '') { throw new \InvalidArgumentException('$id must be a non-empty string'); }

// do find ... }

final public static function create($id, array $data) { if (! is_string($id) || trim($id) === '') { throw new \InvalidArgumentException('$id must be a non-empty string'); }

// do create ... }}

Page 56: PHP for Adults: Clean Code and Object Calisthenics

final class Id { /** @var string */ public $value;

public function __construct($value) { if (! is_string($id) || trim($id) === '') { $message = sprintf('%s must be a non-empty string', $value);

throw new \InvalidArgumentException($message); }

$this->value = $value; }

public function getValue() { return $this->value; }}

Page 57: PHP for Adults: Clean Code and Object Calisthenics

class Item { final public static function find(Id $id) { // do find ... }

final public static function create(Id $id, array $data) { // do create ... }}

Page 58: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Type hinting

▸ Encapsulation

▸ Prevents code duplication

Page 59: PHP for Adults: Clean Code and Object Calisthenics

RULE #4ONE OBJECT OPERATOR (->) PER LINE

Page 60: PHP for Adults: Clean Code and Object Calisthenics

$this->manager->getConfig()->getSegment()->setName("foo");

Properties are hard to mock What if previous call returned NULL?

Page 61: PHP for Adults: Clean Code and Object Calisthenics

JUST USE A NULL OBJECT!

Someone watching this talk, one day

Page 62: PHP for Adults: Clean Code and Object Calisthenics

final class NullObject { public function __get($property) { return new self; }

public function __set($property, $value) { return new self; }

public function __call($method, array $arguments) { return new self; }

public function __callStatic($method, array $arguments) { return new self; }

public function__toString() { return 'null'; } }

Page 63: PHP for Adults: Clean Code and Object Calisthenics

WHY IS IT BAD?

▸ Hide encapsulation problem

▸ Hard to debug and handle exceptions

▸ Codebase must be structured to use NullObject

▸ Hard to read and understand

Page 64: PHP for Adults: Clean Code and Object Calisthenics

EXCEPTION TO RULEFLUENT INTERFACE UNDER SAME METHOD

Page 65: PHP for Adults: Clean Code and Object Calisthenics

$filterChain ->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower());

Page 66: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Law of Demeter

▸ Readability

▸ Increases testability (easier to mock)

▸ Simplifies debugging

Page 67: PHP for Adults: Clean Code and Object Calisthenics

RULE #5DO NOT ABBREVIATE

Page 68: PHP for Adults: Clean Code and Object Calisthenics

THERE ARE 2 HARD PROBLEMS IN COMPUTER SCIENCE: CACHE INVALIDATION, NAMING THINGS AND OFF BY 1 ERRORS.

Tim Bray (mentioning Phil Karlton)

Page 69: PHP for Adults: Clean Code and Object Calisthenics

WHY DO YOU ABBREVIATE?

Page 70: PHP for Adults: Clean Code and Object Calisthenics

CODE DUPLICATION PROBLEM!WRITE SAME NAME REPEATEDLY

Page 71: PHP for Adults: Clean Code and Object Calisthenics

MULTIPLE RESPONSIBILITY PROBLEM!

LONG NAMES

Page 72: PHP for Adults: Clean Code and Object Calisthenics

public function getPage($data) { ... }

"Get" from where?

public function startProcess() { ... }

$trx->process('site.login');

How?

WTF is that?

renderHomePage

forkIntoChildProcess

extendedTranslator

Page 73: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Readability

▸ Better exposing method’s intent

▸ Improved maintainability

▸ Good indicator of code duplication and encapsulation

Page 74: PHP for Adults: Clean Code and Object Calisthenics

RULE #6KEEP YOUR CLASSES SMALL

Page 75: PHP for Adults: Clean Code and Object Calisthenics

OBJECTIVE

▸ Maximum 200 lines per class(including docblock/documentation)

▸ 10 methods per class

▸ Up to 20 lines per method

▸ 15 classes/interfaces/traits per namespace

Page 76: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Single Responsibility Principle

▸ Clear and objective methods

▸ Better code segregation

▸ Cleaner namespaces

Page 77: PHP for Adults: Clean Code and Object Calisthenics

RULE #7LIMIT CLASS INSTANCE VARIABLES IN A CLASS (BETWEEN 2 TO 5)

Page 78: PHP for Adults: Clean Code and Object Calisthenics

class MyRegistrationService { protected $userService; protected $passwordService;

protected $logger;

protected $translator;

protected $entityManager;

protected $imageCropper;

// ... }

Database interactions should be on UserService

Rely on an Event system and move this to a listener

Cross-cutting concerns. Should be auto-injected by your DI through an interface hint

Page 79: PHP for Adults: Clean Code and Object Calisthenics

class MyRegistrationService implements LoggerAwareInterface, TranslatorAwareInterface{ use LoggerAwareTrait; use TranslatorAwareTrait;

protected $userService; protected $passwordService;

protected $eventDispatcher;

// ... }

Page 80: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Single Responsibility Principle

▸ Loose coupling

▸ Better encapsulation

▸ Testability

Page 81: PHP for Adults: Clean Code and Object Calisthenics

RULE #8USE FIRST CLASS COLLECTIONS

Page 82: PHP for Adults: Clean Code and Object Calisthenics

OR IN OTHER TERMS…

Page 83: PHP for Adults: Clean Code and Object Calisthenics

ANY CLASS THAT CONTAINS AN ARRAY MUST NOT HAVE ANY OTHER PROPERTY.

Guilherme Blanco

TEXT

Page 84: PHP for Adults: Clean Code and Object Calisthenics

class User { private $name; // ... private $albumList = array();

public function getPublicAlbumList() { $filteredAlbumList = array();

foreach ($this->albumList as $album) { if ($album->getPrivacy() === AlbumPrivacy::PUBLIC) { $filteredAlbumList[] = $album; } } return $filteredAlbumList; }

// ... }

$publicAlbumList = $user->getPublicAlbumList();

Page 85: PHP for Adults: Clean Code and Object Calisthenics

class AlbumList extends Collection{ public function getPublic() { $filteredAlbumList = array();

foreach ($this->value as $album) { if ($album->getPrivacy() === AlbumPrivacy::PUBLIC) { $filteredAlbumList[] = $album; } } return $filteredAlbumList; } }

class User{ private $name; private $albumList = new AlbumList(); // ... }

$publicAlbumList = $user->getAlbumList()->getPublic();

Page 86: PHP for Adults: Clean Code and Object Calisthenics

class AlbumList extends Collection{ public function getPublic() { return new ArrayCollection( array_filter( $this->value, function (Album $album) { return $album->isPublic(); } ) ); } }

class User{ private $name; private $albumList = new AlbumList(); // ... }

$publicAlbumList = $user->getAlbumList()->getPublic();

Page 87: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Single Responsibility Principle

▸ Collection operations implemented inside of Collection

▸ Usage of SPL classes

▸ Easy to group collections without concerns over their members’ behavior

▸ Filtering, ordering, mapping, combining are good example methods

Page 88: PHP for Adults: Clean Code and Object Calisthenics

RULE #9USE GETTERS AND SETTERS

Page 89: PHP for Adults: Clean Code and Object Calisthenics

class BankAccount { public $balance = 0;

public function deposit($amount) { $this->balance += $amount; }

public function withdraw($amount) { $this->balance -= $amount; }}

// Example: $account = new BankAccount(); $account->deposit(100.00); // ... $account->balance = 0; // ... $account->withdraw(10.00);

Balance can be modified without class being notified, leading to unexpected errors.

Page 90: PHP for Adults: Clean Code and Object Calisthenics

BENEFITS

▸ Operations injection

▸ Transformations encapsulation

▸ Promotes Open/Closed Principle ("O" in SOLID)

Page 91: PHP for Adults: Clean Code and Object Calisthenics

QUESTIONS?

Page 92: PHP for Adults: Clean Code and Object Calisthenics

THANKS! =) GUILHERMEBLANCO

GUILHERMEBLANCO