Skip to content

Commit

Permalink
FEATURE: fgetcsv now returns false on end of file
Browse files Browse the repository at this point in the history
  • Loading branch information
Kharhamel committed Sep 8, 2022
1 parent be78f5b commit 3b5ca30
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 46 deletions.
46 changes: 0 additions & 46 deletions generated/filesystem.php
Expand Up @@ -226,52 +226,6 @@ function fflush($stream): void
}


/**
* Similar to fgets except that
* fgetcsv parses the line it reads for fields in
* CSV format and returns an array containing the fields
* read.
*
* @param resource $stream A valid file pointer to a file successfully opened by
* fopen, popen, or
* fsockopen.
* @param int $length Must be greater than the longest line (in characters) to be found in
* the CSV file (allowing for trailing line-end characters). Otherwise the
* line is split in chunks of length characters,
* unless the split would occur inside an enclosure.
*
* Omitting this parameter (or setting it to 0,
* or NULL in PHP 8.0.0 or later) the maximum line length is not limited,
* which is slightly slower.
* @param string $separator The optional separator parameter sets the field separator (one single-byte character only).
* @param string $enclosure The optional enclosure parameter sets the field enclosure character (one single-byte character only).
* @param string $escape The optional escape parameter sets the escape character (at most one single-byte character).
* An empty string ("") disables the proprietary escape mechanism.
* @return array|null Returns an indexed array containing the fields read on success.
* @throws FilesystemException
*
*/
function fgetcsv($stream, int $length = null, string $separator = ",", string $enclosure = "\"", string $escape = "\\"): ?array
{
error_clear_last();
if ($escape !== "\\") {
$safeResult = \fgetcsv($stream, $length, $separator, $enclosure, $escape);
} elseif ($enclosure !== "\"") {
$safeResult = \fgetcsv($stream, $length, $separator, $enclosure);
} elseif ($separator !== ",") {
$safeResult = \fgetcsv($stream, $length, $separator);
} elseif ($length !== null) {
$safeResult = \fgetcsv($stream, $length);
} else {
$safeResult = \fgetcsv($stream);
}
if ($safeResult === false) {
throw FilesystemException::createFromPhpError();
}
return $safeResult;
}


/**
* This function is similar to file, except that
* file_get_contents returns the file in a
Expand Down
1 change: 1 addition & 0 deletions generator/config/specialCasesFunctions.php
Expand Up @@ -15,4 +15,5 @@
'simplexml_import_dom',
'simplexml_load_file',
'simplexml_load_string',
'fgetcsv', // This function need to return false when iterating on a newline.
];
52 changes: 52 additions & 0 deletions generator/tests/SpecialCasesTest.php
Expand Up @@ -3,6 +3,7 @@
namespace Safe;

use PHPUnit\Framework\TestCase;
use Safe\Exceptions\FilesystemException;
use Safe\Exceptions\PcreException;

class SpecialCasesTest extends TestCase
Expand All @@ -18,4 +19,55 @@ public function testPregReplace()
$this->expectExceptionMessage('PREG_BAD_UTF8_ERROR: Invalid UTF8 character');
preg_replace("/([\s,]+)/u", "foo", "\xc3\x28");
}

public function testFgetcsvWithTrailingNewline()
{
require_once __DIR__.'/../../lib/special_cases.php';
require_once __DIR__.'/../../lib/Exceptions/SafeExceptionInterface.php';
require_once __DIR__.'/../../generated/Exceptions/FilesystemException.php';


if (($handle = \fopen(__DIR__."/csv/test.csv", "r")) === false) {
throw new \RuntimeException('Test file could not be opened.');
}

while (($data = fgetcsv($handle, 1000, ",")) !== false) {
$this->assertEquals(['test', 'test'], $data);
}
\fclose($handle);
}

public function testFgetcsvReturnFalseonEndOfFile()
{
require_once __DIR__.'/../../lib/special_cases.php';
require_once __DIR__.'/../../lib/Exceptions/SafeExceptionInterface.php';
require_once __DIR__.'/../../generated/Exceptions/FilesystemException.php';


if (($handle = \fopen(__DIR__."/csv/test2.csv", "r")) === false) {
throw new \RuntimeException('Test file could not be opened.');
}

while (($data = fgetcsv($handle, 1000, ",")) !== false) {
$this->assertEquals(['test', 'test'], $data);
}
$this->assertEquals(false, $data);
\fclose($handle);
}

/*public function testFgetcsvThrowsOnError()
{
require_once __DIR__.'/../../lib/special_cases.php';
require_once __DIR__.'/../../lib/Exceptions/SafeExceptionInterface.php';
require_once __DIR__.'/../../generated/Exceptions/FilesystemException.php';
if (($handle = \fopen(__DIR__."/csv/test3.csv", "r")) === false) {
throw new \RuntimeException('Test file could not be opened.');
}
$this->expectException(FilesystemException::class);
while (($data = fgetcsv($handle, 1000, ",")) !== false) {
echo var_export($data, true);
}
}*/
}
2 changes: 2 additions & 0 deletions generator/tests/csv/test.csv
@@ -0,0 +1,2 @@
test,test
test,test
2 changes: 2 additions & 0 deletions generator/tests/csv/test2.csv
@@ -0,0 +1,2 @@
test,test
test,test
35 changes: 35 additions & 0 deletions lib/special_cases.php
Expand Up @@ -405,3 +405,38 @@ function fputcsv($stream, array $fields, string $separator = ",", string $enclos
}
return $result;
}

/**
* Similar to fgets except that
* fgetcsv parses the line it reads for fields in
* CSV format and returns an array containing the fields
* read.
*
* @param resource $stream A valid file pointer to a file successfully opened by
* fopen, popen, or
* fsockopen.
* @param int<0, max>|null $length Must be greater than the longest line (in characters) to be found in
* the CSV file (allowing for trailing line-end characters). Otherwise the
* line is split in chunks of length characters,
* unless the split would occur inside an enclosure.
*
* Omitting this parameter (or setting it to 0,
* or NULL in PHP 8.0.0 or later) the maximum line length is not limited,
* which is slightly slower.
* @param string $separator The optional separator parameter sets the field separator (one single-byte character only).
* @param string $enclosure The optional enclosure parameter sets the field enclosure character (one single-byte character only).
* @param string $escape The optional escape parameter sets the escape character (at most one single-byte character).
* An empty string ("") disables the proprietary escape mechanism.
* @return mixed[]|false Returns an indexed array containing the fields read on success or false when there is no more lines.
* @throws FilesystemException
*
*/
function fgetcsv($stream, int $length = null, string $separator = ",", string $enclosure = "\"", string $escape = "\\"): array|false
{
error_clear_last();
$safeResult = \fgetcsv($stream, $length, $separator, $enclosure, $escape);
if ($safeResult === false && \feof($stream) === false) {
throw FilesystemException::createFromPhpError();
}
return $safeResult;
}

0 comments on commit 3b5ca30

Please sign in to comment.