bullet: the functional php micro-framework

51
Bullet: The Functional PHP Micro-Framework Vance Lucas • Co-Founder, Brightbit http://bulletphp.com

Upload: vance-lucas

Post on 04-Jul-2015

24.775 views

Category:

Technology


4 download

TRANSCRIPT

Page 1: Bullet: The Functional PHP Micro-Framework

Bullet: The Functional PHP Micro-Framework

Vance Lucas • Co-Founder, Brightbit http://bulletphp.com

Page 2: Bullet: The Functional PHP Micro-Framework

Who are You?• Vance Lucas

• http://vancelucas.com

• @vlucas (for heckling)

• PHP since 1999 (PHP3)

• Brightbit

• http://brightbit.com

• Design, Development & Consulting for web apps, mobile apps and APIs

Page 3: Bullet: The Functional PHP Micro-Framework

History & Philosophy (Don't worry, it won't be as boring as school was)

Page 4: Bullet: The Functional PHP Micro-Framework

MVC Frameworks

• I’ve created LOTS of MVC frameworks.

• They all sucked.

• Except maybe one.

• Alloy Framework

• Released Feb. 2011

• But it’s dead to me now…

Page 5: Bullet: The Functional PHP Micro-Framework

“The same thing, only different”

Page 6: Bullet: The Functional PHP Micro-Framework

“Lightweight”

“Simple”“Flexible”

“Artisan”

“Modular”

“Fast”

Page 7: Bullet: The Functional PHP Micro-Framework

“The fool hath said in his heart, There is no better architectural pattern than MVC”

* may not be exact quote

Page 8: Bullet: The Functional PHP Micro-Framework

“I don't like MVC because that's not how the web works.

Symfony2 is an HTTP framework; it is a Request/Response

framework. That's the big deal.”

Fabien Potencier http://fabien.potencier.org/article/49/what-is-symfony2

October 25, 2011

Page 9: Bullet: The Functional PHP Micro-Framework

Philosophy• Do more with less (code)

• Low cognitive overhead/complexity

• Embrace HTTP

• Leverage raw PHP without introducing too many “framework concepts”

• Only PHP knowledge should be enough

• Shouldn’t have to “fight the framework”

• “Micro” != No Structure

Page 10: Bullet: The Functional PHP Micro-Framework

“PluginBroker”

“TemplateMapResolver”

“AggregateResolver”

“RouteNotFoundStrategy”

“TemplatePathStack”

“RouteStack”

“SharedEventManager”

“DefaultListenerAggregate”

Page 11: Bullet: The Functional PHP Micro-Framework
Page 12: Bullet: The Functional PHP Micro-Framework

What is Bullet?Well, it’s a Micro-framework for starters…

Page 13: Bullet: The Functional PHP Micro-Framework

Main Concepts

• Micro-framework

• URL Routing, Request, Response, Templates

• Built around HTTP and defined URIs

• Parses one URI segment at a time

• Declarative, functional-style nested routing

• Leverages closures for structure and scope

• Less repetitive code, cleaner routes

Page 14: Bullet: The Functional PHP Micro-Framework

Guiding Rules

• Only one path segment at a time, and only Closures can be used

• Response must be explicitly returned

• Path must be fully consumed (or error)

• Handlers for different behavior:

• Path, Param, Method, Format

• Method and format handlers only run when path has been fully consumed

Page 15: Bullet: The Functional PHP Micro-Framework

Show me some code! !

GET /posts/42

Page 16: Bullet: The Functional PHP Micro-Framework

// Bullet index file!define('BULLET_ROOT', dirname(__DIR__));!define('BULLET_APP_ROOT', BULLET_ROOT . '/app/');!define('BULLET_SRC_ROOT', BULLET_APP_ROOT . '/src/');! !// Composer Autoloader!$loader = require BULLET_ROOT . '/vendor/autoload.php';! !// Bullet App!$app = new Bullet\App(require BULLET_APP_ROOT . 'config.php');!$request = new Bullet\Request();! !// Common include!require BULLET_APP_ROOT . '/common.php';! !// Require all paths/routes!$routesDir = BULLET_APP_ROOT . '/routes/';!require $routesDir . 'index.php';!require $routesDir . 'posts.php';!require $routesDir . 'events.php';!require $routesDir . 'users.php';! !// Response!echo $app->run($request);

Page 17: Bullet: The Functional PHP Micro-Framework

Bullet Routing $app->path('posts', function($req) {! // Param! $this->param('int', function($req, $id) {! $post = Post::find($id);! check_user_acl_for($post);!! // Method! $this->get(function($req) use($post) {!! ! // Format!! ! ! ! ! ! $this->format('json', function() use($post) {!! ! ! ! ! ! ! ! return $post->toArray();!! ! ! ! ! ! });!! ! ! ! ! ! $this->format('html', function() use($post) {!! ! ! ! ! ! ! ! return $this->template('html', …);!! ! ! ! ! ! });! });! });!});

Page 18: Bullet: The Functional PHP Micro-Framework

Quick Code Comparison

Page 19: Bullet: The Functional PHP Micro-Framework

Typical Micro-Framework$app->get('/posts/:id', function($id) use($app) {! $post = Post::find($id);! check_user_acl_for($post);! ! if(is_json()) {! header("Content-Type: application/json");! echo json_encode($result);! exit;! }!! $app->render('posts/view', compact('post'));!});!

Page 20: Bullet: The Functional PHP Micro-Framework

Typical MVC Controller

class BlogController extends BaseController {! public function getView($slug)! {! // Get this blog post data! $post = $this->post->where('slug', '=', $slug)->first();!! // Check if the blog post exists! if (is_null($post)) {! return App::abort(404);! }!! // Show the page! return View::make('site/blog/view_post', compact('post', 'comments', 'canComment'));! }!}

Page 21: Bullet: The Functional PHP Micro-Framework

Bullet Closure Context $app->path('posts', function($req) {! $this->param('int', function($req, $id) {! $post = Post::find($id);! check_user_acl_for($post);!! // View (GET)! $this->get(function($req) use($post) {! // ...! });!! // Delete! $this->delete(function($req) use($post) {! $post->delete();! // ...! });! });!});

Page 22: Bullet: The Functional PHP Micro-Framework

Bullet Route Handlers

Page 23: Bullet: The Functional PHP Micro-Framework

Path Handlers

$app->resource('posts', function($request) {! // ...!});!!$app->path('posts', function($request) {! // ...!});!!$app->path(['posts', 'articles'], function($req) {! // ...!});

Page 24: Bullet: The Functional PHP Micro-Framework

Path Handlers

• Return 404 File Not Found if request path not found

• Can be nested as deep as you want

• /admin/articles/3/comments

Page 25: Bullet: The Functional PHP Micro-Framework

Param Handlers$app->param('int', function($request, $id) {! // ...!});!!$app->param('slug', function($request, $slug) {! // ...!});!!// CUSTOM alphanumeric handler (returns boolean)!$app->registerParamType('alphanum',function($value) {! return ctype_alnum($value);!});!$app->param('alphanum', function($request, $alnum) {! // ...!});

Page 26: Bullet: The Functional PHP Micro-Framework

Param Handlers

• Test function

• true or scalar value executes route

• false skips route

• Value passed in as extra parameter to handler closure

Page 27: Bullet: The Functional PHP Micro-Framework

Method Handlers$app->resource('articles', function($request) {! $this->get(function($request) {! // ...! });!! $this->post(function($request) {! // ...! });!! $this->delete(function($request) {! // ...! });!});

Page 28: Bullet: The Functional PHP Micro-Framework

Method Handlers

• Return 405 Method Not Allowed if request method not found

Page 29: Bullet: The Functional PHP Micro-Framework

Format Handlers

$app->resource('articles', function($request) {! $this->get(function($request) {! $this->format(‘json', function($request) {! // ...! });! $this->format(‘html', function($request) {! // ...! });! });!});

Page 30: Bullet: The Functional PHP Micro-Framework

Format Handlers

• Return 406 Not Acceptable if request format not found

Page 31: Bullet: The Functional PHP Micro-Framework

Other Handlers

$app->domain(‘vancelucas.com', function($request) {! // ...!});!!$app->subdomain(‘api', function($request) {! // ...!});

Page 32: Bullet: The Functional PHP Micro-Framework

Return Types

• String (“hello world”)

• Integer (201 - Sends HTTP status code)

• Boolean False (404 error)

• Array (auto json_encode + headers)

• Bullet\Response instance

• Custom obj. (w/custom response handler)

Page 33: Bullet: The Functional PHP Micro-Framework

Building the URL you want should be easy

Page 34: Bullet: The Functional PHP Micro-Framework

$app->path('admin', function($req) use($app) {! some_acl_check__that_throws_exception_if_not();!! require 'posts.php'; // For /admin/posts ...! require 'events.php'; // For /admin/events ...! require 'comments.php'; // For /admin/comments ...!});

Page 35: Bullet: The Functional PHP Micro-Framework

// RELATIVE url!// /posts/25/comments/57,!// /events/9/comments/57,!// /comments/57!echo $app->url('./comments/' . $comment->id);!!// ROOT url (always /comments/57)!echo $app->url('/comments/' . $comment->id);!

…And Links Too

Page 36: Bullet: The Functional PHP Micro-Framework

Recommended Setup

http://bulletphp.com/docs/organization/

Page 37: Bullet: The Functional PHP Micro-Framework

Events

• Global: ‘before’, and ‘after’

• Dynamic

• [http_status_code] - 404, 500, etc.

• [response_format] - json, html, etc.

• [exception_class] - exception class name like “InvalidArgumentException” or just “Exception” to catch all exceptions

Page 38: Bullet: The Functional PHP Micro-Framework

HTTP Error Handling

$app->on(404, function($req, $res){! $response->content($app->template('errors/404'));!});!

Page 39: Bullet: The Functional PHP Micro-Framework

Exception Handling$app->on('Exception', function($req, $res, \Exception $e) {! if($req->format() === 'json') {! $data = array(! 'exception' => get_class($e),! 'message' => $e->getMessage()! );! if(BULLET_ENV !== 'production') {! $data['file'] = $e->getFile();! $data['line'] = $e->getLine();! $data['trace'] = $e->getTrace();! }!! } else {! $data = $app->template('errors/exception', ['e' => $e]);! }! $res->content($data);!});!

Page 40: Bullet: The Functional PHP Micro-Framework

Nested Sub Requests

$app = new Bullet\App();!$app->path('foo', function($request) {! return "foo";!});!$app->path('bar', function($request) {! $res = $this->run('GET', '/foo'); // `Bullet\Response`! return $res->content() . 'bar';!});!echo $app->run('GET', 'bar'); // output => 'foobar'!

Page 41: Bullet: The Functional PHP Micro-Framework

Getting Started

http://bulletphp.com !

https://github.com/vlucas/bulletphp !

Skeleton App (basic setup / starting point) https://github.com/vlucas/bulletphp-skeleton

!Obligatory blog example

https://github.com/vlucas/bulletphp-blog-example

Page 42: Bullet: The Functional PHP Micro-Framework

MVC Framework Anti-Patterns

Some more controversial than others

Page 43: Bullet: The Functional PHP Micro-Framework

“REST Controller” vs

“Base Controller”

Page 44: Bullet: The Functional PHP Micro-Framework

Can’t use basic PHP knowledge to change

the flow of your application

Page 45: Bullet: The Functional PHP Micro-Framework

!$this->forward('someOtherAction' . $params);!

Page 46: Bullet: The Functional PHP Micro-Framework

$this->beforeFilter('auth', array(! 'except' => 'getLogin'!));!

Page 47: Bullet: The Functional PHP Micro-Framework

/:controller/:action/:id

Page 48: Bullet: The Functional PHP Micro-Framework

Symfony/Component/HttpFoundation/Response.php

HttpFoundation Component Docs

$response->setStatusCode(Response::HTTP_NOT_FOUND);!!class Response {! // ...! const HTTP_CONTINUE = 100;! const HTTP_SWITCHING_PROTOCOLS = 101;! const HTTP_PROCESSING = 102;! const HTTP_OK = 200;! const HTTP_CREATED = 201;! const HTTP_ACCEPTED = 202;! const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;! const HTTP_NO_CONTENT = 204;! // ...!}!

Page 49: Bullet: The Functional PHP Micro-Framework

Zend Framework 2 - Zend/Http/Response.php

class Response {! // ...! const STATUS_CODE_CUSTOM = 0;! const STATUS_CODE_100 = 100;! const STATUS_CODE_101 = 101;! const STATUS_CODE_102 = 102;! const STATUS_CODE_200 = 200;! const STATUS_CODE_201 = 201;! const STATUS_CODE_202 = 202;! const STATUS_CODE_203 = 203;! const STATUS_CODE_204 = 204;! // ...!}!

Page 50: Bullet: The Functional PHP Micro-Framework

Classes for Controllers

Page 51: Bullet: The Functional PHP Micro-Framework

Questions?@vlucas | [email protected]

!!

Rate this talk! https://joind.in/10434