PHPackages                             crell/carica - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [HTTP &amp; Networking](/categories/http)
4. /
5. crell/carica

ActiveLibrary[HTTP &amp; Networking](/categories/http)

crell/carica
============

0.4.3(8mo ago)71151[1 PRs](https://github.com/Crell/Carica/pulls)1LGPL-3.0-or-laterPHPPHP ~8.4CI passing

Since May 13Pushed 7mo ago1 watchersCompare

[ Source](https://github.com/Crell/Carica)[ Packagist](https://packagist.org/packages/crell/carica)[ Docs](https://github.com/Crell/Carica)[ GitHub Sponsors](https://github.com/Crell)[ RSS](/packages/crell-carica/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependencies (16)Versions (11)Used By (1)

Carica HTTP framework
=====================

[](#carica-http-framework)

[![Latest Version on Packagist](https://camo.githubusercontent.com/25860d6634f84df0d764d4a229797adbb52d2816c7e8b9611b3827537922f613/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f4372656c6c2f4361726963612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/Crell/Carica)[![Software License](https://camo.githubusercontent.com/bf1c19b4a07c841715e713542bd6e9c2aaf75ffa2ee2aa3987814d99311f799b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4c47504c76332d677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![Total Downloads](https://camo.githubusercontent.com/cbba91f58f170d9ae014558a95fd401d90a2cbc4eb6b4bb06afd4d4d01820b29/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f4372656c6c2f4361726963612e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/Crell/Carica)

Carica is a collection of loosely coupled tools for working with the PSR HTTP stack.

It includes a robust routing bridge that allows using any arbitrary router, but then supports mapping different values directly to the action for each route. It can map placeholders from the route itself, HTTP query arguments, arbitrary `ServerRequest` attributes, the parsed body of the request (it will parse it for you, too), and even uploaded files. It can even convert route parameters into a loaded object, just by examining the type signature of the action.

Similarly, the action method may return a `ResponseInterface` object, or any other value it chooses. That value can then be converted into a `ResponseInterface` by an `ActionResultRenderer` that you provide. A "turn it all into JSON" one is provided, but it's straightforward to write your own.

All of these features are commonly found only in full stack frameworks, that require you to buy into their specific way of working. Carica is just a series of [PSR-15](https://www.php-fig.org/psr/psr-15/) middleware designed to work together. They can be easily mixed-and-matched into whatever system you want, with or without any of the above-mentioned features.

For simple "I just want something that runs" usage, use [`StandardApplication`](src/StandardApplication.php). With a few constructor parameters, it will give you a fully running system. Or, clone it and modify to suit your needs. Or use it as a guide to wire up your DI container. It is entirely up to you.

*Carica* is the scientific name for the fig tree.

Usage
-----

[](#usage)

### Basic Setup

[](#basic-setup)

We'll assume that you're using the `StandardApplication`, which comes with all the bells and whistles. The particular bells and whistles are listed further below, and if you make your own custom configuration you may leave out any particular bell or whistle if desired.

```
// More on this in a moment.
$router = make_a_router();

$app = new \Crell\Carica\StandardApplication(
responseFactory: $aPSR17ResponseFactory,
streamFactory: $aPsr17StreamFactory,
router: $router,
);
```

That is all that is needed to set up a basic JSON-based application. There are several more optional arguments as documented in the code. Most notably, it is strongly recommended that you provide a `$container` (any PSR-11 `ContainerInterface`), and you may want to provide `$parameterLoaders`.

### Actions and parameters

[](#actions-and-parameters)

Now the action. An "action" in Carica parlance is whatever callable is responsible for handling this specific request. Some systems call these "controllers," others use "controller" to refer to a class with a bunch of action methods. In Carica, it's whatever Closure is returned by the Router; function, method, invokable class, we don't care.

For argument's sake, let's assume it's the `__invoke()` method of a class, and that class has various dependencies, and the class is wired into a DI container so that those dependencies get populated. (This is the recommended usage.)

Consider the following, and assume it's been wired into the router as `POST: /foo/{bar}/baz/{qix}`, and then the request is `POST /foo/1/baz/beep?narf=poink`.

```
use Crell\Carica\ParsedBody;
use Crell\Carica\RequesteAttribute;

class SomeAction
{
    public function __construct(
        private ServiceA $serviceA,
        private ServiceB $serviceB,
    ) {}

    public __invoke(
        #[ParsedBody]
        Message $body,
        Bar $bar,
        string $qix,
        ServerRequestInterface $request,
        #[RequestAttribute('some_known_key')]
        string $someValue,
        string $narf = 'default',
    ): array {
        // Do some logic, using ServiceA and ServiceB.

        return ['result' => 'data'];
    }
}
```

There's a lot going on here, so let's take it one by one. The order of parameters in the action has no meaning.

#### `#[ParsedBody]`

[](#parsedbody)

There may be at most one parameter tagged `#[ParsedBody]`. If so, the body of the incoming request will be parsed either into an array or into the class specified. By default, this is done using [Crell/Serde](https://github.com/Crell/Serde), which is an optional dependency. You should really use it, though. However, the body parsing logic is pluggable (it's just a dependency for the corresponding middleware), so if you'd rather use something else, you are free to do so.

#### `$qix`

[](#qix)

These parameters line up with placeholder names in the route, so the router is expected to extract those values and make them available. They will then be passed to the function by name. They will also be automatically cast to the correct type: `int`, `float`, `string`, or `bool`. For booleans, the values `1`, `"1"`, `"true"`, `"yes"`, and `"on"` will all be interpreted as `true`. The values `0`, `"0"`, `"false"`, `"no"`, and `"off"` will be interpreted as `false`. All other values will result in an error (by default an HTP 400). They may have default values, in which case they are optional.

#### `$narf`

[](#narf)

HTTP Query parameters may be passed directly, too. They also are matched by name. That does mean that a placeholder and query parameter may not have the same name. As shown here, they may have default values in which case they are optional. (This is recommended for HTTP query parameters.)

#### `Bar $bar`

[](#bar-bar)

Alternatively, a parameter may be typed as some class. In that case, the system needs you to provide one or more Parameter Loaders. `StandardApplication` offers a parameter to provide them, but as they will likely have their own dependencies as well (often a database), at that point it's probably best to wire up Carica with your DI container instead. If a placeholder or HTTP query parameter is typed to an object, the Loaders will be called to load the object of that type that corresponds to the found argument. That means in this case, we'll be passed a `Bar` instance that corresponds to the ID `1`.

#### `ServerRequestInterface`

[](#serverrequestinterface)

Optionally, the entire request may be passed to the action. Usually this is not necessary, but it's available. Any one parameter typed to `ServerRequestInterface` will be passed the request to do with as you please.

#### `#[RequestAttribute]`

[](#requestattribute)

A PSR-7 ServerRequest object includes "request attributes," which are arbitrary additional metadata. These may be used for a variety of purposes, including routing, authentication, or other sorts of context. Any parameter tagged with `#[RequestAttribute]` will be passed the request attribute of the same name. Alternatively, you may provide the name of the request attribute if you want to use a different name for the variable. (Often request attributes are keyed by a class name, which doesn't work as a variable name.) This may also have a default value if desired.

#### `#[File]`

[](#file)

Not shown here (as it makes little sense to use at the same time as `#[ParsedBody]`), the `#[File]` attribute may be tagged on any paramter typed to PSR-7's `UploadedFileInterface`. It will then be passed the uploaded file from the request that corresponds to the parameter name. Alternatively, `File` takes a single argument, which can be either a string or an array. If an array, it assumes you are using nested field names (something PHP supports), and the array elements are the "tree" of array levels down to the file. (See [PSR-7](https://www.php-fig.org/psr/psr-7/#16-uploaded-files) for more details, as they're a little involved, and the [`File` attribute](src/File.php) for corresponding examples.)

#### Action-specific middleware

[](#action-specific-middleware)

Additionally, the action itself may specify additional middleware that should run, just for that action.

```
use Crell\Carica\RequesteAttribute;
use Crell\Carica\Middleware;

class SomeAction
{
    public function __construct(
        private ServiceA $serviceA,
        private ServiceB $serviceB,
    ) {}

    #[Middleware(SomeMiddleware::class), Middleware('service_id')]
    public __invoke(int $foo, string $bar): array
    {
        // Do some logic, using ServiceA and ServiceB.

        return ['result' => 'data'];
    }
}
```

In this case, if a container has been provided, the services with IDs `SomeMiddleware::class` and `service_id` will be invoked, in that order, immediately before the action runs. They can do whatever they need, as any other middleware.

If a container is not provided or the service ID is not found, Carica will treat it as a class name and try to instantiate it without any constructor arguments. If the string is not a valid class name or the class has required constructor arguments, PHP will throw an Error that will be converted to an HTTP 500.

### Action return values

[](#action-return-values)

An action may return a PSR-7 `ResponseInterface` object, in which case that object is what will be used (give or take any other middleware). However, it is also free to return any value of any type. If a non-Response is returned, and a `ActionResultRenderer` has been configured, that service is responsible for turning whatever it is into a Response object. Out of the box, `StandardApplication` uses a renderer that will use Crell/Serde to serialize any array or object to JSON, and treat any scalar object as though it's already a valid JSON string. Other alternatives include rendering a result object using an HTML template engine, or even varying the behavior based on the `Accept` header of the request.

### Emitting the response

[](#emitting-the-response)

Carica only goes as far as producing the response object. Sending it to the client is the job of an "Emitter." There are various emitter implementations on the market, but as that is not part of any formal PSR specification Carica does not directly leverage any.

If you are not sure what to use, we recommend the [httpsoft/http-emitter](https://packagist.org/packages/httpsoft/http-emitter) package.

Routing
-------

[](#routing)

Notably, one important aspect of the above design is that it is router independent, but relies on the Router to do a fair bit of lifting. By design, a router can be any class that implements the `Router` interface, which in practice will likely be small bridges that connect to an existing routing library. The provided [`FastRouteRouter`](src/Router/FastRouteRouter.php) bridge serves as an example.

There is no standard mechanism for configuring a router, nor even a standard syntax. (Actually there is an IETF standard, but most PHP routers don't use it.) Therefore, Carica does not provide a common interface for configuring a router. However, it does include a bridge to make FastRoute routes easier to register. (See below.)

Importantly, routers MUST NOT throw exceptions. In case a route is not found, that's a normal return. That allows for a trivially simple [`DelegatingRouter`](src/Router/DelegatingRouter.php), which allows stitching together multiple routers in serial, and allowing each to make an attempt to handle routing. If one cannot, control passes to the next until either a router can handle it, or it just resolves into a NotFound case.

### FastRoute bridge

[](#fastroute-bridge)

Carica includes two classes for use with FastRoute. The first is a wrapper that implements the `Router` interface, which can be plugged directly into the `RouterMiddleware`. It depends on a FastRoute `Dispatcher`, which is what FastRoute calls its prepared and ready-to-use routing logic.

The second is a subclass of FastRoute's `RouteCollector`, which handles pre-computing all the static metadata about a route for better performance. It's used almost the same way as FastRoute's normal approach, but instead of calling FastRoute's `cachedDispatcher()` utility function, you would do something like this:

```
use Crell\Carica\Router\FastRoute\PreParsingRouteCollector;
use FastRoute\Dispatcher\GroupCountBased as RouteDispatcher;

function fastRouteDispatcherFactory(bool $useCache, string $cacheFile, \Closure $callback): RouteDispatcher
{
// Load cached router if available.
if ($useCache && file_exists($cacheFile)) {
  $dispatchData = require $cacheFile;
  if (!is_array($dispatchData)) {
    throw new \RuntimeException('Invalid cache file "' . $cacheFile . '"');
  }
  return new RouteDispatcher($dispatchData);
}

// Load routes from configuration.
$collector = new PreParsingRouteCollector();
$callback($collector);
$envMethod = 'routes' . $env;
if (method_exists(self::class, $envMethod)) {
  self::$envMethod($collector);
}

$dispatchData = $collector->getData();

if ($useCache) {
  file_put_contents(self::CacheFile, '
