diff --git a/composer.json b/composer.json index f33d730c767..4fc6a11b63a 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,8 @@ "psr/cache": "^1.0", "psr/http-client-implementation": "^1.0", "psr/http-factory-implementation": "^1.0", - "psr/http-message": "^1.0" + "psr/http-message": "^1.0", + "symfony/polyfill-php80": "^1.17" }, "require-dev": { "symfony/cache": "^5.1.8", diff --git a/lib/Github/Api/AbstractApi.php b/lib/Github/Api/AbstractApi.php index 666ccf1e4d5..17aa8ce21a0 100644 --- a/lib/Github/Api/AbstractApi.php +++ b/lib/Github/Api/AbstractApi.php @@ -6,79 +6,59 @@ use Github\HttpClient\Message\ResponseMediator; /** - * Abstract class for Api classes. - * * @author Joseph Bielawski + * @author Graham Campbell */ -abstract class AbstractApi implements ApiInterface +abstract class AbstractApi { /** - * The client. + * The client instance. * * @var Client */ - protected $client; + private $client; /** - * The requested page (GitHub pagination). + * The per page parameter. * - * @var null|int + * @var int|null */ - private $page; + private $perPage; /** - * Number of items per page (GitHub pagination). + * Create a new API instance. * - * @var null|int - */ - protected $perPage; - - /** * @param Client $client + * + * @return void */ public function __construct(Client $client) { $this->client = $client; } - public function configure() - { - } - - /** - * @return null|int - */ - public function getPage() - { - return $this->page; - } - /** - * @param null|int $page + * Get the client instance. + * + * @return Client */ - public function setPage($page) + protected function getClient() { - $this->page = (null === $page ? $page : (int) $page); - - return $this; + return $this->client; } /** - * @return null|int + * Get the API version. + * + * @return string */ - public function getPerPage() + protected function getApiVersion() { - return $this->perPage; + return $this->client->getApiVersion(); } - /** - * @param null|int $perPage - */ - public function setPerPage($perPage) + public function configure() { - $this->perPage = (null === $perPage ? $perPage : (int) $perPage); - - return $this; } /** @@ -92,12 +72,10 @@ public function setPerPage($perPage) */ protected function get($path, array $parameters = [], array $requestHeaders = []) { - if (null !== $this->page && !isset($parameters['page'])) { - $parameters['page'] = $this->page; - } if (null !== $this->perPage && !isset($parameters['per_page'])) { $parameters['per_page'] = $this->perPage; } + if (array_key_exists('ref', $parameters) && null === $parameters['ref']) { unset($parameters['ref']); } diff --git a/lib/Github/Api/ApiInterface.php b/lib/Github/Api/ApiInterface.php deleted file mode 100644 index 49d5167c29d..00000000000 --- a/lib/Github/Api/ApiInterface.php +++ /dev/null @@ -1,15 +0,0 @@ - - */ -interface ApiInterface -{ - public function getPerPage(); - - public function setPerPage($perPage); -} diff --git a/lib/Github/Api/CurrentUser.php b/lib/Github/Api/CurrentUser.php index 18e197f8c77..70600a855a8 100644 --- a/lib/Github/Api/CurrentUser.php +++ b/lib/Github/Api/CurrentUser.php @@ -35,7 +35,7 @@ public function update(array $params) */ public function emails() { - return new Emails($this->client); + return new Emails($this->getClient()); } /** @@ -43,7 +43,7 @@ public function emails() */ public function follow() { - return new Followers($this->client); + return new Followers($this->getClient()); } public function followers($page = 1) @@ -71,7 +71,7 @@ public function issues(array $params = [], $includeOrgIssues = true) */ public function keys() { - return new PublicKeys($this->client); + return new PublicKeys($this->getClient()); } /** @@ -79,7 +79,7 @@ public function keys() */ public function notifications() { - return new Notifications($this->client); + return new Notifications($this->getClient()); } /** @@ -87,7 +87,7 @@ public function notifications() */ public function memberships() { - return new Memberships($this->client); + return new Memberships($this->getClient()); } /** @@ -147,7 +147,7 @@ public function repositories($type = 'owner', $sort = 'full_name', $direction = */ public function watchers() { - return new Watchers($this->client); + return new Watchers($this->getClient()); } /** @@ -155,7 +155,7 @@ public function watchers() */ public function starring() { - return new Starring($this->client); + return new Starring($this->getClient()); } /** diff --git a/lib/Github/Api/CurrentUser/Starring.php b/lib/Github/Api/CurrentUser/Starring.php index 5fb6435f1f6..6d706c8741c 100644 --- a/lib/Github/Api/CurrentUser/Starring.php +++ b/lib/Github/Api/CurrentUser/Starring.php @@ -26,7 +26,7 @@ class Starring extends AbstractApi public function configure($bodyType = null) { if ('star' === $bodyType) { - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->client->getApiVersion()); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->getApiVersion()); } return $this; diff --git a/lib/Github/Api/Enterprise.php b/lib/Github/Api/Enterprise.php index 3dbbee3ea2b..b3daf95a177 100644 --- a/lib/Github/Api/Enterprise.php +++ b/lib/Github/Api/Enterprise.php @@ -22,7 +22,7 @@ class Enterprise extends AbstractApi */ public function stats() { - return new Stats($this->client); + return new Stats($this->getClient()); } /** @@ -30,7 +30,7 @@ public function stats() */ public function license() { - return new License($this->client); + return new License($this->getClient()); } /** @@ -38,7 +38,7 @@ public function license() */ public function console() { - return new ManagementConsole($this->client); + return new ManagementConsole($this->getClient()); } /** @@ -46,6 +46,6 @@ public function console() */ public function userAdmin() { - return new UserAdmin($this->client); + return new UserAdmin($this->getClient()); } } diff --git a/lib/Github/Api/Gist/Comments.php b/lib/Github/Api/Gist/Comments.php index 251ec7305ab..d32476f722f 100644 --- a/lib/Github/Api/Gist/Comments.php +++ b/lib/Github/Api/Gist/Comments.php @@ -29,7 +29,7 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } diff --git a/lib/Github/Api/Gists.php b/lib/Github/Api/Gists.php index 69adef426fe..e88481f05bc 100644 --- a/lib/Github/Api/Gists.php +++ b/lib/Github/Api/Gists.php @@ -32,7 +32,7 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->getApiVersion(), $bodyType); return $this; } @@ -177,6 +177,6 @@ public function unstar($id) */ public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } } diff --git a/lib/Github/Api/GitData.php b/lib/Github/Api/GitData.php index d431b788704..b7a9f48c344 100644 --- a/lib/Github/Api/GitData.php +++ b/lib/Github/Api/GitData.php @@ -22,7 +22,7 @@ class GitData extends AbstractApi */ public function blobs() { - return new Blobs($this->client); + return new Blobs($this->getClient()); } /** @@ -30,7 +30,7 @@ public function blobs() */ public function commits() { - return new Commits($this->client); + return new Commits($this->getClient()); } /** @@ -38,7 +38,7 @@ public function commits() */ public function references() { - return new References($this->client); + return new References($this->getClient()); } /** @@ -46,7 +46,7 @@ public function references() */ public function tags() { - return new Tags($this->client); + return new Tags($this->getClient()); } /** @@ -54,6 +54,6 @@ public function tags() */ public function trees() { - return new Trees($this->client); + return new Trees($this->getClient()); } } diff --git a/lib/Github/Api/GitData/Blobs.php b/lib/Github/Api/GitData/Blobs.php index c6269e86663..0562bfe5cba 100644 --- a/lib/Github/Api/GitData/Blobs.php +++ b/lib/Github/Api/GitData/Blobs.php @@ -26,7 +26,7 @@ class Blobs extends AbstractApi public function configure($bodyType = null) { if ('raw' === $bodyType) { - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.raw', $this->client->getApiVersion()); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.raw', $this->getApiVersion()); } return $this; diff --git a/lib/Github/Api/Issue.php b/lib/Github/Api/Issue.php index 875f305f127..08f93d749c0 100644 --- a/lib/Github/Api/Issue.php +++ b/lib/Github/Api/Issue.php @@ -37,7 +37,7 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } @@ -176,7 +176,7 @@ public function unlock($username, $repository, $id) */ public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } /** @@ -188,7 +188,7 @@ public function comments() */ public function events() { - return new Events($this->client); + return new Events($this->getClient()); } /** @@ -200,7 +200,7 @@ public function events() */ public function labels() { - return new Labels($this->client); + return new Labels($this->getClient()); } /** @@ -212,7 +212,7 @@ public function labels() */ public function milestones() { - return new Milestones($this->client); + return new Milestones($this->getClient()); } /** @@ -224,7 +224,7 @@ public function milestones() */ public function assignees() { - return new Assignees($this->client); + return new Assignees($this->getClient()); } /** @@ -236,6 +236,6 @@ public function assignees() */ public function timeline() { - return new Timeline($this->client); + return new Timeline($this->getClient()); } } diff --git a/lib/Github/Api/Issue/Comments.php b/lib/Github/Api/Issue/Comments.php index 396f5060893..392fb5e76ad 100644 --- a/lib/Github/Api/Issue/Comments.php +++ b/lib/Github/Api/Issue/Comments.php @@ -31,7 +31,7 @@ public function configure($bodyType = null) $bodyType = 'full'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } diff --git a/lib/Github/Api/Organization.php b/lib/Github/Api/Organization.php index 46dd975ca0a..aad0d9b57d8 100644 --- a/lib/Github/Api/Organization.php +++ b/lib/Github/Api/Organization.php @@ -82,7 +82,7 @@ public function repositories($organization, $type = 'all', $page = 1, $sort = nu */ public function members() { - return new Members($this->client); + return new Members($this->getClient()); } /** @@ -90,7 +90,7 @@ public function members() */ public function hooks() { - return new Hooks($this->client); + return new Hooks($this->getClient()); } /** @@ -98,7 +98,7 @@ public function hooks() */ public function teams() { - return new Teams($this->client); + return new Teams($this->getClient()); } /** @@ -106,7 +106,7 @@ public function teams() */ public function outsideCollaborators() { - return new OutsideCollaborators($this->client); + return new OutsideCollaborators($this->getClient()); } /** diff --git a/lib/Github/Api/Project/AbstractProjectApi.php b/lib/Github/Api/Project/AbstractProjectApi.php index 15274d7dbc4..986dab45ca6 100644 --- a/lib/Github/Api/Project/AbstractProjectApi.php +++ b/lib/Github/Api/Project/AbstractProjectApi.php @@ -40,6 +40,6 @@ public function deleteProject($id) public function columns() { - return new Columns($this->client); + return new Columns($this->getClient()); } } diff --git a/lib/Github/Api/Project/Columns.php b/lib/Github/Api/Project/Columns.php index 22e5cbafcb8..5146ac223af 100644 --- a/lib/Github/Api/Project/Columns.php +++ b/lib/Github/Api/Project/Columns.php @@ -68,6 +68,6 @@ public function move($id, array $params) public function cards() { - return new Cards($this->client); + return new Cards($this->getClient()); } } diff --git a/lib/Github/Api/PullRequest.php b/lib/Github/Api/PullRequest.php index fcc46c84104..e9453ae7f69 100644 --- a/lib/Github/Api/PullRequest.php +++ b/lib/Github/Api/PullRequest.php @@ -32,7 +32,7 @@ class PullRequest extends AbstractApi public function configure($bodyType = null, $apiVersion = null) { if (null === $apiVersion) { - $apiVersion = $this->client->getApiVersion(); + $apiVersion = $this->getApiVersion(); } if (!in_array($bodyType, ['text', 'html', 'full', 'diff', 'patch'])) { @@ -110,17 +110,17 @@ public function status($username, $repository, $id) public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } public function reviews() { - return new Review($this->client); + return new Review($this->getClient()); } public function reviewRequests() { - return new ReviewRequest($this->client); + return new ReviewRequest($this->getClient()); } /** diff --git a/lib/Github/Api/PullRequest/Comments.php b/lib/Github/Api/PullRequest/Comments.php index 3b71d490771..199f58632a3 100644 --- a/lib/Github/Api/PullRequest/Comments.php +++ b/lib/Github/Api/PullRequest/Comments.php @@ -28,7 +28,7 @@ class Comments extends AbstractApi public function configure($bodyType = null, $apiVersion = null) { if ($apiVersion !== 'squirrel-girl-preview') { - $apiVersion = $this->client->getApiVersion(); + $apiVersion = $this->getApiVersion(); } if (!in_array($bodyType, ['text', 'html', 'full'])) { diff --git a/lib/Github/Api/Repo.php b/lib/Github/Api/Repo.php index 842e4aaf36d..b806c50455b 100644 --- a/lib/Github/Api/Repo.php +++ b/lib/Github/Api/Repo.php @@ -290,7 +290,7 @@ public function dispatch($username, $repository, $eventType, array $clientPayloa */ public function collaborators() { - return new Collaborators($this->client); + return new Collaborators($this->getClient()); } /** @@ -302,7 +302,7 @@ public function collaborators() */ public function comments() { - return new Comments($this->client); + return new Comments($this->getClient()); } /** @@ -314,7 +314,7 @@ public function comments() */ public function commits() { - return new Commits($this->client); + return new Commits($this->getClient()); } /** @@ -329,7 +329,7 @@ public function checks() { @trigger_error(sprintf('The "%s" is deprecated since knp-labs/php-github-api 2.17 and will be removed in knp-labs/php-github-api 3.0. Use the "checkRuns" or "checkSuites" api\'s instead.', __METHOD__), E_USER_DEPRECATED); - return new Checks($this->client); + return new Checks($this->getClient()); } /** @@ -337,7 +337,7 @@ public function checks() */ public function checkRuns(): CheckRuns { - return new CheckRuns($this->client); + return new CheckRuns($this->getClient()); } /** @@ -345,7 +345,7 @@ public function checkRuns(): CheckRuns */ public function checkSuites(): CheckSuites { - return new CheckSuites($this->client); + return new CheckSuites($this->getClient()); } /** @@ -357,7 +357,7 @@ public function checkSuites(): CheckSuites */ public function contents() { - return new Contents($this->client); + return new Contents($this->getClient()); } /** @@ -369,7 +369,7 @@ public function contents() */ public function downloads() { - return new Downloads($this->client); + return new Downloads($this->getClient()); } /** @@ -381,7 +381,7 @@ public function downloads() */ public function releases() { - return new Releases($this->client); + return new Releases($this->getClient()); } /** @@ -393,7 +393,7 @@ public function releases() */ public function keys() { - return new DeployKeys($this->client); + return new DeployKeys($this->getClient()); } /** @@ -405,7 +405,7 @@ public function keys() */ public function forks() { - return new Forks($this->client); + return new Forks($this->getClient()); } /** @@ -417,7 +417,7 @@ public function forks() */ public function stargazers() { - return new Stargazers($this->client); + return new Stargazers($this->getClient()); } /** @@ -429,7 +429,7 @@ public function stargazers() */ public function hooks() { - return new Hooks($this->client); + return new Hooks($this->getClient()); } /** @@ -441,7 +441,7 @@ public function hooks() */ public function labels() { - return new Labels($this->client); + return new Labels($this->getClient()); } /** @@ -453,7 +453,7 @@ public function labels() */ public function statuses() { - return new Statuses($this->client); + return new Statuses($this->getClient()); } /** @@ -486,7 +486,7 @@ public function branches($username, $repository, $branch = null) */ public function protection() { - return new Protection($this->client); + return new Protection($this->getClient()); } /** @@ -609,17 +609,17 @@ public function milestones($username, $repository, array $parameters = []) public function projects() { - return new Projects($this->client); + return new Projects($this->getClient()); } public function traffic() { - return new Traffic($this->client); + return new Traffic($this->getClient()); } public function pages() { - return new Pages($this->client); + return new Pages($this->getClient()); } /** diff --git a/lib/Github/Api/Repository/Comments.php b/lib/Github/Api/Repository/Comments.php index 53feb72cd31..0b31f55ebf7 100644 --- a/lib/Github/Api/Repository/Comments.php +++ b/lib/Github/Api/Repository/Comments.php @@ -31,7 +31,7 @@ public function configure($bodyType = null) $bodyType = 'full'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s+json', $this->getApiVersion(), $bodyType); return $this; } diff --git a/lib/Github/Api/Repository/Contents.php b/lib/Github/Api/Repository/Contents.php index 98c85723c2f..dec82ba7a71 100644 --- a/lib/Github/Api/Repository/Contents.php +++ b/lib/Github/Api/Repository/Contents.php @@ -33,7 +33,7 @@ public function configure($bodyType = null) $bodyType = 'raw'; } - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->client->getApiVersion(), $bodyType); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.%s', $this->getApiVersion(), $bodyType); return $this; } diff --git a/lib/Github/Api/Repository/Releases.php b/lib/Github/Api/Repository/Releases.php index 9d869b72d4a..10dfe09d290 100644 --- a/lib/Github/Api/Repository/Releases.php +++ b/lib/Github/Api/Repository/Releases.php @@ -122,6 +122,6 @@ public function remove($username, $repository, $id) */ public function assets() { - return new Assets($this->client); + return new Assets($this->getClient()); } } diff --git a/lib/Github/Api/Repository/Stargazers.php b/lib/Github/Api/Repository/Stargazers.php index e71777741c7..3bf7b6ae06a 100644 --- a/lib/Github/Api/Repository/Stargazers.php +++ b/lib/Github/Api/Repository/Stargazers.php @@ -27,7 +27,7 @@ class Stargazers extends AbstractApi public function configure($bodyType = null) { if ('star' === $bodyType) { - $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->client->getApiVersion()); + $this->acceptHeaderValue = sprintf('application/vnd.github.%s.star+json', $this->getApiVersion()); } return $this; diff --git a/lib/Github/Client.php b/lib/Github/Client.php index a77a9b8f010..c3402b6484b 100644 --- a/lib/Github/Client.php +++ b/lib/Github/Client.php @@ -2,7 +2,7 @@ namespace Github; -use Github\Api\ApiInterface; +use Github\Api\AbstractApi; use Github\Exception\BadMethodCallException; use Github\Exception\InvalidArgumentException; use Github\HttpClient\Builder; @@ -154,7 +154,7 @@ public static function createWithHttpClient(ClientInterface $httpClient) * * @throws InvalidArgumentException * - * @return ApiInterface + * @return AbstractApi */ public function api($name) { @@ -378,7 +378,7 @@ public function removeCache() * @param string $name * @param array $args * - * @return ApiInterface + * @return AbstractApi */ public function __call($name, $args) { diff --git a/lib/Github/HttpClient/Message/ResponseMediator.php b/lib/Github/HttpClient/Message/ResponseMediator.php index 4664d4f018d..858f0e9600d 100644 --- a/lib/Github/HttpClient/Message/ResponseMediator.php +++ b/lib/Github/HttpClient/Message/ResponseMediator.php @@ -28,19 +28,21 @@ public static function getContent(ResponseInterface $response) /** * @param ResponseInterface $response * - * @return array|void + * @return array */ public static function getPagination(ResponseInterface $response) { - if (!$response->hasHeader('Link')) { - return; + $header = self::getHeader($response, 'Link'); + + if (null === $header) { + return []; } - $header = self::getHeader($response, 'Link'); $pagination = []; foreach (explode(',', $header) as $link) { preg_match('/<(.*)>; rel="(.*)"/i', trim($link, ','), $match); + /** @var string[] $match */ if (3 === count($match)) { $pagination[$match[2]] = $match[1]; } @@ -79,7 +81,7 @@ public static function getApiLimit(ResponseInterface $response) * * @return string|null */ - public static function getHeader(ResponseInterface $response, $name) + public static function getHeader(ResponseInterface $response, string $name) { $headers = $response->getHeader($name); diff --git a/lib/Github/ResultPager.php b/lib/Github/ResultPager.php index defcde23e6a..9cfb5cba403 100644 --- a/lib/Github/ResultPager.php +++ b/lib/Github/ResultPager.php @@ -2,36 +2,49 @@ namespace Github; -use Github\Api\ApiInterface; -use Github\Api\Search; +use Github\Api\AbstractApi; use Github\HttpClient\Message\ResponseMediator; +use ValueError; /** * Pager class for supporting pagination in github classes. * * @author Ramon de la Fuente * @author Mitchel Verschoof + * @author Graham Campbell */ class ResultPager implements ResultPagerInterface { /** - * The GitHub Client to use for pagination. + * The default number of entries to request per page. * - * @var \Github\Client + * @var int */ - protected $client; + private const PER_PAGE = 100; /** - * Comes from pagination headers in Github API results. + * The client to use for pagination. * - * @var array + * @var Client */ - protected $pagination; + private $client; /** - * The Github client to use for pagination. + * The number of entries to request per page. * - * This must be the same instance that you got the Api instance from. + * @var int + */ + private $perPage; + + /** + * The pagination result from the API. + * + * @var array + */ + private $pagination; + + /** + * Create a new result pager instance. * * Example code: * @@ -39,65 +52,68 @@ class ResultPager implements ResultPagerInterface * $api = $client->api('someApi'); * $pager = new \Github\ResultPager($client); * - * @param \Github\Client $client + * @param Client $client + * @param int|null $perPage + * + * @return void */ - public function __construct(Client $client) + public function __construct(Client $client, int $perPage = null) { + if (null !== $perPage && ($perPage < 1 || $perPage > 100)) { + throw new ValueError(sprintf('%s::__construct(): Argument #2 ($perPage) must be between 1 and 100, or null', self::class)); + } + $this->client = $client; + $this->perPage = $perPage ?? self::PER_PAGE; + $this->pagination = []; } /** * {@inheritdoc} */ - public function getPagination() + public function fetch(AbstractApi $api, string $method, array $parameters = []) { - return $this->pagination; + $paginatorPerPage = $this->perPage; + $closure = \Closure::bind(function (AbstractApi $api) use ($paginatorPerPage) { + $clone = clone $api; + $clone->perPage = $paginatorPerPage; + + return $clone; + }, null, AbstractApi::class); + + $api = $closure($api); + $result = $api->$method(...$parameters); + + $this->postFetch(); + + return $result; } /** * {@inheritdoc} */ - public function fetch(ApiInterface $api, $method, array $parameters = []) + public function fetchAll(AbstractApi $api, string $method, array $parameters = []) { - $result = $this->callApi($api, $method, $parameters); - $this->postFetch(); - - return $result; + return iterator_to_array($this->fetchAllLazy($api, $method, $parameters)); } /** * {@inheritdoc} */ - public function fetchAll(ApiInterface $api, $method, array $parameters = []) + public function fetchAllLazy(AbstractApi $api, string $method, array $parameters = []) { - $isSearch = $api instanceof Search; - - // get the perPage from the api - $perPage = $api->getPerPage(); + $result = $this->fetch($api, $method, $parameters); - // set parameters per_page to GitHub max to minimize number of requests - $api->setPerPage(100); - - try { - $result = $this->callApi($api, $method, $parameters); - $this->postFetch(); - - if ($isSearch) { - $result = isset($result['items']) ? $result['items'] : $result; - } + foreach ($result['items'] ?? $result as $item) { + yield $item; + } - while ($this->hasNext()) { - $next = $this->fetchNext(); + while ($this->hasNext()) { + $result = $this->fetchNext(); - if ($isSearch) { - $result = array_merge($result, $next['items']); - } else { - $result = array_merge($result, $next); - } + foreach ($result['items'] ?? $result as $item) { + yield $item; } - } finally { - // restore the perPage - $api->setPerPage($perPage); } return $result; @@ -116,7 +132,7 @@ public function postFetch() */ public function hasNext() { - return $this->has('next'); + return isset($this->pagination['next']); } /** @@ -132,7 +148,7 @@ public function fetchNext() */ public function hasPrevious() { - return $this->has('prev'); + return isset($this->pagination['prev']); } /** @@ -159,42 +175,21 @@ public function fetchLast() return $this->get('last'); } - /** - * @param string $key - * - * @return bool - */ - protected function has($key) - { - return !empty($this->pagination) && isset($this->pagination[$key]); - } - /** * @param string $key * * @return array */ - protected function get($key) + protected function get(string $key) { - if ($this->has($key)) { - $result = $this->client->getHttpClient()->get($this->pagination[$key]); - $this->postFetch(); - - return ResponseMediator::getContent($result); + if (!isset($this->pagination[$key])) { + return []; } - return []; - } + $result = $this->client->getHttpClient()->get($this->pagination[$key]); - /** - * @param ApiInterface $api - * @param string $method - * @param array $parameters - * - * @return mixed - */ - protected function callApi(ApiInterface $api, $method, array $parameters) - { - return call_user_func_array([$api, $method], $parameters); + $this->postFetch(); + + return ResponseMediator::getContent($result); } } diff --git a/lib/Github/ResultPagerInterface.php b/lib/Github/ResultPagerInterface.php index 80660247900..5faf1cb0f53 100644 --- a/lib/Github/ResultPagerInterface.php +++ b/lib/Github/ResultPagerInterface.php @@ -2,44 +2,53 @@ namespace Github; -use Github\Api\ApiInterface; +use Github\Api\AbstractApi; /** * Pager interface. * * @author Ramon de la Fuente * @author Mitchel Verschoof + * @author Graham Campbell */ interface ResultPagerInterface { - /** - * @return null|array pagination result of last request - */ - public function getPagination(); - /** * Fetch a single result (page) from an api call. * - * @param ApiInterface $api the Api instance - * @param string $method the method name to call on the Api instance - * @param array $parameters the method parameters in an array + * @param AbstractApi $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array * * @return array returns the result of the Api::$method() call */ - public function fetch(ApiInterface $api, $method, array $parameters = []); + public function fetch(AbstractApi $api, string $method, array $parameters = []); /** * Fetch all results (pages) from an api call. * * Use with care - there is no maximum. * - * @param ApiInterface $api the Api instance - * @param string $method the method name to call on the Api instance - * @param array $parameters the method parameters in an array + * @param AbstractApi $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array * * @return array returns a merge of the results of the Api::$method() call */ - public function fetchAll(ApiInterface $api, $method, array $parameters = []); + public function fetchAll(AbstractApi $api, string $method, array $parameters = []); + + /** + * Lazily fetch all results (pages) from an api call. + * + * Use with care - there is no maximum. + * + * @param AbstractApi $api the Api instance + * @param string $method the method name to call on the Api instance + * @param array $parameters the method parameters in an array + * + * @return \Generator returns a merge of the results of the Api::$method() call + */ + public function fetchAllLazy(AbstractApi $api, string $method, array $parameters = []); /** * Method that performs the actual work to refresh the pagination property. diff --git a/test/Github/Tests/ResultPagerTest.php b/test/Github/Tests/ResultPagerTest.php index e8892501d44..d7d176f4b78 100644 --- a/test/Github/Tests/ResultPagerTest.php +++ b/test/Github/Tests/ResultPagerTest.php @@ -2,7 +2,6 @@ namespace Github\Tests; -use Github\Api\ApiInterface; use Github\Api\Organization\Members; use Github\Api\Search; use Github\ResultPager; @@ -11,20 +10,26 @@ use Psr\Http\Client\ClientInterface; /** - * ResultPagerTest. - * * @author Ramon de la Fuente * @author Mitchel Verschoof * @author Tobias Nyholm */ class ResultPagerTest extends \PHPUnit\Framework\TestCase { + public function provideFetchCases() + { + return [ + ['fetchAll'], + ['fetchAllLazy'], + ]; + } + /** - * @test + * @test provideFetchCases * - * description fetchAll + * @dataProvider provideFetchCases */ - public function shouldGetAllResults() + public function shouldGetAllResults(string $fetchMethod) { $amountLoops = 3; $content = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; @@ -49,7 +54,14 @@ public function shouldGetAllResults() // Run fetchAll on result paginator $paginator = new ResultPager($client); - $result = $paginator->fetchAll($memberApi, $method, $parameters); + + $result = $paginator->$fetchMethod($memberApi, $method, $parameters); + + if (is_array($result)) { + $this->assertSame('fetchAll', $fetchMethod); + } else { + $result = iterator_to_array($result); + } $this->assertCount($amountLoops * count($content), $result); } @@ -99,19 +111,21 @@ public function shouldGetAllSearchResults() public function testFetch() { $result = 'foo'; - $method = 'bar'; + $method = 'all'; $parameters = ['baz']; - $api = $this->getMockBuilder(ApiInterface::class) + $api = $this->getMockBuilder(Members::class) + ->disableOriginalConstructor() + ->setMethods(['all']) ->getMock(); + $api->expects($this->once()) + ->method('all') + ->with(...$parameters) + ->willReturn($result); $paginator = $this->getMockBuilder(ResultPager::class) ->disableOriginalConstructor() - ->setMethods(['callApi', 'postFetch']) + ->setMethods(['postFetch']) ->getMock(); - $paginator->expects($this->once()) - ->method('callApi') - ->with($api, $method, $parameters) - ->willReturn($result); $paginator->expects($this->once()) ->method('postFetch');