Error trying to inject Google_Client via Objects.yaml or @Flow\Inject

Hi,
As of subject I’m developing a Neos Plugin that requires the Google_Client.
Unfortunately using the usual pattern Interface + Object.yaml does not work and returns:
The object “Google_Client” which was specified as a property in the object configuration of object “[…]” (automatically registered class) does not exist.

I tried with @Flow/Inject annotations but I get the same error.
I also tried with other root namespace classes and all of them gave the same error.

I then saw in the typo3/neos-googleanalytics package that the author uses directly new \Google_Client and it works, indeed; but i would really like to avoid it.

Can somebody please explain me with is it not possible to inject such classes with the very useful afore mentioned methods?

Thanks,
Nicola

What’s the Google_Client?

Hi @bwaidelich ,
sorry, u’re right, should have specified it better.

Google_Client is the class for google api php client library that I include as a vendor via composer, you can find it here: https://packagist.org/packages/google/apiclient

Thanks,
Nicola

Ok, thanks for clarifying.

Dependency Injection only works for classes that are managed by Flow.
And in general it only makes sense to inject Singletons anyways.
Why do you want to avoid

new \Google_Client()

?

You could try to enable object management for the class (see Object Framework — Flow Framework 8.3.x documentation) but I doubt that it works/improves things.

Granted, this exception is not very helpful at all… It should instead say that the class exists but is not managed by the Flow ObjectManager.

Thanks for the explanation @bwaidelich, now I think I did not get right what “managed by Flow” means :confused:.

What confuses me is that the same dependecy injection via Objects.yaml works flawlessly for maxmind-db/reader - Packagist (for example), and the only difference I can see is the namespacing of vendor injected class.

Why do you want to avoid new \Google_Client()?

As of my very personal opinion using it is a more tight coupling than using Objects.yaml; granted this is kinda an edge case using Objects.yaml injection I could change configuration (only) using the same package in another project, i.e. injecting services of the very same vendor package.

Do you suggest another approach from your experience?

Thanks again for the help,
Nicola

Hi @ilCerchiari,

so I took some minutes to dig into this:

According to the docs:

The lifecycle of objects are managed centrally by the object framework. It offers convenient support for Dependency Injection and provides some additional features such as a caching mechanism for objects. Because all packages are built on this foundation it is important to understand the general concept of objects in Flow. Note, the object management features of Flow are by default only enabled for classes in packages belonging to one of the typo3-flow-*` package types. All other classes are not considered by default. If you need that (see Enabling Other Package Classes For Object Management).

In short: For 3rd party libraries (that are by default installed under Packages/Libraries object management is not enabled by default, but you can change this via Settings.yaml (includeClasses) or using Objects.yaml.

It only works if you enable it, for example with the following Objects.yaml:

MaxMind\Db\Reader:
  scope: 'singleton'
  arguments:
    1:
      value: 'foo'

right?

I just tested the same thing with Google_Client :

Google_Client:
  scope: 'singleton'

But I ran into following compile-time exception:
tried to configure unknown object "Google\Client" in package ...

Turns out you stumbled upon a weird bug in Flow that replaces underscores with backslashes. I think that might be a relict from the old namespace-syntax but I’ll check with the team.

Not really, it’s exactly the same amount of coupling really if you refer to a class from that package.
What you could do however, is create an interface that contains only the methods you need and then set the implementation via Objects.yaml - and the implementation could be an adapter class that translates from your API to the original implementation vice versa…
You judge whether that’s worth the effort though.

Hi @bwaidelich u’ve been of unvaluable help.
I think I got your points and I agree with all of them I just have a doubt that maybe was generateed by a confusing explanation of mine.
Let me try to make it clearer. What we usually do (and probaly is wrong or non-optimal) is something like this:

Path\To\My\package\Interface:
  scope: singleton
  className: 'GeoIp2\Database\Reader'
  arguments:
    1:
      value: '%FLOW_PATH_DATA%Persistent/GeoDB/GeoIP2-Country.mmdb'

Then in my package I just inject, via Inject annotation, my interface. This way is very comfortable when in another package I for example want to change the value of the argument (in this specific case the class access different .mmdb files) or even the GEoIp2 class with another, compatible one of course, without changing a line of code.

If I got you right it is exactly, apart from the implementation class that was not needed in the exmaple case, what you stated in your last suggestion, but if I try to replicate the same thing as above with that google class (or other root namespaced classes) it does not work.

I can make further tests on this matter if you think it’d be helpful.

Let me thank you again for your help and explanations.
Nicola

I’m glad to read that :wink:

oh, yes, now it makes much more sense and this is exactly what I suggested.

Thanks, but that’s not required as I already found the culprit: It’s exactly this line in the Flow ConfigurationBuilder that replaces underscores (_) with backslashes (\) during compilation: https://github.com/neos/flow-development-collection/blob/master/TYPO3.Flow/Classes/TYPO3/Flow/Object/Configuration/ConfigurationBuilder.php#L110

I already asked for a reason in the flow core slack channel, I’ll keep you posted

From the flow-core-dev slack channel:

@robert: Allright, took a while to find out - but with the help of @kdambekalns I found it :wink: In 2009 we used the PHP6 namespace operator (e.g. F3::FLOW3::Component::ComponentManagerInterface) and later switched to the namespace operator we used today. But before we even used PHP6, we used underscores as namespace separators, by convention. So, we think that the line you found provides some kind of backwards compatibility of configuration from that time.

The remaining replaces you’ll find are due to the cache framework’s restrictions (you can’t use a backslash there, so we use underscores). You may want to test this, but I think the line can be removed.

I’ll try this and prepare a PR

Where shall I send the Thank-You beers? :grin:

Thanks,
Nicola

You could come to a Neos event and buy me one :wink:
But don’t be too quick: I looked into this, but it turns out it won’t be as easy to fix as I hoped for:
We use the underscore in lots of places as special character (e.g. to separate namespace segments in the proxied classes, as the backslash is not a valid character for the filename / cache identifier)… So when I remove the above mentioned class, I get errors regarding class loading etc.

Well OK, now you owe me a beer: https://github.com/neos/flow-development-collection/pull/655 :wink:
It turned out not to be too much hassle after all. But this is not yet final it needs tests & discussion and might have side effects that I’m not aware of yet

Hi @bwaidelich, that’s way more help that was expected/needed, therefore a huge thanks.
Take your time to test it and discuss it, no rush on my side.

Thanks again,
Nicola