Page.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <?php
  2. /*
  3. * Copyright 2016 Google LLC
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions are
  8. * met:
  9. *
  10. * * Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * * Redistributions in binary form must reproduce the above
  13. * copyright notice, this list of conditions and the following disclaimer
  14. * in the documentation and/or other materials provided with the
  15. * distribution.
  16. * * Neither the name of Google Inc. nor the names of its
  17. * contributors may be used to endorse or promote products derived from
  18. * this software without specific prior written permission.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31. */
  32. namespace Google\ApiCore;
  33. use Generator;
  34. use Google\Protobuf\Internal\MapField;
  35. use Google\Protobuf\Internal\Message;
  36. use IteratorAggregate;
  37. /**
  38. * A Page object wraps an API list method response and provides methods
  39. * to retrieve additional pages using the page token.
  40. */
  41. class Page implements IteratorAggregate
  42. {
  43. const FINAL_PAGE_TOKEN = "";
  44. private $call;
  45. private $callable;
  46. private $options;
  47. private $pageStreamingDescriptor;
  48. private $pageToken; // @phpstan-ignore-line
  49. private $response;
  50. /**
  51. * Page constructor.
  52. *
  53. * @param Call $call
  54. * @param array $options
  55. * @param callable $callable
  56. * @param PageStreamingDescriptor $pageStreamingDescriptor
  57. * @param Message $response
  58. */
  59. public function __construct(
  60. Call $call,
  61. array $options,
  62. callable $callable,
  63. PageStreamingDescriptor $pageStreamingDescriptor,
  64. Message $response
  65. ) {
  66. $this->call = $call;
  67. $this->options = $options;
  68. $this->callable = $callable;
  69. $this->pageStreamingDescriptor = $pageStreamingDescriptor;
  70. $this->response = $response;
  71. $requestPageTokenGetMethod = $this->pageStreamingDescriptor->getRequestPageTokenGetMethod();
  72. $this->pageToken = $this->call->getMessage()->$requestPageTokenGetMethod();
  73. }
  74. /**
  75. * Returns true if there are more pages that can be retrieved from the
  76. * API.
  77. *
  78. * @return bool
  79. */
  80. public function hasNextPage()
  81. {
  82. return strcmp($this->getNextPageToken(), Page::FINAL_PAGE_TOKEN) != 0;
  83. }
  84. /**
  85. * Returns the next page token from the response.
  86. *
  87. * @return string
  88. */
  89. public function getNextPageToken()
  90. {
  91. $responsePageTokenGetMethod = $this->pageStreamingDescriptor->getResponsePageTokenGetMethod();
  92. return $this->getResponseObject()->$responsePageTokenGetMethod();
  93. }
  94. /**
  95. * Retrieves the next Page object using the next page token.
  96. *
  97. * @param int|null $pageSize
  98. * @throws ValidationException if there are no pages remaining, or if pageSize is supplied but
  99. * is not supported by the API
  100. * @throws ApiException if the call to fetch the next page fails.
  101. * @return Page
  102. */
  103. public function getNextPage(int $pageSize = null)
  104. {
  105. if (!$this->hasNextPage()) {
  106. throw new ValidationException(
  107. 'Could not complete getNextPage operation: ' .
  108. 'there are no more pages to retrieve.'
  109. );
  110. }
  111. $oldRequest = $this->getRequestObject();
  112. $requestClass = get_class($oldRequest);
  113. $newRequest = new $requestClass();
  114. $newRequest->mergeFrom($oldRequest);
  115. $requestPageTokenSetMethod = $this->pageStreamingDescriptor->getRequestPageTokenSetMethod();
  116. $newRequest->$requestPageTokenSetMethod($this->getNextPageToken());
  117. if (isset($pageSize)) {
  118. if (!$this->pageStreamingDescriptor->requestHasPageSizeField()) {
  119. throw new ValidationException(
  120. 'pageSize argument was defined, but the method does not ' .
  121. 'support a page size parameter in the optional array argument'
  122. );
  123. }
  124. $requestPageSizeSetMethod = $this->pageStreamingDescriptor->getRequestPageSizeSetMethod();
  125. $newRequest->$requestPageSizeSetMethod($pageSize);
  126. }
  127. $this->call = $this->call->withMessage($newRequest);
  128. $callable = $this->callable;
  129. $response = $callable(
  130. $this->call,
  131. $this->options
  132. )->wait();
  133. return new Page(
  134. $this->call,
  135. $this->options,
  136. $this->callable,
  137. $this->pageStreamingDescriptor,
  138. $response
  139. );
  140. }
  141. /**
  142. * Return the number of elements in the response.
  143. *
  144. * @return int
  145. */
  146. public function getPageElementCount()
  147. {
  148. $resourcesGetMethod = $this->pageStreamingDescriptor->getResourcesGetMethod();
  149. return count($this->getResponseObject()->$resourcesGetMethod());
  150. }
  151. /**
  152. * Return an iterator over the elements in the response.
  153. *
  154. * @return Generator
  155. */
  156. #[\ReturnTypeWillChange]
  157. public function getIterator()
  158. {
  159. $resourcesGetMethod = $this->pageStreamingDescriptor->getResourcesGetMethod();
  160. $items = $this->getResponseObject()->$resourcesGetMethod();
  161. foreach ($items as $key => $element) {
  162. if ($items instanceof MapField) {
  163. yield $key => $element;
  164. } else {
  165. yield $element;
  166. }
  167. }
  168. }
  169. /**
  170. * Return an iterator over Page objects, beginning with this object.
  171. * Additional Page objects are retrieved lazily via API calls until
  172. * all elements have been retrieved.
  173. *
  174. * @return Generator|array<Page>
  175. * @throws ValidationException
  176. * @throws ApiException
  177. */
  178. public function iteratePages()
  179. {
  180. $currentPage = $this;
  181. yield $this;
  182. while ($currentPage->hasNextPage()) {
  183. $currentPage = $currentPage->getNextPage();
  184. yield $currentPage;
  185. }
  186. }
  187. /**
  188. * Gets the request object used to generate the Page.
  189. *
  190. * @return mixed|Message
  191. */
  192. public function getRequestObject()
  193. {
  194. return $this->call->getMessage();
  195. }
  196. /**
  197. * Gets the API response object.
  198. *
  199. * @return mixed|Message
  200. */
  201. public function getResponseObject()
  202. {
  203. return $this->response;
  204. }
  205. /**
  206. * Returns a collection of elements with a fixed size set by
  207. * the collectionSize parameter. The collection will only contain
  208. * fewer than collectionSize elements if there are no more
  209. * pages to be retrieved from the server.
  210. *
  211. * NOTE: it is an error to call this method if an optional parameter
  212. * to set the page size is not supported or has not been set in the
  213. * API call that was used to create this page. It is also an error
  214. * if the collectionSize parameter is less than the page size that
  215. * has been set.
  216. *
  217. * @param int $collectionSize
  218. * @throws ValidationException if a FixedSizeCollection of the specified size cannot be constructed
  219. * @return FixedSizeCollection
  220. */
  221. public function expandToFixedSizeCollection($collectionSize)
  222. {
  223. if (!$this->pageStreamingDescriptor->requestHasPageSizeField()) {
  224. throw new ValidationException(
  225. "FixedSizeCollection is not supported for this method, because " .
  226. "the method does not support an optional argument to set the " .
  227. "page size."
  228. );
  229. }
  230. $request = $this->getRequestObject();
  231. $pageSizeGetMethod = $this->pageStreamingDescriptor->getRequestPageSizeGetMethod();
  232. $pageSize = $request->$pageSizeGetMethod();
  233. if (is_null($pageSize)) {
  234. throw new ValidationException(
  235. "Error while expanding Page to FixedSizeCollection: No page size " .
  236. "parameter found. The page size parameter must be set in the API " .
  237. "optional arguments array, and must be less than the collectionSize " .
  238. "parameter, in order to create a FixedSizeCollection object."
  239. );
  240. }
  241. if ($pageSize > $collectionSize) {
  242. throw new ValidationException(
  243. "Error while expanding Page to FixedSizeCollection: collectionSize " .
  244. "parameter is less than the page size optional argument specified in " .
  245. "the API call. collectionSize: $collectionSize, page size: $pageSize"
  246. );
  247. }
  248. return new FixedSizeCollection($this, $collectionSize);
  249. }
  250. }