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

Spring introduction
Spring introductionSpring introduction
Spring introductionManav Prasad
 
Automação de testes de API utilizando Postman
Automação de testes de API utilizando PostmanAutomação de testes de API utilizando Postman
Automação de testes de API utilizando PostmanLucas Amaral
 
AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)
AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)
AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)Amazon Web Services
 
Spring boot
Spring bootSpring boot
Spring bootsdeeg
 
Karate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingKarate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingRoman Liubun
 
An Introduction To Automated API Testing
An Introduction To Automated API TestingAn Introduction To Automated API Testing
An Introduction To Automated API TestingSauce Labs
 
Automação de Teste para REST, Web e Mobile
Automação de Teste para REST, Web e MobileAutomação de Teste para REST, Web e Mobile
Automação de Teste para REST, Web e MobileElias Nogueira
 
Introduction to Java 8
Introduction to Java 8Introduction to Java 8
Introduction to Java 8Knoldus Inc.
 
Building a REST Service in minutes with Spring Boot
Building a REST Service in minutes with Spring BootBuilding a REST Service in minutes with Spring Boot
Building a REST Service in minutes with Spring BootOmri Spector
 
Java 8 - Features Overview
Java 8 - Features OverviewJava 8 - Features Overview
Java 8 - Features OverviewSergii Stets
 
Getting started with karate dsl
Getting started with karate dslGetting started with karate dsl
Getting started with karate dslKnoldus Inc.
 
Postman Collection Format v2.0 (pre-draft)
Postman Collection Format v2.0 (pre-draft)Postman Collection Format v2.0 (pre-draft)
Postman Collection Format v2.0 (pre-draft)Postman
 
Rest API with Swagger and NodeJS
Rest API with Swagger and NodeJSRest API with Swagger and NodeJS
Rest API with Swagger and NodeJSLuigi Saetta
 
BDD Approach with Karate Framework in Service Tests
BDD Approach with Karate Framework in Service TestsBDD Approach with Karate Framework in Service Tests
BDD Approach with Karate Framework in Service Testskloia
 
O testador esta morto!
O testador esta morto!O testador esta morto!
O testador esta morto!Elias Nogueira
 

What's hot (20)

Spring introduction
Spring introductionSpring introduction
Spring introduction
 
Automação de testes de API utilizando Postman
Automação de testes de API utilizando PostmanAutomação de testes de API utilizando Postman
Automação de testes de API utilizando Postman
 
AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)
AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)
AWS re:Invent 2016: Deep Dive on Amazon Aurora (DAT303)
 
Spring boot
Spring bootSpring boot
Spring boot
 
vitest-en.pdf
vitest-en.pdfvitest-en.pdf
vitest-en.pdf
 
Karate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testingKarate - powerful and simple framework for REST API automation testing
Karate - powerful and simple framework for REST API automation testing
 
An Introduction To Automated API Testing
An Introduction To Automated API TestingAn Introduction To Automated API Testing
An Introduction To Automated API Testing
 
Automação de Teste para REST, Web e Mobile
Automação de Teste para REST, Web e MobileAutomação de Teste para REST, Web e Mobile
Automação de Teste para REST, Web e Mobile
 
Introduction to Java 8
Introduction to Java 8Introduction to Java 8
Introduction to Java 8
 
Building a REST Service in minutes with Spring Boot
Building a REST Service in minutes with Spring BootBuilding a REST Service in minutes with Spring Boot
Building a REST Service in minutes with Spring Boot
 
What's new in Java 11
What's new in Java 11What's new in Java 11
What's new in Java 11
 
Java 8 - Features Overview
Java 8 - Features OverviewJava 8 - Features Overview
Java 8 - Features Overview
 
Angular Unit Testing
Angular Unit TestingAngular Unit Testing
Angular Unit Testing
 
Getting started with karate dsl
Getting started with karate dslGetting started with karate dsl
Getting started with karate dsl
 
Postman Collection Format v2.0 (pre-draft)
Postman Collection Format v2.0 (pre-draft)Postman Collection Format v2.0 (pre-draft)
Postman Collection Format v2.0 (pre-draft)
 
Rest assured
Rest assuredRest assured
Rest assured
 
Rest API with Swagger and NodeJS
Rest API with Swagger and NodeJSRest API with Swagger and NodeJS
Rest API with Swagger and NodeJS
 
BDD Approach with Karate Framework in Service Tests
BDD Approach with Karate Framework in Service TestsBDD Approach with Karate Framework in Service Tests
BDD Approach with Karate Framework in Service Tests
 
Selenium
SeleniumSelenium
Selenium
 
O testador esta morto!
O testador esta morto!O testador esta morto!
O testador esta morto!
 

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

Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsPrecisely
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptxLBM Solutions
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Allon Mureinik
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
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
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Neo4j
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 

Recently uploaded (20)

Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 
Pigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping ElbowsPigging Solutions Piggable Sweeping Elbows
Pigging Solutions Piggable Sweeping Elbows
 
Unlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power SystemsUnlocking the Potential of the Cloud for IBM Power Systems
Unlocking the Potential of the Cloud for IBM Power Systems
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Key Features Of Token Development (1).pptx
Key  Features Of Token  Development (1).pptxKey  Features Of Token  Development (1).pptx
Key Features Of Token Development (1).pptx
 
Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)Injustice - Developers Among Us (SciFiDevCon 2024)
Injustice - Developers Among Us (SciFiDevCon 2024)
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
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
 
Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024Build your next Gen AI Breakthrough - April 2024
Build your next Gen AI Breakthrough - April 2024
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 

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