Can't release with both, so let's pick one. While I'm not keen on event listeners still, this is one place they definitely make sense so we should probably kill off hook_exit().

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

aspilicious’s picture

Status: Active » Needs review
FileSize
18.99 KB

I think this will fail hard because of the caching stuff that happens with hook_exit. Just wanted to help (and learn some more symfony stuff while doing it)

aspilicious’s picture

I also removed this

-  // When serving cached pages with the 'page_cache_without_database'
-  // configuration, system variables need to be loaded. This is a major
-  // performance decrease for non-database page caches, but with Statistics
-  // module, it is likely to also have 'statistics.settings:access_log.enabled'
-  // enabled, in which case we need to bootstrap to the session phase anyway.
-  drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES);

Because I understood the page_cache_without_database is totally broken already.

Status: Needs review » Needs work

The last submitted patch, 1860026-kill-hook-exit-1.patch, failed testing.

Berdir’s picture

+++ b/core/includes/common.incundefined
@@ -2270,6 +2270,7 @@ function l($text, $path, array $options = array()) {
  *   will be a fully-qualified URL that is the destination of the redirect.
  *   This should be passed along to hook_exit() implementations.
+ *   // Should be changed
  */
 function drupal_exit($destination = NULL) {
   if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {

I assume that drupal_exit() should either be killed completely or changed to call $kernel->terminate() ?

+++ b/core/modules/statistics/lib/Drupal/statistics/EventSubscriber/StatisticsCloseSubscriber.phpundefined
@@ -0,0 +1,62 @@
+   * @param Symfony\Component\HttpKernel\Event\PostResponseEvent $event

@param \Symfony\...

+++ b/core/modules/system/tests/modules/system_test/lib/Drupal/system_test/EventSubscriber/Backup 1 of SystemTestCloseSubscriber	
@@ -0,0 +1,40 @@
+ * Definition of Drupal\system_test\EventSubscriber\SystemTestCloseSubscriber.

Should be Contains... now.

aspilicious’s picture

Status: Needs work » Needs review
FileSize
21.82 KB

Simply doesn't work. The terminate stuff is never called (see failing tests).

aspilicious’s picture

aspilicious: $kernel->terminate should be $kernel->terminate() :)
aspilicious: with a request and response object
aspilicious: and the fact that we don't have a response object is probably the reason why we need to kill drupal_exit() and instead do return new Response().

Yeah offcourse my code is incorrect but I'm still wondering why the onTerminate stuff NEVER gets called. The modified hook_exit tests never adds a watchdog record.

aspilicious’s picture

This should at least pass some tests...

David_Rothstein’s picture

OK, I'll bite - why is this a critical issue, and is it even a good idea? I think there can be value in having a "this is Drupal speaking and I'm done my processing of the request" event that is separate from the overall request. And from one look at the patch, it's pretty clear that hook_exit() is a way better developer experience than the event listener.

The one thing that has always confused me about hook_exit() though is that it runs for both cached and non-cached pages, and there is no way to run it for non-cached pages only (i.e., no end-of-request equivalent to hook_init()). So perhaps this is an opportunity to figure out how to distinguish them: What if we kept hook_exit(), but only had it run on non-cached pages; then if you want your code to run on all requests (like Statistics module) you'd use onTerminate() for that?

That would go along pretty nicely with #1833442: Remove hook_boot() (since I certainly don't think anyone has proposed getting rid of hook_init()).

Status: Needs review » Needs work

The last submitted patch, 1860026-kill-hook-exit-7.patch, failed testing.

catch’s picture

@David_Rothstein, I don't like the DX of event listeners either, but I also don't want us to ship with two versions of hook_exit() executed via completely different code paths. This might be better as a meta-issue to track where we have completely duplicated mechanisms between original Drupal bootstrap code and Symfony stuff though.

Running hook_exit() on cached pages should really just stop. Statistics is partly switched to using an AJAX request and there's really not a good use case for doing that (you could use register_shutdown_function() from settings.php if you really, really wanted to).

If we either stop running all bootstrap hooks to running on cached pages, or remove them all in favour of event listeners, then either way we'd be able to remove the concept of 'bootstrap hooks' altogether. That'd save some of the double-processing we do on every full request at the moment and it may not be an option to do one of these if we actually get rid of explicit bootstrap phases by the end of the release.

catch’s picture

Damien Tournoud’s picture

Title: Decide between hook_exit() and the onTerminate event » Remove hook_exit()

I don't see where there is an alternative. We cannot ship with hook_exit() still in there.

Fabianx’s picture

Why can't we have a default system EventListener that calls hook_exit()?

* Such the code path would be the same
* The nice DX is kept
* The meaning of hook_exit slightly changes to not run on page cached pages
** (but that would happen anyway)

Crell’s picture

The "nice DX" of hook_exit() is a red herring, I think. I actually really like the listener model. It's a bit more lines of code, but cleaner.

The problem with hook_exit() is that, like any other hook, we have to be concerned about bootstrap state, code loading order, function_exists(), and all that jazz. An event listener has none of those issues. (OK, it has a slight problem super early in the boot up process vis a vis the config system, but that's not relevant to this issue.)

Moving the call to drupal_exit() inside a listener doesn't resolve even one of drupal_exit()'s problems. It just moves the problem around. It does not let us eliminate the concept of "bootstrap hooks" or "bootstrap modules", which we want to do in order to simplify the knots in the bootstrap process.

sun’s picture

Sorry, slightly OT:

An event listener has none of those issues.

Sorry, @Crell, but I fundamentally disagree there — right now, event listeners are merely hyped as the new fancy tool, but reality is that their hard-coded priorities are going to bite us badly in the ass, unless we start to do something about them. We're not facing that problem right now, because our entire development operates in a one-dimensional environment — Drupal core is in charge of all events that are fired, and it is also in charge of the priorities (order) of all subscribed event listeners. Once you leave that one-dimensional environment and enter Drupal's modular 2-3 dimensions, you are in very deep trouble, because the hard-coded priorities are not at all designed for a modular/pluggable system. They are designed for a system that expects you to manually define custom weights for your particular application — which translates into hacking [core and contrib] code for Drupal. That's why we have configurable module weights as well as hook_module_implements_alter(), and that's why I've asked a couple of times in the past already what we're going to do about that.

aspilicious’s picture

You can define a "priority" on each event. That way you can control the order of each. The hardest problem is choosing the priority but I don't think thats more difficult than choosing a weight of a module.

In fact this is way more flexible as the priority is event based and not module based. Makes it possible for a module to push one listener to the front and another one in the back of the queue.

Symfony also has a limited dependency system for bundes. Each bundle can define a parent. This makes it possible to override stuff written in some modules bundle.

EDIT:
So you could overwrite symfony stuff classes defined by a module by your own implementation and change the priority. Dirty but it does the job. It's a bit like writing own views handlers to overwrite 2 lines of code that you want to change.

Fabianx’s picture

The "nice DX" of hook_exit() is a red herring, I think. I actually really like the listener model. It's a bit more lines of code, but cleaner.

That does not answer my suggestion to have a default listener that calls hook_exit from modules, but not as "bootstrap" hook, but just as a normal hook.

What is the problem?

Crell’s picture

Fabianx: That implies doing a "full bootstrap" for kernel.terminate events, because then drupal_exit() becomes a full, not bootstrap, hook. That makes the problem worse, not better, because it means even in a cached page we need to Load All the Codes in order to fire that one hook.

catch’s picture

Will kernel.terminate actually run when page caching is converted to httpcache though?

Crell’s picture

That depends on how we setup our index.php, I suppose. We can call terminate() on the Kernel from index.php, which will load up and call terminate() on the HttpKernel. From the looks of it that doesn't necessarily imply a boot(), but I'm not certain.

catch’s picture

The less we do on cached pages the better IMO, so I'd go for neither an event listener nor a hook if that's simple. Anything that's running in PHP on a cached page is specific to using internal caching, so if a module is reliant on it it's effectively saying it doesn't work with reverse proxies.

Fabianx’s picture

#21: So Boost for D8 would need to install its own exit_handler in PHP then? Or is Boost like functionality handled by HttpCache (or an injected HttpCacheExtension) instead?

Just thinking of an example here.

I still think its useful to have a hook to save data at session end, think of workbench, but it is okay if the hook is during the PageFlow request like hook_init() and not within bootstrap.

So: Removing hook_exit() all together - I am reluctant on that. Changing hook_exit() to run only in cases where hook_init() does also run (full page request): YES!

Crell’s picture

For non-cached pages, between the kernel.response event (runs before output is sent) and kernel.terminate event (runs after output is sent) I think we're covered so hook_exit() is no longer needed.

For cached pages, I can't think of any use cases for still always running extra code, and catch is correct that the less work we do there the better.

My vote is to kill hook_exit() entirely, and sort out exactly where and when terminate fires when we do the HttpCache conversion and can see and touch the problem space better.

catch’s picture

I checked boost_exit() and it only runs to write to cache on uncached pages, so anything that runs at the end of full page requests should cover that.

Lars Toomre’s picture

I am a bit confused by this issue. With hook_boot() and hook_exit() potentially going away, does this mean that a Drupal website cannot do any "extra" processing before/after serving a cached page to an anonymous user/bot?

catch’s picture

@Lars, if you're not using internal page caching (i.e. using varnish or similar instead), then you can't do that anyway now. #1209532: Count node views via AJAX in the statistics module is one example of where we should not have been using hook_exit() in the first place.

For a module that only works with internal caching, then that module will need to instruct users to paste some code into settings.php (which will always be executed on a cached page).

Lars Toomre’s picture

Thanks @catch. So settings.php code can be used for the 'before' extra processing. I did not realize that.

Is the recommended way to do the 'post' anonymous user processing logic by sending the data to server via a separate AJAX call/request? Thanks in advance.

catch’s picture

Either an ajax callback, or you could register a shutdown function in settings.php.

Crell’s picture

Someone needs to update the patch from #7 to tell hook_exit() to exit...

David_Rothstein’s picture

Title: Remove hook_exit() » Remove hook_exit() for cached page requests, and decide between it and the onTerminate event otherwise

You can define a "priority" on each event. That way you can control the order of each. The hardest problem is choosing the priority but I don't think thats more difficult than choosing a weight of a module.

In fact this is way more flexible as the priority is event based and not module based. Makes it possible for a module to push one listener to the front and another one in the back of the queue.

You're comparing to module weights, but what about hook_module_implements_alter()? That's already a lot more flexible than priorities, and some hook_exit() implementations may require that flexibility.

***

Anyway, it seems like there's definitely consensus in this issue to remove hook_exit() on cached page views, but no consensus on the rest.

Also, I looked into the code a little more, and as far as I can see, what @Fabianx suggested in #13 is actually very close to what the code is already doing (see Drupal\Core\EventSubscriber\RequestCloseSubscriber - this code only runs for non-cached pages and contains an onTerminate event which is where hook_exit() is invoked from on those pages). So all we should really need to do here is remove the other place where hook_exit() is invoked for cached pages.

A patch that did that might be useful for comparison purposes.

David_Rothstein’s picture

Status: Needs work » Needs review
FileSize
9.05 KB
6.03 KB

Something like this, for example. The patch contains a test to verify that hook_exit() is not being called on cached pages.

The only problem is that the statistics stuff will fail; as written, statistics_exit() currently does expect to be run even for cached pages (when it writes to the access log). I'm not sure if there's an issue somewhere to move the access log stuff to an Ajax callback like was already done for the node counter in #1209532: Count node views via AJAX in the statistics module.

Status: Needs review » Needs work

The last submitted patch, 1860026-hook-exit-31.patch, failed testing.

catch’s picture

Yes we do need to fix the statistics access logging (either fix it properly or just drop that feature since really people can just use analytics or check their server logs). If that's causing test failures I'd be happy to break it for cached pages and handle it in a follow-up. I've opened #1869282: Fix statistics access log so it makes sense for sites that don't use a database page cache.

@David, hook_module_implements_alter() is broken for bootstrap hooks, since they go through bootstrap_invoke_all() and never touch module_implements(), however if we remove hook_exit() from cached pages we can run it through module_invoke_all() and then the _alter() would work.

Crell’s picture

Then not using a hook and relying on onTerminate() would not be a regression then anyway, no?

David_Rothstein’s picture

FileSize
9.35 KB

Looks like I forgot to remove 'exit' from the list of bootstrap hooks. This reroll fixes that. It will still fail tests due to the Statistics thing.

That issue with hook_module_implements_alter() is just weird. As far as I can tell, hook_module_implements_alter() does work on hook_exit(), but only for uncached pages. So the hook can actually be invoked in a different order depending on whether it's a cached page view or not! That's pretty broken (and will be "fixed" by this issue either way). I think it means that if your module's implementation of hook_exit() only cares about acting on uncached page views (as many do) then hook_module_implements_alter() actually does work just fine for you currently, though.

David_Rothstein’s picture

Status: Needs work » Needs review

It's not going to pass, but just to see where it's at...

Status: Needs review » Needs work

The last submitted patch, 1860026-hook-exit-35.patch, failed testing.

catch’s picture

The statistics accesslog removal patch is RTBC, so that should unblock this one as soon as I get to committing it.

andypost’s picture

How onTerminate() will affect a sub-requests?

Crell’s picture

onTerminate() is only called after the master response has been sent at the end of index.php (or whatever utility function that was just split off to). If you fire a couple of subrequests in one request, onTerminate() is only called once when the PHP process itself ends. So to #39, "it doens't".

catch’s picture

Priority: Critical » Normal

Opened a meta-critical to track related issues to this one. Demoting this since it's one of a set.

#1888734: Get rid of all 'bootstrap' hooks

catch’s picture

Status: Needs work » Needs review

#35: 1860026-hook-exit-35.patch queued for re-testing.

Crell’s picture

I'm impressed that this still applies. :-) That would make #35 commitable, no?

catch’s picture

Status: Needs review » Needs work

Actually it won't apply now because I just committed another bootstrap hook removal patch, but should be a quick re-roll.

katbailey’s picture

Status: Needs work » Needs review
FileSize
9.32 KB

Rerolled.

ParisLiakos’s picture

Status: Needs review » Needs work

needs one more reroll after #1833442: Remove hook_boot() got in

ParisLiakos’s picture

Status: Needs work » Needs review
FileSize
5.93 KB

Hmm i renamed HookBootExitTest.php to HookExitTest.php as well..it should be done in the other issue..dunno if i should post it as followup there or here, i guess its fine

catch’s picture

Status: Needs review » Reviewed & tested by the community
catch’s picture

Title: Remove hook_exit() for cached page requests, and decide between it and the onTerminate event otherwise » Change notice: Remove hook_exit() for cached page requests, and decide between it and the onTerminate event otherwise
Status: Reviewed & tested by the community » Active

OK so we still need to figure out hook_exit() vs. onTerminate(), but otherwise I've committed/pushed this to 8.x. Just hook_watchdog() left now for bootstrap hooks.

Will need a change notice, and probably a follow-up to decide whether we want to remove the hook altogether or to document the difference between the hook and the event etc.

Crell’s picture

Title: Change notice: Remove hook_exit() for cached page requests, and decide between it and the onTerminate event otherwise » Remove hook_exit() for cached page requests
Status: Active » Fixed

Change notice is here: http://drupal.org/node/1911186

I didn't spend much effort on it, because I opened up #1911178: Remove hook_exit() as a follow up. As that issue notes, I now see exactly one place that hook_exit() is called, which is by the terminate event. That makes it entirely vestigial at this point. We can discuss that in the linked issue, though.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.