Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Slim 4 Cheatsheet

Related Posts

Getting Started

Install the framework with:

composer require slim/slim 4.*
composer require slim/psr7

We went with the slim/psr7 implementation of the request and response, but you can use others.

Then get started by putting the following in ./public/index.php

<?php
require_once(__DIR__ . '/../vendor/autoload.php');
$app = Slim\Factory\AppFactory::create();
$app->addErrorMiddleware($displayErrorDetails=true, $logErrors=true, $logErrorDetails=true);

$app->get('/', function (Psr\Http\Message\ServerRequestInterface $request, Psr\Http\Message\ResponseInterface $response) {
    $body = $response->getBody();
    $body->write('Hello world'); // returns number of bytes written
    $newResponse = $response->withBody($body);
    return $newResponse;
});

$app->run();

run with:

php -S localhost ./public/index.php

Routing

Route Variables

Having a route like /hello?name=john is pretty ugly. It would be much nicer to have a route like /hello/john. You can use something like below to allow a variable name in the route and pass it to a function

$app->get('/hello/{name}', function (Psr\Http\Message\ServerRequestInterface $request, Psr\Http\Message\ResponseInterface $response, array $args) {
    $name = $args['name'];
    $body = $response->getBody();
    $body->write('Hello world'); // returns number of bytes written
    $newResponse = $response->withBody($body);
    return $newResponse;
});

Body Parameters

To get the body parameters, you need to use:

$request->getParsedBody()

... however, for that to work, you need to ensure the body parsing middleware is run by adding:

$app->addBodyParsingMiddleware();

I have not tested if you do not need to make this addition when using x-www-form-urlencoded fields instead of a JSON string body.

Returning JSON Responses

$responseData = array('hello' => 'world');
$responseBody = json_encode($responseData);
$response = new \Slim\Psr7\Response($status=200);
$response->getBody()->write($responseBody);
$response = $response->withHeader('Content-Type', 'application/json');
return $response;

Returning A Redirect

This is easier with just providing you a function:

/**
 * Create a redirect response;
 * @param string $newLocation - where you wish to redirect the user.
 * @param Slim\Psr7\Response $response - the existing response to work with
 * @param bool $useTemporaryRedirect - whether this redirect is temporary, or browser should cache it.
 * @return Slim\Psr7\Response - the redirect response.
 */
function redirect(
    string $newLocation, 
    bool $useTemporaryRedirect=true
) : Slim\Psr7\Response
{
    $httpCode = ($useTemporaryRedirect) ? 302 : 301;
    $response = new \Slim\Psr7\Response($httpCode);
    $response = $response->withHeader('Location', $newLocation);
    return $response;
}

Abstract Slim Controller

Whenever I create a new Slim project, I always like creating this class right off the bat which all my "concrete" controllers extend. This way I access the request, response, and args with $this->m_.

<?php


abstract class AbstractSlimController
{
    protected Psr\Http\Message\RequestInterface $m_request;
    protected \Psr\Http\Message\ResponseInterface $m_response;
    protected $m_args;


    public function __construct(\Psr\Http\Message\RequestInterface $request, \Psr\Http\Message\ResponseInterface $response, $args) {
        $this->m_request = $request;
        $this->m_response = $response;
        $this->m_args = $args;
    }


    // this one is optional - refer to Slim3 - Simplifying Routing At Scale
    // https://blog.programster.org/slim3-simplifying-routing-at-scale
    abstract public static function registerRoutes($app);
}

Middleware

Middleware changed significantly between Slim 3 and 4. This is because Slim 4 makes use of PSR-15 for middleware. This means that if you write middleware for Slim, you can use it anywhere else that makes use of PSR-15 as well. The key difference is that instead of the body of your middleware being inside __invoke , it is now within a public method called process, which only has parameters for the request and the handler (there is no parameter for response anymore).

Example

Thus, if you wanted some authentication middleware, that checks if the user is logged in as an admin if they go to anywhere under /admin, then it would look something like this:

<?php

use \Psr\Http\Message\ServerRequestInterface;
use \Psr\Http\Server\RequestHandlerInterface;
use \Psr\Http\Message\ResponseInterface;

class MiddlewareAdminAuth implements \Psr\Http\Server\MiddlewareInterface
{
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $uri = $_SERVER['REQUEST_URI'];

        if (str_starts_with($uri, "/admin/") && AuthLib::isLoggedInAsAdmin() === false)
        {
            $response = new \Slim\Psr7\Response(302);
            $response = $response->withHeader('Location', '/login');
        }
        else
        {
            $response = $handler->handle($request);
        }

        return $response;
    }
}

You would apply the middleware to the entire app (all routes) like so:

$app = Slim\Factory\AppFactory::create();
$app->add(new MiddlewareHttpBasicAuth());

Alternatively, if you want to apply it to individual routes, you can do so:

$app->get('/', function (Request $request, Response $response, $args) {
    $response->getBody()->write('Hello ');
    return $response;
})->add(new MyMiddlewareClass());

Also, you can apply it to a group of routes:

$app->group('/utils', function(\Slim\Routing\RouteCollectorProxy $group) {
    $group->get('/date', function (Request $request, Response $response) {
        // stuff here
    });

    $group->get('/time', function (Request $request, Response $response) {
        // stuff here
    });
})->add(new MyMiddlewareClass());

You can provide functions instead of classes for middleware, but it gets messy. Find out more at the official docs for middleware.

References

Last updated: 25th September 2021
First published: 15th August 2020