diff --git a/src/ConverterBuilder.php b/src/ConverterBuilder.php index 072e279a..ffc2f129 100644 --- a/src/ConverterBuilder.php +++ b/src/ConverterBuilder.php @@ -7,8 +7,8 @@ use UnitConverter\Calculator\BinaryCalculator; use UnitConverter\Calculator\CalculatorInterface; use UnitConverter\Calculator\SimpleCalculator; -use UnitConverter\Exception\BadMeasurement; use UnitConverter\Registry\UnitRegistry; +use UnitConverter\Unit\UnitInterface; class ConverterBuilder { @@ -34,7 +34,7 @@ class ConverterBuilder */ public function __construct() { - $this->defaultMeasurements = Measure::getDefaultMeasurements(); + $this->defaultMeasurements = Measure::cases(); } /** @@ -69,10 +69,10 @@ public function addDefaultRegistry() * type that are provided with this package. * * @api - * @param string $measurement The type of measurement to seed units for. + * @param Measure $measurement The type of measurement to seed units for. * @return self */ - public function addRegistryFor(string $measurement) + public function addRegistryFor(Measure $measurement) { $this->registry = new UnitRegistry($this->instantiateAllUnitsFor($measurement)); @@ -123,13 +123,14 @@ public function build() * Retrieve instances of all units. * * @internal - * @return array + * @return UnitInterface[] */ private function instantiateAllUnits() { - $measurements = array_map(function (string $measurement) { - return $this->instantiateAllUnitsFor($measurement); - }, $this->defaultMeasurements); + $measurements = array_map( + fn (Measure $measurement): array => $this->instantiateAllUnitsFor($measurement), + $this->defaultMeasurements, + ); return array_merge(...$measurements); } @@ -138,17 +139,13 @@ private function instantiateAllUnits() * Retrieve instances of all units for a given measurement type. * * @internal - * @param string $measurement - * @return array + * @return UnitInterface[] */ - private function instantiateAllUnitsFor(string $measurement): array + private function instantiateAllUnitsFor(Measure $measurement): array { - if (!in_array($measurement, $this->defaultMeasurements)) { - throw BadMeasurement::unknown($measurement); - } - - return array_map(function ($class) { - return new $class(); - }, Measure::getDefaultUnitsFor($measurement)); + return array_map( + static fn (string $fqcn): UnitInterface => new $fqcn(), + $measurement->units() + ); } } diff --git a/src/Measure.php b/src/Measure.php index 2054525b..6784ee07 100644 --- a/src/Measure.php +++ b/src/Measure.php @@ -117,200 +117,226 @@ use UnitConverter\Unit\Volume\Millilitre; use UnitConverter\Unit\Volume\Pint; + /** - * A static class containing constants that define the available - * default types of measurements & the units they govern. + * Governing bodies for a group of units. * - * @version 1.0.0 + * @version 2.0.0 * @since 0.0.1 * @author Jordan Brauer <18744334+jordanbrauer@users.noreply.github.com> * @codeCoverageIgnore */ -class Measure +enum Measure: string { - const AREA = "area"; + /** + * The geometric quantity of any given two-dimensional region, shape, or + * lanar lamina. + */ + case AREA = 'area'; - const DIGITAL_STORAGE = "digital_storage"; + /** + * The measurement of information stored on a piece of hardware or physical device. + */ + case DIGITAL_STORAGE = 'digital_storage'; - const ENERGY = "energy"; + case ENERGY = 'energy'; - const FREQUENCY = 'frequency'; + case FREQUENCY = 'frequency'; - const FUEL_ECONOMY = 'fuel_economy'; + case FUEL_ECONOMY = 'fuel_economy'; - const LENGTH = "length"; + case LENGTH = 'length'; - const MASS = "mass"; + /** + * The quantity of matter in a physical body. It is also a measure of the body's inertia, the + * resistance to acceleration (change of velocity) when a net force is applied. + */ + case MASS = 'mass'; - const PLANE_ANGLE = "plane_angle"; + case PLANE_ANGLE = 'plane_angle'; - const PRESSURE = "pressure"; + case PRESSURE = 'pressure'; - const SPEED = "speed"; + case SPEED = 'speed'; - const TEMPERATURE = "temperature"; + case TEMPERATURE = 'temperature'; - const TIME = "time"; + /** + * The measurement of the continued sequence of existence and events that occurs in an apparently + * irreversible succession from the past, through the present, into the future. + */ + case TIME = 'time'; - const VOLUME = "volume"; + /** + * The measurement of three-dimensional space enclosed by a closed surface. + */ + case VOLUME = 'volume'; /** - * An array containing a list of default measurement types that are - * supported, and the unit classes they govern. - * - * @var array + * @deprecated This method will be removed in a later version. Use the enum instance method `units` instead. */ - private static $defaultMeasurements = [ - self::LENGTH => [ - AstronomicalUnit::class, - Centimetre::class, - Decimetre::class, - Foot::class, - Hand::class, - Inch::class, - Kilometre::class, - Lightyear::class, - Metre::class, - Micrometre::class, - Mile::class, - Millimetre::class, - Nanometre::class, - Parsec::class, - Picometre::class, - Yard::class, - ], - self::AREA => [ - Acre::class, - Hectare::class, - SquareCentimetre::class, - SquareFoot::class, - SquareKilometre::class, - SquareMetre::class, - SquareMile::class, - SquareMillimetre::class, - ], - self::VOLUME => [ - CubicMetre::class, - CubicCentimetre::class, - CubicMillimetre::class, - Gallon::class, - Litre::class, - Millilitre::class, - Pint::class, - ], - self::MASS => [ - Gram::class, - Kilogram::class, - LongTon::class, - Milligram::class, - Newton::class, - Ounce::class, - Pound::class, - ShortTon::class, - Stone::class, - Tonne::class, - ], - self::SPEED => [ - KilometrePerHour::class, - MetrePerSecond::class, - MilePerHour::class, - ], - self::PLANE_ANGLE => [ - Degree::class, - Radian::class, - ], - self::TEMPERATURE => [ - Celsius::class, - Fahrenheit::class, - Kelvin::class, - ], - self::PRESSURE => [ - Atmosphere::class, - Bar::class, - Hectopascal::class, - InchesOfMercury::class, - Kilopascal::class, - Megapascal::class, - Millibar::class, - Pascal::class, - PoundForcePerSquareInch::class, - Torr::class, - ], - self::TIME => [ - Day::class, - Hour::class, - Microsecond::class, - Millisecond::class, - Minute::class, - Month::class, - Nanosecond::class, - Second::class, - Week::class, - Year::class, - ], - self::ENERGY => [ - Calorie::class, - FootPound::class, - Joule::class, - Kilojoule::class, - KilowattHour::class, - Megaelectronvolt::class, - Megajoule::class, - MegawattHour::class, - NewtonMetre::class, - WattHour::class, - ], - self::FREQUENCY => [ - Hertz::class, - Kilohertz::class, - Megahertz::class, - Gigahertz::class, - Terahertz::class, - Millihertz::class, - ], - self::DIGITAL_STORAGE => [ - Bit::class, - Byte::class, - Kilobit::class, - Megabit::class, - Gigabit::class, - Terabit::class, - Kibibit::class, - Mebibit::class, - Gibibit::class, - Tebibit::class, - Kilobyte::class, - Megabyte::class, - Gigabyte::class, - Terabyte::class, - ], - self::FUEL_ECONOMY => [ - KilometrePerLitre::class, - LitrePer100Kilometres::class, - MilesPerGallon::class, - ], - ]; + public static function getDefaultUnitsFor(string|Measure $measurement): array + { + return (\is_string($measurement) ? self::from($measurement) : $measurement)->units(); + } /** - * Return a list of all default supported measurement types. These types - * govern unit's of measurement. - * - * @return array + * @deprecated This method will be removed in a later version. Use the static method `all` instead. */ public static function getDefaultMeasurements(): array { - return array_keys(static::$defaultMeasurements) ?? []; + return self::all(); + } + + /** + * A list containing the names of all measurements. + * + * @return string[] + */ + public static function all(): array + { + return array_map( + static fn (self $measure): string => $measure->value, + self::cases() + ); } /** - * Return a list of all default supported units for a given type of - * measurement. + * A list containing the FQCN of every known unit governed by the measurement. * - * @param string $measurement The measurement type to retrieve units for. - * @return array + * @return string[] */ - public static function getDefaultUnitsFor(string $measurement): array + public function units(): array { - return static::$defaultMeasurements[$measurement] ?? []; + return match ($this) { + self::LENGTH => [ + AstronomicalUnit::class, + Centimetre::class, + Decimetre::class, + Foot::class, + Hand::class, + Inch::class, + Kilometre::class, + Lightyear::class, + Metre::class, + Micrometre::class, + Mile::class, + Millimetre::class, + Nanometre::class, + Parsec::class, + Picometre::class, + Yard::class, + ], + self::AREA => [ + Acre::class, + Hectare::class, + SquareCentimetre::class, + SquareFoot::class, + SquareKilometre::class, + SquareMetre::class, + SquareMile::class, + SquareMillimetre::class, + ], + self::VOLUME => [ + CubicMetre::class, + CubicCentimetre::class, + CubicMillimetre::class, + Gallon::class, + Litre::class, + Millilitre::class, + Pint::class, + ], + self::MASS => [ + Gram::class, + Kilogram::class, + LongTon::class, + Milligram::class, + Newton::class, + Ounce::class, + Pound::class, + ShortTon::class, + Stone::class, + Tonne::class, + ], + self::SPEED => [ + KilometrePerHour::class, + MetrePerSecond::class, + MilePerHour::class, + ], + self::PLANE_ANGLE => [ + Degree::class, + Radian::class, + ], + self::TEMPERATURE => [ + Celsius::class, + Fahrenheit::class, + Kelvin::class, + ], + self::PRESSURE => [ + Atmosphere::class, + Bar::class, + Hectopascal::class, + InchesOfMercury::class, + Kilopascal::class, + Megapascal::class, + Millibar::class, + Pascal::class, + PoundForcePerSquareInch::class, + Torr::class, + ], + self::TIME => [ + Day::class, + Hour::class, + Microsecond::class, + Millisecond::class, + Minute::class, + Month::class, + Nanosecond::class, + Second::class, + Week::class, + Year::class, + ], + self::ENERGY => [ + Calorie::class, + FootPound::class, + Joule::class, + Kilojoule::class, + KilowattHour::class, + Megaelectronvolt::class, + Megajoule::class, + MegawattHour::class, + NewtonMetre::class, + WattHour::class, + ], + self::FREQUENCY => [ + Hertz::class, + Kilohertz::class, + Megahertz::class, + Gigahertz::class, + Terahertz::class, + Millihertz::class, + ], + self::DIGITAL_STORAGE => [ + Bit::class, + Byte::class, + Kilobit::class, + Megabit::class, + Gigabit::class, + Terabit::class, + Kibibit::class, + Mebibit::class, + Gibibit::class, + Tebibit::class, + Kilobyte::class, + Megabyte::class, + Gigabyte::class, + Terabyte::class, + ], + self::FUEL_ECONOMY => [ + KilometrePerLitre::class, + LitrePer100Kilometres::class, + MilesPerGallon::class, + ], + }; } } diff --git a/src/Unit/AbstractUnit.php b/src/Unit/AbstractUnit.php index 7efb6e10..458af537 100644 --- a/src/Unit/AbstractUnit.php +++ b/src/Unit/AbstractUnit.php @@ -18,6 +18,7 @@ use UnitConverter\Calculator\Formula\FormulaInterface; use UnitConverter\Calculator\Formula\UnitConversionFormula; use UnitConverter\Exception\BadUnit; +use UnitConverter\Measure; use UnitConverter\Unit\Family\SiMultipleUnit; use UnitConverter\Unit\Family\SiSubmultipleUnit; use UnitConverter\Unit\Family\SiUnit; @@ -62,7 +63,7 @@ abstract class AbstractUnit implements UnitInterface protected $symbol; /** - * @var string $unitOf What is this unit measuring? Length, temperatutre, etc. + * @var Measure $unitOf What is this unit measuring? Length, temperatutre, etc. */ protected $unitOf; @@ -175,7 +176,7 @@ public function getName(): ?string public function getRegistryKey(): ?string { - return $this->unitOf.'.'.$this->symbol; + return $this->unitOf->value.'.'.$this->symbol; } public function getScientificSymbol(): ?string @@ -190,7 +191,7 @@ public function getSymbol(): ?string public function getUnitOf(): ?string { - return $this->unitOf; + return $this->unitOf->value; } public function getUnits(): ?float @@ -246,7 +247,7 @@ public function setSymbol(string $symbol): UnitInterface return $this; } - public function setUnitOf(string $unitOf): UnitInterface + public function setUnitOf(Measure $unitOf): UnitInterface { $this->unitOf = $unitOf; diff --git a/src/Unit/UnitInterface.php b/src/Unit/UnitInterface.php index 58aafa77..fdbe8f48 100644 --- a/src/Unit/UnitInterface.php +++ b/src/Unit/UnitInterface.php @@ -15,6 +15,7 @@ namespace UnitConverter\Unit; use UnitConverter\Calculator\Formula\FormulaInterface; +use UnitConverter\Measure; /** * Interface for the unit of measurement abstract class. @@ -172,10 +173,10 @@ public function setSymbol(string $symbol): UnitInterface; /** * Sets the type of measurement that this unit is measuring. * - * @param string $unitOf The value to be set as the units governing unit + * @param Measure $unitOf The value to be set as the units governing unit * @return UnitInterface */ - public function setUnitOf(string $unitOf): UnitInterface; + public function setUnitOf(Measure $unitOf): UnitInterface; /** * Sets the amount of base units required to make up 1 of the unit. diff --git a/tests/unit/Registry/UnitRegistry.spec.php b/tests/unit/Registry/UnitRegistry.spec.php index 8bb23fc5..661bf067 100644 --- a/tests/unit/Registry/UnitRegistry.spec.php +++ b/tests/unit/Registry/UnitRegistry.spec.php @@ -24,6 +24,7 @@ use UnitConverter\Unit\Length\Metre; use UnitConverter\Unit\Length\Millimetre; use UnitConverter\Unit\UnitInterface; +use UnitConverter\Measure; /** * @coversDefaultClass UnitConverter\Registry\UnitRegistry @@ -86,9 +87,11 @@ public function assertListMeasurementsMethodReturnsArray() "fuel_economy", ]; - $this->assertEquals($expected, $actual); $this->assertIsArray($actual); $this->assertTrue((count($actual) > 0)); + shuffle($expected); + shuffle($actual); + $this->assertEqualsCanonicalizing($expected, $actual); } /** @@ -118,28 +121,6 @@ public function assertMeasurementIsRegistered() $this->assertFalse($this->registry->isMeasurementRegistered("saiyanPower")); } - /** - * @test - * @covers UnitConverter\Exception\BadMeasurement - */ - public function assertRegisteringUnitsUnderUnknownMeasurementsThrowsBadMeasurementException() - { - $this->expectException(BadMeasurement::class); - $this->expectExceptionCode(BadMeasurement::ERROR_UNKNOWN_MEASUREMENT); - - $this->registry->registerUnit(new class() extends AbstractUnit { - protected function configure(): void - { - $this - ->setName("testtt") - ->setSymbol("Tst") - ->setUnitOf("NO EXIST LOL") - ->setBase(self::class) - ->setUnits(1); - } - }); - } - /** * @test * @covers ::registerMeasurement @@ -176,7 +157,7 @@ protected function configure(): void $this ->setName("saiyanPower") ->setSymbol("sP") - ->setUnitOf("energy") + ->setUnitOf(Measure::ENERGY) ->setBase(self::class) ->setUnits(9001); } diff --git a/tests/unit/Unit/AbstractUnit.spec.php b/tests/unit/Unit/AbstractUnit.spec.php index b3e5d52d..7b9876a7 100644 --- a/tests/unit/Unit/AbstractUnit.spec.php +++ b/tests/unit/Unit/AbstractUnit.spec.php @@ -45,7 +45,7 @@ class AbstractUnitSpec extends TestCase protected function setUp(): void { - $this->registryKey = Measure::LENGTH.".sP"; + $this->registryKey = Measure::LENGTH->value.".sP"; $this->unit = new class() extends AbstractUnit { protected $name = "saiyan power"; @@ -217,10 +217,10 @@ public function assertGettingFormulaWhileNoneExistReturnsNull(): void public function assertGetUnitOfSetUnitOfMethodsCanReadAndWriteToUnitUnitOf() { $this->unit->setUnitOf(Measure::ENERGY); - $actual = $this->unit->getUnitOf(); + $actual = Measure::from($this->unit->getUnitOf()); $this->assertEquals(Measure::ENERGY, $actual); - $this->assertIsString($actual); + $this->assertInstanceOf(Measure::class, $actual); } /** diff --git a/tests/unit/UnitConverter.spec.php b/tests/unit/UnitConverter.spec.php index cc7f09fc..1df6ccbb 100644 --- a/tests/unit/UnitConverter.spec.php +++ b/tests/unit/UnitConverter.spec.php @@ -262,7 +262,7 @@ public function assertConverterReturnsCurrentRegistry(): void * @param string $symbol * @return array */ - private function getPossibleConversionsFor(string $measurement, string $symbol): array + private function getPossibleConversionsFor(Measure $measurement, string $symbol): array { return array_filter(array_map(function ($class) use ($symbol) { $possibleConversion = (new $class())->getSymbol();