Files
PhpSpreadsheet/tests/PhpSpreadsheetTests/Writer/Csv/CsvEnclosureTest.php
oleibman 0cd20f3099 Csv Handling of Booleans (and an 8.1 Deprecation) (#2232)
* Csv Handling of Booleans (and an 8.1 Deprecation)

PhpSpreadsheet writes boolean values to a Csv as null-string/1, and treats input values of 'true' and 'false' as if they were strings. On the other hand, Excel writes boolean values to a Csv as TRUE/FALSE, and case-insensitively treats a matching string as boolean on read. This PR changes PhpSpreadsheet to match Excel.

A side-effect of this change is that it fixes behavior incorrectly reported as a bug in PR #2048. That issue was closed, correctly, as user error. The user had altered Csv Writer, including adding ```declare(strict_types=1);```; that declaration was the cause of the error. The "offending" statements, calls to strpbrk and str_replace, will now work correctly whether or not strict_types is in use.

And, just as I was getting ready to push this, the dailies for PHP 8.1 introduced a change deprecating auto_detect_line_endings. Csv Reader uses that setting; it allows it to process a Csv with Mac line endings, which happens to be something that Excel can do. As they say in https://wiki.php.net/rfc/deprecations_php_8_1, where the proposal passed without a single dissenting vote, "These newlines were used by “Classic” Mac OS, a system which has been discontinued in 2001, nearly two decades ago. Interoperability with such systems is no longer relevant." I tend to agree, but I don't know that we're ready to pull the plug yet. I don't see an easy way to emulate that functionality. For now, I have silenced the deprecation notices with at signs. I have also added a test case which will fail when support for that setting is pulled; this will give time to consider alternatives.

* Scrutinizer: Handling ini_set

This could be interesting. It doesn't like not handling an error condition for ini_set. Let's see if this satisfies it.
2021-08-04 07:00:17 -07:00

298 lines
12 KiB
PHP

<?php
namespace PhpOffice\PhpSpreadsheetTests\Writer\Csv;
use PhpOffice\PhpSpreadsheet\Reader\Csv as CsvReader;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Csv as CsvWriter;
use PhpOffice\PhpSpreadsheetTests\Functional;
class CsvEnclosureTest extends Functional\AbstractFunctional
{
private const CELL_VALUES = [
'A1' => '2020-06-03',
'B1' => '000123',
'C1' => '06.53',
'D1' => 14.22,
'A2' => '2020-06-04',
'B2' => '000234',
'C2' => '07.12',
'D2' => '15.44',
];
private static function getFileData(string $filename): string
{
return file_get_contents($filename) ?: '';
}
public function testNormalEnclosure(): void
{
$delimiter = ';';
$enclosure = '"';
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
foreach (self::CELL_VALUES as $key => $value) {
$sheet->setCellValue($key, $value);
}
$writer = new CsvWriter($spreadsheet);
$writer->setDelimiter($delimiter);
$writer->setEnclosure($enclosure);
$filename = File::temporaryFilename();
$writer->save($filename);
$filedata = self::getFileData($filename);
$filedata = preg_replace('/\\r?\\n/', $delimiter, $filedata);
$reader = new CsvReader();
$reader->setDelimiter($delimiter);
$reader->setEnclosure($enclosure);
$newspreadsheet = $reader->load($filename);
unlink($filename);
$sheet = $newspreadsheet->getActiveSheet();
$expected = '';
foreach (self::CELL_VALUES as $key => $value) {
self::assertEquals($value, $sheet->getCell($key)->getValue());
$expected .= "$enclosure$value$enclosure$delimiter";
}
self::assertEquals($expected, $filedata);
$spreadsheet->disconnectWorksheets();
$newspreadsheet->disconnectWorksheets();
}
public function testNoEnclosure(): void
{
$delimiter = ';';
$enclosure = '';
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
foreach (self::CELL_VALUES as $key => $value) {
$sheet->setCellValue($key, $value);
}
$writer = new CsvWriter($spreadsheet);
$writer->setDelimiter($delimiter);
$writer->setEnclosure($enclosure);
self::assertEquals('', $writer->getEnclosure());
$filename = File::temporaryFilename();
$writer->save($filename);
$filedata = self::getFileData($filename);
$filedata = preg_replace('/\\r?\\n/', $delimiter, $filedata);
$reader = new CsvReader();
$reader->setDelimiter($delimiter);
$reader->setEnclosure($enclosure);
self::assertEquals('"', $reader->getEnclosure());
$newspreadsheet = $reader->load($filename);
unlink($filename);
$sheet = $newspreadsheet->getActiveSheet();
$expected = '';
foreach (self::CELL_VALUES as $key => $value) {
self::assertEquals($value, $sheet->getCell($key)->getValue());
$expected .= "$enclosure$value$enclosure$delimiter";
}
self::assertEquals($expected, $filedata);
$spreadsheet->disconnectWorksheets();
$newspreadsheet->disconnectWorksheets();
}
public function testNotRequiredEnclosure1(): void
{
$delimiter = ';';
$enclosure = '"';
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
foreach (self::CELL_VALUES as $key => $value) {
$sheet->setCellValue($key, $value);
}
$writer = new CsvWriter($spreadsheet);
self::assertTrue($writer->getEnclosureRequired());
$writer->setEnclosureRequired(false)->setDelimiter($delimiter)->setEnclosure($enclosure);
$filename = File::temporaryFilename();
$writer->save($filename);
$filedata = self::getFileData($filename);
$filedata = preg_replace('/\\r?\\n/', $delimiter, $filedata);
$reader = new CsvReader();
$reader->setDelimiter($delimiter);
$reader->setEnclosure($enclosure);
$newspreadsheet = $reader->load($filename);
unlink($filename);
$sheet = $newspreadsheet->getActiveSheet();
$expected = '';
foreach (self::CELL_VALUES as $key => $value) {
self::assertEquals($value, $sheet->getCell($key)->getValue());
$expected .= "$value$delimiter";
}
self::assertEquals($expected, $filedata);
$spreadsheet->disconnectWorksheets();
$newspreadsheet->disconnectWorksheets();
}
public function testNotRequiredEnclosure2(): void
{
$cellValues2 = [
'A1' => '2020-06-03',
'B1' => 'has,separator',
'C1' => 'has;non-separator',
'D1' => 'has"enclosure',
'A2' => 'has space',
'B2' => "has\nnewline",
'C2' => '',
'D2' => 15.44,
'A3' => ' leadingspace',
'B3' => 'trailingspace ',
'C3' => '=D2*2',
'D3' => ',leadingcomma',
'A4' => 'trailingquote"',
'B4' => 'unused',
'C4' => 'unused',
'D4' => 'unused',
'A5' => false,
'B5' => true,
'C5' => null,
'D5' => 0,
];
$calcc3 = '30.88';
$expected1 = '2020-06-03,"has,separator",has;non-separator,"has""enclosure"';
$expected2 = 'has space,"has' . "\n" . 'newline",,15.44';
$expected3 = ' leadingspace,trailingspace ,' . $calcc3 . ',",leadingcomma"';
$expected4 = '"trailingquote""",unused,unused,unused';
$expected5 = 'FALSE,TRUE,,0';
$expectedfile = "$expected1\n$expected2\n$expected3\n$expected4\n$expected5\n";
$delimiter = ',';
$enclosure = '"';
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
foreach ($cellValues2 as $key => $value) {
$sheet->setCellValue($key, $value);
}
$writer = new CsvWriter($spreadsheet);
self::assertTrue($writer->getEnclosureRequired());
$writer->setEnclosureRequired(false)->setDelimiter($delimiter)->setEnclosure($enclosure);
$filename = File::temporaryFilename();
$writer->save($filename);
$filedata = self::getFileData($filename);
$filedata = preg_replace('/\\r/', '', $filedata);
$reader = new CsvReader();
$reader->setDelimiter($delimiter);
$reader->setEnclosure($enclosure);
$newspreadsheet = $reader->load($filename);
unlink($filename);
$sheet = $newspreadsheet->getActiveSheet();
foreach ($cellValues2 as $key => $value) {
self::assertEquals(($key === 'C3') ? $calcc3 : $value, $sheet->getCell($key)->getValue(), "Failure for cell $key");
}
self::assertEquals($expectedfile, $filedata);
$spreadsheet->disconnectWorksheets();
$newspreadsheet->disconnectWorksheets();
}
public function testRequiredEnclosure2(): void
{
$cellValues2 = [
'A1' => '2020-06-03',
'B1' => 'has,separator',
'C1' => 'has;non-separator',
'D1' => 'has"enclosure',
'A2' => 'has space',
'B2' => "has\nnewline",
'C2' => '',
'D2' => 15.44,
'A3' => ' leadingspace',
'B3' => 'trailingspace ',
'C3' => '=D2*2',
'D3' => ',leadingcomma',
'A4' => 'trailingquote"',
'B4' => 'unused',
'C4' => 'unused',
'D4' => 'unused',
'A5' => false,
'B5' => true,
'C5' => null,
'D5' => 0,
];
$calcc3 = '30.88';
$expected1 = '"2020-06-03","has,separator","has;non-separator","has""enclosure"';
$expected2 = '"has space","has' . "\n" . 'newline","","15.44"';
$expected3 = '" leadingspace","trailingspace ","' . $calcc3 . '",",leadingcomma"';
$expected4 = '"trailingquote""","unused","unused","unused"';
$expected5 = '"FALSE","TRUE","","0"';
$expectedfile = "$expected1\n$expected2\n$expected3\n$expected4\n$expected5\n";
$delimiter = ',';
$enclosure = '"';
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
foreach ($cellValues2 as $key => $value) {
$sheet->setCellValue($key, $value);
}
$writer = new CsvWriter($spreadsheet);
self::assertTrue($writer->getEnclosureRequired());
$writer->setEnclosureRequired(true)->setDelimiter($delimiter)->setEnclosure($enclosure);
$filename = File::temporaryFilename();
$writer->save($filename);
$filedata = self::getFileData($filename);
$filedata = preg_replace('/\\r/', '', $filedata);
$reader = new CsvReader();
$reader->setDelimiter($delimiter);
$reader->setEnclosure($enclosure);
$newspreadsheet = $reader->load($filename);
unlink($filename);
$sheet = $newspreadsheet->getActiveSheet();
foreach ($cellValues2 as $key => $value) {
self::assertEquals(($key === 'C3') ? $calcc3 : $value, $sheet->getCell($key)->getValue(), "Failure for cell $key");
}
self::assertEquals($expectedfile, $filedata);
$spreadsheet->disconnectWorksheets();
$newspreadsheet->disconnectWorksheets();
}
public function testGoodReread(): void
{
$delimiter = ',';
$enclosure = '"';
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', '1');
$sheet->setCellValue('B1', '2,3');
$sheet->setCellValue('C1', '4');
$writer = new CsvWriter($spreadsheet);
$writer->setEnclosureRequired(false)->setDelimiter($delimiter)->setEnclosure($enclosure);
$filename = File::temporaryFilename();
$writer->save($filename);
$reader = new CsvReader();
$reader->setDelimiter($delimiter);
$reader->setEnclosure($enclosure);
$newspreadsheet = $reader->load($filename);
unlink($filename);
$sheet = $newspreadsheet->getActiveSheet();
self::assertEquals('1', $sheet->getCell('A1')->getValue());
self::assertEquals('2,3', $sheet->getCell('B1')->getValue());
self::assertEquals('4', $sheet->getCell('C1')->getValue());
$spreadsheet->disconnectWorksheets();
$newspreadsheet->disconnectWorksheets();
}
public function testBadReread(): void
{
$delimiter = ',';
$enclosure = '';
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->setCellValue('A1', '1');
$sheet->setCellValue('B1', '2,3');
$sheet->setCellValue('C1', '4');
$writer = new CsvWriter($spreadsheet);
$writer->setDelimiter($delimiter)->setEnclosure($enclosure);
$filename = File::temporaryFilename();
$writer->save($filename);
$reader = new CsvReader();
$reader->setDelimiter($delimiter);
$reader->setEnclosure($enclosure);
$newspreadsheet = $reader->load($filename);
unlink($filename);
$sheet = $newspreadsheet->getActiveSheet();
self::assertEquals('1', $sheet->getCell('A1')->getValue());
self::assertEquals('2', $sheet->getCell('B1')->getValue());
self::assertEquals('3', $sheet->getCell('C1')->getValue());
self::assertEquals('4', $sheet->getCell('D1')->getValue());
$spreadsheet->disconnectWorksheets();
$newspreadsheet->disconnectWorksheets();
}
}