Category Archives: WordPress

Failures related to the WordPress APC Object Cache plugin

On a site for a newer X-Team client, APC was previously chosen as the object cache plugin. This has caused some headaches due to APC not being available while in CLI mode, rendering WP-CLI mostly broken: any commands which change parts of the database which get object-cached won’t appear until the cache gets invalidated, which may not happen anytime soon. For example, plugin (de)activation is broken in WP-CLI with APC Object Cache, as the manifest of activated plugins is cached.

Today I encountered a more serious problem with APC Object Cache.

I was making some widget changes, and I saw that my changes weren’t being reflected on the frontend. Upon reloading the widgets admin page, I also noticed that my changes didn’t seem to get saved. But if I ran wp option get widget_text, I could see my changes had been written to the database (as, of course, WP-CLI here was bypassing the APC object cache). So for some reason, APC was refusing to invalidate widget caches. Doing some more digging, I was shocked to see that the the Text widgets used in the sidebar, had multi-widget instance numbers surpassing 560,000,000. That’s right, 560 million.  The output of wp option get widget_text included:

array (
  526001242 =>
  array (
    'title' => 'Lorem Ipsum',
    'text' => 'Hello World',
    'filter' => false,
  '_multiwidget' => 1,

Some plugin must have at some time bumped that widget instance number up, because there’s no way there have been millions of Text widgets used on the site. Only a couple dozen Text widget instances are currently present.

In any case, this incredibly-sparse PHP array seemed like a good candidate for what was causing APC cache to fail. I swapped out APC for Memcached Object Cache, and the problem went away. While the Memcached plugin stores arrays as serialized strings, just as they get stored in the options table, the APC plugin is apc_store’ing the array wrapped in an ArrayObject instance:

if ( is_array( $data ) )
    $store_data = new ArrayObject( $data );

While I couldn’t reproduce this problem on VVV or on the site’s staging environment, for some reason on production this array object failed to get stored by APC.

I’ve never had any issues with Memcached Object Cache, so I’m glad I made the switch. Not only did it fix this strange widgets issue, but it’s great to be able to once again rely on WP-CLI!

Re: Sharing Sidebars Across a Multisite Network

In a post yesterday at WebDevStudios about “Sharing Sidebars Across a Multisite Network”, I was reminded of a workaround that I had to put into the Widget Customizer plugin. There is this unfortunate condition inside of the core wp_get_sidebars_widgets() function:

// If loading from front page, consult $_wp_sidebars_widgets rather than options
// to see if wp_convert_widget_settings() has made manipulations in memory.
if ( !is_admin() ) {
    if ( empty($_wp_sidebars_widgets) )
        $_wp_sidebars_widgets = get_option('sidebars_widgets', array());
    $sidebars_widgets = $_wp_sidebars_widgets;
} else {
    $sidebars_widgets = get_option('sidebars_widgets', array());

So you can see here that if wp_get_sidebars_widgets() gets called early (and it does) then the initial value returned will persist even if the underlying sidebars_widgets option changes (or is filtered); after this function is called on the site frontend, the returned value will persist in that private global variable $_wp_sidebars_widgets.

This was a problem for Widget Customizer because it needed to be able to override the sidebars’ widgets on the fly within the customizer preview. Fortunately, the return value of wp_get_sidebars_widgets() is filterable via the sidebars_widgets hook, and so this is what we did to get around the above behavior:

add_filter( 'sidebars_widgets', function ( $sidebars_widgets ) {
    $sidebars_widgets = get_option( 'sidebars_widgets' );
    unset( $sidebars_widgets['array_version'] );
    return $sidebars_widgets;
} );

Would this workaround also allow switch_to_blog() to be able to render sidebars from other blogs on the network?

Revelations about filter_input

WordPress has the unfortunate legacy situation of having to force input variables to be “magic quoted”, something which has been deprecated in PHP for some time. So when you are working with data passed from the user, you have to run those input variables ($_GET, $_POST, $_REQUEST) through stripslashes(), which is really annoying. At least I\’m annoyed by it.

However, I just made a discovery. As of 5.2, PHP comes with an extension for Data Filtering, including a function filter_input function for fetching input variables and passing them through validation, sanitization, and other filters.

Well, it turns out that when you access input variables through filter_input, it bypasses wp_magic_quotes entirely, and so you’ll get raw un-backslashed data back! So instead of accessing $_GET['x'], you call filter_input( INPUT_GET, 'x' ).

Additionally, using filter_input() has the benefit of not having to add isset() and !empty() checks everywhere that you’re interacting with input variables, so as to avoid the plague of PHP notices about undefined array indexes. (Oh, hello most WordPress plugins in existence!)

And the icing on the cake for filter_input is that you get validation and sanitization.

However, there’s one big caveat about all this. The WordPress API functions like update_post_meta() and friends actually expect the input to be magic-quoted. So if you do this:

update_post_meta( get_the_ID(), 'x', 'I love \o/ WordPress!' );

If you grab that postmeta out:

$x = get_post_meta( get_the_ID(), 'x', true );

Then $x will end up being:

I love o/ WordPress!


For more info, see #18322: The Road to Magic Quotes Sanity.

List of Functions Removed from WordPress 3.6 RC2 since Beta 3 (and Added too)

On an ongoing client project at X-Team, since the site was to launch in the summer we took the initiative to build a new site on WordPress 3.6 trunk “knowing” that it would be released before then, and so we could utilize the new Post Formats UI and functions that went along with it. Alas. Initially we thought we could still retain the functionality that hooked into the new Post Formats UI, since the announcement was that “Posts Formats is exiting core, will live as a plugin”. So we expected that the WordPress 3.6 would be released with a companion plugin that restored the Post Formats UI removed from core in Beta 4. On June 10, I tweeted:

But all I got in response were crickets. And a month and a half later at WordCamp San Francisco, it became clear that the Post Formats UI was not going anywhere and would be completely rethought, including radical (exciting) ideas from Matt where a post would not be a format, but rather could contain multiple formats:

So it became clear that with the impending release of WordPress 3.6 and the abandonment of the Post Formats UI, that we’d have to immediately update our codebase from Beta 3 to RC2, and add our own plugin to support the Post Formats UI features that we relied on. But what API functions were removed in Beta 4 form Beta 3? I didn’t find a quick answer from the Google, but came up with a quick way to grep the codebase for the list.

So given a WordPress 3.6 Beta 3 install in a given Git repo, I did:

$ wp core update --version=3.6-rc2

Then to find the list of PHP functions removed since Beta 3, all I had to do was:

$ git diff -w -- $(git status -uno | grep '\.php$' | cut -c4-) | egrep '^-function' | sort

This produces this list of PHP Functions in WordPress 3.6 Beta 3 removed as of RC2:

-function _local_storage_notice() {
-function _post_formats_fix_empty_title( $data, $postarr ) {
-function _post_formats_generate_title( $content, $post_format = '' ) {
-function _post_formats_title( $title, $post_id = 0 ) {
-function _wp_post_revision_meta_keys() {
-function _wp_preview_meta_filter( $value, $object_id, $meta_key, $single ) {
-function add_chat_detection_format( $name, $newline_regex, $delimiter_regex ) {
-function attachment_url_to_postid( $url ) {
-function confirm_another_blog_signup($domain, $path, $blog_title, $user_name, \
                                      $user_email = '', $meta = '') {
-function edit_form_image_editor() {
-function esc_sql( $sql ) {
-function get_attached_audio( $post_id = 0 ) {
-function get_attached_image_srcs( $post_id = 0 ) {
-function get_attached_images( $post_id = 0 ) {
-function get_attached_media( $type, $post_id = 0 ) {
-function get_attached_video( $post_id = 0 ) {
-function get_content_audio( &$content, $html = true, $remove = false ) {
-function get_content_chat( &$content, $remove = false ) {
-function get_content_galleries( &$content, $html = true, $remove = false, $limit = 0 ) {
-function get_content_image( &$content, $html = true, $remove = false ) {
-function get_content_images( &$content, $html = true, $remove = false, $limit = 0 ) {
-function get_content_media( $type, &$content, $html = true, $remove = false, $limit = 0 ) {
-function get_content_quote( &$content, $remove = false, $replace = '' ) {
-function get_content_url( &$content, $remove = false ) {
-function get_content_video( &$content, $html = true, $remove = false ) {
-function get_embedded_audio( &$content, $remove = false ) {
-function get_embedded_media( $type, &$content, $remove = false, $limit = 0 ) {
-function get_embedded_video( &$content, $remove = false ) {
-function get_paged_content( $content = '', $paged = 0 ) {
-function get_post_format_content_class( $format ) {
-function get_post_format_meta( $post_id = 0 ) {
-function get_post_galleries( $post_id = 0, $html = true ) {
-function get_post_galleries_images( $post_id = 0 ) {
-function get_post_gallery( $post_id = 0, $html = true ) {
-function get_post_gallery_images( $post_id = 0 ) {
-function get_the_password_form() {
-function get_the_post_format_chat( $id = 0 ) {
-function get_the_post_format_image( $attached_size = 'full', &$post = null ) {
-function get_the_post_format_media( $type, &$post = null, $limit = 0 ) {
-function get_the_post_format_quote( &$post = null ) {
-function get_the_post_format_url( $id = 0 ) {
-function get_the_remaining_content( $more_link_text = null, $strip_teaser = false ) {
-function img_html_to_post_id( $html, &$matched_html = null ) {
-function paginate_content( $content ) {
-function post_format_content_class( $format ) {
-function post_formats_compat( $content, $id = 0 ) {
-function post_submit_meta_box($post) {
-function the_post_format_audio() {
-function the_post_format_chat() {
-function the_post_format_gallery() {
-function the_post_format_image( $attached_size = 'full' ) {
-function the_post_format_quote() {
-function the_post_format_url() {
-function the_post_format_video() {
-function the_remaining_content( $more_link_text = null, $strip_teaser = false ) {
-function wp_ajax_revisions_data() {
-function wp_ajax_show_post_format_ui() {
-function wp_text_diff_with_count( $left_string, $right_string, $args = null ) {

So now I can just look at our themes and plugins to see if we use any of these functions, and then create a Beta 3 compatibility plugin to implement them.

And out of curiousity, here is a list of the PHP functions added to WordPress 3.6 since Beta 3:

$ (git diff -w -- $(git status -uno | grep '\.php$' | cut -c4-) | egrep '^\+function' | cut -c2-; cat $(git status -uall --porcelain | grep '^??' | grep '\.php$' | cut -c4-) | egrep '^function') | sort

function _canonical_charset( $charset ) {
function _local_storage_notice() {
function confirm_another_blog_signup( $domain, $path, $blog_title, $user_name, \
                                      $user_email = '', $meta = array() ) {
function edit_form_image_editor( $post ) {
function esc_sql( $data ) {
function get_attached_media( $type, $post = 0 ) {
function get_media_embedded_in_content( $content, $types = null ) {
function get_post_galleries( $post, $html = true ) {
function get_post_galleries_images( $post = 0 ) {
function get_post_gallery( $post = 0, $html = true ) {
function get_post_gallery_images( $post = 0 ) {
function get_the_password_form( $post = 0 ) {
function get_url_in_content( $content ) {
function post_submit_meta_box($post, $args = array() ) {
function twentyeleven_admin_enqueue_scripts( $hook_suffix ) {
function twentyeleven_admin_header_image() { ?>
function twentyeleven_admin_header_style() {
function twentyeleven_auto_excerpt_more( $more ) {
function twentyeleven_body_classes( $classes ) {
function twentyeleven_color_schemes() {
function twentyeleven_comment( $comment, $args, $depth ) {
function twentyeleven_content_nav( $html_id ) {
function twentyeleven_continue_reading_link() {
function twentyeleven_custom_excerpt_more( $output ) {
function twentyeleven_customize_preview_js() {
function twentyeleven_customize_register( $wp_customize ) {
function twentyeleven_enqueue_color_scheme() {
function twentyeleven_excerpt_length( $length ) {
function twentyeleven_footer_sidebar_class() {
function twentyeleven_get_default_link_color( $color_scheme = null ) {
function twentyeleven_get_default_theme_options() {
function twentyeleven_get_first_url() {
function twentyeleven_get_gallery_images() {
function twentyeleven_get_theme_options() {
function twentyeleven_header_style() {
function twentyeleven_layout_classes( $existing_classes ) {
function twentyeleven_layouts() {
function twentyeleven_option_page_capability( $capability ) {
function twentyeleven_page_menu_args( $args ) {
function twentyeleven_posted_on() {
function twentyeleven_print_link_color_style() {
function twentyeleven_settings_field_color_scheme() {
function twentyeleven_settings_field_layout() {
function twentyeleven_settings_field_link_color() {
function twentyeleven_setup() {
function twentyeleven_theme_options_add_page() {
function twentyeleven_theme_options_help() {
function twentyeleven_theme_options_init() {
function twentyeleven_theme_options_render_page() {
function twentyeleven_theme_options_validate( $input ) {
function twentyeleven_url_grabber() {
function twentyeleven_widgets_init() {
function twentyten_admin_header_style() {
function twentyten_auto_excerpt_more( $more ) {
function twentyten_comment( $comment, $args, $depth ) {
function twentyten_continue_reading_link() {
function twentyten_custom_excerpt_more( $output ) {
function twentyten_excerpt_length( $length ) {
function twentyten_get_gallery_images() {
function twentyten_page_menu_args( $args ) {
function twentyten_posted_in() {
function twentyten_posted_on() {
function twentyten_remove_gallery_css( $css ) {
function twentyten_remove_recent_comments_style() {
function twentyten_setup() {
function twentyten_widgets_init() {
function twentythirteen_admin_header_image() {
function twentythirteen_admin_header_style() {
function twentythirteen_body_class( $classes ) {
function twentythirteen_content_width() {
function twentythirteen_custom_header_fonts() {
function twentythirteen_custom_header_setup() {
function twentythirteen_customize() {
function twentythirteen_customize_preview_js() {
function twentythirteen_customize_register( $wp_customize ) {
function twentythirteen_entry_date( $echo = true ) {
function twentythirteen_entry_meta() {
function twentythirteen_fonts_url() {
function twentythirteen_get_link_url() {
function twentythirteen_header_style() {
function twentythirteen_paging_nav() {
function twentythirteen_post_nav() {
function twentythirteen_preview() {
function twentythirteen_scripts_styles() {
function twentythirteen_setup() {
function twentythirteen_switch_theme() {
function twentythirteen_the_attached_image() {
function twentythirteen_upgrade_notice() {
function twentythirteen_widgets_init() {
function twentythirteen_wp_title( $title, $sep ) {
function twentytwelve_admin_header_image() {
function twentytwelve_admin_header_style() {
function twentytwelve_body_class( $classes ) {
function twentytwelve_comment( $comment, $args, $depth ) {
function twentytwelve_content_nav( $html_id ) {
function twentytwelve_content_width() {
function twentytwelve_custom_header_fonts() {
function twentytwelve_custom_header_setup() {
function twentytwelve_customize_preview_js() {
function twentytwelve_customize_register( $wp_customize ) {
function twentytwelve_entry_meta() {
function twentytwelve_get_font_url() {
function twentytwelve_header_style() {
function twentytwelve_mce_css( $mce_css ) {
function twentytwelve_page_menu_args( $args ) {
function twentytwelve_scripts_styles() {
function twentytwelve_setup() {
function twentytwelve_widgets_init() {
function twentytwelve_wp_title( $title, $sep ) {
function wp_ajax_get_revision_diffs() {
function wp_get_revision_ui_diff( $post, $compare_from, $compare_to ) {
function wp_http_validate_url( $url ) {
function wp_prepare_revisions_for_js( $post, $selected_revision_id, $from = null ) {
function wp_refresh_post_nonces( $response, $data, $screen_id ) {
function wp_style_add_data( $handle, $key, $value ) {