Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected action/filter execution order #2116

Closed
4 of 6 tasks
kimhf opened this issue Sep 20, 2018 · 2 comments
Closed
4 of 6 tasks

Unexpected action/filter execution order #2116

kimhf opened this issue Sep 20, 2018 · 2 comments

Comments

@kimhf
Copy link
Contributor

kimhf commented Sep 20, 2018

Submit a feature request or bug report


What is the current behavior?

Some notable actions, filters and events are being executed in the following order:

  1. data collected from controllers
  2. wp_enqueue_scripts (action)
  3. the_content (filter) on main query
  4. wp_head (action)
  5. get_header (action)
  6. get_footer (action)
  7. wp_footer (action)

What is the expected or desired behavior?

The actions and filters being executed in the following order:

  1. get_header (action)
  2. wp_head (action)
  3. wp_enqueue_scripts (action)
  4. data collected from controllers
  5. the_content (filter) on main query
  6. get_footer (action)
  7. wp_footer (action)

Bug report

Plugin authors often expect the execution order to be the same on every theme for these key actions and filters.

One example would be Jetpack setting up a the_content filter during get_header. The result is the filter is not applied to the main content loop for a Sage theme.

We also might use template tags in controllers, so 3. party filters should be set up before collecting the data.

Please provide steps to reproduce, including full log output:

The issue was tracked by logging the execution order via priority 0 hooks on tracked actions.

Please describe your local environment:

WordPress version: 4.9.8

OS: Windows 10

NPM/Node version: 8.11.3

Where did the bug happen? Development or remote servers?

On all sites with incompatible plugins.

Is there a related Discourse thread or were any utilized (please link them)?

This issue can be hard to notice, but is related to the issues with wp_enqueue_scripts that is fixed in the template() function. My suggestion would be to replace that fix with something covering more actions/filters to increased compatibility with more plugins.

@kimhf
Copy link
Contributor Author

kimhf commented Sep 20, 2018

This is the solution I used to fix the issues with Jetpack:

Add a new helper and change template() in helpers.php

/**
 * Execute functions hooked on a specific action hook, and buffer the output.
 * The buffer content is output the next time the action hook is executed.
 * All hooks are removed from the action after both action hook executions.
 * @param string $tag The name of the action to be executed.
 * @return void
 */
function do_buffered_action($tag)
{
    ob_start();
    do_action($tag);
    $c = ob_get_clean();
    remove_all_actions($tag);
    add_action($tag, function () use ($c, $tag) {
        echo $c;
        remove_all_actions($tag);
    }, PHP_INT_MAX);
}

/**
 * @param string $file
 * @param array $data
 * @return string
 */
function template($file, $data = [])
{
    return sage('blade')->render($file, $data);
}

Change the template_include filter in filters.php by adding
collect(['get_header', 'wp_head'])->each(__NAMESPACE__.'\\do_buffered_action');

/**
 * Render page using Blade
 */
add_filter('template_include', function ($template) {
    collect(['get_header', 'wp_head'])->each(__NAMESPACE__.'\\do_buffered_action');
    $data = collect(get_body_class())->reduce(function ($data, $class) use ($template) {
        return apply_filters("sage/template/{$class}/data", $data, $template);
    }, []);
    if ($template) {
        echo template($template, $data);
        return get_stylesheet_directory().'/index.php';
    }
    return $template;
}, PHP_INT_MAX);

This makes it possible to execute the get_header and wp_head actions early, but delay the output
until the action is triggered again in the template files.

The tracked actions and filters execute in the same order as in a core theme, and filters set up during get_header is applied to functions used in controllers.

The question is does this have undesired side effects?

@garrettw
Copy link

I can confirm that OP's expected behavior is accurate as per http://rachievee.com/the-wordpress-hooks-firing-sequence/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants