WordPress.org

Make WordPress Core

Opened 7 months ago

Last modified 6 months ago

#46991 new feature request

Stop propagation on actions or filter

Reported by: screamingdev Owned by:
Milestone: Awaiting Review Priority: low
Severity: normal Version:
Component: Plugins Keywords: 2nd-opinion
Focuses: Cc:
PR Number:

Description

It would be great to stop further filter-hooks/action-observer when we reached a state that should not continue.

This helps especially for custom implementations:

  • Rejection actions that should not run.
  • Return filtered values without giving other plugins the chance to change them.

All just for specific cases.

Possible implementation:

  • Normal but impossible: Event-Class as wrapper reflecting whether to continue or stop.
  • Semi-normal-way: Throwing a exception like \WP_Stop_Propagation_Exception telling WP_Hook to stop.
  • Absolute absurd way: Setting a global variable like $stop_propagation = true

... and there may be more.

Change History (4)

#1 @johnbillion
7 months ago

  • Component changed from General to Plugins
  • Keywords reporter-feedback 2nd-opinion added
  • Priority changed from normal to low

Is there a specific use case for this? It sounds like it has the potential to break the actions and filters system with little benefit.

#2 @screamingdev
7 months ago

Surely I can make up some use cases either small and big or simple and important. So I just point out that this leads to a better event-based-system (as possibly known from other languages or frameworks) and give an example for action and filter. I am sure there is a need among developers to stop WordPress or other plugins/themes from doing things. Currently noone has granular control over the things that happen in the event queue / hooks and deregister all remainder or the whole action/filter is no proper solution.

Action:

<?php
add_filter( 'wp_footer' , function () {
  if ( is_sales_page( 'footer' ) ) {
    echo get_page( 'footer-salespage' );
    // stopPropagation
  }
} );

Yee this can be done in the template too and I just made up functions.

Filter:

<?php
add_filter( 'register' , function () {
  // stopPropagation
} );

We simply disable something.


On the one hand this solves all the scenarios where customers needs changes for their own site. So if WordPress wants to become or name itself kind of "framework" with event sourcing then it truly should be capable of stopping events.
On the other hand this blocks out plugins and theme-functionality which is fine because someone truly intended to.

This can be solved in multiple ways:

  • add stopPropagation (and keep it in WordPress)
  • Allow a dropin for WP_Hook
  • Do not make WP_Hook a final class

What else do you like to discuss?

#3 @screamingdev
7 months ago

  • Keywords reporter-feedback removed

#4 @screamingdev
6 months ago

Hint @johnbillion :

  • This is like "Oh, the filter 'the_content' shall not continue with this kind of text".
  • Comparable to Event.stopPropagation() from JavaScript

This is a possible solution directly in \WP_Hook:

<?php

                do {
                        $this->current_priority[ $nesting_level ] = $priority = current( $this->iterations[ $nesting_level ] );

                        foreach ( $this->callbacks[ $priority ] as $the_ ) {
                                if( ! $this->doing_action ) {
                                        $args[ 0 ] = $value;
                                }

                                // Avoid the array_slice if possible.
                                if ( $the_['accepted_args'] == 0 ) {
                                        $value = call_user_func_array( $the_['function'], array() );
                                } elseif ( $the_['accepted_args'] >= $num_args ) {
                                        $value = call_user_func_array( $the_['function'], $args );
                                } else {
                                        $value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int)$the_['accepted_args'] ) );
                                }

                                if ( $value instanceof \WP_Hook_Stop ) {
                                        $value = $value->get_value();
                                        break 2;
                                }
                        }

                } while ( false !== next( $this->iterations[ $nesting_level ] ) );
<?php

class WP_Hook_Stop {
        __construct( $value = '' );
        get_value();
}

This does not break anything and allows some dynamic in the WP_Hook queue.
There is nothing wrong with early termination of things when the solution is alrady known.

Last edited 6 months ago by screamingdev (previous) (diff)
Note: See TracTickets for help on using tickets.