SlideShare a Scribd company logo
Keeping it small
Getting to know the Slim micro framework
php[tek] 2013
Wednesday, May 15, 13
Wednesday, May 15, 13
I love to code
Wednesday, May 15, 13
I love to code
I’m terribly forgetful
Wednesday, May 15, 13
I love to code
I’m terribly forgetful
I take pictures
Wednesday, May 15, 13
I love to code
I’m terribly forgetful
I take pictures
I work at OpenSky
Wednesday, May 15, 13
Micro framework?
Wednesday, May 15, 13
MicroPHP Manifesto
Wednesday, May 15, 13
MicroPHP Manifesto
Written by Ed Finkler
Wednesday, May 15, 13
MicroPHP Manifesto
Written by Ed Finkler
Punk rock vs. Prog rock
Wednesday, May 15, 13
MicroPHP Manifesto
Written by Ed Finkler
Punk rock vs. Prog rock
Prophet or Madman?
Wednesday, May 15, 13
MicroPHP Manifesto
Written by Ed Finkler
Punk rock vs. Prog rock
Prophet or Madman?
Guiding principle
Wednesday, May 15, 13
MicroPHP Manifesto
Written by Ed Finkler
Punk rock vs. Prog rock
Prophet or Madman?
Guiding principle
http://microphp.org/
Wednesday, May 15, 13
Micro framework?
Wednesday, May 15, 13
Micro framework?
Concise codebase
Wednesday, May 15, 13
Micro framework?
Concise codebase
Clear codebase
Wednesday, May 15, 13
Micro framework?
Concise codebase
Clear codebase
Addresses a small set of use cases
Wednesday, May 15, 13
Micro framework?
Concise codebase
Clear codebase
Addresses a small set of use cases
Addresses those use cases well
Wednesday, May 15, 13
I chose Slim PHP
Wednesday, May 15, 13
I chose Slim PHP
and I sucked at it
Wednesday, May 15, 13
What is Slim?
Wednesday, May 15, 13
What is Slim?
Inspired by Sinatra
Wednesday, May 15, 13
What is Slim?
Inspired by Sinatra
Favors cleanliness over terseness
Wednesday, May 15, 13
What is Slim?
Inspired by Sinatra
Favors cleanliness over terseness
Favors common cases over edge cases
Wednesday, May 15, 13
Installing Slim
Wednesday, May 15, 13
Install Composer
curl -s https://getcomposer.org/installer | php
Wednesday, May 15, 13
Install Composer
curl -s https://getcomposer.org/installer | php
If you take nothing else away from this talk, I hope that
it’s Composer. It’s that big of a deal.
Wednesday, May 15, 13
composer.json
{
"require": {
"slim/slim": "2.*"
}
}
Wednesday, May 15, 13
Install via Composer
php composer.phar install
Wednesday, May 15, 13
Add this to index.php
<?php
require 'vendor/autoload.php';
Wednesday, May 15, 13
Pro tip
mv composer.phar /usr/local/bin/composer
Wednesday, May 15, 13
Don’t forget .htaccess!
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
http://docs.slimframework.com/#Route-URL-Rewriting
(nginx documentation available at same URL)
Wednesday, May 15, 13
Hello $name
<?php
require '../vendor/autoload.php';
$app = new SlimSlim();
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
Wednesday, May 15, 13
Hello $name
<?php
require '../vendor/autoload.php';
$app = new SlimSlim();
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
Wednesday, May 15, 13
Hello $name
<?php
require '../vendor/autoload.php';
$app = new SlimSlim();
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
Wednesday, May 15, 13
Hello $name
<?php
require '../vendor/autoload.php';
$app = new SlimSlim();
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
Wednesday, May 15, 13
Hello $name
<?php
require '../vendor/autoload.php';
$app = new SlimSlim();
$app->get('/hello/:name', function ($name) {
echo "Hello, $name";
});
$app->run();
Wednesday, May 15, 13
Getting there
http://localhost/index.php/hello/tek
OR
http://localhost/hello/tek
Wednesday, May 15, 13
Slim in practice
Wednesday, May 15, 13
Flaming Archer!
Wednesday, May 15, 13
“Great repository names are short and memorable.
Need inspiration? How about flaming-archer.”
Wednesday, May 15, 13
Flaming Archer
Wednesday, May 15, 13
Flaming Archer
Photo 365 project
Wednesday, May 15, 13
Flaming Archer
Photo 365 project
Bulk of app built in 4 days
Wednesday, May 15, 13
Flaming Archer
Photo 365 project
Bulk of app built in 4 days
Basic application — a few bells, no whistles
Wednesday, May 15, 13
Flaming Archer
Photo 365 project
Bulk of app built in 4 days
Basic application — a few bells, no whistles
Routing
Wednesday, May 15, 13
Flaming Archer
Photo 365 project
Bulk of app built in 4 days
Basic application — a few bells, no whistles
Routing
Twig templates
Wednesday, May 15, 13
Flaming Archer
Photo 365 project
Bulk of app built in 4 days
Basic application — a few bells, no whistles
Routing
Twig templates
Middleware
Wednesday, May 15, 13
Flaming Archer
Photo 365 project
Bulk of app built in 4 days
Basic application — a few bells, no whistles
Routing
Twig templates
Middleware
Hooks
Wednesday, May 15, 13
4 views
Wednesday, May 15, 13
Wednesday, May 15, 13
Wednesday, May 15, 13
Wednesday, May 15, 13
Wednesday, May 15, 13
phploc --exclude vendor --exclude tests --exclude templates .
phploc 1.7.4 by Sebastian Bergmann.
Directories: 8
Files: 16
Lines of Code (LOC): 1212
Cyclomatic Complexity / Lines of Code: 0.03
Comment Lines of Code (CLOC): 453
Non-Comment Lines of Code (NCLOC): 759
Wednesday, May 15, 13
Configuration
Wednesday, May 15, 13
return array(
'slim' => array(
'templates.path' => __DIR__ . '/templates',
'log.level' => 4,
'log.enabled' => true,
'log.writer' => new SlimExtrasLogDateTimeFileWriter(
array(
'path' => __DIR__ . '/logs',
'name_format' => 'y-m-d'
)
)
),
'twig' => array(
// . . .
),
'cookies' => array(
// . . .
),
'flickr.api.key' => 'FLICKR API KEY',
'pdo' => array(
// . . .
)
);
Wednesday, May 15, 13
return array(
'slim' => array(
'templates.path' => __DIR__ . '/templates',
'log.level' => 4,
'log.enabled' => true,
'log.writer' => new SlimExtrasLogDateTimeFileWriter(
array(
'path' => __DIR__ . '/logs',
'name_format' => 'y-m-d'
)
)
),
'twig' => array(
// . . .
),
'cookies' => array(
// . . .
),
'flickr.api.key' => 'FLICKR API KEY',
'pdo' => array(
// . . .
)
);
Slim
Wednesday, May 15, 13
return array(
'slim' => array(
'templates.path' => __DIR__ . '/templates',
'log.level' => 4,
'log.enabled' => true,
'log.writer' => new SlimExtrasLogDateTimeFileWriter(
array(
'path' => __DIR__ . '/logs',
'name_format' => 'y-m-d'
)
)
),
'twig' => array(
// . . .
),
'cookies' => array(
// . . .
),
'flickr.api.key' => 'FLICKR API KEY',
'pdo' => array(
// . . .
)
);
Slim
Views
Wednesday, May 15, 13
return array(
'slim' => array(
'templates.path' => __DIR__ . '/templates',
'log.level' => 4,
'log.enabled' => true,
'log.writer' => new SlimExtrasLogDateTimeFileWriter(
array(
'path' => __DIR__ . '/logs',
'name_format' => 'y-m-d'
)
)
),
'twig' => array(
// . . .
),
'cookies' => array(
// . . .
),
'flickr.api.key' => 'FLICKR API KEY',
'pdo' => array(
// . . .
)
);
Slim
Views
Cookies
Wednesday, May 15, 13
return array(
'slim' => array(
'templates.path' => __DIR__ . '/templates',
'log.level' => 4,
'log.enabled' => true,
'log.writer' => new SlimExtrasLogDateTimeFileWriter(
array(
'path' => __DIR__ . '/logs',
'name_format' => 'y-m-d'
)
)
),
'twig' => array(
// . . .
),
'cookies' => array(
// . . .
),
'flickr.api.key' => 'FLICKR API KEY',
'pdo' => array(
// . . .
)
);
Slim
Views
Cookies
App specific
Wednesday, May 15, 13
$config = require_once __DIR__ . '/../config.php';
// Prepare app
$app = new SlimSlim($config['slim']);
Configuration
Wednesday, May 15, 13
$config = require_once __DIR__ . '/../config.php';
// Prepare app
$app = new SlimSlim($config['slim']);
Config array
goes here
Configuration
Wednesday, May 15, 13
Routing
Wednesday, May 15, 13
Routing
$app->get('/', function () use ($app, $service) {
$images = $service->findAll();
$app->render('index.html', array('images' => $images));
}
);
Wednesday, May 15, 13
Routing
$app->get('/', function () use ($app, $service) {
$images = $service->findAll();
$app->render('index.html', array('images' => $images));
}
);
HTTP Method
Wednesday, May 15, 13
Routing
$app->get('/', function () use ($app, $service) {
$images = $service->findAll();
$app->render('index.html', array('images' => $images));
}
);
HTTP Method
Resource URI
Wednesday, May 15, 13
Routing
$app->get('/', function () use ($app, $service) {
$images = $service->findAll();
$app->render('index.html', array('images' => $images));
}
);
HTTP Method
Resource URI
Anonymous Function
Wednesday, May 15, 13
Routing
$app->get('/', function () use ($app, $service) {
$images = $service->findAll();
$app->render('index.html', array('images' => $images));
}
);
Wednesday, May 15, 13
Routing
$app->get('/', function () use ($app, $service) {
$images = $service->findAll();
$app->render('index.html', array('images' => $images));
}
);
Grabs all the pics
Wednesday, May 15, 13
Routing
$app->get('/', function () use ($app, $service) {
$images = $service->findAll();
$app->render('index.html', array('images' => $images));
}
);
Grabs all the pics
Passes array of image data to index.html
Wednesday, May 15, 13
GET
$app->get('/:day', function($day) use ($app, $service) {
$image = $service->find($day);
if (!$image) {
$app->notFound();
}
$app->render('images.html', $image);
}
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
Wednesday, May 15, 13
GET
$app->get('/:day', function($day) use ($app, $service) {
$image = $service->find($day);
if (!$image) {
$app->notFound();
}
$app->render('images.html', $image);
}
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
URL parameter
Wednesday, May 15, 13
GET
$app->get('/:day', function($day) use ($app, $service) {
$image = $service->find($day);
if (!$image) {
$app->notFound();
}
$app->render('images.html', $image);
}
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
URL parameter
... gets passed as an
argument
Wednesday, May 15, 13
GET
$app->get('/:day', function($day) use ($app, $service) {
$image = $service->find($day);
if (!$image) {
$app->notFound();
}
$app->render('images.html', $image);
}
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
URL parameter
... gets passed as an
argument
Condition
Wednesday, May 15, 13
GET
$app->get('/:day', function($day) use ($app, $service) {
$image = $service->find($day);
if (!$image) {
$app->notFound();
}
$app->render('images.html', $image);
}
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
URL parameter
... gets passed as an
argument
Condition 1 to 366
Wednesday, May 15, 13
GET
$app->get('/:day', function($day) use ($app, $service) {
$image = $service->find($day);
if (!$image) {
$app->notFound();
}
$app->render('images.html', $image);
}
)->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])'));
404!
Wednesday, May 15, 13
POST (with redirect)
$app->post('/admin/add-photo', function() use ($app, $service) {
$data = $app->request()->post();
$service->save($data);
$app->redirect('/admin');
}
);
Wednesday, May 15, 13
POST (with redirect)
$app->post('/admin/add-photo', function() use ($app, $service) {
$data = $app->request()->post();
$service->save($data);
$app->redirect('/admin');
}
);
$_POST data is in
the request object
Wednesday, May 15, 13
POST (with redirect)
$app->post('/admin/add-photo', function() use ($app, $service) {
$data = $app->request()->post();
$service->save($data);
$app->redirect('/admin');
}
);
$_POST data is in
the request object
302 Redirect
Wednesday, May 15, 13
Multiple methods
$app->map('/login', function() {
// Login
}
)->via('GET', 'POST');
Wednesday, May 15, 13
Multiple methods
$app->map('/login', function() {
// Login
}
)->via('GET', 'POST');
Not an HTTP Method
Wednesday, May 15, 13
Multiple methods
$app->map('/login', function() {
// Login
}
)->via('GET', 'POST');
Not an HTTP Method
via() is the
awesome sauce
Wednesday, May 15, 13
Logging and flash messaging
Wednesday, May 15, 13
$app->post('/admin/clear-cache', function() use ($app) {
$log = $app->getLog();
$cleared = null;
$clear = $app->request()->post('clear');
if ($clear == 1) {
if (apc_clear_cache('user')) {
$cleared = 'Cache was successfully cleared!';
} else {
$cleared = 'Cache was not cleared!';
$log->error('Cache not cleared');
}
}
$app->flash('cleared', $cleared);
$app->redirect('/admin');
}
);
Wednesday, May 15, 13
$app->post('/admin/clear-cache', function() use ($app) {
$log = $app->getLog();
$cleared = null;
$clear = $app->request()->post('clear');
if ($clear == 1) {
if (apc_clear_cache('user')) {
$cleared = 'Cache was successfully cleared!';
} else {
$cleared = 'Cache was not cleared!';
$log->error('Cache not cleared');
}
}
$app->flash('cleared', $cleared);
$app->redirect('/admin');
}
);
Get the log from $app
Wednesday, May 15, 13
$app->post('/admin/clear-cache', function() use ($app) {
$log = $app->getLog();
$cleared = null;
$clear = $app->request()->post('clear');
if ($clear == 1) {
if (apc_clear_cache('user')) {
$cleared = 'Cache was successfully cleared!';
} else {
$cleared = 'Cache was not cleared!';
$log->error('Cache not cleared');
}
}
$app->flash('cleared', $cleared);
$app->redirect('/admin');
}
);
Get the log from $app
Error!
Wednesday, May 15, 13
$app->post('/admin/clear-cache', function() use ($app) {
$log = $app->getLog();
$cleared = null;
$clear = $app->request()->post('clear');
if ($clear == 1) {
if (apc_clear_cache('user')) {
$cleared = 'Cache was successfully cleared!';
} else {
$cleared = 'Cache was not cleared!';
$log->error('Cache not cleared');
}
}
$app->flash('cleared', $cleared);
$app->redirect('/admin');
}
);
Get the log from $app
Error!
Flash message available in
the next request.
Wednesday, May 15, 13
Middleware
“. . . a Slim application can have middleware
that may inspect, analyze, or modify
the application environment, request, and
response before and/or after the Slim
application is invoked.”
http://docs.slimframework.com/#Middleware-Overview
Wednesday, May 15, 13
Middleware
Wednesday, May 15, 13
Middleware
Like layers of an onion
Wednesday, May 15, 13
Middleware
Like layers of an onion
The Slim application is the core of the onion
Wednesday, May 15, 13
Middleware
Like layers of an onion
The Slim application is the core of the onion
Each new middleware wraps any existing middleware
Wednesday, May 15, 13
Middleware
Like layers of an onion
The Slim application is the core of the onion
Each new middleware wraps any existing middleware
Each middleware optionally calls the next middleware in
the chain
Wednesday, May 15, 13
Middleware
Like layers of an onion
The Slim application is the core of the onion
Each new middleware wraps any existing middleware
Each middleware optionally calls the next middleware in
the chain
Middleware is executed from the outside in
Wednesday, May 15, 13
Middleware
Like layers of an onion
The Slim application is the core of the onion
Each new middleware wraps any existing middleware
Each middleware optionally calls the next middleware in
the chain
Middleware is executed from the outside in
Pay careful attention to the order in which you register
middleware
Wednesday, May 15, 13
Middleware
// Prepare app
$app = new SlimSlim($config['slim']);
// . . .
$app->add(new Navigation($auth));
$app->add(new Authentication($auth, $config));
Wednesday, May 15, 13
Middleware
// Prepare app
$app = new SlimSlim($config['slim']);
// . . .
$app->add(new Navigation($auth));
$app->add(new Authentication($auth, $config));
Wednesday, May 15, 13
Middleware
// Prepare app
$app = new SlimSlim($config['slim']);
// . . .
$app->add(new Navigation($auth));
$app->add(new Authentication($auth, $config));
Wednesday, May 15, 13
Existing Middleware
Wednesday, May 15, 13
Existing Middleware
Flash messaging
Wednesday, May 15, 13
Existing Middleware
Flash messaging
Content types
Wednesday, May 15, 13
Existing Middleware
Flash messaging
Content types
Pretty exceptions
Wednesday, May 15, 13
Existing Middleware
Flash messaging
Content types
Pretty exceptions
Session cookie
Wednesday, May 15, 13
Existing Middleware
Flash messaging
Content types
Pretty exceptions
Session cookie
More in Slim Extras
Wednesday, May 15, 13
Roll your own Middleware
Wednesday, May 15, 13
Roll your own Middleware
Super easy
Wednesday, May 15, 13
Roll your own Middleware
Super easy
DRYs up code
Wednesday, May 15, 13
Roll your own Middleware
Super easy
DRYs up code
Awesome application wide functionality
Wednesday, May 15, 13
Roll your own Middleware
Super easy
DRYs up code
Awesome application wide functionality
Just extend SlimMiddleware and implement call()
Wednesday, May 15, 13
class MyMiddleware extends SlimMiddleware
{
public function call()
{
//The Slim application
$app = $this->app;
//The Environment object
$env = $app->environment();
//The Request object
$req = $app->request();
//The Response object
$res = $app->response();
//Optionally call the next middleware
$this->next->call();
}
}
Wednesday, May 15, 13
class MyMiddleware extends SlimMiddleware
{
public function call()
{
//The Slim application
$app = $this->app;
//The Environment object
$env = $app->environment();
//The Request object
$req = $app->request();
//The Response object
$res = $app->response();
//Optionally call the next middleware
$this->next->call();
}
}
Extend this
Wednesday, May 15, 13
class MyMiddleware extends SlimMiddleware
{
public function call()
{
//The Slim application
$app = $this->app;
//The Environment object
$env = $app->environment();
//The Request object
$req = $app->request();
//The Response object
$res = $app->response();
//Optionally call the next middleware
$this->next->call();
}
}
Extend this
Define call()
Wednesday, May 15, 13
class MyMiddleware extends SlimMiddleware
{
public function call()
{
//The Slim application
$app = $this->app;
//The Environment object
$env = $app->environment();
//The Request object
$req = $app->request();
//The Response object
$res = $app->response();
//Optionally call the next middleware
$this->next->call();
}
}
Extend this
Define call()
Inspect, analyze,
and modify!
Wednesday, May 15, 13
class MyMiddleware extends SlimMiddleware
{
public function call()
{
//The Slim application
$app = $this->app;
//The Environment object
$env = $app->environment();
//The Request object
$req = $app->request();
//The Response object
$res = $app->response();
//Optionally call the next middleware
$this->next->call();
}
}
Extend this
Define call()
On to the next!
Inspect, analyze,
and modify!
Wednesday, May 15, 13
Navigation example
Wednesday, May 15, 13
namespace FaMiddleware;
use ZendAuthenticationAuthenticationService;
class Navigation extends SlimMiddleware
{
/** @var AuthenticationService */
private $auth;
public function __construct(AuthenticationService $auth)
{
$this->auth = $auth;
}
public function call()
{
// . . .
}
}
Wednesday, May 15, 13
namespace FaMiddleware;
use ZendAuthenticationAuthenticationService;
class Navigation extends SlimMiddleware
{
/** @var AuthenticationService */
private $auth;
public function __construct(AuthenticationService $auth)
{
$this->auth = $auth;
}
public function call()
{
// . . .
}
}
extends
Wednesday, May 15, 13
namespace FaMiddleware;
use ZendAuthenticationAuthenticationService;
class Navigation extends SlimMiddleware
{
/** @var AuthenticationService */
private $auth;
public function __construct(AuthenticationService $auth)
{
$this->auth = $auth;
}
public function call()
{
// . . .
}
}
Constructor injection
FTW
extends
Wednesday, May 15, 13
public function call()
{
$app = $this->app;
$auth = $this->auth;
$req = $app->request();
$home = array('caption' => 'Home', 'href' => '/');
$admin = array('caption' => 'Admin', 'href' => '/admin');
$login = array('caption' => 'Login', 'href' => '/login');
$logout = array('caption' => 'Logout', 'href' => '/logout');
if ($auth->hasIdentity()) {
$navigation = array($home, $admin, $logout);
} else {
$navigation = array($home, $login);
}
// . . .
}
Wednesday, May 15, 13
public function call()
{
$app = $this->app;
$auth = $this->auth;
$req = $app->request();
$home = array('caption' => 'Home', 'href' => '/');
$admin = array('caption' => 'Admin', 'href' => '/admin');
$login = array('caption' => 'Login', 'href' => '/login');
$logout = array('caption' => 'Logout', 'href' => '/logout');
if ($auth->hasIdentity()) {
$navigation = array($home, $admin, $logout);
} else {
$navigation = array($home, $login);
}
// . . .
}
Arrays of
nav items
Wednesday, May 15, 13
public function call()
{
$app = $this->app;
$auth = $this->auth;
$req = $app->request();
$home = array('caption' => 'Home', 'href' => '/');
$admin = array('caption' => 'Admin', 'href' => '/admin');
$login = array('caption' => 'Login', 'href' => '/login');
$logout = array('caption' => 'Logout', 'href' => '/logout');
if ($auth->hasIdentity()) {
$navigation = array($home, $admin, $logout);
} else {
$navigation = array($home, $login);
}
// . . .
}
Arrays of
nav items
Nav differs based
on auth status
Wednesday, May 15, 13
public function call()
{
// . . .
foreach ($navigation as &$link) {
if ($link['href'] == $req->getPath()) {
$link['class'] = 'active';
} else {
$link['class'] = '';
}
}
$app->view()->appendData(array('navigation' => $navigation));
$this->next->call();
}
Wednesday, May 15, 13
public function call()
{
// . . .
foreach ($navigation as &$link) {
if ($link['href'] == $req->getPath()) {
$link['class'] = 'active';
} else {
$link['class'] = '';
}
}
$app->view()->appendData(array('navigation' => $navigation));
$this->next->call();
}
Match
dispatched path
Wednesday, May 15, 13
public function call()
{
// . . .
foreach ($navigation as &$link) {
if ($link['href'] == $req->getPath()) {
$link['class'] = 'active';
} else {
$link['class'] = '';
}
}
$app->view()->appendData(array('navigation' => $navigation));
$this->next->call();
}
Match
dispatched path
Append
$navigation to
view
Wednesday, May 15, 13
public function call()
{
// . . .
foreach ($navigation as &$link) {
if ($link['href'] == $req->getPath()) {
$link['class'] = 'active';
} else {
$link['class'] = '';
}
}
$app->view()->appendData(array('navigation' => $navigation));
$this->next->call();
}
Match
dispatched path
Append
$navigation to
view
On to the next!
Wednesday, May 15, 13
Route Middleware
Wednesday, May 15, 13
Route Middleware
Anything that returns true for is_callable()
Wednesday, May 15, 13
Route Middleware
Anything that returns true for is_callable()
Apply directly to route!
Wednesday, May 15, 13
Route Middleware
Anything that returns true for is_callable()
Apply directly to route!
Goes between route definition and route callable
Wednesday, May 15, 13
Route Middleware
<?php
function mw1() {
echo "This is middleware!";
}
function mw2() {
echo "This is middleware!";
}
$app = new SlimSlim();
$app->get('/foo', 'mw1', 'mw2', function () {
//Do something
});
http://docs.slimframework.com/#Route-Middleware
Wednesday, May 15, 13
Route Middleware
<?php
function mw1() {
echo "This is middleware!";
}
function mw2() {
echo "This is middleware!";
}
$app = new SlimSlim();
$app->get('/foo', 'mw1', 'mw2', function () {
//Do something
});
http://docs.slimframework.com/#Route-Middleware
Wednesday, May 15, 13
Hooks
A “hook” is a moment in the Slim application lifecycle
at which a priority list of callables assigned to the
hook will be invoked.A hook is identified by a string
name.
http://docs.slimframework.com/#Hooks-Overview
Wednesday, May 15, 13
Hooks
Wednesday, May 15, 13
Hooks
Anything that returns true for is_callable()
Wednesday, May 15, 13
Hooks
Anything that returns true for is_callable()
Prioritized
Wednesday, May 15, 13
Hooks
Anything that returns true for is_callable()
Prioritized
Six default hooks
Wednesday, May 15, 13
Hooks
Wednesday, May 15, 13
slim.before
Hooks
Wednesday, May 15, 13
slim.before
slim.before.router
Hooks
Wednesday, May 15, 13
slim.before
slim.before.router
slim.before.dispatch
Hooks
Wednesday, May 15, 13
slim.before
slim.before.router
slim.before.dispatch
slim.after.dispatch
Hooks
Wednesday, May 15, 13
slim.before
slim.before.router
slim.before.dispatch
slim.after.dispatch
slim.after.router
Hooks
Wednesday, May 15, 13
slim.before
slim.before.router
slim.before.dispatch
slim.after.dispatch
slim.after.router
slim.after
Hooks
Wednesday, May 15, 13
slim.before
slim.before.router
slim.before.dispatch
slim.after.dispatch
slim.after.router
slim.after
Hooks
Wednesday, May 15, 13
Hooks
$app = new SlimSlim();
$app->hook('the.hook.name', function () {
//Do something
}, 5);
Wednesday, May 15, 13
Hooks
$app = new SlimSlim();
$app->hook('the.hook.name', function () {
//Do something
}, 5);
Optional Priority
Wednesday, May 15, 13
Middleware + Hooks = WIN
Wednesday, May 15, 13
Middleware + Hooks
Using middleware to register hooks allows
me to inspect, analyze, or modify my
application at a specific time in the
application’s lifecycle in a DRY, testable, and
reusable manner.
Wednesday, May 15, 13
Authentication Example
Wednesday, May 15, 13
public function call()
{
$app = $this->app;
$req = $app->request();
$auth = $this->auth;
$config = $this->config;
// $checkAuth anonymous function snipped
$this->app->hook('slim.before.router', $checkAuth);
$this->next->call();
}
Wednesday, May 15, 13
public function call()
{
$app = $this->app;
$req = $app->request();
$auth = $this->auth;
$config = $this->config;
// $checkAuth anonymous function snipped
$this->app->hook('slim.before.router', $checkAuth);
$this->next->call();
}
Grab stuff to
inspect
Wednesday, May 15, 13
public function call()
{
$app = $this->app;
$req = $app->request();
$auth = $this->auth;
$config = $this->config;
// $checkAuth anonymous function snipped
$this->app->hook('slim.before.router', $checkAuth);
$this->next->call();
}
Grab stuff to
inspect
(via constructor)
Wednesday, May 15, 13
public function call()
{
$app = $this->app;
$req = $app->request();
$auth = $this->auth;
$config = $this->config;
// $checkAuth anonymous function snipped
$this->app->hook('slim.before.router', $checkAuth);
$this->next->call();
}
Grab stuff to
inspect
Register hook
(via constructor)
Wednesday, May 15, 13
// Snip looping through secured url array
if (preg_match($urlPattern, $req->getPathInfo())
&& !$auth->hasIdentity()) {
if ($req->getPath() !== $config['login.url']) {
$app->redirect($config['login.url']);
}
}
Wednesday, May 15, 13
// Snip looping through secured url array
if (preg_match($urlPattern, $req->getPathInfo())
&& !$auth->hasIdentity()) {
if ($req->getPath() !== $config['login.url']) {
$app->redirect($config['login.url']);
}
}
Match path to
secured url
Wednesday, May 15, 13
// Snip looping through secured url array
if (preg_match($urlPattern, $req->getPathInfo())
&& !$auth->hasIdentity()) {
if ($req->getPath() !== $config['login.url']) {
$app->redirect($config['login.url']);
}
}
Match path to
secured url
Logged in?
Wednesday, May 15, 13
// Snip looping through secured url array
if (preg_match($urlPattern, $req->getPathInfo())
&& !$auth->hasIdentity()) {
if ($req->getPath() !== $config['login.url']) {
$app->redirect($config['login.url']);
}
}
Match path to
secured url
Logged in?
Redirect
Wednesday, May 15, 13
Views
Wednesday, May 15, 13
SlimView
Wednesday, May 15, 13
SlimView
Slim delegates rendering of templates to its view object.
Wednesday, May 15, 13
SlimView
Slim delegates rendering of templates to its view object.
Easily extensible by extending theView class and
returning a string from render()
Wednesday, May 15, 13
SlimView
Slim delegates rendering of templates to its view object.
Easily extensible by extending theView class and
returning a string from render()
Use Slim application’s render() method in your app
Wednesday, May 15, 13
SlimView
Slim delegates rendering of templates to its view object.
Easily extensible by extending theView class and
returning a string from render()
Use Slim application’s render() method in your app
Will echo() template output, buffer the output, and
append to response object’s body
Wednesday, May 15, 13
Rendering an app view
$app->render(
'hello.html',
array( 'name' => 'Josh' ),
200
);
Wednesday, May 15, 13
Rendering an app view
$app->render(
'hello.html',
array( 'name' => 'Josh' ),
200
);
Template
Wednesday, May 15, 13
Rendering an app view
$app->render(
'hello.html',
array( 'name' => 'Josh' ),
200
);
Template View data
(optional)
Wednesday, May 15, 13
Rendering an app view
$app->render(
'hello.html',
array( 'name' => 'Josh' ),
200
);
Template View data
(optional)
HTTP Response
(optional)
Wednesday, May 15, 13
Templates
Wednesday, May 15, 13
Two great tastes
that taste great together
Wednesday, May 15, 13
Twig
Wednesday, May 15, 13
Twig
Concise
Wednesday, May 15, 13
Twig
Concise
Template oriented
Wednesday, May 15, 13
Twig
Concise
Template oriented
Fast
Wednesday, May 15, 13
Twig
Concise
Template oriented
Fast
Multiple inheritance
Wednesday, May 15, 13
Twig
Concise
Template oriented
Fast
Multiple inheritance
Content blocks
Wednesday, May 15, 13
Twig
Concise
Template oriented
Fast
Multiple inheritance
Content blocks
Automatic escaping
Wednesday, May 15, 13
Bootstrap
Because I majorly suck at design
Wednesday, May 15, 13
Caveat
I chose Twig because I wanted to learn Twig,
but you could choose any or more of the
following:
PHP, Mustache, Haml, Haanga, Blitz,
Dwoo . . .
https://github.com/codeguy/Slim-Extras
Wednesday, May 15, 13
layout.html
and
index.html
Wednesday, May 15, 13
layout.html
and
index.html
Wednesday, May 15, 13
layout.html
and
index.html
Wednesday, May 15, 13
layout.html
Wednesday, May 15, 13
<title>{% block page_title %} {% endblock %}</title>
layout.html: title
Wednesday, May 15, 13
<ul class="nav">
{% for link in navigation %}
<li class="{{link.class}}">
<a href="{{link.href}}">{{link.caption}}</
a>
</li>
{% endfor %}
</ul>
layout.html: navigation
Wednesday, May 15, 13
<h1>365 Days of Photography</h1>
<h3>Photographer: Jeremy Kendall</h3>
{% block content %} {% endblock %}
<hr />
layout.html:
headers and content
Wednesday, May 15, 13
index.html
(extends layout.html)
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net{% endblock %}
{% block content %}
{% for image in images %}
<div class="row">
<div class="span6">
<h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
<p>
<a href="/{{image.day}}">
<img src="{{image.sizes.size.5.source}}" />
</a>
</p>
<p>Posted {{image.posted|date("m/d/Y")}}</p>
</div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net{% endblock %}
{% block content %}
{% for image in images %}
<div class="row">
<div class="span6">
<h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
<p>
<a href="/{{image.day}}">
<img src="{{image.sizes.size.5.source}}" />
</a>
</p>
<p>Posted {{image.posted|date("m/d/Y")}}</p>
</div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
extends
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net{% endblock %}
{% block content %}
{% for image in images %}
<div class="row">
<div class="span6">
<h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
<p>
<a href="/{{image.day}}">
<img src="{{image.sizes.size.5.source}}" />
</a>
</p>
<p>Posted {{image.posted|date("m/d/Y")}}</p>
</div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
extends
<title />
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net{% endblock %}
{% block content %}
{% for image in images %}
<div class="row">
<div class="span6">
<h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
<p>
<a href="/{{image.day}}">
<img src="{{image.sizes.size.5.source}}" />
</a>
</p>
<p>Posted {{image.posted|date("m/d/Y")}}</p>
</div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
extends
<title />
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net{% endblock %}
{% block content %}
{% for image in images %}
<div class="row">
<div class="span6">
<h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
<p>
<a href="/{{image.day}}">
<img src="{{image.sizes.size.5.source}}" />
</a>
</p>
<p>Posted {{image.posted|date("m/d/Y")}}</p>
</div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
extends
<title />
iterator
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net{% endblock %}
{% block content %}
{% for image in images %}
<div class="row">
<div class="span6">
<h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
<p>
<a href="/{{image.day}}">
<img src="{{image.sizes.size.5.source}}" />
</a>
</p>
<p>Posted {{image.posted|date("m/d/Y")}}</p>
</div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
extends
<title />
iterator
else
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net{% endblock %}
{% block content %}
{% for image in images %}
<div class="row">
<div class="span6">
<h2><a href="/{{image.day}}">{{image.day}}/365</a></h2>
<p>
<a href="/{{image.day}}">
<img src="{{image.sizes.size.5.source}}" />
</a>
</p>
<p>Posted {{image.posted|date("m/d/Y")}}</p>
</div>
</div>
{% else %}
<p>No images in project</p>
{% endfor %}
{% endblock %}
extends
<title />
iterator
else
format
Wednesday, May 15, 13
login.html
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net | Login{% endblock %}
{% block content %}
<div class="row">
<div class="span4">
<h2>Login</h2>
{% if flash.error %}
<p style="color: red;">{{flash.error}}</p>
{% endif %}
<form name="login" id="login" class="well" method="post">
// Login form . . .
</form>
</div>
</div>
{% endblock %}
login.html
Wednesday, May 15, 13
{% extends 'layout.html' %}
{% block page_title %}365.jeremykendall.net | Login{% endblock %}
{% block content %}
<div class="row">
<div class="span4">
<h2>Login</h2>
{% if flash.error %}
<p style="color: red;">{{flash.error}}</p>
{% endif %}
<form name="login" id="login" class="well" method="post">
// Login form . . .
</form>
</div>
</div>
{% endblock %}
login.html
Wednesday, May 15, 13
The other templates
are just more of the same
Wednesday, May 15, 13
Application Code
Another big hurdle I had with Slim was
figuring out how to organize my application
code.
Wednesday, May 15, 13
Thin Controller, Fat Model
Wednesday, May 15, 13
Thin Controller, Fat Model
Duh!
Wednesday, May 15, 13
Thin Controller, Fat Model
Duh!
All my application code is in a library directory
Wednesday, May 15, 13
Thin Controller, Fat Model
Duh!
All my application code is in a library directory
It’s all namespaced, autoloadable, and testable
Wednesday, May 15, 13
Thin Controller, Fat Model
Duh!
All my application code is in a library directory
It’s all namespaced, autoloadable, and testable
I have (almost) zero business logic in index.php
Wednesday, May 15, 13
Thin Controller, Fat Model
Duh!
All my application code is in a library directory
It’s all namespaced, autoloadable, and testable
I have (almost) zero business logic in index.php
Dependencies are managed by Composer
Wednesday, May 15, 13
Thin Controller, Fat Model
Duh!
All my application code is in a library directory
It’s all namespaced, autoloadable, and testable
I have (almost) zero business logic in index.php
Dependencies are managed by Composer
I could theoretically switch frameworks with very little
effort
Wednesday, May 15, 13
GOTO 0
Wednesday, May 15, 13
Small but powerful GOTO 0
Wednesday, May 15, 13
Small but powerful
Excellent tools to write elegant code
GOTO 0
Wednesday, May 15, 13
Small but powerful
Excellent tools to write elegant code
Routing, middleware, hooks, templates, and
views
GOTO 0
Wednesday, May 15, 13
Small but powerful
Excellent tools to write elegant code
Routing, middleware, hooks, templates, and
views
I just scratched the surface
GOTO 0
Wednesday, May 15, 13
Read
Slim: slimframework.com
Twig: twig.sensiolabs.org
Composer: getcomposer.org
MicroPHP Manifesto: microphp.org
Flaming Archer: http://git.io/rH0nrg
Wednesday, May 15, 13
Questions?
Wednesday, May 15, 13
Thanks!
jeremy@jeremykendall.net
@jeremykendall
http://joind.in/8175
Wednesday, May 15, 13

More Related Content

Similar to Keeping it small - Getting to know the Slim PHP micro framework (20)

PDF
Hello World on Slim Framework 3.x
Ryan Szrama
 
PDF
Silex: From nothing to an API
chrisdkemper
 
PPTX
Introduction to Monsoon PHP framework
Krishna Srikanth Manda
 
PDF
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
Fabien Potencier
 
PDF
Slim PHP when you don't need the kitchen sink
Joe Ferguson
 
PDF
Symfony internals [english]
Raul Fraile
 
PDF
CakePHP 3.0: Embracing the future
José Lorenzo Rodríguez Urdaneta
 
PDF
Mojolicious
Marcos Rebelo
 
PDF
Developing cacheable PHP applications - PHPLimburgBE 2018
Thijs Feryn
 
PDF
Developing cacheable PHP applications - Confoo 2018
Thijs Feryn
 
PPTX
They why behind php frameworks
Kirk Madera
 
PDF
Zendcon 2007 Api Design
unodelostrece
 
PDF
Symfony Performance
Paul Thrasher
 
PDF
composer_talk_20160209
Bradley Wogsland
 
PDF
An Introduction to the Laravel Framework (AFUP Forum PHP 2014)
daylerees
 
PPTX
Ei cakephp
eiei lay
 
PPTX
Cakeph pppt
Wizard Rider
 
PDF
Leverage HTTP to deliver cacheable websites - Codemotion Rome 2018
Thijs Feryn
 
PDF
Leverage HTTP to deliver cacheable websites - Thijs Feryn - Codemotion Rome 2018
Codemotion
 
Hello World on Slim Framework 3.x
Ryan Szrama
 
Silex: From nothing to an API
chrisdkemper
 
Introduction to Monsoon PHP framework
Krishna Srikanth Manda
 
symfony: An Open-Source Framework for Professionals (Dutch Php Conference 2008)
Fabien Potencier
 
Slim PHP when you don't need the kitchen sink
Joe Ferguson
 
Symfony internals [english]
Raul Fraile
 
CakePHP 3.0: Embracing the future
José Lorenzo Rodríguez Urdaneta
 
Mojolicious
Marcos Rebelo
 
Developing cacheable PHP applications - PHPLimburgBE 2018
Thijs Feryn
 
Developing cacheable PHP applications - Confoo 2018
Thijs Feryn
 
They why behind php frameworks
Kirk Madera
 
Zendcon 2007 Api Design
unodelostrece
 
Symfony Performance
Paul Thrasher
 
composer_talk_20160209
Bradley Wogsland
 
An Introduction to the Laravel Framework (AFUP Forum PHP 2014)
daylerees
 
Ei cakephp
eiei lay
 
Cakeph pppt
Wizard Rider
 
Leverage HTTP to deliver cacheable websites - Codemotion Rome 2018
Thijs Feryn
 
Leverage HTTP to deliver cacheable websites - Thijs Feryn - Codemotion Rome 2018
Codemotion
 

More from Jeremy Kendall (14)

PDF
Leveraging the Power of Graph Databases in PHP
Jeremy Kendall
 
PDF
Leveraging the Power of Graph Databases in PHP
Jeremy Kendall
 
PDF
5 Ways to Awesome-ize Your (PHP) Code
Jeremy Kendall
 
PDF
Game Changing Dependency Management
Jeremy Kendall
 
ODP
Php 102: Out with the Bad, In with the Good
Jeremy Kendall
 
KEY
Php 101: PDO
Jeremy Kendall
 
ODP
PHP 102: Out with the Bad, In with the Good
Jeremy Kendall
 
ODP
Intro to #memtech PHP 2011-12-05
Jeremy Kendall
 
ODP
TDD in PHP - Memphis PHP 2011-08-25
Jeremy Kendall
 
ODP
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Jeremy Kendall
 
PDF
Zero to ZF in 10 Minutes
Jeremy Kendall
 
ODP
Tdd in php a brief example
Jeremy Kendall
 
ODP
A Brief Introduction to Zend_Form
Jeremy Kendall
 
ODP
Zero to Zend Framework in 10 minutes
Jeremy Kendall
 
Leveraging the Power of Graph Databases in PHP
Jeremy Kendall
 
Leveraging the Power of Graph Databases in PHP
Jeremy Kendall
 
5 Ways to Awesome-ize Your (PHP) Code
Jeremy Kendall
 
Game Changing Dependency Management
Jeremy Kendall
 
Php 102: Out with the Bad, In with the Good
Jeremy Kendall
 
Php 101: PDO
Jeremy Kendall
 
PHP 102: Out with the Bad, In with the Good
Jeremy Kendall
 
Intro to #memtech PHP 2011-12-05
Jeremy Kendall
 
TDD in PHP - Memphis PHP 2011-08-25
Jeremy Kendall
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Jeremy Kendall
 
Zero to ZF in 10 Minutes
Jeremy Kendall
 
Tdd in php a brief example
Jeremy Kendall
 
A Brief Introduction to Zend_Form
Jeremy Kendall
 
Zero to Zend Framework in 10 minutes
Jeremy Kendall
 
Ad

Recently uploaded (20)

PDF
How to Visualize the ​Spatio-Temporal Data Using CesiumJS​
SANGHEE SHIN
 
PPTX
MuleSoft MCP Support (Model Context Protocol) and Use Case Demo
shyamraj55
 
PPTX
2025 HackRedCon Cyber Career Paths.pptx Scott Stanton
Scott Stanton
 
PDF
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
PDF
Bitkom eIDAS Summit | European Business Wallet: Use Cases, Macroeconomics, an...
Carsten Stoecker
 
PDF
Draugnet: Anonymous Threat Reporting for a World on Fire
treyka
 
PDF
Pipeline Industry IoT - Real Time Data Monitoring
Safe Software
 
PPTX
Wondershare Filmora Crack Free Download 2025
josanj305
 
PDF
Java 25 and Beyond - A Roadmap of Innovations
Ana-Maria Mihalceanu
 
PPTX
Paycifi - Programmable Trust_Breakfast_PPTXT
FinTech Belgium
 
PDF
Quantum Threats Are Closer Than You Think – Act Now to Stay Secure
WSO2
 
PPTX
Enabling the Digital Artisan – keynote at ICOCI 2025
Alan Dix
 
PDF
FME in Overdrive: Unleashing the Power of Parallel Processing
Safe Software
 
PDF
Deploy Faster, Run Smarter: Learn Containers with QNAP
QNAP Marketing
 
PPTX
CapCut Pro PC Crack Latest Version Free Free
josanj305
 
PDF
Enhancing Environmental Monitoring with Real-Time Data Integration: Leveragin...
Safe Software
 
PDF
Dev Dives: Accelerating agentic automation with Autopilot for Everyone
UiPathCommunity
 
PDF
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
PDF
How to Comply With Saudi Arabia’s National Cybersecurity Regulations.pdf
Bluechip Advanced Technologies
 
PDF
Understanding The True Cost of DynamoDB Webinar
ScyllaDB
 
How to Visualize the ​Spatio-Temporal Data Using CesiumJS​
SANGHEE SHIN
 
MuleSoft MCP Support (Model Context Protocol) and Use Case Demo
shyamraj55
 
2025 HackRedCon Cyber Career Paths.pptx Scott Stanton
Scott Stanton
 
Peak of Data & AI Encore AI-Enhanced Workflows for the Real World
Safe Software
 
Bitkom eIDAS Summit | European Business Wallet: Use Cases, Macroeconomics, an...
Carsten Stoecker
 
Draugnet: Anonymous Threat Reporting for a World on Fire
treyka
 
Pipeline Industry IoT - Real Time Data Monitoring
Safe Software
 
Wondershare Filmora Crack Free Download 2025
josanj305
 
Java 25 and Beyond - A Roadmap of Innovations
Ana-Maria Mihalceanu
 
Paycifi - Programmable Trust_Breakfast_PPTXT
FinTech Belgium
 
Quantum Threats Are Closer Than You Think – Act Now to Stay Secure
WSO2
 
Enabling the Digital Artisan – keynote at ICOCI 2025
Alan Dix
 
FME in Overdrive: Unleashing the Power of Parallel Processing
Safe Software
 
Deploy Faster, Run Smarter: Learn Containers with QNAP
QNAP Marketing
 
CapCut Pro PC Crack Latest Version Free Free
josanj305
 
Enhancing Environmental Monitoring with Real-Time Data Integration: Leveragin...
Safe Software
 
Dev Dives: Accelerating agentic automation with Autopilot for Everyone
UiPathCommunity
 
Book industry state of the nation 2025 - Tech Forum 2025
BookNet Canada
 
How to Comply With Saudi Arabia’s National Cybersecurity Regulations.pdf
Bluechip Advanced Technologies
 
Understanding The True Cost of DynamoDB Webinar
ScyllaDB
 
Ad

Keeping it small - Getting to know the Slim PHP micro framework

  • 1. Keeping it small Getting to know the Slim micro framework php[tek] 2013 Wednesday, May 15, 13
  • 3. I love to code Wednesday, May 15, 13
  • 4. I love to code I’m terribly forgetful Wednesday, May 15, 13
  • 5. I love to code I’m terribly forgetful I take pictures Wednesday, May 15, 13
  • 6. I love to code I’m terribly forgetful I take pictures I work at OpenSky Wednesday, May 15, 13
  • 9. MicroPHP Manifesto Written by Ed Finkler Wednesday, May 15, 13
  • 10. MicroPHP Manifesto Written by Ed Finkler Punk rock vs. Prog rock Wednesday, May 15, 13
  • 11. MicroPHP Manifesto Written by Ed Finkler Punk rock vs. Prog rock Prophet or Madman? Wednesday, May 15, 13
  • 12. MicroPHP Manifesto Written by Ed Finkler Punk rock vs. Prog rock Prophet or Madman? Guiding principle Wednesday, May 15, 13
  • 13. MicroPHP Manifesto Written by Ed Finkler Punk rock vs. Prog rock Prophet or Madman? Guiding principle http://microphp.org/ Wednesday, May 15, 13
  • 16. Micro framework? Concise codebase Clear codebase Wednesday, May 15, 13
  • 17. Micro framework? Concise codebase Clear codebase Addresses a small set of use cases Wednesday, May 15, 13
  • 18. Micro framework? Concise codebase Clear codebase Addresses a small set of use cases Addresses those use cases well Wednesday, May 15, 13
  • 19. I chose Slim PHP Wednesday, May 15, 13
  • 20. I chose Slim PHP and I sucked at it Wednesday, May 15, 13
  • 22. What is Slim? Inspired by Sinatra Wednesday, May 15, 13
  • 23. What is Slim? Inspired by Sinatra Favors cleanliness over terseness Wednesday, May 15, 13
  • 24. What is Slim? Inspired by Sinatra Favors cleanliness over terseness Favors common cases over edge cases Wednesday, May 15, 13
  • 26. Install Composer curl -s https://getcomposer.org/installer | php Wednesday, May 15, 13
  • 27. Install Composer curl -s https://getcomposer.org/installer | php If you take nothing else away from this talk, I hope that it’s Composer. It’s that big of a deal. Wednesday, May 15, 13
  • 29. Install via Composer php composer.phar install Wednesday, May 15, 13
  • 30. Add this to index.php <?php require 'vendor/autoload.php'; Wednesday, May 15, 13
  • 31. Pro tip mv composer.phar /usr/local/bin/composer Wednesday, May 15, 13
  • 32. Don’t forget .htaccess! RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^ index.php [QSA,L] http://docs.slimframework.com/#Route-URL-Rewriting (nginx documentation available at same URL) Wednesday, May 15, 13
  • 33. Hello $name <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run(); Wednesday, May 15, 13
  • 34. Hello $name <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run(); Wednesday, May 15, 13
  • 35. Hello $name <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run(); Wednesday, May 15, 13
  • 36. Hello $name <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run(); Wednesday, May 15, 13
  • 37. Hello $name <?php require '../vendor/autoload.php'; $app = new SlimSlim(); $app->get('/hello/:name', function ($name) { echo "Hello, $name"; }); $app->run(); Wednesday, May 15, 13
  • 41. “Great repository names are short and memorable. Need inspiration? How about flaming-archer.” Wednesday, May 15, 13
  • 43. Flaming Archer Photo 365 project Wednesday, May 15, 13
  • 44. Flaming Archer Photo 365 project Bulk of app built in 4 days Wednesday, May 15, 13
  • 45. Flaming Archer Photo 365 project Bulk of app built in 4 days Basic application — a few bells, no whistles Wednesday, May 15, 13
  • 46. Flaming Archer Photo 365 project Bulk of app built in 4 days Basic application — a few bells, no whistles Routing Wednesday, May 15, 13
  • 47. Flaming Archer Photo 365 project Bulk of app built in 4 days Basic application — a few bells, no whistles Routing Twig templates Wednesday, May 15, 13
  • 48. Flaming Archer Photo 365 project Bulk of app built in 4 days Basic application — a few bells, no whistles Routing Twig templates Middleware Wednesday, May 15, 13
  • 49. Flaming Archer Photo 365 project Bulk of app built in 4 days Basic application — a few bells, no whistles Routing Twig templates Middleware Hooks Wednesday, May 15, 13
  • 55. phploc --exclude vendor --exclude tests --exclude templates . phploc 1.7.4 by Sebastian Bergmann. Directories: 8 Files: 16 Lines of Code (LOC): 1212 Cyclomatic Complexity / Lines of Code: 0.03 Comment Lines of Code (CLOC): 453 Non-Comment Lines of Code (NCLOC): 759 Wednesday, May 15, 13
  • 57. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) ); Wednesday, May 15, 13
  • 58. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) ); Slim Wednesday, May 15, 13
  • 59. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) ); Slim Views Wednesday, May 15, 13
  • 60. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) ); Slim Views Cookies Wednesday, May 15, 13
  • 61. return array( 'slim' => array( 'templates.path' => __DIR__ . '/templates', 'log.level' => 4, 'log.enabled' => true, 'log.writer' => new SlimExtrasLogDateTimeFileWriter( array( 'path' => __DIR__ . '/logs', 'name_format' => 'y-m-d' ) ) ), 'twig' => array( // . . . ), 'cookies' => array( // . . . ), 'flickr.api.key' => 'FLICKR API KEY', 'pdo' => array( // . . . ) ); Slim Views Cookies App specific Wednesday, May 15, 13
  • 62. $config = require_once __DIR__ . '/../config.php'; // Prepare app $app = new SlimSlim($config['slim']); Configuration Wednesday, May 15, 13
  • 63. $config = require_once __DIR__ . '/../config.php'; // Prepare app $app = new SlimSlim($config['slim']); Config array goes here Configuration Wednesday, May 15, 13
  • 65. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); Wednesday, May 15, 13
  • 66. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); HTTP Method Wednesday, May 15, 13
  • 67. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); HTTP Method Resource URI Wednesday, May 15, 13
  • 68. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); HTTP Method Resource URI Anonymous Function Wednesday, May 15, 13
  • 69. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); Wednesday, May 15, 13
  • 70. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); Grabs all the pics Wednesday, May 15, 13
  • 71. Routing $app->get('/', function () use ($app, $service) { $images = $service->findAll(); $app->render('index.html', array('images' => $images)); } ); Grabs all the pics Passes array of image data to index.html Wednesday, May 15, 13
  • 72. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); Wednesday, May 15, 13
  • 73. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); URL parameter Wednesday, May 15, 13
  • 74. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); URL parameter ... gets passed as an argument Wednesday, May 15, 13
  • 75. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); URL parameter ... gets passed as an argument Condition Wednesday, May 15, 13
  • 76. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); URL parameter ... gets passed as an argument Condition 1 to 366 Wednesday, May 15, 13
  • 77. GET $app->get('/:day', function($day) use ($app, $service) { $image = $service->find($day); if (!$image) { $app->notFound(); } $app->render('images.html', $image); } )->conditions(array('day' => '([1-9]d?|[12]dd|3[0-5]d|36[0-6])')); 404! Wednesday, May 15, 13
  • 78. POST (with redirect) $app->post('/admin/add-photo', function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect('/admin'); } ); Wednesday, May 15, 13
  • 79. POST (with redirect) $app->post('/admin/add-photo', function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect('/admin'); } ); $_POST data is in the request object Wednesday, May 15, 13
  • 80. POST (with redirect) $app->post('/admin/add-photo', function() use ($app, $service) { $data = $app->request()->post(); $service->save($data); $app->redirect('/admin'); } ); $_POST data is in the request object 302 Redirect Wednesday, May 15, 13
  • 81. Multiple methods $app->map('/login', function() { // Login } )->via('GET', 'POST'); Wednesday, May 15, 13
  • 82. Multiple methods $app->map('/login', function() { // Login } )->via('GET', 'POST'); Not an HTTP Method Wednesday, May 15, 13
  • 83. Multiple methods $app->map('/login', function() { // Login } )->via('GET', 'POST'); Not an HTTP Method via() is the awesome sauce Wednesday, May 15, 13
  • 84. Logging and flash messaging Wednesday, May 15, 13
  • 85. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); } } $app->flash('cleared', $cleared); $app->redirect('/admin'); } ); Wednesday, May 15, 13
  • 86. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); } } $app->flash('cleared', $cleared); $app->redirect('/admin'); } ); Get the log from $app Wednesday, May 15, 13
  • 87. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); } } $app->flash('cleared', $cleared); $app->redirect('/admin'); } ); Get the log from $app Error! Wednesday, May 15, 13
  • 88. $app->post('/admin/clear-cache', function() use ($app) { $log = $app->getLog(); $cleared = null; $clear = $app->request()->post('clear'); if ($clear == 1) { if (apc_clear_cache('user')) { $cleared = 'Cache was successfully cleared!'; } else { $cleared = 'Cache was not cleared!'; $log->error('Cache not cleared'); } } $app->flash('cleared', $cleared); $app->redirect('/admin'); } ); Get the log from $app Error! Flash message available in the next request. Wednesday, May 15, 13
  • 89. Middleware “. . . a Slim application can have middleware that may inspect, analyze, or modify the application environment, request, and response before and/or after the Slim application is invoked.” http://docs.slimframework.com/#Middleware-Overview Wednesday, May 15, 13
  • 91. Middleware Like layers of an onion Wednesday, May 15, 13
  • 92. Middleware Like layers of an onion The Slim application is the core of the onion Wednesday, May 15, 13
  • 93. Middleware Like layers of an onion The Slim application is the core of the onion Each new middleware wraps any existing middleware Wednesday, May 15, 13
  • 94. Middleware Like layers of an onion The Slim application is the core of the onion Each new middleware wraps any existing middleware Each middleware optionally calls the next middleware in the chain Wednesday, May 15, 13
  • 95. Middleware Like layers of an onion The Slim application is the core of the onion Each new middleware wraps any existing middleware Each middleware optionally calls the next middleware in the chain Middleware is executed from the outside in Wednesday, May 15, 13
  • 96. Middleware Like layers of an onion The Slim application is the core of the onion Each new middleware wraps any existing middleware Each middleware optionally calls the next middleware in the chain Middleware is executed from the outside in Pay careful attention to the order in which you register middleware Wednesday, May 15, 13
  • 97. Middleware // Prepare app $app = new SlimSlim($config['slim']); // . . . $app->add(new Navigation($auth)); $app->add(new Authentication($auth, $config)); Wednesday, May 15, 13
  • 98. Middleware // Prepare app $app = new SlimSlim($config['slim']); // . . . $app->add(new Navigation($auth)); $app->add(new Authentication($auth, $config)); Wednesday, May 15, 13
  • 99. Middleware // Prepare app $app = new SlimSlim($config['slim']); // . . . $app->add(new Navigation($auth)); $app->add(new Authentication($auth, $config)); Wednesday, May 15, 13
  • 102. Existing Middleware Flash messaging Content types Wednesday, May 15, 13
  • 103. Existing Middleware Flash messaging Content types Pretty exceptions Wednesday, May 15, 13
  • 104. Existing Middleware Flash messaging Content types Pretty exceptions Session cookie Wednesday, May 15, 13
  • 105. Existing Middleware Flash messaging Content types Pretty exceptions Session cookie More in Slim Extras Wednesday, May 15, 13
  • 106. Roll your own Middleware Wednesday, May 15, 13
  • 107. Roll your own Middleware Super easy Wednesday, May 15, 13
  • 108. Roll your own Middleware Super easy DRYs up code Wednesday, May 15, 13
  • 109. Roll your own Middleware Super easy DRYs up code Awesome application wide functionality Wednesday, May 15, 13
  • 110. Roll your own Middleware Super easy DRYs up code Awesome application wide functionality Just extend SlimMiddleware and implement call() Wednesday, May 15, 13
  • 111. class MyMiddleware extends SlimMiddleware { public function call() { //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } } Wednesday, May 15, 13
  • 112. class MyMiddleware extends SlimMiddleware { public function call() { //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } } Extend this Wednesday, May 15, 13
  • 113. class MyMiddleware extends SlimMiddleware { public function call() { //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } } Extend this Define call() Wednesday, May 15, 13
  • 114. class MyMiddleware extends SlimMiddleware { public function call() { //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } } Extend this Define call() Inspect, analyze, and modify! Wednesday, May 15, 13
  • 115. class MyMiddleware extends SlimMiddleware { public function call() { //The Slim application $app = $this->app; //The Environment object $env = $app->environment(); //The Request object $req = $app->request(); //The Response object $res = $app->response(); //Optionally call the next middleware $this->next->call(); } } Extend this Define call() On to the next! Inspect, analyze, and modify! Wednesday, May 15, 13
  • 117. namespace FaMiddleware; use ZendAuthenticationAuthenticationService; class Navigation extends SlimMiddleware { /** @var AuthenticationService */ private $auth; public function __construct(AuthenticationService $auth) { $this->auth = $auth; } public function call() { // . . . } } Wednesday, May 15, 13
  • 118. namespace FaMiddleware; use ZendAuthenticationAuthenticationService; class Navigation extends SlimMiddleware { /** @var AuthenticationService */ private $auth; public function __construct(AuthenticationService $auth) { $this->auth = $auth; } public function call() { // . . . } } extends Wednesday, May 15, 13
  • 119. namespace FaMiddleware; use ZendAuthenticationAuthenticationService; class Navigation extends SlimMiddleware { /** @var AuthenticationService */ private $auth; public function __construct(AuthenticationService $auth) { $this->auth = $auth; } public function call() { // . . . } } Constructor injection FTW extends Wednesday, May 15, 13
  • 120. public function call() { $app = $this->app; $auth = $this->auth; $req = $app->request(); $home = array('caption' => 'Home', 'href' => '/'); $admin = array('caption' => 'Admin', 'href' => '/admin'); $login = array('caption' => 'Login', 'href' => '/login'); $logout = array('caption' => 'Logout', 'href' => '/logout'); if ($auth->hasIdentity()) { $navigation = array($home, $admin, $logout); } else { $navigation = array($home, $login); } // . . . } Wednesday, May 15, 13
  • 121. public function call() { $app = $this->app; $auth = $this->auth; $req = $app->request(); $home = array('caption' => 'Home', 'href' => '/'); $admin = array('caption' => 'Admin', 'href' => '/admin'); $login = array('caption' => 'Login', 'href' => '/login'); $logout = array('caption' => 'Logout', 'href' => '/logout'); if ($auth->hasIdentity()) { $navigation = array($home, $admin, $logout); } else { $navigation = array($home, $login); } // . . . } Arrays of nav items Wednesday, May 15, 13
  • 122. public function call() { $app = $this->app; $auth = $this->auth; $req = $app->request(); $home = array('caption' => 'Home', 'href' => '/'); $admin = array('caption' => 'Admin', 'href' => '/admin'); $login = array('caption' => 'Login', 'href' => '/login'); $logout = array('caption' => 'Logout', 'href' => '/logout'); if ($auth->hasIdentity()) { $navigation = array($home, $admin, $logout); } else { $navigation = array($home, $login); } // . . . } Arrays of nav items Nav differs based on auth status Wednesday, May 15, 13
  • 123. public function call() { // . . . foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } } $app->view()->appendData(array('navigation' => $navigation)); $this->next->call(); } Wednesday, May 15, 13
  • 124. public function call() { // . . . foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } } $app->view()->appendData(array('navigation' => $navigation)); $this->next->call(); } Match dispatched path Wednesday, May 15, 13
  • 125. public function call() { // . . . foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } } $app->view()->appendData(array('navigation' => $navigation)); $this->next->call(); } Match dispatched path Append $navigation to view Wednesday, May 15, 13
  • 126. public function call() { // . . . foreach ($navigation as &$link) { if ($link['href'] == $req->getPath()) { $link['class'] = 'active'; } else { $link['class'] = ''; } } $app->view()->appendData(array('navigation' => $navigation)); $this->next->call(); } Match dispatched path Append $navigation to view On to the next! Wednesday, May 15, 13
  • 128. Route Middleware Anything that returns true for is_callable() Wednesday, May 15, 13
  • 129. Route Middleware Anything that returns true for is_callable() Apply directly to route! Wednesday, May 15, 13
  • 130. Route Middleware Anything that returns true for is_callable() Apply directly to route! Goes between route definition and route callable Wednesday, May 15, 13
  • 131. Route Middleware <?php function mw1() { echo "This is middleware!"; } function mw2() { echo "This is middleware!"; } $app = new SlimSlim(); $app->get('/foo', 'mw1', 'mw2', function () { //Do something }); http://docs.slimframework.com/#Route-Middleware Wednesday, May 15, 13
  • 132. Route Middleware <?php function mw1() { echo "This is middleware!"; } function mw2() { echo "This is middleware!"; } $app = new SlimSlim(); $app->get('/foo', 'mw1', 'mw2', function () { //Do something }); http://docs.slimframework.com/#Route-Middleware Wednesday, May 15, 13
  • 133. Hooks A “hook” is a moment in the Slim application lifecycle at which a priority list of callables assigned to the hook will be invoked.A hook is identified by a string name. http://docs.slimframework.com/#Hooks-Overview Wednesday, May 15, 13
  • 135. Hooks Anything that returns true for is_callable() Wednesday, May 15, 13
  • 136. Hooks Anything that returns true for is_callable() Prioritized Wednesday, May 15, 13
  • 137. Hooks Anything that returns true for is_callable() Prioritized Six default hooks Wednesday, May 15, 13
  • 146. Hooks $app = new SlimSlim(); $app->hook('the.hook.name', function () { //Do something }, 5); Wednesday, May 15, 13
  • 147. Hooks $app = new SlimSlim(); $app->hook('the.hook.name', function () { //Do something }, 5); Optional Priority Wednesday, May 15, 13
  • 148. Middleware + Hooks = WIN Wednesday, May 15, 13
  • 149. Middleware + Hooks Using middleware to register hooks allows me to inspect, analyze, or modify my application at a specific time in the application’s lifecycle in a DRY, testable, and reusable manner. Wednesday, May 15, 13
  • 151. public function call() { $app = $this->app; $req = $app->request(); $auth = $this->auth; $config = $this->config; // $checkAuth anonymous function snipped $this->app->hook('slim.before.router', $checkAuth); $this->next->call(); } Wednesday, May 15, 13
  • 152. public function call() { $app = $this->app; $req = $app->request(); $auth = $this->auth; $config = $this->config; // $checkAuth anonymous function snipped $this->app->hook('slim.before.router', $checkAuth); $this->next->call(); } Grab stuff to inspect Wednesday, May 15, 13
  • 153. public function call() { $app = $this->app; $req = $app->request(); $auth = $this->auth; $config = $this->config; // $checkAuth anonymous function snipped $this->app->hook('slim.before.router', $checkAuth); $this->next->call(); } Grab stuff to inspect (via constructor) Wednesday, May 15, 13
  • 154. public function call() { $app = $this->app; $req = $app->request(); $auth = $this->auth; $config = $this->config; // $checkAuth anonymous function snipped $this->app->hook('slim.before.router', $checkAuth); $this->next->call(); } Grab stuff to inspect Register hook (via constructor) Wednesday, May 15, 13
  • 155. // Snip looping through secured url array if (preg_match($urlPattern, $req->getPathInfo()) && !$auth->hasIdentity()) { if ($req->getPath() !== $config['login.url']) { $app->redirect($config['login.url']); } } Wednesday, May 15, 13
  • 156. // Snip looping through secured url array if (preg_match($urlPattern, $req->getPathInfo()) && !$auth->hasIdentity()) { if ($req->getPath() !== $config['login.url']) { $app->redirect($config['login.url']); } } Match path to secured url Wednesday, May 15, 13
  • 157. // Snip looping through secured url array if (preg_match($urlPattern, $req->getPathInfo()) && !$auth->hasIdentity()) { if ($req->getPath() !== $config['login.url']) { $app->redirect($config['login.url']); } } Match path to secured url Logged in? Wednesday, May 15, 13
  • 158. // Snip looping through secured url array if (preg_match($urlPattern, $req->getPathInfo()) && !$auth->hasIdentity()) { if ($req->getPath() !== $config['login.url']) { $app->redirect($config['login.url']); } } Match path to secured url Logged in? Redirect Wednesday, May 15, 13
  • 161. SlimView Slim delegates rendering of templates to its view object. Wednesday, May 15, 13
  • 162. SlimView Slim delegates rendering of templates to its view object. Easily extensible by extending theView class and returning a string from render() Wednesday, May 15, 13
  • 163. SlimView Slim delegates rendering of templates to its view object. Easily extensible by extending theView class and returning a string from render() Use Slim application’s render() method in your app Wednesday, May 15, 13
  • 164. SlimView Slim delegates rendering of templates to its view object. Easily extensible by extending theView class and returning a string from render() Use Slim application’s render() method in your app Will echo() template output, buffer the output, and append to response object’s body Wednesday, May 15, 13
  • 165. Rendering an app view $app->render( 'hello.html', array( 'name' => 'Josh' ), 200 ); Wednesday, May 15, 13
  • 166. Rendering an app view $app->render( 'hello.html', array( 'name' => 'Josh' ), 200 ); Template Wednesday, May 15, 13
  • 167. Rendering an app view $app->render( 'hello.html', array( 'name' => 'Josh' ), 200 ); Template View data (optional) Wednesday, May 15, 13
  • 168. Rendering an app view $app->render( 'hello.html', array( 'name' => 'Josh' ), 200 ); Template View data (optional) HTTP Response (optional) Wednesday, May 15, 13
  • 170. Two great tastes that taste great together Wednesday, May 15, 13
  • 177. Twig Concise Template oriented Fast Multiple inheritance Content blocks Automatic escaping Wednesday, May 15, 13
  • 178. Bootstrap Because I majorly suck at design Wednesday, May 15, 13
  • 179. Caveat I chose Twig because I wanted to learn Twig, but you could choose any or more of the following: PHP, Mustache, Haml, Haanga, Blitz, Dwoo . . . https://github.com/codeguy/Slim-Extras Wednesday, May 15, 13
  • 184. <title>{% block page_title %} {% endblock %}</title> layout.html: title Wednesday, May 15, 13
  • 185. <ul class="nav"> {% for link in navigation %} <li class="{{link.class}}"> <a href="{{link.href}}">{{link.caption}}</ a> </li> {% endfor %} </ul> layout.html: navigation Wednesday, May 15, 13
  • 186. <h1>365 Days of Photography</h1> <h3>Photographer: Jeremy Kendall</h3> {% block content %} {% endblock %} <hr /> layout.html: headers and content Wednesday, May 15, 13
  • 188. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %} Wednesday, May 15, 13
  • 189. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %} extends Wednesday, May 15, 13
  • 190. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %} extends <title /> Wednesday, May 15, 13
  • 191. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %} extends <title /> Wednesday, May 15, 13
  • 192. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %} extends <title /> iterator Wednesday, May 15, 13
  • 193. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %} extends <title /> iterator else Wednesday, May 15, 13
  • 194. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net{% endblock %} {% block content %} {% for image in images %} <div class="row"> <div class="span6"> <h2><a href="/{{image.day}}">{{image.day}}/365</a></h2> <p> <a href="/{{image.day}}"> <img src="{{image.sizes.size.5.source}}" /> </a> </p> <p>Posted {{image.posted|date("m/d/Y")}}</p> </div> </div> {% else %} <p>No images in project</p> {% endfor %} {% endblock %} extends <title /> iterator else format Wednesday, May 15, 13
  • 196. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net | Login{% endblock %} {% block content %} <div class="row"> <div class="span4"> <h2>Login</h2> {% if flash.error %} <p style="color: red;">{{flash.error}}</p> {% endif %} <form name="login" id="login" class="well" method="post"> // Login form . . . </form> </div> </div> {% endblock %} login.html Wednesday, May 15, 13
  • 197. {% extends 'layout.html' %} {% block page_title %}365.jeremykendall.net | Login{% endblock %} {% block content %} <div class="row"> <div class="span4"> <h2>Login</h2> {% if flash.error %} <p style="color: red;">{{flash.error}}</p> {% endif %} <form name="login" id="login" class="well" method="post"> // Login form . . . </form> </div> </div> {% endblock %} login.html Wednesday, May 15, 13
  • 198. The other templates are just more of the same Wednesday, May 15, 13
  • 199. Application Code Another big hurdle I had with Slim was figuring out how to organize my application code. Wednesday, May 15, 13
  • 200. Thin Controller, Fat Model Wednesday, May 15, 13
  • 201. Thin Controller, Fat Model Duh! Wednesday, May 15, 13
  • 202. Thin Controller, Fat Model Duh! All my application code is in a library directory Wednesday, May 15, 13
  • 203. Thin Controller, Fat Model Duh! All my application code is in a library directory It’s all namespaced, autoloadable, and testable Wednesday, May 15, 13
  • 204. Thin Controller, Fat Model Duh! All my application code is in a library directory It’s all namespaced, autoloadable, and testable I have (almost) zero business logic in index.php Wednesday, May 15, 13
  • 205. Thin Controller, Fat Model Duh! All my application code is in a library directory It’s all namespaced, autoloadable, and testable I have (almost) zero business logic in index.php Dependencies are managed by Composer Wednesday, May 15, 13
  • 206. Thin Controller, Fat Model Duh! All my application code is in a library directory It’s all namespaced, autoloadable, and testable I have (almost) zero business logic in index.php Dependencies are managed by Composer I could theoretically switch frameworks with very little effort Wednesday, May 15, 13
  • 208. Small but powerful GOTO 0 Wednesday, May 15, 13
  • 209. Small but powerful Excellent tools to write elegant code GOTO 0 Wednesday, May 15, 13
  • 210. Small but powerful Excellent tools to write elegant code Routing, middleware, hooks, templates, and views GOTO 0 Wednesday, May 15, 13
  • 211. Small but powerful Excellent tools to write elegant code Routing, middleware, hooks, templates, and views I just scratched the surface GOTO 0 Wednesday, May 15, 13
  • 212. Read Slim: slimframework.com Twig: twig.sensiolabs.org Composer: getcomposer.org MicroPHP Manifesto: microphp.org Flaming Archer: http://git.io/rH0nrg Wednesday, May 15, 13