1 | <?php |
||
33 | class RecordIterator implements Iterator, RecordIteratorInterface |
||
34 | { |
||
35 | /** |
||
36 | * @var Client |
||
37 | */ |
||
38 | private $oaipmhClient; |
||
39 | |||
40 | /** |
||
41 | * @var string The verb to use |
||
42 | */ |
||
43 | private $verb; |
||
44 | |||
45 | /** |
||
46 | * @var array OAI-PMH parameters passed as part of the request |
||
47 | */ |
||
48 | private $params; |
||
49 | |||
50 | /** |
||
51 | * @var int The number of total entities (if available) |
||
52 | */ |
||
53 | private $totalRecordsInCollection; |
||
54 | |||
55 | /** |
||
56 | * @var DateTimeInterface RecordSet expiration date (if specified) |
||
57 | */ |
||
58 | private $expireDate; |
||
59 | |||
60 | /** |
||
61 | * @var string The resumption token |
||
62 | */ |
||
63 | private $resumptionToken; |
||
64 | |||
65 | /** |
||
66 | * @var array Array of records |
||
67 | */ |
||
68 | private $batch; |
||
69 | |||
70 | /** |
||
71 | * @var int Total number of records processed |
||
72 | */ |
||
73 | private $numProcessed = 0; |
||
74 | |||
75 | /** |
||
76 | * @var boolean Total number of requests made |
||
77 | */ |
||
78 | private $numRequests = 0; |
||
79 | |||
80 | /** |
||
81 | * @var SimpleXMLElement|null Used for tracking the iterator |
||
82 | */ |
||
83 | private $currItem; |
||
84 | |||
85 | /** |
||
86 | * Constructor |
||
87 | * |
||
88 | * @param ClientInterface $client The client to use |
||
89 | * @param string $verb The verb to use when retrieving results from the client |
||
90 | * @param array $params Optional parameters passed to OAI-PMH |
||
91 | * @param string|null $resumptionToken Resumption token, if one exists |
||
92 | */ |
||
93 | public function __construct(ClientInterface $client, $verb, array $params = [], $resumptionToken = null) |
||
94 | { |
||
95 | //Set parameters |
||
96 | $this->oaipmhClient = $client; |
||
|
|||
97 | $this->verb = $verb; |
||
98 | $this->params = $params; |
||
99 | $this->resumptionToken = $resumptionToken; |
||
100 | |||
101 | //Node name error? |
||
102 | if (! $this->getItemNodeName()) { |
||
103 | throw new BaseOaipmhException('Cannot determine item name for verb: ' . $this->verb); |
||
104 | } |
||
105 | } |
||
106 | |||
107 | /** |
||
108 | * @return ClientInterface |
||
109 | */ |
||
110 | public function getClient() |
||
111 | { |
||
112 | return $this->oaipmhClient; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Get the total number of requests made during this run |
||
117 | * |
||
118 | * @return int The number of HTTP requests made |
||
119 | */ |
||
120 | public function getNumRequests() |
||
121 | { |
||
122 | return $this->numRequests; |
||
123 | } |
||
124 | |||
125 | /** |
||
126 | * Get the total number of records processed during this run |
||
127 | * |
||
128 | * @return int The number of records processed |
||
129 | */ |
||
130 | public function getNumRetrieved() |
||
131 | { |
||
132 | return $this->numProcessed; |
||
133 | } |
||
134 | |||
135 | |||
136 | /** |
||
137 | * Get the resumption token if it is specified |
||
138 | * |
||
139 | * @return null|string |
||
140 | */ |
||
141 | public function getResumptionToken() |
||
142 | { |
||
143 | return $this->resumptionToken; |
||
144 | } |
||
145 | |||
146 | /** |
||
147 | * @return DateTimeInterface|null |
||
148 | */ |
||
149 | public function getExpirationDate() |
||
150 | { |
||
151 | return $this->expireDate; |
||
152 | } |
||
153 | |||
154 | /** |
||
155 | * Get the total number of records in the collection if available |
||
156 | * |
||
157 | * This only returns a value if the OAI-PMH server provides this information |
||
158 | * in the response, which not all servers do (it is optional in the OAI-PMH spec) |
||
159 | * |
||
160 | * Also, the number of records may change during the requests, so it should |
||
161 | * be treated as an estimate |
||
162 | * |
||
163 | * @return int|null |
||
164 | */ |
||
165 | public function getTotalRecordCount() |
||
166 | { |
||
167 | if ($this->currItem === null) { |
||
168 | $this->next(); |
||
169 | } |
||
170 | |||
171 | return $this->totalRecordsInCollection; |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Get the next item |
||
176 | * |
||
177 | * Return an item from the currently-retrieved batch, get next batch and |
||
178 | * return first record from it, or return false if no more records |
||
179 | * |
||
180 | * @return SimpleXMLElement|bool |
||
181 | */ |
||
182 | public function nextItem() |
||
183 | { |
||
184 | if ($this->batch === null) { |
||
185 | $this->batch = []; |
||
186 | } |
||
187 | |||
188 | //If no items in batch, and we have a resumptionToken or need to make initial request... |
||
189 | if (count($this->batch) == 0 && ($this->resumptionToken or $this->numRequests == 0)) { |
||
190 | $this->retrieveNextBatch(); |
||
191 | } |
||
192 | |||
193 | //if still items in current batch, return one |
||
194 | if (count($this->batch) > 0) { |
||
195 | $this->numProcessed++; |
||
196 | |||
197 | $item = array_shift($this->batch); |
||
198 | $this->currItem = clone $item; |
||
199 | } else { |
||
200 | $this->currItem = false; |
||
201 | } |
||
202 | |||
203 | return $this->currItem; |
||
204 | } |
||
205 | |||
206 | /** |
||
207 | * Do a request to get the next batch of items |
||
208 | * |
||
209 | * @return int The number of items in the batch after the retrieve |
||
210 | */ |
||
211 | public function retrieveNextBatch() |
||
212 | { |
||
213 | // Set OAI-PMH parameters for request |
||
214 | // If resumptionToken, then we ignore params and just use that |
||
215 | $params = ($this->resumptionToken) |
||
216 | ? ['resumptionToken' => $this->resumptionToken] |
||
217 | : $this->params; |
||
218 | |||
219 | // Node name and verb |
||
220 | $nodeName = $this->getItemNodeName(); |
||
221 | $verb = $this->verb; |
||
222 | |||
223 | //Do it.. |
||
224 | $resp = $this->oaipmhClient->request($verb, $params); |
||
225 | $this->numRequests++; |
||
226 | |||
227 | //Result format error? |
||
228 | if (! isset($resp->$verb->$nodeName)) { |
||
229 | throw new MalformedResponseException(sprintf( |
||
230 | "Expected XML element list '%s' missing for verb '%s'", |
||
231 | $nodeName, |
||
232 | $verb |
||
233 | )); |
||
234 | } |
||
235 | |||
236 | //Set the resumption token and expiration date, if specified in the response |
||
237 | if (isset($resp->$verb->resumptionToken)) { |
||
238 | $this->resumptionToken = (string) $resp->$verb->resumptionToken; |
||
239 | |||
240 | if (isset($resp->$verb->resumptionToken['completeListSize'])) { |
||
241 | $this->totalRecordsInCollection = (int) $resp->$verb->resumptionToken['completeListSize']; |
||
242 | } |
||
243 | if (isset($resp->$verb->resumptionToken['expirationDate'])) { |
||
244 | $t = $resp->$verb->resumptionToken['expirationDate']; |
||
245 | $this->expireDate = DateTime::createFromFormat(DateTime::ISO8601, $t); |
||
246 | } |
||
247 | } else { |
||
248 | //Unset the resumption token when we're at the end of the list |
||
249 | $this->resumptionToken = null; |
||
250 | } |
||
251 | |||
252 | //Process the results |
||
253 | foreach ($resp->$verb->$nodeName as $node) { |
||
254 | $this->batch[] = $node; |
||
255 | } |
||
256 | |||
257 | // If the entire set was sent in one request, store the total count. |
||
258 | if ($this->numRequests == 1 && $this->resumptionToken === null) { |
||
259 | $this->totalRecordsInCollection = count($this->batch); |
||
260 | } |
||
261 | |||
262 | //Return a count |
||
263 | return count($this->batch); |
||
264 | } |
||
265 | |||
266 | /** |
||
267 | * Get Item Node Name |
||
268 | * |
||
269 | * Map the item node name based on the verb |
||
270 | * |
||
271 | * @return string|boolean The element name for the mapping, or false if unmapped |
||
272 | */ |
||
273 | private function getItemNodeName() |
||
274 | { |
||
275 | $mappings = array( |
||
276 | 'ListMetadataFormats' => 'metadataFormat', |
||
277 | 'ListSets' => 'set', |
||
278 | 'ListIdentifiers' => 'header', |
||
279 | 'ListRecords' => 'record' |
||
280 | ); |
||
281 | |||
282 | return (isset($mappings[$this->verb])) ? $mappings[$this->verb] : false; |
||
283 | } |
||
284 | |||
285 | // ---------------------------------------------------------------- |
||
286 | // Leaky abstraction methods |
||
287 | |||
288 | /** |
||
289 | * Get the current batch of records retrieved |
||
290 | * |
||
291 | * @return array|SimpleXMLElement[] |
||
292 | */ |
||
293 | public function getBatch() |
||
294 | { |
||
295 | return $this->batch; |
||
296 | } |
||
297 | |||
298 | /** |
||
299 | * Reset the request state |
||
300 | * @param bool $resetResumptionToken |
||
301 | */ |
||
302 | public function reset($resetResumptionToken = true) |
||
303 | { |
||
304 | $this->numRequests = 0; |
||
305 | $this->numProcessed = 0; |
||
306 | |||
307 | $this->currItem = null; |
||
308 | $this->totalRecordsInCollection = null; |
||
309 | $this->expireDate = null; |
||
310 | |||
311 | if ($resetResumptionToken) { |
||
312 | $this->resumptionToken = null; |
||
313 | } |
||
314 | |||
315 | $this->batch = []; |
||
316 | } |
||
317 | |||
318 | // ---------------------------------------------------------------- |
||
319 | // Iterator methods |
||
320 | |||
321 | public function current() |
||
327 | |||
328 | public function next() |
||
332 | |||
333 | public function key() |
||
341 | |||
342 | public function valid() |
||
346 | |||
347 | public function rewind() |
||
351 | } |
||
352 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.
Either this assignment is in error or an instanceof check should be added for that assignment.