Skip to content

Commit 378beac

Browse files
authored
Xls Writer Parser Handle Boolean Literals as Function Arguments (#3391)
Fix #3369. The parser had failed to account for `TRUE` and `FALSE` when supplied as arguments to a function. I had hoped to be able to do something about its inability to handle defined names as well. I failed. I think another section might need to be added to the Writer output which specifies the defined names. I haven't yet located any suitable documentation.
1 parent 014a120 commit 378beac

File tree

5 files changed

+261
-87
lines changed

5 files changed

+261
-87
lines changed

src/PhpSpreadsheet/Writer/Xls/Parser.php

Lines changed: 131 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -499,43 +499,60 @@ private function convert($token)
499499
{
500500
if (preg_match('/"([^"]|""){0,255}"/', $token)) {
501501
return $this->convertString($token);
502-
} elseif (is_numeric($token)) {
502+
}
503+
if (is_numeric($token)) {
503504
return $this->convertNumber($token);
505+
}
504506
// match references like A1 or $A$1
505-
} elseif (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/', $token)) {
507+
if (preg_match('/^\$?([A-Ia-i]?[A-Za-z])\$?(\d+)$/', $token)) {
506508
return $this->convertRef2d($token);
509+
}
507510
// match external references like Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1
508-
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?(\\d+)$/u', $token)) {
511+
if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?(\\d+)$/u', $token)) {
509512
return $this->convertRef3d($token);
513+
}
510514
// match external references like 'Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1
511-
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\\d+)$/u", $token)) {
515+
if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?(\\d+)$/u", $token)) {
512516
return $this->convertRef3d($token);
517+
}
513518
// match ranges like A1:B2 or $A$1:$B$2
514-
} elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $token)) {
519+
if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)\:(\$)?[A-Ia-i]?[A-Za-z](\$)?(\d+)$/', $token)) {
515520
return $this->convertRange2d($token);
521+
}
516522
// match external ranges like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
517-
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)\\:\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)$/u', $token)) {
523+
if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)\\:\$?([A-Ia-i]?[A-Za-z])?\$?(\\d+)$/u', $token)) {
518524
return $this->convertRange3d($token);
525+
}
519526
// match external ranges like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
520-
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)\\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)$/u", $token)) {
527+
if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)\\:\\$?([A-Ia-i]?[A-Za-z])?\\$?(\\d+)$/u", $token)) {
521528
return $this->convertRange3d($token);
529+
}
522530
// operators (including parentheses)
523-
} elseif (isset($this->ptg[$token])) {
531+
if (isset($this->ptg[$token])) {
524532
return pack('C', $this->ptg[$token]);
533+
}
525534
// match error codes
526-
} elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token == '#N/A') {
535+
if (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token == '#N/A') {
527536
return $this->convertError($token);
528-
} elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
537+
}
538+
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/mui', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
529539
return $this->convertDefinedName($token);
540+
}
530541
// commented so argument number can be processed correctly. See toReversePolish().
531-
/*elseif (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token))
532-
{
533-
return($this->convertFunction($token, $this->_func_args));
534-
}*/
542+
/*if (preg_match("/[A-Z0-9\xc0-\xdc\.]+/", $token))
543+
{
544+
return($this->convertFunction($token, $this->_func_args));
545+
}*/
535546
// if it's an argument, ignore the token (the argument remains)
536-
} elseif ($token == 'arg') {
547+
if ($token == 'arg') {
537548
return '';
538549
}
550+
if (preg_match('/^true$/i', $token)) {
551+
return $this->convertBool(1);
552+
}
553+
if (preg_match('/^false$/i', $token)) {
554+
return $this->convertBool(0);
555+
}
539556

540557
// TODO: use real error codes
541558
throw new WriterException("Unknown token $token");
@@ -563,6 +580,11 @@ private function convertNumber($num)
563580
return pack('Cd', $this->ptg['ptgNum'], $num);
564581
}
565582

583+
private function convertBool(int $num): string
584+
{
585+
return pack('CC', $this->ptg['ptgBool'], $num);
586+
}
587+
566588
/**
567589
* Convert a string token to ptgStr.
568590
*
@@ -747,27 +769,32 @@ private function convertError($errorCode)
747769
return pack('C', 0xFF);
748770
}
749771

772+
/** @var bool */
773+
private $tryDefinedName = false;
774+
750775
private function convertDefinedName(string $name): string
751776
{
752777
if (strlen($name) > 255) {
753778
throw new WriterException('Defined Name is too long');
754779
}
755780

756-
throw new WriterException('Cannot yet write formulae with defined names to Xls');
757-
/*
758-
$nameReference = 1;
759-
foreach ($this->spreadsheet->getDefinedNames() as $definedName) {
760-
if ($name === $definedName->getName()) {
761-
break;
781+
if ($this->tryDefinedName) {
782+
// @codeCoverageIgnoreStart
783+
$nameReference = 1;
784+
foreach ($this->spreadsheet->getDefinedNames() as $definedName) {
785+
if ($name === $definedName->getName()) {
786+
break;
787+
}
788+
++$nameReference;
762789
}
763-
++$nameReference;
764-
}
765790

766-
$ptgRef = pack('Cvxx', $this->ptg['ptgName'], $nameReference);
791+
$ptgRef = pack('Cvxx', $this->ptg['ptgName'], $nameReference);
767792

793+
return $ptgRef;
794+
// @codeCoverageIgnoreEnd
795+
}
768796

769-
return $ptgRef;
770-
*/
797+
throw new WriterException('Cannot yet write formulae with defined names to Xls');
771798
}
772799

773800
/**
@@ -1052,48 +1079,64 @@ private function match($token)
10521079
}
10531080

10541081
return $token;
1082+
}
10551083

1056-
default:
1057-
// if it's a reference A1 or $A$1 or $A1 or A$1
1058-
if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.') && ($this->lookAhead !== '!')) {
1059-
return $token;
1060-
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
1061-
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
1062-
return $token;
1063-
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
1064-
// If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
1065-
return $token;
1066-
} elseif (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) {
1067-
// if it's a range A1:A2 or $A$1:$A$2
1068-
return $token;
1069-
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead)) {
1070-
// If it's an external range like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
1071-
return $token;
1072-
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead)) {
1073-
// If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
1074-
return $token;
1075-
} elseif (is_numeric($token) && (!is_numeric($token . $this->lookAhead) || ($this->lookAhead == '')) && ($this->lookAhead !== '!') && ($this->lookAhead !== ':')) {
1076-
// If it's a number (check that it's not a sheet name or range)
1077-
return $token;
1078-
} elseif (preg_match('/"([^"]|""){0,255}"/', $token) && $this->lookAhead !== '"' && (substr_count($token, '"') % 2 == 0)) {
1079-
// If it's a string (of maximum 255 characters)
1080-
return $token;
1081-
} elseif (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token === '#N/A') {
1082-
// If it's an error code
1083-
return $token;
1084-
} elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) && ($this->lookAhead === '(')) {
1085-
// if it's a function call
1086-
return $token;
1087-
} elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
1088-
return $token;
1089-
} elseif (substr($token, -1) === ')') {
1090-
// It's an argument of some description (e.g. a named range),
1091-
// precise nature yet to be determined
1092-
return $token;
1093-
}
1094-
1095-
return '';
1084+
// if it's a reference A1 or $A$1 or $A1 or A$1
1085+
if (preg_match('/^\$?[A-Ia-i]?[A-Za-z]\$?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.') && ($this->lookAhead !== '!')) {
1086+
return $token;
1087+
}
1088+
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
1089+
if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
1090+
return $token;
10961091
}
1092+
// If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
1093+
if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead) && ($this->lookAhead !== ':') && ($this->lookAhead !== '.')) {
1094+
return $token;
1095+
}
1096+
// if it's a range A1:A2 or $A$1:$A$2
1097+
if (preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $token) && !preg_match('/\d/', $this->lookAhead)) {
1098+
return $token;
1099+
}
1100+
// If it's an external range like Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2
1101+
if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $token) && !preg_match('/\d/', $this->lookAhead)) {
1102+
return $token;
1103+
}
1104+
// If it's an external range like 'Sheet1'!A1:B2 or 'Sheet1:Sheet2'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1:Sheet2'!$A$1:$B$2
1105+
if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $token) && !preg_match('/\d/', $this->lookAhead)) {
1106+
return $token;
1107+
}
1108+
// If it's a number (check that it's not a sheet name or range)
1109+
if (is_numeric($token) && (!is_numeric($token . $this->lookAhead) || ($this->lookAhead == '')) && ($this->lookAhead !== '!') && ($this->lookAhead !== ':')) {
1110+
return $token;
1111+
}
1112+
// If it's a string (of maximum 255 characters)
1113+
if (preg_match('/"([^"]|""){0,255}"/', $token) && $this->lookAhead !== '"' && (substr_count($token, '"') % 2 == 0)) {
1114+
return $token;
1115+
}
1116+
// If it's an error code
1117+
if (preg_match('/^#[A-Z0\\/]{3,5}[!?]{1}$/', $token) || $token === '#N/A') {
1118+
return $token;
1119+
}
1120+
// if it's a function call
1121+
if (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $token) && ($this->lookAhead === '(')) {
1122+
return $token;
1123+
}
1124+
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $token) && $this->spreadsheet->getDefinedName($token) !== null) {
1125+
return $token;
1126+
}
1127+
if (preg_match('/^true$/i', $token) && ($this->lookAhead === ')' || $this->lookAhead === ',')) {
1128+
return $token;
1129+
}
1130+
if (preg_match('/^false$/i', $token) && ($this->lookAhead === ')' || $this->lookAhead === ',')) {
1131+
return $token;
1132+
}
1133+
if (substr($token, -1) === ')') {
1134+
// It's an argument of some description (e.g. a named range),
1135+
// precise nature yet to be determined
1136+
return $token;
1137+
}
1138+
1139+
return '';
10971140
}
10981141

10991142
/**
@@ -1295,19 +1338,22 @@ private function fact()
12951338
$this->advance();
12961339

12971340
return $result;
1298-
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $this->currentToken)) {
1341+
}
1342+
if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?[A-Ia-i]?[A-Za-z]\$?\\d+$/u', $this->currentToken)) {
12991343
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1 or Sheet1!$A$1 or Sheet1:Sheet2!$A$1)
13001344
$result = $this->createTree($this->currentToken, '', '');
13011345
$this->advance();
13021346

13031347
return $result;
1304-
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $this->currentToken)) {
1348+
}
1349+
if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?[A-Ia-i]?[A-Za-z]\\$?\\d+$/u", $this->currentToken)) {
13051350
// If it's an external reference ('Sheet1'!A1 or 'Sheet1:Sheet2'!A1 or 'Sheet1'!$A$1 or 'Sheet1:Sheet2'!$A$1)
13061351
$result = $this->createTree($this->currentToken, '', '');
13071352
$this->advance();
13081353

13091354
return $result;
1310-
} elseif (
1355+
}
1356+
if (
13111357
preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+:(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken) ||
13121358
preg_match('/^(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+\.\.(\$)?[A-Ia-i]?[A-Za-z](\$)?\d+$/', $this->currentToken)
13131359
) {
@@ -1317,21 +1363,24 @@ private function fact()
13171363
$this->advance();
13181364

13191365
return $result;
1320-
} elseif (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $this->currentToken)) {
1366+
}
1367+
if (preg_match('/^' . self::REGEX_SHEET_TITLE_UNQUOTED . '(\\:' . self::REGEX_SHEET_TITLE_UNQUOTED . ')?\\!\$?([A-Ia-i]?[A-Za-z])?\$?\\d+:\$?([A-Ia-i]?[A-Za-z])?\$?\\d+$/u', $this->currentToken)) {
13211368
// If it's an external range (Sheet1!A1:B2 or Sheet1:Sheet2!A1:B2 or Sheet1!$A$1:$B$2 or Sheet1:Sheet2!$A$1:$B$2)
13221369
// must be an error?
13231370
$result = $this->createTree($this->currentToken, '', '');
13241371
$this->advance();
13251372

13261373
return $result;
1327-
} elseif (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $this->currentToken)) {
1374+
}
1375+
if (preg_match("/^'" . self::REGEX_SHEET_TITLE_QUOTED . '(\\:' . self::REGEX_SHEET_TITLE_QUOTED . ")?'\\!\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+:\\$?([A-Ia-i]?[A-Za-z])?\\$?\\d+$/u", $this->currentToken)) {
13281376
// If it's an external range ('Sheet1'!A1:B2 or 'Sheet1'!A1:B2 or 'Sheet1'!$A$1:$B$2 or 'Sheet1'!$A$1:$B$2)
13291377
// must be an error?
13301378
$result = $this->createTree($this->currentToken, '', '');
13311379
$this->advance();
13321380

13331381
return $result;
1334-
} elseif (is_numeric($this->currentToken)) {
1382+
}
1383+
if (is_numeric($this->currentToken)) {
13351384
// If it's a number or a percent
13361385
if ($this->lookAhead === '%') {
13371386
$result = $this->createTree('ptgPercent', $this->currentToken, '');
@@ -1342,15 +1391,23 @@ private function fact()
13421391
$this->advance();
13431392

13441393
return $result;
1345-
} elseif (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $this->currentToken) && ($this->lookAhead === '(')) {
1394+
}
1395+
if (preg_match("/^[A-Z0-9\xc0-\xdc\\.]+$/i", $this->currentToken) && ($this->lookAhead === '(')) {
13461396
// if it's a function call
13471397
return $this->func();
1348-
} elseif (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $this->currentToken) && $this->spreadsheet->getDefinedName($this->currentToken) !== null) {
1398+
}
1399+
if (preg_match('/^' . Calculation::CALCULATION_REGEXP_DEFINEDNAME . '$/miu', $this->currentToken) && $this->spreadsheet->getDefinedName($this->currentToken) !== null) {
13491400
$result = $this->createTree('ptgName', $this->currentToken, '');
13501401
$this->advance();
13511402

13521403
return $result;
13531404
}
1405+
if (preg_match('/^true|false$/i', $this->currentToken)) {
1406+
$result = $this->createTree($this->currentToken, '', '');
1407+
$this->advance();
1408+
1409+
return $result;
1410+
}
13541411

13551412
throw new WriterException('Syntax error: ' . $this->currentToken . ', lookahead: ' . $this->lookAhead . ', current char: ' . $this->currentCharacter);
13561413
}

src/PhpSpreadsheet/Writer/Xls/Worksheet.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,19 @@ private function writeBoolErr($row, $col, $value, $isError, $xfIndex)
802802
const WRITE_FORMULA_RANGE = -2;
803803
const WRITE_FORMULA_EXCEPTION = -3;
804804

805+
/** @var bool */
806+
private static $allowThrow = false;
807+
808+
public static function setAllowThrow(bool $allowThrow): void
809+
{
810+
self::$allowThrow = $allowThrow;
811+
}
812+
813+
public static function getAllowThrow(): bool
814+
{
815+
return self::$allowThrow;
816+
}
817+
805818
/**
806819
* Write a formula to the specified row and column (zero indexed).
807820
* The textual representation of the formula is passed to the parser in
@@ -892,6 +905,10 @@ private function writeFormula($row, $col, $formula, $xfIndex, $calculatedValue)
892905

893906
return self::WRITE_FORMULA_NORMAL;
894907
} catch (PhpSpreadsheetException $e) {
908+
if (self::$allowThrow) {
909+
throw $e;
910+
}
911+
895912
return self::WRITE_FORMULA_EXCEPTION;
896913
}
897914
}

0 commit comments

Comments
 (0)