6
votes

I am learning the Symfony framework and trying to deploy a simple boilerplate app I put together to Heroku using Git.

The deployment fails due to the following fatal error:

Uncaught Symfony\Component\Debug\Exception\ClassNotFoundException: Attempted to load class "WebServerBundle" from namespace "Symfony\Bundle\WebServerBundle

Did you forget a "use" statement for another namespace? in /tmp/build_cbeb92af6c9ee04b07e1f85618211649/src/Kernel.php:32

Kernel.php

<?php

namespace App;

use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\Routing\RouteCollectionBuilder;

class Kernel extends BaseKernel {

    use MicroKernelTrait;

    const CONFIG_EXTS = '.{php,xml,yaml,yml}';

    public function getCacheDir(){

        return $this->getProjectDir().'/var/cache/'.$this->environment;
    }

    public function getLogDir(){

        return $this->getProjectDir().'/var/log';
    }

    public function registerBundles(){

        $contents = require $this->getProjectDir().'/config/bundles.php';
        foreach ($contents as $class => $envs) {
            if (isset($envs['all']) || isset($envs[$this->environment])) {
                yield new $class();
            }
        }
    }

    protected function configureContainer(ContainerBuilder $container, LoaderInterface $loader){

        $container->setParameter('container.autowiring.strict_mode', true);
        $container->setParameter('container.dumper.inline_class_loader', true);
        $confDir = $this->getProjectDir().'/config';
        $loader->load($confDir.'/packages/*'.self::CONFIG_EXTS, 'glob');
        if (is_dir($confDir.'/packages/'.$this->environment)) {
            $loader->load($confDir.'/packages/'.$this->environment.'/**/*'.self::CONFIG_EXTS, 'glob');
        }
        $loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob');
        $loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob');
    }

    protected function configureRoutes(RouteCollectionBuilder $routes){

        $confDir = $this->getProjectDir().'/config';
        if (is_dir($confDir.'/routes/')) {
            $routes->import($confDir.'/routes/*'.self::CONFIG_EXTS, '/', 'glob');
        }
        if (is_dir($confDir.'/routes/'.$this->environment)) {
            $routes->import($confDir.'/routes/'.$this->environment.'/**/*'.self::CONFIG_EXTS, '/', 'glob');
        }
        $routes->import($confDir.'/routes'.self::CONFIG_EXTS, '/', 'glob');
    }
}

What Ive done so far:

  • install the dotenv bundle (composer require symfony/dotenv)
  • run composer dump-autoload
  • set the production environment variable via the heroku CLI as per this article (heroku config:set SYMFONY_ENV=prod)

Some things Ive learned/have noticed:

  • According to the README file for WebServerBundle:

WebServerBundle provides commands for running applications using the PHP built-in web server. It simplifies your local development setup because you don't have to configure a proper web server such as Apache or Nginx to run your application.

.. that is, WebServerBundle is a development dependency - yet it is being included in production.

  • WebServerBundle is also being included in the composer.lock file under autoload

    "autoload": { "psr-4": { "Symfony\\Bundle\\WebServerBundle\\": "" }, ... },

  • My bundles.php file includes WebServerBundle for dev

    return [ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\WebServerBundle\WebServerBundle::class => ['dev' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], ];

  • I have a .env file and a .env.dist file - Im assuming the .env.dist file is for production? They both have the same contents:

    APP_ENV=dev APP_SECRET=0473d15a4ce2723619d2e8b0405186d3

Im pretty new to symfony and do not really understand how a production environment is instantiated other than that the dotenv bundle reads the .env file and sets the environment variables.

Any help and clarity on all of this would be appreciated.

Edit: here is my composer.json file

{
    "type": "project",
    "license": "proprietary",
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "sensio/framework-extra-bundle": "^5.1",
        "symfony/asset": "^4.0",
        "symfony/console": "^4.0",
        "symfony/dotenv": "^4.0",
        "symfony/flex": "^1.0",
        "symfony/framework-bundle": "^4.0",
        "symfony/lts": "^4@dev",
        "symfony/twig-bundle": "^4.0",
        "symfony/yaml": "^4.0"
    },
    "require-dev": {
        "symfony/profiler-pack": "^1.0",
        "symfony/web-server-bundle": "^4.0"
    },
    "config": {
        "preferred-install": {
            "*": "dist"
        },
        "sort-packages": true
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "replace": {
        "symfony/polyfill-apcu": "*",
        "symfony/polyfill-php70": "*",
        "symfony/polyfill-php56": "*"
    },
    "scripts": {
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "assets:install --symlink --relative %PUBLIC_DIR%": "symfony-cmd"
        },
        "post-install-cmd": [
            "@auto-scripts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    },
    "conflict": {
        "symfony/symfony": "*"
    },
    "extra": {
        "symfony": {
            "id": "01C1BVQJ19BG3WAS3299PDHH9P",
            "allow-contrib": false
        }
    }
}
2
can you show us your composer.json? where web-server-bundle came from? could you post the output of composer why symfony/web-server-bundle ? - Nikita U.
Did you forget a "use" statement for another namespace? - Alex Karshin
Can't help with Heruko issue but .env.dist is just a template file for .env. .env.dist gets checked into source control while .env does not. You should not have any secret info in .env.dist and it plays no role in the actual application configuration. Normally .env is not used at all in a production environment. APP_ENV will get set somewhere else. I guess it would not hurt to set it to prod but Heruku itself should be setting your environment variables and there should be no need at all for .env in production. - Cerad
Did you run a composer install remember git does not manage all the bundles you may have installed while developing. The composer install will just install what your composer.lock say you need, it wont upgrade/change anything - RiggsFolly
I have added my composer.json file above. the output of composer why symfony/web-server-bundle is " root dev-staging requires (for development) symfony/web-server-bundle (^4.0)". I did run composer install with the following output Nothing to install or update - yevg

2 Answers

8
votes

I'm not sure about the command you are using, (I use git)

heroku config:set SYMFONY_ENV=prod

But if your .env file has

APP_ENV=dev

That is explictly setting the environment to dev, so it is trying to load dev dependencies (proven by your error) which don't get pushed to the server(read the article you provided).

You need the .env file that is on the server to have APP_ENV=prod The .env is for the specific machine, it is ignored by git, while .env.dist is tracked. So edit the .env.dist and commit it, then once it is on the server just rename it .env then I would run composer install or composer update on the server which will update dependencies and clear the cache. Then refresh your browser.

0
votes

The answer likely depends on how you deploy your app and I assume you will install only production dependencies in your deployment, i.e. you run composer with --no-dev as you should. This means that none of the dev dependencies, which very likely includes the webserver-bundle as this is not needed in production where you use a real webserver like apache or nginx, are installed.

What it boils down to is either your code requiring this dev dependency which is not installed or your webserver is trying to run your app in the wrong APP_ENV.

If it's the latter you should check your webserver config if you set the appropriate environment variables (APP_ENV & APP_DEBUG at the very least, probably also the DATABASE_URL). In apache you have to use SetEnv APP_ENV prod and in nginx you use fastcgi_param APP_ENV prod.

edit: I just read that you use composer, so dev dependencies are definitely not installed as is mentioned in the Getting Started Guide: https://devcenter.heroku.com/articles/php-support#build-behavior

So you can't use the dev environment for Symfony or you have to reinstall the dev-dependencies as regular dependencies using composer (which is absolutely not recommended).