Home > OS >  What causes Symfony to call a service?
What causes Symfony to call a service?

Time:10-11

I have an issue and instead of asking just how to solve it, I would to better understand what is happening so that I could solve it myself.

Using api-platform 2.6.6, I successfully added an endpoint to SwaggerUI to retrieve a JWT Token. I later upgraded to api-platform 2.7.x-dev and the endpoint is no longer is displayed. While my end goal is to restore the endpoint, my immediate goal is to better understand how Symfony is configured to that it will call a service.

According to the 2.6.6 api-platform documentation to add the endpoint a decorator is added and it is registered as a service.

api/src/OpenApi/JwtDecorator.php

<?php

declare(strict_types=1);

namespace App\OpenApi;

use ApiPlatform\Core\OpenApi\Factory\OpenApiFactoryInterface;
use ApiPlatform\Core\OpenApi\OpenApi;
use ApiPlatform\Core\OpenApi\Model;

final class JwtDecorator implements OpenApiFactoryInterface
{
    public function __construct(
        private OpenApiFactoryInterface $decorated
    ) {}

    public function __invoke(array $context = []): OpenApi
    {
        // script goes here.
    }
}

api/config/services.yaml

services:
    # ...   

    App\OpenApi\JwtDecorator:
        decorates: 'api_platform.openapi.factory'
        arguments: ['@.inner'] 

To ensure that this single service is solely responsible for adding the endpoint, I intentionally did not make any changes to api/config/packages/security.yaml, api/config/routes.yaml, and api/config/packages/api_platform.yaml to ensure they were not causing the endpoint from being displayed on the original 2.6.6 version and they were not. I also looked at what changes were made when using composer to install jwt-auth, and nothing seems relevant. I also found that I could rename JwtDecorator to SomeOtherName and have ruled out that the class name has special meaning.

Next, I placed a debug_print_backtrace() in the App\OpenApi\JwtDecorator file outside of the class, and found that when using either 2.6.6 or 2.7.x-dev, the command is executed and the backtrace is identical. My hypothesis is that upon any request, symfony will first execute include() on every file with a php extension located in src (unless maybe explicitly instructed not to in some config file), check if cache is fresh (and if not create cache files), and use the results to populate the service container. Please confirm whether this is correct.

Backtrace in api/src/OpenApi/JwtDecorator.php file (not in constructor)

#0  include() called at [/srv/api/vendor/symfony/error-handler/DebugClassLoader.php:349] # line 349 for 2.6.6 and 346 for 2.7.x-dev.
#1  Symfony\Component\ErrorHandler\DebugClassLoader->loadClass()
#2  ReflectionClass->__construct() called at [/srv/api/vendor/symfony/config/Resource/ReflectionClassResource.php:107]
#3  Symfony\Component\Config\Resource\ReflectionClassResource->computeHash() called at [/srv/api/vendor/symfony/config/Resource/ReflectionClassResource.php:54]
#4  Symfony\Component\Config\Resource\ReflectionClassResource->isFresh() called at [/srv/api/vendor/symfony/config/Resource/SelfCheckingResourceChecker.php:34]
#5  Symfony\Component\Config\Resource\SelfCheckingResourceChecker->isFresh() called at [/srv/api/vendor/symfony/config/ResourceCheckerConfigCache.php:99]
#6  Symfony\Component\Config\ResourceCheckerConfigCache->isFresh() called at [/srv/api/vendor/symfony/config/ConfigCache.php:60]
#7  Symfony\Component\Config\ConfigCache->isFresh() called at [/srv/api/vendor/symfony/http-kernel/Kernel.php:451]
#8  Symfony\Component\HttpKernel\Kernel->initializeContainer() called at [/srv/api/vendor/symfony/http-kernel/Kernel.php:786]
#9  Symfony\Component\HttpKernel\Kernel->preBoot() called at [/srv/api/vendor/symfony/http-kernel/Kernel.php:187]
#10 Symfony\Component\HttpKernel\Kernel->handle() called at [/srv/api/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:37]
#11 Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run() called at [/srv/api/vendor/autoload_runtime.php:35]
#12 require_once(/srv/api/vendor/autoload_runtime.php) called at [/srv/api/public/index.php:5]

Next, I did the same but this time within JwtDecorator's constructor, and it was executed only 2.6.6 but not 2.7.x-dev. Now I know the endpoint is not being displayed because the decorator isn't being called.

I then start looking at the classes in ContainerIx5JWFD, and I find the name of the class is always "get" servicesClassName "service". Now I see why I was able to change the name of the service from JwtDecorator to SomeOtherName and it still worked. I also find that the ContainerIx5JWFD\getJwtDecoratorService class only exists for 2.6.6 and not for 2.7.x-dev.

2.6.6 backtrace JwtDecorator::__construct()

#0  App\OpenApi\JwtDecorator->__construct() called at [/srv/api/var/cache/dev/ContainerIx5JWFD/getJwtDecoratorService.php:25]
#1  ContainerIx5JWFD\getJwtDecoratorService::do() called at [/srv/api/var/cache/dev/ContainerIx5JWFD/App_KernelDevDebugContainer.php:644]
#2  ContainerIx5JWFD\App_KernelDevDebugContainer->load() called at [/srv/api/var/cache/dev/ContainerIx5JWFD/getApiPlatform_SwaggerUi_ActionService.php:23]
#3  ContainerIx5JWFD\getApiPlatform_SwaggerUi_ActionService::do() called at [/srv/api/var/cache/dev/ContainerIx5JWFD/App_KernelDevDebugContainer.php:644]
#4  ContainerIx5JWFD\App_KernelDevDebugContainer->load() called at [/srv/api/var/cache/dev/ContainerIx5JWFD/getApiPlatform_Swagger_Action_UiService.php:22]
#5  ContainerIx5JWFD\getApiPlatform_Swagger_Action_UiService::do() called at [/srv/api/var/cache/dev/ContainerIx5JWFD/App_KernelDevDebugContainer.php:644]
#6  ContainerIx5JWFD\App_KernelDevDebugContainer->load() called at [/srv/api/vendor/symfony/dependency-injection/Container.php:237]
#7  Symfony\Component\DependencyInjection\Container->make() called at [/srv/api/vendor/symfony/dependency-injection/Container.php:219]
#8  Symfony\Component\DependencyInjection\Container->get() called at [/srv/api/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php:53]
#9  Symfony\Component\HttpKernel\Controller\ContainerControllerResolver->instantiateController() called at [/srv/api/vendor/symfony/framework-bundle/Controller/ControllerResolver.php:29]
#10 Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver->instantiateController() called at [/srv/api/vendor/symfony/http-kernel/Controller/ControllerResolver.php:108]
#11 Symfony\Component\HttpKernel\Controller\ControllerResolver->createController() called at [/srv/api/vendor/symfony/http-kernel/Controller/ContainerControllerResolver.php:42]
#12 Symfony\Component\HttpKernel\Controller\ContainerControllerResolver->createController() called at [/srv/api/vendor/symfony/http-kernel/Controller/ControllerResolver.php:86]
#13 Symfony\Component\HttpKernel\Controller\ControllerResolver->getController() called at [/srv/api/vendor/symfony/http-kernel/Controller/TraceableControllerResolver.php:38]
#14 Symfony\Component\HttpKernel\Controller\TraceableControllerResolver->getController() called at [/srv/api/vendor/symfony/http-kernel/HttpKernel.php:139]
#15 Symfony\Component\HttpKernel\HttpKernel->handleRaw() called at [/srv/api/vendor/symfony/http-kernel/HttpKernel.php:78]
#16 Symfony\Component\HttpKernel\HttpKernel->handle() called at [/srv/api/vendor/symfony/http-kernel/Kernel.php:199]
#17 Symfony\Component\HttpKernel\Kernel->handle() called at [/srv/api/vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:37]
#18 Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run() called at [/srv/api/vendor/autoload_runtime.php:35]
#19 require_once(/srv/api/vendor/autoload_runtime.php) called at [/srv/api/public/index.php:5]

So, to troubleshoot this, I shouldn't be following this code path but really the code which creates these cached files. I am stuck and begging for help. What triggers Symfony to create these classes so that the service is called? How do I determine what is different between the 2.6.6 and 2.7.x-dev so it is only called on 2.6.6?

CodePudding user response:

The most important information: wait for the official release of that library, as it will contain a list of upgrades. Renaming a service (like happened here) will probably be listed there.

To get to the point: by searching for api_platform.openapi.factory in the source code, I've found that the service was renamed in a pull request. The new service name is api_platform.openapi.factory.next

  • Related