|  | 
|  | 1 | +<?php | 
|  | 2 | + | 
|  | 3 | +namespace Microsoft\Graph\Core\Tasks; | 
|  | 4 | + | 
|  | 5 | +use Exception; | 
|  | 6 | +use Http\Promise\FulfilledPromise; | 
|  | 7 | +use Http\Promise\Promise; | 
|  | 8 | +use Http\Promise\RejectedPromise; | 
|  | 9 | +use InvalidArgumentException; | 
|  | 10 | +use JsonException; | 
|  | 11 | +use Microsoft\Graph\Core\Models\PageResult; | 
|  | 12 | +use Microsoft\Kiota\Abstractions\HttpMethod; | 
|  | 13 | +use Microsoft\Kiota\Abstractions\NativeResponseHandler; | 
|  | 14 | +use Microsoft\Kiota\Abstractions\RequestAdapter; | 
|  | 15 | +use Microsoft\Kiota\Abstractions\RequestInformation; | 
|  | 16 | +use Microsoft\Kiota\Abstractions\RequestOption; | 
|  | 17 | +use Microsoft\Kiota\Abstractions\Serialization\Parsable; | 
|  | 18 | + | 
|  | 19 | +class PageIterator | 
|  | 20 | +{ | 
|  | 21 | +    private PageResult $currentPage; | 
|  | 22 | +    private RequestAdapter $requestAdapter; | 
|  | 23 | +    private bool $hasNext = false; | 
|  | 24 | +    private int $pauseIndex; | 
|  | 25 | +    /** @var array{string, string} $constructorFunc */ | 
|  | 26 | +    private array $constructorCallable; | 
|  | 27 | +    private array $headers = []; | 
|  | 28 | +    /** @var array<RequestOption>|null  */ | 
|  | 29 | +    private ?array $requestOptions = []; | 
|  | 30 | + | 
|  | 31 | +    /** | 
|  | 32 | +     * @param Parsable|array|object $response paged collection response | 
|  | 33 | +     * @param RequestAdapter $requestAdapter | 
|  | 34 | +     * @param array{string,string} $constructorCallable The method to construct a paged response object. | 
|  | 35 | +     * @throws JsonException | 
|  | 36 | +     */ | 
|  | 37 | +    public function __construct($response, RequestAdapter $requestAdapter, array $constructorCallable) { | 
|  | 38 | +        $this->requestAdapter = $requestAdapter; | 
|  | 39 | +        $this->constructorCallable = $constructorCallable; | 
|  | 40 | +        $this->pauseIndex = 0; | 
|  | 41 | +        $page = self::convertToPage($response); | 
|  | 42 | + | 
|  | 43 | +        if ($page !== null) { | 
|  | 44 | +            $this->currentPage = $page; | 
|  | 45 | +            $this->hasNext = true; | 
|  | 46 | +        } | 
|  | 47 | +        $this->headers = []; | 
|  | 48 | +    } | 
|  | 49 | + | 
|  | 50 | +    /** | 
|  | 51 | +     * @param array $headers | 
|  | 52 | +     */ | 
|  | 53 | +    public function setHeaders(array $headers): void | 
|  | 54 | +    { | 
|  | 55 | +        $this->headers = $headers; | 
|  | 56 | +    } | 
|  | 57 | + | 
|  | 58 | +    /** | 
|  | 59 | +     * @param array $requestOptions | 
|  | 60 | +     */ | 
|  | 61 | +    public function setRequestOptions(array $requestOptions): void | 
|  | 62 | +    { | 
|  | 63 | +        $this->requestOptions = $requestOptions; | 
|  | 64 | +    } | 
|  | 65 | + | 
|  | 66 | +    /** | 
|  | 67 | +     * @param int $pauseIndex | 
|  | 68 | +     */ | 
|  | 69 | +    public function setPauseIndex(int $pauseIndex): void | 
|  | 70 | +    { | 
|  | 71 | +        $this->pauseIndex = $pauseIndex; | 
|  | 72 | +    } | 
|  | 73 | + | 
|  | 74 | +    /** | 
|  | 75 | +     * @param callable(Parsable|array|object): bool $callback The callback function to apply on every entity. Pauses iteration if false is returned | 
|  | 76 | +     * @throws Exception | 
|  | 77 | +     */ | 
|  | 78 | +    public function iterate(callable $callback): void { | 
|  | 79 | +        while(true) { | 
|  | 80 | +            $keepIterating = $this->enumerate($callback); | 
|  | 81 | + | 
|  | 82 | +            if (!$keepIterating) { | 
|  | 83 | +                return; | 
|  | 84 | +            } | 
|  | 85 | +            $nextPage = $this->next(); | 
|  | 86 | + | 
|  | 87 | +            if (empty($nextPage)) { | 
|  | 88 | +                $this->hasNext = false; | 
|  | 89 | +                return; | 
|  | 90 | +            } | 
|  | 91 | +            $this->currentPage = $nextPage; | 
|  | 92 | +            $this->pauseIndex = 0; | 
|  | 93 | +        } | 
|  | 94 | +    } | 
|  | 95 | + | 
|  | 96 | +    /** | 
|  | 97 | +     * @throws Exception | 
|  | 98 | +     */ | 
|  | 99 | +    public function next(): ?PageResult { | 
|  | 100 | +        if (empty($this->currentPage->getOdataNextLink())) { | 
|  | 101 | +            return null; | 
|  | 102 | +        } | 
|  | 103 | + | 
|  | 104 | +        $response = $this->fetchNextPage(); | 
|  | 105 | +        $result = $response->wait(); | 
|  | 106 | +        return self::convertToPage($result); | 
|  | 107 | +    } | 
|  | 108 | + | 
|  | 109 | +    /** | 
|  | 110 | +     * @param $response | 
|  | 111 | +     * @return PageResult|null | 
|  | 112 | +     * @throws JsonException | 
|  | 113 | +     */ | 
|  | 114 | +    public static function convertToPage($response): ?PageResult { | 
|  | 115 | +        $page = new PageResult(); | 
|  | 116 | +        if ($response === null) { | 
|  | 117 | +            throw new InvalidArgumentException('$response cannot be null'); | 
|  | 118 | +        } | 
|  | 119 | + | 
|  | 120 | +        if (is_array($response)) { | 
|  | 121 | +            $value = $response['value']; | 
|  | 122 | +        } else if(is_a($response, Parsable::class) && method_exists($response, 'getValue')) { | 
|  | 123 | +            $value = $response->getValue(); | 
|  | 124 | +        } else { | 
|  | 125 | +            $value = $response->value; | 
|  | 126 | +        } | 
|  | 127 | + | 
|  | 128 | +        if ($value === null) { | 
|  | 129 | +            throw new InvalidArgumentException('The response does not contain a value.'); | 
|  | 130 | +        } | 
|  | 131 | + | 
|  | 132 | +        $parsablePage =  is_a($response, Parsable::class) ? $response : json_decode(json_encode($response,JSON_THROW_ON_ERROR), true); | 
|  | 133 | +        if (is_array($parsablePage)) { | 
|  | 134 | +            $page->setOdataNextLink($parsablePage['@odata.nextLink'] ?? ''); | 
|  | 135 | +        } else { | 
|  | 136 | +            $page->setOdataNextLink($parsablePage->getOdataNextLink()); | 
|  | 137 | +        } | 
|  | 138 | +        $page->setValue($value); | 
|  | 139 | +        return $page; | 
|  | 140 | +    } | 
|  | 141 | +    private function fetchNextPage(): ?Promise { | 
|  | 142 | +        /** @var Parsable $graphResponse */ | 
|  | 143 | +        $graphResponse = null; | 
|  | 144 | + | 
|  | 145 | +        $nextLink = $this->currentPage->getOdataNextLink(); | 
|  | 146 | + | 
|  | 147 | +        if ($nextLink === null) { | 
|  | 148 | +            return new RejectedPromise(new InvalidArgumentException('The response does not have a nextLink')); | 
|  | 149 | +        } | 
|  | 150 | + | 
|  | 151 | +        if (!filter_var($nextLink, FILTER_VALIDATE_URL)) { | 
|  | 152 | +            throw new InvalidArgumentException('Could not parse the nextLink url.'); | 
|  | 153 | +        } | 
|  | 154 | + | 
|  | 155 | +        $requestInfo = new RequestInformation(); | 
|  | 156 | +        $requestInfo->httpMethod = HttpMethod::GET; | 
|  | 157 | +        $requestInfo->setUri($nextLink); | 
|  | 158 | +        $requestInfo->headers = $this->headers; | 
|  | 159 | +        if ($this->requestOptions !== null) { | 
|  | 160 | +            $requestInfo->addRequestOptions(...$this->requestOptions); | 
|  | 161 | +        } | 
|  | 162 | + | 
|  | 163 | +        return $this->requestAdapter->sendAsync($requestInfo, $this->constructorCallable); | 
|  | 164 | +    } | 
|  | 165 | + | 
|  | 166 | +    public function enumerate(?callable $callback): ?bool { | 
|  | 167 | +        $keepIterating = true; | 
|  | 168 | + | 
|  | 169 | +        $pageItems = $this->currentPage->getValue(); | 
|  | 170 | +        if (empty($pageItems)) { | 
|  | 171 | +            return false; | 
|  | 172 | +        } | 
|  | 173 | +        for ($i = $this->pauseIndex; $i < count($pageItems); $i++){ | 
|  | 174 | +            $keepIterating = $callback($pageItems[$i]); | 
|  | 175 | + | 
|  | 176 | +             if (!$keepIterating) { | 
|  | 177 | +                 $this->pauseIndex = $i + 1; | 
|  | 178 | +                 break; | 
|  | 179 | +             } | 
|  | 180 | +        } | 
|  | 181 | +        return $keepIterating; | 
|  | 182 | +    } | 
|  | 183 | + | 
|  | 184 | +    public function hasNext(): bool { | 
|  | 185 | +        return $this->hasNext; | 
|  | 186 | +    } | 
|  | 187 | +} | 
0 commit comments