Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to nest wordpress shortcodes that are the same shortcode?

I know it is possible to nest shortcodes if they use the do_shortcode wrapper, however, the codex states:

"However the parser will fail if a shortcode macro is used to enclose another macro of the same name:"

Is there a way around this?

for example if I have the a shortcode to make a div such as:

[div]some content in a div[/div]

I want to be able to use:

[div]
    [div]a nested div[/div]
[/div]

but this will fail with the standard do_shortcode wrapper.

My temporary workaround is the duplicate the shortcode with _parent appended to the name but than I can only nest 1 level deep unless I created div_parent1, div_parent2 etc...

like image 530
tsdexter Avatar asked Oct 06 '22 03:10

tsdexter


2 Answers

If you are writing the shortcode there is a simple solution. You can write several shortcodes that call the same function. I have shortcodes for creating html blocks such as divs, and have several with names like div, block1, block2 eg.

add_shortcode('div', 'devondev_block');
add_shortcode('block', 'devondev_block');
add_shortcode('block2', 'devondev_block');

They all call the same function. They can be nested as long as you remember to use different short codes.

The WordPress short code support suffers from the attempt to do the parsing with regex only. It is possible to do this sort of parsing with a mixture of regex, a finite state machine, and a stack. That approach can handle nesting and can be very fast, especially when there are very few shortcodes. Every time I encounter this I'm tempted to give it a try.

like image 141
Peter Wooster Avatar answered Oct 10 '22 02:10

Peter Wooster


The API tells it like it is, thus this is not possible:

This is a limitation of the context-free regexp parser used by do_shortcode() - it is very fast but does not count levels of nesting, so it can't match each opening tag with its correct closing tag in these cases.

The functions in question in the latest version (3.4.2) are:

function do_shortcode($content) {
    global $shortcode_tags;

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

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

function get_shortcode_regex() {
    global $shortcode_tags;
    $tagnames = array_keys($shortcode_tags);
    $tagregexp = join( '|', array_map('preg_quote', $tagnames) );

    // WARNING! Do not change this regex without changing do_shortcode_tag() and strip_shortcode_tag()
    return
          '\\['                              // Opening bracket
        . '(\\[?)'                           // 1: Optional second opening bracket for escaping shortcodes: [[tag]]
        . "($tagregexp)"                     // 2: Shortcode name
        . '\\b'                              // Word boundary
        . '('                                // 3: Unroll the loop: Inside the opening shortcode tag
        .     '[^\\]\\/]*'                   // Not a closing bracket or forward slash
        .     '(?:'
        .         '\\/(?!\\])'               // A forward slash not followed by a closing bracket
        .         '[^\\]\\/]*'               // Not a closing bracket or forward slash
        .     ')*?'
        . ')'
        . '(?:'
        .     '(\\/)'                        // 4: Self closing tag ...
        .     '\\]'                          // ... and closing bracket
        . '|'
        .     '\\]'                          // Closing bracket
        .     '(?:'
        .         '('                        // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags
        .             '[^\\[]*+'             // Not an opening bracket
        .             '(?:'
        .                 '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
        .                 '[^\\[]*+'         // Not an opening bracket
        .             ')*+'
        .         ')'
        .         '\\[\\/\\2\\]'             // Closing shortcode tag
        .     ')?'
        . ')'
        . '(\\]?)';                          // 6: Optional second closing brocket for escaping shortcodes: [[tag]]
}
like image 29
doublesharp Avatar answered Oct 10 '22 03:10

doublesharp