Photo by Minh Tran on Unsplash

How Laravel loads .env files

Bootstrapping is cool

#[Pragmatic(Kiwi)]
4 min readFeb 16, 2024

--

It is no secret that during Laravel’s bootstrapping it is loading its environment variables. But how? And where?

That where is in Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables

And the how? Pretty straightforward actually.

As we can see in the following snippet, there are a couple of steps that Laravel takes. Let’s follow them one by one.

class LoadEnvironmentVariables
{
/**
* Bootstrap the given application.
*
* @param \Illuminate\Contracts\Foundation\Application $app
* @return void
*/
public function bootstrap(Application $app)
{
if ($app->configurationIsCached()) {
return;
}

$this->checkForSpecificEnvironmentFile($app);

try {
$this->createDotenv($app)->safeLoad();
} catch (InvalidFileException $e) {
$this->writeErrorAndDie($e);
}
}
}

Is the configuration cached?

This happens in the Application Contract:

    /**
* Determine if the application configuration is cached.
*
* @return bool
*/
public function configurationIsCached()
{
return is_file($this->getCachedConfigPath());
}

/**
* Get the path to the configuration cache file.
*
* @return string
*/
public function getCachedConfigPath()
{
return $this->normalizeCachePath('APP_CONFIG_CACHE', 'cache/config.php');
}

As you can see it checks it returns the path to the ./config.php file, which will be located in the cache/ directory, if it’s… obviously, cached.

If it finds it there, the program is done and it moves on with its life…

What if it’s not cached?

Next, it will run:

  $this->checkForSpecificEnvironmentFile($app);

Doing a series of checks:

    protected function checkForSpecificEnvironmentFile($app)
{
if ($app->runningInConsole() &&
($input = new ArgvInput)->hasParameterOption('--env') &&
$this->setEnvironmentFilePath($app, $app->environmentFile().'.'.$input->getParameterOption('--env'))) {
return;
}

$environment = Env::get('APP_ENV');

if (! $environment) {
return;
}

$this->setEnvironmentFilePath(
$app, $app->environmentFile().'.'.$environment
);
}

If the app is running in the console and we specify the env file it should use, it will set the environment file to what we specified in the command.

From the docs:

Before loading your application’s environment variables, Laravel determines if an APP_ENV environment variable has been externally provided or if the --env CLI argument has been specified. If so, Laravel will attempt to load an .env.[APP_ENV] file if it exists. If it does not exist, the default .env file will be loaded.

If no — env or CLI argument was provided, it sets the environment file path to the default one.

Now what? Do we have the config?

Next, it created an instance of the Dotenv component!

Laravel Dotenv is a component of the Laravel framework designed to simplify the management of environment variables within Laravel applications. It is based on the PHP dotenv library and allows developers to define configuration settings for their applications in a .env file.

        try {
$this->createDotenv($app)->safeLoad();
} catch (InvalidFileException $e) {
$this->writeErrorAndDie($e);
}

The createDotenv is simple:

    protected function createDotenv($app)
{
return Dotenv::create(
Env::getRepository(),
$app->environmentPath(),
$app->environmentFile()
);
}

In my case, the arguments it received in the create call are:

  • Dotenv\Repository\AdapterRepository
  • “/home/user/example-app”
  • “.env”

The creation of Dotenv is a bit more complicated but my goal is not to divide into it, but rather in how it will load the config after we provide it!

This happens in the safeLoad() part, which is executed in the Illuminate Application file:

    public function safeLoad()
{
try {
return $this->load();
} catch (InvalidPathException $e) {
// suppressing exception
return [];
}
}

Woah, just calls load?

    public function load()
{
$entries = $this->parser->parse($this->store->read());

return $this->loader->load($this->repository, $entries);
}

The only part of this code that can throw the InvalidPathException is the store->read() method. So we are only suppressing the errors in cases in which the file is no longer in there, who knows why? Maybe your CI/CD did something during your deploy 👀

At least we handle the InvalidFileException by displaying an error and exiting.

    protected function writeErrorAndDie(InvalidFileException $e)
{
$output = (new ConsoleOutput)->getErrorOutput();

$output->writeln('The environment file is invalid!');
$output->writeln($e->getMessage());

http_response_code(500);

exit(1);
}

It’s using the ConsoleOutput class for Symfony from Symfony\Component\Console\Output, nothing out of the ordinary here, as most things in Laravel are based on Symfony.

What now?

Well, that’s it. That’s how Laravel reads your .env file. But this is not the end, rather just the beginning.

As there are many other things Laravel will want to Bootstrap. Like Configurations, Exception Handlers, Facades, Service Providers, and so on.

I’ll explain them one by one in the future.

To sum it up…

  • Laravel’s bootstrapping process involves loading environment variables, which is handled by the Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables class.
  • If the configuration is cached, Laravel retrieves it from the cache; otherwise, it proceeds to load the environment variables.
  • Laravel checks for specific environment files based on CLI arguments or the APP_ENV environment variable.
  • It then creates an instance of the Dotenv component to manage environment variables from the .env file.
  • The Dotenv component loads the environment variables safely, handling exceptions such as invalid file paths.
  • If an error occurs during loading, Laravel displays an error message and exits.
  • This process ensures that Laravel applications can efficiently manage environment variables for different environments.
  • Laravel’s bootstrapping encompasses various tasks beyond environment variable loading, including configuration, exception handling, and service providers.

--

--