Skip to content

[Routing] Add seamless support for unicode requirements #19604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 25, 2016

Conversation

nicolas-grekas
Copy link
Member

@nicolas-grekas nicolas-grekas commented Aug 12, 2016

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? yes
Tests pass? yes
Fixed tickets #3629, #5236, #19562
License MIT
Doc PR symfony/symfony-docs#6890

This PR adds unicode support to route matching and generation by automatically adding the u modifier to regexps that use either unicode characters or unicode enabled character classes (e.g. \p... \x{...} \X).

As a side note, if one wants to match a single unicode character (vs a single byte), one should use \PM or \X instead of . or set the unicode parameter to true.

@nicolas-grekas
Copy link
Member Author

ping @Tobion

@c960657
Copy link
Contributor

c960657 commented Aug 19, 2016

I like how this approach is simpler than my own PR #19562. I think UTF-8 is the only reasonable encoding to use for URLs (because this is what browsers use when pretty-printing the URLs in the location field), and I assume most people will use UTF-8 internally in their data and source code, so I like how this patch favours UTF-8 while still allowing people to do otherwise if they want.

I think the auto-detection of UTF-8 patterns is a bit too magic. E.g. whether . matches a byte or a character is determined by the whether the pattern contains some of the triggering characters elsewhere. So I am wondering whether we can make a more explicit but backwards-compatible way of enabling/disabling UTF-8.

Here are two examples that are explicit. They are not native PCRE syntax but reuse syntax used in PCRE.

  1. PCRE allows enabling UTF-8 mode from within the pattern using (*UTF8) (mentioned here. This is only valid at the beginning of the pattern, but we could reuse the syntax for triggering UTF-8 and just strip it when concatenating the pattern.
  2. PCRE allows setting internal options using (?, e.g. (?i) and (?-i)will enable and disable case-insensitivity, respectively. This does not work for the u pattern, but perhaps we could reuse the syntax and strip it. This syntax also allows us to make UTF-8 on by default in Symfony 4, so that it has to be explicitly disabled.

According to the manual, using \w is faster than \pL, so it would be nice if one could trigger UTF-8 mode without using \pL.

@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Aug 19, 2016

I didn't know about the (*UTF8) prefix thanks for the link! I don't think we should target adding the u flag by default in 4.0: most of the time, URLs are plain ASCII string. But I agree with you: having magic behavior for . is an issue and recommending \X or \PM to opt into unicode comes with a perf overhead.
Since unicode should really be enabled at the route level (not at the requirement level), I think we could add a conventional prefix to routes for enabling unicode. I propose * for now.
The current magic that detects unicode could be turned into a deprecation (an exception in 4.0) for warning the user when one uses unicode characters/properties while the * prefix is missing.

@nicolas-grekas
Copy link
Member Author

The prefix idea doesn't play well with route prefixing as done by loaders...
The PR now handles a * prefix to enable unicode at the requirements level.
See test cases also.

@stof
Copy link
Member

stof commented Aug 19, 2016

Another solution is to use a route option to opt in unicode mode. Route options are precisely meant to give hints to the route compiler.
This would be easier to explain and remember than the fact of adding a magical (*UTF8) needing to be stripped.

@nicolas-grekas
Copy link
Member Author

@stof but that would be a DX nightmare: if you'd use unicode, you'd need to repeat yourself thousands of times. The current way is a bit magic, but really seamless: if you use any unicode chars or any unicode property, unicode is enabled. There is only one special case: the * prefix to requirements to force unicode matching for . when needed.

@nicolas-grekas
Copy link
Member Author

Doc PR added: symfony/symfony-docs#6890

@fabpot
Copy link
Member

fabpot commented Aug 19, 2016

I don't really like the * convention, but I don't have any better idea, so 👍

@stof
Copy link
Member

stof commented Aug 19, 2016

@fabpot what about my proposal of using my proposal ?

@nicolas-grekas we could keep the autodetection of unicode. The option would be used instead of using a magic * (the other question is whether setting the option to false explicitly should disable the autodetection)

@fabpot
Copy link
Member

fabpot commented Aug 19, 2016

@stof Indeed, I think an option is better than an obscure convention.

@nicolas-grekas
Copy link
Member Author

updated

@nicolas-grekas nicolas-grekas force-pushed the utf8-routes-ter branch 2 times, most recently from c12f06a to d52bda3 Compare August 19, 2016 16:36
@c960657
Copy link
Contributor

c960657 commented Aug 23, 2016

The patch uses the term “Unicode” in variable names etc., but wouldn't it be more correct to use “UTF-8” (the specific encoding we are supporting)? “Unicode” is a much wider concept. Note that the Unicode character properties escape sequences, \p{xx} etc., works even in non-UTF-8-mode.

Also, I think we disagree a bit on what exactly this feature does:

    /**
     * Returns the unicode enforcement status.
     *
     * @return bool Whether unicode matching is enforced or not
     */
    public function getUnicode()

In my eyes, this flag enabled UTF-8 support for regular expressions, i.e. basically adds the u modifier – nothing else. Do you agree, or could you elaborate what "unicode enforcement" means?

@@ -5,6 +5,7 @@ CHANGELOG
-----

* Added support for `bool`, `int`, `float`, `string`, `list` and `map` defaults in XML configurations.
* Added support for unicode requirements

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unicode is a proper noun, so it should be capitalized.

* Existing settings will be overridden.
*
* @param string $unicode Whether unicode matching is enforced or not
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@param bool

@nicolas-grekas nicolas-grekas force-pushed the utf8-routes-ter branch 2 times, most recently from 8276d4b to a1d4bc4 Compare August 24, 2016 05:43
@nicolas-grekas
Copy link
Member Author

nicolas-grekas commented Aug 24, 2016

The patch uses the term “Unicode” in variable names etc., but wouldn't it be more correct to use “UTF-8”

I hesitated on this, and choose Unicode to match what the PCRE doc uses. On the web, Unicode is synonym for UTF-8. Nobody uses any other Unicode encoding there. And the PCRE doc says about "unicode properties", the "unicode" flag, etc. I thought it'd be better to make the vocabularies match.

Unicode character properties escape sequences, \p{xx} etc., works even in non-UTF-8-mode.

Yes, yet it's undocumented (in the PHP doc at least, where on the contrary it's specifically documented under the "Unicode properties" chapter), and thus nobody knows what it does, esp. when considering high-ASCII chars.

this flag [...] adds the u modifier – nothing else.

Yes when given true. But when given false, it doesn't "remove" the u modifier. Instead, it then relies on detecting if a Unicode char or prop to also add the u modifier.
Thus this flag "enforces" the u modifier, whereas setting this flag to false "turns on" auto-detection.

@c960657
Copy link
Contributor

c960657 commented Aug 24, 2016

I hesitated on this, and choose Unicode to match what the PCRE doc uses

In most places, the PCRE man pages refer to "UTF-8 mode" or "UTF mode" (the library also supports UTF-16 and UTF-32).

Yes, yet it's undocumented (in the PHP doc at least, where on the contrary it's specifically documented under the "Unicode properties" chapter), and thus nobody knows what it does, esp. when considering high-ASCII chars.

On the man page it says:
"When in 8-bit non-UTF-8 mode, these sequences are of course limited to testing characters whose codepoints are less than 256, but they do work in this mode."

@nicolas-grekas
Copy link
Member Author

I never read the man page of pcre but I read many times the one on php.net. I guess I'm more like the usual PHP user on this one :)

@c960657
Copy link
Contributor

c960657 commented Aug 25, 2016

I never read the man page of pcre but I read many times the one on php.net.

I'm not sure which parts of the PHP manual you are referring to. I couldn't find any occurrences of the phrase "Unicode mode" outside user comments. "UTF-8 mode" occurs a few places, e.g. in the description of PREG_BAD_UTF8_OFFSET_ERROR.

Unicode mode:
https://www.google.dk/search?q=%22unicode+mode%22+site%3Aphp.net+inurl%3Amanual

UTF-8 mode:
https://www.google.dk/search?q=%22utf-8+mode%22+site%3Aphp.net+inurl%3Amanual

Additionally, the work "Unicode" is used in connection with character classes, but as mentioned above they work with and without UTF-8 mode.

@nicolas-grekas
Copy link
Member Author

@c960657 thanks for you input. I've just renamed unicode to utf8 everywhere.

I've also added a deprecation targeting the current magic: I propose to encourage people to explicitly enable the utf8 flag; throw a deprecation when they don't, and throw a LogicException in 4.0.

@c960657
Copy link
Contributor

c960657 commented Aug 25, 2016

Sounds like a good solution :)

$this->utf8 = $utf8;
}

public function isUtf8()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need a new property on the route ? I suggested using options, which are already meant to provide hints to the route compiler

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would complicate the transition: options are validated and you can't add an unsupported key (no forward compat). This means it wouldn't be possible to write routes in a forward/backward compatible manner in Yml/Xml/Annotations.

@nicolas-grekas nicolas-grekas force-pushed the utf8-routes-ter branch 2 times, most recently from 21c3992 to 70c40f0 Compare August 25, 2016 09:02
@@ -70,6 +70,7 @@ class Route implements \Serializable
* Available options:
*
* * compiler_class: A class name able to compile this route instance (RouteCompiler by default)
* * utf8: Whether UTF-8 matching is enforced ot not
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utf8 is now an option on the route. ping @stof (I don't know were I've got this idea that it wasn't possible...)

@stof
Copy link
Member

stof commented Aug 25, 2016

👍

@fabpot
Copy link
Member

fabpot commented Aug 25, 2016

Thank you @nicolas-grekas.

@fabpot fabpot merged commit a829d34 into symfony:master Aug 25, 2016
fabpot added a commit that referenced this pull request Aug 25, 2016
…s (nicolas-grekas)

This PR was merged into the 3.2-dev branch.

Discussion
----------

[Routing] Add seamless support for unicode requirements

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | yes
| Tests pass?   | yes
| Fixed tickets | #3629, #5236, #19562
| License       | MIT
| Doc PR        | symfony/symfony-docs#6890

This PR adds unicode support to route matching and generation by automatically adding the `u` modifier to regexps that use either unicode characters or unicode enabled character classes (e.g. `\p...` `\x{...}` `\X`).

As a side note, if one wants to match a single unicode character (vs a single byte), one should use `\PM` or `\X` instead of `.` *or* set the `unicode` parameter to true.

Commits
-------

a829d34 [Routing] Add seamless support for unicode requirements
@nicolas-grekas nicolas-grekas deleted the utf8-routes-ter branch September 1, 2016 07:52
xabbuh added a commit to symfony/symfony-docs that referenced this pull request Sep 21, 2016
…rekas)

This PR was merged into the master branch.

Discussion
----------

[Routing] Add doc about unicode requirements

Ref. symfony/symfony#19604

Commits
-------

75ed392 Add doc about unicode requirements
@fabpot fabpot mentioned this pull request Oct 27, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants