From a76f3a1cbc97730e1cca1eb6a21d9b102d5695e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Milan=20Mat=C4=9Bj=C4=8Dek?= Date: Sat, 14 Jan 2023 13:29:31 +0100 Subject: [PATCH] NumberFormat: see changelog --- .gitattributes | 1 - .github/workflows/tests.yml | 28 +++ .travis.yml | 27 --- README.md | 52 +++-- composer.json | 22 ++- ...alidMask.php => InvalidStateException.php} | 2 +- src/NumberFormat.php | 174 ++++++++++++++++- src/NumberFormatFactory.php | 43 +++-- src/NumberFormatState.php | 133 ------------- src/Percent.php | 6 +- src/Tax.php | 3 +- src/UnitFormatState.php | 51 ----- src/UnitPersistentFormatState.php | 27 --- src/Units/Byte.php | 1 - src/Units/Unit.php | 16 +- src/Units/UnitFormat.php | 17 +- src/Utils/Parameters.php | 2 +- src/Utils/UnitValue.php | 21 +- src/compatibility.php | 14 ++ tests/.coveralls.yml | 3 - tests/config/phpstan.neon | 3 - tests/run-tests | 19 -- tests/src/ComatibilityTest.php | 11 ++ tests/src/NumberFormatFactoryTest.php | 9 +- tests/src/NumberFormatStateTest.php | 181 ------------------ tests/src/NumberFormatTest.php | 103 ++++++++++ tests/{ => src}/TestCase.php | 0 tests/src/UnitFormatStateTest.php | 62 ------ 28 files changed, 436 insertions(+), 595 deletions(-) create mode 100644 .github/workflows/tests.yml delete mode 100644 .travis.yml rename src/Exceptions/{InvalidMask.php => InvalidStateException.php} (55%) delete mode 100644 src/NumberFormatState.php delete mode 100644 src/UnitFormatState.php delete mode 100644 src/UnitPersistentFormatState.php create mode 100644 src/compatibility.php delete mode 100644 tests/.coveralls.yml delete mode 100755 tests/run-tests create mode 100644 tests/src/ComatibilityTest.php delete mode 100644 tests/src/NumberFormatStateTest.php create mode 100644 tests/src/NumberFormatTest.php rename tests/{ => src}/TestCase.php (100%) delete mode 100644 tests/src/UnitFormatStateTest.php diff --git a/.gitattributes b/.gitattributes index f6cd387..107292c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,4 @@ .gitattributes export-ignore .gitignore export-ignore -.travis.yml export-ignore tests/ export-ignore *.sh eol=lf diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..342553b --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,28 @@ +name: Tests + +on: [push, pull_request] + +jobs: + tests: + runs-on: ubuntu-latest + strategy: + matrix: + php: ['8.0', '8.1', '8.2'] + + fail-fast: false + + name: PHP ${{ matrix.php }} tests + steps: + - uses: actions/checkout@v3 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + coverage: none + + - run: composer install --no-progress --prefer-dist + - run: composer tests + - if: failure() + uses: actions/upload-artifact@v3 + with: + name: output + path: tests/**/output diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index c572e04..0000000 --- a/.travis.yml +++ /dev/null @@ -1,27 +0,0 @@ -language: php -php: - - 7.1 - - 7.2 - - 7.3 - - 7.4 - - 8.0 - - 8.1 - -matrix: - allow_failures: - - php: 8.1 - -before_script: - - composer self-update - - composer install --no-interaction --prefer-source - - if [ "$TRAVIS_PHP_VERSION" == "7.3" ]; then NTESTER_FLAGS="phpdbg --coverage ./coverage.xml --coverage-src ./src"; else NTESTER_FLAGS=""; fi - -after_script: - - if [ "$TRAVIS_PHP_VERSION" == "7.3" ]; then - wget https://github.com/satooshi/php-coveralls/releases/download/v1.0.1/coveralls.phar - && php coveralls.phar --verbose --config tests/.coveralls.yml - || true; - fi - -script: - - ./tests/run-tests $NTESTER_FLAGS diff --git a/README.md b/README.md index b256536..f32ace2 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,16 @@ Number Format Wrapper above number_format, api is very easy. # Changelog +## v4.0 +- removed dependency on h4kuna/data-type +- support php 7.4+ +- removed interface NumberFormat +- renamed class NumberFormat -> NumberFormat +- removed class UnitFormatState, replace by `(new NumberFormat)->enableExtendFormat()` +- removed class UnitPersistentFormatState, replace by `(new NumberFormat)->enableExtendFormat('1 MY_PERSISTENT_UNIT')` +- method format has second parameter like decimals and third is dynamic defined unit +- char for unit in mask changed to `⎵` + ## v3.0 This version is same like v2.0 but support php7.1+. @@ -24,7 +34,7 @@ Install via composer composer require h4kuna/number-format ``` -### NumberFormatState +### NumberFormat Class has many parameters and all paremetes has default value. You can add parameters normaly by position or name of keys in array like first parameter. @@ -32,10 +42,9 @@ Class has many parameters and all paremetes has default value. You can add param use h4kuna\Number; // set decimals as 3 -$numberFormat = new Number\NumberFormatState(3); +$numberFormat = new Number\NumberFormat(3); // or -$numberFormat = new Number\NumberFormatState(['decimals' => 3]); - +$numberFormat = new Number\NumberFormat(['decimals' => 3]); echo $numberFormat->format(1000); // 1 000,000 ``` @@ -48,26 +57,41 @@ echo $numberFormat->format(1000); // 1 000,000 - emptyValue: string [NULL] has two options dependecy on zeroIsEmpty if is FALSE than empty value transform to zero or TRUE mean zero tranform to emtpy string if is not defined other string - zeroClear: [FALSE] mean 1.20 trim zero from right -> 1.2 - intOnly: [-1] if we have numbers like integers. This mean set 3 and transform number 1050 -> 1,05 -- round: [0] change round function, let's use `NumberFormatState::ROUND_BY_CEIL` or `NumberFormatState::ROUND_BY_FLOOR` +- round: [0] change round function, let's use `NumberFormat::ROUND_BY_CEIL` or `NumberFormat::ROUND_BY_FLOOR` -Here is test for [more use cases](tests/src/NumberFormatStateTest.php). +Here is test for [more use cases](tests/src/NumberFormatTest.php). + +### Format expect unit +```php +use h4kuna\Number; -### UnitFormatState -Use this class for number with unit like Kb, Mb, Gb. Unit symbol is second parameter in [method **format**](src/UnitFormatState.php). Visit [tests](tests/src/UnitFormatStateTest.php). +// set decimals as 3 +$numberFormat = new Number\NumberFormat(); +$numberFormat->enableExtendFormat('⎵ 1'); -#### Parameters +echo $numberFormat->format(1000, 3, '€'); // € 1 000,000 +``` + +#### Parameters for NumberFormat::enableExtendFormat() - mask: ['1 U'] mean 1 pattern for number and U is pattern for unit - showUnit: [TRUE] mean show unit if number is empty - nbsp: [TRUE] mean replace white space in mask by \  -### UnitPersistentFormatState +### Format persistent unit This class is same like previous, but unit is persistent like currencies or temperature. -#### Parameters -- unit: has'nt default value +```php +use h4kuna\Number; + +// set decimals as 3 +$numberFormat = new Number\NumberFormat(); +$numberFormat->enableExtendFormat('€ 1'); + +echo $numberFormat->format(1000); // € 1 000,00 +``` ### NumberFormatFactory -For all previous classes is prepared [factory class](src/NumberFormatFactory.php). This class help you create new instance and support named parameters in constructor. [Visit test](tests/src/NumberFormatFactoryTest.php) +For all previous cases is prepared [factory class](src/NumberFormatFactory.php). This class help you create new instance and support named parameters in constructor. [Visit test](tests/src/NumberFormatFactoryTest.php) ### Tax @@ -92,7 +116,7 @@ echo $percent->diff(120); // 24.0 In your neon file ```neon services: - number: h4kuna\Number\NumberFormatState(decimalPoint: '.', intOnly: 1, decimals: 1) #support named parameters by nette + number: h4kuna\Number\NumberFormat(decimalPoint: '.', intOnly: 1, decimals: 1) #support named parameters by nette latte.latteFactory: setup: diff --git a/composer.json b/composer.json index 4cfba2a..82bc06b 100644 --- a/composer.json +++ b/composer.json @@ -12,30 +12,32 @@ } ], "require": { - "php": ">=7.1", - "h4kuna/data-type": "^2.1.0" + "php": ">=7.4" }, "require-dev": { "nette/utils": "^3.0", - "salamium/testinium": "^0.1", - "phpstan/phpstan": "^0.12" + "nette/tester": "^2.0", + "tracy/tracy": "^2.0", + "phpstan/phpstan": "^1.0" }, "autoload": { "psr-4": { - "h4kuna\\Number\\": "src\\" - } + "h4kuna\\Number\\": "src" + }, + "files": [ + "src/compatibility.php" + ] }, "autoload-dev": { "psr-4": { - "h4kuna\\Number\\Tests\\": "tests\\" + "h4kuna\\Number\\Tests\\": "tests/src" } }, "config": { "sort-packages": true }, "scripts": { - "phpstan": [ - "vendor/bin/phpstan analyse -c tests/config/phpstan.neon --level max src tests" - ] + "phpstan": "vendor/bin/phpstan analyse -c tests/config/phpstan.neon --level max src tests", + "tests": "vendor/bin/tester -s -j 4 -C --colors 1 tests/src" } } diff --git a/src/Exceptions/InvalidMask.php b/src/Exceptions/InvalidStateException.php similarity index 55% rename from src/Exceptions/InvalidMask.php rename to src/Exceptions/InvalidStateException.php index d1080fd..0cf5e66 100644 --- a/src/Exceptions/InvalidMask.php +++ b/src/Exceptions/InvalidStateException.php @@ -2,7 +2,7 @@ namespace h4kuna\Number\Exceptions; -final class InvalidMask extends \RuntimeException +final class InvalidStateException extends \RuntimeException { } diff --git a/src/NumberFormat.php b/src/NumberFormat.php index 09137a0..260054b 100644 --- a/src/NumberFormat.php +++ b/src/NumberFormat.php @@ -2,12 +2,180 @@ namespace h4kuna\Number; -interface NumberFormat +use h4kuna\Number\Exceptions\InvalidStateException; + +class NumberFormat { + public const DISABLE_INT_ONLY = -1; + + public const ZERO_CLEAR = 1; + public const ZERO_IS_EMPTY = 2; + + public const ROUND_DEFAULT = 0; + public const ROUND_BY_CEIL = 1; + public const ROUND_BY_FLOOR = 2; + + /** @var string utf-8   */ + public const NBSP = "\xc2\xa0"; + + private string $thousandsSeparator; + + private int $decimals; + + private string $decimalPoint; + + private ?string $emptyValue = null; + + private int $flag = 0; + + private int $intOnly = 0; + + private \Closure $roundCallback; + + private ?string $mask = null; + + private bool $showUnit = true; + + private bool $nbsp; + + /** @var array{from: array, to: array} */ + private array $replace = [ + 'from' => ['1', '⎵'], + 'to' => ['', ''], + ]; + + + /** + * @param array|int $decimals + */ + public function __construct( + $decimals = 2, + string $decimalPoint = ',', + string $thousandsSeparator = ' ', + bool $nbsp = true, + bool $zeroIsEmpty = false, + ?string $emptyValue = null, + bool $zeroClear = false, + int $intOnly = self::DISABLE_INT_ONLY, + int $round = self::ROUND_DEFAULT + ) + { + if (Utils\Parameters::canExtract($decimals, __METHOD__)) { + extract($decimals); + } + $this->decimals = $decimals; + $this->decimalPoint = $decimalPoint; + $this->thousandsSeparator = $thousandsSeparator; + $this->nbsp = $nbsp; + + if ($emptyValue !== null) { + $this->emptyValue = $emptyValue; + } + + if ($zeroClear) { + $this->flag |= self::ZERO_CLEAR; + } + + if ($zeroIsEmpty) { + $this->flag |= self::ZERO_IS_EMPTY; + if ($this->emptyValue === null) { + $this->emptyValue = ''; + } + } + + if ($intOnly > self::DISABLE_INT_ONLY) { + $this->intOnly = 10 ** $intOnly; + } + + $this->roundCallback = self::createRound($round); + } + /** - * @param float|int|string|null $number + * @param string $mask - Mask must contains number 1 and unit (€, kg, km) or letter upper U for dynamic unit for active third parameter in method format(). */ - function format($number, string $unit = ''): string; + public function enableExtendFormat( + string $mask = '1 ⎵', + bool $showUnit = true + ): void + { + if ($this->mask !== null) { + throw new InvalidStateException('Only onetime is allowed setup.'); + } + $this->mask = $mask; + $this->showUnit = $showUnit; + + if ($this->nbsp) { + $this->replace['from'][] = ' '; + $this->replace['to'][] = self::NBSP; + } + } + + + /** + * @param string|int|float|null $number + */ + public function format($number, ?int $decimals = null, ?string $unit = null): string + { + if (((float) $number) === 0.0) { + if ($this->emptyValue === null) { + $number = 0.0; + } elseif ($this->flag & self::ZERO_IS_EMPTY || !is_numeric($number)) { + return $this->stringFormat($this->emptyValue, $unit); + } + } elseif ($this->intOnly !== 0) { + $number = intval($number) / $this->intOnly; + } + + $decimals ??= $this->decimals; + $formatted = number_format(($this->roundCallback)((float) $number, $decimals), max(0, $decimals), $this->decimalPoint, $this->thousandsSeparator); + + if ($this->flag & self::ZERO_CLEAR && $decimals > 0) { + $formatted = rtrim(rtrim($formatted, '0'), $this->decimalPoint); + } + + return $this->stringFormat($formatted, $unit); + } + + + private static function createRound(int $round): \Closure + { + if ($round === self::ROUND_BY_FLOOR) { + return static function (float $number, int $precision): float { + $move = 10 ** $precision; + + return floor($number * $move) / $move; + }; + } elseif ($round === self::ROUND_BY_CEIL) { + return static function (float $number, int $precision): float { + $move = 10 ** $precision; + + return ceil($number * $move) / $move; + }; + } + + return static function (float $number, int $precision): float { + return round($number, $precision); + }; + } + + + private function stringFormat(string $formatted, ?string $unit = null): string + { + if ($this->mask === null || ($this->showUnit === false && $this->emptyValue === $formatted || $unit === '')) { + return $this->replaceNbsp($formatted); + } + + $this->replace['to'][0] = $formatted; + $this->replace['to'][1] = $unit; + + return str_replace($this->replace['from'], $this->replace['to'], $this->mask); + } + + + private function replaceNbsp(string $formatted): string + { + return $this->nbsp === true ? str_replace(' ', self::NBSP, $formatted) : $formatted; + } } diff --git a/src/NumberFormatFactory.php b/src/NumberFormatFactory.php index 9937127..ed61257 100644 --- a/src/NumberFormatFactory.php +++ b/src/NumberFormatFactory.php @@ -10,24 +10,24 @@ class NumberFormatFactory */ public function createUnitPersistent( string $unit, - $mask = '1 U', + $mask = '1 ⎵', bool $showUnit = true, bool $nbsp = true, int $decimals = 2, string $decimalPoint = ',', - ?string $thousandsSeparator = null, + string $thousandsSeparator = ' ', bool $zeroIsEmpty = false, ?string $emptyValue = null, bool $zeroClear = false, - int $intOnly = NumberFormatState::DISABLE_INT_ONLY, - int $round = NumberFormatState::ROUND_DEFAULT - ): UnitPersistentFormatState + int $intOnly = NumberFormat::DISABLE_INT_ONLY, + int $round = NumberFormat::ROUND_DEFAULT + ): NumberFormat { if (Utils\Parameters::canExtract($mask, __METHOD__, 1)) { extract($mask); } - $uf = $this->createUnit($mask, $showUnit, $nbsp, $decimals, $decimalPoint, $thousandsSeparator, $zeroIsEmpty, $emptyValue, $zeroClear, $intOnly, $round); - return new UnitPersistentFormatState($uf, $unit); + + return $this->createUnit(str_replace('⎵', $unit, $mask), $showUnit, $nbsp, $decimals, $decimalPoint, $thousandsSeparator, $zeroIsEmpty, $emptyValue, $zeroClear, $intOnly, $round); } @@ -35,24 +35,26 @@ public function createUnitPersistent( * @param array|string $mask - can be array like named parameters ['decimalPoint' => '.'] */ public function createUnit( - $mask = '1 U', + $mask = '1 ⎵', bool $showUnit = true, bool $nbsp = true, int $decimals = 2, string $decimalPoint = ',', - ?string $thousandsSeparator = null, + string $thousandsSeparator = ' ', bool $zeroIsEmpty = false, ?string $emptyValue = null, bool $zeroClear = false, - int $intOnly = NumberFormatState::DISABLE_INT_ONLY, - int $round = NumberFormatState::ROUND_DEFAULT - ): UnitFormatState + int $intOnly = NumberFormat::DISABLE_INT_ONLY, + int $round = NumberFormat::ROUND_DEFAULT + ): NumberFormat { - if (Utils\Parameters::canExtract($mask, __METHOD__, 0)) { + if (Utils\Parameters::canExtract($mask, __METHOD__)) { extract($mask); } - $nf = $this->createNumber($decimals, $decimalPoint, $thousandsSeparator, $zeroIsEmpty, $emptyValue, $zeroClear, $intOnly, $round); - return new UnitFormatState($nf, $mask, $showUnit, $nbsp); + $nf = $this->createNumber($decimals, $decimalPoint, $thousandsSeparator, $nbsp, $zeroIsEmpty, $emptyValue, $zeroClear, $intOnly, $round); + $nf->enableExtendFormat($mask, $showUnit); + + return $nf; } @@ -62,15 +64,16 @@ public function createUnit( public function createNumber( $decimals = 2, string $decimalPoint = ',', - ?string $thousandsSeparator = null, + string $thousandsSeparator = ' ', + bool $nbsp = true, bool $zeroIsEmpty = false, ?string $emptyValue = null, bool $zeroClear = false, - int $intOnly = NumberFormatState::DISABLE_INT_ONLY, - int $round = NumberFormatState::ROUND_DEFAULT - ): NumberFormatState + int $intOnly = NumberFormat::DISABLE_INT_ONLY, + int $round = NumberFormat::ROUND_DEFAULT + ): NumberFormat { - return new NumberFormatState($decimals, $decimalPoint, $thousandsSeparator, $zeroIsEmpty, $emptyValue, $zeroClear, $intOnly, $round); + return new NumberFormat($decimals, $decimalPoint, $thousandsSeparator, $nbsp, $zeroIsEmpty, $emptyValue, $zeroClear, $intOnly, $round); } } diff --git a/src/NumberFormatState.php b/src/NumberFormatState.php deleted file mode 100644 index 5a6f341..0000000 --- a/src/NumberFormatState.php +++ /dev/null @@ -1,133 +0,0 @@ -|int $decimals - */ - public function __construct( - $decimals = 2, - string $decimalPoint = ',', - ?string $thousandsSeparator = null, - bool $zeroIsEmpty = false, - ?string $emptyValue = null, - bool $zeroClear = false, - int $intOnly = self::DISABLE_INT_ONLY, - int $round = self::ROUND_DEFAULT - ) - { - if (Utils\Parameters::canExtract($decimals, __METHOD__)) { - extract($decimals); - } - $this->precision = $decimals; - $this->decimalPoint = $decimalPoint; - $this->thousandsSeparator = $thousandsSeparator === null ? self::NBSP : $thousandsSeparator; - - if ($emptyValue !== null) { - $this->emptyValue = $emptyValue; - } - - if ($zeroClear) { - $this->flag |= self::ZERO_CLEAR; - } - - if ($zeroIsEmpty) { - $this->flag |= self::ZERO_IS_EMPTY; - if ($this->emptyValue === null) { - $this->emptyValue = ''; - } - } - - if ($intOnly > self::DISABLE_INT_ONLY) { - $this->intOnly = 10 ** $intOnly; - } - - $this->roundCallback = self::createRound($round, $decimals); - } - - - private static function createRound(int $round, int $precision): \Closure - { - if ($round === self::ROUND_BY_FLOOR) { - return static function (float $number) use ($precision): float { - $move = 10 ** $precision; - return floor($number * $move) / $move; - }; - } elseif ($round === self::ROUND_BY_CEIL) { - return static function (float $number) use ($precision): float { - $move = 10 ** $precision; - return ceil($number * $move) / $move; - }; - } - return static function (float $number) use ($precision): float { - return round($number, $precision); - }; - } - - - public function getEmptyValue(): ?string - { - return $this->emptyValue; - } - - - public function format($number, string $unit = ''): string - { - if (((float) $number) === 0.0) { - if ($this->emptyValue === null) { - $number = 0.0; - } elseif ($this->flag & self::ZERO_IS_EMPTY || !is_numeric($number)) { - return $this->emptyValue; - } - } - - if ($number != 0 && $this->intOnly !== 0) { - $number = $number / $this->intOnly; - } - - $formatted = number_format(($this->roundCallback)((float) $number), max(0, $this->precision), $this->decimalPoint, $this->thousandsSeparator); - - if ($this->flag & self::ZERO_CLEAR && $this->precision > 0) { - return rtrim(rtrim($formatted, '0'), $this->decimalPoint); - } - - return $formatted; - } - -} diff --git a/src/Percent.php b/src/Percent.php index a70fcda..f371cb3 100644 --- a/src/Percent.php +++ b/src/Percent.php @@ -5,11 +5,9 @@ final class Percent { - /** @var float */ - private $ratio; + private float $ratio; - /** @var float */ - private $percent; + private float $percent; public function __construct(float $percent) diff --git a/src/Tax.php b/src/Tax.php index b6ffcfa..a7881df 100644 --- a/src/Tax.php +++ b/src/Tax.php @@ -4,8 +4,7 @@ class Tax { - /** @var Percent */ - private $vat; + private Percent $vat; /** diff --git a/src/UnitFormatState.php b/src/UnitFormatState.php deleted file mode 100644 index 28f6a2d..0000000 --- a/src/UnitFormatState.php +++ /dev/null @@ -1,51 +0,0 @@ -numberFormatState = $numberFormatState; - $this->showUnit = $showUnit; - $this->mask = self::validMask($nbsp, $mask, $numberFormatState); - } - - - public function format($number, string $unit = ''): string - { - $formatted = $this->numberFormatState->format($number); - if ($this->showUnit === false && $this->numberFormatState->getEmptyValue() === $formatted || $unit === '') { - return $formatted; - } - return str_replace(['1', 'U'], [$formatted, $unit], $this->mask); - } - - - private static function validMask(bool $nbsp, string $mask, NumberFormatState $numberFormatState): string - { - if (substr_count($mask, 'U') !== 1 || substr_count($mask, '1') !== 1) { - throw new InvalidMask('Mask must contains number 1 and letter upper U both onetime.'); - } - return $nbsp ? str_replace(' ', $numberFormatState::NBSP, $mask) : $mask; - } - -} diff --git a/src/UnitPersistentFormatState.php b/src/UnitPersistentFormatState.php deleted file mode 100644 index 63132bd..0000000 --- a/src/UnitPersistentFormatState.php +++ /dev/null @@ -1,27 +0,0 @@ -unitFormatState = $unitFormatState; - $this->unit = $unit; - } - - - public function format($number, string $unit = ''): string - { - return $this->unitFormatState->format($number, $this->unit); - } - -} diff --git a/src/Units/Byte.php b/src/Units/Byte.php index c5cbf04..28a2823 100644 --- a/src/Units/Byte.php +++ b/src/Units/Byte.php @@ -4,7 +4,6 @@ class Byte extends Unit { - public const UNITS = [ Unit::BASE => 0, Unit::KILO => 3, diff --git a/src/Units/Unit.php b/src/Units/Unit.php index 6963d61..628ebf4 100644 --- a/src/Units/Unit.php +++ b/src/Units/Unit.php @@ -7,7 +7,6 @@ class Unit { - public const PETA = 'P'; public const TERA = 'T'; public const GIGA = 'G'; @@ -40,10 +39,9 @@ class Unit * These values must be sort ascending! See self::UNITS * @var array */ - protected $allowedUnits; + protected array $allowedUnits; - /** @var string */ - private $from; + private string $from; /** @@ -80,10 +78,8 @@ public function convert(float $number, ?string $unitTo = null): Utils\UnitValue /** - * @param float $number * @param string|null $unitFrom - NULL mean defined in constructor * @param string|null $unitTo - NULL mean automatic - * @return Utils\UnitValue */ public function convertFrom(float $number, ?string $unitFrom, ?string $unitTo = null): Utils\UnitValue { @@ -160,10 +156,10 @@ private function checkUnit(string $unit): void public static function createUnitValue(float $value, string $unit): Utils\UnitValue { - return new Utils\UnitValue([ - 'value' => $value, - 'unit' => $unit, - ]); + return new Utils\UnitValue( + $value, + $unit + ); } diff --git a/src/Units/UnitFormat.php b/src/Units/UnitFormat.php index 739706f..71ca59d 100644 --- a/src/Units/UnitFormat.php +++ b/src/Units/UnitFormat.php @@ -7,17 +7,14 @@ class UnitFormat { - /** @var string */ - private $symbol; + private string $symbol; - /** @var Unit */ - private $unit; + private Unit $unit; - /** @var Number\UnitFormatState */ - private $unitFormatState; + private Number\NumberFormat $unitFormatState; - public function __construct(string $symbol, Unit $unit, Number\UnitFormatState $unitFormatState) + public function __construct(string $symbol, Unit $unit, Number\NumberFormat $unitFormatState) { $this->symbol = $symbol; $this->unit = $unit; @@ -32,14 +29,13 @@ public function convert(float $number, ?string $unitTo = null): string /** - * @param float $number * @param string|null $unitFrom - NULL mean defined in constructor * @param string|null $unitTo - NULL mean automatic - * @return string */ public function convertFrom(float $number, ?string $unitFrom, ?string $unitTo = null): string { $unitValue = $this->unit->convertFrom($number, $unitFrom, $unitTo); + return $this->format($unitValue->value, $unitValue->unit . $this->symbol); } @@ -47,13 +43,14 @@ public function convertFrom(float $number, ?string $unitFrom, ?string $unitTo = public function fromString(string $value, string $unitTo = Unit::BASE): string { $unitValue = $this->unit->fromString($value, $unitTo); + return $this->format($unitValue->value, $unitValue->unit); } private function format(float $value, string $unit): string { - return $this->unitFormatState->format($value, $unit); + return $this->unitFormatState->format($value, null, $unit); } } diff --git a/src/Utils/Parameters.php b/src/Utils/Parameters.php index 090150d..7c66949 100644 --- a/src/Utils/Parameters.php +++ b/src/Utils/Parameters.php @@ -9,7 +9,7 @@ class Parameters /** * @var array */ - private static $parameters = []; + private static array $parameters = []; /** diff --git a/src/Utils/UnitValue.php b/src/Utils/UnitValue.php index 709035b..09e536f 100644 --- a/src/Utils/UnitValue.php +++ b/src/Utils/UnitValue.php @@ -2,18 +2,23 @@ namespace h4kuna\Number\Utils; -use h4kuna\DataType\Immutable\Messenger; - -/** - * @property-read float $value - * @property-read string $unit - */ -final class UnitValue extends Messenger +final class UnitValue { + public /* readonly */ float $value; + + public /* readonly */ string $unit; + + + public function __construct(float $value, string $unit) + { + $this->value = $value; + $this->unit = $unit; + } + public function __toString() { - return $this['value'] . ' ' . $this['unit']; + return $this->value . ' ' . $this->unit; } } diff --git a/src/compatibility.php b/src/compatibility.php new file mode 100644 index 0000000..91ded31 --- /dev/null +++ b/src/compatibility.php @@ -0,0 +1,14 @@ +format(12)); diff --git a/tests/src/NumberFormatFactoryTest.php b/tests/src/NumberFormatFactoryTest.php index 7f8c4d4..07a1f41 100644 --- a/tests/src/NumberFormatFactoryTest.php +++ b/tests/src/NumberFormatFactoryTest.php @@ -17,11 +17,11 @@ final class NumberFormatFactoryTest extends TestCase public function testUnit(): void { $nff = new NumberFormatFactory(); - $uf = $nff->createUnit('1U'); - Assert::same('1,00kg', $uf->format(1, 'kg')); + $uf = $nff->createUnit('1⎵'); + Assert::same('1,00kg', $uf->format(1, null, 'kg')); - $uf = $nff->createUnit(['mask' => '1U', 'decimalPoint' => '.']); - Assert::same('1.00kg', $uf->format(1, 'kg')); + $uf = $nff->createUnit(['mask' => '1⎵', 'decimalPoint' => '.']); + Assert::same('1.00kg', $uf->format(1, null, 'kg')); } @@ -39,6 +39,7 @@ public function testUnitPersistent(): void $cf = $nff->createUnitPersistent('CZK', ['decimalPoint' => '.', 'nbsp' => false, 'decimals' => 1]); Assert::same('1.0 CZK', $cf->format(1)); } + } (new NumberFormatFactoryTest())->run(); diff --git a/tests/src/NumberFormatStateTest.php b/tests/src/NumberFormatStateTest.php deleted file mode 100644 index a8e4007..0000000 --- a/tests/src/NumberFormatStateTest.php +++ /dev/null @@ -1,181 +0,0 @@ -format('1')); - Assert::same('1,00', $nf->format(1)); - Assert::same('-1,00', $nf->format(-1)); - Assert::same('0,00', $nf->format(0)); - Assert::same('1,10', $nf->format(1.1)); - Assert::same('1,11', $nf->format(1.111)); - Assert::same('1,12', $nf->format(1.115)); - Assert::same('1' . $nf::NBSP . '000,00', $nf->format(1000)); - Assert::same('0,00', $nf->format(null)); - } - - - public function testNamedParameters(): void - { - $nf = new NumberFormatState([ - 'emptyValue' => '-', - 'zeroIsEmpty' => true, - 'decimalPoint' => '.', - 'thousandsSeparator' => ',', - 'decimals' => 3, - 'intOnly' => 4, - 'zeroClear' => true, - ]); - Assert::same('1,000.01', $nf->format(10000100)); - Assert::same('1,000.001', $nf->format(10000011)); - Assert::same('-', $nf->format(0)); - } - - - public function testDecimal(): void - { - $nf = new NumberFormatState(1); - Assert::same('1,0', $nf->format(1)); - - $nf = new NumberFormatState(0); - Assert::same('1', $nf->format(1)); - - $nf = new NumberFormatState(-1); - Assert::same('10', $nf->format(11)); - Assert::same('20', $nf->format(15)); - Assert::same('20', $nf->format('15')); - } - - - public function testFloor(): void - { - $nf = new NumberFormatState(['decimals' => 1, 'round' => NumberFormatState::ROUND_BY_FLOOR]); - Assert::same('1,1', $nf->format(1.15)); - Assert::same('1,1', $nf->format(1.14)); - Assert::same('1,2', $nf->format(1.21)); - - $nf = new NumberFormatState(['decimals' => 0, 'round' => NumberFormatState::ROUND_BY_FLOOR]); - Assert::same('1', $nf->format(1)); - Assert::same('1', $nf->format(1.5)); - Assert::same('1', $nf->format(1.4)); - Assert::same('0', $nf->format(0.9)); - - $nf = new NumberFormatState(['decimals' => -1, 'round' => NumberFormatState::ROUND_BY_FLOOR]); - Assert::same('10', $nf->format(11)); - Assert::same('10', $nf->format(15)); - Assert::same('20', $nf->format('20')); - } - - - public function testCeil(): void - { - $nf = new NumberFormatState(['decimals' => 1, 'round' => NumberFormatState::ROUND_BY_CEIL]); - Assert::same('1,1', $nf->format(1.1)); - Assert::same('1,2', $nf->format(1.15)); - Assert::same('1,2', $nf->format(1.14)); - Assert::same('1,3', $nf->format(1.21)); - - $nf = new NumberFormatState(['decimals' => 0, 'round' => NumberFormatState::ROUND_BY_CEIL]); - Assert::same('1', $nf->format(1)); - Assert::same('2', $nf->format(1.5)); - Assert::same('2', $nf->format(1.4)); - Assert::same('1', $nf->format(0.9)); - - $nf = new NumberFormatState(['decimals' => -1, 'round' => NumberFormatState::ROUND_BY_CEIL]); - Assert::same('20', $nf->format(11)); - Assert::same('20', $nf->format(15)); - Assert::same('20', $nf->format('20')); - } - - - public function testDecimalPointThousandSep(): void - { - $nf = new NumberFormatState(2, '.', ','); - Assert::same('1,000.00', $nf->format(1000)); - - $nf = new NumberFormatState(2, NumberFormatState::NBSP . ','); - Assert::same('1' . $nf::NBSP . ',00', $nf->format(1)); - } - - - public function testZeroIsEmpty(): void - { - $nf = new NumberFormatState(2, ',', ' ', true, '-'); - Assert::same('-', $nf->format(0)); - Assert::same('-', $nf->format(0.0)); - Assert::same('-', $nf->format('0')); - Assert::same('-', $nf->format('0.0')); - Assert::same('-', $nf->format(null)); - - $nf = new NumberFormatState(2, ',', ' ', true, null); - Assert::same('', $nf->format(null)); - Assert::same('', $nf->format(0)); - Assert::same('', $nf->format(0.0)); - Assert::same('', $nf->format('0')); - Assert::same('', $nf->format('0.0')); - - $nf = new NumberFormatState(2, ',', ' ', false, null); - Assert::same('0,00', $nf->format(null)); - } - - - public function testEmptyValue(): void - { - $nf = new NumberFormatState(2, ',', ' ', false, '-'); - Assert::same('-', $nf->format(null)); - Assert::same('-', $nf->format('')); - Assert::same('0,00', $nf->format(0)); - Assert::same('0,00', $nf->format(0.0)); - Assert::same('0,00', $nf->format('0')); - Assert::same('0,00', $nf->format('0.0')); - } - - - public function testZeroClear(): void - { - $nf = new NumberFormatState(4, ',', ' ', false, null, true); - Assert::same('0', $nf->format(0)); - Assert::same('0', $nf->format(0.0)); - Assert::same('0', $nf->format('0')); - Assert::same('0', $nf->format('0.0')); - Assert::same('1,1', $nf->format(1.100)); - Assert::same('1,112', $nf->format(1.11195)); - } - - - public function testIntOnly(): void - { - $nf = new NumberFormatState(2, ',', ' ', false, null, false, 4); - Assert::same('0,00', $nf->format(0)); - Assert::same('1,00', $nf->format(10000)); - Assert::same('1 000,35', $nf->format(10003500)); - Assert::same('1 001,00', $nf->format(10009999)); - Assert::same('-1,00', $nf->format(-10000)); - } - - - public function testIntOnlyZero(): void - { - $nf = new NumberFormatState(2, ',', ' ', false, null, false, 0); - Assert::same('0,00', $nf->format(0)); - Assert::same('10 000,00', $nf->format(10000)); - Assert::same('10 003 500,00', $nf->format(10003500)); - } - -} - -(new NumberFormatStateTest())->run(); diff --git a/tests/src/NumberFormatTest.php b/tests/src/NumberFormatTest.php new file mode 100644 index 0000000..19cdc48 --- /dev/null +++ b/tests/src/NumberFormatTest.php @@ -0,0 +1,103 @@ +enableExtendFormat(); + Assert::same('1,00' . NumberFormat::NBSP . 'kg', $nf->format(1, null, 'kg')); + } + + + public function testMask(): void + { + $nf = new NumberFormat(); + $nf->enableExtendFormat('⎵1'); + Assert::same('kg1,00', $nf->format(1, null, 'kg')); + + $nf = new NumberFormat(); + $nf->enableExtendFormat('1-⎵'); + Assert::same('1,00-g', $nf->format(1, null, 'g')); + } + + + public function testVariableUnitMakeNothing(): void + { + $nf = new NumberFormat(); + Assert::same('1,655', $nf->format(1.654987, 3, 'kg')); + } + + + public function testVariableDecimalsUnit(): void + { + $nf = new NumberFormat(['nbsp' => false]); + $nf->enableExtendFormat(); + Assert::same('1,655 kg', $nf->format(1.654987, 3, 'kg')); + } + + + public function testVariableDecimalsCUD(): void + { + $nf = new NumberFormat(['nbsp' => false]); + $nf->enableExtendFormat('1 CUD'); + Assert::same('1,655 CUD', $nf->format(1.654987, 3)); + } + + + public function testVariableDecimalsThirdParameterIsDisabled(): void + { + $nf = new NumberFormat(['nbsp' => false]); + $nf->enableExtendFormat('1 CUD'); + Assert::same('1,655 CUD', $nf->format(1.654987, 3, 'be')); + } + + + public function testVariableDecimals(): void + { + $nf = new NumberFormat(); + Assert::same('1,655', $nf->format(1.654987, 3)); + } + + + public function testNbsp(): void + { + $nf = new NumberFormat(['nbsp' => false]); + $nf->enableExtendFormat(); + Assert::same('1,00 kg', $nf->format(1, null, 'kg')); + } + + + public function testNbspEveryWhere(): void + { + $nf = new NumberFormat(['thousandsSeparator' => ' ']); + $nf->enableExtendFormat('1 ⎵ Foo'); + Assert::same(str_replace('%s', NumberFormat::NBSP, '1%s000,00%skg%sFoo'), $nf->format(1000, null, 'kg')); + } + + + public function testEmptyValue(): void + { + $nf = new NumberFormat(2, ',', ' ', true, false, '-'); + $nf->enableExtendFormat('1⎵'); + Assert::same('-kg', $nf->format(null, null, 'kg')); + + $nf = new NumberFormat(2, ',', ' ', true, false, '-'); + $nf->enableExtendFormat('1⎵', false); + Assert::same('-', $nf->format(null, null, 'kg')); + } + +} + +(new NumberFormatTest())->run(); diff --git a/tests/TestCase.php b/tests/src/TestCase.php similarity index 100% rename from tests/TestCase.php rename to tests/src/TestCase.php diff --git a/tests/src/UnitFormatStateTest.php b/tests/src/UnitFormatStateTest.php deleted file mode 100644 index e9a70fa..0000000 --- a/tests/src/UnitFormatStateTest.php +++ /dev/null @@ -1,62 +0,0 @@ -format(1, 'kg')); - } - - - public function testMask(): void - { - $nf = new UnitFormatState(new NumberFormatState(), 'U1'); - Assert::same('kg1,00', $nf->format(1, 'kg')); - - $nf = new UnitFormatState(new NumberFormatState(), '1-U'); - Assert::same('1,00-g', $nf->format(1, 'g')); - } - - - /** - * @throws \h4kuna\Number\Exceptions\InvalidMask - */ - public function testInvalidMask(): void - { - new UnitFormatState(new NumberFormatState(), '1U-1U'); - } - - - public function testNbsp(): void - { - $nf = new UnitFormatState(new NumberFormatState(), '1 U', true, false); - Assert::same('1,00 kg', $nf->format(1, 'kg')); - } - - - public function testEmptyValue(): void - { - $nf = new UnitFormatState(new NumberFormatState(2, ',', null, false, '-'), '1U'); - Assert::same('-kg', $nf->format(null, 'kg')); - - $nf = new UnitFormatState(new NumberFormatState(2, ',', null, false, '-'), '1U', false); - Assert::same('-', $nf->format(null, 'kg')); - } -} - -(new UnitFormatStateTest())->run();