Repository navigation
fix: stop treating the payload "0" as empty data (#75) - #267
Merged
Merged
Conversation
`empty("0")` is true in PHP, so both emptiness checks rejected the
one-character payload "0" even though FastEncoder, FfiEncoder,
NativeEncoder and the C++ core all encode it.
Encoder and QRCode now compare against '' explicitly. QRCode still
requires a valid URL, so "0" is reported as an invalid URL there
instead of as empty data, which is what it is.
Adds tests/EncoderTest.php covering "0", "00" and "0" repeated ten
times, plus an Encoder/FastEncoder parity check, and two QRCode tests
for the empty and zero payloads.
The parity test added for #75 was a tautology: Encoder::encode() delegates to FastEncoder::encodeVersion() for every version <= 27, and "0", "00" and "0" repeated all resolve to v1, so both sides of the comparison were the same call and the test stayed green even when FastEncoder was made to return a wrong matrix. Comparing two PHP encoders cannot work while that fast path exists, so the payloads move to the mechanism that already pins module bits: three rows in tests/fixtures/qr_reference.csv, checked by QrReferenceTest against Encoder, FastEncoder and FfiEncoder, and by clib/tests/test_csv_fixtures.cpp against the C++ core. The 441 bits per row were generated by a C++ program linked against the C++ core with no PHP in the process, then compared against all three PHP backends (9 comparisons, byte-identical). The C++ fixture reader accepts the rows and rejects a row with one flipped module bit. The surviving EncoderTest assertions now pin what the payload actually determines, v1 at ECL L, instead of counting rows of getData().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
empty("0")istruein PHP, so both emptiness checks rejected the one-character payload"0"even though
FastEncoder,FfiEncoder,NativeEncoderand the C++ core all encode it.Encoderand
QRCodenow compare against''explicitly.QRCodestill requires a valid URL, so"0"is reported there asInvalid URL provided: "0"rather than as empty data — the old message was factually wrong.The empty check stays first, so
''still yieldsData cannot be emptyand nothing else moves.Non-URL payloads go through an encoder directly.
The other backends were swept and needed no change:
FastEncoderalready usesstrlen($url) === 0,FfiEncoderuses$data === '',NativeEncoderpassesZSTR_LENtoC, and
clib/src/encoder.cppcheckslen == 0.How it was tested
The
"0"payloads are pinned by three new rows intests/fixtures/qr_reference.csv. That file isread by two independent mechanisms:
tests/QrReferenceTest.phpcompares every row againstEncoder,FastEncoderandFfiEncoder, andclib/tests/test_csv_fixtures.cppcomparesthe same rows against the C++ core — so one row pins four backends.
linked against
clib/src/encoder.cpp, with no PHP in the process, then compared against allthree PHP backends: 3 payloads × 3 backends = 9 comparisons, all byte-identical. The fixture is
not "whatever PHP now produces".
test_csv_fixturesaccepts the new rows (1772 → 1775 data rows,0 failures) and rejects a copy of the
"0"row with a single module bit flipped(
1 tests, 1 failures, exit 1).tests/EncoderTest.php: asserts v1 / size 21 for"0","00"and"0"repeated ten times(what the payload actually determines), and keeps the empty-string case throwing
Data cannot be empty.tests/QRCodeTest.php: the empty-URL case, plus a test pinning"0"toInvalid URL provided: "0".Red/green
Red was reproduced by reverting only
src/to the base commit9c93e75, with the new tests andfixtures in place:
Red lands on exactly
Encoder matches referencefor"0"while FastEncoder and FFI pass — thebackend disagreement, expressed in the repo's own terms. Note that only the
"0"row goes red;"00"and"0"×10 were never broken, so those rows are guard rails rather than reproducers.The other new tests on base code:
testEmptyUrlThrowsEmptyDatapasses both before and after the fix —''behaviour is unchangedby design — so it is deliberately not claimed as a red case.
Green with the fix restored:
bin/lint.sh— All checks passed.composer validate --strict— valid.composer test— 5487 tests, 20153 assertions, OK. (8 deprecations + 4 PHPUnit deprecationsare pre-existing
imagedestroy()calls; the new tests add none.)ctestinclib/build— 3/3 passed, with the extended CSV.Review round 2
Both blocking review findings were in the tests and are addressed:
Encoder::encode()delegates toFastEncoder::encodeVersion()for every version ≤ 27 and allthree payloads resolve to v1, so both sides of the comparison were the same call; the test
stayed green even with
FastEncodermade to return a wrong matrix. Comparing two PHP encoderscannot work while that fast path exists, so the coverage moved to the fixture rows above. The
misleading docblock sentence was deleted with the test.
testEncodeAcceptsZeroOnlyPayloadpinned nothing. Accepted.assertInstanceOfwasguaranteed by the return type, the version check held for any constructible
Matrix, andassertCount($matrix->getSize(), $matrix->getData())counted rows (nestedbool[][]), so itwould fail confusingly with a 21-vs-441 diff on any
getData()refactor. All three are gone.Non-blocking points: the CHANGELOG "Both sites" wording is fixed, and the PR body's red/green
claim now names the specific cases that go red instead of implying all of them.
src/Builder.php:39keeps its
empty()— it testsshell_exec()output ofwhich/where, never a payload. TheREADME does not document that
QRCodeis URL-only; that gap is being filed separately.Checklist
CHANGELOG.mdupdated under[Unreleased]QRCode-is-URL-only gap is filed separately)feat: ...,fix: ...)Fixes #75