From 5adc524309c8d6cd5ab61c04d3e38f8d61a5dcf6 Mon Sep 17 00:00:00 2001 From: gomcodoctor Date: Thu, 17 Nov 2022 13:14:59 +0530 Subject: [PATCH 1/7] Update CsvReader.php customized reader file --- src/CsvReader.php | 121 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 11 deletions(-) diff --git a/src/CsvReader.php b/src/CsvReader.php index 7423c30..019d6c4 100644 --- a/src/CsvReader.php +++ b/src/CsvReader.php @@ -73,6 +73,13 @@ class CsvReader implements CountableReader, \SeekableIterator */ protected $duplicateHeadersFlag; + + protected $padValue = null; + + protected $truncateHeader = false; + + protected $removeEmptyField = false; + /** * @param \SplFileObject $file * @param string $delimiter @@ -81,6 +88,8 @@ class CsvReader implements CountableReader, \SeekableIterator */ public function __construct(\SplFileObject $file, $delimiter = ',', $enclosure = '"', $escape = '\\') { + ini_set('auto_detect_line_endings', true); + $this->file = $file; $this->file->setFlags( \SplFileObject::READ_CSV | @@ -99,8 +108,10 @@ public function __construct(\SplFileObject $file, $delimiter = ',', $enclosure = * Return the current row as an array * * If a header row has been set, an associative array will be returned + * + * @return array */ - public function current(): ?array + public function current() { // If the CSV has no column headers just return the line if (empty($this->columnHeaders)) { @@ -111,10 +122,15 @@ public function current(): ?array do { $line = $this->file->current(); + $columnHeaders = $this->columnHeaders; + // In non-strict mode pad/slice the line to match the column headers if (!$this->isStrict()) { if ($this->headersCount > count($line)) { - $line = array_pad($line, $this->headersCount, null); // Line too short + if($this->truncateHeader){ + $columnHeaders = array_slice($columnHeaders, 0, count($line)); + } + else $line = array_pad($line, $this->headersCount, $this->getPadValue()); // Line too short } else { $line = array_slice($line, 0, $this->headersCount); // Line too long } @@ -126,8 +142,12 @@ public function current(): ?array } // Count the number of elements in both: they must be equal. - if (count($this->columnHeaders) === count($line)) { - return array_combine(array_keys($this->columnHeaders), $line); + if (count($columnHeaders) === count($line)) { + $finalLine = array_combine(array_keys($columnHeaders), $line); + if($this->isRemoveEmptyField()){ + return array_filter($finalLine, function($value) { return $value !== ''; }); + } + else return $finalLine; } // They are not equal, so log the row as error and skip it. @@ -192,7 +212,7 @@ public function setHeaderRowNumber($rowNumber, $duplicates = null) * row. That way, when you iterate over the rows, that header row is * skipped. */ - public function rewind(): void + public function rewind() { $this->file->rewind(); if (null !== $this->headerRowNumber) { @@ -200,7 +220,10 @@ public function rewind(): void } } - public function count(): int + /** + * {@inheritdoc} + */ + public function count() { if (null === $this->count) { $position = $this->key(); @@ -213,22 +236,34 @@ public function count(): int return $this->count; } - public function next(): void + /** + * {@inheritdoc} + */ + public function next() { $this->file->next(); } - public function valid(): bool + /** + * {@inheritdoc} + */ + public function valid() { return $this->file->valid(); } - public function key(): int + /** + * {@inheritdoc} + */ + public function key() { return $this->file->key(); } - public function seek($pointer): void + /** + * {@inheritdoc} + */ + public function seek($pointer) { $this->file->seek($pointer); } @@ -306,13 +341,15 @@ protected function readHeaderRow($rowNumber) $this->file->seek($rowNumber); $headers = $this->file->current(); + /** BOM file encoding issue */ + $headers[0] = $this->prepareJSON($headers[0]); // Test for duplicate column headers $diff = array_diff_assoc($headers, array_unique($headers)); if (count($diff) > 0) { switch ($this->duplicateHeadersFlag) { case self::DUPLICATE_HEADERS_INCREMENT: $headers = $this->incrementHeaders($headers); - // Fall through + // Fall through case self::DUPLICATE_HEADERS_MERGE: break; default: @@ -385,4 +422,66 @@ protected function mergeDuplicates(array $line) return $values; } + + /** + * @return null + */ + public function getPadValue() + { + return $this->padValue; + } + + /** + * @param null $padValue + */ + public function setPadValue($padValue) + { + $this->padValue = $padValue; + } + + /** + * @return bool + */ + public function isTruncateHeader(): bool + { + return $this->truncateHeader; + } + + /** + * @param bool $truncateHeader + */ + public function setTruncateHeader(bool $truncateHeader) + { + $this->truncateHeader = $truncateHeader; + } + + /** + * @return bool + */ + public function isRemoveEmptyField(): bool + { + return $this->removeEmptyField; + } + + /** + * @param bool $removeEmptyField + */ + public function setRemoveEmptyField(bool $removeEmptyField) + { + $this->removeEmptyField = $removeEmptyField; + } + + function prepareJSON($input) { + //This will convert ASCII/ISO-8859-1 to UTF-8. + //Be careful with the third parameter (encoding detect list), because + //if set wrong, some input encodings will get garbled (including UTF-8!) + $imput = mb_convert_encoding($input, 'UTF-8', 'ASCII,UTF-8,ISO-8859-1'); + //Remove UTF-8 BOM if present, json_decode() does not like it. + if(substr($input, 0, 3) == pack("CCC", 0xEF, 0xBB, 0xBF)) $input = substr($input, 3); + return $input; + } + + + + } From 9e21aaed44aa172f4382130c1626f12ad09eeec7 Mon Sep 17 00:00:00 2001 From: gomcodoctor Date: Thu, 17 Nov 2022 13:26:42 +0530 Subject: [PATCH 2/7] Update CsvReader.php return type added --- src/CsvReader.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/CsvReader.php b/src/CsvReader.php index 019d6c4..2cfea88 100644 --- a/src/CsvReader.php +++ b/src/CsvReader.php @@ -111,7 +111,7 @@ public function __construct(\SplFileObject $file, $delimiter = ',', $enclosure = * * @return array */ - public function current() + public function current(): ?array { // If the CSV has no column headers just return the line if (empty($this->columnHeaders)) { @@ -212,7 +212,7 @@ public function setHeaderRowNumber($rowNumber, $duplicates = null) * row. That way, when you iterate over the rows, that header row is * skipped. */ - public function rewind() + public function rewind(): void { $this->file->rewind(); if (null !== $this->headerRowNumber) { @@ -223,7 +223,7 @@ public function rewind() /** * {@inheritdoc} */ - public function count() + public function count(): int { if (null === $this->count) { $position = $this->key(); @@ -239,7 +239,7 @@ public function count() /** * {@inheritdoc} */ - public function next() + public function next(): void { $this->file->next(); } @@ -247,7 +247,7 @@ public function next() /** * {@inheritdoc} */ - public function valid() + public function valid(): bool { return $this->file->valid(); } @@ -255,7 +255,7 @@ public function valid() /** * {@inheritdoc} */ - public function key() + public function key(): int { return $this->file->key(); } @@ -263,7 +263,7 @@ public function key() /** * {@inheritdoc} */ - public function seek($pointer) + public function seek($pointer): void { $this->file->seek($pointer); } From 671ab7ec9cb8c6a8612e5e8e0c92d776980d8675 Mon Sep 17 00:00:00 2001 From: gomcodoctor Date: Wed, 23 Nov 2022 20:54:11 +0530 Subject: [PATCH 3/7] Update CsvReader.php auto_detect_line_endings removed --- src/CsvReader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CsvReader.php b/src/CsvReader.php index 2cfea88..e727ed7 100644 --- a/src/CsvReader.php +++ b/src/CsvReader.php @@ -88,7 +88,7 @@ class CsvReader implements CountableReader, \SeekableIterator */ public function __construct(\SplFileObject $file, $delimiter = ',', $enclosure = '"', $escape = '\\') { - ini_set('auto_detect_line_endings', true); + // ini_set('auto_detect_line_endings', true); $this->file = $file; $this->file->setFlags( From 5beaf0cfa8e36c76cb83d0a63befd660c0b4caea Mon Sep 17 00:00:00 2001 From: gomcodoctor Date: Wed, 12 Apr 2023 14:59:23 +0530 Subject: [PATCH 4/7] Update composer.json "portphp/portphp": "dev-master" --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0b7f00f..a98b0f6 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "docs": "https://portphp.readthedocs.org" }, "require": { - "portphp/portphp": "^1.6.0" + "portphp/portphp": "dev-master" }, "autoload": { "psr-4": { From 9c24257e7bd6ef091c2e353498c6097cd744ec9e Mon Sep 17 00:00:00 2001 From: gomcodoctor Date: Tue, 2 Sep 2025 09:54:30 +0530 Subject: [PATCH 5/7] Update CsvReader.php trim white space in header added --- src/CsvReader.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CsvReader.php b/src/CsvReader.php index e727ed7..df86afb 100644 --- a/src/CsvReader.php +++ b/src/CsvReader.php @@ -177,6 +177,7 @@ public function getColumnHeaders() */ public function setColumnHeaders(array $columnHeaders) { + $columnHeaders = array_map('trim', $columnHeaders); $this->columnHeaders = array_count_values($columnHeaders); $this->headersCount = count($columnHeaders); } From ef237e434b58cf0675ea56d5bcdcebed3b63ed8d Mon Sep 17 00:00:00 2001 From: gomcodoctor Date: Sun, 12 Oct 2025 14:11:21 +0530 Subject: [PATCH 6/7] Update CsvWriter.php --- src/CsvWriter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CsvWriter.php b/src/CsvWriter.php index 2766b69..31fa52d 100644 --- a/src/CsvWriter.php +++ b/src/CsvWriter.php @@ -63,7 +63,7 @@ public function prepare() /** * {@inheritdoc} */ - public function writeItem(array $item) + public function writeItem(mixed $item) { if ($this->prependHeaderRow && 1 == $this->row++) { $headers = array_keys($item); From f3b6ac42f1617f247d92a9ba71fee0235fa8460a Mon Sep 17 00:00:00 2001 From: Gomcodoctor Date: Fri, 18 Sep 2026 13:38:50 +0530 Subject: [PATCH 7/7] Fix incrementHeaders() to preserve column position for duplicate headers array_count_values() groups every occurrence of a repeated header together in the output, based on that header's first-appearance order in the input - not the position it actually appeared at. For headers like ['a', 'b', 'a', 'c'] this produces ['a', 'a1', 'b', 'c'], which no longer lines up column-for-column with the CSV row's actual values at those same indexes. Walk $headers in original order instead, incrementing a per-header counter as each duplicate is encountered, so the incremented headers array stays positionally aligned with the row data: ['a', 'b', 'a1', 'c']. --- src/CsvReader.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/CsvReader.php b/src/CsvReader.php index df86afb..4839e94 100644 --- a/src/CsvReader.php +++ b/src/CsvReader.php @@ -377,15 +377,17 @@ protected function readHeaderRow($rowNumber) */ protected function incrementHeaders(array $headers) { + $counts = []; $incrementedHeaders = []; - foreach (array_count_values($headers) as $header => $count) { - if ($count > 1) { + + foreach ($headers as $header) { + + if (!isset($counts[$header])) { + $counts[$header] = 0; $incrementedHeaders[] = $header; - for ($i = 1; $i < $count; $i++) { - $incrementedHeaders[] = $header . $i; - } } else { - $incrementedHeaders[] = $header; + $counts[$header]++; + $incrementedHeaders[] = $header . $counts[$header]; } }