Completed
Pull Request — master (#247)
by Sergey
02:34
created

Pagination::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace seregazhuk\PinterestBot\Helpers;
4
5
use Traversable;
6
use EmptyIterator;
7
use IteratorAggregate;
8
use seregazhuk\PinterestBot\Api\Contracts\PaginatedResponse;
9
10
11
/**
12
 * Class Pagination
13
 * Iterate through results of Pinterest Api. By default iterator will return 50 first
14
 * pagination results. To change this behaviour specify another limit as the
15
 * constructor param. For no limits specify zero.
16
 *
17
 * @package seregazhuk\PinterestBot\Helpers
18
 */
19
class Pagination implements IteratorAggregate
20
{
21
    const DEFAULT_LIMIT = 50;
22
23
    /**
24
     * @var int
25
     */
26
    protected $limit;
27
28
    /**
29
     * @var callable
30
     */
31
    protected $callback;
32
33
    /**
34
     * @var int
35
     */
36
    protected $offset;
37
38
    /**
39
     * @var int
40
     */
41
    protected $resultsNum;
42
43
    /**
44
     * @var int
45
     */
46
    protected $processed;
47
48
    /**
49
     * @param int $limit
50
     */
51
    public function __construct($limit = self::DEFAULT_LIMIT)
52
    {
53
        $this->limit = $limit;
54
    }
55
56
    /**
57
     * Sets a callback to make requests. Should be a closure.
58
     *
59
     * @param callable $callback
60
     * @return $this
61
     */
62
    public function paginateOver(callable $callback)
63
    {
64
        $this->callback = $callback;
65
66
        return $this;
67
    }
68
69
    /**
70
     * Syntax sugar for getIterator method
71
     * @return Traversable
72
     */
73
    public function get()
74
    {
75
        return $this->getIterator();
76
    }
77
78
    /**
79
     * Retrieve an external iterator
80
     * @return Traversable
81
     */
82
    public function getIterator()
83
    {
84
        if(empty($this->callback)) return new EmptyIterator();
85
86
        return $this->processCallback();
87
    }
88
89
    /**
90
     * @param int $offset
91
     * @return $this
92
     */
93
    public function skip($offset)
94
    {
95
        $this->offset = $offset;
96
97
        return $this;
98
    }
99
100
    /**
101
     * @param int $limit
102
     * @return $this
103
     */
104
    public function take($limit)
105
    {
106
        $this->limit = $limit;
107
108
        return $this;
109
    }
110
111
    /**
112
     * @return array
113
     */
114
    public function toArray()
115
    {
116
        return iterator_to_array($this->getIterator());
117
    }
118
119
120
    /**
121
     * Check if we execGet results limit in pagination.
122
     *
123
     * @param int $resultsNum
124
     *
125
     * @return bool
126
     */
127
    protected function reachesLimit($resultsNum)
128
    {
129
        return $this->limit && $resultsNum >= $this->limit;
130
    }
131
132
    /**
133
     * @return \Generator|void
134
     */
135
    protected function processCallback()
136
    {
137
        $this->resultsNum = 0;
138
        $this->processed = 0;
139
140
        while (true) {
141
            /** @var PaginatedResponse $response */
142
            $response = call_user_func($this->callback);
143
144
            if ($response->isEmpty()) return;
145
146
            foreach ($this->getResultsFromResponse($response) as $result) {
0 ignored issues
show
Bug introduced by
The expression $this->getResultsFromResponse($response) of type array|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
147
                $this->processed++;
148
149
                if ($this->processed > $this->offset) {
150
                    yield $result;
151
                    $this->resultsNum++;
152
                }
153
154
                if ($this->reachesLimit($this->resultsNum)) return;
155
            }
156
157
            if (!$response->hasBookmarks()) return;
158
        }
159
    }
160
161
    /**
162
     * @param $response
163
     * @return array
164
     */
165
    protected function getResultsFromResponse(PaginatedResponse $response)
166
    {
167
        $results = $response->getResponseData();
168
169
        return $results ? : [];
170
    }
171
}
172