germania-kg/responder

Responder interfaces and classes for ADR pattern

1.2.6 2021-10-27 14:38 UTC

This package is auto-updated.

Last update: 2024-10-27 21:24:40 UTC


README

Germania KG ยท Responder

Packagist PHP version Build Status Scrutinizer Code Quality Code Coverage Build Status

Installation

$ composer require germania-kg/responder

Responder classes

Interfaces

ResponderInterface

The Germania\Responder\ResponderInterface provides a createResponse method which accepts data to create a PSR-7 responde from. The data can be of any type.

Implementing classes must also be callable and implement an invoke function with same signature.

  • Should throw ResponderInvalidArgumentException when passed data is incorrect.
  • Should throw ResponderRuntimeException when s.th. bad happens underway.
public function createResponse( $data ) : ResponseInterface;
public function __invoke( $data ) : ResponseInterface;

ResponderExceptionInterface

The Germania\Responder\ResponderExceptionInterface is the base interface all Responder exceptions have in common. See Exceptions section.

TwigResponder

The constructor accepts a Twig Environment, the name of the array field which holds the template, and optionally a default context variables array.

You can optionally pass a custom PSR-17 Response Factory, per default the Response factory from slim/psr7 will be used.

The template $data passed to createResponse method will be merged with $default_context.

Setup

<?php
use Germania\Responder\TwigResponder;

// Have Twig\Environment at hand
$twig = ...;
$responder = new TwigResponder($twig, "template");

// These are optional
$default_context = array();
$psr17 = new \Nyholm\Psr7\Factory\Psr17Factory;
$responder = new TwigResponder($twig, "template", $default_context, $psr17);

Configuration

$responder->setTwig( $twig );
$responder->setTemplateField('template');
$responder->setDefaultContext( array('another' => 'data') );
$responder->setResponseFactory($psr17);

# Fallback when context lacks 'template' element
$responder->setDefaultTemplate('website.tpl');

Usage

$data = array(
	'template' => 'website.tpl',
  'foo' => 'bar'
)

// These are equal:
$response = $responder->createResponse($data);
$response = $responder($data);

JsonResponder

Creates a JSON response from the given data. Implements ResponderInterface. Responses will have Content-type: application/json.

You can optionally pass a custom PSR-17 Response Factory, per default the Response factory from slim/psr7 will be used.

Setup

<?php
use Germania\Responder\JsonResponder;
use Germania\Responder\ResponderExceptionInterface;
use Slim\Psr7\Factory\ResponseFactory;

$json = \JSON_PRETTY_PRINT;
$psr17 = new ResponseFactory; // Optional

$responder = new JsonResponder($json);
$responder = new JsonResponder($json, $psr17);

Configuration

$responder->setJsonOptions( \JSON_PRETTY_PRINT );
$responder->setResponseContentType('application/json');
$responder->setResponseFactory($psr17);

Usage

try {
  $data = array('foo' => 'bar');
  
  // These
  $response = $responder->createResponse($data);
  $response = $responder($data);

  // Psr\Http\Message\ResponseInterface  
  return $response;
}
catch(ResponderExceptionInterface $e) {
  echo $e->getMessage();
}

CallbackResponder

This class applies a callback to the given data before passing to any inner responder which must be instance of ResponderInterface.

<?php
use Germania\Responder\CallbackResponder;
use Germania\Responder\JsonResponder;

// Do-nothing callback for demonstration purposes
$callback = function($item) { return $item; };
// Any kind of ResponderInterface will do
$inner = new JsonResponder();

$responder = new CallbackResponder($callback, $inner);

Configuration

$responder->setCallback(function($item) { return $item; });
$responder->setResponder($other_responder);

NoContentResponder

Produced empty responses with 204 status.

<?php
use Germania\Responder\NoContentResponder;

$responder = new NoContentResponder();

ErrorResponder

The ErrorResponder mangles Throwables and acts as decorator for another ResponderInterface. It extends from ResponderDecoratorAbstract and implements ResponderInterface.

The error passed to createResponse is converted to an array with an errors element that contains the error and all its previous errors (depending on debug mode). The array will then be passed to the inner responder.

The default response status code is 500 and can be adjusted on call.

Extends from ResponderDecoratorAbstract and implements ResponderInterface.

Setup

<?php
use Germania\Responder\ErrorResponder;
use Germania\Responder\JsonResponder;

$debug = true;
$inner_responder = new JsonResponder(\JSON_PRETTY_PRINT);

$error_responder = new ErrorResponder($debug, $inner_responder);

Configuration

$responder->setDebug( false );

Usage

Optionally pass a custom status code; default is 500.

try {
  // Throw something here
}
catch(\Throwable $e) {
  $response = $error_responder->createResponse($e);
  echo $response->getStatusCode(); // 500
  
  $response = $error_responder->createResponse($e, 503);  
  $response = $error_responder->createResponse($e, 400);

	// Psr\Http\Message\ResponseInterface    
  return $response;
}
$responder->setDebug( false );

Response Examples

These examples assume a JsonResponder was used as inner responder. Note the errors element: it contains the error object, and optionally, its previous errors.

{
  "errors": [
    {
      "type": "RuntimeException",
      "message": "Boo!",
      "code": 0
    }
  ]
}

When debug is TRUE, previous exceptions are included, and the location of the occurrence:

{
  "errors": [
    {
      "type": "Exception",
      "message": "Outer",
      "code": 0,
      "location": "\/path\/to\/file.php:67"
    },
    {
      "type": "MyLibrary\/CustomException",
      "message": "Boo!",
      "code": 0,
      "location": "\/path\/to\/file.php:64"
    }
  ]
}

Exceptions

  • Germania\Responder\ResponderExceptionInterface
  • Germania\Responder\ResponderInvalidArgumentException extends \InvalidArgumentException and implements ResponderExceptionInterface.
  • Germania\Responder\ResponderRuntimeException extends \RuntimeException and implements ResponderExceptionInterface.

Example: Deal with errors

<?php
use Germania\Responder\ResponderInvalidArgumentException;
use Germania\Responder\ResponderRuntimeException;
use Germania\Responder\ResponderExceptionInterface;

try {
  $data = array('foo' => 'bar');
  
  // These are equal:
  $response = $responder->createResponse($data);
  $response = $responder($data);

  // Psr\Http\Message\ResponseInterface  
  return $response;
}
catch(ResponderInvalidArgumentException $e) {
  // $data has been invalid
}
catch(ResponderRuntimeException $e) {
  // Something bad happened
}
catch(ResponderExceptionInterface $e) {
  // Catch any other Responder exceptions
}

Traits

ResponderTrait

Use the Germania\Responder\ResponderTrait in your classes:

// @var ResponderInterface
protected $responder;

// @return ResponderInterface|null
public function getResponder() : ?ResponderInterface;

// @param ResponderInterface $responder
// @return static
public function setResponder( ResponderInterface $responder );

ResponseFactoryTrait

TwigResponder and JsonResponder use the Germania\Responder\ResponseFactoryTrait. Per default they use the Response factory from nyholm/psr7.

// @var ResponseFactory
public $response_factory;

// @param ResponseFactoryInterface $response_factory
// @return static
public function setResponseFactory(ResponseFactoryInterface $response_factory ) : static

// @return ResponseFactoryInterface  
public function getResponseFactory() : ResponseFactoryInterface;

Development

$ git clone git@github.com:GermaniaKG/Responder.git
$ cd Responder
$ composer install