diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4f8bb68 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,6 @@ +.gitattributes export-ignore +.gitignore export-ignore +.travis.yml export-ignore +composer.lock export-ignore +tests/ export-ignore +*.md export-ignore \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..77e472e --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Folders +/vendor +# Files +/composer.lock +/tests/*.log +/tests/tmp diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9f65f46 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +language: php +php: + - 5.4 + - 5.5 + - 5.6 + - 7.0 + - hhvm + +matrix: + allow_failures: + - php: hhvm + +script: + - vendor/bin/tester tests/Formatter -s -p php + +after_failure: + # Print *.actual content + - 'for i in $(find tests -name \*.actual); do echo "--- $i"; cat $i; echo; echo; done' + +before_script: + # Update Composer + - composer self-update + + # Install Nette Tester + - composer install --no-interaction --prefer-source diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..7cba745 --- /dev/null +++ b/composer.json @@ -0,0 +1,23 @@ +{ + "name": "minetro/formatter", + "description": "Simple string formatter for Latte", + "type": "library", + "license": ["BSD-3-Clause"], + "homepage": "https://github.com/minetro/formatter", + "authors": [ + { + "name": "Milan Felix Sulc", + "homepage": "http://jfx.cz" + } + ], + "require": { + "php": ">=5.4.0", + "nette/utils": ">=2.3.0,<2.4.0" + }, + "require-dev": { + "nette/tester": "~1.3.0" + }, + "autoload": { + "classmap": ["src/"] + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..0e7ee2e --- /dev/null +++ b/readme.md @@ -0,0 +1,31 @@ +# Events + +[![Build Status](https://travis-ci.org/minetro/formatter.svg?branch=master)](https://travis-ci.org/minetro/formatter) +[![Downloads this Month](https://img.shields.io/packagist/dm/minetro/formatter.svg?style=flat)](https://packagist.org/packages/minetro/formatter) +[![Latest stable](https://img.shields.io/packagist/v/minetro/formatter.svg?style=flat)](https://packagist.org/packages/minetro/formatter) +[![HHVM Status](https://img.shields.io/hhvm/minetro/formatter.svg?style=flat)](http://hhvm.h4cc.de/package/minetro/formatter) + +Simple string formatter for Latte. + +## Install + +```sh +$ composer require minetro/formatter:~1.0.0 +``` + +## Usage + +### Register in config + +Register in your config file (e.q. config.neon). + +```neon +services + + formatter.number: + class: Minetro\Formatter\NumberFormatter('Kč') + + nette.latteFactory: + setup: + - addFilter(money, [@formatter.number,format]) +``` \ No newline at end of file diff --git a/src/IFormatter.php b/src/IFormatter.php new file mode 100644 index 0000000..68066fa --- /dev/null +++ b/src/IFormatter.php @@ -0,0 +1,18 @@ + + */ +interface IFormatter +{ + + /** + * @param mixed $value + * @return mixed + */ + function format($value); +} diff --git a/src/NumberFormatter.php b/src/NumberFormatter.php new file mode 100644 index 0000000..a1ea795 --- /dev/null +++ b/src/NumberFormatter.php @@ -0,0 +1,202 @@ + + */ +class NumberFormatter implements IFormatter +{ + /** UTF-8   */ + CONST NBSP = "\xc2\xa0"; + + /** Default mask */ + CONST DEFAULT_MASK = "%d"; + + /** @var int|float */ + private $rawValue; + + /** @var string */ + private $thousand = ' '; + + /** @var int */ + private $decimals = 2; + + /** @var string */ + private $point = ','; + + /** @var callable */ + private $callback; + + /** @var bool */ + private $zeros = FALSE; + + /** @var bool */ + private $strict = TRUE; + + /** @var bool */ + private $spaces = TRUE; + + /** @var string */ + private $prefix; + + /** @var string */ + private $suffix; + + /** @var Html */ + private $wrapper; + + /** + * @param string $suffix + * @param string $prefix + */ + function __construct($suffix = NULL, $prefix = NULL) + { + $this->suffix = $suffix; + $this->prefix = $prefix; + + $this->wrapper = Html::el(); + } + + /** + * @param int $decimals + */ + public function setDecimals($decimals) + { + $this->decimals = intval($decimals); + } + + /** + * @param string $point + */ + public function setPoint($point) + { + $this->point = $point; + } + + /** + * @param string $thousand + */ + public function setThousand($thousand) + { + $this->thousand = $thousand; + } + + /** + * @param boolean $zeros + */ + public function setZeros($zeros) + { + $this->zeros = (bool)$zeros; + } + + /** + * @param string $suffix + */ + public function setSuffix($suffix) + { + $this->suffix = $suffix; + } + + /** + * @param string $prefix + */ + public function setPrefix($prefix) + { + $this->prefix = $prefix; + } + + /** + * @param boolean $strict + */ + public function setStrict($strict) + { + $this->strict = (bool)$strict; + } + + /** + * @param boolean $spaces + */ + public function setSpaces($spaces) + { + $this->spaces = (bool)$spaces; + } + + /** + * @param callable $callback + */ + public function setCallback($callback) + { + $this->callback = $callback; + } + + /** + * @return Html + */ + public function prototype() + { + return $this->wrapper; + } + + /** + * @param int|float $value + * @param int $decimals + * @return mixed + */ + public function format($value, $decimals = NULL) + { + $value = trim($value); + $value = str_replace(',', '.', $value); + + if (!is_numeric($value)) { + if ($this->strict) { + throw new InvalidArgumentException("Value must be numeric"); + } else { + return $value; + } + } + + $this->rawValue = $value; + + if ($decimals == NULL) { + $decimals = $this->decimals; + } + + if ($decimals < 0) { + $value = round($value, $decimals); + $decimals = 0; + } + + $number = number_format((float)$value, $decimals, $this->point, $this->thousand); + + if ($decimals > 0 && !$this->zeros) { + $number = rtrim(rtrim($number, '0'), $this->point); + } + + if ($this->callback) { + return call_user_func_array($this->callback, [$this->prefix, $number, $this->suffix]); + } else if ($this->spaces) { + return trim(sprintf("%s %s %s", $this->prefix, $number, $this->suffix)); + } else { + return trim(sprintf("%s%s%s", $this->prefix, $number, $this->suffix)); + } + } + + /** + * @param int|float $value + * @param int $decimals + * @return Html + */ + public function formatHtml($value, $decimals = NULL) + { + $wrapper = clone $this->wrapper; + $wrapper->setHtml($this->format($value, $decimals)); + return $wrapper; + } + +} diff --git a/tests/Formatter/NumberFormatter.phpt b/tests/Formatter/NumberFormatter.phpt new file mode 100644 index 0000000..ba29dd4 --- /dev/null +++ b/tests/Formatter/NumberFormatter.phpt @@ -0,0 +1,174 @@ +format(10)); +}); + +test(function () { + $formatter = new NumberFormatter(); + Assert::same('10,1', $formatter->format(10.1)); + Assert::same('10 000', $formatter->format(10000)); +}); + +test(function () { + $formatter = new NumberFormatter(); + Assert::same('10,1', $formatter->format(10.10)); + Assert::same('10,1', $formatter->format(10.10000)); +}); + +test(function () { + $formatter = new NumberFormatter(); + Assert::same('10,1', $formatter->format(' 10.10 ')); + Assert::same('10,1', $formatter->format('10.10 ')); + Assert::same('10,1', $formatter->format(' 10.10')); +}); + +test(function () { + $suffix = 'cm'; + $formatter = new NumberFormatter($suffix); + Assert::same('10,1 cm', $formatter->format('10.1')); + Assert::same('10,1 cm', $formatter->format('10.1 ')); + Assert::same('10,1 cm', $formatter->format(' 10.1 ')); + + $suffix = ' cm '; + $formatter = new NumberFormatter($suffix); + Assert::same('10,1 cm', $formatter->format('10.1')); + Assert::same('10,1 cm', $formatter->format('10.1 ')); + Assert::same('10,1 cm', $formatter->format(' 10.1 ')); +}); + +test(function () { + $prefix = '~'; + $formatter = new NumberFormatter(NULL, $prefix); + Assert::same('~ 10,1', $formatter->format('10.1')); + + $prefix = ' ~'; + $formatter = new NumberFormatter(NULL, $prefix); + Assert::same('~ 10,1', $formatter->format('10.1')); +}); + +test(function () { + $prefix = '~'; + $suffix = 'cm'; + $formatter = new NumberFormatter($suffix, $prefix); + Assert::same('~ 10,1 cm', $formatter->format('10.1')); +}); + +test(function () { + $prefix1 = '~1'; + $prefix2 = '~2'; + $suffix1 = 'cm1'; + $suffix2 = 'cm2'; + $formatter = new NumberFormatter($suffix1, $prefix2); + $formatter->setSuffix($suffix2); + $formatter->setPrefix($prefix2); + Assert::same('~2 10,1 cm2', $formatter->format('10.1')); +}); + +test(function () { + $formatter = new NumberFormatter(); + $formatter->setPoint('.'); + Assert::same('10.1', $formatter->format('10.1')); +}); + +test(function () { + $formatter = new NumberFormatter(); + $formatter->setDecimals(2); + Assert::same('10,12', $formatter->format('10.123')); + Assert::same('10,13', $formatter->format('10.125')); + + $formatter->setDecimals(-2); + Assert::same('12 500', $formatter->format('12534.20')); + + $formatter->setDecimals(-2); + Assert::same('12 534,222', $formatter->format('12534.222', 3)); + Assert::same('12 534,23', $formatter->format('12534.225', 2)); +}); + +test(function () { + $prefix = '~'; + $formatter = new NumberFormatter(NULL, $prefix); + $formatter->setSpaces(FALSE); + Assert::same('~10,1', $formatter->format('10.1')); + + $postfix = 'cm'; + $formatter = new NumberFormatter($postfix, NULL); + $formatter->setSpaces(FALSE); + Assert::same('10,1cm', $formatter->format('10.1')); +}); + +test(function () { + $formatter = new NumberFormatter(); + $formatter->setThousand('-'); + Assert::same('10-000', $formatter->format('10000')); +}); + +test(function () { + $formatter = new NumberFormatter(); + $formatter->setZeros(FALSE); + Assert::same('10,1', $formatter->format('10.100')); + + $formatter->setZeros(TRUE); + Assert::same('10,10', $formatter->format('10.100')); + + $formatter->setDecimals(3); + $formatter->setZeros(TRUE); + Assert::same('10,100', $formatter->format('10.100')); +}); + +test(function () { + $formatter = new NumberFormatter(); + $formatter->setStrict(TRUE); + Assert::throws(function () use ($formatter) { + $formatter->format('word..'); + }, 'InvalidArgumentException'); + + $formatter->setStrict(FALSE); + Assert::same('word', $formatter->format('word')); +}); + +test(function () { + $formatter = new NumberFormatter(); + $formatter->setCallback(function ($prefix, $value, $suffix) { + return array($prefix, $value, $suffix); + }); + + $res = $formatter->format('10.1'); + Assert::same(NULL, $res[0]); + Assert::same('10,1', $res[1]); + Assert::same(NULL, $res[2]); +}); + +test(function () { + $formatter = new NumberFormatter(); + + $p = $formatter->prototype(); + Assert::equal($p, $formatter->prototype()); + + $p->setName('div'); + Assert::same('div', $p->getName()); +}); + +test(function () { + $formatter = new NumberFormatter(); + + $p = $formatter->prototype(); + $p->setName('div'); + Assert::same('
10,1
', (string)$formatter->formatHtml('10.1')); +}); diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 0000000..dd71535 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,24 @@ +