SlideShare une entreprise Scribd logo
1  sur  135
Télécharger pour lire hors ligne
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

Contenu connexe

Tendances

AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?
AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?
AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?Masamitsu Maehara
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency InjectionTheo Jungeblut
 
Cobolでもやりたいテスト自動化
Cobolでもやりたいテスト自動化 Cobolでもやりたいテスト自動化
Cobolでもやりたいテスト自動化 daisukhayash
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promisesAvoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promisesAnkit Agarwal
 
Introduction to Swagger
Introduction to SwaggerIntroduction to Swagger
Introduction to SwaggerKnoldus Inc.
 
Flutterとプラットフォーム依存の処理の対応について
Flutterとプラットフォーム依存の処理の対応についてFlutterとプラットフォーム依存の処理の対応について
Flutterとプラットフォーム依存の処理の対応についてSatoshi Noda
 
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)Carlos Buenosvinos
 
Javascript under the hood 1
Javascript under the hood 1Javascript under the hood 1
Javascript under the hood 1Thang Tran Duc
 
Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2José Paumard
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix itRafael Dohms
 
RxJS - The Basics & The Future
RxJS - The Basics & The FutureRxJS - The Basics & The Future
RxJS - The Basics & The FutureTracy Lee
 
Introdução básica ao JavaScript
Introdução básica ao JavaScriptIntrodução básica ao JavaScript
Introdução básica ao JavaScriptCarlos Eduardo Kadu
 
gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20
gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20
gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20Phil Wilkins
 
Angular 16 – the rise of Signals
Angular 16 – the rise of SignalsAngular 16 – the rise of Signals
Angular 16 – the rise of SignalsCoding Academy
 
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기YoungjikYoon
 
.NET 7におけるBlazorの新機能
.NET 7におけるBlazorの新機能.NET 7におけるBlazorの新機能
.NET 7におけるBlazorの新機能TomomitsuKusaba
 

Tendances (20)

AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?
AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?
AWS Security JAWS 経済的にハニーポットのログ分析をするためのベストプラクティス?
 
Clean Code II - Dependency Injection
Clean Code II - Dependency InjectionClean Code II - Dependency Injection
Clean Code II - Dependency Injection
 
Node.js e Express
Node.js e ExpressNode.js e Express
Node.js e Express
 
Cobolでもやりたいテスト自動化
Cobolでもやりたいテスト自動化 Cobolでもやりたいテスト自動化
Cobolでもやりたいテスト自動化
 
Angular vs react
Angular vs reactAngular vs react
Angular vs react
 
Avoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promisesAvoiding callback hell in Node js using promises
Avoiding callback hell in Node js using promises
 
Introduction to Swagger
Introduction to SwaggerIntroduction to Swagger
Introduction to Swagger
 
Flutterとプラットフォーム依存の処理の対応について
Flutterとプラットフォーム依存の処理の対応についてFlutterとプラットフォーム依存の処理の対応について
Flutterとプラットフォーム依存の処理の対応について
 
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)
Hexagonal Architecture - PHP Barcelona Monthly Talk (DDD)
 
Javascript under the hood 1
Javascript under the hood 1Javascript under the hood 1
Javascript under the hood 1
 
Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2Lambdas and Streams Master Class Part 2
Lambdas and Streams Master Class Part 2
 
Your code sucks, let's fix it
Your code sucks, let's fix itYour code sucks, let's fix it
Your code sucks, let's fix it
 
RxJS - The Basics & The Future
RxJS - The Basics & The FutureRxJS - The Basics & The Future
RxJS - The Basics & The Future
 
Introdução básica ao JavaScript
Introdução básica ao JavaScriptIntrodução básica ao JavaScript
Introdução básica ao JavaScript
 
gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20
gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20
gRPC, GraphQL, REST - Which API Tech to use - API Conference Berlin oct 20
 
Angular 16 – the rise of Signals
Angular 16 – the rise of SignalsAngular 16 – the rise of Signals
Angular 16 – the rise of Signals
 
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
Gradle Kotlin 컨벤션 플러그인으로 효율적으로 멀티 모듈 관리하기
 
NestJS
NestJSNestJS
NestJS
 
Hexagonal architecture
Hexagonal architectureHexagonal architecture
Hexagonal architecture
 
.NET 7におけるBlazorの新機能
.NET 7におけるBlazorの新機能.NET 7におけるBlazorの新機能
.NET 7におけるBlazorの新機能
 

En vedette

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

En vedette (11)

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

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

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

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

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

Plus de Rafael Dohms

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

Plus de Rafael Dohms (20)

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

Dernier

Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 

Dernier (20)

Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 

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