SlideShare una empresa de Scribd logo
1 de 32
Descargar para leer sin conexión
Durian Building Singapore / Dave Cross / CC BY-NC-SA 2.0

DURIAN
A PHP 5.5 microframework based on generator-style middleware	

http://durianphp.com
BEFORE WE BEGIN…
What the heck are generators ?
GENERATORS
•

Introduced in PHP 5.5 (although HHVM had them earlier)	


•

Generators are basically iterators with a simpler syntax	


•

The mere presence of the yield keyword turns a closure into a
generator constructor	


•

Generators are forward-only (cannot be rewound)	


•

You can send() values into generators	


•

You can throw() exceptions into generators
THE YIELD KEYWORD
class MyIterator implements Iterator	
{	
private $values;	

!

public function __construct(array $values) {	
$this->values = $values;	
}	
public function current() {	
return current($this->values);	
}	
public function key() {	
return key($this->values);	
}	
public function next() {	
return next($this->values);	
}	
public function rewind() {}	
public function valid() {	
return null !== key($this->values);	
}	

}	
 	
$iterator = new MyIterator([1,2,3,4,5]);	
 	
while ($iterator->valid()) {	
echo $iterator->current();	
$iterator->next();	
}

$callback = function (array $values) {	
foreach ($values as $value) {	
yield $value;	
}	
};	
 	
$generator = $callback([1,2,3,4,5]);	
 	
while ($generator->valid()) {	
echo $generator->current();	
$generator->next();	
}
PHP MICROFRAMEWORKS
How do they handle middleware and routing ?
EVENT LISTENERS
$app->before(function (Request $request) use ($app) {	
$app['response_time'] = microtime(true);	
});	
 	
$app->get('/blog', function () use ($app) {	
return $app['blog_service']->getPosts()->toJson();	
});	
 	
$app->after(function (Request $request, Response $response) use ($app) {	
$time = microtime(true) - $app['response_time'];	
$response->headers->set('X-Response-Time', $time);	
});
THE DOWNSIDE
•

A decorator has to be split into two separate
functions to wrap the main application	


•

Data has to be passed between functions	


•

Can be confusing to maintain
HIERARCHICAL ROUTING
$app->path('blog', function ($request) use ($app) {	
$time = microtime(true);	
$blog = BlogService::create()->initialise();	
 	
$app->path('posts', function () use ($app, $blog) {	
$posts = $blog->getAllPosts();	
 	
$app->get(function () use ($app, $posts) {	
return $app->template('posts/index', $posts->toJson());	
});	
});	
 	
$time = microtime(true) - $time;	
$this->response()->header('X-Response-Time', $time);	
});
THE DOWNSIDE
•

Subsequent route and method declarations are now
embedded inside a closure	


•

Closure needs to be executed to proceed	


•

Potentially incurring expensive initialisation or
computations only to be discarded	


•

Middleware code is still split across two locations
“CALLBACK HELL”
$app->path('a', function () use ($app) {	
$app->param('b', function ($b) use ($app) {	
$app->path('c', function () use ($b, $app) {	
$app->param('d', function ($d) use ($app) {	
$app->get(function () use ($d, $app) {	
$app->json(function () use ($app) {	
// ...	
});	
});	
});	
});	
});	
});
How about other languages ?
KOA (NODEJS)
var koa = require('koa');	
var app = koa();	

!
app.use(function *(next){	
var start = new Date;	
yield next;	
var ms = new Date - start;	
console.log('%s %s - %s', this.method, this.url, ms);	
});	

!
app.use(function *(){	
this.body = 'Hello World';	
});	

!
app.listen(3000);
MARTINI (GOLANG)
package main	
import "github.com/codegangsta/martini"	

!
func main() {	
m := martini.Classic()	

!
m.Use(func(c martini.Context, log *log.Logger) {	
log.Println("before a request")	
c.Next()	
log.Println("after a request")	
})	

!
m.Get("/", func() string {	
return "Hello world!"	
})	

!
m.Run()	
}
INTRODUCING DURIAN
•

Take advantage of PHP 5.4, 5.5 features	


•

Unify interface across controllers and middleware	


•

Avoid excessive nesting / callback hell	


•

Use existing library components	


•

None of this has anything to do with durians
COMPONENTS
•

Application container: Pimple by @fabpot	


•

Request/Response: Symfony2 HttpFoundation	


•

Routing: FastRoute by @nikic	


•

Symfony2 HttpKernelInterface (for stackphp
compatibility)
A DURIAN APPLICATION
$app = new DurianApplication();	
!

$app->route('/hello/{name}', function () {	
return 'Hello '.$this->param('name');	
});	
!

$app->run();


•

Nothing special there, basically the same syntax
as every microframework ever
HANDLERS
•

Simple wrapper around closures and generators	


•

Handlers consist of the primary callback and an optional guard
callback

$responseHandler = $app->handler(function () {	
$time = microtime(true);	
yield;	
$time = microtime(true) - $time;	
$this->response()->headers->set('X-Response-Time', $time);	
}, function () use ($app) {	
return $app['debug'];	
});
THE HANDLER STACK
•

Application::handle() iterates through a generator that
produces Handlers to be invoked	


•

Generators produced from handlers are placed into
another stack to be revisited in reverse order	


•

A Handler may produce a generator that produces more
Handlers, which are fed back to the main generator	


•

The route dispatcher is one such handler
function

generator

Route dispatcher
A

D

B

A

C

B

C

D
MODIFYING THE STACK
$app['middleware.response_time'] = $app->handler(function () {	
$time = microtime(true);	
yield;	
$time = microtime(true) - $time;	
$this->response()->headers->set('X-Response-Time', $time);	
}, function () use ($app) {	
return $this->master() && $app['debug'];	
});	

!
$app->handlers([	
'middleware.response_time',	
new DurianMiddlewareRouterMiddleware()	
]);	

!
$app->after(new DurianMiddlewareResponseMiddleware());	

!
$app->before(new DurianMiddlewareWhoopsMiddleware());
ROUTE HANDLER
•

Apply the handler concept to route matching	



$app->handler(function () {	
$this->response('Hello World!');	
}, function () {	
$matcher = new RequestMatcher('^/$');	
return $matcher->matches($this->request());	
});	

•

Compare to	



$app->route('/', function () {	
$this->response('Hello World!');	
});
ROUTE CHAINING
$app['awesome_library'] = $app->share(function ($app) {	
return new MyAwesomeLibrary();	
});	

!
$app->route('/hello', function () use ($app) {	
$app['awesome_library']->performExpensiveOperation();	
yield 'Hello ';	
$app['awesome_library']->performCleanUp();	
})->route('/{name}', function () {	
return $this->last().$this->param('name');	
})->get(function () {	
return ['method' => 'GET', 'message' => $this->last()];	
})->post(function () {	
return ['method' => 'POST', 'message' => $this->last()];	
});
ROUTE DISPATCHING
•

This route definition:	



$albums = $app->route('/albums', A)->get(B)->post(C);	
$albums->route('/{aid:[0-9]+}', D, E)->get(F)->put(G, H)->delete(I);	

•

Gets turned into:	



GET
POST
GET
PUT
DELETE

/albums
/albums
/albums/{aid}
/albums/{aid}
/albums/{aid}

=>
=>
=>
=>
=>

[A,B]"
[A,C]"
[A,D,E,F]"
[A,D,E,G,H]"
[A,D,E,I]
•

Route chaining isn’t mandatory !	


•

You can still use the regular syntax

// Routes will support GET by default	
$app->route('/users');	

!
// Methods can be declared without handlers	
$app->route('/users/{name}')->post();	

!
// Declare multiple methods separated by pipe characters	
$app->route('/users/{name}/friends')->method('GET|POST');
CONTEXT
•

Every handler is bound to the Context object using Closure::bind	


•

A new context is created for every request or sub request
Get the Request object

$request = $this->request();

Get the Response

$response = $this->response();

Set the Response

$this->response("I'm a teapot", 418);

Get the last handler output

$last = $this->last();

Get a route parameter

$id = $this->param('id');

Throw an error

$this->error('Forbidden', 403);
EXCEPTION HANDLING
•

Exceptions are caught and bubbled back up through all registered
generators	


•

Intercept them by wrapping the yield statement with a try/catch block

$exceptionHandlerMiddleware = $app->handler(function () {	
try {	
yield;	
} catch (Exception $exception) {	
$this->response($exception->getMessage(), 500);	
}	
});
AWESOME EXAMPLE
Let’s add two integers together !
$app->route('/add', function () use ($app) {



$app['number_collection'] = $app->share(function ($app) {	
return new NumberCollection();	
});	
$app['number_parser'] = $app->share(function ($app) {	
return new SimpleNumberStringParser();	
});"
yield;	
$addition = new AdditionOperator('SimplePHPEasyPlusNumberSimpleNumber');	
$operation = new ArithmeticOperation($addition);	
$engine = new Engine($operation);	
$calcul = new Calcul($engine, $app['number_collection']);	
$runner = new CalculRunner();	
$runner->run($calcul);	
$result = $calcul->getResult();	
$numericResult = $result->getValue();	
$this->response('The answer is: ' . $numericResult);

})->route('/{first:[0-9]+}', function () use ($app) {

$firstParsedNumber = $app['number_parser']->parse($this->param('first'));	
$firstNumber = new SimpleNumber($firstParsedNumber);	
$firstNumberProxy = new CollectionItemNumberProxy($firstNumber);	
$app['number_collection']->add($firstNumberProxy);

})->route('/{second:[0-9]+}', function () use ($app) {

$secondParsedNumber = $app['number_parser']->parse($this->param('second'));	
$secondNumber = new SimpleNumber($secondParsedNumber);	
$secondNumberProxy = new CollectionItemNumberProxy($secondNumber);	
$app['number_collection']->add($secondNumberProxy);

})->get();
COMING SOON
•

Proper tests and coverage (!!!)	


•

Handlers for format negotiation, session, locale, etc	


•

Dependency injection through reflection (via trait)	


•

Framework/engine-agnostic view composition and
template rendering (separate project)
THANK YOU
bigblah@gmail.com	

https://github.com/gigablah	

http://durianphp.com

Más contenido relacionado

La actualidad más candente

Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)
Kris Wallsmith
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
Fabien Potencier
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
jsmith92
 

La actualidad más candente (20)

Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
 
Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)Doctrine MongoDB ODM (PDXPHP)
Doctrine MongoDB ODM (PDXPHP)
 
Lithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate FrameworksLithium: The Framework for People Who Hate Frameworks
Lithium: The Framework for People Who Hate Frameworks
 
Looping the Loop with SPL Iterators
Looping the Loop with SPL IteratorsLooping the Loop with SPL Iterators
Looping the Loop with SPL Iterators
 
Silex meets SOAP & REST
Silex meets SOAP & RESTSilex meets SOAP & REST
Silex meets SOAP & REST
 
Electrify your code with PHP Generators
Electrify your code with PHP GeneratorsElectrify your code with PHP Generators
Electrify your code with PHP Generators
 
Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5Rich domain model with symfony 2.5 and doctrine 2.5
Rich domain model with symfony 2.5 and doctrine 2.5
 
Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)Introducing Assetic (NYPHP)
Introducing Assetic (NYPHP)
 
Symfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il clienteSymfony2, creare bundle e valore per il cliente
Symfony2, creare bundle e valore per il cliente
 
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo EditionLithium: The Framework for People Who Hate Frameworks, Tokyo Edition
Lithium: The Framework for People Who Hate Frameworks, Tokyo Edition
 
News of the Symfony2 World
News of the Symfony2 WorldNews of the Symfony2 World
News of the Symfony2 World
 
Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3Advanced Querying with CakePHP 3
Advanced Querying with CakePHP 3
 
Zero to SOLID
Zero to SOLIDZero to SOLID
Zero to SOLID
 
Design Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et PimpleDesign Patterns avec PHP 5.3, Symfony et Pimple
Design Patterns avec PHP 5.3, Symfony et Pimple
 
The State of Lithium
The State of LithiumThe State of Lithium
The State of Lithium
 
Forget about loops
Forget about loopsForget about loops
Forget about loops
 
PHP 5.3 Overview
PHP 5.3 OverviewPHP 5.3 Overview
PHP 5.3 Overview
 
Symfony 2.0 on PHP 5.3
Symfony 2.0 on PHP 5.3Symfony 2.0 on PHP 5.3
Symfony 2.0 on PHP 5.3
 
Future of HTTP in CakePHP
Future of HTTP in CakePHPFuture of HTTP in CakePHP
Future of HTTP in CakePHP
 
Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010Symfony2 - WebExpo 2010
Symfony2 - WebExpo 2010
 

Similar a Durian: a PHP 5.5 microframework with generator-style middleware

PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
elliando dias
 
Jsphp 110312161301-phpapp02
Jsphp 110312161301-phpapp02Jsphp 110312161301-phpapp02
Jsphp 110312161301-phpapp02
Seri Moth
 
Aura Project for PHP
Aura Project for PHPAura Project for PHP
Aura Project for PHP
Hari K T
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
elliando dias
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
Kang-min Liu
 

Similar a Durian: a PHP 5.5 microframework with generator-style middleware (20)

PHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolvePHPCon 2016: PHP7 by Witek Adamus / XSolve
PHPCon 2016: PHP7 by Witek Adamus / XSolve
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
Nashvile Symfony Routes Presentation
Nashvile Symfony Routes PresentationNashvile Symfony Routes Presentation
Nashvile Symfony Routes Presentation
 
Functional programming with php7
Functional programming with php7Functional programming with php7
Functional programming with php7
 
The promise of asynchronous php
The promise of asynchronous phpThe promise of asynchronous php
The promise of asynchronous php
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
The promise of asynchronous php
The promise of asynchronous phpThe promise of asynchronous php
The promise of asynchronous php
 
Jsphp 110312161301-phpapp02
Jsphp 110312161301-phpapp02Jsphp 110312161301-phpapp02
Jsphp 110312161301-phpapp02
 
PHP pod mikroskopom
PHP pod mikroskopomPHP pod mikroskopom
PHP pod mikroskopom
 
Elements of Functional Programming in PHP
Elements of Functional Programming in PHPElements of Functional Programming in PHP
Elements of Functional Programming in PHP
 
Aura Project for PHP
Aura Project for PHPAura Project for PHP
Aura Project for PHP
 
Micropage in microtime using microframework
Micropage in microtime using microframeworkMicropage in microtime using microframework
Micropage in microtime using microframework
 
Laravel 101
Laravel 101Laravel 101
Laravel 101
 
Thinking Functionally with JavaScript
Thinking Functionally with JavaScriptThinking Functionally with JavaScript
Thinking Functionally with JavaScript
 
PHP and Rich Internet Applications
PHP and Rich Internet ApplicationsPHP and Rich Internet Applications
PHP and Rich Internet Applications
 
Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4Can't Miss Features of PHP 5.3 and 5.4
Can't Miss Features of PHP 5.3 and 5.4
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
Why async and functional programming in PHP7 suck and how to get overr it?
Why async and functional programming in PHP7 suck and how to get overr it?Why async and functional programming in PHP7 suck and how to get overr it?
Why async and functional programming in PHP7 suck and how to get overr it?
 
Solid principles
Solid principlesSolid principles
Solid principles
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
 

Último

Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 

Último (20)

[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 AmsterdamDEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
DEV meet-up UiPath Document Understanding May 7 2024 Amsterdam
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ..."I see eyes in my soup": How Delivery Hero implemented the safety system for ...
"I see eyes in my soup": How Delivery Hero implemented the safety system for ...
 
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​Elevate Developer Efficiency & build GenAI Application with Amazon Q​
Elevate Developer Efficiency & build GenAI Application with Amazon Q​
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 
Vector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptxVector Search -An Introduction in Oracle Database 23ai.pptx
Vector Search -An Introduction in Oracle Database 23ai.pptx
 
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
Connector Corner: Accelerate revenue generation using UiPath API-centric busi...
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Six Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal OntologySix Myths about Ontologies: The Basics of Formal Ontology
Six Myths about Ontologies: The Basics of Formal Ontology
 
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
Web Form Automation for Bonterra Impact Management (fka Social Solutions Apri...
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 

Durian: a PHP 5.5 microframework with generator-style middleware

  • 1. Durian Building Singapore / Dave Cross / CC BY-NC-SA 2.0 DURIAN A PHP 5.5 microframework based on generator-style middleware http://durianphp.com
  • 2. BEFORE WE BEGIN… What the heck are generators ?
  • 3. GENERATORS • Introduced in PHP 5.5 (although HHVM had them earlier) • Generators are basically iterators with a simpler syntax • The mere presence of the yield keyword turns a closure into a generator constructor • Generators are forward-only (cannot be rewound) • You can send() values into generators • You can throw() exceptions into generators
  • 4. THE YIELD KEYWORD class MyIterator implements Iterator { private $values; ! public function __construct(array $values) { $this->values = $values; } public function current() { return current($this->values); } public function key() { return key($this->values); } public function next() { return next($this->values); } public function rewind() {} public function valid() { return null !== key($this->values); } }   $iterator = new MyIterator([1,2,3,4,5]);   while ($iterator->valid()) { echo $iterator->current(); $iterator->next(); } $callback = function (array $values) { foreach ($values as $value) { yield $value; } };   $generator = $callback([1,2,3,4,5]);   while ($generator->valid()) { echo $generator->current(); $generator->next(); }
  • 6. How do they handle middleware and routing ?
  • 7. EVENT LISTENERS $app->before(function (Request $request) use ($app) { $app['response_time'] = microtime(true); });   $app->get('/blog', function () use ($app) { return $app['blog_service']->getPosts()->toJson(); });   $app->after(function (Request $request, Response $response) use ($app) { $time = microtime(true) - $app['response_time']; $response->headers->set('X-Response-Time', $time); });
  • 8. THE DOWNSIDE • A decorator has to be split into two separate functions to wrap the main application • Data has to be passed between functions • Can be confusing to maintain
  • 9. HIERARCHICAL ROUTING $app->path('blog', function ($request) use ($app) { $time = microtime(true); $blog = BlogService::create()->initialise();   $app->path('posts', function () use ($app, $blog) { $posts = $blog->getAllPosts();   $app->get(function () use ($app, $posts) { return $app->template('posts/index', $posts->toJson()); }); });   $time = microtime(true) - $time; $this->response()->header('X-Response-Time', $time); });
  • 10. THE DOWNSIDE • Subsequent route and method declarations are now embedded inside a closure • Closure needs to be executed to proceed • Potentially incurring expensive initialisation or computations only to be discarded • Middleware code is still split across two locations
  • 11. “CALLBACK HELL” $app->path('a', function () use ($app) { $app->param('b', function ($b) use ($app) { $app->path('c', function () use ($b, $app) { $app->param('d', function ($d) use ($app) { $app->get(function () use ($d, $app) { $app->json(function () use ($app) { // ... }); }); }); }); }); });
  • 12. How about other languages ?
  • 13. KOA (NODEJS) var koa = require('koa'); var app = koa(); ! app.use(function *(next){ var start = new Date; yield next; var ms = new Date - start; console.log('%s %s - %s', this.method, this.url, ms); }); ! app.use(function *(){ this.body = 'Hello World'; }); ! app.listen(3000);
  • 14. MARTINI (GOLANG) package main import "github.com/codegangsta/martini" ! func main() { m := martini.Classic() ! m.Use(func(c martini.Context, log *log.Logger) { log.Println("before a request") c.Next() log.Println("after a request") }) ! m.Get("/", func() string { return "Hello world!" }) ! m.Run() }
  • 15. INTRODUCING DURIAN • Take advantage of PHP 5.4, 5.5 features • Unify interface across controllers and middleware • Avoid excessive nesting / callback hell • Use existing library components • None of this has anything to do with durians
  • 16. COMPONENTS • Application container: Pimple by @fabpot • Request/Response: Symfony2 HttpFoundation • Routing: FastRoute by @nikic • Symfony2 HttpKernelInterface (for stackphp compatibility)
  • 17. A DURIAN APPLICATION $app = new DurianApplication(); ! $app->route('/hello/{name}', function () { return 'Hello '.$this->param('name'); }); ! $app->run();
 • Nothing special there, basically the same syntax as every microframework ever
  • 18. HANDLERS • Simple wrapper around closures and generators • Handlers consist of the primary callback and an optional guard callback
 $responseHandler = $app->handler(function () { $time = microtime(true); yield; $time = microtime(true) - $time; $this->response()->headers->set('X-Response-Time', $time); }, function () use ($app) { return $app['debug']; });
  • 19. THE HANDLER STACK • Application::handle() iterates through a generator that produces Handlers to be invoked • Generators produced from handlers are placed into another stack to be revisited in reverse order • A Handler may produce a generator that produces more Handlers, which are fed back to the main generator • The route dispatcher is one such handler
  • 21. MODIFYING THE STACK $app['middleware.response_time'] = $app->handler(function () { $time = microtime(true); yield; $time = microtime(true) - $time; $this->response()->headers->set('X-Response-Time', $time); }, function () use ($app) { return $this->master() && $app['debug']; }); ! $app->handlers([ 'middleware.response_time', new DurianMiddlewareRouterMiddleware() ]); ! $app->after(new DurianMiddlewareResponseMiddleware()); ! $app->before(new DurianMiddlewareWhoopsMiddleware());
  • 22. ROUTE HANDLER • Apply the handler concept to route matching 
 $app->handler(function () { $this->response('Hello World!'); }, function () { $matcher = new RequestMatcher('^/$'); return $matcher->matches($this->request()); }); • Compare to 
 $app->route('/', function () { $this->response('Hello World!'); });
  • 23. ROUTE CHAINING $app['awesome_library'] = $app->share(function ($app) { return new MyAwesomeLibrary(); }); ! $app->route('/hello', function () use ($app) { $app['awesome_library']->performExpensiveOperation(); yield 'Hello '; $app['awesome_library']->performCleanUp(); })->route('/{name}', function () { return $this->last().$this->param('name'); })->get(function () { return ['method' => 'GET', 'message' => $this->last()]; })->post(function () { return ['method' => 'POST', 'message' => $this->last()]; });
  • 24. ROUTE DISPATCHING • This route definition: 
 $albums = $app->route('/albums', A)->get(B)->post(C); $albums->route('/{aid:[0-9]+}', D, E)->get(F)->put(G, H)->delete(I); • Gets turned into: 
 GET POST GET PUT DELETE /albums /albums /albums/{aid} /albums/{aid} /albums/{aid} => => => => => [A,B]" [A,C]" [A,D,E,F]" [A,D,E,G,H]" [A,D,E,I]
  • 25. • Route chaining isn’t mandatory ! • You can still use the regular syntax
 // Routes will support GET by default $app->route('/users'); ! // Methods can be declared without handlers $app->route('/users/{name}')->post(); ! // Declare multiple methods separated by pipe characters $app->route('/users/{name}/friends')->method('GET|POST');
  • 26. CONTEXT • Every handler is bound to the Context object using Closure::bind • A new context is created for every request or sub request Get the Request object $request = $this->request(); Get the Response $response = $this->response(); Set the Response $this->response("I'm a teapot", 418); Get the last handler output $last = $this->last(); Get a route parameter $id = $this->param('id'); Throw an error $this->error('Forbidden', 403);
  • 27. EXCEPTION HANDLING • Exceptions are caught and bubbled back up through all registered generators • Intercept them by wrapping the yield statement with a try/catch block
 $exceptionHandlerMiddleware = $app->handler(function () { try { yield; } catch (Exception $exception) { $this->response($exception->getMessage(), 500); } });
  • 28. AWESOME EXAMPLE Let’s add two integers together !
  • 29.
  • 30. $app->route('/add', function () use ($app) {
 
 $app['number_collection'] = $app->share(function ($app) { return new NumberCollection(); }); $app['number_parser'] = $app->share(function ($app) { return new SimpleNumberStringParser(); });" yield; $addition = new AdditionOperator('SimplePHPEasyPlusNumberSimpleNumber'); $operation = new ArithmeticOperation($addition); $engine = new Engine($operation); $calcul = new Calcul($engine, $app['number_collection']); $runner = new CalculRunner(); $runner->run($calcul); $result = $calcul->getResult(); $numericResult = $result->getValue(); $this->response('The answer is: ' . $numericResult);
 })->route('/{first:[0-9]+}', function () use ($app) {
 $firstParsedNumber = $app['number_parser']->parse($this->param('first')); $firstNumber = new SimpleNumber($firstParsedNumber); $firstNumberProxy = new CollectionItemNumberProxy($firstNumber); $app['number_collection']->add($firstNumberProxy);
 })->route('/{second:[0-9]+}', function () use ($app) {
 $secondParsedNumber = $app['number_parser']->parse($this->param('second')); $secondNumber = new SimpleNumber($secondParsedNumber); $secondNumberProxy = new CollectionItemNumberProxy($secondNumber); $app['number_collection']->add($secondNumberProxy);
 })->get();
  • 31. COMING SOON • Proper tests and coverage (!!!) • Handlers for format negotiation, session, locale, etc • Dependency injection through reflection (via trait) • Framework/engine-agnostic view composition and template rendering (separate project)