diff --git a/CHANGELOG.md b/CHANGELOG.md index 1572e96869..026a5e10b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Ignore ignoredErrors when not applicable. [Issue #4375](https://github.com/PHPOffice/PhpSpreadsheet/issues/4375) [PR #4377](https://github.com/PHPOffice/PhpSpreadsheet/pull/4377) - Better handling of defined names on sheets whose titles include apostrophes. [Issue #4356](https://github.com/PHPOffice/PhpSpreadsheet/issues/4356) [Issue #4362](https://github.com/PHPOffice/PhpSpreadsheet/issues/4362) [Issue #4376](https://github.com/PHPOffice/PhpSpreadsheet/issues/4376) [PR #4360](https://github.com/PHPOffice/PhpSpreadsheet/pull/4360) - Partial solution for removing rows or columns that include edge ranges. [Issue #1449](https://github.com/PHPOffice/PhpSpreadsheet/issues/1449) [PR #3528](https://github.com/PHPOffice/PhpSpreadsheet/pull/3528) +- Prefer mb_str_split to str_split. [PR #3341](https://github.com/PHPOffice/PhpSpreadsheet/pull/3341) ## 2025-02-08 - 4.0.0 diff --git a/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php b/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php index 0003a9fda1..40eacf1d18 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/ConvertHex.php @@ -96,7 +96,7 @@ public static function toDecimal($value) } $binX = ''; - foreach (str_split($value) as $char) { + foreach (mb_str_split($value, 1, 'UTF-8') as $char) { $binX .= str_pad(base_convert($char, 16, 2), 4, '0', STR_PAD_LEFT); } if (strlen($binX) == 40 && $binX[0] == '1') { diff --git a/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php b/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php index 5e3c12482e..d6ef56cb7d 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/ConvertOctal.php @@ -96,7 +96,7 @@ public static function toDecimal($value) } $binX = ''; - foreach (str_split($value) as $char) { + foreach (mb_str_split($value, 1, 'UTF-8') as $char) { $binX .= str_pad(decbin((int) $char), 3, '0', STR_PAD_LEFT); } if (strlen($binX) == 30 && $binX[0] == '1') { diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php b/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php index 98c3e3dce6..8901d3f4bf 100644 --- a/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php +++ b/src/PhpSpreadsheet/Calculation/MathTrig/Arabic.php @@ -74,11 +74,14 @@ public static function evaluate(mixed $roman): array|int|string // Convert the roman numeral to an arabic number $negativeNumber = $roman[0] === '-'; if ($negativeNumber) { - $roman = substr($roman, 1); + $roman = trim(substr($roman, 1)); + if ($roman === '') { + return ExcelError::NAN(); + } } try { - $arabic = self::calculateArabic(str_split($roman)); + $arabic = self::calculateArabic(mb_str_split($roman, 1, 'UTF-8')); } catch (Exception) { return ExcelError::VALUE(); // Invalid character detected } diff --git a/src/PhpSpreadsheet/Reader/Csv/Delimiter.php b/src/PhpSpreadsheet/Reader/Csv/Delimiter.php index b6c324737b..348331e35b 100644 --- a/src/PhpSpreadsheet/Reader/Csv/Delimiter.php +++ b/src/PhpSpreadsheet/Reader/Csv/Delimiter.php @@ -55,7 +55,7 @@ protected function countPotentialDelimiters(): void protected function countDelimiterValues(string $line, array $delimiterKeys): void { - $splitString = str_split($line, 1); + $splitString = mb_str_split($line, 1, 'UTF-8'); $distribution = array_count_values($splitString); $countLine = array_intersect_key($distribution, $delimiterKeys); diff --git a/src/PhpSpreadsheet/Reader/Security/XmlScanner.php b/src/PhpSpreadsheet/Reader/Security/XmlScanner.php index a80562b9f8..6d4ed449eb 100644 --- a/src/PhpSpreadsheet/Reader/Security/XmlScanner.php +++ b/src/PhpSpreadsheet/Reader/Security/XmlScanner.php @@ -87,7 +87,7 @@ private function findCharSet(string $xml): string public function scan($xml): string { // Don't rely purely on libxml_disable_entity_loader() - $pattern = '/\0*' . implode('\0*', str_split($this->pattern)) . '\0*/'; + $pattern = '/\0*' . implode('\0*', mb_str_split($this->pattern, 1, 'UTF-8')) . '\0*/'; $xml = "$xml"; if (preg_match($pattern, $xml)) { diff --git a/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php b/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php index c6c648ba24..ad7dfc4e52 100644 --- a/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php +++ b/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalFormattingRuleExtension.php @@ -34,7 +34,7 @@ public function __construct(?string $id = null, string $cfRule = self::CONDITION private function generateUuid(): string { - $chars = str_split('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'); + $chars = mb_str_split('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', 1, 'UTF-8'); foreach ($chars as $i => $char) { if ($char === 'x') { diff --git a/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php b/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php index 3c0d79aaa5..4b3d301d21 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/DateFormatter.php @@ -207,6 +207,6 @@ private static function setLowercaseCallback(array $matches): string private static function escapeQuotesCallback(array $matches): string { - return '\\' . implode('\\', str_split($matches[1])); + return '\\' . implode('\\', mb_str_split($matches[1], 1, 'UTF-8')); } } diff --git a/tests/data/Calculation/Engineering/HEX2DEC.php b/tests/data/Calculation/Engineering/HEX2DEC.php index 54c95ade95..b2c35e554e 100644 --- a/tests/data/Calculation/Engineering/HEX2DEC.php +++ b/tests/data/Calculation/Engineering/HEX2DEC.php @@ -12,6 +12,7 @@ ['4886718345', '123456789'], [ExcelError::NAN(), '123.45'], ['0', '0'], + ['0', ''], [ExcelError::NAN(), 'G3579A'], [ExcelError::VALUE(), true], [ExcelError::VALUE(), false], diff --git a/tests/data/Calculation/Engineering/OCT2DEC.php b/tests/data/Calculation/Engineering/OCT2DEC.php index fb88a71f54..9f3a919404 100644 --- a/tests/data/Calculation/Engineering/OCT2DEC.php +++ b/tests/data/Calculation/Engineering/OCT2DEC.php @@ -15,6 +15,7 @@ [ExcelError::NAN(), '3579'], ['44', '54'], ['-165', '7777777533'], // 2's Complement + ['0', ''], [ExcelError::NAN(), '37777777770'], // too many digits ['536870911', '3777777777'], // highest positive ['-536870912', '4000000000'], // lowest negative diff --git a/tests/data/Calculation/MathTrig/ARABIC.php b/tests/data/Calculation/MathTrig/ARABIC.php index f3dce71e4b..c3bf746847 100644 --- a/tests/data/Calculation/MathTrig/ARABIC.php +++ b/tests/data/Calculation/MathTrig/ARABIC.php @@ -59,4 +59,12 @@ '#VALUE!', 'WRONG', ], + [ + 0, + '', + ], + [ + '#NUM!', + '-', + ], ]; diff --git a/tests/data/Style/NumberFormatDates.php b/tests/data/Style/NumberFormatDates.php index 477deba73f..82458ee3b2 100644 --- a/tests/data/Style/NumberFormatDates.php +++ b/tests/data/Style/NumberFormatDates.php @@ -8,38 +8,32 @@ 22269.0625, 'dd-mm-yyyy hh:mm:ss', ], - // Oasis uses upper-case - [ + 'Oasis uses upper-case' => [ '12/19/1960 01:30:00', 22269.0625, 'MM/DD/YYYY HH:MM:SS', ], - // Date with plaintext escaped with a \ - [ + 'plaintext escaped with backslash' => [ '1960-12-19T01:30:00', 22269.0625, 'yyyy-mm-dd\Thh:mm:ss', ], - // Date with plaintext in quotes - [ + 'plaintext in quotes' => [ '1960-12-19T01:30:00 Z', 22269.0625, 'yyyy-mm-dd"T"hh:mm:ss \Z', ], - // Date with quoted formatting characters - [ + 'quoted formatting characters' => [ 'y-m-d 1960-12-19 h:m:s 01:30:00', 22269.0625, '"y-m-d" yyyy-mm-dd "h:m:s" hh:mm:ss', ], - // Date with quoted formatting characters - [ - 'y-m-d 1960-12-19 h:m:s 01:30:00', + 'quoted formatting non-ascii characters' => [ + '§1960-12-19', 22269.0625, - '"y-m-d "yyyy-mm-dd" h:m:s "hh:mm:ss', + '"§"yyyy-mm-dd', ], - // Date with fractional/decimal time - [ + 'fractional/decimal time' => [ '2023/02/28 0:00:00.000', 44985, 'yyyy/mm/dd\ h:mm:ss.000',