5

According to the documentation, a command class must extend Command or ContainerAwareCommand to be automatically discovered and registered (provided its bundle is registered in the Kernel, of course).

Since Symfony 4+, this discovery doesn't work as expected.

Try this:

composer create-project symfony/skeleton test-maxmind-sf4
cd test-maxmind-sf4
composer req cravler/maxmind-geoip-bundle dev-master
php bin/console list

You will notice that:

  • cravler:maxmind:geoip-update is not registered (nothing under a "cravler" namespace
  • Cravler\MaxMindGeoIpBundle\Command\UpdateDatabaseCommand extends ContainerAwareCommand
  • Bundle Cravler\MaxMindGeoIpBundle\CravlerMaxMindGeoIpBundle is registered in config/bundles.php for all environments (auto-generated recipe)

Now when I do exactly the same thing with Symfony 3, everything works properly.

composer create-project symfony/skeleton test-maxmind-sf3 ^3.0
cd test-maxmind-sf3
composer req cravler/maxmind-geoip-bundle dev-master
php bin/console list

What's missing there?

Thank you,

Ben

Ben
  • 845
  • 1
  • 8
  • 18
  • I have not tried your test but typically there is a colon when specifying a version cravler/maxmind-geoip-bundle:dev-master – Cerad Mar 06 '18 at 15:29
  • 1
    Hello Cerad, that's not relevant here. Installing a dependency works without the colon, and the point is that the same dependency works on SF3+, not SF4+. – Ben Mar 06 '18 at 15:51
  • So I tried your experiment and as far as I can tell, the autoconfig stuff only applies to the application code. I tried editing the bundle and setting the defaults to autoconfigure but no change. Then I looked at a few other bundles and they all listed their console commands as services. Even the framework bundles does this. So I think you will need to ask the maintainer to add a service definition. – Cerad Mar 06 '18 at 16:05
  • Hello Cerad, consider I'm one of the maintainers :) You shouldn't, theorically, have to explicitely define your commands in a config file. As soon as the bundle is registered in the Kernel, all its commands are automagically discovered. It just doesn't work on this bundle, on SF4+ only, and I'm just trying to figure out why... – Ben Mar 06 '18 at 16:11

4 Answers4

7

From the UPGRADE FROM to 4.0 Guide here:

Relying on convention-based commands discovery is not supported anymore. Use PSR-4 based service discovery instead.

Before:

# app/config/services.yml
services:
    # ...

    # implicit registration of all commands in the `Command` folder

After:

# app/config/services.yml
services:
    # ...

    # explicit commands registration
    AppBundle\Command\:
        resource: '../../src/AppBundle/Command/*'
        tags: ['console.command']

Hope this help

Matteo
  • 37,680
  • 11
  • 100
  • 115
  • 1
    Good catch Matteo, the Symfony documentation was not up-to-date with this, it still states that any PHP class extending Command is registered as a console command. I've made a PR to clarify this. Thank you! – Ben Mar 07 '18 at 09:44
  • 1
    https://github.com/cravler/CravlerMaxMindGeoIpBundle/commit/e832df7e61c640a623c4f3aa8f42a7eed1e5f423 – Matteo Mar 07 '18 at 09:47
  • 2
    Symfony documentation has just been updated to disambiguate this point. :-) – Ben Mar 07 '18 at 15:51
2

Basically you have to add a few things to your bundle's services file in order to autoconfig your services. Following the example in config/services.yaml:

# Cravler/MaxMindGeolpBundle/Resources/config/services.xml
<services>
    <defaults autowire="true" autoconfigure="true" public="false" />
    <prototype 
        namespace="Cravler\MaxMindGeoIpBundle\" 
        resource="../../*" 
        exclude="../../*/{Entity,Migrations,Tests}" />

    <service id="cravler_max_mind_geo_ip.service.geo_ip_service"
             public="true"
             class="Cravler\MaxMindGeoIpBundle\Service\GeoIpService">
    </service>
</services>

Clear the cache and your command should be there.

And then of course you should probably tweak the command itself and inject the ip service instead of locating it.

I did poke around a bit and did not find this approach documented anywhere. And as I mentioned in the comments, all the bundles I did check still explicitly defined their command services. So I'm not sure if this approach is discouraged or not.

Update: as @Matteo said in their answer, prior to 4.0 any classes defined in the Command directory were treated as commands. This scanning was considered to be magical and removed. However, around the same time, Harry Potter was added to the core team and magic was his thing. So the scanning of the Command directory was replaced with all sorts of auto wiring and auto tagging spells.

Cerad
  • 48,157
  • 8
  • 90
  • 92
1

If you are absolutely sure you config is correct and command is still not recognized, try resetting composer's autoloader:

$ composer dump-autoload

This helped in my case.

mc.watras
  • 381
  • 3
  • 10
0

I think the command should be registered within the bundle, not the app. I think this will work:

# Cravler\/axMindGeoIpBundle/src/Resources/config/services.xml
<services>
  <service
      id="Cravler\MaxMindGeoIpBundle\Command\UpdateDatabaseCommand" 
      class="Cravler\MaxMindGeoIpBundle\Command\UpdateDatabaseCommand" 
      public="false">
      <tag name="console.command" />
  </service>
</services>

The command class extends Symfony\Component\Console\Command\Command.

Tac Tacelosky
  • 3,165
  • 3
  • 27
  • 28