Completed
Push — master ( 359f1d...544858 )
by Casey
01:25
created

RecordIterator::getExpirationDate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/**
4
 * PHPOAIPMH Library
5
 *
6
 * @license http://opensource.org/licenses/MIT
7
 * @link https://github.com/caseyamcl/phpoaipmh
8
 * @version 2.0
9
 * @package caseyamcl/phpoaipmh
10
 * @author Casey McLaughlin <[email protected]>
11
 *
12
 * For the full copyright and license information, please view the LICENSE
13
 * file that was distributed with this source code.
14
 *
15
 * ------------------------------------------------------------------
16
 */
17
18
namespace Phpoaipmh;
19
20
use Phpoaipmh\Exception\BaseOaipmhException;
21
use Phpoaipmh\Exception\MalformedResponseException;
22
23
/**
24
 * Response List Entity iterates over records returned from an OAI-PMH Endpoint
25
 *
26
 * @author Casey McLaughlin <[email protected]>
27
 * @since v2.0
28
 */
29
class RecordIterator implements \Iterator, RecordIteratorInterface
30
{
31
    /**
32
     * @var Client
33
     */
34
    private $oaipmhClient;
35
36
    /**
37
     * @var string  The verb to use
38
     */
39
    private $verb;
40
41
    /**
42
     * @var array  OAI-PMH parameters passed as part of the request
43
     */
44
    private $params;
45
46
    /**
47
     * @var int  The number of total entities (if available)
48
     */
49
    private $totalRecordsInCollection;
50
51
    /**
52
     * @var \DateTime  Recordset expiration date (if specified)
53
     */
54
    private $expireDate;
55
56
    /**
57
     * @var string  The resumption token
58
     */
59
    private $resumptionToken;
60
61
    /**
62
     * @var array  Array of records
63
     */
64
    private $batch;
65
66
    /**
67
     * @var int Total number of records processed
68
     */
69
    private $numProcessed = 0;
70
71
    /**
72
     * @var boolean  Total number of requests made
73
     */
74
    private $numRequests = 0;
75
76
    /**
77
     * @var \SimpleXMLElement|null  Used for tracking the iterator
78
     */
79
    private $currItem;
80
81
    /**
82
     * Constructor
83
     *
84
     * @param ClientInterface $client  The client to use
85
     * @param string          $verb    The verb to use when retrieving results from the client
86
     * @param array           $params  Optional parameters passed to OAI-PMH
87
     */
88
    public function __construct(ClientInterface $client, $verb, array $params = array(), $resumptionToken = null)
89
    {
90
        //Set parameters
91
        $this->oaipmhClient     = $client;
0 ignored issues
show
Documentation Bug introduced by
$client is of type object<Phpoaipmh\ClientInterface>, but the property $oaipmhClient was declared to be of type object<Phpoaipmh\Client>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

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.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
92
        $this->verb             = $verb;
93
        $this->params           = $params;
94
        $this->resumptionToken  = $resumptionToken;
95
96
        //Node name error?
97
        if (! $this->getItemNodeName()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->getItemNodeName() of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
98
            throw new BaseOaipmhException('Cannot determine item name for verb: ' . $this->verb);
99
        }
100
    }
101
102
    /**
103
     * Get the total number of requests made during this run
104
     *
105
     * @return int The number of HTTP requests made
106
     */
107
    public function getNumRequests()
108
    {
109
        return $this->numRequests;
110
    }
111
112
    /**
113
     * Get the total number of records processed during this run
114
     *
115
     * @return int The number of records processed
116
     */
117
    public function getNumRetrieved()
118
    {
119
        return $this->numProcessed;
120
    }
121
122
123
    /**
124
     * Get the resumption token if it is specified
125
     *
126
     * @return null|string
127
     */
128
    public function getResumptionToken()
129
    {
130
        return $this->resumptionToken;
131
    }
132
133
    /**
134
     * @return \DateTime|null
135
     */
136
    public function getExpirationDate()
137
    {
138
        return $this->expireDate;
139
    }
140
141
    /**
142
     * Get the total number of records in the collection if available
143
     *
144
     * This only returns a value if the OAI-PMH server provides this information
145
     * in the response, which not all servers do (it is optional in the OAI-PMH spec)
146
     *
147
     * Also, the number of records may change during the requests, so it should
148
     * be treated as an estimate
149
     *
150
     * @return int|null
151
     * @deprecated Use `countTotalRecords()`
152
     */
153
    public function getTotalRecordsInCollection()
154
    {
155
        return $this->getTotalRecordCount();
156
    }
157
158
    /**
159
     * Get the total number of records in the collection if available
160
     *
161
     * This only returns a value if the OAI-PMH server provides this information
162
     * in the response, which not all servers do (it is optional in the OAI-PMH spec)
163
     *
164
     * Also, the number of records may change during the requests, so it should
165
     * be treated as an estimate
166
     *
167
     * @return int|null
168
     */
169
    public function getTotalRecordCount()
170
    {
171
        if ($this->currItem === null) {
172
            $this->next();
173
        }
174
175
        return $this->totalRecordsInCollection;
176
    }
177
178
    /**
179
     * Get the next item
180
     *
181
     * Return an item from the currently-retrieved batch, get next batch and
182
     * return first record from it, or return false if no more records
183
     *
184
     * @return \SimpleXMLElement|bool
185
     */
186
    public function nextItem()
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->retrieveBatch();
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;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type object<SimpleXMLElement>|null of property $currItem.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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
    private function retrieveBatch()
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("Expected XML element list '%s' missing for verb '%s'", $nodeName, $verb));
230
        }
231
232
        //Set the resumption token and expiration date, if specified in the response
233
        if (isset($resp->$verb->resumptionToken)) {
234
            $this->resumptionToken = (string) $resp->$verb->resumptionToken;
235
236
            if (isset($resp->$verb->resumptionToken['completeListSize'])) {
237
                $this->totalRecordsInCollection = (int) $resp->$verb->resumptionToken['completeListSize'];
238
            }
239
            if (isset($resp->$verb->resumptionToken['expirationDate'])) {
240
                $t = $resp->$verb->resumptionToken['expirationDate'];
241
                $this->expireDate = \DateTime::createFromFormat(\DateTime::ISO8601, $t);
0 ignored issues
show
Documentation Bug introduced by
It seems like \DateTime::createFromFor...\DateTime::ISO8601, $t) can also be of type false. However, the property $expireDate is declared as type object<DateTime>. Maybe add an additional type check?

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 mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
242
            }
243
        } else {
244
            //Unset the resumption token when we're at the end of the list
245
            $this->resumptionToken = null;
246
        }
247
248
        //Process the results
249
        foreach ($resp->$verb->$nodeName as $node) {
250
            $this->batch[] = $node;
251
        }
252
253
        //Return a count
254
        return count($this->batch);
255
    }
256
257
    /**
258
     * Get Item Node Name
259
     *
260
     * Map the item node name based on the verb
261
     *
262
     * @return string|boolean The element name for the mapping, or false if unmapped
263
     */
264
    private function getItemNodeName()
265
    {
266
        $mappings = array(
267
            'ListMetadataFormats' => 'metadataFormat',
268
            'ListSets'            => 'set',
269
            'ListIdentifiers'     => 'header',
270
            'ListRecords'         => 'record'
271
        );
272
273
        return (isset($mappings[$this->verb])) ? $mappings[$this->verb] : false;
274
    }
275
276
    // ----------------------------------------------------------------
277
    // Leaky abstraction methods
278
279
    /**
280
     * Get the current batch of records retrieved
281
     *
282
     * @return array|\SimpleXMLElement[]
283
     */
284
    public function getBatch()
285
    {
286
        return $this->batch;
287
    }
288
289
    /**
290
     * Reset the request state
291
     */
292
    public function reset()
293
    {
294
        $this->numRequests  = 0;
0 ignored issues
show
Documentation Bug introduced by
The property $numRequests was declared of type boolean, but 0 is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
295
        $this->numProcessed = 0;
296
297
        $this->currItem                 = null;
298
        $this->resumptionToken          = null;
299
        $this->totalRecordsInCollection = null;
300
        $this->expireDate               = null;
301
302
        $this->batch = [];
303
    }
304
305
    // ----------------------------------------------------------------
306
    // Iterator methods
307
308
    public function current()
309
    {
310
        return ($this->currItem === null)
311
            ? $this->nextItem()
312
            : $this->currItem;
313
    }
314
315
    public function next()
316
    {
317
        return $this->nextItem();
318
    }
319
320
    public function key()
321
    {
322
        if ($this->currItem === null) {
323
            $this->nextItem();
324
        }
325
326
        return $this->getNumRetrieved();
327
    }
328
329
    public function valid()
330
    {
331
        return ($this->currItem !== false);
332
    }
333
334
    public function rewind()
335
    {
336
        $this->reset();
337
    }
338
}
339