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.

PhpStorm Review

Late last year I kept hearing a lot about PhpStorm 8, especially from WordPress people and wanted to try it out but the price was stopping me, (it turns out the price is probably worth paying – but it’s hard to know if the price is worth paying until you’ve already paid the price). Luckily for me WP e-Commerce has some licenses for core contributors, and apparently I count, (JetBrains, the people who make PhpStorm, provide licenses to open-source projects at no charge – thanks!).

One of the things that JetBrains asked in return for the licenses was that we try to write reviews of our experience, and after many months of using PhpStorm, here are my thoughts.

PhpStorm crashed several times in the 24 hours after I installed it, not a good start. Support suggested installing the EAP version, which is like a beta stream, which fixed the problem. Since then I’ve switched back & forth between the current release and EAP versions of PhpStorm, but I usually use the EAP, because I like shiny things.

Performance

I had heard that PhpStorm was fast, but it’s a Java app, (like Netbeans or Eclipse), so I had my doubts. When first installed there were some pretty big performance issues, especially related to scrolling. PhpStorm was exhibiting what I refer to as “that Java scroll lag” – one of the reasons I tend to avoid Java apps. There was also a problem that when scrolling with the mouse the text would disappear, which makes mouse scrolling nearly useless. Eventually I learned that there’s a version of PhpStorm for OS X that comes with a bundled, tweaked, Java Run-time Environment, and using that version of PhpStorm solved these problems.

When comparing PhpStorm’s performance to Sublime Text, (2 or 3), Sublime Text is still way faster. PhpStorm feels like a normal app, but ST3 has that amazing snappiness that Sublime Text users have come to love, and want to see everywhere. While PhpStorm’s performance is satisfactory, it’s not as blazing fast as the chatter led me to believe, but the combination of decent speed, and the other benefits that of using a real IDE, mean it helps me write cleaner code faster.

Setup

Like most coding programs, especially Java ones, PhpStorm hurt my eyes out of the box, but the look is tweakable enough to make it look how I want. There are two built-in themes, (“Default” and “Darcula”), and you can load 3rd-party colour schemes for the text editor. I think it might be possible to load 3rd-party themes as well but I haven’t investigated this. Switching to the Darcula theme, and switching to the Predawn colour scheme, (I can’t remember where I found the PhpStorm port, maybe it even came with it), and Inconsolata made PhpStorm feel like home.

Coming from ST3 I re-mapped some keyboard shortcuts to match ST3 make the transition easier and make it easier to switch between the two when needed.

I had to download extra drivers to connect to MySQL, which was kind of annoying, but I suspect might be license-related.

And Java. PhpStorm 8 requires Java 6, which is no longer installed on Macs because it’s so old, that’s why there’s a version offered for OS X with a bundled JRE. It’s possible to install Java 6 with a package from Apple, because we all love having several versions of Java installed, or it’s possible to tweak some plist files to let PhpStorm run with Java 8, (the current version). Fortunately, it looks like PhpStorm 9, (which I’m running the EAP of now), will do away with this requirement.

Code Inspection

This is an IDE, so it’s aware of any databases that get used. This is great most of the time, (the code inspector tells you if you mis-type a column name, for example), but the it also fusses when I build big SQL queries by concatenating strings. So far I’ve just ignored these complaints.

If I include a file like this in WP: include( get_stylesheet_directory() . ‘/myfile.php’); PhpStorm thinks that myfile.php doesn’t exist, even if it does. Since this is the “right way” to include files in WP themes, and PhpStorm advertises built-in WP knowledge, PhpStorm should understand it.

When making a commit to your preferred Source-Control system, (Git, right?), PhpStorm inspects the code you’re committing, and if there are errors or warnings it pops up a window saying so, and gives you a chance to fix these problems. This is a great way to slowly clean up the code of older projects with lots of legacy, (read: “written before I knew better”), code. However, it’s also kind of annoying when unfixable errors or warnings are included, like when I’ve concatenated together an SQL query.

I was working on a plugin that works with WooCommerce and PhpStorm complained that I was assigning the return value of a function that returns void to a variable. It turns out that WooCommerce has several PHP DocBlocks that state a function returns void when it actually returns a value. Thanks to a WP e-Commerce copy of PhpStorm for pointing out errors in WooCommerce!

Annoyances

If someone from Jetbrains reads this, consider these bug reports!

text-gone-bonkers

Sometimes the fonts go bonkers. When I was first setting PhpStorm to use Inconsolata they did, (that was using the release version), and a few times on the EAP I committed some code, and when the commit finished the display went crazy again.

PHPStorm Power Consumption

Power consumption – I’m not sure how it compares to Sublime Text, but it’s consuming more power than Photoshop today – #2 on the list behind Safari with 11 tabs open.

 

The first time I used the “Pull Up” refactor tool, which lets you pull methods up from a class into the parent class, I pulled up a bunch of static methods. PhpStorm didn’t actually go and find the places where the methods were called and change the calls, which I thought was the point of the “smart” IDE. Inheritance would mean that these methods would still be available, but there’s no reason to load the child class if everything I need is in the parent.

The auto-complete can be really bizarre. Often doesn’t pick the logical thing, or even the first thing in the suggestion list. For example, if I type “col” then press tab or enter autocomplete completes as “columns” not “color,” even though color is the first suggestion. It even insists on completing as “columns” if I type “colo” and press tab/enter. This drives me mad.

Search for any file, (I’ve got it mapped to Cmd+P to match Sublime Text): If a project has several files with the same name there doesn’t seem to be mapping that makes sense. To me it would make sense for the first option to be the one I used the most recently, or the one “closest” to whatever file I’m already looking at in the directory structure. If the files are sorted at the moment, it looks like it’s alphabetical, which should change.

Auto-change formatting: I try to use WordPress style in most of my PHP & Javascript – especially for WordPress-related work! WP style suggests putting a space around function parameters in both Javascript and PHP. However, if I’m doing chained calls in Javascript PhpStorm removes the space after the last function argument when I press the . key. For example:

$( 'body' )
// becomes
$( 'body').
// as soon as the period is typed

When PhpStorm 8.0.3 came out I started getting SSL error messages on startup. Apparently there’s a problem in some SSL-related part of the bundled JRE. 8.0.3 came out on February 13 and there’s still no fix. The current EAP doesn’t have this problem.

PhpStorm’s licensing scheme for open-source projects has changed this year, so I need a Jetbrains account, the licence is issued from WP e-Commerce to that account, then I’m supposed to sign in to the account with PhpStorm and it’ll activate. Unfortunately the SSL bug prevents PhpStorm from talking to the Jetbrains servers, so I can’t activate my PhpStorm license. Luckily the EAP comes with its own license, otherwise I would be out of luck. A bug that prevents customers from activating software is a very big bug.

Conclusion

PhpStorm is good, very good at some things, especially the IDE-type functionality I was looking for. Integrated code inspections & linting were a welcome surprise that is really helping my code look better and encouraging me to write better in-line documentation. There have been problems, though, that might have caused me to abandon PhpStorm I hadn’t pre-decided to give it a real chance. I nearly always use the EAP both because I like shiny things, and because the bugs in the release version of the software are annoying, or serious, enough to drive me into the arms of the EAP.

So, is PhpStorm worth the purchase price? Probably. It handles code completion, data-source awareness, most refactoring, and all its IDE-related functionality quickly and as promised, (or very nearly so). The problems tend to be in other areas, and from the state of the current PhpStorm 9 EAP, most of these problems will be solved soon.

Conditionally Loading Javascript & CSS with Fancybox for WordPress

The Problem

I’ve been working on improving the performance of a client’s WordPress-based website recently and it’s become very clear to me just how much CSS & Javascript plugins load, even when it’s not needed.

On this particular website 47% of the Javascript and 57% of the CSS loaded on the site’s homepage is not required on the homepage, but is required elsewhere, so it should be loaded conditionally. Sometimes this is easy, but sometimes it’s not.

Fancybox for WordPress is not an easy plugin to make load client-side resources, after all, it’s made to show a lightbox on any image, and especially on any gallery. When the wp_enqueue_scripts action happens, where it is recommended that we set the scripts and styles that will be used on a page, we don’t yet know what will be on the page. Fortunately, we can call the wp_enqueue_script() function after that, during the body of the page, and those scripts will be printed in the wp_footer() function. The same is true for wp_enqueue_style(). So, here’s what we do:

  1. Stop Fancybox for WordPress from including its scripts & styles by default.
  2. If a gallery or image that we want to use Fancybox on is displayed, we tell WordPress to display the Fancybox scripts & styles in the footer.
  3. Enjoy faster pageloads.

Sounds simple, but there are a few interesting bits. Now for the code.

The Code

I use three functions to check if the Fancybox scripts & styles are needed. They can be in a theme’s functions.php, (where I have them at the moment), or they could even be rolled into their own plugin.

First tell Fancybox not to include its code by default. Instead of manually dequeuing every style & script, remove the Fancybox functions that do the enqueuing from the wp_enqueue_scripts and wp_head action hooks:

// don't enqueue Fancybox scripts by default.
function jb_dequeue_fancybox_css_js() {
    remove_action( 'wp_enqueue_scripts', 'mfbfw_styles' );
    remove_action( 'wp_enqueue_scripts', 'mfbfw_scripts' );
    remove_action( 'wp_head', 'mfbfw_init' );
}
add_action( 'wp', 'jb_dequeue_fancybox_css_js' );

Next create a function that re-enables Fancybox, and remembers that it did so. Here there are two static variables, which will hold their value between function calls, (kind of like hidden globals), which store state. The $fancybox_is_used variable is returned so this function can be called with no arguments to find out if Fancybox has been used on page. Note the priority of 25 when hooking mfbfw_init() to the wp_footer action. This is needed because mfbfw_init() prints some Javascript to the page that relies on jQuery, and WordPress prints the enqueued scripts in the wp_footer action with a priority of 20, so mfbfw_init() needs to execute with a priority higher than 20.

function jb_fancybox_is_used( $used = false ) {

    // this is returned so we can call this function with no arguments to learn
    // if Fancybox has been used on a particular page.
    static $fancybox_is_used = false;

    // remember if Fancybox has been re-enabled already, so we don't enqueue the 
    // scripts multiple times
    static $fancybox_is_setup = false;

    if( $used === true ) {
        $fancybox_is_used = true;
    }

    if( $fancybox_is_used && ! $fancybox_is_setup ) {
        if( function_exists( 'mfbfw_styles' ) ) {
            mfbfw_styles();  // enqueue fancybox styles
            mfbfw_scripts(); // enqueue fancybox scripts
            // the 25 is important. WordPress prints footer scripts in the
            // wp_footer action with a priority of 20, and mfbfw_init() has to
            // be called after the footer scripts are already on the page.
            add_action( 'wp_footer', 'mfbfw_init', 25 ); 
        }
        $fancybox_is_setup = true;
    }

    return $fancybox_is_used;
}

Finally, make a function that looks for places where Fancybox is used on the page. On the site I’m working on the CSS class fancybox is used on images and in galleries that I want to be Fancyboxed, so I look for the string “fancybox” in the_content filter, and when it’s found I call jb_fancybox_is_used( true ) to re-enable Fancybox on the page. I added this to the_content with priority 11 because shortcodes, including gallery shortcodes, are executed at priority 10, and I want to be able to look through the output of short codes for the fancybox CSS class:

function jb_hunt_for_fancybox( $content ) {

    if( false !== stripos( $content, 'fancybox') || false !== stripos( $content, 'thickbox' ) ) {
        jb_fancybox_is_used( true );
    }
    return $content;
}
add_filter( 'the_content', 'jb_hunt_for_fancybox', 11 );

If you include a something you want to fancybox in a template you can call jb_fancybox_is_used( true ) manually from the template file to include the CSS & Javascript.

Other Ways

This isn’t the only way to conditionally include Fancybox’s Javascript & CSS. Instead of using jb_hunt_for_fancybox() to filter the_content there’s probably an action or filter in the gallery shortcode that jb_fancybox_is_used() could be hooked onto. It may even be possible to use the $wp_query object in an action hook just before wp_enqueue_scripts to determine if there is content on the page that needs to be Fancyboxed, let that decide whether or not to run jb_dequeue_fancybox_css_js(), and forget about the other two functions.

Let’s Do Better

Plugin authors should be working hard to only add what is needed to each page load. Who is doing a great job? How can we hack our themes to bend other plugins to our will? Comment or tweet @johnbeales to let me know.

Get your Development Server Running after Upgrading to Snow Leopard

After upgrading to Snow Leopard today I tried to access my localhost versions of the websites I have under development and was met by this great message, (impatient? skip to the step-by-step solution):

Forbidden

You don’t have permission to access / on this server.

Great. I couldn’t even access http://john-mbp/ – my computer’s name, and the URL that the System Preferences’ Web Sharing pane tells me I can access. However, all is not lost.

Part 1: Get the Virtualhosts Online

The first place I looked was my httpd-vhosts.conf file, located in /var/apache2/extra, (this has not changed since 10.5), and as I suspected it had been reset to the Apache defaults, (thanks Apple). I suspected this might happen though, and had made a backup of the file, (actually, I backed up my whole hard drive, then made a separate, easier to find, copy of the file). Replacing the new httpd-vhosts.conf with the old one and doing an sudo apachectl graceful got the virtualhosts responding to requests again.

Part 2: Establish a MySQL Connection

While my virtualhosts were up and running at this point they did not have a MySQL connection. Maybe this is because my MySQL installation is from MacPorts so the socket is in a different place, or maybe not. Either way it had to be fixed.

Upon running a phpinfo(); I discovered a couple of things. First, the included version of PHP is now 5.3.0 and second, PHP was not loading any php.ini file, so I went on a php.ini hunt and found 2 files: php.ini.default and php.ini.default-5.2-previous, both located in /etc. Copying php.ini.default to /etc/php.ini and doing a sudo apachectl graceful brought my MySQL connections back online, leaving one final problem.

Part 3: Getting rid of the timezone warnings

Since I usually code with an error reporting level of E_ALL, and PHP 5.3 doesn’t seem to like not having a timezone set in the php.ini file, (it’s not set by default, at least not in Snow Leopard), I was getting a bunch of warnings whenever I used the date(); function in PHP, so, I needed to set a default timezone in php.ini. If you don’t already know the PHP name for your timezone, go to the PHP timezones page and find it, then open php.ini, (sudo vi /etc/php.ini the sudo is important), and search for “timez” to find the timezone section of the file. Now, on the line that says “;date.timezone = ” add the name of your timezone after the equals sign and remove the semicolon from the beginning of the line. Once you’ve done that, save the file, (it’s set as read-only for some reason so in vi you need to do :w! to make it save), then quit your text editor and do one more sudo apachectl graceful and you should be good to go.

Step by Step

  1. before installing, back up your computer
  2. before installing, make a copy of /etc/apache2/extra/httpd-vhosts.conf and put it somewhere safe
  3. install Snow Leopard
  4. replace the new /etc/apache2/extra/httpd-vhosts.conf with the old one you saved in step 2. If you don’t have a backup you’ll have to re-enter your virtualhosts in the new httpd-vhosts.conf file which isn’t the end of the world
  5. in Terminal type sudo apachectl graceful and enter your password if prompted to restart apache with the new configuration. Your virtualhosts should now be online
  6. rename or copy /etc/php.ini.default to /etc/php.ini (sudo cp /etc/php.ini.default /etc/php.ini)
  7. Find the name of your PHP timezone, (look on the PHP timezones page). Write it down.
  8. Edit the new php.ini file to have the correct timezone.
    1. open the new php.ini file (sudo vi /etc/php.ini)
    2. find the timezone section, (type /timez and press enter).
    3. move your cursor to the line that says “;date.timezone = ” using the arrow keys
    4. press the i key to edit the text
    5. add the name of your PHP timezone after the equals sign
    6. remove the semicolon from the beginning of the line
    7. press the escape key to stop editing the text
    8. type :w! to save the file
    9. type :q to quit the text editor
  9. One final sudo apachectl graceful and you should be back up and running.

PHP’s mysql_connect() Reuses Connections by Default

As I mentioned yesterday, I’m doing some work in WordPress right now, and a few minutes ago I tweeted that my custom code is messing with WP’s wp_get_archives() and wp_list_categories() functions, well, I found the problem.

I am including the 4RoadService.com header & utilities files in my WordPress theme, and I am using the same user here on my test server for both the main 4RoadService.com database and the WordPress database. It turns out that when the 4RoadService.com database connection was initialized, since it uses the same connection info as the WordPress database, the existing connection was just reused, (this behaviour, by the way, is well described in the PHP documentation), then when the 4RoadService.com connection was told to use the main 4RoadService database it did, thus switching our one and only connection away from the WordPress database, and making WordPress think that there were no posts on the blog.

Fortunately, there is a quick workaround, just add one more attribute to the mysql_connect() function so it looks like this:

$dblink = mysql_connect($host, $user, $pass, true);

This way a new connection is established, and the WordPress connection is left alone.

I am left wondering why, in the loop, WordPress was able to see my posts, perhaps it establishes a second database connection in there. However, I’m not going to spend the afternoon poking through the guts of WordPress.