Search code examples
phpwordpressshortcode

Wordpress shortcodes pass array of values


I am creating some WordPress short codes aimed at providing internal navigation on a page (one page with a lot of content sections and it own menu).

This is what I have:

//menu
function internal_menu($atts) {
  extract(shortcode_atts(array(
   'href1' => '#jl1',
   'href2' => '#jl2',
   'href3' => '#jl3',
   'href4' => '#jl4',
  ), $atts));
  return '<div id="internalPageMenu">
    <ul>
        <li><a href="' . $href1 . '"><i class="fa fa-bars"></i>link 1</a></li>
        <li><a href="' . $href2 . '">link 2</a></li>
        <li><a href="' . $href3 . '">link 3</a></li>
        <li><a href="' . $href4 . '">link 4</a></li>
    </ul>
    </div>';
}
add_shortcode('internal-menu', 'internal_menu');

//menu target
function internal_menu_target($atts) {
  extract(shortcode_atts(array(
   'id' => 'jl1',
   'text' => '',
   ), $atts));
   return '<h3 id="' . $id . '">' . $text . '</h3>';
}
add_shortcode('internal-menu-target', 'internal_menu_target');

And using this in my Wordpress admin panel:

[internal-menu]
[internal-menu-target id="jl1"]
Some content
[internal-menu-target id="jl2"]
...etc...

How do I make the menu dynamic (not restricted to the number of items it can have)? For example the short code would be:

[internal-menu targets="jl1, jl2, jl3, jl4, jl5, ...etc..."]

Solution

  • foreach would be your answer here. It will be the easiest and cleanest in my opinion. Before I give you a code example, lets analyze your code and look at all your flaws and how we will correct them

    FLAWS

    • Never ever use extract(). exctract() creates variables on the fly which is problematic. You cannot properly debug extract() (if you even can), so when it fails you really have your work cut out for you, unnecessarily. For these reasons, it was completely removed from core and the codex. See trac ticket 22400. You should have an evil list with query_posts and extract() in the top two positions, that i how bad these two are.

    • You are not sanitizing and validating input data which can lead to hacker injecting jquery into your code in order to hack your site. Never trust any data that comes from user side and URL's, it might be infected.

    • As you already know, taken from your code, shortcodes cannot except array value, the values must be string. In your case, we need to create an array from string values. Again, because you can't trust a user to not use spaces before or after commas, it is wise and recommended to remove all white spaces, if any, in order for your explode function to correctly create your array

    • With this new approach, you need to make sure that your values in your strings are the in the correct order and that the strings is the correct lenght. If not, you will get unexpected output

    Lets tackle the first shortcode: (PLEASE NOTE: All the code below is untested. It might be buggy or have syntax errors)

    internal-menu

    //menu
    function internal_menu( $atts ) 
    {
        $attributes = shortcode_atts(
            array(
               'href' => '',
             ), 
            $atts
        );
    
        $output = '',
        // Check if href has a value before we continue to eliminate bugs
        if ( !$attribute['href'] )
            return $output;
        // Create our array of values
        // First, sanitize the data and remove white spaces
        $no_whitespaces = preg_replace( '/\s*,\s*/', ',', filter_var( $attributes['href'], FILTER_SANITIZE_STRING ) ); 
        $href_array = explode( ',', $no_whitespaces );
    
        $output .= '<div id="internalPageMenu">';
            $output .= '<ul>';
    
                foreach ( $href_array as $k => $v ) { 
                    // From your code, link 1 is different, so I kept it as is
                    if ( $k == 0 ) {
                        $output .= '<li><a href="#' . $v . '"><i class="fa fa-bars"></i>link 1</a></li>';
                    } else { 
                        $output .= '<li><a href="#' . $v . '">link ' . ($k + 1 ) . '</a></li>';
                    }
                }
    
            $output .= '</ul>';
        $output .= '</div>';
    
        return $output;
    }
    add_shortcode( 'internal-menu', 'internal_menu' );
    

    You can then use the shortcode as follow

    [internal-menu href='jl1, jl2, jl3, jl4']
    

    internal-menu-target

    //menu target
    function internal_menu_target($atts) 
    {
        $attributes = shortcode_atts(
            array(
               'id' => '',
               'text' => '',
             ), 
            $atts
        );
    
        $output = '',
        // Check if href has a value before we continue to eliminate bugs
        if ( !$attribute['id'] || !$attribute['text'] )
            return $output;
    
        // Create our array of values
        // First, sanitize the data and remove white spaces
        $no_whitespaces_ids = preg_replace( '/\s*,\s*/', ',', filter_var( $attributes['id'], FILTER_SANITIZE_STRING ) ); 
        $ids_array = explode( ',', $no_whitespaces_ids );
    
        $no_whitespaces_text = preg_replace( '/\s*,\s*/', ',', filter_var( $attributes['text'], FILTER_SANITIZE_STRING ) ); 
        $text_array = explode( ',', $no_whitespaces_text );
    
        // We need to make sure that our two arrays are exactly the same lenght before we continue
        if ( count( $ids_array ) != count( $text_array ) )
            return $output;
    
        // We now need to combine the two arrays, ids will be keys and text will be value in our new arrays
        $combined_array = array_combine( $ids_array, $text_array );
        foreach ( $combined_array as $k => $v )
            $output .= '<h3 id="' . $k . '">' . $v . '</h3>';
    
        return $output;
    }
    add_shortcode('internal-menu-target', 'internal_menu_target');
    

    You can use this shortcode as follow:

    [internal-menu-target id='1,2,3,4' text='text 1, text 2, text 3, text 4']