SlideShare a Scribd company logo
1 of 214
Download to read offline
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

What's hot

JWT: jku x5u
JWT: jku x5uJWT: jku x5u
JWT: jku x5usnyff
 
Auto-Pilot for Apache Spark Using Machine Learning
Auto-Pilot for Apache Spark Using Machine LearningAuto-Pilot for Apache Spark Using Machine Learning
Auto-Pilot for Apache Spark Using Machine LearningDatabricks
 
Spring Boot & WebSocket
Spring Boot & WebSocketSpring Boot & WebSocket
Spring Boot & WebSocketMing-Ying Wu
 
Racing The Web - Hackfest 2016
Racing The Web - Hackfest 2016Racing The Web - Hackfest 2016
Racing The Web - Hackfest 2016Aaron Hnatiw
 
REST APIs with Spring
REST APIs with SpringREST APIs with Spring
REST APIs with SpringJoshua Long
 
Getting The Best Performance With PySpark
Getting The Best Performance With PySparkGetting The Best Performance With PySpark
Getting The Best Performance With PySparkSpark Summit
 
Node.js Express
Node.js  ExpressNode.js  Express
Node.js ExpressEyal Vardi
 
Curso de WebServlets (Java EE 7)
Curso de WebServlets (Java EE 7)Curso de WebServlets (Java EE 7)
Curso de WebServlets (Java EE 7)Helder da Rocha
 
Reactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-JavaReactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-JavaKasun Indrasiri
 
Sling Models Using Sightly and JSP by Deepak Khetawat
Sling Models Using Sightly and JSP by Deepak KhetawatSling Models Using Sightly and JSP by Deepak Khetawat
Sling Models Using Sightly and JSP by Deepak KhetawatAEM HUB
 
An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...
An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...
An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...Abhay Bhargav
 
Understanding Memory Management In Spark For Fun And Profit
Understanding Memory Management In Spark For Fun And ProfitUnderstanding Memory Management In Spark For Fun And Profit
Understanding Memory Management In Spark For Fun And ProfitSpark Summit
 
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...Edureka!
 

What's hot (20)

JWT: jku x5u
JWT: jku x5uJWT: jku x5u
JWT: jku x5u
 
Auto-Pilot for Apache Spark Using Machine Learning
Auto-Pilot for Apache Spark Using Machine LearningAuto-Pilot for Apache Spark Using Machine Learning
Auto-Pilot for Apache Spark Using Machine Learning
 
Spring Boot & WebSocket
Spring Boot & WebSocketSpring Boot & WebSocket
Spring Boot & WebSocket
 
Racing The Web - Hackfest 2016
Racing The Web - Hackfest 2016Racing The Web - Hackfest 2016
Racing The Web - Hackfest 2016
 
REST APIs with Spring
REST APIs with SpringREST APIs with Spring
REST APIs with Spring
 
Getting The Best Performance With PySpark
Getting The Best Performance With PySparkGetting The Best Performance With PySpark
Getting The Best Performance With PySpark
 
Nodejs buffers
Nodejs buffersNodejs buffers
Nodejs buffers
 
Security in NodeJS applications
Security in NodeJS applicationsSecurity in NodeJS applications
Security in NodeJS applications
 
Servlet
ServletServlet
Servlet
 
session.ppt
session.pptsession.ppt
session.ppt
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
Node.js Express
Node.js  ExpressNode.js  Express
Node.js Express
 
Curso de WebServlets (Java EE 7)
Curso de WebServlets (Java EE 7)Curso de WebServlets (Java EE 7)
Curso de WebServlets (Java EE 7)
 
Reactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-JavaReactive Programming in Java 8 with Rx-Java
Reactive Programming in Java 8 with Rx-Java
 
Sling Models Using Sightly and JSP by Deepak Khetawat
Sling Models Using Sightly and JSP by Deepak KhetawatSling Models Using Sightly and JSP by Deepak Khetawat
Sling Models Using Sightly and JSP by Deepak Khetawat
 
Git & Github for beginners
Git & Github for beginnersGit & Github for beginners
Git & Github for beginners
 
PHP - Introduction to PHP AJAX
PHP -  Introduction to PHP AJAXPHP -  Introduction to PHP AJAX
PHP - Introduction to PHP AJAX
 
An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...
An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...
An Attacker's View of Serverless and GraphQL Apps - Abhay Bhargav - AppSec Ca...
 
Understanding Memory Management In Spark For Fun And Profit
Understanding Memory Management In Spark For Fun And ProfitUnderstanding Memory Management In Spark For Fun And Profit
Understanding Memory Management In Spark For Fun And Profit
 
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js +  Expres...
Node.js Express Tutorial | Node.js Tutorial For Beginners | Node.js + Expres...
 

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

Tek 2013 - Building Web Apps from a New Angle with AngularJS
Tek 2013 - Building Web Apps from a New Angle with AngularJSTek 2013 - Building Web Apps from a New Angle with AngularJS
Tek 2013 - Building Web Apps from a New Angle with AngularJSPablo Godel
 
Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!cloudbring
 
Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)
Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)
Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)Cyrille Le Clerc
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of PluginYasuo Harada
 
What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...Richard McIntyre
 
OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...
OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...
OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...NETWAYS
 
High Performance WordPress
High Performance WordPressHigh Performance WordPress
High Performance WordPressvnsavage
 
Building a Startup Stack with AngularJS
Building a Startup Stack with AngularJSBuilding a Startup Stack with AngularJS
Building a Startup Stack with AngularJSFITC
 
Application Logging With The ELK Stack
Application Logging With The ELK StackApplication Logging With The ELK Stack
Application Logging With The ELK Stackbenwaine
 
Functional Reactive Programming in the Netflix API
Functional Reactive Programming in the Netflix APIFunctional Reactive Programming in the Netflix API
Functional Reactive Programming in the Netflix APIC4Media
 
Advanced App Building - Tips, Tricks & Lessons Learned
Advanced App Building - Tips, Tricks & Lessons LearnedAdvanced App Building - Tips, Tricks & Lessons Learned
Advanced App Building - Tips, Tricks & Lessons LearnedJay Graves
 
Fast Slim Correct: The History and Evolution of JavaScript.
Fast Slim Correct: The History and Evolution of JavaScript.Fast Slim Correct: The History and Evolution of JavaScript.
Fast Slim Correct: The History and Evolution of JavaScript.John Dalziel
 
Drupal 8 configuration system for coders and site builders - Drupalaton 2013
Drupal 8 configuration system for coders and site builders - Drupalaton 2013Drupal 8 configuration system for coders and site builders - Drupalaton 2013
Drupal 8 configuration system for coders and site builders - Drupalaton 2013swentel
 
Learn flask in 90mins
Learn flask in 90minsLearn flask in 90mins
Learn flask in 90minsLarry Cai
 

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

Tek 2013 - Building Web Apps from a New Angle with AngularJS
Tek 2013 - Building Web Apps from a New Angle with AngularJSTek 2013 - Building Web Apps from a New Angle with AngularJS
Tek 2013 - Building Web Apps from a New Angle with AngularJS
 
Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!Rapid Prototyping FTW!!!
Rapid Prototyping FTW!!!
 
Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)
Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)
Open Source Monitoring for Java with JMX and Graphite (GeeCON 2013)
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of Plugin
 
A false digital alibi on mac os x
A false digital alibi on mac os xA false digital alibi on mac os x
A false digital alibi on mac os x
 
What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...What is this DI and AOP stuff anyway...
What is this DI and AOP stuff anyway...
 
OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...
OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...
OSDC 2014: Ole Michaelis & Sönke Rümpler: Make it SOLID - Software Architectu...
 
High Performance WordPress
High Performance WordPressHigh Performance WordPress
High Performance WordPress
 
Building a Startup Stack with AngularJS
Building a Startup Stack with AngularJSBuilding a Startup Stack with AngularJS
Building a Startup Stack with AngularJS
 
ZF3 introduction
ZF3 introductionZF3 introduction
ZF3 introduction
 
Application Logging With The ELK Stack
Application Logging With The ELK StackApplication Logging With The ELK Stack
Application Logging With The ELK Stack
 
Functional Reactive Programming in the Netflix API
Functional Reactive Programming in the Netflix APIFunctional Reactive Programming in the Netflix API
Functional Reactive Programming in the Netflix API
 
Mojolicious lite
Mojolicious liteMojolicious lite
Mojolicious lite
 
Advanced App Building - Tips, Tricks & Lessons Learned
Advanced App Building - Tips, Tricks & Lessons LearnedAdvanced App Building - Tips, Tricks & Lessons Learned
Advanced App Building - Tips, Tricks & Lessons Learned
 
Empezando con Twig
Empezando con TwigEmpezando con Twig
Empezando con Twig
 
Fast Slim Correct: The History and Evolution of JavaScript.
Fast Slim Correct: The History and Evolution of JavaScript.Fast Slim Correct: The History and Evolution of JavaScript.
Fast Slim Correct: The History and Evolution of JavaScript.
 
Drupal 8 configuration system for coders and site builders - Drupalaton 2013
Drupal 8 configuration system for coders and site builders - Drupalaton 2013Drupal 8 configuration system for coders and site builders - Drupalaton 2013
Drupal 8 configuration system for coders and site builders - Drupalaton 2013
 
Demystifying Maven
Demystifying MavenDemystifying Maven
Demystifying Maven
 
Introduce Django
Introduce DjangoIntroduce Django
Introduce Django
 
Learn flask in 90mins
Learn flask in 90minsLearn flask in 90mins
Learn flask in 90mins
 

More from Jeremy Kendall

Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPJeremy Kendall
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPJeremy Kendall
 
5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) CodeJeremy Kendall
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency ManagementJeremy Kendall
 
Php 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodPhp 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodJeremy Kendall
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkJeremy Kendall
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkJeremy Kendall
 
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the GoodPHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the GoodJeremy Kendall
 
Intro to #memtech PHP 2011-12-05
Intro to #memtech PHP   2011-12-05Intro to #memtech PHP   2011-12-05
Intro to #memtech PHP 2011-12-05Jeremy Kendall
 
TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25Jeremy Kendall
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormZend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormJeremy Kendall
 
Zero to ZF in 10 Minutes
Zero to ZF in 10 MinutesZero to ZF in 10 Minutes
Zero to ZF in 10 MinutesJeremy Kendall
 
Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief exampleJeremy Kendall
 
A Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormA Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormJeremy Kendall
 
Zero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesZero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesJeremy Kendall
 

More from Jeremy Kendall (16)

Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
 
Leveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHPLeveraging the Power of Graph Databases in PHP
Leveraging the Power of Graph Databases in PHP
 
5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code5 Ways to Awesome-ize Your (PHP) Code
5 Ways to Awesome-ize Your (PHP) Code
 
Game Changing Dependency Management
Game Changing Dependency ManagementGame Changing Dependency Management
Game Changing Dependency Management
 
Php 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the GoodPhp 102: Out with the Bad, In with the Good
Php 102: Out with the Bad, In with the Good
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
 
Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
 
Php 101: PDO
Php 101: PDOPhp 101: PDO
Php 101: PDO
 
PHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the GoodPHP 102: Out with the Bad, In with the Good
PHP 102: Out with the Bad, In with the Good
 
Intro to #memtech PHP 2011-12-05
Intro to #memtech PHP   2011-12-05Intro to #memtech PHP   2011-12-05
Intro to #memtech PHP 2011-12-05
 
TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25TDD in PHP - Memphis PHP 2011-08-25
TDD in PHP - Memphis PHP 2011-08-25
 
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_FormZend_Form to the Rescue - A Brief Introduction to Zend_Form
Zend_Form to the Rescue - A Brief Introduction to Zend_Form
 
Zero to ZF in 10 Minutes
Zero to ZF in 10 MinutesZero to ZF in 10 Minutes
Zero to ZF in 10 Minutes
 
Tdd in php a brief example
Tdd in php   a brief exampleTdd in php   a brief example
Tdd in php a brief example
 
A Brief Introduction to Zend_Form
A Brief Introduction to Zend_FormA Brief Introduction to Zend_Form
A Brief Introduction to Zend_Form
 
Zero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutesZero to Zend Framework in 10 minutes
Zero to Zend Framework in 10 minutes
 

Recently uploaded

unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxBkGupta21
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfPrecisely
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESmohitsingh558521
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 

Recently uploaded (20)

unit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptxunit 4 immunoblotting technique complete.pptx
unit 4 immunoblotting technique complete.pptx
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdfHyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
Hyperautomation and AI/ML: A Strategy for Digital Transformation Success.pdf
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICESSALESFORCE EDUCATION CLOUD | FEXLE SERVICES
SALESFORCE EDUCATION CLOUD | FEXLE SERVICES
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 

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