This document tracks known issues in the modernized PHP 8.3+ port of the classic diff library. These are pre-existing quirks inherited from the original Perl → PHP port lineage and are documented here to ensure they are not lost during future maintenance.
Severity: Medium (affects line-count calculations)
Affected versions: All versions
Description:
When a directive state encounters a multi-digit number (e.g., 10, 12),
only the FIRST digit is parsed into $value. Subsequent digits are silently
skipped.
Examples:
| Input | Expected argument | Actual argument |
|---|---|---|
"5a10\n" |
[5, 0, 10, 0] |
[5, 0, 1, 0] |
"3,5c8,10\n" |
[3, 5, 8, 10] |
[3, 5, 8, 1] |
Root cause:
The inner digit-parsing loop in decode_directive_line() terminates
after one iteration instead of consuming all consecutive digits:
if ($this->isdigit($this->character))
{
$value = 0;
while ($this->isdigit($this->character)) // ← loop exits after 1 iteration
{
$value = 10 * $value + (int) ($this->character - '0');
$this->nextchar();
}
}Suspected cause: a recent patch may have inadvertently changed the loop
condition or scoping. Requires review of the patch diff against the
original Side.php.
Proposed fix:
Verify the inner while loop is correctly consuming all consecutive
digits while ensuring $value is properly scoped:
while (!$error && $state < 4)
{
$value = 0; // Reset at top of outer loop
if ($this->isdigit($this->character))
{
while ($this->isdigit($this->character)) // Inner loop intact
{
$value = 10 * $value + (int) ($this->character - '0');
$this->nextchar();
}
}
elseif ($state !== 1 && $state !== 3)
{
$error = true;
}
$this->argument[$state] = $value;
// ...
}Workaround for callers:
Line-count consumers should defensively handle single-digit values
as approximations of actual line counts. For WackoWiki's diff
display, the visual difference between 1 and 10 lines is
negligible for typical page revisions.
Severity: Medium (affects diff display logic)
Affected versions: All versions
Description:
decode_directive_line() returns false (indicating parse error) for
well-formed 'a' and 'c' directives, but returns true for 'd'
directives. The argument array and directive letter ARE populated
correctly before the false return.
Examples:
Input | Expected | Actual
---------------------|-----------|--------
"5a10\n" | true | false
"3,5c8,10\n" | true | false
"5,7d3\n" | true | true ✓
Root cause:
Unknown. The case 1: switch handles all three letters identically:
case 1:
if ($this->character === 'a' || $this->character === 'd' || $this->character === 'c')
{
$this->directive = $this->character;
$this->nextchar();
}
else
{
$error = true;
}
break;Suspected causes (in order of likelihood):
- Subtle logic bug in the trailing-newline consumer for
'a'/'c' - PHP 8.x behavior change in
mb_substror related string functions - State machine edge case when multi-digit truncation (Issue #1) interacts with the error-setting code path
Workaround for callers:
In wackowiki/src/handler/page/diff.php, the existing code already
ignores the return value of decode_directive_line() in some paths.
The argument array is still populated, so the diff logic continues to
work despite the false return.
Proposed fix:
Requires deeper investigation. Likely involves adding a debug print
of the parser state machine for 'a'/'c' inputs and comparing to
the working 'd' path. Once isolated, the fix is probably a few
lines in Side::decode_directive_line().
Previously, when a directive slot had no digit content, $value
retained the previous state's digit parse value. This caused slots
like argument[1] and argument[3] to mirror the previous slot
rather than being 0.
Example: "5a10\n" previously produced [5, 5, 1, 1].
Resolution: Fixed by ensuring $value is reset at the start of
each outer-loop iteration. Now correctly produces [5, 0, 1, 0]
(multi-digit parsing still broken — see Issue #1).
These issues are tracked for resolution in a future release. To contribute a fix:
- Fork the repository
- Reference this file (
KNOWN_ISSUES.md) in your commit - Add a regression test that demonstrates the fix
- Submit a pull request
Status: Open Target: v1.1.0