I am migrating from symofony 2.7 to symfony 4.0. With success I migrated one bundle. Now I am migrating the second bundle and the error message is coming up. I don't get at all what symfony 4.0 wants from me.
If I turn on autowire: true this error message is coming up.
Cannot autowire service "App\Kernel": argument "$environment" of method "Symfony\Component\HttpKernel\Kernel::__construct()" must have a type-hint or be given a value explicitly.
Can somebody help me?
If I turn it off, no message is coming up.
Update
I registered my bundle only in bundles.php
App\Backend\AccountBundle\BackendAccountBundle::class => ['all' => true],
Usually the Kernel is added to the Service Container as a so called synthetic service, meaning it's not generated by the DI-container from configuration. Rather the id is set and then the previously configured service is just added to the container. It seems rather odd that your bundle's container wants to create a new kernel here. So I would check where and how you want to access the kernel in any of the bundle's services and whether you actually want to pass in the kernel and not something else. If you do you might want to check the Service Container-documentation on synthetic services.
As to the error itself. Symfony's autowiring often falls flat when you have services that require parameters like with the Kernel:
public function __construct(string $environment, bool $debug) {...}
In these cases you have to either have a parameter defined in your services.yaml that matches the name of the parameter:
# config/services.yaml
parameters:
environment: prod
debug: false
or you have to tell the configuration which parameters you want to have in those places.
App\Kernel:
$environment: prod
$debug: false
This will tell the autowiring that only the 2 arguments named environment and debug should be overwritten with the values you provide, but the rest is done via autowiring. This way you can skip the arguments: part of the definition and you can also skip all arguments you know are correctly set via autowiring.
For example if you have a service like this:
class MyService {
public function __construct(OtherServce $service, string $someParameter) {}
}
# config/services.yaml
services:
_defaults:
autowiring: true
MyService:
$someParameter: 'someValue'
This is the same as explicitly writing:
services:
MyService:
class: MyService
arguments:
- '#OtherServce'
- 'someValue'
Related
I'd like to use https://github.com/tedious/TedivmStashBundle bundle in my symfony 4 project. Added by composer, configured in /config/stash.yaml file and according to the profiler bar, it's working basically.
Now, I want to use to caching values in my controller. I've try to add the service stash by its name to the method's parameter, using the same name as the example says:
$pool = $this->container->get('stash');
but the framework did not find the service. It can't be added like the example explained neither.
How can I use this bundle as a service (autowired) in my symfony 4 app?
UPDATE
services.yaml:
stash.default_cache:
class: Tedivm\StashBundle\Service\CacheService
public: true
arguments:
$name: 'stash'
Tedivm\StashBundle\Service\CacheService: '#stash.default_cache'
Controller:
public function something(Request $request, CacheService $service, ...
It's looks like working now :)
Thanx for the suggestion (and the correct solution later) to #Cerad
Here is an example of how to approach this sort of problem when dealing with bundles that are not quite ready for autowire.
Start by installing a test project:
symfony new --full stash --version=lts
composer require tedivm/stash-bundle
Note that the bundle does not directly support Symfony 5 hence the lts. Note also that the bundles does not support Flex so you have to add your own config/packages/stash.yaml file per the bundle's readme file.
# config/packages/stash.yaml
stash:
drivers: [ FileSystem ]
FileSystem: ~
At this point we can determine which service we need to inject:
bin/console debug:container stash
Information for Service "stash.default_cache"
=============================================
Class CacheService
---------------- -----------------------------------------
Option Value
---------------- -----------------------------------------
Service ID stash.default_cache
Class Tedivm\StashBundle\Service\CacheService
Most of the time you would like to use an interface for injection but a peek at the source code reveals that the bundle does not use interfaces. As a side note, calling a third party service 'stash' is not a good idea. It really should have been 'tedivm.stash' but I digress.
We can now create an alias and then typehint against it:
# config/services.yaml
Tedivm\StashBundle\Service\CacheService : '#stash' # alias for typehinting
# Controller class
public function index(CacheService $cacheService)
{
return new Response('Cache ' . get_class($cacheService));
}
And that should do it.
After upgrading to Symfony 3.4 from 2.8, I am attempting to get rid of warnings about using services from the container. One hang up is my controller all extend from an abstract controller which needs access to the monolog logger. I've decided to use autwiring for my controllers and have added a constructor in the base controller which has a LoggerInterface $logger as the only argument. In attempt to configure this once, I've added the $logger variable with a reference to the logger service under the bind section of services.yml.
However, I keep getting the error:
Unused binding "$logger" in service "security.authentication.failure_handler.secured_area.form_login"
I believe this error is supposed to appear only if no services have a constructor argument with that variable name. Now I know that my controllers all have this in the abstract class, as well as being part of some of my other services, so this seems wrong. How can I get rid of this error?
Here is what my services.yml looks like:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
bind:
$logger: "#logger"
$env: "%sys_env%"
_instanceof:
\Twig_Extension:
tags: ['twig.extension']
AppBundle\:
resource: '../../../../../../src/AppBundle/{Controller,Service,Twig}/*'
exclude: '../../../../../../src/AppBundle/Service/Exception/*'
# SECURITY ########################################################################
security.authentication.failure_handler:
class: AppBundle\Security\AuthenticationFailureHandler
autowire: false
arguments: ["#http_kernel", "#security.http_utils", {}, "#app.service.security", "#doctrine.orm.entity_manager", "#logger"]
tags:
- { name: 'monolog.logger', channel: 'security' }
UPDATE 1:
I noticed that in security.authentication.failure_handler I have a reference to one of my services: app.service.security. I forgot to declare that below, so I added the following to services.yml:
app.service.security:
class: AppBundle\Service\SecurityService
That got rid of the logger error, however now I'm seeing an error about the $env string variable:
Unused binding "$env" in service "security.authentication.failure_handler.secured_area.form_login".
I'm concerned that the error message is not the real error, and this is a red herring. The bind options seem a little flaky. Any advice appreciated...
UPDATE 2:
I've decided to get rid of the bind and instanceof config and am setting up the values manually, but now this is the error: Cannot autowire service "app.service.security": argument "$sysInfoService" of method "AppBundle\Service\SecurityService::__construct()" references class "AppBundle\Service\SystemInfoService" but no such service exists. You should maybe alias this class to the existing "app.service.system_info" service.
What's weird is that I believe I'm doing exactly what the error is suggesting to do; I've added aliases for the supposedly autowired service:
app.service.system_info:
class: AppBundle\Service\SystemInfoService
app.service.security:
class: AppBundle\Service\SecurityService
I do have some services which I manually declare with autowired: false in order to manually set the arguments. That should be ok, I think; you should be able to have autowired and manual wiring coexisting in the service container, right?
I have the following services.yaml file: # This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.
# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration
parameters:
locale: 'en'
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
public: false # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller'
tags: ['controller.service_arguments']
# add more service definitions when explicit configuration is needed
# please note that last definitions always *replace* previous ones
App\Service\Processor\TestClauses:
public: true
App\Service\Processor\Factory:
arguments:
- 'App\Service\Processor\TestClauses'
-
- 'MilkProductionProcessor'
ie. I'm happy to autowire everything, but this one service that needs an array as input.
This does not seem to work unless I make all services public. My understanding of the documentation https://symfony.com/doc/4.1/service_container.html#public-versus-private-services is that I just have to make services public that I want to inject manually
The "root service" is injected to a command. When I run this command:
1) With the services.yaml as is I get
[WARNING] Some commands could not be registered:
In Factory.php line 15:
Argument 1 passed to App\Service\Processor\Factory::__construct() must impl
ement interface App\Service\Processor\TestClausesInterface, string given, c
alled in /home/jochen/projects/freshagenda/symfony/var/cache/dev/Container7
4x3zkp/getProcessFilesCommandService.php on line 16
There are no commands defined in the "app" namespace.
Did you mean this?
doctrine:mapping
2) When I make services:_defaults:public true
it moves forward
App\ServiceThatNeedsArrayAsInput:
arguments:
$array: ...
Everything else can be autowired and autoconfigured. In constructor of the ServiceThatNeedsArrayAsInput you should receive that $array from arguments - difference from earlier versions is that you explicitly say to which variable you wish to bind argument defined in services.yml
// ServiceThatNeedsArrayAsInput.php
public function __construct(array $array) {} // Only array from arguments
public function __construct(array $array, AutowiredService $service) {} // Just add it here and DI will autoinject it, no need to change services.yml
Your example
I'm not entirely sure what are you trying to do here but if you want to inject autowired services, there's no need to define it explicitly in services.yml - check the above example - you only need to add Classname to constructor.
App\Service\Processor\Factory:
arguments:
$array: ['MilkProductionProcessor']
When enabling AppCache in my Symfony application, suddenly any injected ApcCache gets replaced with AppCache instances, yielding:
Argument passed to MyClass::__construct() must be an instance of Doctrine\Common\Cache\ApcCache, instance of AppCache given
The injection happens through:
services:
my_class:
class: MyClass
arguments:
- #cache
cache:
class: Doctrine\Common\Cache\ApcCache
Which seems very weird. Any suggestions on how to fix this would be much appreciated.
According to the previous response and my analyze, it seems that when using the http cache ($kernel = new AppCache($kernel); in app.php), Symfony create a #cache variable which is an instance of AppCache. It will override the #cache variable you created. Thus name differently your variable like :
services:
my_class:
class: MyClass
arguments:
- #apc_cache
apc_cache:
class: Doctrine\Common\Cache\ApcCache
It seems to be a bug of Symfony3.0.3
I am trying to override the standard template guesser ( located in Sensio\Bundle\FrameworkExtraBundle\Templating; ) becouse I want to use annotations to set the view but need to change the logic of how the actual view file is chosen.
I have seen this: https://github.com/elnur/ElnurTemplateGuesserBundle
but I was wondering if there is a way to just override the service in configuration.
I tried setting:
services:
sensio_framework_extra.view.guesser:
class: myCompany\myBundle\Templating\TemplateGuesser
but I get:
ContextErrorException: Catchable Fatal Error:
Argument 1 passed to Sensio\Bundle\FrameworkExtraBundle\Templating\TemplateGuesser::__construct()
must implement interface Symfony\Component\HttpKernel\KernelInterface, none given
Am I supposed to set an argument in the service config setting? But how do I reference the HttpKernel?
Or am I missing something?
TIA.
You can inject the kernel the same way the as the original TemplateGuesser. The name of the Kernel service is just simply kernel.
services:
sensio_framework_extra.view.guesser:
class: myCompany\myBundle\Templating\TemplateGuesser
arguments: [ "#kernel" ]
To see a full list of services in the container, run
$ php app/console container:debug
Of which you'll see the kernel listed as one of them.