1<?php
2
3/**
4 * This file is part of the FreeDSx LDAP package.
5 *
6 * (c) Chad Sikorra <Chad.Sikorra@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace FreeDSx\Ldap\Server\Paging;
13
14use DateTime;
15use DateTimeInterface;
16use FreeDSx\Ldap\Control\ControlBag;
17use FreeDSx\Ldap\Control\PagingControl;
18use FreeDSx\Ldap\Operation\Request\SearchRequest;
19
20/**
21 * Encapsulates the paging request from a client and provides some helpful methods.
22 *
23 * @author Chad Sikorra <Chad.Sikorra@gmail.com>
24 */
25final class PagingRequest
26{
27    /**
28     * @var PagingControl
29     */
30    private $control;
31
32    /**
33     * @var SearchRequest
34     */
35    private $request;
36
37    /**
38     * @var ControlBag
39     */
40    private $controls;
41
42    /**
43     * @var int
44     */
45    private $iteration = 1;
46
47    /**
48     * @var string
49     */
50    private $nextCookie;
51
52    /**
53     * @var DateTimeInterface|null
54     */
55    private $lastProcessed = null;
56
57    /**
58     * @var DateTimeInterface
59     */
60    private $created;
61
62    /**
63     * @var string
64     */
65    private $uniqueId;
66
67    /**
68     * @var bool
69     */
70    private $hasProcessed = false;
71
72    public function __construct(
73        PagingControl $control,
74        SearchRequest $request,
75        ControlBag $controls,
76        string $nextCookie,
77        ?DateTimeInterface $created = null,
78        ?string $uniqueId = null
79    ) {
80        $this->control = $control;
81        $this->request = $request;
82        $this->controls = $controls;
83        $this->nextCookie = $nextCookie;
84        $this->created = $created ?? new DateTime();
85        $this->uniqueId = $uniqueId ?? random_bytes(16);
86    }
87
88    /**
89     * An opaque value that uniquely identifies this paging request across its lifecycle.
90     *
91     * @return string
92     */
93    public function getUniqueId(): string
94    {
95        return $this->uniqueId;
96    }
97
98    /**
99     * Whether the paging control is considered critical or not.
100     *
101     * @return bool
102     */
103    public function isCritical(): bool
104    {
105        return $this->control->getCriticality();
106    }
107
108    /**
109     * When the paging request was originally created.
110     *
111     * @return DateTimeInterface
112     */
113    public function createdAt(): DateTimeInterface
114    {
115        return $this->created;
116    }
117
118    /**
119     * When the request was last processed. May be null if not processed yet.
120     *
121     * @return DateTimeInterface|null
122     */
123    public function lastProcessedAt(): ?DateTimeInterface
124    {
125        return $this->lastProcessed;
126    }
127
128    /**
129     * @return SearchRequest
130     */
131    public function getSearchRequest(): SearchRequest
132    {
133        return $this->request;
134    }
135
136    /**
137     * @return ControlBag
138     */
139    public function controls(): ControlBag
140    {
141        return $this->controls;
142    }
143
144    /**
145     * @return string
146     */
147    public function getCookie(): string
148    {
149        return $this->control->getCookie();
150    }
151
152    /**
153     * The current iteration of paging that this request represents. Incremented by one for each request.
154     *
155     * @return int
156     */
157    public function getIteration(): int
158    {
159        return $this->iteration;
160    }
161
162    /**
163     * The size of the result set to return, as requested by the client.
164     *
165     * @return int
166     */
167    public function getSize(): int
168    {
169        return $this->control->getSize();
170    }
171
172    /**
173     * Whether this represents a request to abandon the paging request.
174     *
175     * @return bool
176     */
177    public function isAbandonRequest(): bool
178    {
179        return $this->control->getSize() === 0
180            && $this->control->getCookie() !== '';
181    }
182
183    /**
184     * Whether this represents the start of an unprocessed paging request.
185     *
186     * @return bool
187     */
188    public function isPagingStart(): bool
189    {
190        if ($this->hasProcessed) {
191            return false;
192        }
193
194        return $this->control->getCookie() === ''
195            && $this->control->getSize() >= 0;
196    }
197
198    /**
199     * @return string
200     * @internal
201     */
202    public function getNextCookie(): string
203    {
204        return $this->nextCookie;
205    }
206
207    /**
208     * @param PagingControl $pagingControl
209     * @internal
210     */
211    public function updatePagingControl(PagingControl $pagingControl): void
212    {
213        $this->control = $pagingControl;
214    }
215
216    /**
217     * @param string $cookie
218     * @internal
219     */
220    public function updateNextCookie(string $cookie): void
221    {
222        $this->nextCookie = $cookie;
223    }
224
225    /**
226     * @internal
227     */
228    public function markProcessed(): void
229    {
230        $this->lastProcessed = new DateTime();
231        $this->iteration++;
232        $this->hasProcessed = true;
233    }
234}
235