4
php > preg_match("@/m(/[^/]+)+/t/?@", "/m/part/other-part/t", $m);
php > var_dump($m);
array(2) {
  [0]=>
  string(20) "/m/part/other-part/t"
  [1]=>
  string(11) "/other-part"
}
php > preg_match_all("@/m(/[^/]+)+/t/?@", "/m/part/other-part/t", $m);
php > var_dump($m);
array(2) {
  [0]=>
  array(1) {
    [0]=>
    string(20) "/m/part/other-part/t"
  }
  [1]=>
  array(1) {
    [0]=>
    string(11) "/other-part"
  }
}

With said example I would like the capture to match both /part and /other-part, unfortunately with regex /m(/[^/]+)+/t/? doesn't capture both, as I expect.

This capture should not be bound to only match this sample, it should capture an undefined number of repetitions of the capture group; e.g. /m/part/other-part/and-another/more/t

UPDATE: Given that this is expected behavior my question stands as of how I would be able to achieve this matching of mine?

mhitza
  • 5,709
  • 2
  • 29
  • 52
  • 1
    I would expect the capture group to be applied repeatedly due to the quantifier `+` – mhitza Mar 14 '12 at 14:28
  • Also "does not work" is not good to have in a question title. And in fact the quantifier does work as announced in PCRE. – hakre Mar 14 '12 at 14:31
  • @hakre Removed the does not work part from the title. Based on your answer it seems that my memory is cheating me because I remembered doing quantification on capture groups. – mhitza Mar 14 '12 at 14:33
  • Looking closer I assume you want to capture the subgroup matches, which is not supported with PHP, here is a javascript example: [Can you retrieve multiple regex matches in JavaScript?](http://stackoverflow.com/q/6571106/367456). – hakre Mar 14 '12 at 14:36

3 Answers3

2

Thats the way capturing groups are working. repeated capturing groups have only the last match stored after the regex finished. Thats in your test "/other-part".

Try this instead

/m((?:/[^/]+)+)/t/?

See it here on Regexr, while hovering over the match, you can see the content of the capturing group.

Just make your group non-capturing by adding a ?: at the start and put another one around the whole repetition.

In php

preg_match_all("@/m((?:/[^/]+)+)/t/?@", "/m/part/other-part/t", $m);
var_dump($m);

Output:

array(2) { 
    [0]=> array(1) { 
        [0]=> 
        string(20) "/m/part/other-part/t" 
    }
    [1]=> array(1) { 
        [0]=> 
        string(16) "/part/other-part" 
    }
}
stema
  • 90,351
  • 20
  • 107
  • 135
  • Please see my updated question. Because if that is so, I still would want to know how this can be achieved. – mhitza Mar 14 '12 at 14:36
2

Try this one out:

preg_match_all("@(?:/m)?/([^/]+)(?:/t)?@", "/m/part/other-part/another-part/t", $m);
var_dump($m);

It gives:

array(2) {
  [0]=>
  array(3) {
    [0]=>
    string(7) "/m/part"
    [1]=>
    string(11) "/other-part"
    [2]=>
    string(15) "/another-part/t"
  }
  [1]=>
  array(3) {
    [0]=>
    string(4) "part"
    [1]=>
    string(10) "other-part"
    [2]=>
    string(12) "another-part"
  }
}

//EDIT

IMO the best way to do what you want is to use preg_match() from @stema and explode result by / to get list of parts you want.

piotrekkr
  • 2,785
  • 2
  • 21
  • 35
  • `/m` optional at the beginning? I understand why you do that, but isn't that imprecise? – hakre Mar 14 '12 at 14:46
  • Yeah it's imprecise but if it's not optional than regex fails to explode parts that he wants. I would use simple `preg_match()` + `explode()` to get parts he wants. – piotrekkr Mar 14 '12 at 14:52
  • @piotrekkr your response is more conforming to the question (and I'll accept it for that); but I'll go with stema's response instead where I will do the preg_match() and explode instead of having for this a strict pre check if the string starts with (/m) and ends with (/t/?) :) – mhitza Mar 14 '12 at 14:56
0

As already written in a comment, you can't do this at once because preg_match does not allow you to return the same subgroup matches as well (like you can do with Javascript or .Net, see Get repeated matches with preg_match_all()). So you can divide the operation onto multiple steps:

  1. Match the subject, extract the part you're interested in.
  2. Match the interested part only.

Code:

$subject = '/m/part/other-part/t';
$subpattern = '/[^/]+';
$pattern = sprintf('~/m(?<path>(?:%s)+)/t/?~', $subpattern);
$r = preg_match($pattern, $subject, $matches);
if (!$r) return;
$r = preg_match_all("~$subpattern~", $matches['path'], $matches);
var_dump($matches);

Output:

array(1) {
  [0]=>
  array(2) {
    [0]=>
    string(5) "/part"
    [1]=>
    string(11) "/other-part"
  }
}
Community
  • 1
  • 1
hakre
  • 193,403
  • 52
  • 435
  • 836