env() Gotcha in Laravel When Caching Configuration

Earlier today I got caught out by an interesting little issue when using env() to access environment configurable values on a deployed Laravel app. Despite having read through the documentation I managed to completely miss the caveat on using this function whilst caching the app’s configuration.

It turns out that if you execute the config:cache artisan command to cache configuration you can no longer access environment variables using env() outside of the configuration files in /config.

When the configuration is cached the .env file no longer gets loaded. Instead Laravel uses the cached configuration so that the app loads quicker. Obviously something we ideally want to do on a production server; so when deploying an app it is a good idea to run config:cache. However, the consequence of this is that env() must not be used anywhere other than within the configuration files.

The Mistake

I was caught out by this today because we had been storing credentials for a payment gateway within the .env file and then loading them within a class through the constructor something like this:-

public function __construct() {
	$this->accountId = env('PAYMENT_GATEWAY_ACCOUNT_ID');
	$this->secret = env('PAYMENT_GATEWAY_SECRET');
}

This worked when testing locally, but as soon as we deployed and cached the configuration env('PAYMENT_GATEWAY_ACCOUNT_ID') and env('PAYMENT_GATEWAY_SECRET') were both returning null despite being set in our .env file on the server.

.env was no longer being loaded as we were caching the configuration.

The Solution

The fix was to create a new configuration file for our payment gateway and to read the values from .env in there. For example:-

<?php
// ./config/payment_gateway.php

return [
	'account_id' => env('PAYMENT_GATEWAY_ACCOUNT_ID'),
	'secret' => env('PAYMENT_GATEWAY_SECRET'),
];

Then our class constructor just needed updating to use the new configuration values using the config() helper function.

public function __construct() {
	$this->accountId = config('payment_gateway.account_id');
	$this->secret = config('payment_gateway.secret');
}

Now our environment configurable values for our payment gateway are loaded in correctly when we cache the configuration and can be easily accessed throughout the code.

Re-reading the official documentation this now seems painfully obvious, but it tripped us up at first. I hope by sharing this here it will help others in a similar state of confusion.

Related Content

Published on