diff --git a/CHANGELOG.md b/CHANGELOG.md index d6974dd068..d6b156e623 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a - Limited Printarea support for Html/Pdf. [Issue #3941](https://github.com/PHPOffice/PhpSpreadsheet/issues/3941) [PR #4711](https://github.com/PHPOffice/PhpSpreadsheet/pull/4711) - Implement missing `INFO` function. [PR #4709](https://github.com/PHPOffice/PhpSpreadsheet/pull/4709) +- Implement missing `BAHTTEXT` function. [PR #4715](https://github.com/PHPOffice/PhpSpreadsheet/pull/4715) ### Removed diff --git a/docs/references/function-list-by-category.md b/docs/references/function-list-by-category.md index c8aa192e28..7a8b8728d7 100644 --- a/docs/references/function-list-by-category.md +++ b/docs/references/function-list-by-category.md @@ -525,7 +525,7 @@ Excel Function | PhpSpreadsheet Function -------------------------|-------------------------------------- ARRAYTOTEXT | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Text::fromArray ASC | **Not yet Implemented** -BAHTTEXT | **Not yet Implemented** +BAHTTEXT | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Thai::getBahtText CHAR | \PhpOffice\PhpSpreadsheet\Calculation\TextData\CharacterConvert::character CLEAN | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Trim::nonPrintable CODE | \PhpOffice\PhpSpreadsheet\Calculation\TextData\CharacterConvert::code diff --git a/docs/references/function-list-by-name-compact.md b/docs/references/function-list-by-name-compact.md index e137d63486..6cb6508ee4 100644 --- a/docs/references/function-list-by-name-compact.md +++ b/docs/references/function-list-by-name-compact.md @@ -43,7 +43,7 @@ AVERAGEIFS | STATISTICAL | Statistical\Conditional::AVER Excel Function | Category | PhpSpreadsheet Function -------------------------|-----------------------|-------------------------------------- -BAHTTEXT | TEXT_AND_DATA | **Not yet Implemented** +BAHTTEXT | TEXT_AND_DATA | TextData\Thai::getBahtText BASE | MATH_AND_TRIG | MathTrig\Base::evaluate BESSELI | ENGINEERING | Engineering\BesselI::BESSELI BESSELJ | ENGINEERING | Engineering\BesselJ::BESSELJ diff --git a/docs/references/function-list-by-name.md b/docs/references/function-list-by-name.md index 1f6683b337..3acf558874 100644 --- a/docs/references/function-list-by-name.md +++ b/docs/references/function-list-by-name.md @@ -39,7 +39,7 @@ AVERAGEIFS | CATEGORY_STATISTICAL | \PhpOffice\PhpSpread Excel Function | Category | PhpSpreadsheet Function -------------------------|--------------------------------|-------------------------------------- -BAHTTEXT | CATEGORY_TEXT_AND_DATA | **Not yet Implemented** +BAHTTEXT | CATEGORY_TEXT_AND_DATA | \PhpOffice\PhpSpreadsheet\Calculation\TextData\Thai::getBahtText BASE | CATEGORY_MATH_AND_TRIG | \PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Base::evaluate BESSELI | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering\BesselI::BESSELI BESSELJ | CATEGORY_ENGINEERING | \PhpOffice\PhpSpreadsheet\Calculation\Engineering\BesselJ::BESSELJ diff --git a/src/PhpSpreadsheet/Calculation/FunctionArray.php b/src/PhpSpreadsheet/Calculation/FunctionArray.php index 74e778fda2..bc7f3a76d7 100644 --- a/src/PhpSpreadsheet/Calculation/FunctionArray.php +++ b/src/PhpSpreadsheet/Calculation/FunctionArray.php @@ -149,7 +149,7 @@ class FunctionArray extends CalculationBase ], 'BAHTTEXT' => [ 'category' => Category::CATEGORY_TEXT_AND_DATA, - 'functionCall' => [Functions::class, 'DUMMY'], + 'functionCall' => [TextData\Thai::class, 'getBahtText'], 'argumentCount' => '1', ], 'BASE' => [ diff --git a/src/PhpSpreadsheet/Calculation/TextData/Thai.php b/src/PhpSpreadsheet/Calculation/TextData/Thai.php new file mode 100644 index 0000000000..0d08db0ade --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/TextData/Thai.php @@ -0,0 +1,139 @@ + 'ศูนย์', + 1 => 'หนึ่ง', + 2 => 'สอง', + 3 => 'สาม', + 4 => 'สี่', + 5 => 'ห้า', + 6 => 'หก', + 7 => 'เจ็ด', + 8 => 'แปด', + 9 => 'เก้า', + ]; + + private const THAI_UNITS = [ + 1 => 'สิบ', + 2 => 'ร้อย', + 3 => 'พัน', + 4 => 'หมื่น', + 5 => 'แสน', + 6 => 'ล้าน', + ]; + + private const THAI_COMPOUND_ONE = 'เอ็ด'; + private const THAI_COMPOUND_TWO = 'ยี่'; + private const THAI_INTEGER = 'ถ้วน'; + private const THAI_MINUS = 'ลบ'; + private const THAI_BAHT = 'บาท'; + private const THAI_SATANG = 'สตางค์'; + + /** + * BAHTTEXT. + * + * @param mixed $number The number or array of numbers to convert + * + * @return array|string If an array of values is passed as the argument, then the returned result will also be an array with the same dimensions + */ + public static function getBahtText(mixed $number): array|string + { + if (is_array($number)) { + return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number); + } + + if (is_string($number) && preg_match('/^-?\d+$/', $number)) { + $isNegative = str_starts_with($number, '-'); + $baht = ltrim($number, '-0') ?: '0'; + $satang = '00'; + } elseif (is_bool($number) || is_numeric($number)) { + $number += 0; + $isNegative = $number < 0; + [$baht, $satang] = explode('.', number_format(abs($number), 2, '.', '')); + } else { + return ExcelError::VALUE(); + } + + $hasWhole = $baht !== '0'; + $hasFraction = $satang !== '00'; + + if (!$hasWhole && !$hasFraction) { + return self::THAI_DIGITS[0] . self::THAI_BAHT . self::THAI_INTEGER; + } + + $text = $isNegative + ? self::THAI_MINUS + : ''; + + if ($hasWhole) { + $text .= self::convertLarge($baht) . self::THAI_BAHT; + } + + $text .= $hasFraction + ? self::convertBlock($satang) . self::THAI_SATANG + : self::THAI_INTEGER; + + return $text; + } + + private static function convertLarge(string $digits): string + { + $length = strlen($digits) % 6 ?: 6; + + $chunks = [ + substr($digits, 0, $length), + ...str_split(substr($digits, $length), 6), + ]; + + $chunks = array_filter($chunks, fn (string $chunk): bool => $chunk !== ''); + + return implode( + self::THAI_UNITS[6], + array_map(self::convertBlock(...), $chunks) + ); + } + + private static function convertBlock(string $block): string + { + $out = ''; + $length = strlen($block); + $i = 0; + + // Hundreds and higher powers + for ($power = $length - 1; $power >= 2; --$power) { + $digit = $block[$i++]; + if ($digit !== '0') { + $out .= self::THAI_DIGITS[$digit] . self::THAI_UNITS[$power]; + } + } + + // Tens + $ten = $length > 1 ? $block[$i++] : '0'; + if ($ten !== '0') { + $out .= match ($ten) { + '1' => '', + '2' => self::THAI_COMPOUND_TWO, + default => self::THAI_DIGITS[$ten], + } . self::THAI_UNITS[1]; + } + + // Ones + $one = $block[$i] ?? '0'; + if ($one !== '0') { + $out .= $ten !== '0' && $one === '1' + ? self::THAI_COMPOUND_ONE + : self::THAI_DIGITS[$one]; + } + + return $out; + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/BahtTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/BahtTest.php new file mode 100644 index 0000000000..e4f530c731 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/BahtTest.php @@ -0,0 +1,37 @@ +getActiveSheet(); + + if (is_bool($number)) { + $formulaValue = $number ? 'TRUE' : 'FALSE'; + } elseif (is_string($number)) { + $formulaValue = '"' . $number . '"'; + } else { + $formulaValue = (string) $number; + } + + $sheet->getCell('A1')->setValue('=BAHTTEXT(' . $formulaValue . ')'); + $result = $sheet->getCell('A1')->getCalculatedValue(); + + self::assertSame($expectedResult, $result); + $spreadsheet->disconnectWorksheets(); + } + + public static function providerBAHTTEXT(): array + { + return require 'tests/data/Calculation/TextData/BAHTTEXT.php'; + } +} diff --git a/tests/data/Calculation/TextData/BAHTTEXT.php b/tests/data/Calculation/TextData/BAHTTEXT.php new file mode 100644 index 0000000000..18ce7fbace --- /dev/null +++ b/tests/data/Calculation/TextData/BAHTTEXT.php @@ -0,0 +1,98 @@ +