mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-31 12:40:00 +00:00
TextGrid Improvements
Helper/TextGrid was intended to assist with some samples. However, it has emerged recently on two tickets. PR #4342 intended to introduce functionality very similar to TextGrid, and was closed for that reason. Issue #1640 was closed as stale over 4 years ago, despite the fact that TextGrid seems an adequate resolution for it. Since it seems that there is a use for this function beyond its original intended usage, I added a few parameters to give it some flexibility - the ability to omit row and/or column headers, and the ability to add a divider line between rows. A description of this function is added to the formal documentation.
This commit is contained in:
@@ -1169,6 +1169,38 @@ One benefit of flags is that you can pass several flags in a single method call.
|
||||
Two or more flags can be passed together using PHP's `|` operator.
|
||||
|
||||
```php
|
||||
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile("myExampleFile.xlsx");
|
||||
$reader->load("spreadsheetWithCharts.xlsx", $reader::READ_DATA_ONLY | $reader::IGNORE_EMPTY_CELLS);
|
||||
$reader = \PhpOffice\PhpSpreadsheet\IOFactory::createReaderForFile('myExampleFile.xlsx');
|
||||
$reader->load(
|
||||
'spreadsheetWithCharts.xlsx',
|
||||
$reader::READ_DATA_ONLY | $reader::IGNORE_EMPTY_CELLS
|
||||
);
|
||||
```
|
||||
|
||||
## Writing Data as a Plaintext Grid
|
||||
|
||||
Although not really a spreadsheet format, it can be useful to write data in grid format to a plaintext file.
|
||||
Code like the following can be used:
|
||||
```php
|
||||
$array = $sheet->toArray(null, true, true, true);
|
||||
$textGrid = new \PhpOffice\PhpSpreadsheet\Shared\TextGrid(
|
||||
$array,
|
||||
true, // true for cli, false for html
|
||||
// Starting with release 4.2,
|
||||
// the output format can be tweaked by uncommenting
|
||||
// any of the following 3 optional parameters.
|
||||
// rowDividers: true,
|
||||
// rowHeaders: false,
|
||||
// columnHeaders: false,
|
||||
);
|
||||
$result = $textGrid->render();
|
||||
```
|
||||
You can then echo `$result` to a terminal, or write it to a file with `file_put_contents`. The result will resemble:
|
||||
```
|
||||
+-----+------------------+---+----------+
|
||||
| A | B | C | D |
|
||||
+---+-----+------------------+---+----------+
|
||||
| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |
|
||||
| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |
|
||||
| 3 | xyz | xyz | | |
|
||||
+---+-----+------------------+---+----------+
|
||||
```
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Helper;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
|
||||
|
||||
class TextGrid
|
||||
{
|
||||
private bool $isCli;
|
||||
@@ -14,7 +16,13 @@ class TextGrid
|
||||
|
||||
private string $gridDisplay;
|
||||
|
||||
public function __construct(array $matrix, bool $isCli = true)
|
||||
private bool $rowDividers = false;
|
||||
|
||||
private bool $rowHeaders = true;
|
||||
|
||||
private bool $columnHeaders = true;
|
||||
|
||||
public function __construct(array $matrix, bool $isCli = true, bool $rowDividers = false, bool $rowHeaders = true, bool $columnHeaders = true)
|
||||
{
|
||||
$this->rows = array_keys($matrix);
|
||||
$this->columns = array_keys($matrix[$this->rows[0]]);
|
||||
@@ -29,11 +37,14 @@ class TextGrid
|
||||
|
||||
$this->matrix = $matrix;
|
||||
$this->isCli = $isCli;
|
||||
$this->rowDividers = $rowDividers;
|
||||
$this->rowHeaders = $rowHeaders;
|
||||
$this->columnHeaders = $columnHeaders;
|
||||
}
|
||||
|
||||
public function render(): string
|
||||
{
|
||||
$this->gridDisplay = $this->isCli ? '' : '<pre>';
|
||||
$this->gridDisplay = $this->isCli ? '' : ('<pre>' . PHP_EOL);
|
||||
|
||||
if (!empty($this->rows)) {
|
||||
$maxRow = max($this->rows);
|
||||
@@ -42,7 +53,9 @@ class TextGrid
|
||||
|
||||
$this->renderColumnHeader($maxRowLength, $columnWidths);
|
||||
$this->renderRows($maxRowLength, $columnWidths);
|
||||
$this->renderFooter($maxRowLength, $columnWidths);
|
||||
if (!$this->rowDividers) {
|
||||
$this->renderFooter($maxRowLength, $columnWidths);
|
||||
}
|
||||
}
|
||||
|
||||
$this->gridDisplay .= $this->isCli ? '' : '</pre>';
|
||||
@@ -53,30 +66,48 @@ class TextGrid
|
||||
private function renderRows(int $maxRowLength, array $columnWidths): void
|
||||
{
|
||||
foreach ($this->matrix as $row => $rowData) {
|
||||
$this->gridDisplay .= '|' . str_pad((string) $this->rows[$row], $maxRowLength, ' ', STR_PAD_LEFT) . ' ';
|
||||
if ($this->rowHeaders) {
|
||||
$this->gridDisplay .= '|' . str_pad((string) $this->rows[$row], $maxRowLength, ' ', STR_PAD_LEFT) . ' ';
|
||||
}
|
||||
$this->renderCells($rowData, $columnWidths);
|
||||
$this->gridDisplay .= '|' . PHP_EOL;
|
||||
if ($this->rowDividers) {
|
||||
$this->renderFooter($maxRowLength, $columnWidths);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function renderCells(array $rowData, array $columnWidths): void
|
||||
{
|
||||
foreach ($rowData as $column => $cell) {
|
||||
$displayCell = ($this->isCli) ? (string) $cell : htmlentities((string) $cell);
|
||||
$valueForLength = StringHelper::convertToString($cell, convertBool: true);
|
||||
$displayCell = $this->isCli ? $valueForLength : htmlentities($valueForLength);
|
||||
$this->gridDisplay .= '| ';
|
||||
$this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($cell ?? '') + 1);
|
||||
$this->gridDisplay .= $displayCell . str_repeat(' ', $columnWidths[$column] - mb_strlen($valueForLength) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private function renderColumnHeader(int $maxRowLength, array $columnWidths): void
|
||||
private function renderColumnHeader(int $maxRowLength, array &$columnWidths): void
|
||||
{
|
||||
$this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
|
||||
if (!$this->columnHeaders) {
|
||||
$this->renderFooter($maxRowLength, $columnWidths);
|
||||
|
||||
return;
|
||||
}
|
||||
foreach ($this->columns as $column => $reference) {
|
||||
$columnWidths[$column] = max($columnWidths[$column], mb_strlen($reference));
|
||||
}
|
||||
if ($this->rowHeaders) {
|
||||
$this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
|
||||
}
|
||||
foreach ($this->columns as $column => $reference) {
|
||||
$this->gridDisplay .= '+-' . str_repeat('-', $columnWidths[$column] + 1);
|
||||
}
|
||||
$this->gridDisplay .= '+' . PHP_EOL;
|
||||
|
||||
$this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
|
||||
if ($this->rowHeaders) {
|
||||
$this->gridDisplay .= str_repeat(' ', $maxRowLength + 2);
|
||||
}
|
||||
foreach ($this->columns as $column => $reference) {
|
||||
$this->gridDisplay .= '| ' . str_pad((string) $reference, $columnWidths[$column] + 1, ' ');
|
||||
}
|
||||
@@ -87,7 +118,9 @@ class TextGrid
|
||||
|
||||
private function renderFooter(int $maxRowLength, array $columnWidths): void
|
||||
{
|
||||
$this->gridDisplay .= '+' . str_repeat('-', $maxRowLength + 1);
|
||||
if ($this->rowHeaders) {
|
||||
$this->gridDisplay .= '+' . str_repeat('-', $maxRowLength + 1);
|
||||
}
|
||||
foreach ($this->columns as $column => $reference) {
|
||||
$this->gridDisplay .= '+-';
|
||||
$this->gridDisplay .= str_pad((string) '', $columnWidths[$column] + 1, '-');
|
||||
@@ -112,13 +145,7 @@ class TextGrid
|
||||
$columnData = array_values($columnData);
|
||||
|
||||
foreach ($columnData as $columnValue) {
|
||||
if (is_string($columnValue)) {
|
||||
$columnWidth = max($columnWidth, mb_strlen($columnValue));
|
||||
} elseif (is_bool($columnValue)) {
|
||||
$columnWidth = max($columnWidth, mb_strlen($columnValue ? 'TRUE' : 'FALSE'));
|
||||
}
|
||||
|
||||
$columnWidth = max($columnWidth, mb_strlen((string) $columnWidth));
|
||||
$columnWidth = max($columnWidth, mb_strlen(StringHelper::convertToString($columnValue, convertBool: true)));
|
||||
}
|
||||
|
||||
return $columnWidth;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Shared;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use Stringable;
|
||||
|
||||
@@ -647,8 +648,12 @@ class StringHelper
|
||||
return strlen("$string");
|
||||
}
|
||||
|
||||
public static function convertToString(mixed $value, bool $throw = true, string $default = ''): string
|
||||
/** @param bool $convertBool If true, convert bool to locale-aware TRUE/FALSE rather than 1/null-string */
|
||||
public static function convertToString(mixed $value, bool $throw = true, string $default = '', bool $convertBool = false): string
|
||||
{
|
||||
if ($convertBool && is_bool($value)) {
|
||||
return $value ? Calculation::getTRUE() : Calculation::getFALSE();
|
||||
}
|
||||
if ($value === null || is_scalar($value) || $value instanceof Stringable) {
|
||||
return (string) $value;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Helper;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Helper\TextGrid;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TextGridTest extends TestCase
|
||||
{
|
||||
#[DataProvider('providerTextGrid')]
|
||||
public function testTextGrid(
|
||||
bool $cli,
|
||||
bool $rowDividers,
|
||||
bool $rowHeaders,
|
||||
bool $columnHeaders,
|
||||
array $expected
|
||||
): void {
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->fromArray([
|
||||
[6, '=TEXT(A1,"yyyy-mm-dd hh:mm")', null, 0.572917],
|
||||
['="6"', '=TEXT(A2,"yyyy-mm-dd hh:mm")', null, '1<>2'],
|
||||
['xyz', '=TEXT(A3,"yyyy-mm-dd hh:mm")'],
|
||||
], strictNullComparison: true);
|
||||
$textGrid = new TextGrid(
|
||||
$sheet->toArray(null, true, true, true),
|
||||
$cli,
|
||||
rowDividers: $rowDividers,
|
||||
rowHeaders: $rowHeaders,
|
||||
columnHeaders: $columnHeaders
|
||||
);
|
||||
$result = $textGrid->render();
|
||||
// Note that, for cli, string will end with PHP_EOL,
|
||||
// so explode will add an extra null-string element
|
||||
// to its array output.
|
||||
$lines = explode(PHP_EOL, $result);
|
||||
self::assertSame($expected, $lines);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
|
||||
public static function providerTextGrid(): array
|
||||
{
|
||||
return [
|
||||
'cli default values' => [
|
||||
true, false, true, true,
|
||||
[
|
||||
' +-----+------------------+---+----------+',
|
||||
' | A | B | C | D |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |',
|
||||
'| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |',
|
||||
'| 3 | xyz | xyz | | |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'',
|
||||
],
|
||||
],
|
||||
'html default values' => [
|
||||
false, false, true, true,
|
||||
[
|
||||
'<pre>',
|
||||
' +-----+------------------+---+----------+',
|
||||
' | A | B | C | D |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |',
|
||||
'| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |',
|
||||
'| 3 | xyz | xyz | | |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'</pre>',
|
||||
],
|
||||
],
|
||||
'cli rowDividers' => [
|
||||
true, true, true, true,
|
||||
[
|
||||
' +-----+------------------+---+----------+',
|
||||
' | A | B | C | D |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'| 3 | xyz | xyz | | |',
|
||||
'+---+-----+------------------+---+----------+',
|
||||
'',
|
||||
],
|
||||
],
|
||||
'cli no columnHeaders' => [
|
||||
true, false, true, false,
|
||||
[
|
||||
'+---+-----+------------------+--+----------+',
|
||||
'| 1 | 6 | 1900-01-06 00:00 | | 0.572917 |',
|
||||
'| 2 | 6 | 1900-01-06 00:00 | | 1<>2 |',
|
||||
'| 3 | xyz | xyz | | |',
|
||||
'+---+-----+------------------+--+----------+',
|
||||
'',
|
||||
],
|
||||
],
|
||||
'cli no row headers' => [
|
||||
true, false, false, true,
|
||||
[
|
||||
'+-----+------------------+---+----------+',
|
||||
'| A | B | C | D |',
|
||||
'+-----+------------------+---+----------+',
|
||||
'| 6 | 1900-01-06 00:00 | | 0.572917 |',
|
||||
'| 6 | 1900-01-06 00:00 | | 1<>2 |',
|
||||
'| xyz | xyz | | |',
|
||||
'+-----+------------------+---+----------+',
|
||||
'',
|
||||
],
|
||||
],
|
||||
'cli row dividers, no row nor column headers' => [
|
||||
true, true, false, false,
|
||||
[
|
||||
'+-----+------------------+--+----------+',
|
||||
'| 6 | 1900-01-06 00:00 | | 0.572917 |',
|
||||
'+-----+------------------+--+----------+',
|
||||
'| 6 | 1900-01-06 00:00 | | 1<>2 |',
|
||||
'+-----+------------------+--+----------+',
|
||||
'| xyz | xyz | | |',
|
||||
'+-----+------------------+--+----------+',
|
||||
'',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function testBool(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->fromArray([
|
||||
[0, 1],
|
||||
[true, false],
|
||||
[true, true],
|
||||
], strictNullComparison: true);
|
||||
$textGrid = new TextGrid(
|
||||
$sheet->toArray(null, true, false, true),
|
||||
true,
|
||||
rowDividers: false,
|
||||
rowHeaders: false,
|
||||
columnHeaders: false,
|
||||
);
|
||||
$expected = [
|
||||
'+------+-------+',
|
||||
'| 0 | 1 |',
|
||||
'| TRUE | FALSE |',
|
||||
'| TRUE | TRUE |',
|
||||
'+------+-------+',
|
||||
'',
|
||||
];
|
||||
$result = $textGrid->render();
|
||||
$lines = explode(PHP_EOL, $result);
|
||||
self::assertSame($expected, $lines);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user