We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Bug? Unexpected behaviour when using regular expresion grouping

Let's say I have a URI look like this:

/list/[one][two][three]/pick/124

To make sure the url is always in the right format, I tried to add a router look like this:

/list/{list:(\[[^\[]+\])+}/pick/{count:[0-9]+}

and then when I access the parameters inside the controller, getParam('list') works just fine, but getParam('count') return '[three]' instead of 124.

Is this a Phalcon bug, or I'm doing something wrong?

Edit: I think the behaviour is weird because phalcon also uses ( and ) for placeholder.



98.9k

What version are you using?

I'm testing with 1.1.0:

preg_match('#^/list/(\[[^\[]+\])+/pick/([0-9]+)$#', '/list/[one][two][three]/pick/124', $matches);
var_dump($matches);

Shows:

array(3) { [0]=> string(32) "/list/[one][two][three]/pick/124" [1]=> string(7) "[three]" [2]=> string(3) "124" }


4.8k

ok maybe my regexp was wrong, because if not there shouldn't be any

[1]=> string(7) "[three]"

at all, I guest $this->dispatcher->getParam('count') will just bind to the second one in the array instead of its matched pattern



98.9k

I added the route to the router, then printed the generated route:

$route = $router->add('/list/{list:(\[[^\[]+\])+}/pick/{count:[0-9]+}');
var_dump($route);
object(Phalcon\Mvc\Router\Route)#53 (9) {
  ["_pattern":protected]=>
  string(46) "/list/{list:(\[[^\[]+\])+}/pick/{count:[0-9]+}"
  ["_compiledPattern":protected]=>
  string(37) "#^/list/(\[[^\[]+\])+/pick/([0-9]+)$#"
  ["_paths":protected]=>
  array(2) {
    ["list"]=>
    int(1)
    ["count"]=>
    int(2)
  }
  ["_methods":protected]=>
  NULL
  ["_hostname":protected]=>
  NULL
  ["_converters":protected]=>
  NULL
  ["_id":protected]=>
  int(4)
  ["_name":protected]=>
  NULL
  ["_beforeMatch":protected]=>
  NULL
}

You can see above the regexp generated and how 'list' will use the value at the first position and 'count' the second



4.8k
Phalcon\Mvc\Router\Route Object
(
    [_pattern:protected] => /list/{list:(\[[^\[\]]+\])+}/pick/{count:[0-9]+}
    [_compiledPattern:protected] => #^/list/((\[[^\[\]]+\])+)/count/([0-9]+)$#
    [_paths:protected] => Array
        (
            [list] => 1
            [pick] => 2
        )

    [_methods:protected] => 
    [_converters:protected] => 
    [_id:protected] => 2
    [_name:protected] => 
)

preg_match('#^/list/((\[[^\[\]]+\])+)/pick/([0-9]+)$#', '/list/[one][two][three]/pick/124', $matches);
var_dump($matches);

array(3) {
  [0]=>
  string(34) "/active/[one][two][three]/page/124"
  [1]=>
  string(17) "[one][two][three]"
  [2]=>
  string(7) "[three]"
  [3]=>
  string(3) "124"
}

[2] should be "124" not "[three]", I dont know why this is happen



98.9k

Are you using 1.1.0?



4.8k

Yeah I'm using the beta version, forgot to update it. Let us have a try



4.8k

Now 'list' is displayed incorrect instead of 'count'

list:'[three]'
page:124


98.9k

This is what I got:

$route = $router->add('/list/{list:(\[[^\[\]]+\])+}/pick/{count:[0-9]+}');
var_dump($route);

Output:

object(Phalcon\Mvc\Router\Route)#53 (9) {
  ["_pattern":protected]=>
  string(48) "/list/{list:(\[[^\[\]]+\])+}/pick/{count:[0-9]+}"
  ["_compiledPattern":protected]=>
  string(39) "#^/list/(\[[^\[\]]+\])+/pick/([0-9]+)$#"
  ["_paths":protected]=>
  array(2) {
    ["list"]=>
    int(1)
    ["count"]=>
    int(2)
  }
  ["_methods":protected]=>
  NULL
  ["_hostname":protected]=>
  NULL
  ["_converters":protected]=>
  NULL
  ["_id":protected]=>
  int(4)
  ["_name":protected]=>
  NULL
  ["_beforeMatch":protected]=>
  NULL
}

Checking regexp:

preg_match("#^/list/(\[[^\[\]]+\])+/pick/([0-9]+)$#", "/list/[one][two][three]/pick/124", $matches);
print_r($matches);
Array ( [0] => /list/[one][two][three]/pick/124 [1] => [three] [2] => 124 )


4.8k

It still isn't right. ([[^[]]+])+ is supposed to match the longest string, because of it greedy nature, I tried to wrap another level of parentheses to it, and the result return four values as seen before

preg_match("#^/list/((\[[^\[\]]+\])+)/pick/([0-9]+)$#", "/list/[one][two][three]/pick/124", $matches);
print_r($matches);
Array ( [0] => /list/[one][two][three]/pick/124 [1] => [one][two][three]  [2] => [three] [3] => 124 )

why there exists the extra [three] is still a mystery to me.

The problem is an extra () in you regex expresion, Each () in a regex expresion is a value in the result.

the correct expresion is : "^/list/([[^[]]+])+/pick/([0-9]+)$"

Regards