Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

[Dependency Injection] Optional parameter containing % character. #50469

Copy link
Copy link
Closed
@infinitum11

Description

@infinitum11
Issue body actions

Symfony version(s) affected

6.2.11

Description

The ParameterNotFoundException exception is thrown when:

  1. an explicitly wired service has two or more optional parameters where the 1st optional parameter contains a "%" character.
  2. the 2nd optional parameter is explicitly set (while the 1st is omitted) in the ~src/config/service.yaml.

How to reproduce

  1. Install a fresh Symfony app.
  2. Create a service that accepts two (or more) optional parameters. The 1st optional parameter must contain a "%" character.
# ~src/Service/Shouter.php
<?php declare(strict_types=1);
namespace App\Service;

class Shouter
{
    public function __construct(
        private readonly string $format = '%s%s',
        private readonly string $endMark = '!',
    ) {}

    public function shout(string $str): string
    {
        return sprintf($this->format, $str, $this->endMark);
    }
}
  1. Wire the service.
# ~config/services.yaml
parameters:

services:
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Service/'
            - '../src/Kernel.php'
    shouter:
        class: App\Service\Shouter
        arguments:
            $endMark: '!'
  1. Create a dummy controller to see in action.
# ~src/Controller/HomeController.php
<?php declare(strict_types=1);
namespace App\Controller;

use App\Service\Shouter;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class HomeController extends AbstractController
{
    #[Route('/', 'app_shouter')]
    public function home(#[Autowire(service: 'shouter')] Shouter $shouter): Response
    {
        return new Response($shouter->shout('John'));
    }
}
Stack Trace

Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException:
You have requested a non-existent parameter "s".

at var/cache/dev/ContainerMAWqtVy/App_KernelDevDebugContainer.php:1841
at ContainerMAWqtVy\App_KernelDevDebugContainer->getParameter()
(var/cache/dev/ContainerMAWqtVy/getShouterService.php:22)
at ContainerMAWqtVy\getShouterService::do()
(var/cache/dev/ContainerMAWqtVy/App_KernelDevDebugContainer.php:417)
at ContainerMAWqtVy\App_KernelDevDebugContainer->load()
(vendor/symfony/dependency-injection/Container.php:382)
at Symfony\Component\DependencyInjection\Container->getService()
(vendor/symfony/dependency-injection/Argument/ServiceLocator.php:40)
at Symfony\Component\DependencyInjection\Argument\ServiceLocator->get()
(vendor/symfony/http-kernel/Controller/ArgumentResolver/ServiceValueResolver.php:84)
at Symfony\Component\HttpKernel\Controller\ArgumentResolver\ServiceValueResolver->resolve()
(vendor/symfony/http-kernel/Controller/ArgumentResolver/TraceableValueResolver.php:60)
at Symfony\Component\HttpKernel\Controller\ArgumentResolver\TraceableValueResolver->resolve()
(vendor/symfony/http-kernel/Controller/ArgumentResolver.php:54)
at Symfony\Component\HttpKernel\Controller\ArgumentResolver->getArguments()
(vendor/symfony/http-kernel/Controller/TraceableArgumentResolver.php:40)
at Symfony\Component\HttpKernel\Controller\TraceableArgumentResolver->getArguments()
(vendor/symfony/http-kernel/HttpKernel.php:155)
at Symfony\Component\HttpKernel\HttpKernel->handleRaw()
(vendor/symfony/http-kernel/HttpKernel.php:74)
at Symfony\Component\HttpKernel\HttpKernel->handle()
(vendor/symfony/http-kernel/Kernel.php:184)
at Symfony\Component\HttpKernel\Kernel->handle()
(vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php:35)
at Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner->run()
(vendor/autoload_runtime.php:29)
at require_once('/home/xxx/projects/php/symfony_sandbox/vendor/autoload_runtime.php')
(public/index.php:5)

Compiled container I think **''.$container->getParameter('s').'s',** is not something we expect to see here.
<?php

namespace ContainerMAWqtVy;

use Symfony\Component\DependencyInjection\Argument\RewindableGenerator;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;

/**
 * @internal This class has been auto-generated by the Symfony Dependency Injection Component.
 */
class getShouterService extends App_KernelDevDebugContainer
{
    /**
     * Gets the private 'shouter' shared autowired service.
     *
     * @return \App\Service\Shouter
     */
    public static function do($container, $lazyLoad = true)
    {
        include_once \dirname(__DIR__, 4).'/src/Service/Shouter.php';

        return $container->privates['shouter'] = new \App\Service\Shouter(''.$container->getParameter('s').'s', '!');
    }
}

Possible Solution

No response

Additional Context

If we set the 1st optional parameter explicitly, then everything works fine. However, when a service has a lot of optional parameters it becomes cumbersome.

    shouter:
        class: App\Service\Shouter
        arguments:
            $format: '%%s%%s'
            $endMark: '!'

In case when we have access to that service we could write optional parameters in the way how '%' is escaped in the config i.e. with double '%%' private readonly string $format = '%%s%%s', but this won't help with 3rd party libraries:

    shouter:
        class: App\Service\Shouter
        arguments:
            $endMark: '!'
    public function __construct(
        private readonly string $format = '%%s%%s',
        private readonly string $endMark = '!',
    ) {}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.