正则表达式查找标记(preg_replace_callback)

I need to find all occurrences of a Tag for a WP-Plugin.

<wpg3>10|300|defaultTemplate|eyJhbGlnbiI6ImFsaWdubGVmdCJ9</wpg3>

There are serval possible Versions of the tag (,,, ...) but start and end do match. The Groups are optional: Should mean, that there could be none or one, or two or three "|", which separate the Options.

My problem: if there is only one Tag in my search-string everything will work as expected. But if I add a second tag to my string , the callback is only called once, instead of once per Tag. There must be something missing at the Beginning or in the end. The Regexp just fails using multiple tags if the last Argument (features) is missing.

  $return = preg_replace_callback('/<wpg[23](?P<unused>id)?>(?P<uri_or_id>[^\|]*)[\|]?(?P<width>[^\|]*)[\|]?(?P<template>[^\|]*)[\|]?(?P<features>[^\|]*)<\/wpg[23](?P<unused2>id)?>/i', array( $this, 'wpg3_content' ), $content );

I taking the example above I wanna get:

Array
(
    [0] => 10|300|defaultTemplate|eyJhbGlnbiI6ImFsaWdubGVmdCJ9
    [unused] =>
    [1] => 
    [uri_or_id] => 10
    [2] => 10
    [width] => 300
    [3] => 300
    [template] => defaultTemplate
    [4] => defaultTemplate
    [features] => eyJhbGlnbiI6ImFsaWdubGVmdCJ9
    [5] => eyJhbGlnbiI6ImFsaWdubGVmdCJ9
)

can you do a preg_match_all on tags first of all

preg_match_all("/<([^>]*)?>/",$in, $out);

then loop through the $out array, where you should have the tag name and content.

if the tag matches what you want then

explode($out[2],"|")

or are you looking to do everything in your regex?

Once you answer my comment above, I might have something more precise. Here is what I have so far. I made it in Python because it was easier for me, but you get the idea.

Here is my regex:

regex = re.compile('''
    <(?P<tag>wpg[23])(?P<unused>id)?>
   (?:
      (?P<uri_or_id>[^\|<]+)
       (?:
           \|(?P<width>[^\|<]+)
           (?:
              \|(?P<template>[^\|<]+)
              (?:
                 \|(?P<features>[^\|<]+)
               )?
            )?
        )?
    )?</(?P=tag)(?P<unused2>id)?>''', re.IGNORECASE|re.VERBOSE)

Each text in the options is mandatory, but optional non-matching groups ensure that options are indeed optional. I also use a look-behind expression (?P=tag) to be sure that the closing tag matches the opening tag. I protected the matches a bit more than [^\|] with [^\|>] to prevent your multi-tag issues.

My test strings:

# Your example
>>> text
'<wpg3>10|300|defaultTemplate|eyJhbGlnbiI6ImFsaWdubGVmdCJ9</wpg3>'

# Options should be, well, optional
>>> text2
'<wpg3>10|300|defaultTemplate</wpg3>'

# These two should fail if I understood properly    
>>> text3
'<wpg3>10|300|defaultTemplate|</wpg3>'
>>> text4
'<wpg3>10|300||</wpg3>'

# Now with more than one tag
>>> text5
'<wpg3>10|300|defaultTemplate|eyJhbGlnbiI6ImFsaWdubGVmdCJ9</wpg3><wpg3>25|35|hello|world</wpg3>'
>>> text6
'<wpg3>10|300|defaultTemplate|eyJhbGlnbiI6ImFsaWdubGVmdCJ9</wpg3><wpg2>25|35|hello|world</wpg2>'

# This should fail because tags mismatch
>>> text7
'<wpg3>10|300|defaultTemplate|eyJhbGlnbiI6ImFsaWdubGVmdCJ9</wpg2>'

And here are the tests:

# Parses as expected
>>> regex.match(text).groups()
('wpg3', None, '10', '300', 'defaultTemplate', 'eyJhbGlnbiI6ImFsaWdubGVmdCJ9', None)
>>> regex.match(text2).groups()
('wpg3', None, '10', '300', 'defaultTemplate', None, None)

# These two fail as expected
>>> regex.match(text3)
>>> regex.match(text4)

# Multi-tags now
>>> for m in regex.finditer(text5):
...    m.groups()
... 
('wpg3', None, '10', '300', 'defaultTemplate', 'eyJhbGlnbiI6ImFsaWdubGVmdCJ9', None)
('wpg3', None, '25', '35', 'hello', 'world', None)
>>> for m in regex.finditer(text6):
...    m.groups()
... 
('wpg3', None, '10', '300', 'defaultTemplate', 'eyJhbGlnbiI6ImFsaWdubGVmdCJ9', None)
('wpg2', None, '25', '35', 'hello', 'world', None)

# The last one fails (tag mismatch)
>>> regex.match(text7)

Does that correspond to what you need?