Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fatal error with ltrim() when using the EmptyEscapeParser #358

Merged
merged 4 commits into from
Oct 9, 2019

Conversation

on2
Copy link
Contributor

@on2 on2 commented Oct 7, 2019

When calling $csv->setEscape(''); I've noticed that when the last line of my read-only CSV created from path was empty the following error would occur:

Fatal error: Uncaught TypeError: ltrim() expects parameter 1 to be string, boolean given in [...]vendor/league/csv/src/Polyfill/EmptyEscapeParser.php on line 156

I believe this to be down to differences between the SplFileObject and League\Csv\Stream implementations. For the empty line fgets() on a SplFileObject returns '' but using the procedural style or League\Csv\Stream, false is returned:

$path = sys_get_temp_dir().'/test.csv';
file_put_contents($path, '"1","2"' . "\n");

$fh = new SplFileObject($path, 'r');
$fh->fgets();
var_dump($fh->fgets()); // '' is returned

$fh = fopen($path, 'r');
fgets($fh);
var_dump(fgets($fh)); // false is returned

$fh = League\Csv\Stream::createFromPath($path);
$fh->fgets();
var_dump($fh->fgets()); // false is returned

unlink($path);

I'm not sure this pull request contains the best way to rectify the issue but tests are passing and if nothing else this can start a discussion.

@on2 on2 changed the title Added a test when using a reading only resource ('r') with the empty … Fatal error with ltrim() when using the EmptyEscapeParser Oct 8, 2019
@on2 on2 changed the title Fatal error with ltrim() when using the EmptyEscapeParser Fatal error with ltrim() when using the EmptyEscapeParser Oct 8, 2019
@on2 on2 marked this pull request as ready for review October 8, 2019 00:29
@@ -150,7 +150,9 @@ private static function filterDocument($document)
private static function extractRecord(): array
{
$record = [];
self::$line = self::$document->fgets();
if (false === (self::$line = self::$document->fgets())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would make the conditions more explicit ...

if (false === self::$line) {
    return [null];
}

and that should be enough no ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree but that seems to confuse PHPStan:

 ------ ------------------------------------------------------------------ 
  Line   src/Polyfill/EmptyEscapeParser.php                                
 ------ ------------------------------------------------------------------ 
  166    Strict comparison using !== between false and string will always  
         evaluate to true.                                                 
 ------ ------------------------------------------------------------------ 

See also: https://travis-ci.org/thephpleague/csv/jobs/594868542

It seems a bad idea to ignore the error completely but to ignore the error in just that file would require updating PHPStan which seems to open a can of worms (at least in my development environment).

This won't work with the current version of PHPStan. We need v0.11+:

	ignoreErrors:
		-
			message: '#Strict comparison using \!\=\= between false and string will always evaluate to true.#'
			path: src/Polyfill/EmptyEscapeParser.php

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can not update because of the PHP7.0+ requirement but I have no issue ignoring this PHPstan notice, this is kind of a trade off. The day we drop PHP7.2- we will be able to upgrade/improve the ignore message IMHO.

Copy link
Member

@nyamsprod nyamsprod Oct 8, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your patch is applied at the wrong lines 🤔 if you put the patch inside the while loop then PHPStan will not complained 👍 . You just need to patch the $buffer not so much the raw self::$line.

            $buffer = '';
            if (false !== self::$line) {
                $buffer = ltrim(self::$line, self::$trim_mask);
            }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's great - trade-off avoided!

@nyamsprod nyamsprod self-assigned this Oct 8, 2019
];

$path = sys_get_temp_dir().'/test.csv';
file_put_contents($path, $source);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to use php://temp or add a file to the data directory under so that tests are not dependent of the underlying OS

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I completely overlooked the data directory. I've rewritten the test to use the foo_readonly.csv file.

@nyamsprod
Copy link
Member

I'll merge your patch. I may update the code but as it is it's fine by me ... thanks. Hopefully I'll make a release for that later this week. And I won't forget to mention your name in the CHANGELOG 🎉

@nyamsprod nyamsprod merged commit 8effaf0 into thephpleague:master Oct 9, 2019
@nyamsprod
Copy link
Member

version 9.4.1 is released with your bugfix 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants