I created a solution for my nested shortcodes, maybe you could use something from it.
I used a recursive regex (notice |(?R)
), so it might be not that fast as the official do_shortcode, but it allows nesting shortcodes with the same name.
My request for it was to not have a fixed list of shortcodes (so all existing in the content shortcodes must be parsed), to allow any level of nesting, and also to use a structure of $atts, which contain some parameters, existing at the time of filling this exact content.
For $atts I wrote my own function, too, as I wanted $atts to include both everything coming from the content (and not only those, whose default value is defined), and the default values for those fields, existing in the default array and not coming from the shortcode values.
Here's my atts function:
function complete_atts_with_defaults( $pairs, $atts = array() ) {
$out = $atts;
foreach ($pairs as $name => $default) {
if ( array_key_exists($name, $atts) && !empty( $atts[$name] ) ) { continue; }
//else
$out[$name] = $default;
}
return $out;
}
And here's the other code:
define( 'OUR_SHORTCODE_FULL_REGEX', '@\[([a-z0-9_]+)([^\]]*)\]((?:[^[]|(?R))+)\[\/\1\]@' );
define( 'OUR_SHORTCODE_HALF_REGEX', '@\[([a-z0-9_]+)([^\]]*)\/\]@' );
function fill_all_post_placeholders( $text, $atts = array() ) {
//...
$text = str_replace( '%5B', '[', $text );
$text = str_replace( '%5D', ']', $text );
$atts = complete_atts_with_defaults(
array(
'value1' => 'Hi :)',
'value2' => '',
'value3' => '',
), $atts );
//...
$text = preg_replace_callback( OUR_SHORTCODE_HALF_REGEX, function( $match ) use ( $atts ) { return replace_half_shortcode_placeholder( $match[1], $match[2], $atts ); }, $text );
$text = preg_replace_callback( OUR_SHORTCODE_FULL_REGEX, function( $match ) use ( $atts ) { return replace_shortcode_placeholder( $match[1], $match[2], $match[3], $atts ); }, $text );
return $text;
}
function replace_shortcode_placeholder( $name, $params, $content, $atts ) {
$content = preg_replace_callback( OUR_SHORTCODE_FULL_REGEX, function( $match ) use ( $atts ) { return replace_shortcode_placeholder( $match[1], $match[2], $match[3], $atts ); }, $content );
// $function_to_call = '_rp_' . $name;
// if ( function_exists( $function_to_call ) ) { return $function_to_call( $name, $params, $atts ); }
// //else
// $value = $atts[ $name ];
// if ( empty( $value ) ) { return ''; }
// //else
// return $value;
return '<br>name: ' . $name . ' <br>params: ' . $params . ' <br>content: (' . $content . ')<br><br>';
}
function replace_half_shortcode_placeholder( $name, $params, $atts ) {
// $function_to_call = '_rp_' . $name;
// if ( function_exists( $function_to_call ) ) { return $function_to_call( $name, $params, $atts ); }
// //else
// $value = $atts[ $name ];
// if ( empty( $value ) ) { return ''; }
// //else
// return $value;
return '<br>name half: ' . $name . ' <br>params half: ' . $params . '<br><br>';
}
I left for you the commented area intact, so that you could see how those functions could be used.
These "shortcodes" of mine are not registered, as you can see in the commented code, they work differently, dynamically.
Also, the necessary rule for these shortcodes to work would be, to always add '/' to the self-closing tags, like one would do in XML. (In the code they are called "half-shortcodes".)
Example to the content parsed by this code (sorry for the gibberish):
[show_text_if campaign="2899"]subscribing to this newsletter [show_text_if campaign="2901" field_value="'a href='http://ajshdash.com/asdhal/asdasdasd'" field_value2="айксчд асй даксчй д"]subscribing to this new newsletter[/show_text_if] [red value1="dfsdfs" value2="sdfkls dfh skjha skd"/][/show_text_if]
[color value3="askdfjajls" value4="lskdfhjsldhfs df"/]
[show_text_if_not campaign="2899"]asdklja slkd alkd alskdj alsjd alsj als jdalsk jals dalskj asjd <a href="http://example.com/experiment/" target="_blank">Experiment</a> once more hooray! [green value4="askljjlaskd"/] Я сегодня это Я и я! :)[/show_text_if_not]
[show_text_if field_name="campaign" field_value="2899"]slkjd alsjkd aljsd ajs d new[/show_text_if]
And I understand, that this code is not a direct solution to what you asked for, but maybe you could use some of it, to work out the solution yourself.