Can't set Slim's default settings after Slim\Container instantiation


#1

I noticed a behavioral “quirk” ( not so much a bug ) with Slim’s DI container based on Pimple implementation, and how it handles Slim’s default settings. I hope this helps other people since the documentation is a little light on this topic.

Originally I had the following array for the settings and all was good:

$c = new Container(['settings' => [
    'displayErrorDetails' => true,
    'determineRouteBeforeAppMiddleware' => true,
    'debug' => true]
]);

Inspecting the container and source code I noticed that “settings” key was special in that I didn’t need to list ALL the configuration options if passed in the Container constructor.

However as I tried to split up the settings and use the container to define the settings, I ran into the following issue.

Won’t work

public/index.php

$c = new Container;
.
.
require_once '../config/slim.php';
.
.
$app = new Slim($c);

config/slim.php

$c['settings'] = [
    'displayErrorDetails' => true,
    'determineRouteBeforeAppMiddleware' => true,
    'debug' => true]
];

This will trigger the following error:

 Undefined index: outputBuffering in /vendor/slim/slim/Slim/App.php on line 247

I can’t define application settings after I’ve instantiated the Container without having to define ALL the default configuration options. This is due to the “settings” key getting overridden and not merged, which makes 100% sense but…

Will work

index.php

// passing in settings in Container constructor
$c = new Container(['settings' => [
    'displayErrorDetails' => true,
    'determineRouteBeforeAppMiddleware' => true,
    'debug' => true]
]);

** Alternatively I can just set EVERY config option **

config/slim.php

// using container object
$c['settings'] = [
    'displayErrorDetails' => true,
    'determineRouteBeforeAppMiddleware' => true,
    'debug' => true,
    'httpVersion' => '1.1',
    'responseChunkSize' => 4096,
    'outputBuffering' => 'append',
    'addContentLengthHeader' => true,
    'routerCacheFile' => false
];

I was hoping to organize my config options and only use Slim’s container to keep things uniform. I understand why this behaves this way. If I want to use Slim’s container like my other configs I need to include all of Slim’s default settings. Since there are only a few it’s not a big deal, however this may become an issue if I want to set a custom logger or if Slim adds more config options in the future.


#2

The Slim container constructor is indeed a bit quirky with regards to the settings entry. It merges the default settings with the settings entry passed in the constructor.

Maybe the following would work to update the settings?

$c['settings'] = array_merge(
    $c['settings'], 
    [
        'displayErrorDetails' => true,
        'determineRouteBeforeAppMiddleware' => true,
        'debug' => true
    ]
);

Note: If you have a suggestion on how to improve the documentation, you can create an issue or a pull request at https://github.com/slimphp/Slim-Website.


#3

On the surface using array_merge looks like it should work, except for this:

Warning: array_merge(): Argument #1 is not an array in /app/config/slim.php on line 5

and

Fatal error: Uncaught RuntimeException: Cannot override frozen service "settings". in /vendor/pimple/pimple/src/Pimple/Container.php on line 77

and

RuntimeException: Cannot override frozen service "settings". in /vendor/pimple/pimple/src/Pimple/Container.php on line 77

I’ve settled for passing an associative array in the Container constructor.

$c = new Container(['settings' => [
    'displayErrorDetails' => true,
    'determineRouteBeforeAppMiddleware' => true,
    'debug' => true]
]);

#4

Sorry, I should have tried this with a small test application before posting.

The first error is because $container['settings'] does not return an array as I thought, but a Slim\Collection instance.

The second and third error are probably because one the $container['settings'] is called, the service is “frozen”. You can override the frozen service by calling unset($container['settings']), but a better way is to use the replace method on the collection:

$container
    ->get('settings')
    ->replace([
        'displayErrorDetails' => true,
        'determineRouteBeforeAppMiddleware' => true,
        'debug' => true
    ]);

The update function will replace values for existing keys and add any new values that are specified.

Hope that helps,

Lennaert


#5

You’re right, it is returning a Collection instance, I didn’t bother to investigate further. While not ideal for my current structure this does work, and I’ll probably use this. Thanks!


#6

No problem :slight_smile:

I have created a pull request to add this information to the user guide.