Skip to content

fix: don't silently drop an unterminated multiline value - #616

Closed
raphyabak wants to merge 1 commit into
vlucas:masterfrom
raphyabak:fix/unterminated-multiline-value
Closed

fix: don't silently drop an unterminated multiline value#616
raphyabak wants to merge 1 commit into
vlucas:masterfrom
raphyabak:fix/unterminated-multiline-value

Conversation

@raphyabak

Copy link
Copy Markdown

Summary

Fixes #610

When a double-quoted value was left unterminated at the end of the input, Lines::process() discarded the buffered content instead of emitting it, so the whole entry — and every line after it, since they were still being buffered as part of the same unclosed multiline value — vanished with no error:

Dotenv::parse('FOO="bar');            // returns [] — no exception
Dotenv::parse("A=\"oops\nB=keep");    // returns [] — B is silently dropped too

This was inconsistent with the single-quoted case, which already reports a missing closing quote — single-quoted values aren't treated as multiline at all, so an unterminated one reaches EntryParser directly and fails loudly:

Dotenv::parse("FOO='bar");            // throws "a missing closing quote"

Fix

Emit the still-open buffer as a final output entry instead of discarding it when the input ends mid-multiline-value. It then reaches EntryParser exactly like a normal entry, and the lexer ends in one of the REJECT_STATES at EOF, so it raises the same "a missing closing quote" error the single-quoted path already produces. A properly closed multiline value is unaffected, since the buffer is already flushed to $output by the time the loop ends.

As noted in the issue, this is a behaviour change — input that previously parsed to an empty or truncated array now throws — so it targets master rather than a 4.x patch release. The prior behaviour was silent data loss, which is the stronger correctness concern.

Test plan

  • Added two cases to tests/Dotenv/Parser/LinesTest.php: an unterminated double-quoted value is emitted (not dropped), and a following line is no longer silently swallowed.
  • Verified both new tests fail against the pre-fix code with the exact reported symptom (Lines::process() returning []), and pass with the fix.
  • Verified a properly closed multiline value, a normal single-line value, and empty input are all unaffected.
  • Ran the full test suite (vendor/bin/phpunit): 282 tests, all passing (280 pre-existing + 2 new).
  • vendor/bin/phpstan analyze (configured scope: src, level max): no errors.

When a double-quoted value was left unterminated at the end of the
input, Lines::process() discarded the buffered content instead of
emitting it, so the whole entry - and every line after it, since they
were still being buffered as part of the same unclosed multiline
value - vanished with no error.

This was inconsistent with the single-quoted case, which already
reports a missing closing quote: single-quoted values aren't treated
as multiline at all, so an unterminated one reaches EntryParser
directly and fails loudly.

Emit the still-open buffer as a final output entry instead of
discarding it. It then reaches EntryParser exactly like a normal
entry, and the lexer ends in one of the REJECT_STATES at EOF, so it
raises the same 'a missing closing quote' error the single-quoted
path already produces. A properly closed multiline value is
unaffected, since the buffer is already flushed to $output by the
time the loop ends.

This is a behaviour change - input that previously parsed to an
empty or truncated array now throws - but the prior behaviour was
silent data loss.
@GrahamCampbell

Copy link
Copy Markdown
Collaborator

This is a major breaking change, which is why I opened an issue to track this, instead of implementing it. Please do not just point AI an issue without maintainer approval - if I wanted to do that, I could do that myself.

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.

Unterminated double-quoted value at end of input is silently dropped

2 participants