Skip to content

Commit fa352fc

Browse files
committed
[Intl] Add PHP 8.5 IntlListFormatter as installable polyfill
Adds a new polyfill component `symfony/polyfill-intl-listformatter` that provides the functionality of the new `IntlListFormatter` to PHP 7.2 and later. It is a new composer-installable package (similar to how Intl `MessageFormatter` is a separate installable package, and the PHP 8.5 polyfill can require it as a dependency too. It only supports `en` locale. However, PHP applications that require this package can extend the namespaced polyfill (`\Symfony\Polyfill\Intl\ListFormatter\IntlListFormatter`) and add their own list patterns by extending the static variable (`\Symfony\Polyfill\Intl\ListFormatter\IntlListFormatter::$listPatterns`) that holds these patterns. - [ICU listPatterns](https://github.com/unicode-org/cldr-json/blob/main/cldr-json/cldr-misc-full/main/en/listPatterns.json) - [php-src commit](php/php-src@3f7545245) - [PHP.Watch: IntlListFormatter](https://php.watch/versions/8.5/IntlListFormatter) Closes GH-530.
1 parent 634ccdf commit fa352fc

File tree

8 files changed

+460
-0
lines changed

8 files changed

+460
-0
lines changed

composer.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"symfony/polyfill-intl-grapheme": "self.version",
3939
"symfony/polyfill-intl-icu": "self.version",
4040
"symfony/polyfill-intl-messageformatter": "self.version",
41+
"symfony/polyfill-intl-listformatter": "self.version",
4142
"symfony/polyfill-intl-idn": "self.version",
4243
"symfony/polyfill-intl-normalizer": "self.version",
4344
"symfony/polyfill-mbstring": "self.version",
@@ -62,6 +63,7 @@
6263
"classmap": [
6364
"src/Intl/Icu/Resources/stubs",
6465
"src/Intl/MessageFormatter/Resources/stubs",
66+
"src/Intl/ListFormatter/Resources/stubs",
6567
"src/Intl/Normalizer/Resources/stubs",
6668
"src/Php85/Resources/stubs",
6769
"src/Php84/Resources/stubs",
Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Polyfill\Intl\ListFormatter;
13+
14+
/**
15+
* A polyfill implementation of the IntlListFormatter class provided by the intl extension.
16+
*
17+
* @author Ayesh Karunaratne <[email protected]>
18+
*
19+
* @internal
20+
*/
21+
class IntlListFormatter
22+
{
23+
public const TYPE_AND = 0;
24+
public const TYPE_OR = 1;
25+
public const TYPE_UNITS = 3;
26+
27+
public const WIDTH_WIDE = 0;
28+
public const WIDTH_SHORT = 1;
29+
public const WIDTH_NARROW = 2;
30+
31+
/**
32+
* @var string
33+
*/
34+
private $locale;
35+
/**
36+
* @var int
37+
*/
38+
private $type;
39+
/**
40+
* @var int
41+
*/
42+
private $width;
43+
44+
protected static $listPatterns = [
45+
'en' => [
46+
'listPattern-type-standard' => [
47+
'start' => '{0}, {1}',
48+
'middle' => '{0}, {1}',
49+
'end' => '{0}, and {1}',
50+
2 => '{0} and {1}',
51+
],
52+
'listPattern-type-or' => [
53+
'start' => '{0}, {1}',
54+
'middle' => '{0}, {1}',
55+
'end' => '{0}, or {1}',
56+
2 => '{0} or {1}',
57+
],
58+
'listPattern-type-or-narrow' => [
59+
'start' => '{0}, {1}',
60+
'middle' => '{0}, {1}',
61+
'end' => '{0}, or {1}',
62+
2 => '{0} or {1}',
63+
],
64+
'listPattern-type-or-short' => [
65+
'start' => '{0}, {1}',
66+
'middle' => '{0}, {1}',
67+
'end' => '{0}, or {1}',
68+
2 => '{0} or {1}',
69+
],
70+
'listPattern-type-standard-narrow' => [
71+
'start' => '{0}, {1}',
72+
'middle' => '{0}, {1}',
73+
'end' => '{0}, {1}',
74+
2 => '{0}, {1}',
75+
],
76+
'listPattern-type-standard-short' => [
77+
'start' => '{0}, {1}',
78+
'middle' => '{0}, {1}',
79+
'end' => '{0}, & {1}',
80+
2 => '{0} & {1}',
81+
],
82+
'listPattern-type-unit' => [
83+
'start' => '{0}, {1}',
84+
'middle' => '{0}, {1}',
85+
'end' => '{0}, {1}',
86+
2 => '{0}, {1}',
87+
],
88+
'listPattern-type-unit-narrow' => [
89+
'start' => '{0} {1}',
90+
'middle' => '{0} {1}',
91+
'end' => '{0} {1}',
92+
2 => '{0} {1}',
93+
],
94+
'listPattern-type-unit-short' => [
95+
'start' => '{0}, {1}',
96+
'middle' => '{0}, {1}',
97+
'end' => '{0}, {1}',
98+
2 => '{0}, {1}',
99+
],
100+
],
101+
];
102+
103+
public function __construct(
104+
string $locale,
105+
int $type = self::TYPE_AND,
106+
int $width = self::WIDTH_WIDE
107+
) {
108+
$exceptionClass = PHP_VERSION_ID >= 80000 ? \ValueError::class : \InvalidArgumentException::class;
109+
if ($locale !== 'en' && strpos($locale, 'en') !== 0) {
110+
throw new $exceptionClass('Invalid locale, only "en" and "en-*" locales are supported');
111+
}
112+
113+
if ($type !== self::TYPE_AND && $type !== self::TYPE_OR && $type !== self::TYPE_UNITS) {
114+
throw new $exceptionClass('Argument #2 ($type) must be one of IntlListFormatter::TYPE_AND, IntlListFormatter::TYPE_OR, or IntlListFormatter::TYPE_UNITS.');
115+
}
116+
117+
if ($width !== self::WIDTH_WIDE && $width !== self::WIDTH_SHORT && $width !== self::WIDTH_NARROW) {
118+
throw new $exceptionClass('Argument #3 ($width) must be one of IntlListFormatter::WIDTH_WIDE, IntlListFormatter::WIDTH_SHORT, or IntlListFormatter::WIDTH_NARROW.');
119+
}
120+
121+
122+
$this->locale = 'en';
123+
$this->type = $type;
124+
$this->width = $width;
125+
}
126+
127+
public function format(array $strings): string
128+
{
129+
$itemCount = count($strings);
130+
131+
if ($itemCount === 0) {
132+
return '';
133+
}
134+
135+
$strings = array_values($strings);
136+
137+
if ($itemCount === 1) {
138+
return (string) $strings[0];
139+
}
140+
141+
$pattern = $this->getListPattern();
142+
143+
switch ($this->type) {
144+
case self::TYPE_AND:
145+
$lookupKeyType = 'standard';
146+
break;
147+
case self::TYPE_OR:
148+
$lookupKeyType = 'or';
149+
break;
150+
case self::TYPE_UNITS:
151+
$lookupKeyType = 'unit';
152+
break;
153+
}
154+
155+
switch ($this->width) {
156+
case self::WIDTH_WIDE:
157+
$lookupKeyWidth = '';
158+
break;
159+
case self::WIDTH_SHORT:
160+
$lookupKeyWidth = '-short';
161+
break;
162+
case self::WIDTH_NARROW:
163+
$lookupKeyWidth = '-narrow';
164+
break;
165+
}
166+
167+
$pattern = $pattern['listPattern-type-' . $lookupKeyType . $lookupKeyWidth];
168+
169+
if ($itemCount === 2) {
170+
return strtr($pattern[2], ['{0}' => (string) $strings[0], '{1}' => (string) $strings[1]]);
171+
}
172+
173+
if ($itemCount === 3) {
174+
$start = strtr($pattern['start'], ['{0}' => (string) $strings[0], '{1}' => (string) $strings[1]]);
175+
return strtr($pattern['end'], ['{0}' => $start, '{1}' => (string) $strings[2]]);
176+
}
177+
178+
$result = strtr($pattern['start'], ['{0}' => (string) $strings[0], '{1}' => (string) $strings[1]]);
179+
180+
for ($i = 2; $i < $itemCount - 1; $i++) {
181+
$result = strtr($pattern["middle"], [
182+
"{0}" => $result,
183+
"{1}" => $strings[$i],
184+
]);
185+
}
186+
187+
return strtr($pattern["end"], [
188+
"{0}" => $result,
189+
"{1}" => $strings[$itemCount - 1],
190+
]);
191+
}
192+
193+
protected function getListPattern(): array {
194+
return self::$listPatterns[$this->locale];
195+
}
196+
197+
public function getErrorCode()
198+
{
199+
return 0;
200+
}
201+
202+
public function getErrorMessage()
203+
{
204+
return '';
205+
}
206+
}

src/Intl/ListFormatter/LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2018-present Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

src/Intl/ListFormatter/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Symfony Polyfill / Intl: ListFormatter
2+
=========================================
3+
4+
This component provides a fallback implementation for the
5+
[`ListFormatter`](https://php.net/ListFormatter) class provided
6+
by the [Intl](https://php.net/intl) extension.
7+
8+
More information can be found in the
9+
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
10+
11+
License
12+
=======
13+
14+
This library is released under the [MIT license](LICENSE).
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
class IntlException extends Exception
13+
{
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
final class IntlListFormatter extends Symfony\Polyfill\Intl\ListFormatter\IntlListFormatter
13+
{
14+
}

src/Intl/ListFormatter/composer.json

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "symfony/polyfill-intl-listformatter",
3+
"type": "library",
4+
"description": "Symfony polyfill for intl's ListFormatter class and related functions",
5+
"keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "listformatter"],
6+
"homepage": "https://symfony.com",
7+
"license": "MIT",
8+
"authors": [
9+
{
10+
"name": "Nicolas Grekas",
11+
"email": "[email protected]"
12+
},
13+
{
14+
"name": "Symfony Community",
15+
"homepage": "https://symfony.com/contributors"
16+
}
17+
],
18+
"require": {
19+
"php": ">=7.2"
20+
},
21+
"autoload": {
22+
"psr-4": { "Symfony\\Polyfill\\Intl\\ListFormatter\\": "" },
23+
"classmap": [ "Resources/stubs" ]
24+
},
25+
"suggest": {
26+
"ext-intl": "For best performance"
27+
},
28+
"minimum-stability": "dev",
29+
"extra": {
30+
"thanks": {
31+
"name": "symfony/polyfill",
32+
"url": "https://github.com/symfony/polyfill"
33+
}
34+
}
35+
}

0 commit comments

Comments
 (0)