I was browsing through Drupal 8's source code to try to educate myself a bit about Symfony's dependency injection and how it's used in Drupal.
Then I stumbled upon these lines in core/lib/Drupal/Core/DependencyInjection/ContainerBuilder.php:
/**
* Overrides Symfony\Component\DependencyInjection\ContainerBuilder::set().
*
* Drupal's container builder can be used at runtime after compilation, so we
* override Symfony's ContainerBuilder's restriction on setting services in a
* frozen builder.
*
* @todo Restrict this to synthetic services only. Ideally, the upstream
* ContainerBuilder class should be fixed to allow setting synthetic
* services in a frozen builder.
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER) {
Container::set($id, $service, $scope);
}
So then I looked at core/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Container.php where the set method is defined like this:
/**
* Sets a service.
*
* @param string $id The service identifier
* @param object $service The service instance
* @param string $scope The scope of the service
*
* @api
*/
public function set($id, $service, $scope = self::SCOPE_CONTAINER)
{
if (self::SCOPE_PROTOTYPE === $scope) {
throw new InvalidArgumentException('You cannot set services of scope "prototype".');
}
$id = strtolower($id);
if (self::SCOPE_CONTAINER !== $scope) {
if (!isset($this->scopedServices[$scope])) {
throw new RuntimeException('You cannot set services of inactive scopes.');
}
$this->scopedServices[$scope][$id] = $service;
}
$this->services[$id] = $service;
}
The thing that struck me is that is isn't defined as a static method, but it is called as one. At first I thought this might be some Drupal/Symfony/PHP magic I didn't know about, so I downloaded the Drupal 8 version of Devel and went to /devel/php to test the following code snippet:
use Symfony\Component\DependencyInjection\Container;
Container::set('test', new StdClass());
I got the following error:
Fatal error: Using $this when not in object context in core/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Container.php on line 201
I assume the set method of Drupal's ContainerBuilder isn't used anywhere in core? If it is, how come it doesn't throw the fatal error?
I set the priority to minor and the category to support request because obviously this doesn't actually break anything (I can use Drupal as expected). I'm just curious about how this actually works... I would be very thankful to anyone who's able and willing to explain this to me :-)
Comments
Comment #1
dawehnerYeah well, this is only possible if you are in the context of the calling code, so sure your custom code is simply not wrong.
You should get the container via drupal_container() and then you can set.
Comment #2.0
(not verified) commentedUpdated issue summary.
Comment #3
Codenator commentedWhen I call use Drupal\Core\Extension\InfoParser; in my project i got the same error.
I try use InfoParser::parse($file); in code.
Thanks.
p.s. Maybe I use wrong class for parse info?
drupal_parse_info_file not working for me like in drupal 7.
p.s. I find answer but should be information in documentation about this because it is about developing modules
so using \Drupal::service('info_parser')->parse($file); help well
I am new there if I doo wrong post please explain me how do right way.
Comment #4
dawehnerDo you use still php 5.3?
Comment #5
Codenator commentedNo php 5.5
Comment #7
cilefen commentedComment #8
cilefen commentedComment #16
longwaveAnswering this because I've just run across this myself: although it looks like a static call, the Symfony ContainerBuilder extends Container, so Container is the grandparent class of the Drupal ContainerBuilder, and specifying the class name is the correct - and only - way to call a grandparent (or higher) method. This isn't very well documented in the PHP docs, as far as I could see.
See https://3v4l.org/P80HK for a simplified example.