Xaver
Xaver

Reputation: 11682

Regular Expression with wordpress shortcodes

I'm trying to find all shortcodes within a string which looks like this:

 [a_col] One

 [/a_col] 

 outside
 [b_col]

 Two

 [/b_col] [c_col]  Three  [/c_col]

I need the content (eg "Three") and the letter from the col (a, b or c) Here's the expression I'm using

preg_match_all('#\[(a|b|c)_col\](.*)\[\/\1_col\]#m', $string, $hits);

but $hits contains only the last one.

The content can have any character even "[" or "]"

EDIT:

I would like to get "outside" as well which can be any string (except these cols). How can I handle that or should I parse this in a second step?

Upvotes: 1

Views: 774

Answers (3)

Aram Kocharyan
Aram Kocharyan

Reputation: 20431

This will capture anything in the content, as well as attributes, and will allow any characters in the content.

<?php

$input = '[a_col some="thing"] One[/a_col]
[b_col] Two [/b_col] 
[c_col] [Three] [/c_col] ';

preg_match_all('#\[(a|b|c)_col([^\[]*)\](.*?)\[\/\1_col\]#msi', $input, $matches);

print_r($matches);

?>

EDIT:

You may want to then trim the matches, since it appears there may be some whitespace. Alternatively, you can use regex for removing the whitespace in the content:

preg_match_all('#\[(a|b|c)_col([^\[]*)\]\s*(.*?)\s*\[\/\1_col\]#msi', $input, $matches);

OUTPUT:

Array
(
    [0] => Array
        (
            [0] => [a_col some="thing"] One[/a_col]
            [1] => [b_col] Two [/b_col]
            [2] => [c_col] [Three] [/c_col]
        )

    [1] => Array
        (
            [0] => a
            [1] => b
            [2] => c
        )

    [2] => Array
        (
            [0] =>  some="thing"
            [1] => 
            [2] => 
        )

    [3] => Array
        (
            [0] =>  One
            [1] =>  Two 
            [2] =>  [Three] 
        )

)

It might also be helpful to use this for capturing the attribute names and values stored in $matches[2]. Consider $atts to be the first element in $matches[2]. Of course, would iterate over the array of attributes and perform this on each.

preg_match_all('#([^="\'\s]+)[\t ]*=[\t ]*("|\')(.*?)\2#', $atts, $att_matches);

This gives an array where the names are stored in $att_matches[1] and their corresponding values are stored in $att_matches[3].

Upvotes: 2

Sam Greenhalgh
Sam Greenhalgh

Reputation: 6136

I don't have an environment I can test with here but you could use a look behind and look ahead assertion and a back reference to match tags around the content. Something like this.

(?<=\[(\w)\]).*(?=\[\/\1\])

Upvotes: 0

Billy Moon
Billy Moon

Reputation: 58541

use ((.|\n)*) instead of (.*) to capture multiple lines...

<?php
 $string = "
 [a_col] One

 [/a_col] 
 [b_col]

 Two

 [/b_col] [c_col]  Three  [/c_col]";
  preg_match_all('#\[(a|b|c)_col\]((.|\n)*)\[\/\1_col\]#m', $string, $hits);

  echo "<textarea style='width:90%;height:90%;'>";
  print_r($hits);
  echo "</textarea>";
?>

Upvotes: 1

Related Questions