set service class from .env or ENV in system - php

I have symfony 4 setup
in services.yml I have the following configured service
MyCompany\Interfaces\QueueProducerInterface:
class: MyCompany\Service\KafkaProducer
arguments:
$queueConfig: '#queue_config'
I want to change the setting dynamically in my .envfile, so I can inject it later, depending on the env (dev, production)....so I tried something like this:
in .env
QUEUE_DRIVER="MyCompany\Service\KafkaProducer"
and now in services.yaml
MyCompany\Interfaces\QueueProducerInterface:
# class: MyCompany\Service\KafkaProducer
class: '.%env(QUEUE_DRIVER)%'
arguments:
$queueConfig: '#queue_config'
but when I run for instance composer update I get a container error
Script cache:clear returned with error code 1
!!
!! In Compiler.php line 112:
!!
!! Incompatible use of dynamic environment variables "QUEUE_DRIVER" found in p
!! arameters.
!!
!!
!! In AbstractRecursivePass.php line 123:
!!
!! Invalid service "MyCompany\Interfaces\QueueProducerInterface": class ".%env(
!! QUEUE_DRIVER)%" does not exist.
!!
!!
how do I solve this problem? or better said, how can I set the value of this class dynamically depending on ENV.

I solved it, by creating a services.yaml inside the folder config/packages/dev/ and also `config/packages/prod/' with different settings.

In newer versions of symfony you can do that in a single services.yaml using the when#xxx syntax and use an interface, like this:
For example:
parameters:
my.nice.service.class: Some\NameSpace\RealServiceClass
when#dev:
parameters:
my.nice.service.class: Some\DevelHelper\RealServiceClass
Some\NameSpace\WorkerInterface:
class: '%my.nice.service.class%'
In your consuming class just typehint the Some\NameSpace\WorkerInterface interface in the constructor and it will be injected properly.
Of course the interface Some\NameSpace\WorkerInterface must exist in the code and both RealServiceClass and RealServiceClass must implement the interface.

Related

autowire Predis Interface in symfony

i wanna use ClientInterface in my class constructor and i give an error :
Cannot autowire service "App\Service\DelayReportService": argument "$client" of method "__construct()" references interface "Predis\ClientInterface" but no such service exists. Did you create a class that implements this interface?
seems to be i should add it manually to services.yml i added it like :
Predis\ClientInterface: '#Predis\Client'
and now i give this error:
You have requested a non-existent service "Predis\Client".
what is the solution and why symfony itself dont handle it?
you seem to be confused about how to define a service... which isn't surprising tbh
look here
https://symfony.com/doc/5.4/service_container.html#explicitly-configuring-services-and-arguments
for example
services:
App\Service\Concerns\IDevJobService:
class: App\Tests\Service\TestDevJobService
autowire: true
public: true
where
IDevJobService is an INTERFACE
and
TestDevJobService
is the actual implementation that will be auto injected
using # inside the yaml files is done to reference a service that has already been defined ELSEWHERE
https://symfony.com/doc/5.4/service_container.html#service-parameters
you probably want to watch symfonycasts services tutorial (I am not affiliated and I havent watched it myself yet (sure wish I did)).
EDIT
Predis\Client is a 3rd party class. It isn't in your App namespace or in your src folder. Symfony checks the src folder for class that it will then make to a service. See services.yaml there is a comment there, look for exclude and resource. And I'm not sure, even if you autoload it, that you can then just do #Predis\Client to reference an existing service.
be sure as well to debug your config using
php bin/console debug:autowiring
under linux you could do as well php bin/console debug:autowiring | grep Predis to find it more quickly (if it is there at all)

The definition for "security.user_password_hasher" has no class, during Symfony 5.3 phpunit tests?

The question is somehow related to: How to access a private service in Symfony 5.3 phpunit tests?. I set up env services_test.yaml by:
services:
security.user_password_hasher:
public: true
When I try retrieve security.user_password_hasher I get exception:
$encoded = static::getContainer()->get('security.user_password_hasher')->hashPassword($user, $password);
Symfony\Component\DependencyInjection\Exception\RuntimeException: The definition for "security.user_password_hasher" has no class. If you intend to inject this service dynamically at runtime, please mark it as synthetic=true. If this is an abstract definition solely used by child definitions, please add abstract=true, otherwise specify a class to get rid of this error.
Why I getting this exception? In the same test suite I retrieve EntityManagerInterface::class and everything is okay.
When I access UserPasswordHasherInterface (it's alias for security.user_password_hasher) in dev env by injecting it in controller public function homepage(UserPasswordHasherInterface $hash) no error is thrown. Why changing it settings to public: true crashing tests? It's that this service is special, and need special care in test env? Or maybe because test env change some defaults settings?
I'm newbie to Symfony. My guess is that container do some hidden magic. Someone can reveal the secret?
What does mean: [NOTE] The "security.user_password_hasher" service or alias has been removed or inlined when the container was compiled?

Defining Memcache as Symfony service produces unhelpful error

I'm trying to define Memcache as a service in Symfony 3.4 and it doesn't appear to want to work.
This has previously worked correctly with a Symfony 2 build which is where the configuration is taken from.
I've got the following code in my services.yml:
memcache:
class: \Memcache
arguments:
persistent_id: elearn
calls:
- [ addServer, [ %memcached_host%, %memcached_port% ]]
However when attempting to clear the Symfony cache I'm getting the following error:
In AbstractRecursivePass.php line 126:
Invalid service "memcache": class "\Memcache" has no constructor.
This would make sense to me if we were trying to pass some constructor arguments in the service definition, as, like it says Memcache has no constructor but clearly we aren't doing that.
Am I missing something obvious?
Removing the
arguments
Option allowed me to get past the error. Not yet certain if memcache is behaving as expected.

symfony2 CLI [Symfony\Component\Debug\Exception\ContextErrorException]

I'm having a problem whilst trying to create a command with Symfony2's Console component, in a full Symfony stack app.
If i try to pass my services in via DI, the command throws the following error when i try to run it:
[Symfony\Component\Debug\Exception\ContextErrorException]
Notice: Trying to get property of non-object
If I create the command with ContainerAwareCommand and try to get my service with
$this->getContainer()->get('mtgu.api.card.list.response.data');
I get
[LogicException]
The container cannot be retrieved as the application instance is not yet set.
My service is defiantly being loaded, as its used in a front end controller. This problem gets stranger, as if I pass a repository service - I don't get this problem!
Is there some trick to setting up a service to be passible by this? Or have I messed up my configuration somehow?
Im "autoloading" all my services by doing this in my DI Extension rather than including them all through the the main services.yml. I thought this or the ordering of the yml includes maybe effecting it - but I have tried manually including everything but still no joy!
$finder = new Finder();
$finder->name('services.yml');
/**
* #var $file SplFileInfo
*/
foreach($finder->in(__DIR__.'/../') as $file) {
$loader = new Loader\YamlFileLoader($container, new FileLocator($file->getPath()));
$loader->load('services.yml');
}
Vendor/Bundle/Command/services.yml
services:
mtgu.command.slugify:
class: MightyStudios\MtguBundle\Command\SlugifyCommand
tags:
- { name: console.command }
arguments:
- #mtgu.api.card.list.response.data
I think this maybe just some config issue, but Google has failed me to find the answer! Has anyone else run into this problem and can they shed any light!?
Many thanks
A better structure would be to put your services files in the Resources/config directory of your bundle (See also all core bundles). However, that's aside.
The problem is described by the exception: The container cannot be retrieved as the application instance is not yet set. Which is thrown in the ContainerAwareCommand#getContainer() method when $this->application is null. The application is set in the first line of the Application#add() method.
This means that you call $this->getContainer() before you add the command to the application. Maybe you use it in your constructor?
If so, remove it and only use the container in Command#execute(), Command#interact() or Command#initialize().

logging in symfony 2.3

I am trying to write my own messages to the log in Symfony 2.3, from anywhere, and not just the Controller (which I realize you can just do a "$this->get('logger')".
I've seen that in Symfony 1 you can use sfContext, but that class no longer seems to be a viable choice in 2.3.
Any help is appreciated.
Symfony2 has Service-oriented architecture (http://en.wikipedia.org/wiki/Service-oriented_architecture) and logger is one of service (by default Monolog). In controller you have access to service via $this->get('service_name'). Here is more info about service container: http://symfony.com/doc/current/book/service_container.html#what-is-a-service-container. If you wanna use logger in another service you have to define service and inject logger service. Example:
# section with defined service in your config.yml file (by default in config.yml)
services:
# your service name
my_service:
# your class name
class: Fully\Qualified\Loader\Class\Name
# arguments passed to service constructor. In this case #logger
arguments: ["#logger"]
# tags, info: http://symfony.com/doc/current/components/dependency_injection/tags.html
tags:
- { name: monolog.logger, channel: acme }
Additionally you should familiarize with dependency injection docs: http://symfony.com/doc/current/components/dependency_injection/index.html
I hope that helped. If not, please let me know where exactly you want to use logger.

Categories