Closed (duplicate)
Project:
Drupal core
Version:
8.0.x-dev
Component:
base system
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
28 Apr 2012 at 22:00 UTC
Updated:
29 Jul 2014 at 20:39 UTC
Jump to comment: Most recent file
Comments
Comment #1
robloachWe should instead move the Queue system to use Dependency Injection and drupal_container() so that it can be lazy-instantiated on-demand. This reduces the load on the bootstrap as it will only load the Queue API when it's needed.
To accomplish this, we would do the following:
$container, we register a "queue" service to the QueueFactory classqueue()directlyIn code, it would look something like this:
In the above example, 'queue' is registered as the QueueFactory, which handles the creation/handling of all the queues, and 'myqueue' is a Queue that we're retrieving from the factory. Since we're sticking the factory itself in the Drupal dependency injection container, the factory isn't created until we call
->get('queue').The result is a reduced bootstrap and further decoupling since we don't depend on the
queue()function.Comment #2
robloachHere you go.
Comment #3
timmillwoodThanks for this Rob, I will review today!
Comment #4
timmillwoodWorking perfectly!
Comment #5
chx commentedSo now we have a FactoryFactory that returns various Factory classes? I am sorry but this is just hilarious given how Dries didn't write Drupal in Java so many years ago. You are basically saying he made a mistake and now we are rewriting Drupal in almost-Java. But, oh please, go ahead.
Comment #6
robloachActually, queue() implements the same factory pattern as what is proposed here. The only difference is that it's now referenced through the Container so that we don't need to load it during the Drupal bootstrap.
Comment #7
cosmicdreams commented+1 for #2.
Comment #8
chx commentedOh of course go ahead with #2. I am just hoping that by release time we have something less verbose. d()->queue($queuename) for example.
Comment #9
robloach@chx, what are your thoughts on...
->get('queue')['myqueue']... ArrayAccess would allow us to do that sort of thing.Comment #10
cosmicdreams commented@Rob Loach I thought that was only for php 5.4
Comment #11
chx commentedJust d()->queue('queuename') and be done? __get is your friend.
Edit: yes, array dereferencing is only 5.4
Comment #12
Crell commentedAnything that is spawned from the DI container will not need to call drupal_container() anyway, since the container can and should be setup to inject such dependent systems into the object on creation, which is exactly the point. So the lengthy drupal_container() incantation is a transitional syntax only. Don't Fear the Arrow. :-)
I'm good with this patch, too. (Although I'd love to see it eventually converted to a component.)
Comment #13
chx commentedMeh my suggestion doesnt work, so what about di()->queue['queuename']? One __get one arrayaccess.
Comment #14
robloach#1552744: Bootstrap for the Dependency Injection Container and make sure SimpleTest abides to it
Comment #15
robloachSame as the previous patch, with the definition moved in to the container itself with #1552744: Bootstrap for the Dependency Injection Container and make sure SimpleTest abides to it.
Comment #16
timmillwoodLooks good!
Comment #19
chx commentedWhile I am absolutely blind how can
be the same... pattern? whatever. To my eyes, which are totally not pattern based and stuck in the past, this is horrible DX. However we have committed the same for the language system already so I am totally wrong. So, I am no longer up to maintain this. Patch reflects this.
Comment #20
robloachWould a helper function like this help? This is all just syntax, and will likely change once the Kernel is in...
Then
$queue = queue($queue_name);would turn into...We could also extend QueueFactory to support __get() and turn it into something like this:
Again, this is simply syntax. It's better to actually fix the architecture and un-couple things. The current task is to get rid of all static and global variables so that we have proper scope of our Drupal instances.
Comment #21
chx commentedI dunno. It's not only the length , it's the complexity. it's absolutely trivial to document , teach, IDE integrate queue() -- it returns a QueueInterface implementing class. To the contrary, try to do either with your new d() or drupal_container->get(). I thought we learned our lesson with form API about how strings describing behavior is tricky to document. If you have a good solution, I am all ears.
Comment #22
Crell commentedWhere I want to end up is this:
No hard-coded dependencies. No long chains of incomprehension. No magic. No undocumentable strings. Nothing that prevents unit tests, reuse, or anything else that's cool. And Foo does not even know that it is being managed by a DIC; all it knows is that it has a queue and a database connection and it is moving data from one to the other.
We're a long way from there. Centralizing all of our various hard-coded front-end functions into a DIC object is a step toward getting there. It is not the final step. What it allows us to do is then refactor those things IN the DIC to be independent of it.
For the above to work, it means Foo, the Queue, and the DB connection all need to be managed through the DIC so that the DIC can put each one in the right place at the right time. This patch puts the Queue in the DIC. Next step would be to put the DB connections in the DIC, and pull from there when setting up a DB-backed Queue. That means we replace db_query() in Queue with $this->db->query(). Now we can unit test Queue with a DB that has just the queue table, or even a fake DB that has no DB behind it at all.
Then we instantiate Foo from the DIC. And the DIC handles injecting all of those things we need. And now the Ewoks dance.
This patch is step one to getting there. There are many more steps to come. Yes, it's already May, but it's only May. Let's not dig our heels in out of fear that the transitional state is no better than the current state; let's move forward toward that final state as quickly as possible, lest we end up stuck in a transitional state out of fear.
[Edit: Fix typo in code sample.]
Comment #23
neclimdulWell we should consider what we think is reasonable for core to enforce and what's going to be in place to make it reasonable for developers. We're trying to as a community reduce our STUPIDity and that's I think true of everyone in this thread. And its not like we have hard and fast control over the code, if it ends up being onerous, contrib will just write shortcuts. @see ctools
To that end, while I think its noble to pursue removing globals and not being STUPID, the fact is that whether you use the drupal_container() or queue() that relies on the container global, they are both equally dependent on a global system. We're not actually buying anything by imposing some extra typing. More importantly, the only reason we would impose this sort of "pain" on our developers would be to ensure that they use a less painful method but we don't have enough of that in place to really justify it at this point.
So while it might seem worse, I actually think its fine and probably better that for the time being we stick with the queue() wrapper and just update it to use the container for resolution. When we can safetly look at core and the contrib use cases and say "we have in place the structure to inject a queue object into 90% of our use cases" then I think we open an issue to remove queue() and we can easily all get behind it.
Comment #24
Crell commentedI can live with #23.
Comment #25
msonnabaum commentedI like #23, or I'm even good with going back to:
$queue = DrupalQueue::get('blah');Not really sure why we changed that to begin with.
Comment #26
webchickThat sounds like needs work.
Comment #27
chx commentedThis, again, belongs to Dries. This is not different from the previous God-object discussions but with deadlines looming the decision needs to be made and it needs to made now. Here are the options.
new NiceClass($container()->get('queue')->queue($queue_name))instantiating nice classes like in #22. If you do not pass that variable around but instead call a singleton then you have implemented a global variable. Hardly what we want...Comment #28
Crell commentedchx: drupal_container() is the alternative to passing a god object everywhere. But for most things, we want to avoid using even that and pass objects into the object; using a DIC is exactly what reduces or avoids needing to mess around with NiceClass($container()->get('queue')->queue($queue_name)) yourself, because it does it for you. That's what a DIC is: A system that does that stuff for you so you don't have to. :-)
Comment #29
robloach@msonnabaum at #25:
This, and
queue()for that matter, are essentially Singletons, and we want to avoid using Singletons.Comment #30
chx commented#28 but how can it do stuff if we are dealing with functions. And yes, we want to avoid Singletons!
Comment #31
robloachThis was a Singleton, and we want to avoid using Singletons. Having the
drupal_container()architecture in place fixes this problem. It may be an interim solution while the Kernel goes in, but it is a solution that works. The patch is there, and it's green.Comment #32
msonnabaum commentedI'm not saying don't use DI, I'm just saying stick the drupal_container() mess in a wrapper so we don't make DX worse.
Comment #33
robloachMove queue() to use drupal_container() ? I suppose we could do that.
Comment #34
effulgentsia commentedSounds to me like everyone more or less agrees with #23, so here's #15 updated to that. As long as we stick to procedural wrappers for now, I don't think this needs feedback from Dries until we have patches for the next level of DI integration. @chx: correct me if you disagree, and with this approach, do you still want to remove yourself from base system maintenance?
Comment #35
neclimdulShould the factory have a local store or rely on the container for the instance caching? Otherwise looks straight forward and good.
Comment #36
effulgentsia commentedEventually, I think we'll want to move queue to the plugin system where the "mapper" caches instances, but we don't have the plugin system in core yet. We do not want to do
drupal_container()->get("queue.$name")for reasons discussed in #1519376: [Meta] Extend Symfony's service container and use that for simple swappable systems and plugin access and discovery (can't compile open-ended service names).Comment #37
effulgentsia commentedWe need to figure out a documentation standard for procedural wrappers to DI services. I don't know if a simple @see makes clear that that's where the documentation is for the default implementation, but that a different class can swap out that one.
I think we have some inconsistencies in HEAD where some places use single backslash (within single quotes) and some use double backslash (search for
'Drupal\in your IDE to see what I mean). Single quotes allow either. It would be nice for us to pick an approach and stick to it. My preference is single backslash unless there's a compelling reason for double.If the class is called *Factory, should the method be changed to getInstance() (the standard method name for returning an instance that is reused on successive calls)?
Comment #38
chx commentedWe need to figure out a documentation standard for procedural wrappers to DI services -- yes we need to and it needs to include some @param docs but at least type and @return type. See above for IDE integration et al. It's the biggest reason I was so against this d_i thing. However, going this way is like making a decision I asked from Dries. You began on creating the ugly functions wrapping the nice classes of the future. So is this what we want?
Comment #39
catchWith the queue() factory change, I believe that was sneaked into one of the PSR-0 patches, purely because it's consistent with the pattern used for cache(), lock() etc. there wasn't any other particular reason for it.
fwiw I'm very happy with doing this for now, seems like the best possible transitional step:
Comment #40
timmillwoodWould we be looking at putting the code from #39 into bootstrap.inc or common.inc?
I think it could happily go in common.inc as it is now, then if queue is needed before a full boot drupal_container() can be used.
Comment #41
aspilicious commentedAnd can we make sure the docs are ok.
* Instantiate and cache a queue.Should start with a 3th person verb.
Thank you!
Comment #42
catchfwiw webchick brought up in another issue that language is already using drupal_container() directly. However in that case, it was directly accessing a global, with no previous procedural wrapper (and we do have procedural wrappers for some globals like current_path(), just nothing like language_interface() in this case).
Comment #43
chx commentedThat patch went in only because I wasn't monitoring language issues closely...
Comment #44
webchickHm. I can't remember what I said in that other issue, but +100 for a queue() wrapper around this for DX (e.g. #39). If we can do a similar language() wrapper around the DiC stuff for language things, all the better.
I guess the only reason not to do this is effulgentsia's benchmarks #1578090: Benchmark/profile kernel showed that PHP function calls are terrible for performance, but this seems like a reasonable trade-off for increased accessibility.
Comment #45
effulgentsia commentedThat only starts to matter when you get into hundreds or thousands. This varies by processor speed, but a rough guideline is 1000 no-op function calls take 1ms, which can be 1% or more of the total page request time. Having a queue() wrapper, language() wrapper, etc. doesn't mean it must be used everywhere. It can be used in most places, and functions that get called hundreds of times per page request (e.g., t(), url()) can bypass using those wrappers.
I'll repeat what I said in #36 though: IMO, all subsystems that involve factories (queue, cache, etc.) will end up getting refactored as plugins once we have a plugin system in core, and I think we're getting close to having that. So unless moving queue to DI is an urgent requirement, it might not be the best use of people's time to finish this issue, only to have it refactored again later. Meanwhile, I'm sure we have plenty of other systems that don't involve factories that are excellent candidates for moving into DI directly.
Comment #46
berdirWasn't aware of this issue, #1814496: Make queue a container service went just in.