As explained before Wordpress still doesn't support nested shortcodes of the same type.
So you need to duplicate your shortcodes for nested instances.
However you may try this approach based on some regex preprocessing added via 'the_content'
hook.
.
- before parsing/converting shortcodes we automatically add numeric suffixes like
[div_1][/div_1]
- we duplicate shortcodes in a loop according to a maximum nesting level
Preprocess content for numeric suffixes
function counter_suffix_to_nested_shortcodes($content, $nested_shortcodes = [])
{
// Define the regular expression pattern for the shortcodes
$pattern = '/(\[\/?[a-zA-Z0-9\-\_]+(?: [^\]]+)?(?:_\d+)?\])/is';
// Define a function to handle the replacements
$callback = function ($matches) use (&$suffixStack, &$nested_shortcodes) {
// get tag name
$tag = $matches[0];
$pattern = '/\[\/?([A-Za-z0-9\-\_]+)/';
preg_match($pattern, $tag, $tagNameMatches);
$tag_name = $tagNameMatches[1];
$last_tag='';
//$suffixStack=[];
$suffixCounter = 0;
// not in array of shortcode type: return shortcode as it is
if (!in_array($tag_name, $nested_shortcodes)) {
return $tag;
}
// Extract the shortcode name
preg_match($pattern, $tag, $tagNameMatches);
$shortcode_name = $tagNameMatches[1];
// Check if it's a closing tag
if (strpos($tag, '/' . $tag_name) !== false) {
$suffix = array_pop($suffixStack);
// Ensure the suffix is correctly placed in the closing tag
if ($suffix > 0) {
$tag = str_replace(['[/', ']'], ['[/', '_' . $suffix . ']'], $tag);
$last_tag = "[$tag_name]";
}
} else {
// Only increment suffixCounter if it's not previous tag and suffixStack is not empty/reset
if ( !empty($suffixStack) && $tag_name != $last_tag) {
$suffixCounter = count($suffixStack);
} else {
//reset counter
$suffixCounter = 0;
$suffixStack = [];
}
$suffixStack[] = $suffixCounter;
// get new shortcode retaining all attributes
$suffix = $suffixCounter > 0 ? '_' . $suffixCounter : '';
$tag = str_replace('[' . $shortcode_name, '[' . $shortcode_name . $suffix, $tag);
}
return $tag;
};
// Initialize the suffix stack and counter
$suffixStack = [];
// Perform the replacement using the callback function
$content = preg_replace_callback($pattern, $callback, $content);
return $content;
}
// pereprocess content: don't wrap shortcodes
function sanitize_nested_shortcodes($content)
{
global $nested_shortcodes;
// unwrap shortcodes in p tags
$replace = [
'<p>[' => '[',
']</p>' => ']',
'</p>[' => '[',
']<br />' => ']',
'“' => '"',
'’' => '"',
'‘' => '"',
'″' => '"'
];
// add index suffixes to nested shortcodes
$content = counter_suffix_to_nested_shortcodes(strtr($content, $replace), $nested_shortcodes);
return $content;
}
add_filter('the_content', 'sanitize_nested_shortcodes'); }
}
Define shortcode
/**
* div_generate shortcode
* add class names and IDs
*/
function div_generate($atts, $content = null)
{
extract(shortcode_atts(
[
'class' => '',
'id' => ''
], $atts));
$cnt_filtered =
'<div id="' . $id . '" class="' . $class . '">' .
do_shortcode($content) .
'</div>';
return $cnt_filtered;
}
Define shortcode
/**
* div_generate shortcode
* add class names and IDs
*/
function div_generate($atts, $content = null)
{
extract(shortcode_atts(
[
'class' => '',
'id' => ''
], $atts));
$cnt_filtered =
'<div id="' . $id . '" class="' . $class . '">' .
do_shortcode($content) .
'</div>';
return $cnt_filtered;
}
Duplicate shortcodes
/**
* define nested shortcode
* names/identifieers:
* this prevents to break existing shortcodes
* to allow 20 nesting levels
*/
$nested_shortcodes = ['div'];
$nesting_max = 20;
// duplicate shortcodes
foreach($nested_shortcodes as $shortcode){
for ($i = 0; $i < $nesting_max; $i++) {
$suffix = $i === 0 ? '' : '_' . $i;
add_shortcode( $shortcode.$suffix, $shortcode.'_generate');
}
}
Now we can easily add nested shortcodes in WP editor without manually adding suffixes like so:
[div class="outer"]
[div class="nested-level1"]
<p>Content</p>
[div class="nested-level2"]
<p>Content</p>
[/div]
[/div]
[/div]
Output (before parsing)
[div class="outer"]
[div_1 class="nested-level1"]
<p>Content</p>
[div_2 class="nested-level2"]
<p>Content</p>
[/div_2]
[/div_1]
[/div]
Output (parsed)
<div id="" class="outer">
<div id="" class="nested-level1">
<p>Content</p>
<div id="" class="nested-level2">
<p>Content</p>
</div>
</div>
</div>
do_shortcode()
" - just do that. calldo_shortcode()
in your own shortcode hook. That's how it is done in Wordpress. -- That is the first line of the section "Nested Shortcodes" by the way. – Busterbustle