PHP Symfony request stack not found on bundle extension load -
i'm trying create reusable logging bundle can have custom formatter log messages. formatter set in main app's config file that:
custom_logger: formatter: appbundle\services\messageformatter
then in loggerbundle/dependencyinjection/customloggerextension.php
after receive configuration i'm trying logger service , set formatter
class customloggerextension extends configurableextension { public function loadinternal(array $mergedconfig, containerbuilder $container) { $loader = new yamlfileloader($container, new filelocator(__dir__ . '/../resources/config')); $loader->load('services.yml'); $class = $mergedconfig['formatter']; $obj = new $class; $container->get('custom.logger')->setformatter($obj);
but problem logger uses request stack
services: custom.logger: class: loggerbundle\services\logger arguments: ['@request_stack']
and when try service in loadinternal
function seems request stack not initialized yet , receive error: you have requested non-existent service "request_stack"
what correct way of doing this?
edit (2016-04-21 21:54:11)
that's weird. if remove request stack , set formatter in loadinternal
, it's not in service when i'm getting controller in main app. must doing wrong :)
ok, think figured out.
when load()
or in case loadinternal()
method called, symfony passes there new container merged main one. if request service , created there, it's not saved anywhere in temporary container , destroyed (only service definitions passed main one).
since services loaded(created) on demand, it's not right instantiate on bundle load. want instead configure definitions
here how got working:
use symfony\component\httpkernel\dependencyinjection\configurableextension; use symfony\component\dependencyinjection\compiler\compilerpassinterface; use symfony\component\dependencyinjection\containerbuilder; use symfony\component\dependencyinjection\loader\yamlfileloader; use symfony\component\dependencyinjection\reference; use symfony\component\dependencyinjection\definition; use symfony\component\config\filelocator; use loggerbundle\formatter\messageformatterinterface; class loggerbundleextension extends configurableextension implements compilerpassinterface { protected $customformatter; public function loadinternal(array $mergedconfig, containerbuilder $container) { $loader = new yamlfileloader($container, new filelocator(__dir__ . '/../resources/config')); $loader->load('services.yml'); if (array_key_exists('formatter', $mergedconfig)) { $this->customformatter = $mergedconfig['formatter']; } } public function process(containerbuilder $containerbuilder) { if ($this->customformatter) { $class = $this->customformatter; //if formatter service id $serviceused = $containerbuilder->has($this->customformatter); if ($serviceused) { $class = $containerbuilder->getdefinition($this->customformatter)->getclass(); } if (!class_exists($class) || !is_a($class, messageformatterinterface::class, true)) { throw new \errorexception('invalid logger formatter'); } if ($serviceused) { $containerbuilder ->getdefinition('custom.logger') ->addmethodcall('setformatter', array(new reference($this->customformatter))); } else { $containerbuilder ->getdefinition('custom.logger') ->addmethodcall('setformatter', array(new definition($this->customformatter))); } } } }
in loadinternal()
check if formatter set in main app's config , save it. main thing happening in process()
function (you have implement symfony\component\dependencyinjection\compiler\compilerpassinterface
run).
from docs.
as process() called after extensions loaded, allows edit service definitions of other extensions retrieving information service definitions.
so, doing is:
- checking if
formatter
option value class name or service id(if that's case, retrieve class name service definition) - validate class name (checking if it's valid class , making sure implements required interface)
- adding definition logger service tells container call
setformatter
provided arguments when logger service created(when first retrieved container)
basically $containerbuilder->getdefinition('custom.logger')->addmethodcall('setformatter', array(new reference('my_formatter')));
is same declaring in config:
services: custom.logger: class: loggerbundle\services\logger arguments: ['@request_stack'] calls: - [setformatter, ['@my_formatter']]
Comments
Post a Comment