Can someone help me understanding regular expressions that Yii 2 uses?

First of all, sorry, if this question sounds really dumb, but being a developer for 25+ years and I still haven’t… developed even an inch of my personal love toward regular expressions. Even more, I feel chills coming down my spin each time I have to deal with them or even think about them.

As I understand that these are regular expressions:

public array $tokens = [
    '{id}' => '<id:\d[\d,]*>',
]

Am I correct?

So, my first question is, why Yii 2 is using such way of formatting / providing them, different than pure PHP is using:

$exp = "/w3schools/i";
$pattern = "/ain/i";

Why Yii 2 is using single backslash, if PHP is using single slash?

I have found some other Yii 2 example (the one that I am interested the most – modifying URL patterns for REST app):

'tokens' => [
    '{id}' => '<id:\\d[\\d,]*>',
    '{type}' => '<type:\\w[\\w,]*>',
    '{key}' => '<key:\\w[\\w,]*>',
],

And, as you can see, it uses double backslash instead of single backslash (Yii 2 guide) or single slash (PHP guide). Which only adds to my mystery.

So, second question, what is suggested / preferred way of using delimiters in regular expressions when using them in Yii2? And what is the difference, if any?

Third question or rather doubt is that mentioned example is using \\d to denote any digit (Yii 2 guide has \d for this, and W3Schools has it as well. But that Stack Overflow example has \\w to denote any character, which I can’t find in W3Schools page.

Fourth question is, if there is any specific page in Yii 2 Guide that has exactly this format (<id:\d[\d,]*>) described for dumb newbies like myself?

The final question, that binds all of them above is how can I modify my existing URLRule configuration to accept dots (.) in URLs that are executed against my REST app?

Right now I have it coded like that:

'class' => \yii\rest\UrlRule::class,
'controller' => ['device'],
'pluralize' => false,
'extraPatterns' => ['GET,HEAD find/{key}' => 'find'],
'tokens' => [
    '{id}' => '<id:\\d[\\d,]*>',
    '{key}' => '<key:\\w[\\w,]*>',
],

And the URLs like api/web/device/find/TEST or api/web/device/find/TEST2 works just like a charm. But in the same time the URL that contains dots (api/web/device/find/TEST.040) fails with 404.

How can I add support for dots to my URL?

Now I’m just guessing but isn’t the first of the double backslashes for escaping (creating re from text string).
If so you should try this
[\\w\\.,].

Works like a charm, thank you!

That’s why I was asking, if there is any guide for this (“Doing regular expressions in Yii 2”). Because – as you can see – I don’t even know the obvious part. And I don’t know, which characters are included in \\w token and which must be explicitly added – as you have suggested.

Anyway, thanks again for solving this.

Hello Tomasz.

'{id}' => '<id:\d[\d,]*>',

As I understand that these are regular expressions?

Partly. These are patterns of parameters being forwarded to action with regular expression which describe “how to extract them”.

It is quite good described in https://www.yiiframework.com/doc/guide/2.0/en/runtime-routing#url-rules

In above example:

<id: // parameter definition start with name = id
\d[\d,]* // regular expression "one digit (\d) and zero or more (*) characters from set of digits and commas [\d,] 
> \\ parameter definition end

There are bunch of online editors very useful for testing and familiarize regexes, e.g. https://www.phpliveregex.com/p/K6r (above example)

tbc…

1 Like

Bart, thank you so much! You have wrote “TBC”, but I am unsure, if there is anything more to add. Your answer is (as always) simple, clear and straight-to-the-point.

Since I want to be able to pass any character as my action’s $key parameter, based on your answer and the link that you have provided, I have figured out that all I need is:

//PHP Live Regex's Cheat Sheet: \S -- Any non-whitespace character
'{key}' => '<key:[\S]*>',

It seems to be working as I need it (i.e. my URL accepts any key except the one that contains space).

Now, the only problem I have is how to correctly configure yii\validators\RegularExpressionValidator’s $pattern property to achieve the same on model level?

I.e. when I am using:

['key', 'match', 'pattern' => '/\S/'],

I am getting logical error. Keys like TE ST are allowed (pass data validation) while should be rejected.

['key', 'match', 'pattern' => '/\S/'],

You have to change your regex a bit since yours means “only one non-whitespace character” which “T” from “TE ST” is so the match is true.

Correct one will be '/^\S+$/' which means "from the beginning ‘^’ at least one or more non-whitespace characters ‘\S+’ to the end ‘$’ "

Test it: https://www.phpliveregex.com/p/K6x

1 Like

Thanks a lot!

Just an additional information in case you hate regular expressions as much as me and prefer a less “scary” way to write it…

<id:\d+>
<key:\S+>

Would be valid as well…
So you can have rules like

user/<id:\d+>
That will match user/1 and user/99999

Direct demonstrations from my code

'{hash}'            => '<hash:\S+>',
'{orderId}'         => '<orderId:\d+>'

Most of the time those 2 examples (digits / non whitespace characters) tend to be totally enough for the majority of use cases.

2 Likes

Good to know that I am not the only one you hates regular expressions here! :] Actually, in my case it isn’t “hate” actually. I found them very useful, though they scare me to death! :]

Your answer is most welcomed as it clarifies a lot in my thinking.

I was also very surprised to learn that “by default” (guide, many YouTubes) people are using <id:\\d[\d+,]*. I think that doesn’t make much sense from reg-exp perspective. I mean: what’s the deal in single digit followed by many digits (if I am getting this reg-exp correctly)?.

As I briefly tested in my app, it seems that it doesn’t even make sense from Yii 2 perspective. I don’t know why we are allowing comma, if commas seems to be not supported. I have tried api/web/patient/1,2, thinking that I’ll get two records, but I’ve got only first. The ,2 part was ignored.

So, based on your suggestion I have changed my code to nearly exactly as yours:

'extraPatterns' => ['GET,HEAD find/{key}' => 'find'],
'tokens' => [
    '{id}' => '<id:\d+>',
    '{key}' => '<key:\S+>',
],

And the only matter that is left, is how to provide the same reg-exp rule to the model validation? I assume that I must follow the way that Bart has described above, so:

['id', 'match', 'pattern' => '/^\d+$/'],
['key', 'match', 'pattern' => '/^\S+$/'],

Am I correct?

Of course id matching pattern and the whole idea of using match validator here is just an example. Normally we replace it with simple ['id', 'integer']. I only wrote this “version” to compare your idea of reg-exps used in controller configuration vs. the same model validation.

It can be like that on purpose: the pattern means it must begin with digit then you can have more digits or commas (and plus sign too if it is between [ ]), e.g:

1 | 11 | 1,2 | 1,2+3

but not:
,1 | ,11 | +3

You can achieve it with rule like that: web/patient/<id:\d+(,\d+)*>
It must starts with minimum one digit ‘\d+’ then it can have zero or more ‘*’ groups of comma and minimum one digit ‘(,\d+)*’

This way you can request: 1,2,345 but not 1, or 1,2,345,
Then in the action you can explode it (link) by ‘,’ and you have an array of ids.

Yes, you are :slight_smile:

TBC… or not :wink: