Dutch PHP
Conference:
Distilled
Chris Saylor
Stephen Young
@cjsaylor
@young_steveo
Focus of this talk
PHP by the Numbers
Emergent Design
What's new in PHP 5.5
Unbreakable Domain Models
PHP by the Numbers
Measuring Complexity
Cyclomatic complexity
N-path complexity
Cyclomatic complexity
The cyclomatic complexity of a method is the count of the number
of independent decision points in the method, plus one for the
method entry.
It's a fancy term for measuring the complexity of a method.
Decision points in a method increase complexity.
(e.g. if, else, foreach, etc.)
Plugins will calculate it for you.
(PHP Mess Detector, JSHint, Grunt!)
The lower, the better
1—4 low complexity, easy to maintain
5—7 moderate complexity
8—10 high complexity, hard to test
11+ ?
N-path complexity
The number of acyclic execution paths through a function; an
objective measure of complexity related to the ease with which
software can be comprehensively tested
acyclic execution paths?
It's a lot like cyclomatic complexity.
It measures how many straight lines you can draw through a
method.
A method with a single IFstatement has an N-path of 2.
A method with 2 IFstatements has an N-path of 4.
3 IFstatements would make the N-path 8.
Another way to look at it
The value of a method's N-path complexity is equal to the number
of unit tests needed to ensure that you have 100% code coverage.
A "quick estimate" for N-Path:
2^(cyclomaticComplexity - 1)
You have probably seen this
Running Grunt!
Running "jshint:source" (jshint) task
Linting app/webroot/js/views/doesEverythingView.js...
ERROR
[L31:C38] W074: This function's cyclomatic complexity is too high. (18)
Things to avoid
Violating the Single Responsibility Principle
A Controller method with logic to perform CRUD operations.
A Product Model with logic for formatting currency.
Seperation of Concerns
Too complex: A single method that executes four business
rules to perform a task.
Better: Four methods that each execute a single business rule.
Plan Ahead
It's common to be handed a user story that is simple to describe but
complex to implement.
Take time to break the logic into the smallest possible units before
writing code.
Emergent Design with
PHPSpec
Topics covered
1. PHPSpec overview
2. emergent design
3. simple design & smells
4. designing composition with mocks
What is PHPSpec?
it started as a port for rspec
Problem
PHP !== Ruby
in ruby everything is an object
and all objects are open
Also, monkey patching code at runtime is a typical practice in the Ruby world.
blowling.score.should eq(0)
an early PHPSpec syntax example
Trying to emulate ruby in PHP looks ugly
$this->spec($bowling->getScore())->shouldEqual(0);
PHPSpec's new goals
fun to work with
development tool
let's not get in the way
enforce TDD
do it the PHP way
Test Driven Development
yellow — Write the test first
red — Implement the class/method; test is failing
green — Test is passing
How PHPUnit handles the Yellow step
PHPUnit 3.7.14 by Sebastian Bergmann.
PHP Fatal error: Class 'Markdown' not found in /Users/stephenyoung/Documents/projects/Lab/phpuni
t/tests/MarkdownTest.php on line 8
Fatal error: Class 'Markdown' not found in /Users/stephenyoung/Documents/projects/Lab/phpunit/te
sts/MarkdownTest.php on line 8
How PHPSpec handles the Yellow step
it does this for missing methods too.
> specCustomer
✘ it converts plain text to html paragraphs
Class Markdown does not exist.
Do you want me to create it for you? [Y/n]
Mocking
It suffices to say that mocking is painful in PHPUnit.
PHPSpec has a very easy-to-use Mocking library.
Too much to go into here.
That's Enough about PHPSpec
It's a pretty awesome tool. Go check it out.
Emergent Design
What is software design?
Software design
is
the art of describing how to to solve a problem.
First learn design, then learn emergent
design
Alan Kay on Messaging
"The key in making great and growable systems is
much more to design how its modules
communicate rather than what their internal
properties and behaviors should be."
$this->person->getCar()->getEngine()->ignite();
Focusing on messaging makes code more
flexible
$this->person->startCar();
Software design
is
the art of describing how to to solve problems with roles,
responsibilities and messages
Big Design Up Front
It's hard to change later.
We need to think about things before developing.
We need to make sure we don't miss anything.
This is just the way we do it.
Y A G N I
61%
of all requested features are actually
delivered
27%
of all requested features are actually used
5% to 10%
are responsible for realising the
envisioned benefits
we should design for the high priority items and make it easy to
change later.
Agile Software design
is
the art of describing how to to solve problems with roles,
responsibilities and messages
in a change-friendly way
Easier said than done?
1. Test
2. Code
3. Refactor
4. Repeat
Use simple design rules to refactor
1. All tests run and pass
2. Remove duplication
3. Remove opacity
4. Remove complexity
What is the result of this method in your
code?
It is testable.
It is modular.
It is expressive.
It is simple.
What is the alternative?
Viscosity
Immobility, Rigidity, Fragility
Unreadable
Complex
Simple design enables smell detection
Simple Design
1. All tests run and pass
2. Remove duplication
3. Remove opacity
4. Remove complexity
smells
1. Test smells?
2. DRY smells?
3. Opacity smells?
4. Complexity smells?
Test smells
Lack of tests
Setup is too complex
Unclear exercise
No expectation
DRY Smells
Copy Pasta
Logic duplication
Duplicated constants
Alternative classes with different interfaces
Opacity smells
Naming not in the domain
Name does not express intent
Feature envy
Method too long
Method does more than one thing
Complexity smells
Unnecessary else
Unnecessary if
Unnecessary switch
Too many passed arguments
Use design patterns to refactor
What can happen in a method?
return a value
throw an exception
delegate
modify state not the final behavior
print something we should probably delegate that too
Design delegation with mocks
start by defining behavior
internally delegate to another method
Finally
Define new role
Extract collaborators using mocks
Move behavior definition to new collaborator test
What's new in PHP 5.5
Performance
Performance boost compared to 5.3 -> 5.4 is not as great as 5.4 ->
5.5
APC replacement
Zend's OPCache is now packaged with PHP 5.5 and APC is now
depricated
array_column
$keyValues = [
['key1' => 'val1', 'key2' => 'val2'],
['key1' => 'val3', 'key2' => 'val4']
];
var_dump(array_column($keyValues, 'key1'));
// yields array('val1', 'val3')
Foreach with list
$test = [
[1, 2],
[3, 4]
];
foreach ($test as list($a, $b)) {
echo "a: $a; b: $b";
}
// displays:
// a: 1; b: 2
// a: 3; b: 4
Finally a finally!
try {
doSomeWork();
return true;
} finally {
cleanUpSomeStuff();
echo "I am reachable!";
}
echo "I am not reachable :( ";
Generators
function lotsOfRecords() {
while ($row = $this->db->getNext()) {
yield $row['id'] => $row;
}
}
foreach (lotsOfRecords() as $id => $row) {
// do stuff
}
Password hashing API
echo password_hash('somepassword', PASSWORD_BCRYPT);
// $2y$12$QjSH496pcT5CEbzjD/vtVeH03tfHKFy36d4J0Ltp3lRtee9HDxY3K
password_verify(
'somepassword',
'$2y$12$QjSH496pcT5CEbzjD/vtVeH03tfHKFy36d4J0Ltp3lRtee9HDxY3K'
);
// true
Unbreakable Domain Models
Use objects as consistency
boundaries
class Customer {
public function __construct($email) {
if( /* ugly regex here */) {
throw new InvalidArgumentException();
}
$this->email = $email;
}
}
Single Responsibility Principle
class Email {
private $email;
public function __construct($email) {
if( /* ugly regex here */) {
throw new InvalidArgumentException();
}
$this->email = $email;
}
public function __toString() {
return $this->email;
}
}
Customer class is now tighter
class Customer {
/** @var Email */
private $email;
public function __construct(Email $email) {
$this->email = $email;
}
}
Encapsulate state and
behavior with Value Objects
A user story may be:
"A customer orders products and pays for them."
Procedural
$order = new Order;
$order->setCustomer($customer);
$order->setProducts($products);
$order->setStatus(Order::UNPAID);
// ...
$order->setPaidAmount(500);
$order->setPaidCurrency(‘EUR’);
$order->setStatus(Order::PAID);
Improve it with object for
consistency
$order = new Order;
$order->setCustomer($customer);
$order->setProducts($products);
$order->setStatus(
new PaymentStatus(PaymentStatus::UNPAID)
);
$order->setPaidAmount(500);
$order->setPaidCurrency(‘EUR’);
$order->setStatus(
new PaymentStatus(PaymentStatus::PAID)
);
Improve it with more
consistency
$order = new Order;
$order->setCustomer($customer);
$order->setProducts($products);
$order->setStatus(
new PaymentStatus(PaymentStatus::UNPAID)
);
$order->setPaidMonetary(
new Money(500, new Currency(‘EUR’))
);
$order->setStatus(
new PaymentStatus(PaymentStatus::PAID)
);
Even more
$order = new Order($customer, $products);
// set PaymentStatus in Order::__construct()
$order->setPaidMonetary(
new Money(500, new Currency(‘EUR’))
);
$order->setStatus(
new PaymentStatus(PaymentStatus::PAID)
);
Getting ridiculous now
$order = new Order($customer, $products);
$order->pay(
new Money(500, new Currency(‘EUR’))
);
// set PaymentStatus in Order#pay()
Encapsulation through
specification
"I want to give a discount to a customer that has at least 3 orders."
interface CustomerSpecification {
/** @return bool */
public function isSatisfiedBy(Customer $customer);
}
class CustomerIsPremium implements CustomerSpecification {
private $orderRepository;
public function __construct(
OrderRepository $orderRepository
) {...}
/** @return bool */
public function isSatisfiedBy(Customer $customer) {
$count = $this->orderRepository->countFor($customer);
return $count >= 3;
}
}
$customerIsPremium = new CustomerIsPremium($orderRepository)
if($customerIsPremium->isSatisfiedBy($customer)) {
// send special offer
}
Credits
PHP By the numbers - Anthony Ferrara
Emergent Design with phpspec - Marcello Duarte
Unbreakable Domain Models - Mathias Verraes
Let's have a look at PHP 5.5 - Julien Pauli

Dutch PHP Conference 2013: Distilled