Case
Case

Reputation: 4272

PHP Preg_march_all searching mulitple items with order not important

I want to match 2 results in a single string, both need to exist for something to be returned but the order in which they are in the string is not important.

 $row = '[one]This[/one][two]That[/two]';
 preg_match_all('#(\[one\](.*?)\[/one\])(\[two\](.*?)\[/two\])#', $row, $result_match);

If $row was instead. $row = '[two]That[/two][one]This[/one]';

is it possible for the preg_match_all to find the results anyway.

I need to capture THAT and THIS and know if it is inside one or two. Capture either both one and two or only capturing a single of the one or two would tell me which one the text was inside.

Update:

The final result I settled on is:

#[(?!\1)((?:one|two))](.*?)[\/\1]# 

Users after this new feature released now want to be able to nest the commands. I have tried changing ?:one|two to ?!.one|two to use negative look forward to match the final result in the string.

They want to be able to do this:

[one]This[one]This Again[/one][two]That Again[/two][/one][two]That[/two]

The desired capture would be

This[one]This Again[/one][two]That Again[/two]

And

[two]That[/two]

Update 2:

This is the full scope of what I am trying to do.

[run][one]This[/one][two]That[/two][/run]
[run][one]This 2[/one][two]That 2[/two][/run]
[run][one]IF THIS RESULT DO THIS [run][one]This[/one][two]That[/two][/run][/one][two]IF THIS RESULT DO THAT [run][one]This[/one][two]That[/two][/run][/two][/run]

The users do this all in one post and they want to be able to do nested runs. The desired results would be..

[one]This[/one][two]That[/two]
[one]This 2[/one][two]That 2[/two]
[one]IF THIS RESULT DO THIS [run][one]This[/one][two]That[/two][/run][/one][two]IF THIS RESULT DO THAT [run][one]This[/one][two]That[/two][/run][/two]

I can foreach the results to handle nested runs. Is this even possible?

Upvotes: 2

Views: 129

Answers (1)

anubhava
anubhava

Reputation: 785611

You may use this regex with a negative lookahead, capture group and back-reference:

~(?:.*?\[(?!\1)([12])\].*?\[/\1\]){2}~

RegEx Demo 1

Regex Description:

  • (?:: Start a non-capture group
    • .*?: Match 0 or more characters (non-greedy)
    • \[: Match [
    • (?!\1): Assert that we don't have text same as back-reference \1 ahead
    • ([12]): Match 1 or 2 and capture in group #1
    • \]: Match ]
    • .*?: Match 0 or more characters (non-greedy)
    • \[: Match [
    • /\1: Using back-reference, match same text as what we have in capture group #1
    • \]: Match ]
  • ): End non-capture group
  • {2}: Match 2 instances of above non-capture group

Update 2:

After your update above regex won't work anymore as you want to capture text between the tags. You may use: this regex:

~(?|\[one\](.*?)\[/one\].*?\[two\](.*?)\[/two\]|\[two\](.*?)\[/two\].*?\[one\](.*?)\[/one\])~

Your captured groups are available in #1 and #2.

RegEx Demo 2


Update 3:

Based on recent conversation you may use this recursive regex to match bbcode data between outermost [run] and [/run] tags:

$re = '~\[(run)] ( (?: (?: (?! \[/?\1] ) . )* | (?R) )* ) \[/\1]~sx';

Read more about recursive patterns

RegEx Demo 3

Upvotes: 1

Related Questions