0

I have an option admins_settings that has an array of sub-options that is stored in the wp_options table. Before I get its value from the table, I check if this field exists by running this code:

if ( !isset( get_option( 'admins_settings' )['option_name'] ) ) {
    return false;
} else {
    return get_option( 'admins_settings' )['option_name'];
}

Here, I'm hitting the table 2 times (if the field exists) to get its value. Is there any way to get the value of the field with one hit?

I know that the get_option function accepts a default value if the option doesn't exist. But this works with the simple values:

get_option( 'admins_settings', false );

But not with array values:

get_option( 'admins_settings' )['option_name'];

Because the option admins_settings could be there, but it doesn't have that item option_name.

Again, is there any way to get the sub-value of the field with one hit?

2
  • I found something that may help here. I can do something like this: get_option('admins_settings')['option_name'] ?? false; But I'm not sure if that will hit the table one time because (as mentioned in the example) this is equivalent to: isset(get_option('admins_settings')['option_name']) ? get_option('admins_settings')['option_name'] : false; Can anyone confirm, please? Apr 19, 2022 at 22:29
  • The wordpress developer documentation states: "Not initializing an option and using boolean false as a return value is a bad practice as it triggers an additional database query." But in a fight I'd back @tom-j-nowell
    – dj.cowan
    Feb 9 at 13:55

2 Answers 2

3

I'm hitting the table 2 times (if the field exists) to get its value.

No, you aren't, and there is no evidence for this. You may not be hitting the database at all if it has autoload enabled. WordPress caches things in memory during the same request, and it bulk fetches options set to autoload for performance reasons.

The same is true of other things such as post meta. WordPress will attempt to store this data in WP Cache to avoid going back to the database again. E.g. when you fetch a post, it also fetches its post meta and its terms all at once to avoid lots of small database queries. This is also why object caches have such a huge boost to performance as it allows that cache to stay alive across multiple requests.

But this works with the simple values:

But not with array values:

They are all simple values, and they are always simple values. Options are strings, and have always been strings. If you try to save an array/object then WordPress will attempt to turn it into a string using PHP serialize, then deserialize it when you fetch it. This has security consequences.

There is no such thing as "sub-options", "nested options", or "child options". There is just options. admins_settings is your option, and WordPress sees the entire array as the value, not a series of values, but a singular value that must be serialized. You cannot provide defaults for sub-values this way because options cannot have sub-values. That your value contains structured data is coincidental.

Instead, you have 2 options

  1. Store separate values as separate values, ideally with a prefix
  2. Treat you array as an array and not a weird sub-option ( because there's no such thing as sub-options )

So you would check it the same way you would check an array has a value:

$data = [];
if ( isset( $data['test'] ) ) {
    return $data;
} else {
    return false;
}

Remember, the options table uses plaintext for the value column, the idea that you can store objects and arrays is an illusion WordPress provides to make life easier, and one that would be near impossible to fix after nearly 2 decades.

2
  • Thank you @Tom! That was a great in-depth answer and explains a lot. And you are right, I've installed the Query Monitor and tested it. I found that it hits the DB only once for the first time, then I didn't see any DB query related to that field when I refreshed the page. Many thanks!! Apr 20, 2022 at 1:33
  • 1
    something related that might be useful to know, is that autoloaded options are more performant due to the bulk fetch, but if you put a lot of data in them it can degrade performance as it's always loaded into memory. E.g. if you have several megabytes because you used lots of long text widgets you can slow down your site and increase memory consumption. The same if you decide to store lots of big things. Structured data also means it's harder to change the option values using external tools such as SQL queries or the options.php user interface
    – Tom J Nowell
    Apr 20, 2022 at 9:20
1

Upon entering the if() statement of your code, it will return to the calling function either way. So, you could simply use:

return get_option('admins_settings')['option_name'];

which will return boolean false if the option doesn't exist or it will return the option value if it does exist.

1
  • I tried this and I got this error Notice: Trying to access array offset on value of type bool in functions.php on line 121. But that was fixed by trying this return get_option('admins_settings')['option_name'] ?? flase; But this won't answer my question: Does this hit the DB one time or twice to get the value? Apr 19, 2022 at 23:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.