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

Contenu connexe

Tendances

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
 

Tendances (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
 

Similaire à 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
 

Similaire à 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)
 

Dernier

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Dernier (20)

Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 
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...
 
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
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
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
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
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 

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)