SlideShare a Scribd company logo
PHP 4 ADULTS
OBJECT CALISTHENICS AND CLEAN CODE
GUILHERMEBLANCO
GUILHERMEBLANCO
Guilherme Blanco
MOTIVATION
▸ Readability
▸ Maintainability
▸ Reusability
▸ Testability
SUMMING UP…
PHP for Adults: Clean Code and Object Calisthenics
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
Ad

More Related Content

What's hot (20)

Lazy java
Lazy javaLazy java
Lazy java
Mario Fusco
 
關於SQL Injection的那些奇技淫巧
關於SQL Injection的那些奇技淫巧關於SQL Injection的那些奇技淫巧
關於SQL Injection的那些奇技淫巧
Orange Tsai
 
ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web API
habib_786
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 Way
Debasish Ghosh
 
Easy data-with-spring-data-jpa
Easy data-with-spring-data-jpaEasy data-with-spring-data-jpa
Easy data-with-spring-data-jpa
Staples
 
Clean coding-practices
Clean coding-practicesClean coding-practices
Clean coding-practices
John Ferguson Smart Limited
 
Rest API
Rest APIRest API
Rest API
Rohana K Amarakoon
 
Spring Security 5
Spring Security 5Spring Security 5
Spring Security 5
Jesus Perez Franco
 
Hexagonal Design in Django
Hexagonal Design in DjangoHexagonal Design in Django
Hexagonal Design in Django
mvschaik
 
Broadleaf Presents Thymeleaf
Broadleaf Presents ThymeleafBroadleaf Presents Thymeleaf
Broadleaf Presents Thymeleaf
Broadleaf Commerce
 
Podstawy PHP
Podstawy PHPPodstawy PHP
Podstawy PHP
Maciej Dmowski
 
RESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkRESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP Framework
Bo-Yi Wu
 
Monadic Java
Monadic JavaMonadic Java
Monadic Java
Mario Fusco
 
Tweaking the interactive grid
Tweaking the interactive gridTweaking the interactive grid
Tweaking the interactive grid
Roel Hartman
 
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
Evolving a Clean, Pragmatic Architecture - A Craftsman's GuideEvolving a Clean, Pragmatic Architecture - A Craftsman's Guide
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
Victor Rentea
 
Advanced PHPUnit Testing
Advanced PHPUnit TestingAdvanced PHPUnit Testing
Advanced PHPUnit Testing
Mike Lively
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
Mario Fusco
 
Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017
Roman Elizarov
 
4.3 MySQL + PHP
4.3 MySQL + PHP4.3 MySQL + PHP
4.3 MySQL + PHP
Jalpesh Vasa
 
Dependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functionalDependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functional
Marian Wamsiedel
 
關於SQL Injection的那些奇技淫巧
關於SQL Injection的那些奇技淫巧關於SQL Injection的那些奇技淫巧
關於SQL Injection的那些奇技淫巧
Orange Tsai
 
ASP.NET Web API
ASP.NET Web APIASP.NET Web API
ASP.NET Web API
habib_786
 
Functional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 WayFunctional Domain Modeling - The ZIO 2 Way
Functional Domain Modeling - The ZIO 2 Way
Debasish Ghosh
 
Easy data-with-spring-data-jpa
Easy data-with-spring-data-jpaEasy data-with-spring-data-jpa
Easy data-with-spring-data-jpa
Staples
 
Hexagonal Design in Django
Hexagonal Design in DjangoHexagonal Design in Django
Hexagonal Design in Django
mvschaik
 
RESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkRESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP Framework
Bo-Yi Wu
 
Tweaking the interactive grid
Tweaking the interactive gridTweaking the interactive grid
Tweaking the interactive grid
Roel Hartman
 
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
Evolving a Clean, Pragmatic Architecture - A Craftsman's GuideEvolving a Clean, Pragmatic Architecture - A Craftsman's Guide
Evolving a Clean, Pragmatic Architecture - A Craftsman's Guide
Victor Rentea
 
Advanced PHPUnit Testing
Advanced PHPUnit TestingAdvanced PHPUnit Testing
Advanced PHPUnit Testing
Mike Lively
 
From object oriented to functional domain modeling
From object oriented to functional domain modelingFrom object oriented to functional domain modeling
From object oriented to functional domain modeling
Mario Fusco
 
Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017Deep dive into Coroutines on JVM @ KotlinConf 2017
Deep dive into Coroutines on JVM @ KotlinConf 2017
Roman Elizarov
 
Dependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functionalDependency injection in Java, from naive to functional
Dependency injection in Java, from naive to functional
Marian Wamsiedel
 

Viewers also liked (13)

“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 do
Guilherme Blanco
 
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
Caio Ribeiro Pereira
 
Thinking Object-Oriented
Thinking Object-OrientedThinking Object-Oriented
Thinking Object-Oriented
adil raja
 
PHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHPPHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHP
Guilherme 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 it
Rafael 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|tek13
Rafael Dohms
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
Guilherme Blanco
 
SPL Datastructures
SPL DatastructuresSPL Datastructures
SPL Datastructures
Felipe Ribeiro
 
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
Rafael Dohms
 
RESTful API Design, Second Edition
RESTful API Design, Second EditionRESTful API Design, Second Edition
RESTful API Design, Second Edition
Apigee | Google Cloud
 
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 programming
Exotel
 
“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 do
Guilherme Blanco
 
Thinking Object-Oriented
Thinking Object-OrientedThinking Object-Oriented
Thinking Object-Oriented
adil raja
 
PHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHPPHPubSP Object Calisthenics aplicado ao PHP
PHPubSP Object Calisthenics aplicado ao PHP
Guilherme 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 it
Rafael 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|tek13
Rafael Dohms
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
Guilherme Blanco
 
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
Rafael 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 programming
Exotel
 
Ad

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
Sam 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 / XSolve
XSolve
 
Oops in php
Oops in phpOops in php
Oops in php
Gourishankar R Pujar
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
Rifat 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 tomorrow
Pete 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 Pimple
Hugo Hamon
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console component
Hugo Hamon
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
Hugo 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 UnCon
Rafael Dohms
 
Functional programming with php7
Functional programming with php7Functional programming with php7
Functional programming with php7
Sérgio Rafael Siqueira
 
Anonymous classes
Anonymous classesAnonymous classes
Anonymous classes
Darkmira
 
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
Lucas Arruda
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
Abbas Ali
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
Hugo Hamon
 
Crazy things done on PHP
Crazy things done on PHPCrazy things done on PHP
Crazy things done on PHP
Taras Kalapun
 
Shell.php
Shell.phpShell.php
Shell.php
Dado Antik
 
“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.4
Jeff Carouth
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
Jace Ju
 
Adding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy ApplicationsAdding Dependency Injection to Legacy Applications
Adding Dependency Injection to Legacy Applications
Sam 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 / XSolve
XSolve
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
Rifat 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 tomorrow
Pete 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 Pimple
Hugo Hamon
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console component
Hugo Hamon
 
Database Design Patterns
Database Design PatternsDatabase Design Patterns
Database Design Patterns
Hugo 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 UnCon
Rafael Dohms
 
Anonymous classes
Anonymous classesAnonymous classes
Anonymous classes
Darkmira
 
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
Lucas Arruda
 
Tidy Up Your Code
Tidy Up Your CodeTidy Up Your Code
Tidy Up Your Code
Abbas Ali
 
The History of PHPersistence
The History of PHPersistenceThe History of PHPersistence
The History of PHPersistence
Hugo Hamon
 
Crazy things done on PHP
Crazy things done on PHPCrazy things done on PHP
Crazy things done on PHP
Taras 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.4
Jeff Carouth
 
Advanced php testing in action
Advanced php testing in actionAdvanced php testing in action
Advanced php testing in action
Jace Ju
 
Ad

More from Guilherme Blanco (10)

Enterprise php
Enterprise phpEnterprise php
Enterprise php
Guilherme Blanco
 
PHP 7
PHP 7PHP 7
PHP 7
Guilherme Blanco
 
Javascript para adultos
Javascript para adultosJavascript para adultos
Javascript para adultos
Guilherme Blanco
 
Dependency injection
Dependency injectionDependency injection
Dependency injection
Guilherme Blanco
 
Doctrine2 Seminário PHP
Doctrine2 Seminário PHPDoctrine2 Seminário PHP
Doctrine2 Seminário PHP
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 PHP
Guilherme 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 PHP
Guilherme Blanco
 
PHP, Daemons e Multimedia
PHP, Daemons e MultimediaPHP, Daemons e Multimedia
PHP, Daemons e Multimedia
Guilherme 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 PHP
Guilherme Blanco
 
Desenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmDesenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine Orm
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 PHP
Guilherme 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 PHP
Guilherme 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 PHP
Guilherme Blanco
 
Desenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine OrmDesenvolvimento Agil Com Doctrine Orm
Desenvolvimento Agil Com Doctrine Orm
Guilherme Blanco
 

Recently uploaded (20)

TrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business ConsultingTrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business Consulting
Trs Labs
 
Heap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and DeletionHeap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and Deletion
Jaydeep Kale
 
Social Media App Development Company-EmizenTech
Social Media App Development Company-EmizenTechSocial Media App Development Company-EmizenTech
Social Media App Development Company-EmizenTech
Steve Jonas
 
Vaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without HallucinationsVaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without Hallucinations
john409870
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
Semantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AISemantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AI
artmondano
 
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptxSpecial Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
shyamraj55
 
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
organizerofv
 
Technology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data AnalyticsTechnology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data Analytics
InData Labs
 
Quantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur MorganQuantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur Morgan
Arthur Morgan
 
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes
 
Top 10 IT Help Desk Outsourcing Services
Top 10 IT Help Desk Outsourcing ServicesTop 10 IT Help Desk Outsourcing Services
Top 10 IT Help Desk Outsourcing Services
Infrassist Technologies Pvt. Ltd.
 
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
Alan Dix
 
Cyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of securityCyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of security
riccardosl1
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-UmgebungenHCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
panagenda
 
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven InsightsAndrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell
 
Rusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond SparkRusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond Spark
carlyakerly1
 
TrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business ConsultingTrsLabs - Fintech Product & Business Consulting
TrsLabs - Fintech Product & Business Consulting
Trs Labs
 
Heap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and DeletionHeap, Types of Heap, Insertion and Deletion
Heap, Types of Heap, Insertion and Deletion
Jaydeep Kale
 
Social Media App Development Company-EmizenTech
Social Media App Development Company-EmizenTechSocial Media App Development Company-EmizenTech
Social Media App Development Company-EmizenTech
Steve Jonas
 
Vaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without HallucinationsVaibhav Gupta BAML: AI work flows without Hallucinations
Vaibhav Gupta BAML: AI work flows without Hallucinations
john409870
 
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
#StandardsGoals for 2025: Standards & certification roundup - Tech Forum 2025
BookNet Canada
 
Semantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AISemantic Cultivators : The Critical Future Role to Enable AI
Semantic Cultivators : The Critical Future Role to Enable AI
artmondano
 
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptxSpecial Meetup Edition - TDX Bengaluru Meetup #52.pptx
Special Meetup Edition - TDX Bengaluru Meetup #52.pptx
shyamraj55
 
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
IEDM 2024 Tutorial2_Advances in CMOS Technologies and Future Directions for C...
organizerofv
 
Technology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data AnalyticsTechnology Trends in 2025: AI and Big Data Analytics
Technology Trends in 2025: AI and Big Data Analytics
InData Labs
 
Quantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur MorganQuantum Computing Quick Research Guide by Arthur Morgan
Quantum Computing Quick Research Guide by Arthur Morgan
Arthur Morgan
 
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes Partner Innovation Updates for May 2025
ThousandEyes
 
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
AI Changes Everything – Talk at Cardiff Metropolitan University, 29th April 2...
Alan Dix
 
Cyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of securityCyber Awareness overview for 2025 month of security
Cyber Awareness overview for 2025 month of security
riccardosl1
 
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In FranceManifest Pre-Seed Update | A Humanoid OEM Deeptech In France
Manifest Pre-Seed Update | A Humanoid OEM Deeptech In France
chb3
 
tecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdftecnologias de las primeras civilizaciones.pdf
tecnologias de las primeras civilizaciones.pdf
fjgm517
 
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep DiveDesigning Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
Designing Low-Latency Systems with Rust and ScyllaDB: An Architectural Deep Dive
ScyllaDB
 
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-UmgebungenHCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
HCL Nomad Web – Best Practices und Verwaltung von Multiuser-Umgebungen
panagenda
 
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven InsightsAndrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell: Transforming Business Strategy Through Data-Driven Insights
Andrew Marnell
 
Rusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond SparkRusty Waters: Elevating Lakehouses Beyond Spark
Rusty Waters: Elevating Lakehouses Beyond Spark
carlyakerly1
 

PHP for Adults: Clean Code and Object Calisthenics

  • 1. PHP 4 ADULTS OBJECT CALISTHENICS AND CLEAN CODE
  • 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)