Passing a shortcode attribute to a sub-function

This is both a WordPress and a PHP question, since a PHP programmer may know a technique that the WP group doesn’t.

I have been attempting to write a shortcode that fires the Foundation Reveal code for use inline with the WP Editor. I envision having it work like this:

Read More
[reveal size="medium" bg="true" slug="page-slug"]Open a modal with this link![/reveal]

The shortcode specifies the modal size, whether or not to use the black background, and which page slug to incorporate content from in a custom single-page loop (which allows us to use a non-public custom post type to hold the modal data). There are additional attributes but I haven’t written them in yet in the interest of simplicity.

Here’s where it gets tricky.

A modal comes in two parts, the link and the hidden DIV. The DIV typically goes at the footer of the site. I could put it inline and solve my problem, but that would play havoc on Google’s webcache. No, the modal needs to be an aside in the footer.

So I got a bit clever. I had the short code return the target link, but I also had it register a hook (placed in footer.php) to which a content DIV is attached. Everything was working fine until I realized a problem with my variable scope. First, here’s my current code:

/**
 * Foundation Reveal Shortcode
 */

// Create an action to be placed in the site footer. We will target this with the second half of the function
function foundation_reveal() {
    do_action('foundation_reveal');
}

// Set up the shortcode for the modal
function reveal_setup($atts, $content) {

    // Extract the attributes
    extract(shortcode_atts(array(
        'size'  =>  'medium',
        'slug'  =>  'dummy',
        'bg'    =>  'reveal-modal'
    ), $atts));

    $modal_link  = '<a href="#" data-reveal-id="' . $slug . '">';
    $modal_link .= $content;
    $modal_link .= '</a>';

    if ($slug == 'dummy') {
        $the_modal = '<div id="' . $slug . '" class="' . $size . ' ' . $bg . '" data-reveal>' . 'reminder/dummy content goes here' . '</div>';
    } else {
        $the_modal = '<div id="' . $slug . '" class="' . $size . ' ' . $bg . '" data-reveal>' . 'post loop based on page slug goes here' . '</div>';
    }

    function reveal_content($the_modal) {
        echo $the_modal;
    }

    add_action('foundation_reveal', 'reveal_content');

    return $modal_link;
}


/**
 * Setup shortcodes for this theme
 */

// Register all shortcodes
function gmfi_shortcodes() {
    add_shortcode('reveal', 'reveal_setup');
}

// Add shortcodes to the init hoook
add_action( 'init', 'gmfi_shortcodes');

And since it’s kind of helpful to see this whole thing color coded by my IDE, here’s a link to that image.

The problem will be evident to any PHP programmer right away. In order to pass something like $slug to the secondary function, which gets hooked into the footer, I would need to pass the variable by reference.

i.e., I would have to do this:

add_action('foundation_reveal', 'reveal_content($slug)');

That, unfortunately, is not how WordPress works.

So here’s the question then… can you think of another way to pass that variable by reference? Because if not, I may have to suck it up and write a custom hook/action script. That option seems wasteful if there is a way to do it using the available WordPress components. But, then again, if there was another way, one of the WordPress/Foundation plugin suites probably would have done it already.

Either way, I need you all to help me figure out my next step.

Thanks so much for sticking with me as I explained all of that!

Additional note: The foundation_reveal() hook in the footer definitely gets fired, it just doesn’t get any of the class or ID data passed to it.

Related posts

2 comments

  1. There are different ways to get the result. You can use a class, store the div content in a class or instance variable and output it when needed.

    As alternative you can use a function with a static variable, to hold the content.

    I’ll use this second alternative, converting it in a class is an exercise for you 😉

    In addiction you can use the core 'wp_footer' hook to output the content, in this way you don’t have to add an additional hook, nor use a template tag.

    function reveal_setup($atts = array(), $content = '') {
      // Setup the static variable. Use an array to allow multiple calls per page
      static $the_modals = array();
      // if the function is called from wp_footer hook
      if ( current_filter() === 'wp_footer' ) {
        if ( is_array($the_modals) && ! empty($the_modals) ) {
          foreach( $the_modals as $amodal ) {
            echo $amodal;
          }
        }
      // if the function is called from shortcode
      } else {
        // Get the attributes
        $atts = shortcode_atts(
          array( 'size' => 'medium', 'slug' => 'dummy','bg' => 'reveal-modal' ),
          $atts,
          'reveal' // enable filtering
        );
        // prepare the_modal link
        $modal_link  = '<a href="#" data-reveal-id="' . $atts['slug'] . '">';
        $modal_link .= $content;
        $modal_link .= '</a>';
        // prepare the_modal content
        $modal_format = '<div id="%s" class="%s %s" data-reveal>';
        $the_modal = sprintf( $modal_format, $atts['slug'], $atts['size'], $atts['bg'] );
        if ( $atts['slug'] == 'dummy' ) {
          $the_modal .= 'reminder/dummy content goes here';
        } else {
          $the_modal .= 'post loop based on page slug goes here';
        }
        $the_modal .= '</div>';
        // save the modal content in the static modals array
        $the_modals[] = $the_modal;
        // add the present function to wp_footer hook if it is not already added
        if ( ! has_action('wp_footer', __FUNCTION__) ) {
          add_action( 'wp_footer', __FUNCTION__ );
        }
        // return the modal link
        return $modal_link;
      }
    }
    
    add_shortcode('reveal', 'reveal_setup');
    

    That’s all. Untested.

    PS: extract usage is a bad practice, (although I know it’s largely used in core code and in Codex examples…)

  2. You could also use an anonymous function and let it access outside variables via the use keyword (if you got PHP 5.3+ ).

    Examples:

    add_action( 'init', function() {
    
        // Example 1: Append stuff to the titles    
        $stuff = '...';    
        add_filter( 'the_title', function( $title ) use ( $stuff ){
            return $title . $stuff ;
        });
    
        // Example 2: Display message in the footer   
        $msg = 'The End';
        add_action( 'wp_footer', function() use ( $msg ){
            echo $msg ;
        });
    
        // Example 3: Run an action only once  
        add_action( 'the_content', $callback = function( $content ) use ( &$callback ){
            remove_filter( current_filter(), $callback ); // __FUNCTION__ doesn't work here.
            return strtolower( $content );
        });
    
    });
    

Comments are closed.