Flex: [Proposal] Make "App" namespace configurable

Created on 30 May 2017  路  19Comments  路  Source: symfony/flex

At present, if a developer wants to use anything other than the "App" namespace for their application, they have to change their configuration in several places, and newly installed recipes often assume the default when adding their own config.

This leads to a very poor DX - for instance, consider the following process for using your own namespace:

  1. Create a new project using composer create-project symfony/skeleton:3.3.x-dev foo
  2. Alter the autoload directives in composer.json to map Foo\ to src/ and Foo\Tests\ to tests/
  3. Alter src/Kernel.php, change namespace App to namespace Foo
  4. Alter web/index.php, change use App\Kernel to use Foo\Kernel
  5. Alter etc/packages/app.yaml, change App\: and App\Controller\: to Foo\: and Foo\Controller\:

At this point, you have a Flex application correctly configured to use the "Foo" namespace. However, installing a single dependency can break this - for instance, simply running composer req cli will fail.
The installation process creates a bin/console script which tries to use App\Kernel. It then tries to execute that script as part of the installation process, resulting in an error and a rollback of composer.json, leaving the recipe part installed.

Besides the poor DX (and admittedly personal preference), I would say that allowing developers to use their own namespaces facilitates code re-use and makes extracting useful functionality from an app into it's own package significantly easier.

To improve this situation, I would propose that you allow the developer to specify their own namespace in composer.json (perhaps as a key under extra), and expose this to Flex and it's recipes.

From my investigation so far, I've identified the two main locations where the namespace is hardcoded, and methods to make that configurable:

  • Wrapper files like bin/console and web/index.php which do not run inside the application context need a hardcoded string - this could be achieved by adding basic variable substitution to CopyFrom*Configurator, or possibly by adding a class_alias in autoload.php
  • Bundle configuration like app.yaml and doctrine.yaml have access to the application context, so these could potentially be adjusted to use a parameter (i.e. %app_namespace%) and have that automatically added to the parameters file

Most helpful comment

Note that the App namespace is for application specific logic, when you create a bundle-less Application you can still use your own namespace for the actual Application.

    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "Acme\\": "src/"
        }
    },

All 19 comments

Can the people down voting offer an explanation as to why?

The problem with this is that it implies PHP files to be _altered_ by Symfony Flex, which is a really bad idea, as it can have a lot of edge cases.

Plus, composer configuration has to be changed too.

Too many things to "alter". For now, the only altered files are container.yaml, .gitignore and Makefile, which is already a lot to maintain. If we add the fact that _every_ PHP file added has to change its namespace just because someone (one person?) doesn't like the fact that src is under App.
I'm exaggerating a bit about the number of people that may dislike the App namespace for src/, but still, it's all about best practices. Putting no namespace on src/ like we used to in older Symfony version has an impact on autoload performances, so defining proper namespaces is the best solution.

What could be done instead? Writing "Controller\\": "src/Controller" in composer.json? Yes, it's very nice, the problem with that is that every new folder has to be registered, which is a bit problematic as every recipe should then handle this namespace if not present in composer, and boom, add a new "autoload-rules" or whatever configurator for that.

What other solution? Use something like a placeholder in our php files like this:

<?php

namespace %SRC_NAMESPACE%\Controller;

// ...

Bull馃挬, it would create invalid PHP files, and force Flex to loop through all PHP files of the recipe (or even the PACKAGE if you use the copy-from-package configurator, but even then, why copy a file from the package that would be changed by Flex anyway?), which makes installation slower again and forces recipes maintainer to adapt to this behavior.

My arguments for the down vote may not be very clear nor objective, but I think it's not DX at all to set something "this specific" as configurable in a Flex project.

And also, you can still change your composer autoload rules yourself and change generated files namespaces, nothing prevents you from doing it. Actually, that's something I might do in all my projects that will use Flex in the future, because I don't like the App namespace and I prefer to take more time & configuration to setup all the namespaces I need in src/ manually, as I'm already doing in my projects. So you see, even if I don't like App namespace under src/, it's not a big deal to get rid of it and use something else. Even though it's boring because Flex was made for us not to alter files we just installed.

But hey, Flex is already awesome and does 90% of the work for us, we can still use our ten fingers to do the left 10% which are specific to our opinions and tastes 馃槈

Thanks for the response, @Pierstoval - I recognise that my proposal isn't very objective either; it's an opinionated subject, so I'll try to stick to the technical.

I believe that you're misrepresenting the work that would have to be done by the configurators - you say it would "force Flex to loop through all PHP files of the recipe (or even the PACKAGE", but that's not what I suggested. The configurators are limited by the manifest, and that wouldn't change at all.

Further, you say that Flex would have to change the composer configuration - this is not what I said at all. Any changes to composer.json (adding the configuration to extra.app_namespace and changing the autoloads) would be done by the user. Composer.json would remain nothing more than a config file to Flex.

The problem regarding a placeholder meaning that we'd ship invalid PHP files is one that I hadn't considered, and is a very good point - that does limit a number of external things like CI services as well. However, I'd argue that the scope is severely limited to the point that it _may_ be acceptable.

As I mentioned in my initial comment, the only PHP files that require alteration would be ones which run outside of the application context (i.e. bin/console and web/index.php) - the vast majority of files run within the app context and would have access to the namespace as a parameter, removing the need for altering the files.

I'm also not advocating for removing the namespace from src/ - not sure where you're getting that.

Now on the more constructive side, I do see your point regarding us doing the last 10% ourselves. If Flex isn't meant to be one-size-fits-all then so be it, but I would suggest that the current DX for those that decide to change their namespaces is still unsatisfactory.

I mentioned before that if you try to composer req cli in such an app you get an error, composer.json rolls back, and Flex leaves the recipe installed - to demonstrate this, I've provided a simple demo: https://github.com/predakanga/symfony-flex-issue-93 - make serve runs happily, but try running composer req cli and you'll see just how bad the DX is.

That said, there are several less intrusive ways that this DX could be improved - from adding a class_exists() check to bin/console and web/index.php, to having Flex warn the user if they're using a non-standard namespace. I'd suggest that the latter is better, because it stops issues like that of the incorrect Doctrine configuration flying under the radar.

Symfony is great at the principle of least surprise - I think that by improving the DX here we can reinforce that.

I've just realised that both bin/console and web/index.php load up DotEnv before they instantiate the kernel - I guess it would be possible to store the namespace in .env and fetch it from there, completely removing the need for file alteration - strikes me as an inappropriate hack, though.

Loading a namespace dynamically is too much: every PHP file has to use a namespace, so every PHP file should be changed according to an environment var, to me this doesn't make sense, it's overloading original Flex behavior for something that can be supported in another way.

According to the repo you linked with the issue (which is nice to have as it clearly shows the problem), what Flex should support is not a way to customize the namespace under src/, but more a method to support any kind of namespace under src/. This can easily be done by checking the namespace in the Kernel.php file, which is kinda the core of the app itself, or in composer configuration.

I think Flex should _adapt_ some stuff according to the _already-configured namespace_, not the contrary. But still, it needs an additionnal configurator, which is a bit tricky to maintain.

And we have an advantage: this namespace is configured in composer.json, which can be retrieved from the plugin.

Any idea @fabpot, @stof?

@Pierstoval I didn't describe it very well I guess - "support any kind of namespace under src" is exactly what I mean

Yeah, probably misunderstood that, sorry. I'm not against suppot for namespace, just find it to be complicated, so finding a solution may be a bit hard.
But I'm against making the namespace "configurable", and env vars is the worst idea for example.

Note that the App namespace is for application specific logic, when you create a bundle-less Application you can still use your own namespace for the actual Application.

    "autoload": {
        "psr-4": {
            "App\\": "src/",
            "Acme\\": "src/"
        }
    },

@sstok Interesting - I see your point, but overlapping PSR-4 definitions like that can have some bad side effects.

It's a good workaround though; I can toss AppKernel.php in app/ and set composer.json up with

    "autoload": {
        "psr-4": {
            "App\\": "app/",
            "Acme\\": "src/"
        }
    },

With that approach you fix the bin/console errors, although bundles like Doctrine will still install config referring to the wrong namespace

Putting the kernel back in app/ is so 2016 :trollface:

Closing this one as supported this use case would introduce some complexity. As mentioned by @sstok, there is a simple solution with PSR-4.

I know it has been already decided to treat this as non-issue. But i can not help wondering what sort of autoload performance issues are being mentioned.

Putting no namespace on src/ like we used to in older Symfony version has an impact on autoload performances, so defining proper namespaces is the best solution.

Optimised autoloader is a map of fully qualified class name to the file path. Having or not having namespace on the class name is not going to affect prod performance in any way

@vivecltd I believe the optimized class loader scans the files in the directories for actual namespace/classnames. So if you have App\AppKernel in src and mapped both App and Acme to src, it wouldn't just try to create a map for App\AppKernel and Acme\AppKernel. There might be slight overhead generating the classes, assuming the optimized loader scans the same physical directory multiple times.

There is another thing to do when changing autoload as @sstok suggested for a default project:
There is a default services definition with App namespace in Symfony 4. It should be replaced with your custom one.

 App\:
     resource: '../src/*'
     exclude: '../src/{Entity,Migrations,Tests,Kernel.php}'

to

 Acme\:
     resource: '../src/*'
     exclude: '../src/{Entity,Migrations,Tests,Kernel.php}'

I don't think it is so important to allow to change the App namespace. App for the generic current app is ok. You will never use the current app as a dependency for another app.

But, why I cannot put my own symfony independent domain code in the src dir?
Where is the complexity problem to have a src/App dir bound to App namespace, so I can add my own src\Acme dir bound to Acme namespace? Is it not more intuitive?
Having bound the src dir to App I'm forced to put my symfony independent domanin code in a...lib(domain_src?) dir. Binding both App and Acme namespaces to src is counterintuitive (IMHO).

So, if I want:

  • to not break Flex
  • to not change a lot of symfony configuration
  • and to not diverge to much from the symfony suggestions
  • to keep me free to not put my code under the App\Acme namespace

the simplest solution seems to put my code in another dir (exiled form the src dir... for the moment)

You can also completely drop the App namespace, after all, it's just a default value, but you can drop it and it's ok. I do it on my projects, and it's working perfectly fine, with very few composer.json tweaks.

Note that using the default App namespace in every application will prevent from using the upcoming preloading feature (PHP 7.4) on a server with 2 or more Symfony apps, as multiple classes with the same FQCN on a single server will conflict.

@BenMorel You won't be able to have 2 Symfony apps anyway (or 2 any PHP apps for that matter), except if all their dependencies share the exact same versions, highly improbable.

@fabpot That's true 馃憤

Was this page helpful?
0 / 5 - 0 ratings