Category: Slim Framework

Handling JSON data errors in Slim 3

When you send JSON data into a Slim Framework application with a content-type of application/json, then Slim will decode it for you if you use getParsedBody():

Using curl to test:

If there's an error however, you get this:

If you care about this, you can use json_last_error_msg() and return an error:

(note – in real code, you should check that the Accept header was a JSON oneā€¦)

Don't forget the JSON_PRETTY_PRINT as a human is going to be reading this error, so make it easier for them.

Use jsonlint for more information

If you really want to provide great diagnostics, then use jsonlint:

Update your handler like this:

(lint() will return NULL or a ParsingException, so we don't need to test for anything else.)

The result looks like this:

This is much more informative!

Checklist for releasing Slim

Release process for Slim so that I don't forget any steps; based on a check list created by Asgrim. I should probably automate some of this!

Preparation:

  • Ensure all merged PRs have been associated to the tag we're about to release.
    Find them via this search: [is:pr is:closed no:milestone is:merged].
  • Close the milestone on GitHub.
  • Create a new milestone for the next patch release.
  • Ensure that you have the latest master & that your index is clean.
  • Find the ID of the current milestone. This is the numeric id found in the URL of the milestone detail page (e.g. 34).
  • Generate the changeling using changelog_generator & copy to clipboard:
    changelog_generator.php -t {your-github-api-token} -u slimphp -r Slim -m {ID} | pbcopy

Tag and release:

  • Edit App.php and update the VERSION constant to the correct version number. Commit the change.
  • Tag the commit: git tag -s {x.y.z} & paste the change log generated above into the tag message.
  • Update App.php again and set the VERSION constant to {x.y+1.0}-dev & commit.
  • Push the commits and the new tag: git push --follow-tags
  • Go to the releases page and click "Draft a new release":
    • Enter the tag name and ensure that you see a green tick and "Existing tag" appear.
    • Set the release title to the same as the tag name.
    • Paste the change log generated above into the release notes box (it is already formatted with Markdown).
    • Click "Publish release".
  • Write announcement blog post for website & publish.

Filtering the PSR-7 body in middleware

Sometimes, there's a requirement to alter the data in the Response's body after it has been created by your controller action. For example, we may want to ensure that our brand name is consistently capitalised.

One way to do this is to create middleware that looks like this:

This works perfectly, so if my response body contains "I consult through my company, nineteen feet.", then the output of the $brandFilter middleware is "I consult through thought my company, Nineteen Feet.", which is exactly what we want.

That's great, but what happens if the new string is shorter than the old one? For instance, suppose I want to replace all uses of "nineteen feet" with "19FT".

Using the same middleware, but changing the str_ireplace call with:

creates this output:

That's not quite what we wanted! This result is because the new string is shorter, so the characters from the old string are still in the PHP stream and as we haven't change the length, they are still there. The PSR-7 StreamInterface doesn't allow us access to the underlying stream, so we can't call ftruncate on it.

Hence, the easiest solution is to replace the Response's PSR-7 StreamInterface object with a new one containing what we need:

The output is now what we expect:

Slim 3.4.0 now provides PSR-7!

I've been neglecting Slim's PR queue recently, so this weekend I dedicated a lot of time to merging all the good work that our contributors have done. As a result, I'm delighted to release version 3.4.0!

This release has a larger set of changes in it than I would have ideally liked which is a direct consequence of having gone two months between releases rather than one.

One particularly interesting addition that we have a made this release is adding a provide section to our composer.json file:

This means that we have informed Composer that Slim provides a valid implementation of the interfaces in psr/http-message-implementation virtual package that defines the PSR-7 interfaces.

This means that when you install a Composer package that requires psr/http-message-implementation in your Slim project, then Composer will now recognise that Slim satisfies this requirement and won't insist you install another PSR-7 implementation just for package resolution!

There's lots of other goodies in 3.4.0, so check out the release notes and upgrade!

DI Factories for Slim controllers

When using classes for route actions in Slim 3, I recommend using a single class for each route. However you can use a single class for multiple routes.

To register a class method to a route you pass a string as the route callable where the class name is separate from method by a colon like this:

Slim will retrieve MyController from the DI container and then call the listAction method using the usual signature:

function (Request $request, Response $response, $args = []) : Response;

If you don't specify a method, then Slim will see if it treat the class as a callable, so you can implement __invoke() and then register the route like this:

and Slim will call MyController::__invoke() for you.

Writing a DI factory for your class

Usually, your controller action will need some dependencies in order to work, such as access to a service layer class, or ORM's entity manager.

To handle this, you should inject the dependency in your controller's constructor by writing a factory for the DI container. This sounds scary and complicated, but a factory is just another way of saying "a function that instantiates an object". This is the simplest DI container factory we can write for MyController:

The closure is the factory and as you can see, it simply returns an new instance of MyController. It is registered with Slim's default DI container by assigning the closure to an array key (['MyController']) and it is vital that the string you use here is the same as the string you use before the colon in the route configuration ('MyController:list'.

Injecting the dependencies

To inject the dependencies, we register them with the DI container too as factories and then retrieve them in our controller factory.

Firstly, register a dependency:

Now we can use this in our controller factory. To do this note that the factory closure has a parameter, $c, which is the DI container itself. This means we can retrieve anything that's registered with the DI container by using the get() method.

Hence we update our controller factory like this:

The MyController constructor now receives our dependency and can store it to a class property ready for use in the route action method like this:

There are numerous advantages to doing this. The main one for me is that there are no surprise dependencies any more. You can look at the constructor and know exactly which classes this class needs to do its job. You can also test it more easily which is beneficial!

I prefer to use one class for each route action as I can ensure that the dependencies that are injected are the correct ones for this action. When using multiple action methods in a controller class, you start needing to inject classes that are only used for just one or two of the actions and this is inefficient, especially if those dependencies are relatively expensive to construct. There are ways around this if you use a more powerful DI container such as Zend-ServiceManager though.

Overriding Slim 3's error handling

Slim 3 registers two error handers:

This means that if you want to override the default error handlers, you need to override both error handlers with your own code.

Each error handler is a callable. The signatures are:

  • errorHandler: function ($request, $response, $exception)
  • phpErrorHandler: function ($request, $response, $error)

To override a error handler, simply register a new callable with the Container:

If you don't want to repeat yourself, you can register the phpErrorHandler like this:

Note that you can register any PHP callable, so a class with an __invoke() method also works.

Handling PHP notices

As an aside, Slim 2's error handler catches PHP notices for you (which can be infuriating or very useful!). Slim 3 doesn't do this by default, so if you want to catch PHP notices then you need to register your own error handler like this:

That's all there is to it.

Using Eloquent in Slim Framework

Eloquent is one of the easier ORMs to get started with, so let's look at how to use and test it within a Slim Framework application.

Set up

You can add Eloquent to your Slim project via composer:

You now need to bootstrap it. To do this we need some settings, so lets do that in our standard Slim configuration:

We then instantiate the capsule manager and boot Eloquent , probably in index.php:

From this point, you can simply follow the documentation to create your Eloquent models and use them within your route callables.

Testing your models

At first glance, it seems quite hard to test your models as the find method is a static.

For example, consider this route callback within an action class:

To test it, I followed a tip from Taylor Otwell and wrapped the find method in a class that I could then mock:

The callback then becomes:

Like the view, the authorRepository is injected into the constructor and so can be mocked out. The full class looks like this:

To test using PHPUnit, we start by creating a mock for the AuthorRepository:

Then we set the expectation that the all method will return a collection of Authors:

We do the same for the view object:

Now we can instantiate the action class with our two mock objects. We then create a request and response objects and run the action and test the returned response:

Summary

Eloquent is quite a nice implementation of the Active Record pattern. If that meets your needs, then it is easy to use within a Slim Framework application, so go for it!

Configuration in Slim Framework

Configuration in Slim Framework is nice and simple: the App's constructor takes a configuration array for the DI container;

Setting up

The settings sub-array is used to hold the settings of your application:

Slim comes with a number of settings that you can change. The most important is displayErrorDetails. This defaults to false, but if you set it to true, then it will display the details of any exceptions when rendering an error page. Ensure that this is set to false in production!

You can put any other settings you want to in the configuration, under any name you like as I have done with the logger key, which contains information about how to configure a Monolog instance.

Retrieving settings

The settings are stored in the DI container so you can access them via the settings key in container factories. For example, I can set up a container factory for to create my Monolog instance like this:

Slim's default container is Pimple, so we use the array notation to register a new service called 'logger' in this case. The logger settings are retrieved using $settings = $c->get('settings')['logger'].

Separate file for configuration

You should use a separate file for your configuration. This is most easily done using require. Firstly, we create config.php and return the configuration array from it:

config.php:

As require returns whatever the included file returns, we load like this:

Environment variables using dotenv

Consider using environment variables for the differences between servers. These can be configured as part of your apache/nginx setup or you can use dotenv.

In this case, create a .env file and add it to .gitignore:

.env:

We can use these environment variables in our configuration file via getenv:

config.php:

We then load using:

Multiple configuration files

You may also want to split your configuration into multiple files which are then merged together with files loaded later overriding the settings from previous files.

One use case is to have a local.config.php that's not in git that contains per-server configuration to be merged with your master configuration in config.php.

For example, your live configuration should turn off display of errors and maybe set a different logging level:

local.config.php:

Merging the two arrays is a little complicated as array_merge_recursive doesn't do what you expect and will result in the 'displayErrorDetails' key becoming an array with two elements. You could write your own merge method, but it's easier to use zend-stdlib's merge method:

Ini/Yaml/JSON/XML configuration

If you want to use something other than PHP arrays, then use zend-config along with glob. In this case, you place all your configuration files in a single directory, such as config/ and then name them with .global.{type} and .local.{type} to control order.

For example, to load the configuration files global.yaml, db.global.yaml and then local.yaml in that order:

One useful feature of zend-config is that you can mix and match between formats, so local.yaml could be local.ini and it would still work.

Summary

As you can see, using configuration with Slim is very easy; all the choices come down to how you want to organise your configuration so that you can manage the differences between environments easily.

Testing Slim Framework actions

To test a Slim Framework action, you need a request and a response object and mock whatever is in the action. This is one way to do this.

Consider this simple echo action that returns the query parameters to you as a JSON encoded string:

This is one of those useful API endpoints that your users can use to check that everything is working as expected.

The action under test

The code for this endpoint is a class called EchoAction which looks like this:

and it is registered with the Slim App like so:

Testing

Testing it isn't too complex as there are no dependencies on the EchoAction class itself, so we just have instantiate the class, invoke it and write an test.

For a URL of the form /echo?foo=bar, the core test code is:

Creating the request and response

Creating the $response is easy as the constructor parameters all have defaults:

The $request is a little more complex as it's constructor signature looks like this:

However, Slim actually creates a Slim\Http\Request using the static createFromEnvironment() factory method, which takes a Slim\Http\Environment instance. Roughly, it does this:

Setting up a $_SERVER array with the relevant elements can be a little tiresome in testing though. Fortunately, Josh needed to test Slim itself, so the Environment object has a handy static method called mock() that does this for us.

We use it like this:

As you can see, mock() takes an array which contains $_SERVER keys that we wish to set up for our particular test. Usually we set the REQUEST_METHOD, REQUEST_URI and, if we need it, QUERY_STRING. We need QUERY_STRING as this is the key in $_SERVER that Request uses to determine the query parameters for the request.

Putting it all together

Hence, our completed test looks like this:

All in all, it's not too complicated to test a Slim action and Environment::mock() makes it easy to set up particular test cases.

Improved error handling in Slim 3.2.0

We released Slim 3.2.0 yesterday which includes a number of minor bug fixes since 3.1.0 and also a few nice improvements around the way we handle errors.

Writing to the error log

Slim has a simple exception handler implemented that displays an error page that looks like this:

Slim error 1

It's not very informative, is it? That's intentional as we don't want to leak information on a live website. To display the error information you need to enable the displayErrorDetails setting like this:

Not too difficult, if you know the setting! If you don't then you're staring at a blank page and have no idea what to do next.

To help solve this Slim 3.2.0 now writes the error information to the PHP error log when the displayErrorDetails is disabled. The built in PHP web server writes the error log to stderr, so I see this in my terminal:

Slim error 2

As you can see, all the information needed to find the issue is there, so the developer can get on with her day and solve the problem at hand.

PHP 7 errors

One of the new features of PHP 7 is that it can throw Error exceptions which can then be caught and processed as you would with a standard Exception. However, Slim 3's error handler is type hinted on Exception and so doesn't catch these new Errors.

To resolve this, Slim 3.2 ships with a new PHP 7 error handler which works exactly like the current exception handler but it catch Error. Here's an example (with displayErrorDetails enabled!):

Slim error 3

To sum up

I'm very happy to have more robust error handling in Slim as I think good errors as key for usability and makes Slim that much easier and enjoyable to use. If you find any error situations in Slim that you feel could be improved, please raise an issue.