SlideShare a Scribd company logo
1 of 92
Download to read offline
PHP 4 ADULTS
OBJECT CALISTHENICS AND CLEAN CODE
GUILHERMEBLANCO
GUILHERMEBLANCO
Guilherme Blanco
MOTIVATION
▸ Readability
▸ Maintainability
▸ Reusability
▸ Testability
SUMMING UP…
CLEAN CODE
S T U P I D
SINGLETON
TIGHT COUPLING
UNTESTABILITY
PREMATURE OPTIMIZATION
INDESCRIPTIVE NAMING
DUPLICATION
S O L I D
SINGLE RESPONSIBILITY
OPEN/CLOSED PRINCIPLE
LISKOV SUBSTITUTION PRINCIPLE
INTERFACE SEGREGATION
DEPENDENCY INVERSION
LISKOV SUBSTITUTION
PRINCIPLE
interface Bird
{
public function setLocation($longitude, $latitude);
public function setHeight($height);
public function draw();
}
class Penguin implements Bird
{
public function setHeight($height)
{
// Do nothing
}
}
interface Bird
{
public function setLocation($longitude, $latitude);
public function draw();
}
interface FlightfulBird extends Bird 

{ 

public function setHeight($height);
}
DEPENDENCY
INVERSION PRINCIPLE
namespace DatingUserBundleEntity
{
class User
{
/** @var DatingUserBundleEntityImage */
protected $avatar;
}
}
namespace DatingMediaBundleEntity
{
class Image
{
/** @var DatingUserBundleEntityUser */
protected $owner;
} 

}
namespace DatingUserBundleEntity
{
class User
{
/** @var AvatarInterface */
protected $avatar;
}
interface AvatarInterface
{
// ...
}
}
namespace DatingMediaBundleEntity
{
use DatingUserBundleEntityAvatarInterface;
class Image implements AvatarInterface
{
/** @var DatingUserBundleEntityUser */
protected $owner;
} 

}
OBJECT
CALISTHENICS
RULE #1
ONLY ONE INDENTATION LEVEL PER
METHOD
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;
}
1
2
3
4
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
EARLY RETURNS
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;
}
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;
}
1
2
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;
}
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;
}
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;
}
1
2
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;
}
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;
}
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
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;
}
BENEFITS
▸ Single Responsibility Principle ("S" in SOLID)
▸ Reusability
RULE #2
NO "ELSE" KEYWORD
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);
}
}
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!
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);
}
}
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);
}
}
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);
}
}
UML
NORMAL VS. ALTERNATIVE FLOWS
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
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');
}
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');
}
BENEFITS
▸ Prevents code duplication
▸ Increases legibility
▸ Reduce cyclomatic complexity
RULE #3
ENCAPSULATE ALL PRIMITIVE TYPES
AND STRINGS
RULE #3
ENCAPSULATE ALL PRIMITIVE TYPES
AND STRINGS
IF THEY HAVE BEHAVIOR
BUT… WHY?
EXCESSIVE USAGE OF OBJECTS IN
PHP (IF PHP <7!) DRASTICALLY
INCREASES MEMORY FOOTPRINT!
Guilherme Blanco
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 ...
}
}
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 ...
}
}
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 ...
}
}
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;
}
}
class Item
{
final public static function find(Id $id)
{
// do find ...
}
final public static function create(Id $id, array $data)
{
// do create ...
}
}
BENEFITS
▸ Type hinting
▸ Encapsulation
▸ Prevents code duplication
RULE #4
ONE OBJECT OPERATOR (->) PER LINE
$this->manager->getConfig()->getSegment()->setName("foo");
Properties are hard to mock What if previous call returned NULL?
JUST USE A NULL OBJECT!
Someone watching this talk, one day
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';
}
}
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
EXCEPTION TO RULE
FLUENT INTERFACE UNDER SAME
METHOD
$filterChain 

->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower())
;
BENEFITS
▸ Law of Demeter
▸ Readability
▸ Increases testability (easier to mock)
▸ Simplifies debugging
RULE #5
DO NOT ABBREVIATE
THERE ARE 2 HARD PROBLEMS IN
COMPUTER SCIENCE: CACHE
INVALIDATION, NAMING THINGS AND
OFF BY 1 ERRORS.
Tim Bray (mentioning Phil Karlton)
WHY DO YOU
ABBREVIATE?
CODE DUPLICATION PROBLEM!
WRITE SAME NAME REPEATEDLY
MULTIPLE RESPONSIBILITY
PROBLEM!
LONG NAMES
public function getPage($data) { ... }
"Get" from where?
public function startProcess() { ... }
$trx->process('site.login');
How?
WTF is that?
renderHomePage
forkIntoChildProcess
extendedTranslator
BENEFITS
▸ Readability
▸ Better exposing method’s intent
▸ Improved maintainability
▸ Good indicator of code duplication and encapsulation
RULE #6
KEEP YOUR CLASSES SMALL
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
BENEFITS
▸ Single Responsibility Principle
▸ Clear and objective methods
▸ Better code segregation
▸ Cleaner namespaces
RULE #7
LIMIT CLASS INSTANCE VARIABLES IN A
CLASS (BETWEEN 2 TO 5)
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
class MyRegistrationService
implements LoggerAwareInterface, TranslatorAwareInterface
{
use LoggerAwareTrait;
use TranslatorAwareTrait;
protected $userService;
protected $passwordService;
protected $eventDispatcher;
// ...
}
BENEFITS
▸ Single Responsibility Principle
▸ Loose coupling
▸ Better encapsulation
▸ Testability
RULE #8
USE FIRST CLASS COLLECTIONS
OR IN OTHER
TERMS…
ANY CLASS THAT CONTAINS AN
ARRAY MUST NOT HAVE ANY
OTHER PROPERTY.
Guilherme Blanco
TEXT
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();
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();
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();
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
RULE #9
USE GETTERS AND SETTERS
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.
BENEFITS
▸ Operations injection
▸ Transformations encapsulation
▸ Promotes Open/Closed Principle ("O" in SOLID)
QUESTIONS?
THANKS! =)
GUILHERMEBLANCO
GUILHERMEBLANCO

More Related Content

What's hot

Node.js File system & Streams
Node.js File system & StreamsNode.js File system & Streams
Node.js File system & StreamsEyal Vardi
 
Oops concepts in php
Oops concepts in phpOops concepts in php
Oops concepts in phpCPD INDIA
 
Clean Architecture em PHP
Clean Architecture em PHPClean Architecture em PHP
Clean Architecture em PHPElton Minetto
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Chris Richardson
 
Chapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En JavaChapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En JavaAziz Darouichi
 
PHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsGuilherme Blanco
 
Quick tour of PHP from inside
Quick tour of PHP from insideQuick tour of PHP from inside
Quick tour of PHP from insidejulien pauli
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?Nikita Popov
 
Desenvolvimento de uma API RESTful com Java EE
Desenvolvimento de uma API RESTful com Java EEDesenvolvimento de uma API RESTful com Java EE
Desenvolvimento de uma API RESTful com Java EELuan Felipe Knebel
 
Class 5 - PHP Strings
Class 5 - PHP StringsClass 5 - PHP Strings
Class 5 - PHP StringsAhmed Swilam
 
Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)
Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)
Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)Helder da Rocha
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsYura Nosenko
 
Class 3 - PHP Functions
Class 3 - PHP FunctionsClass 3 - PHP Functions
Class 3 - PHP FunctionsAhmed Swilam
 

What's hot (20)

Solid principles
Solid principlesSolid principles
Solid principles
 
Node.js File system & Streams
Node.js File system & StreamsNode.js File system & Streams
Node.js File system & Streams
 
Oops concepts in php
Oops concepts in phpOops concepts in php
Oops concepts in php
 
Clean Architecture em PHP
Clean Architecture em PHPClean Architecture em PHP
Clean Architecture em PHP
 
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...Map(), flatmap() and reduce() are your new best friends: simpler collections,...
Map(), flatmap() and reduce() are your new best friends: simpler collections,...
 
Chapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En JavaChapitre8: Collections et Enumerations En Java
Chapitre8: Collections et Enumerations En Java
 
PHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object Calisthenics
 
Quick tour of PHP from inside
Quick tour of PHP from insideQuick tour of PHP from inside
Quick tour of PHP from inside
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
What's new in PHP 8.0?
What's new in PHP 8.0?What's new in PHP 8.0?
What's new in PHP 8.0?
 
Desenvolvimento de uma API RESTful com Java EE
Desenvolvimento de uma API RESTful com Java EEDesenvolvimento de uma API RESTful com Java EE
Desenvolvimento de uma API RESTful com Java EE
 
Class 5 - PHP Strings
Class 5 - PHP StringsClass 5 - PHP Strings
Class 5 - PHP Strings
 
Recursividade
RecursividadeRecursividade
Recursividade
 
Control Structures In Php 2
Control Structures In Php 2Control Structures In Php 2
Control Structures In Php 2
 
Sequelize
SequelizeSequelize
Sequelize
 
Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)
Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)
Curso de RESTful WebServices em Java com JAX-RS (Java EE 7)
 
PHP - Introduction to PHP AJAX
PHP -  Introduction to PHP AJAXPHP -  Introduction to PHP AJAX
PHP - Introduction to PHP AJAX
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applications
 
Aula 5 - Cookies e Sessões em PHP
Aula 5 - Cookies e Sessões em PHPAula 5 - Cookies e Sessões em PHP
Aula 5 - Cookies e Sessões em PHP
 
Class 3 - PHP Functions
Class 3 - PHP FunctionsClass 3 - PHP Functions
Class 3 - PHP Functions
 

Viewers also liked

“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.Rafael Dohms
 
ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers doGuilherme Blanco
 
Thinking Object-Oriented
Thinking Object-OrientedThinking Object-Oriented
Thinking Object-Orientedadil raja
 
PHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHPPHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHPGuilherme Blanco
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13Rafael Dohms
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix itRafael Dohms
 
Granny Was a Hacker (CampJS Version)
Granny Was a Hacker (CampJS Version)Granny Was a Hacker (CampJS Version)
Granny Was a Hacker (CampJS Version)Kristine Howard
 
Introduction to Go programming
Introduction to Go programmingIntroduction to Go programming
Introduction to Go programmingExotel
 

Viewers also liked (12)

“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.“Writing code that lasts” … or writing code you won’t hate tomorrow.
“Writing code that lasts” … or writing code you won’t hate tomorrow.
 
ORM dont kill your DB, developers do
ORM dont kill your DB, developers doORM dont kill your DB, developers do
ORM dont kill your DB, developers do
 
Realtime com node.js e socket.io
Realtime com node.js e socket.ioRealtime com node.js e socket.io
Realtime com node.js e socket.io
 
Thinking Object-Oriented
Thinking Object-OrientedThinking Object-Oriented
Thinking Object-Oriented
 
PHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHPPHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHP
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13Your code sucks, let's fix it! - php|tek13
Your code sucks, let's fix it! - php|tek13
 
SPL Datastructures
SPL DatastructuresSPL Datastructures
SPL Datastructures
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix it
 
RESTful API Design, Second Edition
RESTful API Design, Second EditionRESTful API Design, Second Edition
RESTful API Design, Second Edition
 
Granny Was a Hacker (CampJS Version)
Granny Was a Hacker (CampJS Version)Granny Was a Hacker (CampJS Version)
Granny Was a Hacker (CampJS Version)
 
Introduction to Go programming
Introduction to Go programmingIntroduction to Go programming
Introduction to Go programming
 

Similar to PHP for Adults: Clean Code and Object Calisthenics

Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsSam Hennessy
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにYuya Takeyama
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolveXSolve
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency InjectionRifat Nabi
 
How to write code you won't hate tomorrow
How to write code you won't hate tomorrowHow to write code you won't hate tomorrow
How to write code you won't hate tomorrowPete McFarlane
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleHugo Hamon
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console componentHugo Hamon
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design PatternsHugo Hamon
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
Anonymous classes
Anonymous classesAnonymous classes
Anonymous classesDarkmira
 
1st CI&T Lightning Talks: Writing better code with Object Calisthenics
1st CI&T Lightning Talks: Writing better code with Object Calisthenics1st CI&T Lightning Talks: Writing better code with Object Calisthenics
1st CI&T Lightning Talks: Writing better code with Object CalisthenicsLucas Arruda
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your CodeAbbas Ali
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistenceHugo Hamon
 
Crazy things done on PHP
Crazy things done on PHPCrazy things done on PHP
Crazy things done on PHPTaras Kalapun
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...DevClub_lv
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Jeff Carouth
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in actionJace Ju
 

Similar to PHP for Adults: Clean Code and Object Calisthenics (20)

Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
 
PHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くためにPHPUnit でよりよくテストを書くために
PHPUnit でよりよくテストを書くために
 
PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
Oops in php
Oops in phpOops in php
Oops in php
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
How to write code you won't hate tomorrow
How to write code you won't hate tomorrowHow to write code you won't hate tomorrow
How to write code you won't hate tomorrow
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console component
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
 
Functional programming with php7
Functional programming with php7Functional programming with php7
Functional programming with php7
 
Anonymous classes
Anonymous classesAnonymous classes
Anonymous classes
 
1st CI&T Lightning Talks: Writing better code with Object Calisthenics
1st CI&T Lightning Talks: Writing better code with Object Calisthenics1st CI&T Lightning Talks: Writing better code with Object Calisthenics
1st CI&T Lightning Talks: Writing better code with Object Calisthenics
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
 
Crazy things done on PHP
Crazy things done on PHPCrazy things done on PHP
Crazy things done on PHP
 
Shell.php
Shell.phpShell.php
Shell.php
 
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
“SOLID principles in PHP – how to apply them in PHP and why should we care“ b...
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
 

More from Guilherme Blanco

IPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHPIPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHPGuilherme Blanco
 
Doctrine 2.0: A evolução da persistência em PHP
Doctrine 2.0: A evolução da persistência em PHPDoctrine 2.0: A evolução da persistência em PHP
Doctrine 2.0: A evolução da persistência em PHPGuilherme Blanco
 
Doctrine 2.0 Enterprise Persistence Layer for PHP
Doctrine 2.0 Enterprise Persistence Layer for PHPDoctrine 2.0 Enterprise Persistence Layer for PHP
Doctrine 2.0 Enterprise Persistence Layer for PHPGuilherme Blanco
 
Desenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmDesenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmGuilherme Blanco
 

More from Guilherme Blanco (10)

Enterprise php
Enterprise phpEnterprise php
Enterprise php
 
PHP 7
PHP 7PHP 7
PHP 7
 
Javascript para adultos
Javascript para adultosJavascript para adultos
Javascript para adultos
 
Dependency injection
Dependency injectionDependency injection
Dependency injection
 
Doctrine2 Seminário PHP
Doctrine2 Seminário PHPDoctrine2 Seminário PHP
Doctrine2 Seminário PHP
 
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHPIPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
 
Doctrine 2.0: A evolução da persistência em PHP
Doctrine 2.0: A evolução da persistência em PHPDoctrine 2.0: A evolução da persistência em PHP
Doctrine 2.0: A evolução da persistência em PHP
 
PHP, Daemons e Multimedia
PHP, Daemons e MultimediaPHP, Daemons e Multimedia
PHP, Daemons e Multimedia
 
Doctrine 2.0 Enterprise Persistence Layer for PHP
Doctrine 2.0 Enterprise Persistence Layer for PHPDoctrine 2.0 Enterprise Persistence Layer for PHP
Doctrine 2.0 Enterprise Persistence Layer for PHP
 
Desenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmDesenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine Orm
 

Recently uploaded

Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentPim van der Noll
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationKnoldus Inc.
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI AgeCprime
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Nikki Chapple
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Farhan Tariq
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Hiroshi SHIBATA
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...itnewsafrica
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPathCommunity
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfIngrid Airi González
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructureitnewsafrica
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024TopCSSGallery
 

Recently uploaded (20)

Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native developmentEmixa Mendix Meetup 11 April 2024 about Mendix Native development
Emixa Mendix Meetup 11 April 2024 about Mendix Native development
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog Presentation
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
A Framework for Development in the AI Age
A Framework for Development in the AI AgeA Framework for Development in the AI Age
A Framework for Development in the AI Age
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
Microsoft 365 Copilot: How to boost your productivity with AI – Part one: Ado...
 
Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...Genislab builds better products and faster go-to-market with Lean project man...
Genislab builds better products and faster go-to-market with Lean project man...
 
Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024Long journey of Ruby standard library at RubyConf AU 2024
Long journey of Ruby standard library at RubyConf AU 2024
 
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...Abdul Kader Baba- Managing Cybersecurity Risks  and Compliance Requirements i...
Abdul Kader Baba- Managing Cybersecurity Risks and Compliance Requirements i...
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
UiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to HeroUiPath Community: Communication Mining from Zero to Hero
UiPath Community: Communication Mining from Zero to Hero
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Generative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdfGenerative Artificial Intelligence: How generative AI works.pdf
Generative Artificial Intelligence: How generative AI works.pdf
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024Top 10 Hubspot Development Companies in 2024
Top 10 Hubspot Development Companies in 2024
 

PHP for Adults: Clean Code and Object Calisthenics

  • 1. PHP 4 ADULTS OBJECT CALISTHENICS AND CLEAN CODE
  • 5.
  • 7. S T U P I D
  • 9. S O L I D
  • 10. SINGLE RESPONSIBILITY OPEN/CLOSED PRINCIPLE LISKOV SUBSTITUTION PRINCIPLE INTERFACE SEGREGATION DEPENDENCY INVERSION
  • 12. interface Bird { public function setLocation($longitude, $latitude); public function setHeight($height); public function draw(); }
  • 13. class Penguin implements Bird { public function setHeight($height) { // Do nothing } }
  • 14. interface Bird { public function setLocation($longitude, $latitude); public function draw(); } interface FlightfulBird extends Bird 
 { 
 public function setHeight($height); }
  • 16. namespace DatingUserBundleEntity { class User { /** @var DatingUserBundleEntityImage */ protected $avatar; } } namespace DatingMediaBundleEntity { class Image { /** @var DatingUserBundleEntityUser */ protected $owner; } 
 }
  • 17. namespace DatingUserBundleEntity { class User { /** @var AvatarInterface */ protected $avatar; } interface AvatarInterface { // ... } } namespace DatingMediaBundleEntity { use DatingUserBundleEntityAvatarInterface; class Image implements AvatarInterface { /** @var DatingUserBundleEntityUser */ protected $owner; } 
 }
  • 19. RULE #1 ONLY ONE INDENTATION LEVEL PER METHOD
  • 20. 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; }
  • 21. 1 2 3 4 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
  • 23. 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; }
  • 24. 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; }
  • 25. 1 2 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; }
  • 26. 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; }
  • 27. 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; }
  • 28. 1 2 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; }
  • 29. 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; }
  • 30. 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
  • 31. 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; }
  • 32. BENEFITS ▸ Single Responsibility Principle ("S" in SOLID) ▸ Reusability
  • 33. RULE #2 NO "ELSE" KEYWORD
  • 34. 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); } }
  • 35. 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!
  • 36. 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); } }
  • 37. 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); } }
  • 38. 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); } }
  • 40. 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); } }
  • 41. 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); } }
  • 42. 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); } }
  • 43. 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); } }
  • 44. 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); } }
  • 45. 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); } }
  • 46. 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'); }
  • 47. 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'); }
  • 48. BENEFITS ▸ Prevents code duplication ▸ Increases legibility ▸ Reduce cyclomatic complexity
  • 49. RULE #3 ENCAPSULATE ALL PRIMITIVE TYPES AND STRINGS
  • 50. RULE #3 ENCAPSULATE ALL PRIMITIVE TYPES AND STRINGS IF THEY HAVE BEHAVIOR
  • 52. EXCESSIVE USAGE OF OBJECTS IN PHP (IF PHP <7!) DRASTICALLY INCREASES MEMORY FOOTPRINT! Guilherme Blanco
  • 53. 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 ... } }
  • 54. 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 ... } }
  • 55. 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 ... } }
  • 56. 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; } }
  • 57. class Item { final public static function find(Id $id) { // do find ... } final public static function create(Id $id, array $data) { // do create ... } }
  • 58. BENEFITS ▸ Type hinting ▸ Encapsulation ▸ Prevents code duplication
  • 59. RULE #4 ONE OBJECT OPERATOR (->) PER LINE
  • 61. JUST USE A NULL OBJECT! Someone watching this talk, one day
  • 62. 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'; } }
  • 63. 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
  • 64. EXCEPTION TO RULE FLUENT INTERFACE UNDER SAME METHOD
  • 66. BENEFITS ▸ Law of Demeter ▸ Readability ▸ Increases testability (easier to mock) ▸ Simplifies debugging
  • 67. RULE #5 DO NOT ABBREVIATE
  • 68. THERE ARE 2 HARD PROBLEMS IN COMPUTER SCIENCE: CACHE INVALIDATION, NAMING THINGS AND OFF BY 1 ERRORS. Tim Bray (mentioning Phil Karlton)
  • 70. CODE DUPLICATION PROBLEM! WRITE SAME NAME REPEATEDLY
  • 72. public function getPage($data) { ... } "Get" from where? public function startProcess() { ... } $trx->process('site.login'); How? WTF is that? renderHomePage forkIntoChildProcess extendedTranslator
  • 73. BENEFITS ▸ Readability ▸ Better exposing method’s intent ▸ Improved maintainability ▸ Good indicator of code duplication and encapsulation
  • 74. RULE #6 KEEP YOUR CLASSES SMALL
  • 75. 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
  • 76. BENEFITS ▸ Single Responsibility Principle ▸ Clear and objective methods ▸ Better code segregation ▸ Cleaner namespaces
  • 77. RULE #7 LIMIT CLASS INSTANCE VARIABLES IN A CLASS (BETWEEN 2 TO 5)
  • 78. 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
  • 79. class MyRegistrationService implements LoggerAwareInterface, TranslatorAwareInterface { use LoggerAwareTrait; use TranslatorAwareTrait; protected $userService; protected $passwordService; protected $eventDispatcher; // ... }
  • 80. BENEFITS ▸ Single Responsibility Principle ▸ Loose coupling ▸ Better encapsulation ▸ Testability
  • 81. RULE #8 USE FIRST CLASS COLLECTIONS
  • 83. ANY CLASS THAT CONTAINS AN ARRAY MUST NOT HAVE ANY OTHER PROPERTY. Guilherme Blanco TEXT
  • 84. 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();
  • 85. 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();
  • 86. 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();
  • 87. 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
  • 88. RULE #9 USE GETTERS AND SETTERS
  • 89. 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.
  • 90. BENEFITS ▸ Operations injection ▸ Transformations encapsulation ▸ Promotes Open/Closed Principle ("O" in SOLID)