status = self::STATUS_PENDING; } /** @return void */ public function markErrored(): void { $this->status = self::STATUS_ERRORED; } /** * Classify a successful (non-throwing) row read. A non-empty result is * always trustworthy; an empty result is trustworthy only when the live * source count agrees there are no rows for the requested page. The count * is probed (via $liveCountProbe, expected to return -1 when unavailable) * only when the rows are empty. Strict-mode reads * (`_abj404_throw_on_view_query_error`) classify pending/error themselves * and are never probed here. * * @param array $rows * @param array $tableOptions * @param callable():int $liveCountProbe * @return void */ public function classifyRows(array $rows, array $tableOptions, callable $liveCountProbe): void { if (!empty($rows) || !empty($tableOptions['_abj404_throw_on_view_query_error'])) { // Non-empty rows are trustworthy; strict-mode empties are the // caller's own (already-classified) pending/error responsibility. $this->status = self::STATUS_COMPLETE; return; } $this->status = self::emptyRowsAreStale((int)$liveCountProbe(), $tableOptions) ? self::STATUS_STALE_EMPTY : self::STATUS_COMPLETE; } /** * Pure heuristic: do empty rows contradict the live source count? True * when the count says rows should appear on the requested page yet none * were returned. An empty page past the end of a real result set * (offset >= count) is a legitimate empty, not a stale snapshot. * * @param int $liveCount negative or zero means "no rows / unavailable" * @param array $tableOptions supplies perpage/paged * @return bool */ public static function emptyRowsAreStale(int $liveCount, array $tableOptions): bool { if ($liveCount <= 0) { return false; } $perpage = isset($tableOptions['perpage']) && is_numeric($tableOptions['perpage']) ? max(1, intval($tableOptions['perpage'])) : 25; $paged = isset($tableOptions['paged']) && is_numeric($tableOptions['paged']) ? max(1, intval($tableOptions['paged'])) : 1; return $liveCount > ($paged - 1) * $perpage; } /** * Whether the last classified read is NOT a trustworthy "genuinely empty" * listing (pending, errored, or stale-empty). * * @return bool */ public function wasIncomplete(): bool { return in_array($this->status, array( self::STATUS_PENDING, self::STATUS_ERRORED, self::STATUS_STALE_EMPTY, ), true); } /** @return string One of the STATUS_* constants. */ public function status(): string { return $this->status; } }