WordPress.org

Make WordPress Core

Opened 13 months ago

Last modified 4 months ago

#51699 reopened enhancement

The only correct method to check for existence of option

Reported by: ttodua Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Options, Meta APIs Keywords:
Focuses: Cc:

Description

There are many valid cases to check if particular option exists in WP (checking for leftovers of other plugins, or existence of some theme options, or some other cases).

The current advertised method to check that is:

if ( ! get_option('optionName') ) 

However, that is no way the correct approach as it is not able to check for option existence (because if the option is set with value: null/false/0/empty-string, then the check for existence will be meaningless with that approach).

So, I suggest to add a correct function named option_exists to get the correct answer:

function option_exists( $option_name, $site_wide = false ) {
        global $wpdb; 
        return $wpdb->query( $wpdb->prepare( "SELECT * FROM ". ($site_wide ? $wpdb->base_prefix : $wpdb->prefix). "options WHERE option_name ='%s' LIMIT 1", $option_name ) );
}

Attachments (1)

51699-1.patch (1.2 KB) - added by ttodua 13 months ago.

Download all attachments as: .zip

Change History (11)

@ttodua
13 months ago

#1 @apedog
13 months ago

Testing with if ( get_option('some_option') !== false ) will not return false negatives for null, 0, ''.
But will still miss boolean false... :/

This seems to me like a useful method.
I think SELECT 1 in the query would be more appropriate. You don't want to return the option. You want to return a boolean.

#2 follow-up: @SergeyBiryukov
13 months ago

  • Component changed from General to Options, Meta APIs

Thanks for the ticket!

Just noting that get_option() has an optional $default argument, which serves exactly this purpose, it's a default value to return if the option does not exist.

So the existence could be checked with a snippet like this:

if ( 'not-exists' === get_option( 'my_option', 'not-exists' ) ) {
	echo 'The option does not exist.';
}

#3 follow-up: @ttodua
13 months ago

@apedog well, select 1 might be good also, i am not SQL master, you might adopt that. Just found in internet, that the method i posted is fastest among others, however, you can tell your thoughts.

@SergeyBiryukov thanks, but the $default option is not even related to this ticket. the ticket is about "existence" of option (nevertheless it's value, let it be string, boolean, or whatever, even no-one knows what value specific option might have, string, bool, or whatever). So, your offered one is not related to the problem directly, and doesn't solve it.

#4 in reply to: ↑ 3 @digitalsetups
11 months ago

  • Resolution set to invalid
  • Status changed from new to closed

Replying to ttodua:

@apedog well, select 1 might be good also, i am not SQL master, you might adopt that. Just found in internet, that the method i posted is fastest among others, however, you can tell your thoughts.

@SergeyBiryukov thanks, but the $default option is not even related to this ticket. the ticket is about "existence" of option (nevertheless it's value, let it be string, boolean, or whatever, even no-one knows what value specific option might have, string, bool, or whatever). So, your offered one is not related to the problem directly, and doesn't solve it.

@SergeyBiryukov is correct and the default option actually does the work that you're willing to achieve with your custom function. The $default option is just like your return value for the existence i-e: you're returning 'boolean' while $default in get_options is allowing us to set 'string' something to return if option doesn't exists.

#5 @apedog
11 months ago

  • Resolution invalid deleted
  • Status changed from closed to reopened

I don't think this is an invalid ticket. I'd say an option_exists() function would be a valuable addition to the API. I know I searched for such a function quite often when I started with WordPress.

@SergeyBiryukov's solution does work. But it's a workaround. It makes creative use of get_option's optional parameter and results in non-human-readable code (it is not immidiately obvious what the code does). And it relies on the test string not being used as the value of the option (either intentionally or accidentally). ie. it's not foolproof 100% of the time.
This is ok for most purposes, but it does not, strictly speaking, test the database if the option exists.

#6 @ttodua
11 months ago

@apedog You told every word exactly I was preparing to say. :thumb_up:

#7 in reply to: ↑ 2 @apedog
10 months ago

Replying to SergeyBiryukov:

So the existence could be checked with a snippet like this:

if ( 'not-exists' === get_option( 'my_option', 'not-exists' ) ) {
	echo 'The option does not exist.';
} else {
	echo 'The option exists.';
}

Sergey's solution above (I've edited it slightly), in fact, does not work.

If a plugin makes use of the "pre_option_{$option}" filter (which shortcircuits get_option to return a custom value), this solution will show that the option exists - even when no such option exists in the options table.

An option_exists API function would actually solve for such a situtation.

This ticket was mentioned in Slack in #core by apedog. View the logs.


10 months ago

#9 @swissspidy
10 months ago

  • Type changed from defect (bug) to enhancement

#10 @rupw
4 months ago

In addition to pre_option_{$option} you should use the alloptions and notoptions caches to save database queries

  • if the key exists in alloptions return true
  • if the key exists in notoptions return false
  • only then make the database query
  • if the value exists save it in alloptions (after running the usual filters); if not add the key to notoptions

so that if we all option_exists() and get_option() it will make at most one database query, and ditto if you call option_exists() twice for the same key it will make at most one database query. (However this is starting to duplicate a lot of the get_option logic, so maybe this should use that or be built into that somehow instead.)

Note: See TracTickets for help on using tickets.