REMOVE THE MAGIC
with
Functional PHP
https://slides.com/davesters/remove-magic-functional-php
OUR APPLICATIONS
ARE FULL OF MAGIC
- Large frameworks
- ORMs
- Multiple layers of inheritance
- Dependency Injection
- Convention over Configuration
HOW DO WE
FIGHT MAGIC?
Level up our magic resistance
SIMPLICITY
The state or quality of being simple
+10 MR
CONVENTION
OVER
CONFIGURATION
ORMs
$results = $accounts
->where("first_name = 'Bob'")
->andWhere("last_name = 'Sacameno'")
->andWhere('deleted IS NULL')
->andWhere('ref_id = 2')
->not()->andWhere('other_id = 3')
->orderByDesc('created')
->limit(10);
$results = $db->query("SELECT * FROM accounts WHERE
first_name = 'bob' AND last_name = 'Sacameno' AND
deleted IS NULL AND ref_id = 2 AND other_id <> 3
ORDER BY created DESC LIMIT 10");
DEPENDENCY INJECTION
$container->bind('iFooContext', '\Foo\Context');
$container->bind('iBarController', '\Controllers\BarController');
…
AUTOMAPPERS
class Account {
public $id, $name, $email;
}
$account = Mapper::map('Account', $databaseAccount);
Mapper::createMap('Account')->forMember('name', function($a) {
return $a['first_name'] . ‘ ‘ . $a['last_name'];
});
Mapper::createMap('Account')->forMember('email', function($a) {
return strtolower($a['email']);
});
AUTOMAPPERS
class Account {
public $id, $name, $email;
public function map($account) {
$this->id = $account->id;
$this->name = $account->first_name.' '.$account->last_name;
$this->email = strtolower($account->email);
}
}
$account->map($databaseAccount);
"Typing is not the bottleneck"
- Michael Hill?
OBLIGATORY QUOTE SLIDE
SEGWAY
SEGUE
You are responsible for ALL the code in your project
FUNCTIONAL PROGRAMMING
"A style of building computer programs, that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data..."
- Wikipedia
+50 MR
TENETS OF FUNCTIONAL PROGRAMMING
-
First-class functions
-
Pure functions
-
Immutable Data
-
Plenty more...
FIRST-CLASS FUNCTIONS
$add = function ($a, $b) {
return $a + $b;
});
HIGHER-ORDER FUNCTIONS
function getSomeAsyncData(‘foo’, function($data) {
// do something with async data here.
});
PURE FUNCTIONS
function add($a, $b) {
return $a + $b;
}
REFERENTIAL TRANSPARENCY
$sum = add(2, 3);
$sum = 5;
IMMUTABLE DATA
$numbers = [1, 2, 3, 4, 5];
for ($x = 0; $x < count($numbers); $x++) {
$numbers[$x] = $numbers[$x] + 5;
}
An immutable object is an object whose state cannot be modified after it is created.
foreach ($numbers as &$num) {
$num += 5;
}
IMMUTABLE DATA
$numbers = [1, 2, 3, 4, 5];
for ($x = 0; $x < count($numbers); $x++) {
$numbers[$x] = $numbers[$x] + 5;
}
$biggerNumbers = array_map(function($num) {
return $num + 5;
}, $numbers);
Functional Way
$biggerNumbers = array_map('add5', $numbers);
function add5($a) {
return $a + 5;
}
function add5($a) {
return add($a, 5);
}
$num = add5(7); // 12
function add5($num) {
return $num + 5;
}
function add($a, $b) {
return $a + $b;
}
FUNCTION COMPOSITION
TODO APP
LET'S BUILD SOMETHING...
Index.php
require 'vendor/autoload.php';
require 'src/TodoApp.php';
require 'src/Mustache.php';
$handlers = [];
$mustache = Mustache::Render(new Mustache_Engine(), function($tpl) {
return file_get_contents(__DIR__ . "/views/$tpl.mustache");
});
$klein = new \Klein\Klein();
$todoApp = new TodoApp($klein, $mustache, $handlers);
$todoApp->setRoutes();
$todoApp->start();
TodoApp.php
class TodoApp {
private $app, $handlers, $mustache;
public function __construct(Klein $app, $mustache, $handlers) {
$this->app = $app;
$this->handlers = $handlers;
$this->mustache = $mustache;
}
public function start() {
$this->app->dispatch();
}
public function setRoutes() { ... }
private function handle($view, $handlers) { ... }
}
TodoApp.php routes
public function setRoutes() {
$h = $this->handlers;
$this->app->respond('/', $this->handle('index', $h['index']));
}
private function handle($view, array $handlers) {
// Loop over $handlers array and build view model
$this->render($this->mustache($view, $model));
}
IndexHandler.php
class IndexHandler {
public static function Handle($dataSource) {
return function($model, $params) {
$todos = $dataSource('SELECT id, todo, completed
FROM todos WHERE deleted IS NULL ORDER BY created');
return [
'title' => 'Todo App',
'todos' => $todos,
'count' => count($todos)
];
};
}
}
$handlers = [
'index' => [ Handlers\IndexHandler::Handle($dataSource) ]
];
Add and Update Handlers
$handlers = [
'index' => [ Handlers\IndexHandler::Handle($dataSource) ],
'add' => [ Handlers\AddTodoHandler::Handle($dataSource) ],
'update' => [ Handlers\UpdateTodoHandler::Handle($dataSource) ]
];
IndexQuery.php
class IndexQuery {
public static function Query($dataSource) {
return function($model, $params) {
$todos = $dataSource('SELECT id, todo, completed
FROM todos WHERE deleted IS NULL ORDER BY created');
return [ 'todos' => $todos ];
};
}
}
IndexHandler.php
class IndexHandler {
public static function Handle() {
return function($model, $params) {
return [
'title' => 'Todo App',
'todos' => $model['todos']
'count' => count($model['todos']
];
};
}
}
$handlers = [
'index' => [
Queries\IndexQuery::Query($dataSource),
Handlers\IndexHandler::Handle()
],
...
];
Summary
- What problem am I having that this library is going to solve?
-
Can I change my problem around so I don’t need it?
Thanks
Sample App:
https://github.com/davesters/functional-todo-example
"8 Lines of Code" Keynote:
http://www.infoq.com/presentations/8-lines-code-refactoring
Contact:
Twitter - @davesters
Github - davesters
Website - http://www.lovesmesomecode.com
Remove the magic with functional PHP
By David Corona
Remove the magic with functional PHP
- 878