Skip to content

Commit e101c99

Browse files
committed
Implement formatters for common units
Includes formatters that perform unit promotion. We DO NOT do unit conversion (for example, inches to centimeters), we only choose within the appropriate system (metric or imperial) the best representation for a given value. For example, saying `10cm` is much better than saying `0.1m`. Both are still within the metric system. Considerations made during this change: - We only use unit symbols, not the full names. This simplifies aspects such as translations, since most units are universal but their localized full names are not. There is some space for later accepting full names (new MetricFormatter('meter')) and deciding the output representation automatically. This is also the reason why PHP enums were not used as the source of truth for supported units. - Time unit symbols are *not universal*, but they were considered for inclusion for their value in technical systems (simplifying a log entry or measure). - The SI (colloquially known as "metric system") is the widest adopted standard, so only "Imperial" was prefixed. We used the "Metric" name to denote SI Length units.
1 parent abb8a25 commit e101c99

25 files changed

Lines changed: 1256 additions & 10 deletions

README.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,20 @@ echo f::create()
3737

3838
## Formatters
3939

40-
| Formatter | Description |
41-
| ---------------------------------------------------- | --------------------------------------------------- |
42-
| [DateFormatter](docs/DateFormatter.md) | Date and time formatting with flexible parsing |
43-
| [MaskFormatter](docs/MaskFormatter.md) | Range-based string masking with Unicode support |
44-
| [NumberFormatter](docs/NumberFormatter.md) | Number formatting with thousands and decimal separators |
45-
| [PatternFormatter](docs/PatternFormatter.md) | Pattern-based string filtering with placeholders |
46-
| [PlaceholderFormatter](docs/PlaceholderFormatter.md) | Template interpolation with placeholder replacement |
40+
| Formatter | Description |
41+
| ---------------------------------------------------------- | ---------------------------------------------------------------- |
42+
| [AreaFormatter](docs/AreaFormatter.md) | Metric area promotion (mm², cm², m², a, ha, km²) |
43+
| [DateFormatter](docs/DateFormatter.md) | Date and time formatting with flexible parsing |
44+
| [ImperialAreaFormatter](docs/ImperialAreaFormatter.md) | Imperial area promotion (in², ft², yd², ac, mi²) |
45+
| [ImperialLengthFormatter](docs/ImperialLengthFormatter.md) | Imperial length promotion (in, ft, yd, mi) |
46+
| [ImperialMassFormatter](docs/ImperialMassFormatter.md) | Imperial mass promotion (oz, lb, st, ton) |
47+
| [MaskFormatter](docs/MaskFormatter.md) | Range-based string masking with Unicode support |
48+
| [MassFormatter](docs/MassFormatter.md) | Metric mass promotion (mg, g, kg, t) |
49+
| [MetricFormatter](docs/MetricFormatter.md) | Metric length promotion (mm, cm, m, km) |
50+
| [NumberFormatter](docs/NumberFormatter.md) | Number formatting with thousands and decimal separators |
51+
| [PatternFormatter](docs/PatternFormatter.md) | Pattern-based string filtering with placeholders |
52+
| [PlaceholderFormatter](docs/PlaceholderFormatter.md) | Template interpolation with placeholder replacement |
53+
| [TimeFormatter](docs/TimeFormatter.md) | Time promotion (mil, c, dec, y, mo, w, d, h, min, s, ms, us, ns) |
4754

4855
## Contributing
4956

docs/AreaFormatter.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
-->
6+
7+
# AreaFormatter
8+
9+
The `AreaFormatter` promotes metric area values between `mm²`, `cm²`, ``, `a`, `ha`, and `km²`.
10+
11+
- Non-numeric input is returned unchanged.
12+
- Promotion is based on magnitude.
13+
- Output uses symbols only (no spaces), e.g. `1m²`, `2ha`.
14+
15+
## Usage
16+
17+
```php
18+
use Respect\StringFormatter\AreaFormatter;
19+
20+
$formatter = new AreaFormatter('m^2');
21+
22+
echo $formatter->format('10000');
23+
// Outputs: 1ha
24+
```
25+
26+
## API
27+
28+
### `AreaFormatter::__construct`
29+
30+
- `__construct(string $unit)`
31+
32+
The `$unit` is the input unit (the unit you are providing values in).
33+
34+
Accepted units: `mm^2`, `cm^2`, `m^2`, `a`, `ha`, `km^2`.

docs/ImperialAreaFormatter.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
-->
6+
7+
# ImperialAreaFormatter
8+
9+
The `ImperialAreaFormatter` promotes imperial area values between `in²`, `ft²`, `yd²`, `ac`, and `mi²`.
10+
11+
- Non-numeric input is returned unchanged.
12+
- Promotion is based on magnitude.
13+
- Output uses symbols only (no spaces), e.g. `1ft²`, `2ac`.
14+
15+
## Usage
16+
17+
```php
18+
use Respect\StringFormatter\ImperialAreaFormatter;
19+
20+
$formatter = new ImperialAreaFormatter('ft^2');
21+
22+
echo $formatter->format('43560');
23+
// Outputs: 1ac
24+
```
25+
26+
## API
27+
28+
### `ImperialAreaFormatter::__construct`
29+
30+
- `__construct(string $unit)`
31+
32+
The `$unit` is the input unit (the unit you are providing values in).
33+
34+
Accepted units: `in^2`, `ft^2`, `yd^2`, `ac`, `mi^2`.

docs/ImperialLengthFormatter.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
-->
6+
7+
# ImperialLengthFormatter
8+
9+
The `ImperialLengthFormatter` promotes imperial length values between `in`, `ft`, `yd`, and `mi`.
10+
11+
- Non-numeric input is returned unchanged.
12+
- Promotion is based on magnitude.
13+
- Output uses symbols only (no spaces), e.g. `1ft`, `2yd`.
14+
15+
## Usage
16+
17+
```php
18+
use Respect\StringFormatter\ImperialLengthFormatter;
19+
20+
$formatter = new ImperialLengthFormatter('in');
21+
22+
echo $formatter->format('12');
23+
// Outputs: 1ft
24+
```
25+
26+
## API
27+
28+
### `ImperialLengthFormatter::__construct`
29+
30+
- `__construct(string $unit)`
31+
32+
The `$unit` is the input unit (the unit you are providing values in).
33+
34+
Accepted units: `in`, `ft`, `yd`, `mi`.

docs/ImperialMassFormatter.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
-->
6+
7+
# ImperialMassFormatter
8+
9+
The `ImperialMassFormatter` promotes imperial mass values between `oz`, `lb`, `st`, and `ton`.
10+
11+
- Non-numeric input is returned unchanged.
12+
- Promotion is based on magnitude.
13+
- Output uses symbols only (no spaces), e.g. `1lb`, `8oz`.
14+
15+
## Usage
16+
17+
```php
18+
use Respect\StringFormatter\ImperialMassFormatter;
19+
20+
$formatter = new ImperialMassFormatter('oz');
21+
22+
echo $formatter->format('16');
23+
// Outputs: 1lb
24+
```
25+
26+
## API
27+
28+
### `ImperialMassFormatter::__construct`
29+
30+
- `__construct(string $unit)`
31+
32+
The `$unit` is the input unit (the unit you are providing values in).
33+
34+
Accepted units: `oz`, `lb`, `st`, `ton`.
35+
36+
## Notes
37+
38+
- `ton` represents the imperial long ton (`2240lb`).

docs/MassFormatter.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
-->
6+
7+
# MassFormatter
8+
9+
The `MassFormatter` promotes metric *mass* values between `mg`, `g`, `kg`, and `t`.
10+
11+
- Non-numeric input is returned unchanged.
12+
- Promotion is based on magnitude.
13+
- Output uses symbols only (no spaces), e.g. `1kg`, `500mg`.
14+
15+
## Usage
16+
17+
```php
18+
use Respect\StringFormatter\MassFormatter;
19+
20+
$formatter = new MassFormatter('g');
21+
22+
echo $formatter->format('1000');
23+
// Outputs: 1kg
24+
25+
echo $formatter->format('0.001');
26+
// Outputs: 1mg
27+
```
28+
29+
## API
30+
31+
### `MassFormatter::__construct`
32+
33+
- `__construct(string $unit)`
34+
35+
The `$unit` is the input unit (the unit you are providing values in).
36+
37+
Accepted units: `mg`, `g`, `kg`, `t`.
38+
39+
### `format`
40+
41+
- `format(string $input): string`
42+
43+
If the input is numeric, it is promoted to the closest appropriate metric scale and returned with the corresponding symbol.

docs/MetricFormatter.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
-->
6+
7+
# MetricFormatter
8+
9+
The `MetricFormatter` promotes metric *length* values between `mm`, `cm`, `m`, and `km`.
10+
11+
- Non-numeric input is returned unchanged.
12+
- Promotion is based on magnitude.
13+
- Output uses symbols only (no spaces), e.g. `1km`, `10cm`.
14+
15+
## Usage
16+
17+
```php
18+
use Respect\StringFormatter\MetricFormatter;
19+
20+
$formatter = new MetricFormatter('m');
21+
22+
echo $formatter->format('1000');
23+
// Outputs: 1km
24+
25+
echo $formatter->format('0.1');
26+
// Outputs: 10cm
27+
```
28+
29+
## API
30+
31+
### `MetricFormatter::__construct`
32+
33+
- `__construct(string $unit)`
34+
35+
The `$unit` is the input unit (the unit you are providing values in).
36+
37+
Accepted units: `mm`, `cm`, `m`, `km`.
38+
39+
### `format`
40+
41+
- `format(string $input): string`
42+
43+
If the input is numeric, it is promoted to the closest appropriate metric scale and returned with the corresponding symbol.
44+
45+
## Behavior
46+
47+
### Promotion rule
48+
49+
The formatter chooses a unit where the promoted value is in the range $[1, 1000)$ when possible. If not possible, it uses the smallest (`mm`) or largest (`km`) unit as needed.
50+
51+
### No rounding
52+
53+
Values are not rounded. Trailing fractional zeros are trimmed:
54+
55+
```php
56+
$formatter = new MetricFormatter('m');
57+
58+
echo $formatter->format('1.23000');
59+
// Outputs: 1.23m
60+
```

docs/TimeFormatter.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!--
2+
SPDX-FileCopyrightText: (c) Respect Project Contributors
3+
SPDX-License-Identifier: ISC
4+
SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
5+
-->
6+
7+
# TimeFormatter
8+
9+
The `TimeFormatter` promotes time values between multiple units.
10+
11+
- Non-numeric input is returned unchanged.
12+
- Promotion is based on magnitude.
13+
- Output uses symbols only (no spaces), e.g. `1h`, `500ms`.
14+
15+
## Usage
16+
17+
```php
18+
use Respect\StringFormatter\TimeFormatter;
19+
20+
$formatter = new TimeFormatter('s');
21+
22+
echo $formatter->format('60');
23+
// Outputs: 1min
24+
25+
echo $formatter->format('0.001');
26+
// Outputs: 1ms
27+
```
28+
29+
## API
30+
31+
### `TimeFormatter::__construct`
32+
33+
- `__construct(string $unit)`
34+
35+
The `$unit` is the input unit (the unit you are providing values in).
36+
37+
Accepted symbols:
38+
39+
- `ns`, `us`, `ms`, `s`, `min`, `h`, `d`, `w`, `mo`, `y`, `dec`, `c`, `mil`
40+
41+
## Notes on non-standard symbols
42+
43+
- `y` uses a fixed year of 365 days.
44+
- `mo` uses 1/12 of a fixed year (approx. 30.41 days).
45+
- `w` uses 7 days.
46+
- `dec`, `c`, and `mil` are based on that fixed year.

src/AreaFormatter.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
/*
4+
* SPDX-FileCopyrightText: (c) Respect Project Contributors
5+
* SPDX-License-Identifier: ISC
6+
* SPDX-FileContributor: Alexandre Gomes Gaigalas <alganet@gmail.com>
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace Respect\StringFormatter;
12+
13+
use Respect\StringFormatter\Internal\UnitPromoter;
14+
15+
final readonly class AreaFormatter implements Formatter
16+
{
17+
use UnitPromoter;
18+
19+
private const array UNIT_RATIOS = [
20+
'km^2' => [1_000_000, 1],
21+
'ha' => [10_000, 1],
22+
'a' => [100, 1],
23+
'm^2' => [1, 1],
24+
'cm^2' => [1, 10_000],
25+
'mm^2' => [1, 1_000_000],
26+
];
27+
28+
private const array UNIT_ALIASES = [
29+
'km^2' => 'km²',
30+
'ha' => 'ha',
31+
'a' => 'a',
32+
'm^2' => '',
33+
'cm^2' => 'cm²',
34+
'mm^2' => 'mm²',
35+
];
36+
37+
public function __construct(string $unit)
38+
{
39+
if (!isset(self::UNIT_RATIOS[$unit])) {
40+
throw new InvalidFormatterException('Unsupported area unit');
41+
}
42+
43+
$this->unit = $unit;
44+
}
45+
}

0 commit comments

Comments
 (0)