How Laravel loads .env files
Tags: laravel, php, webdev, programming
Published: 2024-02-17 21:45:32

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.

```php
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:

```php
    /**
     * 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:

```php
    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.

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

```

The createDotenv is simple:

```php
    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:

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

Woah, just calls load?

```php
    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.

```php
    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 :")