Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
### Added

- Xlsx Reader Optionally Ignore Rows With No Cells. [Issue #3982](https://github.com/PHPOffice/PhpSpreadsheet/issues/3982) [PR #4035](https://github.com/PHPOffice/PhpSpreadsheet/pull/4035)
- Option for CSV output file to have varying numbers of columns for each row. [Issue #1415](https://github.com/PHPOffice/PhpSpreadsheet/issues/1415) [PR #4076](https://github.com/PHPOffice/PhpSpreadsheet/pull/4076)

### Changed

Expand All @@ -33,7 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org).
- Mpdf and Tcpdf Borders on Merged Cells. [Issue #3557](https://github.com/PHPOffice/PhpSpreadsheet/issues/3557) [PR #4047](https://github.com/PHPOffice/PhpSpreadsheet/pull/4047)
- Xls Conditional Format Improvements. [PR #4030](https://github.com/PHPOffice/PhpSpreadsheet/pull/4030) [PR #4033](https://github.com/PHPOffice/PhpSpreadsheet/pull/4033)
- Conditional Range Unions and Intersections [Issue #4039](https://github.com/PHPOffice/PhpSpreadsheet/issues/4039) [PR #4042](https://github.com/PHPOffice/PhpSpreadsheet/pull/4042)
- Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4049](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040)
- Csv Reader allow use of html mimetype. [Issue #4036](https://github.com/PHPOffice/PhpSpreadsheet/issues/4036) [PR #4040](https://github.com/PHPOffice/PhpSpreadsheet/pull/4040)
- Problem rendering line chart with missing plot label. [PR #4074](https://github.com/PHPOffice/PhpSpreadsheet/pull/4074)
- More RTL in Xlsx/Html Comments [Issue #4004](https://github.com/PHPOffice/PhpSpreadsheet/issues/4004) [PR #4065](https://github.com/PHPOffice/PhpSpreadsheet/pull/4065)
- Empty String in sharedStrings. [Issue #4063](https://github.com/PHPOffice/PhpSpreadsheet/issues/4063) [PR #4064](https://github.com/PHPOffice/PhpSpreadsheet/pull/4064)

Expand Down
12 changes: 12 additions & 0 deletions docs/topics/reading-and-writing-to-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -679,6 +679,18 @@ $writer->setOutputEncoding('SJIS-WIN');
$writer->save("05featuredemo.csv");
```

#### Writing CSV files with varying numbers of columns

A CSV file can have a different number of columns in each row. This
differs from the default behavior when saving as a .csv in Excel, but
can be enabled in PhpSpreadsheet by using the following code:

``` php
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Csv($spreadsheet);
$writer->setVariableColumns(true);
$writer->save("05featuredemo.csv");
```

#### Decimal and thousands separators

If the worksheet you are exporting contains numbers with decimal or
Expand Down
40 changes: 40 additions & 0 deletions src/PhpSpreadsheet/Writer/Csv.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheet\Writer;

use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use Stringable;

Expand Down Expand Up @@ -54,6 +55,13 @@ class Csv extends BaseWriter
*/
private string $outputEncoding = '';

/**
* Whether number of columns should be allowed to vary
* between rows, or use a fixed range based on the max
* column overall.
*/
private bool $variableColumns = false;

/**
* Create a new CSV.
*/
Expand Down Expand Up @@ -105,7 +113,17 @@ public function save($filename, int $flags = 0): void
$maxRow = $sheet->getHighestDataRow();

// Write rows to file
$row = 0;
foreach ($sheet->rangeToArrayYieldRows("A1:$maxCol$maxRow", '', $this->preCalculateFormulas) as $cellsArray) {
++$row;
if ($this->variableColumns) {
$column = $sheet->getHighestDataColumn($row);
if ($column === 'A' && !$sheet->cellExists("A$row")) {
$cellsArray = [];
} else {
array_splice($cellsArray, Coordinate::columnIndexFromString($column));
}
}
$this->writeLine($this->fileHandle, $cellsArray);
}

Expand Down Expand Up @@ -303,4 +321,26 @@ private function writeLine($fileHandle, array $values): void
}
fwrite($fileHandle, $line);
}

/**
* Get whether number of columns should be allowed to vary
* between rows, or use a fixed range based on the max
* column overall.
*/
public function getVariableColumns(): bool
{
return $this->variableColumns;
}

/**
* Set whether number of columns should be allowed to vary
* between rows, or use a fixed range based on the max
* column overall.
*/
public function setVariableColumns(bool $pValue): self
{
$this->variableColumns = $pValue;

return $this;
}
}
81 changes: 81 additions & 0 deletions tests/PhpSpreadsheetTests/Writer/Csv/VariableColumnsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Writer\Csv;

use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Csv;
use PHPUnit\Framework\TestCase;

class VariableColumnsTest extends TestCase
{
public function testVariableColumns(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray(
[
[1, 2, 3, 4],
[1, 2],
[1, 2, 3, 4, 5],
[],
[1],
[1, 2, 3],
]
);

$filename = File::temporaryFilename();
$writer = new Csv($spreadsheet);
$writer->setVariableColumns(true);
$writer->save($filename);

$contents = (string) file_get_contents($filename);
unlink($filename);
$spreadsheet->disconnectWorksheets();

$rows = explode(PHP_EOL, $contents);

self::assertSame('"1","2","3","4"', $rows[0]);
self::assertSame('"1","2"', $rows[1]);
self::assertSame('"1","2","3","4","5"', $rows[2]);
self::assertSame('', $rows[3]);
self::assertSame('"1"', $rows[4]);
self::assertSame('"1","2","3"', $rows[5]);
}

public function testFixedColumns(): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
$sheet->fromArray(
[
[1, 2, 3, 4],
[1, 2],
[1, 2, 3, 4, 5],
[],
[1],
[1, 2, 3],
]
);

$filename = File::temporaryFilename();
$writer = new Csv($spreadsheet);
self::assertFalse($writer->getVariableColumns());
$writer->save($filename);

$contents = (string) file_get_contents($filename);
unlink($filename);
$spreadsheet->disconnectWorksheets();

$rows = explode(PHP_EOL, $contents);

self::assertSame('"1","2","3","4",""', $rows[0]);
self::assertSame('"1","2","","",""', $rows[1]);
self::assertSame('"1","2","3","4","5"', $rows[2]);
self::assertSame('"","","","",""', $rows[3]);
self::assertSame('"1","","","",""', $rows[4]);
self::assertSame('"1","2","3","",""', $rows[5]);
}
}