Beware of APP_DEBUG='false'

Beware of APP_DEBUG='false'

Symfony contains a great way to use environment variables in the service configuration. With the environment processor it's possible to supply bool variables the following way.

# Subscribers
App\EventSubscriber\ExceptionSubscriber:
  arguments:
    $areAllExceptionsTranslated: '%env(bool:ARE_ALL_EXCEPTIONS_TRANSLATED)%'
  tags: [kernel.event_subscriber]

The related configuration in the .env can look like the this:

ARE_ALL_EXCEPTIONS_TRANSLATED=false

With bool:* this will be translated into the boolean false instead of the string "false" it's by default. Therefore within our service with can use bool as type:

public function __construct(
    TranslatorInterface $translator,
    RequestLanguage $requestLanguage,
    bool $areAllExceptionsTranslated
) {
    $this->translator = $translator;
    $this->requestLanguage = $requestLanguage;
    $this->areAllExceptionsTranslated = $areAllExceptionsTranslated;
}

As we're getting used to it, we must not forget, that this is custom logic by Symfony available through the environment processor and not default behaviour of PHP.

A bool cast with (bool) "false" results in true. Which means that we can't use APP_DEBUG=false in our .env with Symfony by default. Because Symfony has the following line in the console and the index.php:

$kernel = new Kernel($_SERVER['APP_ENV'], (bool) $_SERVER['APP_DEBUG']);

This results in new Kernel('prod', true) when using APP_ENV=prod and APP_DEBUG=false. Only APP_DEBUG=0 would give us the desired result.

Alternatively we adapt the files with the logic borrowed from the environment processor from Symfony to have the same logic here. The interesting part is this:

if ('bool' === $prefix) {
    return (bool) (filter_var($env, \FILTER_VALIDATE_BOOLEAN) ?: filter_var($env, \FILTER_VALIDATE_INT) ?: filter_var($env, \FILTER_VALIDATE_FLOAT));
}
EnvVarProcessor.php

With this, our adapted console and index.php will look like the following. You might need to adapt it if you have custom changes in your variants.

#!/usr/bin/env php
<?php

use App\Kernel;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\ErrorHandler\Debug;

if (!in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
    echo 'Warning: The console should be invoked via the CLI version of PHP, not the '.PHP_SAPI.' SAPI'.PHP_EOL;
}

set_time_limit(0);

require dirname(__DIR__).'/vendor/autoload.php';

if (!class_exists(Application::class)) {
    throw new LogicException('You need to add "symfony/framework-bundle" and "symfony/dotenv" as Composer dependencies.');
}

$input = new ArgvInput();
if (null !== $env = $input->getParameterOption(['--env', '-e'], null, true)) {
    putenv('APP_ENV='.$_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = $env);
}

if ($input->hasParameterOption('--no-debug', true)) {
    putenv('APP_DEBUG='.$_SERVER['APP_DEBUG'] = $_ENV['APP_DEBUG'] = 'false');
}

$isAppDebug = (bool) (
  filter_var($_ENV['APP_DEBUG'], \FILTER_VALIDATE_BOOLEAN)
  ?: filter_var($_ENV['APP_DEBUG'], \FILTER_VALIDATE_INT)
    ?: filter_var($_ENV['APP_DEBUG'], \FILTER_VALIDATE_FLOAT)
);

if ($isAppDebug) {
    umask(0000);

    if (class_exists(Debug::class)) {
        Debug::enable();
    }
}

$kernel = new Kernel($_SERVER['APP_ENV'], $isAppDebug);
$application = new Application($kernel);
$application->run($input);
console
<?php

use App\Kernel;
use Symfony\Component\ErrorHandler\Debug;
use Symfony\Component\HttpFoundation\Request;

require dirname(__DIR__).'/vendor/autoload.php';

$isAppDebug = (bool) (
filter_var($_SERVER['APP_DEBUG'], \FILTER_VALIDATE_BOOLEAN)
    ?: filter_var($_SERVER['APP_DEBUG'], \FILTER_VALIDATE_INT)
    ?: filter_var($_SERVER['APP_DEBUG'], \FILTER_VALIDATE_FLOAT)
);

if ($isAppDebug) {
    umask(0000);

    Debug::enable();
}

$kernel = new Kernel($_SERVER['APP_ENV'], $isAppDebug);
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);
index.php