Regular Expression with wordpress shortcodes - php

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?

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].

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>";
?>

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\])

Related

How to identify exactly what whitespace?

I am writing a php script to dissect information copied from an external webpage.
I paste the external data into a text area, which is passed through PHP's post function.
One of the lines looks something like this:
972 Date Name Information
The issue is, the first space after "972" is not actually a space. When I execute the strpos function with needle " ", it returns the position of the space following "Date". Possible solutions are:
Execute strpos which searches for all possible whitespaces.
Find some way to make my browser echo out the actual whitespace code so I know what to enter for the needle.
Suggestions?
You can use Regular Expression to intercept any character that is a whitespace of any kind, plus chr(160) to intercept non-breaking space. This should work:
$str = "972 Date Name Information";
if (preg_match_all('/[\s'.chr(160).']/', $str, $matches, PREG_OFFSET_CAPTURE)) {
print_r($matches);
}
It should give you the following result:
Array
(
[0] => Array
(
[0] => Array
(
[0] => �
[1] => 3
)
[1] => Array
(
[0] =>
[1] => 8
)
[2] => Array
(
[0] =>
[1] => 13
)
)
)
where the numbers at index [1] are the positions of the various whitespace characters in the string.

PHP Regex containing its limiters as ocurrences

I have this string:
{include="folder/file" vars="key:value"}
I have a regex to catch the file and the vars like this:
|\{include\=[\'\"](.*)\/(.*)[\'\"](.*)\}|U
First (.*) = folder
Second (.*) = file
Third (.*) = params (and I have some functions to parse it)
But there are some cases where I need to catch the params where they contains brackets {}. Like this:
{include="file" vars="key:{value}"}
The regext is working but it catches the results only until the first closing bracket. Like this:
{include="file" vars="key:{value}
So some part of the code remains out.
How can I make to allow those brackets as part of the results instead as a closing limiter???
Thanks!
You can use this regex:
\{include=['"](?:(.*)\/(.*?)|(\w+))['"] vars="(.*?)"\}
Working demo
MATCH 1
1. [10-16] `folder`
2. [17-21] `file`
4. [29-38] `key:value`
MATCH 2
3. [51-55] `file`
4. [63-74] `key:{value}`
Having in mind what #naomik said, I think I should change my regex.
What I want to make now is detecting this structure:
{word="value" word="value" ... n times}
I have this regex: (\w+)=['"](.*?)['"]
it detects :
{include="folder/file"}
{include="folder/file" vars="key:value"}
{vars="key:{value}" include="folder/file"} (order changed)
it works fine BUT I dont know how to add the initial and final brackets to the regex. When I add them it doesnt work like I want anymore
Live Demo
Another robust regexp that covers your first question :
preg_match_all("{include=[\"']{1}([^\"']+)[\"']{1} vars=[\"']{1}([^\"]+)[\"']{1}}", $str, $matches);
You'll get this kind of result into $matches :
Array
(
[0] => Array
(
[0] => {include="folder/file" vars="key:{value}"}
[1] => {include="folder/file" vars="key:value"}
[2] => {include="folder/file" vars="key:value"}
[3] => {include="file" vars="key:{value}"}
)
[1] => Array
(
[0] => folder/file
[1] => folder/file
[2] => folder/file
[3] => file
)
[2] => Array
(
[0] => key:{value}
[1] => key:value
[2] => key:value
[3] => key:{value}
)
)
you can access to what matters this way : $matches[1][0] and $matches[2][0] for the first elem, $matches[1][1] $matches[2][1] for the second, etc.
It does not store folder or file in separate results. For this, you'll have to write a sub piece of code. There is no elegant way to write a regex that is covering both include="folder/file" and include="file".
It does not support the inversion of include and vars. If you want to support this, you'll have to split your input data into chunks (line by line or text between braces) before your try to match the content with something like this :
preg_match_all("([\w]+)=[\"']{1}([^\"']+)[\"']{1}", $chunk, $matches);
then matches will contain something like this :
Array
(
[0] => Array
(
[0] => vars="key:{value}"
[1] => include="folder/file"
)
[1] => Array
(
[0] => vars
[1] => include
)
[2] => Array
(
[0] => key:{value}
[1] => folder/file
)
)
Then you know that $matches[1][0] contains 'vars', you can gets vars value in $matches[2][0]. For $matches[1][1] it contais 'include', you can then get 'folder/file' in $matches[2][1].

How to get a particular string using preg_replace?

i want to get a particular value from string in php. Following is the string
$string = 'users://data01=[1,2]/data02=[2,3]/*';
preg_replace('/(.*)\[(.*)\](.*)\[(.*)\](.*)/', '$2', $str);
i want to get value of data01. i mean [1,2].
How can i achieve this using preg_replace?
How can solve this ?
preg_replace() is the wrong tool, I have used preg_match_all() in case you need that other item later and trimmed down your regex to capture the part of the string you are looking for.
$string = 'users://data01=[1,2]/data02=[2,3]/*';
preg_match_all('/\[([0-9,]+)\]/',$string,$match);
print_r($match);
/*
print_r($match) output:
Array
(
[0] => Array
(
[0] => [1,2]
[1] => [2,3]
)
[1] => Array
(
[0] => 1,2
[1] => 2,3
)
)
*/
echo "Your match: " . $match[1][0];
?>
This enables you to have the captured characters or the matched pattern , so you can have [1,2] or just 1,2
preg_replace is used to replace by regular expression!
I think you want to use preg_match_all() to get each data attribute from the string.
The regex you want is:
$string = 'users://data01=[1,2]/data02=[2,3]/*';
preg_match_all('#data[0-9]{2}=(\[[0-9,]+\])#',$string,$matches);
print_r($matches);
Array
(
[0] => Array
(
[0] => data01=[1,2]
[1] => data02=[2,3]
)
[1] => Array
(
[0] => [1,2]
[1] => [2,3]
)
)
I have tested this as working.
preg_replace is for replacing stuff. preg_match is for extracting stuff.
So you want:
preg_match('/(.*?)\[(.*?)\](.*?)\[(.*?)\](.*)/', $str, $match);
var_dump($match);
See what you get, and work from there.

split regular expression php

I have a string like that :
0d(Hi)i(Hello)4d(who)i(where)540d(begin)i(began)
And i want to make it an array with that.
I try first to add separator, in order to use the php function explode.
;0,d(Hi),i(Hello);4,d(who),i(where);540,d(begin),i(began)
It works but the problem is I want to minimize the separator to save disk space.
Therefore i want to know by using preg_split, regular expression, if it's possible to have a huge array like that without using separator :
Array ( [0] => Array ( [0] => 0 [1] => d(hi) [2] => i(Hello) )
[1] => Array ( [0] => 4 [1] => d(who) [2] => i(where) )
[2] => Array ( [0] => 540 [1] => d(begin) [2] => i(began) )
)
I try some code & regex, but I saw that the value in the regular expression was not present in the final result (like explode function, in the final array we do not have the delimitor.)
More over, i have some difficulties to build the regex. Here is the one that I made :
$modif = preg_split("/[0-9]+(d(.+))?(i(.+))?/", $data);
I must precise that d() and i() can not be present (but at least one)
Thanks
If you do
preg_match_all('/(\d+)(d\([^()]*\))?(i\([^()]*\))?/', $subject, $result, PREG_SET_ORDER);
on your original string, then you'll get an array where
$result[$i][0]
contains the ith match (i. e. $result[0][0] would be 0d(Hi)i(Hello)) and where
$result[$i][$c]
contains the cth capturing group of the ith match (i. e. $result[0][1] is 0, $result[0][2] is d(Hi) and $result[0][2] is i(Hello)).
Is that what you wanted?

Get position of all matches in group

Consider the following example:
$target = 'Xa,a,aX';
$pattern = '/X((a),?)*X/';
$matches = array();
preg_match_all($pattern,$target,$matches,PREG_OFFSET_CAPTURE|PREG_PATTERN_ORDER);
var_dump($matches);
What it does is returning only the last 'a' in the series, but what I need is all the 'a's.
Particularly, I need the position of ALL EACH OF the 'a's inside the string separately, thus PREG_OFFSET_CAPTURE.
The example is much more complex, see the related question: pattern matching an array, not their elements per se
Thanks
It groups a single match since the regex X((a),?)*X matches the entire string. The last ((a),?) will be grouped.
What you want to match is an a that has an X before it (and the start of the string), has a comma ahead of it, or has an X ahead of it (and the end of the string).
$target = 'Xa,a,aX';
$pattern = '/(?<=^X)a|a(?=X$|,)/';
preg_match_all($pattern, $target, $matches, PREG_OFFSET_CAPTURE);
print_r($matches);
Output:
Array
(
[0] => Array
(
[0] => Array
(
[0] => a
[1] => 1
)
[1] => Array
(
[0] => a
[1] => 3
)
[2] => Array
(
[0] => a
[1] => 5
)
)
)
When your regex includes X, it matches once. It finds one large match with groups in it. What you want is many matches, each with its own position.
So, in my opinion the best you can do is simply search for /a/ or /a,?/ without any X. Then matches[0] will contain all appearances of 'a'
If you need them between X, pre-select this part of the string.

Categories