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

Rafael Dohms
Rafael DohmsLead Backend Engineer at Usabilla
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
1 of 135

Recommended

GNMT로 알아보는 신경망 기반 기계번역 by
GNMT로 알아보는 신경망 기반 기계번역GNMT로 알아보는 신경망 기반 기계번역
GNMT로 알아보는 신경망 기반 기계번역Byeong il Ko
5.1K views53 slides
개인 일정관리에 Agile을 끼얹으면? by
개인 일정관리에 Agile을 끼얹으면?개인 일정관리에 Agile을 끼얹으면?
개인 일정관리에 Agile을 끼얹으면?Curt Park
3.8K views107 slides
Testing web services by
Testing web servicesTesting web services
Testing web servicesTaras Lytvyn
34.1K views54 slides
Indexing by
IndexingIndexing
IndexingMike Dirolf
6.2K views27 slides
Thinking Object-Oriented by
Thinking Object-OrientedThinking Object-Oriented
Thinking Object-Orientedadil raja
1.4K views13 slides
“Writing code that lasts” … or writing code you won’t hate tomorrow. by
“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
2.5K views77 slides

More Related Content

Viewers also liked

The Design Dilemma – WCATL by
The Design Dilemma – WCATLThe Design Dilemma – WCATL
The Design Dilemma – WCATLMallie Hart
737 views15 slides
Object Calisthenics Applied to PHP by
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHPGuilherme Blanco
22.5K views58 slides
PHP for Adults: Clean Code and Object Calisthenics by
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object CalisthenicsGuilherme Blanco
2.8K views92 slides
PHP para Adultos: Clean Code e Object Calisthenics by
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object CalisthenicsGuilherme Blanco
17.2K views90 slides
Frameworks PHP by
Frameworks PHPFrameworks PHP
Frameworks PHPAugusto Pascutti
4.3K views146 slides
Php Presentation by
Php PresentationPhp Presentation
Php PresentationManish Bothra
95.9K views51 slides

Viewers also liked(8)

The Design Dilemma – WCATL by Mallie Hart
The Design Dilemma – WCATLThe Design Dilemma – WCATL
The Design Dilemma – WCATL
Mallie Hart737 views
Object Calisthenics Applied to PHP by Guilherme Blanco
Object Calisthenics Applied to PHPObject Calisthenics Applied to PHP
Object Calisthenics Applied to PHP
Guilherme Blanco22.5K views
PHP for Adults: Clean Code and Object Calisthenics by Guilherme Blanco
PHP for Adults: Clean Code and Object CalisthenicsPHP for Adults: Clean Code and Object Calisthenics
PHP for Adults: Clean Code and Object Calisthenics
Guilherme Blanco2.8K views
PHP para Adultos: Clean Code e Object Calisthenics by Guilherme Blanco
PHP para Adultos: Clean Code e Object CalisthenicsPHP para Adultos: Clean Code e Object Calisthenics
PHP para Adultos: Clean Code e Object Calisthenics
Guilherme Blanco17.2K views
Integração contínua em PHP com Jenkins by Gilmar Pupo
Integração contínua em PHP com JenkinsIntegração contínua em PHP com Jenkins
Integração contínua em PHP com Jenkins
Gilmar Pupo15.5K views

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

Your code sucks, let's fix it (CakeFest2012) by
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
24.7K views132 slides
Your code sucks, let's fix it by
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
11.1K views132 slides
Your code sucks, let's fix it - PHP Master Series 2012 by
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
34.5K views134 slides
You code sucks, let's fix it by
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix itRafael Dohms
56.4K views56 slides
Your code sucks, let's fix it - DPC UnCon by
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
32.5K views56 slides
MIND sweeping introduction to PHP by
MIND sweeping introduction to PHPMIND sweeping introduction to PHP
MIND sweeping introduction to PHPBUDNET
2K views40 slides

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

Your code sucks, let's fix it (CakeFest2012) by Rafael Dohms
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 Dohms24.7K views
Your code sucks, let's fix it by Rafael Dohms
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
Rafael Dohms11.1K views
Your code sucks, let's fix it - PHP Master Series 2012 by Rafael Dohms
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
Rafael Dohms34.5K views
You code sucks, let's fix it by Rafael Dohms
You code sucks, let's fix itYou code sucks, let's fix it
You code sucks, let's fix it
Rafael Dohms56.4K views
Your code sucks, let's fix it - DPC UnCon by Rafael Dohms
Your code sucks, let's fix it - DPC UnConYour code sucks, let's fix it - DPC UnCon
Your code sucks, let's fix it - DPC UnCon
Rafael Dohms32.5K views
MIND sweeping introduction to PHP by BUDNET
MIND sweeping introduction to PHPMIND sweeping introduction to PHP
MIND sweeping introduction to PHP
BUDNET2K views
Introduction to PHP by Bradley Holt
Introduction to PHPIntroduction to PHP
Introduction to PHP
Bradley Holt42.6K views
Zend Certification Preparation Tutorial by Lorna Mitchell
Zend Certification Preparation TutorialZend Certification Preparation Tutorial
Zend Certification Preparation Tutorial
Lorna Mitchell24.3K views
Beyond MVC: from Model to Domain by Jeremy Cook
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to Domain
Jeremy Cook1.5K views
Becoming a better WordPress Developer by Joey Kudish
Becoming a better WordPress DeveloperBecoming a better WordPress Developer
Becoming a better WordPress Developer
Joey Kudish1.6K views
[PL] Jak nie zostać "programistą" PHP? by Radek Benkel
[PL] Jak nie zostać "programistą" PHP?[PL] Jak nie zostać "programistą" PHP?
[PL] Jak nie zostać "programistą" PHP?
Radek Benkel3K views
Entry-level PHP for WordPress by sprclldr
Entry-level PHP for WordPressEntry-level PHP for WordPress
Entry-level PHP for WordPress
sprclldr608 views
06-classes.ppt (copy).pptx by Thắng It
06-classes.ppt (copy).pptx06-classes.ppt (copy).pptx
06-classes.ppt (copy).pptx
Thắng It2 views
Intro to php by Sp Singh
Intro to phpIntro to php
Intro to php
Sp Singh242 views
How to test complex SaaS applications - The family july 2014 by Guillaume POTIER
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
Guillaume POTIER673 views

More from Rafael Dohms

Application Metrics - IPC2023 by
Application Metrics - IPC2023Application Metrics - IPC2023
Application Metrics - IPC2023Rafael Dohms
7 views87 slides
How'd we get here? A guide to Architectural Decision Records by
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
306 views42 slides
Architectural Decision Records - PHPConfBR by
Architectural Decision Records - PHPConfBRArchitectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBRRafael Dohms
299 views42 slides
Application Metrics (with Prometheus examples) by
Application Metrics (with Prometheus examples)Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)Rafael Dohms
998 views89 slides
Application metrics - Confoo 2019 by
Application metrics - Confoo 2019Application metrics - Confoo 2019
Application metrics - Confoo 2019Rafael Dohms
747 views90 slides
Writing code you won’t hate tomorrow - PHPCE18 by
Writing code you won’t hate tomorrow - PHPCE18Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18Rafael Dohms
1.2K views86 slides

More from Rafael Dohms(20)

Application Metrics - IPC2023 by Rafael Dohms
Application Metrics - IPC2023Application Metrics - IPC2023
Application Metrics - IPC2023
Rafael Dohms7 views
How'd we get here? A guide to Architectural Decision Records by Rafael Dohms
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
Rafael Dohms306 views
Architectural Decision Records - PHPConfBR by Rafael Dohms
Architectural Decision Records - PHPConfBRArchitectural Decision Records - PHPConfBR
Architectural Decision Records - PHPConfBR
Rafael Dohms299 views
Application Metrics (with Prometheus examples) by Rafael Dohms
Application Metrics (with Prometheus examples)Application Metrics (with Prometheus examples)
Application Metrics (with Prometheus examples)
Rafael Dohms998 views
Application metrics - Confoo 2019 by Rafael Dohms
Application metrics - Confoo 2019Application metrics - Confoo 2019
Application metrics - Confoo 2019
Rafael Dohms747 views
Writing code you won’t hate tomorrow - PHPCE18 by Rafael Dohms
Writing code you won’t hate tomorrow - PHPCE18Writing code you won’t hate tomorrow - PHPCE18
Writing code you won’t hate tomorrow - PHPCE18
Rafael Dohms1.2K views
Application Metrics (with Prometheus examples) #PHPDD18 by Rafael Dohms
Application Metrics (with Prometheus examples) #PHPDD18Application Metrics (with Prometheus examples) #PHPDD18
Application Metrics (with Prometheus examples) #PHPDD18
Rafael Dohms757 views
Application metrics with Prometheus - DPC18 by Rafael Dohms
Application metrics with Prometheus - DPC18Application metrics with Prometheus - DPC18
Application metrics with Prometheus - DPC18
Rafael Dohms1.3K views
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf by Rafael 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
Rafael Dohms3.9K views
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHP Yo... by Rafael 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...
Rafael Dohms1.4K views
Composer The Right Way - 010PHP by Rafael Dohms
Composer The Right Way - 010PHPComposer The Right Way - 010PHP
Composer The Right Way - 010PHP
Rafael Dohms3.5K views
Writing Code That Lasts - #Magento2Seminar, Utrecht by Rafael Dohms
Writing Code That Lasts - #Magento2Seminar, UtrechtWriting Code That Lasts - #Magento2Seminar, Utrecht
Writing Code That Lasts - #Magento2Seminar, Utrecht
Rafael Dohms976 views
Composer the Right Way - PHPSRB16 by Rafael Dohms
Composer the Right Way - PHPSRB16Composer the Right Way - PHPSRB16
Composer the Right Way - PHPSRB16
Rafael Dohms3.9K views
Composer the Right Way - MM16NL by Rafael Dohms
Composer the Right Way - MM16NLComposer the Right Way - MM16NL
Composer the Right Way - MM16NL
Rafael Dohms1.1K views
Composer The Right Way - PHPUGMRN by Rafael Dohms
Composer The Right Way - PHPUGMRNComposer The Right Way - PHPUGMRN
Composer The Right Way - PHPUGMRN
Rafael Dohms1.4K views
Composer the Right Way - PHPBNL16 by Rafael Dohms
Composer the Right Way - PHPBNL16Composer the Right Way - PHPBNL16
Composer the Right Way - PHPBNL16
Rafael Dohms2.6K views
A Journey into your Lizard Brain - PHP Conference Brasil 2015 by Rafael Dohms
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
Rafael Dohms2.2K views
“Writing code that lasts” … or writing code you won’t hate tomorrow. by 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.
Rafael Dohms967 views
“Writing code that lasts” … or writing code you won’t hate tomorrow. by 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.
Rafael Dohms2.8K views
“Writing code that lasts” … or writing code you won’t hate tomorrow. by 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.
Rafael Dohms3.4K views

Recently uploaded

Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ... by
Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...
Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...ShapeBlue
146 views15 slides
Future of AR - Facebook Presentation by
Future of AR - Facebook PresentationFuture of AR - Facebook Presentation
Future of AR - Facebook PresentationRob McCarty
62 views27 slides
Why and How CloudStack at weSystems - Stephan Bienek - weSystems by
Why and How CloudStack at weSystems - Stephan Bienek - weSystemsWhy and How CloudStack at weSystems - Stephan Bienek - weSystems
Why and How CloudStack at weSystems - Stephan Bienek - weSystemsShapeBlue
197 views13 slides
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ... by
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...ShapeBlue
79 views17 slides
Business Analyst Series 2023 - Week 4 Session 7 by
Business Analyst Series 2023 -  Week 4 Session 7Business Analyst Series 2023 -  Week 4 Session 7
Business Analyst Series 2023 - Week 4 Session 7DianaGray10
126 views31 slides
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ... by
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...ShapeBlue
123 views28 slides

Recently uploaded(20)

Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ... by ShapeBlue
Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...
Backroll, News and Demo - Pierre Charton, Matthias Dhellin, Ousmane Diarra - ...
ShapeBlue146 views
Future of AR - Facebook Presentation by Rob McCarty
Future of AR - Facebook PresentationFuture of AR - Facebook Presentation
Future of AR - Facebook Presentation
Rob McCarty62 views
Why and How CloudStack at weSystems - Stephan Bienek - weSystems by ShapeBlue
Why and How CloudStack at weSystems - Stephan Bienek - weSystemsWhy and How CloudStack at weSystems - Stephan Bienek - weSystems
Why and How CloudStack at weSystems - Stephan Bienek - weSystems
ShapeBlue197 views
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ... by ShapeBlue
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...
Import Export Virtual Machine for KVM Hypervisor - Ayush Pandey - University ...
ShapeBlue79 views
Business Analyst Series 2023 - Week 4 Session 7 by DianaGray10
Business Analyst Series 2023 -  Week 4 Session 7Business Analyst Series 2023 -  Week 4 Session 7
Business Analyst Series 2023 - Week 4 Session 7
DianaGray10126 views
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ... by ShapeBlue
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
How to Re-use Old Hardware with CloudStack. Saving Money and the Environment ...
ShapeBlue123 views
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue by ShapeBlue
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue
2FA and OAuth2 in CloudStack - Andrija Panić - ShapeBlue
ShapeBlue103 views
NTGapps NTG LowCode Platform by Mustafa Kuğu
NTGapps NTG LowCode Platform NTGapps NTG LowCode Platform
NTGapps NTG LowCode Platform
Mustafa Kuğu365 views
DRBD Deep Dive - Philipp Reisner - LINBIT by ShapeBlue
DRBD Deep Dive - Philipp Reisner - LINBITDRBD Deep Dive - Philipp Reisner - LINBIT
DRBD Deep Dive - Philipp Reisner - LINBIT
ShapeBlue140 views
CloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlue by ShapeBlue
CloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlueCloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlue
CloudStack Managed User Data and Demo - Harikrishna Patnala - ShapeBlue
ShapeBlue94 views
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ... by ShapeBlue
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...
Live Demo Showcase: Unveiling Dell PowerFlex’s IaaS Capabilities with Apache ...
ShapeBlue85 views
Automating a World-Class Technology Conference; Behind the Scenes of CiscoLive by Network Automation Forum
Automating a World-Class Technology Conference; Behind the Scenes of CiscoLiveAutomating a World-Class Technology Conference; Behind the Scenes of CiscoLive
Automating a World-Class Technology Conference; Behind the Scenes of CiscoLive
Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O... by ShapeBlue
Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O...Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O...
Declarative Kubernetes Cluster Deployment with Cloudstack and Cluster API - O...
ShapeBlue88 views
What’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlue by ShapeBlue
What’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlueWhat’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlue
What’s New in CloudStack 4.19 - Abhishek Kumar - ShapeBlue
ShapeBlue222 views
"Surviving highload with Node.js", Andrii Shumada by Fwdays
"Surviving highload with Node.js", Andrii Shumada "Surviving highload with Node.js", Andrii Shumada
"Surviving highload with Node.js", Andrii Shumada
Fwdays53 views
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda... by ShapeBlue
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...
Hypervisor Agnostic DRS in CloudStack - Brief overview & demo - Vishesh Jinda...
ShapeBlue120 views
Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava... by ShapeBlue
Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava...Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava...
Centralized Logging Feature in CloudStack using ELK and Grafana - Kiran Chava...
ShapeBlue101 views
Updates on the LINSTOR Driver for CloudStack - Rene Peinthor - LINBIT by ShapeBlue
Updates on the LINSTOR Driver for CloudStack - Rene Peinthor - LINBITUpdates on the LINSTOR Driver for CloudStack - Rene Peinthor - LINBIT
Updates on the LINSTOR Driver for CloudStack - Rene Peinthor - LINBIT
ShapeBlue166 views
Digital Personal Data Protection (DPDP) Practical Approach For CISOs by Priyanka Aash
Digital Personal Data Protection (DPDP) Practical Approach For CISOsDigital Personal Data Protection (DPDP) Practical Approach For CISOs
Digital Personal Data Protection (DPDP) Practical Approach For CISOs
Priyanka Aash153 views
Extending KVM Host HA for Non-NFS Storage - Alex Ivanov - StorPool by ShapeBlue
Extending KVM Host HA for Non-NFS Storage -  Alex Ivanov - StorPoolExtending KVM Host HA for Non-NFS Storage -  Alex Ivanov - StorPool
Extending KVM Host HA for Non-NFS Storage - Alex Ivanov - StorPool
ShapeBlue84 views

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