From 9d0114d2dbb8d07a0e79dfc7bcda8dd7212c952f Mon Sep 17 00:00:00 2001 From: Olivier Laviale Date: Fri, 1 Jul 2022 18:21:51 +0200 Subject: [PATCH] Improve units fluent interface --- MIGRATION.md | 22 ++- README.md | 45 +++-- lib/Units.php | 274 +++++++++++++++-------------- lib/Units/NumberPerUnit.php | 91 ++++++++++ lib/Units/NumberWithUnit.php | 93 ++++++++++ lib/Units/Sequence.php | 2 + lib/Units/Unit.php | 10 +- tests/Units/NumberPerUnitTest.php | 76 ++++++++ tests/Units/NumberWithUnitTest.php | 113 ++++++++++++ tests/Units/UnitTest.php | 36 +--- tests/UnitsTest.php | 72 ++++---- 11 files changed, 605 insertions(+), 229 deletions(-) create mode 100644 lib/Units/NumberPerUnit.php create mode 100644 lib/Units/NumberWithUnit.php create mode 100644 tests/Units/NumberPerUnitTest.php create mode 100644 tests/Units/NumberWithUnitTest.php diff --git a/MIGRATION.md b/MIGRATION.md index 415c8d2..1b33334 100644 --- a/MIGRATION.md +++ b/MIGRATION.md @@ -62,9 +62,29 @@ None - Removed the `localized()` method on entities that don't naturally require access to the repository: `NumberFormatter` and `ListFormatter`. You can use - `$repository->locales['fr']->localize($formatter)` to get a localized formatter, the + `$repository->locales['fr']->localize($formatter)` to get a localized formatter, or the `number_formatter` and `list_formater` properties of the `Locale` object. +- The fluent interface of units is now more on par with the rest of the API. + + ```php + duration_hour(23); + echo $units->duration_hour(23, $units::LENGTH_SHORT); + echo $units->volume_liter->per_unit(12.345, $units->duration_hour); + echo $units->volume_liter->per_unit(12.345, $units->duration_hour, $units::LENGTH_SHORT); + ``` + + ```php + duration_hour(23); + echo $units->duration_hour(23)->as_short; + echo $units->volume_liter(12.345)->per($units->duration_hour); + echo $units->volume_liter(12.345)->per($units->duration_hour)->as_short; + ``` + ### Deprecated Features - The localized currency formatter no longer supports a `$symbols` parameter. If you need to diff --git a/README.md b/README.md index 2ff58d0..a97bad1 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ The __CLDR__ package provides means to internationalize your application by leveraging the data and conventions defined by the [Unicode Common Locale Data Repository](http://cldr.unicode.org/) (CLDR). -It provides many useful locale information and data (such as locale names for territories, +It offers many helpful locale information and data (such as locale names for territories, languages, days…) as well as formatters for numbers, currencies, dates and times, units, sequences, lists… @@ -62,19 +62,19 @@ echo $fr_datetime->as_short; // 04/11/2013 20:21 # You can format units $units = $repository->locales['en']->units; -$units->duration_hour->name; // hours -$units->duration_hour->short_name; // h -$units->duration_hour(1); // 1 hour -$units->duration_hour(23); // 23 hours -$units->duration_hour(23, $units::LENGTH_SHORT); // 23 hr -$units->duration_hour(23, $units::LENGTH_NARROW); // 23h +echo $units->duration_hour->name; // hours +echo $units->duration_hour->short_name; // h +echo $units->duration_hour(1); // 1 hour +echo $units->duration_hour(23); // 23 hours +echo $units->duration_hour(23)->as_short; // 23 hr +echo $units->duration_hour(23)->as_narrow; // 23h # You can format a unit per another unit -$units->volume_liter->per_unit(12.345, $units->duration_hour); +echo $units->volume_liter(12.345)->per($units->duration_hour); // 12.345 liters per hour -$units->volume_liter->per_unit(12.345, $units->duration_hour, $units::LENGTH_SHORT); +echo $units->volume_liter(12.345)->per($units->duration_hour)->as_short; // 12.345 L/h -$units->volume_liter->per_unit(12.345, $units->duration_hour, $units::LENGTH_NARROW); +echo $units->volume_liter(12.345)->per($units->duration_hour)->as_narrow; // 12.345L/h # You can format sequences of units @@ -694,15 +694,15 @@ units: /* @var $repository \ICanBoogie\CLDR\Repository */ $units = $repository->locales['en']->units; -$units->duration_hour->name; // hours -$units->duration_hour->short_name; // h -$units->duration_hour(1); // 1 hour -$units->duration_hour(23); // 23 hours -$units->duration_hour(23, $units::LENGTH_SHORT); // 23 hr -$units->duration_hour(23, $units::LENGTH_NARROW); // 23h +echo $units->duration_hour->name; // hours +echo $units->duration_hour->short_name; // h +echo $units->duration_hour(1); // 1 hour +echo $units->duration_hour(23); // 23 hours +echo $units->duration_hour(23)->as_short; // 23 hr +echo $units->duration_hour(23)->as_narrow; // 23h ``` -[Many units are available](http://unicode.org/reports/tr35/tr35-general.html#Unit_Elements). +[Many units are available](https://www.unicode.org/reports/tr35/tr35-57/tr35-general.html#Unit_Elements). ### Per unit @@ -716,14 +716,13 @@ be used in preference. /* @var $repository \ICanBoogie\CLDR\Repository */ $units = $repository->locales['en']->units; -$units->volume_liter->per_unit(12.345, $units->duration_hour); -// 12.345 liters per hour -$units->volume_liter->per_unit(12.345, $units->duration_hour, $units::LENGTH_SHORT); -// 12.345 Lph -$units->volume_liter->per_unit(12.345, $units->duration_hour, $units::LENGTH_NARROW); -// 12.345l/h +echo $units->volume_liter(12.345)->per($units->duration_hour); // 12.345 liters per hour +echo $units->volume_liter(12.345)->per($units->duration_hour)->as_short; // 12.345 Lph +echo $units->volume_liter(12.345)->per($units->duration_hour)->as_narrow; // 12.345l/h ``` + + ### Units in composed sequence Units may be used in composed sequences, such as **5° 30m** for 5 degrees 30 minutes, or **3 ft, 2 diff --git a/lib/Units.php b/lib/Units.php index f824813..268d3b8 100644 --- a/lib/Units.php +++ b/lib/Units.php @@ -13,11 +13,12 @@ use BadMethodCallException; use ICanBoogie\Accessor\AccessorTrait; +use ICanBoogie\CLDR\Units\NumberWithUnit; use ICanBoogie\CLDR\Units\Sequence; use ICanBoogie\CLDR\Units\Unit; use LogicException; -use function array_shift; +use function count; use function is_string; use function str_replace; use function strtr; @@ -147,127 +148,127 @@ * @property-read Unit $volume_tablespoon * @property-read Unit $volume_teaspoon * - * @method string acceleration_g_force(mixed $number, string $length = "long") - * @method string acceleration_meter_per_second_squared(mixed $number, string $length = "long") - * @method string angle_arc_minute(mixed $number, string $length = "long") - * @method string angle_arc_second(mixed $number, string $length = "long") - * @method string angle_degree(mixed $number, string $length = "long") - * @method string angle_radian(mixed $number, string $length = "long") - * @method string area_acre(mixed $number, string $length = "long") - * @method string area_hectare(mixed $number, string $length = "long") - * @method string area_square_centimeter(mixed $number, string $length = "long") - * @method string area_square_foot(mixed $number, string $length = "long") - * @method string area_square_inch(mixed $number, string $length = "long") - * @method string area_square_kilometer(mixed $number, string $length = "long") - * @method string area_square_meter(mixed $number, string $length = "long") - * @method string area_square_mile(mixed $number, string $length = "long") - * @method string area_square_yard(mixed $number, string $length = "long") - * @method string consumption_liter_per_kilometer(mixed $number, string $length = "long") - * @method string consumption_mile_per_gallon(mixed $number, string $length = "long") - * @method string digital_bit(mixed $number, string $length = "long") - * @method string digital_byte(mixed $number, string $length = "long") - * @method string digital_gigabit(mixed $number, string $length = "long") - * @method string digital_gigabyte(mixed $number, string $length = "long") - * @method string digital_kilobit(mixed $number, string $length = "long") - * @method string digital_kilobyte(mixed $number, string $length = "long") - * @method string digital_megabit(mixed $number, string $length = "long") - * @method string digital_megabyte(mixed $number, string $length = "long") - * @method string digital_terabit(mixed $number, string $length = "long") - * @method string digital_terabyte(mixed $number, string $length = "long") - * @method string duration_day(mixed $number, string $length = "long") - * @method string duration_hour(mixed $number, string $length = "long") - * @method string duration_microsecond(mixed $number, string $length = "long") - * @method string duration_millisecond(mixed $number, string $length = "long") - * @method string duration_minute(mixed $number, string $length = "long") - * @method string duration_month(mixed $number, string $length = "long") - * @method string duration_nanosecond(mixed $number, string $length = "long") - * @method string duration_second(mixed $number, string $length = "long") - * @method string duration_week(mixed $number, string $length = "long") - * @method string duration_year(mixed $number, string $length = "long") - * @method string electric_ampere(mixed $number, string $length = "long") - * @method string electric_milliampere(mixed $number, string $length = "long") - * @method string electric_ohm(mixed $number, string $length = "long") - * @method string electric_volt(mixed $number, string $length = "long") - * @method string energy_calorie(mixed $number, string $length = "long") - * @method string energy_foodcalorie(mixed $number, string $length = "long") - * @method string energy_joule(mixed $number, string $length = "long") - * @method string energy_kilocalorie(mixed $number, string $length = "long") - * @method string energy_kilojoule(mixed $number, string $length = "long") - * @method string energy_kilowatt_hour(mixed $number, string $length = "long") - * @method string frequency_gigahertz(mixed $number, string $length = "long") - * @method string frequency_hertz(mixed $number, string $length = "long") - * @method string frequency_kilohertz(mixed $number, string $length = "long") - * @method string frequency_megahertz(mixed $number, string $length = "long") - * @method string length_astronomical_unit(mixed $number, string $length = "long") - * @method string length_centimeter(mixed $number, string $length = "long") - * @method string length_decimeter(mixed $number, string $length = "long") - * @method string length_fathom(mixed $number, string $length = "long") - * @method string length_foot(mixed $number, string $length = "long") - * @method string length_furlong(mixed $number, string $length = "long") - * @method string length_inch(mixed $number, string $length = "long") - * @method string length_kilometer(mixed $number, string $length = "long") - * @method string length_light_year(mixed $number, string $length = "long") - * @method string length_meter(mixed $number, string $length = "long") - * @method string length_micrometer(mixed $number, string $length = "long") - * @method string length_mile(mixed $number, string $length = "long") - * @method string length_millimeter(mixed $number, string $length = "long") - * @method string length_nanometer(mixed $number, string $length = "long") - * @method string length_nautical_mile(mixed $number, string $length = "long") - * @method string length_parsec(mixed $number, string $length = "long") - * @method string length_picometer(mixed $number, string $length = "long") - * @method string length_yard(mixed $number, string $length = "long") - * @method string light_lux(mixed $number, string $length = "long") - * @method string mass_carat(mixed $number, string $length = "long") - * @method string mass_gram(mixed $number, string $length = "long") - * @method string mass_kilogram(mixed $number, string $length = "long") - * @method string mass_metric_ton(mixed $number, string $length = "long") - * @method string mass_microgram(mixed $number, string $length = "long") - * @method string mass_milligram(mixed $number, string $length = "long") - * @method string mass_ounce(mixed $number, string $length = "long") - * @method string mass_ounce_troy(mixed $number, string $length = "long") - * @method string mass_pound(mixed $number, string $length = "long") - * @method string mass_stone(mixed $number, string $length = "long") - * @method string mass_ton(mixed $number, string $length = "long") - * @method string power_gigawatt(mixed $number, string $length = "long") - * @method string power_horsepower(mixed $number, string $length = "long") - * @method string power_kilowatt(mixed $number, string $length = "long") - * @method string power_megawatt(mixed $number, string $length = "long") - * @method string power_milliwatt(mixed $number, string $length = "long") - * @method string power_watt(mixed $number, string $length = "long") - * @method string pressure_hectopascal(mixed $number, string $length = "long") - * @method string pressure_inch_hg(mixed $number, string $length = "long") - * @method string pressure_millibar(mixed $number, string $length = "long") - * @method string pressure_millimeter_of_mercury(mixed $number, string $length = "long") - * @method string pressure_pound_per_square_inch(mixed $number, string $length = "long") - * @method string proportion_karat(mixed $number, string $length = "long") - * @method string speed_kilometer_per_hour(mixed $number, string $length = "long") - * @method string speed_meter_per_second(mixed $number, string $length = "long") - * @method string speed_mile_per_hour(mixed $number, string $length = "long") - * @method string temperature_celsius(mixed $number, string $length = "long") - * @method string temperature_fahrenheit(mixed $number, string $length = "long") - * @method string temperature_kelvin(mixed $number, string $length = "long") - * @method string volume_acre_foot(mixed $number, string $length = "long") - * @method string volume_bushel(mixed $number, string $length = "long") - * @method string volume_centiliter(mixed $number, string $length = "long") - * @method string volume_cubic_centimeter(mixed $number, string $length = "long") - * @method string volume_cubic_foot(mixed $number, string $length = "long") - * @method string volume_cubic_inch(mixed $number, string $length = "long") - * @method string volume_cubic_kilometer(mixed $number, string $length = "long") - * @method string volume_cubic_meter(mixed $number, string $length = "long") - * @method string volume_cubic_mile(mixed $number, string $length = "long") - * @method string volume_cubic_yard(mixed $number, string $length = "long") - * @method string volume_cup(mixed $number, string $length = "long") - * @method string volume_deciliter(mixed $number, string $length = "long") - * @method string volume_fluid_ounce(mixed $number, string $length = "long") - * @method string volume_gallon(mixed $number, string $length = "long") - * @method string volume_hectoliter(mixed $number, string $length = "long") - * @method string volume_liter(mixed $number, string $length = "long") - * @method string volume_megaliter(mixed $number, string $length = "long") - * @method string volume_milliliter(mixed $number, string $length = "long") - * @method string volume_pint(mixed $number, string $length = "long") - * @method string volume_quart(mixed $number, string $length = "long") - * @method string volume_tablespoon(mixed $number, string $length = "long") - * @method string volume_teaspoon(mixed $number, string $length = "long") + * @method NumberWithUnit acceleration_g_force(float|int $number) + * @method NumberWithUnit acceleration_meter_per_second_squared(float|int $number) + * @method NumberWithUnit angle_arc_minute(float|int $number) + * @method NumberWithUnit angle_arc_second(float|int $number) + * @method NumberWithUnit angle_degree(float|int $number) + * @method NumberWithUnit angle_radian(float|int $number) + * @method NumberWithUnit area_acre(float|int $number) + * @method NumberWithUnit area_hectare(float|int $number) + * @method NumberWithUnit area_square_centimeter(float|int $number) + * @method NumberWithUnit area_square_foot(float|int $number) + * @method NumberWithUnit area_square_inch(float|int $number) + * @method NumberWithUnit area_square_kilometer(float|int $number) + * @method NumberWithUnit area_square_meter(float|int $number) + * @method NumberWithUnit area_square_mile(float|int $number) + * @method NumberWithUnit area_square_yard(float|int $number) + * @method NumberWithUnit consumption_liter_per_kilometer(float|int $number) + * @method NumberWithUnit consumption_mile_per_gallon(float|int $number) + * @method NumberWithUnit digital_bit(float|int $number) + * @method NumberWithUnit digital_byte(float|int $number) + * @method NumberWithUnit digital_gigabit(float|int $number) + * @method NumberWithUnit digital_gigabyte(float|int $number) + * @method NumberWithUnit digital_kilobit(float|int $number) + * @method NumberWithUnit digital_kilobyte(float|int $number) + * @method NumberWithUnit digital_megabit(float|int $number) + * @method NumberWithUnit digital_megabyte(float|int $number) + * @method NumberWithUnit digital_terabit(float|int $number) + * @method NumberWithUnit digital_terabyte(float|int $number) + * @method NumberWithUnit duration_day(float|int $number) + * @method NumberWithUnit duration_hour(float|int $number) + * @method NumberWithUnit duration_microsecond(float|int $number) + * @method NumberWithUnit duration_millisecond(float|int $number) + * @method NumberWithUnit duration_minute(float|int $number) + * @method NumberWithUnit duration_month(float|int $number) + * @method NumberWithUnit duration_nanosecond(float|int $number) + * @method NumberWithUnit duration_second(float|int $number) + * @method NumberWithUnit duration_week(float|int $number) + * @method NumberWithUnit duration_year(float|int $number) + * @method NumberWithUnit electric_ampere(float|int $number) + * @method NumberWithUnit electric_milliampere(float|int $number) + * @method NumberWithUnit electric_ohm(float|int $number) + * @method NumberWithUnit electric_volt(float|int $number) + * @method NumberWithUnit energy_calorie(float|int $number) + * @method NumberWithUnit energy_foodcalorie(float|int $number) + * @method NumberWithUnit energy_joule(float|int $number) + * @method NumberWithUnit energy_kilocalorie(float|int $number) + * @method NumberWithUnit energy_kilojoule(float|int $number) + * @method NumberWithUnit energy_kilowatt_hour(float|int $number) + * @method NumberWithUnit frequency_gigahertz(float|int $number) + * @method NumberWithUnit frequency_hertz(float|int $number) + * @method NumberWithUnit frequency_kilohertz(float|int $number) + * @method NumberWithUnit frequency_megahertz(float|int $number) + * @method NumberWithUnit length_astronomical_unit(float|int $number) + * @method NumberWithUnit length_centimeter(float|int $number) + * @method NumberWithUnit length_decimeter(float|int $number) + * @method NumberWithUnit length_fathom(float|int $number) + * @method NumberWithUnit length_foot(float|int $number) + * @method NumberWithUnit length_furlong(float|int $number) + * @method NumberWithUnit length_inch(float|int $number) + * @method NumberWithUnit length_kilometer(float|int $number) + * @method NumberWithUnit length_light_year(float|int $number) + * @method NumberWithUnit length_meter(float|int $number) + * @method NumberWithUnit length_micrometer(float|int $number) + * @method NumberWithUnit length_mile(float|int $number) + * @method NumberWithUnit length_millimeter(float|int $number) + * @method NumberWithUnit length_nanometer(float|int $number) + * @method NumberWithUnit length_nautical_mile(float|int $number) + * @method NumberWithUnit length_parsec(float|int $number) + * @method NumberWithUnit length_picometer(float|int $number) + * @method NumberWithUnit length_yard(float|int $number) + * @method NumberWithUnit light_lux(float|int $number) + * @method NumberWithUnit mass_carat(float|int $number) + * @method NumberWithUnit mass_gram(float|int $number) + * @method NumberWithUnit mass_kilogram(float|int $number) + * @method NumberWithUnit mass_metric_ton(float|int $number) + * @method NumberWithUnit mass_microgram(float|int $number) + * @method NumberWithUnit mass_milligram(float|int $number) + * @method NumberWithUnit mass_ounce(float|int $number) + * @method NumberWithUnit mass_ounce_troy(float|int $number) + * @method NumberWithUnit mass_pound(float|int $number) + * @method NumberWithUnit mass_stone(float|int $number) + * @method NumberWithUnit mass_ton(float|int $number) + * @method NumberWithUnit power_gigawatt(float|int $number) + * @method NumberWithUnit power_horsepower(float|int $number) + * @method NumberWithUnit power_kilowatt(float|int $number) + * @method NumberWithUnit power_megawatt(float|int $number) + * @method NumberWithUnit power_milliwatt(float|int $number) + * @method NumberWithUnit power_watt(float|int $number) + * @method NumberWithUnit pressure_hectopascal(float|int $number) + * @method NumberWithUnit pressure_inch_hg(float|int $number) + * @method NumberWithUnit pressure_millibar(float|int $number) + * @method NumberWithUnit pressure_millimeter_of_mercury(float|int $number) + * @method NumberWithUnit pressure_pound_per_square_inch(float|int $number) + * @method NumberWithUnit proportion_karat(float|int $number) + * @method NumberWithUnit speed_kilometer_per_hour(float|int $number) + * @method NumberWithUnit speed_meter_per_second(float|int $number) + * @method NumberWithUnit speed_mile_per_hour(float|int $number) + * @method NumberWithUnit temperature_celsius(float|int $number) + * @method NumberWithUnit temperature_fahrenheit(float|int $number) + * @method NumberWithUnit temperature_kelvin(float|int $number) + * @method NumberWithUnit volume_acre_foot(float|int $number) + * @method NumberWithUnit volume_bushel(float|int $number) + * @method NumberWithUnit volume_centiliter(float|int $number) + * @method NumberWithUnit volume_cubic_centimeter(float|int $number) + * @method NumberWithUnit volume_cubic_foot(float|int $number) + * @method NumberWithUnit volume_cubic_inch(float|int $number) + * @method NumberWithUnit volume_cubic_kilometer(float|int $number) + * @method NumberWithUnit volume_cubic_meter(float|int $number) + * @method NumberWithUnit volume_cubic_mile(float|int $number) + * @method NumberWithUnit volume_cubic_yard(float|int $number) + * @method NumberWithUnit volume_cup(float|int $number) + * @method NumberWithUnit volume_deciliter(float|int $number) + * @method NumberWithUnit volume_fluid_ounce(float|int $number) + * @method NumberWithUnit volume_gallon(float|int $number) + * @method NumberWithUnit volume_hectoliter(float|int $number) + * @method NumberWithUnit volume_liter(float|int $number) + * @method NumberWithUnit volume_megaliter(float|int $number) + * @method NumberWithUnit volume_milliliter(float|int $number) + * @method NumberWithUnit volume_pint(float|int $number) + * @method NumberWithUnit volume_quart(float|int $number) + * @method NumberWithUnit volume_tablespoon(float|int $number) + * @method NumberWithUnit volume_teaspoon(float|int $number) */ class Units { @@ -323,18 +324,25 @@ public function __construct(Locale $locale) $this->data = $locale['units']; } - public function __call(string $name, array $arguments): string + public function __call(string $name, array $arguments): NumberWithUnit { $unit = strtr($name, '_', '-'); - if (isset($this->data[self::DEFAULT_LENGTH][$unit])) + if (empty($this->data[self::DEFAULT_LENGTH][$unit])) { - $number = array_shift($arguments); + throw new BadMethodCallException("No such unit: $unit"); + } + + $n = count($arguments); - return $this->format($number, $unit, ...$arguments); + if ($n !== 1) + { + throw new BadMethodCallException("$name() expects one argument, got $n"); } - throw new BadMethodCallException("Unit is not defined: $name."); + $number = $arguments[0]; + + return new NumberWithUnit($number + 0, $unit, $this); } /** @@ -357,7 +365,7 @@ public function __get(string $property) */ public function assert_is_unit(string $unit): void { - if (!isset($this->data[self::DEFAULT_LENGTH][$unit])) + if (empty($this->data[self::DEFAULT_LENGTH][$unit])) { throw new LogicException("No such unit: $unit"); } @@ -388,8 +396,12 @@ public function format($number, string $unit, string $length = self::DEFAULT_LEN * * @see http://unicode.org/reports/tr35/tr35-general.html#perUnitPatterns */ - public function format_combination($number, string $number_unit, string $per_unit, string $length = self::DEFAULT_LENGTH): string - { + public function format_combination( + $number, + string $number_unit, + string $per_unit, + string $length = self::DEFAULT_LENGTH + ): string { $formatted = $this->format($number, $number_unit, $length); $data = $this->data[$length][$per_unit]; @@ -477,7 +489,7 @@ private function count_for($number): string } /** - * @param int|float|string $number + * @param numeric $number */ private function ensure_number_if_formatted($number): string { diff --git a/lib/Units/NumberPerUnit.php b/lib/Units/NumberPerUnit.php new file mode 100644 index 0000000..04f7e0f --- /dev/null +++ b/lib/Units/NumberPerUnit.php @@ -0,0 +1,91 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR\Units; + +use ICanBoogie\Accessor\AccessorTrait; +use ICanBoogie\CLDR\Units; + +/** + * @internal + * + * @property-read string $as_long A long format of the number. + * @property-read string $as_short A short format of the number. + * @property-read string $as_narrow A narrow format of the number. + */ +final class NumberPerUnit +{ + /** + * @uses get_as_long + * @uses get_as_short + * @uses get_as_narrow + */ + use AccessorTrait; + + /** + * @var float|int + */ + private $number; + + /** + * @var string + */ + private $number_unit; + + /** + * @var string + */ + private $per_unit; + + /** + * @var Units + */ + private $units; + + /** + * @param float|int $number + */ + public function __construct($number, string $number_unit, string $per_unit, Units $units) + { + $this->number = $number; + $this->number_unit = $number_unit; + $this->per_unit = $per_unit; + $this->units = $units; + } + + public function __toString(): string + { + return $this->as(Units::DEFAULT_LENGTH); + } + + private function get_as_long(): string + { + return $this->as(Units::LENGTH_LONG); + } + + private function get_as_short(): string + { + return $this->as(Units::LENGTH_SHORT); + } + + private function get_as_narrow(): string + { + return $this->as(Units::LENGTH_NARROW); + } + + /** + * @param Units::LENGTH_* $length + */ + private function as(string $length): string + { + return $this->units->format_combination($this->number, $this->number_unit, $this->per_unit, $length); + } +} diff --git a/lib/Units/NumberWithUnit.php b/lib/Units/NumberWithUnit.php new file mode 100644 index 0000000..01b057b --- /dev/null +++ b/lib/Units/NumberWithUnit.php @@ -0,0 +1,93 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR\Units; + +use ICanBoogie\Accessor\AccessorTrait; +use ICanBoogie\CLDR\Units; + +/** + * @internal + * + * @property-read string $as_long A long format of the number. + * @property-read string $as_short A short format of the number. + * @property-read string $as_narrow A narrow format of the number. + */ +final class NumberWithUnit +{ + /** + * @uses get_as_long + * @uses get_as_short + * @uses get_as_narrow + */ + use AccessorTrait; + + /** + * @var float|int + */ + private $number; + + /** + * @var string + */ + private $unit; + + /** + * @var Units + */ + private $units; + + /** + * @param float|int $number + */ + public function __construct($number, string $unit, Units $units) + { + $this->number = $number; + $this->unit = $unit; + $this->units = $units; + } + + public function __toString(): string + { + return $this->as(Units::DEFAULT_LENGTH); + } + + /** + * @param string|Unit $unit + */ + public function per($unit): NumberPerUnit + { + return new NumberPerUnit($this->number, $this->unit, $unit, $this->units); + } + + private function get_as_long(): string + { + return $this->as(Units::LENGTH_LONG); + } + + private function get_as_short(): string + { + return $this->as(Units::LENGTH_SHORT); + } + + private function get_as_narrow(): string + { + return $this->as(Units::LENGTH_NARROW); + } + + /** + * @param Units::LENGTH_* $length + */ + private function as(string $length): string + { + return $this->units->format($this->number, $this->unit, $length); + } +} diff --git a/lib/Units/Sequence.php b/lib/Units/Sequence.php index a2caa84..db5fff1 100644 --- a/lib/Units/Sequence.php +++ b/lib/Units/Sequence.php @@ -17,6 +17,8 @@ /** * Representation of a unit/number sequence. * + * @internal + * * @property-read string $as_long Long string representation. * @property-read string $as_short Short string representation. * @property-read string $as_narrow Narrow string representation. diff --git a/lib/Units/Unit.php b/lib/Units/Unit.php index 8d78f91..d368eb5 100644 --- a/lib/Units/Unit.php +++ b/lib/Units/Unit.php @@ -74,15 +74,7 @@ public function __toString(): string } /** - * @param int|float $number - */ - public function per_unit($number, string $unit, string $length = Units::DEFAULT_LENGTH): string - { - return $this->units->format_combination($number, (string) $this, $unit, $length); - } - - /** - * @param string $length One of `Units::LENGTH_*`. + * @param Units::LENGTH_* $length */ private function name_for(string $length): string { diff --git a/tests/Units/NumberPerUnitTest.php b/tests/Units/NumberPerUnitTest.php new file mode 100644 index 0000000..a72dbea --- /dev/null +++ b/tests/Units/NumberPerUnitTest.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR\Units; + +use ICanBoogie\CLDR\LocaleCollection; +use ICanBoogie\CLDR\StringHelpers; +use ICanBoogie\CLDR\Units; +use PHPUnit\Framework\TestCase; + +use function ICanBoogie\CLDR\get_repository; + +final class NumberPerUnitTest extends TestCase +{ + use StringHelpers; + + /** + * @var LocaleCollection + */ + static private $locales; + + static public function setUpBeforeClass(): void + { + self::$locales = get_repository()->locales; + } + + public function test_to_string(): void + { + $stu = new NumberPerUnit(123.4504, 'digital-gigabyte', 'duration-hour', $this->units_for('fr')); + + $this->assertSame("123,45 gigaoctets par heure", (string) $stu); + } + + /** + * @dataProvider provide_test_cases + * + * @param float|int $number + */ + public function test_cases(string $locale, $number, string $number_unit, string $per_unit, string $length, string $expected): void + { + $stu = new NumberPerUnit($number, $number_unit, $per_unit, $this->units_for($locale)); + + $this->assertSame($expected, $stu->{ 'as_' . $length }); + } + + public function provide_test_cases(): array + { + return [ + + [ 'en', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_LONG, "12.345 liters per hour" ], + [ 'en', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_SHORT, "12.345 L/h" ], + [ 'en', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_NARROW, "12.345L/h" ], + + [ 'fr', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_LONG, "12,345 litres par heure" ], + [ 'fr', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_SHORT, "12,345 l/h" ], + [ 'fr', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_NARROW, "12,345l/h" ], + + [ 'fr', 12.345, 'volume-liter', 'area-square-meter', Units::LENGTH_LONG, "12,345 litres par mètre carré"], + [ 'fr', 12.345, 'angle-revolution', 'length-light-year', Units::LENGTH_LONG, "12,345 tours par années-lumière"], + + ]; + } + + private function units_for($locale): Units + { + return self::$locales[$locale]->units; + } +} diff --git a/tests/Units/NumberWithUnitTest.php b/tests/Units/NumberWithUnitTest.php new file mode 100644 index 0000000..0ff12b6 --- /dev/null +++ b/tests/Units/NumberWithUnitTest.php @@ -0,0 +1,113 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace ICanBoogie\CLDR\Units; + +use ICanBoogie\CLDR\LocaleCollection; +use ICanBoogie\CLDR\StringHelpers; +use ICanBoogie\CLDR\Units; +use PHPUnit\Framework\TestCase; + +use function ICanBoogie\CLDR\get_repository; + +final class NumberWithUnitTest extends TestCase +{ + use StringHelpers; + + /** + * @var LocaleCollection + */ + static private $locales; + + static public function setUpBeforeClass(): void + { + self::$locales = get_repository()->locales; + } + + public function test_to_string(): void + { + $stu = new NumberWithUnit(123.4504, 'digital-gigabyte', $this->units_for('fr')); + + $this->assertSame("123,45 gigaoctets", (string) $stu); + } + + /** + * @dataProvider provide_test_cases + * + * @param float|int $number + */ + public function test_cases(string $locale, string $unit, $number, string $length, string $expected): void + { + $stu = new NumberWithUnit($number, $unit, $this->units_for($locale)); + + $this->assertSame($expected, $stu->{ 'as_' . $length }); + } + + public function provide_test_cases(): array + { + return [ + + [ 'fr', 'acceleration-g-force', 123.4504, Units::LENGTH_LONG, "123,45 fois l’accélération de pesanteur terrestre" ], + [ 'fr', 'digital-gigabyte', 123.4504, Units::LENGTH_LONG, "123,45 gigaoctets" ], + [ 'fr', 'digital-gigabyte', 123.4504, Units::LENGTH_SHORT, "123,45 Go" ], + [ 'fr', 'digital-gigabyte', 123.4504, Units::LENGTH_NARROW, "123,45 Go" ], + [ 'fr', 'duration-hour', 123.4504, Units::LENGTH_LONG, "123,45 heures" ], + [ 'fr', 'duration-hour', 123.4504, Units::LENGTH_SHORT, "123,45 h" ], + [ 'fr', 'duration-hour', 123.4504, Units::LENGTH_NARROW, "123,45h" ], + + ]; + } + + /** + * @dataProvider provide_per + * + * @param float|int $number + */ + public function test_per( + string $locale, + $number, + string $number_unit, + string $per_unit, + string $length, + string $expected + ): void + { + $stu = new NumberWithUnit($number, $number_unit, $this->units_for($locale)); + + $this->assertSame( + $expected, + $stu->per($per_unit)->{ 'as_' . $length } + ); + } + + public function provide_per(): array + { + return [ + + [ 'en', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_LONG, "12.345 liters per hour" ], + [ 'en', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_SHORT, "12.345 L/h" ], + [ 'en', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_NARROW, "12.345L/h" ], + + [ 'fr', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_LONG, "12,345 litres par heure" ], + [ 'fr', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_SHORT, "12,345 l/h" ], + [ 'fr', 12.345, 'volume-liter', 'duration-hour', Units::LENGTH_NARROW, "12,345l/h" ], + + [ 'fr', 12.345, 'volume-liter', 'area-square-meter', Units::LENGTH_LONG, "12,345 litres par mètre carré"], + [ 'fr', 12.345, 'angle-revolution', 'length-light-year', Units::LENGTH_LONG, "12,345 tours par années-lumière"], + + ]; + } + + private function units_for($locale): Units + { + return self::$locales[$locale]->units; + } +} diff --git a/tests/Units/UnitTest.php b/tests/Units/UnitTest.php index 5afffa5..84e4e15 100644 --- a/tests/Units/UnitTest.php +++ b/tests/Units/UnitTest.php @@ -14,17 +14,12 @@ use ICanBoogie\CLDR\Units; use PHPUnit\Framework\TestCase; -class UnitTest extends TestCase +final class UnitTest extends TestCase { /** * @dataProvider provide_test_properties - * - * @param string $unit - * @param string $property - * @param string $length - * @param string $expected */ - public function test_properties($unit, $property, $length, $expected) + public function test_properties(string $unit, string $property, string $length, string $expected): void { $units = $this->mockUnits(function ($units) use ($unit, $length, $expected) { @@ -37,7 +32,7 @@ public function test_properties($unit, $property, $length, $expected) $this->assertSame($expected, (new Unit($units, $unit))->$property); } - public function provide_test_properties() + public function provide_test_properties(): array { return [ @@ -49,38 +44,17 @@ public function provide_test_properties() ]; } - public function test_to_string() + public function test_to_string(): void { $unit = uniqid(); $this->assertSame($unit, (string) new Unit($this->mockUnits(), $unit)); } - public function test_per_unit() - { - $number = uniqid(); - $unit = uniqid(); - $per_unit = uniqid(); - $length = uniqid(); - $expected = uniqid(); - - $units = $this->mockUnits(function ($units) use($number, $unit, $per_unit, $length, $expected) { - - $units->format_combination($number, $unit, $per_unit, $length) - ->shouldBeCalled() - ->willReturn($expected); - - }); - - $this->assertSame($expected, (new Unit($units, $unit))->per_unit($number, $per_unit, $length)); - } - /** * @param callable|null $init - * - * @return Units */ - private function mockUnits(callable $init = null) + private function mockUnits(callable $init = null): Units { $units = $this->prophesize(Units::class); diff --git a/tests/UnitsTest.php b/tests/UnitsTest.php index 604e288..9955eec 100644 --- a/tests/UnitsTest.php +++ b/tests/UnitsTest.php @@ -14,7 +14,7 @@ use BadMethodCallException; use PHPUnit\Framework\TestCase; -class UnitsTest extends TestCase +final class UnitsTest extends TestCase { use StringHelpers; @@ -31,22 +31,24 @@ static public function setUpBeforeClass(): void /** * @dataProvider provide_test_cases * - * @param string $locale - * @param string $unit - * @param number $number - * @param string $length - * @param string $expected + * @param float|int $number */ - public function test_cases($locale, $unit, $number, $length, $expected) + public function test_cases(string $locale, string $unit, $number, string $length, string $expected): void { - $this->assertSame($expected, $this->units_for($locale)->$unit($number, $length)); + $this->assertSame($expected, $this->units_for($locale)->$unit($number)->{ 'as_' . $length }); } - public function provide_test_cases() + public function provide_test_cases(): array { return [ - [ 'fr', 'acceleration-g-force', 123.4504, Units::LENGTH_LONG, "123,45 fois l’accélération de pesanteur terrestre" ] + [ 'fr', 'acceleration-g-force', 123.4504, Units::LENGTH_LONG, "123,45 fois l’accélération de pesanteur terrestre" ], + [ 'fr', 'digital_gigabyte', 123.4504, Units::LENGTH_LONG, "123,45 gigaoctets" ], + [ 'fr', 'digital_gigabyte', 123.4504, Units::LENGTH_SHORT, "123,45 Go" ], + [ 'fr', 'digital_gigabyte', 123.4504, Units::LENGTH_NARROW, "123,45 Go" ], + [ 'fr', 'duration_hour', 123.4504, Units::LENGTH_LONG, "123,45 heures" ], + [ 'fr', 'duration_hour', 123.4504, Units::LENGTH_SHORT, "123,45 h" ], + [ 'fr', 'duration_hour', 123.4504, Units::LENGTH_NARROW, "123,45h" ], ]; } @@ -54,14 +56,16 @@ public function provide_test_cases() /** * @dataProvider provide_test_format_combination * - * @param string $locale - * @param number $number - * @param string $number_unit - * @param string $per_unit - * @param string $length - * @param string $expected + * @param float|int $number */ - public function test_format_combination($locale, $number, $number_unit, $per_unit, $length, $expected) + public function test_format_combination( + string $locale, + $number, + string $number_unit, + string $per_unit, + string $length, + string $expected + ): void { $this->assertSame( $expected, @@ -69,7 +73,7 @@ public function test_format_combination($locale, $number, $number_unit, $per_uni ); } - public function provide_test_format_combination() + public function provide_test_format_combination(): array { return [ @@ -89,17 +93,13 @@ public function provide_test_format_combination() /** * @dataProvider provide_test_format_sequence - * - * @param string $locale - * @param callable $sequence - * @param string $expected */ - public function test_format_sequence($locale, callable $sequence, $expected) + public function test_format_sequence(string $locale, callable $sequence, string $expected): void { $this->assertStringSame($expected, $sequence($this->units_for($locale))); } - public function provide_test_format_sequence() + public function provide_test_format_sequence(): array { $s1 = Spaces::NARROW_NO_BREAK_SPACE; $s2 = Spaces::NO_BREAK_SPACE; @@ -198,17 +198,13 @@ public function provide_test_format_sequence() /** * @dataProvider provide_name_for - * - * @param string $unit - * @param string $length - * @param string $expected_name */ - public function test_name_for($unit, $length, $expected_name) + public function test_name_for(string $unit, string $length, string $expected_name): void { $this->assertSame($expected_name, $this->units_for('fr')->name_for($unit, $length)); } - public function provide_name_for() + public function provide_name_for(): array { return [ @@ -222,7 +218,7 @@ public function provide_name_for() ]; } - public function test_getter() + public function test_getter(): void { $units = $this->units_for('fr'); $unit = $units->angle_degree; @@ -235,12 +231,20 @@ public function test_getter() */ public function should_fail_with_undefined_unit() { - $this->expectExceptionMessage("Unit is not defined: madonna."); + $this->expectExceptionMessage("No such unit: undefined-unit"); $this->expectException(BadMethodCallException::class); - $this->units_for('fr')->madonna(); + $this->units_for('fr')->{ 'undefined_unit' }(); + } + + public function test_unit_method_requires_one_argument(): void + { + $this->expectExceptionMessage("acceleration_g_force() expects one argument, got 0"); + $this->expectException(BadMethodCallException::class); + + $this->units_for('en')->acceleration_g_force(); } - private function units_for($locale) + private function units_for($locale): Units { return new Units(self::$locales[$locale]); }