Completed
Pull Request — master (#229)
by Sergey
02:32
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 seregazhuk\PinterestBot\Api\Contracts\PaginatedResponse;
6
use Traversable;
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->reachesLimit($resultsNum)) return;
94
            }
95
96
            if ($this->checkEndBookMarks()) return;
97
        }
98
    }
99
100
    /**
101
     * @param int $offset
102
     * @return $this
103
     */
104
    public function skip($offset)
105
    {
106
        $this->offset = $offset;
107
108
        return $this;
109
    }
110
111
    /**
112
     * @param int $limit
113
     * @return $this
114
     */
115
    public function take($limit)
116
    {
117
        $this->limit = $limit;
118
119
        return $this;
120
    }
121
122
    /**
123
     * @return array
124
     */
125
    public function toArray()
126
    {
127
        return iterator_to_array($this->getIterator());
128
    }
129
130
    /**
131
     * @return array
132
     */
133
    protected function getCurrentResults()
134
    {
135
        $callback = $this->callback;
136
137
        $response = $callback($this->bookmarks);
138
139
        return $this->processResponse($response);
140
    }
141
142
    /**
143
     * @param PaginatedResponse $response
144
     * @return array
145
     */
146
    protected function processResponse(PaginatedResponse $response)
147
    {
148
        if ($response->isEmpty()) return [];
149
150
        $this->bookmarks = $response->getBookmarks();
151
152
        return $response->getResponseData();
153
    }
154
155
    /**
156
     * Check if we get results limit in pagination.
157
     *
158
     * @param int $resultsNum
159
     *
160
     * @return bool
161
     */
162
    protected function reachesLimit($resultsNum)
163
    {
164
        return $this->limit && $resultsNum >= $this->limit;
165
    }
166
167
168
    /**
169
     * Checks for -end- substring in bookmarks. This is pinterest sign of
170
     * the finished pagination.
171
     *
172
     * @return bool
173
     */
174
    protected function checkEndBookMarks()
175
    {
176
        return !empty($this->bookmarks) && $this->bookmarks[0] == '-end-';
177
    }
178
}
179