Completed
Pull Request — master (#228)
by Sergey
04:23
created

Pagination::take()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace seregazhuk\PinterestBot\Helpers;
4
5
use Traversable;
6
use seregazhuk\PinterestBot\Api\Contracts\PaginatedResponse;
7
8
/**
9
 * Class Pagination
10
 * Iterate through results of Pinterest Api. By default iterator will return 50 first
11
 * pagination results. To change this behaviour specify another limit as the
12
 * constructor param. For no limits specify zero.
13
 *
14
 * @package seregazhuk\PinterestBot\Helpers
15
 */
16
class Pagination implements \IteratorAggregate
17
{
18
    const DEFAULT_LIMIT = 50;
19
20
    /**
21
     * @var int
22
     */
23
    protected $limit;
24
25
    /**
26
     * @var array
27
     */
28
    protected $bookmarks = [];
29
30
    /**
31
     * @var callable
32
     */
33
    protected $callback;
34
35
    /**
36
     * @var int
37
     */
38
    protected $offset;
39
40
    /**
41
     * @param int $limit
42
     */
43
    public function __construct($limit = self::DEFAULT_LIMIT)
44
    {
45
        $this->limit = $limit;
46
    }
47
48
    /**
49
     * Sets a callback to make requests. Should be a closure
50
     * that accepts a $bookmarks array as an argument.
51
     *
52
     * @param callable $callback
53
     * @return $this
54
     */
55
    public function paginateOver(callable $callback)
56
    {
57
        $this->callback = $callback;
58
59
        return $this;
60
    }
61
62
    /**
63
     * Syntax sugar for getIterator method
64
     * @return Traversable
65
     */
66
    public function get()
67
    {
68
        return $this->getIterator();
69
    }
70
71
    /**
72
     * Retrieve an external iterator
73
     * @return Traversable
74
     */
75
    public function getIterator()
76
    {
77
        $resultsNum = 0;
78
        $processed = 0;
79
80
        while (true) {
81
            $results = $this->getCurrentResults();
82
83
            if (empty($results)) return;
84
85
            foreach ($results as $result) {
0 ignored issues
show
Bug introduced by
The expression $results 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...
86
                $processed++;
87
88
                if($processed > $this->offset) {
89
                    yield $result;
90
                    $resultsNum++;
91
                }
92
93
                if ($this->paginationFinished($resultsNum)) return;
94
            }
95
        }
96
    }
97
98
    /**
99
     * @param int $offset
100
     * @return $this
101
     */
102
    public function skip($offset)
103
    {
104
        $this->offset = $offset;
105
106
        return $this;
107
    }
108
109
    /**
110
     * @param int $limit
111
     * @return $this
112
     */
113
    public function take($limit)
114
    {
115
        $this->limit = $limit;
116
117
        return $this;
118
    }
119
120
    /**
121
     * @return array
122
     */
123
    public function toArray()
124
    {
125
        return iterator_to_array($this->getIterator());
126
    }
127
128
    /**
129
     * @return array
130
     */
131
    protected function getCurrentResults()
132
    {
133
        $callback = $this->callback;
134
135
        $response = $callback($this->bookmarks);
136
137
        return $this->processResponse($response);
138
    }
139
140
    /**
141
     * @param PaginatedResponse $response
142
     * @return array
143
     */
144
    protected function processResponse(PaginatedResponse $response)
145
    {
146
        if ($response->isEmpty()) return [];
147
148
        $this->bookmarks = $response->getBookmarks();
149
150
        return $response->getResponseData();
151
    }
152
153
    /**
154
     * @param int $resultsNum
155
     * @return bool
156
     */
157
    protected function paginationFinished($resultsNum)
158
    {
159
        return $this->reachesLimit($resultsNum) || $this->checkEndBookMarks();
160
    }
161
162
    /**
163
     * Check if we get results limit in pagination.
164
     *
165
     * @param int $resultsNum
166
     *
167
     * @return bool
168
     */
169
    protected function reachesLimit($resultsNum)
170
    {
171
        return $this->limit && $resultsNum >= $this->limit;
172
    }
173
174
175
    /**
176
     * Checks for -end- substring in bookmarks. This is pinterest sign of
177
     * the finished pagination.
178
     *
179
     * @return bool
180
     */
181
    protected function checkEndBookMarks()
182
    {
183
        return !empty($this->bookmarks) && $this->bookmarks[0] == '-end-';
184
    }
185
}
186