Opened 8 years ago
Last modified 8 years ago
#43621 new feature request
Introduce `add_action_once` and `add_filter_once` sugar.
| Reported by: |
|
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
@
8 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
@
8 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
@
8 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?