Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/wp-includes/option.php
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,40 @@ function add_option( $option, $value = '', $deprecated = '', $autoload = null )

$value = sanitize_option( $option, $value );

/**
* Filters whether to short-circuit adding a specific option.
*
* The dynamic portion of the hook name, `$option`, refers to the option name.
*
* Returning a non-null value will short-circuit adding the option, and the
* returned value will be used as a boolean success/failure return value.
*
* @since 7.2.0
*
* @param null $skip Whether to short-circuit adding the option. Default null.
* @param string $option Name of the option to add.
* @param mixed $value Option value.
*/
$skip = apply_filters( "pre_add_option_{$option}", null, $option, $value );

/**
* Filters whether to short-circuit the process of adding an option.
*
* Returning a non-null value will short-circuit adding the option, and the
* returned value will be used as a boolean success/failure return value.
*
* @since 7.2.0
*
* @param null $skip Whether to short-circuit the process. Default null.
* @param string $option Name of the option to add.
* @param mixed $value Option value.
*/
$skip = apply_filters( 'pre_add_option', $skip, $option, $value );

if ( ! is_null( $skip ) ) {
return (bool) $skip;
}

/*
* Make sure the option doesn't already exist.
* We can check the 'notoptions' cache before we ask for a DB query.
Expand Down
55 changes: 55 additions & 0 deletions tests/phpunit/tests/option/option.php
Original file line number Diff line number Diff line change
Expand Up @@ -615,4 +615,59 @@ public function helper_object_cache_stats_cmd_get() {

return $stats['cmd_get'];
}

/**
* Data provider for test_pre_add_option_filter_short_circuits().
*
* @return array[]
*/
public function data_pre_add_option_filter_short_circuits() {
return array(
'returns true' => array( '__return_true', true ),
'returns false' => array( '__return_false', false ),
);
}

/**
* @ticket 37928
*
* @dataProvider data_pre_add_option_filter_short_circuits
*
* @covers ::add_option
*
* @param callable $callback The filter callback to use.
* @param bool $expected The expected return value of add_option().
*/
public function test_pre_add_option_filter_short_circuits( $callback, $expected ) {
add_filter( 'pre_add_option', $callback );
$result = add_option( 'test_pre_add_option', 'value' );
remove_filter( 'pre_add_option', $callback );

$this->assertSame( $expected, $result );
// Confirm the option was NOT actually written to the database.
$this->assertFalse( get_option( 'test_pre_add_option' ) );
}

/**
* @ticket 37928
*
* @covers ::add_option
*/
public function test_pre_add_option_dynamic_filter_short_circuits_specific_option() {
add_filter( 'pre_add_option_test_specific_option', '__return_true' );
$result_specific = add_option( 'test_specific_option', 'value' );

// A different option must NOT be short-circuited by the per-option filter.
$result_other = add_option( 'test_other_option', 'value' );

remove_filter( 'pre_add_option_test_specific_option', '__return_true' );

// The targeted option was short-circuited.
$this->assertTrue( $result_specific );
$this->assertFalse( get_option( 'test_specific_option' ) );

// The other option was written normally.
$this->assertTrue( $result_other );
$this->assertSame( 'value', get_option( 'test_other_option' ) );
}
}
Loading