*/ private array $items; /** * Total row count across every page (after filtering, before * pagination). */ private int $total; /** * 1-based page number echoed back from the originating query. */ private int $page; /** * Records per page echoed back from the originating query. */ private int $perPage; /** * Construct the result envelope with full validation. * * @param list $items Page slice. * @param int $total Total matching rows (>= 0). * @param int $page 1-based page number (>= 1). * @param int $perPage Records per page (>= 1). * * @throws InvalidArgumentException When `$items` is not a list of * {@see LogRecord}, when `$total` * is negative, or when `$page` / * `$perPage` are below 1. */ public function __construct( array $items, int $total, int $page, int $perPage ) { $expectedIndex = 0; foreach ( $items as $key => $item ) { if ( $key !== $expectedIndex ) { $message = 'LogListResult: items must be a 0-indexed list.'; // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } if ( ! $item instanceof LogRecord ) { $message = 'LogListResult: every items entry must be a LogRecord.'; // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } ++$expectedIndex; } if ( $total < 0 ) { $message = sprintf( 'LogListResult: total must be >= 0, got %d.', $total ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } if ( $page < 1 ) { $message = sprintf( 'LogListResult: page must be >= 1, got %d.', $page ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } if ( $perPage < 1 ) { $message = sprintf( 'LogListResult: perPage must be >= 1, got %d.', $perPage ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Developer-facing message, never rendered to HTML. throw new InvalidArgumentException( $message ); } /** @var list $items */ $this->items = $items; $this->total = $total; $this->page = $page; $this->perPage = $perPage; } /** * Page slice, in newest-first order produced by the repository. * * @return list */ public function items(): array { return $this->items; } /** * Total row count across every page. */ public function total(): int { return $this->total; } /** * 1-based page number echoed back from the originating query. */ public function page(): int { return $this->page; } /** * Records per page echoed back from the originating query. */ public function perPage(): int { return $this->perPage; } /** * Total number of pages spanned by `total` at `perPage` * granularity. * * Returns 0 when `total` is 0 (the conventional empty-result * shape) and `(int) ceil($total / $perPage)` otherwise. */ public function pageCount(): int { if ( $this->total === 0 ) { return 0; } return (int) ceil( $this->total / $this->perPage ); } /** * Whether the result envelope carries no rows. */ public function isEmpty(): bool { return $this->total === 0; } }