From 8bed9c256a3d47982e5a1c5fcf860228751de859 Mon Sep 17 00:00:00 2001 From: Emmanuel Valverde Ramos Date: Mon, 15 Jul 2019 15:20:17 +0200 Subject: [PATCH 01/24] Created shell to work with Decimal part as integer --- Src/Domain/Decimal.php | 35 +++++++++++++++++++++++++++++ Src/Domain/DecimalPartInterface.php | 12 ++++++++++ Src/Domain/NumericPartInterface.php | 12 ++++++++++ Src/Domain/NumericPrice.php | 32 -------------------------- Src/Domain/PriceAble.php | 10 --------- Src/Domain/StringPrice.php | 25 --------------------- 6 files changed, 59 insertions(+), 67 deletions(-) create mode 100644 Src/Domain/Decimal.php create mode 100644 Src/Domain/DecimalPartInterface.php create mode 100644 Src/Domain/NumericPartInterface.php delete mode 100644 Src/Domain/NumericPrice.php delete mode 100644 Src/Domain/PriceAble.php delete mode 100644 Src/Domain/StringPrice.php diff --git a/Src/Domain/Decimal.php b/Src/Domain/Decimal.php new file mode 100644 index 0000000..df5a4a2 --- /dev/null +++ b/Src/Domain/Decimal.php @@ -0,0 +1,35 @@ +decimal = $decimal; + } + + public function __toString(): string + { + return (string) $this->decimal; + } + + public function getDecimals(): int + { + return $this->decimal; + } + + public function equals(DecimalPartInterface $numericPart): bool + { + return $this->getDecimals() === $numericPart->getDecimals(); + } + + public function __invoke(): int + { + return $this->getDecimals(); + } +} diff --git a/Src/Domain/DecimalPartInterface.php b/Src/Domain/DecimalPartInterface.php new file mode 100644 index 0000000..4d706ce --- /dev/null +++ b/Src/Domain/DecimalPartInterface.php @@ -0,0 +1,12 @@ +integer = $integer; - $this->decimal = $decimal; - } - - public function getFloatingPrice(): float - { - return (float) ($this->integer . '.' . $this->decimal); - } - - public function __toString() - { - return (string) $this->integer . '.' . $this->decimal; - } - - public static function createPrice(int $integer, int $decimal): self - { - return new static($integer, $decimal); - } -} diff --git a/Src/Domain/PriceAble.php b/Src/Domain/PriceAble.php deleted file mode 100644 index 5df8bd6..0000000 --- a/Src/Domain/PriceAble.php +++ /dev/null @@ -1,10 +0,0 @@ -setPrice($price); - } - - private function setPrice(string $price): void - { - $this->price = $price; - } - - public function __toString(): string - { - return $this->price; - } -} From d7486b4b655af509d17a9e9e2aedbb90c7646248 Mon Sep 17 00:00:00 2001 From: Sikay Date: Mon, 15 Jul 2019 19:22:44 +0200 Subject: [PATCH 02/24] Parte entera de Number --- Src/Domain/IntegerPart.php | 97 ++++++++++++++++++++++++++++++++++++++ Src/Domain/Numerable.php | 16 +++++++ 2 files changed, 113 insertions(+) create mode 100644 Src/Domain/IntegerPart.php create mode 100644 Src/Domain/Numerable.php diff --git a/Src/Domain/IntegerPart.php b/Src/Domain/IntegerPart.php new file mode 100644 index 0000000..e1e3c0e --- /dev/null +++ b/Src/Domain/IntegerPart.php @@ -0,0 +1,97 @@ +setIntegerPart($integerPart); + } + + private function setIntegerPart(string $integerPart) + { + $this->integerPart = $this->parseIntegerPart($integerPart); + } + + private function parseIntegerPart(string $integer): string + { + $default = $this->defaultValuesForIntegerValidation($integer); + if (null !== $default) { + return $default; + } + + $this->checkNumber($integer); + + return $integer; + } + + private function defaultValuesForIntegerValidation(string $integer): ?string + { + if (self::EMPTY_STRING === $integer || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integer) { + return self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + if (self::NEGATIVE_SIGN === $integer) { + return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + return null; + } + + private function checkNumber(string $integer): void + { + $nonZero = false; + $characters = strlen($integer); + for ($position = 0; $position < $characters; ++$position) { + $digit = $integer[$position]; + + if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { + throw new \InvalidArgumentException( + sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $integer, $digit) + ); + } + + if (false === $nonZero && '0' === $digit) { + throw new \InvalidArgumentException( + 'Leading zeros are not allowed' + ); + } + + $nonZero = true; + } + } + + public function isNegative(): bool + { + return self::NEGATIVE_SIGN === $this->integerPart[0]; + } + + public function __invoke(): string + { + $this->getIntegerPart(); + } + + public function getIntegerPart(): string + { + return $this->integerPart; + } + + public function equals (self $integerPart): bool + { + return $this->getIntegerPart() === $integerPart->getIntegerPart(); + } +} \ No newline at end of file diff --git a/Src/Domain/Numerable.php b/Src/Domain/Numerable.php new file mode 100644 index 0000000..21e56ed --- /dev/null +++ b/Src/Domain/Numerable.php @@ -0,0 +1,16 @@ + 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; +} \ No newline at end of file From c4653f3d0c98bdf910bb9b950d6e91d661dfe4da Mon Sep 17 00:00:00 2001 From: Sikay Date: Mon, 15 Jul 2019 21:08:15 +0200 Subject: [PATCH 03/24] Parte entera de Number --- Src/Domain/IntegerPart.php | 97 ++++++++++++++++++++++++++++++++++++++ Src/Domain/Numerable.php | 16 +++++++ 2 files changed, 113 insertions(+) create mode 100644 Src/Domain/IntegerPart.php create mode 100644 Src/Domain/Numerable.php diff --git a/Src/Domain/IntegerPart.php b/Src/Domain/IntegerPart.php new file mode 100644 index 0000000..e1e3c0e --- /dev/null +++ b/Src/Domain/IntegerPart.php @@ -0,0 +1,97 @@ +setIntegerPart($integerPart); + } + + private function setIntegerPart(string $integerPart) + { + $this->integerPart = $this->parseIntegerPart($integerPart); + } + + private function parseIntegerPart(string $integer): string + { + $default = $this->defaultValuesForIntegerValidation($integer); + if (null !== $default) { + return $default; + } + + $this->checkNumber($integer); + + return $integer; + } + + private function defaultValuesForIntegerValidation(string $integer): ?string + { + if (self::EMPTY_STRING === $integer || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integer) { + return self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + if (self::NEGATIVE_SIGN === $integer) { + return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + return null; + } + + private function checkNumber(string $integer): void + { + $nonZero = false; + $characters = strlen($integer); + for ($position = 0; $position < $characters; ++$position) { + $digit = $integer[$position]; + + if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { + throw new \InvalidArgumentException( + sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $integer, $digit) + ); + } + + if (false === $nonZero && '0' === $digit) { + throw new \InvalidArgumentException( + 'Leading zeros are not allowed' + ); + } + + $nonZero = true; + } + } + + public function isNegative(): bool + { + return self::NEGATIVE_SIGN === $this->integerPart[0]; + } + + public function __invoke(): string + { + $this->getIntegerPart(); + } + + public function getIntegerPart(): string + { + return $this->integerPart; + } + + public function equals (self $integerPart): bool + { + return $this->getIntegerPart() === $integerPart->getIntegerPart(); + } +} \ No newline at end of file diff --git a/Src/Domain/Numerable.php b/Src/Domain/Numerable.php new file mode 100644 index 0000000..21e56ed --- /dev/null +++ b/Src/Domain/Numerable.php @@ -0,0 +1,16 @@ + 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; +} \ No newline at end of file From f40790e019afe6e52c45a246af0ca23b092028ea Mon Sep 17 00:00:00 2001 From: Emmanuel Valverde Ramos Date: Tue, 16 Jul 2019 12:37:54 +0200 Subject: [PATCH 04/24] Make the Decimal class, create validations for the number class and adjust the test --- Src/Domain/Decimal.php | 53 ++++++++++++- Src/Domain/DecimalPartInterface.php | 2 +- Src/Domain/Number.php | 88 ++++++++++----------- Src/Domain/NumberFormatDecorator.php | 2 +- Src/Domain/NumericPartInterface.php | 2 +- Tests/NumberTest.php | 111 ++++++++++++--------------- 6 files changed, 142 insertions(+), 116 deletions(-) diff --git a/Src/Domain/Decimal.php b/Src/Domain/Decimal.php index df5a4a2..b171703 100644 --- a/Src/Domain/Decimal.php +++ b/Src/Domain/Decimal.php @@ -6,19 +6,33 @@ class Decimal implements NumericPartInterface, DecimalPartInterface { + private const EMPTY_STRING = ''; + private const VALIDATOR_REGEX = '/^[\d]+$/m'; + private $decimal; - private function __construct(int $decimal) + private function __construct(string $decimal) + { + $this->setDecimal($decimal); + } + + public static function fromString(string $decimal): self + { + return new self($decimal); + } + + private function setDecimal(string $decimal): void { + $this->validateDecimal($decimal); $this->decimal = $decimal; } public function __toString(): string { - return (string) $this->decimal; + return $this->decimal; } - public function getDecimals(): int + public function getDecimals(): string { return $this->decimal; } @@ -28,8 +42,39 @@ public function equals(DecimalPartInterface $numericPart): bool return $this->getDecimals() === $numericPart->getDecimals(); } - public function __invoke(): int + public function __invoke(): string { return $this->getDecimals(); } + + private function validateDecimal(string $number): bool + { + if (self::EMPTY_STRING === $number) { + return true; + } + + $this->mustBeNumeric($number); + + return $this->validValueForADecimal($number); + } + + private function mustBeNumeric(string $number) + { + if (!is_numeric($number)) { + throw new \InvalidArgumentException( + sprintf('Invalid decimal part %1$s. Is not numeric', $number) + ); + } + } + + private function validValueForADecimal($number): bool + { + try { + $number = (string) $number; + } catch (\Exception $e) { + return false; + } + + return (bool) preg_match_all(self::VALIDATOR_REGEX, $number, $matches, PREG_SET_ORDER, 0); + } } diff --git a/Src/Domain/DecimalPartInterface.php b/Src/Domain/DecimalPartInterface.php index 4d706ce..e29ad97 100644 --- a/Src/Domain/DecimalPartInterface.php +++ b/Src/Domain/DecimalPartInterface.php @@ -6,7 +6,7 @@ interface DecimalPartInterface { - public function getDecimals(): int; + public function getDecimals(): string; public function equals(DecimalPartInterface $decimalPart): bool; } diff --git a/Src/Domain/Number.php b/Src/Domain/Number.php index c8a927c..6aeb779 100644 --- a/Src/Domain/Number.php +++ b/Src/Domain/Number.php @@ -7,8 +7,7 @@ final class Number { private $integerPart; - - private $fractionalPart; + private $decimals; private const EMPTY_STRING = ''; private const FLOAT_FORMAT = '%.14F'; @@ -16,19 +15,26 @@ final class Number private const NUMERIC_SEPARATOR = '.'; private const DEFAULT_POSITIVE_NUMERIC_VALUE = '0'; private const FIRST_CHART = '0'; + private const HALF_DECIMAL_VALUE = '5'; + private const VALIDATOR_REGEX = '/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/m'; + private const VALID_NUMBER_MSG = 'Valid numeric value expected'; - private const VALID_NUMBERS = [0 => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; + private const VALID_NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - private function __construct(string $integerPart, string $fractionalPart = '') + private function __construct(string $integerPart, string $decimals = '') { - $this->preconditionNotNullArguments($integerPart, $fractionalPart); + $this->notNullArguments($integerPart, $decimals); - $this->integerPart = $this->parseIntegerPart((string) $integerPart); - $this->fractionalPart = $this->parseFractionalPart((string) $fractionalPart); + $this->integerPart = $this->parseIntegerPart($integerPart); + $this->decimals = Decimal::fromString($decimals)->__toString(); } - public static function fromString(string $number): self + private static function fromString(string $number): self { + if (!static::validNumber($number)) { + throw new \InvalidArgumentException(self::VALID_NUMBER_MSG); + } + $decimalSeparatorPosition = strpos($number, self::NUMERIC_SEPARATOR); if (false === $decimalSeparatorPosition) { return new self($number, self::EMPTY_STRING); @@ -40,15 +46,6 @@ public static function fromString(string $number): self ); } - public static function fromFloat(float $number): self - { - if (false === is_float($number)) { - throw new \InvalidArgumentException('Floating point value expected'); - } - - return self::fromString(sprintf(self::FLOAT_FORMAT, $number)); - } - /** * @param float|int|string $number * @@ -68,7 +65,7 @@ public static function fromNumber($number): self return self::fromString($number); } - throw new \InvalidArgumentException('Valid numeric value expected'); + throw new \InvalidArgumentException(self::VALID_NUMBER_MSG); } public function __invoke(): string @@ -78,26 +75,26 @@ public function __invoke(): string public function __toString(): string { - if (self::EMPTY_STRING === $this->fractionalPart) { + if (self::EMPTY_STRING === $this->decimals) { return $this->integerPart; } - return $this->integerPart . self::NUMERIC_SEPARATOR . $this->fractionalPart; + return $this->integerPart . self::NUMERIC_SEPARATOR . $this->decimals; } public function isDecimal(): bool { - return self::EMPTY_STRING !== $this->fractionalPart; + return self::EMPTY_STRING !== $this->decimals; } public function isInteger(): bool { - return self::EMPTY_STRING === $this->fractionalPart; + return self::EMPTY_STRING === $this->decimals; } public function isHalf(): bool { - return '5' === $this->fractionalPart; + return self::HALF_DECIMAL_VALUE === $this->getDecimals(); } public function isCurrentEven(): bool @@ -109,17 +106,17 @@ public function isCurrentEven(): bool public function isCloserToNext(): bool { - if (self::EMPTY_STRING === $this->fractionalPart) { + if (self::EMPTY_STRING === $this->getDecimals()) { return false; } - return $this->fractionalPart[0] >= 5; + return $this->getDecimals()[0] >= 5; } public function toFloat(): float { - return (self::EMPTY_STRING !== $this->fractionalPart) ? - (float) ($this->integerPart . self::NUMERIC_SEPARATOR . $this->fractionalPart) : + return (self::EMPTY_STRING !== $this->getDecimals()) ? + (float) ($this->integerPart . self::NUMERIC_SEPARATOR . $this->getDecimals()) : (float) $this->integerPart; } @@ -133,9 +130,9 @@ public function getIntegerPart(): string return $this->integerPart; } - public function getFractionalPart(): string + public function getDecimals(): string { - return $this->fractionalPart; + return $this->decimals; } private function parseIntegerPart(string $number): string @@ -181,34 +178,27 @@ private function defaultValuesForIntegerValidation(string $number): ?string return null; } - private function parseFractionalPart(string $number): string + public function equals(self $number): bool { - if (self::EMPTY_STRING === $number) { - return $number; - } - - for ($position = 0, $characters = strlen($number); $position < $characters; ++$position) { - $digit = $number[$position]; - if (!isset(self::VALID_NUMBERS[$digit])) { - throw new \InvalidArgumentException( - sprintf('Invalid fractional part %1$s. Invalid digit %2$s found', $number, $digit) - ); - } - } - - return $number; + return $this->integerPart === $number->getIntegerPart() && + $this->getDecimals() === $number->getDecimals(); } - public function equal(self $number): bool + private function validNumber($number): bool { - return $this->integerPart === $number->getIntegerPart() && - $this->fractionalPart === $number->getFractionalPart(); + try { + $number = (string) $number; + } catch (\Exception $e) { + return false; + } + + return (bool) preg_match_all(self::VALIDATOR_REGEX, $number, $matches, PREG_SET_ORDER, 0); } - private function preconditionNotNullArguments(string $integerPart, string $fractionalPart) + private function notNullArguments(string $integerPart, string $fractionalPart) { if (self::EMPTY_STRING === $integerPart && self::EMPTY_STRING === $fractionalPart) { - throw new \InvalidArgumentException('Empty number is invalid'); + throw new \InvalidArgumentException('An empty number is invalid'); } } } diff --git a/Src/Domain/NumberFormatDecorator.php b/Src/Domain/NumberFormatDecorator.php index 9646971..01bda36 100644 --- a/Src/Domain/NumberFormatDecorator.php +++ b/Src/Domain/NumberFormatDecorator.php @@ -32,7 +32,7 @@ public function roundedNumber(float $round = 0.5): Number } if (!is_null($round) && !is_null($result) && mb_strlen($result) > mb_strlen($round)) { - $result = $this->numberFormat($result, strlen(($this->number->getFractionalPart()))); + $result = $this->numberFormat($result, strlen(($this->number->getDecimals()))); } return Number::fromFloat($result); diff --git a/Src/Domain/NumericPartInterface.php b/Src/Domain/NumericPartInterface.php index 9cb09a1..8e61da3 100644 --- a/Src/Domain/NumericPartInterface.php +++ b/Src/Domain/NumericPartInterface.php @@ -8,5 +8,5 @@ interface NumericPartInterface { public function __toString(): string; - public function __invoke(): int; + public function __invoke(): string; } diff --git a/Tests/NumberTest.php b/Tests/NumberTest.php index 06b6c53..77680f2 100644 --- a/Tests/NumberTest.php +++ b/Tests/NumberTest.php @@ -50,19 +50,10 @@ class NumberTest extends TestCase /** * @test */ - public function typeErrorForNamedConstructorFloat() + public function shouldThrowInvalidArgumentForNamedConstructorString() { - $this->expectException(\TypeError::class); - Number::fromFloat(null); - } - - /** - * @test - */ - public function typeErrorForNamedConstructorString() - { - $this->expectException(\TypeError::class); - Number::fromString(null); + $this->expectException(\InvalidArgumentException::class); + Number::fromNumber(null); } /** @@ -80,16 +71,16 @@ public function typeErrorForNamedConstructorNumber() public function shouldThrowInvalidArgumentExceptionOnEmptyString() { $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::EMPTY_STRING); + Number::fromNumber(self::EMPTY_STRING); } /** * @test */ - public function shouldThrowTypeErrorOnEmptyArray() + public function shouldThrowInvalidArgumentOnEmptyArray() { - $this->expectException(\TypeError::class); - Number::fromString(self::EMPTY_ARRAY); + $this->expectException(\InvalidArgumentException::class); + Number::fromNumber(self::EMPTY_ARRAY); } /** @@ -98,7 +89,7 @@ public function shouldThrowTypeErrorOnEmptyArray() public function shouldThrowInvalidArgumentExceptionOnFromBadFormattedNumberString() { $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::A_BAD_FORMATTED_NUMBER_ARRAY); + Number::fromNumber(self::A_BAD_FORMATTED_NUMBER_ARRAY); } /** @@ -107,7 +98,7 @@ public function shouldThrowInvalidArgumentExceptionOnFromBadFormattedNumberStrin public function shouldNotCreateANumberFromAStringWithTwoPoints() { $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::AN_OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_POINTS_STRING); + Number::fromNumber(self::AN_OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_POINTS_STRING); } /** @@ -116,7 +107,7 @@ public function shouldNotCreateANumberFromAStringWithTwoPoints() public function shouldNotCreateANumberFromAStringWithPointAndComma() { $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::OTHER_BAD_FORMATTED_NUMBER_STRING); + Number::fromNumber(self::OTHER_BAD_FORMATTED_NUMBER_STRING); } /** @@ -125,7 +116,7 @@ public function shouldNotCreateANumberFromAStringWithPointAndComma() public function shouldNotCreateANumberFromAStringWithTwoCommas() { $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_COMMAS_STRING); + Number::fromNumber(self::OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_COMMAS_STRING); } //************************** @@ -138,7 +129,7 @@ public function shouldNotCreateANumberFromAStringWithTwoCommas() public function shouldThrowInvalidArgumentExceptionOnEmptyStringWithSpaces() { $this->expectException(\InvalidArgumentException::class); - Number::fromString(self::EMPTY_STRING_WITH_SPACES); + Number::fromNumber(self::EMPTY_STRING_WITH_SPACES); } /** @@ -146,7 +137,7 @@ public function shouldThrowInvalidArgumentExceptionOnEmptyStringWithSpaces() */ public function shouldCreateNumberFromAPositiveDecimalString() { - $number = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -155,7 +146,7 @@ public function shouldCreateNumberFromAPositiveDecimalString() */ public function shouldCreateNumberFromANegativeDecimalString() { - $number = Number::fromString(self::A_NEGATIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::A_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -164,7 +155,7 @@ public function shouldCreateNumberFromANegativeDecimalString() */ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPartString() { - $number = Number::fromString(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -173,7 +164,7 @@ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPartStri */ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPartString() { - $number = Number::fromString(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING); + $number = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING); $this->assertTrue(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -182,7 +173,7 @@ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPartStri */ public function shouldCreateNumberFromAPositiveIntegerString() { - $number = Number::fromString(self::A_POSITIVE_INTEGER_STRING); + $number = Number::fromNumber(self::A_POSITIVE_INTEGER_STRING); $this->assertTrue(self::A_POSITIVE_INTEGER_STRING === $number->__toString()); } @@ -191,7 +182,7 @@ public function shouldCreateNumberFromAPositiveIntegerString() */ public function shouldCreateNumberFromANegativeIntegerString() { - $number = Number::fromString(self::A_NEGATIVE_INTEGER_STRING); + $number = Number::fromNumber(self::A_NEGATIVE_INTEGER_STRING); $this->assertTrue(self::A_NEGATIVE_INTEGER_STRING === $number->__toString()); } @@ -200,7 +191,7 @@ public function shouldCreateNumberFromANegativeIntegerString() */ public function shouldRemoveTailingZerosFromFloat() { - $number = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -209,8 +200,8 @@ public function shouldRemoveTailingZerosFromFloat() */ public function shouldCompareTwoStringFloatsAsEquals() { - $number_a = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $number_b = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -219,8 +210,8 @@ public function shouldCompareTwoStringFloatsAsEquals() */ public function shouldCompareTowStringFloatOneWithTailingZerosAsEquals() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -233,7 +224,7 @@ public function shouldCompareTowStringFloatOneWithTailingZerosAsEquals() */ public function shouldCreateNumberFromAPositiveFloat() { - $number = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === (string) $number); } @@ -242,7 +233,7 @@ public function shouldCreateNumberFromAPositiveFloat() */ public function shouldCreateNumberFromAPositiveDecimal() { - $number = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue(self::A_POSITIVE_DECIMAL_NUMBER_STRING === (string) $number); } @@ -251,7 +242,7 @@ public function shouldCreateNumberFromAPositiveDecimal() */ public function shouldCreateNumberFromANegativeDecimal() { - $number = Number::fromFloat(self::A_NEGATIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER); $this->assertTrue(self::A_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -260,7 +251,7 @@ public function shouldCreateNumberFromANegativeDecimal() */ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPart() { - $number = Number::fromFloat(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); $this->assertTrue(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -269,7 +260,7 @@ public function shouldCreateNumberFromAPositiveDecimalWithOnlyFractionalPart() */ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPart() { - $number = Number::fromFloat(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER); + $number = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER); $this->assertTrue(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING === $number->__toString()); } @@ -278,7 +269,7 @@ public function shouldCreateNumberFromANegativeDecimalWithOnlyFractionalPart() */ public function shouldCreateNumberFromAPositiveInteger() { - $number = Number::fromFloat(self::A_POSITIVE_INTEGER); + $number = Number::fromNumber(self::A_POSITIVE_INTEGER); $this->assertTrue(self::A_POSITIVE_INTEGER_STRING === $number->__toString()); } @@ -287,7 +278,7 @@ public function shouldCreateNumberFromAPositiveInteger() */ public function shouldCreateNumberFromANegativeInteger() { - $number = Number::fromFloat(self::A_NEGATIVE_INTEGER); + $number = Number::fromNumber(self::A_NEGATIVE_INTEGER); $this->assertTrue(self::A_NEGATIVE_INTEGER_STRING === $number->__toString()); } @@ -296,8 +287,8 @@ public function shouldCreateNumberFromANegativeInteger() */ public function shouldCompareTwoFloatAsEquals() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -306,8 +297,8 @@ public function shouldCompareTwoFloatAsEquals() */ public function shouldCompareTwoFloatOneWithTailingZerosAsEquals() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $this->assertTrue($number_a->toFloat() === $number_b->toFloat()); } @@ -320,9 +311,9 @@ public function shouldCompareTwoFloatOneWithTailingZerosAsEquals() */ public function shouldBeEqualsComparingTwoEqualAndPositiveFloats() { - $number_a = Number::fromFloat(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $this->assertFalse($number_a->equal($number_b)); + $number_a = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $this->assertFalse($number_a->equals($number_b)); } /** @@ -330,9 +321,9 @@ public function shouldBeEqualsComparingTwoEqualAndPositiveFloats() */ public function shouldNotBeEqualsComparingTwoDifferentAndPositiveFloats() { - $number_a = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $number_b = Number::fromFloat(self::A_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -342,7 +333,7 @@ public function shouldBeEqualsComparingTwoEqualAndPositiveFloatsWithNumberConstr { $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -350,9 +341,9 @@ public function shouldBeEqualsComparingTwoEqualAndPositiveFloatsWithNumberConstr */ public function shouldBeEqualsComparingTwoEqualFloatsWithStringConstructor() { - $number_a = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $number_b = Number::fromString(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -362,7 +353,7 @@ public function shouldBeEqualTwoNumbersWithTailingZeros() { $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -372,7 +363,7 @@ public function shouldBeEqualTwoNegativeNumbersUnderOneWithTailingZeros() { $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS); $number_b = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -382,7 +373,7 @@ public function shouldBeEqualTwoNegativeNumbersUnderOneWithTailingZerosString() { $number_a = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -392,7 +383,7 @@ public function shouldBeEqualTwoPositiveNumbersUnderOneWithTailingZerosString() { $number_a = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -402,7 +393,7 @@ public function shouldBeEqualTwoPositiveNumbersUnderOneWithTailingZeros() { $number_a = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER_TAILING_ZEROS); $number_b = Number::fromNumber(self::AN_OTHER_POSITIVE_DECIMAL_NUMBER); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -412,7 +403,7 @@ public function shouldBeEqualTwoNegativeNumbersWithTailingZeros() { $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS_STRING); $number_b = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** @@ -422,7 +413,7 @@ public function shouldBeEqualTwoNegativeDecimalNumbersWithTailingZeros() { $number_a = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_TAILING_ZEROS); $number_b = Number::fromNumber(self::AN_OTHER_NEGATIVE_DECIMAL_NUMBER_STRING); - $this->assertTrue($number_a->equal($number_b)); + $this->assertTrue($number_a->equals($number_b)); } /** From d8f726ee406d5123693600717c576d80dae3068e Mon Sep 17 00:00:00 2001 From: Emmanuel Valverde Ramos Date: Tue, 16 Jul 2019 13:00:31 +0200 Subject: [PATCH 05/24] Making a small refactor on the Number class to work with the Decimal class but never expose the Decimal class to others --- Src/Domain/Number.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/Domain/Number.php b/Src/Domain/Number.php index 6aeb779..2c8aafa 100644 --- a/Src/Domain/Number.php +++ b/Src/Domain/Number.php @@ -26,7 +26,7 @@ private function __construct(string $integerPart, string $decimals = '') $this->notNullArguments($integerPart, $decimals); $this->integerPart = $this->parseIntegerPart($integerPart); - $this->decimals = Decimal::fromString($decimals)->__toString(); + $this->decimals = Decimal::fromString($decimals); } private static function fromString(string $number): self @@ -75,11 +75,11 @@ public function __invoke(): string public function __toString(): string { - if (self::EMPTY_STRING === $this->decimals) { + if (self::EMPTY_STRING === $this->getDecimals()) { return $this->integerPart; } - return $this->integerPart . self::NUMERIC_SEPARATOR . $this->decimals; + return $this->integerPart . self::NUMERIC_SEPARATOR . $this->getDecimals(); } public function isDecimal(): bool @@ -132,7 +132,7 @@ public function getIntegerPart(): string public function getDecimals(): string { - return $this->decimals; + return $this->decimals->__toString(); } private function parseIntegerPart(string $number): string From ace1ca40fedb1dfa57a95e175f68aff10cde1cfc Mon Sep 17 00:00:00 2001 From: Emmanuel Valverde Ramos Date: Tue, 16 Jul 2019 16:11:03 +0200 Subject: [PATCH 06/24] Created some test to the Deciamal Class and how it should behaviour --- Src/Domain/Decimal.php | 15 ++- .../Exception/DecimalInvalidArgument.php | 7 ++ .../Exception/NumberInvalidArgument.php | 7 ++ Src/Domain/Number.php | 36 ++++---- Tests/DecimalTest.php | 81 +++++++++++++++++ Tests/NumberTest.php | 91 +++++++++++++++++-- 6 files changed, 207 insertions(+), 30 deletions(-) create mode 100644 Src/Domain/Exception/DecimalInvalidArgument.php create mode 100644 Src/Domain/Exception/NumberInvalidArgument.php create mode 100644 Tests/DecimalTest.php diff --git a/Src/Domain/Decimal.php b/Src/Domain/Decimal.php index b171703..55b62f1 100644 --- a/Src/Domain/Decimal.php +++ b/Src/Domain/Decimal.php @@ -4,6 +4,8 @@ namespace WeDev\Price\Domain; +use WeDev\Price\Domain\Exception\DecimalInvalidArgument; + class Decimal implements NumericPartInterface, DecimalPartInterface { private const EMPTY_STRING = ''; @@ -23,7 +25,11 @@ public static function fromString(string $decimal): self private function setDecimal(string $decimal): void { - $this->validateDecimal($decimal); + if (!$this->validateDecimal($decimal)) { + throw new DecimalInvalidArgument( + sprintf('Invalid decimal part %1$s. Is not numeric', $decimal) + ); + } $this->decimal = $decimal; } @@ -52,7 +58,6 @@ private function validateDecimal(string $number): bool if (self::EMPTY_STRING === $number) { return true; } - $this->mustBeNumeric($number); return $this->validValueForADecimal($number); @@ -61,7 +66,7 @@ private function validateDecimal(string $number): bool private function mustBeNumeric(string $number) { if (!is_numeric($number)) { - throw new \InvalidArgumentException( + throw new DecimalInvalidArgument( sprintf('Invalid decimal part %1$s. Is not numeric', $number) ); } @@ -72,7 +77,9 @@ private function validValueForADecimal($number): bool try { $number = (string) $number; } catch (\Exception $e) { - return false; + throw new DecimalInvalidArgument( + sprintf('Invalid decimal part %1$s. Is not numeric', $number) + ); } return (bool) preg_match_all(self::VALIDATOR_REGEX, $number, $matches, PREG_SET_ORDER, 0); diff --git a/Src/Domain/Exception/DecimalInvalidArgument.php b/Src/Domain/Exception/DecimalInvalidArgument.php new file mode 100644 index 0000000..7bc0480 --- /dev/null +++ b/Src/Domain/Exception/DecimalInvalidArgument.php @@ -0,0 +1,7 @@ +notNullArguments($integerPart, $decimals); - $this->integerPart = $this->parseIntegerPart($integerPart); + $this->integer = $this->parseIntegerPart($integerPart); $this->decimals = Decimal::fromString($decimals); } private static function fromString(string $number): self { if (!static::validNumber($number)) { - throw new \InvalidArgumentException(self::VALID_NUMBER_MSG); + throw new NumberInvalidArgument(self::VALID_NUMBER_MSG); } $decimalSeparatorPosition = strpos($number, self::NUMERIC_SEPARATOR); @@ -65,7 +67,7 @@ public static function fromNumber($number): self return self::fromString($number); } - throw new \InvalidArgumentException(self::VALID_NUMBER_MSG); + throw new NumberInvalidArgument(self::VALID_NUMBER_MSG); } public function __invoke(): string @@ -76,10 +78,10 @@ public function __invoke(): string public function __toString(): string { if (self::EMPTY_STRING === $this->getDecimals()) { - return $this->integerPart; + return $this->integer; } - return $this->integerPart . self::NUMERIC_SEPARATOR . $this->getDecimals(); + return $this->integer . self::NUMERIC_SEPARATOR . $this->getDecimals(); } public function isDecimal(): bool @@ -99,7 +101,7 @@ public function isHalf(): bool public function isCurrentEven(): bool { - $lastIntegerPartNumber = $this->integerPart[strlen($this->integerPart) - 1]; + $lastIntegerPartNumber = $this->integer[strlen($this->integer) - 1]; return 0 === $lastIntegerPartNumber % 2; } @@ -110,24 +112,24 @@ public function isCloserToNext(): bool return false; } - return $this->getDecimals()[0] >= 5; + return (int) $this->getDecimals()[0] >= 5; } public function toFloat(): float { return (self::EMPTY_STRING !== $this->getDecimals()) ? - (float) ($this->integerPart . self::NUMERIC_SEPARATOR . $this->getDecimals()) : - (float) $this->integerPart; + (float) ($this->integer . self::NUMERIC_SEPARATOR . $this->getDecimals()) : + (float) $this->integer; } public function isNegative(): bool { - return self::NEGATIVE_SIGN === $this->integerPart[0]; + return self::NEGATIVE_SIGN === $this->integer[0]; } - public function getIntegerPart(): string + public function getInteger(): string { - return $this->integerPart; + return $this->integer; } public function getDecimals(): string @@ -148,13 +150,13 @@ private function parseIntegerPart(string $number): string $digit = $number[$position]; if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { - throw new \InvalidArgumentException( + throw new NumberInvalidArgument( sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $number, $digit) ); } if (false === $nonZero && '0' === $digit) { - throw new \InvalidArgumentException( + throw new NumberInvalidArgument( 'Leading zeros are not allowed' ); } @@ -180,7 +182,7 @@ private function defaultValuesForIntegerValidation(string $number): ?string public function equals(self $number): bool { - return $this->integerPart === $number->getIntegerPart() && + return $this->integer === $number->getInteger() && $this->getDecimals() === $number->getDecimals(); } @@ -198,7 +200,7 @@ private function validNumber($number): bool private function notNullArguments(string $integerPart, string $fractionalPart) { if (self::EMPTY_STRING === $integerPart && self::EMPTY_STRING === $fractionalPart) { - throw new \InvalidArgumentException('An empty number is invalid'); + throw new NumberInvalidArgument('An empty number is invalid'); } } } diff --git a/Tests/DecimalTest.php b/Tests/DecimalTest.php new file mode 100644 index 0000000..eccb6fc --- /dev/null +++ b/Tests/DecimalTest.php @@ -0,0 +1,81 @@ +expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::A_BAD_FORMATTED_DECIMAL); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalTwoFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL_2); + } + + /** + * @test + */ + public function shouldCreateAWellFormattedDecimal(): void + { + $decimal = Decimal::fromString(self::A_WELL_FORMATTED_DECIMAL); + $this->assertTrue(self::A_WELL_FORMATTED_DECIMAL === $decimal->getDecimals()); + } + + /** + * @test + */ + public function shouldBeEqual(): void + { + $a_decimal = Decimal::fromString(self::A_WELL_FORMATTED_DECIMAL); + $an_other_decimal = Decimal::fromString(self::A_WELL_FORMATTED_DECIMAL); + $this->assertTrue($an_other_decimal->equals($a_decimal)); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalFourFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL_4); + } + + /** + * @test + */ + public function shouldNotCreateAnOtherDecimalTheeFromBadFormattedDecimal(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL_3); + } +} diff --git a/Tests/NumberTest.php b/Tests/NumberTest.php index 77680f2..3039a52 100644 --- a/Tests/NumberTest.php +++ b/Tests/NumberTest.php @@ -3,6 +3,7 @@ namespace WeDev\Price\Tests; use PHPUnit\Framework\TestCase; +use WeDev\Price\Domain\Exception\NumberInvalidArgument; use WeDev\Price\Domain\Number; class NumberTest extends TestCase @@ -52,7 +53,7 @@ class NumberTest extends TestCase */ public function shouldThrowInvalidArgumentForNamedConstructorString() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(null); } @@ -61,7 +62,7 @@ public function shouldThrowInvalidArgumentForNamedConstructorString() */ public function typeErrorForNamedConstructorNumber() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(null); } @@ -70,7 +71,7 @@ public function typeErrorForNamedConstructorNumber() */ public function shouldThrowInvalidArgumentExceptionOnEmptyString() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(self::EMPTY_STRING); } @@ -79,7 +80,7 @@ public function shouldThrowInvalidArgumentExceptionOnEmptyString() */ public function shouldThrowInvalidArgumentOnEmptyArray() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(self::EMPTY_ARRAY); } @@ -88,7 +89,7 @@ public function shouldThrowInvalidArgumentOnEmptyArray() */ public function shouldThrowInvalidArgumentExceptionOnFromBadFormattedNumberString() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(self::A_BAD_FORMATTED_NUMBER_ARRAY); } @@ -97,7 +98,7 @@ public function shouldThrowInvalidArgumentExceptionOnFromBadFormattedNumberStrin */ public function shouldNotCreateANumberFromAStringWithTwoPoints() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(self::AN_OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_POINTS_STRING); } @@ -106,7 +107,7 @@ public function shouldNotCreateANumberFromAStringWithTwoPoints() */ public function shouldNotCreateANumberFromAStringWithPointAndComma() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(self::OTHER_BAD_FORMATTED_NUMBER_STRING); } @@ -115,7 +116,7 @@ public function shouldNotCreateANumberFromAStringWithPointAndComma() */ public function shouldNotCreateANumberFromAStringWithTwoCommas() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(self::OTHER_BAD_FORMATTED_NUMBER_WITH_TWO_COMMAS_STRING); } @@ -128,7 +129,7 @@ public function shouldNotCreateANumberFromAStringWithTwoCommas() */ public function shouldThrowInvalidArgumentExceptionOnEmptyStringWithSpaces() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(NumberInvalidArgument::class); Number::fromNumber(self::EMPTY_STRING_WITH_SPACES); } @@ -487,4 +488,76 @@ public function shouldNotBehalfByNegativeDecimalNumberBiggerThanHalfString() $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF_STRING); $this->assertFalse($number_a->isHalf()); } + + /** + * @test + */ + public function shouldBeCloseToNextNumberFloat() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF); + $this->assertTrue($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNumberFloat() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNegativeNumberFloat() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldBeCloseToNextNegativeNumberFloat() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF); + $this->assertTrue($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldBeCloseToNextNumberString() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF_STRING); + $this->assertTrue($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNumberString() + { + $number_a = Number::fromNumber(self::A_POSITIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF_STRING); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldNotBeCloseToNextNegativeNumberString() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_SMALLER_THAN_HALF_STRING); + $this->assertFalse($number_a->isCloserToNext()); + } + + /** + * @test + */ + public function shouldBeCloseToNextNegativeNumberString() + { + $number_a = Number::fromNumber(self::A_NEGATIVE_DECIMAL_NUMBER_BIGGER_THAN_HALF_STRING); + $this->assertTrue($number_a->isCloserToNext()); + } } From 481a069ee4a0b862925dd922110c410772d48d59 Mon Sep 17 00:00:00 2001 From: Emmanuel Valverde Ramos Date: Tue, 16 Jul 2019 16:29:33 +0200 Subject: [PATCH 07/24] Added a test with a random String --- Tests/DecimalTest.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Tests/DecimalTest.php b/Tests/DecimalTest.php index eccb6fc..d65597e 100644 --- a/Tests/DecimalTest.php +++ b/Tests/DecimalTest.php @@ -14,6 +14,7 @@ class DecimalTest extends TestCase private const A_WELL_FORMATTED_DECIMAL = '5151'; private const AN_OTHER_BAD_FORMATTED_DECIMAL_3 = '51.51'; private const AN_OTHER_BAD_FORMATTED_DECIMAL_4 = '5,1.51'; + private const A_RANDOM_STRING = 'sdlk,jgfhjhskdhgfkjshdhgkhkjhsdkjfhsdkjh'; /** * @test @@ -78,4 +79,13 @@ public function shouldNotCreateAnOtherDecimalTheeFromBadFormattedDecimal(): void $this->expectException(DecimalInvalidArgument::class); Decimal::fromString(self::AN_OTHER_BAD_FORMATTED_DECIMAL_3); } + + /** + * @test + */ + public function shouldNotCreatDecimalFromAString(): void + { + $this->expectException(DecimalInvalidArgument::class); + Decimal::fromString(self::A_RANDOM_STRING); + } } From af3f813139468f3d86d033406cc31238098c79d7 Mon Sep 17 00:00:00 2001 From: Sikay Date: Tue, 16 Jul 2019 17:35:56 +0200 Subject: [PATCH 08/24] Small refactoring, modified construct to private, created an interface for integerPart --- Src/Domain/IntegerPart.php | 51 ++++++++++++++++------------- Src/Domain/IntegerPartInterface.php | 16 +++++++++ Src/Domain/Numerable.php | 2 +- 3 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 Src/Domain/IntegerPartInterface.php diff --git a/Src/Domain/IntegerPart.php b/Src/Domain/IntegerPart.php index e1e3c0e..ef172d7 100644 --- a/Src/Domain/IntegerPart.php +++ b/Src/Domain/IntegerPart.php @@ -10,79 +10,84 @@ namespace WeDev\Price\Domain; -class integerPart extends Numerable +class integerPart extends Numerable implements IntegerPartInterface { private $integerPart; private const DEFAULT_POSITIVE_NUMERIC_VALUE = '0'; private const NEGATIVE_SIGN = '-'; + private const START_WITH_ZERO_NOT_ALLOWED_MGS = 'Leading zeros are not allowed'; + private const INVALID_INTEGER_PART_MGS = 'Invalid integer part %1$s. Invalid digit %2$s found'; - public function __construct(string $integerPart) + private function __construct(string $integerPart) { $this->setIntegerPart($integerPart); } + public static function fromString(string $integerPart) + { + return new self($integerPart); + } + private function setIntegerPart(string $integerPart) { $this->integerPart = $this->parseIntegerPart($integerPart); } - private function parseIntegerPart(string $integer): string + private function parseIntegerPart(string $integerPart): string { - $default = $this->defaultValuesForIntegerValidation($integer); - if (null !== $default) { - return $default; + $defaultIntegerValue = $this->defaultValuesForIntegerValidation($integerPart); + if (null !== $defaultIntegerValue) { + return $defaultIntegerValue; } - $this->checkNumber($integer); + $this->validateIntegerPart($integerPart); - return $integer; + return $integerPart; } - private function defaultValuesForIntegerValidation(string $integer): ?string + private function defaultValuesForIntegerValidation(string $integerPart): ?string { - if (self::EMPTY_STRING === $integer || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integer) { + if (self::EMPTY_STRING === $integerPart || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integerPart) { return self::DEFAULT_POSITIVE_NUMERIC_VALUE; } - if (self::NEGATIVE_SIGN === $integer) { + if (self::NEGATIVE_SIGN === $integerPart) { return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; } return null; } - private function checkNumber(string $integer): void + private function validateIntegerPart(string $integerPart): void { $nonZero = false; - $characters = strlen($integer); + $characters = strlen($integerPart); for ($position = 0; $position < $characters; ++$position) { - $digit = $integer[$position]; + $digit = $integerPart[$position]; if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { throw new \InvalidArgumentException( - sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $integer, $digit) + sprintf(self::INVALID_INTEGER_PART_MGS, $integerPart, $digit) ); } if (false === $nonZero && '0' === $digit) { - throw new \InvalidArgumentException( - 'Leading zeros are not allowed' - ); + throw new \InvalidArgumentException(self::START_WITH_ZERO_NOT_ALLOWED_MGS); } $nonZero = true; } } - public function isNegative(): bool + public function __invoke(): string { - return self::NEGATIVE_SIGN === $this->integerPart[0]; + $this->getIntegerPart(); } - public function __invoke(): string + public function __toString(): string { - $this->getIntegerPart(); + return $this->integerPart; } public function getIntegerPart(): string @@ -90,7 +95,7 @@ public function getIntegerPart(): string return $this->integerPart; } - public function equals (self $integerPart): bool + public function equals (IntegerPartInterface $integerPart): bool { return $this->getIntegerPart() === $integerPart->getIntegerPart(); } diff --git a/Src/Domain/IntegerPartInterface.php b/Src/Domain/IntegerPartInterface.php new file mode 100644 index 0000000..331cbb8 --- /dev/null +++ b/Src/Domain/IntegerPartInterface.php @@ -0,0 +1,16 @@ + 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; + protected const VALID_NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; } \ No newline at end of file From 4fca211592b576c619dc1aa9d8379847958e582f Mon Sep 17 00:00:00 2001 From: Sikay Date: Thu, 18 Jul 2019 17:30:13 +0200 Subject: [PATCH 09/24] Validation by regex, name changes to integer and created exception to integer --- .../Exception/IntegerInvalidArgument.php | 8 ++ Src/Domain/Integer.php | 80 ++++++++++++++ Src/Domain/IntegerInterface.php | 10 ++ Src/Domain/IntegerPart.php | 102 ------------------ Src/Domain/IntegerPartInterface.php | 16 --- 5 files changed, 98 insertions(+), 118 deletions(-) create mode 100644 Src/Domain/Exception/IntegerInvalidArgument.php create mode 100644 Src/Domain/Integer.php create mode 100644 Src/Domain/IntegerInterface.php delete mode 100644 Src/Domain/IntegerPart.php delete mode 100644 Src/Domain/IntegerPartInterface.php diff --git a/Src/Domain/Exception/IntegerInvalidArgument.php b/Src/Domain/Exception/IntegerInvalidArgument.php new file mode 100644 index 0000000..ffc21ec --- /dev/null +++ b/Src/Domain/Exception/IntegerInvalidArgument.php @@ -0,0 +1,8 @@ +setInteger($integer); + } + + public static function fromString(string $integer) + { + return new self($integer); + } + + private function setInteger(string $integer) + { + $this->validateInteger($integer); + $this->integer = $integer; + } + + private function validateInteger(string $integer) + { + if (self::EMPTY_STRING === $integer) { + throw new IntegerInvalidArgument( + sprintf(self::EMPTY_STRING_MGS, $integer) + ); + } + + $this->validValueForAInteger($integer); + } + + private function validValueForAInteger($integer) + { + if (!preg_match(self::VALIDATOR_REGEX, $integer)) { + throw new IntegerInvalidArgument( + sprintf(self::INVALID_INTEGER_PART_MGS, $integer) + ); + } + if (preg_match(self::LEADING_ZERO_VALIDATOR_REGEX, $integer)) { + throw new IntegerInvalidArgument( + sprintf(self::START_WITH_ZERO_NOT_ALLOWED_MGS, $integer) + ); + } + } + + public function __invoke(): string + { + return $this->getInteger(); + } + + public function __toString(): string + { + return $this->integer; + } + + public function getInteger(): string + { + return $this->integer; + } + + public function equals (IntegerInterface $integer): bool + { + return $this->getInteger() === $integer->getInteger(); + } +} \ No newline at end of file diff --git a/Src/Domain/IntegerInterface.php b/Src/Domain/IntegerInterface.php new file mode 100644 index 0000000..65eabec --- /dev/null +++ b/Src/Domain/IntegerInterface.php @@ -0,0 +1,10 @@ +setIntegerPart($integerPart); - } - - public static function fromString(string $integerPart) - { - return new self($integerPart); - } - - private function setIntegerPart(string $integerPart) - { - $this->integerPart = $this->parseIntegerPart($integerPart); - } - - private function parseIntegerPart(string $integerPart): string - { - $defaultIntegerValue = $this->defaultValuesForIntegerValidation($integerPart); - if (null !== $defaultIntegerValue) { - return $defaultIntegerValue; - } - - $this->validateIntegerPart($integerPart); - - return $integerPart; - } - - private function defaultValuesForIntegerValidation(string $integerPart): ?string - { - if (self::EMPTY_STRING === $integerPart || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integerPart) { - return self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - if (self::NEGATIVE_SIGN === $integerPart) { - return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - return null; - } - - private function validateIntegerPart(string $integerPart): void - { - $nonZero = false; - $characters = strlen($integerPart); - for ($position = 0; $position < $characters; ++$position) { - $digit = $integerPart[$position]; - - if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { - throw new \InvalidArgumentException( - sprintf(self::INVALID_INTEGER_PART_MGS, $integerPart, $digit) - ); - } - - if (false === $nonZero && '0' === $digit) { - throw new \InvalidArgumentException(self::START_WITH_ZERO_NOT_ALLOWED_MGS); - } - - $nonZero = true; - } - } - - public function __invoke(): string - { - $this->getIntegerPart(); - } - - public function __toString(): string - { - return $this->integerPart; - } - - public function getIntegerPart(): string - { - return $this->integerPart; - } - - public function equals (IntegerPartInterface $integerPart): bool - { - return $this->getIntegerPart() === $integerPart->getIntegerPart(); - } -} \ No newline at end of file diff --git a/Src/Domain/IntegerPartInterface.php b/Src/Domain/IntegerPartInterface.php deleted file mode 100644 index 331cbb8..0000000 --- a/Src/Domain/IntegerPartInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - Date: Thu, 18 Jul 2019 17:30:53 +0200 Subject: [PATCH 10/24] Added tests for the new integer class --- Tests/IntegerTest.php | 164 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 Tests/IntegerTest.php diff --git a/Tests/IntegerTest.php b/Tests/IntegerTest.php new file mode 100644 index 0000000..63ed4b3 --- /dev/null +++ b/Tests/IntegerTest.php @@ -0,0 +1,164 @@ +expectException(IntegerInvalidArgument::class); + Integer::fromString(self::AN_EMPTY_STRING); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromARandomString() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_RANDOM_STRING); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPosiiveInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithSign() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_SIGN); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLeadingZeros() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPositiveIntegerWithLeadingZeros() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPositiveIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZerosAndSign() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_AND_SIGN); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromAWellFormattedRealInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_WELL_FORMATTED_REAL_INTEGER); + } +} From 7b71be4fe7c7c1262b427d5d0ff398318b51160e Mon Sep 17 00:00:00 2001 From: Sikay Date: Fri, 19 Jul 2019 17:00:32 +0200 Subject: [PATCH 11/24] WIP: Added new test and modified the error. Eliminated space in method within the class integer --- Src/Domain/Integer.php | 2 +- Tests/IntegerTest.php | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Src/Domain/Integer.php b/Src/Domain/Integer.php index 6082774..334248e 100644 --- a/Src/Domain/Integer.php +++ b/Src/Domain/Integer.php @@ -73,7 +73,7 @@ public function getInteger(): string return $this->integer; } - public function equals (IntegerInterface $integer): bool + public function equals(IntegerInterface $integer): bool { return $this->getInteger() === $integer->getInteger(); } diff --git a/Tests/IntegerTest.php b/Tests/IntegerTest.php index 63ed4b3..27db06d 100644 --- a/Tests/IntegerTest.php +++ b/Tests/IntegerTest.php @@ -27,7 +27,7 @@ class IntegerTest extends TestCase private const A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS = '+0004729'; private const A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS_2 = '+000573.69'; private const A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_AND_SIGN = '~0004729'; - private const A_WELL_FORMATTED_REAL_INTEGER = 3847; + private const A_WELL_FORMATTED_NUMERIC_INTEGER = 3847; private const A_WELL_FORMATTED_STRING_INTEGER = '4762'; @@ -156,9 +156,37 @@ public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros /** * @test */ - public function shouldNotCreateAnIntegerFromAWellFormattedRealInteger() + public function shouldNotCreateANumericIntegerFromAWellFormattedNumericInteger() { - $this->expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_WELL_FORMATTED_REAL_INTEGER); + $integer = Integer::fromString(self::A_WELL_FORMATTED_NUMERIC_INTEGER); + $this->assertFalse(self::A_WELL_FORMATTED_NUMERIC_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldCreateAnIntegerStringFromAWellFormattedRealInteger() + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_NUMERIC_INTEGER); + $this->assertTrue((string) self::A_WELL_FORMATTED_NUMERIC_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldCreateAnIntegerFromAWellFormattedStringInteger() + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $this->assertTrue(self::A_WELL_FORMATTED_STRING_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldBeEqual(): void + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $other_Integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $this->assertTrue($other_Integer->equals($integer)); } } From 7f0202e8442a8fa9a063c71d53704828f6093bbf Mon Sep 17 00:00:00 2001 From: khru Date: Sat, 20 Jul 2019 19:07:22 +0200 Subject: [PATCH 12/24] decimal: modify the name of the decimal interface --- Src/Domain/Decimal.php | 4 ++-- Src/Domain/{DecimalPartInterface.php => DecimalInterface.php} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename Src/Domain/{DecimalPartInterface.php => DecimalInterface.php} (52%) diff --git a/Src/Domain/Decimal.php b/Src/Domain/Decimal.php index 55b62f1..f2f6694 100644 --- a/Src/Domain/Decimal.php +++ b/Src/Domain/Decimal.php @@ -6,7 +6,7 @@ use WeDev\Price\Domain\Exception\DecimalInvalidArgument; -class Decimal implements NumericPartInterface, DecimalPartInterface +class Decimal implements NumericPartInterface, DecimalInterface { private const EMPTY_STRING = ''; private const VALIDATOR_REGEX = '/^[\d]+$/m'; @@ -43,7 +43,7 @@ public function getDecimals(): string return $this->decimal; } - public function equals(DecimalPartInterface $numericPart): bool + public function equals(DecimalInterface $numericPart): bool { return $this->getDecimals() === $numericPart->getDecimals(); } diff --git a/Src/Domain/DecimalPartInterface.php b/Src/Domain/DecimalInterface.php similarity index 52% rename from Src/Domain/DecimalPartInterface.php rename to Src/Domain/DecimalInterface.php index e29ad97..fbf85f2 100644 --- a/Src/Domain/DecimalPartInterface.php +++ b/Src/Domain/DecimalInterface.php @@ -4,9 +4,9 @@ namespace WeDev\Price\Domain; -interface DecimalPartInterface +interface DecimalInterface { public function getDecimals(): string; - public function equals(DecimalPartInterface $decimalPart): bool; + public function equals(DecimalInterface $decimalPart): bool; } From f56174cc1b530ef8d8023f25f10701b99f062a04 Mon Sep 17 00:00:00 2001 From: Sikay Date: Wed, 24 Jul 2019 18:43:37 +0200 Subject: [PATCH 13/24] Updated: integer for cleaning zeros, number for changes in integer and integer tests for new functionality --- Src/Domain/Integer.php | 42 +++++++++++++++++------ Src/Domain/Number.php | 76 +++++++++--------------------------------- Tests/IntegerTest.php | 41 +++++++++++------------ 3 files changed, 68 insertions(+), 91 deletions(-) diff --git a/Src/Domain/Integer.php b/Src/Domain/Integer.php index 334248e..87bf5ba 100644 --- a/Src/Domain/Integer.php +++ b/Src/Domain/Integer.php @@ -11,11 +11,12 @@ class Integer implements IntegerInterface private $integer; private const EMPTY_STRING = ''; - private const START_WITH_ZERO_NOT_ALLOWED_MGS = 'Leading zeros are not allowed'; + private const ZERO_STRING = '0'; private const INVALID_INTEGER_PART_MGS = 'Invalid integer part %1$s'; private const EMPTY_STRING_MGS = 'Integer part can not be empty'; private const VALIDATOR_REGEX = '/^[-+]?[\d]+$/'; - private const LEADING_ZERO_VALIDATOR_REGEX = '/^[-+]?[0]+/'; + private const SIGN_REGEX = '/^[-+]/'; + private const LEADING_ZERO_WITH_SIGN_VALIDATOR_REGEX = '/^[-+][0]+/'; private function __construct(string $integer) { @@ -30,17 +31,16 @@ public static function fromString(string $integer) private function setInteger(string $integer) { $this->validateInteger($integer); - $this->integer = $integer; + $this->integer = $this->cleanLeadingZero($integer); } private function validateInteger(string $integer) { - if (self::EMPTY_STRING === $integer) { + if ($this->isIntegerEmpty($integer)) { throw new IntegerInvalidArgument( sprintf(self::EMPTY_STRING_MGS, $integer) ); } - $this->validValueForAInteger($integer); } @@ -51,11 +51,33 @@ private function validValueForAInteger($integer) sprintf(self::INVALID_INTEGER_PART_MGS, $integer) ); } - if (preg_match(self::LEADING_ZERO_VALIDATOR_REGEX, $integer)) { - throw new IntegerInvalidArgument( - sprintf(self::START_WITH_ZERO_NOT_ALLOWED_MGS, $integer) - ); + } + + private function isIntegerEmpty($integer): bool + { + if (self::EMPTY_STRING === $integer) { + return true; } + return false; + } + + private function cleanLeadingZero($integer): string + { + if (preg_match(self::LEADING_ZERO_WITH_SIGN_VALIDATOR_REGEX, $integer)) { + $integer = $integer[0] . ltrim(substr($integer, 1), self::ZERO_STRING); + } else { + $integer = ltrim($integer, self::ZERO_STRING); + } + + if (preg_match(self::SIGN_REGEX, $integer) && strlen($integer) == 1) { + $integer = $integer[0] . self::ZERO_STRING; + } + + if ($this->isIntegerEmpty($integer)) { + $integer = self::ZERO_STRING; + } + + return $integer; } public function __invoke(): string @@ -77,4 +99,4 @@ public function equals(IntegerInterface $integer): bool { return $this->getInteger() === $integer->getInteger(); } -} \ No newline at end of file +} diff --git a/Src/Domain/Number.php b/Src/Domain/Number.php index c8a927c..40d956b 100644 --- a/Src/Domain/Number.php +++ b/Src/Domain/Number.php @@ -6,7 +6,7 @@ final class Number { - private $integerPart; + private $integer; private $fractionalPart; @@ -14,16 +14,15 @@ final class Number private const FLOAT_FORMAT = '%.14F'; private const NEGATIVE_SIGN = '-'; private const NUMERIC_SEPARATOR = '.'; - private const DEFAULT_POSITIVE_NUMERIC_VALUE = '0'; private const FIRST_CHART = '0'; private const VALID_NUMBERS = [0 => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; - private function __construct(string $integerPart, string $fractionalPart = '') + private function __construct(string $integer, string $fractionalPart = '') { - $this->preconditionNotNullArguments($integerPart, $fractionalPart); + $this->preconditionNotNullArguments($integer, $fractionalPart); - $this->integerPart = $this->parseIntegerPart((string) $integerPart); + $this->integer = Integer::fromString((string) $integer)->__toString(); $this->fractionalPart = $this->parseFractionalPart((string) $fractionalPart); } @@ -79,10 +78,10 @@ public function __invoke(): string public function __toString(): string { if (self::EMPTY_STRING === $this->fractionalPart) { - return $this->integerPart; + return $this->integer; } - return $this->integerPart . self::NUMERIC_SEPARATOR . $this->fractionalPart; + return $this->integer . self::NUMERIC_SEPARATOR . $this->fractionalPart; } public function isDecimal(): bool @@ -102,9 +101,9 @@ public function isHalf(): bool public function isCurrentEven(): bool { - $lastIntegerPartNumber = $this->integerPart[strlen($this->integerPart) - 1]; + $lastIntegerNumber = $this->integer[strlen($this->integer) - 1]; - return 0 === $lastIntegerPartNumber % 2; + return 0 === $lastIntegerNumber % 2; } public function isCloserToNext(): bool @@ -119,18 +118,18 @@ public function isCloserToNext(): bool public function toFloat(): float { return (self::EMPTY_STRING !== $this->fractionalPart) ? - (float) ($this->integerPart . self::NUMERIC_SEPARATOR . $this->fractionalPart) : - (float) $this->integerPart; + (float) ($this->integer . self::NUMERIC_SEPARATOR . $this->fractionalPart) : + (float) $this->integer; } public function isNegative(): bool { - return self::NEGATIVE_SIGN === $this->integerPart[0]; + return self::NEGATIVE_SIGN === $this->integer[0]; } - public function getIntegerPart(): string + public function getInteger(): string { - return $this->integerPart; + return $this->integer; } public function getFractionalPart(): string @@ -138,49 +137,6 @@ public function getFractionalPart(): string return $this->fractionalPart; } - private function parseIntegerPart(string $number): string - { - $default = $this->defaultValuesForIntegerValidation($number); - if (null !== $default) { - return $default; - } - - $nonZero = false; - $characters = strlen($number); - for ($position = 0; $position < $characters; ++$position) { - $digit = $number[$position]; - - if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { - throw new \InvalidArgumentException( - sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $number, $digit) - ); - } - - if (false === $nonZero && '0' === $digit) { - throw new \InvalidArgumentException( - 'Leading zeros are not allowed' - ); - } - - $nonZero = true; - } - - return $number; - } - - private function defaultValuesForIntegerValidation(string $number): ?string - { - if (self::EMPTY_STRING === $number || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $number) { - return self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - if (self::NEGATIVE_SIGN === $number) { - return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - return null; - } - private function parseFractionalPart(string $number): string { if (self::EMPTY_STRING === $number) { @@ -201,13 +157,13 @@ private function parseFractionalPart(string $number): string public function equal(self $number): bool { - return $this->integerPart === $number->getIntegerPart() && + return $this->integer === $number->getInteger() && $this->fractionalPart === $number->getFractionalPart(); } - private function preconditionNotNullArguments(string $integerPart, string $fractionalPart) + private function preconditionNotNullArguments(string $integer, string $fractionalPart) { - if (self::EMPTY_STRING === $integerPart && self::EMPTY_STRING === $fractionalPart) { + if (self::EMPTY_STRING === $integer && self::EMPTY_STRING === $fractionalPart) { throw new \InvalidArgumentException('Empty number is invalid'); } } diff --git a/Tests/IntegerTest.php b/Tests/IntegerTest.php index 27db06d..2c929d8 100644 --- a/Tests/IntegerTest.php +++ b/Tests/IntegerTest.php @@ -1,10 +1,4 @@ expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS); + $integer = Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS); + $intWithoutLeadingZero = ltrim(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS, self::A_STRING_INTEGER_ZERO); + $this->assertTrue((string) $intWithoutLeadingZero === $integer->getInteger()); } /** @@ -111,10 +101,10 @@ public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros /** * @test */ - public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLeadingZeros() + public function shouldNotTheSameIntegerFromABadFormattedNegativeIntegerWithLeadingZeros() { - $this->expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS); + $integer = Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS); + $this->assertFalse(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS === $integer->getInteger()); } /** @@ -129,10 +119,10 @@ public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLead /** * @test */ - public function shouldNotCreateAnIntegerFromABadFormattedPositiveIntegerWithLeadingZeros() + public function shouldNotTheSameIntegerFromABadFormattedPositiveIntegerWithLeadingZeros() { - $this->expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS); + $integer = Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS); + $this->assertFalse(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS === $integer->getInteger()); } /** @@ -189,4 +179,13 @@ public function shouldBeEqual(): void $other_Integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); $this->assertTrue($other_Integer->equals($integer)); } + + /** + * @test + */ + public function shouldCreateAnIntegerFromAWellFormattedStringIntegerZero(): void + { + $integer = Integer::fromString(self::A_STRING_INTEGER_ZERO); + $this->assertTrue(self::A_STRING_INTEGER_ZERO === $integer->getInteger()); + } } From 23b8053b98b0497274d810730ac24362976a41f8 Mon Sep 17 00:00:00 2001 From: Sikay Date: Wed, 24 Jul 2019 19:02:30 +0200 Subject: [PATCH 14/24] Merge conflicts --- Src/Domain/Number.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Src/Domain/Number.php b/Src/Domain/Number.php index 4a3655c..27c474a 100644 --- a/Src/Domain/Number.php +++ b/Src/Domain/Number.php @@ -20,13 +20,11 @@ final class Number private const VALIDATOR_REGEX = '/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/m'; private const VALID_NUMBER_MSG = 'Valid numeric value expected'; - private const VALID_NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - private function __construct(string $integer, string $decimals = '') { $this->notNullArguments($integer, $decimals); - $this->integer = Integer::fromString($integer) + $this->integer = Integer::fromString($integer); $this->decimals = Decimal::fromString($decimals); } @@ -77,7 +75,7 @@ public function __invoke(): string public function __toString(): string { if (self::EMPTY_STRING === $this->getDecimals()) { - return $this->integer; + return $this->getInteger(); } return $this->integer . self::NUMERIC_SEPARATOR . $this->getDecimals(); @@ -128,7 +126,7 @@ public function isNegative(): bool public function getInteger(): string { - return $this->integer; + return $this->integer->__toString(); } public function getDecimals(): string @@ -138,7 +136,7 @@ public function getDecimals(): string public function equals(self $number): bool { - return $this->getInteger === $number->getInteger() && + return $this->getInteger() === $number->getInteger() && $this->getDecimals() === $number->getDecimals(); } From cad2cb3ed81bc65e2546a3f52025f48322ba6a60 Mon Sep 17 00:00:00 2001 From: Sikay Date: Mon, 15 Jul 2019 21:08:15 +0200 Subject: [PATCH 15/24] Parte entera de Number --- Src/Domain/IntegerPart.php | 97 ++++++++++++++++++++++++++++++++++++++ Src/Domain/Numerable.php | 16 +++++++ 2 files changed, 113 insertions(+) create mode 100644 Src/Domain/IntegerPart.php create mode 100644 Src/Domain/Numerable.php diff --git a/Src/Domain/IntegerPart.php b/Src/Domain/IntegerPart.php new file mode 100644 index 0000000..e1e3c0e --- /dev/null +++ b/Src/Domain/IntegerPart.php @@ -0,0 +1,97 @@ +setIntegerPart($integerPart); + } + + private function setIntegerPart(string $integerPart) + { + $this->integerPart = $this->parseIntegerPart($integerPart); + } + + private function parseIntegerPart(string $integer): string + { + $default = $this->defaultValuesForIntegerValidation($integer); + if (null !== $default) { + return $default; + } + + $this->checkNumber($integer); + + return $integer; + } + + private function defaultValuesForIntegerValidation(string $integer): ?string + { + if (self::EMPTY_STRING === $integer || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integer) { + return self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + if (self::NEGATIVE_SIGN === $integer) { + return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; + } + + return null; + } + + private function checkNumber(string $integer): void + { + $nonZero = false; + $characters = strlen($integer); + for ($position = 0; $position < $characters; ++$position) { + $digit = $integer[$position]; + + if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { + throw new \InvalidArgumentException( + sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $integer, $digit) + ); + } + + if (false === $nonZero && '0' === $digit) { + throw new \InvalidArgumentException( + 'Leading zeros are not allowed' + ); + } + + $nonZero = true; + } + } + + public function isNegative(): bool + { + return self::NEGATIVE_SIGN === $this->integerPart[0]; + } + + public function __invoke(): string + { + $this->getIntegerPart(); + } + + public function getIntegerPart(): string + { + return $this->integerPart; + } + + public function equals (self $integerPart): bool + { + return $this->getIntegerPart() === $integerPart->getIntegerPart(); + } +} \ No newline at end of file diff --git a/Src/Domain/Numerable.php b/Src/Domain/Numerable.php new file mode 100644 index 0000000..21e56ed --- /dev/null +++ b/Src/Domain/Numerable.php @@ -0,0 +1,16 @@ + 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; +} \ No newline at end of file From 798b3390e07fe9f802cc775980124553c6a8c442 Mon Sep 17 00:00:00 2001 From: Sikay Date: Tue, 16 Jul 2019 17:35:56 +0200 Subject: [PATCH 16/24] Small refactoring, modified construct to private, created an interface for integerPart --- Src/Domain/IntegerPart.php | 51 ++++++++++++++++------------- Src/Domain/IntegerPartInterface.php | 16 +++++++++ Src/Domain/Numerable.php | 2 +- 3 files changed, 45 insertions(+), 24 deletions(-) create mode 100644 Src/Domain/IntegerPartInterface.php diff --git a/Src/Domain/IntegerPart.php b/Src/Domain/IntegerPart.php index e1e3c0e..ef172d7 100644 --- a/Src/Domain/IntegerPart.php +++ b/Src/Domain/IntegerPart.php @@ -10,79 +10,84 @@ namespace WeDev\Price\Domain; -class integerPart extends Numerable +class integerPart extends Numerable implements IntegerPartInterface { private $integerPart; private const DEFAULT_POSITIVE_NUMERIC_VALUE = '0'; private const NEGATIVE_SIGN = '-'; + private const START_WITH_ZERO_NOT_ALLOWED_MGS = 'Leading zeros are not allowed'; + private const INVALID_INTEGER_PART_MGS = 'Invalid integer part %1$s. Invalid digit %2$s found'; - public function __construct(string $integerPart) + private function __construct(string $integerPart) { $this->setIntegerPart($integerPart); } + public static function fromString(string $integerPart) + { + return new self($integerPart); + } + private function setIntegerPart(string $integerPart) { $this->integerPart = $this->parseIntegerPart($integerPart); } - private function parseIntegerPart(string $integer): string + private function parseIntegerPart(string $integerPart): string { - $default = $this->defaultValuesForIntegerValidation($integer); - if (null !== $default) { - return $default; + $defaultIntegerValue = $this->defaultValuesForIntegerValidation($integerPart); + if (null !== $defaultIntegerValue) { + return $defaultIntegerValue; } - $this->checkNumber($integer); + $this->validateIntegerPart($integerPart); - return $integer; + return $integerPart; } - private function defaultValuesForIntegerValidation(string $integer): ?string + private function defaultValuesForIntegerValidation(string $integerPart): ?string { - if (self::EMPTY_STRING === $integer || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integer) { + if (self::EMPTY_STRING === $integerPart || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integerPart) { return self::DEFAULT_POSITIVE_NUMERIC_VALUE; } - if (self::NEGATIVE_SIGN === $integer) { + if (self::NEGATIVE_SIGN === $integerPart) { return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; } return null; } - private function checkNumber(string $integer): void + private function validateIntegerPart(string $integerPart): void { $nonZero = false; - $characters = strlen($integer); + $characters = strlen($integerPart); for ($position = 0; $position < $characters; ++$position) { - $digit = $integer[$position]; + $digit = $integerPart[$position]; if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { throw new \InvalidArgumentException( - sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $integer, $digit) + sprintf(self::INVALID_INTEGER_PART_MGS, $integerPart, $digit) ); } if (false === $nonZero && '0' === $digit) { - throw new \InvalidArgumentException( - 'Leading zeros are not allowed' - ); + throw new \InvalidArgumentException(self::START_WITH_ZERO_NOT_ALLOWED_MGS); } $nonZero = true; } } - public function isNegative(): bool + public function __invoke(): string { - return self::NEGATIVE_SIGN === $this->integerPart[0]; + $this->getIntegerPart(); } - public function __invoke(): string + public function __toString(): string { - $this->getIntegerPart(); + return $this->integerPart; } public function getIntegerPart(): string @@ -90,7 +95,7 @@ public function getIntegerPart(): string return $this->integerPart; } - public function equals (self $integerPart): bool + public function equals (IntegerPartInterface $integerPart): bool { return $this->getIntegerPart() === $integerPart->getIntegerPart(); } diff --git a/Src/Domain/IntegerPartInterface.php b/Src/Domain/IntegerPartInterface.php new file mode 100644 index 0000000..331cbb8 --- /dev/null +++ b/Src/Domain/IntegerPartInterface.php @@ -0,0 +1,16 @@ + 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1]; + protected const VALID_NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; } \ No newline at end of file From 65290a1d7c60637029a7ae6cf21fd54cf83dbf65 Mon Sep 17 00:00:00 2001 From: Sikay Date: Thu, 18 Jul 2019 17:30:13 +0200 Subject: [PATCH 17/24] Validation by regex, name changes to integer and created exception to integer --- .../Exception/IntegerInvalidArgument.php | 8 ++ Src/Domain/Integer.php | 80 ++++++++++++++ Src/Domain/IntegerInterface.php | 10 ++ Src/Domain/IntegerPart.php | 102 ------------------ Src/Domain/IntegerPartInterface.php | 16 --- 5 files changed, 98 insertions(+), 118 deletions(-) create mode 100644 Src/Domain/Exception/IntegerInvalidArgument.php create mode 100644 Src/Domain/Integer.php create mode 100644 Src/Domain/IntegerInterface.php delete mode 100644 Src/Domain/IntegerPart.php delete mode 100644 Src/Domain/IntegerPartInterface.php diff --git a/Src/Domain/Exception/IntegerInvalidArgument.php b/Src/Domain/Exception/IntegerInvalidArgument.php new file mode 100644 index 0000000..ffc21ec --- /dev/null +++ b/Src/Domain/Exception/IntegerInvalidArgument.php @@ -0,0 +1,8 @@ +setInteger($integer); + } + + public static function fromString(string $integer) + { + return new self($integer); + } + + private function setInteger(string $integer) + { + $this->validateInteger($integer); + $this->integer = $integer; + } + + private function validateInteger(string $integer) + { + if (self::EMPTY_STRING === $integer) { + throw new IntegerInvalidArgument( + sprintf(self::EMPTY_STRING_MGS, $integer) + ); + } + + $this->validValueForAInteger($integer); + } + + private function validValueForAInteger($integer) + { + if (!preg_match(self::VALIDATOR_REGEX, $integer)) { + throw new IntegerInvalidArgument( + sprintf(self::INVALID_INTEGER_PART_MGS, $integer) + ); + } + if (preg_match(self::LEADING_ZERO_VALIDATOR_REGEX, $integer)) { + throw new IntegerInvalidArgument( + sprintf(self::START_WITH_ZERO_NOT_ALLOWED_MGS, $integer) + ); + } + } + + public function __invoke(): string + { + return $this->getInteger(); + } + + public function __toString(): string + { + return $this->integer; + } + + public function getInteger(): string + { + return $this->integer; + } + + public function equals (IntegerInterface $integer): bool + { + return $this->getInteger() === $integer->getInteger(); + } +} \ No newline at end of file diff --git a/Src/Domain/IntegerInterface.php b/Src/Domain/IntegerInterface.php new file mode 100644 index 0000000..65eabec --- /dev/null +++ b/Src/Domain/IntegerInterface.php @@ -0,0 +1,10 @@ +setIntegerPart($integerPart); - } - - public static function fromString(string $integerPart) - { - return new self($integerPart); - } - - private function setIntegerPart(string $integerPart) - { - $this->integerPart = $this->parseIntegerPart($integerPart); - } - - private function parseIntegerPart(string $integerPart): string - { - $defaultIntegerValue = $this->defaultValuesForIntegerValidation($integerPart); - if (null !== $defaultIntegerValue) { - return $defaultIntegerValue; - } - - $this->validateIntegerPart($integerPart); - - return $integerPart; - } - - private function defaultValuesForIntegerValidation(string $integerPart): ?string - { - if (self::EMPTY_STRING === $integerPart || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $integerPart) { - return self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - if (self::NEGATIVE_SIGN === $integerPart) { - return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - return null; - } - - private function validateIntegerPart(string $integerPart): void - { - $nonZero = false; - $characters = strlen($integerPart); - for ($position = 0; $position < $characters; ++$position) { - $digit = $integerPart[$position]; - - if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { - throw new \InvalidArgumentException( - sprintf(self::INVALID_INTEGER_PART_MGS, $integerPart, $digit) - ); - } - - if (false === $nonZero && '0' === $digit) { - throw new \InvalidArgumentException(self::START_WITH_ZERO_NOT_ALLOWED_MGS); - } - - $nonZero = true; - } - } - - public function __invoke(): string - { - $this->getIntegerPart(); - } - - public function __toString(): string - { - return $this->integerPart; - } - - public function getIntegerPart(): string - { - return $this->integerPart; - } - - public function equals (IntegerPartInterface $integerPart): bool - { - return $this->getIntegerPart() === $integerPart->getIntegerPart(); - } -} \ No newline at end of file diff --git a/Src/Domain/IntegerPartInterface.php b/Src/Domain/IntegerPartInterface.php deleted file mode 100644 index 331cbb8..0000000 --- a/Src/Domain/IntegerPartInterface.php +++ /dev/null @@ -1,16 +0,0 @@ - Date: Thu, 18 Jul 2019 17:30:53 +0200 Subject: [PATCH 18/24] Added tests for the new integer class --- Tests/IntegerTest.php | 164 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 Tests/IntegerTest.php diff --git a/Tests/IntegerTest.php b/Tests/IntegerTest.php new file mode 100644 index 0000000..63ed4b3 --- /dev/null +++ b/Tests/IntegerTest.php @@ -0,0 +1,164 @@ +expectException(IntegerInvalidArgument::class); + Integer::fromString(self::AN_EMPTY_STRING); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromARandomString() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_RANDOM_STRING); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPosiiveInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithSign() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_SIGN); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLeadingZeros() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPositiveIntegerWithLeadingZeros() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedPositiveIntegerWithLeadingZeros2() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS_2); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZerosAndSign() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_AND_SIGN); + } + + /** + * @test + */ + public function shouldNotCreateAnIntegerFromAWellFormattedRealInteger() + { + $this->expectException(IntegerInvalidArgument::class); + Integer::fromString(self::A_WELL_FORMATTED_REAL_INTEGER); + } +} From b5a390bc147c6b1a185d2d79b14d4c0e49c48d68 Mon Sep 17 00:00:00 2001 From: Sikay Date: Fri, 19 Jul 2019 17:00:32 +0200 Subject: [PATCH 19/24] WIP: Added new test and modified the error. Eliminated space in method within the class integer --- Src/Domain/Integer.php | 2 +- Tests/IntegerTest.php | 36 ++++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Src/Domain/Integer.php b/Src/Domain/Integer.php index 6082774..334248e 100644 --- a/Src/Domain/Integer.php +++ b/Src/Domain/Integer.php @@ -73,7 +73,7 @@ public function getInteger(): string return $this->integer; } - public function equals (IntegerInterface $integer): bool + public function equals(IntegerInterface $integer): bool { return $this->getInteger() === $integer->getInteger(); } diff --git a/Tests/IntegerTest.php b/Tests/IntegerTest.php index 63ed4b3..27db06d 100644 --- a/Tests/IntegerTest.php +++ b/Tests/IntegerTest.php @@ -27,7 +27,7 @@ class IntegerTest extends TestCase private const A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS = '+0004729'; private const A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS_2 = '+000573.69'; private const A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS_AND_SIGN = '~0004729'; - private const A_WELL_FORMATTED_REAL_INTEGER = 3847; + private const A_WELL_FORMATTED_NUMERIC_INTEGER = 3847; private const A_WELL_FORMATTED_STRING_INTEGER = '4762'; @@ -156,9 +156,37 @@ public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros /** * @test */ - public function shouldNotCreateAnIntegerFromAWellFormattedRealInteger() + public function shouldNotCreateANumericIntegerFromAWellFormattedNumericInteger() { - $this->expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_WELL_FORMATTED_REAL_INTEGER); + $integer = Integer::fromString(self::A_WELL_FORMATTED_NUMERIC_INTEGER); + $this->assertFalse(self::A_WELL_FORMATTED_NUMERIC_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldCreateAnIntegerStringFromAWellFormattedRealInteger() + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_NUMERIC_INTEGER); + $this->assertTrue((string) self::A_WELL_FORMATTED_NUMERIC_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldCreateAnIntegerFromAWellFormattedStringInteger() + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $this->assertTrue(self::A_WELL_FORMATTED_STRING_INTEGER === $integer->getInteger()); + } + + /** + * @test + */ + public function shouldBeEqual(): void + { + $integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $other_Integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); + $this->assertTrue($other_Integer->equals($integer)); } } From 40a94bdeb2fc95f6546f0516a05bc8e77e9ec7f4 Mon Sep 17 00:00:00 2001 From: Sikay Date: Wed, 24 Jul 2019 18:43:37 +0200 Subject: [PATCH 20/24] Updated: integer for cleaning zeros, number for changes in integer and integer tests for new functionality --- Src/Domain/Integer.php | 42 +++++++++++++++++++++++-------- Src/Domain/Number.php | 56 ++++-------------------------------------- Tests/IntegerTest.php | 41 +++++++++++++++---------------- 3 files changed, 57 insertions(+), 82 deletions(-) diff --git a/Src/Domain/Integer.php b/Src/Domain/Integer.php index 334248e..87bf5ba 100644 --- a/Src/Domain/Integer.php +++ b/Src/Domain/Integer.php @@ -11,11 +11,12 @@ class Integer implements IntegerInterface private $integer; private const EMPTY_STRING = ''; - private const START_WITH_ZERO_NOT_ALLOWED_MGS = 'Leading zeros are not allowed'; + private const ZERO_STRING = '0'; private const INVALID_INTEGER_PART_MGS = 'Invalid integer part %1$s'; private const EMPTY_STRING_MGS = 'Integer part can not be empty'; private const VALIDATOR_REGEX = '/^[-+]?[\d]+$/'; - private const LEADING_ZERO_VALIDATOR_REGEX = '/^[-+]?[0]+/'; + private const SIGN_REGEX = '/^[-+]/'; + private const LEADING_ZERO_WITH_SIGN_VALIDATOR_REGEX = '/^[-+][0]+/'; private function __construct(string $integer) { @@ -30,17 +31,16 @@ public static function fromString(string $integer) private function setInteger(string $integer) { $this->validateInteger($integer); - $this->integer = $integer; + $this->integer = $this->cleanLeadingZero($integer); } private function validateInteger(string $integer) { - if (self::EMPTY_STRING === $integer) { + if ($this->isIntegerEmpty($integer)) { throw new IntegerInvalidArgument( sprintf(self::EMPTY_STRING_MGS, $integer) ); } - $this->validValueForAInteger($integer); } @@ -51,11 +51,33 @@ private function validValueForAInteger($integer) sprintf(self::INVALID_INTEGER_PART_MGS, $integer) ); } - if (preg_match(self::LEADING_ZERO_VALIDATOR_REGEX, $integer)) { - throw new IntegerInvalidArgument( - sprintf(self::START_WITH_ZERO_NOT_ALLOWED_MGS, $integer) - ); + } + + private function isIntegerEmpty($integer): bool + { + if (self::EMPTY_STRING === $integer) { + return true; } + return false; + } + + private function cleanLeadingZero($integer): string + { + if (preg_match(self::LEADING_ZERO_WITH_SIGN_VALIDATOR_REGEX, $integer)) { + $integer = $integer[0] . ltrim(substr($integer, 1), self::ZERO_STRING); + } else { + $integer = ltrim($integer, self::ZERO_STRING); + } + + if (preg_match(self::SIGN_REGEX, $integer) && strlen($integer) == 1) { + $integer = $integer[0] . self::ZERO_STRING; + } + + if ($this->isIntegerEmpty($integer)) { + $integer = self::ZERO_STRING; + } + + return $integer; } public function __invoke(): string @@ -77,4 +99,4 @@ public function equals(IntegerInterface $integer): bool { return $this->getInteger() === $integer->getInteger(); } -} \ No newline at end of file +} diff --git a/Src/Domain/Number.php b/Src/Domain/Number.php index b87fc20..ba42780 100644 --- a/Src/Domain/Number.php +++ b/Src/Domain/Number.php @@ -15,19 +15,16 @@ final class Number private const FLOAT_FORMAT = '%.14F'; private const NEGATIVE_SIGN = '-'; private const NUMERIC_SEPARATOR = '.'; - private const DEFAULT_POSITIVE_NUMERIC_VALUE = '0'; private const FIRST_CHART = '0'; private const HALF_DECIMAL_VALUE = '5'; private const VALIDATOR_REGEX = '/^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/m'; private const VALID_NUMBER_MSG = 'Valid numeric value expected'; - private const VALID_NUMBERS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - - private function __construct(string $integerPart, string $decimals = '') + private function __construct(string $integer, string $decimals = '') { - $this->notNullArguments($integerPart, $decimals); + $this->notNullArguments($integer, $decimals); - $this->integer = $this->parseIntegerPart($integerPart); + $this->integer = Integer::fromString((string) $integer)->__toString(); $this->decimals = Decimal::fromString($decimals); } @@ -101,9 +98,9 @@ public function isHalf(): bool public function isCurrentEven(): bool { - $lastIntegerPartNumber = $this->integer[strlen($this->integer) - 1]; + $lastIntegerNumber = $this->integer[strlen($this->integer) - 1]; - return 0 === $lastIntegerPartNumber % 2; + return 0 === $lastIntegerNumber % 2; } public function isCloserToNext(): bool @@ -137,49 +134,6 @@ public function getDecimals(): string return $this->decimals->__toString(); } - private function parseIntegerPart(string $number): string - { - $default = $this->defaultValuesForIntegerValidation($number); - if (null !== $default) { - return $default; - } - - $nonZero = false; - $characters = strlen($number); - for ($position = 0; $position < $characters; ++$position) { - $digit = $number[$position]; - - if (!isset(self::VALID_NUMBERS[$digit]) && !(0 === $position && self::NEGATIVE_SIGN === $digit)) { - throw new NumberInvalidArgument( - sprintf('Invalid integer part %1$s. Invalid digit %2$s found', $number, $digit) - ); - } - - if (false === $nonZero && '0' === $digit) { - throw new NumberInvalidArgument( - 'Leading zeros are not allowed' - ); - } - - $nonZero = true; - } - - return $number; - } - - private function defaultValuesForIntegerValidation(string $number): ?string - { - if (self::EMPTY_STRING === $number || self::DEFAULT_POSITIVE_NUMERIC_VALUE === $number) { - return self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - if (self::NEGATIVE_SIGN === $number) { - return self::NEGATIVE_SIGN . self::DEFAULT_POSITIVE_NUMERIC_VALUE; - } - - return null; - } - public function equals(self $number): bool { return $this->integer === $number->getInteger() && diff --git a/Tests/IntegerTest.php b/Tests/IntegerTest.php index 27db06d..2c929d8 100644 --- a/Tests/IntegerTest.php +++ b/Tests/IntegerTest.php @@ -1,10 +1,4 @@ expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS); + $integer = Integer::fromString(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS); + $intWithoutLeadingZero = ltrim(self::A_BAD_FORMATTED_INTEGER_WITH_LEADING_ZEROS, self::A_STRING_INTEGER_ZERO); + $this->assertTrue((string) $intWithoutLeadingZero === $integer->getInteger()); } /** @@ -111,10 +101,10 @@ public function shouldNotCreateAnIntegerFromABadFormattedIntegerWithLeadingZeros /** * @test */ - public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLeadingZeros() + public function shouldNotTheSameIntegerFromABadFormattedNegativeIntegerWithLeadingZeros() { - $this->expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS); + $integer = Integer::fromString(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS); + $this->assertFalse(self::A_BAD_FORMATTED_NEGATIVE_INTEGER_WITH_LEADING_ZEROS === $integer->getInteger()); } /** @@ -129,10 +119,10 @@ public function shouldNotCreateAnIntegerFromABadFormattedNegativeIntegerWithLead /** * @test */ - public function shouldNotCreateAnIntegerFromABadFormattedPositiveIntegerWithLeadingZeros() + public function shouldNotTheSameIntegerFromABadFormattedPositiveIntegerWithLeadingZeros() { - $this->expectException(IntegerInvalidArgument::class); - Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS); + $integer = Integer::fromString(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS); + $this->assertFalse(self::A_BAD_FORMATTED_POSITIVE_INTEGER_WITH_LEADING_ZEROS === $integer->getInteger()); } /** @@ -189,4 +179,13 @@ public function shouldBeEqual(): void $other_Integer = Integer::fromString(self::A_WELL_FORMATTED_STRING_INTEGER); $this->assertTrue($other_Integer->equals($integer)); } + + /** + * @test + */ + public function shouldCreateAnIntegerFromAWellFormattedStringIntegerZero(): void + { + $integer = Integer::fromString(self::A_STRING_INTEGER_ZERO); + $this->assertTrue(self::A_STRING_INTEGER_ZERO === $integer->getInteger()); + } } From 2ed84a9cd6b354d5a70814586db29fbb37ba7a47 Mon Sep 17 00:00:00 2001 From: Sikay Date: Wed, 24 Jul 2019 19:02:30 +0200 Subject: [PATCH 21/24] Merge conflicts --- Src/Domain/Number.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Src/Domain/Number.php b/Src/Domain/Number.php index ba42780..27c474a 100644 --- a/Src/Domain/Number.php +++ b/Src/Domain/Number.php @@ -24,7 +24,7 @@ private function __construct(string $integer, string $decimals = '') { $this->notNullArguments($integer, $decimals); - $this->integer = Integer::fromString((string) $integer)->__toString(); + $this->integer = Integer::fromString($integer); $this->decimals = Decimal::fromString($decimals); } @@ -75,7 +75,7 @@ public function __invoke(): string public function __toString(): string { if (self::EMPTY_STRING === $this->getDecimals()) { - return $this->integer; + return $this->getInteger(); } return $this->integer . self::NUMERIC_SEPARATOR . $this->getDecimals(); @@ -126,7 +126,7 @@ public function isNegative(): bool public function getInteger(): string { - return $this->integer; + return $this->integer->__toString(); } public function getDecimals(): string @@ -136,7 +136,7 @@ public function getDecimals(): string public function equals(self $number): bool { - return $this->integer === $number->getInteger() && + return $this->getInteger() === $number->getInteger() && $this->getDecimals() === $number->getDecimals(); } From a7068e0acdeb6ed342a57a49b9e199eef2a40a93 Mon Sep 17 00:00:00 2001 From: Sikay Date: Wed, 22 Apr 2020 18:09:18 +0200 Subject: [PATCH 22/24] prueba github actions --- Src/Domain/.github/workflows/tests.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Src/Domain/.github/workflows/tests.yml diff --git a/Src/Domain/.github/workflows/tests.yml b/Src/Domain/.github/workflows/tests.yml new file mode 100644 index 0000000..d7343e3 --- /dev/null +++ b/Src/Domain/.github/workflows/tests.yml @@ -0,0 +1,24 @@ +name: CICD + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v1 + - name: Run composer install + run: composer install -n --prefer-dist + - name: Run yarn + run: yarn && yarn dev + - name: Run tests + run: ./vendor/bin/phpunit + env: + APP_ENV: testing + - name: Upload artifacts + uses: actions/upload-artifact@master + if: failure() + with: + name: Logs + path: ./storage/logs From 50619d61311372491eb4e519872520d68794077b Mon Sep 17 00:00:00 2001 From: Sikay Date: Wed, 22 Apr 2020 18:22:58 +0200 Subject: [PATCH 23/24] prueba actions --- Src/Domain/.github/workflows/{tests.yml => cicd.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Src/Domain/.github/workflows/{tests.yml => cicd.yml} (100%) diff --git a/Src/Domain/.github/workflows/tests.yml b/Src/Domain/.github/workflows/cicd.yml similarity index 100% rename from Src/Domain/.github/workflows/tests.yml rename to Src/Domain/.github/workflows/cicd.yml From 6fbc6e1e7883ffd3e5c51f4fc72b9b5151fb7344 Mon Sep 17 00:00:00 2001 From: Sikay Date: Wed, 22 Apr 2020 18:24:59 +0200 Subject: [PATCH 24/24] subiendo action con contenido --- Src/Domain/.github/workflows/cicd.yml | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/Src/Domain/.github/workflows/cicd.yml b/Src/Domain/.github/workflows/cicd.yml index d7343e3..639319d 100644 --- a/Src/Domain/.github/workflows/cicd.yml +++ b/Src/Domain/.github/workflows/cicd.yml @@ -10,15 +10,5 @@ jobs: - uses: actions/checkout@v1 - name: Run composer install run: composer install -n --prefer-dist - - name: Run yarn - run: yarn && yarn dev - name: Run tests run: ./vendor/bin/phpunit - env: - APP_ENV: testing - - name: Upload artifacts - uses: actions/upload-artifact@master - if: failure() - with: - name: Logs - path: ./storage/logs