diff --git a/src/Exception/InvalidPaginationArgumentException.php b/src/Exception/InvalidPaginationArgumentException.php index d6db55b..474b84b 100644 --- a/src/Exception/InvalidPaginationArgumentException.php +++ b/src/Exception/InvalidPaginationArgumentException.php @@ -25,10 +25,12 @@ class InvalidPaginationArgumentException extends \InvalidArgumentException imple private array $parameters; /** - * @param array $parameters The parameter values that were provided - * @param string $message The error message - * @param int $code The error code (optional) - * @param \Throwable|null $previous The previous exception (optional) + * Create a new InvalidPaginationArgumentException containing the parameter values that caused the error. + * + * @param array $parameters Associative map of parameter names to the values that triggered the exception. + * @param string $message Human-readable error message. + * @param int $code Optional error code. + * @param \Throwable|null $previous Optional previous exception for chaining. */ public function __construct( array $parameters, @@ -41,13 +43,13 @@ public function __construct( } /** - * Create an exception for invalid parameter values. + * Create an exception representing a single invalid pagination parameter. * - * @param string $parameterName The name of the invalid parameter - * @param mixed $value The invalid value - * @param string $description Description of what the parameter represents + * @param string $parameterName The name of the invalid parameter. + * @param mixed $value The provided value for the parameter. + * @param string $description Short description of what the parameter represents. * - * @return self + * @return self An exception containing the invalid parameter and a message describing the expected value. */ public static function forInvalidParameter( string $parameterName, @@ -67,13 +69,13 @@ public static function forInvalidParameter( } /** - * Create an exception for invalid zero limit combinations. + * Create an exception describing an invalid combination where `limit` is zero but `offset` or `nowCount` are non-zero. * - * @param int $offset The offset value - * @param int $limit The limit value (should be 0) - * @param int $nowCount The nowCount value + * @param int $offset The pagination offset that was provided. + * @param int $limit The pagination limit value (expected to be zero in this check). + * @param int $nowCount The current count of items already paginated. * - * @return self + * @return self An exception instance containing the keys `offset`, `limit`, and `nowCount` in its parameters. */ public static function forInvalidZeroLimit(int $offset, int $limit, int $nowCount): self { @@ -96,11 +98,11 @@ public static function forInvalidZeroLimit(int $offset, int $limit, int $nowCoun } /** - * Get a specific parameter value. + * Retrieve the value for a named parameter stored on the exception. * - * @param string $name The parameter name + * @param string $name Name of the parameter to retrieve. * - * @return mixed The parameter value, or null if not set + * @return mixed The parameter's value if set, or null if not present. */ public function getParameter(string $name): mixed { diff --git a/src/Exception/InvalidPaginationResultException.php b/src/Exception/InvalidPaginationResultException.php index 369573f..8670ae1 100644 --- a/src/Exception/InvalidPaginationResultException.php +++ b/src/Exception/InvalidPaginationResultException.php @@ -45,12 +45,12 @@ public static function forInvalidCallbackResult(mixed $result, string $expectedT } /** - * Create an exception for invalid source result type. + * Create an exception representing a source result type mismatch. * - * @param mixed $result The invalid result - * @param string $expectedType The expected type/class + * @param mixed $result The value returned by the source. + * @param string $expectedType The expected class or type name. * - * @return self + * @return self An exception instance describing the expected and actual types. */ public static function forInvalidSourceResult(mixed $result, string $expectedType): self { diff --git a/src/OffsetAdapter.php b/src/OffsetAdapter.php index 91041cf..9284654 100644 --- a/src/OffsetAdapter.php +++ b/src/OffsetAdapter.php @@ -28,23 +28,24 @@ readonly class OffsetAdapter { /** - * Create an adapter with a custom source implementation. + * Initialize the adapter with the given data source. * - * @param SourceInterface $source + * Stores the provided SourceInterface implementation for fetching page-based data. + * + * @param SourceInterface $source The underlying page-based data source. */ public function __construct(protected SourceInterface $source) { } /** - * Create an adapter using a callback function. + * Create an adapter backed by a callback-based source. * - * This is the most convenient way to use the adapter for simple cases. - * Your callback will receive (page, pageSize) and should return a Generator. + * The callback is called with ($page, $pageSize) and must return a Generator yielding items of type `T`. * - * @param callable(int, int): \Generator $callback + * @param callable(int, int): \Generator $callback Callback that provides page data. * - * @return self + * @return self An adapter instance that uses the provided callback as its data source. */ public static function fromCallback(callable $callback): self { @@ -52,15 +53,16 @@ public static function fromCallback(callable $callback): self } /** - * Execute pagination request with offset and limit. + * Execute an offset-based pagination request and return a result wrapper. * - * @param int $offset Starting position (0-based) - * @param int $limit Maximum number of items to return - * @param int $nowCount Current count of items already fetched (used for progress tracking in multi-request scenarios) + * @param int $offset Starting position (0-based). + * @param int $limit Maximum number of items to return; zero means no limit. + * @param int $nowCount Current count of items already fetched (used for progress tracking across requests). * - * @throws \Throwable + * @throws InvalidPaginationArgumentException If any argument is invalid (negative values or zero limit with non-zero offset/nowCount). + * @throws \Throwable For errors raised by the underlying source during data retrieval. * - * @return OffsetResult + * @return OffsetResult A wrapper exposing the paginated items (via generator() and fetchAll()) respecting the provided offset and limit. */ public function execute(int $offset, int $limit, int $nowCount = 0): OffsetResult { @@ -77,17 +79,15 @@ public function execute(int $offset, int $limit, int $nowCount = 0): OffsetResul } /** - * Get results as a generator (advanced usage). - * - * For most use cases, use execute() instead and call fetchAll() on the result. + * Return a generator that yields paginated results for the given offset and limit. * - * @param int $offset - * @param int $limit - * @param int $nowCount + * @param int $offset The zero-based offset of the first item to return. + * @param int $limit The maximum number of items to return; use 0 for no limit. + * @param int $nowCount The number of items already delivered prior to this call (affects internal page calculation). * - * @throws \Throwable + * @throws \Throwable Propagates errors thrown by the underlying source. * - * @return \Generator + * @return \Generator A generator that yields the resulting items. */ public function generator(int $offset, int $limit, int $nowCount = 0): \Generator { @@ -95,17 +95,13 @@ public function generator(int $offset, int $limit, int $nowCount = 0): \Generato } /** - * Execute pagination and return all results as an array. - * - * This is a convenience method for the most common use case. - * - * @param int $offset - * @param int $limit - * @param int $nowCount + * Fetches all items for the given offset and limit and returns them as an array. * - * @throws \Throwable + * @param int $offset The zero-based offset at which to start retrieving items. + * @param int $limit The maximum number of items to retrieve (0 means no limit). + * @param int $nowCount The number of items already delivered before this call; affects pagination calculation. * - * @return array + * @return array The list of items retrieved for the requested offset and limit. */ public function fetchAll(int $offset, int $limit, int $nowCount = 0): array { @@ -113,9 +109,18 @@ public function fetchAll(int $offset, int $limit, int $nowCount = 0): array } /** - * @throws \Throwable + * Produces a sequence of per-page generators that provide items according to the offset/limit pagination request. * - * @return \Generator<\Generator> + * The returned generator yields generators (one per fetched page) that each produce items of type `T`. Pagination continues + * until the overall requested `limit` is satisfied, the underlying source signals completion, or the computed page/page size is non-positive. + * + * @param int $offset Number of items to skip before starting to collect results. + * @param int $limit Maximum number of items to return (0 means no limit). + * @param int $nowCount Current count of already-delivered items to consider when computing subsequent pages. + * + * @throws \Throwable Propagates unexpected errors from the underlying source or pagination logic. + * + * @return \Generator<\Generator> A generator that yields per-page generators of items. */ protected function logic(int $offset, int $limit, int $nowCount): \Generator { @@ -150,6 +155,15 @@ protected function logic(int $offset, int $limit, int $nowCount): \Generator } } + /** + * Validate pagination arguments and throw when they are invalid. + * + * @param int $offset Starting position in the dataset. + * @param int $limit Maximum number of items to return (0 means no limit). + * @param int $nowCount Number of items already fetched prior to this request. + * + * @throws InvalidPaginationArgumentException If any parameter is negative, or if `$limit` is 0 while `$offset` or `$nowCount` is non‑zero. + */ private function assertArgumentsAreValid(int $offset, int $limit, int $nowCount): void { foreach ([['offset', $offset], ['limit', $limit], ['nowCount', $nowCount]] as [$name, $value]) { @@ -170,7 +184,14 @@ private function assertArgumentsAreValid(int $offset, int $limit, int $nowCount) } /** - * Create a generator that respects the overall limit. + * Yields items from the provided source generator while enforcing an overall limit. + * + * @param \Generator $sourceGenerator Generator producing source items. + * @param int $limit Overall maximum number of items to yield; 0 means no limit. + * @param int &$totalDelivered Reference to a counter incremented for each yielded item. + * @param int &$currentNowCount Reference to the current "now" count incremented for each yielded item. + * + * @return \Generator Yields items from `$sourceGenerator` until `$limit` is reached or the source is exhausted; updates `$totalDelivered` and `$currentNowCount`. */ private function createLimitedGenerator( \Generator $sourceGenerator, @@ -190,7 +211,12 @@ private function createLimitedGenerator( } /** - * Determine if pagination should continue. + * Decides whether pagination should continue based on the requested limit and items already delivered. + * + * @param int $limit The overall requested maximum number of items; zero indicates no limit. + * @param int $delivered The number of items delivered so far. + * + * @return bool `true` if pagination should continue (when `$limit` is zero or `$delivered` is less than `$limit`), `false` otherwise. */ private function shouldContinuePagination(int $limit, int $delivered): bool { diff --git a/src/OffsetResult.php b/src/OffsetResult.php index 8ddc676..bc7077f 100644 --- a/src/OffsetResult.php +++ b/src/OffsetResult.php @@ -30,7 +30,11 @@ class OffsetResult private \Generator $generator; /** - * @param \Generator<\Generator> $sourceResultGenerator + * Create an OffsetResult from a generator that yields page generators. + * + * The provided generator must yield per-page generators whose values are items of type T; the constructor stores an internal generator that will iterate items across all pages in sequence. The internal generator can be consumed only once. + * + * @param \Generator<\Generator> $sourceResultGenerator Generator that yields per-page generators of items of type T. */ public function __construct(\Generator $sourceResultGenerator) { @@ -38,7 +42,9 @@ public function __construct(\Generator $sourceResultGenerator) } /** - * @return OffsetResult + * Create an OffsetResult that yields no items. + * + * @return OffsetResult An OffsetResult containing zero elements. */ public static function empty(): self { @@ -49,7 +55,11 @@ public static function empty(): self } /** - * @return T|null + * Retrieve the next item from the internal generator. + * + * The internal generator is advanced so subsequent calls return the following items. + * + * @return T|null The next yielded value, or `null` if there are no more items. */ public function fetch(): mixed { @@ -64,7 +74,11 @@ public function fetch(): mixed } /** - * @return array + * Retrieve all remaining items from the internal generator as an array. + * + * Consuming the returned items advances the internal generator until it is exhausted. + * + * @return array An array containing every remaining yielded item; empty if none remain. */ public function fetchAll(): array { @@ -79,27 +93,35 @@ public function fetchAll(): array } /** - * Returns the internal generator for advanced use cases. + * Get the internal generator used to stream paginated items. * - * Warning: The generator can only be consumed once. After calling - * fetch(), fetchAll(), or iterating this generator, it will be exhausted. + * The returned generator can be consumed only once; calling fetch(), fetchAll(), or iterating the generator will exhaust it. * - * @return \Generator + * @return \Generator The internal generator that yields items of type T. */ public function generator(): \Generator { return $this->generator; } + /** + * Number of items fetched so far. + * + * @return int The count of items that have been retrieved from the internal generator. + */ public function getFetchedCount(): int { return $this->fetchedCount; } /** - * @param \Generator<\Generator> $generator + * Flatten a generator of page generators and yield each item in sequence. + * + * Increments the instance's fetched count for every yielded item. + * + * @param \Generator<\Generator> $generator Generator that yields page generators; each page generator yields items of type T. * - * @return \Generator + * @return \Generator Generator that yields items of type T from all pages in order. */ protected function execute(\Generator $generator): \Generator { diff --git a/src/SourceCallbackAdapter.php b/src/SourceCallbackAdapter.php index 0f494ca..f8d0d8e 100644 --- a/src/SourceCallbackAdapter.php +++ b/src/SourceCallbackAdapter.php @@ -30,16 +30,22 @@ class SourceCallbackAdapter implements SourceInterface { /** - * @param callable(int, int): \Generator $callback + * Wraps a callable data source for use as a SourceInterface implementation. + * + * @param callable(int, int): \Generator $callback A callable that accepts the 1-based page number and page size, and yields items of type `T`. */ public function __construct(private $callback) { } /** - * @throws InvalidPaginationResultException + * Invoke the configured callback to produce a page of results as a Generator. + * + * Calls the adapter's callback with the provided page and page size and returns the resulting Generator. + * + * @throws InvalidPaginationResultException If the callback does not return a `\Generator`. * - * @return \Generator + * @return \Generator A Generator that yields page results of type `T`. */ public function execute(int $page, int $pageSize): \Generator { diff --git a/tests/ArraySource.php b/tests/ArraySource.php index 7e879af..a0839d3 100644 --- a/tests/ArraySource.php +++ b/tests/ArraySource.php @@ -21,14 +21,21 @@ class ArraySource implements SourceInterface { /** - * @param array $data + * Create a new ArraySource containing the provided items. + * + * @param array $data The array of items to expose as the source. */ public function __construct(protected array $data) { } /** - * @return \Generator + * Provides the items for a specific page from the internal array. + * + * @param int $page Page number; values less than 1 are treated as 1. + * @param int $pageSize Number of items per page; if less than or equal to 0 no items are yielded. + * + * @return \Generator A generator that yields the items for the requested page. */ public function execute(int $page, int $pageSize): \Generator {