live.php's Redis branch now calls ConnectionTracker::acceptedLineIP(),
which skips an empty (HMAC) line id without asking Redis and otherwise
returns the oldest active connection's IP. The HMAC guard and the
oldest-row choice are now covered by tests: a null id never queries
Redis, and unordered LINE# keys yield the earliest date_start's IP.
getLineConnectionRows() takes an int line id; its untested HMAC-identity
form is dropped. The source-grep bans are replaced by a check that
live.php calls the seam, the misnamed key-shape test is renamed, and the
redundant igbinary skip is removed (tests/bootstrap.php polyfills it).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQHeypQpfAXTdCysmi7b6a
In Redis mode live.php took ConnectionTracker::getLineConnections() as
connection rows, but it answers the LINE# set's members: connection
keys (uuid strings). array_column() over them gave [], and
array_multisort([], SORT_ASC, $rConnections) threw "ValueError: Array
sizes are inconsistent" on PHP 8 as soon as the line had one active
connection, including the same viewer's next playlist refresh. The
request died with a 500 instead of being limited. For an HMAC token the
line id is null, so passing it to getLineConnections(int) was a
TypeError before any connection was counted.
Add ConnectionTracker::getLineConnectionRows(), which reads and
unserializes the rows behind a line's LINE# keys (an unreachable Redis,
a failed call or an unreadable payload reads as no connection), and
ConnectionTracker::oldestConnectionIP(), which picks the user_ip with the
earliest date_start. live.php now accepts that IP, as the MySQL branch
does with the first lines_live row. HMAC identities skip the check, as
they already do in MySQL mode where `user_id = NULL` matches nothing.
getLineConnections() and ConnectionLimiter are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQHeypQpfAXTdCysmi7b6a