SlideShare a Scribd company logo
1 of 135
Download to read offline
Your code sucks,
let’s !x it!
Object Calisthenics and Code readability
Rafael Dohms
@rdohms
photocredit:EliWhite
Evangelist, Speaker and
Contributor.
Developer at WEBclusive.
Enabler at AmsterdamPHP.
Rafael Dohms
@rdohms
photocredit:EliWhite
Evangelist, Speaker and
Contributor.
Developer at WEBclusive.
Enabler at AmsterdamPHP.
Rafael Dohms
@rdohms
Why does my
code suck?
Why does my
code suck?
Is it Readable?
Why does my
code suck?
Is it Readable?
Is it Testable?
Why does my
code suck?
Is it Readable?
Is it Testable?
Is it Maintainable?
Why does my
code suck?
Is it Readable?
Is it Testable?
Is it Maintainable?
Is it Reusable?
Does it look like this?<?php
$list=mysql_connect("******","*******","*****");
if(!$list)echo 'Cannot login.';
else{
mysql_select_db("******", $list);
$best=array("0","0","0","0","0","0");
$id=mysql_num_rows(mysql_query("SELECT * FROM allnews"));
$count=0;
for($i=0;$i<6;$i++){
while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+
+;
$best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}
$id=$id-$count;
$maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y'));
while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){
if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){
$small=$best[0];
while($i=0;$i<6;$i++){
if(mysql_query("SELECT score FROM allnews WHERE id=
$small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]"))
$small=$best[i+1];}
if(mysql_query("SELECT score FROM allnews WHERE id=
$small")<mysql_query("SELECT score FROM allnews WHERE id=$id-$count")){
while($i=0;$i<6;$i++){
if($small==$best[i])$best[i]=mysql_query("SELECT id FROM
allnews WHERE id=$id-$count");}}}}
while($i=0;$i<6;$i++)
echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT
type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id=
$best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT
image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>';
mysql_close($list);
}
?>
Does it look like this?<?php
$list=mysql_connect("******","*******","*****");
if(!$list)echo 'Cannot login.';
else{
mysql_select_db("******", $list);
$best=array("0","0","0","0","0","0");
$id=mysql_num_rows(mysql_query("SELECT * FROM allnews"));
$count=0;
for($i=0;$i<6;$i++){
while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+
+;
$best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}
$id=$id-$count;
$maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y'));
while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){
if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){
$small=$best[0];
while($i=0;$i<6;$i++){
if(mysql_query("SELECT score FROM allnews WHERE id=
$small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]"))
$small=$best[i+1];}
if(mysql_query("SELECT score FROM allnews WHERE id=
$small")<mysql_query("SELECT score FROM allnews WHERE id=$id-$count")){
while($i=0;$i<6;$i++){
if($small==$best[i])$best[i]=mysql_query("SELECT id FROM
allnews WHERE id=$id-$count");}}}}
while($i=0;$i<6;$i++)
echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT
type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id=
$best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT
image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>';
mysql_close($list);
}
?>
If Rebecca Black was a developer
How do we fix it?
Object Calisthenics
Object Calisthenics
cal·is·then·ics-noun-/ˌkaləsˈTHeniks/
Calisthenics are a form of dynamic
exercise consisting of a variety of
simple, often rhythmical, movements,
generally using minimal equipment or
apparatus.
Object Calisthenics
cal·is·then·ics-noun-/ˌkaləsˈTHeniks/
Calisthenics are a form of dynamic
exercise consisting of a variety of
simple, often rhythmical, movements,
generally using minimal equipment or
apparatus.
A variety of simple, often
rhythmical, exercises to achieve
better OO and code quality
Object Calisthenics
“So here’s an exercise that can help you to internalize
principles of good object-oriented design and actually
use them in real life.”
-- Jeff Bay
Object Calisthenics
“So here’s an exercise that can help you to internalize
principles of good object-oriented design and actually
use them in real life.”
-- Jeff Bay
Important:
PHP != JAVA
Adaptations will be done
Object Calisthenics
+
Readability Tips
Object Calisthenics
+
Readability Tips
“You need to write code that minimizes the time it would
take someone else to understand it—even if that
someone else is you.”
-- Dustin Boswell and Trevor Foucher
Object Calisthenics
+
Readability Tips
“You need to write code that minimizes the time it would
take someone else to understand it—even if that
someone else is you.”
-- Dustin Boswell and Trevor Foucher
I’m a tip
Disclaimer:
“These are guidelines exercises,
not rules”
OC #1
“Only one indentation
level per method”
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
}
return $valid;
}
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
}
return $valid;
}
0
1
2
3
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
}
return $valid;
}
0
1
2
3
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$validationResult = validateSingleProduct($rawProduct, $requiredFields);
if ( ! $validationResult){
$valid = false;
}
}
return $valid;
}
function validateSingleProduct($product, $requiredFields)
{
$valid = true;
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
return $valid;
}
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$validationResult = validateSingleProduct($rawProduct, $requiredFields);
if ( ! $validationResult){
$valid = false;
}
}
return $valid;
}
function validateSingleProduct($product, $requiredFields)
{
$valid = true;
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
return $valid;
}
whitespace
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$validationResult = validateSingleProduct($rawProduct, $requiredFields);
if ( ! $validationResult){
$valid = false;
}
}
return $valid;
}
function validateSingleProduct($product, $requiredFields)
{
$valid = true;
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
return $valid;
}
whitespace
duplicated logic
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$validationResult = validateSingleProduct($rawProduct, $requiredFields);
if ( ! $validationResult){
$valid = false;
}
}
return $valid;
}
function validateSingleProduct($product, $requiredFields)
{
$valid = true;
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
return $valid;
}
whitespace
duplicated logic
0
1
2
0
1
2
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$validationResult = validateSingleProduct($rawProduct, $requiredFields);
if ( ! $validationResult){
$valid = false;
}
}
return $valid;
}
function validateSingleProduct($product, $requiredFields)
{
$valid = true;
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
return $valid;
}
0
1
2
0
1
2
function validateProducts($products) {
// Check to make sure that our valid fields are in there
$requiredFields = array(
'price',
'name',
'description',
'type',
);
$valid = true;
foreach ($products as $rawProduct) {
$validationResult = validateSingleProduct($rawProduct, $requiredFields);
if ( ! $validationResult){
$valid = false;
}
}
return $valid;
}
function validateSingleProduct($product, $requiredFields)
{
$valid = true;
$fields = array_keys($rawProduct);
foreach ($requiredFields as $requiredField) {
if (!in_array($requiredField, $fields)) {
$valid = false;
}
}
return $valid;
}
0
1
2
0
1
2
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
I see cheating!
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
I see cheating!
Single line IF, simple operations
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
I see cheating!
Single line IF, simple operations
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
return earlySingle line IF, simple operations
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
return earlySingle line IF, simple operations
C (native) functions are
faster then PHP
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
function validateProducts($storeData) {
$requiredFields = array('price','name','description','type');
foreach ($storeData['products'] as $rawProduct) {
if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false;
}
return true;
}
function validateSingleProduct($product, $requiredFields)
{
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
function validateProductList($products)
{
$validProducts = array_filter($products, 'isValidProduct');
return (count($products) == count($validProducts));
}
function isValidProduct($rawProduct)
{
$requiredFields = array('price', 'name', 'description', 'type');
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
function validateProductList($products)
{
$validProducts = array_filter($products, 'isValidProduct');
return (count($products) == count($validProducts));
}
function isValidProduct($rawProduct)
{
$requiredFields = array('price', 'name', 'description', 'type');
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
faster iteration
function validateProductList($products)
{
$validProducts = array_filter($products, 'isValidProduct');
return (count($products) == count($validProducts));
}
function isValidProduct($rawProduct)
{
$requiredFields = array('price', 'name', 'description', 'type');
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
faster iteration
reusable method
function validateProductList($products)
{
$validProducts = array_filter($products, 'isValidProduct');
return (count($products) == count($validProducts));
}
function isValidProduct($rawProduct)
{
$requiredFields = array('price', 'name', 'description', 'type');
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
faster iteration
reusable method
method name matches “true” result
function validateProductList($products)
{
$validProducts = array_filter($products, 'isValidProduct');
return (count($products) == count($validProducts));
}
function isValidProduct($rawProduct)
{
$requiredFields = array('price', 'name', 'description', 'type');
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
faster iteration
reusable method
method name matches “true” result
assertable return: expected/returned
function validateProductList($products)
{
$validProducts = array_filter($products, 'isValidProduct');
return (count($products) == count($validProducts));
}
function isValidProduct($rawProduct)
{
$requiredFields = array('price', 'name', 'description', 'type');
$fields = array_keys($rawProduct);
$missingFields = array_diff($requiredFields, $fields);
return (count($missingFields) == 0);
}
faster iteration
reusable method
method name matches “true” result
assertable return: expected/returned
List is more readable the plural
Key Benefits
• Single Responsibility Principle (S in SOLID)
• Increases re-use
OC #2
“Do not use the ‘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);
}
}
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);
}
}
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);
}
}
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);
}
}
intermediate variable
intermediate variable
public function createPost($request)
{
$entity = new Post();
$repository = $this->getRepository('MyBundle:Post');
$form = new MyForm($entity);
$form->bind($request);
if ( ! $form->isValid()){
return array('form' => $form, 'error' => 'Invalid fields');
}
if ($repository->exists($entity)){
return array('form' => $form, 'error' => 'Duplicate post title');
}
$repository->save($entity);
return $this->redirect('create_ok');
}
public function createPost($request)
{
$entity = new Post();
$repository = $this->getRepository('MyBundle:Post');
$form = new MyForm($entity);
$form->bind($request);
if ( ! $form->isValid()){
return array('form' => $form, 'error' => 'Invalid fields');
}
if ($repository->exists($entity)){
return array('form' => $form, 'error' => 'Duplicate post title');
}
$repository->save($entity);
return $this->redirect('create_ok');
}
public function createPost($request)
{
$entity = new Post();
$repository = $this->getRepository('MyBundle:Post');
$form = new MyForm($entity);
$form->bind($request);
if ( ! $form->isValid()){
return array('form' => $form, 'error' => 'Invalid fields');
}
if ($repository->exists($entity)){
return array('form' => $form, 'error' => 'Duplicate post title');
}
$repository->save($entity);
return $this->redirect('create_ok');
}
removed intermediates
public function createPost($request)
{
$entity = new Post();
$repository = $this->getRepository('MyBundle:Post');
$form = new MyForm($entity);
$form->bind($request);
if ( ! $form->isValid()){
return array('form' => $form, 'error' => 'Invalid fields');
}
if ($repository->exists($entity)){
return array('form' => $form, 'error' => 'Duplicate post title');
}
$repository->save($entity);
return $this->redirect('create_ok');
}
removed intermediates
early return
public function createPost($request)
{
$entity = new Post();
$repository = $this->getRepository('MyBundle:Post');
$form = new MyForm($entity);
$form->bind($request);
if ( ! $form->isValid()){
return array('form' => $form, 'error' => 'Invalid fields');
}
if ($repository->exists($entity)){
return array('form' => $form, 'error' => 'Duplicate post title');
}
$repository->save($entity);
return $this->redirect('create_ok');
}
removed intermediates
early return
Alternate solution:
Use Exceptions
public function createPost($request)
{
$entity = new Post();
$repository = $this->getRepository('MyBundle:Post');
$form = new MyForm($entity);
$form->bind($request);
if ( ! $form->isValid()){
return array('form' => $form, 'error' => 'Invalid fields');
}
if ($repository->exists($entity)){
return array('form' => $form, 'error' => 'Duplicate post title');
}
$repository->save($entity);
return $this->redirect('create_ok');
}
Separate code
into blocks.
Its like using
Paragraphs.
removed intermediates
early return
Alternate solution:
Use Exceptions
Key Benefits
• Helps avoid code duplication
• Easier to read (single true path)
• Reduces cyclomatic complexity
OC #3
“Wrap primitive types
and strings”
Adapted
* if there is behavior
//...
$component->repaint(false);
//...
$component->repaint(false);
unclear operation
//...
$component->repaint(false);
class UIComponent
{
//...
public function repaint($animate = true){
//...
}
}
unclear operation
//...
$component->repaint(false);
class UIComponent
{
//...
public function repaint( Animate $animate ){
//...
}
}
class Animate
{
public $animate;
public function __construct( $animate = true ) {
$this->animate = $animate;}
}
//...
$component->repaint( new Animate(false) );
class UIComponent
{
//...
public function repaint( Animate $animate ){
//...
}
}
class Animate
{
public $animate;
public function __construct( $animate = true ) {
$this->animate = $animate;}
}
//...
$component->repaint( new Animate(false) );
This can now encapsulate all
animation related operations
Key Benefits
• Helps identify what should be an Object
• Type Hinting
• Encapsulation of operations
OC #4
“Only one -> per line”
Adapted
* getter chain or a fluent interface
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
Source: CodeIgniter
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
properties are harder to mock
Source: CodeIgniter
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
properties are harder to mock
no whitespace
Source: CodeIgniter
- Underlying encapsulation problem
- Hard to debug and test
- Hard to read and understand
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
properties are harder to mock
no whitespace
Source: CodeIgniter
- Underlying encapsulation problem
- Hard to debug and test
- Hard to read and understand
$this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this-
>CI->uri->slash_segment(2, 'both');
$this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading');
properties are harder to mock
no whitespace
move everything to uri object
$this->getCI()->getUriBuilder()->getBaseUri(‘leading’);
Source: CodeIgniter
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
Source: Zend Framework App
Source: Symfony 2 Docs.
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
fluent interface
Source: Zend Framework App
Source: Symfony 2 Docs.
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
operator alignment
fluent interface
Source: Zend Framework App
Source: Symfony 2 Docs.
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
operator alignment
fluent interface
$user = $this->get('security.context')->getToken()->getUser();
Source: Zend Framework App
Source: Symfony 2 Docs.
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
operator alignment
fluent interface
$user = $this->get('security.context')->getToken()->getUser();
only getters (no operations)
Source: Zend Framework App
Source: Symfony 2 Docs.
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
operator alignment
fluent interface
$user = $this->get('security.context')->getToken()->getUser();
only getters (no operations)
Source: Zend Framework App
Source: Symfony 2 Docs.
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
operator alignment
fluent interface
$user = $this->get('security.context')->getToken()->getUser();
only getters (no operations)
where did my
autocomplete go?
Source: Zend Framework App
Source: Symfony 2 Docs.
$filterChain->addFilter(new Zend_Filter_Alpha())
->addFilter(new Zend_Filter_StringToLower());
operator alignment
fluent interface
$user = $this->get('security.context')->getToken()->getUser();
only getters (no operations)
return null?where did my
autocomplete go?
Source: Zend Framework App
Source: Symfony 2 Docs.
Key Benefits
• Readability
• Easier Mocking (Testing)
• Easier to Debug
• Demeter’s Law
OC #5
“Do not Abbreviate”
if($sx >= $sy) {
if ($sx > $strSysMatImgW) {
$ny = $strSysMatImgW * $sy / $sx;
$nx = $strSysMatImgW;
}
if ($ny > $strSysMatImgH) {
$nx = $strSysMatImgH * $sx / $sy;
$ny = $strSysMatImgH;
}
} else {
if ($sy > $strSysMatImgH) {
$nx = $strSysMatImgH * $sx / $sy;
$ny = $strSysMatImgH;
}
if($nx > $strSysMatImgW) {
$ny = $strSysMatImgW * $sy / $sx;
$nx = $strSysMatImgW;
}
}
if($sx >= $sy) {
if ($sx > $strSysMatImgW) {
$ny = $strSysMatImgW * $sy / $sx;
$nx = $strSysMatImgW;
}
if ($ny > $strSysMatImgH) {
$nx = $strSysMatImgH * $sx / $sy;
$ny = $strSysMatImgH;
}
} else {
if ($sy > $strSysMatImgH) {
$nx = $strSysMatImgH * $sx / $sy;
$ny = $strSysMatImgH;
}
if($nx > $strSysMatImgW) {
$ny = $strSysMatImgW * $sy / $sx;
$nx = $strSysMatImgW;
}
}
?
?
?
?
?
Why do you abbreviate?
Why do you abbreviate?
Its repeated many times,
and i’m lazy.
Why do you abbreviate?
Its repeated many times,
and i’m lazy.
Underlying Problem!
You need to transfer those operations into a separate class.
function processResponseHeadersAndDefineOutput($response) { ... }
Why do you abbreviate?
This method name is too long to type,
and i’m lazy.
function processResponseHeadersAndDefineOutput($response) { ... }
Why do you abbreviate?
This method name is too long to type,
and i’m lazy.
function processResponseHeadersAndDefineOutput($response) { ... }
more than one
responsibility?
Why do you abbreviate?
function getPage($data) { ... }
function startProcess() { ... }
$tr->process(“site.login”);
function getPage($data) { ... }
get from where?
function startProcess() { ... }
$tr->process(“site.login”);
Use clearer names:
fetchPage()
downloadPage()
function getPage($data) { ... }
get from where?
function startProcess() { ... }
$tr->process(“site.login”);
Use clearer names:
fetchPage()
downloadPage()
function getPage($data) { ... }
get from where?
function startProcess() { ... }
Use a thesaurus:
fork, create, begin, open
$tr->process(“site.login”);
Use clearer names:
fetchPage()
downloadPage()
function getPage($data) { ... }
get from where?
function startProcess() { ... }
Use a thesaurus:
fork, create, begin, open
$tr->process(“site.login”);
Table row?
Use clearer names:
fetchPage()
downloadPage()
function getPage($data) { ... }
get from where?
function startProcess() { ... }
Use a thesaurus:
fork, create, begin, open
$tr->process(“site.login”);
Easy understanding, complete scope:
$translatorService
Table row?
Key Benefits
• Clearer communication and maintainability
• Indicates underlying problems
OC #6
“Keep your classes
small”
Adapted
200 lines per class
10 methods per class
15 classes per package
200 lines per class
10 methods per class
15 classes per package
Increased to include
docblocks
200 lines per class
10 methods per class
15 classes per package
15-20 lines per method
Increased to include
docblocks
200 lines per class
10 methods per class
15 classes per package
15-20 lines per method
Increased to include
docblocks
read this as
namespace or folder
Key Benefits
• Single Responsibility
• Objective and clear methods
• Slimmer namespaces
• Avoids clunky folders
OC #7
“Limit the number of
instance variables in a
class (2 to 5)”
Adapted
class MyRegistrationService
{
protected $userService;
protected $passwordService;
protected $logger;
protected $translator;
protected $entityManager;
protected $imageCropper;
// ...
}
class MyRegistrationService
{
protected $userService;
protected $passwordService;
protected $logger;
protected $translator;
protected $entityManager;
protected $imageCropper;
// ...
}
Limit: 5
class MyRegistrationService
{
protected $userService;
protected $passwordService;
protected $logger;
protected $translator;
protected $entityManager;
protected $imageCropper;
// ...
}
Limit: 5
Use an event based
system and move this
to listener
All DB interaction
should be in
userService
Key Benefits
• Shorter dependency list
• Easier Mocking for unit test
OC #8
“Use first class
collections”
$collection->getIterator();
$collection->filter(...);
$collection->append(...);
$collection->map(...);
Doctrine:
ArrayCollection
Key Benefits
• Implements collection operations
• Uses SPL interfaces
• Easier to merge collections and not worry
about member behavior in them
OC #9
“Do not use getters/
setters”
Dropped
* Use them if you code PHP
/**
* THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
*/
class DoctrineTestsModelsCMSCmsUserProxy
extends DoctrineTestsModelsCMSCmsUser
implements DoctrineORMProxyProxy
{
public function getId()
{
$this->__load();
return parent::getId();
}
public function getStatus()
{
$this->__load();
return parent::getStatus();
}
/**
* THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE.
*/
class DoctrineTestsModelsCMSCmsUserProxy
extends DoctrineTestsModelsCMSCmsUser
implements DoctrineORMProxyProxy
{
public function getId()
{
$this->__load();
return parent::getId();
}
public function getStatus()
{
$this->__load();
return parent::getStatus();
}
Example: Doctrine uses getters to
inject lazy loading operations
Key Benefits
• Injector operations
• Encapsulation of transformations
OC #10 (bonus!)
“Document your code!”
Created!
//check to see if the section above set the $overall_pref variable to void
if ($overall_pref == 'void')
// implode the revised array of selections in group three into a string
// variable so that it can be transferred to the database at the end of the
// page
$groupthree = implode($groupthree_array, "nr");
//check to see if the section above set the $overall_pref variable to void
if ($overall_pref == 'void')
really?
// implode the revised array of selections in group three into a string
// variable so that it can be transferred to the database at the end of the
// page
$groupthree = implode($groupthree_array, "nr");
//check to see if the section above set the $overall_pref variable to void
if ($overall_pref == 'void')
really?
// implode the revised array of selections in group three into a string
// variable so that it can be transferred to the database at the end of the
// page
$groupthree = implode($groupthree_array, "nr");
Documenting because i’m doing it wrong in an unusual way
$priority = isset($event['priority']) ? $event['priority'] : 0;
if (!isset($event['event'])) {
throw new InvalidArgumentException(...));
}
if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace(array(
'/(?<=b)[a-z]/ie',
'/[^a-z0-9]/i'
), array('strtoupper("0")', ''), $event['event']);
}
$definition->addMethodCall(
'addListenerService',
array($event['event'],
array($listenerId,
$event['method']),
$priority
));
Source: Symfony2
$priority = isset($event['priority']) ? $event['priority'] : 0;
if (!isset($event['event'])) {
throw new InvalidArgumentException(...));
}
if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace(array(
'/(?<=b)[a-z]/ie',
'/[^a-z0-9]/i'
), array('strtoupper("0")', ''), $event['event']);
}
$definition->addMethodCall(
'addListenerService',
array($event['event'],
array($listenerId,
$event['method']),
$priority
));
What does this do?
Source: Symfony2
$priority = isset($event['priority']) ? $event['priority'] : 0;
if (!isset($event['event'])) {
throw new InvalidArgumentException(...));
}
if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace(array(
'/(?<=b)[a-z]/ie',
'/[^a-z0-9]/i'
), array('strtoupper("0")', ''), $event['event']);
}
$definition->addMethodCall(
'addListenerService',
array($event['event'],
array($listenerId,
$event['method']),
$priority
));
What does this do?
Add a simple comment:
//Strips special chars and camel cases to onXxx
Source: Symfony2
$priority = isset($event['priority']) ? $event['priority'] : 0;
if (!isset($event['event'])) {
throw new InvalidArgumentException(...));
}
if (!isset($event['method'])) {
$event['method'] = 'on'.preg_replace(array(
'/(?<=b)[a-z]/ie',
'/[^a-z0-9]/i'
), array('strtoupper("0")', ''), $event['event']);
}
$definition->addMethodCall(
'addListenerService',
array($event['event'],
array($listenerId,
$event['method']),
$priority
));
What does this do?
Add a simple comment:
//Strips special chars and camel cases to onXxx
Source: Symfony2
Don’t explain bad
code, fix it!
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);
mark todo items so the
changes don’t get lost
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);
mark todo items so the
changes don’t get lost
A note on cost of
running function
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);
mark todo items so the
changes don’t get lost
A note on cost of
running function
Do a mind dump,
then clean it up.
/**
* Checks whether an element is contained in the collection.
* This is an O(n) operation, where n is the size of the collection.
*
* @todo implement caching for better performance
* @param mixed $element The element to search for.
* @return boolean TRUE if the collection contains the element, or FALSE.
*/
function contains($element);
mark todo items so the
changes don’t get lost
A note on cost of
running function
Do a mind dump,
then clean it up.
Generate API docs
with phpDocumentor
Key Benefits
• Automatic API documentation
• Transmission of “line of thought”
• Avoids confusion
Recap
• #1 - Only one indentation level per method.
• #2 - Do not use the ‘else’ keyword.
• #3 - Wrap primitive types and string, if it has behavior.
• #4 - Only one -> per line, if not getter or fluent.
• #5 - Do not Abbreviate.
• #6 - Keep your classes small
• #7 - Limit the number of instance variables in a class (max: 5)
• #8 - Use first class collections
• #9 - Use getter/setter
• #10 - Document your code!
Questions?
http://slides.doh.ms
http://doh.ms
@rdohms
http://fixthatcode.com
http://joind.in/8183
Recommended Links:
http://goo.gl/OcSNx
http://goo.gl/unrij
DISCLAIMER: This talk re-uses some of the examples used by Guilherme Blanco in his
original Object Calisthenic talk. These principles were studied and applied by us while we
worked together in previous jobs. The result taught us all a lesson we really want to spread
to other developers.
The ThoughtWorks Anthology
The Art of Readable Code

More Related Content

What's hot

Pipeline oriented programming
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programmingScott Wlaschin
 
Javascript under the hood 1
Javascript under the hood 1Javascript under the hood 1
Javascript under the hood 1Thang Tran Duc
 
SQLアンチパターン - ナイーブツリー
SQLアンチパターン - ナイーブツリーSQLアンチパターン - ナイーブツリー
SQLアンチパターン - ナイーブツリーke-m kamekoopa
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...Philip Schwarz
 
ざっくり DDD 入門!!
ざっくり DDD 入門!!ざっくり DDD 入門!!
ざっくり DDD 入門!!Yukei Wachi
 
Haskell study 12
Haskell study 12Haskell study 12
Haskell study 12Nam Hyeonuk
 
Optcarrot: A Pure-Ruby NES Emulator
Optcarrot: A Pure-Ruby NES EmulatorOptcarrot: A Pure-Ruby NES Emulator
Optcarrot: A Pure-Ruby NES Emulatormametter
 
Designing with Capabilities
Designing with CapabilitiesDesigning with Capabilities
Designing with CapabilitiesScott Wlaschin
 
190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁KWANGIL KIM
 
超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programming
超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programming超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programming
超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programmingmametter
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented ProgrammingScott Wlaschin
 
multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것
multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것
multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것흥배 최
 
TRICK 2022 Results
TRICK 2022 ResultsTRICK 2022 Results
TRICK 2022 Resultsmametter
 
What's Wrong With Object-Oriented Programming?
What's Wrong With Object-Oriented Programming?What's Wrong With Object-Oriented Programming?
What's Wrong With Object-Oriented Programming?Yegor Bugayenko
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data ObjectsWez Furlong
 

What's hot (20)

Clojure Monad
Clojure MonadClojure Monad
Clojure Monad
 
jQuery na Prática!
jQuery na Prática!jQuery na Prática!
jQuery na Prática!
 
Pipeline oriented programming
Pipeline oriented programmingPipeline oriented programming
Pipeline oriented programming
 
Javascript under the hood 1
Javascript under the hood 1Javascript under the hood 1
Javascript under the hood 1
 
SQLアンチパターン - ナイーブツリー
SQLアンチパターン - ナイーブツリーSQLアンチパターン - ナイーブツリー
SQLアンチパターン - ナイーブツリー
 
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
 
2つのmosaic plotと日本語表示
2つのmosaic plotと日本語表示2つのmosaic plotと日本語表示
2つのmosaic plotと日本語表示
 
ざっくり DDD 入門!!
ざっくり DDD 入門!!ざっくり DDD 入門!!
ざっくり DDD 入門!!
 
Haskell study 12
Haskell study 12Haskell study 12
Haskell study 12
 
Optcarrot: A Pure-Ruby NES Emulator
Optcarrot: A Pure-Ruby NES EmulatorOptcarrot: A Pure-Ruby NES Emulator
Optcarrot: A Pure-Ruby NES Emulator
 
Designing with Capabilities
Designing with CapabilitiesDesigning with Capabilities
Designing with Capabilities
 
Dancing with dalvik
Dancing with dalvikDancing with dalvik
Dancing with dalvik
 
190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁190119 unreal engine c++ 입문 및 팁
190119 unreal engine c++ 입문 및 팁
 
超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programming
超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programming超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programming
超絶技巧 Ruby プログラミング - Esoteric, Obfuscated Ruby Programming
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것
multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것
multi-thread 어플리케이션에 대해 모든 개발자가 알아 두지 않으면 안 되는 것
 
TRICK 2022 Results
TRICK 2022 ResultsTRICK 2022 Results
TRICK 2022 Results
 
F# for C# Programmers
F# for C# ProgrammersF# for C# Programmers
F# for C# Programmers
 
What's Wrong With Object-Oriented Programming?
What's Wrong With Object-Oriented Programming?What's Wrong With Object-Oriented Programming?
What's Wrong With Object-Oriented Programming?
 
PHP Data Objects
PHP Data ObjectsPHP Data Objects
PHP Data Objects
 

Viewers also liked

Thinking Object-Oriented
Thinking Object-OrientedThinking Object-Oriented
Thinking Object-Orientedadil raja
 
“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
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16Rafael Dohms
 
The Design Dilemma – WCATL
The Design Dilemma – WCATLThe Design Dilemma – WCATL
The Design Dilemma – WCATLMallie Hart
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHPGuilherme Blanco
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsGuilherme Blanco
 
PHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsGuilherme Blanco
 
PHP: Linguagem + Mysql + MVC + AJAX
PHP: Linguagem + Mysql + MVC + AJAX PHP: Linguagem + Mysql + MVC + AJAX
PHP: Linguagem + Mysql + MVC + AJAX Sérgio Souza Costa
 
Integração contínua em PHP com Jenkins
Integração contínua em PHP com JenkinsIntegração contínua em PHP com Jenkins
Integração contínua em PHP com JenkinsGilmar Pupo
 

Viewers also liked (12)

Thinking Object-Oriented
Thinking Object-OrientedThinking Object-Oriented
Thinking Object-Oriented
 
“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.
 
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
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
“Writing code that lasts” … or writing code you won’t hate tomorrow. - #PHPSRB16
 
The Design Dilemma – WCATL
The Design Dilemma – WCATLThe Design Dilemma – WCATL
The Design Dilemma – WCATL
 
Object Calisthenics Applied to PHP
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
 
PHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
 
PHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object Calisthenics
 
Frameworks PHP
Frameworks PHPFrameworks PHP
Frameworks PHP
 
Php Presentation
Php PresentationPhp Presentation
Php Presentation
 
PHP: Linguagem + Mysql + MVC + AJAX
PHP: Linguagem + Mysql + MVC + AJAX PHP: Linguagem + Mysql + MVC + AJAX
PHP: Linguagem + Mysql + MVC + AJAX
 
Integração contínua em PHP com Jenkins
Integração contínua em PHP com JenkinsIntegração contínua em PHP com Jenkins
Integração contínua em PHP com Jenkins
 

Similar to Your code sucks, let's fix it! - php|tek13

Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Rafael Dohms
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012Rafael Dohms
 
You code sucks, let's fix it
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix itRafael Dohms
 
Your code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnConRafael Dohms
 
MIND sweeping introduction to PHP
MIND sweeping introduction to PHPMIND sweeping introduction to PHP
MIND sweeping introduction to PHPBUDNET
 
Php Crash Course - Macq Electronique 2010
Php Crash Course - Macq Electronique 2010Php Crash Course - Macq Electronique 2010
Php Crash Course - Macq Electronique 2010Michelangelo van Dam
 
Introduction to PHP
Introduction to PHPIntroduction to PHP
Introduction to PHPBradley Holt
 
Zend Certification Preparation Tutorial
Zend Certification Preparation TutorialZend Certification Preparation Tutorial
Zend Certification Preparation TutorialLorna Mitchell
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to DomainJeremy Cook
 
Becoming a better WordPress Developer
Becoming a better WordPress DeveloperBecoming a better WordPress Developer
Becoming a better WordPress DeveloperJoey Kudish
 
[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?Radek Benkel
 
Entry-level PHP for WordPress
Entry-level PHP for WordPressEntry-level PHP for WordPress
Entry-level PHP for WordPresssprclldr
 
Dev traning 2016 basics of PHP
Dev traning 2016   basics of PHPDev traning 2016   basics of PHP
Dev traning 2016 basics of PHPSacheen Dhanjie
 
06-classes.ppt (copy).pptx
06-classes.ppt (copy).pptx06-classes.ppt (copy).pptx
06-classes.ppt (copy).pptxThắng It
 
Intro to php
Intro to phpIntro to php
Intro to phpSp Singh
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014Guillaume POTIER
 
Synapseindia reviews sharing intro on php
Synapseindia reviews sharing intro on phpSynapseindia reviews sharing intro on php
Synapseindia reviews sharing intro on phpSynapseindiaComplaints
 

Similar to Your code sucks, let's fix it! - php|tek13 (20)

Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)Your code sucks, let's fix it (CakeFest2012)
Your code sucks, let's fix it (CakeFest2012)
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012Your code sucks, let's fix it - PHP Master Series 2012
Your code sucks, let's fix it - PHP Master Series 2012
 
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
 
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
 
MIND sweeping introduction to PHP
MIND sweeping introduction to PHPMIND sweeping introduction to PHP
MIND sweeping introduction to PHP
 
"Javascript" por Tiago Rodrigues
"Javascript" por Tiago Rodrigues"Javascript" por Tiago Rodrigues
"Javascript" por Tiago Rodrigues
 
Php Crash Course - Macq Electronique 2010
Php Crash Course - Macq Electronique 2010Php Crash Course - Macq Electronique 2010
Php Crash Course - Macq Electronique 2010
 
Introduction to PHP
Introduction to PHPIntroduction to PHP
Introduction to PHP
 
Zend Certification Preparation Tutorial
Zend Certification Preparation TutorialZend Certification Preparation Tutorial
Zend Certification Preparation Tutorial
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to Domain
 
Becoming a better WordPress Developer
Becoming a better WordPress DeveloperBecoming a better WordPress Developer
Becoming a better WordPress Developer
 
[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?
 
Data Types In PHP
Data Types In PHPData Types In PHP
Data Types In PHP
 
Entry-level PHP for WordPress
Entry-level PHP for WordPressEntry-level PHP for WordPress
Entry-level PHP for WordPress
 
Dev traning 2016 basics of PHP
Dev traning 2016   basics of PHPDev traning 2016   basics of PHP
Dev traning 2016 basics of PHP
 
06-classes.ppt (copy).pptx
06-classes.ppt (copy).pptx06-classes.ppt (copy).pptx
06-classes.ppt (copy).pptx
 
Intro to php
Intro to phpIntro to php
Intro to php
 
How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014How to test complex SaaS applications - The family july 2014
How to test complex SaaS applications - The family july 2014
 
Synapseindia reviews sharing intro on php
Synapseindia reviews sharing intro on phpSynapseindia reviews sharing intro on php
Synapseindia reviews sharing intro on php
 

More from Rafael Dohms

The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024Rafael Dohms
 
Application Metrics - IPC2023
Application Metrics - IPC2023Application Metrics - IPC2023
Application Metrics - IPC2023Rafael Dohms
 
How'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision RecordsHow'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision RecordsRafael Dohms
 
Architectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBRArchitectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBRRafael Dohms
 
Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)Rafael Dohms
 
Application metrics - Confoo 2019
Application metrics - Confoo 2019Application metrics - Confoo 2019
Application metrics - Confoo 2019Rafael Dohms
 
Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18Rafael Dohms
 
Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18Rafael Dohms
 
Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18Rafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonfRafael Dohms
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...Rafael Dohms
 
Composer The Right Way - 010PHP
Composer The Right Way - 010PHPComposer The Right Way - 010PHP
Composer The Right Way - 010PHPRafael Dohms
 
Writing Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, UtrechtWriting Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, UtrechtRafael Dohms
 
Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16Rafael Dohms
 
Composer the Right Way - MM16NL
Composer the Right Way - MM16NLComposer the Right Way - MM16NL
Composer the Right Way - MM16NLRafael Dohms
 
Composer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRNComposer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRNRafael Dohms
 
Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16Rafael Dohms
 
A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015Rafael Dohms
 
“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
 
“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
 

More from Rafael Dohms (20)

The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024The Individual Contributor Path - DPC2024
The Individual Contributor Path - DPC2024
 
Application Metrics - IPC2023
Application Metrics - IPC2023Application Metrics - IPC2023
Application Metrics - IPC2023
 
How'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision RecordsHow'd we get here? A guide to Architectural Decision Records
How'd we get here? A guide to Architectural Decision Records
 
Architectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBRArchitectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBR
 
Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)
 
Application metrics - Confoo 2019
Application metrics - Confoo 2019Application metrics - Confoo 2019
Application metrics - Confoo 2019
 
Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18
 
Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18
 
Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
 
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo...
 
Composer The Right Way - 010PHP
Composer The Right Way - 010PHPComposer The Right Way - 010PHP
Composer The Right Way - 010PHP
 
Writing Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, UtrechtWriting Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, Utrecht
 
Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16
 
Composer the Right Way - MM16NL
Composer the Right Way - MM16NLComposer the Right Way - MM16NL
Composer the Right Way - MM16NL
 
Composer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRNComposer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRN
 
Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16
 
A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015A Journey into your Lizard Brain - PHP Conference Brasil 2015
A Journey into your Lizard Brain - PHP Conference Brasil 2015
 
“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.
 
“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.
 

Recently uploaded

Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.YounusS2
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXTarek Kalaji
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintMahmoud Rabie
 
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...Aggregage
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?IES VE
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1DianaGray10
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfJamie (Taka) Wang
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding TeamAdam Moalla
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsSeth Reyes
 
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPAAnypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPAshyamraj55
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesMd Hossain Ali
 
IaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdf
IaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdfIaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdf
IaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdfDaniel Santiago Silva Capera
 
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDEADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDELiveplex
 
UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6DianaGray10
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaborationbruanjhuli
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024SkyPlanner
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPathCommunity
 
Linked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond OntologiesLinked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond OntologiesDavid Newbury
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1DianaGray10
 

Recently uploaded (20)

Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.Basic Building Blocks of Internet of Things.
Basic Building Blocks of Internet of Things.
 
VoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBXVoIP Service and Marketing using Odoo and Asterisk PBX
VoIP Service and Marketing using Odoo and Asterisk PBX
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership Blueprint
 
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
The Data Metaverse: Unpacking the Roles, Use Cases, and Tech Trends in Data a...
 
How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?How Accurate are Carbon Emissions Projections?
How Accurate are Carbon Emissions Projections?
 
Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1Secure your environment with UiPath and CyberArk technologies - Session 1
Secure your environment with UiPath and CyberArk technologies - Session 1
 
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
activity_diagram_combine_v4_20190827.pdfactivity_diagram_combine_v4_20190827.pdf
 
9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team9 Steps For Building Winning Founding Team
9 Steps For Building Winning Founding Team
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and Hazards
 
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPAAnypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPA
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
 
IaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdf
IaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdfIaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdf
IaC & GitOps in a Nutshell - a FridayInANuthshell Episode.pdf
 
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDEADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
ADOPTING WEB 3 FOR YOUR BUSINESS: A STEP-BY-STEP GUIDE
 
UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6UiPath Studio Web workshop series - Day 6
UiPath Studio Web workshop series - Day 6
 
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online CollaborationCOMPUTER 10: Lesson 7 - File Storage and Online Collaboration
COMPUTER 10: Lesson 7 - File Storage and Online Collaboration
 
Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024Salesforce Miami User Group Event - 1st Quarter 2024
Salesforce Miami User Group Event - 1st Quarter 2024
 
201610817 - edge part1
201610817 - edge part1201610817 - edge part1
201610817 - edge part1
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation Developers
 
Linked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond OntologiesLinked Data in Production: Moving Beyond Ontologies
Linked Data in Production: Moving Beyond Ontologies
 
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1UiPath Platform: The Backend Engine Powering Your Automation - Session 1
UiPath Platform: The Backend Engine Powering Your Automation - Session 1
 

Your code sucks, let's fix it! - php|tek13

  • 1. Your code sucks, let’s !x it! Object Calisthenics and Code readability Rafael Dohms @rdohms
  • 2. photocredit:EliWhite Evangelist, Speaker and Contributor. Developer at WEBclusive. Enabler at AmsterdamPHP. Rafael Dohms @rdohms
  • 3. photocredit:EliWhite Evangelist, Speaker and Contributor. Developer at WEBclusive. Enabler at AmsterdamPHP. Rafael Dohms @rdohms
  • 5. Why does my code suck? Is it Readable?
  • 6. Why does my code suck? Is it Readable? Is it Testable?
  • 7. Why does my code suck? Is it Readable? Is it Testable? Is it Maintainable?
  • 8. Why does my code suck? Is it Readable? Is it Testable? Is it Maintainable? Is it Reusable?
  • 9. Does it look like this?<?php $list=mysql_connect("******","*******","*****"); if(!$list)echo 'Cannot login.'; else{ mysql_select_db("******", $list); $best=array("0","0","0","0","0","0"); $id=mysql_num_rows(mysql_query("SELECT * FROM allnews")); $count=0; for($i=0;$i<6;$i++){ while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+ +; $best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");} $id=$id-$count; $maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y')); while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){ if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){ $small=$best[0]; while($i=0;$i<6;$i++){ if(mysql_query("SELECT score FROM allnews WHERE id= $small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]")) $small=$best[i+1];} if(mysql_query("SELECT score FROM allnews WHERE id= $small")<mysql_query("SELECT score FROM allnews WHERE id=$id-$count")){ while($i=0;$i<6;$i++){ if($small==$best[i])$best[i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}}}} while($i=0;$i<6;$i++) echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id= $best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>'; mysql_close($list); } ?>
  • 10. Does it look like this?<?php $list=mysql_connect("******","*******","*****"); if(!$list)echo 'Cannot login.'; else{ mysql_select_db("******", $list); $best=array("0","0","0","0","0","0"); $id=mysql_num_rows(mysql_query("SELECT * FROM allnews")); $count=0; for($i=0;$i<6;$i++){ while(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")!="he")$count+ +; $best[$i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");} $id=$id-$count; $maxdate=mktime(0,0,0,date('m'),date('d')-7,date('Y')); while(mysql_query("SELECT date FROM allnews WHERE id=$id-$count")>=$maxdate){ if(mysql_query("SELECT language FROM allnews WHERE id=$id-$count")=="he"){ $small=$best[0]; while($i=0;$i<6;$i++){ if(mysql_query("SELECT score FROM allnews WHERE id= $small)"<mysql_query("SELECT score FROM allnews WHERE id=$best[i+1]")) $small=$best[i+1];} if(mysql_query("SELECT score FROM allnews WHERE id= $small")<mysql_query("SELECT score FROM allnews WHERE id=$id-$count")){ while($i=0;$i<6;$i++){ if($small==$best[i])$best[i]=mysql_query("SELECT id FROM allnews WHERE id=$id-$count");}}}} while($i=0;$i<6;$i++) echo '<a href="news-page.php?id='.$best[i].'"><div class="box '.mysql_query("SELECT type FROM allnews WHERE id=$best[i]").'">'.mysql_query("SELECT title FROM allnews WHERE id= $best[i]").'<div class="img" style="background-image:url(images/'.mysql_query("SELECT image1 FROM allnews WHERE id=$best[i]").');"></div></div></a>'; mysql_close($list); } ?> If Rebecca Black was a developer
  • 11. How do we fix it?
  • 13. Object Calisthenics cal·is·then·ics-noun-/ˌkaləsˈTHeniks/ Calisthenics are a form of dynamic exercise consisting of a variety of simple, often rhythmical, movements, generally using minimal equipment or apparatus.
  • 14. Object Calisthenics cal·is·then·ics-noun-/ˌkaləsˈTHeniks/ Calisthenics are a form of dynamic exercise consisting of a variety of simple, often rhythmical, movements, generally using minimal equipment or apparatus. A variety of simple, often rhythmical, exercises to achieve better OO and code quality
  • 15. Object Calisthenics “So here’s an exercise that can help you to internalize principles of good object-oriented design and actually use them in real life.” -- Jeff Bay
  • 16. Object Calisthenics “So here’s an exercise that can help you to internalize principles of good object-oriented design and actually use them in real life.” -- Jeff Bay Important: PHP != JAVA Adaptations will be done
  • 18. Object Calisthenics + Readability Tips “You need to write code that minimizes the time it would take someone else to understand it—even if that someone else is you.” -- Dustin Boswell and Trevor Foucher
  • 19. Object Calisthenics + Readability Tips “You need to write code that minimizes the time it would take someone else to understand it—even if that someone else is you.” -- Dustin Boswell and Trevor Foucher I’m a tip
  • 20. Disclaimer: “These are guidelines exercises, not rules”
  • 21. OC #1 “Only one indentation level per method”
  • 22. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } } return $valid; }
  • 23. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } } return $valid; } 0 1 2 3
  • 24. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } } return $valid; } 0 1 2 3
  • 25. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; }
  • 26. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; } whitespace
  • 27. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; } whitespace duplicated logic
  • 28. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; } whitespace duplicated logic 0 1 2 0 1 2
  • 29. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; } 0 1 2 0 1 2
  • 30. function validateProducts($products) { // Check to make sure that our valid fields are in there $requiredFields = array( 'price', 'name', 'description', 'type', ); $valid = true; foreach ($products as $rawProduct) { $validationResult = validateSingleProduct($rawProduct, $requiredFields); if ( ! $validationResult){ $valid = false; } } return $valid; } function validateSingleProduct($product, $requiredFields) { $valid = true; $fields = array_keys($rawProduct); foreach ($requiredFields as $requiredField) { if (!in_array($requiredField, $fields)) { $valid = false; } } return $valid; } 0 1 2 0 1 2
  • 31. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 32. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } I see cheating!
  • 33. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } I see cheating! Single line IF, simple operations
  • 34. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } I see cheating! Single line IF, simple operations
  • 35. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } return earlySingle line IF, simple operations
  • 36. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } return earlySingle line IF, simple operations C (native) functions are faster then PHP
  • 37. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 38. function validateProducts($storeData) { $requiredFields = array('price','name','description','type'); foreach ($storeData['products'] as $rawProduct) { if ( ! validateSingleProduct($rawProduct, $requiredFields)) return false; } return true; } function validateSingleProduct($product, $requiredFields) { $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 39. function validateProductList($products) { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); }
  • 40. function validateProductList($products) { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } faster iteration
  • 41. function validateProductList($products) { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } faster iteration reusable method
  • 42. function validateProductList($products) { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } faster iteration reusable method method name matches “true” result
  • 43. function validateProductList($products) { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } faster iteration reusable method method name matches “true” result assertable return: expected/returned
  • 44. function validateProductList($products) { $validProducts = array_filter($products, 'isValidProduct'); return (count($products) == count($validProducts)); } function isValidProduct($rawProduct) { $requiredFields = array('price', 'name', 'description', 'type'); $fields = array_keys($rawProduct); $missingFields = array_diff($requiredFields, $fields); return (count($missingFields) == 0); } faster iteration reusable method method name matches “true” result assertable return: expected/returned List is more readable the plural
  • 45. Key Benefits • Single Responsibility Principle (S in SOLID) • Increases re-use
  • 46. OC #2 “Do not use the ‘else’ keyword”
  • 47. 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); } }
  • 48. 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); } }
  • 49. 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); } }
  • 50. 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); } }
  • 51. 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); } }
  • 52. 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); } }
  • 53. 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); } }
  • 54. 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); } } intermediate variable intermediate variable
  • 55. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); }
  • 56. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); }
  • 57. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); } removed intermediates
  • 58. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); } removed intermediates early return
  • 59. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); } removed intermediates early return Alternate solution: Use Exceptions
  • 60. public function createPost($request) { $entity = new Post(); $repository = $this->getRepository('MyBundle:Post'); $form = new MyForm($entity); $form->bind($request); if ( ! $form->isValid()){ return array('form' => $form, 'error' => 'Invalid fields'); } if ($repository->exists($entity)){ return array('form' => $form, 'error' => 'Duplicate post title'); } $repository->save($entity); return $this->redirect('create_ok'); } Separate code into blocks. Its like using Paragraphs. removed intermediates early return Alternate solution: Use Exceptions
  • 61. Key Benefits • Helps avoid code duplication • Easier to read (single true path) • Reduces cyclomatic complexity
  • 62. OC #3 “Wrap primitive types and strings” Adapted * if there is behavior
  • 66. class UIComponent { //... public function repaint($animate = true){ //... } } unclear operation //... $component->repaint(false);
  • 67. class UIComponent { //... public function repaint( Animate $animate ){ //... } } class Animate { public $animate; public function __construct( $animate = true ) { $this->animate = $animate;} } //... $component->repaint( new Animate(false) );
  • 68. class UIComponent { //... public function repaint( Animate $animate ){ //... } } class Animate { public $animate; public function __construct( $animate = true ) { $this->animate = $animate;} } //... $component->repaint( new Animate(false) ); This can now encapsulate all animation related operations
  • 69. Key Benefits • Helps identify what should be an Object • Type Hinting • Encapsulation of operations
  • 70. OC #4 “Only one -> per line” Adapted * getter chain or a fluent interface
  • 71. $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); Source: CodeIgniter
  • 72. $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); properties are harder to mock Source: CodeIgniter
  • 73. $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); properties are harder to mock no whitespace Source: CodeIgniter
  • 74. - Underlying encapsulation problem - Hard to debug and test - Hard to read and understand $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); properties are harder to mock no whitespace Source: CodeIgniter
  • 75. - Underlying encapsulation problem - Hard to debug and test - Hard to read and understand $this->base_url = $this->CI->config->site_url().'/'.$this->CI->uri->segment(1).$this- >CI->uri->slash_segment(2, 'both'); $this->base_uri = $this->CI->uri->segment(1).$this->CI->uri->slash_segment(2, 'leading'); properties are harder to mock no whitespace move everything to uri object $this->getCI()->getUriBuilder()->getBaseUri(‘leading’); Source: CodeIgniter
  • 78. $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment fluent interface Source: Zend Framework App Source: Symfony 2 Docs.
  • 79. $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment fluent interface $user = $this->get('security.context')->getToken()->getUser(); Source: Zend Framework App Source: Symfony 2 Docs.
  • 80. $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment fluent interface $user = $this->get('security.context')->getToken()->getUser(); only getters (no operations) Source: Zend Framework App Source: Symfony 2 Docs.
  • 81. $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment fluent interface $user = $this->get('security.context')->getToken()->getUser(); only getters (no operations) Source: Zend Framework App Source: Symfony 2 Docs.
  • 82. $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment fluent interface $user = $this->get('security.context')->getToken()->getUser(); only getters (no operations) where did my autocomplete go? Source: Zend Framework App Source: Symfony 2 Docs.
  • 83. $filterChain->addFilter(new Zend_Filter_Alpha()) ->addFilter(new Zend_Filter_StringToLower()); operator alignment fluent interface $user = $this->get('security.context')->getToken()->getUser(); only getters (no operations) return null?where did my autocomplete go? Source: Zend Framework App Source: Symfony 2 Docs.
  • 84. Key Benefits • Readability • Easier Mocking (Testing) • Easier to Debug • Demeter’s Law
  • 85. OC #5 “Do not Abbreviate”
  • 86. if($sx >= $sy) { if ($sx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } if ($ny > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } } else { if ($sy > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } if($nx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } }
  • 87. if($sx >= $sy) { if ($sx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } if ($ny > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } } else { if ($sy > $strSysMatImgH) { $nx = $strSysMatImgH * $sx / $sy; $ny = $strSysMatImgH; } if($nx > $strSysMatImgW) { $ny = $strSysMatImgW * $sy / $sx; $nx = $strSysMatImgW; } } ? ? ? ? ?
  • 88. Why do you abbreviate?
  • 89. Why do you abbreviate? Its repeated many times, and i’m lazy.
  • 90. Why do you abbreviate? Its repeated many times, and i’m lazy. Underlying Problem! You need to transfer those operations into a separate class.
  • 92. This method name is too long to type, and i’m lazy. function processResponseHeadersAndDefineOutput($response) { ... } Why do you abbreviate?
  • 93. This method name is too long to type, and i’m lazy. function processResponseHeadersAndDefineOutput($response) { ... } more than one responsibility? Why do you abbreviate?
  • 94. function getPage($data) { ... } function startProcess() { ... } $tr->process(“site.login”);
  • 95. function getPage($data) { ... } get from where? function startProcess() { ... } $tr->process(“site.login”);
  • 96. Use clearer names: fetchPage() downloadPage() function getPage($data) { ... } get from where? function startProcess() { ... } $tr->process(“site.login”);
  • 97. Use clearer names: fetchPage() downloadPage() function getPage($data) { ... } get from where? function startProcess() { ... } Use a thesaurus: fork, create, begin, open $tr->process(“site.login”);
  • 98. Use clearer names: fetchPage() downloadPage() function getPage($data) { ... } get from where? function startProcess() { ... } Use a thesaurus: fork, create, begin, open $tr->process(“site.login”); Table row?
  • 99. Use clearer names: fetchPage() downloadPage() function getPage($data) { ... } get from where? function startProcess() { ... } Use a thesaurus: fork, create, begin, open $tr->process(“site.login”); Easy understanding, complete scope: $translatorService Table row?
  • 100. Key Benefits • Clearer communication and maintainability • Indicates underlying problems
  • 101. OC #6 “Keep your classes small” Adapted
  • 102. 200 lines per class 10 methods per class 15 classes per package
  • 103. 200 lines per class 10 methods per class 15 classes per package Increased to include docblocks
  • 104. 200 lines per class 10 methods per class 15 classes per package 15-20 lines per method Increased to include docblocks
  • 105. 200 lines per class 10 methods per class 15 classes per package 15-20 lines per method Increased to include docblocks read this as namespace or folder
  • 106. Key Benefits • Single Responsibility • Objective and clear methods • Slimmer namespaces • Avoids clunky folders
  • 107. OC #7 “Limit the number of instance variables in a class (2 to 5)” Adapted
  • 108. class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... }
  • 109. class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... } Limit: 5
  • 110. class MyRegistrationService { protected $userService; protected $passwordService; protected $logger; protected $translator; protected $entityManager; protected $imageCropper; // ... } Limit: 5 Use an event based system and move this to listener All DB interaction should be in userService
  • 111. Key Benefits • Shorter dependency list • Easier Mocking for unit test
  • 112. OC #8 “Use first class collections”
  • 114. Key Benefits • Implements collection operations • Uses SPL interfaces • Easier to merge collections and not worry about member behavior in them
  • 115. OC #9 “Do not use getters/ setters” Dropped * Use them if you code PHP
  • 116. /** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class DoctrineTestsModelsCMSCmsUserProxy extends DoctrineTestsModelsCMSCmsUser implements DoctrineORMProxyProxy { public function getId() { $this->__load(); return parent::getId(); } public function getStatus() { $this->__load(); return parent::getStatus(); }
  • 117. /** * THIS CLASS WAS GENERATED BY THE DOCTRINE ORM. DO NOT EDIT THIS FILE. */ class DoctrineTestsModelsCMSCmsUserProxy extends DoctrineTestsModelsCMSCmsUser implements DoctrineORMProxyProxy { public function getId() { $this->__load(); return parent::getId(); } public function getStatus() { $this->__load(); return parent::getStatus(); } Example: Doctrine uses getters to inject lazy loading operations
  • 118. Key Benefits • Injector operations • Encapsulation of transformations
  • 119. OC #10 (bonus!) “Document your code!” Created!
  • 120. //check to see if the section above set the $overall_pref variable to void if ($overall_pref == 'void') // implode the revised array of selections in group three into a string // variable so that it can be transferred to the database at the end of the // page $groupthree = implode($groupthree_array, "nr");
  • 121. //check to see if the section above set the $overall_pref variable to void if ($overall_pref == 'void') really? // implode the revised array of selections in group three into a string // variable so that it can be transferred to the database at the end of the // page $groupthree = implode($groupthree_array, "nr");
  • 122. //check to see if the section above set the $overall_pref variable to void if ($overall_pref == 'void') really? // implode the revised array of selections in group three into a string // variable so that it can be transferred to the database at the end of the // page $groupthree = implode($groupthree_array, "nr"); Documenting because i’m doing it wrong in an unusual way
  • 123. $priority = isset($event['priority']) ? $event['priority'] : 0; if (!isset($event['event'])) { throw new InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( '/(?<=b)[a-z]/ie', '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, $event['method']), $priority )); Source: Symfony2
  • 124. $priority = isset($event['priority']) ? $event['priority'] : 0; if (!isset($event['event'])) { throw new InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( '/(?<=b)[a-z]/ie', '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, $event['method']), $priority )); What does this do? Source: Symfony2
  • 125. $priority = isset($event['priority']) ? $event['priority'] : 0; if (!isset($event['event'])) { throw new InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( '/(?<=b)[a-z]/ie', '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, $event['method']), $priority )); What does this do? Add a simple comment: //Strips special chars and camel cases to onXxx Source: Symfony2
  • 126. $priority = isset($event['priority']) ? $event['priority'] : 0; if (!isset($event['event'])) { throw new InvalidArgumentException(...)); } if (!isset($event['method'])) { $event['method'] = 'on'.preg_replace(array( '/(?<=b)[a-z]/ie', '/[^a-z0-9]/i' ), array('strtoupper("0")', ''), $event['event']); } $definition->addMethodCall( 'addListenerService', array($event['event'], array($listenerId, $event['method']), $priority )); What does this do? Add a simple comment: //Strips special chars and camel cases to onXxx Source: Symfony2 Don’t explain bad code, fix it!
  • 127. /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element);
  • 128. /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); mark todo items so the changes don’t get lost
  • 129. /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); mark todo items so the changes don’t get lost A note on cost of running function
  • 130. /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); mark todo items so the changes don’t get lost A note on cost of running function Do a mind dump, then clean it up.
  • 131. /** * Checks whether an element is contained in the collection. * This is an O(n) operation, where n is the size of the collection. * * @todo implement caching for better performance * @param mixed $element The element to search for. * @return boolean TRUE if the collection contains the element, or FALSE. */ function contains($element); mark todo items so the changes don’t get lost A note on cost of running function Do a mind dump, then clean it up. Generate API docs with phpDocumentor
  • 132. Key Benefits • Automatic API documentation • Transmission of “line of thought” • Avoids confusion
  • 133. Recap • #1 - Only one indentation level per method. • #2 - Do not use the ‘else’ keyword. • #3 - Wrap primitive types and string, if it has behavior. • #4 - Only one -> per line, if not getter or fluent. • #5 - Do not Abbreviate. • #6 - Keep your classes small • #7 - Limit the number of instance variables in a class (max: 5) • #8 - Use first class collections • #9 - Use getter/setter • #10 - Document your code!
  • 135. Recommended Links: http://goo.gl/OcSNx http://goo.gl/unrij DISCLAIMER: This talk re-uses some of the examples used by Guilherme Blanco in his original Object Calisthenic talk. These principles were studied and applied by us while we worked together in previous jobs. The result taught us all a lesson we really want to spread to other developers. The ThoughtWorks Anthology The Art of Readable Code