Make WordPress Core

Opened 3 years ago

Last modified 2 months ago

#51699 reopened enhancement

The only correct method to check for existence of option

Reported by: ttodua's profile 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 3 years ago.

Download all attachments as: .zip

Change History (12)

@ttodua
3 years ago

#1 @apedog
3 years 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
3 years 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
3 years 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
3 years 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
3 years 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
3 years ago

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

#7 in reply to: ↑ 2 @apedog
3 years 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.


3 years ago

#9 @swissspidy
3 years ago

  • Type changed from defect (bug) to enhancement

#10 @rupw
3 years 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.)

#11 @ttodua
2 months ago

@SergeyBiryukov any thoughts on this?

Note: See TracTickets for help on using tickets.