-
Notifications
You must be signed in to change notification settings - Fork 7.8k
Proposal: add IntlDatePatternGenerator #6771
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
Conversation
--SKIPIF-- | ||
<?php | ||
if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> | ||
<?php if (version_compare(INTL_ICU_VERSION, '50.1.2', '<')) die('skip for ICU < 50.1.2'); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As an aside:
This line is included in almost all intl dateformat tests - there, it is used in this form:
<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') >= 0) die('skip for ICU < 50.1.2'); ?>
This, however, ends up skipping the test on my machine (INTL_ICU_VERSION === '67.1'
). I assume version_compare
is used incorrectly here? Should this be fixed for all other tests as well (and switched to what is the more readable form in my opinion)? Is this line still necessary (as PHP 7.4 requires ICU 50.1)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skip messages are a bit deceptive: if fact, die('skip for ICU < 50.1.2');
means that the test is skip
ped because it's for ICU < 50.1.2
. It also took quite some time for me to get accustomed to this.
If ICU 50.1 is required now then these tests are probably not in use anymore 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I don't think the version_compare
is necessary for this test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, that explains the message.
I still dont understand why these tests are not required for newer ICU versions - I would expect basic tests like
<?php if (version_compare(INTL_ICU_VERSION, '50.1.2') >= 0) die('skip for ICU < 50.1.2'); ?> |
to still be in use for newer versions/after PHP 7.4.
(For an incomplete list of affected tests, this is the commit that starts skipping a bunch of them for ICU >= 51.2 (was later changed to 50.1): 4840b0a)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tests for other ICU versions are under _variant2, _variant3 etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If ICU 50.1 is required now then these tests are probably not in use anymore thinking
I believe the reason why these tests still exist is that our requirement is ICU 50.1, while this checks for ICU 50.1.2.
Though maybe we can bump up the requirements slightly, it looks like RHEL 7 nowadays ships with ICU 50.2 (according to https://rpms.remirepo.net/rpmphp/zoom.php?rpm=icu). @remicollet Would a 50.2 requirement be safe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same RHEL version comes with PHP 5: https://rpms.remirepo.net/rpmphp/zoom.php?rpm=php so is RHEL even supposed to dictate what PHP 8 should do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I'd look forward to an IntlDateTimePatternGenerator
class, even though it would expose only a small subset of all the ICU capabilities. IMO the static method on IntlDateFormatter
doesn't really fit there.
--SKIPIF-- | ||
<?php | ||
if (!extension_loaded('intl')) die('skip intl extension not enabled'); ?> | ||
<?php if (version_compare(INTL_ICU_VERSION, '50.1.2', '<')) die('skip for ICU < 50.1.2'); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And I don't think the version_compare
is necessary for this test.
d3bb97c
to
2eaed3c
Compare
@kocsismate Thank you for the review! |
DTPATTERNGEN_METHOD_FETCH_OBJECT_NO_CHECK; | ||
|
||
if (dtpgo->dtpg != NULL) { | ||
intl_errors_set(DTPATTERNGEN_ERROR_P(dtpgo), U_ILLEGAL_ARGUMENT_ERROR, "datetimepatterngenerator_create: cannot call constructor twice", 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason of this? I see that the check is from IntlDateFormatter
, but I don't know why these are necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For IntlDateFormatter
, there is this test:
php-src/ext/intl/tests/bug62081.phpt
Lines 12 to 13 in 8d743d5
$x = new IntlDateFormatter('en', 1, 1); | |
var_dump($x->__construct('en', 1, 1)); |
How is that (anti-)pattern handled in other cases?
|
||
intl_stringFromChar(skeleton_uncleaned, skeleton_str, skeleton_len, DTPATTERNGEN_ERROR_CODE_P(dtpgo)); | ||
|
||
INTL_METHOD_CHECK_STATUS(dtpgo, "datetimepatterngenerator_get_best_pattern: skeleton was not a valid UTF-8 string"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
INTL_METHOD_CHECK_STATUS(dtpgo, "datetimepatterngenerator_get_best_pattern: skeleton was not a valid UTF-8 string"); | |
INTL_METHOD_CHECK_STATUS(dtpgo, "Skeleton is not a valid UTF-8 string"); |
So the function name shouldn't be added (it's superfluous), the message should start with a capital character, and AFAIR we don't use past tense
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I'm wondering if we can consider an invalid UTF-8 skeleton a programmatic error? As far as I see, we can, because the input should come from the programmer, not from the user. If my hypothesis is ok, then we could throw an exception instead of these warnings, like this:
zend_argument_value_error(2, "must be a valid UTF-8 string")
(the 2
should be adapted in case of the method counterpart, since $skeleton
is the 1st parameter there).
Last year, we migrated hundreds of warnings to exceptions, so it would be nice to follow this practice here as well, if possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have adjusted the messages.
I would agree that this is most likely a programming error - I used a warning here for consistency with similar cases (eg. IntlDateFormatter::setPattern()
), but I can switch to using an exception if preferred.
cb8c1c5
to
5084f32
Compare
5084f32
to
068cceb
Compare
05f307a
to
5b74562
Compare
Rebased and removed the legacy procedural API. |
Looks like this needs a rebase. While at it, it would be great to add a mention of the new class in the UPGRADING file. |
Co-authored-by: Nikita Popov <[email protected]>
7e50a00
to
19ffd97
Compare
Rebased. |
Just checking - am I reading things correctly when I say that the two procedural functions mentioned in the RFC were not implemented in the final merged implementation ? |
@jrfnl, ugh, apparently not. I wonder whether that has been explicitly discussed, or was just forgotten. |
@jrfnl That was discussed and dropped during the vote, see: https://externals.io/message/114473#114658 and the messages following it. |
Thanks @cmb69 and @deltragon for your quick responses. Just wanted to verify as I'm working on adding detecting for the PHP 8.1 features/deprecations to PHPCompatibility. |
Note: in contrast to the original RFC proposal, [the procedural functions mentioned in the RFC were not included in the final implementation](php/php-src#6771 (comment)). Includes unit test. Refs: * https://www.php.net/manual/en/migration81.new-classes.php#migration81.new-classes.intl * https://wiki.php.net/rfc/intldatetimepatterngenerator * php/php-src#6771 * php/php-src@ae9f6e7
ICU exposes the DateTimePatternGenerator class that allows generating a formatting pattern from a given "skeleton" for a given locale.
This allows more flexibility than the current
$format
argument ofIntlDateFormatter::format()
, which takes either a few pre-defined constants or a hard-coded format.This functionality also has been requested in the past.
For example, if an application requires a format that will always use the long version of a year, but the short version of a month (eg. "MM/dd/YYYY" for "en_US", or "dd.MM.YYYY" for "de_DE"), one is out of luck.
IntlDateFormatter::SHORT
will use the short year for "de_DE" ("dd.MM.YY"), andIntlDateFormatter::MEDIUM
will use the long version of a month for "en_US" ("MMM dd, YYYY").With
IntlDatePatternGenerator::getBestPattern()
, a skeleton can be provided to generate the appropriate pattern for a given locale.In the use-case given above, the skeleton "YYYYMMdd" will generate the desired patterns for each locale, which can then be passed to
IntlDateFormatter::format()
.Note about the implementation:
This had been previously implemented as a static method on
IntlDateFormatter
, creating a newDateTimePatternGenerator
on every call.It is now a separate class that has to be instantiated before use. This leaves us open to implementing other methods/options provided by the underlying
DateTimePatternGenerator
. I am not sure if there is any use in those, however, asgetBestPattern()
seems to be the only one with a real-world use-case to me.(Note: I am aware that this needs to go to internals/an RFC first. I'm currently having issues with my email provider that prevent me from subscribing to/sending to the list. I will send this proposal once these are resolved).