SlideShare una empresa de Scribd logo
1 de 214
Descargar para leer sin conexión
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

Más contenido relacionado

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

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
 
Pyramid Lighter/Faster/Better web apps
Pyramid Lighter/Faster/Better web appsPyramid Lighter/Faster/Better web apps
Pyramid Lighter/Faster/Better web appsDylan Jay
 
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010singingfish
 

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

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
 
Pyramid Lighter/Faster/Better web apps
Pyramid Lighter/Faster/Better web appsPyramid Lighter/Faster/Better web apps
Pyramid Lighter/Faster/Better web apps
 
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
Don't RTFM, WTFM - Open Source Documentation - German Perl Workshop 2010
 

Más de 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
 

Más de 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
 

Último

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processorsdebabhi2
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
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
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsJoaquim Jorge
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?Antenna Manufacturer Coco
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessPixlogix Infotech
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024The Digital Insurer
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 

Último (20)

TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
Exploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone ProcessorsExploring the Future Potential of AI-Enabled Smartphone Processors
Exploring the Future Potential of AI-Enabled Smartphone Processors
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
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
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
Bajaj Allianz Life Insurance Company - Insurer Innovation Award 2024
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 

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