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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a

### Added

- Make Reader/Csv Extendable, add new preferred Reader/CsvNoEscape class. [Issue #4836](https://github.com/PHPOffice/PhpSpreadsheet/issues/4836) [PR #4837](https://github.com/PHPOffice/PhpSpreadsheet/pull/4837) [PR #4845](https://github.com/PHPOffice/PhpSpreadsheet/pull/4845)
- XLOOKUP function. [Issue #1453](https://github.com/PHPOffice/PhpSpreadsheet/issues/1453) [PR #4844](https://github.com/PHPOffice/PhpSpreadsheet/pull/4844)
- Introduction of a benchmark test suite, independent of the default unit test suite. Users can use it as a template for experimenting and making decisions concerning performance. [PR #4824](https://github.com/PHPOffice/PhpSpreadsheet/pull/4824)
- Make Reader/Csv Extendable, add new preferred Reader/CsvNoEscape class. [Issue #4836](https://github.com/PHPOffice/PhpSpreadsheet/issues/4836) [PR #4837](https://github.com/PHPOffice/PhpSpreadsheet/pull/4837)

### Removed

Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Cell/Cell.php
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ public function getCalculatedValueString(): string
$value = array_shift($value);
}

return StringHelper::convertToString($value, false);
return StringHelper::convertToString($value, false, convertBool: true);
}

/**
Expand Down
24 changes: 12 additions & 12 deletions src/PhpSpreadsheet/Reader/BaseReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function getReadDataOnly(): bool
return $this->readDataOnly;
}

public function setReadDataOnly(bool $readCellValuesOnly): self
public function setReadDataOnly(bool $readCellValuesOnly): static
{
$this->readDataOnly = $readCellValuesOnly;

Expand All @@ -106,7 +106,7 @@ public function getReadEmptyCells(): bool
return $this->readEmptyCells;
}

public function setReadEmptyCells(bool $readEmptyCells): self
public function setReadEmptyCells(bool $readEmptyCells): static
{
$this->readEmptyCells = $readEmptyCells;

Expand All @@ -118,7 +118,7 @@ public function getIgnoreRowsWithNoCells(): bool
return $this->ignoreRowsWithNoCells;
}

public function setIgnoreRowsWithNoCells(bool $ignoreRowsWithNoCells): self
public function setIgnoreRowsWithNoCells(bool $ignoreRowsWithNoCells): static
{
$this->ignoreRowsWithNoCells = $ignoreRowsWithNoCells;

Expand All @@ -130,7 +130,7 @@ public function getIncludeCharts(): bool
return $this->includeCharts;
}

public function setIncludeCharts(bool $includeCharts): self
public function setIncludeCharts(bool $includeCharts): static
{
$this->includeCharts = $includeCharts;

Expand All @@ -142,7 +142,7 @@ public function getEnableDrawingPassThrough(): bool
return $this->enableDrawingPassThrough;
}

public function setEnableDrawingPassThrough(bool $enableDrawingPassThrough): self
public function setEnableDrawingPassThrough(bool $enableDrawingPassThrough): static
{
$this->enableDrawingPassThrough = $enableDrawingPassThrough;

Expand All @@ -156,7 +156,7 @@ public function getLoadSheetsOnly(): ?array
}

/** @param null|string|string[] $sheetList */
public function setLoadSheetsOnly(string|array|null $sheetList): self
public function setLoadSheetsOnly(string|array|null $sheetList): static
{
if ($sheetList === null) {
return $this->setLoadAllSheets();
Expand All @@ -167,7 +167,7 @@ public function setLoadSheetsOnly(string|array|null $sheetList): self
return $this;
}

public function setLoadAllSheets(): self
public function setLoadAllSheets(): static
{
$this->loadSheetsOnly = null;

Expand All @@ -179,7 +179,7 @@ public function getReadFilter(): IReadFilter
return $this->readFilter;
}

public function setReadFilter(IReadFilter $readFilter): self
public function setReadFilter(IReadFilter $readFilter): static
{
$this->readFilter = $readFilter;

Expand All @@ -192,7 +192,7 @@ public function setReadFilter(IReadFilter $readFilter): self
* these can be specified within a spreadsheet
* in a way that can subject the caller to security exploits.
*/
public function setAllowExternalImages(bool $allowExternalImages): self
public function setAllowExternalImages(bool $allowExternalImages): static
{
$this->allowExternalImages = $allowExternalImages;

Expand All @@ -213,7 +213,7 @@ public function getAllowExternalImages(): bool
*
* @param Closure(string):bool $isWhitelisted
*/
public function setIsWhitelisted(Closure $isWhitelisted): self
public function setIsWhitelisted(Closure $isWhitelisted): static
{
$this->isWhitelisted = $isWhitelisted;

Expand All @@ -224,7 +224,7 @@ public function setIsWhitelisted(Closure $isWhitelisted): self
* Create a blank sheet if none are read,
* possibly due to a typo when using LoadSheetsOnly.
*/
public function setCreateBlankSheetIfNoneRead(bool $createBlankSheetIfNoneRead): self
public function setCreateBlankSheetIfNoneRead(bool $createBlankSheetIfNoneRead): static
{
$this->createBlankSheetIfNoneRead = $createBlankSheetIfNoneRead;

Expand Down Expand Up @@ -346,7 +346,7 @@ public function getValueBinder(): ?IValueBinder
return $this->valueBinder;
}

public function setValueBinder(?IValueBinder $valueBinder): self
public function setValueBinder(?IValueBinder $valueBinder): static
{
$this->valueBinder = $valueBinder;

Expand Down
85 changes: 61 additions & 24 deletions src/PhpSpreadsheet/Reader/Csv.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,17 +89,17 @@ class Csv extends BaseReader

protected bool $preserveNumericFormatting = false;

private bool $preserveNullString = false;
protected bool $preserveNullString = false;

private bool $sheetNameIsFileName = false;
protected bool $sheetNameIsFileName = false;

private string $getTrue = 'true';
protected ?string $getTrue = null;

private string $getFalse = 'false';
protected ?string $getFalse = null;

private string $thousandsSeparator = ',';
protected ?string $thousandsSeparator = null;

private string $decimalSeparator = '.';
protected ?string $decimalSeparator = null;

/**
* Create a new CSV Reader instance.
Expand Down Expand Up @@ -301,18 +301,27 @@ private function openFileOrMemory(string $filename): void
}
$this->openFile($filename);
if ($this->inputEncoding !== 'UTF-8') {
fclose($this->fileHandle);
$entireFile = file_get_contents($filename);
$fileHandle = fopen('php://memory', 'r+b');
if ($fileHandle !== false && $entireFile !== false) {
$this->fileHandle = $fileHandle;
$data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
fwrite($this->fileHandle, $data);
$this->skipBOM();
}
$this->convertNonUtf8($filename);
}
}

protected function convertNonUtf8(string $filename): void
{
fclose($this->fileHandle);
$entireFile = file_get_contents($filename);
if ($entireFile === false) {
throw new ReaderException("Unable to get contents of $filename"); // @codeCoverageIgnore
}
$fileHandle = fopen('php://memory', 'r+b');
if ($fileHandle === false) {
throw new ReaderException('Unable to open php://memory'); // @codeCoverageIgnore
}
$this->fileHandle = $fileHandle;
$data = StringHelper::convertEncoding($entireFile, 'UTF-8', $this->inputEncoding);
fwrite($this->fileHandle, $data);
$this->skipBOM();
}

public function setTestAutoDetect(bool $value): self
{
$this->testAutodetect = $value;
Expand Down Expand Up @@ -418,10 +427,10 @@ private function loadStringOrFile2(string $filename, Spreadsheet $spreadsheet, b
$rowData = self::getCsv($fileHandle, 0, $delimiter, $this->enclosure, $this->escapeCharacter);
$valueBinder = $this->valueBinder ?? Cell::getValueBinder();
$preserveBooleanString = method_exists($valueBinder, 'getBooleanConversion') && $valueBinder->getBooleanConversion();
$this->getTrue = Calculation::getTRUE();
$this->getFalse = Calculation::getFALSE();
$this->thousandsSeparator = StringHelper::getThousandsSeparator();
$this->decimalSeparator = StringHelper::getDecimalSeparator();
$this->getTrue ??= Calculation::getTRUE();
$this->getFalse ??= Calculation::getFALSE();
$this->thousandsSeparator ??= StringHelper::getThousandsSeparator();
$this->decimalSeparator ??= StringHelper::getDecimalSeparator();
while (is_array($rowData)) {
$noOutputYet = true;
$columnLetter = 'A';
Expand Down Expand Up @@ -466,9 +475,9 @@ private function loadStringOrFile2(string $filename, Spreadsheet $spreadsheet, b
private function convertBoolean(mixed &$rowDatum): void
{
if (is_string($rowDatum)) {
if (strcasecmp($this->getTrue, $rowDatum) === 0 || strcasecmp('true', $rowDatum) === 0) {
if (strcasecmp((string) $this->getTrue, $rowDatum) === 0 || strcasecmp('true', $rowDatum) === 0) {
$rowDatum = true;
} elseif (strcasecmp($this->getFalse, $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) {
} elseif (strcasecmp((string) $this->getFalse, $rowDatum) === 0 || strcasecmp('false', $rowDatum) === 0) {
$rowDatum = false;
}
} else {
Expand All @@ -484,15 +493,15 @@ private function convertFormattedNumber(mixed &$rowDatum): string
$numberFormatMask = '';
if ($this->castFormattedNumberToNumeric === true && is_string($rowDatum)) {
$numeric = str_replace(
[$this->thousandsSeparator, $this->decimalSeparator],
[(string) $this->thousandsSeparator, (string) $this->decimalSeparator],
['', '.'],
$rowDatum
);

if (is_numeric($numeric)) {
$decimalPos = strpos($rowDatum, $this->decimalSeparator);
$decimalPos = strpos($rowDatum, (string) $this->decimalSeparator);
if ($this->preserveNumericFormatting === true) {
$numberFormatMask = (str_contains($rowDatum, $this->thousandsSeparator))
$numberFormatMask = (str_contains($rowDatum, (string) $this->thousandsSeparator))
? '#,##0' : '0';
if ($decimalPos !== false) {
$decimals = strlen($rowDatum) - $decimalPos - 1;
Expand Down Expand Up @@ -760,4 +769,32 @@ private static function getDefaultEscapeCharacter(int $version = PHP_VERSION_ID)
{
return $version < 90000 ? '\\' : '';
}

public function setGetTrue(?string $getTrue): self
{
$this->getTrue = $getTrue;

return $this;
}

public function setGetFalse(?string $getFalse): self
{
$this->getFalse = $getFalse;

return $this;
}

public function setDecimalSeparator(?string $decimalSeparator): self
{
$this->decimalSeparator = $decimalSeparator;

return $this;
}

public function setThousandsSeparator(?string $thousandsSeparator): self
{
$this->thousandsSeparator = $thousandsSeparator;

return $this;
}
}
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Writer/Xls/Worksheet.php
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ public function close(): void
match ($calctype) {
'integer', 'double' => $this->writeNumber($row, $column, is_numeric($calculatedValue) ? ((float) $calculatedValue) : 0.0, $xfIndex),
'string' => $this->writeString($row, $column, $calculatedValueString, $xfIndex),
'boolean' => $this->writeBoolErr($row, $column, (int) $calculatedValueString, 0, $xfIndex),
'boolean' => $this->writeBoolErr($row, $column, (int) $calculatedValue, 0, $xfIndex), // @phpstan-ignore-line
default => $this->writeString($row, $column, $cell->getValueString(), $xfIndex),
};
}
Expand Down
32 changes: 32 additions & 0 deletions tests/PhpSpreadsheetTests/Reader/Csv/CsvIconv2.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace PhpOffice\PhpSpreadsheetTests\Reader\Csv;

use PhpOffice\PhpSpreadsheet\Reader\Csv;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;

class CsvIconv2 extends Csv
{
protected function convertNonUtf8(string $filename): void
{
fclose($this->fileHandle);
$filtered = "php://filter/convert.iconv.{$this->inputEncoding}.utf-8/resource=$filename";
$sourceStream = fopen($filtered, 'rb');
if ($sourceStream === false) {
throw new ReaderException("Unable to get contents of $filename"); // @codeCoverageIgnore
}
$fileHandle = fopen('php://memory', 'r+b');
if ($fileHandle === false) {
throw new ReaderException('Unable to open php://memory'); // @codeCoverageIgnore
}
$bytes = stream_copy_to_stream($sourceStream, $fileHandle);
fclose($sourceStream);
if ($bytes === false) {
throw new ReaderException('Unable to copy stream'); // @codeCoverageIgnore
}
$this->fileHandle = $fileHandle;
$this->skipBOM();
}
}
Loading
Loading