0

I have a template that adds a shortcode. The add_shortcode is at the top of the template, before any template output. Note that any attributes for the shortcode are optional. I don't care if they exist or not. I will just test for the attribute values and do something if they exist.

When are the shortcode attributes available for use in the template? Psuedocode example ($atts are the shortcode's attributes array):

// template header is here
// add the shortcode
add_shortcode("mycode", "myshortcodefunction");

// is $atts available here? Because they are need by the next 
//   included function
include('someotherfunction.php');  // edited to add this line


function myshortcodefunction() {
   echo "a shortcode processed here ";
   echo "and here are the attributes:<br>";
   print_r($atts);
   return;
  }

// is $atts available here?

// template code here
// show the page content in the loop
the_post();

// is $atts available here?

// end of template

// is $atts available here?

In which of those 'spots' in the template page code does $atts contain the shortcode parameters? And how do I use those parameters outside of the add_shortcode function?

Can I create a constant that contains $atts?

// ending template stuff

Added

The overall intent of my project is to use the attributes in an 'included' function that is loaded after the add_shortcode statement. That included function does a lot of work, including displaying text (a form), and using the shortcode attributes in other parts of that included function.

So, I need access to the shortcode attributes before the content (and it's shortcode) is processed.... in the first "is $atts available here" after the add_shortcode.

I've also changed the pseudocode above to show where that included function is placed - directly after the add_shortcode.

2
  • Were you actually aware of function myshortcodefunction( $atts ), i.e. the $atts can and should be accessed like that? 🤔 And there are functions you can use to manually parse attributes from a shortcode, but what are you really trying to do - why do you need to access $atts outside the callback?
    – Sally CJ
    Jun 10, 2022 at 23:20
  • 1
    @SallyCJ See additional information added to the question. Jun 11, 2022 at 3:37

2 Answers 2

1

I need access to the shortcode attributes before the content (and it's shortcode) is processed.... in the first "is $atts available here" after the add_shortcode

In that case, you can manually parse the shortcode and retrieve its attributes (if any) like so, which uses get_shortcode_regex() and shortcode_parse_atts():

$content = 'before blah2 [foo bar="baz" num="123"] after';

preg_match( '/' . get_shortcode_regex() . '/s', $content, $matches );
$atts = isset( $matches[3] ) ? shortcode_parse_atts( $matches[3] ) : '';

var_dump( $atts );
/* Output:
array(2) {
  ["bar"]=>
  string(3) "baz"
  ["num"]=>
  string(3) "123"
}
*/
  • Note that the above will only retrieve the attributes of the first shortcode found in $content. So use that code only if you're sure the content would never contain any other shortcodes..

Or you can try this function which parses all shortcodes of a certain tag and return an array of attributes:

function my_shortcode_parse_atts( $text, $tag ) {
    $all_atts = array();

    preg_match_all( '/' . get_shortcode_regex() . '/s', $text, $matches );

    // If the shortcode ($tag) is found in $text, we parse the attributes.
    if ( isset( $matches[3] ) ) {
        // $matches[3] is the attributes string like ' bar="baz" num="123"'
        foreach ( $matches[3] as $i => $s ) {
            if ( $tag === $matches[2][ $i ] ) {
                $all_atts[] = shortcode_parse_atts( $s );
            }
        }
    }

    return $all_atts;
}
  • Sample usage and output:

    $content  = 'before [foo] blah2 [foo bar="baz" num="123"] after';
    $all_atts = my_shortcode_parse_atts( $content, 'foo' );
    
    var_dump( $all_atts );
    /* Output:
    array(2) {
      [0]=>
      string(0) ""
      [1]=>
      array(2) {
        ["bar"]=>
        string(3) "baz"
        ["num"]=>
        string(3) "123"
      }
    }
    */
    

    Note: In the above $content, there are 2 instances of the foo shortcode; one without any attributes, and the other with 2 attributes.

Also, I used a dummy text, so it's up to you on how to get the actual $content's value..

3
  • The project entails using a big 'include' functions that is also used outside of the WP environment. It needs access to the attributes before it displays the form that is displayed by the shortcode. I can't do it via a theme, since the big 'include' file is used in many sites - WP and non-WP. The shortcode only needs to be available via the page that uses the template. The template can be used in anyone's theme; they just need to add the big 'include' file and some functions inside the big include to make the form function as designed. Jun 11, 2022 at 4:19
  • Does the revised answer help?
    – Sally CJ
    Jun 11, 2022 at 14:23
  • 1
    Yes, it did help. I upvoted your answer, but added my own answer with the complete code that I used, in case others might wander in to this answer. Appreciate the help. Jun 12, 2022 at 22:05
0

The answer from @SallyCJ got me started on the solution that worked best for me. It uses some of her code. There are references to other resources that helped out.

function my_get_shortcode_atts($shortcode_id) {
/* given the $shortcode_id you are looking for, find it in the content
     before the loop 
   returns an array of the $atts element from the content
     the array will be empty if there are no attributes for the shortcode
*/


// get the content before the loop 
// (see https://wordpress.stackexchange.com/a/377671/29416 )
global $post;
$pageID =  $post->ID;
$fst_content = get_the_content(null, false, $pageID);

// extract shortcode attributes 
// (from  https://wordpress.stackexchange.com/a/172285/29416
preg_match_all( '/' . get_shortcode_regex() . '/s', $fst_content , $matches );
    $shortcode_atts = array();
    if( isset( $matches[2] ) )
    {
        foreach( (array) $matches[2] as $key => $value )
        {
            if( $shortcode_id === $value )
                $shortcode_atts[] = shortcode_parse_atts( $matches[3][$key] );
        }
    }
    if (! count($shortcode_atts)) {  // this returns false if shortcode not found
        return array();
        }
// 0th element is the attributes, so we get just that; 
// if there is more than one, you may need to adjust this part
$shortcode_atts = $shortcode_atts[0];
define("SHORTCODE_ATTS", $shortcode_atts);  // so I can use the $atts anywhere
return $shortcode_atts;
}

Once I have the attributes, I can use this in my 'big function file' to set some constants:

    if (function_exists('get_bloginfo')) {  
       // get_bloginfo function won't exist in non-WP environment
       // you can use other WP-unique functions if you wish
    // get shortcode atts into an array, otherwise false
    $atts = my_get_shortcode_atts("my_shortcode_name");
    // do whatever you want with the atts
    //    you may want to test for no shortcode attributes ($atts will be false)
    //    in my case, I'll use the constant defined to do things
    }

Now I can use my 'big functions file' in non-WP environments, as well as with shortcodes in a WP post/page. And the SHORTCODE_ATTS constant is used to do things based on the attributes (if any) found in the shortcode.

Since this is the solution that worked for me, it will be marked as such. But an upvote given on @SallyCJ's answer because it helped me with the above solution.

1
  • I'm glad my answer helped :), but some notes: 1) You could actually simply use get_the_content(null, false, get_post());, i.e. use get_post() .. 2) Contants should only be defined once, so you might want to do like if ( defined( 'SHORTCODE_ATTS' ) ) { return SHORTCODE_ATTS; } at the top in your my_get_shortcode_atts() function.. and 3) Instead of using constants, I would probably use the wp_cache functions, something like this..
    – Sally CJ
    Jun 14, 2022 at 2:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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