Why X Theme and Pro sometimes cause PHP warnings on WPEngine

The Problem

Earlier this fall I was working on an e-commerce site that’s hosted on WPEngine that used Themeco’s Pro as its parent theme most pages hadn’t seen any customization yet, most of the code was stock, and this appeared on the Products page:

PHP Warning:  Invalid argument supplied for foreach() in wp-content/mu-plugins/wpengine-common/plugin.php

Hmm. Who is passing what to a foreach?

It turns out that both X Theme and it’s progeny Pro do this turn off WordPress’s automatic responsive images:

// Disable WordPress 4.4 Responsive Images
// =============================================================================

if ( ! function_exists( 'x_disable_wp_image_srcset' ) ) :
  function x_disable_wp_image_srcset( $source ) {
    return false;
  }
  add_filter( 'wp_calculate_image_srcset', 'x_disable_wp_image_srcset' );
endif;

They’re hooking into the wp_calculate_image_srcset filter and returning false, which stops WordPress from adding a srcset attribute to an image, (more on why in a moment). The problem here is that the value being passed to the filter, $sources, is an array, but x_disable_wp_image_srcset() turns that array into a boolean false, so then anything else hooking into wp_calculate_image_srcset, for example, WPEngine’s CDN system, tries to iterate over the $sources array, but it’s not an array, hence the error. By filtering $sources to a boolean false Themeco is breaking the expected, and documented, behaviour of the wp_calculate_image_srcset filter.

Solutions

Luckily, there are two easy fixes for this problem. The first is to make sure you’re using the latest version of WPEngine’s must-use plugin. They’ve caught on to the problem and check to make sure $sources is actually an array:

if ( is_array( $sources ) ) {
    foreach ( $sources as $source ) {
        // do the stuff
    }
}

If you can’t do that, at least Themeco made the function pluggable, so you can add your own version of x_disable_wp_image_srcset() to your theme’s functions.php or a plugin that returns an empty array and plays nicely with other plugins:

// This is a pluggable function. If we don't define it here then Pro does in 
// pro/framework/functions/global/admin/thumbnails/setup.php
function x_disable_wp_image_srcset( $sources, $size_array, $image_src, $image_meta, $attachment_id ) {
    return [];
}

Or, you could use responsive images.

Confusion, (aka, why this happens in the first place)

When I first discovered this problem I E-mailed Themeco to tell them about the problem, however they pointed me at the documentation for the wp_calculate_image_srcset() function, which states the function can return false, and sent me on my way:

Hey John,

Thanks for writing in. In looking over WordPress' official documentation for that function/hook, I believe that boolean false should be the correct value to return:

https://developer.wordpress.org/reference/functions/wp_calculate_image_srcset/

In the "Return" section you'll see that it is supposed to return a string or a boolean false if there is an "error or only one source exists." It seems as though WPEngine should need to revise their function to account for these instances anyway even if not for X or Pro (and at least work to catch any fatal errors like this).

Hopefully that helps, cheers.


Best Regards,
Your Themeco Team

This confusion occurs because the convention in WordPress is that if a filter and a function have the same name, the filter filters the output of the function. However, in this case the convention is broken – when I raised the issue in the Advanced WordPress Facebook group a very experienced WP developer made the exact same mistake and assumed that allowable return types for the function should be fine to return from the filter. After we cleared up the confusion I filed a bug about the confusing names, go star it so it gets some attention!

A Fluke of PHP

So why does returning false in the wp_calculate_image_srcset filter stop WordPress from using responsive images? Here’s the relevant code from media.php:

$sources = apply_filters( 'wp_calculate_image_srcset', $sources, $size_array, $image_src, $image_meta, $attachment_id );

// Only return a 'srcset' value if there is more than one source.
if ( ! $src_matched || count( $sources ) < 2 ) {
    return false;
}

To follow along, if $sources is turned into a boolean false at the apply_filters() call, then a count( false ) happens in the condition on the next non-comment line. Instead of an error like I expected, running count() on any non-array value returns 1, so count( false ) == 1, also count( 0 ) == 1

Conclusion

First, be a good citizen of the WordPress ecosystem and don’t change the type of a filtered value to something other than the documented types for that value. Second, if you have any non-core themes or plugins installed you can’t trust that a value coming into a filter will be the type that the docs say, so check! When WPEngine realized there was a problem an is_array() fixed the problem. Third, read support E-mails thoroughly and make sure you understand them, it hurts when I go to the effort to document a problem and come up with a solution, and I’m told it’s not a problem. Finally, go +1 my ticket so maybe others won’t get bitten.

Xdebug is not friends with WordPress + create_function()

If you use Xdebug for debugging WordPress-based sites there’s something you should be aware of. If function created with PHP’s create_function() are hooked to WordPress actions or filters, any time $wp_filters is in the scope Xdebug sends invalid XML to the Xdebug client, (like your IDE). If the Xdebug client doesn’t deal with the invalid characters before attempting to parse the XML it will fail. IDEs deal with the parsing failure in different ways, SublimeTextXdebug doesn’t show the call stack or list of current variables but doesn’t crash entirely so debugging can continue. I don’t know how other IDEs handle the failure.

Why the XML is Invalid

create_function() makes a new function, gives it a random name that starts with a null character, and returns that function name as a string. When that string is passed to WordPress’s add_filter() or add_action() functions the name that create_function() returned is used as an array key in deep within the $wp_filters global. When Xdebug sends the list of variables back to an Xdebug client it is sent as XML and the null character encoded as &#0; which is illegal in XML.

How to solve the Problem

Ideally Xdebug would only send valid XML to the Xdebug client, but the bug report has been open for a year and doesn’t seem to be high priority so developers should solve the problem by avoiding create_function()

Avoiding create_function() is Good Coding

create_function() eval()‘s the code that’s passed as the second argument, and eval() is something to be avoided, so create_function() is too. This Xdebug bug makes us avoid create_function() for actions and filters in order to keep Xdebug useful, with the side effect of making our code a bit more secure.

Using Microsoft’s VPC Images with VMWare Fusion

We all know the sinking feeling when we have to test a website in Internet Explorer. You built the site, it looks beautiful in Firefox and maybe Safari too, but now you have to open up Internet Explorer 6, 7, and 8, and make sure it plays nice with all three of them. Even better, Microsoft has made it so that you can only have one version of IE installed on a computer at a time. True, you can use things like Multiple IEs or other similar products, but they never play quite right.

Fortunately, Microsoft has supplied us with Virtual PC images of Windows with Internet Explorer installed. Unfortunately, Virtual PC is a Windows-only program so you need a PC to run them on. Or do you?

You don’t! If you’re using OS X you can use VMWare Fusion to run those Microsoft VPC images, after a little tweaking. It is much easier if you have a copy of Windows available to you during the install process, (that’s how I did it), but I don’t believe this is an absolute necessity. Here’s how it works:

Basically, you need to download the VPC images, extract them, and convert them into VMWare Fusion virtual machines. It sounds trickier than it is.

First, download and extract the VPC images. If you can use Windows to do this it’s easy, (the images have self-extractors), if not try p7zip, (see instructions in this forum thread).

[edit: July 15, 2009]: Then, somehow, you have to convert your VHD files into VMC files. The easiest way to do this is to use Microsoft’s VPC to make a new virtual machine from the VHD files, but you do need windows to do that. You will be converting these .VMC files into VMWare native virtual machines.
[/edit]

To convert the VPC images to something else, use VMWare vCenter Converter. It’s a stand-alone program for Windows or Linux that easily converts VPC images to VMWare Fusion virtual machines, as well as several other formats. You can even choose between Fusion 1.x and Fusion 2.x. It will even install the VMWare tools pagkage for you. I did the conversion under Windows, but there’s probably a way to get the linux version to run under OS X, at least hopefully.

Once the conversion is complete, fire up OS X & VMWare Fusion and open your new Virtual PC image. There are some things that run on the first startup of each machine, give it a few minutes then hit cancel on all of the “Please insert the XP SP3 CD” messages that remain, it doesn’t seem to hurt Windows. I think it’s looking for a battery driver in my case, (maybe I should try to install the Bootcamp battery driver?).

That’s it, enjoy testing. I am able to run, slowly, all 3 IE versions with the Windows XP images, and my computer isn’t as slow as when I run only my Bootcamp Vista install under VMWare Fusion. I’m thrilled to have these 3 new debugging tools at my disposal.