Cartoon avatar of Charles Sprayberry
cspray.io

Customize Symfony Namespaces

Recently I have been looking at the setup process for various frameworks. I'm wanting to learn some lessons to apply to my personal framework and review which production-ready system I'd like to start using for a new application. Having decided to go with Symfony I wanted to talk about a minor annoyance I have with the default setup and how to properly fix it.

The Default Namespace

While I understand why the popular PHP framework skeletons all have the same top-level namespace of App it isn't a pattern I like. In my humble opinion, the top-level namespace should be more descriptive and correlate to the vendor that produced the package. This could be a single individual, in which case I like my GitHub username, or it could be the name of an organization. It is possible to change the default namespace used by Symfony, but you need to do so in several places and is easy to mess up. This article talks about how to properly change the default namespace in a Symfony 5.3+ project so that everything works correctly. I'm going to show how to upgrade the App namespace to Cspray\MyCoolApp, you should replace this with something appropriate for your use case!

Step 1 - Update composer.json

The very first step is to update your composer.json file to ensure autoloading picks up your new namespace.

{
  "autoload": {
    "psr-4": {
      "Cspray\\MyCoolApp\\": "src/"
    }
  },
  "autoload-dev": {
    "psr-4": {
      "Cspray\\MyCoolApp\\": "tests/"
    }
  }
  
}

After you have finished modifying this file run composer dump-autoload to ensure Composer knows to autoload code from the correct directories.

Step 2 - Update Generated Code

Next update the namespace for your Kernel. In the skeletons that I worked with this was the only generated code that needed to change. In other skeletons you'll need to ensure that all generated code has their namespace updated.

<?php declare(strict_types=1);

namespace Cspray\MyCoolApp;

use Symfony\Component\HttpKernel\Kernel as BaseKernel;

class Kernel extends BaseKernel {

    // rest of the Kernel implementation should be unchanged
    
}

Step 3 - Update entrypoints

Next update the entrypoints for Symfony that are instantiating and using your Kernel. You'll need to update the use statement to point to your namespaced Kernel. Change the bin/console and public/index.php files.

// ./bin/console
#!/usr/bin/env php
<?php declare(strict_types=1);

use Cspray\MyCoolApp\Kernel;

// rest of the bin/console code
// ./public/index.php
<?php declare(strict_types=1);

use Cspray\MyCoolApp\Kernel;

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

// rest of the public/index.php code

Step 4 - Update services mapping

Next update the namespace used by Symfony to determine autowired services. You can find this in config/services.yaml.

services:
  # default definitions

  Cspray\MyCoolApp\:
    resource: '../src/'
    exclude:
      - '../src/DependencyInjection/'
      - '../src/Entity/'
      - '../src/Kernel.php'
      - '../src/Tests/'

Step 5 - Update config/preload.php

Next update the cached preload file path to reflect your new namespace. This file is autogenerated by Symfony and will include the fully-qualified class name with namespace separators replaced by underscores.

<?php declare(strict_types=1);

if (file_exists(dirname(__DIR__).'/var/cache/prod/Cspray_MyCoolApp_KernelProdContainer.preload.php')) {
    require dirname(__DIR__).'/var/cache/prod/Cspray_MyCoolApp_KernelProdContainer.preload.php';
}

Step 6 - Update Doctrine mappings

Next update the mappings that Doctrine uses to parse your entities. This can be found in config/doctrine.yaml.

doctrine:
  dbal:
    # dbal specific configuration
  orm:
    # orm specific configuration
    mappings:
      Cspray\MyCoolApp:
          is_bundle: false
          type: annotation
          dir: '%kernel.project_dir%/src/Entity'
          prefix: 'Cspray\MyCoolApp\Entity'
          alias: App

Step 7 - Update Maker Config

Finally, we need to make sure that any code we generate with the Symfony MakerBundle reflects the correct namespace. If it doesn't exist already create a new file config/packages/dev/maker.yaml and ensure it looks like the following.

maker:
  root_namespace: 'Cspray\MyCoolApp'

Conclusion

At this point your skeleton Symfony app should be able to process requests, execute console commands, and make generated code with the correct namespace. Updating the default namespace in a Symfony app isn't as straightforward or easy as I'd like it to be. Hopefully this guide will help in making this task a little easier to carry out. If I continue to create other Symfony apps I may create a command line tool to automate the steps seen in this guide. Either way, I hope you find this useful when creating your own Symfony apps!