Records::current()   A
last analyzed

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 namespace Scriptotek\OaiPmh;
2
3
use Evenement\EventEmitter;
4
5
/**
6
 * When iterating, methods are called in the following order:
7
 * 
8
 * rewind()
9
 * valid()
10
 * current()
11
 *
12
 * next()
13
 * valid()
14
 * current()
15
 *
16
 * ...
17
 *
18
 * next()
19
 * valid()
20
 */
21
class Records extends EventEmitter implements \Iterator
22
{
23
24
    // When we no longer need to support PHP 5.3:
25
    // - Upgrade to Evenement 2.0 and use trait instead of extending
26
27
    private $from;
28
    private $until;
29
    private $set;
30
    private $client;
31
    private $lastResponse;
32
33
    private $position;
34
    private $initialPosition;
35
    private $resumptionToken;
36
    private $initialResumptionToken;
37
38
    /** @var int Total number of records in the result set. Only defined if the
39
        server returns 'completeListSize', which is optional. Even if defined, the
40
        value may still be only an estimate. */
41
    public $numberOfRecords = null;
42
43
    private $data = array();
44
45
    /**
46
     * Create a new records iterator
47
     *
48
     * @param string $from Start date
49
     * @param string $until End date
50
     * @param string $set Data set
51
     * @param Client $client OAI-PMH client reference
52
     * @param string $resumptionToken
53
     */
54
    public function __construct($from, $until, $set, Client $client, $resumptionToken = null)
55
    {
56
        $this->from = $from;
57
        $this->until = $until;
58
        $this->set = $set;
59
        $this->client = $client;
60
        $this->resumptionToken = $resumptionToken;
61
62
        $this->position = 1;
63
        $this->fetchMore();
64
65
        // Store initial position, might not be 1 if we are resuming an operation
66
        $this->initialPosition = $this->position;
67
        $this->initialResumptionToken = $resumptionToken;
68
    }
69
70
    /**
71
     * Return error message from last reponse, if any
72
     *
73
     * @return string|null
74
     */
75
    public function __get($prop)
76
    {
77
        if (in_array($prop, array('error', 'errorCode'))) {
78
            return $this->lastResponse->{$prop};
79
        }
80
    }
81
82
    /**
83
     * Return the current resumption token
84
     *
85
     * @return string|null
86
     */
87
    public function getResumptionToken()
88
    {
89
        return $this->resumptionToken;
90
    }
91
92
    /**
93
     * Return the last response object
94
     *
95
     * @return ListRecordsResponse|null
96
     */
97
    public function getLastResponse()
98
    {
99
        return $this->lastResponse;
100
    }
101
102
    /**
103
     * Fetch more records from the service
104
     */
105
    private function fetchMore()
106
    {
107
        if (is_null($this->resumptionToken)) {
108
            $args = array(
109
                'from' => $this->from,
110
                'until' => $this->until,
111
                'set' => $this->set,
112
            );
113
        } else {
114
            $args = array(
115
                'resumptionToken' => $this->resumptionToken,
116
                'metadataPrefix' => null,
117
            );
118
        }
119
120
        $attempt = 0;
121
        while (true) {
122
            $body = $this->client->request('ListRecords', $args);
123
            try {
124
                $this->lastResponse = new ListRecordsResponse($body);
125
                break;
126
            } catch (\Danmichaelo\QuiteSimpleXMLElement\InvalidXMLException $e) {
127
                $this->emit('request.error', array(
128
                    'message' => $e->getMessage(),
129
                ));
130
                $attempt++;
131
                if ($attempt > $this->client->maxRetries) {
132
                    // Give up
133
                    throw new ConnectionError('Failed to get a valid XML response from the server.', null, $e);
134
                }
135
                time_nanosleep(intval($this->client->sleepTimeOnError), intval($this->client->sleepTimeOnError * 1000000000));
136
            }
137
        }
138
139
        $this->data = $this->lastResponse->records;
140
141
        if (!is_null($this->lastResponse->numberOfRecords)) {
142
            $this->numberOfRecords = $this->lastResponse->numberOfRecords;
143
            if (!is_null($this->lastResponse->cursor)) {
144
                $this->position = $this->lastResponse->cursor + 1;
145
            }
146
        }
147
148
        if (isset($this->lastResponse->resumptionToken)) {
149
            $this->resumptionToken = $this->lastResponse->resumptionToken;
150
        } else {
151
            $this->resumptionToken = null;
152
        }
153
    }
154
155
    /**
156
     * Return the current element
157
     *
158
     * @return mixed
159
     */
160
    public function current()
161
    {
162
        return $this->data[0];
163
    }
164
165
    /**
166
     * Return the key of the current element
167
     *
168
     * @return int
169
     */
170
    public function key()
171
    {
172
        return $this->position;
173
    }
174
175
    /**
176
     * Rewind the Iterator to the first element
177
     */
178
    public function rewind()
179
    {
180
        if ($this->position != $this->initialPosition) {
181
            $this->position = $this->initialPosition;
182
            $this->resumptionToken = $this->initialResumptionToken;
183
            $this->data = array();
184
            $this->fetchMore();
185
        }
186
    }
187
188
    /**
189
     * Move forward to next element
190
     */
191
    public function next()
192
    {
193
        if (count($this->data) > 0) {
194
            array_shift($this->data);
195
        }
196
        ++$this->position;
197
198
        if (count($this->data) == 0 && !is_null($this->resumptionToken)) {
199
            $this->fetchMore();
200
        }
201
202
        if (count($this->data) == 0) {
203
            return null;
204
        }
205
    }
206
207
    /**
208
     * Check if current position is valid
209
     *
210
     * @return bool
211
     */
212
    public function valid()
213
    {
214
        return count($this->data) != 0;
215
    }
216
}
217