InstagramBatchRequest::getRequests()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Maztech;
4
5
use ArrayIterator;
6
use IteratorAggregate;
7
use ArrayAccess;
8
use Maztech\Authentication\AccessToken;
9
use Maztech\Exceptions\InstagramSDKException;
10
11
/**
12
 * Class BatchRequest
13
 *
14
 * @package Instagram
15
 */
16
class InstagramBatchRequest extends InstagramRequest implements IteratorAggregate, ArrayAccess
17
{
18
    /**
19
     * @var array An array of InstagramRequest entities to send.
20
     */
21
    protected $requests = [];
22
23
    /**
24
     * @var array An array of files to upload.
25
     */
26
    protected $attachedFiles;
27
28
    /**
29
     * Creates a new Request entity.
30
     *
31
     * @param InstagramApp|null        $app
32
     * @param array                   $requests
33
     * @param AccessToken|string|null $accessToken
34
     * @param string|null             $graphVersion
35
     */
36
    public function __construct(InstagramApp $app = null, array $requests = [], $accessToken = null, $graphVersion = null)
37
    {
38
        parent::__construct($app, $accessToken, 'POST', '', [], null, $graphVersion);
0 ignored issues
show
Unused Code introduced by
The call to Maztech\InstagramRequest::__construct() has too many arguments starting with $graphVersion. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

38
        parent::/** @scrutinizer ignore-call */ 
39
                __construct($app, $accessToken, 'POST', '', [], null, $graphVersion);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
39
40
        $this->add($requests);
41
    }
42
43
    /**
44
     * Adds a new request to the array.
45
     *
46
     * @param InstagramRequest|array $request
47
     * @param string|null|array     $options Array of batch request options e.g. 'name', 'omit_response_on_success'.
48
     *                                       If a string is given, it is the value of the 'name' option.
49
     *
50
     * @return InstagramBatchRequest
51
     *
52
     * @throws \InvalidArgumentException
53
     */
54
    public function add($request, $options = null)
55
    {
56
        if (is_array($request)) {
57
            foreach ($request as $key => $req) {
58
                $this->add($req, $key);
59
            }
60
61
            return $this;
62
        }
63
64
        if (!$request instanceof InstagramRequest) {
0 ignored issues
show
introduced by
$request is always a sub-type of Maztech\InstagramRequest.
Loading history...
65
            throw new \InvalidArgumentException('Argument for add() must be of type array or InstagramRequest.');
66
        }
67
68
        if (null === $options) {
69
            $options = [];
70
        } elseif (!is_array($options)) {
71
            $options = ['name' => $options];
72
        }
73
74
        $this->addFallbackDefaults($request);
75
76
        $name = isset($options['name']) ? $options['name'] : null;
77
78
        unset($options['name']);
79
80
        $requestToAdd = [
81
            'name' => $name,
82
            'request' => $request,
83
            'options' => $options
84
        ];
85
86
        $this->requests[] = $requestToAdd;
87
88
        return $this;
89
    }
90
91
    /**
92
     * Ensures that the InstagramApp and access token fall back when missing.
93
     *
94
     * @param InstagramRequest $request
95
     *
96
     * @throws InstagramSDKException
97
     */
98
    public function addFallbackDefaults(InstagramRequest $request)
99
    {
100
        if (!$request->getApp()) {
101
            $app = $this->getApp();
102
            if (!$app) {
0 ignored issues
show
introduced by
$app is of type Maztech\InstagramApp, thus it always evaluated to true.
Loading history...
103
                throw new InstagramSDKException('Missing InstagramApp on InstagramRequest and no fallback detected on InstagramBatchRequest.');
104
            }
105
            $request->setApp($app);
106
        }
107
108
        if (!$request->getAccessToken()) {
109
            $accessToken = $this->getAccessToken();
110
            if (!$accessToken) {
111
                throw new InstagramSDKException('Missing access token on InstagramRequest and no fallback detected on InstagramBatchRequest.');
112
            }
113
            $request->setAccessToken($accessToken);
114
        }
115
    }
116
117
    /**
118
     * Return the InstagramRequest entities.
119
     *
120
     * @return array
121
     */
122
    public function getRequests()
123
    {
124
        return $this->requests;
125
    }
126
127
    /**
128
     * Prepares the requests to be sent as a batch request.
129
     */
130
    public function prepareRequestsForBatch()
131
    {
132
        $this->validateBatchRequestCount();
133
134
        $params = [
135
            'batch' => $this->convertRequestsToJson(),
136
            'include_headers' => true,
137
        ];
138
        $this->setParams($params);
139
    }
140
141
    /**
142
     * Converts the requests into a JSON(P) string.
143
     *
144
     * @return string
145
     */
146
    public function convertRequestsToJson()
147
    {
148
        $requests = [];
149
        foreach ($this->requests as $request) {
150
            $options = [];
151
152
            if (null !== $request['name']) {
153
                $options['name'] = $request['name'];
154
            }
155
156
            $options += $request['options'];
157
158
            $requests[] = $this->requestEntityToBatchArray($request['request'], $options, $request['attached_files']);
159
        }
160
161
        return json_encode($requests);
162
    }
163
164
    /**
165
     * Validate the request count before sending them as a batch.
166
     *
167
     * @throws InstagramSDKException
168
     */
169
    public function validateBatchRequestCount()
170
    {
171
        $batchCount = count($this->requests);
172
        if ($batchCount === 0) {
173
            throw new InstagramSDKException('There are no batch requests to send.');
174
        } elseif ($batchCount > 50) {
175
            // Per: https://developers.Instagram.com/docs/graph-api/making-multiple-requests#limits
176
            throw new InstagramSDKException('You cannot send more than 50 batch requests at a time.');
177
        }
178
    }
179
180
    /**
181
     * Converts a Request entity into an array that is batch-friendly.
182
     *
183
     * @param InstagramRequest   $request       The request entity to convert.
184
     * @param string|null|array $options       Array of batch request options e.g. 'name', 'omit_response_on_success'.
185
     *                                         If a string is given, it is the value of the 'name' option.
186
     * @param string|null       $attachedFiles Names of files associated with the request.
187
     *
188
     * @return array
189
     */
190
    public function requestEntityToBatchArray(InstagramRequest $request, $options = null, $attachedFiles = null)
191
    {
192
193
        if (null === $options) {
194
            $options = [];
195
        } elseif (!is_array($options)) {
196
            $options = ['name' => $options];
197
        }
198
199
        $compiledHeaders = [];
200
        $headers = $request->getHeaders();
201
        foreach ($headers as $name => $value) {
202
            $compiledHeaders[] = $name . ': ' . $value;
203
        }
204
205
        $batch = [
206
            'headers' => $compiledHeaders,
207
            'method' => $request->getMethod(),
208
            'relative_url' => $request->getUrl(),
209
        ];
210
211
        // Since file uploads are moved to the root request of a batch request,
212
        // the child requests will always be URL-encoded.
213
        $body = $request->getUrlEncodedBody()->getBody();
214
        if ($body) {
215
            $batch['body'] = $body;
216
        }
217
218
        $batch += $options;
219
220
        if (null !== $attachedFiles) {
221
            $batch['attached_files'] = $attachedFiles;
222
        }
223
224
        return $batch;
225
    }
226
227
    /**
228
     * Get an iterator for the items.
229
     *
230
     * @return ArrayIterator
231
     */
232
    public function getIterator()
233
    {
234
        return new ArrayIterator($this->requests);
235
    }
236
237
    /**
238
     * @inheritdoc
239
     */
240
    public function offsetSet($offset, $value)
241
    {
242
        $this->add($value, $offset);
243
    }
244
245
    /**
246
     * @inheritdoc
247
     */
248
    public function offsetExists($offset)
249
    {
250
        return isset($this->requests[$offset]);
251
    }
252
253
    /**
254
     * @inheritdoc
255
     */
256
    public function offsetUnset($offset)
257
    {
258
        unset($this->requests[$offset]);
259
    }
260
261
    /**
262
     * @inheritdoc
263
     */
264
    public function offsetGet($offset)
265
    {
266
        return isset($this->requests[$offset]) ? $this->requests[$offset] : null;
267
    }
268
}
269