• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

janu-software / facebook-php-sdk / 13285164661

12 Feb 2025 12:17PM UTC coverage: 93.359%. Remained the same
13285164661

push

github

stanislav-janu
php 8.1 lock file

956 of 1024 relevant lines covered (93.36%)

11.45 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.45
/src/BatchRequest.php
1
<?php
2

3
declare(strict_types=1);
4
/**
5
 * Copyright 2017 Facebook, Inc.
6
 * You are hereby granted a non-exclusive, worldwide, royalty-free license to
7
 * use, copy, modify, and distribute this software in source code or binary
8
 * form for use in connection with the web services and APIs provided by
9
 * Facebook.
10
 * As with any software that integrates with the Facebook platform, your use
11
 * of this software is subject to the Facebook Developer Principles and
12
 * Policies [http://developers.facebook.com/policy/]. This copyright notice
13
 * shall be included in all copies or substantial portions of the software.
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
 * DEALINGS IN THE SOFTWARE.
21
 */
22

23
namespace JanuSoftware\Facebook;
24

25
use ArrayAccess;
26
use ArrayIterator;
27
use InvalidArgumentException;
28
use IteratorAggregate;
29
use JanuSoftware\Facebook\Authentication\AccessToken;
30
use JanuSoftware\Facebook\Exception\SDKException;
31
use JanuSoftware\Facebook\FileUpload\File;
32
use Safe\Exceptions\JsonException;
33
use function Safe\json_encode;
34

35

36
class BatchRequest extends Request implements IteratorAggregate, ArrayAccess
37
{
38
        protected array $requests = [];
39

40
        /** @var File[] */
41
        protected array $attachedFiles = [];
42

43

44
        /**
45
         * Creates a new Request entity.
46
         *
47
         * @param Request[] $requests
48
         */
49
        public function __construct(
50
                ?Application $application = null,
51
                array $requests = [],
52
                AccessToken|string|null $accessToken = null,
53
                ?string $graphVersion = null,
54
        ) {
55
                parent::__construct($application, $accessToken, 'POST', '', [], null, $graphVersion);
28✔
56

57
                $this->add($requests);
28✔
58
        }
59

60

61
        /**
62
         * Adds a new request to the array.
63
         *
64
         * @param Request[]|Request $request
65
         * @param array|string|null $options Array of batch request options e.g. 'name', 'omit_response_on_success'.
66
         * If a string is given, it is the value of the 'name' option.
67
         *
68
         * @throws InvalidArgumentException|SDKException
69
         */
70
        public function add(array|Request $request, null|array|string $options = null): static
71
        {
72
                if (is_array($request)) {
28✔
73
                        foreach ($request as $key => $req) {
28✔
74
                                $this->add($req, is_int($key) ? (string) $key : $key);
9✔
75
                        }
76

77
                        return $this;
28✔
78
                }
79

80
                if ($options === null) {
20✔
81
                        $options = [];
5✔
82
                } elseif (!is_array($options)) {
18✔
83
                        $options = ['name' => $options];
16✔
84
                }
85

86
                $this->addFallbackDefaults($request);
20✔
87

88
                // File uploads
89
                $attachedFiles = $this->extractFileAttachments($request);
20✔
90

91
                $name = $options['name'] ?? null;
20✔
92

93
                unset($options['name']);
20✔
94

95
                $requestToAdd = [
20✔
96
                        'name' => $name,
20✔
97
                        'request' => $request,
20✔
98
                        'options' => $options,
20✔
99
                        'attached_files' => $attachedFiles,
20✔
100
                ];
20✔
101

102
                $this->requests[] = $requestToAdd;
20✔
103

104
                return $this;
20✔
105
        }
106

107

108
        /**
109
         * Ensures that the Application and access token fall back when missing.
110
         * @throws SDKException
111
         */
112
        public function addFallbackDefaults(Request $request): void
113
        {
114
                if ($request->getApplication() === null) {
25✔
115
                        $application = $this->getApplication();
17✔
116
                        if (!$application instanceof Application) {
17✔
117
                                throw new SDKException('Missing Application on Request and no fallback detected on BatchRequest.');
1✔
118
                        }
119
                        $request->setApp($application);
16✔
120
                }
121

122
                if ($request->getAccessToken() === null) {
24✔
123
                        $accessToken = $this->getAccessToken();
17✔
124
                        if ($accessToken === null) {
17✔
125
                                throw new SDKException('Missing access token on Request and no fallback detected on BatchRequest.');
1✔
126
                        }
127
                        $request->setAccessToken($accessToken);
16✔
128
                }
129
        }
130

131

132
        /**
133
         * Extracts the files from a request.
134
         * @throws SDKException
135
         */
136
        public function extractFileAttachments(Request $request): ?string
137
        {
138
                if (!$request->containsFileUploads()) {
20✔
139
                        return null;
18✔
140
                }
141

142
                $files = $request->getFiles();
3✔
143
                $fileNames = [];
3✔
144
                foreach ($files as $file) {
3✔
145
                        $fileName = uniqid();
3✔
146
                        $this->addFile($fileName, $file);
3✔
147
                        $fileNames[] = $fileName;
3✔
148
                }
149

150
                $request->resetFiles();
3✔
151

152
                // @TODO Does Graph support multiple uploads on one endpoint?
153
                return implode(',', $fileNames);
3✔
154
        }
155

156

157
        /**
158
         * Return the Request entities.
159
         * @return mixed[]
160
         */
161
        public function getRequests(): array
162
        {
163
                return $this->requests;
10✔
164
        }
165

166

167
        /**
168
         * Prepares the requests to be sent as a batch request.
169
         */
170
        public function prepareRequestsForBatch(): void
171
        {
172
                $this->validateBatchRequestCount();
5✔
173

174
                $params = [
5✔
175
                        'batch' => $this->convertRequestsToJson(),
5✔
176
                        'include_headers' => true,
5✔
177
                ];
5✔
178
                $this->setParams($params);
5✔
179
        }
180

181

182
        /**
183
         * Converts the requests into a JSON(P) string.
184
         * @throws JsonException
185
         */
186
        public function convertRequestsToJson(): string
187
        {
188
                $requests = [];
5✔
189
                foreach ($this->requests as $request) {
5✔
190
                        $options = [];
5✔
191

192
                        if ($request['name'] !== null) {
5✔
193
                                $options['name'] = $request['name'];
5✔
194
                        }
195

196
                        $options += $request['options'];
5✔
197

198
                        $requests[] = $this->requestEntityToBatchArray($request['request'], $options, $request['attached_files']);
5✔
199
                }
200

201
                return json_encode($requests);
5✔
202
        }
203

204

205
        /**
206
         * Validate the request count before sending them as a batch.
207
         * @throws SDKException
208
         */
209
        public function validateBatchRequestCount(): void
210
        {
211
                $batchCount = count($this->requests);
8✔
212
                if ($batchCount === 0) {
8✔
213
                        throw new SDKException('There are no batch requests to send.');
1✔
214
                } elseif ($batchCount > 50) {
7✔
215
                        // Per: https://developers.facebook.com/docs/graph-api/making-multiple-requests#limits
216
                        throw new SDKException('You cannot send more than 50 batch requests at a time.');
1✔
217
                }
218
        }
219

220

221
        /**
222
         * Converts a Request entity into an array that is batch-friendly.
223
         *
224
         * @param Request             $request       the request entity to convert
225
         * @param string|mixed[]|null $options       Array of batch request options e.g. 'name', 'omit_response_on_success'. If a string is given, it is the value of the 'name' option.
226
         * @param string|null         $attachedFiles names of files associated with the request
227
         *
228
         * @return mixed[]
229
         */
230
        public function requestEntityToBatchArray(
231
                Request $request,
232
                array|string|null $options = null,
233
                ?string $attachedFiles = null,
234
        ): array {
235
                if ($options === null) {
10✔
236
                        $options = [];
×
237
                } elseif (!is_array($options)) {
10✔
238
                        $options = ['name' => $options];
4✔
239
                }
240

241
                $compiledHeaders = [];
10✔
242
                $headers = $request->getHeaders();
10✔
243
                foreach ($headers as $name => $value) {
10✔
244
                        $compiledHeaders[] = $name . ': ' . $value;
10✔
245
                }
246

247
                $batch = [
10✔
248
                        'headers' => $compiledHeaders,
10✔
249
                        'method' => $request->getMethod(),
10✔
250
                        'relative_url' => $request->getUrl(),
10✔
251
                ];
10✔
252

253
                // Since file uploads are moved to the root request of a batch request,
254
                // the child requests will always be URL-encoded.
255
                $stream = $request->getUrlEncodedBody()
10✔
256
                        ->getBody();
10✔
257
                if ($stream !== null) {
10✔
258
                        $batch['body'] = $stream->getContents();
6✔
259
                }
260

261
                $batch += $options;
10✔
262

263
                if ($attachedFiles !== null) {
10✔
264
                        $batch['attached_files'] = $attachedFiles;
3✔
265
                }
266

267
                return $batch;
10✔
268
        }
269

270

271
        /**
272
         * Get an iterator for the items.
273
         */
274
        public function getIterator(): ArrayIterator
275
        {
276
                return new ArrayIterator($this->requests);
×
277
        }
278

279

280
        /**
281
         * {@inheritdoc}
282
         */
283
        public function offsetSet($offset, $value): void
284
        {
285
                $this->add($value, $offset);
×
286
        }
287

288

289
        /**
290
         * {@inheritdoc}
291
         */
292
        public function offsetExists($offset): bool
293
        {
294
                return isset($this->requests[$offset]);
5✔
295
        }
296

297

298
        /**
299
         * {@inheritdoc}
300
         */
301
        public function offsetUnset($offset): void
302
        {
303
                unset($this->requests[$offset]);
×
304
        }
305

306

307
        /**
308
         * {@inheritdoc}
309
         */
310
        public function offsetGet($offset): mixed
311
        {
312
                return $this->requests[$offset] ?? null;
5✔
313
        }
314
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc