Skip to content

Commit

Permalink
NumberFormat: see changelog
Browse files Browse the repository at this point in the history
  • Loading branch information
h4kuna committed Jan 14, 2023
1 parent 4e1c9a4 commit a76f3a1
Show file tree
Hide file tree
Showing 28 changed files with 436 additions and 595 deletions.
1 change: 0 additions & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
.gitattributes export-ignore
.gitignore export-ignore
.travis.yml export-ignore
tests/ export-ignore
*.sh eol=lf
28 changes: 28 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -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
27 changes: 0 additions & 27 deletions .travis.yml

This file was deleted.

52 changes: 38 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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+.

Expand All @@ -24,18 +34,17 @@ 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.

```php
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
```
Expand All @@ -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 \&nbsp

### 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

Expand All @@ -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:
Expand Down
22 changes: 12 additions & 10 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace h4kuna\Number\Exceptions;

final class InvalidMask extends \RuntimeException
final class InvalidStateException extends \RuntimeException
{

}
174 changes: 171 additions & 3 deletions src/NumberFormat.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>, to: array<string>} */
private array $replace = [
'from' => ['1', ''],
'to' => ['', ''],
];


/**
* @param array<string, bool|int|string|null>|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;
}

}
Loading

0 comments on commit a76f3a1

Please sign in to comment.