PHP - Object Calisthenics
I am Giorgio Cefaro I work as Lead Software Engineer in Hootsuite You can find me at @giorrrgio Hello!
“ Our code sucks! let’s fix it!
Calisthenics /ˌkalɪsˈθɛnɪks/ kalòs (καλός) good sthénos (σθένος) strength
Calisthenics are not rules
Code class SpreadSheet { private $headers; private $rows; public function printCSV(string $endLine, string $separator): string { $sheet = $this->headers . $endLine; foreach ($this->rows as $row) { foreach ($row as $cell) { $sheet .= $cell->print() . $separator; } $sheet .= $endLine; } return $sheet; } }
Only one level of indentation per line 1
Code class SpreadSheet { private $headers; private $rows; public function printCSV(string $endLine, string $separator): string { return $this->headers . $endLine . $this->printRows(); } private function printRows(string $endLine, string $separator): string { $printed = ''; foreach ($this->rows as $row) { $printed .= $this->printRow($row, $separator) . $endLine; } return $printed; } private function printRow(array $row, string $separator): string { $printed = ''; foreach ($row as $cell) { $printed .= $cell->print() . $separator; } return $printed; } }
Code class SpreadSheet { public function isMultitab(): bool { if (count($this->tabs) > 1) { return true; } else { return false; } } }
Don’t use the ELSE keyword 2
Code class SpreadSheet { public function isMultitab(): bool { if (count($this->tabs) > 1) { return true; } return false; } }
Code class Task { /** @var int Task priority. */ private $priority; public function isHighPriority(): bool { return $this->priority > 9; } }
Wrap all primitives and strings 3
Code class Task { /** @var Priority Task priority. */ private $priority; public function isHighPriority(): bool { return $this->priority->isHigh(); } } class Priority { const PRIORITY_HIGH = 9; /** @var int priority value. */ private $value; public function isHigh(): bool { return $this->value > self::PRIORITY_HIGH; } }
Code class SpreadSheet { private $headers; private $title; private $rows; public function __construct( string $headers, string $title, array $rows) { //[...] } public function addRow(Row $row) { $this->rows[] = $row; } }
First class collections 4
Code class SpreadSheet { private $headers; private $title; private $rows; public function __construct(string $headers, string $title, RowCollection $rows) { //[...] } public function addRow(Row $row) { $this->rows->add($row); } } class RowCollection implements ArrayAccess { private $rows; public function __construct(array $rows) { $this->rows = $rows; } //[...] }
Code class SpreadSheet { private $user; public function userCanUseSpreadsheet() { return $this->user->getSettings()->canUseSpreadsheet(); } }
One arrow per line5
● Each unit should have only limited knowledge about other units: only units "closely" related to the current unit. ● Each unit should only talk to its friends; don't talk to strangers. ● Only talk to your immediate friends. Law of Demeter
Code class SpreadSheet { private $user; public function userCanUseSpreadsheet() { return $this->user->canUseSpreadsheet(); } } class User { private $settings; public function canUseSpreadsheet() { return $this->settings->canUseSpreadsheet(); } }
Don’t abbreviate6
Keep all entities small 7
No classes with more than two instance variables 8
“ Decomposing objects from a set of attributes into a hierarchy of collaborating objects, leads much more directly to an effective object model https://github.com/TheLadders/object-calisthenics#rule-8-no-classes-with-more-than-two-instance-variables
Code class User { private $firstname; private $lastname; public function getFirstname() { return $this->firstname; } public function setFirstname($firstname) { return $this->firstname = $firstname; } public function getLastname() { return $this->lastname; } public function setLastname($lastname) { return $this->lastname = $lastname; } }
No Getters No Setters No Properties 9
Code class User { private $firstname; private $lastname; private function __constructor(string $firstname, string $lastname) { $this->firstname = $firstname; $this->lastname = $lastname; } public static function create(string $firstname, string $lastname): self { return new self($firstname, $lastname); } public function toArray(): array { return [ 'firstname' => $firstname, 'lastname' => $lastname ]; } }
Any questions ? You can find me at ◉ @giorrrgio Thanks!

PHP object calisthenics

  • 1.
    PHP - ObjectCalisthenics
  • 2.
    I am GiorgioCefaro I work as Lead Software Engineer in Hootsuite You can find me at @giorrrgio Hello!
  • 3.
  • 4.
  • 5.
  • 6.
    Code class SpreadSheet { private $headers; private$rows; public function printCSV(string $endLine, string $separator): string { $sheet = $this->headers . $endLine; foreach ($this->rows as $row) { foreach ($row as $cell) { $sheet .= $cell->print() . $separator; } $sheet .= $endLine; } return $sheet; } }
  • 7.
    Only one levelof indentation per line 1
  • 8.
    Code class SpreadSheet { private$headers; private $rows; public function printCSV(string $endLine, string $separator): string { return $this->headers . $endLine . $this->printRows(); } private function printRows(string $endLine, string $separator): string { $printed = ''; foreach ($this->rows as $row) { $printed .= $this->printRow($row, $separator) . $endLine; } return $printed; } private function printRow(array $row, string $separator): string { $printed = ''; foreach ($row as $cell) { $printed .= $cell->print() . $separator; } return $printed; } }
  • 9.
    Code class SpreadSheet { public functionisMultitab(): bool { if (count($this->tabs) > 1) { return true; } else { return false; } } }
  • 10.
    Don’t use theELSE keyword 2
  • 11.
    Code class SpreadSheet { public functionisMultitab(): bool { if (count($this->tabs) > 1) { return true; } return false; } }
  • 12.
    Code class Task { /** @varint Task priority. */ private $priority; public function isHighPriority(): bool { return $this->priority > 9; } }
  • 13.
  • 14.
    Code class Task { /** @varPriority Task priority. */ private $priority; public function isHighPriority(): bool { return $this->priority->isHigh(); } } class Priority { const PRIORITY_HIGH = 9; /** @var int priority value. */ private $value; public function isHigh(): bool { return $this->value > self::PRIORITY_HIGH; } }
  • 15.
    Code class SpreadSheet { private $headers; private$title; private $rows; public function __construct( string $headers, string $title, array $rows) { //[...] } public function addRow(Row $row) { $this->rows[] = $row; } }
  • 16.
  • 17.
    Code class SpreadSheet { private $headers; private$title; private $rows; public function __construct(string $headers, string $title, RowCollection $rows) { //[...] } public function addRow(Row $row) { $this->rows->add($row); } } class RowCollection implements ArrayAccess { private $rows; public function __construct(array $rows) { $this->rows = $rows; } //[...] }
  • 18.
    Code class SpreadSheet { private $user; publicfunction userCanUseSpreadsheet() { return $this->user->getSettings()->canUseSpreadsheet(); } }
  • 19.
  • 20.
    ● Each unitshould have only limited knowledge about other units: only units "closely" related to the current unit. ● Each unit should only talk to its friends; don't talk to strangers. ● Only talk to your immediate friends. Law of Demeter
  • 21.
    Code class SpreadSheet { private $user; publicfunction userCanUseSpreadsheet() { return $this->user->canUseSpreadsheet(); } } class User { private $settings; public function canUseSpreadsheet() { return $this->settings->canUseSpreadsheet(); } }
  • 22.
  • 23.
  • 24.
    No classes withmore than two instance variables 8
  • 25.
    “ Decomposing objects froma set of attributes into a hierarchy of collaborating objects, leads much more directly to an effective object model https://github.com/TheLadders/object-calisthenics#rule-8-no-classes-with-more-than-two-instance-variables
  • 26.
    Code class User { private $firstname; private$lastname; public function getFirstname() { return $this->firstname; } public function setFirstname($firstname) { return $this->firstname = $firstname; } public function getLastname() { return $this->lastname; } public function setLastname($lastname) { return $this->lastname = $lastname; } }
  • 27.
  • 28.
    Code class User { private $firstname; private$lastname; private function __constructor(string $firstname, string $lastname) { $this->firstname = $firstname; $this->lastname = $lastname; } public static function create(string $firstname, string $lastname): self { return new self($firstname, $lastname); } public function toArray(): array { return [ 'firstname' => $firstname, 'lastname' => $lastname ]; } }
  • 29.
    Any questions ? Youcan find me at ◉ @giorrrgio Thanks!