PHP Catchable fatal error: Object of class stdClass (or something else) could not be converted to string

Does this error look familiar to you?

Catchable fatal error: 
Object of class stdClass could not be converted to string in /wp-content/plugins/relevanssi/lib/common.php on line 396

That example is specific to Relevanssi, a plugin that enhances the standard WordPress search functionality, but I’ve seen this happen a few times in a few different ways.

What’s going on?

The error explains what’s happening — some piece of PHP code is trying to treat an object like a string. But why are there unexpected objects in my WordPress?

Digging into Relevanssi

The specific error above with Relevanssi is a good one to investigate.

Let’s start by looking at line 396 of lib/common.php:

function relevanssi_remove_punct($a) {
    $a = preg_replace ('/<&#91;^>]*>/', ' ', $a);

Ok, so Relevanssi is doing some string replacement, and is assuming that $a is a string. This would definitely be a problem if $a is NOT a string, e.g. an Object.

So where does $a come from?

function relevanssi_tokenize($str, $remove_stops = true, $min_word_length = -1) {
    $tokens = array();
    if (is_array($str)) {
        foreach ($str as $part) {
            $tokens = array_merge($tokens, relevanssi_tokenize($part, $remove_stops, $min_word_length));
        }
    }
    if (is_array($str)) return $tokens;
    
    if ( function_exists('mb_internal_encoding') )
        mb_internal_encoding("UTF-8");

    if ($remove_stops) {
        $stopword_list = relevanssi_fetch_stopwords();
    }

    if (function_exists('relevanssi_thousandsep')) {    
        $str = relevanssi_thousandsep($str);
    }

    $str = apply_filters('relevanssi_remove_punctuation', $str);

Ok, so relevanssi_tokenize is being called with a $str parameter, which eventually gets filtered through the relevanssi_remove_punctuation filter, which relevanssi_remove_punct() is hooked into.

But where does $str come from?

Let’s take a look at lib/indexing.php:

if ($custom_fields) {
    $remove_underscore_fields = false;
    if ($custom_fields == 'all')
        $custom_fields = get_post_custom_keys($post->ID);
    if ($custom_fields == 'visible') {
        $custom_fields = get_post_custom_keys($post->ID);
        $remove_underscore_fields = true;
    }
    $custom_fields = apply_filters('relevanssi_index_custom_fields', $custom_fields);
    if (is_array($custom_fields)) {
        $custom_fields = array_unique($custom_fields);  // no reason to index duplicates
        foreach ($custom_fields as $field) {
            if ($remove_underscore_fields) {
                if (substr($field, 0, 1) == '_') continue;
            }
            $values = get_post_meta($post->ID, $field, false);
            if ("" == $values) continue;
            foreach ($values as $value) {
                relevanssi_index_acf($insert_data, $post->ID, $field, $value);
                $value_tokens = relevanssi_tokenize($value, true, $min_word_length);

What Relevanssi is doing here is retrieving all post meta (i.e. custom fields), and at the very end of the above snippet it’s calling relevanssi_tokenize, and the first parameter is the postmeta $value.

As we saw above, relevanssi_tokenize treats its first parameter as a $string.

To be fair to relevanssi_tokenize, it does check to see if the $str parameter is an array, and if so it basically converts it to a string. However, it does NOT check to see if the value is an object, and this is where the problem occurs.

But where is the object coming from?

It’s hard to say exactly. It could be coming from a lot of places. What many people don’t know is that custom fields/post meta can be used to store all kinds of data. If you call update_post_meta() and pass an object as the value, WordPress will automatically serialize() it for you.

Serializing basically converts the object into a string, so it can be stored in plain text and brought back to life later with all of the same properties. When you call get_post_meta() to retrieve it, it will get unserialized and turned back into an object.

So what does it all mean?

We should never make assumptions about the type of data we will get back from get_post_meta(). Just checking to see if something is an array is not enough. There are two ways to fix this type of error:

  1. If you are storing objects yourself, and you are telling Relevanssi to index all 'visible' postmeta, make sure you prefix your postmeta key with an underscore, like this: _example_key. This identifies the custom field as being hidden, and Relevanssi will ignore it (see the line about if $custom_fields == 'visible;' and $remove_underscore_fields = true;
  2. If you are a plugin author, make sure you check for objects before treating them like strings. A simple is_string() call could have been very helpful here.

I hope you found this helpful!

Leave a Comment