Merge pull request #3 from PHPOffice/master

Sync Up With Base
This commit is contained in:
oleibman 2020-05-24 22:51:25 -07:00 committed by GitHub
commit 79d024fec0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 545 additions and 226 deletions

View File

@ -18,7 +18,7 @@ build:
tools: tools:
external_code_coverage: external_code_coverage:
timeout: 3600 timeout: 600
build_failure_conditions: build_failure_conditions:
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed - 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed

View File

@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Fix RATE, PRICE, XIRR, and XNPV Functions [#1456](https://github.com/PHPOffice/PhpSpreadsheet/pull/1456) - Fix RATE, PRICE, XIRR, and XNPV Functions [#1456](https://github.com/PHPOffice/PhpSpreadsheet/pull/1456)
- Save Excel 2010+ functions properly in XLSX [#1461](https://github.com/PHPOffice/PhpSpreadsheet/pull/1461) - Save Excel 2010+ functions properly in XLSX [#1461](https://github.com/PHPOffice/PhpSpreadsheet/pull/1461)
- Several improvements in HTML writer [#1464](https://github.com/PHPOffice/PhpSpreadsheet/pull/1464) - Several improvements in HTML writer [#1464](https://github.com/PHPOffice/PhpSpreadsheet/pull/1464)
- Fix incorrect behaviour when saving XLSX file with drawings [#1462](https://github.com/PHPOffice/PhpSpreadsheet/pull/1462),
- Fix Crash while trying setting a cell the value "123456\n" [#1476](https://github.com/PHPOffice/PhpSpreadsheet/pull/1481)
### Changed ### Changed

View File

@ -17,3 +17,4 @@ $helper->logRead('Xlsx', $filename, $callStartTime);
// Save // Save
$helper->write($spreadsheet, __FILE__); $helper->write($spreadsheet, __FILE__);
unlink($filename);

View File

@ -1,13 +1,15 @@
<?php <?php
use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\Csv as CsvReader;
use PhpOffice\PhpSpreadsheet\Writer\Csv as CsvWriter;
require __DIR__ . '/../Header.php'; require __DIR__ . '/../Header.php';
$spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $spreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php';
$helper->log('Write to CSV format'); $helper->log('Write to CSV format');
/** @var \PhpOffice\PhpSpreadsheet\Writer\Csv $writer */ /** @var \PhpOffice\PhpSpreadsheet\Writer\Csv $writer */
$writer = IOFactory::createWriter($spreadsheet, 'Csv')->setDelimiter(',') $writer = new CsvWriter($spreadsheet);
$writer->setDelimiter(',')
->setEnclosure('"') ->setEnclosure('"')
->setSheetIndex(0); ->setSheetIndex(0);
@ -19,13 +21,15 @@ $helper->logWrite($writer, $filename, $callStartTime);
$helper->log('Read from CSV format'); $helper->log('Read from CSV format');
/** @var \PhpOffice\PhpSpreadsheet\Reader\Csv $reader */ /** @var \PhpOffice\PhpSpreadsheet\Reader\Csv $reader */
$reader = IOFactory::createReader('Csv')->setDelimiter(',') $reader = new CsvReader();
$reader->setDelimiter(',')
->setEnclosure('"') ->setEnclosure('"')
->setSheetIndex(0); ->setSheetIndex(0);
$callStartTime = microtime(true); $callStartTime = microtime(true);
$spreadsheetFromCSV = $reader->load($filename); $spreadsheetFromCSV = $reader->load($filename);
$helper->logRead('Csv', $filename, $callStartTime); $helper->logRead('Csv', $filename, $callStartTime);
unlink($filename);
// Write Xlsx // Write Xlsx
$helper->write($spreadsheetFromCSV, __FILE__, ['Xlsx']); $helper->write($spreadsheetFromCSV, __FILE__, ['Xlsx']);
@ -33,7 +37,7 @@ $helper->write($spreadsheetFromCSV, __FILE__, ['Xlsx']);
// Write CSV // Write CSV
$filenameCSV = $helper->getFilename(__FILE__, 'csv'); $filenameCSV = $helper->getFilename(__FILE__, 'csv');
/** @var \PhpOffice\PhpSpreadsheet\Writer\Csv $writerCSV */ /** @var \PhpOffice\PhpSpreadsheet\Writer\Csv $writerCSV */
$writerCSV = IOFactory::createWriter($spreadsheetFromCSV, 'Csv'); $writerCSV = new CsvWriter($spreadsheetFromCSV);
$writerCSV->setExcelCompatibility(true); $writerCSV->setExcelCompatibility(true);
$callStartTime = microtime(true); $callStartTime = microtime(true);

View File

@ -17,6 +17,7 @@ $helper->logWrite($writer, $filename, $callStartTime);
$callStartTime = microtime(true); $callStartTime = microtime(true);
$spreadsheet = IOFactory::load($filename); $spreadsheet = IOFactory::load($filename);
$helper->logRead('Xls', $filename, $callStartTime); $helper->logRead('Xls', $filename, $callStartTime);
unlink($filename);
// Save // Save
$helper->write($spreadsheet, __FILE__); $helper->write($spreadsheet, __FILE__);

View File

@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet; namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Reader\IReadFilter; use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
require __DIR__ . '/../Header.php'; require __DIR__ . '/../Header.php';
@ -29,10 +30,11 @@ class MyReadFilter implements IReadFilter
} }
$helper->log('Load from Xlsx file'); $helper->log('Load from Xlsx file');
$reader = IOFactory::createReader('Xlsx'); $reader = new XlsxReader();
$reader->setReadFilter(new MyReadFilter()); $reader->setReadFilter(new MyReadFilter());
$callStartTime = microtime(true); $callStartTime = microtime(true);
$spreadsheet = $reader->load($filename); $spreadsheet = $reader->load($filename);
unlink($filename);
$helper->logRead('Xlsx', $filename, $callStartTime); $helper->logRead('Xlsx', $filename, $callStartTime);
$helper->log('Remove unnecessary rows'); $helper->log('Remove unnecessary rows');
$spreadsheet->getActiveSheet()->removeRow(2, 18); $spreadsheet->getActiveSheet()->removeRow(2, 18);

View File

@ -1,21 +1,22 @@
<?php <?php
use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XLsxReader;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XLsxWriter;
require __DIR__ . '/../Header.php'; require __DIR__ . '/../Header.php';
$sampleSpreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php'; $sampleSpreadsheet = require __DIR__ . '/../templates/sampleSpreadsheet.php';
$filename = $helper->getTemporaryFilename(); $filename = $helper->getTemporaryFilename();
$writer = new Xlsx($sampleSpreadsheet); $writer = new XlsxWriter($sampleSpreadsheet);
$callStartTime = microtime(true); $callStartTime = microtime(true);
$writer->save($filename); $writer->save($filename);
$helper->logWrite($writer, $filename, $callStartTime); $helper->logWrite($writer, $filename, $callStartTime);
$callStartTime = microtime(true); $callStartTime = microtime(true);
$reader = IOFactory::createReader('Xlsx'); $reader = new XlsxReader();
$spreadsheet = $reader->load($filename); $spreadsheet = $reader->load($filename);
$helper->logRead('Xlsx', $filename, $callStartTime); $helper->logRead('Xlsx', $filename, $callStartTime);
unlink($filename);
$helper->log('Iterate worksheets'); $helper->log('Iterate worksheets');
foreach ($spreadsheet->getWorksheetIterator() as $worksheet) { foreach ($spreadsheet->getWorksheetIterator() as $worksheet) {
$helper->log('Worksheet - ' . $worksheet->getTitle()); $helper->log('Worksheet - ' . $worksheet->getTitle());

View File

@ -24,3 +24,5 @@ var_dump($sheetList);
$helper->log('Worksheet Names:'); $helper->log('Worksheet Names:');
var_dump($sheetInfo); var_dump($sheetInfo);
unlink($filename);

View File

@ -1,20 +1,22 @@
<?php <?php
use PhpOffice\PhpSpreadsheet\IOFactory; use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
require __DIR__ . '/../Header.php'; require __DIR__ . '/../Header.php';
// Create temporary file that will be read // Create temporary file that will be read
$sampleSpreadsheet = require __DIR__ . '/../templates/chartSpreadsheet.php'; $sampleSpreadsheet = require __DIR__ . '/../templates/chartSpreadsheet.php';
$filename = $helper->getTemporaryFilename(); $filename = $helper->getTemporaryFilename();
$writer = new Xlsx($sampleSpreadsheet); $writer = new XlsxWriter($sampleSpreadsheet);
$writer->setIncludeCharts(true);
$writer->save($filename); $writer->save($filename);
$helper->log('Load from Xlsx file'); $helper->log('Load from Xlsx file');
$reader = IOFactory::createReader('Xlsx'); $reader = new XlsxReader();
$reader->setIncludeCharts(true); $reader->setIncludeCharts(true);
$spreadsheet = $reader->load($filename); $spreadsheet = $reader->load($filename);
unlink($filename);
$helper->log('Update cell data values that are displayed in the chart'); $helper->log('Update cell data values that are displayed in the chart');
$worksheet = $spreadsheet->getActiveSheet(); $worksheet = $spreadsheet->getActiveSheet();
@ -31,7 +33,7 @@ $worksheet->fromArray(
// Save Excel 2007 file // Save Excel 2007 file
$filename = $helper->getFilename(__FILE__); $filename = $helper->getFilename(__FILE__);
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx'); $writer = new XlsxWriter($spreadsheet);
$writer->setIncludeCharts(true); $writer->setIncludeCharts(true);
$callStartTime = microtime(true); $callStartTime = microtime(true);
$writer->save($filename); $writer->save($filename);

View File

@ -37,6 +37,10 @@ class Calculation
const RETURN_ARRAY_AS_VALUE = 'value'; const RETURN_ARRAY_AS_VALUE = 'value';
const RETURN_ARRAY_AS_ARRAY = 'array'; const RETURN_ARRAY_AS_ARRAY = 'array';
const FORMULA_OPEN_FUNCTION_BRACE = '{';
const FORMULA_CLOSE_FUNCTION_BRACE = '}';
const FORMULA_STRING_QUOTE = '"';
private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE; private static $returnArrayAsType = self::RETURN_ARRAY_AS_VALUE;
/** /**
@ -2593,11 +2597,11 @@ class Calculation
for ($i = 0; $i < $strlen; ++$i) { for ($i = 0; $i < $strlen; ++$i) {
$chr = mb_substr($formula, $i, 1); $chr = mb_substr($formula, $i, 1);
switch ($chr) { switch ($chr) {
case '{': case self::FORMULA_OPEN_FUNCTION_BRACE:
$inBraces = true; $inBraces = true;
break; break;
case '}': case self::FORMULA_CLOSE_FUNCTION_BRACE:
$inBraces = false; $inBraces = false;
break; break;
@ -2626,10 +2630,10 @@ class Calculation
if (self::$localeLanguage !== 'en_us') { if (self::$localeLanguage !== 'en_us') {
$inBraces = false; $inBraces = false;
// If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
if (strpos($formula, '"') !== false) { if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) {
// So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
// the formula // the formula
$temp = explode('"', $formula); $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
$i = false; $i = false;
foreach ($temp as &$value) { foreach ($temp as &$value) {
// Only count/replace in alternating array entries // Only count/replace in alternating array entries
@ -2640,7 +2644,7 @@ class Calculation
} }
unset($value); unset($value);
// Then rebuild the formula string // Then rebuild the formula string
$formula = implode('"', $temp); $formula = implode(self::FORMULA_STRING_QUOTE, $temp);
} else { } else {
// If there's no quoted strings, then we do a simple count/replace // If there's no quoted strings, then we do a simple count/replace
$formula = preg_replace($from, $to, $formula); $formula = preg_replace($from, $to, $formula);
@ -2741,7 +2745,7 @@ class Calculation
return $value; return $value;
} }
// Return strings wrapped in quotes // Return strings wrapped in quotes
return '"' . $value . '"'; return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
// Convert numeric errors to NaN error // Convert numeric errors to NaN error
} elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) { } elseif ((is_float($value)) && ((is_nan($value)) || (is_infinite($value)))) {
return Functions::NAN(); return Functions::NAN();
@ -2760,7 +2764,7 @@ class Calculation
public static function unwrapResult($value) public static function unwrapResult($value)
{ {
if (is_string($value)) { if (is_string($value)) {
if ((isset($value[0])) && ($value[0] == '"') && (substr($value, -1) == '"')) { if ((isset($value[0])) && ($value[0] == self::FORMULA_STRING_QUOTE) && (substr($value, -1) == self::FORMULA_STRING_QUOTE)) {
return substr($value, 1, -1); return substr($value, 1, -1);
} }
// Convert numeric errors to NAN error // Convert numeric errors to NAN error
@ -3227,8 +3231,8 @@ class Calculation
} }
return '{ ' . implode($rpad, $returnMatrix) . ' }'; return '{ ' . implode($rpad, $returnMatrix) . ' }';
} elseif (is_string($value) && (trim($value, '"') == $value)) { } elseif (is_string($value) && (trim($value, self::FORMULA_STRING_QUOTE) == $value)) {
return '"' . $value . '"'; return self::FORMULA_STRING_QUOTE . $value . self::FORMULA_STRING_QUOTE;
} elseif (is_bool($value)) { } elseif (is_bool($value)) {
return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE']; return ($value) ? self::$localeBoolean['TRUE'] : self::$localeBoolean['FALSE'];
} }
@ -3282,34 +3286,34 @@ class Calculation
*/ */
private function convertMatrixReferences($formula) private function convertMatrixReferences($formula)
{ {
static $matrixReplaceFrom = ['{', ';', '}']; static $matrixReplaceFrom = [self::FORMULA_OPEN_FUNCTION_BRACE, ';', self::FORMULA_CLOSE_FUNCTION_BRACE];
static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))']; static $matrixReplaceTo = ['MKMATRIX(MKMATRIX(', '),MKMATRIX(', '))'];
// Convert any Excel matrix references to the MKMATRIX() function // Convert any Excel matrix references to the MKMATRIX() function
if (strpos($formula, '{') !== false) { if (strpos($formula, self::FORMULA_OPEN_FUNCTION_BRACE) !== false) {
// If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators // If there is the possibility of braces within a quoted string, then we don't treat those as matrix indicators
if (strpos($formula, '"') !== false) { if (strpos($formula, self::FORMULA_STRING_QUOTE) !== false) {
// So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded // So instead we skip replacing in any quoted strings by only replacing in every other array element after we've exploded
// the formula // the formula
$temp = explode('"', $formula); $temp = explode(self::FORMULA_STRING_QUOTE, $formula);
// Open and Closed counts used for trapping mismatched braces in the formula // Open and Closed counts used for trapping mismatched braces in the formula
$openCount = $closeCount = 0; $openCount = $closeCount = 0;
$i = false; $i = false;
foreach ($temp as &$value) { foreach ($temp as &$value) {
// Only count/replace in alternating array entries // Only count/replace in alternating array entries
if ($i = !$i) { if ($i = !$i) {
$openCount += substr_count($value, '{'); $openCount += substr_count($value, self::FORMULA_OPEN_FUNCTION_BRACE);
$closeCount += substr_count($value, '}'); $closeCount += substr_count($value, self::FORMULA_CLOSE_FUNCTION_BRACE);
$value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value); $value = str_replace($matrixReplaceFrom, $matrixReplaceTo, $value);
} }
} }
unset($value); unset($value);
// Then rebuild the formula string // Then rebuild the formula string
$formula = implode('"', $temp); $formula = implode(self::FORMULA_STRING_QUOTE, $temp);
} else { } else {
// If there's no quoted strings, then we do a simple count/replace // If there's no quoted strings, then we do a simple count/replace
$openCount = substr_count($formula, '{'); $openCount = substr_count($formula, self::FORMULA_OPEN_FUNCTION_BRACE);
$closeCount = substr_count($formula, '}'); $closeCount = substr_count($formula, self::FORMULA_CLOSE_FUNCTION_BRACE);
$formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula); $formula = str_replace($matrixReplaceFrom, $matrixReplaceTo, $formula);
} }
// Trap for mismatched braces and trigger an appropriate error // Trap for mismatched braces and trigger an appropriate error
@ -3715,9 +3719,9 @@ class Calculation
} }
$localeConstant = false; $localeConstant = false;
if ($opCharacter == '"') { if ($opCharacter == self::FORMULA_STRING_QUOTE) {
// UnEscape any quotes within the string // UnEscape any quotes within the string
$val = self::wrapResult(str_replace('""', '"', self::unwrapResult($val))); $val = self::wrapResult(str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($val)));
} elseif (is_numeric($val)) { } elseif (is_numeric($val)) {
if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) { if ((strpos($val, '.') !== false) || (stripos($val, 'e') !== false) || ($val > PHP_INT_MAX) || ($val < -PHP_INT_MAX)) {
$val = (float) $val; $val = (float) $val;
@ -4058,7 +4062,7 @@ class Calculation
$result = '#VALUE!'; $result = '#VALUE!';
} }
} else { } else {
$result = '"' . str_replace('""', '"', self::unwrapResult($operand1) . self::unwrapResult($operand2)) . '"'; $result = self::FORMULA_STRING_QUOTE . str_replace('""', self::FORMULA_STRING_QUOTE, self::unwrapResult($operand1) . self::unwrapResult($operand2)) . self::FORMULA_STRING_QUOTE;
} }
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result)); $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($result));
$stack->push('Value', $result); $stack->push('Value', $result);
@ -4078,9 +4082,15 @@ class Calculation
$cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]); $cellIntersect[$row] = array_intersect_key($operand1[$row], $operand2[$row]);
} }
} }
$cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' . Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow); if (count(Functions::flattenArray($cellIntersect)) === 0) {
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
$stack->push('Error', Functions::null(), null);
} else {
$cellRef = Coordinate::stringFromColumnIndex(min($oCol) + 1) . min($oRow) . ':' .
Coordinate::stringFromColumnIndex(max($oCol) + 1) . max($oRow);
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect)); $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails($cellIntersect));
$stack->push('Value', $cellIntersect, $cellRef); $stack->push('Value', $cellIntersect, $cellRef);
}
break; break;
} }
@ -4284,7 +4294,7 @@ class Calculation
$branchStore[$storeKey] = self::$excelConstants[$excelConstant]; $branchStore[$storeKey] = self::$excelConstants[$excelConstant];
} }
$this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant])); $this->debugLog->writeDebugLog('Evaluating Constant ', $excelConstant, ' as ', $this->showTypeDetails(self::$excelConstants[$excelConstant]));
} elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == '"') || ($token[0] == '#')) { } elseif ((is_numeric($token)) || ($token === null) || (is_bool($token)) || ($token == '') || ($token[0] == self::FORMULA_STRING_QUOTE) || ($token[0] == '#')) {
$stack->push('Value', $token); $stack->push('Value', $token);
if (isset($storeKey)) { if (isset($storeKey)) {
$branchStore[$storeKey] = $token; $branchStore[$storeKey] = $token;
@ -4329,7 +4339,7 @@ class Calculation
if (is_string($operand)) { if (is_string($operand)) {
// We only need special validations for the operand if it is a string // We only need special validations for the operand if it is a string
// Start by stripping off the quotation marks we use to identify true excel string values internally // Start by stripping off the quotation marks we use to identify true excel string values internally
if ($operand > '' && $operand[0] == '"') { if ($operand > '' && $operand[0] == self::FORMULA_STRING_QUOTE) {
$operand = self::unwrapResult($operand); $operand = self::unwrapResult($operand);
} }
// If the string is a numeric value, we treat it as a numeric, so no further testing // If the string is a numeric value, we treat it as a numeric, so no further testing
@ -4342,7 +4352,7 @@ class Calculation
return false; return false;
} elseif (!Shared\StringHelper::convertToNumberIfFraction($operand)) { } elseif (!Shared\StringHelper::convertToNumberIfFraction($operand)) {
// If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations // If not a numeric or a fraction, then it's a text string, and so can't be used in mathematical binary operations
$stack->push('Value', '#VALUE!'); $stack->push('Error', '#VALUE!');
$this->debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!')); $this->debugLog->writeDebugLog('Evaluation Result is a ', $this->showTypeDetails('#VALUE!'));
return false; return false;
@ -4402,10 +4412,10 @@ class Calculation
} }
// Simple validate the two operands if they are string values // Simple validate the two operands if they are string values
if (is_string($operand1) && $operand1 > '' && $operand1[0] == '"') { if (is_string($operand1) && $operand1 > '' && $operand1[0] == self::FORMULA_STRING_QUOTE) {
$operand1 = self::unwrapResult($operand1); $operand1 = self::unwrapResult($operand1);
} }
if (is_string($operand2) && $operand2 > '' && $operand2[0] == '"') { if (is_string($operand2) && $operand2 > '' && $operand2[0] == self::FORMULA_STRING_QUOTE) {
$operand2 = self::unwrapResult($operand2); $operand2 = self::unwrapResult($operand2);
} }
@ -4570,7 +4580,7 @@ class Calculation
case '/': case '/':
if ($operand2 == 0) { if ($operand2 == 0) {
// Trap for Divide by Zero error // Trap for Divide by Zero error
$stack->push('Value', '#DIV/0!'); $stack->push('Error', '#DIV/0!');
$this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!')); $this->debugLog->writeDebugLog('Evaluation Result is ', $this->showTypeDetails('#DIV/0!'));
return false; return false;

View File

@ -1339,6 +1339,8 @@ class MathTrig
// Is it a numeric value? // Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) { if ((is_numeric($arg)) && (!is_string($arg))) {
$returnValue += $arg; $returnValue += $arg;
} elseif (Functions::isError($arg)) {
return $arg;
} }
} }

View File

@ -65,6 +65,8 @@ class DefaultValueBinder implements IValueBinder
return DataType::TYPE_STRING; return DataType::TYPE_STRING;
} elseif ((strpos($pValue, '.') === false) && ($pValue > PHP_INT_MAX)) { } elseif ((strpos($pValue, '.') === false) && ($pValue > PHP_INT_MAX)) {
return DataType::TYPE_STRING; return DataType::TYPE_STRING;
} elseif (!is_numeric($pValue)) {
return DataType::TYPE_STRING;
} }
return DataType::TYPE_NUMERIC; return DataType::TYPE_NUMERIC;

View File

@ -933,7 +933,12 @@ class Html extends BaseReader
*/ */
private function setBorderStyle(Style $cellStyle, $styleValue, $type): void private function setBorderStyle(Style $cellStyle, $styleValue, $type): void
{ {
if (trim($styleValue) === Border::BORDER_NONE) {
$borderStyle = Border::BORDER_NONE;
$color = null;
} else {
[, $borderStyle, $color] = explode(' ', $styleValue); [, $borderStyle, $color] = explode(' ', $styleValue);
}
$cellStyle->applyFromArray([ $cellStyle->applyFromArray([
'borders' => [ 'borders' => [

View File

@ -1000,12 +1000,13 @@ class Xlsx extends BaseReader
Settings::getLibXmlLoaderOptions() Settings::getLibXmlLoaderOptions()
); );
$drawings = []; $drawings = [];
if (isset($relsVML->Relationship)) {
foreach ($relsVML->Relationship as $ele) { foreach ($relsVML->Relationship as $ele) {
if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') { if ($ele['Type'] == 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image') {
$drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']); $drawings[(string) $ele['Id']] = self::dirAdd($vmlRelationship, $ele['Target']);
} }
} }
}
// Fetch VML document // Fetch VML document
$vmlDrawing = simplexml_load_string( $vmlDrawing = simplexml_load_string(
$this->securityScanner->scan($this->getFromZipArchive($zip, $vmlRelationship)), $this->securityScanner->scan($this->getFromZipArchive($zip, $vmlRelationship)),

View File

@ -6,6 +6,65 @@ use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class CodePage class CodePage
{ {
private static $pageArray = [
0 => 'CP1252', // CodePage is not always correctly set when the xls file was saved by Apple's Numbers program
367 => 'ASCII', // ASCII
437 => 'CP437', // OEM US
//720 => 'notsupported', // OEM Arabic
737 => 'CP737', // OEM Greek
775 => 'CP775', // OEM Baltic
850 => 'CP850', // OEM Latin I
852 => 'CP852', // OEM Latin II (Central European)
855 => 'CP855', // OEM Cyrillic
857 => 'CP857', // OEM Turkish
858 => 'CP858', // OEM Multilingual Latin I with Euro
860 => 'CP860', // OEM Portugese
861 => 'CP861', // OEM Icelandic
862 => 'CP862', // OEM Hebrew
863 => 'CP863', // OEM Canadian (French)
864 => 'CP864', // OEM Arabic
865 => 'CP865', // OEM Nordic
866 => 'CP866', // OEM Cyrillic (Russian)
869 => 'CP869', // OEM Greek (Modern)
874 => 'CP874', // ANSI Thai
932 => 'CP932', // ANSI Japanese Shift-JIS
936 => 'CP936', // ANSI Chinese Simplified GBK
949 => 'CP949', // ANSI Korean (Wansung)
950 => 'CP950', // ANSI Chinese Traditional BIG5
1200 => 'UTF-16LE', // UTF-16 (BIFF8)
1250 => 'CP1250', // ANSI Latin II (Central European)
1251 => 'CP1251', // ANSI Cyrillic
1252 => 'CP1252', // ANSI Latin I (BIFF4-BIFF7)
1253 => 'CP1253', // ANSI Greek
1254 => 'CP1254', // ANSI Turkish
1255 => 'CP1255', // ANSI Hebrew
1256 => 'CP1256', // ANSI Arabic
1257 => 'CP1257', // ANSI Baltic
1258 => 'CP1258', // ANSI Vietnamese
1361 => 'CP1361', // ANSI Korean (Johab)
10000 => 'MAC', // Apple Roman
10001 => 'CP932', // Macintosh Japanese
10002 => 'CP950', // Macintosh Chinese Traditional
10003 => 'CP1361', // Macintosh Korean
10004 => 'MACARABIC', // Apple Arabic
10005 => 'MACHEBREW', // Apple Hebrew
10006 => 'MACGREEK', // Macintosh Greek
10007 => 'MACCYRILLIC', // Macintosh Cyrillic
10008 => 'CP936', // Macintosh - Simplified Chinese (GB 2312)
10010 => 'MACROMANIA', // Macintosh Romania
10017 => 'MACUKRAINE', // Macintosh Ukraine
10021 => 'MACTHAI', // Macintosh Thai
10029 => 'MACCENTRALEUROPE', // Macintosh Central Europe
10079 => 'MACICELAND', // Macintosh Icelandic
10081 => 'MACTURKISH', // Macintosh Turkish
10082 => 'MACCROATIAN', // Macintosh Croatian
21010 => 'UTF-16LE', // UTF-16 (BIFF8) This isn't correct, but some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE
32768 => 'MAC', // Apple Roman
//32769 => 'unsupported', // ANSI Latin I (BIFF2-BIFF3)
65000 => 'UTF-7', // Unicode (UTF-7)
65001 => 'UTF-8', // Unicode (UTF-8)
];
/** /**
* Convert Microsoft Code Page Identifier to Code Page Name which iconv * Convert Microsoft Code Page Identifier to Code Page Name which iconv
* and mbstring understands. * and mbstring understands.
@ -14,123 +73,20 @@ class CodePage
* *
* @return string Code Page Name * @return string Code Page Name
*/ */
public static function numberToName($codePage) public static function numberToName(int $codePage): string
{ {
switch ($codePage) { if (array_key_exists($codePage, self::$pageArray)) {
case 367: return self::$pageArray[$codePage];
return 'ASCII'; // ASCII }
case 437: if ($codePage == 720 || $codePage == 32769) {
return 'CP437'; // OEM US throw new PhpSpreadsheetException("Code page $codePage not supported."); // OEM Arabic
case 720:
throw new PhpSpreadsheetException('Code page 720 not supported.'); // OEM Arabic
case 737:
return 'CP737'; // OEM Greek
case 775:
return 'CP775'; // OEM Baltic
case 850:
return 'CP850'; // OEM Latin I
case 852:
return 'CP852'; // OEM Latin II (Central European)
case 855:
return 'CP855'; // OEM Cyrillic
case 857:
return 'CP857'; // OEM Turkish
case 858:
return 'CP858'; // OEM Multilingual Latin I with Euro
case 860:
return 'CP860'; // OEM Portugese
case 861:
return 'CP861'; // OEM Icelandic
case 862:
return 'CP862'; // OEM Hebrew
case 863:
return 'CP863'; // OEM Canadian (French)
case 864:
return 'CP864'; // OEM Arabic
case 865:
return 'CP865'; // OEM Nordic
case 866:
return 'CP866'; // OEM Cyrillic (Russian)
case 869:
return 'CP869'; // OEM Greek (Modern)
case 874:
return 'CP874'; // ANSI Thai
case 932:
return 'CP932'; // ANSI Japanese Shift-JIS
case 936:
return 'CP936'; // ANSI Chinese Simplified GBK
case 949:
return 'CP949'; // ANSI Korean (Wansung)
case 950:
return 'CP950'; // ANSI Chinese Traditional BIG5
case 1200:
return 'UTF-16LE'; // UTF-16 (BIFF8)
case 1250:
return 'CP1250'; // ANSI Latin II (Central European)
case 1251:
return 'CP1251'; // ANSI Cyrillic
case 0:
// CodePage is not always correctly set when the xls file was saved by Apple's Numbers program
case 1252:
return 'CP1252'; // ANSI Latin I (BIFF4-BIFF7)
case 1253:
return 'CP1253'; // ANSI Greek
case 1254:
return 'CP1254'; // ANSI Turkish
case 1255:
return 'CP1255'; // ANSI Hebrew
case 1256:
return 'CP1256'; // ANSI Arabic
case 1257:
return 'CP1257'; // ANSI Baltic
case 1258:
return 'CP1258'; // ANSI Vietnamese
case 1361:
return 'CP1361'; // ANSI Korean (Johab)
case 10000:
return 'MAC'; // Apple Roman
case 10001:
return 'CP932'; // Macintosh Japanese
case 10002:
return 'CP950'; // Macintosh Chinese Traditional
case 10003:
return 'CP1361'; // Macintosh Korean
case 10004:
return 'MACARABIC'; // Apple Arabic
case 10005:
return 'MACHEBREW'; // Apple Hebrew
case 10006:
return 'MACGREEK'; // Macintosh Greek
case 10007:
return 'MACCYRILLIC'; // Macintosh Cyrillic
case 10008:
return 'CP936'; // Macintosh - Simplified Chinese (GB 2312)
case 10010:
return 'MACROMANIA'; // Macintosh Romania
case 10017:
return 'MACUKRAINE'; // Macintosh Ukraine
case 10021:
return 'MACTHAI'; // Macintosh Thai
case 10029:
return 'MACCENTRALEUROPE'; // Macintosh Central Europe
case 10079:
return 'MACICELAND'; // Macintosh Icelandic
case 10081:
return 'MACTURKISH'; // Macintosh Turkish
case 10082:
return 'MACCROATIAN'; // Macintosh Croatian
case 21010:
return 'UTF-16LE'; // UTF-16 (BIFF8) This isn't correct, but some Excel writer libraries erroneously use Codepage 21010 for UTF-16LE
case 32768:
return 'MAC'; // Apple Roman
case 32769:
throw new PhpSpreadsheetException('Code page 32769 not supported.'); // ANSI Latin I (BIFF2-BIFF3)
case 65000:
return 'UTF-7'; // Unicode (UTF-7)
case 65001:
return 'UTF-8'; // Unicode (UTF-8)
} }
throw new PhpSpreadsheetException('Unknown codepage: ' . $codePage); throw new PhpSpreadsheetException('Unknown codepage: ' . $codePage);
} }
public static function getEncodings(): array
{
return self::$pageArray;
}
} }

View File

@ -4,10 +4,10 @@ namespace PhpOffice\PhpSpreadsheet\Shared;
use DateTimeInterface; use DateTimeInterface;
use DateTimeZone; use DateTimeZone;
use Exception;
use PhpOffice\PhpSpreadsheet\Calculation\DateTime; use PhpOffice\PhpSpreadsheet\Calculation\DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Cell; use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat; use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
class Date class Date
@ -97,17 +97,18 @@ class Date
* @param DateTimeZone|string $timeZone The timezone to set for all Excel datetimestamp to PHP DateTime Object conversions * @param DateTimeZone|string $timeZone The timezone to set for all Excel datetimestamp to PHP DateTime Object conversions
* *
* @return bool Success or failure * @return bool Success or failure
* @return bool Success or failure
*/ */
public static function setDefaultTimezone($timeZone) public static function setDefaultTimezone($timeZone)
{ {
if ($timeZone = self::validateTimeZone($timeZone)) { try {
$timeZone = self::validateTimeZone($timeZone);
self::$defaultTimeZone = $timeZone; self::$defaultTimeZone = $timeZone;
$retval = true;
return true; } catch (PhpSpreadsheetException $e) {
$retval = false;
} }
return false; return $retval;
} }
/** /**
@ -130,17 +131,17 @@ class Date
* @param DateTimeZone|string $timeZone The timezone to validate, either as a timezone string or object * @param DateTimeZone|string $timeZone The timezone to validate, either as a timezone string or object
* *
* @return DateTimeZone The timezone as a timezone object * @return DateTimeZone The timezone as a timezone object
* @return DateTimeZone The timezone as a timezone object
*/ */
protected static function validateTimeZone($timeZone) private static function validateTimeZone($timeZone)
{ {
if (is_object($timeZone) && $timeZone instanceof DateTimeZone) { if ($timeZone instanceof DateTimeZone) {
return $timeZone; return $timeZone;
} elseif (is_string($timeZone)) { }
if (in_array($timeZone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC))) {
return new DateTimeZone($timeZone); return new DateTimeZone($timeZone);
} }
throw new Exception('Invalid timezone'); throw new PhpSpreadsheetException('Invalid timezone');
} }
/** /**
@ -316,7 +317,7 @@ class Date
*/ */
public static function isDateTime(Cell $pCell) public static function isDateTime(Cell $pCell)
{ {
return is_numeric($pCell->getValue()) && return is_numeric($pCell->getCalculatedValue()) &&
self::isDateTimeFormat( self::isDateTimeFormat(
$pCell->getWorksheet()->getStyle( $pCell->getWorksheet()->getStyle(
$pCell->getCoordinate() $pCell->getCoordinate()

View File

@ -23,7 +23,7 @@ class TimeZone
*/ */
private static function validateTimeZone($timezone) private static function validateTimeZone($timezone)
{ {
return in_array($timezone, DateTimeZone::listIdentifiers()); return in_array($timezone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC));
} }
/** /**
@ -73,10 +73,6 @@ class TimeZone
$timezone = self::$timezone; $timezone = self::$timezone;
} }
if ($timezone == 'UST') {
return 0;
}
$objTimezone = new DateTimeZone($timezone); $objTimezone = new DateTimeZone($timezone);
$transitions = $objTimezone->getTransitions($timestamp, $timestamp); $transitions = $objTimezone->getTransitions($timestamp, $timestamp);

View File

@ -183,7 +183,6 @@ class Rels extends WriterPart
$objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
// Write drawing relationships? // Write drawing relationships?
$d = 0;
$drawingOriginalIds = []; $drawingOriginalIds = [];
$unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData(); $unparsedLoadedData = $pWorksheet->getParent()->getUnparsedLoadedData();
if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) { if (isset($unparsedLoadedData['sheets'][$pWorksheet->getCodeName()]['drawingOriginalIds'])) {
@ -197,13 +196,19 @@ class Rels extends WriterPart
} }
if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) { if (($pWorksheet->getDrawingCollection()->count() > 0) || (count($charts) > 0) || $drawingOriginalIds) {
$relPath = '../drawings/drawing' . $pWorksheetId . '.xml'; $rId = 1;
$rId = ++$d;
// Use original $relPath to get original $rId.
// Take first. In future can be overwritten.
// (! synchronize with \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Worksheet::writeDrawings)
reset($drawingOriginalIds);
$relPath = key($drawingOriginalIds);
if (isset($drawingOriginalIds[$relPath])) { if (isset($drawingOriginalIds[$relPath])) {
$rId = (int) (substr($drawingOriginalIds[$relPath], 3)); $rId = (int) (substr($drawingOriginalIds[$relPath], 3));
} }
// Generate new $relPath to write drawing relationship
$relPath = '../drawings/drawing' . $pWorksheetId . '.xml';
$this->writeRelationship( $this->writeRelationship(
$objWriter, $objWriter,
$rId, $rId,

View File

@ -1215,6 +1215,7 @@ class Worksheet extends WriterPart
if (isset($unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) { if (isset($unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'])) {
$drawingOriginalIds = $unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds']; $drawingOriginalIds = $unparsedLoadedData['sheets'][$pSheet->getCodeName()]['drawingOriginalIds'];
// take first. In future can be overriten // take first. In future can be overriten
// (! synchronize with \PhpOffice\PhpSpreadsheet\Writer\Xlsx\Rels::writeWorksheetRelationships)
$rId = reset($drawingOriginalIds); $rId = reset($drawingOriginalIds);
} }

View File

@ -18,5 +18,6 @@ class DefinedNameConfusedForCellTest extends TestCase
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test'); $filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
$writer->save($filename); $writer->save($filename);
self::assertTrue(true); self::assertTrue(true);
unlink($filename);
} }
} }

View File

@ -0,0 +1,54 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Engine;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
class RangeTest extends TestCase
{
protected $spreadSheet;
protected function setUp(): void
{
$this->spreadSheet = new Spreadsheet();
$this->spreadSheet->getActiveSheet()
->setCellValue('A1', 1)
->setCellValue('B1', 2)
->setCellValue('C1', 3)
->setCellValue('A2', 4)
->setCellValue('B2', 5)
->setCellValue('C2', 6)
->setCellValue('A3', 7)
->setCellValue('B3', 8)
->setCellValue('C3', 9);
}
/**
* @dataProvider providerRangeEvaluation
*
* @param mixed $formula
* @param int $expectedResult
*/
public function testRangeEvaluation($formula, $expectedResult): void
{
$workSheet = $this->spreadSheet->getActiveSheet();
$workSheet->setCellValue('E1', $formula);
$actualRresult = $workSheet->getCell('E1')->getCalculatedValue();
self::assertSame($expectedResult, $actualRresult);
}
public function providerRangeEvaluation()
{
return[
['=SUM(A1:B3,A1:C2)', 48],
['=SUM(A1:B3 A1:C2)', 12],
['=SUM(A1:A3,C1:C3)', 30],
['=SUM(A1:A3 C1:C3)', Functions::null()],
['=SUM(A1:B2,B2:C3)', 40],
['=SUM(A1:B2 B2:C3)', 5],
];
}
}

View File

@ -57,6 +57,7 @@ class DefaultValueBinderTest extends TestCase
['#REF!'], ['#REF!'],
[new DateTime()], [new DateTime()],
[new DateTimeImmutable()], [new DateTimeImmutable()],
['123456\n'],
]; ];
} }

View File

@ -200,6 +200,37 @@ EOF;
self::assertEquals($expected, $sheet->getCell('B3')->getValue()); self::assertEquals($expected, $sheet->getCell('B3')->getValue());
} }
public function testLineBreakEscape(): void
{
$reader = new Csv();
$spreadsheet = $reader->load('tests/data/Reader/CSV/line_break_in_enclosure_with_escaped_quotes.csv');
$sheet = $spreadsheet->getActiveSheet();
$expected = <<<EOF
This is a "test csv file"
with both "line breaks"
and "escaped
quotes" that breaks
the delimiters
EOF;
self::assertEquals($expected, $sheet->getCell('B3')->getValue());
}
public function testUtf32LineBreakEscape(): void
{
$reader = new Csv();
$reader->setInputEncoding('UTF-32LE');
$spreadsheet = $reader->load('tests/data/Reader/CSV/line_break_escaped_32le.csv');
$sheet = $spreadsheet->getActiveSheet();
$expected = <<<EOF
This is a "test csv file"
with both "line breaks"
and "escaped
quotes" that breaks
the delimiters
EOF;
self::assertEquals($expected, $sheet->getCell('B3')->getValue());
}
public function testSeparatorLine(): void public function testSeparatorLine(): void
{ {
$reader = new Csv(); $reader = new Csv();

View File

@ -11,14 +11,6 @@ use PHPUnit\Framework\TestCase;
class Xlsx2Test extends TestCase class Xlsx2Test extends TestCase
{ {
protected function tearDown(): void
{
$outfile = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
if (file_exists($outfile)) {
unlink($outfile);
}
}
public function testLoadXlsxConditionalFormatting2(): void public function testLoadXlsxConditionalFormatting2(): void
{ {
// Make sure Conditionals are read correctly from existing file // Make sure Conditionals are read correctly from existing file
@ -63,6 +55,7 @@ class Xlsx2Test extends TestCase
$writer = IOFactory::createWriter($spreadshee1, 'Xlsx'); $writer = IOFactory::createWriter($spreadshee1, 'Xlsx');
$writer->save($outfile); $writer->save($outfile);
$spreadsheet = $reader->load($outfile); $spreadsheet = $reader->load($outfile);
unlink($outfile);
$worksheet = $spreadsheet->getActiveSheet(); $worksheet = $spreadsheet->getActiveSheet();
$conditionalStyle = $worksheet->getConditionalStyles('A2:A8'); $conditionalStyle = $worksheet->getConditionalStyles('A2:A8');
@ -110,6 +103,7 @@ class Xlsx2Test extends TestCase
$writer->save($outfile); $writer->save($outfile);
$reader = IOFactory::createReader('Xlsx'); $reader = IOFactory::createReader('Xlsx');
$spreadsheet = $reader->load($outfile); $spreadsheet = $reader->load($outfile);
unlink($outfile);
$worksheet = $spreadsheet->getActiveSheet(); $worksheet = $spreadsheet->getActiveSheet();
$conditionalStyle = $worksheet->getConditionalStyles('A1:A6'); $conditionalStyle = $worksheet->getConditionalStyles('A1:A6');

View File

@ -222,6 +222,7 @@ class XlsxTest extends TestCase
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($excel); $writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($excel);
$writer->save($resultFilename); $writer->save($resultFilename);
$excel = $reader->load($resultFilename); $excel = $reader->load($resultFilename);
unlink($resultFilename);
// Fake assert. The only thing we need is to ensure the file is loaded without exception // Fake assert. The only thing we need is to ensure the file is loaded without exception
self::assertNotNull($excel); self::assertNotNull($excel);
} }

View File

@ -24,6 +24,22 @@ class CodePageTest extends TestCase
return require 'tests/data/Shared/CodePage.php'; return require 'tests/data/Shared/CodePage.php';
} }
public function testCoverage(): void
{
$covered = [];
$expected = CodePage::getEncodings();
foreach ($expected as $key => $val) {
$covered[$key] = 0;
}
$tests = $this->providerCodePage();
foreach ($tests as $test) {
$covered[$test[1]] = 1;
}
foreach ($covered as $key => $val) {
self::assertEquals(1, $val, "Codepage $key not tested");
}
}
public function testNumberToNameWithInvalidCodePage(): void public function testNumberToNameWithInvalidCodePage(): void
{ {
$invalidCodePage = 12345; $invalidCodePage = 12345;

View File

@ -3,10 +3,23 @@
namespace PhpOffice\PhpSpreadsheetTests\Shared; namespace PhpOffice\PhpSpreadsheetTests\Shared;
use PhpOffice\PhpSpreadsheet\Shared\Date; use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class DateTest extends TestCase class DateTest extends TestCase
{ {
private $dttimezone;
protected function setUp(): void
{
$this->dttimezone = Date::getDefaultTimeZone();
}
protected function tearDown(): void
{
Date::setDefaultTimeZone($this->dttimezone);
}
public function testSetExcelCalendar(): void public function testSetExcelCalendar(): void
{ {
$calendarValues = [ $calendarValues = [
@ -168,4 +181,41 @@ class DateTest extends TestCase
{ {
return require 'tests/data/Shared/Date/ExcelToTimestamp1900Timezone.php'; return require 'tests/data/Shared/Date/ExcelToTimestamp1900Timezone.php';
} }
public function testVarious(): void
{
Date::setDefaultTimeZone('UTC');
self::assertFalse(Date::stringToExcel('2019-02-29'));
self::assertTrue((bool) Date::stringToExcel('2019-02-28'));
self::assertTrue((bool) Date::stringToExcel('2019-02-28 11:18'));
self::assertFalse(Date::stringToExcel('2019-02-28 11:71'));
$date = Date::PHPToExcel('2020-01-01');
self::assertEquals(43831.0, $date);
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('B1', 'x');
$val = $sheet->getCell('B1')->getValue();
self::assertFalse(Date::timestampToExcel($val));
$cell = $sheet->getCell('A1');
self::assertNotNull($cell);
$cell->setValue($date);
$sheet->getStyle('A1')
->getNumberFormat()
->setFormatCode(NumberFormat::FORMAT_DATE_DATETIME);
self::assertTrue(null !== $cell && Date::isDateTime($cell));
$cella2 = $sheet->getCell('A2');
self::assertNotNull($cella2);
$cella2->setValue('=A1+2');
$sheet->getStyle('A2')
->getNumberFormat()
->setFormatCode(NumberFormat::FORMAT_DATE_DATETIME);
self::assertTrue(null !== $cella2 && Date::isDateTime($cella2));
$cella3 = $sheet->getCell('A3');
self::assertNotNull($cella3);
$cella3->setValue('=A1+4');
$sheet->getStyle('A3')
->getNumberFormat()
->setFormatCode('0.00E+00');
self::assertFalse(null !== $cella3 && Date::isDateTime($cella3));
}
} }

View File

@ -2,11 +2,29 @@
namespace PhpOffice\PhpSpreadsheetTests\Shared; namespace PhpOffice\PhpSpreadsheetTests\Shared;
use DateTime;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\TimeZone; use PhpOffice\PhpSpreadsheet\Shared\TimeZone;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class TimeZoneTest extends TestCase class TimeZoneTest extends TestCase
{ {
private $tztimezone;
private $dttimezone;
protected function setUp(): void
{
$this->tztimezone = TimeZone::getTimeZone();
$this->dttimezone = Date::getDefaultTimeZone();
}
protected function tearDown(): void
{
TimeZone::setTimeZone($this->tztimezone);
Date::setDefaultTimeZone($this->dttimezone);
}
public function testSetTimezone(): void public function testSetTimezone(): void
{ {
$timezoneValues = [ $timezoneValues = [
@ -20,13 +38,51 @@ class TimeZoneTest extends TestCase
foreach ($timezoneValues as $timezoneValue) { foreach ($timezoneValues as $timezoneValue) {
$result = TimeZone::setTimezone($timezoneValue); $result = TimeZone::setTimezone($timezoneValue);
self::assertTrue($result); self::assertTrue($result);
$result = Date::setDefaultTimezone($timezoneValue);
self::assertTrue($result);
} }
} }
public function testSetTimezoneBackwardCompatible(): void
{
$bcTimezone = 'Etc/GMT+10';
$result = TimeZone::setTimezone($bcTimezone);
self::assertTrue($result);
$result = Date::setDefaultTimezone($bcTimezone);
self::assertTrue($result);
}
public function testSetTimezoneWithInvalidValue(): void public function testSetTimezoneWithInvalidValue(): void
{ {
$unsupportedTimezone = 'Etc/GMT+10'; $unsupportedTimezone = 'XEtc/GMT+10';
$result = TimeZone::setTimezone($unsupportedTimezone); $result = TimeZone::setTimezone($unsupportedTimezone);
self::assertFalse($result); self::assertFalse($result);
$result = Date::setDefaultTimezone($unsupportedTimezone);
self::assertFalse($result);
}
public function testTimeZoneAdjustmentsInvalidTz(): void
{
$this->expectException(\PhpOffice\PhpSpreadsheet\Exception::class);
$dtobj = DateTime::createFromFormat('Y-m-d H:i:s', '2008-09-22 00:00:00');
$tstmp = $dtobj->getTimestamp();
$unsupportedTimeZone = 'XEtc/GMT+10';
TimeZone::getTimeZoneAdjustment($unsupportedTimeZone, $tstmp);
}
public function testTimeZoneAdjustments(): void
{
$dtobj = DateTime::createFromFormat('Y-m-d H:i:s', '2008-01-01 00:00:00');
$tstmp = $dtobj->getTimestamp();
$supportedTimeZone = 'UTC';
$adj = TimeZone::getTimeZoneAdjustment($supportedTimeZone, $tstmp);
self::assertEquals(0, $adj);
$supportedTimeZone = 'America/Toronto';
$adj = TimeZone::getTimeZoneAdjustment($supportedTimeZone, $tstmp);
self::assertEquals(-18000, $adj);
$supportedTimeZone = 'America/Chicago';
TimeZone::setTimeZone($supportedTimeZone);
$adj = TimeZone::getTimeZoneAdjustment(null, $tstmp);
self::assertEquals(-21600, $adj);
} }
} }

View File

@ -9,6 +9,18 @@ use PhpOffice\PhpSpreadsheetTests\Functional;
class ImagesRootTest extends Functional\AbstractFunctional class ImagesRootTest extends Functional\AbstractFunctional
{ {
private $curdir;
protected function setUp(): void
{
$this->curdir = getcwd();
}
protected function tearDown(): void
{
chdir($this->curdir);
}
public function testImagesRoot(): void public function testImagesRoot(): void
{ {
$spreadsheet = new Spreadsheet(); $spreadsheet = new Spreadsheet();
@ -20,7 +32,6 @@ class ImagesRootTest extends Functional\AbstractFunctional
$newdir = __DIR__ . '/../../../data/Reader/HTML'; $newdir = __DIR__ . '/../../../data/Reader/HTML';
$stub = 'image.jpg'; $stub = 'image.jpg';
$imagePath = "./$stub"; $imagePath = "./$stub";
$curdir = getcwd();
chdir($newdir); chdir($newdir);
self::assertFileExists($imagePath); self::assertFileExists($imagePath);
$drawing->setPath($imagePath); $drawing->setPath($imagePath);
@ -34,7 +45,6 @@ class ImagesRootTest extends Functional\AbstractFunctional
$writer = new Html($spreadsheet); $writer = new Html($spreadsheet);
$writer->setImagesRoot($root); $writer->setImagesRoot($root);
$html = $writer->generateHTMLAll(); $html = $writer->generateHTMLAll();
chdir($curdir);
$dom = new DOMDocument(); $dom = new DOMDocument();
$dom->loadHTML($html); $dom->loadHTML($html);
$body = $dom->getElementsByTagName('body')[0]; $body = $dom->getElementsByTagName('body')[0];

View File

@ -30,9 +30,8 @@ class InvalidFileNameTest extends Functional\AbstractFunctional
$writer->save(''); $writer->save('');
} }
public function testEmptyTempdirNamePdf(): void public function testNotEmptyTempdirNamePdf(): void
{ {
$this->expectException(WriterException::class);
$spreadsheet = new Spreadsheet(); $spreadsheet = new Spreadsheet();
$spreadsheet->getActiveSheet()->getCell('A1')->setValue('Cell 1'); $spreadsheet->getActiveSheet()->getCell('A1')->setValue('Cell 1');
$writer = new Mpdf($spreadsheet); $writer = new Mpdf($spreadsheet);
@ -41,6 +40,16 @@ class InvalidFileNameTest extends Functional\AbstractFunctional
$writer->setPaperSize(PageSetup::PAPERSIZE_LEDGER); $writer->setPaperSize(PageSetup::PAPERSIZE_LEDGER);
self::assertEquals($writer->getPaperSize(), PageSetup::PAPERSIZE_LEDGER); self::assertEquals($writer->getPaperSize(), PageSetup::PAPERSIZE_LEDGER);
self::assertEquals(File::sysGetTempDir() . '/phpsppdf', $writer->getTempDir()); self::assertEquals(File::sysGetTempDir() . '/phpsppdf', $writer->getTempDir());
$writer->setTempDir(File::sysGetTempDir());
self::assertEquals(File::sysGetTempDir(), $writer->getTempDir());
}
public function testEmptyTempdirNamePdf(): void
{
$this->expectException(WriterException::class);
$spreadsheet = new Spreadsheet();
$spreadsheet->getActiveSheet()->getCell('A1')->setValue('Cell 1');
$writer = new Mpdf($spreadsheet);
$writer->setTempDir(''); $writer->setTempDir('');
} }

View File

@ -9,14 +9,6 @@ use PHPUnit\Framework\TestCase;
class FormulaErrTest extends TestCase class FormulaErrTest extends TestCase
{ {
protected function tearDown(): void
{
$filename = tempnam(File::sysGetTempDir(), 'phpspreadsheet-test');
if (file_exists($filename)) {
unlink($filename);
}
}
public function testFormulaError(): void public function testFormulaError(): void
{ {
$obj = new \PhpOffice\PhpSpreadsheet\Spreadsheet(); $obj = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
@ -31,6 +23,7 @@ class FormulaErrTest extends TestCase
$writer->save($filename); $writer->save($filename);
$reader = IOFactory::createReader('Xls'); $reader = IOFactory::createReader('Xls');
$robj = $reader->load($filename); $robj = $reader->load($filename);
unlink($filename);
$sheet0 = $robj->setActiveSheetIndex(0); $sheet0 = $robj->setActiveSheetIndex(0);
$a1 = $sheet0->getCell('A1')->getCalculatedValue(); $a1 = $sheet0->getCell('A1')->getCalculatedValue();
self::assertEquals(2, $a1); self::assertEquals(2, $a1);

View File

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Settings;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class DrawingsTest extends AbstractFunctional
{
/**
* @var int
*/
protected $prevValue;
protected function setUp(): void
{
$this->prevValue = Settings::getLibXmlLoaderOptions();
// Disable validating XML with the DTD
Settings::setLibXmlLoaderOptions($this->prevValue & ~LIBXML_DTDVALID & ~LIBXML_DTDATTR & ~LIBXML_DTDLOAD);
}
protected function tearDown(): void
{
Settings::setLibXmlLoaderOptions($this->prevValue);
}
/**
* Test save and load XLSX file with drawing on 2nd worksheet.
*/
public function testSaveLoadWithDrawingOn2ndWorksheet(): void
{
// Read spreadsheet from file
$inputFilename = 'tests/data/Writer/XLSX/drawing_on_2nd_page.xlsx';
$reader = new Xlsx();
$spreadsheet = $reader->load($inputFilename);
// Save spreadsheet to file and read it back
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, 'Xlsx');
// Fake assert. The only thing we need is to ensure the file is loaded without exception
self::assertNotNull($reloadedSpreadsheet);
}
}

View File

@ -28,6 +28,7 @@ class FloatsRetainedTest extends TestCase
$reader = new Reader(); $reader = new Reader();
$sheet = $reader->load($outputFilename); $sheet = $reader->load($outputFilename);
unlink($outputFilename);
self::assertSame($value, $sheet->getActiveSheet()->getCell('A1')->getValue()); self::assertSame($value, $sheet->getActiveSheet()->getCell('A1')->getValue());
} }

View File

@ -73,4 +73,8 @@ return [
'e', 'e',
'#DIV/0!', '#DIV/0!',
], ],
[
's',
'123456\n',
],
]; ];

Binary file not shown.
Can't render this file because it contains an unexpected character in line 2 and column 24.

View File

@ -1,21 +1,21 @@
Name,Copy,URL Name,Copy,URL
Test,"This is a \"test csv file\" Test,"This is a ""test csv file""
with both \"line breaks\" with both ""line breaks""
and \"escaped and ""escaped
quotes\" that breaks quotes"" that breaks
the delimiters",http://google.com the delimiters",http://google.com
Test,"This is a \"test csv file\" Test,"This is a ""test csv file""
with both \"line breaks\" with both ""line breaks""
and \"escaped and ""escaped
quotes\" that breaks quotes"" that breaks
the delimiters",http://google.com the delimiters",http://google.com
Test,"This is a \"test csv file\" Test,"This is a ""test csv file""
with both \"line breaks\" with both ""line breaks""
and \"escaped and ""escaped
quotes\" that breaks quotes"" that breaks
the delimiters",http://google.com the delimiters",http://google.com
Test,"This is a \"test csv file\" Test,"This is a ""test csv file""
with both \"line breaks\" with both ""line breaks""
and \"escaped and ""escaped
quotes\" that breaks quotes"" that breaks
the delimiters",http://google.com the delimiters",http://google.com

Can't render this file because it contains an unexpected character in line 2 and column 18.

View File

@ -1,6 +1,11 @@
<?php <?php
return [ return [
// ANSI Latin I (BIFF4-BIFF7)
[
'CP1252',
0,
],
// ASCII // ASCII
[ [
'ASCII', 'ASCII',
@ -127,11 +132,6 @@ return [
1251, 1251,
], ],
// ANSI Latin I (BIFF4-BIFF7) // ANSI Latin I (BIFF4-BIFF7)
[
'CP1252',
0,
],
// ANSI Latin I (BIFF4-BIFF7)
[ [
'CP1252', 'CP1252',
1252, 1252,
@ -176,6 +176,31 @@ return [
'MAC', 'MAC',
10000, 10000,
], ],
// Macintosh Japanese
[
'CP932',
10001,
],
// Macintosh Chinese Traditional
[
'CP950',
10002,
],
// Macintosh Korean
[
'CP1361',
10003,
],
// Apple Arabic
[
'MACARABIC',
10004,
],
// Apple Hebrew
[
'MACHEBREW',
10005,
],
// Macintosh Greek // Macintosh Greek
[ [
'MACGREEK', 'MACGREEK',
@ -186,6 +211,26 @@ return [
'MACCYRILLIC', 'MACCYRILLIC',
10007, 10007,
], ],
// Macintosh - Simplified Chinese (GB 2312)
[
'CP936',
10008,
],
// Macintosh Romania
[
'MACROMANIA',
10010,
],
// Macintosh Ukraine
[
'MACUKRAINE',
10017,
],
// Macintosh Thai
[
'MACTHAI',
10021,
],
// Macintosh Central Europe // Macintosh Central Europe
[ [
'MACCENTRALEUROPE', 'MACCENTRALEUROPE',
@ -201,6 +246,16 @@ return [
'MACTURKISH', 'MACTURKISH',
10081, 10081,
], ],
// Macintosh Croatian
[
'MACCROATIAN',
10082,
],
// UTF-16 (BIFF8) grandfathers erroneous libraries
[
'UTF-16LE',
21010,
],
// Apple Roman // Apple Roman
[ [
'MAC', 'MAC',

View File

@ -146,4 +146,8 @@ return [
false, false,
'#,##0.00 "dollars"', '#,##0.00 "dollars"',
], ],
[
true,
'"date " y-m-d',
],
]; ];

Binary file not shown.