Opened 7 years ago
Last modified 7 years ago
#43621 new feature request
Introduce `add_action_once` and `add_filter_once` sugar.
Reported by: | soulseekah | Owned by: | |
---|---|---|---|
Milestone: | Awaiting Review | Priority: | normal |
Severity: | normal | Version: | |
Component: | Plugins | Keywords: | has-patch has-unit-tests dev-feedback |
Focuses: | Cc: |
Description (last modified by )
It is often useful (especially when writing tests for filters, actions) to run a callback only once, regardless of how many times the filter/action is actually applied/done.
add_filter_once( 'test_action_once', '__return_true' ); $this->assertTrue( apply_filters( 'test_action_once', false ) ); $this->assertFalse( apply_filters( 'test_action_once', false ) );
This would allow developers to run anonymous callbacks that remove themselves from the filter after running once. This can currently be done with the following ugly workarounds:
add_action( 'run_many_times', function() { // do stuff once and self-destruct remove_action( 'run_many_times', current( $GLOBALS['wp_filter'][ current_filter() ]->callbacks[ 10 ] )['function'] ); } );
or
$once = null; add_action( 'run_many_times', $null = function() use ( &$once ) { // do stuff once and self-destruct remove_action( 'run_many_times', $once ); } );
This is not a duplicate of #38743, the concept is different, the naming is the same, yes.
Non-clashing names here?
add_self_destructing_filter()
add_ephemeral_filter()
open to other suggestions :)
Attachments (3)
Change History (8)
#3
@
7 years ago
- Keywords has-patch has-unit-tests dev-feedback added
Initial implementation and tests for add_action_once
, add_filter_once
in 43621.once.diff. This depends on two new methods inside WP_Hook
, both of which are implemented and tested in 43621.wp_hook.diff
@johnbillion Indeed, my usual use-case for ephemeral callbacks is for writing self-cleaning tests. However, I have recently found myself suppressing wp_mail
sending via the phpmailer_init
hook for several emails being sent out in a row.
Another practical example, I thought of is to augment the first title on a post archive page:
add_filter_once( 'the_title', 'color_the_title_red' );
This would make the topmost post on the page be output with a red title, while the rest is output in the regular way.
I hope this makes sense.
Special thanks to @hokku @campusboy1987 @denisco @SergeyBiryukov for reviewing and working on this during our online contributor workshop.
#4
@
7 years ago
I'm not the biggest fan of implementing something like this in the first place over explicitly add/removing callbacks as needed, but as is this patch only supports the use of one "once" callback (per priority), e.g.
add_filter_once( 'test_filter', '__return_false' ); add_filter_once( 'test_filter', '__return_true' ); // $wp_filter['test_filter']->callbacks // // array(1) { // [10]=> // array(3) { // ["__return_false"]=>... // ["_remove_filter_once"]=>... // ["__return_true"]=>... // } // } apply_filters( 'test_filter', null ); // true apply_filters( 'test_filter', null ); // true // ... apply_filters( 'test_filter', null ); // true // etc
_remove_filter_once()
could be re-implemented as an anonymous func to ensure it's always added, but then you'd also have to check that the callback was actually added as well so other, non-once callbacks aren't accidentally removed ...
#5
@
7 years ago
@vortfu Thanks for looking at this. Indeed, the bug stems from the fact that adding a callback with the same name is not possible. _wp_filter_build_unique_id
returns the same ID for it, so _remove_filter_once
is not actually added a second time.
could be re-implemented as an anonymous func
PHP 5.2 does not support anonymous functions, unfortunately. Thus the crazy hacking :) (and bugs).
43621.2.diff contains both the 43621.wp_hook.diff patch and fixes on 43621.once.diff.
This is certainly a pattern that the test suite uses, and I could see a use for it. That said, is it a pattern that's used in non-test code often?