Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a Rector for Symfony service name migrations? #42

Closed
Bilge opened this issue Oct 21, 2021 · 14 comments
Closed

Is there a Rector for Symfony service name migrations? #42

Bilge opened this issue Oct 21, 2021 · 14 comments

Comments

@Bilge
Copy link

Bilge commented Oct 21, 2021

It's nice that that's lots of little Rectors for doing simple things, but it seems to me the most laborious and complex task of upgrading an old Symfony project is upgrading to the new Symfony 3.3 configuration. Specifically, this means replacing service "machine names" (e.g. my.foo) with FQCN names (e.g. My\Foo::class).

Without this, SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION cannot do its job and isn't very helpful, because it only promotes services called via get() that specify FQCN class names already. In older projects, this means little or no services are fixed because machine names for services were the norm back then. Nevertheless, it should be possible to read the service configuration and map those names back to the actual class names to both perform the constructor injection and rename the service. However, I cannot find any Rector to do this.

@Bilge Bilge changed the title Is there a Rector for doing Symfony service name migrations? Is there a Rector for Symfony service name migrations? Oct 21, 2021
@TomasVotruba
Copy link
Member

Hi, thanks for this question.

Could you provide minimalistic diff of what exactly you have in mind? Just to be sure we understand your request correctly.

@Bilge
Copy link
Author

Bilge commented Oct 21, 2021

<!-- services.xml -->
<service id="my.service" class="My\MyService">
    <argument type="service" id="My\OtherService" />
</service>
+use My\MyService;

class Foo extends Controller
{
    function bar()
    {
-        $this->get('my.service');
+        $this->get(MyService::class);
    }
}

Thereafter, the existing SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION Rector set could take over and do its job of refactoring the DI service grab into constructor injection, now that the service class name is used instead of the "machine name".

Note that this would not be possible in the case where two different service definitions are declared for the same class name. In this rare case, probably the fixer should do nothing and defer to manual intervention. If it was super smart, it could perhaps create a ServiceLocator or something, but that's out of scope here.

@TomasVotruba
Copy link
Member

TomasVotruba commented Oct 23, 2021

Thanks for reporting. Looking at rules in linked set, this should be supported. I'm doing a bit cleanup of the rules just to be sure.

I'll need reproducible repository to verify where bug is. Could you share opened repository in Github? Ideally with single class and minimal composer.json. It will speed up th efix.

@Bilge
Copy link
Author

Bilge commented Oct 23, 2021

What do you mean by, where bug is? There is no bug here. Either the feature exists or it doesn't. If it doesn't then this can be treated as a feature request. That is, unless you mean SYMFONY_CONSTRUCTOR_INJECTION is supposed to work for more than just services whose definition name matches the class name?

@TomasVotruba
Copy link
Member

TomasVotruba commented Oct 23, 2021

@Bilge
Copy link
Author

Bilge commented Oct 23, 2021

I see. Is it likely to not work when the service definitions are in XML format and/or in many separate bundles? In old versions of Symfony, it was recommended that even all first-party code be placed inside bundles, and indeed is this case in this project, though those bundles are in the same repository and in the same directory structure as everything else, so it shouldn't matter too much...

@TomasVotruba
Copy link
Member

It works for specifically located xml config at the moment. That should be easy to implement 😉
Could you send the minimal repository with such example?

@Bilge
Copy link
Author

Bilge commented Oct 23, 2021

OK, I made https://github.com/Bilge/rector-fail.

@TomasVotruba
Copy link
Member

Thank you, I'll look into it 👍

@TomasVotruba
Copy link
Member

TomasVotruba commented Oct 23, 2021

I've tried the repository and after linking the XML via SYMFONY_CONTAINER_XML_PATH_PARAMETER:

// rector.php
<?php

declare(strict_types=1);

use Rector\Core\Configuration\Option;
use Rector\Symfony\Set\SymfonySetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    // get parameters
    $parameters = $containerConfigurator->parameters();
    $parameters->set(Option::PATHS, [
        __DIR__ . '/src'
    ]);

    // path to dumped service container - run the project at least once to build container services map
    $parameters->set(Option::SYMFONY_CONTAINER_XML_PATH_PARAMETER,
    __DIR__ . '/var/cache/dev/OrgOrg_KernelDevDebugContainer.xml');

    $containerConfigurator->import(SymfonySetList::SYMFONY_CONSTRUCTOR_INJECTION);
};

...it works as expected 👍


Screenshot from 2021-10-23 19-11-05


See https://github.com/rectorphp/rector-symfony#use-sets

Could you complete your example? What effect has it on the result?
I do not have an access, otherwise I'd send PR to your repository.

@Bilge
Copy link
Author

Bilge commented Oct 23, 2021

Yes, that does seem to work.

 class DefaultController extends Controller
 {
+    public function __construct(private \Org\Service\MyRegularService $myRegularService, private \Org\FirstBundle\Service\MyBundleService $myBundleService)
+    {
+    }
     public function indexAction()
     {
-        $regularService = $this->get('my.regular.service');
-        $bundleService = $this->get('my.bundle.service');
-        $regularServiceAsClassName = $this->get(MyRegularService::class);
+        $regularService = $this->myRegularService;
+        $bundleService = $this->myBundleService;
+        $regularServiceAsClassName = $this->myRegularService;

         return $this->render('FirstBundle:Default:index.html.twig');
     }

It's weird that that has always been in the readme. It does not really draw attention to itself there.

@TomasVotruba
Copy link
Member

That's issue with readmes. It's full of details that only one person needs :)

I'd be happy if you could send update of README.md that would help you next person in the situation 👍

@Bilge
Copy link
Author

Bilge commented Oct 23, 2021

I was thinking of a few different options.

  1. Maybe a note next to the relevant fixer(s) or set(s) reminding to set the configuration option for those fixers that need it.
  2. Maybe a notice/warning generated by Rector itself if a fixer is used that requires the option but it is missing.

@TomasVotruba
Copy link
Member

Maybe a notice/warning generated by Rector itself if a fixer is used that requires the option but it is missing.

This would be awesome 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants