Slim 3

The Good, the Bad and the Beta

Ian Littman (@iansltx)

http://ian.im/slim3july15

echo about($me);

  • I write and maintain web apps
    • Mainly APIs
    • Aura, Phalcon, Slim
    • Currencly freelancing (and very busy)
  • I've written a couple libraries
  • I use Slim 3 in prod right now
  • I host Austin Web Developer Lunch (see Meetup)

What we'll talk about

  • Why Slim?
  • How Slim 3 works
    • Middleware + PSR-7
    • Routing (FastRoute)
    • Dependency Injection + Container Interop
  • Slim 3 in the real world
    • A new microservice
    • An upgrade of Raphple (rendered views, Slim 2)

Why Slim? (vs. Silex, Lumen)

  • Thin and light - 2727 logical lines of code for slim/slim
  • Few dependencies
    • FastRoute
    • Pimple DI (can be replaced)
    • PSR-7 Message Interface
    • Continer-Interop Interface
  • Standards based
    • Mix and match libraries
    • Defaults can be overridden (DIC, HTTP message)
  • Not highly opinionated

What's it good for?

  • Prototyping
  • APIs (and microservices!)
  • Not being a full-stack framework

Let's get started

​<?php require __DIR__ . '/vendor/autoload.php';

$app = new \Slim\App();
$app->get('/', function($req, $res) {
    return $res->write('Hello world!');
});
$app->get('/{name}', function($req, $res, $args) {
    return $res->write('Hello ' . $args['name'] . '!');
});

$app->run();

Let's get it running

composer init -s dev
composer require slim/slim
php -S localhost:8080 index.php

How It Works: Middleware

  • Sencha Connect + Express
  • Ruby Rack (and, yes, Sinatra)
  • Layered execution
    • Each middleware is a callable with
      • Request
      • Response
      • Callback to next middleware (which may be the app)
    • Middleware must return a Response
      • Short-circuit return (e.g. caching, failed auth)
      • Return (potentially after further modification result of $next($req, $res);
  • Immutable objects
  • Can be assigned globally, or to a single route

How It Works: Middleware

<?php // $app is instantiated above

$app->add(function($req, $res, $next) { // runs second, right before app
    if (!$this->auth->isLoggedIn($req))
        return new UnauthedResponse; // assuming it's PSR-7 compliant
    return $next($req, $res);
});

$app->add(function($req, $res, $next) { // runs first (outermost)
    $startTime = microtime(true);
    $res = $next($req->withHeader('Start-Time', $startTime), $res);
    return $res->write(
        '<!-- Total Time: ' . microtime(true) - $startTime . ' -->');
});

How It Works: Routing

  • Super-fast regex-based router
    • /users
    • /users/{id}
    • /users/{id:/d+}
  • Routes can cover one or more methods
    • $app->get('/my/route', function($req, $res, $args) {...});
    • $app->match(['PUT', 'PATCH'], '/not/restful/{id}', $closure);
  • Routes can dispatch to closures or call objects from the DI container
    • $app->get('/me', 'MyController::someAction');
    • Looks up MyController in the DI container, then calls someAction with the same arguments as a closure would get

  • Route groups (prefixes) are supported

How It Works: DI

$container = new \Slim\Container(); // Pimple + Slim defaults
$container['myService'] = function($c) {
    return new MyService($c->get('someOtherDependency');
};

// if you provide the expected services,
// any Container Interop DI container will work here
$app = new \Slim\App($container);

$app->get('/', function($req, $res) { // $this is bound to app
    // app proxies __get() to container
    return $res->write($this->myService->doACoolThing($req);
});

No longer in Slim 3

  • View rendering (use Twig if you want)
  • Encryption
  • Response cookie handling (coming back soon, maybe...)
  • Session/flash support (Composer packages are available)
  • Echoing out...just kidding, you can still echo out from route closures

Case Study #1: Microservice

  • Purpose: render PDFs from templates using pdflib ext
    • Internal API, no auth, HAL formatting
    • Multiple clients
    • Heartbeat endpoint with failover
    • Streamed request logs, custom errors
  • LOC: ~300
  • Brand new, using Slim 3 from ~3 months ago

Case Study #1: Microservice

  • The Good
    • Fast
    • Relatively quick to dev on, despite lack of docs
    • Overriding $app['errorHandler'] is useful
      • Returns a function that returns a function...
      • ...that takes $req, $res, $exception as params...
      • ...and returns a response
  • The Bad
    • See "The Beta"
  • The Beta
    • Lack of docs (moving target due to pre-alpha)
    • No multipart/form-data support at the time
      • Uploaded files support still missing
      • Workaround: add DI proxies to $_POST, $_FILES

Case Study #2: Raphple

  • Purpose: raffle stuff off using SMS via Twilio
    • Rendered views
    • ~5 hours of effort
    • Built in Slim 2 with a lot of closure use's
  • Non-template LOC before/after: ~200/~280
    • More classes, no more pulling from global scope
    • Added quick 'n' dirty view rendering
    • Added quick 'n' dirty cookie setting
  • ~1.5 hours, including testing, today
  • 2790c230a6cf83367d92f3c2d1036c16e695ed71

Case Study #2: Raphple

  • The Good
    • Method-chaining on responses is cleaner than v2
    • Logic changes were minimal
    • Request-related changes were straightforward
    • Framework includes less stuff that isn't used
  • The Bad
    • An easy way to set response cookies was removed
    • High % of route LOC changed due to no render() and switch in request format (but I knew that going in)
  • The Beta
    • Docs are sparse and not up to date (e.g. cookies)

Should I use Slim?

  • When?

    • Now: maybe

    • When released: better idea

  • How?

    • APIs: good call

    • Microservices: yep

    • If expecting an opinionated framework: maybe not

    • If you're opinionated: you'll probably like it

More Resources

Q&A