Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

I've built a super-crazy Wordpress framework that has a million and one shortcodes, one of those is a columns shortcode which supports a count parameter that allows you to define a column and the appropriate class will be added.

My shortcode is as follows:

add_shortcode('column', 'column_shortcode');
function column_shortcode($atts, $content = '')
{
    extract(shortcode_atts(array(
       'count' => 12
    ), $atts));

    if (stripos($count, '_') === FALSE)
    {
        if ((int) $count > 12)
        {
            $count = 12;
        }
    }

    $html = '<div class="column-'.$count.' column">';
    $html .= do_shortcode($content);
    $html .= '</div>';

    return $html;
}

There appears to be an issue however when the column shortcode is used within a column, is this a known issue or does Wordpress not support shortcodes of the same name being nested within one another?

What essentially happens is the column inside is prematurely closed off in the outputted HTML and I see [/column] wrapped in paragraph tags being shown on the front-end. However if I create another shortcode and call it child, using the exact same code as above only the child shrotcode is just [child] instead of [column] everything works as expected.

Here is the code from the WYSIWYG editor in my Wordpress installation. Exhibit A is the ideal scenario, I don't want to have to use two different shortcodes that do the same thing. Exhibit B is what works by simply duplicating the shortcode.

Exhibit A - does not work

[column count="9"]

[column count="8_9"]

Welcome to the site.

Who's brave enough to fly into something we all keep calling a death sphere? Well, then good news! It's a suppository. A true inspiration for the children. Ah, computer dating. It's like pimping, but you rarely have to use the phrase "upside your head."

[/column]

[/column]

Exhibit B - works

[column count="9"]

[child count="8_9"]

Welcome to the site.

Who's brave enough to fly into something we all keep calling a death sphere? Well, then good news! It's a suppository. A true inspiration for the children. Ah, computer dating. It's like pimping, but you rarely have to use the phrase "upside your head."

[/child]

[/column]
share|improve this question
1  
Polluting the world, one shortcode at a time ;) A million and one is madness, and same name shortcodes nested within one another rips hole in space time continuum, but, while on the topic of columns have you looked at: Grid Columns - the principle is one column shortcode to rule them all. I wonder if that meets your needs, whether you can borrow from it or implement it directly into your framework? –  userabuser Oct 24 '12 at 10:59
    
Userabuser, that Github link you supplied might actually be of some use, it even accounts for adding first and last classes as well all while using one shortcode. Thanks for that, I'll definitely get some inspiration from it or perhaps just use it. –  DigitalSea Oct 24 '12 at 11:15

2 Answers 2

up vote 1 down vote accepted

This is a known issue. If you have one shortcode inside another which has the same name, the wordpress parser will not be able to handle them correctly. This is also mentioned in the codex page for shortcode API under limitations.

From the linked page However the parser will fail if a shortcode macro is used to enclose another macro of the same name

The solution i suggest is to add extra parameters to the main shortcode call & in your shortcode function, recursively call itself based on if those extra parameters are present

EXAMPLE

[column count="9" child="8_9" 8_9_text="

Welcome to the site.

Who's brave enough to fly into something we all keep calling a death sphere? Well, then good news! It's a suppository. A true inspiration for the children. Ah, computer dating. It's like pimping, but you rarely have to use the phrase "upside your head."
" /]

Then in the code

function shortcode_handler($atts, $content, $tag) {
    if(isset($atts['child']))
        $output = shortcode_handler(array('count'=>$atts['child']), $atts[$atts['child'].'_text'])

    // Do something else with the output

}

I know it doesn't look good but maybe you can work something out in the context of your application.

share|improve this answer
    
Thanks for responding! Haha, I don't think I've ever read the documentation for shortcodes before. I've just known how to do them so never had to look, rookie mistake right there not consulting the documentation. When you say recursively call the function, would you mind sharing a code example? I'm having a hard time doing the code in my head as to how I would recursively call the shortcode function to parse out those additional column shortcodes. –  DigitalSea Oct 24 '12 at 11:17
    
edited the answer –  Mridul Aggarwal Oct 24 '12 at 11:40
    
Perfect. I get you now, wasn't entirely sure if you meant calling the same shortcode function from within itself, or if you meant something else. Thanks for clarifying that, the solution works well. –  DigitalSea Oct 24 '12 at 23:59

I wrote a little function that will enable shortcodes within themselves. It should work perfectly for your problem. It's been super helpful for me.

Open up the file 'wp-includes/shortcodes.php' and change the do_shortcode() function (on line 201) to the following

function do_shortcode($content) {
    global $shortcode_tags;

    if (empty($shortcode_tags) || !is_array($shortcode_tags))
        return $content;

    $content = enable_recursive_shortcodes($content);

    $pattern = get_shortcode_regex();
    return preg_replace_callback( "/$pattern/s", 'do_shortcode_tag', $content );
}

All that was changed is the the addition of *$content = enable_recursive_shortcodes($content);*

That function goes through the $content passed to it and adds a suffix to the shortcode names of shortcodes within themselves. Then only the outer shortcodes are processed, and when their content is processed, the suffix is removed allowing them to be processed. There is no limit to how many levels deep it can go. (example: [box ][box ][box ][box ]Content[/box][/box][/box][/box] )

Add the following function above the do_shortcode() function

/**
 * Add shortcode suffix enabling recursive shortchodes.
 *
 * This function goes inside the set of $recursive_tags and adds a suffix to
 * the shortcode name so it will allow a shortcode to call itself in its content
 *
 * @since 2.5
 * @return string Content with recursive shortcodes modified out.
 */
function enable_recursive_shortcodes($content) {

    $recursive_tags = array( 'column', 'div', 'span', 'table' ); // define recursive shortcodes, these must have closing tags 
    $suffix = '__recursive'; // suffix is added to the tag of recursive shortcodes

    $content = str_replace( $suffix, '', $content ); // remove old suffix on shortcodes to start process over

    foreach( $recursive_tags as $recursive_tag ) {

        $open_tag = '[' . $recursive_tag . ' ';
        $close_tag = '[/'. $recursive_tag . ']';

        $open_tags = 0;

        $open_pos = stripos( $content, $open_tag ); // find first opening tag

        $offset = $open_pos + strlen( $open_tag ); // set offset for first closing tag

        while( $open_pos !== false ) {

            $close_pos = stripos( $content, $close_tag, $offset ); // find first closing tag

            if(++$open_tags > 1) { // if we are inside an open tag

                // modify open tag from original shortcode name
                $content = substr( $content, 0, $open_pos ) . 
                            '[' .$recursive_tag . $suffix . ' ' . 
                              substr( $content, $offset ); 
                // modify closing tag from original shortcode name
                $content = substr( $content, 0, $close_pos + strlen($suffix) ) . 
                            '[/'.$recursive_tag.'__recursive]' . 
                            substr( $content, $close_pos + strlen( $close_tag ) + strlen($suffix) ); 
                $open_tags--;
            }

            $open_pos = stripos( $content, $open_tag, $offset ); // find next opening tag

            if( $close_pos < $open_pos ) $open_tags--; // if closing tag comes before next opening tag, lower open tag count

            $offset = $open_pos + strlen($open_tag); // set new offset for next open tag search

        }

    }

    return $content;

}

Be sure to update the $recursive_tags variable as you need. Only those tags will be checked for same-name children. Those tags must have closing tags [column ][/column] because [column /] will not work. They also must have a space after the shortcode name in the opening tag and no spaces in the closing tag because of how the function searches through tags. If anyone updates the code to allow [column] or [column /] style shortcodes to work, please share.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.