SlideShare a Scribd company logo
Object Calisthenics
      Applied to PHP
Object Calisthenics
The speaker




    @guilhermeblanco

    http://github.com/guilhermeblanco
Object Calisthenics
Agenda




‣ Motivation
‣ Rules
‣ Application
Object Calisthenics
Erm... WTH is Object Calisthenics?




‣ Object Calisthenics
Object Calisthenics
Erm... WTH is Object Calisthenics?




‣ Object Calisthenics

     Term derived from greek,
     “exercise”, under the
     context of gymnastics.
Object Calisthenics
Erm... WTH is Object Calisthenics?




‣ Jeff Bay in The ThoughtWorks                                     Anthology [1]

  coined the term Object Calisthenics in computers,
  as a group of exercises to Object Oriented
  programming.



[1] The ThoughtWorks Anthology: Essays on Software Technology and Innovation
Object Calisthenics
Motivation




‣ Readable Code
‣ Comprehensible
‣ Testable
‣ Maintainable
                   Learning about good code practices
                      at Object CaIisthenics talk of
                     @guilhermeblanco on @gtaphp
Object Calisthenics
Rules




‣ Nine (9) rules “very” simple...
Object Calisthenics
Rule 1: Only one indentation level per method




‣ Only one indentation level per method
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;
}
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());

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

      return $input;
}
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());

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

      return $input;
}
Object Calisthenics
Rule 1: Only one indentation level per method
public function validateForm($filters = array(), $validators = array(), $options = null)
{
    $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();'
                );
            }
        }
    }
}
Object Calisthenics
Rule 1: Only one indentation level per method
public function validateForm($filters = array(), $validators = array(), $options = null)
{
    $data = $_POST;

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

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

0 foreach ($input->getMessages() as $field => $messageList) {
   1 foreach(strpos($message, "empty")) {
              ($messageList as $message) {
       2 if throw new Tss_FormException(
           3 "The field {$field} cannot be empty!",
                    3, 'javascript:history.back();'
                );
            } else {
                throw new Tss_FormException(
                     "{$message}", 3, 'javascript:history.back();'
                );
            }
        }
    }
}
Object Calisthenics
Rule 1: Only one indentation level per method
public function validateForm($filters = array(), $validators = array(), $options = null)
{
    $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();'
            );
        }
    }
}
Object Calisthenics
Rule 1: Only one indentation level per method
public function validateForm($filters = array(), $validators = array(), $options = null)
{
    $data = $_POST;

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

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

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

            throw new Tss_FormException(
                $errorMessage, 3, 'javascript:history.back();'
            );
        }
    }
}
Object Calisthenics
Rule 1: Only one indentation level per method
public function validatePost($filters = array(), $validators = array(), $options = null)
{
    $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 => $message) {
        $messageKey   = key($message);
        $message      = $message[$messageKey];
        $errorMessage = (strpos($message, "empty") === false)
            ? "The field {$field} cannot be empty!"
            : "{$message}";

        throw new Tss_FormException(
            $errorMessage, 3, 'javascript:history.back();'
        );
    }
}
Object Calisthenics
Rule 1: Only one indentation level per method
public function validatePost($filters = array(), $validators = array(), $options = null)
{
    $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 => $message) {
        $messageKey   = key($message);
        $message      = $message[$messageKey];
        $errorMessage = (strpos($message, "empty") === false)
            ? "The field {$field} cannot be empty!"
            : "{$message}";

        throw new Tss_FormException(
            $errorMessage, 3, 'javascript:history.back();'
        );
    }
}
Object Calisthenics
Rule 2: Do not use the “else” keyword




‣ Never use the “else” keyword
Object Calisthenics
Rule 2: Do not use the “else” keyword
function login()   {
    $login     =   $this->input->post('email', true);
    $password =    $this->input->post('password', true);
    $reference =   $this->input->post('reference', true);

    if ($this->clients_model->login($login, $password)) {
        redirect($reference);
    } else {
        $this->session->set_flashdata('error', 'User or password invalid.');
        $this->session->set_flashdata('reference', $reference);
        redirect('clients');
    }
}
Object Calisthenics
Rule 2: Do not use the “else” keyword
function login()   {
    $login     =   $this->input->post('email', true);
    $password =    $this->input->post('password', true);
    $reference =   $this->input->post('reference', true);

    if ($this->clients_model->login($login, $password)) {
        redirect($reference);
    } else {
        $this->session->set_flashdata('error', 'User or password invalid.');
        $this->session->set_flashdata('reference', $reference);
        redirect('clients');
    }
}
Object Calisthenics
Rule 2: Do not use the “else” keyword
function login()   {
    $login     =   $this->input->post('email', true);
    $password =    $this->input->post('password', true);
    $reference =   $this->input->post('reference', true);

    if ($this->clients_model->login($login, $password)) {
        redirect($reference);
    } else {
        $this->session->set_flashdata('error', 'User or password invalid.');
        $this->session->set_flashdata('reference', $reference);
        redirect('clients');
    }
}
Object Calisthenics
Rule 2: Do not use the “else” keyword
function login()   {
    $login     =   $this->input->post('email', true);
    $password =    $this->input->post('password', true);
    $reference =   $this->input->post('reference', true);

    if ( ! ($this->clients_model->login($login, $password))) {
        $this->session->set_flashdata('error', 'User or password invalid.');
        $this->session->set_flashdata('reference', $reference);

        $reference = 'clients';
    }

    redirect($reference);
}
Object Calisthenics
Rule 3: Wrap primitive types and strings




‣ Wrap all primitive types and strings
Object Calisthenics
Rule 3: Wrap primitive types and strings




‣ This rule cannot be completely ported to PHP, because
  the language does not perform well with an entirely
  Object Oriented code with a huge amount of instances
Object Calisthenics
Rule 3: Wrap primitive types and strings




‣ But... if the variable of primitive type has a behavior, it
  must be encapsulated
Object Calisthenics
Rule 3: Wrap primitive types and strings
class UIComponent
{
! // ...
!
    public function repaint($animate = true)
    {
        // ...
    }
}

// ...
$component->repaint(false);
Object Calisthenics
Rule 3: Wrap primitive types and strings
class UIComponent
{
! // ...
!
    public function repaint($animate = true)
    {
        // ...
    }
}

// ...
$component->repaint(false);
Object Calisthenics
Rule 3: Wrap primitive types and strings
class UIComponent
{
! // ...
!
    public function repaint(Animate $animate)
    {
        // ...
    }
}

class Animate
{
    public $animate;

    public function __construct($animate = true)
    {
        $this->animate = $animate;
    }
}

// ...
$component->repaint(new Animate(false));
Object Calisthenics
Rule 4: Only one dot per line




‣ Only one dot (arrow for PHP) per line
Object Calisthenics
Rule 4: Only one dot per line




‣ Not applicable to PHP...
Object Calisthenics
Rule 4: Only one dot per line




‣ ...but multiple nested calls...
   ‣ tend to expose an encapsulation problem
   ‣ increase difficulty to debug and exception handling
   ‣ do not represent an atomic action
Object Calisthenics
Rule 4: Only one dot per line




‣ We could adapt to the language, contemplating...
Object Calisthenics
Rule 4: Only one dot per line

‣ A chain of different objects, but only if the execution
  only includes getters and setters


               $user->getLocationPoint()->getCountry()->getName();
Object Calisthenics
Rule 4: Only one dot per line

‣ A chain of a unique object, through the usage of a
  fluent interface


            $filterChain->addFilter(new Zend_Filter_Alpha())
                        ->addFilter(new Zend_Filter_StringToLower());
Object Calisthenics
Rule 5: Do not abbreviate




‣ Do not abbreviate
Object Calisthenics
Rule 5: Do not abbreviate
Object Calisthenics
Rule 5: Do not abbreviate



‣ Think about it... why do you want to abbreviate?
Object Calisthenics
Rule 5: Do not abbreviate



‣ Think about it... why do you want to abbreviate?
   ‣ Write the same name repeatedly?
Object Calisthenics
Rule 5: Do not abbreviate



‣ Think about it... why do you want to abbreviate?
   ‣ Write the same name repeatedly?
     ‣ Then your method is reused multiple times,
       signalling a code duplication.
Object Calisthenics
Rule 5: Do not abbreviate



‣ Think about it... why do you want to abbreviate?
   ‣ Write the same name repeatedly?
     ‣ Then your method is reused multiple times,
       signalling a code duplication.
   ‣ Method name too long?
Object Calisthenics
Rule 5: Do not abbreviate



‣ Think about it... why do you want to abbreviate?
   ‣ Write the same name repeatedly?
     ‣ Then your method is reused multiple times,
       signalling a code duplication.
   ‣ Method name too long?
     ‣ Maybe your class has multiple responsibilities or it
       is missing a helper class (bad architecture).
Object Calisthenics
Rule 5: Do not abbreviate



‣ Think about it... why do you want to abbreviate?
   ‣ Write the same name repeatedly?
     ‣ Then your method is reused multiple times,
       signalling a code duplication.
   ‣ Method name too long?
     ‣ Maybe your class has multiple responsibilities or it
       is missing a helper class (bad architecture).
Object Calisthenics
Rule 6: Keep your entities small




‣ Keep your entities small
Object Calisthenics
Rule 6: Keep your entities small




‣ Original rule: 50 lines per class
Object Calisthenics
Rule 6: Keep your entities small




‣ Adapted to PHP: 100 lines per class and no more than
  15 classes per package.
‣ The change is necessary because of the lack of rule for
  documentation, which can easily occupy up to 50% of
  the lines of a class.
Object Calisthenics
Rule 7: Do not create classes with more than 2 instance variables




‣ Do not create classes with more than 2 instance
  variables
Object Calisthenics
Rule 7: Do not create classes with more than 2 instance variables




‣ Objective:
   ‣ Low cohesion
   ‣ Better encapsulation
Object Calisthenics
Rule 7: Do not create classes with more than 2 instance variables




‣ The original rule points to 2 instance variables
‣ To PHP, the suggestion is no more than 5 variables
Object Calisthenics
Rule 8: Use first class collections




‣ Use first class collections
Object Calisthenics
Rule 8: Use first class collections




‣ The rule is simple: Any class that contains a collection
  (array to PHP), cannot contain any other properties
Object Calisthenics
Rule 8: Use first class collections




‣ Objectives:
   ‣ Specific behaviors have a good place to stay
   ‣ Filtering, combining, mapping, ...
Object Calisthenics
Rule 8: Use first class collections




‣ DoctrineCommonCollectionsArrayCollection
   ‣ Countable
   ‣ IteratorAggregate (inherits Traversable)
   ‣ ArrayAccess
Object Calisthenics
Rule 9: Do not create getter/setter methods to properties




‣ Do not create getter/setter methods to properties
Object Calisthenics
Rule 9: Do not create getter/setter methods to properties




‣ Non-applicable to PHP due to language’s nature
Object Calisthenics
Rule 9: Do not create getter/setter methods to properties
/**
  * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
  */
class ApplicationCoreDomainUserModelUserProxy
! extends ApplicationCoreDomainUserModelUser
! implements DoctrineORMProxyProxy
{
     // ...

    public function getId()
    {
        $this->__load();
        return parent::getId();
    }

    public function setId($id)
    {
        $this->__load();
        return parent::setId($id);
    }

    // ...
}
Object Calisthenics
“Rule 10”: Document your code!




‣ Document your code!!!!!!!!!!!!!!!!!
Object Calisthenics
That’s all folks!




‣ Questions?



    @guilhermeblanco

    http://github.com/guilhermeblanco

More Related Content

What's hot (20)

PDF
TypeScript Best Practices
felixbillon
 
PPTX
Java script form validation
AbhishekMondal42
 
PPTX
php2 : formulaire-session-PDO
Abdoulaye Dieng
 
PDF
Load Data Fast!
Karwin Software Solutions LLC
 
PPT
Sql Server Security
Vinod Kumar
 
PPT
Control Structures In Php 2
Digital Insights - Digital Marketing Agency
 
PDF
Scalable JavaScript Application Architecture
Nicholas Zakas
 
PPT
Class 3 - PHP Functions
Ahmed Swilam
 
PDF
Railway Oriented Programming
Scott Wlaschin
 
PDF
An introduction to Rust: the modern programming language to develop safe and ...
Claudio Capobianco
 
PPTX
Introduction to DOM
Daniel Bragais
 
PPTX
Api security
teodorcotruta
 
PDF
Serving ML easily with FastAPI
Sebastián Ramírez Montaño
 
PDF
Why Task Queues - ComoRichWeb
Bryan Helmig
 
PPT
jQuery
Mostafa Bayomi
 
PPT
ASP.NET MVC Presentation
ivpol
 
PDF
MongoDB Performance Tuning
MongoDB
 
PPTX
NGSIv1 を知っている開発者向けの NGSIv2 の概要 (Orion 3.2.0対応)
fisuda
 
PDF
W3C Tutorial on Semantic Web and Linked Data at WWW 2013
Fabien Gandon
 
PDF
Ajp notes-chapter-06
Ankit Dubey
 
TypeScript Best Practices
felixbillon
 
Java script form validation
AbhishekMondal42
 
php2 : formulaire-session-PDO
Abdoulaye Dieng
 
Sql Server Security
Vinod Kumar
 
Control Structures In Php 2
Digital Insights - Digital Marketing Agency
 
Scalable JavaScript Application Architecture
Nicholas Zakas
 
Class 3 - PHP Functions
Ahmed Swilam
 
Railway Oriented Programming
Scott Wlaschin
 
An introduction to Rust: the modern programming language to develop safe and ...
Claudio Capobianco
 
Introduction to DOM
Daniel Bragais
 
Api security
teodorcotruta
 
Serving ML easily with FastAPI
Sebastián Ramírez Montaño
 
Why Task Queues - ComoRichWeb
Bryan Helmig
 
ASP.NET MVC Presentation
ivpol
 
MongoDB Performance Tuning
MongoDB
 
NGSIv1 を知っている開発者向けの NGSIv2 の概要 (Orion 3.2.0対応)
fisuda
 
W3C Tutorial on Semantic Web and Linked Data at WWW 2013
Fabien Gandon
 
Ajp notes-chapter-06
Ankit Dubey
 

Similar to Object Calisthenics Applied to PHP (9)

PDF
PHP for Adults: Clean Code and Object Calisthenics
Guilherme Blanco
 
PDF
You code sucks, let's fix it
Rafael Dohms
 
PDF
Your code sucks, let's fix it - DPC UnCon
Rafael Dohms
 
PDF
Unit testing with zend framework tek11
Michelangelo van Dam
 
KEY
Unit testing with zend framework PHPBenelux
Michelangelo van Dam
 
PDF
Error Reporting in ZF2: form messages, custom error pages, logging
Steve Maraspin
 
KEY
Data::FormValidator Simplified
Fred Moyer
 
PDF
Gravity Forms Hooks & Filters
iamdangavin
 
KEY
Unit testing zend framework apps
Michelangelo van Dam
 
PHP for Adults: Clean Code and Object Calisthenics
Guilherme Blanco
 
You code sucks, let's fix it
Rafael Dohms
 
Your code sucks, let's fix it - DPC UnCon
Rafael Dohms
 
Unit testing with zend framework tek11
Michelangelo van Dam
 
Unit testing with zend framework PHPBenelux
Michelangelo van Dam
 
Error Reporting in ZF2: form messages, custom error pages, logging
Steve Maraspin
 
Data::FormValidator Simplified
Fred Moyer
 
Gravity Forms Hooks & Filters
iamdangavin
 
Unit testing zend framework apps
Michelangelo van Dam
 
Ad

More from Guilherme Blanco (13)

PDF
Enterprise php
Guilherme Blanco
 
PDF
PHP para Adultos: Clean Code e Object Calisthenics
Guilherme Blanco
 
PDF
ORM dont kill your DB, developers do
Guilherme Blanco
 
KEY
PHPubSP Object Calisthenics aplicado ao PHP
Guilherme Blanco
 
ODP
Javascript para adultos
Guilherme Blanco
 
ODP
Dependency injection
Guilherme Blanco
 
PDF
Doctrine2 Seminário PHP
Guilherme Blanco
 
PDF
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
Guilherme Blanco
 
KEY
Doctrine 2.0: A evolução da persistência em PHP
Guilherme Blanco
 
KEY
PHP, Daemons e Multimedia
Guilherme Blanco
 
PPTX
Doctrine 2.0 Enterprise Persistence Layer for PHP
Guilherme Blanco
 
PPT
Desenvolvimento Agil Com Doctrine Orm
Guilherme Blanco
 
Enterprise php
Guilherme Blanco
 
PHP para Adultos: Clean Code e Object Calisthenics
Guilherme Blanco
 
ORM dont kill your DB, developers do
Guilherme Blanco
 
PHPubSP Object Calisthenics aplicado ao PHP
Guilherme Blanco
 
Javascript para adultos
Guilherme Blanco
 
Dependency injection
Guilherme Blanco
 
Doctrine2 Seminário PHP
Guilherme Blanco
 
IPC2010SE Doctrine2 Enterprise Persistence Layer for PHP
Guilherme Blanco
 
Doctrine 2.0: A evolução da persistência em PHP
Guilherme Blanco
 
PHP, Daemons e Multimedia
Guilherme Blanco
 
Doctrine 2.0 Enterprise Persistence Layer for PHP
Guilherme Blanco
 
Desenvolvimento Agil Com Doctrine Orm
Guilherme Blanco
 
Ad

Recently uploaded (20)

PDF
Productivity Management Software | Workstatus
Lovely Baghel
 
PDF
How Current Advanced Cyber Threats Transform Business Operation
Eryk Budi Pratama
 
PDF
Shuen Mei Parth Sharma Boost Productivity, Innovation and Efficiency wit...
AWS Chicago
 
PPTX
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
PDF
Rethinking Security Operations - Modern SOC.pdf
Haris Chughtai
 
PDF
Lecture A - AI Workflows for Banking.pdf
Dr. LAM Yat-fai (林日辉)
 
PDF
Bitcoin+ Escalando sin concesiones - Parte 1
Fernando Paredes García
 
PDF
How a Code Plagiarism Checker Protects Originality in Programming
Code Quiry
 
PPTX
UI5Con 2025 - Beyond UI5 Controls with the Rise of Web Components
Wouter Lemaire
 
PDF
Generative AI in Healthcare: Benefits, Use Cases & Challenges
Lily Clark
 
PDF
Building Resilience with Digital Twins : Lessons from Korea
SANGHEE SHIN
 
PPTX
Darren Mills The Migration Modernization Balancing Act: Navigating Risks and...
AWS Chicago
 
PPTX
The Yotta x CloudStack Advantage: Scalable, India-First Cloud
ShapeBlue
 
PDF
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
Fwdays
 
PDF
CIFDAQ Market Insight for 14th July 2025
CIFDAQ
 
PDF
OpenInfra ID 2025 - Are Containers Dying? Rethinking Isolation with MicroVMs.pdf
Muhammad Yuga Nugraha
 
PDF
Apache CloudStack 201: Let's Design & Build an IaaS Cloud
ShapeBlue
 
PPTX
Top Managed Service Providers in Los Angeles
Captain IT
 
PPTX
Extensions Framework (XaaS) - Enabling Orchestrate Anything
ShapeBlue
 
PDF
Upskill to Agentic Automation 2025 - Kickoff Meeting
DianaGray10
 
Productivity Management Software | Workstatus
Lovely Baghel
 
How Current Advanced Cyber Threats Transform Business Operation
Eryk Budi Pratama
 
Shuen Mei Parth Sharma Boost Productivity, Innovation and Efficiency wit...
AWS Chicago
 
Building and Operating a Private Cloud with CloudStack and LINBIT CloudStack ...
ShapeBlue
 
Rethinking Security Operations - Modern SOC.pdf
Haris Chughtai
 
Lecture A - AI Workflows for Banking.pdf
Dr. LAM Yat-fai (林日辉)
 
Bitcoin+ Escalando sin concesiones - Parte 1
Fernando Paredes García
 
How a Code Plagiarism Checker Protects Originality in Programming
Code Quiry
 
UI5Con 2025 - Beyond UI5 Controls with the Rise of Web Components
Wouter Lemaire
 
Generative AI in Healthcare: Benefits, Use Cases & Challenges
Lily Clark
 
Building Resilience with Digital Twins : Lessons from Korea
SANGHEE SHIN
 
Darren Mills The Migration Modernization Balancing Act: Navigating Risks and...
AWS Chicago
 
The Yotta x CloudStack Advantage: Scalable, India-First Cloud
ShapeBlue
 
"Effect, Fiber & Schema: tactical and technical characteristics of Effect.ts"...
Fwdays
 
CIFDAQ Market Insight for 14th July 2025
CIFDAQ
 
OpenInfra ID 2025 - Are Containers Dying? Rethinking Isolation with MicroVMs.pdf
Muhammad Yuga Nugraha
 
Apache CloudStack 201: Let's Design & Build an IaaS Cloud
ShapeBlue
 
Top Managed Service Providers in Los Angeles
Captain IT
 
Extensions Framework (XaaS) - Enabling Orchestrate Anything
ShapeBlue
 
Upskill to Agentic Automation 2025 - Kickoff Meeting
DianaGray10
 

Object Calisthenics Applied to PHP

  • 1. Object Calisthenics Applied to PHP
  • 2. Object Calisthenics The speaker @guilhermeblanco http://github.com/guilhermeblanco
  • 4. Object Calisthenics Erm... WTH is Object Calisthenics? ‣ Object Calisthenics
  • 5. Object Calisthenics Erm... WTH is Object Calisthenics? ‣ Object Calisthenics Term derived from greek, “exercise”, under the context of gymnastics.
  • 6. Object Calisthenics Erm... WTH is Object Calisthenics? ‣ Jeff Bay in The ThoughtWorks Anthology [1] coined the term Object Calisthenics in computers, as a group of exercises to Object Oriented programming. [1] The ThoughtWorks Anthology: Essays on Software Technology and Innovation
  • 7. Object Calisthenics Motivation ‣ Readable Code ‣ Comprehensible ‣ Testable ‣ Maintainable Learning about good code practices at Object CaIisthenics talk of @guilhermeblanco on @gtaphp
  • 8. Object Calisthenics Rules ‣ Nine (9) rules “very” simple...
  • 9. Object Calisthenics Rule 1: Only one indentation level per method ‣ Only one indentation level per method
  • 10. 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; }
  • 11. 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()); 0 if ($input->hasInvalid() || $input->hasMissing()) { 1 foreach ($input->getMessages() as $field => $messageList) { 2 foreach(strpos($message, "empty")) { ($messageList as $message) { 3 if throw new Tss_FormException( 4 "The field {$field} cannot be empty!", 3, 'javascript:history.back();' ); } else { throw new Tss_FormException( "{$message}", 3, 'javascript:history.back();' ); } } } } return $input; }
  • 12. 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()); 0 if ($input->hasInvalid() || $input->hasMissing()) { 1 foreach ($input->getMessages() as $field => $messageList) { 2 foreach(strpos($message, "empty")) { ($messageList as $message) { 3 if throw new Tss_FormException( 4 "The field {$field} cannot be empty!", 3, 'javascript:history.back();' ); } else { throw new Tss_FormException( "{$message}", 3, 'javascript:history.back();' ); } } } } return $input; }
  • 13. Object Calisthenics Rule 1: Only one indentation level per method public function validateForm($filters = array(), $validators = array(), $options = null) { $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();' ); } } } }
  • 14. Object Calisthenics Rule 1: Only one indentation level per method public function validateForm($filters = array(), $validators = array(), $options = null) { $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; } 0 foreach ($input->getMessages() as $field => $messageList) { 1 foreach(strpos($message, "empty")) { ($messageList as $message) { 2 if throw new Tss_FormException( 3 "The field {$field} cannot be empty!", 3, 'javascript:history.back();' ); } else { throw new Tss_FormException( "{$message}", 3, 'javascript:history.back();' ); } } } }
  • 15. Object Calisthenics Rule 1: Only one indentation level per method public function validateForm($filters = array(), $validators = array(), $options = null) { $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();' ); } } }
  • 16. Object Calisthenics Rule 1: Only one indentation level per method public function validateForm($filters = array(), $validators = array(), $options = null) { $data = $_POST; $input = new Zend_Filter_Input($filters, $validators, $data, $options); $input->setDefaultEscapeFilter(new Zend_Filter_StringTrim()); if ( ! ($input->hasInvalid() || $input->hasMissing())) { return $input; } 0 foreach ($input->getMessages() as $field => $messageList) { 1 foreach ($messageList as $message) { "empty") === false) 2 $errorMessage = (strpos($message,be empty!" ? "The field {$field} cannot : "{$message}"; throw new Tss_FormException( $errorMessage, 3, 'javascript:history.back();' ); } } }
  • 17. Object Calisthenics Rule 1: Only one indentation level per method public function validatePost($filters = array(), $validators = array(), $options = null) { $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 => $message) { $messageKey = key($message); $message = $message[$messageKey]; $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : "{$message}"; throw new Tss_FormException( $errorMessage, 3, 'javascript:history.back();' ); } }
  • 18. Object Calisthenics Rule 1: Only one indentation level per method public function validatePost($filters = array(), $validators = array(), $options = null) { $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 => $message) { $messageKey = key($message); $message = $message[$messageKey]; $errorMessage = (strpos($message, "empty") === false) ? "The field {$field} cannot be empty!" : "{$message}"; throw new Tss_FormException( $errorMessage, 3, 'javascript:history.back();' ); } }
  • 19. Object Calisthenics Rule 2: Do not use the “else” keyword ‣ Never use the “else” keyword
  • 20. Object Calisthenics Rule 2: Do not use the “else” keyword function login() { $login = $this->input->post('email', true); $password = $this->input->post('password', true); $reference = $this->input->post('reference', true); if ($this->clients_model->login($login, $password)) { redirect($reference); } else { $this->session->set_flashdata('error', 'User or password invalid.'); $this->session->set_flashdata('reference', $reference); redirect('clients'); } }
  • 21. Object Calisthenics Rule 2: Do not use the “else” keyword function login() { $login = $this->input->post('email', true); $password = $this->input->post('password', true); $reference = $this->input->post('reference', true); if ($this->clients_model->login($login, $password)) { redirect($reference); } else { $this->session->set_flashdata('error', 'User or password invalid.'); $this->session->set_flashdata('reference', $reference); redirect('clients'); } }
  • 22. Object Calisthenics Rule 2: Do not use the “else” keyword function login() { $login = $this->input->post('email', true); $password = $this->input->post('password', true); $reference = $this->input->post('reference', true); if ($this->clients_model->login($login, $password)) { redirect($reference); } else { $this->session->set_flashdata('error', 'User or password invalid.'); $this->session->set_flashdata('reference', $reference); redirect('clients'); } }
  • 23. Object Calisthenics Rule 2: Do not use the “else” keyword function login() { $login = $this->input->post('email', true); $password = $this->input->post('password', true); $reference = $this->input->post('reference', true); if ( ! ($this->clients_model->login($login, $password))) { $this->session->set_flashdata('error', 'User or password invalid.'); $this->session->set_flashdata('reference', $reference); $reference = 'clients'; } redirect($reference); }
  • 24. Object Calisthenics Rule 3: Wrap primitive types and strings ‣ Wrap all primitive types and strings
  • 25. Object Calisthenics Rule 3: Wrap primitive types and strings ‣ This rule cannot be completely ported to PHP, because the language does not perform well with an entirely Object Oriented code with a huge amount of instances
  • 26. Object Calisthenics Rule 3: Wrap primitive types and strings ‣ But... if the variable of primitive type has a behavior, it must be encapsulated
  • 27. Object Calisthenics Rule 3: Wrap primitive types and strings class UIComponent { ! // ... ! public function repaint($animate = true) { // ... } } // ... $component->repaint(false);
  • 28. Object Calisthenics Rule 3: Wrap primitive types and strings class UIComponent { ! // ... ! public function repaint($animate = true) { // ... } } // ... $component->repaint(false);
  • 29. Object Calisthenics Rule 3: Wrap primitive types and strings class UIComponent { ! // ... ! public function repaint(Animate $animate) { // ... } } class Animate { public $animate; public function __construct($animate = true) { $this->animate = $animate; } } // ... $component->repaint(new Animate(false));
  • 30. Object Calisthenics Rule 4: Only one dot per line ‣ Only one dot (arrow for PHP) per line
  • 31. Object Calisthenics Rule 4: Only one dot per line ‣ Not applicable to PHP...
  • 32. Object Calisthenics Rule 4: Only one dot per line ‣ ...but multiple nested calls... ‣ tend to expose an encapsulation problem ‣ increase difficulty to debug and exception handling ‣ do not represent an atomic action
  • 33. Object Calisthenics Rule 4: Only one dot per line ‣ We could adapt to the language, contemplating...
  • 34. Object Calisthenics Rule 4: Only one dot per line ‣ A chain of different objects, but only if the execution only includes getters and setters $user->getLocationPoint()->getCountry()->getName();
  • 35. Object Calisthenics Rule 4: Only one dot per line ‣ A chain of a unique object, through the usage of a fluent interface $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower());
  • 36. Object Calisthenics Rule 5: Do not abbreviate ‣ Do not abbreviate
  • 37. Object Calisthenics Rule 5: Do not abbreviate
  • 38. Object Calisthenics Rule 5: Do not abbreviate ‣ Think about it... why do you want to abbreviate?
  • 39. Object Calisthenics Rule 5: Do not abbreviate ‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly?
  • 40. Object Calisthenics Rule 5: Do not abbreviate ‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication.
  • 41. Object Calisthenics Rule 5: Do not abbreviate ‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication. ‣ Method name too long?
  • 42. Object Calisthenics Rule 5: Do not abbreviate ‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication. ‣ Method name too long? ‣ Maybe your class has multiple responsibilities or it is missing a helper class (bad architecture).
  • 43. Object Calisthenics Rule 5: Do not abbreviate ‣ Think about it... why do you want to abbreviate? ‣ Write the same name repeatedly? ‣ Then your method is reused multiple times, signalling a code duplication. ‣ Method name too long? ‣ Maybe your class has multiple responsibilities or it is missing a helper class (bad architecture).
  • 44. Object Calisthenics Rule 6: Keep your entities small ‣ Keep your entities small
  • 45. Object Calisthenics Rule 6: Keep your entities small ‣ Original rule: 50 lines per class
  • 46. Object Calisthenics Rule 6: Keep your entities small ‣ Adapted to PHP: 100 lines per class and no more than 15 classes per package. ‣ The change is necessary because of the lack of rule for documentation, which can easily occupy up to 50% of the lines of a class.
  • 47. Object Calisthenics Rule 7: Do not create classes with more than 2 instance variables ‣ Do not create classes with more than 2 instance variables
  • 48. Object Calisthenics Rule 7: Do not create classes with more than 2 instance variables ‣ Objective: ‣ Low cohesion ‣ Better encapsulation
  • 49. Object Calisthenics Rule 7: Do not create classes with more than 2 instance variables ‣ The original rule points to 2 instance variables ‣ To PHP, the suggestion is no more than 5 variables
  • 50. Object Calisthenics Rule 8: Use first class collections ‣ Use first class collections
  • 51. Object Calisthenics Rule 8: Use first class collections ‣ The rule is simple: Any class that contains a collection (array to PHP), cannot contain any other properties
  • 52. Object Calisthenics Rule 8: Use first class collections ‣ Objectives: ‣ Specific behaviors have a good place to stay ‣ Filtering, combining, mapping, ...
  • 53. Object Calisthenics Rule 8: Use first class collections ‣ DoctrineCommonCollectionsArrayCollection ‣ Countable ‣ IteratorAggregate (inherits Traversable) ‣ ArrayAccess
  • 54. Object Calisthenics Rule 9: Do not create getter/setter methods to properties ‣ Do not create getter/setter methods to properties
  • 55. Object Calisthenics Rule 9: Do not create getter/setter methods to properties ‣ Non-applicable to PHP due to language’s nature
  • 56. Object Calisthenics Rule 9: Do not create getter/setter methods to properties /** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class ApplicationCoreDomainUserModelUserProxy ! extends ApplicationCoreDomainUserModelUser ! implements DoctrineORMProxyProxy { // ... public function getId() { $this->__load(); return parent::getId(); } public function setId($id) { $this->__load(); return parent::setId($id); } // ... }
  • 57. Object Calisthenics “Rule 10”: Document your code! ‣ Document your code!!!!!!!!!!!!!!!!!
  • 58. Object Calisthenics That’s all folks! ‣ Questions? @guilhermeblanco http://github.com/guilhermeblanco

Editor's Notes