From d33354b199c58adee6f82f3c6d0ba8f7eeb2796e Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Sun, 1 Oct 2023 22:02:49 -0500 Subject: [PATCH 1/5] Start of Rates structure with Flow --- composer.json | 3 +- composer.lock | 96 ++++++++++++++++++++++++++++++++++++++- src/AbstractUnitz.php | 4 +- src/Color.php | 2 +- src/Distillate.php | 2 +- src/Gravity.php | 2 +- src/Pressure.php | 2 +- src/Rate/AbstractRate.php | 73 +++++++++++++++++++++++++++++ src/Rate/Flow.php | 34 ++++++++++++++ src/Temperature.php | 2 +- src/Time.php | 2 +- src/Volume.php | 2 +- src/Weight.php | 2 +- tests/ColorTest.php | 8 ---- tests/GravityTest.php | 8 ---- tests/PressureTest.php | 8 ---- tests/Rate/FlowTest.php | 66 +++++++++++++++++++++++++++ tests/TemperatureTest.php | 8 ---- tests/VolumeTest.php | 8 ---- tests/WeightTest.php | 8 ---- 20 files changed, 279 insertions(+), 61 deletions(-) create mode 100644 src/Rate/AbstractRate.php create mode 100644 src/Rate/Flow.php create mode 100644 tests/Rate/FlowTest.php diff --git a/composer.json b/composer.json index e0255b3..264ac3b 100644 --- a/composer.json +++ b/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": "^8.1" + "php": "^8.1", + "doctrine/inflector": "^2.0" }, "require-dev": { "phpunit/phpunit": "^10.2" diff --git a/composer.lock b/composer.lock index 89de56a..3ffa2c4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,100 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0349f553d7df173a1da7b57ac2e1b944", - "packages": [], + "content-hash": "9e1a06f662037173be577d03e160f873", + "packages": [ + { + "name": "doctrine/inflector", + "version": "2.0.8", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "reference": "f9301a5b2fb1216b2b08f02ba04dc45423db6bff", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.8" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2023-06-16T13:40:37+00:00" + } + ], "packages-dev": [ { "name": "myclabs/deep-copy", diff --git a/src/AbstractUnitz.php b/src/AbstractUnitz.php index afdb4da..62077b7 100644 --- a/src/AbstractUnitz.php +++ b/src/AbstractUnitz.php @@ -43,7 +43,7 @@ public function setValue(float $value): self * @param array $values * @return bool */ - protected function hasOnlyOneValue(array $values): bool + protected function hasOneOrNoneValue(array $values): bool { $count = 0; foreach ($values as $value) { @@ -52,7 +52,7 @@ protected function hasOnlyOneValue(array $values): bool } } - return $count === 1; + return $count <= 1; } /** diff --git a/src/Color.php b/src/Color.php index feadcce..1e88c2f 100644 --- a/src/Color.php +++ b/src/Color.php @@ -17,7 +17,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue([$srm, $ebc, $lovibond, $userValue])) { + if (!$this->hasOneOrNoneValue([$srm, $ebc, $lovibond, $userValue])) { throw new InvalidArgumentException('Only one Color type can be set at a time.'); } diff --git a/src/Distillate.php b/src/Distillate.php index 12f4185..ff9c3ea 100644 --- a/src/Distillate.php +++ b/src/Distillate.php @@ -15,7 +15,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue([$proof, $percentAlcohol, $userValue])) { + if (!$this->hasOneOrNoneValue([$proof, $percentAlcohol, $userValue])) { throw new InvalidArgumentException('Only one Distillate type can be set at a time.'); } diff --git a/src/Gravity.php b/src/Gravity.php index 9269a72..02c22e3 100644 --- a/src/Gravity.php +++ b/src/Gravity.php @@ -19,7 +19,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue([$plato, $specificGravity, $brix, $userValue])) { + if (!$this->hasOneOrNoneValue([$plato, $specificGravity, $brix, $userValue])) { throw new InvalidArgumentException('Only one Gravity type can be set at a time.'); } diff --git a/src/Pressure.php b/src/Pressure.php index 5f911fb..9107171 100644 --- a/src/Pressure.php +++ b/src/Pressure.php @@ -15,7 +15,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue([$bar, $psi, $userValue])) { + if (!$this->hasOneOrNoneValue([$bar, $psi, $userValue])) { throw new InvalidArgumentException('Only one Pressure type can be set at a time.'); } diff --git a/src/Rate/AbstractRate.php b/src/Rate/AbstractRate.php new file mode 100644 index 0000000..11fbf09 --- /dev/null +++ b/src/Rate/AbstractRate.php @@ -0,0 +1,73 @@ +inflector = InflectorFactory::create()->build(); + } + + protected function setNumerator(BaseUnitz $numerator): void + { + $this->numerator = $numerator; + } + + protected function setDenominator(BaseUnitz $denominator): void + { + $this->denominator = $denominator; + } + + public function __call(string $name, array $arguments) + { + $getPosition = strpos($name, 'get'); + if ($getPosition === false || $getPosition !== 0) { + throw new RuntimeException('All methods must start with "get"'); + } + + $name = str_replace('get', '', $name); + + $perPosition = strpos($name, 'Per'); + if ($perPosition === false) { + throw new RuntimeException('All methods must contain "Per" between the two units'); + } + + $units = array_filter(explode('Per', $name)); + if (count($units) !== 2) { + throw new RuntimeException( + 'All methods must contain two units separated by "Per", get' . $name . '() has ' . count( + $units + ) + ); + } + + $numeratorName = $this->inflector->singularize(ucfirst(strtolower($units[0]))); + $denominatorName = $this->inflector->singularize(ucfirst(strtolower($units[1]))); + + if (!method_exists($this->numerator, "get$numeratorName")) { + throw new RuntimeException("Method get$numeratorName does not exist on " . get_class($this->numerator)); + } + $numerator = $this->numerator->{"get$numeratorName"}(); + + if (!method_exists($this->denominator, "get$denominatorName")) { + throw new RuntimeException( + "Method get$denominatorName does not exist on " . get_class($this->denominator) + ); + } + $denominator = $this->denominator->{"get$denominatorName"}(); + + $unitsClass = get_class($this->numerator); + + return (new $unitsClass())->{"set$numeratorName"}($numerator / $denominator); + } +} \ No newline at end of file diff --git a/src/Rate/Flow.php b/src/Rate/Flow.php new file mode 100644 index 0000000..4bce03a --- /dev/null +++ b/src/Rate/Flow.php @@ -0,0 +1,34 @@ +setNumerator($volume); + $this->setDenominator($time); + } + + /** + * @return \Unitz\Volume + */ + public function getVolume(): Volume + { + return $this->volume; + } + + /** + * @return \Unitz\Time + */ + public function getTime(): Time + { + return $this->time; + } +} \ No newline at end of file diff --git a/src/Temperature.php b/src/Temperature.php index 8c8d605..c120d15 100644 --- a/src/Temperature.php +++ b/src/Temperature.php @@ -15,7 +15,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue([$fahrenheit, $celsius, $userValue])) { + if (!$this->hasOneOrNoneValue([$fahrenheit, $celsius, $userValue])) { throw new InvalidArgumentException('Only one Temperature type can be set at a time.'); } diff --git a/src/Time.php b/src/Time.php index 3af4962..4c8fce0 100644 --- a/src/Time.php +++ b/src/Time.php @@ -27,7 +27,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue( + if (!$this->hasOneOrNoneValue( [$millisecond, $second, $minute, $hour, $day, $week, $month, $year, $userValue] )) { throw new InvalidArgumentException('Only one Time type can be set at a time.'); diff --git a/src/Volume.php b/src/Volume.php index 664beac..e4f0ce4 100644 --- a/src/Volume.php +++ b/src/Volume.php @@ -23,7 +23,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue([$ounce, $gallon, $barrel, $milliliter, $liter, $hectoliter, $userValue])) { + if (!$this->hasOneOrNoneValue([$ounce, $gallon, $barrel, $milliliter, $liter, $hectoliter, $userValue])) { throw new InvalidArgumentException('Only one Volume type can be set at a time.'); } diff --git a/src/Weight.php b/src/Weight.php index 6a6c36b..50c07e5 100644 --- a/src/Weight.php +++ b/src/Weight.php @@ -19,7 +19,7 @@ public function __construct( ?float $userValue = null, array $preferences = [] ) { - if (!$this->hasOnlyOneValue([$ounce, $pound, $gram, $kilogram, $userValue])) { + if (!$this->hasOneOrNoneValue([$ounce, $pound, $gram, $kilogram, $userValue])) { throw new InvalidArgumentException('Only one Weight type can be set at a time.'); } diff --git a/tests/ColorTest.php b/tests/ColorTest.php index c6f367e..ba7c0e1 100644 --- a/tests/ColorTest.php +++ b/tests/ColorTest.php @@ -93,14 +93,6 @@ public function testSetLovibondWillReturnSrmWithGetValueAndDefaultPreferences(): $this->assertEquals($expected, $actual); } - public function testWillThrowExceptionWithNoValuesSet(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Only one Color type can be set at a time.'); - - new Color(); - } - public function testWillThrowExceptionWithTooManyValuesSet(): void { $this->expectException(InvalidArgumentException::class); diff --git a/tests/GravityTest.php b/tests/GravityTest.php index 0079c6a..e30d177 100644 --- a/tests/GravityTest.php +++ b/tests/GravityTest.php @@ -123,14 +123,6 @@ public function testSetBrixWillReturnBrixWithGetBrix(): void $this->assertEquals($expected, $actual); } - public function testWillThrowExceptionWithNoValuesSet(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Only one Gravity type can be set at a time.'); - - new Gravity(); - } - public function testWillThrowExceptionWithTooManyValuesSet(): void { $this->expectException(InvalidArgumentException::class); diff --git a/tests/PressureTest.php b/tests/PressureTest.php index b182c9b..07faa3c 100644 --- a/tests/PressureTest.php +++ b/tests/PressureTest.php @@ -65,14 +65,6 @@ public function testSetBarWillReturnBarWithGetBar(): void $this->assertEquals($expected, $actual); } - public function testWillThrowExceptionWithNoValuesSet(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Only one Pressure type can be set at a time.'); - - new Pressure(); - } - public function testWillThrowExceptionWithTooManyValuesSet(): void { $this->expectException(InvalidArgumentException::class); diff --git a/tests/Rate/FlowTest.php b/tests/Rate/FlowTest.php new file mode 100644 index 0000000..3397f53 --- /dev/null +++ b/tests/Rate/FlowTest.php @@ -0,0 +1,66 @@ +expectException(RuntimeException::class); + $this->expectExceptionMessage('All methods must start with "get"'); + + $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); + $flow->gimmeGallonsPerMinute(); + } + + public function testVolumeWithMissingPerDividerThrowsRuntimeException(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('All methods must contain "Per" between the two units'); + + $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); + $flow->getGallonsByMinute(); + } + + public function testVolumeWithMissingFirstUnitThrowsRuntimeException(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('All methods must contain two units separated by "Per", getPerMinute() has 1'); + + $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); + $flow->getPerMinute(); + } + + public function testVolumeWithMissingSecondUnitThrowsRuntimeException(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('All methods must contain two units separated by "Per", getGallonsPer() has 1'); + + $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); + $flow->getGallonsPer(); + } + + public function testVolumeWithInvalidNumeratorUnitTypeThrowsRuntimeException(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Method getMonkey does not exist on Unitz\Volume'); + + $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); + $flow->getMonkeysPerMinute(); + } + + public function testVolumeWithInvalidDenominatorUnitTypeThrowsRuntimeException(): void + { + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Method getBannana does not exist on Unitz\Time'); + + $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); + $flow->getGallonsPerBannanas(); + } +} \ No newline at end of file diff --git a/tests/TemperatureTest.php b/tests/TemperatureTest.php index 1528732..f4d98c8 100644 --- a/tests/TemperatureTest.php +++ b/tests/TemperatureTest.php @@ -65,14 +65,6 @@ public function testSetCelsiusWillReturnCelsiusWithGetCelsius(): void $this->assertEquals($expected, $actual); } - public function testWillThrowExceptionWithNoValuesSet(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Only one Temperature type can be set at a time.'); - - new Temperature(); - } - public function testWillThrowExceptionWithTooManyValuesSet(): void { $this->expectException(InvalidArgumentException::class); diff --git a/tests/VolumeTest.php b/tests/VolumeTest.php index cc5adcd..10d656d 100644 --- a/tests/VolumeTest.php +++ b/tests/VolumeTest.php @@ -360,14 +360,6 @@ public function testSetHectoliterWillReturnHectoliterWithGetHectoliter(): void $this->assertEquals($expected, $actual); } - public function testWillThrowExceptionWithNoValuesSet(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Only one Volume type can be set at a time.'); - - new Volume(); - } - public function testWillThrowExceptionWithTooManyValuesSet(): void { $this->expectException(InvalidArgumentException::class); diff --git a/tests/WeightTest.php b/tests/WeightTest.php index 6ef082e..ad4fd30 100644 --- a/tests/WeightTest.php +++ b/tests/WeightTest.php @@ -174,14 +174,6 @@ public function testSetKilogramWillReturnKilogramWithGetKilogram(): void $this->assertEquals($expected, $actual); } - public function testWillThrowExceptionWithNoValuesSet(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Only one Weight type can be set at a time.'); - - new Weight(); - } - public function testWillThrowExceptionWithTooManyValuesSet(): void { $this->expectException(InvalidArgumentException::class); From 9d49fd82535082d700cbc376b31a93bb4daa0a60 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 2 Oct 2023 21:30:48 -0500 Subject: [PATCH 2/5] Refactor and document magic method definitions --- src/Rate/AbstractRate.php | 8 ++++-- src/Rate/Flow.php | 51 +++++++++++++++++++++++++++++++++++++-- tests/Rate/FlowTest.php | 6 +++++ 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/Rate/AbstractRate.php b/src/Rate/AbstractRate.php index 11fbf09..aa19e58 100644 --- a/src/Rate/AbstractRate.php +++ b/src/Rate/AbstractRate.php @@ -66,8 +66,12 @@ public function __call(string $name, array $arguments) } $denominator = $this->denominator->{"get$denominatorName"}(); - $unitsClass = get_class($this->numerator); + $numeratorReflection = new \ReflectionClass($this->numerator); + $unitsClassName = $numeratorReflection->getName(); + $unitsClassShortName = $numeratorReflection->getShortName(); - return (new $unitsClass())->{"set$numeratorName"}($numerator / $denominator); + return (new $unitsClassName(preferences: [$unitsClassShortName => $numeratorName]))->{"set$numeratorName"}( + $numerator / $denominator + ); } } \ No newline at end of file diff --git a/src/Rate/Flow.php b/src/Rate/Flow.php index 4bce03a..0b94e75 100644 --- a/src/Rate/Flow.php +++ b/src/Rate/Flow.php @@ -5,10 +5,57 @@ use Unitz\Time; use Unitz\Volume; +/** Magic methods available through AbstractRate::__call() + * @method Volume getOuncesPerMillisecond() + * @method Volume getOuncesPerSecond() + * @method Volume getOuncesPerMinute() + * @method Volume getOuncesPerHour() + * @method Volume getOuncesPerDay() + * @method Volume getOuncesPerWeek() + * @method Volume getOuncesPerMonth() + * @method Volume getOuncesPerYear() + * @method Volume getGallonsPerMillisecond + * @method Volume getGallonsPerSecond() + * @method Volume getGallonsPerMinute() + * @method Volume getGallonsPerHour() + * @method Volume getGallonsPerDay() + * @method Volume getGallonsPerWeek() + * @method Volume getGallonsPerMonth() + * @method Volume getGallonsPerYear() + * @method Volume getLitersPerMillisecond() + * @method Volume getLitersPerSecond() + * @method Volume getLitersPerMinute() + * @method Volume getLitersPerHour() + * @method Volume getLitersPerDay() + * @method Volume getLitersPerWeek() + * @method Volume getLitersPerMonth() + * @method Volume getLitersPerYear() + * @method Volume getMillilitersPerMillisecond() + * @method Volume getMillilitersPerSecond() + * @method Volume getMillilitersPerMinute() + * @method Volume getMillilitersPerHour() + * @method Volume getMillilitersPerDay() + * @method Volume getMillilitersPerWeek() + * @method Volume getMillilitersPerMonth() + * @method Volume getMillilitersPerYear() + * @method Volume getHectolitersPerMillisecond + * @method Volume getHectolitersPerSecond() + * @method Volume getHectolitersPerMinute() + * @method Volume getHectolitersPerHour() + * @method Volume getHectolitersPerDay() + * @method Volume getHectolitersPerWeek() + * @method Volume getHectolitersPerMonth() + * @method Volume getHectolitersPerYear() + * @method Volume getBarrelsPerSecond() + * @method Volume getBarrelsPerMinute() + * @method Volume getBarrelsPerHour() + * @method Volume getBarrelsPerDay() + * @method Volume getBarrelsPerWeek() + * @method Volume getBarrelsPerMonth() + * @method Volume getBarrelsPerYear() + */ class Flow extends AbstractRate { - public const RETURNABLE_CLASS = 'Volume'; - public function __construct(private readonly Volume $volume, private readonly Time $time) { parent::__construct(); diff --git a/tests/Rate/FlowTest.php b/tests/Rate/FlowTest.php index 3397f53..724fdae 100644 --- a/tests/Rate/FlowTest.php +++ b/tests/Rate/FlowTest.php @@ -63,4 +63,10 @@ public function testVolumeWithInvalidDenominatorUnitTypeThrowsRuntimeException() $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); $flow->getGallonsPerBannanas(); } + + public function testFlowReturnsCorrectValue(): void + { + $flow = new Flow(new Volume(gallon: 3), new Time(minute: 1)); + $this->assertEquals(new Volume(gallon: 3), $flow->getGallonsPerMinute()); + } } \ No newline at end of file From 8bbc522bc1955b72dd53050dca368e4488f17037 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 2 Oct 2023 21:43:18 -0500 Subject: [PATCH 3/5] Update doc blocks --- src/Rate/AbstractRate.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Rate/AbstractRate.php b/src/Rate/AbstractRate.php index aa19e58..0e15956 100644 --- a/src/Rate/AbstractRate.php +++ b/src/Rate/AbstractRate.php @@ -18,17 +18,30 @@ public function __construct() $this->inflector = InflectorFactory::create()->build(); } + /** + * @param \Unitz\BaseUnitz $numerator + * @return void + */ protected function setNumerator(BaseUnitz $numerator): void { $this->numerator = $numerator; } + /** + * @param \Unitz\BaseUnitz $denominator + * @return void + */ protected function setDenominator(BaseUnitz $denominator): void { $this->denominator = $denominator; } - public function __call(string $name, array $arguments) + /** + * @param string $name + * @param array $arguments + * @return \Unitz\BaseUnitz + */ + public function __call(string $name, array $arguments): BaseUnitz { $getPosition = strpos($name, 'get'); if ($getPosition === false || $getPosition !== 0) { From 5b9dcb3e9702bf707d68700cce128d5948682951 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 2 Oct 2023 22:22:33 -0500 Subject: [PATCH 4/5] AbstractRate refactor on __call method --- src/Rate/AbstractRate.php | 81 +++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 21 deletions(-) diff --git a/src/Rate/AbstractRate.php b/src/Rate/AbstractRate.php index 0e15956..1841eb1 100644 --- a/src/Rate/AbstractRate.php +++ b/src/Rate/AbstractRate.php @@ -9,6 +9,9 @@ abstract class AbstractRate { + private const UNIT_NAME_NUMERATOR = 0; + private const UNIT_NAME_DENOMINATOR = 1; + private Inflector $inflector; private BaseUnitz $numerator; private BaseUnitz $denominator; @@ -37,19 +40,52 @@ protected function setDenominator(BaseUnitz $denominator): void } /** - * @param string $name + * @param string $methodName * @param array $arguments * @return \Unitz\BaseUnitz + * @throws \RuntimeException */ - public function __call(string $name, array $arguments): BaseUnitz + public function __call(string $methodName, array $arguments): BaseUnitz + { + $unitNames = $this->getUnitNames($this->checkAndRemoveGetPrefix($methodName)); + + $numeratorName = $this->processUnitName($unitNames[self::UNIT_NAME_NUMERATOR]); + $denominatorName = $this->processUnitName($unitNames[self::UNIT_NAME_DENOMINATOR]); + + $numerator = $this->getUnitValue($this->numerator, $numeratorName); + $denominator = $this->getUnitValue($this->denominator, $denominatorName); + + $numeratorReflection = new \ReflectionClass($this->numerator); + $unitsClassName = $numeratorReflection->getName(); + $unitsClassShortName = $numeratorReflection->getShortName(); + + return (new $unitsClassName(preferences: [$unitsClassShortName => $numeratorName]))->{"set$numeratorName"}( + $numerator / $denominator + ); + } + + /** + * @param string $name + * @return string + * @throws \RuntimeException + */ + private function checkAndRemoveGetPrefix(string $name): string { $getPosition = strpos($name, 'get'); if ($getPosition === false || $getPosition !== 0) { throw new RuntimeException('All methods must start with "get"'); } - $name = str_replace('get', '', $name); + return str_replace('get', '', $name); + } + /** + * @param string $name + * @return array + * @throws \RuntimeException + */ + private function getUnitNames(string $name): array + { $perPosition = strpos($name, 'Per'); if ($perPosition === false) { throw new RuntimeException('All methods must contain "Per" between the two units'); @@ -64,27 +100,30 @@ public function __call(string $name, array $arguments): BaseUnitz ); } - $numeratorName = $this->inflector->singularize(ucfirst(strtolower($units[0]))); - $denominatorName = $this->inflector->singularize(ucfirst(strtolower($units[1]))); + return $units; + } - if (!method_exists($this->numerator, "get$numeratorName")) { - throw new RuntimeException("Method get$numeratorName does not exist on " . get_class($this->numerator)); - } - $numerator = $this->numerator->{"get$numeratorName"}(); + /** + * @param string $unitName + * @return string + */ + private function processUnitName(string $unitName): string + { + return $this->inflector->singularize(ucfirst(strtolower($unitName))); + } - if (!method_exists($this->denominator, "get$denominatorName")) { - throw new RuntimeException( - "Method get$denominatorName does not exist on " . get_class($this->denominator) - ); + /** + * @param \Unitz\BaseUnitz $unit + * @param string $unitName + * @return float + * @throws \RuntimeException + */ + private function getUnitValue(BaseUnitz $unit, string $unitName): float + { + if (!method_exists($unit, "get$unitName")) { + throw new RuntimeException("Method get$unitName does not exist on " . get_class($unit)); } - $denominator = $this->denominator->{"get$denominatorName"}(); - - $numeratorReflection = new \ReflectionClass($this->numerator); - $unitsClassName = $numeratorReflection->getName(); - $unitsClassShortName = $numeratorReflection->getShortName(); - return (new $unitsClassName(preferences: [$unitsClassShortName => $numeratorName]))->{"set$numeratorName"}( - $numerator / $denominator - ); + return $unit->{"get$unitName"}(); } } \ No newline at end of file From fded471cbbf9cc6e95ec35aa8b0ab51cc582e882 Mon Sep 17 00:00:00 2001 From: Ben Griffith Date: Mon, 2 Oct 2023 22:26:13 -0500 Subject: [PATCH 5/5] AbstractRate tiny refactor --- src/Rate/AbstractRate.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Rate/AbstractRate.php b/src/Rate/AbstractRate.php index 1841eb1..18644e8 100644 --- a/src/Rate/AbstractRate.php +++ b/src/Rate/AbstractRate.php @@ -4,6 +4,7 @@ use Doctrine\Inflector\Inflector; use Doctrine\Inflector\InflectorFactory; +use ReflectionClass; use RuntimeException; use Unitz\BaseUnitz; @@ -55,7 +56,7 @@ public function __call(string $methodName, array $arguments): BaseUnitz $numerator = $this->getUnitValue($this->numerator, $numeratorName); $denominator = $this->getUnitValue($this->denominator, $denominatorName); - $numeratorReflection = new \ReflectionClass($this->numerator); + $numeratorReflection = new ReflectionClass($this->numerator); $unitsClassName = $numeratorReflection->getName(); $unitsClassShortName = $numeratorReflection->getShortName();